Test MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ
[librseq.git] / tests / param_test.c
CommitLineData
544cdc88 1// SPDX-License-Identifier: LGPL-2.1-only
31b44ba2
MD
2#ifndef _GNU_SOURCE
3#define _GNU_SOURCE
4#endif
5#include <assert.h>
5368dcb4 6#include <linux/membarrier.h>
31b44ba2
MD
7#include <pthread.h>
8#include <sched.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <syscall.h>
14#include <unistd.h>
15#include <poll.h>
16#include <sys/types.h>
17#include <signal.h>
18#include <errno.h>
19#include <stddef.h>
20
31b44ba2
MD
21#define NR_INJECT 9
22static int loop_cnt[NR_INJECT + 1];
23
24static int loop_cnt_1 asm("asm_loop_cnt_1") __attribute__((used));
25static int loop_cnt_2 asm("asm_loop_cnt_2") __attribute__((used));
26static int loop_cnt_3 asm("asm_loop_cnt_3") __attribute__((used));
27static int loop_cnt_4 asm("asm_loop_cnt_4") __attribute__((used));
28static int loop_cnt_5 asm("asm_loop_cnt_5") __attribute__((used));
29static int loop_cnt_6 asm("asm_loop_cnt_6") __attribute__((used));
30
31static int opt_modulo, verbose;
32
33static int opt_yield, opt_signal, opt_sleep,
34 opt_disable_rseq, opt_threads = 200,
35 opt_disable_mod = 0, opt_test = 's', opt_mb = 0;
36
37#ifndef RSEQ_SKIP_FASTPATH
38static long long opt_reps = 5000;
39#else
40static long long opt_reps = 100;
41#endif
42
43static __thread __attribute__((tls_model("initial-exec")))
44unsigned int signals_delivered;
45
46#ifndef BENCHMARK
47
c6e1dc81
MD
48static inline pid_t rseq_gettid(void)
49{
50 return syscall(__NR_gettid);
51}
52
31b44ba2
MD
53static __thread __attribute__((tls_model("initial-exec"), unused))
54int yield_mod_cnt, nr_abort;
55
56#define printf_verbose(fmt, ...) \
57 do { \
58 if (verbose) \
59 printf(fmt, ## __VA_ARGS__); \
60 } while (0)
61
62#ifdef __i386__
63
64#define INJECT_ASM_REG "eax"
65
66#define RSEQ_INJECT_CLOBBER \
67 , INJECT_ASM_REG
68
69#define RSEQ_INJECT_ASM(n) \
70 "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
71 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
72 "jz 333f\n\t" \
73 "222:\n\t" \
74 "dec %%" INJECT_ASM_REG "\n\t" \
75 "jnz 222b\n\t" \
76 "333:\n\t"
77
78#elif defined(__x86_64__)
79
80#define INJECT_ASM_REG_P "rax"
81#define INJECT_ASM_REG "eax"
82
83#define RSEQ_INJECT_CLOBBER \
84 , INJECT_ASM_REG_P \
85 , INJECT_ASM_REG
86
87#define RSEQ_INJECT_ASM(n) \
88 "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
89 "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
90 "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
91 "jz 333f\n\t" \
92 "222:\n\t" \
93 "dec %%" INJECT_ASM_REG "\n\t" \
94 "jnz 222b\n\t" \
95 "333:\n\t"
96
97#elif defined(__s390__)
98
99#define RSEQ_INJECT_INPUT \
100 , [loop_cnt_1]"m"(loop_cnt[1]) \
101 , [loop_cnt_2]"m"(loop_cnt[2]) \
102 , [loop_cnt_3]"m"(loop_cnt[3]) \
103 , [loop_cnt_4]"m"(loop_cnt[4]) \
104 , [loop_cnt_5]"m"(loop_cnt[5]) \
105 , [loop_cnt_6]"m"(loop_cnt[6])
106
107#define INJECT_ASM_REG "r12"
108
109#define RSEQ_INJECT_CLOBBER \
110 , INJECT_ASM_REG
111
112#define RSEQ_INJECT_ASM(n) \
113 "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
114 "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
115 "je 333f\n\t" \
116 "222:\n\t" \
117 "ahi %%" INJECT_ASM_REG ", -1\n\t" \
118 "jnz 222b\n\t" \
119 "333:\n\t"
120
121#elif defined(__ARMEL__)
122
123#define RSEQ_INJECT_INPUT \
124 , [loop_cnt_1]"m"(loop_cnt[1]) \
125 , [loop_cnt_2]"m"(loop_cnt[2]) \
126 , [loop_cnt_3]"m"(loop_cnt[3]) \
127 , [loop_cnt_4]"m"(loop_cnt[4]) \
128 , [loop_cnt_5]"m"(loop_cnt[5]) \
129 , [loop_cnt_6]"m"(loop_cnt[6])
130
131#define INJECT_ASM_REG "r4"
132
133#define RSEQ_INJECT_CLOBBER \
134 , INJECT_ASM_REG
135
136#define RSEQ_INJECT_ASM(n) \
137 "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
138 "cmp " INJECT_ASM_REG ", #0\n\t" \
139 "beq 333f\n\t" \
140 "222:\n\t" \
141 "subs " INJECT_ASM_REG ", #1\n\t" \
142 "bne 222b\n\t" \
143 "333:\n\t"
144
145#elif defined(__AARCH64EL__)
146
147#define RSEQ_INJECT_INPUT \
148 , [loop_cnt_1] "Qo" (loop_cnt[1]) \
149 , [loop_cnt_2] "Qo" (loop_cnt[2]) \
150 , [loop_cnt_3] "Qo" (loop_cnt[3]) \
151 , [loop_cnt_4] "Qo" (loop_cnt[4]) \
152 , [loop_cnt_5] "Qo" (loop_cnt[5]) \
153 , [loop_cnt_6] "Qo" (loop_cnt[6])
154
155#define INJECT_ASM_REG RSEQ_ASM_TMP_REG32
156
157#define RSEQ_INJECT_ASM(n) \
158 " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \
159 " cbz " INJECT_ASM_REG ", 333f\n" \
160 "222:\n" \
161 " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \
162 " cbnz " INJECT_ASM_REG ", 222b\n" \
163 "333:\n"
164
f1c6b55b 165#elif defined(__PPC__)
31b44ba2
MD
166
167#define RSEQ_INJECT_INPUT \
168 , [loop_cnt_1]"m"(loop_cnt[1]) \
169 , [loop_cnt_2]"m"(loop_cnt[2]) \
170 , [loop_cnt_3]"m"(loop_cnt[3]) \
171 , [loop_cnt_4]"m"(loop_cnt[4]) \
172 , [loop_cnt_5]"m"(loop_cnt[5]) \
173 , [loop_cnt_6]"m"(loop_cnt[6])
174
175#define INJECT_ASM_REG "r18"
176
177#define RSEQ_INJECT_CLOBBER \
178 , INJECT_ASM_REG
179
180#define RSEQ_INJECT_ASM(n) \
181 "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
182 "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
183 "beq 333f\n\t" \
184 "222:\n\t" \
185 "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
186 "bne 222b\n\t" \
187 "333:\n\t"
188
189#elif defined(__mips__)
190
191#define RSEQ_INJECT_INPUT \
192 , [loop_cnt_1]"m"(loop_cnt[1]) \
193 , [loop_cnt_2]"m"(loop_cnt[2]) \
194 , [loop_cnt_3]"m"(loop_cnt[3]) \
195 , [loop_cnt_4]"m"(loop_cnt[4]) \
196 , [loop_cnt_5]"m"(loop_cnt[5]) \
197 , [loop_cnt_6]"m"(loop_cnt[6])
198
199#define INJECT_ASM_REG "$5"
200
201#define RSEQ_INJECT_CLOBBER \
202 , INJECT_ASM_REG
203
204#define RSEQ_INJECT_ASM(n) \
205 "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
206 "beqz " INJECT_ASM_REG ", 333f\n\t" \
207 "222:\n\t" \
208 "addiu " INJECT_ASM_REG ", -1\n\t" \
209 "bnez " INJECT_ASM_REG ", 222b\n\t" \
210 "333:\n\t"
211
212#else
213#error unsupported target
214#endif
215
216#define RSEQ_INJECT_FAILED \
217 nr_abort++;
218
219#define RSEQ_INJECT_C(n) \
220{ \
221 int loc_i, loc_nr_loops = loop_cnt[n]; \
222 \
223 for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
224 rseq_barrier(); \
225 } \
226 if (loc_nr_loops == -1 && opt_modulo) { \
227 if (yield_mod_cnt == opt_modulo - 1) { \
228 if (opt_sleep > 0) \
229 poll(NULL, 0, opt_sleep); \
230 if (opt_yield) \
231 sched_yield(); \
232 if (opt_signal) \
233 raise(SIGUSR1); \
234 yield_mod_cnt = 0; \
235 } else { \
236 yield_mod_cnt++; \
237 } \
238 } \
239}
240
241#else
242
243#define printf_verbose(fmt, ...)
244
245#endif /* BENCHMARK */
246
247#include <rseq/rseq.h>
248
249struct percpu_lock_entry {
250 intptr_t v;
251} __attribute__((aligned(128)));
252
253struct percpu_lock {
254 struct percpu_lock_entry c[CPU_SETSIZE];
255};
256
257struct test_data_entry {
258 intptr_t count;
259} __attribute__((aligned(128)));
260
261struct spinlock_test_data {
262 struct percpu_lock lock;
263 struct test_data_entry c[CPU_SETSIZE];
264};
265
266struct spinlock_thread_test_data {
267 struct spinlock_test_data *data;
268 long long reps;
269 int reg;
270};
271
272struct inc_test_data {
273 struct test_data_entry c[CPU_SETSIZE];
274};
275
276struct inc_thread_test_data {
277 struct inc_test_data *data;
278 long long reps;
279 int reg;
280};
281
282struct percpu_list_node {
283 intptr_t data;
284 struct percpu_list_node *next;
285};
286
287struct percpu_list_entry {
288 struct percpu_list_node *head;
289} __attribute__((aligned(128)));
290
291struct percpu_list {
292 struct percpu_list_entry c[CPU_SETSIZE];
293};
294
295#define BUFFER_ITEM_PER_CPU 100
296
297struct percpu_buffer_node {
298 intptr_t data;
299};
300
301struct percpu_buffer_entry {
302 intptr_t offset;
303 intptr_t buflen;
304 struct percpu_buffer_node **array;
305} __attribute__((aligned(128)));
306
307struct percpu_buffer {
308 struct percpu_buffer_entry c[CPU_SETSIZE];
309};
310
311#define MEMCPY_BUFFER_ITEM_PER_CPU 100
312
313struct percpu_memcpy_buffer_node {
314 intptr_t data1;
315 uint64_t data2;
316};
317
318struct percpu_memcpy_buffer_entry {
319 intptr_t offset;
320 intptr_t buflen;
321 struct percpu_memcpy_buffer_node *array;
322} __attribute__((aligned(128)));
323
324struct percpu_memcpy_buffer {
325 struct percpu_memcpy_buffer_entry c[CPU_SETSIZE];
326};
327
328/* A simple percpu spinlock. Grabs lock on current cpu. */
329static int rseq_this_cpu_lock(struct percpu_lock *lock)
330{
331 int cpu;
332
333 for (;;) {
334 int ret;
335
336 cpu = rseq_cpu_start();
337 ret = rseq_cmpeqv_storev(&lock->c[cpu].v,
338 0, 1, cpu);
339 if (rseq_likely(!ret))
340 break;
341 /* Retry if comparison fails or rseq aborts. */
342 }
343 /*
344 * Acquire semantic when taking lock after control dependency.
345 * Matches rseq_smp_store_release().
346 */
347 rseq_smp_acquire__after_ctrl_dep();
348 return cpu;
349}
350
351static void rseq_percpu_unlock(struct percpu_lock *lock, int cpu)
352{
353 assert(lock->c[cpu].v == 1);
354 /*
355 * Release lock, with release semantic. Matches
356 * rseq_smp_acquire__after_ctrl_dep().
357 */
358 rseq_smp_store_release(&lock->c[cpu].v, 0);
359}
360
6e284b80 361static void *test_percpu_spinlock_thread(void *arg)
31b44ba2 362{
d268885a 363 struct spinlock_thread_test_data *thread_data = (struct spinlock_thread_test_data *) arg;
31b44ba2
MD
364 struct spinlock_test_data *data = thread_data->data;
365 long long i, reps;
366
367 if (!opt_disable_rseq && thread_data->reg &&
368 rseq_register_current_thread())
369 abort();
370 reps = thread_data->reps;
371 for (i = 0; i < reps; i++) {
af895f04 372 int cpu = rseq_this_cpu_lock(&data->lock);
31b44ba2
MD
373 data->c[cpu].count++;
374 rseq_percpu_unlock(&data->lock, cpu);
375#ifndef BENCHMARK
376 if (i != 0 && !(i % (reps / 10)))
377 printf_verbose("tid %d: count %lld\n",
378 (int) rseq_gettid(), i);
379#endif
380 }
381 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
382 (int) rseq_gettid(), nr_abort, signals_delivered);
383 if (!opt_disable_rseq && thread_data->reg &&
384 rseq_unregister_current_thread())
385 abort();
386 return NULL;
387}
388
389/*
390 * A simple test which implements a sharded counter using a per-cpu
391 * lock. Obviously real applications might prefer to simply use a
392 * per-cpu increment; however, this is reasonable for a test and the
393 * lock can be extended to synchronize more complicated operations.
394 */
6e284b80 395static void test_percpu_spinlock(void)
31b44ba2
MD
396{
397 const int num_threads = opt_threads;
398 int i, ret;
399 uint64_t sum;
400 pthread_t test_threads[num_threads];
401 struct spinlock_test_data data;
402 struct spinlock_thread_test_data thread_data[num_threads];
403
404 memset(&data, 0, sizeof(data));
405 for (i = 0; i < num_threads; i++) {
406 thread_data[i].reps = opt_reps;
407 if (opt_disable_mod <= 0 || (i % opt_disable_mod))
408 thread_data[i].reg = 1;
409 else
410 thread_data[i].reg = 0;
411 thread_data[i].data = &data;
412 ret = pthread_create(&test_threads[i], NULL,
413 test_percpu_spinlock_thread,
414 &thread_data[i]);
415 if (ret) {
416 errno = ret;
417 perror("pthread_create");
418 abort();
419 }
420 }
421
422 for (i = 0; i < num_threads; i++) {
423 ret = pthread_join(test_threads[i], NULL);
424 if (ret) {
425 errno = ret;
426 perror("pthread_join");
427 abort();
428 }
429 }
430
431 sum = 0;
432 for (i = 0; i < CPU_SETSIZE; i++)
433 sum += data.c[i].count;
434
435 assert(sum == (uint64_t)opt_reps * num_threads);
436}
437
6e284b80 438static void *test_percpu_inc_thread(void *arg)
31b44ba2 439{
d268885a 440 struct inc_thread_test_data *thread_data = (struct inc_thread_test_data *) arg;
31b44ba2
MD
441 struct inc_test_data *data = thread_data->data;
442 long long i, reps;
443
444 if (!opt_disable_rseq && thread_data->reg &&
445 rseq_register_current_thread())
446 abort();
447 reps = thread_data->reps;
448 for (i = 0; i < reps; i++) {
449 int ret;
450
451 do {
452 int cpu;
453
454 cpu = rseq_cpu_start();
455 ret = rseq_addv(&data->c[cpu].count, 1, cpu);
456 } while (rseq_unlikely(ret));
457#ifndef BENCHMARK
458 if (i != 0 && !(i % (reps / 10)))
459 printf_verbose("tid %d: count %lld\n",
460 (int) rseq_gettid(), i);
461#endif
462 }
463 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
464 (int) rseq_gettid(), nr_abort, signals_delivered);
465 if (!opt_disable_rseq && thread_data->reg &&
466 rseq_unregister_current_thread())
467 abort();
468 return NULL;
469}
470
6e284b80 471static void test_percpu_inc(void)
31b44ba2
MD
472{
473 const int num_threads = opt_threads;
474 int i, ret;
475 uint64_t sum;
476 pthread_t test_threads[num_threads];
477 struct inc_test_data data;
478 struct inc_thread_test_data thread_data[num_threads];
479
480 memset(&data, 0, sizeof(data));
481 for (i = 0; i < num_threads; i++) {
482 thread_data[i].reps = opt_reps;
483 if (opt_disable_mod <= 0 || (i % opt_disable_mod))
484 thread_data[i].reg = 1;
485 else
486 thread_data[i].reg = 0;
487 thread_data[i].data = &data;
488 ret = pthread_create(&test_threads[i], NULL,
489 test_percpu_inc_thread,
490 &thread_data[i]);
491 if (ret) {
492 errno = ret;
493 perror("pthread_create");
494 abort();
495 }
496 }
497
498 for (i = 0; i < num_threads; i++) {
499 ret = pthread_join(test_threads[i], NULL);
500 if (ret) {
501 errno = ret;
502 perror("pthread_join");
503 abort();
504 }
505 }
506
507 sum = 0;
508 for (i = 0; i < CPU_SETSIZE; i++)
509 sum += data.c[i].count;
510
511 assert(sum == (uint64_t)opt_reps * num_threads);
512}
513
6e284b80 514static void this_cpu_list_push(struct percpu_list *list,
31b44ba2
MD
515 struct percpu_list_node *node,
516 int *_cpu)
517{
518 int cpu;
519
520 for (;;) {
521 intptr_t *targetptr, newval, expect;
522 int ret;
523
524 cpu = rseq_cpu_start();
525 /* Load list->c[cpu].head with single-copy atomicity. */
526 expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head);
527 newval = (intptr_t)node;
528 targetptr = (intptr_t *)&list->c[cpu].head;
529 node->next = (struct percpu_list_node *)expect;
530 ret = rseq_cmpeqv_storev(targetptr, expect, newval, cpu);
531 if (rseq_likely(!ret))
532 break;
533 /* Retry if comparison fails or rseq aborts. */
534 }
535 if (_cpu)
536 *_cpu = cpu;
537}
538
539/*
540 * Unlike a traditional lock-less linked list; the availability of a
541 * rseq primitive allows us to implement pop without concerns over
542 * ABA-type races.
543 */
6e284b80 544static struct percpu_list_node *this_cpu_list_pop(struct percpu_list *list,
31b44ba2
MD
545 int *_cpu)
546{
547 struct percpu_list_node *node = NULL;
548 int cpu;
549
550 for (;;) {
551 struct percpu_list_node *head;
552 intptr_t *targetptr, expectnot, *load;
553 off_t offset;
554 int ret;
555
556 cpu = rseq_cpu_start();
557 targetptr = (intptr_t *)&list->c[cpu].head;
558 expectnot = (intptr_t)NULL;
559 offset = offsetof(struct percpu_list_node, next);
560 load = (intptr_t *)&head;
561 ret = rseq_cmpnev_storeoffp_load(targetptr, expectnot,
562 offset, load, cpu);
563 if (rseq_likely(!ret)) {
564 node = head;
565 break;
566 }
567 if (ret > 0)
568 break;
569 /* Retry if rseq aborts. */
570 }
571 if (_cpu)
572 *_cpu = cpu;
573 return node;
574}
575
576/*
577 * __percpu_list_pop is not safe against concurrent accesses. Should
578 * only be used on lists that are not concurrently modified.
579 */
6e284b80 580static struct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu)
31b44ba2
MD
581{
582 struct percpu_list_node *node;
583
584 node = list->c[cpu].head;
585 if (!node)
586 return NULL;
587 list->c[cpu].head = node->next;
588 return node;
589}
590
6e284b80 591static void *test_percpu_list_thread(void *arg)
31b44ba2
MD
592{
593 long long i, reps;
594 struct percpu_list *list = (struct percpu_list *)arg;
595
596 if (!opt_disable_rseq && rseq_register_current_thread())
597 abort();
598
599 reps = opt_reps;
600 for (i = 0; i < reps; i++) {
601 struct percpu_list_node *node;
602
603 node = this_cpu_list_pop(list, NULL);
604 if (opt_yield)
605 sched_yield(); /* encourage shuffling */
606 if (node)
607 this_cpu_list_push(list, node, NULL);
608 }
609
610 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
611 (int) rseq_gettid(), nr_abort, signals_delivered);
612 if (!opt_disable_rseq && rseq_unregister_current_thread())
613 abort();
614
615 return NULL;
616}
617
618/* Simultaneous modification to a per-cpu linked list from many threads. */
6e284b80 619static void test_percpu_list(void)
31b44ba2
MD
620{
621 const int num_threads = opt_threads;
622 int i, j, ret;
623 uint64_t sum = 0, expected_sum = 0;
624 struct percpu_list list;
625 pthread_t test_threads[num_threads];
626 cpu_set_t allowed_cpus;
627
628 memset(&list, 0, sizeof(list));
629
630 /* Generate list entries for every usable cpu. */
631 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
632 for (i = 0; i < CPU_SETSIZE; i++) {
633 if (!CPU_ISSET(i, &allowed_cpus))
634 continue;
635 for (j = 1; j <= 100; j++) {
636 struct percpu_list_node *node;
637
638 expected_sum += j;
639
d268885a 640 node = (struct percpu_list_node *) malloc(sizeof(*node));
31b44ba2
MD
641 assert(node);
642 node->data = j;
643 node->next = list.c[i].head;
644 list.c[i].head = node;
645 }
646 }
647
648 for (i = 0; i < num_threads; i++) {
649 ret = pthread_create(&test_threads[i], NULL,
650 test_percpu_list_thread, &list);
651 if (ret) {
652 errno = ret;
653 perror("pthread_create");
654 abort();
655 }
656 }
657
658 for (i = 0; i < num_threads; i++) {
659 ret = pthread_join(test_threads[i], NULL);
660 if (ret) {
661 errno = ret;
662 perror("pthread_join");
663 abort();
664 }
665 }
666
667 for (i = 0; i < CPU_SETSIZE; i++) {
668 struct percpu_list_node *node;
669
670 if (!CPU_ISSET(i, &allowed_cpus))
671 continue;
672
673 while ((node = __percpu_list_pop(&list, i))) {
674 sum += node->data;
675 free(node);
676 }
677 }
678
679 /*
680 * All entries should now be accounted for (unless some external
681 * actor is interfering with our allowed affinity while this
682 * test is running).
683 */
684 assert(sum == expected_sum);
685}
686
6e284b80 687static bool this_cpu_buffer_push(struct percpu_buffer *buffer,
31b44ba2
MD
688 struct percpu_buffer_node *node,
689 int *_cpu)
690{
691 bool result = false;
692 int cpu;
693
694 for (;;) {
695 intptr_t *targetptr_spec, newval_spec;
696 intptr_t *targetptr_final, newval_final;
697 intptr_t offset;
698 int ret;
699
700 cpu = rseq_cpu_start();
701 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
702 if (offset == buffer->c[cpu].buflen)
703 break;
704 newval_spec = (intptr_t)node;
705 targetptr_spec = (intptr_t *)&buffer->c[cpu].array[offset];
706 newval_final = offset + 1;
707 targetptr_final = &buffer->c[cpu].offset;
708 if (opt_mb)
709 ret = rseq_cmpeqv_trystorev_storev_release(
710 targetptr_final, offset, targetptr_spec,
711 newval_spec, newval_final, cpu);
712 else
713 ret = rseq_cmpeqv_trystorev_storev(targetptr_final,
714 offset, targetptr_spec, newval_spec,
715 newval_final, cpu);
716 if (rseq_likely(!ret)) {
717 result = true;
718 break;
719 }
720 /* Retry if comparison fails or rseq aborts. */
721 }
722 if (_cpu)
723 *_cpu = cpu;
724 return result;
725}
726
6e284b80 727static struct percpu_buffer_node *this_cpu_buffer_pop(struct percpu_buffer *buffer,
31b44ba2
MD
728 int *_cpu)
729{
730 struct percpu_buffer_node *head;
731 int cpu;
732
733 for (;;) {
734 intptr_t *targetptr, newval;
735 intptr_t offset;
736 int ret;
737
738 cpu = rseq_cpu_start();
739 /* Load offset with single-copy atomicity. */
740 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
741 if (offset == 0) {
742 head = NULL;
743 break;
744 }
745 head = RSEQ_READ_ONCE(buffer->c[cpu].array[offset - 1]);
746 newval = offset - 1;
747 targetptr = (intptr_t *)&buffer->c[cpu].offset;
748 ret = rseq_cmpeqv_cmpeqv_storev(targetptr, offset,
749 (intptr_t *)&buffer->c[cpu].array[offset - 1],
750 (intptr_t)head, newval, cpu);
751 if (rseq_likely(!ret))
752 break;
753 /* Retry if comparison fails or rseq aborts. */
754 }
755 if (_cpu)
756 *_cpu = cpu;
757 return head;
758}
759
760/*
761 * __percpu_buffer_pop is not safe against concurrent accesses. Should
762 * only be used on buffers that are not concurrently modified.
763 */
6e284b80 764static struct percpu_buffer_node *__percpu_buffer_pop(struct percpu_buffer *buffer,
31b44ba2
MD
765 int cpu)
766{
767 struct percpu_buffer_node *head;
768 intptr_t offset;
769
770 offset = buffer->c[cpu].offset;
771 if (offset == 0)
772 return NULL;
773 head = buffer->c[cpu].array[offset - 1];
774 buffer->c[cpu].offset = offset - 1;
775 return head;
776}
777
6e284b80 778static void *test_percpu_buffer_thread(void *arg)
31b44ba2
MD
779{
780 long long i, reps;
781 struct percpu_buffer *buffer = (struct percpu_buffer *)arg;
782
783 if (!opt_disable_rseq && rseq_register_current_thread())
784 abort();
785
786 reps = opt_reps;
787 for (i = 0; i < reps; i++) {
788 struct percpu_buffer_node *node;
789
790 node = this_cpu_buffer_pop(buffer, NULL);
791 if (opt_yield)
792 sched_yield(); /* encourage shuffling */
793 if (node) {
794 if (!this_cpu_buffer_push(buffer, node, NULL)) {
795 /* Should increase buffer size. */
796 abort();
797 }
798 }
799 }
800
801 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
802 (int) rseq_gettid(), nr_abort, signals_delivered);
803 if (!opt_disable_rseq && rseq_unregister_current_thread())
804 abort();
805
806 return NULL;
807}
808
809/* Simultaneous modification to a per-cpu buffer from many threads. */
6e284b80 810static void test_percpu_buffer(void)
31b44ba2
MD
811{
812 const int num_threads = opt_threads;
813 int i, j, ret;
814 uint64_t sum = 0, expected_sum = 0;
815 struct percpu_buffer buffer;
816 pthread_t test_threads[num_threads];
817 cpu_set_t allowed_cpus;
818
819 memset(&buffer, 0, sizeof(buffer));
820
821 /* Generate list entries for every usable cpu. */
822 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
823 for (i = 0; i < CPU_SETSIZE; i++) {
824 if (!CPU_ISSET(i, &allowed_cpus))
825 continue;
826 /* Worse-case is every item in same CPU. */
827 buffer.c[i].array =
d268885a 828 (struct percpu_buffer_node **)
31b44ba2
MD
829 malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
830 BUFFER_ITEM_PER_CPU);
831 assert(buffer.c[i].array);
832 buffer.c[i].buflen = CPU_SETSIZE * BUFFER_ITEM_PER_CPU;
833 for (j = 1; j <= BUFFER_ITEM_PER_CPU; j++) {
834 struct percpu_buffer_node *node;
835
836 expected_sum += j;
837
838 /*
839 * We could theoretically put the word-sized
840 * "data" directly in the buffer. However, we
841 * want to model objects that would not fit
842 * within a single word, so allocate an object
843 * for each node.
844 */
d268885a 845 node = (struct percpu_buffer_node *) malloc(sizeof(*node));
31b44ba2
MD
846 assert(node);
847 node->data = j;
848 buffer.c[i].array[j - 1] = node;
849 buffer.c[i].offset++;
850 }
851 }
852
853 for (i = 0; i < num_threads; i++) {
854 ret = pthread_create(&test_threads[i], NULL,
855 test_percpu_buffer_thread, &buffer);
856 if (ret) {
857 errno = ret;
858 perror("pthread_create");
859 abort();
860 }
861 }
862
863 for (i = 0; i < num_threads; i++) {
864 ret = pthread_join(test_threads[i], NULL);
865 if (ret) {
866 errno = ret;
867 perror("pthread_join");
868 abort();
869 }
870 }
871
872 for (i = 0; i < CPU_SETSIZE; i++) {
873 struct percpu_buffer_node *node;
874
875 if (!CPU_ISSET(i, &allowed_cpus))
876 continue;
877
878 while ((node = __percpu_buffer_pop(&buffer, i))) {
879 sum += node->data;
880 free(node);
881 }
882 free(buffer.c[i].array);
883 }
884
885 /*
886 * All entries should now be accounted for (unless some external
887 * actor is interfering with our allowed affinity while this
888 * test is running).
889 */
890 assert(sum == expected_sum);
891}
892
6e284b80 893static bool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer *buffer,
31b44ba2
MD
894 struct percpu_memcpy_buffer_node item,
895 int *_cpu)
896{
897 bool result = false;
898 int cpu;
899
900 for (;;) {
901 intptr_t *targetptr_final, newval_final, offset;
902 char *destptr, *srcptr;
903 size_t copylen;
904 int ret;
905
906 cpu = rseq_cpu_start();
907 /* Load offset with single-copy atomicity. */
908 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
909 if (offset == buffer->c[cpu].buflen)
910 break;
911 destptr = (char *)&buffer->c[cpu].array[offset];
912 srcptr = (char *)&item;
913 /* copylen must be <= 4kB. */
914 copylen = sizeof(item);
915 newval_final = offset + 1;
916 targetptr_final = &buffer->c[cpu].offset;
917 if (opt_mb)
918 ret = rseq_cmpeqv_trymemcpy_storev_release(
919 targetptr_final, offset,
920 destptr, srcptr, copylen,
921 newval_final, cpu);
922 else
923 ret = rseq_cmpeqv_trymemcpy_storev(targetptr_final,
924 offset, destptr, srcptr, copylen,
925 newval_final, cpu);
926 if (rseq_likely(!ret)) {
927 result = true;
928 break;
929 }
930 /* Retry if comparison fails or rseq aborts. */
931 }
932 if (_cpu)
933 *_cpu = cpu;
934 return result;
935}
936
6e284b80 937static bool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
31b44ba2
MD
938 struct percpu_memcpy_buffer_node *item,
939 int *_cpu)
940{
941 bool result = false;
942 int cpu;
943
944 for (;;) {
945 intptr_t *targetptr_final, newval_final, offset;
946 char *destptr, *srcptr;
947 size_t copylen;
948 int ret;
949
950 cpu = rseq_cpu_start();
951 /* Load offset with single-copy atomicity. */
952 offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
953 if (offset == 0)
954 break;
955 destptr = (char *)item;
956 srcptr = (char *)&buffer->c[cpu].array[offset - 1];
957 /* copylen must be <= 4kB. */
958 copylen = sizeof(*item);
959 newval_final = offset - 1;
960 targetptr_final = &buffer->c[cpu].offset;
961 ret = rseq_cmpeqv_trymemcpy_storev(targetptr_final,
962 offset, destptr, srcptr, copylen,
963 newval_final, cpu);
964 if (rseq_likely(!ret)) {
965 result = true;
966 break;
967 }
968 /* Retry if comparison fails or rseq aborts. */
969 }
970 if (_cpu)
971 *_cpu = cpu;
972 return result;
973}
974
975/*
976 * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
977 * only be used on buffers that are not concurrently modified.
978 */
6e284b80 979static bool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
31b44ba2
MD
980 struct percpu_memcpy_buffer_node *item,
981 int cpu)
982{
983 intptr_t offset;
984
985 offset = buffer->c[cpu].offset;
986 if (offset == 0)
987 return false;
988 memcpy(item, &buffer->c[cpu].array[offset - 1], sizeof(*item));
989 buffer->c[cpu].offset = offset - 1;
990 return true;
991}
992
6e284b80 993static void *test_percpu_memcpy_buffer_thread(void *arg)
31b44ba2
MD
994{
995 long long i, reps;
996 struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg;
997
998 if (!opt_disable_rseq && rseq_register_current_thread())
999 abort();
1000
1001 reps = opt_reps;
1002 for (i = 0; i < reps; i++) {
1003 struct percpu_memcpy_buffer_node item;
1004 bool result;
1005
1006 result = this_cpu_memcpy_buffer_pop(buffer, &item, NULL);
1007 if (opt_yield)
1008 sched_yield(); /* encourage shuffling */
1009 if (result) {
1010 if (!this_cpu_memcpy_buffer_push(buffer, item, NULL)) {
1011 /* Should increase buffer size. */
1012 abort();
1013 }
1014 }
1015 }
1016
1017 printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
1018 (int) rseq_gettid(), nr_abort, signals_delivered);
1019 if (!opt_disable_rseq && rseq_unregister_current_thread())
1020 abort();
1021
1022 return NULL;
1023}
1024
1025/* Simultaneous modification to a per-cpu buffer from many threads. */
6e284b80 1026static void test_percpu_memcpy_buffer(void)
31b44ba2
MD
1027{
1028 const int num_threads = opt_threads;
1029 int i, j, ret;
1030 uint64_t sum = 0, expected_sum = 0;
1031 struct percpu_memcpy_buffer buffer;
1032 pthread_t test_threads[num_threads];
1033 cpu_set_t allowed_cpus;
1034
1035 memset(&buffer, 0, sizeof(buffer));
1036
1037 /* Generate list entries for every usable cpu. */
1038 sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
1039 for (i = 0; i < CPU_SETSIZE; i++) {
1040 if (!CPU_ISSET(i, &allowed_cpus))
1041 continue;
1042 /* Worse-case is every item in same CPU. */
1043 buffer.c[i].array =
d268885a 1044 (struct percpu_memcpy_buffer_node *)
31b44ba2
MD
1045 malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
1046 MEMCPY_BUFFER_ITEM_PER_CPU);
1047 assert(buffer.c[i].array);
1048 buffer.c[i].buflen = CPU_SETSIZE * MEMCPY_BUFFER_ITEM_PER_CPU;
1049 for (j = 1; j <= MEMCPY_BUFFER_ITEM_PER_CPU; j++) {
1050 expected_sum += 2 * j + 1;
1051
1052 /*
1053 * We could theoretically put the word-sized
1054 * "data" directly in the buffer. However, we
1055 * want to model objects that would not fit
1056 * within a single word, so allocate an object
1057 * for each node.
1058 */
1059 buffer.c[i].array[j - 1].data1 = j;
1060 buffer.c[i].array[j - 1].data2 = j + 1;
1061 buffer.c[i].offset++;
1062 }
1063 }
1064
1065 for (i = 0; i < num_threads; i++) {
1066 ret = pthread_create(&test_threads[i], NULL,
1067 test_percpu_memcpy_buffer_thread,
1068 &buffer);
1069 if (ret) {
1070 errno = ret;
1071 perror("pthread_create");
1072 abort();
1073 }
1074 }
1075
1076 for (i = 0; i < num_threads; i++) {
1077 ret = pthread_join(test_threads[i], NULL);
1078 if (ret) {
1079 errno = ret;
1080 perror("pthread_join");
1081 abort();
1082 }
1083 }
1084
1085 for (i = 0; i < CPU_SETSIZE; i++) {
1086 struct percpu_memcpy_buffer_node item;
1087
1088 if (!CPU_ISSET(i, &allowed_cpus))
1089 continue;
1090
1091 while (__percpu_memcpy_buffer_pop(&buffer, &item, i)) {
1092 sum += item.data1;
1093 sum += item.data2;
1094 }
1095 free(buffer.c[i].array);
1096 }
1097
1098 /*
1099 * All entries should now be accounted for (unless some external
1100 * actor is interfering with our allowed affinity while this
1101 * test is running).
1102 */
1103 assert(sum == expected_sum);
1104}
1105
544cdc88
MJ
1106
1107static void test_signal_interrupt_handler(__attribute__ ((unused)) int signo)
31b44ba2
MD
1108{
1109 signals_delivered++;
1110}
1111
1112static int set_signal_handler(void)
1113{
1114 int ret = 0;
1115 struct sigaction sa;
1116 sigset_t sigset;
1117
1118 ret = sigemptyset(&sigset);
1119 if (ret < 0) {
1120 perror("sigemptyset");
1121 return ret;
1122 }
1123
1124 sa.sa_handler = test_signal_interrupt_handler;
1125 sa.sa_mask = sigset;
1126 sa.sa_flags = 0;
1127 ret = sigaction(SIGUSR1, &sa, NULL);
1128 if (ret < 0) {
1129 perror("sigaction");
1130 return ret;
1131 }
1132
1133 printf_verbose("Signal handler set for SIGUSR1\n");
1134
1135 return ret;
1136}
1137
5368dcb4
MD
1138/* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
1139#ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
1140struct test_membarrier_thread_args {
1141 int stop;
1142 intptr_t percpu_list_ptr;
1143};
1144
1145/* Worker threads modify data in their "active" percpu lists. */
1146static
1147void *test_membarrier_worker_thread(void *arg)
1148{
1149 struct test_membarrier_thread_args *args =
1150 (struct test_membarrier_thread_args *)arg;
1151 const int iters = opt_reps;
1152 int i;
1153
1154 if (rseq_register_current_thread()) {
1155 fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1156 errno, strerror(errno));
1157 abort();
1158 }
1159
1160 /* Wait for initialization. */
1161 while (!rseq_smp_load_acquire(&args->percpu_list_ptr)) { }
1162
1163 for (i = 0; i < iters; ++i) {
1164 int ret;
1165
1166 do {
1167 int cpu = rseq_cpu_start();
1168
1169 ret = rseq_offset_deref_addv(&args->percpu_list_ptr,
1170 sizeof(struct percpu_list_entry) * cpu, 1, cpu);
1171 } while (rseq_unlikely(ret));
1172 }
1173
1174 if (rseq_unregister_current_thread()) {
1175 fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1176 errno, strerror(errno));
1177 abort();
1178 }
1179 return NULL;
1180}
1181
1182static
1183void test_membarrier_init_percpu_list(struct percpu_list *list)
1184{
1185 int i;
1186
1187 memset(list, 0, sizeof(*list));
1188 for (i = 0; i < CPU_SETSIZE; i++) {
1189 struct percpu_list_node *node;
1190
1191 node = (struct percpu_list_node *) malloc(sizeof(*node));
1192 assert(node);
1193 node->data = 0;
1194 node->next = NULL;
1195 list->c[i].head = node;
1196 }
1197}
1198
1199static
1200void test_membarrier_free_percpu_list(struct percpu_list *list)
1201{
1202 int i;
1203
1204 for (i = 0; i < CPU_SETSIZE; i++)
1205 free(list->c[i].head);
1206}
1207
1208static
1209int sys_membarrier(int cmd, int flags, int cpu_id)
1210{
1211 return syscall(__NR_membarrier, cmd, flags, cpu_id);
1212}
1213
1214/*
1215 * The manager thread swaps per-cpu lists that worker threads see,
1216 * and validates that there are no unexpected modifications.
1217 */
1218static
1219void *test_membarrier_manager_thread(void *arg)
1220{
1221 struct test_membarrier_thread_args *args =
1222 (struct test_membarrier_thread_args *)arg;
1223 struct percpu_list list_a, list_b;
1224 intptr_t expect_a = 0, expect_b = 0;
1225 int cpu_a = 0, cpu_b = 0;
1226
1227 if (rseq_register_current_thread()) {
1228 fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
1229 errno, strerror(errno));
1230 abort();
1231 }
1232
1233 /* Init lists. */
1234 test_membarrier_init_percpu_list(&list_a);
1235 test_membarrier_init_percpu_list(&list_b);
1236
1237 /* Initialize lists before publishing them. */
1238 rseq_smp_wmb();
1239
1240 RSEQ_WRITE_ONCE(args->percpu_list_ptr, (intptr_t)&list_a);
1241
1242 while (!RSEQ_READ_ONCE(args->stop)) {
1243 /* list_a is "active". */
1244 cpu_a = rand() % CPU_SETSIZE;
1245 /*
1246 * As list_b is "inactive", we should never see changes
1247 * to list_b.
1248 */
1249 if (expect_b != RSEQ_READ_ONCE(list_b.c[cpu_b].head->data)) {
1250 fprintf(stderr, "Membarrier test failed\n");
1251 abort();
1252 }
1253
1254 /* Make list_b "active". */
1255 RSEQ_WRITE_ONCE(args->percpu_list_ptr, (intptr_t)&list_b);
1256 if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,
1257 MEMBARRIER_CMD_FLAG_CPU, cpu_a) &&
1258 errno != ENXIO /* missing CPU */) {
1259 perror("sys_membarrier");
1260 abort();
1261 }
1262 /*
1263 * Cpu A should now only modify list_b, so the values
1264 * in list_a should be stable.
1265 */
1266 expect_a = RSEQ_READ_ONCE(list_a.c[cpu_a].head->data);
1267
1268 cpu_b = rand() % CPU_SETSIZE;
1269 /*
1270 * As list_a is "inactive", we should never see changes
1271 * to list_a.
1272 */
1273 if (expect_a != RSEQ_READ_ONCE(list_a.c[cpu_a].head->data)) {
1274 fprintf(stderr, "Membarrier test failed\n");
1275 abort();
1276 }
1277
1278 /* Make list_a "active". */
1279 RSEQ_WRITE_ONCE(args->percpu_list_ptr, (intptr_t)&list_a);
1280 if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,
1281 MEMBARRIER_CMD_FLAG_CPU, cpu_b) &&
1282 errno != ENXIO /* missing CPU */) {
1283 perror("sys_membarrier");
1284 abort();
1285 }
1286 /* Remember a value from list_b. */
1287 expect_b = RSEQ_READ_ONCE(list_b.c[cpu_b].head->data);
1288 }
1289
1290 test_membarrier_free_percpu_list(&list_a);
1291 test_membarrier_free_percpu_list(&list_b);
1292
1293 if (rseq_unregister_current_thread()) {
1294 fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
1295 errno, strerror(errno));
1296 abort();
1297 }
1298 return NULL;
1299}
1300
1301static
1302void test_membarrier(void)
1303{
1304 const int num_threads = opt_threads;
1305 struct test_membarrier_thread_args thread_args;
1306 pthread_t worker_threads[num_threads];
1307 pthread_t manager_thread;
1308 int i, ret;
1309
1310 if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0, 0)) {
1311 perror("sys_membarrier");
1312 abort();
1313 }
1314
1315 thread_args.stop = 0;
1316 thread_args.percpu_list_ptr = 0;
1317 ret = pthread_create(&manager_thread, NULL,
1318 test_membarrier_manager_thread, &thread_args);
1319 if (ret) {
1320 errno = ret;
1321 perror("pthread_create");
1322 abort();
1323 }
1324
1325 for (i = 0; i < num_threads; i++) {
1326 ret = pthread_create(&worker_threads[i], NULL,
1327 test_membarrier_worker_thread, &thread_args);
1328 if (ret) {
1329 errno = ret;
1330 perror("pthread_create");
1331 abort();
1332 }
1333 }
1334
1335
1336 for (i = 0; i < num_threads; i++) {
1337 ret = pthread_join(worker_threads[i], NULL);
1338 if (ret) {
1339 errno = ret;
1340 perror("pthread_join");
1341 abort();
1342 }
1343 }
1344
1345 RSEQ_WRITE_ONCE(thread_args.stop, 1);
1346 ret = pthread_join(manager_thread, NULL);
1347 if (ret) {
1348 errno = ret;
1349 perror("pthread_join");
1350 abort();
1351 }
1352}
1353#else /* RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV */
1354static
1355void test_membarrier(void)
1356{
1357 fprintf(stderr, "rseq_offset_deref_addv is not implemented on this architecture. "
1358 "Skipping membarrier test.\n");
1359}
1360#endif
1361
544cdc88 1362static void show_usage(char **argv)
31b44ba2
MD
1363{
1364 printf("Usage : %s <OPTIONS>\n",
1365 argv[0]);
1366 printf("OPTIONS:\n");
1367 printf(" [-1 loops] Number of loops for delay injection 1\n");
1368 printf(" [-2 loops] Number of loops for delay injection 2\n");
1369 printf(" [-3 loops] Number of loops for delay injection 3\n");
1370 printf(" [-4 loops] Number of loops for delay injection 4\n");
1371 printf(" [-5 loops] Number of loops for delay injection 5\n");
1372 printf(" [-6 loops] Number of loops for delay injection 6\n");
1373 printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
1374 printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
1375 printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
1376 printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
1377 printf(" [-y] Yield\n");
1378 printf(" [-k] Kill thread with signal\n");
1379 printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
1380 printf(" [-t N] Number of threads (default 200)\n");
1381 printf(" [-r N] Number of repetitions per thread (default 5000)\n");
1382 printf(" [-d] Disable rseq system call (no initialization)\n");
1383 printf(" [-D M] Disable rseq for each M threads\n");
5368dcb4 1384 printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
31b44ba2 1385 printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
d1cdec98 1386 printf(" [-c] Check if the rseq syscall is available.\n");
31b44ba2
MD
1387 printf(" [-v] Verbose output.\n");
1388 printf(" [-h] Show this help.\n");
1389 printf("\n");
1390}
1391
1392int main(int argc, char **argv)
1393{
1394 int i;
1395
1396 for (i = 1; i < argc; i++) {
1397 if (argv[i][0] != '-')
1398 continue;
1399 switch (argv[i][1]) {
1400 case '1':
1401 case '2':
1402 case '3':
1403 case '4':
1404 case '5':
1405 case '6':
1406 case '7':
1407 case '8':
1408 case '9':
1409 if (argc < i + 2) {
544cdc88 1410 show_usage(argv);
31b44ba2
MD
1411 goto error;
1412 }
1413 loop_cnt[argv[i][1] - '0'] = atol(argv[i + 1]);
1414 i++;
1415 break;
1416 case 'm':
1417 if (argc < i + 2) {
544cdc88 1418 show_usage(argv);
31b44ba2
MD
1419 goto error;
1420 }
1421 opt_modulo = atol(argv[i + 1]);
1422 if (opt_modulo < 0) {
544cdc88 1423 show_usage(argv);
31b44ba2
MD
1424 goto error;
1425 }
1426 i++;
1427 break;
1428 case 's':
1429 if (argc < i + 2) {
544cdc88 1430 show_usage(argv);
31b44ba2
MD
1431 goto error;
1432 }
1433 opt_sleep = atol(argv[i + 1]);
1434 if (opt_sleep < 0) {
544cdc88 1435 show_usage(argv);
31b44ba2
MD
1436 goto error;
1437 }
1438 i++;
1439 break;
1440 case 'y':
1441 opt_yield = 1;
1442 break;
1443 case 'k':
1444 opt_signal = 1;
1445 break;
1446 case 'd':
1447 opt_disable_rseq = 1;
1448 break;
1449 case 'D':
1450 if (argc < i + 2) {
544cdc88 1451 show_usage(argv);
31b44ba2
MD
1452 goto error;
1453 }
1454 opt_disable_mod = atol(argv[i + 1]);
1455 if (opt_disable_mod < 0) {
544cdc88 1456 show_usage(argv);
31b44ba2
MD
1457 goto error;
1458 }
1459 i++;
1460 break;
1461 case 't':
1462 if (argc < i + 2) {
544cdc88 1463 show_usage(argv);
31b44ba2
MD
1464 goto error;
1465 }
1466 opt_threads = atol(argv[i + 1]);
1467 if (opt_threads < 0) {
544cdc88 1468 show_usage(argv);
31b44ba2
MD
1469 goto error;
1470 }
1471 i++;
1472 break;
1473 case 'r':
1474 if (argc < i + 2) {
544cdc88 1475 show_usage(argv);
31b44ba2
MD
1476 goto error;
1477 }
1478 opt_reps = atoll(argv[i + 1]);
1479 if (opt_reps < 0) {
544cdc88 1480 show_usage(argv);
31b44ba2
MD
1481 goto error;
1482 }
1483 i++;
1484 break;
1485 case 'h':
544cdc88 1486 show_usage(argv);
31b44ba2
MD
1487 goto end;
1488 case 'T':
1489 if (argc < i + 2) {
544cdc88 1490 show_usage(argv);
31b44ba2
MD
1491 goto error;
1492 }
1493 opt_test = *argv[i + 1];
1494 switch (opt_test) {
1495 case 's':
1496 case 'l':
1497 case 'i':
1498 case 'b':
1499 case 'm':
5368dcb4 1500 case 'r':
31b44ba2
MD
1501 break;
1502 default:
544cdc88 1503 show_usage(argv);
31b44ba2
MD
1504 goto error;
1505 }
1506 i++;
1507 break;
1508 case 'v':
1509 verbose = 1;
1510 break;
1511 case 'M':
1512 opt_mb = 1;
1513 break;
d1cdec98
MJ
1514 case 'c':
1515 if (rseq_available()) {
1516 printf_verbose("The rseq syscall is available.\n");
1517 goto end;
1518 } else {
1519 printf_verbose("The rseq syscall is unavailable.\n");
1520 goto no_rseq;
1521 }
31b44ba2 1522 default:
544cdc88 1523 show_usage(argv);
31b44ba2
MD
1524 goto error;
1525 }
1526 }
1527
1528 loop_cnt_1 = loop_cnt[1];
1529 loop_cnt_2 = loop_cnt[2];
1530 loop_cnt_3 = loop_cnt[3];
1531 loop_cnt_4 = loop_cnt[4];
1532 loop_cnt_5 = loop_cnt[5];
1533 loop_cnt_6 = loop_cnt[6];
1534
1535 if (set_signal_handler())
1536 goto error;
1537
1538 if (!opt_disable_rseq && rseq_register_current_thread())
1539 goto error;
1540 switch (opt_test) {
1541 case 's':
1542 printf_verbose("spinlock\n");
1543 test_percpu_spinlock();
1544 break;
1545 case 'l':
1546 printf_verbose("linked list\n");
1547 test_percpu_list();
1548 break;
1549 case 'b':
1550 printf_verbose("buffer\n");
1551 test_percpu_buffer();
1552 break;
1553 case 'm':
1554 printf_verbose("memcpy buffer\n");
1555 test_percpu_memcpy_buffer();
1556 break;
1557 case 'i':
1558 printf_verbose("counter increment\n");
1559 test_percpu_inc();
1560 break;
5368dcb4
MD
1561 case 'r':
1562 printf_verbose("membarrier\n");
1563 test_membarrier();
1564 break;
31b44ba2
MD
1565 }
1566 if (!opt_disable_rseq && rseq_unregister_current_thread())
1567 abort();
1568end:
1569 return 0;
1570
1571error:
1572 return -1;
d1cdec98
MJ
1573
1574no_rseq:
1575 return 2;
31b44ba2 1576}
This page took 0.147916 seconds and 4 git commands to generate.