1 // SPDX-License-Identifier: LGPL-2.1
4 #include <linux/membarrier.h>
15 #include <sys/types.h>
20 static inline pid_t
rseq_gettid(void)
22 return syscall(__NR_gettid
);
26 static int loop_cnt
[NR_INJECT
+ 1];
28 static int loop_cnt_1
asm("asm_loop_cnt_1") __attribute__((used
));
29 static int loop_cnt_2
asm("asm_loop_cnt_2") __attribute__((used
));
30 static int loop_cnt_3
asm("asm_loop_cnt_3") __attribute__((used
));
31 static int loop_cnt_4
asm("asm_loop_cnt_4") __attribute__((used
));
32 static int loop_cnt_5
asm("asm_loop_cnt_5") __attribute__((used
));
33 static int loop_cnt_6
asm("asm_loop_cnt_6") __attribute__((used
));
35 static int opt_modulo
, verbose
;
37 static int opt_yield
, opt_signal
, opt_sleep
,
38 opt_disable_rseq
, opt_threads
= 200,
39 opt_disable_mod
= 0, opt_test
= 's', opt_mb
= 0;
41 #ifndef RSEQ_SKIP_FASTPATH
42 static long long opt_reps
= 5000;
44 static long long opt_reps
= 100;
47 static __thread
__attribute__((tls_model("initial-exec")))
48 unsigned int signals_delivered
;
52 static __thread
__attribute__((tls_model("initial-exec"), unused
))
53 unsigned int yield_mod_cnt
, nr_abort
;
55 #define printf_verbose(fmt, ...) \
58 printf(fmt, ## __VA_ARGS__); \
63 #define INJECT_ASM_REG "eax"
65 #define RSEQ_INJECT_CLOBBER \
68 #define RSEQ_INJECT_ASM(n) \
69 "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
70 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
73 "dec %%" INJECT_ASM_REG "\n\t" \
77 #elif defined(__x86_64__)
79 #define INJECT_ASM_REG_P "rax"
80 #define INJECT_ASM_REG "eax"
82 #define RSEQ_INJECT_CLOBBER \
86 #define RSEQ_INJECT_ASM(n) \
87 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
88 "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
89 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
92 "dec %%" INJECT_ASM_REG "\n\t" \
96 #elif defined(__s390__)
98 #define RSEQ_INJECT_INPUT \
99 , [loop_cnt_1]"m"(loop_cnt[1]) \
100 , [loop_cnt_2]"m"(loop_cnt[2]) \
101 , [loop_cnt_3]"m"(loop_cnt[3]) \
102 , [loop_cnt_4]"m"(loop_cnt[4]) \
103 , [loop_cnt_5]"m"(loop_cnt[5]) \
104 , [loop_cnt_6]"m"(loop_cnt[6])
106 #define INJECT_ASM_REG "r12"
108 #define RSEQ_INJECT_CLOBBER \
111 #define RSEQ_INJECT_ASM(n) \
112 "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
113 "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
116 "ahi %%" INJECT_ASM_REG ", -1\n\t" \
120 #elif defined(__ARMEL__)
122 #define RSEQ_INJECT_INPUT \
123 , [loop_cnt_1]"m"(loop_cnt[1]) \
124 , [loop_cnt_2]"m"(loop_cnt[2]) \
125 , [loop_cnt_3]"m"(loop_cnt[3]) \
126 , [loop_cnt_4]"m"(loop_cnt[4]) \
127 , [loop_cnt_5]"m"(loop_cnt[5]) \
128 , [loop_cnt_6]"m"(loop_cnt[6])
130 #define INJECT_ASM_REG "r4"
132 #define RSEQ_INJECT_CLOBBER \
135 #define RSEQ_INJECT_ASM(n) \
136 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
137 "cmp " INJECT_ASM_REG ", #0\n\t" \
140 "subs " INJECT_ASM_REG ", #1\n\t" \
144 #elif defined(__AARCH64EL__)
146 #define RSEQ_INJECT_INPUT \
147 , [loop_cnt_1] "Qo" (loop_cnt[1]) \
148 , [loop_cnt_2] "Qo" (loop_cnt[2]) \
149 , [loop_cnt_3] "Qo" (loop_cnt[3]) \
150 , [loop_cnt_4] "Qo" (loop_cnt[4]) \
151 , [loop_cnt_5] "Qo" (loop_cnt[5]) \
152 , [loop_cnt_6] "Qo" (loop_cnt[6])
154 #define INJECT_ASM_REG RSEQ_ASM_TMP_REG32
156 #define RSEQ_INJECT_ASM(n) \
157 " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \
158 " cbz " INJECT_ASM_REG ", 333f\n" \
160 " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \
161 " cbnz " INJECT_ASM_REG ", 222b\n" \
166 #define RSEQ_INJECT_INPUT \
167 , [loop_cnt_1]"m"(loop_cnt[1]) \
168 , [loop_cnt_2]"m"(loop_cnt[2]) \
169 , [loop_cnt_3]"m"(loop_cnt[3]) \
170 , [loop_cnt_4]"m"(loop_cnt[4]) \
171 , [loop_cnt_5]"m"(loop_cnt[5]) \
172 , [loop_cnt_6]"m"(loop_cnt[6])
174 #define INJECT_ASM_REG "r18"
176 #define RSEQ_INJECT_CLOBBER \
179 #define RSEQ_INJECT_ASM(n) \
180 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
181 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
184 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
188 #elif defined(__mips__)
190 #define RSEQ_INJECT_INPUT \
191 , [loop_cnt_1]"m"(loop_cnt[1]) \
192 , [loop_cnt_2]"m"(loop_cnt[2]) \
193 , [loop_cnt_3]"m"(loop_cnt[3]) \
194 , [loop_cnt_4]"m"(loop_cnt[4]) \
195 , [loop_cnt_5]"m"(loop_cnt[5]) \
196 , [loop_cnt_6]"m"(loop_cnt[6])
198 #define INJECT_ASM_REG "$5"
200 #define RSEQ_INJECT_CLOBBER \
203 #define RSEQ_INJECT_ASM(n) \
204 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
205 "beqz " INJECT_ASM_REG ", 333f\n\t" \
207 "addiu " INJECT_ASM_REG ", -1\n\t" \
208 "bnez " INJECT_ASM_REG ", 222b\n\t" \
212 #error unsupported target
215 #define RSEQ_INJECT_FAILED \
218 #define RSEQ_INJECT_C(n) \
220 int loc_i, loc_nr_loops = loop_cnt[n]; \
222 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
225 if (loc_nr_loops == -1 && opt_modulo) { \
226 if (yield_mod_cnt == opt_modulo - 1) { \
228 poll(NULL, 0, opt_sleep); \
242 #define printf_verbose(fmt, ...)
244 #endif /* BENCHMARK */
248 struct percpu_lock_entry
{
250 } __attribute__((aligned(128)));
253 struct percpu_lock_entry c
[CPU_SETSIZE
];
256 struct test_data_entry
{
258 } __attribute__((aligned(128)));
260 struct spinlock_test_data
{
261 struct percpu_lock lock
;
262 struct test_data_entry c
[CPU_SETSIZE
];
265 struct spinlock_thread_test_data
{
266 struct spinlock_test_data
*data
;
271 struct inc_test_data
{
272 struct test_data_entry c
[CPU_SETSIZE
];
275 struct inc_thread_test_data
{
276 struct inc_test_data
*data
;
281 struct percpu_list_node
{
283 struct percpu_list_node
*next
;
286 struct percpu_list_entry
{
287 struct percpu_list_node
*head
;
288 } __attribute__((aligned(128)));
291 struct percpu_list_entry c
[CPU_SETSIZE
];
294 #define BUFFER_ITEM_PER_CPU 100
296 struct percpu_buffer_node
{
300 struct percpu_buffer_entry
{
303 struct percpu_buffer_node
**array
;
304 } __attribute__((aligned(128)));
306 struct percpu_buffer
{
307 struct percpu_buffer_entry c
[CPU_SETSIZE
];
310 #define MEMCPY_BUFFER_ITEM_PER_CPU 100
312 struct percpu_memcpy_buffer_node
{
317 struct percpu_memcpy_buffer_entry
{
320 struct percpu_memcpy_buffer_node
*array
;
321 } __attribute__((aligned(128)));
323 struct percpu_memcpy_buffer
{
324 struct percpu_memcpy_buffer_entry c
[CPU_SETSIZE
];
327 /* A simple percpu spinlock. Grabs lock on current cpu. */
328 static int rseq_this_cpu_lock(struct percpu_lock
*lock
)
335 cpu
= rseq_cpu_start();
336 ret
= rseq_cmpeqv_storev(&lock
->c
[cpu
].v
,
338 if (rseq_likely(!ret
))
340 /* Retry if comparison fails or rseq aborts. */
343 * Acquire semantic when taking lock after control dependency.
344 * Matches rseq_smp_store_release().
346 rseq_smp_acquire__after_ctrl_dep();
350 static void rseq_percpu_unlock(struct percpu_lock
*lock
, int cpu
)
352 assert(lock
->c
[cpu
].v
== 1);
354 * Release lock, with release semantic. Matches
355 * rseq_smp_acquire__after_ctrl_dep().
357 rseq_smp_store_release(&lock
->c
[cpu
].v
, 0);
360 void *test_percpu_spinlock_thread(void *arg
)
362 struct spinlock_thread_test_data
*thread_data
= arg
;
363 struct spinlock_test_data
*data
= thread_data
->data
;
366 if (!opt_disable_rseq
&& thread_data
->reg
&&
367 rseq_register_current_thread())
369 reps
= thread_data
->reps
;
370 for (i
= 0; i
< reps
; i
++) {
371 int cpu
= rseq_cpu_start();
373 cpu
= rseq_this_cpu_lock(&data
->lock
);
374 data
->c
[cpu
].count
++;
375 rseq_percpu_unlock(&data
->lock
, cpu
);
377 if (i
!= 0 && !(i
% (reps
/ 10)))
378 printf_verbose("tid %d: count %lld\n",
379 (int) rseq_gettid(), i
);
382 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
383 (int) rseq_gettid(), nr_abort
, signals_delivered
);
384 if (!opt_disable_rseq
&& thread_data
->reg
&&
385 rseq_unregister_current_thread())
391 * A simple test which implements a sharded counter using a per-cpu
392 * lock. Obviously real applications might prefer to simply use a
393 * per-cpu increment; however, this is reasonable for a test and the
394 * lock can be extended to synchronize more complicated operations.
396 void test_percpu_spinlock(void)
398 const int num_threads
= opt_threads
;
401 pthread_t test_threads
[num_threads
];
402 struct spinlock_test_data data
;
403 struct spinlock_thread_test_data thread_data
[num_threads
];
405 memset(&data
, 0, sizeof(data
));
406 for (i
= 0; i
< num_threads
; i
++) {
407 thread_data
[i
].reps
= opt_reps
;
408 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
409 thread_data
[i
].reg
= 1;
411 thread_data
[i
].reg
= 0;
412 thread_data
[i
].data
= &data
;
413 ret
= pthread_create(&test_threads
[i
], NULL
,
414 test_percpu_spinlock_thread
,
418 perror("pthread_create");
423 for (i
= 0; i
< num_threads
; i
++) {
424 ret
= pthread_join(test_threads
[i
], NULL
);
427 perror("pthread_join");
433 for (i
= 0; i
< CPU_SETSIZE
; i
++)
434 sum
+= data
.c
[i
].count
;
436 assert(sum
== (uint64_t)opt_reps
* num_threads
);
439 void *test_percpu_inc_thread(void *arg
)
441 struct inc_thread_test_data
*thread_data
= arg
;
442 struct inc_test_data
*data
= thread_data
->data
;
445 if (!opt_disable_rseq
&& thread_data
->reg
&&
446 rseq_register_current_thread())
448 reps
= thread_data
->reps
;
449 for (i
= 0; i
< reps
; i
++) {
455 cpu
= rseq_cpu_start();
456 ret
= rseq_addv(&data
->c
[cpu
].count
, 1, cpu
);
457 } while (rseq_unlikely(ret
));
459 if (i
!= 0 && !(i
% (reps
/ 10)))
460 printf_verbose("tid %d: count %lld\n",
461 (int) rseq_gettid(), i
);
464 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
465 (int) rseq_gettid(), nr_abort
, signals_delivered
);
466 if (!opt_disable_rseq
&& thread_data
->reg
&&
467 rseq_unregister_current_thread())
472 void test_percpu_inc(void)
474 const int num_threads
= opt_threads
;
477 pthread_t test_threads
[num_threads
];
478 struct inc_test_data data
;
479 struct inc_thread_test_data thread_data
[num_threads
];
481 memset(&data
, 0, sizeof(data
));
482 for (i
= 0; i
< num_threads
; i
++) {
483 thread_data
[i
].reps
= opt_reps
;
484 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
485 thread_data
[i
].reg
= 1;
487 thread_data
[i
].reg
= 0;
488 thread_data
[i
].data
= &data
;
489 ret
= pthread_create(&test_threads
[i
], NULL
,
490 test_percpu_inc_thread
,
494 perror("pthread_create");
499 for (i
= 0; i
< num_threads
; i
++) {
500 ret
= pthread_join(test_threads
[i
], NULL
);
503 perror("pthread_join");
509 for (i
= 0; i
< CPU_SETSIZE
; i
++)
510 sum
+= data
.c
[i
].count
;
512 assert(sum
== (uint64_t)opt_reps
* num_threads
);
515 void this_cpu_list_push(struct percpu_list
*list
,
516 struct percpu_list_node
*node
,
522 intptr_t *targetptr
, newval
, expect
;
525 cpu
= rseq_cpu_start();
526 /* Load list->c[cpu].head with single-copy atomicity. */
527 expect
= (intptr_t)RSEQ_READ_ONCE(list
->c
[cpu
].head
);
528 newval
= (intptr_t)node
;
529 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
530 node
->next
= (struct percpu_list_node
*)expect
;
531 ret
= rseq_cmpeqv_storev(targetptr
, expect
, newval
, cpu
);
532 if (rseq_likely(!ret
))
534 /* Retry if comparison fails or rseq aborts. */
541 * Unlike a traditional lock-less linked list; the availability of a
542 * rseq primitive allows us to implement pop without concerns over
545 struct percpu_list_node
*this_cpu_list_pop(struct percpu_list
*list
,
548 struct percpu_list_node
*node
= NULL
;
552 struct percpu_list_node
*head
;
553 intptr_t *targetptr
, expectnot
, *load
;
557 cpu
= rseq_cpu_start();
558 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
559 expectnot
= (intptr_t)NULL
;
560 offset
= offsetof(struct percpu_list_node
, next
);
561 load
= (intptr_t *)&head
;
562 ret
= rseq_cmpnev_storeoffp_load(targetptr
, expectnot
,
564 if (rseq_likely(!ret
)) {
570 /* Retry if rseq aborts. */
578 * __percpu_list_pop is not safe against concurrent accesses. Should
579 * only be used on lists that are not concurrently modified.
581 struct percpu_list_node
*__percpu_list_pop(struct percpu_list
*list
, int cpu
)
583 struct percpu_list_node
*node
;
585 node
= list
->c
[cpu
].head
;
588 list
->c
[cpu
].head
= node
->next
;
592 void *test_percpu_list_thread(void *arg
)
595 struct percpu_list
*list
= (struct percpu_list
*)arg
;
597 if (!opt_disable_rseq
&& rseq_register_current_thread())
601 for (i
= 0; i
< reps
; i
++) {
602 struct percpu_list_node
*node
;
604 node
= this_cpu_list_pop(list
, NULL
);
606 sched_yield(); /* encourage shuffling */
608 this_cpu_list_push(list
, node
, NULL
);
611 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
612 (int) rseq_gettid(), nr_abort
, signals_delivered
);
613 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
619 /* Simultaneous modification to a per-cpu linked list from many threads. */
620 void test_percpu_list(void)
622 const int num_threads
= opt_threads
;
624 uint64_t sum
= 0, expected_sum
= 0;
625 struct percpu_list list
;
626 pthread_t test_threads
[num_threads
];
627 cpu_set_t allowed_cpus
;
629 memset(&list
, 0, sizeof(list
));
631 /* Generate list entries for every usable cpu. */
632 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
633 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
634 if (!CPU_ISSET(i
, &allowed_cpus
))
636 for (j
= 1; j
<= 100; j
++) {
637 struct percpu_list_node
*node
;
641 node
= malloc(sizeof(*node
));
644 node
->next
= list
.c
[i
].head
;
645 list
.c
[i
].head
= node
;
649 for (i
= 0; i
< num_threads
; i
++) {
650 ret
= pthread_create(&test_threads
[i
], NULL
,
651 test_percpu_list_thread
, &list
);
654 perror("pthread_create");
659 for (i
= 0; i
< num_threads
; i
++) {
660 ret
= pthread_join(test_threads
[i
], NULL
);
663 perror("pthread_join");
668 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
669 struct percpu_list_node
*node
;
671 if (!CPU_ISSET(i
, &allowed_cpus
))
674 while ((node
= __percpu_list_pop(&list
, i
))) {
681 * All entries should now be accounted for (unless some external
682 * actor is interfering with our allowed affinity while this
685 assert(sum
== expected_sum
);
688 bool this_cpu_buffer_push(struct percpu_buffer
*buffer
,
689 struct percpu_buffer_node
*node
,
696 intptr_t *targetptr_spec
, newval_spec
;
697 intptr_t *targetptr_final
, newval_final
;
701 cpu
= rseq_cpu_start();
702 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
703 if (offset
== buffer
->c
[cpu
].buflen
)
705 newval_spec
= (intptr_t)node
;
706 targetptr_spec
= (intptr_t *)&buffer
->c
[cpu
].array
[offset
];
707 newval_final
= offset
+ 1;
708 targetptr_final
= &buffer
->c
[cpu
].offset
;
710 ret
= rseq_cmpeqv_trystorev_storev_release(
711 targetptr_final
, offset
, targetptr_spec
,
712 newval_spec
, newval_final
, cpu
);
714 ret
= rseq_cmpeqv_trystorev_storev(targetptr_final
,
715 offset
, targetptr_spec
, newval_spec
,
717 if (rseq_likely(!ret
)) {
721 /* Retry if comparison fails or rseq aborts. */
728 struct percpu_buffer_node
*this_cpu_buffer_pop(struct percpu_buffer
*buffer
,
731 struct percpu_buffer_node
*head
;
735 intptr_t *targetptr
, newval
;
739 cpu
= rseq_cpu_start();
740 /* Load offset with single-copy atomicity. */
741 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
746 head
= RSEQ_READ_ONCE(buffer
->c
[cpu
].array
[offset
- 1]);
748 targetptr
= (intptr_t *)&buffer
->c
[cpu
].offset
;
749 ret
= rseq_cmpeqv_cmpeqv_storev(targetptr
, offset
,
750 (intptr_t *)&buffer
->c
[cpu
].array
[offset
- 1],
751 (intptr_t)head
, newval
, cpu
);
752 if (rseq_likely(!ret
))
754 /* Retry if comparison fails or rseq aborts. */
762 * __percpu_buffer_pop is not safe against concurrent accesses. Should
763 * only be used on buffers that are not concurrently modified.
765 struct percpu_buffer_node
*__percpu_buffer_pop(struct percpu_buffer
*buffer
,
768 struct percpu_buffer_node
*head
;
771 offset
= buffer
->c
[cpu
].offset
;
774 head
= buffer
->c
[cpu
].array
[offset
- 1];
775 buffer
->c
[cpu
].offset
= offset
- 1;
779 void *test_percpu_buffer_thread(void *arg
)
782 struct percpu_buffer
*buffer
= (struct percpu_buffer
*)arg
;
784 if (!opt_disable_rseq
&& rseq_register_current_thread())
788 for (i
= 0; i
< reps
; i
++) {
789 struct percpu_buffer_node
*node
;
791 node
= this_cpu_buffer_pop(buffer
, NULL
);
793 sched_yield(); /* encourage shuffling */
795 if (!this_cpu_buffer_push(buffer
, node
, NULL
)) {
796 /* Should increase buffer size. */
802 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
803 (int) rseq_gettid(), nr_abort
, signals_delivered
);
804 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
810 /* Simultaneous modification to a per-cpu buffer from many threads. */
811 void test_percpu_buffer(void)
813 const int num_threads
= opt_threads
;
815 uint64_t sum
= 0, expected_sum
= 0;
816 struct percpu_buffer buffer
;
817 pthread_t test_threads
[num_threads
];
818 cpu_set_t allowed_cpus
;
820 memset(&buffer
, 0, sizeof(buffer
));
822 /* Generate list entries for every usable cpu. */
823 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
824 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
825 if (!CPU_ISSET(i
, &allowed_cpus
))
827 /* Worse-case is every item in same CPU. */
829 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
830 BUFFER_ITEM_PER_CPU
);
831 assert(buffer
.c
[i
].array
);
832 buffer
.c
[i
].buflen
= CPU_SETSIZE
* BUFFER_ITEM_PER_CPU
;
833 for (j
= 1; j
<= BUFFER_ITEM_PER_CPU
; j
++) {
834 struct percpu_buffer_node
*node
;
839 * We could theoretically put the word-sized
840 * "data" directly in the buffer. However, we
841 * want to model objects that would not fit
842 * within a single word, so allocate an object
845 node
= malloc(sizeof(*node
));
848 buffer
.c
[i
].array
[j
- 1] = node
;
849 buffer
.c
[i
].offset
++;
853 for (i
= 0; i
< num_threads
; i
++) {
854 ret
= pthread_create(&test_threads
[i
], NULL
,
855 test_percpu_buffer_thread
, &buffer
);
858 perror("pthread_create");
863 for (i
= 0; i
< num_threads
; i
++) {
864 ret
= pthread_join(test_threads
[i
], NULL
);
867 perror("pthread_join");
872 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
873 struct percpu_buffer_node
*node
;
875 if (!CPU_ISSET(i
, &allowed_cpus
))
878 while ((node
= __percpu_buffer_pop(&buffer
, i
))) {
882 free(buffer
.c
[i
].array
);
886 * All entries should now be accounted for (unless some external
887 * actor is interfering with our allowed affinity while this
890 assert(sum
== expected_sum
);
893 bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer
*buffer
,
894 struct percpu_memcpy_buffer_node item
,
901 intptr_t *targetptr_final
, newval_final
, offset
;
902 char *destptr
, *srcptr
;
906 cpu
= rseq_cpu_start();
907 /* Load offset with single-copy atomicity. */
908 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
909 if (offset
== buffer
->c
[cpu
].buflen
)
911 destptr
= (char *)&buffer
->c
[cpu
].array
[offset
];
912 srcptr
= (char *)&item
;
913 /* copylen must be <= 4kB. */
914 copylen
= sizeof(item
);
915 newval_final
= offset
+ 1;
916 targetptr_final
= &buffer
->c
[cpu
].offset
;
918 ret
= rseq_cmpeqv_trymemcpy_storev_release(
919 targetptr_final
, offset
,
920 destptr
, srcptr
, copylen
,
923 ret
= rseq_cmpeqv_trymemcpy_storev(targetptr_final
,
924 offset
, destptr
, srcptr
, copylen
,
926 if (rseq_likely(!ret
)) {
930 /* Retry if comparison fails or rseq aborts. */
937 bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
938 struct percpu_memcpy_buffer_node
*item
,
945 intptr_t *targetptr_final
, newval_final
, offset
;
946 char *destptr
, *srcptr
;
950 cpu
= rseq_cpu_start();
951 /* Load offset with single-copy atomicity. */
952 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
955 destptr
= (char *)item
;
956 srcptr
= (char *)&buffer
->c
[cpu
].array
[offset
- 1];
957 /* copylen must be <= 4kB. */
958 copylen
= sizeof(*item
);
959 newval_final
= offset
- 1;
960 targetptr_final
= &buffer
->c
[cpu
].offset
;
961 ret
= rseq_cmpeqv_trymemcpy_storev(targetptr_final
,
962 offset
, destptr
, srcptr
, copylen
,
964 if (rseq_likely(!ret
)) {
968 /* Retry if comparison fails or rseq aborts. */
976 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
977 * only be used on buffers that are not concurrently modified.
979 bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
980 struct percpu_memcpy_buffer_node
*item
,
985 offset
= buffer
->c
[cpu
].offset
;
988 memcpy(item
, &buffer
->c
[cpu
].array
[offset
- 1], sizeof(*item
));
989 buffer
->c
[cpu
].offset
= offset
- 1;
993 void *test_percpu_memcpy_buffer_thread(void *arg
)
996 struct percpu_memcpy_buffer
*buffer
= (struct percpu_memcpy_buffer
*)arg
;
998 if (!opt_disable_rseq
&& rseq_register_current_thread())
1002 for (i
= 0; i
< reps
; i
++) {
1003 struct percpu_memcpy_buffer_node item
;
1006 result
= this_cpu_memcpy_buffer_pop(buffer
, &item
, NULL
);
1008 sched_yield(); /* encourage shuffling */
1010 if (!this_cpu_memcpy_buffer_push(buffer
, item
, NULL
)) {
1011 /* Should increase buffer size. */
1017 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
1018 (int) rseq_gettid(), nr_abort
, signals_delivered
);
1019 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
1025 /* Simultaneous modification to a per-cpu buffer from many threads. */
1026 void test_percpu_memcpy_buffer(void)
1028 const int num_threads
= opt_threads
;
1030 uint64_t sum
= 0, expected_sum
= 0;
1031 struct percpu_memcpy_buffer buffer
;
1032 pthread_t test_threads
[num_threads
];
1033 cpu_set_t allowed_cpus
;
1035 memset(&buffer
, 0, sizeof(buffer
));
1037 /* Generate list entries for every usable cpu. */
1038 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
1039 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1040 if (!CPU_ISSET(i
, &allowed_cpus
))
1042 /* Worse-case is every item in same CPU. */
1044 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
1045 MEMCPY_BUFFER_ITEM_PER_CPU
);
1046 assert(buffer
.c
[i
].array
);
1047 buffer
.c
[i
].buflen
= CPU_SETSIZE
* MEMCPY_BUFFER_ITEM_PER_CPU
;
1048 for (j
= 1; j
<= MEMCPY_BUFFER_ITEM_PER_CPU
; j
++) {
1049 expected_sum
+= 2 * j
+ 1;
1052 * We could theoretically put the word-sized
1053 * "data" directly in the buffer. However, we
1054 * want to model objects that would not fit
1055 * within a single word, so allocate an object
1058 buffer
.c
[i
].array
[j
- 1].data1
= j
;
1059 buffer
.c
[i
].array
[j
- 1].data2
= j
+ 1;
1060 buffer
.c
[i
].offset
++;
1064 for (i
= 0; i
< num_threads
; i
++) {
1065 ret
= pthread_create(&test_threads
[i
], NULL
,
1066 test_percpu_memcpy_buffer_thread
,
1070 perror("pthread_create");
1075 for (i
= 0; i
< num_threads
; i
++) {
1076 ret
= pthread_join(test_threads
[i
], NULL
);
1079 perror("pthread_join");
1084 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1085 struct percpu_memcpy_buffer_node item
;
1087 if (!CPU_ISSET(i
, &allowed_cpus
))
1090 while (__percpu_memcpy_buffer_pop(&buffer
, &item
, i
)) {
1094 free(buffer
.c
[i
].array
);
1098 * All entries should now be accounted for (unless some external
1099 * actor is interfering with our allowed affinity while this
1102 assert(sum
== expected_sum
);
1105 static void test_signal_interrupt_handler(int signo
)
1107 signals_delivered
++;
1110 static int set_signal_handler(void)
1113 struct sigaction sa
;
1116 ret
= sigemptyset(&sigset
);
1118 perror("sigemptyset");
1122 sa
.sa_handler
= test_signal_interrupt_handler
;
1123 sa
.sa_mask
= sigset
;
1125 ret
= sigaction(SIGUSR1
, &sa
, NULL
);
1127 perror("sigaction");
1131 printf_verbose("Signal handler set for SIGUSR1\n");
1136 /* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
1137 #ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
1138 struct test_membarrier_thread_args
{
1140 intptr_t percpu_list_ptr
;
1143 /* Worker threads modify data in their "active" percpu lists. */
1144 void *test_membarrier_worker_thread(void *arg
)
1146 struct test_membarrier_thread_args
*args
=
1147 (struct test_membarrier_thread_args
*)arg
;
1148 const int iters
= opt_reps
;
1151 if (rseq_register_current_thread()) {
1152 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1153 errno
, strerror(errno
));
1157 /* Wait for initialization. */
1158 while (!atomic_load(&args
->percpu_list_ptr
)) {}
1160 for (i
= 0; i
< iters
; ++i
) {
1164 int cpu
= rseq_cpu_start();
1166 ret
= rseq_offset_deref_addv(&args
->percpu_list_ptr
,
1167 sizeof(struct percpu_list_entry
) * cpu
, 1, cpu
);
1168 } while (rseq_unlikely(ret
));
1171 if (rseq_unregister_current_thread()) {
1172 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1173 errno
, strerror(errno
));
1179 void test_membarrier_init_percpu_list(struct percpu_list
*list
)
1183 memset(list
, 0, sizeof(*list
));
1184 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1185 struct percpu_list_node
*node
;
1187 node
= malloc(sizeof(*node
));
1191 list
->c
[i
].head
= node
;
1195 void test_membarrier_free_percpu_list(struct percpu_list
*list
)
1199 for (i
= 0; i
< CPU_SETSIZE
; i
++)
1200 free(list
->c
[i
].head
);
1203 static int sys_membarrier(int cmd
, int flags
, int cpu_id
)
1205 return syscall(__NR_membarrier
, cmd
, flags
, cpu_id
);
1209 * The manager thread swaps per-cpu lists that worker threads see,
1210 * and validates that there are no unexpected modifications.
1212 void *test_membarrier_manager_thread(void *arg
)
1214 struct test_membarrier_thread_args
*args
=
1215 (struct test_membarrier_thread_args
*)arg
;
1216 struct percpu_list list_a
, list_b
;
1217 intptr_t expect_a
= 0, expect_b
= 0;
1218 int cpu_a
= 0, cpu_b
= 0;
1220 if (rseq_register_current_thread()) {
1221 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1222 errno
, strerror(errno
));
1227 test_membarrier_init_percpu_list(&list_a
);
1228 test_membarrier_init_percpu_list(&list_b
);
1230 atomic_store(&args
->percpu_list_ptr
, (intptr_t)&list_a
);
1232 while (!atomic_load(&args
->stop
)) {
1233 /* list_a is "active". */
1234 cpu_a
= rand() % CPU_SETSIZE
;
1236 * As list_b is "inactive", we should never see changes
1239 if (expect_b
!= atomic_load(&list_b
.c
[cpu_b
].head
->data
)) {
1240 fprintf(stderr
, "Membarrier test failed\n");
1244 /* Make list_b "active". */
1245 atomic_store(&args
->percpu_list_ptr
, (intptr_t)&list_b
);
1246 if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
1247 MEMBARRIER_CMD_FLAG_CPU
, cpu_a
) &&
1248 errno
!= ENXIO
/* missing CPU */) {
1249 perror("sys_membarrier");
1253 * Cpu A should now only modify list_b, so the values
1254 * in list_a should be stable.
1256 expect_a
= atomic_load(&list_a
.c
[cpu_a
].head
->data
);
1258 cpu_b
= rand() % CPU_SETSIZE
;
1260 * As list_a is "inactive", we should never see changes
1263 if (expect_a
!= atomic_load(&list_a
.c
[cpu_a
].head
->data
)) {
1264 fprintf(stderr
, "Membarrier test failed\n");
1268 /* Make list_a "active". */
1269 atomic_store(&args
->percpu_list_ptr
, (intptr_t)&list_a
);
1270 if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
1271 MEMBARRIER_CMD_FLAG_CPU
, cpu_b
) &&
1272 errno
!= ENXIO
/* missing CPU*/) {
1273 perror("sys_membarrier");
1276 /* Remember a value from list_b. */
1277 expect_b
= atomic_load(&list_b
.c
[cpu_b
].head
->data
);
1280 test_membarrier_free_percpu_list(&list_a
);
1281 test_membarrier_free_percpu_list(&list_b
);
1283 if (rseq_unregister_current_thread()) {
1284 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1285 errno
, strerror(errno
));
1291 void test_membarrier(void)
1293 const int num_threads
= opt_threads
;
1294 struct test_membarrier_thread_args thread_args
;
1295 pthread_t worker_threads
[num_threads
];
1296 pthread_t manager_thread
;
1299 if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
, 0, 0)) {
1300 perror("sys_membarrier");
1304 thread_args
.stop
= 0;
1305 thread_args
.percpu_list_ptr
= 0;
1306 ret
= pthread_create(&manager_thread
, NULL
,
1307 test_membarrier_manager_thread
, &thread_args
);
1310 perror("pthread_create");
1314 for (i
= 0; i
< num_threads
; i
++) {
1315 ret
= pthread_create(&worker_threads
[i
], NULL
,
1316 test_membarrier_worker_thread
, &thread_args
);
1319 perror("pthread_create");
1325 for (i
= 0; i
< num_threads
; i
++) {
1326 ret
= pthread_join(worker_threads
[i
], NULL
);
1329 perror("pthread_join");
1334 atomic_store(&thread_args
.stop
, 1);
1335 ret
= pthread_join(manager_thread
, NULL
);
1338 perror("pthread_join");
1342 #else /* RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV */
1343 void test_membarrier(void)
1345 fprintf(stderr
, "rseq_offset_deref_addv is not implemented on this architecture. "
1346 "Skipping membarrier test.\n");
1350 static void show_usage(int argc
, char **argv
)
1352 printf("Usage : %s <OPTIONS>\n",
1354 printf("OPTIONS:\n");
1355 printf(" [-1 loops] Number of loops for delay injection 1\n");
1356 printf(" [-2 loops] Number of loops for delay injection 2\n");
1357 printf(" [-3 loops] Number of loops for delay injection 3\n");
1358 printf(" [-4 loops] Number of loops for delay injection 4\n");
1359 printf(" [-5 loops] Number of loops for delay injection 5\n");
1360 printf(" [-6 loops] Number of loops for delay injection 6\n");
1361 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
1362 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
1363 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
1364 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
1365 printf(" [-y] Yield\n");
1366 printf(" [-k] Kill thread with signal\n");
1367 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
1368 printf(" [-t N] Number of threads (default 200)\n");
1369 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
1370 printf(" [-d] Disable rseq system call (no initialization)\n");
1371 printf(" [-D M] Disable rseq for each M threads\n");
1372 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
1373 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
1374 printf(" [-v] Verbose output.\n");
1375 printf(" [-h] Show this help.\n");
1379 int main(int argc
, char **argv
)
1383 for (i
= 1; i
< argc
; i
++) {
1384 if (argv
[i
][0] != '-')
1386 switch (argv
[i
][1]) {
1397 show_usage(argc
, argv
);
1400 loop_cnt
[argv
[i
][1] - '0'] = atol(argv
[i
+ 1]);
1405 show_usage(argc
, argv
);
1408 opt_modulo
= atol(argv
[i
+ 1]);
1409 if (opt_modulo
< 0) {
1410 show_usage(argc
, argv
);
1417 show_usage(argc
, argv
);
1420 opt_sleep
= atol(argv
[i
+ 1]);
1421 if (opt_sleep
< 0) {
1422 show_usage(argc
, argv
);
1434 opt_disable_rseq
= 1;
1438 show_usage(argc
, argv
);
1441 opt_disable_mod
= atol(argv
[i
+ 1]);
1442 if (opt_disable_mod
< 0) {
1443 show_usage(argc
, argv
);
1450 show_usage(argc
, argv
);
1453 opt_threads
= atol(argv
[i
+ 1]);
1454 if (opt_threads
< 0) {
1455 show_usage(argc
, argv
);
1462 show_usage(argc
, argv
);
1465 opt_reps
= atoll(argv
[i
+ 1]);
1467 show_usage(argc
, argv
);
1473 show_usage(argc
, argv
);
1477 show_usage(argc
, argv
);
1480 opt_test
= *argv
[i
+ 1];
1490 show_usage(argc
, argv
);
1502 show_usage(argc
, argv
);
1507 loop_cnt_1
= loop_cnt
[1];
1508 loop_cnt_2
= loop_cnt
[2];
1509 loop_cnt_3
= loop_cnt
[3];
1510 loop_cnt_4
= loop_cnt
[4];
1511 loop_cnt_5
= loop_cnt
[5];
1512 loop_cnt_6
= loop_cnt
[6];
1514 if (set_signal_handler())
1517 if (!opt_disable_rseq
&& rseq_register_current_thread())
1521 printf_verbose("spinlock\n");
1522 test_percpu_spinlock();
1525 printf_verbose("linked list\n");
1529 printf_verbose("buffer\n");
1530 test_percpu_buffer();
1533 printf_verbose("memcpy buffer\n");
1534 test_percpu_memcpy_buffer();
1537 printf_verbose("counter increment\n");
1541 printf_verbose("membarrier\n");
1545 if (!opt_disable_rseq
&& rseq_unregister_current_thread())