aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>2018-06-02 08:44:07 -0400
committerThomas Gleixner <tglx@linutronix.de>2018-06-06 05:58:35 -0400
commitc960e9909d33b9780534729afc42493328ca3e18 (patch)
treed4c7ee1191a2ee52bf7ba956b3da82b7f1d15c98
parent01a5ec4217599fd78ba76fa7199a350c5fb4650f (diff)
rseq/selftests: Provide parametrized tests
"param_test" is a parametrizable restartable sequences test. See the "--help" output for usage. "param_test_benchmark" is the same as "param_test", but it removes testing book-keeping code to allow accurate benchmarks. "param_test_compare_twice" is the same as "param_test", but it performs each comparison within rseq critical section twice, thus validating invariants. If any of the second comparisons fails, an error message is printed and the test aborts. Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Joel Fernandes <joelaf@google.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Dave Watson <davejwatson@fb.com> Cc: Will Deacon <will.deacon@arm.com> Cc: Shuah Khan <shuahkh@osg.samsung.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: linux-kselftest@vger.kernel.org Cc: "H . Peter Anvin" <hpa@zytor.com> Cc: Chris Lameter <cl@linux.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: Andrew Hunter <ahh@google.com> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Cc: "Paul E . McKenney" <paulmck@linux.vnet.ibm.com> Cc: Paul Turner <pjt@google.com> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Ben Maurer <bmaurer@fb.com> Cc: linux-api@vger.kernel.org Cc: Andy Lutomirski <luto@amacapital.net> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Link: https://lkml.kernel.org/r/20180602124408.8430-16-mathieu.desnoyers@efficios.com
-rw-r--r--tools/testing/selftests/rseq/param_test.c1260
1 files changed, 1260 insertions, 0 deletions
diff --git a/tools/testing/selftests/rseq/param_test.c b/tools/testing/selftests/rseq/param_test.c
new file mode 100644
index 000000000000..6a9f602a8718
--- /dev/null
+++ b/tools/testing/selftests/rseq/param_test.c
@@ -0,0 +1,1260 @@
1// SPDX-License-Identifier: LGPL-2.1
2#define _GNU_SOURCE
3#include <assert.h>
4#include <pthread.h>
5#include <sched.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <syscall.h>
11#include <unistd.h>
12#include <poll.h>
13#include <sys/types.h>
14#include <signal.h>
15#include <errno.h>
16#include <stddef.h>
17
18static inline pid_t gettid(void)
19{
20 return syscall(__NR_gettid);
21}
22
23#define NR_INJECT 9
24static int loop_cnt[NR_INJECT + 1];
25
26static int loop_cnt_1 asm("asm_loop_cnt_1") __attribute__((used));
27static int loop_cnt_2 asm("asm_loop_cnt_2") __attribute__((used));
28static int loop_cnt_3 asm("asm_loop_cnt_3") __attribute__((used));
29static int loop_cnt_4 asm("asm_loop_cnt_4") __attribute__((used));
30static int loop_cnt_5 asm("asm_loop_cnt_5") __attribute__((used));
31static int loop_cnt_6 asm("asm_loop_cnt_6") __attribute__((used));
32
33static int opt_modulo, verbose;
34
35static int opt_yield, opt_signal, opt_sleep,
36 opt_disable_rseq, opt_threads = 200,
37 opt_disable_mod = 0, opt_test = 's', opt_mb = 0;
38
39#ifndef RSEQ_SKIP_FASTPATH
40static long long opt_reps = 5000;
41#else
42static long long opt_reps = 100;
43#endif
44
45static __thread __attribute__((tls_model("initial-exec")))
46unsigned int signals_delivered;
47
48#ifndef BENCHMARK
49
50static __thread __attribute__((tls_model("initial-exec"), unused))
51unsigned int yield_mod_cnt, nr_abort;
52
53#define printf_verbose(fmt, ...) \
54 do { \
55 if (verbose) \
56 printf(fmt, ## __VA_ARGS__); \
57 } while (0)
58
59#if defined(__x86_64__) || defined(__i386__)
60
61#define INJECT_ASM_REG "eax"
62
63#define RSEQ_INJECT_CLOBBER \
64 , INJECT_ASM_REG
65
66#ifdef __i386__
67
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" \
71 "jz 333f\n\t" \
72 "222:\n\t" \
73 "dec %%" INJECT_ASM_REG "\n\t" \
74 "jnz 222b\n\t" \
75 "333:\n\t"
76
77#elif defined(__x86_64__)
78
79#define RSEQ_INJECT_ASM(n) \
80 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG "\n\t" \
81 "mov (%%" INJECT_ASM_REG "), %%" INJECT_ASM_REG "\n\t" \
82 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
83 "jz 333f\n\t" \
84 "222:\n\t" \
85 "dec %%" INJECT_ASM_REG "\n\t" \
86 "jnz 222b\n\t" \
87 "333:\n\t"
88
89#else
90#error "Unsupported architecture"
91#endif
92
93#elif defined(__ARMEL__)
94
95#define RSEQ_INJECT_INPUT \
96 , [loop_cnt_1]"m"(loop_cnt[1]) \
97 , [loop_cnt_2]"m"(loop_cnt[2]) \
98 , [loop_cnt_3]"m"(loop_cnt[3]) \
99 , [loop_cnt_4]"m"(loop_cnt[4]) \
100 , [loop_cnt_5]"m"(loop_cnt[5]) \
101 , [loop_cnt_6]"m"(loop_cnt[6])
102
103#define INJECT_ASM_REG "r4"
104
105#define RSEQ_INJECT_CLOBBER \
106 , INJECT_ASM_REG
107
108#define RSEQ_INJECT_ASM(n) \
109 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
110 "cmp " INJECT_ASM_REG ", #0\n\t" \
111 "beq 333f\n\t" \
112 "222:\n\t" \
113 "subs " INJECT_ASM_REG ", #1\n\t" \
114 "bne 222b\n\t" \
115 "333:\n\t"
116
117#elif __PPC__
118
119#define RSEQ_INJECT_INPUT \
120 , [loop_cnt_1]"m"(loop_cnt[1]) \
121 , [loop_cnt_2]"m"(loop_cnt[2]) \
122 , [loop_cnt_3]"m"(loop_cnt[3]) \
123 , [loop_cnt_4]"m"(loop_cnt[4]) \
124 , [loop_cnt_5]"m"(loop_cnt[5]) \
125 , [loop_cnt_6]"m"(loop_cnt[6])
126
127#define INJECT_ASM_REG "r18"
128
129#define RSEQ_INJECT_CLOBBER \
130 , INJECT_ASM_REG
131
132#define RSEQ_INJECT_ASM(n) \
133 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
134 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
135 "beq 333f\n\t" \
136 "222:\n\t" \
137 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
138 "bne 222b\n\t" \
139 "333:\n\t"
140#else
141#error unsupported target
142#endif
143
144#define RSEQ_INJECT_FAILED \
145 nr_abort++;
146
147#define RSEQ_INJECT_C(n) \
148{ \
149 int loc_i, loc_nr_loops = loop_cnt[n]; \
150 \
151 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
152 rseq_barrier(); \
153 } \
154 if (loc_nr_loops == -1 && opt_modulo) { \
155 if (yield_mod_cnt == opt_modulo - 1) { \
156 if (opt_sleep > 0) \
157 poll(NULL, 0, opt_sleep); \
158 if (opt_yield) \
159 sched_yield(); \
160 if (opt_signal) \
161 raise(SIGUSR1); \
162 yield_mod_cnt = 0; \
163 } else { \
164 yield_mod_cnt++; \
165 } \
166 } \
167}
168
169#else
170
171#define printf_verbose(fmt, ...)
172
173#endif /* BENCHMARK */
174
175#include "rseq.h"
176
177struct percpu_lock_entry {
178 intptr_t v;
179} __attribute__((aligned(128)));
180
181struct percpu_lock {
182 struct percpu_lock_entry c[CPU_SETSIZE];
183};
184
185struct test_data_entry {
186 intptr_t count;
187} __attribute__((aligned(128)));
188
189struct spinlock_test_data {
190 struct percpu_lock lock;
191 struct test_data_entry c[CPU_SETSIZE];
192};
193
194struct spinlock_thread_test_data {
195 struct spinlock_test_data *data;
196 long long reps;
197 int reg;
198};
199
200struct inc_test_data {
201 struct test_data_entry c[CPU_SETSIZE];
202};
203
204struct inc_thread_test_data {
205 struct inc_test_data *data;
206 long long reps;
207 int reg;
208};
209
210struct percpu_list_node {
211 intptr_t data;
212 struct percpu_list_node *next;
213};
214
215struct percpu_list_entry {
216 struct percpu_list_node *head;
217} __attribute__((aligned(128)));
218
219struct percpu_list {
220 struct percpu_list_entry c[CPU_SETSIZE];
221};
222
223#define BUFFER_ITEM_PER_CPU 100
224
225struct percpu_buffer_node {
226 intptr_t data;
227};
228
229struct percpu_buffer_entry {
230 intptr_t offset;
231 intptr_t buflen;
232 struct percpu_buffer_node **array;
233} __attribute__((aligned(128)));
234
235struct percpu_buffer {
236 struct percpu_buffer_entry c[CPU_SETSIZE];
237};
238
239#define MEMCPY_BUFFER_ITEM_PER_CPU 100
240
241struct percpu_memcpy_buffer_node {
242 intptr_t data1;
243 uint64_t data2;
244};
245
246struct percpu_memcpy_buffer_entry {
247 intptr_t offset;
248 intptr_t buflen;
249 struct percpu_memcpy_buffer_node *array;
250} __attribute__((aligned(128)));
251
252struct percpu_memcpy_buffer {
253 struct percpu_memcpy_buffer_entry c[CPU_SETSIZE];
254};
255
256/* A simple percpu spinlock. Grabs lock on current cpu. */
257static int rseq_this_cpu_lock(struct percpu_lock *lock)
258{
259 int cpu;
260
261 for (;;) {
262 int ret;
263
264 cpu = rseq_cpu_start();
265 ret = rseq_cmpeqv_storev(&lock->c[cpu].v,
266 0, 1, cpu);
267 if (rseq_likely(!ret))
268 break;
269 /* Retry if comparison fails or rseq aborts. */
270 }
271 /*
272 * Acquire semantic when taking lock after control dependency.
273 * Matches rseq_smp_store_release().
274 */
275 rseq_smp_acquire__after_ctrl_dep();
276 return cpu;
277}
278
279static void rseq_percpu_unlock(struct percpu_lock *lock, int cpu)
280{
281 assert(lock->c[cpu].v == 1);
282 /*
283 * Release lock, with release semantic. Matches
284 * rseq_smp_acquire__after_ctrl_dep().
285 */
286 rseq_smp_store_release(&lock->c[cpu].v, 0);
287}
288
289void *test_percpu_spinlock_thread(void *arg)
290{
291 struct spinlock_thread_test_data *thread_data = arg;
292 struct spinlock_test_data *data = thread_data->data;
293 long long i, reps;
294
295 if (!opt_disable_rseq && thread_data->reg &&
296 rseq_register_current_thread())
297 abort();
298 reps = thread_data->reps;
299 for (i = 0; i < reps; i++) {
300 int cpu = rseq_cpu_start();
301
302 cpu = rseq_this_cpu_lock(&data->lock);
303 data->c[cpu].count++;
304 rseq_percpu_unlock(&data->lock, cpu);
305#ifndef BENCHMARK
306 if (i != 0 && !(i % (reps / 10)))
307 printf_verbose("tid %d: count %lld\n", (int) gettid(), i);
308#endif
309 }
310 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
311 (int) gettid(), nr_abort, signals_delivered);
312 if (!opt_disable_rseq && thread_data->reg &&
313 rseq_unregister_current_thread())
314 abort();
315 return NULL;
316}
317
318/*
319 * A simple test which implements a sharded counter using a per-cpu
320 * lock. Obviously real applications might prefer to simply use a
321 * per-cpu increment; however, this is reasonable for a test and the
322 * lock can be extended to synchronize more complicated operations.
323 */
324void test_percpu_spinlock(void)
325{
326 const int num_threads = opt_threads;
327 int i, ret;
328 uint64_t sum;
329 pthread_t test_threads[num_threads];
330 struct spinlock_test_data data;
331 struct spinlock_thread_test_data thread_data[num_threads];
332
333 memset(&data, 0, sizeof(data));
334 for (i = 0; i < num_threads; i++) {
335 thread_data[i].reps = opt_reps;
336 if (opt_disable_mod <= 0 || (i % opt_disable_mod))
337 thread_data[i].reg = 1;
338 else
339 thread_data[i].reg = 0;
340 thread_data[i].data = &data;
341 ret = pthread_create(&test_threads[i], NULL,
342 test_percpu_spinlock_thread,
343 &thread_data[i]);
344 if (ret) {
345 errno = ret;
346 perror("pthread_create");
347 abort();
348 }
349 }
350
351 for (i = 0; i < num_threads; i++) {
352 ret = pthread_join(test_threads[i], NULL);
353 if (ret) {
354 errno = ret;
355 perror("pthread_join");
356 abort();
357 }
358 }
359
360 sum = 0;
361 for (i = 0; i < CPU_SETSIZE; i++)
362 sum += data.c[i].count;
363
364 assert(sum == (uint64_t)opt_reps * num_threads);
365}
366
367void *test_percpu_inc_thread(void *arg)
368{
369 struct inc_thread_test_data *thread_data = arg;
370 struct inc_test_data *data = thread_data->data;
371 long long i, reps;
372
373 if (!opt_disable_rseq && thread_data->reg &&
374 rseq_register_current_thread())
375 abort();
376 reps = thread_data->reps;
377 for (i = 0; i < reps; i++) {
378 int ret;
379
380 do {
381 int cpu;
382
383 cpu = rseq_cpu_start();
384 ret = rseq_addv(&data->c[cpu].count, 1, cpu);
385 } while (rseq_unlikely(ret));
386#ifndef BENCHMARK
387 if (i != 0 && !(i % (reps / 10)))
388 printf_verbose("tid %d: count %lld\n", (int) gettid(), i);
389#endif
390 }
391 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
392 (int) gettid(), nr_abort, signals_delivered);
393 if (!opt_disable_rseq && thread_data->reg &&
394 rseq_unregister_current_thread())
395 abort();
396 return NULL;
397}
398
399void test_percpu_inc(void)
400{
401 const int num_threads = opt_threads;
402 int i, ret;
403 uint64_t sum;
404 pthread_t test_threads[num_threads];
405 struct inc_test_data data;
406 struct inc_thread_test_data thread_data[num_threads];
407
408 memset(&data, 0, sizeof(data));
409 for (i = 0; i < num_threads; i++) {
410 thread_data[i].reps = opt_reps;
411 if (opt_disable_mod <= 0 || (i % opt_disable_mod))
412 thread_data[i].reg = 1;
413 else
414 thread_data[i].reg = 0;
415 thread_data[i].data = &data;
416 ret = pthread_create(&test_threads[i], NULL,
417 test_percpu_inc_thread,
418 &thread_data[i]);
419 if (ret) {
420 errno = ret;
421 perror("pthread_create");
422 abort();
423 }
424 }
425
426 for (i = 0; i < num_threads; i++) {
427 ret = pthread_join(test_threads[i], NULL);
428 if (ret) {
429 errno = ret;
430 perror("pthread_join");
431 abort();
432 }
433 }
434
435 sum = 0;
436 for (i = 0; i < CPU_SETSIZE; i++)
437 sum += data.c[i].count;
438
439 assert(sum == (uint64_t)opt_reps * num_threads);
440}
441
442void this_cpu_list_push(struct percpu_list *list,
443 struct percpu_list_node *node,
444 int *_cpu)
445{
446 int cpu;
447
448 for (;;) {
449 intptr_t *targetptr, newval, expect;
450 int ret;
451
452 cpu = rseq_cpu_start();
453 /* Load list->c[cpu].head with single-copy atomicity. */
454 expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head);
455 newval = (intptr_t)node;
456 targetptr = (intptr_t *)&list->c[cpu].head;
457 node->next = (struct percpu_list_node *)expect;
458 ret = rseq_cmpeqv_storev(targetptr, expect, newval, cpu);
459 if (rseq_likely(!ret))
460 break;
461 /* Retry if comparison fails or rseq aborts. */
462 }
463 if (_cpu)
464 *_cpu = cpu;
465}
466
467/*
468 * Unlike a traditional lock-less linked list; the availability of a
469 * rseq primitive allows us to implement pop without concerns over
470 * ABA-type races.
471 */
472struct percpu_list_node *this_cpu_list_pop(struct percpu_list *list,
473 int *_cpu)
474{
475 struct percpu_list_node *node = NULL;
476 int cpu;
477
478 for (;;) {
479 struct percpu_list_node *head;
480 intptr_t *targetptr, expectnot, *load;
481 off_t offset;
482 int ret;
483
484 cpu = rseq_cpu_start();
485 targetptr = (intptr_t *)&list->c[cpu].head;
486 expectnot = (intptr_t)NULL;
487 offset = offsetof(struct percpu_list_node, next);
488 load = (intptr_t *)&head;
489 ret = rseq_cmpnev_storeoffp_load(targetptr, expectnot,
490 offset, load, cpu);
491 if (rseq_likely(!ret)) {
492 node = head;
493 break;
494 }
495 if (ret > 0)
496 break;
497 /* Retry if rseq aborts. */
498 }
499 if (_cpu)
500 *_cpu = cpu;
501 return node;
502}
503
504/*
505 * __percpu_list_pop is not safe against concurrent accesses. Should
506 * only be used on lists that are not concurrently modified.
507 */
508struct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu)
509{
510 struct percpu_list_node *node;
511
512 node = list->c[cpu].head;
513 if (!node)
514 return NULL;
515 list->c[cpu].head = node->next;
516 return node;
517}
518
519void *test_percpu_list_thread(void *arg)
520{
521 long long i, reps;
522 struct percpu_list *list = (struct percpu_list *)arg;
523
524 if (!opt_disable_rseq && rseq_register_current_thread())
525 abort();
526
527 reps = opt_reps;
528 for (i = 0; i < reps; i++) {
529 struct percpu_list_node *node;
530
531 node = this_cpu_list_pop(list, NULL);
532 if (opt_yield)
533 sched_yield(); /* encourage shuffling */
534 if (node)
535 this_cpu_list_push(list, node, NULL);
536 }
537
538 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
539 (int) gettid(), nr_abort, signals_delivered);
540 if (!opt_disable_rseq && rseq_unregister_current_thread())
541 abort();
542
543 return NULL;
544}
545
546/* Simultaneous modification to a per-cpu linked list from many threads. */
547void test_percpu_list(void)
548{
549 const int num_threads = opt_threads;
550 int i, j, ret;
551 uint64_t sum = 0, expected_sum = 0;
552 struct percpu_list list;
553 pthread_t test_threads[num_threads];
554 cpu_set_t allowed_cpus;
555
556 memset(&list, 0, sizeof(list));
557
558 /* Generate list entries for every usable cpu. */
559 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
560 for (i = 0; i < CPU_SETSIZE; i++) {
561 if (!CPU_ISSET(i, &allowed_cpus))
562 continue;
563 for (j = 1; j <= 100; j++) {
564 struct percpu_list_node *node;
565
566 expected_sum += j;
567
568 node = malloc(sizeof(*node));
569 assert(node);
570 node->data = j;
571 node->next = list.c[i].head;
572 list.c[i].head = node;
573 }
574 }
575
576 for (i = 0; i < num_threads; i++) {
577 ret = pthread_create(&test_threads[i], NULL,
578 test_percpu_list_thread, &list);
579 if (ret) {
580 errno = ret;
581 perror("pthread_create");
582 abort();
583 }
584 }
585
586 for (i = 0; i < num_threads; i++) {
587 ret = pthread_join(test_threads[i], NULL);
588 if (ret) {
589 errno = ret;
590 perror("pthread_join");
591 abort();
592 }
593 }
594
595 for (i = 0; i < CPU_SETSIZE; i++) {
596 struct percpu_list_node *node;
597
598 if (!CPU_ISSET(i, &allowed_cpus))
599 continue;
600
601 while ((node = __percpu_list_pop(&list, i))) {
602 sum += node->data;
603 free(node);
604 }
605 }
606
607 /*
608 * All entries should now be accounted for (unless some external
609 * actor is interfering with our allowed affinity while this
610 * test is running).
611 */
612 assert(sum == expected_sum);
613}
614
615bool this_cpu_buffer_push(struct percpu_buffer *buffer,
616 struct percpu_buffer_node *node,
617 int *_cpu)
618{
619 bool result = false;
620 int cpu;
621
622 for (;;) {
623 intptr_t *targetptr_spec, newval_spec;
624 intptr_t *targetptr_final, newval_final;
625 intptr_t offset;
626 int ret;
627
628 cpu = rseq_cpu_start();
629 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
630 if (offset == buffer->c[cpu].buflen)
631 break;
632 newval_spec = (intptr_t)node;
633 targetptr_spec = (intptr_t *)&buffer->c[cpu].array[offset];
634 newval_final = offset + 1;
635 targetptr_final = &buffer->c[cpu].offset;
636 if (opt_mb)
637 ret = rseq_cmpeqv_trystorev_storev_release(
638 targetptr_final, offset, targetptr_spec,
639 newval_spec, newval_final, cpu);
640 else
641 ret = rseq_cmpeqv_trystorev_storev(targetptr_final,
642 offset, targetptr_spec, newval_spec,
643 newval_final, cpu);
644 if (rseq_likely(!ret)) {
645 result = true;
646 break;
647 }
648 /* Retry if comparison fails or rseq aborts. */
649 }
650 if (_cpu)
651 *_cpu = cpu;
652 return result;
653}
654
655struct percpu_buffer_node *this_cpu_buffer_pop(struct percpu_buffer *buffer,
656 int *_cpu)
657{
658 struct percpu_buffer_node *head;
659 int cpu;
660
661 for (;;) {
662 intptr_t *targetptr, newval;
663 intptr_t offset;
664 int ret;
665
666 cpu = rseq_cpu_start();
667 /* Load offset with single-copy atomicity. */
668 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
669 if (offset == 0) {
670 head = NULL;
671 break;
672 }
673 head = RSEQ_READ_ONCE(buffer->c[cpu].array[offset - 1]);
674 newval = offset - 1;
675 targetptr = (intptr_t *)&buffer->c[cpu].offset;
676 ret = rseq_cmpeqv_cmpeqv_storev(targetptr, offset,
677 (intptr_t *)&buffer->c[cpu].array[offset - 1],
678 (intptr_t)head, newval, cpu);
679 if (rseq_likely(!ret))
680 break;
681 /* Retry if comparison fails or rseq aborts. */
682 }
683 if (_cpu)
684 *_cpu = cpu;
685 return head;
686}
687
688/*
689 * __percpu_buffer_pop is not safe against concurrent accesses. Should
690 * only be used on buffers that are not concurrently modified.
691 */
692struct percpu_buffer_node *__percpu_buffer_pop(struct percpu_buffer *buffer,
693 int cpu)
694{
695 struct percpu_buffer_node *head;
696 intptr_t offset;
697
698 offset = buffer->c[cpu].offset;
699 if (offset == 0)
700 return NULL;
701 head = buffer->c[cpu].array[offset - 1];
702 buffer->c[cpu].offset = offset - 1;
703 return head;
704}
705
706void *test_percpu_buffer_thread(void *arg)
707{
708 long long i, reps;
709 struct percpu_buffer *buffer = (struct percpu_buffer *)arg;
710
711 if (!opt_disable_rseq && rseq_register_current_thread())
712 abort();
713
714 reps = opt_reps;
715 for (i = 0; i < reps; i++) {
716 struct percpu_buffer_node *node;
717
718 node = this_cpu_buffer_pop(buffer, NULL);
719 if (opt_yield)
720 sched_yield(); /* encourage shuffling */
721 if (node) {
722 if (!this_cpu_buffer_push(buffer, node, NULL)) {
723 /* Should increase buffer size. */
724 abort();
725 }
726 }
727 }
728
729 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
730 (int) gettid(), nr_abort, signals_delivered);
731 if (!opt_disable_rseq && rseq_unregister_current_thread())
732 abort();
733
734 return NULL;
735}
736
737/* Simultaneous modification to a per-cpu buffer from many threads. */
738void test_percpu_buffer(void)
739{
740 const int num_threads = opt_threads;
741 int i, j, ret;
742 uint64_t sum = 0, expected_sum = 0;
743 struct percpu_buffer buffer;
744 pthread_t test_threads[num_threads];
745 cpu_set_t allowed_cpus;
746
747 memset(&buffer, 0, sizeof(buffer));
748
749 /* Generate list entries for every usable cpu. */
750 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
751 for (i = 0; i < CPU_SETSIZE; i++) {
752 if (!CPU_ISSET(i, &allowed_cpus))
753 continue;
754 /* Worse-case is every item in same CPU. */
755 buffer.c[i].array =
756 malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
757 BUFFER_ITEM_PER_CPU);
758 assert(buffer.c[i].array);
759 buffer.c[i].buflen = CPU_SETSIZE * BUFFER_ITEM_PER_CPU;
760 for (j = 1; j <= BUFFER_ITEM_PER_CPU; j++) {
761 struct percpu_buffer_node *node;
762
763 expected_sum += j;
764
765 /*
766 * We could theoretically put the word-sized
767 * "data" directly in the buffer. However, we
768 * want to model objects that would not fit
769 * within a single word, so allocate an object
770 * for each node.
771 */
772 node = malloc(sizeof(*node));
773 assert(node);
774 node->data = j;
775 buffer.c[i].array[j - 1] = node;
776 buffer.c[i].offset++;
777 }
778 }
779
780 for (i = 0; i < num_threads; i++) {
781 ret = pthread_create(&test_threads[i], NULL,
782 test_percpu_buffer_thread, &buffer);
783 if (ret) {
784 errno = ret;
785 perror("pthread_create");
786 abort();
787 }
788 }
789
790 for (i = 0; i < num_threads; i++) {
791 ret = pthread_join(test_threads[i], NULL);
792 if (ret) {
793 errno = ret;
794 perror("pthread_join");
795 abort();
796 }
797 }
798
799 for (i = 0; i < CPU_SETSIZE; i++) {
800 struct percpu_buffer_node *node;
801
802 if (!CPU_ISSET(i, &allowed_cpus))
803 continue;
804
805 while ((node = __percpu_buffer_pop(&buffer, i))) {
806 sum += node->data;
807 free(node);
808 }
809 free(buffer.c[i].array);
810 }
811
812 /*
813 * All entries should now be accounted for (unless some external
814 * actor is interfering with our allowed affinity while this
815 * test is running).
816 */
817 assert(sum == expected_sum);
818}
819
820bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer *buffer,
821 struct percpu_memcpy_buffer_node item,
822 int *_cpu)
823{
824 bool result = false;
825 int cpu;
826
827 for (;;) {
828 intptr_t *targetptr_final, newval_final, offset;
829 char *destptr, *srcptr;
830 size_t copylen;
831 int ret;
832
833 cpu = rseq_cpu_start();
834 /* Load offset with single-copy atomicity. */
835 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
836 if (offset == buffer->c[cpu].buflen)
837 break;
838 destptr = (char *)&buffer->c[cpu].array[offset];
839 srcptr = (char *)&item;
840 /* copylen must be <= 4kB. */
841 copylen = sizeof(item);
842 newval_final = offset + 1;
843 targetptr_final = &buffer->c[cpu].offset;
844 if (opt_mb)
845 ret = rseq_cmpeqv_trymemcpy_storev_release(
846 targetptr_final, offset,
847 destptr, srcptr, copylen,
848 newval_final, cpu);
849 else
850 ret = rseq_cmpeqv_trymemcpy_storev(targetptr_final,
851 offset, destptr, srcptr, copylen,
852 newval_final, cpu);
853 if (rseq_likely(!ret)) {
854 result = true;
855 break;
856 }
857 /* Retry if comparison fails or rseq aborts. */
858 }
859 if (_cpu)
860 *_cpu = cpu;
861 return result;
862}
863
864bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
865 struct percpu_memcpy_buffer_node *item,
866 int *_cpu)
867{
868 bool result = false;
869 int cpu;
870
871 for (;;) {
872 intptr_t *targetptr_final, newval_final, offset;
873 char *destptr, *srcptr;
874 size_t copylen;
875 int ret;
876
877 cpu = rseq_cpu_start();
878 /* Load offset with single-copy atomicity. */
879 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
880 if (offset == 0)
881 break;
882 destptr = (char *)item;
883 srcptr = (char *)&buffer->c[cpu].array[offset - 1];
884 /* copylen must be <= 4kB. */
885 copylen = sizeof(*item);
886 newval_final = offset - 1;
887 targetptr_final = &buffer->c[cpu].offset;
888 ret = rseq_cmpeqv_trymemcpy_storev(targetptr_final,
889 offset, destptr, srcptr, copylen,
890 newval_final, cpu);
891 if (rseq_likely(!ret)) {
892 result = true;
893 break;
894 }
895 /* Retry if comparison fails or rseq aborts. */
896 }
897 if (_cpu)
898 *_cpu = cpu;
899 return result;
900}
901
902/*
903 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
904 * only be used on buffers that are not concurrently modified.
905 */
906bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
907 struct percpu_memcpy_buffer_node *item,
908 int cpu)
909{
910 intptr_t offset;
911
912 offset = buffer->c[cpu].offset;
913 if (offset == 0)
914 return false;
915 memcpy(item, &buffer->c[cpu].array[offset - 1], sizeof(*item));
916 buffer->c[cpu].offset = offset - 1;
917 return true;
918}
919
920void *test_percpu_memcpy_buffer_thread(void *arg)
921{
922 long long i, reps;
923 struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg;
924
925 if (!opt_disable_rseq && rseq_register_current_thread())
926 abort();
927
928 reps = opt_reps;
929 for (i = 0; i < reps; i++) {
930 struct percpu_memcpy_buffer_node item;
931 bool result;
932
933 result = this_cpu_memcpy_buffer_pop(buffer, &item, NULL);
934 if (opt_yield)
935 sched_yield(); /* encourage shuffling */
936 if (result) {
937 if (!this_cpu_memcpy_buffer_push(buffer, item, NULL)) {
938 /* Should increase buffer size. */
939 abort();
940 }
941 }
942 }
943
944 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
945 (int) gettid(), nr_abort, signals_delivered);
946 if (!opt_disable_rseq && rseq_unregister_current_thread())
947 abort();
948
949 return NULL;
950}
951
952/* Simultaneous modification to a per-cpu buffer from many threads. */
953void test_percpu_memcpy_buffer(void)
954{
955 const int num_threads = opt_threads;
956 int i, j, ret;
957 uint64_t sum = 0, expected_sum = 0;
958 struct percpu_memcpy_buffer buffer;
959 pthread_t test_threads[num_threads];
960 cpu_set_t allowed_cpus;
961
962 memset(&buffer, 0, sizeof(buffer));
963
964 /* Generate list entries for every usable cpu. */
965 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
966 for (i = 0; i < CPU_SETSIZE; i++) {
967 if (!CPU_ISSET(i, &allowed_cpus))
968 continue;
969 /* Worse-case is every item in same CPU. */
970 buffer.c[i].array =
971 malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
972 MEMCPY_BUFFER_ITEM_PER_CPU);
973 assert(buffer.c[i].array);
974 buffer.c[i].buflen = CPU_SETSIZE * MEMCPY_BUFFER_ITEM_PER_CPU;
975 for (j = 1; j <= MEMCPY_BUFFER_ITEM_PER_CPU; j++) {
976 expected_sum += 2 * j + 1;
977
978 /*
979 * We could theoretically put the word-sized
980 * "data" directly in the buffer. However, we
981 * want to model objects that would not fit
982 * within a single word, so allocate an object
983 * for each node.
984 */
985 buffer.c[i].array[j - 1].data1 = j;
986 buffer.c[i].array[j - 1].data2 = j + 1;
987 buffer.c[i].offset++;
988 }
989 }
990
991 for (i = 0; i < num_threads; i++) {
992 ret = pthread_create(&test_threads[i], NULL,
993 test_percpu_memcpy_buffer_thread,
994 &buffer);
995 if (ret) {
996 errno = ret;
997 perror("pthread_create");
998 abort();
999 }
1000 }
1001
1002 for (i = 0; i < num_threads; i++) {
1003 ret = pthread_join(test_threads[i], NULL);
1004 if (ret) {
1005 errno = ret;
1006 perror("pthread_join");
1007 abort();
1008 }
1009 }
1010
1011 for (i = 0; i < CPU_SETSIZE; i++) {
1012 struct percpu_memcpy_buffer_node item;
1013
1014 if (!CPU_ISSET(i, &allowed_cpus))
1015 continue;
1016
1017 while (__percpu_memcpy_buffer_pop(&buffer, &item, i)) {
1018 sum += item.data1;
1019 sum += item.data2;
1020 }
1021 free(buffer.c[i].array);
1022 }
1023
1024 /*
1025 * All entries should now be accounted for (unless some external
1026 * actor is interfering with our allowed affinity while this
1027 * test is running).
1028 */
1029 assert(sum == expected_sum);
1030}
1031
1032static void test_signal_interrupt_handler(int signo)
1033{
1034 signals_delivered++;
1035}
1036
1037static int set_signal_handler(void)
1038{
1039 int ret = 0;
1040 struct sigaction sa;
1041 sigset_t sigset;
1042
1043 ret = sigemptyset(&sigset);
1044 if (ret < 0) {
1045 perror("sigemptyset");
1046 return ret;
1047 }
1048
1049 sa.sa_handler = test_signal_interrupt_handler;
1050 sa.sa_mask = sigset;
1051 sa.sa_flags = 0;
1052 ret = sigaction(SIGUSR1, &sa, NULL);
1053 if (ret < 0) {
1054 perror("sigaction");
1055 return ret;
1056 }
1057
1058 printf_verbose("Signal handler set for SIGUSR1\n");
1059
1060 return ret;
1061}
1062
1063static void show_usage(int argc, char **argv)
1064{
1065 printf("Usage : %s <OPTIONS>\n",
1066 argv[0]);
1067 printf("OPTIONS:\n");
1068 printf(" [-1 loops] Number of loops for delay injection 1\n");
1069 printf(" [-2 loops] Number of loops for delay injection 2\n");
1070 printf(" [-3 loops] Number of loops for delay injection 3\n");
1071 printf(" [-4 loops] Number of loops for delay injection 4\n");
1072 printf(" [-5 loops] Number of loops for delay injection 5\n");
1073 printf(" [-6 loops] Number of loops for delay injection 6\n");
1074 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
1075 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
1076 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
1077 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
1078 printf(" [-y] Yield\n");
1079 printf(" [-k] Kill thread with signal\n");
1080 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
1081 printf(" [-t N] Number of threads (default 200)\n");
1082 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
1083 printf(" [-d] Disable rseq system call (no initialization)\n");
1084 printf(" [-D M] Disable rseq for each M threads\n");
1085 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement\n");
1086 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
1087 printf(" [-v] Verbose output.\n");
1088 printf(" [-h] Show this help.\n");
1089 printf("\n");
1090}
1091
1092int main(int argc, char **argv)
1093{
1094 int i;
1095
1096 for (i = 1; i < argc; i++) {
1097 if (argv[i][0] != '-')
1098 continue;
1099 switch (argv[i][1]) {
1100 case '1':
1101 case '2':
1102 case '3':
1103 case '4':
1104 case '5':
1105 case '6':
1106 case '7':
1107 case '8':
1108 case '9':
1109 if (argc < i + 2) {
1110 show_usage(argc, argv);
1111 goto error;
1112 }
1113 loop_cnt[argv[i][1] - '0'] = atol(argv[i + 1]);
1114 i++;
1115 break;
1116 case 'm':
1117 if (argc < i + 2) {
1118 show_usage(argc, argv);
1119 goto error;
1120 }
1121 opt_modulo = atol(argv[i + 1]);
1122 if (opt_modulo < 0) {
1123 show_usage(argc, argv);
1124 goto error;
1125 }
1126 i++;
1127 break;
1128 case 's':
1129 if (argc < i + 2) {
1130 show_usage(argc, argv);
1131 goto error;
1132 }
1133 opt_sleep = atol(argv[i + 1]);
1134 if (opt_sleep < 0) {
1135 show_usage(argc, argv);
1136 goto error;
1137 }
1138 i++;
1139 break;
1140 case 'y':
1141 opt_yield = 1;
1142 break;
1143 case 'k':
1144 opt_signal = 1;
1145 break;
1146 case 'd':
1147 opt_disable_rseq = 1;
1148 break;
1149 case 'D':
1150 if (argc < i + 2) {
1151 show_usage(argc, argv);
1152 goto error;
1153 }
1154 opt_disable_mod = atol(argv[i + 1]);
1155 if (opt_disable_mod < 0) {
1156 show_usage(argc, argv);
1157 goto error;
1158 }
1159 i++;
1160 break;
1161 case 't':
1162 if (argc < i + 2) {
1163 show_usage(argc, argv);
1164 goto error;
1165 }
1166 opt_threads = atol(argv[i + 1]);
1167 if (opt_threads < 0) {
1168 show_usage(argc, argv);
1169 goto error;
1170 }
1171 i++;
1172 break;
1173 case 'r':
1174 if (argc < i + 2) {
1175 show_usage(argc, argv);
1176 goto error;
1177 }
1178 opt_reps = atoll(argv[i + 1]);
1179 if (opt_reps < 0) {
1180 show_usage(argc, argv);
1181 goto error;
1182 }
1183 i++;
1184 break;
1185 case 'h':
1186 show_usage(argc, argv);
1187 goto end;
1188 case 'T':
1189 if (argc < i + 2) {
1190 show_usage(argc, argv);
1191 goto error;
1192 }
1193 opt_test = *argv[i + 1];
1194 switch (opt_test) {
1195 case 's':
1196 case 'l':
1197 case 'i':
1198 case 'b':
1199 case 'm':
1200 break;
1201 default:
1202 show_usage(argc, argv);
1203 goto error;
1204 }
1205 i++;
1206 break;
1207 case 'v':
1208 verbose = 1;
1209 break;
1210 case 'M':
1211 opt_mb = 1;
1212 break;
1213 default:
1214 show_usage(argc, argv);
1215 goto error;
1216 }
1217 }
1218
1219 loop_cnt_1 = loop_cnt[1];
1220 loop_cnt_2 = loop_cnt[2];
1221 loop_cnt_3 = loop_cnt[3];
1222 loop_cnt_4 = loop_cnt[4];
1223 loop_cnt_5 = loop_cnt[5];
1224 loop_cnt_6 = loop_cnt[6];
1225
1226 if (set_signal_handler())
1227 goto error;
1228
1229 if (!opt_disable_rseq && rseq_register_current_thread())
1230 goto error;
1231 switch (opt_test) {
1232 case 's':
1233 printf_verbose("spinlock\n");
1234 test_percpu_spinlock();
1235 break;
1236 case 'l':
1237 printf_verbose("linked list\n");
1238 test_percpu_list();
1239 break;
1240 case 'b':
1241 printf_verbose("buffer\n");
1242 test_percpu_buffer();
1243 break;
1244 case 'm':
1245 printf_verbose("memcpy buffer\n");
1246 test_percpu_memcpy_buffer();
1247 break;
1248 case 'i':
1249 printf_verbose("counter increment\n");
1250 test_percpu_inc();
1251 break;
1252 }
1253 if (!opt_disable_rseq && rseq_unregister_current_thread())
1254 abort();
1255end:
1256 return 0;
1257
1258error:
1259 return -1;
1260}