1 // SPDX-License-Identifier: LGPL-2.1-only
6 #include <linux/version.h>
7 #include <linux/membarrier.h>
17 #include <sys/types.h>
22 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
24 MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
= (1 << 7),
25 MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
= (1 << 8),
29 MEMBARRIER_CMD_FLAG_CPU
= (1 << 0),
34 static int loop_cnt
[NR_INJECT
+ 1];
36 static int loop_cnt_1
asm("asm_loop_cnt_1") __attribute__((used
));
37 static int loop_cnt_2
asm("asm_loop_cnt_2") __attribute__((used
));
38 static int loop_cnt_3
asm("asm_loop_cnt_3") __attribute__((used
));
39 static int loop_cnt_4
asm("asm_loop_cnt_4") __attribute__((used
));
40 static int loop_cnt_5
asm("asm_loop_cnt_5") __attribute__((used
));
41 static int loop_cnt_6
asm("asm_loop_cnt_6") __attribute__((used
));
43 static int opt_modulo
, verbose
;
45 static int opt_yield
, opt_signal
, opt_sleep
,
46 opt_disable_rseq
, opt_threads
= 200,
47 opt_disable_mod
= 0, opt_test
= 's', opt_mb
= 0;
49 #ifndef RSEQ_SKIP_FASTPATH
50 static long long opt_reps
= 5000;
52 static long long opt_reps
= 100;
55 static __thread
__attribute__((tls_model("initial-exec")))
56 unsigned int signals_delivered
;
60 static inline pid_t
rseq_gettid(void)
62 return syscall(__NR_gettid
);
65 static __thread
__attribute__((tls_model("initial-exec"), unused
))
66 int yield_mod_cnt
, nr_abort
;
68 #define printf_verbose(fmt, ...) \
71 printf(fmt, ## __VA_ARGS__); \
76 #define INJECT_ASM_REG "eax"
78 #define RSEQ_INJECT_CLOBBER \
81 #define RSEQ_INJECT_ASM(n) \
82 "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
83 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
86 "dec %%" INJECT_ASM_REG "\n\t" \
90 #elif defined(__x86_64__)
92 #define INJECT_ASM_REG_P "rax"
93 #define INJECT_ASM_REG "eax"
95 #define RSEQ_INJECT_CLOBBER \
99 #define RSEQ_INJECT_ASM(n) \
100 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
101 "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
102 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
105 "dec %%" INJECT_ASM_REG "\n\t" \
109 #elif defined(__s390__)
111 #define RSEQ_INJECT_INPUT \
112 , [loop_cnt_1]"m"(loop_cnt[1]) \
113 , [loop_cnt_2]"m"(loop_cnt[2]) \
114 , [loop_cnt_3]"m"(loop_cnt[3]) \
115 , [loop_cnt_4]"m"(loop_cnt[4]) \
116 , [loop_cnt_5]"m"(loop_cnt[5]) \
117 , [loop_cnt_6]"m"(loop_cnt[6])
119 #define INJECT_ASM_REG "r12"
121 #define RSEQ_INJECT_CLOBBER \
124 #define RSEQ_INJECT_ASM(n) \
125 "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
126 "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
129 "ahi %%" INJECT_ASM_REG ", -1\n\t" \
133 #elif defined(__ARMEL__)
135 #define RSEQ_INJECT_INPUT \
136 , [loop_cnt_1]"m"(loop_cnt[1]) \
137 , [loop_cnt_2]"m"(loop_cnt[2]) \
138 , [loop_cnt_3]"m"(loop_cnt[3]) \
139 , [loop_cnt_4]"m"(loop_cnt[4]) \
140 , [loop_cnt_5]"m"(loop_cnt[5]) \
141 , [loop_cnt_6]"m"(loop_cnt[6])
143 #define INJECT_ASM_REG "r4"
145 #define RSEQ_INJECT_CLOBBER \
148 #define RSEQ_INJECT_ASM(n) \
149 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
150 "cmp " INJECT_ASM_REG ", #0\n\t" \
153 "subs " INJECT_ASM_REG ", #1\n\t" \
157 #elif defined(__AARCH64EL__)
159 #define RSEQ_INJECT_INPUT \
160 , [loop_cnt_1] "Qo" (loop_cnt[1]) \
161 , [loop_cnt_2] "Qo" (loop_cnt[2]) \
162 , [loop_cnt_3] "Qo" (loop_cnt[3]) \
163 , [loop_cnt_4] "Qo" (loop_cnt[4]) \
164 , [loop_cnt_5] "Qo" (loop_cnt[5]) \
165 , [loop_cnt_6] "Qo" (loop_cnt[6])
167 #define INJECT_ASM_REG RSEQ_ASM_TMP_REG32
169 #define RSEQ_INJECT_ASM(n) \
170 " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \
171 " cbz " INJECT_ASM_REG ", 333f\n" \
173 " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \
174 " cbnz " INJECT_ASM_REG ", 222b\n" \
177 #elif defined(__PPC__)
179 #define RSEQ_INJECT_INPUT \
180 , [loop_cnt_1]"m"(loop_cnt[1]) \
181 , [loop_cnt_2]"m"(loop_cnt[2]) \
182 , [loop_cnt_3]"m"(loop_cnt[3]) \
183 , [loop_cnt_4]"m"(loop_cnt[4]) \
184 , [loop_cnt_5]"m"(loop_cnt[5]) \
185 , [loop_cnt_6]"m"(loop_cnt[6])
187 #define INJECT_ASM_REG "r18"
189 #define RSEQ_INJECT_CLOBBER \
192 #define RSEQ_INJECT_ASM(n) \
193 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
194 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
197 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
201 #elif defined(__mips__)
203 #define RSEQ_INJECT_INPUT \
204 , [loop_cnt_1]"m"(loop_cnt[1]) \
205 , [loop_cnt_2]"m"(loop_cnt[2]) \
206 , [loop_cnt_3]"m"(loop_cnt[3]) \
207 , [loop_cnt_4]"m"(loop_cnt[4]) \
208 , [loop_cnt_5]"m"(loop_cnt[5]) \
209 , [loop_cnt_6]"m"(loop_cnt[6])
211 #define INJECT_ASM_REG "$5"
213 #define RSEQ_INJECT_CLOBBER \
216 #define RSEQ_INJECT_ASM(n) \
217 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
218 "beqz " INJECT_ASM_REG ", 333f\n\t" \
220 "addiu " INJECT_ASM_REG ", -1\n\t" \
221 "bnez " INJECT_ASM_REG ", 222b\n\t" \
224 #elif defined(__riscv)
226 #define RSEQ_INJECT_INPUT \
227 , [loop_cnt_1]"m"(loop_cnt[1]) \
228 , [loop_cnt_2]"m"(loop_cnt[2]) \
229 , [loop_cnt_3]"m"(loop_cnt[3]) \
230 , [loop_cnt_4]"m"(loop_cnt[4]) \
231 , [loop_cnt_5]"m"(loop_cnt[5]) \
232 , [loop_cnt_6]"m"(loop_cnt[6])
234 #define INJECT_ASM_REG "t1"
236 #define RSEQ_INJECT_CLOBBER \
239 #define RSEQ_INJECT_ASM(n) \
240 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
241 "beqz " INJECT_ASM_REG ", 333f\n\t" \
243 "addi " INJECT_ASM_REG "," INJECT_ASM_REG ", -1\n\t" \
244 "bnez " INJECT_ASM_REG ", 222b\n\t" \
248 #error unsupported target
251 #define RSEQ_INJECT_FAILED \
254 #define RSEQ_INJECT_C(n) \
256 int loc_i, loc_nr_loops = loop_cnt[n]; \
258 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
261 if (loc_nr_loops == -1 && opt_modulo) { \
262 if (yield_mod_cnt == opt_modulo - 1) { \
264 poll(NULL, 0, opt_sleep); \
278 #define printf_verbose(fmt, ...)
280 #endif /* BENCHMARK */
282 #include <rseq/rseq.h>
284 struct percpu_lock_entry
{
286 } __attribute__((aligned(128)));
289 struct percpu_lock_entry c
[CPU_SETSIZE
];
292 struct test_data_entry
{
294 } __attribute__((aligned(128)));
296 struct spinlock_test_data
{
297 struct percpu_lock lock
;
298 struct test_data_entry c
[CPU_SETSIZE
];
301 struct spinlock_thread_test_data
{
302 struct spinlock_test_data
*data
;
307 struct inc_test_data
{
308 struct test_data_entry c
[CPU_SETSIZE
];
311 struct inc_thread_test_data
{
312 struct inc_test_data
*data
;
317 struct percpu_list_node
{
319 struct percpu_list_node
*next
;
322 struct percpu_list_entry
{
323 struct percpu_list_node
*head
;
324 } __attribute__((aligned(128)));
327 struct percpu_list_entry c
[CPU_SETSIZE
];
330 #define BUFFER_ITEM_PER_CPU 100
332 struct percpu_buffer_node
{
336 struct percpu_buffer_entry
{
339 struct percpu_buffer_node
**array
;
340 } __attribute__((aligned(128)));
342 struct percpu_buffer
{
343 struct percpu_buffer_entry c
[CPU_SETSIZE
];
346 #define MEMCPY_BUFFER_ITEM_PER_CPU 100
348 struct percpu_memcpy_buffer_node
{
353 struct percpu_memcpy_buffer_entry
{
356 struct percpu_memcpy_buffer_node
*array
;
357 } __attribute__((aligned(128)));
359 struct percpu_memcpy_buffer
{
360 struct percpu_memcpy_buffer_entry c
[CPU_SETSIZE
];
363 /* A simple percpu spinlock. Grabs lock on current cpu. */
364 static int rseq_this_cpu_lock(struct percpu_lock
*lock
)
371 cpu
= rseq_cpu_start();
372 ret
= rseq_cmpeqv_storev(&lock
->c
[cpu
].v
,
374 if (rseq_likely(!ret
))
376 /* Retry if comparison fails or rseq aborts. */
379 * Acquire semantic when taking lock after control dependency.
380 * Matches rseq_smp_store_release().
382 rseq_smp_acquire__after_ctrl_dep();
386 static void rseq_percpu_unlock(struct percpu_lock
*lock
, int cpu
)
388 assert(lock
->c
[cpu
].v
== 1);
390 * Release lock, with release semantic. Matches
391 * rseq_smp_acquire__after_ctrl_dep().
393 rseq_smp_store_release(&lock
->c
[cpu
].v
, 0);
396 static void *test_percpu_spinlock_thread(void *arg
)
398 struct spinlock_thread_test_data
*thread_data
= (struct spinlock_thread_test_data
*) arg
;
399 struct spinlock_test_data
*data
= thread_data
->data
;
402 if (!opt_disable_rseq
&& thread_data
->reg
&&
403 rseq_register_current_thread())
405 reps
= thread_data
->reps
;
406 for (i
= 0; i
< reps
; i
++) {
407 int cpu
= rseq_this_cpu_lock(&data
->lock
);
408 data
->c
[cpu
].count
++;
409 rseq_percpu_unlock(&data
->lock
, cpu
);
411 if (i
!= 0 && !(i
% (reps
/ 10)))
412 printf_verbose("tid %d: count %lld\n",
413 (int) rseq_gettid(), i
);
416 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
417 (int) rseq_gettid(), nr_abort
, signals_delivered
);
418 if (!opt_disable_rseq
&& thread_data
->reg
&&
419 rseq_unregister_current_thread())
425 * A simple test which implements a sharded counter using a per-cpu
426 * lock. Obviously real applications might prefer to simply use a
427 * per-cpu increment; however, this is reasonable for a test and the
428 * lock can be extended to synchronize more complicated operations.
430 static void test_percpu_spinlock(void)
432 const int num_threads
= opt_threads
;
435 pthread_t test_threads
[num_threads
];
436 struct spinlock_test_data data
;
437 struct spinlock_thread_test_data thread_data
[num_threads
];
439 memset(&data
, 0, sizeof(data
));
440 for (i
= 0; i
< num_threads
; i
++) {
441 thread_data
[i
].reps
= opt_reps
;
442 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
443 thread_data
[i
].reg
= 1;
445 thread_data
[i
].reg
= 0;
446 thread_data
[i
].data
= &data
;
447 ret
= pthread_create(&test_threads
[i
], NULL
,
448 test_percpu_spinlock_thread
,
452 perror("pthread_create");
457 for (i
= 0; i
< num_threads
; i
++) {
458 ret
= pthread_join(test_threads
[i
], NULL
);
461 perror("pthread_join");
467 for (i
= 0; i
< CPU_SETSIZE
; i
++)
468 sum
+= data
.c
[i
].count
;
470 assert(sum
== (uint64_t)opt_reps
* num_threads
);
473 static void *test_percpu_inc_thread(void *arg
)
475 struct inc_thread_test_data
*thread_data
= (struct inc_thread_test_data
*) arg
;
476 struct inc_test_data
*data
= thread_data
->data
;
479 if (!opt_disable_rseq
&& thread_data
->reg
&&
480 rseq_register_current_thread())
482 reps
= thread_data
->reps
;
483 for (i
= 0; i
< reps
; i
++) {
489 cpu
= rseq_cpu_start();
490 ret
= rseq_addv(&data
->c
[cpu
].count
, 1, cpu
);
491 } while (rseq_unlikely(ret
));
493 if (i
!= 0 && !(i
% (reps
/ 10)))
494 printf_verbose("tid %d: count %lld\n",
495 (int) rseq_gettid(), i
);
498 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
499 (int) rseq_gettid(), nr_abort
, signals_delivered
);
500 if (!opt_disable_rseq
&& thread_data
->reg
&&
501 rseq_unregister_current_thread())
506 static void test_percpu_inc(void)
508 const int num_threads
= opt_threads
;
511 pthread_t test_threads
[num_threads
];
512 struct inc_test_data data
;
513 struct inc_thread_test_data thread_data
[num_threads
];
515 memset(&data
, 0, sizeof(data
));
516 for (i
= 0; i
< num_threads
; i
++) {
517 thread_data
[i
].reps
= opt_reps
;
518 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
519 thread_data
[i
].reg
= 1;
521 thread_data
[i
].reg
= 0;
522 thread_data
[i
].data
= &data
;
523 ret
= pthread_create(&test_threads
[i
], NULL
,
524 test_percpu_inc_thread
,
528 perror("pthread_create");
533 for (i
= 0; i
< num_threads
; i
++) {
534 ret
= pthread_join(test_threads
[i
], NULL
);
537 perror("pthread_join");
543 for (i
= 0; i
< CPU_SETSIZE
; i
++)
544 sum
+= data
.c
[i
].count
;
546 assert(sum
== (uint64_t)opt_reps
* num_threads
);
549 static void this_cpu_list_push(struct percpu_list
*list
,
550 struct percpu_list_node
*node
,
556 intptr_t *targetptr
, newval
, expect
;
559 cpu
= rseq_cpu_start();
560 /* Load list->c[cpu].head with single-copy atomicity. */
561 expect
= (intptr_t)RSEQ_READ_ONCE(list
->c
[cpu
].head
);
562 newval
= (intptr_t)node
;
563 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
564 node
->next
= (struct percpu_list_node
*)expect
;
565 ret
= rseq_cmpeqv_storev(targetptr
, expect
, newval
, cpu
);
566 if (rseq_likely(!ret
))
568 /* Retry if comparison fails or rseq aborts. */
575 * Unlike a traditional lock-less linked list; the availability of a
576 * rseq primitive allows us to implement pop without concerns over
579 static struct percpu_list_node
*this_cpu_list_pop(struct percpu_list
*list
,
582 struct percpu_list_node
*node
= NULL
;
586 struct percpu_list_node
*head
;
587 intptr_t *targetptr
, expectnot
, *load
;
591 cpu
= rseq_cpu_start();
592 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
593 expectnot
= (intptr_t)NULL
;
594 offset
= offsetof(struct percpu_list_node
, next
);
595 load
= (intptr_t *)&head
;
596 ret
= rseq_cmpnev_storeoffp_load(targetptr
, expectnot
,
598 if (rseq_likely(!ret
)) {
604 /* Retry if rseq aborts. */
612 * __percpu_list_pop is not safe against concurrent accesses. Should
613 * only be used on lists that are not concurrently modified.
615 static struct percpu_list_node
*__percpu_list_pop(struct percpu_list
*list
, int cpu
)
617 struct percpu_list_node
*node
;
619 node
= list
->c
[cpu
].head
;
622 list
->c
[cpu
].head
= node
->next
;
626 static void *test_percpu_list_thread(void *arg
)
629 struct percpu_list
*list
= (struct percpu_list
*)arg
;
631 if (!opt_disable_rseq
&& rseq_register_current_thread())
635 for (i
= 0; i
< reps
; i
++) {
636 struct percpu_list_node
*node
;
638 node
= this_cpu_list_pop(list
, NULL
);
640 sched_yield(); /* encourage shuffling */
642 this_cpu_list_push(list
, node
, NULL
);
645 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
646 (int) rseq_gettid(), nr_abort
, signals_delivered
);
647 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
653 /* Simultaneous modification to a per-cpu linked list from many threads. */
654 static void test_percpu_list(void)
656 const int num_threads
= opt_threads
;
658 uint64_t sum
= 0, expected_sum
= 0;
659 struct percpu_list list
;
660 pthread_t test_threads
[num_threads
];
661 cpu_set_t allowed_cpus
;
663 memset(&list
, 0, sizeof(list
));
665 /* Generate list entries for every usable cpu. */
666 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
667 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
668 if (!CPU_ISSET(i
, &allowed_cpus
))
670 for (j
= 1; j
<= 100; j
++) {
671 struct percpu_list_node
*node
;
675 node
= (struct percpu_list_node
*) malloc(sizeof(*node
));
678 node
->next
= list
.c
[i
].head
;
679 list
.c
[i
].head
= node
;
683 for (i
= 0; i
< num_threads
; i
++) {
684 ret
= pthread_create(&test_threads
[i
], NULL
,
685 test_percpu_list_thread
, &list
);
688 perror("pthread_create");
693 for (i
= 0; i
< num_threads
; i
++) {
694 ret
= pthread_join(test_threads
[i
], NULL
);
697 perror("pthread_join");
702 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
703 struct percpu_list_node
*node
;
705 if (!CPU_ISSET(i
, &allowed_cpus
))
708 while ((node
= __percpu_list_pop(&list
, i
))) {
715 * All entries should now be accounted for (unless some external
716 * actor is interfering with our allowed affinity while this
719 assert(sum
== expected_sum
);
722 static bool this_cpu_buffer_push(struct percpu_buffer
*buffer
,
723 struct percpu_buffer_node
*node
,
730 intptr_t *targetptr_spec
, newval_spec
;
731 intptr_t *targetptr_final
, newval_final
;
735 cpu
= rseq_cpu_start();
736 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
737 if (offset
== buffer
->c
[cpu
].buflen
)
739 newval_spec
= (intptr_t)node
;
740 targetptr_spec
= (intptr_t *)&buffer
->c
[cpu
].array
[offset
];
741 newval_final
= offset
+ 1;
742 targetptr_final
= &buffer
->c
[cpu
].offset
;
744 ret
= rseq_cmpeqv_trystorev_storev_release(
745 targetptr_final
, offset
, targetptr_spec
,
746 newval_spec
, newval_final
, cpu
);
748 ret
= rseq_cmpeqv_trystorev_storev(targetptr_final
,
749 offset
, targetptr_spec
, newval_spec
,
751 if (rseq_likely(!ret
)) {
755 /* Retry if comparison fails or rseq aborts. */
762 static struct percpu_buffer_node
*this_cpu_buffer_pop(struct percpu_buffer
*buffer
,
765 struct percpu_buffer_node
*head
;
769 intptr_t *targetptr
, newval
;
773 cpu
= rseq_cpu_start();
774 /* Load offset with single-copy atomicity. */
775 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
780 head
= RSEQ_READ_ONCE(buffer
->c
[cpu
].array
[offset
- 1]);
782 targetptr
= (intptr_t *)&buffer
->c
[cpu
].offset
;
783 ret
= rseq_cmpeqv_cmpeqv_storev(targetptr
, offset
,
784 (intptr_t *)&buffer
->c
[cpu
].array
[offset
- 1],
785 (intptr_t)head
, newval
, cpu
);
786 if (rseq_likely(!ret
))
788 /* Retry if comparison fails or rseq aborts. */
796 * __percpu_buffer_pop is not safe against concurrent accesses. Should
797 * only be used on buffers that are not concurrently modified.
799 static struct percpu_buffer_node
*__percpu_buffer_pop(struct percpu_buffer
*buffer
,
802 struct percpu_buffer_node
*head
;
805 offset
= buffer
->c
[cpu
].offset
;
808 head
= buffer
->c
[cpu
].array
[offset
- 1];
809 buffer
->c
[cpu
].offset
= offset
- 1;
813 static void *test_percpu_buffer_thread(void *arg
)
816 struct percpu_buffer
*buffer
= (struct percpu_buffer
*)arg
;
818 if (!opt_disable_rseq
&& rseq_register_current_thread())
822 for (i
= 0; i
< reps
; i
++) {
823 struct percpu_buffer_node
*node
;
825 node
= this_cpu_buffer_pop(buffer
, NULL
);
827 sched_yield(); /* encourage shuffling */
829 if (!this_cpu_buffer_push(buffer
, node
, NULL
)) {
830 /* Should increase buffer size. */
836 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
837 (int) rseq_gettid(), nr_abort
, signals_delivered
);
838 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
844 /* Simultaneous modification to a per-cpu buffer from many threads. */
845 static void test_percpu_buffer(void)
847 const int num_threads
= opt_threads
;
849 uint64_t sum
= 0, expected_sum
= 0;
850 struct percpu_buffer buffer
;
851 pthread_t test_threads
[num_threads
];
852 cpu_set_t allowed_cpus
;
854 memset(&buffer
, 0, sizeof(buffer
));
856 /* Generate list entries for every usable cpu. */
857 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
858 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
859 if (!CPU_ISSET(i
, &allowed_cpus
))
861 /* Worse-case is every item in same CPU. */
863 (struct percpu_buffer_node
**)
864 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
865 BUFFER_ITEM_PER_CPU
);
866 assert(buffer
.c
[i
].array
);
867 buffer
.c
[i
].buflen
= CPU_SETSIZE
* BUFFER_ITEM_PER_CPU
;
868 for (j
= 1; j
<= BUFFER_ITEM_PER_CPU
; j
++) {
869 struct percpu_buffer_node
*node
;
874 * We could theoretically put the word-sized
875 * "data" directly in the buffer. However, we
876 * want to model objects that would not fit
877 * within a single word, so allocate an object
880 node
= (struct percpu_buffer_node
*) malloc(sizeof(*node
));
883 buffer
.c
[i
].array
[j
- 1] = node
;
884 buffer
.c
[i
].offset
++;
888 for (i
= 0; i
< num_threads
; i
++) {
889 ret
= pthread_create(&test_threads
[i
], NULL
,
890 test_percpu_buffer_thread
, &buffer
);
893 perror("pthread_create");
898 for (i
= 0; i
< num_threads
; i
++) {
899 ret
= pthread_join(test_threads
[i
], NULL
);
902 perror("pthread_join");
907 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
908 struct percpu_buffer_node
*node
;
910 if (!CPU_ISSET(i
, &allowed_cpus
))
913 while ((node
= __percpu_buffer_pop(&buffer
, i
))) {
917 free(buffer
.c
[i
].array
);
921 * All entries should now be accounted for (unless some external
922 * actor is interfering with our allowed affinity while this
925 assert(sum
== expected_sum
);
928 static bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer
*buffer
,
929 struct percpu_memcpy_buffer_node item
,
936 intptr_t *targetptr_final
, newval_final
, offset
;
937 char *destptr
, *srcptr
;
941 cpu
= rseq_cpu_start();
942 /* Load offset with single-copy atomicity. */
943 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
944 if (offset
== buffer
->c
[cpu
].buflen
)
946 destptr
= (char *)&buffer
->c
[cpu
].array
[offset
];
947 srcptr
= (char *)&item
;
948 /* copylen must be <= 4kB. */
949 copylen
= sizeof(item
);
950 newval_final
= offset
+ 1;
951 targetptr_final
= &buffer
->c
[cpu
].offset
;
953 ret
= rseq_cmpeqv_trymemcpy_storev_release(
954 targetptr_final
, offset
,
955 destptr
, srcptr
, copylen
,
958 ret
= rseq_cmpeqv_trymemcpy_storev(targetptr_final
,
959 offset
, destptr
, srcptr
, copylen
,
961 if (rseq_likely(!ret
)) {
965 /* Retry if comparison fails or rseq aborts. */
972 static bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
973 struct percpu_memcpy_buffer_node
*item
,
980 intptr_t *targetptr_final
, newval_final
, offset
;
981 char *destptr
, *srcptr
;
985 cpu
= rseq_cpu_start();
986 /* Load offset with single-copy atomicity. */
987 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
990 destptr
= (char *)item
;
991 srcptr
= (char *)&buffer
->c
[cpu
].array
[offset
- 1];
992 /* copylen must be <= 4kB. */
993 copylen
= sizeof(*item
);
994 newval_final
= offset
- 1;
995 targetptr_final
= &buffer
->c
[cpu
].offset
;
996 ret
= rseq_cmpeqv_trymemcpy_storev(targetptr_final
,
997 offset
, destptr
, srcptr
, copylen
,
999 if (rseq_likely(!ret
)) {
1003 /* Retry if comparison fails or rseq aborts. */
1011 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
1012 * only be used on buffers that are not concurrently modified.
1014 static bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
1015 struct percpu_memcpy_buffer_node
*item
,
1020 offset
= buffer
->c
[cpu
].offset
;
1023 memcpy(item
, &buffer
->c
[cpu
].array
[offset
- 1], sizeof(*item
));
1024 buffer
->c
[cpu
].offset
= offset
- 1;
1028 static void *test_percpu_memcpy_buffer_thread(void *arg
)
1031 struct percpu_memcpy_buffer
*buffer
= (struct percpu_memcpy_buffer
*)arg
;
1033 if (!opt_disable_rseq
&& rseq_register_current_thread())
1037 for (i
= 0; i
< reps
; i
++) {
1038 struct percpu_memcpy_buffer_node item
;
1041 result
= this_cpu_memcpy_buffer_pop(buffer
, &item
, NULL
);
1043 sched_yield(); /* encourage shuffling */
1045 if (!this_cpu_memcpy_buffer_push(buffer
, item
, NULL
)) {
1046 /* Should increase buffer size. */
1052 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
1053 (int) rseq_gettid(), nr_abort
, signals_delivered
);
1054 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
1060 /* Simultaneous modification to a per-cpu buffer from many threads. */
1061 static void test_percpu_memcpy_buffer(void)
1063 const int num_threads
= opt_threads
;
1065 uint64_t sum
= 0, expected_sum
= 0;
1066 struct percpu_memcpy_buffer buffer
;
1067 pthread_t test_threads
[num_threads
];
1068 cpu_set_t allowed_cpus
;
1070 memset(&buffer
, 0, sizeof(buffer
));
1072 /* Generate list entries for every usable cpu. */
1073 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
1074 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1075 if (!CPU_ISSET(i
, &allowed_cpus
))
1077 /* Worse-case is every item in same CPU. */
1079 (struct percpu_memcpy_buffer_node
*)
1080 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
1081 MEMCPY_BUFFER_ITEM_PER_CPU
);
1082 assert(buffer
.c
[i
].array
);
1083 buffer
.c
[i
].buflen
= CPU_SETSIZE
* MEMCPY_BUFFER_ITEM_PER_CPU
;
1084 for (j
= 1; j
<= MEMCPY_BUFFER_ITEM_PER_CPU
; j
++) {
1085 expected_sum
+= 2 * j
+ 1;
1088 * We could theoretically put the word-sized
1089 * "data" directly in the buffer. However, we
1090 * want to model objects that would not fit
1091 * within a single word, so allocate an object
1094 buffer
.c
[i
].array
[j
- 1].data1
= j
;
1095 buffer
.c
[i
].array
[j
- 1].data2
= j
+ 1;
1096 buffer
.c
[i
].offset
++;
1100 for (i
= 0; i
< num_threads
; i
++) {
1101 ret
= pthread_create(&test_threads
[i
], NULL
,
1102 test_percpu_memcpy_buffer_thread
,
1106 perror("pthread_create");
1111 for (i
= 0; i
< num_threads
; i
++) {
1112 ret
= pthread_join(test_threads
[i
], NULL
);
1115 perror("pthread_join");
1120 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1121 struct percpu_memcpy_buffer_node item
;
1123 if (!CPU_ISSET(i
, &allowed_cpus
))
1126 while (__percpu_memcpy_buffer_pop(&buffer
, &item
, i
)) {
1130 free(buffer
.c
[i
].array
);
1134 * All entries should now be accounted for (unless some external
1135 * actor is interfering with our allowed affinity while this
1138 assert(sum
== expected_sum
);
1142 static void test_signal_interrupt_handler(__attribute__ ((unused
)) int signo
)
1144 signals_delivered
++;
1147 static int set_signal_handler(void)
1150 struct sigaction sa
;
1153 ret
= sigemptyset(&sigset
);
1155 perror("sigemptyset");
1159 sa
.sa_handler
= test_signal_interrupt_handler
;
1160 sa
.sa_mask
= sigset
;
1162 ret
= sigaction(SIGUSR1
, &sa
, NULL
);
1164 perror("sigaction");
1168 printf_verbose("Signal handler set for SIGUSR1\n");
1174 int sys_membarrier(int cmd
, int flags
, int cpu_id
)
1176 return syscall(__NR_membarrier
, cmd
, flags
, cpu_id
);
1180 bool membarrier_private_expedited_rseq_available(void)
1182 int status
= sys_membarrier(MEMBARRIER_CMD_QUERY
, 0, 0);
1185 perror("membarrier");
1188 if (!(status
& MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
))
1193 /* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
1194 #ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
1195 struct test_membarrier_thread_args
{
1197 intptr_t percpu_list_ptr
;
1200 /* Worker threads modify data in their "active" percpu lists. */
1202 void *test_membarrier_worker_thread(void *arg
)
1204 struct test_membarrier_thread_args
*args
=
1205 (struct test_membarrier_thread_args
*)arg
;
1206 const int iters
= opt_reps
;
1209 if (rseq_register_current_thread()) {
1210 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1211 errno
, strerror(errno
));
1215 /* Wait for initialization. */
1216 while (!rseq_smp_load_acquire(&args
->percpu_list_ptr
)) { }
1218 for (i
= 0; i
< iters
; ++i
) {
1222 int cpu
= rseq_cpu_start();
1224 ret
= rseq_offset_deref_addv(&args
->percpu_list_ptr
,
1225 sizeof(struct percpu_list_entry
) * cpu
, 1, cpu
);
1226 } while (rseq_unlikely(ret
));
1229 if (rseq_unregister_current_thread()) {
1230 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1231 errno
, strerror(errno
));
1238 void test_membarrier_init_percpu_list(struct percpu_list
*list
)
1242 memset(list
, 0, sizeof(*list
));
1243 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1244 struct percpu_list_node
*node
;
1246 node
= (struct percpu_list_node
*) malloc(sizeof(*node
));
1250 list
->c
[i
].head
= node
;
1255 void test_membarrier_free_percpu_list(struct percpu_list
*list
)
1259 for (i
= 0; i
< CPU_SETSIZE
; i
++)
1260 free(list
->c
[i
].head
);
1264 * The manager thread swaps per-cpu lists that worker threads see,
1265 * and validates that there are no unexpected modifications.
1268 void *test_membarrier_manager_thread(void *arg
)
1270 struct test_membarrier_thread_args
*args
=
1271 (struct test_membarrier_thread_args
*)arg
;
1272 struct percpu_list list_a
, list_b
;
1273 intptr_t expect_a
= 0, expect_b
= 0;
1274 int cpu_a
= 0, cpu_b
= 0;
1276 if (rseq_register_current_thread()) {
1277 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1278 errno
, strerror(errno
));
1283 test_membarrier_init_percpu_list(&list_a
);
1284 test_membarrier_init_percpu_list(&list_b
);
1286 /* Initialize lists before publishing them. */
1289 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_a
);
1291 while (!RSEQ_READ_ONCE(args
->stop
)) {
1292 /* list_a is "active". */
1293 cpu_a
= rand() % CPU_SETSIZE
;
1295 * As list_b is "inactive", we should never see changes
1298 if (expect_b
!= RSEQ_READ_ONCE(list_b
.c
[cpu_b
].head
->data
)) {
1299 fprintf(stderr
, "Membarrier test failed\n");
1303 /* Make list_b "active". */
1304 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_b
);
1305 if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
1306 MEMBARRIER_CMD_FLAG_CPU
, cpu_a
) &&
1307 errno
!= ENXIO
/* missing CPU */) {
1308 perror("sys_membarrier");
1312 * Cpu A should now only modify list_b, so the values
1313 * in list_a should be stable.
1315 expect_a
= RSEQ_READ_ONCE(list_a
.c
[cpu_a
].head
->data
);
1317 cpu_b
= rand() % CPU_SETSIZE
;
1319 * As list_a is "inactive", we should never see changes
1322 if (expect_a
!= RSEQ_READ_ONCE(list_a
.c
[cpu_a
].head
->data
)) {
1323 fprintf(stderr
, "Membarrier test failed\n");
1327 /* Make list_a "active". */
1328 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_a
);
1329 if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
1330 MEMBARRIER_CMD_FLAG_CPU
, cpu_b
) &&
1331 errno
!= ENXIO
/* missing CPU */) {
1332 perror("sys_membarrier");
1335 /* Remember a value from list_b. */
1336 expect_b
= RSEQ_READ_ONCE(list_b
.c
[cpu_b
].head
->data
);
1339 test_membarrier_free_percpu_list(&list_a
);
1340 test_membarrier_free_percpu_list(&list_b
);
1342 if (rseq_unregister_current_thread()) {
1343 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1344 errno
, strerror(errno
));
1351 void test_membarrier(void)
1353 const int num_threads
= opt_threads
;
1354 struct test_membarrier_thread_args thread_args
;
1355 pthread_t worker_threads
[num_threads
];
1356 pthread_t manager_thread
;
1359 if (!membarrier_private_expedited_rseq_available()) {
1360 fprintf(stderr
, "Membarrier private expedited rseq not available. "
1361 "Skipping membarrier test.\n");
1364 if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
, 0, 0)) {
1365 perror("sys_membarrier");
1369 thread_args
.stop
= 0;
1370 thread_args
.percpu_list_ptr
= 0;
1371 ret
= pthread_create(&manager_thread
, NULL
,
1372 test_membarrier_manager_thread
, &thread_args
);
1375 perror("pthread_create");
1379 for (i
= 0; i
< num_threads
; i
++) {
1380 ret
= pthread_create(&worker_threads
[i
], NULL
,
1381 test_membarrier_worker_thread
, &thread_args
);
1384 perror("pthread_create");
1390 for (i
= 0; i
< num_threads
; i
++) {
1391 ret
= pthread_join(worker_threads
[i
], NULL
);
1394 perror("pthread_join");
1399 RSEQ_WRITE_ONCE(thread_args
.stop
, 1);
1400 ret
= pthread_join(manager_thread
, NULL
);
1403 perror("pthread_join");
1407 #else /* RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV */
1409 void test_membarrier(void)
1411 if (!membarrier_private_expedited_rseq_available()) {
1412 fprintf(stderr
, "Membarrier private expedited rseq not available. "
1413 "Skipping membarrier test.\n");
1416 fprintf(stderr
, "rseq_offset_deref_addv is not implemented on this architecture. "
1417 "Skipping membarrier test.\n");
1421 static void show_usage(char **argv
)
1423 printf("Usage : %s <OPTIONS>\n",
1425 printf("OPTIONS:\n");
1426 printf(" [-1 loops] Number of loops for delay injection 1\n");
1427 printf(" [-2 loops] Number of loops for delay injection 2\n");
1428 printf(" [-3 loops] Number of loops for delay injection 3\n");
1429 printf(" [-4 loops] Number of loops for delay injection 4\n");
1430 printf(" [-5 loops] Number of loops for delay injection 5\n");
1431 printf(" [-6 loops] Number of loops for delay injection 6\n");
1432 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
1433 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
1434 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
1435 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
1436 printf(" [-y] Yield\n");
1437 printf(" [-k] Kill thread with signal\n");
1438 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
1439 printf(" [-t N] Number of threads (default 200)\n");
1440 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
1441 printf(" [-d] Disable rseq system call (no initialization)\n");
1442 printf(" [-D M] Disable rseq for each M threads\n");
1443 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
1444 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
1445 printf(" [-c] Check if the rseq syscall is available.\n");
1446 printf(" [-v] Verbose output.\n");
1447 printf(" [-h] Show this help.\n");
1451 int main(int argc
, char **argv
)
1455 for (i
= 1; i
< argc
; i
++) {
1456 if (argv
[i
][0] != '-')
1458 switch (argv
[i
][1]) {
1472 loop_cnt
[argv
[i
][1] - '0'] = atol(argv
[i
+ 1]);
1480 opt_modulo
= atol(argv
[i
+ 1]);
1481 if (opt_modulo
< 0) {
1492 opt_sleep
= atol(argv
[i
+ 1]);
1493 if (opt_sleep
< 0) {
1506 opt_disable_rseq
= 1;
1513 opt_disable_mod
= atol(argv
[i
+ 1]);
1514 if (opt_disable_mod
< 0) {
1525 opt_threads
= atol(argv
[i
+ 1]);
1526 if (opt_threads
< 0) {
1537 opt_reps
= atoll(argv
[i
+ 1]);
1552 opt_test
= *argv
[i
+ 1];
1574 if (rseq_available()) {
1575 printf_verbose("The rseq syscall is available.\n");
1578 printf_verbose("The rseq syscall is unavailable.\n");
1587 loop_cnt_1
= loop_cnt
[1];
1588 loop_cnt_2
= loop_cnt
[2];
1589 loop_cnt_3
= loop_cnt
[3];
1590 loop_cnt_4
= loop_cnt
[4];
1591 loop_cnt_5
= loop_cnt
[5];
1592 loop_cnt_6
= loop_cnt
[6];
1594 if (set_signal_handler())
1597 if (!opt_disable_rseq
&& rseq_register_current_thread())
1601 printf_verbose("spinlock\n");
1602 test_percpu_spinlock();
1605 printf_verbose("linked list\n");
1609 printf_verbose("buffer\n");
1610 test_percpu_buffer();
1613 printf_verbose("memcpy buffer\n");
1614 test_percpu_memcpy_buffer();
1617 printf_verbose("counter increment\n");
1621 printf_verbose("membarrier\n");
1625 if (!opt_disable_rseq
&& rseq_unregister_current_thread())