futex-lock-pi.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2015 Davidlohr Bueso.
  4. */
  5. /* For the CLR_() macros */
  6. #include <string.h>
  7. #include <pthread.h>
  8. #include <signal.h>
  9. #include "../util/mutex.h"
  10. #include "../util/stat.h"
  11. #include <subcmd/parse-options.h>
  12. #include <linux/compiler.h>
  13. #include <linux/kernel.h>
  14. #include <linux/zalloc.h>
  15. #include <errno.h>
  16. #include <perf/cpumap.h>
  17. #include "bench.h"
  18. #include "futex.h"
  19. #include <err.h>
  20. #include <stdlib.h>
  21. #include <sys/time.h>
  22. #include <sys/mman.h>
  23. struct worker {
  24. int tid;
  25. u_int32_t *futex;
  26. pthread_t thread;
  27. unsigned long ops;
  28. };
  29. static u_int32_t global_futex = 0;
  30. static struct worker *worker;
  31. static bool done = false;
  32. static int futex_flag = 0;
  33. static struct mutex thread_lock;
  34. static unsigned int threads_starting;
  35. static struct stats throughput_stats;
  36. static struct cond thread_parent, thread_worker;
  37. static struct bench_futex_parameters params = {
  38. .runtime = 10,
  39. };
  40. static const struct option options[] = {
  41. OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
  42. OPT_UINTEGER('r', "runtime", &params.runtime, "Specify runtime (in seconds)"),
  43. OPT_BOOLEAN( 'M', "multi", &params.multi, "Use multiple futexes"),
  44. OPT_BOOLEAN( 's', "silent", &params.silent, "Silent mode: do not display data/details"),
  45. OPT_BOOLEAN( 'S', "shared", &params.fshared, "Use shared futexes instead of private ones"),
  46. OPT_BOOLEAN( 'm', "mlockall", &params.mlockall, "Lock all current and future memory"),
  47. OPT_END()
  48. };
  49. static const char * const bench_futex_lock_pi_usage[] = {
  50. "perf bench futex lock-pi <options>",
  51. NULL
  52. };
  53. static void print_summary(void)
  54. {
  55. unsigned long avg = avg_stats(&throughput_stats);
  56. double stddev = stddev_stats(&throughput_stats);
  57. printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
  58. !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
  59. (int)bench__runtime.tv_sec);
  60. }
  61. static void toggle_done(int sig __maybe_unused,
  62. siginfo_t *info __maybe_unused,
  63. void *uc __maybe_unused)
  64. {
  65. /* inform all threads that we're done for the day */
  66. done = true;
  67. gettimeofday(&bench__end, NULL);
  68. timersub(&bench__end, &bench__start, &bench__runtime);
  69. }
  70. static void *workerfn(void *arg)
  71. {
  72. struct worker *w = (struct worker *) arg;
  73. unsigned long ops = w->ops;
  74. mutex_lock(&thread_lock);
  75. threads_starting--;
  76. if (!threads_starting)
  77. cond_signal(&thread_parent);
  78. cond_wait(&thread_worker, &thread_lock);
  79. mutex_unlock(&thread_lock);
  80. do {
  81. int ret;
  82. again:
  83. ret = futex_lock_pi(w->futex, NULL, futex_flag);
  84. if (ret) { /* handle lock acquisition */
  85. if (!params.silent)
  86. warn("thread %d: Could not lock pi-lock for %p (%d)",
  87. w->tid, w->futex, ret);
  88. if (done)
  89. break;
  90. goto again;
  91. }
  92. usleep(1);
  93. ret = futex_unlock_pi(w->futex, futex_flag);
  94. if (ret && !params.silent)
  95. warn("thread %d: Could not unlock pi-lock for %p (%d)",
  96. w->tid, w->futex, ret);
  97. ops++; /* account for thread's share of work */
  98. } while (!done);
  99. w->ops = ops;
  100. return NULL;
  101. }
  102. static void create_threads(struct worker *w, struct perf_cpu_map *cpu)
  103. {
  104. cpu_set_t *cpuset;
  105. unsigned int i;
  106. int nrcpus = cpu__max_cpu().cpu;
  107. size_t size;
  108. threads_starting = params.nthreads;
  109. cpuset = CPU_ALLOC(nrcpus);
  110. BUG_ON(!cpuset);
  111. size = CPU_ALLOC_SIZE(nrcpus);
  112. for (i = 0; i < params.nthreads; i++) {
  113. pthread_attr_t thread_attr;
  114. pthread_attr_init(&thread_attr);
  115. worker[i].tid = i;
  116. if (params.multi) {
  117. worker[i].futex = calloc(1, sizeof(u_int32_t));
  118. if (!worker[i].futex)
  119. err(EXIT_FAILURE, "calloc");
  120. } else
  121. worker[i].futex = &global_futex;
  122. CPU_ZERO_S(size, cpuset);
  123. CPU_SET_S(perf_cpu_map__cpu(cpu, i % perf_cpu_map__nr(cpu)).cpu, size, cpuset);
  124. if (pthread_attr_setaffinity_np(&thread_attr, size, cpuset)) {
  125. CPU_FREE(cpuset);
  126. err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
  127. }
  128. if (pthread_create(&w[i].thread, &thread_attr, workerfn, &worker[i])) {
  129. CPU_FREE(cpuset);
  130. err(EXIT_FAILURE, "pthread_create");
  131. }
  132. pthread_attr_destroy(&thread_attr);
  133. }
  134. CPU_FREE(cpuset);
  135. }
  136. int bench_futex_lock_pi(int argc, const char **argv)
  137. {
  138. int ret = 0;
  139. unsigned int i;
  140. struct sigaction act;
  141. struct perf_cpu_map *cpu;
  142. argc = parse_options(argc, argv, options, bench_futex_lock_pi_usage, 0);
  143. if (argc)
  144. goto err;
  145. cpu = perf_cpu_map__new_online_cpus();
  146. if (!cpu)
  147. err(EXIT_FAILURE, "calloc");
  148. memset(&act, 0, sizeof(act));
  149. sigfillset(&act.sa_mask);
  150. act.sa_sigaction = toggle_done;
  151. sigaction(SIGINT, &act, NULL);
  152. if (params.mlockall) {
  153. if (mlockall(MCL_CURRENT | MCL_FUTURE))
  154. err(EXIT_FAILURE, "mlockall");
  155. }
  156. if (!params.nthreads)
  157. params.nthreads = perf_cpu_map__nr(cpu);
  158. worker = calloc(params.nthreads, sizeof(*worker));
  159. if (!worker)
  160. err(EXIT_FAILURE, "calloc");
  161. if (!params.fshared)
  162. futex_flag = FUTEX_PRIVATE_FLAG;
  163. printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n",
  164. getpid(), params.nthreads, params.runtime);
  165. init_stats(&throughput_stats);
  166. mutex_init(&thread_lock);
  167. cond_init(&thread_parent);
  168. cond_init(&thread_worker);
  169. threads_starting = params.nthreads;
  170. gettimeofday(&bench__start, NULL);
  171. create_threads(worker, cpu);
  172. mutex_lock(&thread_lock);
  173. while (threads_starting)
  174. cond_wait(&thread_parent, &thread_lock);
  175. cond_broadcast(&thread_worker);
  176. mutex_unlock(&thread_lock);
  177. sleep(params.runtime);
  178. toggle_done(0, NULL, NULL);
  179. for (i = 0; i < params.nthreads; i++) {
  180. ret = pthread_join(worker[i].thread, NULL);
  181. if (ret)
  182. err(EXIT_FAILURE, "pthread_join");
  183. }
  184. /* cleanup & report results */
  185. cond_destroy(&thread_parent);
  186. cond_destroy(&thread_worker);
  187. mutex_destroy(&thread_lock);
  188. for (i = 0; i < params.nthreads; i++) {
  189. unsigned long t = bench__runtime.tv_sec > 0 ?
  190. worker[i].ops / bench__runtime.tv_sec : 0;
  191. update_stats(&throughput_stats, t);
  192. if (!params.silent)
  193. printf("[thread %3d] futex: %p [ %ld ops/sec ]\n",
  194. worker[i].tid, worker[i].futex, t);
  195. if (params.multi)
  196. zfree(&worker[i].futex);
  197. }
  198. print_summary();
  199. free(worker);
  200. perf_cpu_map__put(cpu);
  201. return ret;
  202. err:
  203. usage_with_options(bench_futex_lock_pi_usage, options);
  204. exit(EXIT_FAILURE);
  205. }