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>
23 #include <rseq/percpu-alloc.h>
25 #define PERCPU_POOL_LEN (1024*1024) /* 1MB */
27 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
29 MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
= (1 << 7),
30 MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
= (1 << 8),
34 MEMBARRIER_CMD_FLAG_CPU
= (1 << 0),
39 static int loop_cnt
[NR_INJECT
+ 1];
41 static int loop_cnt_1
asm("asm_loop_cnt_1") __attribute__((used
));
42 static int loop_cnt_2
asm("asm_loop_cnt_2") __attribute__((used
));
43 static int loop_cnt_3
asm("asm_loop_cnt_3") __attribute__((used
));
44 static int loop_cnt_4
asm("asm_loop_cnt_4") __attribute__((used
));
45 static int loop_cnt_5
asm("asm_loop_cnt_5") __attribute__((used
));
46 static int loop_cnt_6
asm("asm_loop_cnt_6") __attribute__((used
));
48 static int opt_modulo
, verbose
;
50 static int opt_yield
, opt_signal
, opt_sleep
,
51 opt_disable_rseq
, opt_threads
= 200,
52 opt_disable_mod
= 0, opt_test
= 's';
54 static long long opt_reps
= 5000;
56 static __thread
__attribute__((tls_model("initial-exec")))
57 unsigned int signals_delivered
;
59 static inline pid_t
rseq_gettid(void)
61 return syscall(__NR_gettid
);
66 static __thread
__attribute__((tls_model("initial-exec"), unused
))
67 int yield_mod_cnt
, nr_abort
;
69 #define printf_verbose(fmt, ...) \
72 printf(fmt, ## __VA_ARGS__); \
77 #define INJECT_ASM_REG "eax"
79 #define RSEQ_INJECT_CLOBBER \
82 #define RSEQ_INJECT_ASM(n) \
83 "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
84 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
87 "dec %%" INJECT_ASM_REG "\n\t" \
91 #elif defined(__x86_64__)
93 #define INJECT_ASM_REG_P "rax"
94 #define INJECT_ASM_REG "eax"
96 #define RSEQ_INJECT_CLOBBER \
100 #define RSEQ_INJECT_ASM(n) \
101 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
102 "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
103 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
106 "dec %%" INJECT_ASM_REG "\n\t" \
110 #elif defined(__s390__)
112 #define RSEQ_INJECT_INPUT \
113 , [loop_cnt_1]"m"(loop_cnt[1]) \
114 , [loop_cnt_2]"m"(loop_cnt[2]) \
115 , [loop_cnt_3]"m"(loop_cnt[3]) \
116 , [loop_cnt_4]"m"(loop_cnt[4]) \
117 , [loop_cnt_5]"m"(loop_cnt[5]) \
118 , [loop_cnt_6]"m"(loop_cnt[6])
120 #define INJECT_ASM_REG "r12"
122 #define RSEQ_INJECT_CLOBBER \
125 #define RSEQ_INJECT_ASM(n) \
126 "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
127 "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
130 "ahi %%" INJECT_ASM_REG ", -1\n\t" \
134 #elif defined(__ARMEL__)
136 #define RSEQ_INJECT_INPUT \
137 , [loop_cnt_1]"m"(loop_cnt[1]) \
138 , [loop_cnt_2]"m"(loop_cnt[2]) \
139 , [loop_cnt_3]"m"(loop_cnt[3]) \
140 , [loop_cnt_4]"m"(loop_cnt[4]) \
141 , [loop_cnt_5]"m"(loop_cnt[5]) \
142 , [loop_cnt_6]"m"(loop_cnt[6])
144 #define INJECT_ASM_REG "r4"
146 #define RSEQ_INJECT_CLOBBER \
149 #define RSEQ_INJECT_ASM(n) \
150 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
151 "cmp " INJECT_ASM_REG ", #0\n\t" \
154 "subs " INJECT_ASM_REG ", #1\n\t" \
158 #elif defined(__AARCH64EL__)
160 #define RSEQ_INJECT_INPUT \
161 , [loop_cnt_1] "Qo" (loop_cnt[1]) \
162 , [loop_cnt_2] "Qo" (loop_cnt[2]) \
163 , [loop_cnt_3] "Qo" (loop_cnt[3]) \
164 , [loop_cnt_4] "Qo" (loop_cnt[4]) \
165 , [loop_cnt_5] "Qo" (loop_cnt[5]) \
166 , [loop_cnt_6] "Qo" (loop_cnt[6])
168 #define INJECT_ASM_REG RSEQ_ASM_TMP_REG32
170 #define RSEQ_INJECT_ASM(n) \
171 " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \
172 " cbz " INJECT_ASM_REG ", 333f\n" \
174 " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \
175 " cbnz " INJECT_ASM_REG ", 222b\n" \
178 #elif defined(__PPC__)
180 #define RSEQ_INJECT_INPUT \
181 , [loop_cnt_1]"m"(loop_cnt[1]) \
182 , [loop_cnt_2]"m"(loop_cnt[2]) \
183 , [loop_cnt_3]"m"(loop_cnt[3]) \
184 , [loop_cnt_4]"m"(loop_cnt[4]) \
185 , [loop_cnt_5]"m"(loop_cnt[5]) \
186 , [loop_cnt_6]"m"(loop_cnt[6])
188 #define INJECT_ASM_REG "r18"
190 #define RSEQ_INJECT_CLOBBER \
193 #define RSEQ_INJECT_ASM(n) \
194 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
195 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
198 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
202 #elif defined(__mips__)
204 #define RSEQ_INJECT_INPUT \
205 , [loop_cnt_1]"m"(loop_cnt[1]) \
206 , [loop_cnt_2]"m"(loop_cnt[2]) \
207 , [loop_cnt_3]"m"(loop_cnt[3]) \
208 , [loop_cnt_4]"m"(loop_cnt[4]) \
209 , [loop_cnt_5]"m"(loop_cnt[5]) \
210 , [loop_cnt_6]"m"(loop_cnt[6])
212 #define INJECT_ASM_REG "$5"
214 #define RSEQ_INJECT_CLOBBER \
217 #define RSEQ_INJECT_ASM(n) \
218 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
219 "beqz " INJECT_ASM_REG ", 333f\n\t" \
221 "addiu " INJECT_ASM_REG ", -1\n\t" \
222 "bnez " INJECT_ASM_REG ", 222b\n\t" \
225 #elif defined(__riscv)
227 #define RSEQ_INJECT_INPUT \
228 , [loop_cnt_1]"m"(loop_cnt[1]) \
229 , [loop_cnt_2]"m"(loop_cnt[2]) \
230 , [loop_cnt_3]"m"(loop_cnt[3]) \
231 , [loop_cnt_4]"m"(loop_cnt[4]) \
232 , [loop_cnt_5]"m"(loop_cnt[5]) \
233 , [loop_cnt_6]"m"(loop_cnt[6])
235 #define INJECT_ASM_REG "t1"
237 #define RSEQ_INJECT_CLOBBER \
240 #define RSEQ_INJECT_ASM(n) \
241 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
242 "beqz " INJECT_ASM_REG ", 333f\n\t" \
244 "addi " INJECT_ASM_REG "," INJECT_ASM_REG ", -1\n\t" \
245 "bnez " INJECT_ASM_REG ", 222b\n\t" \
249 #error unsupported target
252 #define RSEQ_INJECT_FAILED \
255 #define RSEQ_INJECT_C(n) \
257 int loc_i, loc_nr_loops = loop_cnt[n]; \
259 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
262 if (loc_nr_loops == -1 && opt_modulo) { \
263 if (yield_mod_cnt == opt_modulo - 1) { \
265 poll(NULL, 0, opt_sleep); \
279 #define printf_verbose(fmt, ...)
281 #endif /* BENCHMARK */
283 #include <rseq/rseq.h>
285 static enum rseq_mo opt_mo
= RSEQ_MO_RELAXED
;
287 static int sys_membarrier(int cmd
, int flags
, int cpu_id
)
289 return syscall(__NR_membarrier
, cmd
, flags
, cpu_id
);
292 #ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
293 #define TEST_MEMBARRIER
296 #ifdef BUILDOPT_RSEQ_PERCPU_MM_CID
297 # define RSEQ_PERCPU RSEQ_PERCPU_MM_CID
299 int get_current_cpu_id(void)
301 return rseq_current_mm_cid();
304 bool rseq_validate_cpu_id(void)
306 return rseq_mm_cid_available();
309 bool rseq_use_cpu_index(void)
311 return false; /* Use mm_cid */
313 # ifdef TEST_MEMBARRIER
315 * Membarrier does not currently support targeting a mm_cid, so
316 * issue the barrier on all cpus.
319 int rseq_membarrier_expedited(__attribute__ ((unused
)) int cpu
)
321 return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
324 # endif /* TEST_MEMBARRIER */
326 # define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID
328 int get_current_cpu_id(void)
330 return rseq_cpu_start();
333 bool rseq_validate_cpu_id(void)
335 return rseq_current_cpu_raw() >= 0;
338 bool rseq_use_cpu_index(void)
340 return true; /* Use cpu_id as index. */
342 # ifdef TEST_MEMBARRIER
344 int rseq_membarrier_expedited(int cpu
)
346 return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
,
347 MEMBARRIER_CMD_FLAG_CPU
, cpu
);
349 # endif /* TEST_MEMBARRIER */
356 struct test_data_entry
{
360 struct spinlock_test_data
{
361 struct percpu_lock lock
;
365 struct spinlock_thread_test_data
{
366 struct spinlock_test_data
*data
; /* Per-cpu pointer */
371 struct inc_test_data
{
372 struct test_data_entry c
[CPU_SETSIZE
];
375 struct inc_thread_test_data
{
376 struct inc_test_data
*data
;
381 struct percpu_list_node
{
383 struct percpu_list_node
*next
;
386 struct percpu_list_entry
{
387 struct percpu_list_node
*head
;
388 } __attribute__((aligned(128)));
391 struct percpu_list_entry c
[CPU_SETSIZE
];
394 #define BUFFER_ITEM_PER_CPU 100
396 struct percpu_buffer_node
{
400 struct percpu_buffer_entry
{
403 struct percpu_buffer_node
**array
;
404 } __attribute__((aligned(128)));
406 struct percpu_buffer
{
407 struct percpu_buffer_entry c
[CPU_SETSIZE
];
410 #define MEMCPY_BUFFER_ITEM_PER_CPU 100
412 struct percpu_memcpy_buffer_node
{
417 struct percpu_memcpy_buffer_entry
{
420 struct percpu_memcpy_buffer_node
*array
;
421 } __attribute__((aligned(128)));
423 struct percpu_memcpy_buffer
{
424 struct percpu_memcpy_buffer_entry c
[CPU_SETSIZE
];
427 /* A simple percpu spinlock. Grabs lock on current cpu. */
428 static int rseq_this_cpu_lock(struct percpu_lock
*lock
/* Per-cpu pointer */)
435 cpu
= get_current_cpu_id();
437 fprintf(stderr
, "pid: %d: tid: %d, cpu: %d: cid: %d\n",
438 getpid(), (int) rseq_gettid(), rseq_current_cpu_raw(), cpu
);
441 ret
= rseq_load_cbne_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
442 &rseq_percpu_ptr(lock
, cpu
)->v
,
444 if (rseq_likely(!ret
))
446 /* Retry if comparison fails or rseq aborts. */
449 * Acquire semantic when taking lock after control dependency.
450 * Matches rseq_smp_store_release().
452 rseq_smp_acquire__after_ctrl_dep();
456 static void rseq_percpu_unlock(struct percpu_lock
*lock
/* Per-cpu pointer */, int cpu
)
458 assert(rseq_percpu_ptr(lock
, cpu
)->v
== 1);
460 * Release lock, with release semantic. Matches
461 * rseq_smp_acquire__after_ctrl_dep().
463 rseq_smp_store_release(&rseq_percpu_ptr(lock
, cpu
)->v
, 0);
466 static void *test_percpu_spinlock_thread(void *arg
)
468 struct spinlock_thread_test_data
*thread_data
= (struct spinlock_thread_test_data
*) arg
;
469 struct spinlock_test_data
*data
= thread_data
->data
; /* Per-cpu pointer */
472 if (!opt_disable_rseq
&& thread_data
->reg
&&
473 rseq_register_current_thread())
475 reps
= thread_data
->reps
;
476 for (i
= 0; i
< reps
; i
++) {
477 int cpu
= rseq_this_cpu_lock(&data
->lock
);
478 rseq_percpu_ptr(data
, cpu
)->count
++;
479 rseq_percpu_unlock(&data
->lock
, cpu
);
481 if (i
!= 0 && !(i
% (reps
/ 10)))
482 printf_verbose("tid %d: count %lld\n",
483 (int) rseq_gettid(), i
);
486 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
487 (int) rseq_gettid(), nr_abort
, signals_delivered
);
488 if (!opt_disable_rseq
&& thread_data
->reg
&&
489 rseq_unregister_current_thread())
495 * A simple test which implements a sharded counter using a per-cpu
496 * lock. Obviously real applications might prefer to simply use a
497 * per-cpu increment; however, this is reasonable for a test and the
498 * lock can be extended to synchronize more complicated operations.
500 static void test_percpu_spinlock(void)
502 const int num_threads
= opt_threads
;
505 pthread_t test_threads
[num_threads
];
506 struct spinlock_test_data
*data
; /* Per-cpu pointer */
507 struct spinlock_thread_test_data thread_data
[num_threads
];
508 struct rseq_percpu_pool
*mempool
;
510 mempool
= rseq_percpu_pool_create(sizeof(struct spinlock_test_data
),
511 PERCPU_POOL_LEN
, CPU_SETSIZE
, PROT_READ
| PROT_WRITE
,
512 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0, 0);
514 perror("rseq_percpu_pool_create");
517 data
= (struct spinlock_test_data
*)rseq_percpu_zmalloc(mempool
);
519 perror("rseq_percpu_zmalloc");
523 for (i
= 0; i
< num_threads
; i
++) {
524 thread_data
[i
].reps
= opt_reps
;
525 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
526 thread_data
[i
].reg
= 1;
528 thread_data
[i
].reg
= 0;
529 thread_data
[i
].data
= data
;
530 ret
= pthread_create(&test_threads
[i
], NULL
,
531 test_percpu_spinlock_thread
,
535 perror("pthread_create");
540 for (i
= 0; i
< num_threads
; i
++) {
541 ret
= pthread_join(test_threads
[i
], NULL
);
544 perror("pthread_join");
550 for (i
= 0; i
< CPU_SETSIZE
; i
++)
551 sum
+= rseq_percpu_ptr(data
, i
)->count
;
553 assert(sum
== (uint64_t)opt_reps
* num_threads
);
554 rseq_percpu_free(data
);
555 ret
= rseq_percpu_pool_destroy(mempool
);
557 perror("rseq_percpu_pool_destroy");
562 static void *test_percpu_inc_thread(void *arg
)
564 struct inc_thread_test_data
*thread_data
= (struct inc_thread_test_data
*) arg
;
565 struct inc_test_data
*data
= thread_data
->data
;
568 if (!opt_disable_rseq
&& thread_data
->reg
&&
569 rseq_register_current_thread())
571 reps
= thread_data
->reps
;
572 for (i
= 0; i
< reps
; i
++) {
578 cpu
= get_current_cpu_id();
579 ret
= rseq_load_add_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
580 &data
->c
[cpu
].count
, 1, cpu
);
581 } while (rseq_unlikely(ret
));
583 if (i
!= 0 && !(i
% (reps
/ 10)))
584 printf_verbose("tid %d: count %lld\n",
585 (int) rseq_gettid(), i
);
588 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
589 (int) rseq_gettid(), nr_abort
, signals_delivered
);
590 if (!opt_disable_rseq
&& thread_data
->reg
&&
591 rseq_unregister_current_thread())
596 static void test_percpu_inc(void)
598 const int num_threads
= opt_threads
;
601 pthread_t test_threads
[num_threads
];
602 struct inc_test_data data
;
603 struct inc_thread_test_data thread_data
[num_threads
];
605 memset(&data
, 0, sizeof(data
));
606 for (i
= 0; i
< num_threads
; i
++) {
607 thread_data
[i
].reps
= opt_reps
;
608 if (opt_disable_mod
<= 0 || (i
% opt_disable_mod
))
609 thread_data
[i
].reg
= 1;
611 thread_data
[i
].reg
= 0;
612 thread_data
[i
].data
= &data
;
613 ret
= pthread_create(&test_threads
[i
], NULL
,
614 test_percpu_inc_thread
,
618 perror("pthread_create");
623 for (i
= 0; i
< num_threads
; i
++) {
624 ret
= pthread_join(test_threads
[i
], NULL
);
627 perror("pthread_join");
633 for (i
= 0; i
< CPU_SETSIZE
; i
++)
634 sum
+= data
.c
[i
].count
;
636 assert(sum
== (uint64_t)opt_reps
* num_threads
);
639 static void this_cpu_list_push(struct percpu_list
*list
,
640 struct percpu_list_node
*node
,
646 intptr_t *targetptr
, newval
, expect
;
649 cpu
= get_current_cpu_id();
650 /* Load list->c[cpu].head with single-copy atomicity. */
651 expect
= (intptr_t)RSEQ_READ_ONCE(list
->c
[cpu
].head
);
652 newval
= (intptr_t)node
;
653 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
654 node
->next
= (struct percpu_list_node
*)expect
;
655 ret
= rseq_load_cbne_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
656 targetptr
, expect
, newval
, cpu
);
657 if (rseq_likely(!ret
))
659 /* Retry if comparison fails or rseq aborts. */
666 * Unlike a traditional lock-less linked list; the availability of a
667 * rseq primitive allows us to implement pop without concerns over
670 static struct percpu_list_node
*this_cpu_list_pop(struct percpu_list
*list
,
673 struct percpu_list_node
*node
= NULL
;
677 struct percpu_list_node
*head
;
678 intptr_t *targetptr
, expectnot
, *load
;
682 cpu
= get_current_cpu_id();
683 targetptr
= (intptr_t *)&list
->c
[cpu
].head
;
684 expectnot
= (intptr_t)NULL
;
685 offset
= offsetof(struct percpu_list_node
, next
);
686 load
= (intptr_t *)&head
;
687 ret
= rseq_load_cbeq_store_add_load_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
688 targetptr
, expectnot
,
690 if (rseq_likely(!ret
)) {
696 /* Retry if rseq aborts. */
704 * __percpu_list_pop is not safe against concurrent accesses. Should
705 * only be used on lists that are not concurrently modified.
707 static struct percpu_list_node
*__percpu_list_pop(struct percpu_list
*list
, int cpu
)
709 struct percpu_list_node
*node
;
711 node
= list
->c
[cpu
].head
;
714 list
->c
[cpu
].head
= node
->next
;
718 static void *test_percpu_list_thread(void *arg
)
721 struct percpu_list
*list
= (struct percpu_list
*)arg
;
723 if (!opt_disable_rseq
&& rseq_register_current_thread())
727 for (i
= 0; i
< reps
; i
++) {
728 struct percpu_list_node
*node
;
730 node
= this_cpu_list_pop(list
, NULL
);
732 sched_yield(); /* encourage shuffling */
734 this_cpu_list_push(list
, node
, NULL
);
737 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
738 (int) rseq_gettid(), nr_abort
, signals_delivered
);
739 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
745 /* Simultaneous modification to a per-cpu linked list from many threads. */
746 static void test_percpu_list(void)
748 const int num_threads
= opt_threads
;
750 uint64_t sum
= 0, expected_sum
= 0;
751 struct percpu_list list
;
752 pthread_t test_threads
[num_threads
];
753 cpu_set_t allowed_cpus
;
755 memset(&list
, 0, sizeof(list
));
757 /* Generate list entries for every usable cpu. */
758 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
759 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
760 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
762 for (j
= 1; j
<= 100; j
++) {
763 struct percpu_list_node
*node
;
767 node
= (struct percpu_list_node
*) malloc(sizeof(*node
));
770 node
->next
= list
.c
[i
].head
;
771 list
.c
[i
].head
= node
;
775 for (i
= 0; i
< num_threads
; i
++) {
776 ret
= pthread_create(&test_threads
[i
], NULL
,
777 test_percpu_list_thread
, &list
);
780 perror("pthread_create");
785 for (i
= 0; i
< num_threads
; i
++) {
786 ret
= pthread_join(test_threads
[i
], NULL
);
789 perror("pthread_join");
794 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
795 struct percpu_list_node
*node
;
797 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
800 while ((node
= __percpu_list_pop(&list
, i
))) {
807 * All entries should now be accounted for (unless some external
808 * actor is interfering with our allowed affinity while this
811 assert(sum
== expected_sum
);
814 static bool this_cpu_buffer_push(struct percpu_buffer
*buffer
,
815 struct percpu_buffer_node
*node
,
822 intptr_t *targetptr_spec
, newval_spec
;
823 intptr_t *targetptr_final
, newval_final
;
827 cpu
= get_current_cpu_id();
828 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
829 if (offset
== buffer
->c
[cpu
].buflen
)
831 newval_spec
= (intptr_t)node
;
832 targetptr_spec
= (intptr_t *)&buffer
->c
[cpu
].array
[offset
];
833 newval_final
= offset
+ 1;
834 targetptr_final
= &buffer
->c
[cpu
].offset
;
835 ret
= rseq_load_cbne_store_store__ptr(opt_mo
, RSEQ_PERCPU
,
836 targetptr_final
, offset
, targetptr_spec
,
837 newval_spec
, newval_final
, cpu
);
838 if (rseq_likely(!ret
)) {
842 /* Retry if comparison fails or rseq aborts. */
849 static struct percpu_buffer_node
*this_cpu_buffer_pop(struct percpu_buffer
*buffer
,
852 struct percpu_buffer_node
*head
;
856 intptr_t *targetptr
, newval
;
860 cpu
= get_current_cpu_id();
861 /* Load offset with single-copy atomicity. */
862 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
867 head
= RSEQ_READ_ONCE(buffer
->c
[cpu
].array
[offset
- 1]);
869 targetptr
= (intptr_t *)&buffer
->c
[cpu
].offset
;
870 ret
= rseq_load_cbne_load_cbne_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
872 (intptr_t *)&buffer
->c
[cpu
].array
[offset
- 1],
873 (intptr_t)head
, newval
, cpu
);
874 if (rseq_likely(!ret
))
876 /* Retry if comparison fails or rseq aborts. */
884 * __percpu_buffer_pop is not safe against concurrent accesses. Should
885 * only be used on buffers that are not concurrently modified.
887 static struct percpu_buffer_node
*__percpu_buffer_pop(struct percpu_buffer
*buffer
,
890 struct percpu_buffer_node
*head
;
893 offset
= buffer
->c
[cpu
].offset
;
896 head
= buffer
->c
[cpu
].array
[offset
- 1];
897 buffer
->c
[cpu
].offset
= offset
- 1;
901 static void *test_percpu_buffer_thread(void *arg
)
904 struct percpu_buffer
*buffer
= (struct percpu_buffer
*)arg
;
906 if (!opt_disable_rseq
&& rseq_register_current_thread())
910 for (i
= 0; i
< reps
; i
++) {
911 struct percpu_buffer_node
*node
;
913 node
= this_cpu_buffer_pop(buffer
, NULL
);
915 sched_yield(); /* encourage shuffling */
917 if (!this_cpu_buffer_push(buffer
, node
, NULL
)) {
918 /* Should increase buffer size. */
924 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
925 (int) rseq_gettid(), nr_abort
, signals_delivered
);
926 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
932 /* Simultaneous modification to a per-cpu buffer from many threads. */
933 static void test_percpu_buffer(void)
935 const int num_threads
= opt_threads
;
937 uint64_t sum
= 0, expected_sum
= 0;
938 struct percpu_buffer buffer
;
939 pthread_t test_threads
[num_threads
];
940 cpu_set_t allowed_cpus
;
942 memset(&buffer
, 0, sizeof(buffer
));
944 /* Generate list entries for every usable cpu. */
945 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
946 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
947 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
949 /* Worse-case is every item in same CPU. */
951 (struct percpu_buffer_node
**)
952 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
953 BUFFER_ITEM_PER_CPU
);
954 assert(buffer
.c
[i
].array
);
955 buffer
.c
[i
].buflen
= CPU_SETSIZE
* BUFFER_ITEM_PER_CPU
;
956 for (j
= 1; j
<= BUFFER_ITEM_PER_CPU
; j
++) {
957 struct percpu_buffer_node
*node
;
962 * We could theoretically put the word-sized
963 * "data" directly in the buffer. However, we
964 * want to model objects that would not fit
965 * within a single word, so allocate an object
968 node
= (struct percpu_buffer_node
*) malloc(sizeof(*node
));
971 buffer
.c
[i
].array
[j
- 1] = node
;
972 buffer
.c
[i
].offset
++;
976 for (i
= 0; i
< num_threads
; i
++) {
977 ret
= pthread_create(&test_threads
[i
], NULL
,
978 test_percpu_buffer_thread
, &buffer
);
981 perror("pthread_create");
986 for (i
= 0; i
< num_threads
; i
++) {
987 ret
= pthread_join(test_threads
[i
], NULL
);
990 perror("pthread_join");
995 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
996 struct percpu_buffer_node
*node
;
998 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
1001 while ((node
= __percpu_buffer_pop(&buffer
, i
))) {
1005 free(buffer
.c
[i
].array
);
1009 * All entries should now be accounted for (unless some external
1010 * actor is interfering with our allowed affinity while this
1013 assert(sum
== expected_sum
);
1016 static bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer
*buffer
,
1017 struct percpu_memcpy_buffer_node item
,
1020 bool result
= false;
1024 intptr_t *targetptr_final
, newval_final
, offset
;
1025 char *destptr
, *srcptr
;
1029 cpu
= get_current_cpu_id();
1030 /* Load offset with single-copy atomicity. */
1031 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
1032 if (offset
== buffer
->c
[cpu
].buflen
)
1034 destptr
= (char *)&buffer
->c
[cpu
].array
[offset
];
1035 srcptr
= (char *)&item
;
1036 /* copylen must be <= 4kB. */
1037 copylen
= sizeof(item
);
1038 newval_final
= offset
+ 1;
1039 targetptr_final
= &buffer
->c
[cpu
].offset
;
1040 ret
= rseq_load_cbne_memcpy_store__ptr(
1041 opt_mo
, RSEQ_PERCPU
,
1042 targetptr_final
, offset
,
1043 destptr
, srcptr
, copylen
,
1045 if (rseq_likely(!ret
)) {
1049 /* Retry if comparison fails or rseq aborts. */
1056 static bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
1057 struct percpu_memcpy_buffer_node
*item
,
1060 bool result
= false;
1064 intptr_t *targetptr_final
, newval_final
, offset
;
1065 char *destptr
, *srcptr
;
1069 cpu
= get_current_cpu_id();
1070 /* Load offset with single-copy atomicity. */
1071 offset
= RSEQ_READ_ONCE(buffer
->c
[cpu
].offset
);
1074 destptr
= (char *)item
;
1075 srcptr
= (char *)&buffer
->c
[cpu
].array
[offset
- 1];
1076 /* copylen must be <= 4kB. */
1077 copylen
= sizeof(*item
);
1078 newval_final
= offset
- 1;
1079 targetptr_final
= &buffer
->c
[cpu
].offset
;
1080 ret
= rseq_load_cbne_memcpy_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
1081 targetptr_final
, offset
, destptr
, srcptr
, copylen
,
1083 if (rseq_likely(!ret
)) {
1087 /* Retry if comparison fails or rseq aborts. */
1095 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
1096 * only be used on buffers that are not concurrently modified.
1098 static bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer
*buffer
,
1099 struct percpu_memcpy_buffer_node
*item
,
1104 offset
= buffer
->c
[cpu
].offset
;
1107 memcpy(item
, &buffer
->c
[cpu
].array
[offset
- 1], sizeof(*item
));
1108 buffer
->c
[cpu
].offset
= offset
- 1;
1112 static void *test_percpu_memcpy_buffer_thread(void *arg
)
1115 struct percpu_memcpy_buffer
*buffer
= (struct percpu_memcpy_buffer
*)arg
;
1117 if (!opt_disable_rseq
&& rseq_register_current_thread())
1121 for (i
= 0; i
< reps
; i
++) {
1122 struct percpu_memcpy_buffer_node item
;
1125 result
= this_cpu_memcpy_buffer_pop(buffer
, &item
, NULL
);
1127 sched_yield(); /* encourage shuffling */
1129 if (!this_cpu_memcpy_buffer_push(buffer
, item
, NULL
)) {
1130 /* Should increase buffer size. */
1136 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
1137 (int) rseq_gettid(), nr_abort
, signals_delivered
);
1138 if (!opt_disable_rseq
&& rseq_unregister_current_thread())
1144 /* Simultaneous modification to a per-cpu buffer from many threads. */
1145 static void test_percpu_memcpy_buffer(void)
1147 const int num_threads
= opt_threads
;
1149 uint64_t sum
= 0, expected_sum
= 0;
1150 struct percpu_memcpy_buffer buffer
;
1151 pthread_t test_threads
[num_threads
];
1152 cpu_set_t allowed_cpus
;
1154 memset(&buffer
, 0, sizeof(buffer
));
1156 /* Generate list entries for every usable cpu. */
1157 sched_getaffinity(0, sizeof(allowed_cpus
), &allowed_cpus
);
1158 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1159 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
1161 /* Worse-case is every item in same CPU. */
1163 (struct percpu_memcpy_buffer_node
*)
1164 malloc(sizeof(*buffer
.c
[i
].array
) * CPU_SETSIZE
*
1165 MEMCPY_BUFFER_ITEM_PER_CPU
);
1166 assert(buffer
.c
[i
].array
);
1167 buffer
.c
[i
].buflen
= CPU_SETSIZE
* MEMCPY_BUFFER_ITEM_PER_CPU
;
1168 for (j
= 1; j
<= MEMCPY_BUFFER_ITEM_PER_CPU
; j
++) {
1169 expected_sum
+= 2 * j
+ 1;
1172 * We could theoretically put the word-sized
1173 * "data" directly in the buffer. However, we
1174 * want to model objects that would not fit
1175 * within a single word, so allocate an object
1178 buffer
.c
[i
].array
[j
- 1].data1
= j
;
1179 buffer
.c
[i
].array
[j
- 1].data2
= j
+ 1;
1180 buffer
.c
[i
].offset
++;
1184 for (i
= 0; i
< num_threads
; i
++) {
1185 ret
= pthread_create(&test_threads
[i
], NULL
,
1186 test_percpu_memcpy_buffer_thread
,
1190 perror("pthread_create");
1195 for (i
= 0; i
< num_threads
; i
++) {
1196 ret
= pthread_join(test_threads
[i
], NULL
);
1199 perror("pthread_join");
1204 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1205 struct percpu_memcpy_buffer_node item
;
1207 if (rseq_use_cpu_index() && !CPU_ISSET(i
, &allowed_cpus
))
1210 while (__percpu_memcpy_buffer_pop(&buffer
, &item
, i
)) {
1214 free(buffer
.c
[i
].array
);
1218 * All entries should now be accounted for (unless some external
1219 * actor is interfering with our allowed affinity while this
1222 assert(sum
== expected_sum
);
1226 static void test_signal_interrupt_handler(__attribute__ ((unused
)) int signo
)
1228 signals_delivered
++;
1231 static int set_signal_handler(void)
1234 struct sigaction sa
;
1237 ret
= sigemptyset(&sigset
);
1239 perror("sigemptyset");
1243 sa
.sa_handler
= test_signal_interrupt_handler
;
1244 sa
.sa_mask
= sigset
;
1246 ret
= sigaction(SIGUSR1
, &sa
, NULL
);
1248 perror("sigaction");
1252 printf_verbose("Signal handler set for SIGUSR1\n");
1258 bool membarrier_private_expedited_rseq_available(void)
1260 int status
= sys_membarrier(MEMBARRIER_CMD_QUERY
, 0, 0);
1263 perror("membarrier");
1266 if (!(status
& MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
))
1271 /* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
1272 #ifdef TEST_MEMBARRIER
1273 struct test_membarrier_thread_args
{
1275 intptr_t percpu_list_ptr
;
1278 /* Worker threads modify data in their "active" percpu lists. */
1280 void *test_membarrier_worker_thread(void *arg
)
1282 struct test_membarrier_thread_args
*args
=
1283 (struct test_membarrier_thread_args
*)arg
;
1284 const int iters
= opt_reps
;
1287 if (rseq_register_current_thread()) {
1288 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1289 errno
, strerror(errno
));
1293 /* Wait for initialization. */
1294 while (!rseq_smp_load_acquire(&args
->percpu_list_ptr
)) { }
1296 for (i
= 0; i
< iters
; ++i
) {
1300 int cpu
= get_current_cpu_id();
1302 ret
= rseq_load_add_load_add_store__ptr(RSEQ_MO_RELAXED
, RSEQ_PERCPU
,
1303 &args
->percpu_list_ptr
,
1304 sizeof(struct percpu_list_entry
) * cpu
, 1, cpu
);
1305 } while (rseq_unlikely(ret
));
1308 if (rseq_unregister_current_thread()) {
1309 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1310 errno
, strerror(errno
));
1317 void test_membarrier_init_percpu_list(struct percpu_list
*list
)
1321 memset(list
, 0, sizeof(*list
));
1322 for (i
= 0; i
< CPU_SETSIZE
; i
++) {
1323 struct percpu_list_node
*node
;
1325 node
= (struct percpu_list_node
*) malloc(sizeof(*node
));
1329 list
->c
[i
].head
= node
;
1334 void test_membarrier_free_percpu_list(struct percpu_list
*list
)
1338 for (i
= 0; i
< CPU_SETSIZE
; i
++)
1339 free(list
->c
[i
].head
);
1343 * The manager thread swaps per-cpu lists that worker threads see,
1344 * and validates that there are no unexpected modifications.
1347 void *test_membarrier_manager_thread(void *arg
)
1349 struct test_membarrier_thread_args
*args
=
1350 (struct test_membarrier_thread_args
*)arg
;
1351 struct percpu_list list_a
, list_b
;
1352 intptr_t expect_a
= 0, expect_b
= 0;
1353 int cpu_a
= 0, cpu_b
= 0;
1355 if (rseq_register_current_thread()) {
1356 fprintf(stderr
, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1357 errno
, strerror(errno
));
1362 test_membarrier_init_percpu_list(&list_a
);
1363 test_membarrier_init_percpu_list(&list_b
);
1365 /* Initialize lists before publishing them. */
1368 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_a
);
1370 while (!RSEQ_READ_ONCE(args
->stop
)) {
1371 /* list_a is "active". */
1372 cpu_a
= rand() % CPU_SETSIZE
;
1374 * As list_b is "inactive", we should never see changes
1377 if (expect_b
!= RSEQ_READ_ONCE(list_b
.c
[cpu_b
].head
->data
)) {
1378 fprintf(stderr
, "Membarrier test failed\n");
1382 /* Make list_b "active". */
1383 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_b
);
1384 if (rseq_membarrier_expedited(cpu_a
) &&
1385 errno
!= ENXIO
/* missing CPU */) {
1386 perror("sys_membarrier");
1390 * Cpu A should now only modify list_b, so the values
1391 * in list_a should be stable.
1393 expect_a
= RSEQ_READ_ONCE(list_a
.c
[cpu_a
].head
->data
);
1395 cpu_b
= rand() % CPU_SETSIZE
;
1397 * As list_a is "inactive", we should never see changes
1400 if (expect_a
!= RSEQ_READ_ONCE(list_a
.c
[cpu_a
].head
->data
)) {
1401 fprintf(stderr
, "Membarrier test failed\n");
1405 /* Make list_a "active". */
1406 RSEQ_WRITE_ONCE(args
->percpu_list_ptr
, (intptr_t)&list_a
);
1407 if (rseq_membarrier_expedited(cpu_b
) &&
1408 errno
!= ENXIO
/* missing CPU */) {
1409 perror("sys_membarrier");
1412 /* Remember a value from list_b. */
1413 expect_b
= RSEQ_READ_ONCE(list_b
.c
[cpu_b
].head
->data
);
1416 test_membarrier_free_percpu_list(&list_a
);
1417 test_membarrier_free_percpu_list(&list_b
);
1419 if (rseq_unregister_current_thread()) {
1420 fprintf(stderr
, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1421 errno
, strerror(errno
));
1428 void test_membarrier(void)
1430 const int num_threads
= opt_threads
;
1431 struct test_membarrier_thread_args thread_args
;
1432 pthread_t worker_threads
[num_threads
];
1433 pthread_t manager_thread
;
1436 if (!membarrier_private_expedited_rseq_available()) {
1437 fprintf(stderr
, "Membarrier private expedited rseq not available. "
1438 "Skipping membarrier test.\n");
1441 if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
, 0, 0)) {
1442 perror("sys_membarrier");
1446 thread_args
.stop
= 0;
1447 thread_args
.percpu_list_ptr
= 0;
1448 ret
= pthread_create(&manager_thread
, NULL
,
1449 test_membarrier_manager_thread
, &thread_args
);
1452 perror("pthread_create");
1456 for (i
= 0; i
< num_threads
; i
++) {
1457 ret
= pthread_create(&worker_threads
[i
], NULL
,
1458 test_membarrier_worker_thread
, &thread_args
);
1461 perror("pthread_create");
1467 for (i
= 0; i
< num_threads
; i
++) {
1468 ret
= pthread_join(worker_threads
[i
], NULL
);
1471 perror("pthread_join");
1476 RSEQ_WRITE_ONCE(thread_args
.stop
, 1);
1477 ret
= pthread_join(manager_thread
, NULL
);
1480 perror("pthread_join");
1484 #else /* TEST_MEMBARRIER */
1486 void test_membarrier(void)
1488 if (!membarrier_private_expedited_rseq_available()) {
1489 fprintf(stderr
, "Membarrier private expedited rseq not available. "
1490 "Skipping membarrier test.\n");
1493 fprintf(stderr
, "rseq_load_add_load_add_store__ptr is not implemented on this architecture. "
1494 "Skipping membarrier test.\n");
1498 static void show_usage(char **argv
)
1500 printf("Usage : %s <OPTIONS>\n",
1502 printf("OPTIONS:\n");
1503 printf(" [-1 loops] Number of loops for delay injection 1\n");
1504 printf(" [-2 loops] Number of loops for delay injection 2\n");
1505 printf(" [-3 loops] Number of loops for delay injection 3\n");
1506 printf(" [-4 loops] Number of loops for delay injection 4\n");
1507 printf(" [-5 loops] Number of loops for delay injection 5\n");
1508 printf(" [-6 loops] Number of loops for delay injection 6\n");
1509 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
1510 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
1511 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
1512 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
1513 printf(" [-y] Yield\n");
1514 printf(" [-k] Kill thread with signal\n");
1515 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
1516 printf(" [-t N] Number of threads (default 200)\n");
1517 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
1518 printf(" [-d] Disable rseq system call (no initialization)\n");
1519 printf(" [-D M] Disable rseq for each M threads\n");
1520 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
1521 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
1522 printf(" [-c] Check if the rseq syscall is available.\n");
1523 printf(" [-v] Verbose output.\n");
1524 printf(" [-h] Show this help.\n");
1528 int main(int argc
, char **argv
)
1532 for (i
= 1; i
< argc
; i
++) {
1533 if (argv
[i
][0] != '-')
1535 switch (argv
[i
][1]) {
1549 loop_cnt
[argv
[i
][1] - '0'] = atol(argv
[i
+ 1]);
1557 opt_modulo
= atol(argv
[i
+ 1]);
1558 if (opt_modulo
< 0) {
1569 opt_sleep
= atol(argv
[i
+ 1]);
1570 if (opt_sleep
< 0) {
1583 opt_disable_rseq
= 1;
1590 opt_disable_mod
= atol(argv
[i
+ 1]);
1591 if (opt_disable_mod
< 0) {
1602 opt_threads
= atol(argv
[i
+ 1]);
1603 if (opt_threads
< 0) {
1614 opt_reps
= atoll(argv
[i
+ 1]);
1629 opt_test
= *argv
[i
+ 1];
1648 opt_mo
= RSEQ_MO_RELEASE
;
1651 if (rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL
)) {
1652 printf_verbose("The rseq syscall is available.\n");
1655 printf_verbose("The rseq syscall is unavailable.\n");
1664 loop_cnt_1
= loop_cnt
[1];
1665 loop_cnt_2
= loop_cnt
[2];
1666 loop_cnt_3
= loop_cnt
[3];
1667 loop_cnt_4
= loop_cnt
[4];
1668 loop_cnt_5
= loop_cnt
[5];
1669 loop_cnt_6
= loop_cnt
[6];
1671 if (set_signal_handler())
1674 if (!opt_disable_rseq
&& rseq_register_current_thread())
1676 if (!opt_disable_rseq
&& !rseq_validate_cpu_id()) {
1677 printf_verbose("The rseq cpu id getter is unavailable\n");
1682 printf_verbose("spinlock\n");
1683 test_percpu_spinlock();
1686 printf_verbose("linked list\n");
1690 printf_verbose("buffer\n");
1691 test_percpu_buffer();
1694 printf_verbose("memcpy buffer\n");
1695 test_percpu_memcpy_buffer();
1698 printf_verbose("counter increment\n");
1702 printf_verbose("membarrier\n");
1706 if (!opt_disable_rseq
&& rseq_unregister_current_thread())