1 // SPDX-License-Identifier: MIT
2 // SPDX-FileCopyrightText: 2020-2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 #include <linux/version.h>
8 #include <linux/membarrier.h>
18 #include <sys/types.h>
24 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
26 MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
= (1 << 7),
27 MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
= (1 << 8),
31 MEMBARRIER_CMD_FLAG_CPU
= (1 << 0),
36 static int loop_cnt
[NR_INJECT
+ 1];
38 static int loop_cnt_1
asm("asm_loop_cnt_1") __attribute__((used
));
39 static int loop_cnt_2
asm("asm_loop_cnt_2") __attribute__((used
));
40 static int loop_cnt_3
asm("asm_loop_cnt_3") __attribute__((used
));
41 static int loop_cnt_4
asm("asm_loop_cnt_4") __attribute__((used
));
42 static int loop_cnt_5
asm("asm_loop_cnt_5") __attribute__((used
));
43 static int loop_cnt_6
asm("asm_loop_cnt_6") __attribute__((used
));
45 static int opt_modulo
, verbose
;
47 static int opt_yield
, opt_signal
, opt_sleep
,
48 opt_disable_rseq
, opt_threads
= 200,
49 opt_disable_mod
= 0, opt_test
= 's';
51 static long long opt_reps
= 5000;
53 static __thread
__attribute__((tls_model("initial-exec")))
54 unsigned int signals_delivered
;
56 static inline pid_t
rseq_gettid(void)
58 return syscall(__NR_gettid
);
63 static __thread
__attribute__((tls_model("initial-exec"), unused
))
64 int yield_mod_cnt
, nr_abort
;
66 #define printf_verbose(fmt, ...) \
69 printf(fmt, ## __VA_ARGS__); \
74 #define INJECT_ASM_REG "eax"
76 #define RSEQ_INJECT_CLOBBER \
79 #define RSEQ_INJECT_ASM(n) \
80 "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
81 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
84 "dec %%" INJECT_ASM_REG "\n\t" \
88 #elif defined(__x86_64__)
90 #define INJECT_ASM_REG_P "rax"
91 #define INJECT_ASM_REG "eax"
93 #define RSEQ_INJECT_CLOBBER \
97 #define RSEQ_INJECT_ASM(n) \
98 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
99 "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
100 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
103 "dec %%" INJECT_ASM_REG "\n\t" \
107 #elif defined(__s390__)
109 #define RSEQ_INJECT_INPUT \
110 , [loop_cnt_1]"m"(loop_cnt[1]) \
111 , [loop_cnt_2]"m"(loop_cnt[2]) \
112 , [loop_cnt_3]"m"(loop_cnt[3]) \
113 , [loop_cnt_4]"m"(loop_cnt[4]) \
114 , [loop_cnt_5]"m"(loop_cnt[5]) \
115 , [loop_cnt_6]"m"(loop_cnt[6])
117 #define INJECT_ASM_REG "r12"
119 #define RSEQ_INJECT_CLOBBER \
122 #define RSEQ_INJECT_ASM(n) \
123 "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
124 "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
127 "ahi %%" INJECT_ASM_REG ", -1\n\t" \
131 #elif defined(__ARMEL__)
133 #define RSEQ_INJECT_INPUT \
134 , [loop_cnt_1]"m"(loop_cnt[1]) \
135 , [loop_cnt_2]"m"(loop_cnt[2]) \
136 , [loop_cnt_3]"m"(loop_cnt[3]) \
137 , [loop_cnt_4]"m"(loop_cnt[4]) \
138 , [loop_cnt_5]"m"(loop_cnt[5]) \
139 , [loop_cnt_6]"m"(loop_cnt[6])
141 #define INJECT_ASM_REG "r4"
143 #define RSEQ_INJECT_CLOBBER \
146 #define RSEQ_INJECT_ASM(n) \
147 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
148 "cmp " INJECT_ASM_REG ", #0\n\t" \
151 "subs " INJECT_ASM_REG ", #1\n\t" \
155 #elif defined(__AARCH64EL__)
157 #define RSEQ_INJECT_INPUT \
158 , [loop_cnt_1] "Qo" (loop_cnt[1]) \
159 , [loop_cnt_2] "Qo" (loop_cnt[2]) \
160 , [loop_cnt_3] "Qo" (loop_cnt[3]) \
161 , [loop_cnt_4] "Qo" (loop_cnt[4]) \
162 , [loop_cnt_5] "Qo" (loop_cnt[5]) \
163 , [loop_cnt_6] "Qo" (loop_cnt[6])
165 #define INJECT_ASM_REG RSEQ_ASM_TMP_REG32
167 #define RSEQ_INJECT_ASM(n) \
168 " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \
169 " cbz " INJECT_ASM_REG ", 333f\n" \
171 " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \
172 " cbnz " INJECT_ASM_REG ", 222b\n" \
175 #elif defined(__PPC__)
177 #define RSEQ_INJECT_INPUT \
178 , [loop_cnt_1]"m"(loop_cnt[1]) \
179 , [loop_cnt_2]"m"(loop_cnt[2]) \
180 , [loop_cnt_3]"m"(loop_cnt[3]) \
181 , [loop_cnt_4]"m"(loop_cnt[4]) \
182 , [loop_cnt_5]"m"(loop_cnt[5]) \
183 , [loop_cnt_6]"m"(loop_cnt[6])
185 #define INJECT_ASM_REG "r18"
187 #define RSEQ_INJECT_CLOBBER \
190 #define RSEQ_INJECT_ASM(n) \
191 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
192 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
195 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
199 #elif defined(__mips__)
201 #define RSEQ_INJECT_INPUT \
202 , [loop_cnt_1]"m"(loop_cnt[1]) \
203 , [loop_cnt_2]"m"(loop_cnt[2]) \
204 , [loop_cnt_3]"m"(loop_cnt[3]) \
205 , [loop_cnt_4]"m"(loop_cnt[4]) \
206 , [loop_cnt_5]"m"(loop_cnt[5]) \
207 , [loop_cnt_6]"m"(loop_cnt[6])
209 #define INJECT_ASM_REG "$5"
211 #define RSEQ_INJECT_CLOBBER \
214 #define RSEQ_INJECT_ASM(n) \
215 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
216 "beqz " INJECT_ASM_REG ", 333f\n\t" \
218 "addiu " INJECT_ASM_REG ", -1\n\t" \
219 "bnez " INJECT_ASM_REG ", 222b\n\t" \
222 #elif defined(__riscv)
224 #define RSEQ_INJECT_INPUT \
225 , [loop_cnt_1]"m"(loop_cnt[1]) \
226 , [loop_cnt_2]"m"(loop_cnt[2]) \
227 , [loop_cnt_3]"m"(loop_cnt[3]) \
228 , [loop_cnt_4]"m"(loop_cnt[4]) \
229 , [loop_cnt_5]"m"(loop_cnt[5]) \
230 , [loop_cnt_6]"m"(loop_cnt[6])
232 #define INJECT_ASM_REG "t1"
234 #define RSEQ_INJECT_CLOBBER \
237 #define RSEQ_INJECT_ASM(n) \
238 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
239 "beqz " INJECT_ASM_REG ", 333f\n\t" \
241 "addi " INJECT_ASM_REG "," INJECT_ASM_REG ", -1\n\t" \
242 "bnez " INJECT_ASM_REG ", 222b\n\t" \
246 #error unsupported target
249 #define RSEQ_INJECT_FAILED \
252 #define RSEQ_INJECT_C(n) \
254 int loc_i, loc_nr_loops = loop_cnt[n]; \
256 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
259 if (loc_nr_loops == -1 && opt_modulo) { \
260 if (yield_mod_cnt == opt_modulo - 1) { \
262 poll(NULL, 0, opt_sleep); \
276 #define printf_verbose(fmt, ...)
278 #endif /* BENCHMARK */
280 #include <rseq/rseq.h>
282 static enum rseq_mo opt_mo
= RSEQ_MO_RELAXED
;
284 static int sys_membarrier(int cmd
, int flags
, int cpu_id
)
286 return syscall(__NR_membarrier
, cmd
, flags
, cpu_id
);
289 #ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
290 #define TEST_MEMBARRIER
293 #ifdef BUILDOPT_RSEQ_PERCPU_MM_CID
294 # define RSEQ_PERCPU RSEQ_PERCPU_MM_CID
296 int get_current_cpu_id(void)
298 return rseq_current_mm_cid();
301 bool rseq_validate_cpu_id(void)
303 return rseq_mm_cid_available();
306 bool rseq_use_cpu_index(void)
308 return false; /* Use mm_cid */
310 # ifdef TEST_MEMBARRIER
312 * Membarrier does not currently support targeting a mm_cid, so
313 * issue the barrier on all cpus.
316 int rseq_membarrier_expedited(__attribute__ ((unused
)) int cpu
)
318 return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
321 # endif /* TEST_MEMBARRIER */
323 # define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID
325 int get_current_cpu_id(void)
327 return rseq_cpu_start();
330 bool rseq_validate_cpu_id(void)
332 return rseq_current_cpu_raw() >= 0;
335 bool rseq_use_cpu_index(void)
337 return true; /* Use cpu_id as index. */
339 # ifdef TEST_MEMBARRIER
341 int rseq_membarrier_expedited(int cpu
)
343 return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
344 MEMBARRIER_CMD_FLAG_CPU
, cpu
);
346 # endif /* TEST_MEMBARRIER */
349 struct percpu_lock_entry
{
351 } __attribute__((aligned(128)));
354 struct percpu_lock_entry c
[CPU_SETSIZE
];
357 struct test_data_entry
{
359 } __attribute__((aligned(128)));
361 struct spinlock_test_data
{
362 struct percpu_lock lock
;
363 struct test_data_entry c
[CPU_SETSIZE
];
366 struct spinlock_thread_test_data
{
367 struct spinlock_test_data
*data
;
372 struct inc_test_data
{
373 struct test_data_entry c
[CPU_SETSIZE
];
376 struct inc_thread_test_data
{
377 struct inc_test_data
*data
;
382 struct percpu_list_node
{
384 struct percpu_list_node
*next
;
387 struct percpu_list_entry
{
388 struct percpu_list_node
*head
;
389 } __attribute__((aligned(128)));
392 struct percpu_list_entry c
[CPU_SETSIZE
];
395 #define BUFFER_ITEM_PER_CPU 100
397 struct percpu_buffer_node
{
401 struct percpu_buffer_entry
{
404 struct percpu_buffer_node
**array
;
405 } __attribute__((aligned(128)));
407 struct percpu_buffer
{
408 struct percpu_buffer_entry c
[CPU_SETSIZE
];
411 #define MEMCPY_BUFFER_ITEM_PER_CPU 100
413 struct percpu_memcpy_buffer_node
{
418 struct percpu_memcpy_buffer_entry
{
421 struct percpu_memcpy_buffer_node
*array
;
422 } __attribute__((aligned(128)));
424 struct percpu_memcpy_buffer
{
425 struct percpu_memcpy_buffer_entry c
[CPU_SETSIZE
];
428 /* A simple percpu spinlock. Grabs lock on current cpu. */
429 static int rseq_this_cpu_lock(struct percpu_lock
*lock
)
436 cpu
= get_current_cpu_id();
438 fprintf(stderr
, "pid: %d: tid: %d, cpu: %d: cid: %d\n",
439 getpid(), (int) rseq_gettid(), rseq_current_cpu_raw(), cpu
);
442 ret
= rseq_load_cbne_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
445 if (rseq_likely(!ret
))
447 /* Retry if comparison fails or rseq aborts. */
450 * Acquire semantic when taking lock after control dependency.
451 * Matches rseq_smp_store_release().
453 rseq_smp_acquire__after_ctrl_dep();
457 static void rseq_percpu_unlock(struct percpu_lock
*lock
, int cpu
)
459 assert(lock
->c
[cpu
].v
== 1);
461 * Release lock, with release semantic. Matches
462 * rseq_smp_acquire__after_ctrl_dep().
464 rseq_smp_store_release(&lock
->c
[cpu
].v
, 0);
467 static void *test_percpu_spinlock_thread(void *arg
)
469 struct spinlock_thread_test_data
*thread_data
= (struct spinlock_thread_test_data
*) arg
;
470 struct spinlock_test_data
*data
= thread_data
->data
;
473 if (!opt_disable_rseq
&& thread_data
->reg
&&
474 rseq_register_current_thread())
476 reps
= thread_data
->reps
;
477 for (i
= 0; i
< reps
; i
++) {
478 int cpu
= rseq_this_cpu_lock(&data
->lock
);
479 data
->c
[cpu
].count
++;
480 rseq_percpu_unlock(&data
->lock
, cpu
);
482 if (i
!= 0 && !(i
% (reps
/ 10)))
483 printf_verbose("tid %d: count %lld\n",
484 (int) rseq_gettid(), i
);
487 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
488 (int) rseq_gettid(), nr_abort
, signals_delivered
);
489 if (!opt_disable_rseq
&& thread_data
->reg
&&
490 rseq_unregister_current_thread())
496 * A simple test which implements a sharded counter using a per-cpu
497 * lock. Obviously real applications might prefer to simply use a
498 * per-cpu increment; however, this is reasonable for a test and the
499 * lock can be extended to synchronize more complicated operations.
501 static void test_percpu_spinlock(void)
503 const int num_threads
= opt_threads
;
506 pthread_t test_threads
[num_threads
];
507 struct spinlock_test_data data
;
508 struct spinlock_thread_test_data thread_data
[num_threads
];
510 memset(&data
, 0, sizeof(data
));
511 for (i
= 0; i
< num_threads
; i
++) {
512 thread_data
[i
].reps
= opt_reps
;
513 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
514 thread_data
[i
].reg
= 1;
516 thread_data
[i
].reg
= 0;
517 thread_data
[i
].data
= &data
;
518 ret
= pthread_create(&test_threads
[i
], NULL
,
519 test_percpu_spinlock_thread
,
523 perror("pthread_create");
528 for (i
= 0; i
< num_threads
; i
++) {
529 ret
= pthread_join(test_threads
[i
], NULL
);
532 perror("pthread_join");
538 for (i
= 0; i
< CPU_SETSIZE
; i
++)
539 sum
+= data
.c
[i
].count
;
541 assert(sum
== (uint64_t)opt_reps
* num_threads
);
544 static void *test_percpu_inc_thread(void *arg
)
546 struct inc_thread_test_data
*thread_data
= (struct inc_thread_test_data
*) arg
;
547 struct inc_test_data
*data
= thread_data
->data
;
550 if (!opt_disable_rseq
&& thread_data
->reg
&&
551 rseq_register_current_thread())
553 reps
= thread_data
->reps
;
554 for (i
= 0; i
< reps
; i
++) {
560 cpu
= get_current_cpu_id();
561 ret
= rseq_load_add_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
562 &data
->c
[cpu
].count
, 1, cpu
);
563 } while (rseq_unlikely(ret
));
565 if (i
!= 0 && !(i
% (reps
/ 10)))
566 printf_verbose("tid %d: count %lld\n",
567 (int) rseq_gettid(), i
);
570 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
571 (int) rseq_gettid(), nr_abort
, signals_delivered
);
572 if (!opt_disable_rseq
&& thread_data
->reg
&&
573 rseq_unregister_current_thread())
578 static void test_percpu_inc(void)
580 const int num_threads
= opt_threads
;
583 pthread_t test_threads
[num_threads
];
584 struct inc_test_data data
;
585 struct inc_thread_test_data thread_data
[num_threads
];
587 memset(&data
, 0, sizeof(data
));
588 for (i
= 0; i
< num_threads
; i
++) {
589 thread_data
[i
].reps
= opt_reps
;
590 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
591 thread_data
[i
].reg
= 1;
593 thread_data
[i
].reg
= 0;
594 thread_data
[i
].data
= &data
;
595 ret
= pthread_create(&test_threads
[i
], NULL
,
596 test_percpu_inc_thread
,
600 perror("pthread_create");
605 for (i
= 0; i
< num_threads
; i
++) {
606 ret
= pthread_join(test_threads
[i
], NULL
);
609 perror("pthread_join");
615 for (i
= 0; i
< CPU_SETSIZE
; i
++)
616 sum
+= data
.c
[i
].count
;
618 assert(sum
== (uint64_t)opt_reps
* num_threads
);
621 static void this_cpu_list_push(struct percpu_list
*list
,
622 struct percpu_list_node
*node
,
628 intptr_t *targetptr
, newval
, expect
;
631 cpu
= get_current_cpu_id();
632 /* Load list->c[cpu].head with single-copy atomicity. */
633 expect
= (intptr_t)RSEQ_READ_ONCE(list
->c
[cpu
].head
);
634 newval
= (intptr_t)node
;
635 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
636 node
->next
= (struct percpu_list_node
*)expect
;
637 ret
= rseq_load_cbne_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
638 targetptr
, expect
, newval
, cpu
);
639 if (rseq_likely(!ret
))
641 /* Retry if comparison fails or rseq aborts. */
648 * Unlike a traditional lock-less linked list; the availability of a
649 * rseq primitive allows us to implement pop without concerns over
652 static struct percpu_list_node
*this_cpu_list_pop(struct percpu_list
*list
,
655 struct percpu_list_node
*node
= NULL
;
659 struct percpu_list_node
*head
;
660 intptr_t *targetptr
, expectnot
, *load
;
664 cpu
= get_current_cpu_id();
665 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
666 expectnot
= (intptr_t)NULL
;
667 offset
= offsetof(struct percpu_list_node
, next
);
668 load
= (intptr_t *)&head
;
669 ret
= rseq_load_cbeq_store_add_load_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
670 targetptr
, expectnot
,
672 if (rseq_likely(!ret
)) {
678 /* Retry if rseq aborts. */
686 * __percpu_list_pop is not safe against concurrent accesses. Should
687 * only be used on lists that are not concurrently modified.
689 static struct percpu_list_node
*__percpu_list_pop(struct percpu_list
*list
, int cpu
)
691 struct percpu_list_node
*node
;
693 node
= list
->c
[cpu
].head
;
696 list
->c
[cpu
].head
= node
->next
;
700 static void *test_percpu_list_thread(void *arg
)
703 struct percpu_list
*list
= (struct percpu_list
*)arg
;
705 if (!opt_disable_rseq
&& rseq_register_current_thread())
709 for (i
= 0; i
< reps
; i
++) {
710 struct percpu_list_node
*node
;
712 node
= this_cpu_list_pop(list
, NULL
);
714 sched_yield(); /* encourage shuffling */
716 this_cpu_list_push(list
, node
, NULL
);
719 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
720 (int) rseq_gettid(), nr_abort
, signals_delivered
);
721 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
727 /* Simultaneous modification to a per-cpu linked list from many threads. */
728 static void test_percpu_list(void)
730 const int num_threads
= opt_threads
;
732 uint64_t sum
= 0, expected_sum
= 0;
733 struct percpu_list list
;
734 pthread_t test_threads
[num_threads
];
735 cpu_set_t allowed_cpus
;
737 memset(&list
, 0, sizeof(list
));
739 /* Generate list entries for every usable cpu. */
740 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
741 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
742 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
744 for (j
= 1; j
<= 100; j
++) {
745 struct percpu_list_node
*node
;
749 node
= (struct percpu_list_node
*) malloc(sizeof(*node
));
752 node
->next
= list
.c
[i
].head
;
753 list
.c
[i
].head
= node
;
757 for (i
= 0; i
< num_threads
; i
++) {
758 ret
= pthread_create(&test_threads
[i
], NULL
,
759 test_percpu_list_thread
, &list
);
762 perror("pthread_create");
767 for (i
= 0; i
< num_threads
; i
++) {
768 ret
= pthread_join(test_threads
[i
], NULL
);
771 perror("pthread_join");
776 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
777 struct percpu_list_node
*node
;
779 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
782 while ((node
= __percpu_list_pop(&list
, i
))) {
789 * All entries should now be accounted for (unless some external
790 * actor is interfering with our allowed affinity while this
793 assert(sum
== expected_sum
);
796 static bool this_cpu_buffer_push(struct percpu_buffer
*buffer
,
797 struct percpu_buffer_node
*node
,
804 intptr_t *targetptr_spec
, newval_spec
;
805 intptr_t *targetptr_final
, newval_final
;
809 cpu
= get_current_cpu_id();
810 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
811 if (offset
== buffer
->c
[cpu
].buflen
)
813 newval_spec
= (intptr_t)node
;
814 targetptr_spec
= (intptr_t *)&buffer
->c
[cpu
].array
[offset
];
815 newval_final
= offset
+ 1;
816 targetptr_final
= &buffer
->c
[cpu
].offset
;
817 ret
= rseq_load_cbne_store_store__ptr(opt_mo
, RSEQ_PERCPU
,
818 targetptr_final
, offset
, targetptr_spec
,
819 newval_spec
, newval_final
, cpu
);
820 if (rseq_likely(!ret
)) {
824 /* Retry if comparison fails or rseq aborts. */
831 static struct percpu_buffer_node
*this_cpu_buffer_pop(struct percpu_buffer
*buffer
,
834 struct percpu_buffer_node
*head
;
838 intptr_t *targetptr
, newval
;
842 cpu
= get_current_cpu_id();
843 /* Load offset with single-copy atomicity. */
844 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
849 head
= RSEQ_READ_ONCE(buffer
->c
[cpu
].array
[offset
- 1]);
851 targetptr
= (intptr_t *)&buffer
->c
[cpu
].offset
;
852 ret
= rseq_load_cbne_load_cbne_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
854 (intptr_t *)&buffer
->c
[cpu
].array
[offset
- 1],
855 (intptr_t)head
, newval
, cpu
);
856 if (rseq_likely(!ret
))
858 /* Retry if comparison fails or rseq aborts. */
866 * __percpu_buffer_pop is not safe against concurrent accesses. Should
867 * only be used on buffers that are not concurrently modified.
869 static struct percpu_buffer_node
*__percpu_buffer_pop(struct percpu_buffer
*buffer
,
872 struct percpu_buffer_node
*head
;
875 offset
= buffer
->c
[cpu
].offset
;
878 head
= buffer
->c
[cpu
].array
[offset
- 1];
879 buffer
->c
[cpu
].offset
= offset
- 1;
883 static void *test_percpu_buffer_thread(void *arg
)
886 struct percpu_buffer
*buffer
= (struct percpu_buffer
*)arg
;
888 if (!opt_disable_rseq
&& rseq_register_current_thread())
892 for (i
= 0; i
< reps
; i
++) {
893 struct percpu_buffer_node
*node
;
895 node
= this_cpu_buffer_pop(buffer
, NULL
);
897 sched_yield(); /* encourage shuffling */
899 if (!this_cpu_buffer_push(buffer
, node
, NULL
)) {
900 /* Should increase buffer size. */
906 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
907 (int) rseq_gettid(), nr_abort
, signals_delivered
);
908 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
914 /* Simultaneous modification to a per-cpu buffer from many threads. */
915 static void test_percpu_buffer(void)
917 const int num_threads
= opt_threads
;
919 uint64_t sum
= 0, expected_sum
= 0;
920 struct percpu_buffer buffer
;
921 pthread_t test_threads
[num_threads
];
922 cpu_set_t allowed_cpus
;
924 memset(&buffer
, 0, sizeof(buffer
));
926 /* Generate list entries for every usable cpu. */
927 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
928 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
929 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
931 /* Worse-case is every item in same CPU. */
933 (struct percpu_buffer_node
**)
934 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
935 BUFFER_ITEM_PER_CPU
);
936 assert(buffer
.c
[i
].array
);
937 buffer
.c
[i
].buflen
= CPU_SETSIZE
* BUFFER_ITEM_PER_CPU
;
938 for (j
= 1; j
<= BUFFER_ITEM_PER_CPU
; j
++) {
939 struct percpu_buffer_node
*node
;
944 * We could theoretically put the word-sized
945 * "data" directly in the buffer. However, we
946 * want to model objects that would not fit
947 * within a single word, so allocate an object
950 node
= (struct percpu_buffer_node
*) malloc(sizeof(*node
));
953 buffer
.c
[i
].array
[j
- 1] = node
;
954 buffer
.c
[i
].offset
++;
958 for (i
= 0; i
< num_threads
; i
++) {
959 ret
= pthread_create(&test_threads
[i
], NULL
,
960 test_percpu_buffer_thread
, &buffer
);
963 perror("pthread_create");
968 for (i
= 0; i
< num_threads
; i
++) {
969 ret
= pthread_join(test_threads
[i
], NULL
);
972 perror("pthread_join");
977 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
978 struct percpu_buffer_node
*node
;
980 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
983 while ((node
= __percpu_buffer_pop(&buffer
, i
))) {
987 free(buffer
.c
[i
].array
);
991 * All entries should now be accounted for (unless some external
992 * actor is interfering with our allowed affinity while this
995 assert(sum
== expected_sum
);
998 static bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer
*buffer
,
999 struct percpu_memcpy_buffer_node item
,
1002 bool result
= false;
1006 intptr_t *targetptr_final
, newval_final
, offset
;
1007 char *destptr
, *srcptr
;
1011 cpu
= get_current_cpu_id();
1012 /* Load offset with single-copy atomicity. */
1013 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
1014 if (offset
== buffer
->c
[cpu
].buflen
)
1016 destptr
= (char *)&buffer
->c
[cpu
].array
[offset
];
1017 srcptr
= (char *)&item
;
1018 /* copylen must be <= 4kB. */
1019 copylen
= sizeof(item
);
1020 newval_final
= offset
+ 1;
1021 targetptr_final
= &buffer
->c
[cpu
].offset
;
1022 ret
= rseq_load_cbne_memcpy_store__ptr(
1023 opt_mo
, RSEQ_PERCPU
,
1024 targetptr_final
, offset
,
1025 destptr
, srcptr
, copylen
,
1027 if (rseq_likely(!ret
)) {
1031 /* Retry if comparison fails or rseq aborts. */
1038 static bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
1039 struct percpu_memcpy_buffer_node
*item
,
1042 bool result
= false;
1046 intptr_t *targetptr_final
, newval_final
, offset
;
1047 char *destptr
, *srcptr
;
1051 cpu
= get_current_cpu_id();
1052 /* Load offset with single-copy atomicity. */
1053 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
1056 destptr
= (char *)item
;
1057 srcptr
= (char *)&buffer
->c
[cpu
].array
[offset
- 1];
1058 /* copylen must be <= 4kB. */
1059 copylen
= sizeof(*item
);
1060 newval_final
= offset
- 1;
1061 targetptr_final
= &buffer
->c
[cpu
].offset
;
1062 ret
= rseq_load_cbne_memcpy_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
1063 targetptr_final
, offset
, destptr
, srcptr
, copylen
,
1065 if (rseq_likely(!ret
)) {
1069 /* Retry if comparison fails or rseq aborts. */
1077 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
1078 * only be used on buffers that are not concurrently modified.
1080 static bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
1081 struct percpu_memcpy_buffer_node
*item
,
1086 offset
= buffer
->c
[cpu
].offset
;
1089 memcpy(item
, &buffer
->c
[cpu
].array
[offset
- 1], sizeof(*item
));
1090 buffer
->c
[cpu
].offset
= offset
- 1;
1094 static void *test_percpu_memcpy_buffer_thread(void *arg
)
1097 struct percpu_memcpy_buffer
*buffer
= (struct percpu_memcpy_buffer
*)arg
;
1099 if (!opt_disable_rseq
&& rseq_register_current_thread())
1103 for (i
= 0; i
< reps
; i
++) {
1104 struct percpu_memcpy_buffer_node item
;
1107 result
= this_cpu_memcpy_buffer_pop(buffer
, &item
, NULL
);
1109 sched_yield(); /* encourage shuffling */
1111 if (!this_cpu_memcpy_buffer_push(buffer
, item
, NULL
)) {
1112 /* Should increase buffer size. */
1118 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
1119 (int) rseq_gettid(), nr_abort
, signals_delivered
);
1120 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
1126 /* Simultaneous modification to a per-cpu buffer from many threads. */
1127 static void test_percpu_memcpy_buffer(void)
1129 const int num_threads
= opt_threads
;
1131 uint64_t sum
= 0, expected_sum
= 0;
1132 struct percpu_memcpy_buffer buffer
;
1133 pthread_t test_threads
[num_threads
];
1134 cpu_set_t allowed_cpus
;
1136 memset(&buffer
, 0, sizeof(buffer
));
1138 /* Generate list entries for every usable cpu. */
1139 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
1140 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1141 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
1143 /* Worse-case is every item in same CPU. */
1145 (struct percpu_memcpy_buffer_node
*)
1146 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
1147 MEMCPY_BUFFER_ITEM_PER_CPU
);
1148 assert(buffer
.c
[i
].array
);
1149 buffer
.c
[i
].buflen
= CPU_SETSIZE
* MEMCPY_BUFFER_ITEM_PER_CPU
;
1150 for (j
= 1; j
<= MEMCPY_BUFFER_ITEM_PER_CPU
; j
++) {
1151 expected_sum
+= 2 * j
+ 1;
1154 * We could theoretically put the word-sized
1155 * "data" directly in the buffer. However, we
1156 * want to model objects that would not fit
1157 * within a single word, so allocate an object
1160 buffer
.c
[i
].array
[j
- 1].data1
= j
;
1161 buffer
.c
[i
].array
[j
- 1].data2
= j
+ 1;
1162 buffer
.c
[i
].offset
++;
1166 for (i
= 0; i
< num_threads
; i
++) {
1167 ret
= pthread_create(&test_threads
[i
], NULL
,
1168 test_percpu_memcpy_buffer_thread
,
1172 perror("pthread_create");
1177 for (i
= 0; i
< num_threads
; i
++) {
1178 ret
= pthread_join(test_threads
[i
], NULL
);
1181 perror("pthread_join");
1186 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1187 struct percpu_memcpy_buffer_node item
;
1189 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
1192 while (__percpu_memcpy_buffer_pop(&buffer
, &item
, i
)) {
1196 free(buffer
.c
[i
].array
);
1200 * All entries should now be accounted for (unless some external
1201 * actor is interfering with our allowed affinity while this
1204 assert(sum
== expected_sum
);
1208 static void test_signal_interrupt_handler(__attribute__ ((unused
)) int signo
)
1210 signals_delivered
++;
1213 static int set_signal_handler(void)
1216 struct sigaction sa
;
1219 ret
= sigemptyset(&sigset
);
1221 perror("sigemptyset");
1225 sa
.sa_handler
= test_signal_interrupt_handler
;
1226 sa
.sa_mask
= sigset
;
1228 ret
= sigaction(SIGUSR1
, &sa
, NULL
);
1230 perror("sigaction");
1234 printf_verbose("Signal handler set for SIGUSR1\n");
1240 bool membarrier_private_expedited_rseq_available(void)
1242 int status
= sys_membarrier(MEMBARRIER_CMD_QUERY
, 0, 0);
1245 perror("membarrier");
1248 if (!(status
& MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
))
1253 /* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
1254 #ifdef TEST_MEMBARRIER
1255 struct test_membarrier_thread_args
{
1257 intptr_t percpu_list_ptr
;
1260 /* Worker threads modify data in their "active" percpu lists. */
1262 void *test_membarrier_worker_thread(void *arg
)
1264 struct test_membarrier_thread_args
*args
=
1265 (struct test_membarrier_thread_args
*)arg
;
1266 const int iters
= opt_reps
;
1269 if (rseq_register_current_thread()) {
1270 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1271 errno
, strerror(errno
));
1275 /* Wait for initialization. */
1276 while (!rseq_smp_load_acquire(&args
->percpu_list_ptr
)) { }
1278 for (i
= 0; i
< iters
; ++i
) {
1282 int cpu
= get_current_cpu_id();
1284 ret
= rseq_load_add_load_add_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
1285 &args
->percpu_list_ptr
,
1286 sizeof(struct percpu_list_entry
) * cpu
, 1, cpu
);
1287 } while (rseq_unlikely(ret
));
1290 if (rseq_unregister_current_thread()) {
1291 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1292 errno
, strerror(errno
));
1299 void test_membarrier_init_percpu_list(struct percpu_list
*list
)
1303 memset(list
, 0, sizeof(*list
));
1304 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1305 struct percpu_list_node
*node
;
1307 node
= (struct percpu_list_node
*) malloc(sizeof(*node
));
1311 list
->c
[i
].head
= node
;
1316 void test_membarrier_free_percpu_list(struct percpu_list
*list
)
1320 for (i
= 0; i
< CPU_SETSIZE
; i
++)
1321 free(list
->c
[i
].head
);
1325 * The manager thread swaps per-cpu lists that worker threads see,
1326 * and validates that there are no unexpected modifications.
1329 void *test_membarrier_manager_thread(void *arg
)
1331 struct test_membarrier_thread_args
*args
=
1332 (struct test_membarrier_thread_args
*)arg
;
1333 struct percpu_list list_a
, list_b
;
1334 intptr_t expect_a
= 0, expect_b
= 0;
1335 int cpu_a
= 0, cpu_b
= 0;
1337 if (rseq_register_current_thread()) {
1338 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1339 errno
, strerror(errno
));
1344 test_membarrier_init_percpu_list(&list_a
);
1345 test_membarrier_init_percpu_list(&list_b
);
1347 /* Initialize lists before publishing them. */
1350 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_a
);
1352 while (!RSEQ_READ_ONCE(args
->stop
)) {
1353 /* list_a is "active". */
1354 cpu_a
= rand() % CPU_SETSIZE
;
1356 * As list_b is "inactive", we should never see changes
1359 if (expect_b
!= RSEQ_READ_ONCE(list_b
.c
[cpu_b
].head
->data
)) {
1360 fprintf(stderr
, "Membarrier test failed\n");
1364 /* Make list_b "active". */
1365 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_b
);
1366 if (rseq_membarrier_expedited(cpu_a
) &&
1367 errno
!= ENXIO
/* missing CPU */) {
1368 perror("sys_membarrier");
1372 * Cpu A should now only modify list_b, so the values
1373 * in list_a should be stable.
1375 expect_a
= RSEQ_READ_ONCE(list_a
.c
[cpu_a
].head
->data
);
1377 cpu_b
= rand() % CPU_SETSIZE
;
1379 * As list_a is "inactive", we should never see changes
1382 if (expect_a
!= RSEQ_READ_ONCE(list_a
.c
[cpu_a
].head
->data
)) {
1383 fprintf(stderr
, "Membarrier test failed\n");
1387 /* Make list_a "active". */
1388 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_a
);
1389 if (rseq_membarrier_expedited(cpu_b
) &&
1390 errno
!= ENXIO
/* missing CPU */) {
1391 perror("sys_membarrier");
1394 /* Remember a value from list_b. */
1395 expect_b
= RSEQ_READ_ONCE(list_b
.c
[cpu_b
].head
->data
);
1398 test_membarrier_free_percpu_list(&list_a
);
1399 test_membarrier_free_percpu_list(&list_b
);
1401 if (rseq_unregister_current_thread()) {
1402 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1403 errno
, strerror(errno
));
1410 void test_membarrier(void)
1412 const int num_threads
= opt_threads
;
1413 struct test_membarrier_thread_args thread_args
;
1414 pthread_t worker_threads
[num_threads
];
1415 pthread_t manager_thread
;
1418 if (!membarrier_private_expedited_rseq_available()) {
1419 fprintf(stderr
, "Membarrier private expedited rseq not available. "
1420 "Skipping membarrier test.\n");
1423 if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
, 0, 0)) {
1424 perror("sys_membarrier");
1428 thread_args
.stop
= 0;
1429 thread_args
.percpu_list_ptr
= 0;
1430 ret
= pthread_create(&manager_thread
, NULL
,
1431 test_membarrier_manager_thread
, &thread_args
);
1434 perror("pthread_create");
1438 for (i
= 0; i
< num_threads
; i
++) {
1439 ret
= pthread_create(&worker_threads
[i
], NULL
,
1440 test_membarrier_worker_thread
, &thread_args
);
1443 perror("pthread_create");
1449 for (i
= 0; i
< num_threads
; i
++) {
1450 ret
= pthread_join(worker_threads
[i
], NULL
);
1453 perror("pthread_join");
1458 RSEQ_WRITE_ONCE(thread_args
.stop
, 1);
1459 ret
= pthread_join(manager_thread
, NULL
);
1462 perror("pthread_join");
1466 #else /* TEST_MEMBARRIER */
1468 void test_membarrier(void)
1470 if (!membarrier_private_expedited_rseq_available()) {
1471 fprintf(stderr
, "Membarrier private expedited rseq not available. "
1472 "Skipping membarrier test.\n");
1475 fprintf(stderr
, "rseq_load_add_load_add_store__ptr is not implemented on this architecture. "
1476 "Skipping membarrier test.\n");
1480 static void show_usage(char **argv
)
1482 printf("Usage : %s <OPTIONS>\n",
1484 printf("OPTIONS:\n");
1485 printf(" [-1 loops] Number of loops for delay injection 1\n");
1486 printf(" [-2 loops] Number of loops for delay injection 2\n");
1487 printf(" [-3 loops] Number of loops for delay injection 3\n");
1488 printf(" [-4 loops] Number of loops for delay injection 4\n");
1489 printf(" [-5 loops] Number of loops for delay injection 5\n");
1490 printf(" [-6 loops] Number of loops for delay injection 6\n");
1491 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
1492 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
1493 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
1494 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
1495 printf(" [-y] Yield\n");
1496 printf(" [-k] Kill thread with signal\n");
1497 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
1498 printf(" [-t N] Number of threads (default 200)\n");
1499 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
1500 printf(" [-d] Disable rseq system call (no initialization)\n");
1501 printf(" [-D M] Disable rseq for each M threads\n");
1502 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
1503 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
1504 printf(" [-c] Check if the rseq syscall is available.\n");
1505 printf(" [-v] Verbose output.\n");
1506 printf(" [-h] Show this help.\n");
1510 int main(int argc
, char **argv
)
1514 for (i
= 1; i
< argc
; i
++) {
1515 if (argv
[i
][0] != '-')
1517 switch (argv
[i
][1]) {
1531 loop_cnt
[argv
[i
][1] - '0'] = atol(argv
[i
+ 1]);
1539 opt_modulo
= atol(argv
[i
+ 1]);
1540 if (opt_modulo
< 0) {
1551 opt_sleep
= atol(argv
[i
+ 1]);
1552 if (opt_sleep
< 0) {
1565 opt_disable_rseq
= 1;
1572 opt_disable_mod
= atol(argv
[i
+ 1]);
1573 if (opt_disable_mod
< 0) {
1584 opt_threads
= atol(argv
[i
+ 1]);
1585 if (opt_threads
< 0) {
1596 opt_reps
= atoll(argv
[i
+ 1]);
1611 opt_test
= *argv
[i
+ 1];
1630 opt_mo
= RSEQ_MO_RELEASE
;
1633 if (rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL
)) {
1634 printf_verbose("The rseq syscall is available.\n");
1637 printf_verbose("The rseq syscall is unavailable.\n");
1646 loop_cnt_1
= loop_cnt
[1];
1647 loop_cnt_2
= loop_cnt
[2];
1648 loop_cnt_3
= loop_cnt
[3];
1649 loop_cnt_4
= loop_cnt
[4];
1650 loop_cnt_5
= loop_cnt
[5];
1651 loop_cnt_6
= loop_cnt
[6];
1653 if (set_signal_handler())
1656 if (!opt_disable_rseq
&& rseq_register_current_thread())
1658 if (!opt_disable_rseq
&& !rseq_validate_cpu_id()) {
1659 printf_verbose("The rseq cpu id getter is unavailable\n");
1664 printf_verbose("spinlock\n");
1665 test_percpu_spinlock();
1668 printf_verbose("linked list\n");
1672 printf_verbose("buffer\n");
1673 test_percpu_buffer();
1676 printf_verbose("memcpy buffer\n");
1677 test_percpu_memcpy_buffer();
1680 printf_verbose("counter increment\n");
1684 printf_verbose("membarrier\n");
1688 if (!opt_disable_rseq
&& rseq_unregister_current_thread())