12 #include <sys/types.h>
16 static inline pid_t
gettid(void)
18 return syscall(__NR_gettid
);
22 static int loop_cnt
[NR_INJECT
+ 1];
24 static int opt_modulo
;
26 static int opt_yield
, opt_signal
, opt_sleep
, opt_fallback_cnt
= 3,
27 opt_disable_rseq
, opt_threads
= 200,
28 opt_reps
= 5000, opt_disable_mod
= 0, opt_test
= 's';
30 static __thread
unsigned int signals_delivered
;
32 static struct rseq_lock rseq_lock
;
36 static __thread
unsigned int yield_mod_cnt
, nr_retry
;
38 #define printf_nobench(fmt, ...) printf(fmt, ## __VA_ARGS__)
40 #define RSEQ_INJECT_INPUT \
41 , [loop_cnt_1]"m"(loop_cnt[1]) \
42 , [loop_cnt_2]"m"(loop_cnt[2]) \
43 , [loop_cnt_3]"m"(loop_cnt[3]) \
44 , [loop_cnt_4]"m"(loop_cnt[4])
46 #if defined(__x86_64__) || defined(__i386__)
48 #define INJECT_ASM_REG "eax"
50 #define RSEQ_INJECT_CLOBBER \
53 #define RSEQ_INJECT_ASM(n) \
54 "mov %[loop_cnt_" #n "], %%" INJECT_ASM_REG "\n\t" \
55 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
58 "dec %%" INJECT_ASM_REG "\n\t" \
62 #elif defined(__ARMEL__)
64 #define INJECT_ASM_REG "r4"
66 #define RSEQ_INJECT_CLOBBER \
69 #define RSEQ_INJECT_ASM(n) \
70 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
71 "cmp " INJECT_ASM_REG ", #0\n\t" \
74 "subs " INJECT_ASM_REG ", #1\n\t" \
79 #error unsupported target
82 #define RSEQ_INJECT_FAILED \
85 #define RSEQ_INJECT_C(n) \
87 int loc_i, loc_nr_loops = loop_cnt[n]; \
89 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
92 if (loc_nr_loops == -1 && opt_modulo) { \
93 if (yield_mod_cnt == opt_modulo - 1) { \
95 poll(NULL, 0, opt_sleep); \
107 #define RSEQ_FALLBACK_CNT \
112 #define printf_nobench(fmt, ...)
114 #endif /* BENCHMARK */
118 struct percpu_lock_entry
{
120 } __attribute__((aligned(128)));
123 struct percpu_lock_entry c
[CPU_SETSIZE
];
126 struct test_data_entry
{
128 } __attribute__((aligned(128)));
130 struct spinlock_test_data
{
131 struct percpu_lock lock
;
132 struct test_data_entry c
[CPU_SETSIZE
];
135 struct spinlock_thread_test_data
{
136 struct spinlock_test_data
*data
;
141 struct inc_test_data
{
142 struct test_data_entry c
[CPU_SETSIZE
];
145 struct inc_thread_test_data
{
146 struct inc_test_data
*data
;
151 struct percpu_list_node
{
153 struct percpu_list_node
*next
;
156 struct percpu_list_entry
{
157 struct percpu_list_node
*head
;
158 } __attribute__((aligned(128)));
161 struct percpu_list_entry c
[CPU_SETSIZE
];
164 /* A simple percpu spinlock. Returns the cpu lock was acquired on. */
165 static int rseq_percpu_lock(struct percpu_lock
*lock
)
167 struct rseq_state rseq_state
;
168 intptr_t *targetptr
, newval
;
173 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
175 if (unlikely(lock
->c
[cpu
].v
)) {
179 targetptr
= (intptr_t *)&lock
->c
[cpu
].v
;
186 * Acquire semantic when taking lock after control dependency.
187 * Matches smp_store_release().
189 smp_acquire__after_ctrl_dep();
193 static void rseq_percpu_unlock(struct percpu_lock
*lock
, int cpu
)
195 assert(lock
->c
[cpu
].v
== 1);
197 * Release lock, with release semantic. Matches
198 * smp_acquire__after_ctrl_dep().
200 smp_store_release(&lock
->c
[cpu
].v
, 0);
203 void *test_percpu_spinlock_thread(void *arg
)
205 struct spinlock_thread_test_data
*thread_data
= arg
;
206 struct spinlock_test_data
*data
= thread_data
->data
;
209 if (!opt_disable_rseq
&& thread_data
->reg
210 && rseq_init_current_thread())
212 for (i
= 0; i
< thread_data
->reps
; i
++) {
213 cpu
= rseq_percpu_lock(&data
->lock
);
214 data
->c
[cpu
].count
++;
215 rseq_percpu_unlock(&data
->lock
, cpu
);
217 if (i
!= 0 && !(i
% (thread_data
->reps
/ 10)))
218 printf("tid %d: count %d\n", (int) gettid(), i
);
221 printf_nobench("tid %d: number of retry: %d, signals delivered: %u, nr_fallback %u, nr_fallback_wait %u\n",
222 (int) gettid(), nr_retry
, signals_delivered
,
223 __rseq_thread_state
.fallback_cnt
,
224 __rseq_thread_state
.fallback_wait_cnt
);
229 * A simple test which implements a sharded counter using a per-cpu
230 * lock. Obviously real applications might prefer to simply use a
231 * per-cpu increment; however, this is reasonable for a test and the
232 * lock can be extended to synchronize more complicated operations.
234 void test_percpu_spinlock(void)
236 const int num_threads
= opt_threads
;
238 pthread_t test_threads
[num_threads
];
239 struct spinlock_test_data data
;
240 struct spinlock_thread_test_data thread_data
[num_threads
];
242 memset(&data
, 0, sizeof(data
));
243 for (i
= 0; i
< num_threads
; i
++) {
244 thread_data
[i
].reps
= opt_reps
;
245 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
246 thread_data
[i
].reg
= 1;
248 thread_data
[i
].reg
= 0;
249 thread_data
[i
].data
= &data
;
250 ret
= pthread_create(&test_threads
[i
], NULL
,
251 test_percpu_spinlock_thread
, &thread_data
[i
]);
254 perror("pthread_create");
259 for (i
= 0; i
< num_threads
; i
++) {
260 pthread_join(test_threads
[i
], NULL
);
263 perror("pthread_join");
269 for (i
= 0; i
< CPU_SETSIZE
; i
++)
270 sum
+= data
.c
[i
].count
;
272 assert(sum
== opt_reps
* num_threads
);
275 void *test_percpu_inc_thread(void *arg
)
277 struct inc_thread_test_data
*thread_data
= arg
;
278 struct inc_test_data
*data
= thread_data
->data
;
281 if (!opt_disable_rseq
&& thread_data
->reg
282 && rseq_init_current_thread())
284 for (i
= 0; i
< thread_data
->reps
; i
++) {
285 struct rseq_state rseq_state
;
286 intptr_t *targetptr
, newval
;
290 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
292 newval
= (intptr_t)data
->c
[cpu
].count
+ 1;
293 targetptr
= (intptr_t *)&data
->c
[cpu
].count
;
297 if (i
!= 0 && !(i
% (thread_data
->reps
/ 10)))
298 printf("tid %d: count %d\n", (int) gettid(), i
);
301 printf_nobench("tid %d: number of retry: %d, signals delivered: %u, nr_fallback %u, nr_fallback_wait %u\n",
302 (int) gettid(), nr_retry
, signals_delivered
,
303 __rseq_thread_state
.fallback_cnt
,
304 __rseq_thread_state
.fallback_wait_cnt
);
308 void test_percpu_inc(void)
310 const int num_threads
= opt_threads
;
312 pthread_t test_threads
[num_threads
];
313 struct inc_test_data data
;
314 struct inc_thread_test_data thread_data
[num_threads
];
316 memset(&data
, 0, sizeof(data
));
317 for (i
= 0; i
< num_threads
; i
++) {
318 thread_data
[i
].reps
= opt_reps
;
319 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
320 thread_data
[i
].reg
= 1;
322 thread_data
[i
].reg
= 0;
323 thread_data
[i
].data
= &data
;
324 ret
= pthread_create(&test_threads
[i
], NULL
,
325 test_percpu_inc_thread
, &thread_data
[i
]);
328 perror("pthread_create");
333 for (i
= 0; i
< num_threads
; i
++) {
334 pthread_join(test_threads
[i
], NULL
);
337 perror("pthread_join");
343 for (i
= 0; i
< CPU_SETSIZE
; i
++)
344 sum
+= data
.c
[i
].count
;
346 assert(sum
== opt_reps
* num_threads
);
349 int percpu_list_push(struct percpu_list
*list
, struct percpu_list_node
*node
)
351 struct rseq_state rseq_state
;
352 intptr_t *targetptr
, newval
;
356 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
358 newval
= (intptr_t)node
;
359 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
360 node
->next
= list
->c
[cpu
].head
;
367 * Unlike a traditional lock-less linked list; the availability of a
368 * rseq primitive allows us to implement pop without concerns over
371 struct percpu_list_node
*percpu_list_pop(struct percpu_list
*list
)
373 struct percpu_list_node
*head
, *next
;
374 struct rseq_state rseq_state
;
375 intptr_t *targetptr
, newval
;
379 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
381 head
= list
->c
[cpu
].head
;
386 newval
= (intptr_t) next
;
387 targetptr
= (intptr_t *) &list
->c
[cpu
].head
;
394 void *test_percpu_list_thread(void *arg
)
397 struct percpu_list
*list
= (struct percpu_list
*)arg
;
399 if (rseq_init_current_thread())
402 for (i
= 0; i
< opt_reps
; i
++) {
403 struct percpu_list_node
*node
= percpu_list_pop(list
);
406 sched_yield(); /* encourage shuffling */
408 percpu_list_push(list
, node
);
414 /* Simultaneous modification to a per-cpu linked list from many threads. */
415 void test_percpu_list(void)
417 const int num_threads
= opt_threads
;
419 long sum
= 0, expected_sum
= 0;
420 struct percpu_list list
;
421 pthread_t test_threads
[num_threads
];
422 cpu_set_t allowed_cpus
;
424 memset(&list
, 0, sizeof(list
));
426 /* Generate list entries for every usable cpu. */
427 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
428 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
429 if (!CPU_ISSET(i
, &allowed_cpus
))
431 for (j
= 1; j
<= 100; j
++) {
432 struct percpu_list_node
*node
;
436 node
= malloc(sizeof(*node
));
439 node
->next
= list
.c
[i
].head
;
440 list
.c
[i
].head
= node
;
444 for (i
= 0; i
< num_threads
; i
++) {
445 ret
= pthread_create(&test_threads
[i
], NULL
,
446 test_percpu_list_thread
, &list
);
449 perror("pthread_create");
454 for (i
= 0; i
< num_threads
; i
++) {
455 pthread_join(test_threads
[i
], NULL
);
458 perror("pthread_join");
463 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
465 struct percpu_list_node
*node
;
467 if (!CPU_ISSET(i
, &allowed_cpus
))
471 CPU_SET(i
, &pin_mask
);
472 sched_setaffinity(0, sizeof(pin_mask
), &pin_mask
);
474 while ((node
= percpu_list_pop(&list
))) {
481 * All entries should now be accounted for (unless some external
482 * actor is interfering with our allowed affinity while this
485 assert(sum
== expected_sum
);
488 static void test_signal_interrupt_handler(int signo
)
493 static int set_signal_handler(void)
499 ret
= sigemptyset(&sigset
);
501 perror("sigemptyset");
505 sa
.sa_handler
= test_signal_interrupt_handler
;
508 ret
= sigaction(SIGUSR1
, &sa
, NULL
);
514 printf_nobench("Signal handler set for SIGUSR1\n");
519 static void show_usage(int argc
, char **argv
)
521 printf("Usage : %s <OPTIONS>\n",
523 printf("OPTIONS:\n");
524 printf(" [-1 loops] Number of loops for delay injection 1\n");
525 printf(" [-2 loops] Number of loops for delay injection 2\n");
526 printf(" [-3 loops] Number of loops for delay injection 3\n");
527 printf(" [-4 loops] Number of loops for delay injection 4\n");
528 printf(" [-5 loops] Number of loops for delay injection 5 (-1 to enable -m)\n");
529 printf(" [-6 loops] Number of loops for delay injection 6 (-1 to enable -m)\n");
530 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
531 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
532 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
533 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
534 printf(" [-y] Yield\n");
535 printf(" [-k] Kill thread with signal\n");
536 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
537 printf(" [-f N] Use fallback every N failure (>= 1)\n");
538 printf(" [-t N] Number of threads (default 200)\n");
539 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
540 printf(" [-d] Disable rseq system call (no initialization)\n");
541 printf(" [-D M] Disable rseq for each M threads\n");
542 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (i)ncrement\n");
543 printf(" [-h] Show this help.\n");
547 int main(int argc
, char **argv
)
551 if (rseq_init_lock(&rseq_lock
)) {
552 perror("rseq_init_lock");
555 if (set_signal_handler())
557 for (i
= 1; i
< argc
; i
++) {
558 if (argv
[i
][0] != '-')
560 switch (argv
[i
][1]) {
571 show_usage(argc
, argv
);
574 loop_cnt
[argv
[i
][1] - '0'] = atol(argv
[i
+ 1]);
579 show_usage(argc
, argv
);
582 opt_modulo
= atol(argv
[i
+ 1]);
583 if (opt_modulo
< 0) {
584 show_usage(argc
, argv
);
591 show_usage(argc
, argv
);
594 opt_sleep
= atol(argv
[i
+ 1]);
596 show_usage(argc
, argv
);
608 opt_disable_rseq
= 1;
612 show_usage(argc
, argv
);
615 opt_disable_mod
= atol(argv
[i
+ 1]);
616 if (opt_disable_mod
< 0) {
617 show_usage(argc
, argv
);
624 show_usage(argc
, argv
);
627 opt_fallback_cnt
= atol(argv
[i
+ 1]);
628 if (opt_fallback_cnt
< 1) {
629 show_usage(argc
, argv
);
636 show_usage(argc
, argv
);
639 opt_threads
= atol(argv
[i
+ 1]);
640 if (opt_threads
< 0) {
641 show_usage(argc
, argv
);
648 show_usage(argc
, argv
);
651 opt_reps
= atol(argv
[i
+ 1]);
653 show_usage(argc
, argv
);
659 show_usage(argc
, argv
);
663 show_usage(argc
, argv
);
666 opt_test
= *argv
[i
+ 1];
673 show_usage(argc
, argv
);
679 show_usage(argc
, argv
);
684 if (!opt_disable_rseq
&& rseq_init_current_thread())
688 printf_nobench("spinlock\n");
689 test_percpu_spinlock();
692 printf_nobench("linked list\n");
696 printf_nobench("counter increment\n");
704 if (rseq_destroy_lock(&rseq_lock
))
705 perror("rseq_destroy_lock");