1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2013 Davidlohr Bueso <davidlohr@hp.com>
5 * futex-hash: Stress the hell out of the Linux kernel futex uaddr hashing.
7 * This program is particularly useful for measuring the kernel's futex hash
8 * table/function implementation. In order for it to make sense, use with as
9 * many threads and futexes as possible.
12 /* For the CLR_() macros */
19 #include <linux/compiler.h>
20 #include <linux/kernel.h>
21 #include <linux/zalloc.h>
24 #include <perf/cpumap.h>
26 #include "../util/mutex.h"
27 #include "../util/stat.h"
28 #include <subcmd/parse-options.h>
34 static bool done
= false;
35 static int futex_flag
= 0;
37 struct timeval bench__start
, bench__end
, bench__runtime
;
38 static struct mutex thread_lock
;
39 static unsigned int threads_starting
;
40 static struct stats throughput_stats
;
41 static struct cond thread_parent
, thread_worker
;
50 static struct bench_futex_parameters params
= {
55 static const struct option options
[] = {
56 OPT_UINTEGER('t', "threads", ¶ms
.nthreads
, "Specify amount of threads"),
57 OPT_UINTEGER('r', "runtime", ¶ms
.runtime
, "Specify runtime (in seconds)"),
58 OPT_UINTEGER('f', "futexes", ¶ms
.nfutexes
, "Specify amount of futexes per threads"),
59 OPT_BOOLEAN( 's', "silent", ¶ms
.silent
, "Silent mode: do not display data/details"),
60 OPT_BOOLEAN( 'S', "shared", ¶ms
.fshared
, "Use shared futexes instead of private ones"),
61 OPT_BOOLEAN( 'm', "mlockall", ¶ms
.mlockall
, "Lock all current and future memory"),
65 static const char * const bench_futex_hash_usage
[] = {
66 "perf bench futex hash <options>",
70 static void *workerfn(void *arg
)
73 struct worker
*w
= (struct worker
*) arg
;
75 unsigned long ops
= w
->ops
; /* avoid cacheline bouncing */
77 mutex_lock(&thread_lock
);
79 if (!threads_starting
)
80 cond_signal(&thread_parent
);
81 cond_wait(&thread_worker
, &thread_lock
);
82 mutex_unlock(&thread_lock
);
85 for (i
= 0; i
< params
.nfutexes
; i
++, ops
++) {
87 * We want the futex calls to fail in order to stress
88 * the hashing of uaddr and not measure other steps,
89 * such as internal waitqueue handling, thus enlarging
90 * the critical region protected by hb->lock.
92 ret
= futex_wait(&w
->futex
[i
], 1234, NULL
, futex_flag
);
94 (!ret
|| errno
!= EAGAIN
|| errno
!= EWOULDBLOCK
))
95 warn("Non-expected futex return call");
103 static void toggle_done(int sig __maybe_unused
,
104 siginfo_t
*info __maybe_unused
,
105 void *uc __maybe_unused
)
107 /* inform all threads that we're done for the day */
109 gettimeofday(&bench__end
, NULL
);
110 timersub(&bench__end
, &bench__start
, &bench__runtime
);
113 static void print_summary(void)
115 unsigned long avg
= avg_stats(&throughput_stats
);
116 double stddev
= stddev_stats(&throughput_stats
);
118 printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
119 !params
.silent
? "\n" : "", avg
, rel_stddev_stats(stddev
, avg
),
120 (int)bench__runtime
.tv_sec
);
123 int bench_futex_hash(int argc
, const char **argv
)
127 struct sigaction act
;
129 pthread_attr_t thread_attr
;
130 struct worker
*worker
= NULL
;
131 struct perf_cpu_map
*cpu
;
135 argc
= parse_options(argc
, argv
, options
, bench_futex_hash_usage
, 0);
137 usage_with_options(bench_futex_hash_usage
, options
);
141 cpu
= perf_cpu_map__new_online_cpus();
145 memset(&act
, 0, sizeof(act
));
146 sigfillset(&act
.sa_mask
);
147 act
.sa_sigaction
= toggle_done
;
148 sigaction(SIGINT
, &act
, NULL
);
150 if (params
.mlockall
) {
151 if (mlockall(MCL_CURRENT
| MCL_FUTURE
))
152 err(EXIT_FAILURE
, "mlockall");
155 if (!params
.nthreads
) /* default to the number of CPUs */
156 params
.nthreads
= perf_cpu_map__nr(cpu
);
158 worker
= calloc(params
.nthreads
, sizeof(*worker
));
163 futex_flag
= FUTEX_PRIVATE_FLAG
;
165 printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
166 getpid(), params
.nthreads
, params
.nfutexes
, params
.fshared
? "shared":"private", params
.runtime
);
168 init_stats(&throughput_stats
);
169 mutex_init(&thread_lock
);
170 cond_init(&thread_parent
);
171 cond_init(&thread_worker
);
173 threads_starting
= params
.nthreads
;
174 pthread_attr_init(&thread_attr
);
175 gettimeofday(&bench__start
, NULL
);
177 nrcpus
= cpu__max_cpu().cpu
;
178 cpuset
= CPU_ALLOC(nrcpus
);
180 size
= CPU_ALLOC_SIZE(nrcpus
);
182 for (i
= 0; i
< params
.nthreads
; i
++) {
184 worker
[i
].futex
= calloc(params
.nfutexes
, sizeof(*worker
[i
].futex
));
185 if (!worker
[i
].futex
)
188 CPU_ZERO_S(size
, cpuset
);
190 CPU_SET_S(perf_cpu_map__cpu(cpu
, i
% perf_cpu_map__nr(cpu
)).cpu
, size
, cpuset
);
191 ret
= pthread_attr_setaffinity_np(&thread_attr
, size
, cpuset
);
194 err(EXIT_FAILURE
, "pthread_attr_setaffinity_np");
196 ret
= pthread_create(&worker
[i
].thread
, &thread_attr
, workerfn
,
197 (void *)(struct worker
*) &worker
[i
]);
200 err(EXIT_FAILURE
, "pthread_create");
205 pthread_attr_destroy(&thread_attr
);
207 mutex_lock(&thread_lock
);
208 while (threads_starting
)
209 cond_wait(&thread_parent
, &thread_lock
);
210 cond_broadcast(&thread_worker
);
211 mutex_unlock(&thread_lock
);
213 sleep(params
.runtime
);
214 toggle_done(0, NULL
, NULL
);
216 for (i
= 0; i
< params
.nthreads
; i
++) {
217 ret
= pthread_join(worker
[i
].thread
, NULL
);
219 err(EXIT_FAILURE
, "pthread_join");
222 /* cleanup & report results */
223 cond_destroy(&thread_parent
);
224 cond_destroy(&thread_worker
);
225 mutex_destroy(&thread_lock
);
227 for (i
= 0; i
< params
.nthreads
; i
++) {
228 unsigned long t
= bench__runtime
.tv_sec
> 0 ?
229 worker
[i
].ops
/ bench__runtime
.tv_sec
: 0;
230 update_stats(&throughput_stats
, t
);
231 if (!params
.silent
) {
232 if (params
.nfutexes
== 1)
233 printf("[thread %2d] futex: %p [ %ld ops/sec ]\n",
234 worker
[i
].tid
, &worker
[i
].futex
[0], t
);
236 printf("[thread %2d] futexes: %p ... %p [ %ld ops/sec ]\n",
237 worker
[i
].tid
, &worker
[i
].futex
[0],
238 &worker
[i
].futex
[params
.nfutexes
-1], t
);
241 zfree(&worker
[i
].futex
);
250 err(EXIT_FAILURE
, "calloc");