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]) \
45 , [loop_cnt_5]"m"(loop_cnt[5])
47 #if defined(__x86_64__) || defined(__i386__)
49 #define INJECT_ASM_REG "eax"
51 #define RSEQ_INJECT_CLOBBER \
54 #define RSEQ_INJECT_ASM(n) \
55 "mov %[loop_cnt_" #n "], %%" INJECT_ASM_REG "\n\t" \
56 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
59 "dec %%" INJECT_ASM_REG "\n\t" \
63 #elif defined(__ARMEL__)
65 #define INJECT_ASM_REG "r4"
67 #define RSEQ_INJECT_CLOBBER \
70 #define RSEQ_INJECT_ASM(n) \
71 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
72 "cmp " INJECT_ASM_REG ", #0\n\t" \
75 "subs " INJECT_ASM_REG ", #1\n\t" \
80 #define INJECT_ASM_REG "r18"
82 #define RSEQ_INJECT_CLOBBER \
85 #define RSEQ_INJECT_ASM(n) \
86 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
87 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
90 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
94 #error unsupported target
97 #define RSEQ_INJECT_FAILED \
100 #define RSEQ_INJECT_C(n) \
102 int loc_i, loc_nr_loops = loop_cnt[n]; \
104 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
107 if (loc_nr_loops == -1 && opt_modulo) { \
108 if (yield_mod_cnt == opt_modulo - 1) { \
110 poll(NULL, 0, opt_sleep); \
122 #define RSEQ_FALLBACK_CNT \
127 #define printf_nobench(fmt, ...)
129 #endif /* BENCHMARK */
133 struct percpu_lock_entry
{
135 } __attribute__((aligned(128)));
138 struct percpu_lock_entry c
[CPU_SETSIZE
];
141 struct test_data_entry
{
143 } __attribute__((aligned(128)));
145 struct spinlock_test_data
{
146 struct percpu_lock lock
;
147 struct test_data_entry c
[CPU_SETSIZE
];
150 struct spinlock_thread_test_data
{
151 struct spinlock_test_data
*data
;
156 struct inc_test_data
{
157 struct test_data_entry c
[CPU_SETSIZE
];
160 struct inc_thread_test_data
{
161 struct inc_test_data
*data
;
166 struct percpu_list_node
{
168 struct percpu_list_node
*next
;
171 struct percpu_list_entry
{
172 struct percpu_list_node
*head
;
173 } __attribute__((aligned(128)));
176 struct percpu_list_entry c
[CPU_SETSIZE
];
179 #define BUFFER_ITEM_PER_CPU 100
181 struct percpu_buffer_node
{
185 struct percpu_buffer_entry
{
188 struct percpu_buffer_node
**array
;
189 } __attribute__((aligned(128)));
191 struct percpu_buffer
{
192 struct percpu_buffer_entry c
[CPU_SETSIZE
];
195 #define MEMCPY_BUFFER_ITEM_PER_CPU 100
197 struct percpu_memcpy_buffer_node
{
202 struct percpu_memcpy_buffer_entry
{
205 struct percpu_memcpy_buffer_node
*array
;
206 } __attribute__((aligned(128)));
208 struct percpu_memcpy_buffer
{
209 struct percpu_memcpy_buffer_entry c
[CPU_SETSIZE
];
212 /* A simple percpu spinlock. Returns the cpu lock was acquired on. */
213 static int rseq_percpu_lock(struct percpu_lock
*lock
)
215 struct rseq_state rseq_state
;
216 intptr_t *targetptr
, newval
;
221 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
223 if (unlikely(lock
->c
[cpu
].v
)) {
227 targetptr
= (intptr_t *)&lock
->c
[cpu
].v
;
234 * Acquire semantic when taking lock after control dependency.
235 * Matches smp_store_release().
237 smp_acquire__after_ctrl_dep();
241 static void rseq_percpu_unlock(struct percpu_lock
*lock
, int cpu
)
243 assert(lock
->c
[cpu
].v
== 1);
245 * Release lock, with release semantic. Matches
246 * smp_acquire__after_ctrl_dep().
248 smp_store_release(&lock
->c
[cpu
].v
, 0);
251 void *test_percpu_spinlock_thread(void *arg
)
253 struct spinlock_thread_test_data
*thread_data
= arg
;
254 struct spinlock_test_data
*data
= thread_data
->data
;
257 if (!opt_disable_rseq
&& thread_data
->reg
258 && rseq_register_current_thread())
260 for (i
= 0; i
< thread_data
->reps
; i
++) {
261 cpu
= rseq_percpu_lock(&data
->lock
);
262 data
->c
[cpu
].count
++;
263 rseq_percpu_unlock(&data
->lock
, cpu
);
265 if (i
!= 0 && !(i
% (thread_data
->reps
/ 10)))
266 printf("tid %d: count %d\n", (int) gettid(), i
);
269 printf_nobench("tid %d: number of retry: %d, signals delivered: %u, nr_fallback %u, nr_fallback_wait %u\n",
270 (int) gettid(), nr_retry
, signals_delivered
,
271 rseq_get_fallback_cnt(),
272 rseq_get_fallback_wait_cnt());
273 if (rseq_unregister_current_thread())
279 * A simple test which implements a sharded counter using a per-cpu
280 * lock. Obviously real applications might prefer to simply use a
281 * per-cpu increment; however, this is reasonable for a test and the
282 * lock can be extended to synchronize more complicated operations.
284 void test_percpu_spinlock(void)
286 const int num_threads
= opt_threads
;
289 pthread_t test_threads
[num_threads
];
290 struct spinlock_test_data data
;
291 struct spinlock_thread_test_data thread_data
[num_threads
];
293 memset(&data
, 0, sizeof(data
));
294 for (i
= 0; i
< num_threads
; i
++) {
295 thread_data
[i
].reps
= opt_reps
;
296 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
297 thread_data
[i
].reg
= 1;
299 thread_data
[i
].reg
= 0;
300 thread_data
[i
].data
= &data
;
301 ret
= pthread_create(&test_threads
[i
], NULL
,
302 test_percpu_spinlock_thread
, &thread_data
[i
]);
305 perror("pthread_create");
310 for (i
= 0; i
< num_threads
; i
++) {
311 pthread_join(test_threads
[i
], NULL
);
314 perror("pthread_join");
320 for (i
= 0; i
< CPU_SETSIZE
; i
++)
321 sum
+= data
.c
[i
].count
;
323 assert(sum
== (uint64_t)opt_reps
* num_threads
);
326 void *test_percpu_inc_thread(void *arg
)
328 struct inc_thread_test_data
*thread_data
= arg
;
329 struct inc_test_data
*data
= thread_data
->data
;
332 if (!opt_disable_rseq
&& thread_data
->reg
333 && rseq_register_current_thread())
335 for (i
= 0; i
< thread_data
->reps
; i
++) {
336 struct rseq_state rseq_state
;
337 intptr_t *targetptr
, newval
;
341 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
343 newval
= (intptr_t)data
->c
[cpu
].count
+ 1;
344 targetptr
= (intptr_t *)&data
->c
[cpu
].count
;
348 if (i
!= 0 && !(i
% (thread_data
->reps
/ 10)))
349 printf("tid %d: count %d\n", (int) gettid(), i
);
352 printf_nobench("tid %d: number of retry: %d, signals delivered: %u, nr_fallback %u, nr_fallback_wait %u\n",
353 (int) gettid(), nr_retry
, signals_delivered
,
354 rseq_get_fallback_cnt(),
355 rseq_get_fallback_wait_cnt());
356 if (rseq_unregister_current_thread())
361 void test_percpu_inc(void)
363 const int num_threads
= opt_threads
;
366 pthread_t test_threads
[num_threads
];
367 struct inc_test_data data
;
368 struct inc_thread_test_data thread_data
[num_threads
];
370 memset(&data
, 0, sizeof(data
));
371 for (i
= 0; i
< num_threads
; i
++) {
372 thread_data
[i
].reps
= opt_reps
;
373 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
374 thread_data
[i
].reg
= 1;
376 thread_data
[i
].reg
= 0;
377 thread_data
[i
].data
= &data
;
378 ret
= pthread_create(&test_threads
[i
], NULL
,
379 test_percpu_inc_thread
, &thread_data
[i
]);
382 perror("pthread_create");
387 for (i
= 0; i
< num_threads
; i
++) {
388 pthread_join(test_threads
[i
], NULL
);
391 perror("pthread_join");
397 for (i
= 0; i
< CPU_SETSIZE
; i
++)
398 sum
+= data
.c
[i
].count
;
400 assert(sum
== (uint64_t)opt_reps
* num_threads
);
403 int percpu_list_push(struct percpu_list
*list
, struct percpu_list_node
*node
)
405 struct rseq_state rseq_state
;
406 intptr_t *targetptr
, newval
;
410 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
412 newval
= (intptr_t)node
;
413 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
414 node
->next
= list
->c
[cpu
].head
;
421 * Unlike a traditional lock-less linked list; the availability of a
422 * rseq primitive allows us to implement pop without concerns over
425 struct percpu_list_node
*percpu_list_pop(struct percpu_list
*list
)
427 struct percpu_list_node
*head
, *next
;
428 struct rseq_state rseq_state
;
429 intptr_t *targetptr
, newval
;
433 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
435 head
= list
->c
[cpu
].head
;
440 newval
= (intptr_t) next
;
441 targetptr
= (intptr_t *) &list
->c
[cpu
].head
;
448 void *test_percpu_list_thread(void *arg
)
451 struct percpu_list
*list
= (struct percpu_list
*)arg
;
453 if (rseq_register_current_thread())
456 for (i
= 0; i
< opt_reps
; i
++) {
457 struct percpu_list_node
*node
= percpu_list_pop(list
);
460 sched_yield(); /* encourage shuffling */
462 percpu_list_push(list
, node
);
465 if (rseq_unregister_current_thread())
471 /* Simultaneous modification to a per-cpu linked list from many threads. */
472 void test_percpu_list(void)
474 const int num_threads
= opt_threads
;
476 uint64_t sum
= 0, expected_sum
= 0;
477 struct percpu_list list
;
478 pthread_t test_threads
[num_threads
];
479 cpu_set_t allowed_cpus
;
481 memset(&list
, 0, sizeof(list
));
483 /* Generate list entries for every usable cpu. */
484 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
485 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
486 if (!CPU_ISSET(i
, &allowed_cpus
))
488 for (j
= 1; j
<= 100; j
++) {
489 struct percpu_list_node
*node
;
493 node
= malloc(sizeof(*node
));
496 node
->next
= list
.c
[i
].head
;
497 list
.c
[i
].head
= node
;
501 for (i
= 0; i
< num_threads
; i
++) {
502 ret
= pthread_create(&test_threads
[i
], NULL
,
503 test_percpu_list_thread
, &list
);
506 perror("pthread_create");
511 for (i
= 0; i
< num_threads
; i
++) {
512 pthread_join(test_threads
[i
], NULL
);
515 perror("pthread_join");
520 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
522 struct percpu_list_node
*node
;
524 if (!CPU_ISSET(i
, &allowed_cpus
))
528 CPU_SET(i
, &pin_mask
);
529 sched_setaffinity(0, sizeof(pin_mask
), &pin_mask
);
531 while ((node
= percpu_list_pop(&list
))) {
538 * All entries should now be accounted for (unless some external
539 * actor is interfering with our allowed affinity while this
542 assert(sum
== expected_sum
);
545 bool percpu_buffer_push(struct percpu_buffer
*buffer
,
546 struct percpu_buffer_node
*node
)
548 struct rseq_state rseq_state
;
549 intptr_t *targetptr_spec
, newval_spec
;
550 intptr_t *targetptr_final
, newval_final
;
554 do_rseq2(&rseq_lock
, rseq_state
, cpu
, result
,
555 targetptr_spec
, newval_spec
, targetptr_final
, newval_final
,
557 intptr_t offset
= buffer
->c
[cpu
].offset
;
559 if (offset
== buffer
->c
[cpu
].buflen
) {
562 newval_spec
= (intptr_t)node
;
563 targetptr_spec
= (intptr_t *)&buffer
->c
[cpu
].array
[offset
];
564 newval_final
= offset
+ 1;
565 targetptr_final
= &buffer
->c
[cpu
].offset
;
572 struct percpu_buffer_node
*percpu_buffer_pop(struct percpu_buffer
*buffer
)
574 struct percpu_buffer_node
*head
;
575 struct rseq_state rseq_state
;
576 intptr_t *targetptr
, newval
;
580 do_rseq(&rseq_lock
, rseq_state
, cpu
, result
, targetptr
, newval
,
582 intptr_t offset
= buffer
->c
[cpu
].offset
;
587 head
= buffer
->c
[cpu
].array
[offset
- 1];
589 targetptr
= (intptr_t *)&buffer
->c
[cpu
].offset
;
599 void *test_percpu_buffer_thread(void *arg
)
602 struct percpu_buffer
*buffer
= (struct percpu_buffer
*)arg
;
604 if (rseq_register_current_thread())
607 for (i
= 0; i
< opt_reps
; i
++) {
608 struct percpu_buffer_node
*node
= percpu_buffer_pop(buffer
);
611 sched_yield(); /* encourage shuffling */
613 if (!percpu_buffer_push(buffer
, node
)) {
614 /* Should increase buffer size. */
620 if (rseq_unregister_current_thread())
626 /* Simultaneous modification to a per-cpu buffer from many threads. */
627 void test_percpu_buffer(void)
629 const int num_threads
= opt_threads
;
631 uint64_t sum
= 0, expected_sum
= 0;
632 struct percpu_buffer buffer
;
633 pthread_t test_threads
[num_threads
];
634 cpu_set_t allowed_cpus
;
636 memset(&buffer
, 0, sizeof(buffer
));
638 /* Generate list entries for every usable cpu. */
639 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
640 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
641 if (!CPU_ISSET(i
, &allowed_cpus
))
643 /* Worse-case is every item in same CPU. */
645 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
646 * BUFFER_ITEM_PER_CPU
);
647 assert(buffer
.c
[i
].array
);
648 buffer
.c
[i
].buflen
= CPU_SETSIZE
* BUFFER_ITEM_PER_CPU
;
649 for (j
= 1; j
<= BUFFER_ITEM_PER_CPU
; j
++) {
650 struct percpu_buffer_node
*node
;
655 * We could theoretically put the word-sized
656 * "data" directly in the buffer. However, we
657 * want to model objects that would not fit
658 * within a single word, so allocate an object
661 node
= malloc(sizeof(*node
));
664 buffer
.c
[i
].array
[j
- 1] = node
;
665 buffer
.c
[i
].offset
++;
669 for (i
= 0; i
< num_threads
; i
++) {
670 ret
= pthread_create(&test_threads
[i
], NULL
,
671 test_percpu_buffer_thread
, &buffer
);
674 perror("pthread_create");
679 for (i
= 0; i
< num_threads
; i
++) {
680 pthread_join(test_threads
[i
], NULL
);
683 perror("pthread_join");
688 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
690 struct percpu_buffer_node
*node
;
692 if (!CPU_ISSET(i
, &allowed_cpus
))
696 CPU_SET(i
, &pin_mask
);
697 sched_setaffinity(0, sizeof(pin_mask
), &pin_mask
);
699 while ((node
= percpu_buffer_pop(&buffer
))) {
703 free(buffer
.c
[i
].array
);
707 * All entries should now be accounted for (unless some external
708 * actor is interfering with our allowed affinity while this
711 assert(sum
== expected_sum
);
714 bool percpu_memcpy_buffer_push(struct percpu_memcpy_buffer
*buffer
,
715 struct percpu_memcpy_buffer_node item
)
717 struct rseq_state rseq_state
;
718 char *destptr
, *srcptr
;
720 intptr_t *targetptr_final
, newval_final
;
724 do_rseq_memcpy(&rseq_lock
, rseq_state
, cpu
, result
,
725 destptr
, srcptr
, copylen
, targetptr_final
, newval_final
,
727 intptr_t offset
= buffer
->c
[cpu
].offset
;
729 if (offset
== buffer
->c
[cpu
].buflen
) {
732 destptr
= (char *)&buffer
->c
[cpu
].array
[offset
];
733 srcptr
= (char *)&item
;
734 copylen
= sizeof(item
);
735 newval_final
= offset
+ 1;
736 targetptr_final
= &buffer
->c
[cpu
].offset
;
743 bool percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
744 struct percpu_memcpy_buffer_node
*item
)
746 struct rseq_state rseq_state
;
747 char *destptr
, *srcptr
;
749 intptr_t *targetptr_final
, newval_final
;
753 do_rseq_memcpy(&rseq_lock
, rseq_state
, cpu
, result
,
754 destptr
, srcptr
, copylen
, targetptr_final
, newval_final
,
756 intptr_t offset
= buffer
->c
[cpu
].offset
;
761 destptr
= (char *)item
;
762 srcptr
= (char *)&buffer
->c
[cpu
].array
[offset
- 1];
763 copylen
= sizeof(*item
);
764 newval_final
= offset
- 1;
765 targetptr_final
= &buffer
->c
[cpu
].offset
;
772 void *test_percpu_memcpy_buffer_thread(void *arg
)
775 struct percpu_memcpy_buffer
*buffer
= (struct percpu_memcpy_buffer
*)arg
;
777 if (rseq_register_current_thread())
780 for (i
= 0; i
< opt_reps
; i
++) {
781 struct percpu_memcpy_buffer_node item
;
784 result
= percpu_memcpy_buffer_pop(buffer
, &item
);
786 sched_yield(); /* encourage shuffling */
788 if (!percpu_memcpy_buffer_push(buffer
, item
)) {
789 /* Should increase buffer size. */
795 if (rseq_unregister_current_thread())
801 /* Simultaneous modification to a per-cpu buffer from many threads. */
802 void test_percpu_memcpy_buffer(void)
804 const int num_threads
= opt_threads
;
806 uint64_t sum
= 0, expected_sum
= 0;
807 struct percpu_memcpy_buffer buffer
;
808 pthread_t test_threads
[num_threads
];
809 cpu_set_t allowed_cpus
;
811 memset(&buffer
, 0, sizeof(buffer
));
813 /* Generate list entries for every usable cpu. */
814 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
815 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
816 if (!CPU_ISSET(i
, &allowed_cpus
))
818 /* Worse-case is every item in same CPU. */
820 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
821 * MEMCPY_BUFFER_ITEM_PER_CPU
);
822 assert(buffer
.c
[i
].array
);
823 buffer
.c
[i
].buflen
= CPU_SETSIZE
* MEMCPY_BUFFER_ITEM_PER_CPU
;
824 for (j
= 1; j
<= MEMCPY_BUFFER_ITEM_PER_CPU
; j
++) {
825 expected_sum
+= 2 * j
+ 1;
828 * We could theoretically put the word-sized
829 * "data" directly in the buffer. However, we
830 * want to model objects that would not fit
831 * within a single word, so allocate an object
834 buffer
.c
[i
].array
[j
- 1].data1
= j
;
835 buffer
.c
[i
].array
[j
- 1].data2
= j
+ 1;
836 buffer
.c
[i
].offset
++;
840 for (i
= 0; i
< num_threads
; i
++) {
841 ret
= pthread_create(&test_threads
[i
], NULL
,
842 test_percpu_memcpy_buffer_thread
, &buffer
);
845 perror("pthread_create");
850 for (i
= 0; i
< num_threads
; i
++) {
851 pthread_join(test_threads
[i
], NULL
);
854 perror("pthread_join");
859 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
861 struct percpu_memcpy_buffer_node item
;
863 if (!CPU_ISSET(i
, &allowed_cpus
))
867 CPU_SET(i
, &pin_mask
);
868 sched_setaffinity(0, sizeof(pin_mask
), &pin_mask
);
870 while (percpu_memcpy_buffer_pop(&buffer
, &item
)) {
874 free(buffer
.c
[i
].array
);
878 * All entries should now be accounted for (unless some external
879 * actor is interfering with our allowed affinity while this
882 assert(sum
== expected_sum
);
885 static void test_signal_interrupt_handler(int signo
)
890 static int set_signal_handler(void)
896 ret
= sigemptyset(&sigset
);
898 perror("sigemptyset");
902 sa
.sa_handler
= test_signal_interrupt_handler
;
905 ret
= sigaction(SIGUSR1
, &sa
, NULL
);
911 printf_nobench("Signal handler set for SIGUSR1\n");
916 static void show_usage(int argc
, char **argv
)
918 printf("Usage : %s <OPTIONS>\n",
920 printf("OPTIONS:\n");
921 printf(" [-1 loops] Number of loops for delay injection 1\n");
922 printf(" [-2 loops] Number of loops for delay injection 2\n");
923 printf(" [-3 loops] Number of loops for delay injection 3\n");
924 printf(" [-4 loops] Number of loops for delay injection 4\n");
925 printf(" [-5 loops] Number of loops for delay injection 5\n");
926 printf(" [-6 loops] Number of loops for delay injection 6 (-1 to enable -m)\n");
927 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
928 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
929 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
930 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
931 printf(" [-y] Yield\n");
932 printf(" [-k] Kill thread with signal\n");
933 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
934 printf(" [-f N] Use fallback every N failure (>= 1)\n");
935 printf(" [-t N] Number of threads (default 200)\n");
936 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
937 printf(" [-d] Disable rseq system call (no initialization)\n");
938 printf(" [-D M] Disable rseq for each M threads\n");
939 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement\n");
940 printf(" [-h] Show this help.\n");
944 int main(int argc
, char **argv
)
948 if (rseq_init_lock(&rseq_lock
)) {
949 perror("rseq_init_lock");
952 if (set_signal_handler())
954 for (i
= 1; i
< argc
; i
++) {
955 if (argv
[i
][0] != '-')
957 switch (argv
[i
][1]) {
968 show_usage(argc
, argv
);
971 loop_cnt
[argv
[i
][1] - '0'] = atol(argv
[i
+ 1]);
976 show_usage(argc
, argv
);
979 opt_modulo
= atol(argv
[i
+ 1]);
980 if (opt_modulo
< 0) {
981 show_usage(argc
, argv
);
988 show_usage(argc
, argv
);
991 opt_sleep
= atol(argv
[i
+ 1]);
993 show_usage(argc
, argv
);
1005 opt_disable_rseq
= 1;
1009 show_usage(argc
, argv
);
1012 opt_disable_mod
= atol(argv
[i
+ 1]);
1013 if (opt_disable_mod
< 0) {
1014 show_usage(argc
, argv
);
1021 show_usage(argc
, argv
);
1024 opt_fallback_cnt
= atol(argv
[i
+ 1]);
1025 if (opt_fallback_cnt
< 1) {
1026 show_usage(argc
, argv
);
1033 show_usage(argc
, argv
);
1036 opt_threads
= atol(argv
[i
+ 1]);
1037 if (opt_threads
< 0) {
1038 show_usage(argc
, argv
);
1045 show_usage(argc
, argv
);
1048 opt_reps
= atol(argv
[i
+ 1]);
1050 show_usage(argc
, argv
);
1056 show_usage(argc
, argv
);
1060 show_usage(argc
, argv
);
1063 opt_test
= *argv
[i
+ 1];
1072 show_usage(argc
, argv
);
1078 show_usage(argc
, argv
);
1083 if (!opt_disable_rseq
&& rseq_register_current_thread())
1087 printf_nobench("spinlock\n");
1088 test_percpu_spinlock();
1091 printf_nobench("linked list\n");
1095 printf_nobench("buffer\n");
1096 test_percpu_buffer();
1099 printf_nobench("memcpy buffer\n");
1100 test_percpu_memcpy_buffer();
1103 printf_nobench("counter increment\n");
1107 if (rseq_unregister_current_thread())
1113 if (rseq_destroy_lock(&rseq_lock
))
1114 perror("rseq_destroy_lock");