1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2015 Davidlohr Bueso.
6 /* For the CLR_() macros */
11 #include "../util/mutex.h"
12 #include "../util/stat.h"
13 #include <subcmd/parse-options.h>
14 #include <linux/compiler.h>
15 #include <linux/kernel.h>
16 #include <linux/zalloc.h>
18 #include <perf/cpumap.h>
34 static u_int32_t global_futex
= 0;
35 static struct worker
*worker
;
36 static bool done
= false;
37 static int futex_flag
= 0;
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
;
43 static struct bench_futex_parameters params
= {
47 static const struct option options
[] = {
48 OPT_UINTEGER('t', "threads", ¶ms
.nthreads
, "Specify amount of threads"),
49 OPT_UINTEGER('r', "runtime", ¶ms
.runtime
, "Specify runtime (in seconds)"),
50 OPT_BOOLEAN( 'M', "multi", ¶ms
.multi
, "Use multiple futexes"),
51 OPT_BOOLEAN( 's', "silent", ¶ms
.silent
, "Silent mode: do not display data/details"),
52 OPT_BOOLEAN( 'S', "shared", ¶ms
.fshared
, "Use shared futexes instead of private ones"),
53 OPT_BOOLEAN( 'm', "mlockall", ¶ms
.mlockall
, "Lock all current and future memory"),
57 static const char * const bench_futex_lock_pi_usage
[] = {
58 "perf bench futex lock-pi <options>",
62 static void print_summary(void)
64 unsigned long avg
= avg_stats(&throughput_stats
);
65 double stddev
= stddev_stats(&throughput_stats
);
67 printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
68 !params
.silent
? "\n" : "", avg
, rel_stddev_stats(stddev
, avg
),
69 (int)bench__runtime
.tv_sec
);
72 static void toggle_done(int sig __maybe_unused
,
73 siginfo_t
*info __maybe_unused
,
74 void *uc __maybe_unused
)
76 /* inform all threads that we're done for the day */
78 gettimeofday(&bench__end
, NULL
);
79 timersub(&bench__end
, &bench__start
, &bench__runtime
);
82 static void *workerfn(void *arg
)
84 struct worker
*w
= (struct worker
*) arg
;
85 unsigned long ops
= w
->ops
;
87 mutex_lock(&thread_lock
);
89 if (!threads_starting
)
90 cond_signal(&thread_parent
);
91 cond_wait(&thread_worker
, &thread_lock
);
92 mutex_unlock(&thread_lock
);
97 ret
= futex_lock_pi(w
->futex
, NULL
, futex_flag
);
99 if (ret
) { /* handle lock acquisition */
101 warn("thread %d: Could not lock pi-lock for %p (%d)",
102 w
->tid
, w
->futex
, ret
);
110 ret
= futex_unlock_pi(w
->futex
, futex_flag
);
111 if (ret
&& !params
.silent
)
112 warn("thread %d: Could not unlock pi-lock for %p (%d)",
113 w
->tid
, w
->futex
, ret
);
114 ops
++; /* account for thread's share of work */
121 static void create_threads(struct worker
*w
, struct perf_cpu_map
*cpu
)
125 int nrcpus
= cpu__max_cpu().cpu
;
128 threads_starting
= params
.nthreads
;
130 cpuset
= CPU_ALLOC(nrcpus
);
132 size
= CPU_ALLOC_SIZE(nrcpus
);
134 for (i
= 0; i
< params
.nthreads
; i
++) {
135 pthread_attr_t thread_attr
;
137 pthread_attr_init(&thread_attr
);
141 worker
[i
].futex
= calloc(1, sizeof(u_int32_t
));
142 if (!worker
[i
].futex
)
143 err(EXIT_FAILURE
, "calloc");
145 worker
[i
].futex
= &global_futex
;
147 CPU_ZERO_S(size
, cpuset
);
148 CPU_SET_S(perf_cpu_map__cpu(cpu
, i
% perf_cpu_map__nr(cpu
)).cpu
, size
, cpuset
);
150 if (pthread_attr_setaffinity_np(&thread_attr
, size
, cpuset
)) {
152 err(EXIT_FAILURE
, "pthread_attr_setaffinity_np");
155 if (pthread_create(&w
[i
].thread
, &thread_attr
, workerfn
, &worker
[i
])) {
157 err(EXIT_FAILURE
, "pthread_create");
159 pthread_attr_destroy(&thread_attr
);
164 int bench_futex_lock_pi(int argc
, const char **argv
)
168 struct sigaction act
;
169 struct perf_cpu_map
*cpu
;
171 argc
= parse_options(argc
, argv
, options
, bench_futex_lock_pi_usage
, 0);
175 cpu
= perf_cpu_map__new_online_cpus();
177 err(EXIT_FAILURE
, "calloc");
179 memset(&act
, 0, sizeof(act
));
180 sigfillset(&act
.sa_mask
);
181 act
.sa_sigaction
= toggle_done
;
182 sigaction(SIGINT
, &act
, NULL
);
184 if (params
.mlockall
) {
185 if (mlockall(MCL_CURRENT
| MCL_FUTURE
))
186 err(EXIT_FAILURE
, "mlockall");
189 if (!params
.nthreads
)
190 params
.nthreads
= perf_cpu_map__nr(cpu
);
192 worker
= calloc(params
.nthreads
, sizeof(*worker
));
194 err(EXIT_FAILURE
, "calloc");
197 futex_flag
= FUTEX_PRIVATE_FLAG
;
199 printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n",
200 getpid(), params
.nthreads
, params
.runtime
);
202 init_stats(&throughput_stats
);
203 mutex_init(&thread_lock
);
204 cond_init(&thread_parent
);
205 cond_init(&thread_worker
);
207 threads_starting
= params
.nthreads
;
208 gettimeofday(&bench__start
, NULL
);
210 create_threads(worker
, cpu
);
212 mutex_lock(&thread_lock
);
213 while (threads_starting
)
214 cond_wait(&thread_parent
, &thread_lock
);
215 cond_broadcast(&thread_worker
);
216 mutex_unlock(&thread_lock
);
218 sleep(params
.runtime
);
219 toggle_done(0, NULL
, NULL
);
221 for (i
= 0; i
< params
.nthreads
; i
++) {
222 ret
= pthread_join(worker
[i
].thread
, NULL
);
224 err(EXIT_FAILURE
, "pthread_join");
227 /* cleanup & report results */
228 cond_destroy(&thread_parent
);
229 cond_destroy(&thread_worker
);
230 mutex_destroy(&thread_lock
);
232 for (i
= 0; i
< params
.nthreads
; i
++) {
233 unsigned long t
= bench__runtime
.tv_sec
> 0 ?
234 worker
[i
].ops
/ bench__runtime
.tv_sec
: 0;
236 update_stats(&throughput_stats
, t
);
238 printf("[thread %3d] futex: %p [ %ld ops/sec ]\n",
239 worker
[i
].tid
, worker
[i
].futex
, t
);
242 zfree(&worker
[i
].futex
);
248 perf_cpu_map__put(cpu
);
251 usage_with_options(bench_futex_lock_pi_usage
, options
);