18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
28c2ecf20Sopenharmony_ci#define _GNU_SOURCE
38c2ecf20Sopenharmony_ci#include <assert.h>
48c2ecf20Sopenharmony_ci#include <linux/membarrier.h>
58c2ecf20Sopenharmony_ci#include <pthread.h>
68c2ecf20Sopenharmony_ci#include <sched.h>
78c2ecf20Sopenharmony_ci#include <stdatomic.h>
88c2ecf20Sopenharmony_ci#include <stdint.h>
98c2ecf20Sopenharmony_ci#include <stdio.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <syscall.h>
138c2ecf20Sopenharmony_ci#include <unistd.h>
148c2ecf20Sopenharmony_ci#include <poll.h>
158c2ecf20Sopenharmony_ci#include <sys/types.h>
168c2ecf20Sopenharmony_ci#include <signal.h>
178c2ecf20Sopenharmony_ci#include <errno.h>
188c2ecf20Sopenharmony_ci#include <stddef.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic inline pid_t rseq_gettid(void)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	return syscall(__NR_gettid);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define NR_INJECT	9
268c2ecf20Sopenharmony_cistatic int loop_cnt[NR_INJECT + 1];
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int loop_cnt_1 asm("asm_loop_cnt_1") __attribute__((used));
298c2ecf20Sopenharmony_cistatic int loop_cnt_2 asm("asm_loop_cnt_2") __attribute__((used));
308c2ecf20Sopenharmony_cistatic int loop_cnt_3 asm("asm_loop_cnt_3") __attribute__((used));
318c2ecf20Sopenharmony_cistatic int loop_cnt_4 asm("asm_loop_cnt_4") __attribute__((used));
328c2ecf20Sopenharmony_cistatic int loop_cnt_5 asm("asm_loop_cnt_5") __attribute__((used));
338c2ecf20Sopenharmony_cistatic int loop_cnt_6 asm("asm_loop_cnt_6") __attribute__((used));
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int opt_modulo, verbose;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int opt_yield, opt_signal, opt_sleep,
388c2ecf20Sopenharmony_ci		opt_disable_rseq, opt_threads = 200,
398c2ecf20Sopenharmony_ci		opt_disable_mod = 0, opt_test = 's', opt_mb = 0;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#ifndef RSEQ_SKIP_FASTPATH
428c2ecf20Sopenharmony_cistatic long long opt_reps = 5000;
438c2ecf20Sopenharmony_ci#else
448c2ecf20Sopenharmony_cistatic long long opt_reps = 100;
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic __thread __attribute__((tls_model("initial-exec")))
488c2ecf20Sopenharmony_ciunsigned int signals_delivered;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#ifndef BENCHMARK
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic __thread __attribute__((tls_model("initial-exec"), unused))
538c2ecf20Sopenharmony_ciunsigned int yield_mod_cnt, nr_abort;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define printf_verbose(fmt, ...)			\
568c2ecf20Sopenharmony_ci	do {						\
578c2ecf20Sopenharmony_ci		if (verbose)				\
588c2ecf20Sopenharmony_ci			printf(fmt, ## __VA_ARGS__);	\
598c2ecf20Sopenharmony_ci	} while (0)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#ifdef __i386__
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define INJECT_ASM_REG	"eax"
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \
668c2ecf20Sopenharmony_ci	, INJECT_ASM_REG
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
698c2ecf20Sopenharmony_ci	"mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \
708c2ecf20Sopenharmony_ci	"test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
718c2ecf20Sopenharmony_ci	"jz 333f\n\t" \
728c2ecf20Sopenharmony_ci	"222:\n\t" \
738c2ecf20Sopenharmony_ci	"dec %%" INJECT_ASM_REG "\n\t" \
748c2ecf20Sopenharmony_ci	"jnz 222b\n\t" \
758c2ecf20Sopenharmony_ci	"333:\n\t"
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#elif defined(__x86_64__)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define INJECT_ASM_REG_P	"rax"
808c2ecf20Sopenharmony_ci#define INJECT_ASM_REG		"eax"
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \
838c2ecf20Sopenharmony_ci	, INJECT_ASM_REG_P \
848c2ecf20Sopenharmony_ci	, INJECT_ASM_REG
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
878c2ecf20Sopenharmony_ci	"lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \
888c2ecf20Sopenharmony_ci	"mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \
898c2ecf20Sopenharmony_ci	"test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \
908c2ecf20Sopenharmony_ci	"jz 333f\n\t" \
918c2ecf20Sopenharmony_ci	"222:\n\t" \
928c2ecf20Sopenharmony_ci	"dec %%" INJECT_ASM_REG "\n\t" \
938c2ecf20Sopenharmony_ci	"jnz 222b\n\t" \
948c2ecf20Sopenharmony_ci	"333:\n\t"
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#elif defined(__s390__)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define RSEQ_INJECT_INPUT \
998c2ecf20Sopenharmony_ci	, [loop_cnt_1]"m"(loop_cnt[1]) \
1008c2ecf20Sopenharmony_ci	, [loop_cnt_2]"m"(loop_cnt[2]) \
1018c2ecf20Sopenharmony_ci	, [loop_cnt_3]"m"(loop_cnt[3]) \
1028c2ecf20Sopenharmony_ci	, [loop_cnt_4]"m"(loop_cnt[4]) \
1038c2ecf20Sopenharmony_ci	, [loop_cnt_5]"m"(loop_cnt[5]) \
1048c2ecf20Sopenharmony_ci	, [loop_cnt_6]"m"(loop_cnt[6])
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define INJECT_ASM_REG	"r12"
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \
1098c2ecf20Sopenharmony_ci	, INJECT_ASM_REG
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
1128c2ecf20Sopenharmony_ci	"l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
1138c2ecf20Sopenharmony_ci	"ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \
1148c2ecf20Sopenharmony_ci	"je 333f\n\t" \
1158c2ecf20Sopenharmony_ci	"222:\n\t" \
1168c2ecf20Sopenharmony_ci	"ahi %%" INJECT_ASM_REG ", -1\n\t" \
1178c2ecf20Sopenharmony_ci	"jnz 222b\n\t" \
1188c2ecf20Sopenharmony_ci	"333:\n\t"
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci#elif defined(__ARMEL__)
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#define RSEQ_INJECT_INPUT \
1238c2ecf20Sopenharmony_ci	, [loop_cnt_1]"m"(loop_cnt[1]) \
1248c2ecf20Sopenharmony_ci	, [loop_cnt_2]"m"(loop_cnt[2]) \
1258c2ecf20Sopenharmony_ci	, [loop_cnt_3]"m"(loop_cnt[3]) \
1268c2ecf20Sopenharmony_ci	, [loop_cnt_4]"m"(loop_cnt[4]) \
1278c2ecf20Sopenharmony_ci	, [loop_cnt_5]"m"(loop_cnt[5]) \
1288c2ecf20Sopenharmony_ci	, [loop_cnt_6]"m"(loop_cnt[6])
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define INJECT_ASM_REG	"r4"
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \
1338c2ecf20Sopenharmony_ci	, INJECT_ASM_REG
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
1368c2ecf20Sopenharmony_ci	"ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
1378c2ecf20Sopenharmony_ci	"cmp " INJECT_ASM_REG ", #0\n\t" \
1388c2ecf20Sopenharmony_ci	"beq 333f\n\t" \
1398c2ecf20Sopenharmony_ci	"222:\n\t" \
1408c2ecf20Sopenharmony_ci	"subs " INJECT_ASM_REG ", #1\n\t" \
1418c2ecf20Sopenharmony_ci	"bne 222b\n\t" \
1428c2ecf20Sopenharmony_ci	"333:\n\t"
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#elif defined(__AARCH64EL__)
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci#define RSEQ_INJECT_INPUT \
1478c2ecf20Sopenharmony_ci	, [loop_cnt_1] "Qo" (loop_cnt[1]) \
1488c2ecf20Sopenharmony_ci	, [loop_cnt_2] "Qo" (loop_cnt[2]) \
1498c2ecf20Sopenharmony_ci	, [loop_cnt_3] "Qo" (loop_cnt[3]) \
1508c2ecf20Sopenharmony_ci	, [loop_cnt_4] "Qo" (loop_cnt[4]) \
1518c2ecf20Sopenharmony_ci	, [loop_cnt_5] "Qo" (loop_cnt[5]) \
1528c2ecf20Sopenharmony_ci	, [loop_cnt_6] "Qo" (loop_cnt[6])
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#define INJECT_ASM_REG	RSEQ_ASM_TMP_REG32
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
1578c2ecf20Sopenharmony_ci	"	ldr	" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n"	\
1588c2ecf20Sopenharmony_ci	"	cbz	" INJECT_ASM_REG ", 333f\n"			\
1598c2ecf20Sopenharmony_ci	"222:\n"							\
1608c2ecf20Sopenharmony_ci	"	sub	" INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n"	\
1618c2ecf20Sopenharmony_ci	"	cbnz	" INJECT_ASM_REG ", 222b\n"			\
1628c2ecf20Sopenharmony_ci	"333:\n"
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci#elif defined(__PPC__)
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#define RSEQ_INJECT_INPUT \
1678c2ecf20Sopenharmony_ci	, [loop_cnt_1]"m"(loop_cnt[1]) \
1688c2ecf20Sopenharmony_ci	, [loop_cnt_2]"m"(loop_cnt[2]) \
1698c2ecf20Sopenharmony_ci	, [loop_cnt_3]"m"(loop_cnt[3]) \
1708c2ecf20Sopenharmony_ci	, [loop_cnt_4]"m"(loop_cnt[4]) \
1718c2ecf20Sopenharmony_ci	, [loop_cnt_5]"m"(loop_cnt[5]) \
1728c2ecf20Sopenharmony_ci	, [loop_cnt_6]"m"(loop_cnt[6])
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci#define INJECT_ASM_REG	"r18"
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \
1778c2ecf20Sopenharmony_ci	, INJECT_ASM_REG
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
1808c2ecf20Sopenharmony_ci	"lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
1818c2ecf20Sopenharmony_ci	"cmpwi %%" INJECT_ASM_REG ", 0\n\t" \
1828c2ecf20Sopenharmony_ci	"beq 333f\n\t" \
1838c2ecf20Sopenharmony_ci	"222:\n\t" \
1848c2ecf20Sopenharmony_ci	"subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \
1858c2ecf20Sopenharmony_ci	"bne 222b\n\t" \
1868c2ecf20Sopenharmony_ci	"333:\n\t"
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci#elif defined(__mips__)
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci#define RSEQ_INJECT_INPUT \
1918c2ecf20Sopenharmony_ci	, [loop_cnt_1]"m"(loop_cnt[1]) \
1928c2ecf20Sopenharmony_ci	, [loop_cnt_2]"m"(loop_cnt[2]) \
1938c2ecf20Sopenharmony_ci	, [loop_cnt_3]"m"(loop_cnt[3]) \
1948c2ecf20Sopenharmony_ci	, [loop_cnt_4]"m"(loop_cnt[4]) \
1958c2ecf20Sopenharmony_ci	, [loop_cnt_5]"m"(loop_cnt[5]) \
1968c2ecf20Sopenharmony_ci	, [loop_cnt_6]"m"(loop_cnt[6])
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci#define INJECT_ASM_REG	"$5"
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \
2018c2ecf20Sopenharmony_ci	, INJECT_ASM_REG
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \
2048c2ecf20Sopenharmony_ci	"lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \
2058c2ecf20Sopenharmony_ci	"beqz " INJECT_ASM_REG ", 333f\n\t" \
2068c2ecf20Sopenharmony_ci	"222:\n\t" \
2078c2ecf20Sopenharmony_ci	"addiu " INJECT_ASM_REG ", -1\n\t" \
2088c2ecf20Sopenharmony_ci	"bnez " INJECT_ASM_REG ", 222b\n\t" \
2098c2ecf20Sopenharmony_ci	"333:\n\t"
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci#else
2128c2ecf20Sopenharmony_ci#error unsupported target
2138c2ecf20Sopenharmony_ci#endif
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci#define RSEQ_INJECT_FAILED \
2168c2ecf20Sopenharmony_ci	nr_abort++;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci#define RSEQ_INJECT_C(n) \
2198c2ecf20Sopenharmony_ci{ \
2208c2ecf20Sopenharmony_ci	int loc_i, loc_nr_loops = loop_cnt[n]; \
2218c2ecf20Sopenharmony_ci	\
2228c2ecf20Sopenharmony_ci	for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \
2238c2ecf20Sopenharmony_ci		rseq_barrier(); \
2248c2ecf20Sopenharmony_ci	} \
2258c2ecf20Sopenharmony_ci	if (loc_nr_loops == -1 && opt_modulo) { \
2268c2ecf20Sopenharmony_ci		if (yield_mod_cnt == opt_modulo - 1) { \
2278c2ecf20Sopenharmony_ci			if (opt_sleep > 0) \
2288c2ecf20Sopenharmony_ci				poll(NULL, 0, opt_sleep); \
2298c2ecf20Sopenharmony_ci			if (opt_yield) \
2308c2ecf20Sopenharmony_ci				sched_yield(); \
2318c2ecf20Sopenharmony_ci			if (opt_signal) \
2328c2ecf20Sopenharmony_ci				raise(SIGUSR1); \
2338c2ecf20Sopenharmony_ci			yield_mod_cnt = 0; \
2348c2ecf20Sopenharmony_ci		} else { \
2358c2ecf20Sopenharmony_ci			yield_mod_cnt++; \
2368c2ecf20Sopenharmony_ci		} \
2378c2ecf20Sopenharmony_ci	} \
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci#else
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci#define printf_verbose(fmt, ...)
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci#endif /* BENCHMARK */
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci#include "rseq.h"
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistruct percpu_lock_entry {
2498c2ecf20Sopenharmony_ci	intptr_t v;
2508c2ecf20Sopenharmony_ci} __attribute__((aligned(128)));
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistruct percpu_lock {
2538c2ecf20Sopenharmony_ci	struct percpu_lock_entry c[CPU_SETSIZE];
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistruct test_data_entry {
2578c2ecf20Sopenharmony_ci	intptr_t count;
2588c2ecf20Sopenharmony_ci} __attribute__((aligned(128)));
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistruct spinlock_test_data {
2618c2ecf20Sopenharmony_ci	struct percpu_lock lock;
2628c2ecf20Sopenharmony_ci	struct test_data_entry c[CPU_SETSIZE];
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistruct spinlock_thread_test_data {
2668c2ecf20Sopenharmony_ci	struct spinlock_test_data *data;
2678c2ecf20Sopenharmony_ci	long long reps;
2688c2ecf20Sopenharmony_ci	int reg;
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistruct inc_test_data {
2728c2ecf20Sopenharmony_ci	struct test_data_entry c[CPU_SETSIZE];
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistruct inc_thread_test_data {
2768c2ecf20Sopenharmony_ci	struct inc_test_data *data;
2778c2ecf20Sopenharmony_ci	long long reps;
2788c2ecf20Sopenharmony_ci	int reg;
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistruct percpu_list_node {
2828c2ecf20Sopenharmony_ci	intptr_t data;
2838c2ecf20Sopenharmony_ci	struct percpu_list_node *next;
2848c2ecf20Sopenharmony_ci};
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistruct percpu_list_entry {
2878c2ecf20Sopenharmony_ci	struct percpu_list_node *head;
2888c2ecf20Sopenharmony_ci} __attribute__((aligned(128)));
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistruct percpu_list {
2918c2ecf20Sopenharmony_ci	struct percpu_list_entry c[CPU_SETSIZE];
2928c2ecf20Sopenharmony_ci};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci#define BUFFER_ITEM_PER_CPU	100
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistruct percpu_buffer_node {
2978c2ecf20Sopenharmony_ci	intptr_t data;
2988c2ecf20Sopenharmony_ci};
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistruct percpu_buffer_entry {
3018c2ecf20Sopenharmony_ci	intptr_t offset;
3028c2ecf20Sopenharmony_ci	intptr_t buflen;
3038c2ecf20Sopenharmony_ci	struct percpu_buffer_node **array;
3048c2ecf20Sopenharmony_ci} __attribute__((aligned(128)));
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistruct percpu_buffer {
3078c2ecf20Sopenharmony_ci	struct percpu_buffer_entry c[CPU_SETSIZE];
3088c2ecf20Sopenharmony_ci};
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci#define MEMCPY_BUFFER_ITEM_PER_CPU	100
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistruct percpu_memcpy_buffer_node {
3138c2ecf20Sopenharmony_ci	intptr_t data1;
3148c2ecf20Sopenharmony_ci	uint64_t data2;
3158c2ecf20Sopenharmony_ci};
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistruct percpu_memcpy_buffer_entry {
3188c2ecf20Sopenharmony_ci	intptr_t offset;
3198c2ecf20Sopenharmony_ci	intptr_t buflen;
3208c2ecf20Sopenharmony_ci	struct percpu_memcpy_buffer_node *array;
3218c2ecf20Sopenharmony_ci} __attribute__((aligned(128)));
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistruct percpu_memcpy_buffer {
3248c2ecf20Sopenharmony_ci	struct percpu_memcpy_buffer_entry c[CPU_SETSIZE];
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/* A simple percpu spinlock. Grabs lock on current cpu. */
3288c2ecf20Sopenharmony_cistatic int rseq_this_cpu_lock(struct percpu_lock *lock)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	int cpu;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	for (;;) {
3338c2ecf20Sopenharmony_ci		int ret;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
3368c2ecf20Sopenharmony_ci		ret = rseq_cmpeqv_storev(&lock->c[cpu].v,
3378c2ecf20Sopenharmony_ci					 0, 1, cpu);
3388c2ecf20Sopenharmony_ci		if (rseq_likely(!ret))
3398c2ecf20Sopenharmony_ci			break;
3408c2ecf20Sopenharmony_ci		/* Retry if comparison fails or rseq aborts. */
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci	/*
3438c2ecf20Sopenharmony_ci	 * Acquire semantic when taking lock after control dependency.
3448c2ecf20Sopenharmony_ci	 * Matches rseq_smp_store_release().
3458c2ecf20Sopenharmony_ci	 */
3468c2ecf20Sopenharmony_ci	rseq_smp_acquire__after_ctrl_dep();
3478c2ecf20Sopenharmony_ci	return cpu;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void rseq_percpu_unlock(struct percpu_lock *lock, int cpu)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	assert(lock->c[cpu].v == 1);
3538c2ecf20Sopenharmony_ci	/*
3548c2ecf20Sopenharmony_ci	 * Release lock, with release semantic. Matches
3558c2ecf20Sopenharmony_ci	 * rseq_smp_acquire__after_ctrl_dep().
3568c2ecf20Sopenharmony_ci	 */
3578c2ecf20Sopenharmony_ci	rseq_smp_store_release(&lock->c[cpu].v, 0);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_civoid *test_percpu_spinlock_thread(void *arg)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct spinlock_thread_test_data *thread_data = arg;
3638c2ecf20Sopenharmony_ci	struct spinlock_test_data *data = thread_data->data;
3648c2ecf20Sopenharmony_ci	long long i, reps;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && thread_data->reg &&
3678c2ecf20Sopenharmony_ci	    rseq_register_current_thread())
3688c2ecf20Sopenharmony_ci		abort();
3698c2ecf20Sopenharmony_ci	reps = thread_data->reps;
3708c2ecf20Sopenharmony_ci	for (i = 0; i < reps; i++) {
3718c2ecf20Sopenharmony_ci		int cpu = rseq_this_cpu_lock(&data->lock);
3728c2ecf20Sopenharmony_ci		data->c[cpu].count++;
3738c2ecf20Sopenharmony_ci		rseq_percpu_unlock(&data->lock, cpu);
3748c2ecf20Sopenharmony_ci#ifndef BENCHMARK
3758c2ecf20Sopenharmony_ci		if (i != 0 && !(i % (reps / 10)))
3768c2ecf20Sopenharmony_ci			printf_verbose("tid %d: count %lld\n",
3778c2ecf20Sopenharmony_ci				       (int) rseq_gettid(), i);
3788c2ecf20Sopenharmony_ci#endif
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
3818c2ecf20Sopenharmony_ci		       (int) rseq_gettid(), nr_abort, signals_delivered);
3828c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && thread_data->reg &&
3838c2ecf20Sopenharmony_ci	    rseq_unregister_current_thread())
3848c2ecf20Sopenharmony_ci		abort();
3858c2ecf20Sopenharmony_ci	return NULL;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci/*
3898c2ecf20Sopenharmony_ci * A simple test which implements a sharded counter using a per-cpu
3908c2ecf20Sopenharmony_ci * lock.  Obviously real applications might prefer to simply use a
3918c2ecf20Sopenharmony_ci * per-cpu increment; however, this is reasonable for a test and the
3928c2ecf20Sopenharmony_ci * lock can be extended to synchronize more complicated operations.
3938c2ecf20Sopenharmony_ci */
3948c2ecf20Sopenharmony_civoid test_percpu_spinlock(void)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	const int num_threads = opt_threads;
3978c2ecf20Sopenharmony_ci	int i, ret;
3988c2ecf20Sopenharmony_ci	uint64_t sum;
3998c2ecf20Sopenharmony_ci	pthread_t test_threads[num_threads];
4008c2ecf20Sopenharmony_ci	struct spinlock_test_data data;
4018c2ecf20Sopenharmony_ci	struct spinlock_thread_test_data thread_data[num_threads];
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	memset(&data, 0, sizeof(data));
4048c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
4058c2ecf20Sopenharmony_ci		thread_data[i].reps = opt_reps;
4068c2ecf20Sopenharmony_ci		if (opt_disable_mod <= 0 || (i % opt_disable_mod))
4078c2ecf20Sopenharmony_ci			thread_data[i].reg = 1;
4088c2ecf20Sopenharmony_ci		else
4098c2ecf20Sopenharmony_ci			thread_data[i].reg = 0;
4108c2ecf20Sopenharmony_ci		thread_data[i].data = &data;
4118c2ecf20Sopenharmony_ci		ret = pthread_create(&test_threads[i], NULL,
4128c2ecf20Sopenharmony_ci				     test_percpu_spinlock_thread,
4138c2ecf20Sopenharmony_ci				     &thread_data[i]);
4148c2ecf20Sopenharmony_ci		if (ret) {
4158c2ecf20Sopenharmony_ci			errno = ret;
4168c2ecf20Sopenharmony_ci			perror("pthread_create");
4178c2ecf20Sopenharmony_ci			abort();
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
4228c2ecf20Sopenharmony_ci		ret = pthread_join(test_threads[i], NULL);
4238c2ecf20Sopenharmony_ci		if (ret) {
4248c2ecf20Sopenharmony_ci			errno = ret;
4258c2ecf20Sopenharmony_ci			perror("pthread_join");
4268c2ecf20Sopenharmony_ci			abort();
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	sum = 0;
4318c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++)
4328c2ecf20Sopenharmony_ci		sum += data.c[i].count;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	assert(sum == (uint64_t)opt_reps * num_threads);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_civoid *test_percpu_inc_thread(void *arg)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct inc_thread_test_data *thread_data = arg;
4408c2ecf20Sopenharmony_ci	struct inc_test_data *data = thread_data->data;
4418c2ecf20Sopenharmony_ci	long long i, reps;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && thread_data->reg &&
4448c2ecf20Sopenharmony_ci	    rseq_register_current_thread())
4458c2ecf20Sopenharmony_ci		abort();
4468c2ecf20Sopenharmony_ci	reps = thread_data->reps;
4478c2ecf20Sopenharmony_ci	for (i = 0; i < reps; i++) {
4488c2ecf20Sopenharmony_ci		int ret;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		do {
4518c2ecf20Sopenharmony_ci			int cpu;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci			cpu = rseq_cpu_start();
4548c2ecf20Sopenharmony_ci			ret = rseq_addv(&data->c[cpu].count, 1, cpu);
4558c2ecf20Sopenharmony_ci		} while (rseq_unlikely(ret));
4568c2ecf20Sopenharmony_ci#ifndef BENCHMARK
4578c2ecf20Sopenharmony_ci		if (i != 0 && !(i % (reps / 10)))
4588c2ecf20Sopenharmony_ci			printf_verbose("tid %d: count %lld\n",
4598c2ecf20Sopenharmony_ci				       (int) rseq_gettid(), i);
4608c2ecf20Sopenharmony_ci#endif
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
4638c2ecf20Sopenharmony_ci		       (int) rseq_gettid(), nr_abort, signals_delivered);
4648c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && thread_data->reg &&
4658c2ecf20Sopenharmony_ci	    rseq_unregister_current_thread())
4668c2ecf20Sopenharmony_ci		abort();
4678c2ecf20Sopenharmony_ci	return NULL;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_civoid test_percpu_inc(void)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	const int num_threads = opt_threads;
4738c2ecf20Sopenharmony_ci	int i, ret;
4748c2ecf20Sopenharmony_ci	uint64_t sum;
4758c2ecf20Sopenharmony_ci	pthread_t test_threads[num_threads];
4768c2ecf20Sopenharmony_ci	struct inc_test_data data;
4778c2ecf20Sopenharmony_ci	struct inc_thread_test_data thread_data[num_threads];
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	memset(&data, 0, sizeof(data));
4808c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
4818c2ecf20Sopenharmony_ci		thread_data[i].reps = opt_reps;
4828c2ecf20Sopenharmony_ci		if (opt_disable_mod <= 0 || (i % opt_disable_mod))
4838c2ecf20Sopenharmony_ci			thread_data[i].reg = 1;
4848c2ecf20Sopenharmony_ci		else
4858c2ecf20Sopenharmony_ci			thread_data[i].reg = 0;
4868c2ecf20Sopenharmony_ci		thread_data[i].data = &data;
4878c2ecf20Sopenharmony_ci		ret = pthread_create(&test_threads[i], NULL,
4888c2ecf20Sopenharmony_ci				     test_percpu_inc_thread,
4898c2ecf20Sopenharmony_ci				     &thread_data[i]);
4908c2ecf20Sopenharmony_ci		if (ret) {
4918c2ecf20Sopenharmony_ci			errno = ret;
4928c2ecf20Sopenharmony_ci			perror("pthread_create");
4938c2ecf20Sopenharmony_ci			abort();
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
4988c2ecf20Sopenharmony_ci		ret = pthread_join(test_threads[i], NULL);
4998c2ecf20Sopenharmony_ci		if (ret) {
5008c2ecf20Sopenharmony_ci			errno = ret;
5018c2ecf20Sopenharmony_ci			perror("pthread_join");
5028c2ecf20Sopenharmony_ci			abort();
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	sum = 0;
5078c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++)
5088c2ecf20Sopenharmony_ci		sum += data.c[i].count;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	assert(sum == (uint64_t)opt_reps * num_threads);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_civoid this_cpu_list_push(struct percpu_list *list,
5148c2ecf20Sopenharmony_ci			struct percpu_list_node *node,
5158c2ecf20Sopenharmony_ci			int *_cpu)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	int cpu;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	for (;;) {
5208c2ecf20Sopenharmony_ci		intptr_t *targetptr, newval, expect;
5218c2ecf20Sopenharmony_ci		int ret;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
5248c2ecf20Sopenharmony_ci		/* Load list->c[cpu].head with single-copy atomicity. */
5258c2ecf20Sopenharmony_ci		expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head);
5268c2ecf20Sopenharmony_ci		newval = (intptr_t)node;
5278c2ecf20Sopenharmony_ci		targetptr = (intptr_t *)&list->c[cpu].head;
5288c2ecf20Sopenharmony_ci		node->next = (struct percpu_list_node *)expect;
5298c2ecf20Sopenharmony_ci		ret = rseq_cmpeqv_storev(targetptr, expect, newval, cpu);
5308c2ecf20Sopenharmony_ci		if (rseq_likely(!ret))
5318c2ecf20Sopenharmony_ci			break;
5328c2ecf20Sopenharmony_ci		/* Retry if comparison fails or rseq aborts. */
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	if (_cpu)
5358c2ecf20Sopenharmony_ci		*_cpu = cpu;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci/*
5398c2ecf20Sopenharmony_ci * Unlike a traditional lock-less linked list; the availability of a
5408c2ecf20Sopenharmony_ci * rseq primitive allows us to implement pop without concerns over
5418c2ecf20Sopenharmony_ci * ABA-type races.
5428c2ecf20Sopenharmony_ci */
5438c2ecf20Sopenharmony_cistruct percpu_list_node *this_cpu_list_pop(struct percpu_list *list,
5448c2ecf20Sopenharmony_ci					   int *_cpu)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct percpu_list_node *node = NULL;
5478c2ecf20Sopenharmony_ci	int cpu;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	for (;;) {
5508c2ecf20Sopenharmony_ci		struct percpu_list_node *head;
5518c2ecf20Sopenharmony_ci		intptr_t *targetptr, expectnot, *load;
5528c2ecf20Sopenharmony_ci		long offset;
5538c2ecf20Sopenharmony_ci		int ret;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
5568c2ecf20Sopenharmony_ci		targetptr = (intptr_t *)&list->c[cpu].head;
5578c2ecf20Sopenharmony_ci		expectnot = (intptr_t)NULL;
5588c2ecf20Sopenharmony_ci		offset = offsetof(struct percpu_list_node, next);
5598c2ecf20Sopenharmony_ci		load = (intptr_t *)&head;
5608c2ecf20Sopenharmony_ci		ret = rseq_cmpnev_storeoffp_load(targetptr, expectnot,
5618c2ecf20Sopenharmony_ci						   offset, load, cpu);
5628c2ecf20Sopenharmony_ci		if (rseq_likely(!ret)) {
5638c2ecf20Sopenharmony_ci			node = head;
5648c2ecf20Sopenharmony_ci			break;
5658c2ecf20Sopenharmony_ci		}
5668c2ecf20Sopenharmony_ci		if (ret > 0)
5678c2ecf20Sopenharmony_ci			break;
5688c2ecf20Sopenharmony_ci		/* Retry if rseq aborts. */
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci	if (_cpu)
5718c2ecf20Sopenharmony_ci		*_cpu = cpu;
5728c2ecf20Sopenharmony_ci	return node;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci/*
5768c2ecf20Sopenharmony_ci * __percpu_list_pop is not safe against concurrent accesses. Should
5778c2ecf20Sopenharmony_ci * only be used on lists that are not concurrently modified.
5788c2ecf20Sopenharmony_ci */
5798c2ecf20Sopenharmony_cistruct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct percpu_list_node *node;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	node = list->c[cpu].head;
5848c2ecf20Sopenharmony_ci	if (!node)
5858c2ecf20Sopenharmony_ci		return NULL;
5868c2ecf20Sopenharmony_ci	list->c[cpu].head = node->next;
5878c2ecf20Sopenharmony_ci	return node;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_civoid *test_percpu_list_thread(void *arg)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	long long i, reps;
5938c2ecf20Sopenharmony_ci	struct percpu_list *list = (struct percpu_list *)arg;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_register_current_thread())
5968c2ecf20Sopenharmony_ci		abort();
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	reps = opt_reps;
5998c2ecf20Sopenharmony_ci	for (i = 0; i < reps; i++) {
6008c2ecf20Sopenharmony_ci		struct percpu_list_node *node;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		node = this_cpu_list_pop(list, NULL);
6038c2ecf20Sopenharmony_ci		if (opt_yield)
6048c2ecf20Sopenharmony_ci			sched_yield();  /* encourage shuffling */
6058c2ecf20Sopenharmony_ci		if (node)
6068c2ecf20Sopenharmony_ci			this_cpu_list_push(list, node, NULL);
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
6108c2ecf20Sopenharmony_ci		       (int) rseq_gettid(), nr_abort, signals_delivered);
6118c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_unregister_current_thread())
6128c2ecf20Sopenharmony_ci		abort();
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return NULL;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci/* Simultaneous modification to a per-cpu linked list from many threads.  */
6188c2ecf20Sopenharmony_civoid test_percpu_list(void)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	const int num_threads = opt_threads;
6218c2ecf20Sopenharmony_ci	int i, j, ret;
6228c2ecf20Sopenharmony_ci	uint64_t sum = 0, expected_sum = 0;
6238c2ecf20Sopenharmony_ci	struct percpu_list list;
6248c2ecf20Sopenharmony_ci	pthread_t test_threads[num_threads];
6258c2ecf20Sopenharmony_ci	cpu_set_t allowed_cpus;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	memset(&list, 0, sizeof(list));
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* Generate list entries for every usable cpu. */
6308c2ecf20Sopenharmony_ci	sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
6318c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
6328c2ecf20Sopenharmony_ci		if (!CPU_ISSET(i, &allowed_cpus))
6338c2ecf20Sopenharmony_ci			continue;
6348c2ecf20Sopenharmony_ci		for (j = 1; j <= 100; j++) {
6358c2ecf20Sopenharmony_ci			struct percpu_list_node *node;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci			expected_sum += j;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci			node = malloc(sizeof(*node));
6408c2ecf20Sopenharmony_ci			assert(node);
6418c2ecf20Sopenharmony_ci			node->data = j;
6428c2ecf20Sopenharmony_ci			node->next = list.c[i].head;
6438c2ecf20Sopenharmony_ci			list.c[i].head = node;
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
6488c2ecf20Sopenharmony_ci		ret = pthread_create(&test_threads[i], NULL,
6498c2ecf20Sopenharmony_ci				     test_percpu_list_thread, &list);
6508c2ecf20Sopenharmony_ci		if (ret) {
6518c2ecf20Sopenharmony_ci			errno = ret;
6528c2ecf20Sopenharmony_ci			perror("pthread_create");
6538c2ecf20Sopenharmony_ci			abort();
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
6588c2ecf20Sopenharmony_ci		ret = pthread_join(test_threads[i], NULL);
6598c2ecf20Sopenharmony_ci		if (ret) {
6608c2ecf20Sopenharmony_ci			errno = ret;
6618c2ecf20Sopenharmony_ci			perror("pthread_join");
6628c2ecf20Sopenharmony_ci			abort();
6638c2ecf20Sopenharmony_ci		}
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
6678c2ecf20Sopenharmony_ci		struct percpu_list_node *node;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci		if (!CPU_ISSET(i, &allowed_cpus))
6708c2ecf20Sopenharmony_ci			continue;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		while ((node = __percpu_list_pop(&list, i))) {
6738c2ecf20Sopenharmony_ci			sum += node->data;
6748c2ecf20Sopenharmony_ci			free(node);
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/*
6798c2ecf20Sopenharmony_ci	 * All entries should now be accounted for (unless some external
6808c2ecf20Sopenharmony_ci	 * actor is interfering with our allowed affinity while this
6818c2ecf20Sopenharmony_ci	 * test is running).
6828c2ecf20Sopenharmony_ci	 */
6838c2ecf20Sopenharmony_ci	assert(sum == expected_sum);
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cibool this_cpu_buffer_push(struct percpu_buffer *buffer,
6878c2ecf20Sopenharmony_ci			  struct percpu_buffer_node *node,
6888c2ecf20Sopenharmony_ci			  int *_cpu)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	bool result = false;
6918c2ecf20Sopenharmony_ci	int cpu;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	for (;;) {
6948c2ecf20Sopenharmony_ci		intptr_t *targetptr_spec, newval_spec;
6958c2ecf20Sopenharmony_ci		intptr_t *targetptr_final, newval_final;
6968c2ecf20Sopenharmony_ci		intptr_t offset;
6978c2ecf20Sopenharmony_ci		int ret;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
7008c2ecf20Sopenharmony_ci		offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
7018c2ecf20Sopenharmony_ci		if (offset == buffer->c[cpu].buflen)
7028c2ecf20Sopenharmony_ci			break;
7038c2ecf20Sopenharmony_ci		newval_spec = (intptr_t)node;
7048c2ecf20Sopenharmony_ci		targetptr_spec = (intptr_t *)&buffer->c[cpu].array[offset];
7058c2ecf20Sopenharmony_ci		newval_final = offset + 1;
7068c2ecf20Sopenharmony_ci		targetptr_final = &buffer->c[cpu].offset;
7078c2ecf20Sopenharmony_ci		if (opt_mb)
7088c2ecf20Sopenharmony_ci			ret = rseq_cmpeqv_trystorev_storev_release(
7098c2ecf20Sopenharmony_ci				targetptr_final, offset, targetptr_spec,
7108c2ecf20Sopenharmony_ci				newval_spec, newval_final, cpu);
7118c2ecf20Sopenharmony_ci		else
7128c2ecf20Sopenharmony_ci			ret = rseq_cmpeqv_trystorev_storev(targetptr_final,
7138c2ecf20Sopenharmony_ci				offset, targetptr_spec, newval_spec,
7148c2ecf20Sopenharmony_ci				newval_final, cpu);
7158c2ecf20Sopenharmony_ci		if (rseq_likely(!ret)) {
7168c2ecf20Sopenharmony_ci			result = true;
7178c2ecf20Sopenharmony_ci			break;
7188c2ecf20Sopenharmony_ci		}
7198c2ecf20Sopenharmony_ci		/* Retry if comparison fails or rseq aborts. */
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci	if (_cpu)
7228c2ecf20Sopenharmony_ci		*_cpu = cpu;
7238c2ecf20Sopenharmony_ci	return result;
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistruct percpu_buffer_node *this_cpu_buffer_pop(struct percpu_buffer *buffer,
7278c2ecf20Sopenharmony_ci					       int *_cpu)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	struct percpu_buffer_node *head;
7308c2ecf20Sopenharmony_ci	int cpu;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	for (;;) {
7338c2ecf20Sopenharmony_ci		intptr_t *targetptr, newval;
7348c2ecf20Sopenharmony_ci		intptr_t offset;
7358c2ecf20Sopenharmony_ci		int ret;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
7388c2ecf20Sopenharmony_ci		/* Load offset with single-copy atomicity. */
7398c2ecf20Sopenharmony_ci		offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
7408c2ecf20Sopenharmony_ci		if (offset == 0) {
7418c2ecf20Sopenharmony_ci			head = NULL;
7428c2ecf20Sopenharmony_ci			break;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci		head = RSEQ_READ_ONCE(buffer->c[cpu].array[offset - 1]);
7458c2ecf20Sopenharmony_ci		newval = offset - 1;
7468c2ecf20Sopenharmony_ci		targetptr = (intptr_t *)&buffer->c[cpu].offset;
7478c2ecf20Sopenharmony_ci		ret = rseq_cmpeqv_cmpeqv_storev(targetptr, offset,
7488c2ecf20Sopenharmony_ci			(intptr_t *)&buffer->c[cpu].array[offset - 1],
7498c2ecf20Sopenharmony_ci			(intptr_t)head, newval, cpu);
7508c2ecf20Sopenharmony_ci		if (rseq_likely(!ret))
7518c2ecf20Sopenharmony_ci			break;
7528c2ecf20Sopenharmony_ci		/* Retry if comparison fails or rseq aborts. */
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci	if (_cpu)
7558c2ecf20Sopenharmony_ci		*_cpu = cpu;
7568c2ecf20Sopenharmony_ci	return head;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci/*
7608c2ecf20Sopenharmony_ci * __percpu_buffer_pop is not safe against concurrent accesses. Should
7618c2ecf20Sopenharmony_ci * only be used on buffers that are not concurrently modified.
7628c2ecf20Sopenharmony_ci */
7638c2ecf20Sopenharmony_cistruct percpu_buffer_node *__percpu_buffer_pop(struct percpu_buffer *buffer,
7648c2ecf20Sopenharmony_ci					       int cpu)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	struct percpu_buffer_node *head;
7678c2ecf20Sopenharmony_ci	intptr_t offset;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	offset = buffer->c[cpu].offset;
7708c2ecf20Sopenharmony_ci	if (offset == 0)
7718c2ecf20Sopenharmony_ci		return NULL;
7728c2ecf20Sopenharmony_ci	head = buffer->c[cpu].array[offset - 1];
7738c2ecf20Sopenharmony_ci	buffer->c[cpu].offset = offset - 1;
7748c2ecf20Sopenharmony_ci	return head;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_civoid *test_percpu_buffer_thread(void *arg)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	long long i, reps;
7808c2ecf20Sopenharmony_ci	struct percpu_buffer *buffer = (struct percpu_buffer *)arg;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_register_current_thread())
7838c2ecf20Sopenharmony_ci		abort();
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	reps = opt_reps;
7868c2ecf20Sopenharmony_ci	for (i = 0; i < reps; i++) {
7878c2ecf20Sopenharmony_ci		struct percpu_buffer_node *node;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		node = this_cpu_buffer_pop(buffer, NULL);
7908c2ecf20Sopenharmony_ci		if (opt_yield)
7918c2ecf20Sopenharmony_ci			sched_yield();  /* encourage shuffling */
7928c2ecf20Sopenharmony_ci		if (node) {
7938c2ecf20Sopenharmony_ci			if (!this_cpu_buffer_push(buffer, node, NULL)) {
7948c2ecf20Sopenharmony_ci				/* Should increase buffer size. */
7958c2ecf20Sopenharmony_ci				abort();
7968c2ecf20Sopenharmony_ci			}
7978c2ecf20Sopenharmony_ci		}
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
8018c2ecf20Sopenharmony_ci		       (int) rseq_gettid(), nr_abort, signals_delivered);
8028c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_unregister_current_thread())
8038c2ecf20Sopenharmony_ci		abort();
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return NULL;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci/* Simultaneous modification to a per-cpu buffer from many threads.  */
8098c2ecf20Sopenharmony_civoid test_percpu_buffer(void)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	const int num_threads = opt_threads;
8128c2ecf20Sopenharmony_ci	int i, j, ret;
8138c2ecf20Sopenharmony_ci	uint64_t sum = 0, expected_sum = 0;
8148c2ecf20Sopenharmony_ci	struct percpu_buffer buffer;
8158c2ecf20Sopenharmony_ci	pthread_t test_threads[num_threads];
8168c2ecf20Sopenharmony_ci	cpu_set_t allowed_cpus;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	memset(&buffer, 0, sizeof(buffer));
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/* Generate list entries for every usable cpu. */
8218c2ecf20Sopenharmony_ci	sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
8228c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
8238c2ecf20Sopenharmony_ci		if (!CPU_ISSET(i, &allowed_cpus))
8248c2ecf20Sopenharmony_ci			continue;
8258c2ecf20Sopenharmony_ci		/* Worse-case is every item in same CPU. */
8268c2ecf20Sopenharmony_ci		buffer.c[i].array =
8278c2ecf20Sopenharmony_ci			malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
8288c2ecf20Sopenharmony_ci			       BUFFER_ITEM_PER_CPU);
8298c2ecf20Sopenharmony_ci		assert(buffer.c[i].array);
8308c2ecf20Sopenharmony_ci		buffer.c[i].buflen = CPU_SETSIZE * BUFFER_ITEM_PER_CPU;
8318c2ecf20Sopenharmony_ci		for (j = 1; j <= BUFFER_ITEM_PER_CPU; j++) {
8328c2ecf20Sopenharmony_ci			struct percpu_buffer_node *node;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci			expected_sum += j;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci			/*
8378c2ecf20Sopenharmony_ci			 * We could theoretically put the word-sized
8388c2ecf20Sopenharmony_ci			 * "data" directly in the buffer. However, we
8398c2ecf20Sopenharmony_ci			 * want to model objects that would not fit
8408c2ecf20Sopenharmony_ci			 * within a single word, so allocate an object
8418c2ecf20Sopenharmony_ci			 * for each node.
8428c2ecf20Sopenharmony_ci			 */
8438c2ecf20Sopenharmony_ci			node = malloc(sizeof(*node));
8448c2ecf20Sopenharmony_ci			assert(node);
8458c2ecf20Sopenharmony_ci			node->data = j;
8468c2ecf20Sopenharmony_ci			buffer.c[i].array[j - 1] = node;
8478c2ecf20Sopenharmony_ci			buffer.c[i].offset++;
8488c2ecf20Sopenharmony_ci		}
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
8528c2ecf20Sopenharmony_ci		ret = pthread_create(&test_threads[i], NULL,
8538c2ecf20Sopenharmony_ci				     test_percpu_buffer_thread, &buffer);
8548c2ecf20Sopenharmony_ci		if (ret) {
8558c2ecf20Sopenharmony_ci			errno = ret;
8568c2ecf20Sopenharmony_ci			perror("pthread_create");
8578c2ecf20Sopenharmony_ci			abort();
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
8628c2ecf20Sopenharmony_ci		ret = pthread_join(test_threads[i], NULL);
8638c2ecf20Sopenharmony_ci		if (ret) {
8648c2ecf20Sopenharmony_ci			errno = ret;
8658c2ecf20Sopenharmony_ci			perror("pthread_join");
8668c2ecf20Sopenharmony_ci			abort();
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
8718c2ecf20Sopenharmony_ci		struct percpu_buffer_node *node;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		if (!CPU_ISSET(i, &allowed_cpus))
8748c2ecf20Sopenharmony_ci			continue;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci		while ((node = __percpu_buffer_pop(&buffer, i))) {
8778c2ecf20Sopenharmony_ci			sum += node->data;
8788c2ecf20Sopenharmony_ci			free(node);
8798c2ecf20Sopenharmony_ci		}
8808c2ecf20Sopenharmony_ci		free(buffer.c[i].array);
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/*
8848c2ecf20Sopenharmony_ci	 * All entries should now be accounted for (unless some external
8858c2ecf20Sopenharmony_ci	 * actor is interfering with our allowed affinity while this
8868c2ecf20Sopenharmony_ci	 * test is running).
8878c2ecf20Sopenharmony_ci	 */
8888c2ecf20Sopenharmony_ci	assert(sum == expected_sum);
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cibool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer *buffer,
8928c2ecf20Sopenharmony_ci				 struct percpu_memcpy_buffer_node item,
8938c2ecf20Sopenharmony_ci				 int *_cpu)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	bool result = false;
8968c2ecf20Sopenharmony_ci	int cpu;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	for (;;) {
8998c2ecf20Sopenharmony_ci		intptr_t *targetptr_final, newval_final, offset;
9008c2ecf20Sopenharmony_ci		char *destptr, *srcptr;
9018c2ecf20Sopenharmony_ci		size_t copylen;
9028c2ecf20Sopenharmony_ci		int ret;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
9058c2ecf20Sopenharmony_ci		/* Load offset with single-copy atomicity. */
9068c2ecf20Sopenharmony_ci		offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
9078c2ecf20Sopenharmony_ci		if (offset == buffer->c[cpu].buflen)
9088c2ecf20Sopenharmony_ci			break;
9098c2ecf20Sopenharmony_ci		destptr = (char *)&buffer->c[cpu].array[offset];
9108c2ecf20Sopenharmony_ci		srcptr = (char *)&item;
9118c2ecf20Sopenharmony_ci		/* copylen must be <= 4kB. */
9128c2ecf20Sopenharmony_ci		copylen = sizeof(item);
9138c2ecf20Sopenharmony_ci		newval_final = offset + 1;
9148c2ecf20Sopenharmony_ci		targetptr_final = &buffer->c[cpu].offset;
9158c2ecf20Sopenharmony_ci		if (opt_mb)
9168c2ecf20Sopenharmony_ci			ret = rseq_cmpeqv_trymemcpy_storev_release(
9178c2ecf20Sopenharmony_ci				targetptr_final, offset,
9188c2ecf20Sopenharmony_ci				destptr, srcptr, copylen,
9198c2ecf20Sopenharmony_ci				newval_final, cpu);
9208c2ecf20Sopenharmony_ci		else
9218c2ecf20Sopenharmony_ci			ret = rseq_cmpeqv_trymemcpy_storev(targetptr_final,
9228c2ecf20Sopenharmony_ci				offset, destptr, srcptr, copylen,
9238c2ecf20Sopenharmony_ci				newval_final, cpu);
9248c2ecf20Sopenharmony_ci		if (rseq_likely(!ret)) {
9258c2ecf20Sopenharmony_ci			result = true;
9268c2ecf20Sopenharmony_ci			break;
9278c2ecf20Sopenharmony_ci		}
9288c2ecf20Sopenharmony_ci		/* Retry if comparison fails or rseq aborts. */
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci	if (_cpu)
9318c2ecf20Sopenharmony_ci		*_cpu = cpu;
9328c2ecf20Sopenharmony_ci	return result;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cibool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
9368c2ecf20Sopenharmony_ci				struct percpu_memcpy_buffer_node *item,
9378c2ecf20Sopenharmony_ci				int *_cpu)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	bool result = false;
9408c2ecf20Sopenharmony_ci	int cpu;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	for (;;) {
9438c2ecf20Sopenharmony_ci		intptr_t *targetptr_final, newval_final, offset;
9448c2ecf20Sopenharmony_ci		char *destptr, *srcptr;
9458c2ecf20Sopenharmony_ci		size_t copylen;
9468c2ecf20Sopenharmony_ci		int ret;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		cpu = rseq_cpu_start();
9498c2ecf20Sopenharmony_ci		/* Load offset with single-copy atomicity. */
9508c2ecf20Sopenharmony_ci		offset = RSEQ_READ_ONCE(buffer->c[cpu].offset);
9518c2ecf20Sopenharmony_ci		if (offset == 0)
9528c2ecf20Sopenharmony_ci			break;
9538c2ecf20Sopenharmony_ci		destptr = (char *)item;
9548c2ecf20Sopenharmony_ci		srcptr = (char *)&buffer->c[cpu].array[offset - 1];
9558c2ecf20Sopenharmony_ci		/* copylen must be <= 4kB. */
9568c2ecf20Sopenharmony_ci		copylen = sizeof(*item);
9578c2ecf20Sopenharmony_ci		newval_final = offset - 1;
9588c2ecf20Sopenharmony_ci		targetptr_final = &buffer->c[cpu].offset;
9598c2ecf20Sopenharmony_ci		ret = rseq_cmpeqv_trymemcpy_storev(targetptr_final,
9608c2ecf20Sopenharmony_ci			offset, destptr, srcptr, copylen,
9618c2ecf20Sopenharmony_ci			newval_final, cpu);
9628c2ecf20Sopenharmony_ci		if (rseq_likely(!ret)) {
9638c2ecf20Sopenharmony_ci			result = true;
9648c2ecf20Sopenharmony_ci			break;
9658c2ecf20Sopenharmony_ci		}
9668c2ecf20Sopenharmony_ci		/* Retry if comparison fails or rseq aborts. */
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci	if (_cpu)
9698c2ecf20Sopenharmony_ci		*_cpu = cpu;
9708c2ecf20Sopenharmony_ci	return result;
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci/*
9748c2ecf20Sopenharmony_ci * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should
9758c2ecf20Sopenharmony_ci * only be used on buffers that are not concurrently modified.
9768c2ecf20Sopenharmony_ci */
9778c2ecf20Sopenharmony_cibool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer,
9788c2ecf20Sopenharmony_ci				struct percpu_memcpy_buffer_node *item,
9798c2ecf20Sopenharmony_ci				int cpu)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	intptr_t offset;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	offset = buffer->c[cpu].offset;
9848c2ecf20Sopenharmony_ci	if (offset == 0)
9858c2ecf20Sopenharmony_ci		return false;
9868c2ecf20Sopenharmony_ci	memcpy(item, &buffer->c[cpu].array[offset - 1], sizeof(*item));
9878c2ecf20Sopenharmony_ci	buffer->c[cpu].offset = offset - 1;
9888c2ecf20Sopenharmony_ci	return true;
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_civoid *test_percpu_memcpy_buffer_thread(void *arg)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	long long i, reps;
9948c2ecf20Sopenharmony_ci	struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_register_current_thread())
9978c2ecf20Sopenharmony_ci		abort();
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	reps = opt_reps;
10008c2ecf20Sopenharmony_ci	for (i = 0; i < reps; i++) {
10018c2ecf20Sopenharmony_ci		struct percpu_memcpy_buffer_node item;
10028c2ecf20Sopenharmony_ci		bool result;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		result = this_cpu_memcpy_buffer_pop(buffer, &item, NULL);
10058c2ecf20Sopenharmony_ci		if (opt_yield)
10068c2ecf20Sopenharmony_ci			sched_yield();  /* encourage shuffling */
10078c2ecf20Sopenharmony_ci		if (result) {
10088c2ecf20Sopenharmony_ci			if (!this_cpu_memcpy_buffer_push(buffer, item, NULL)) {
10098c2ecf20Sopenharmony_ci				/* Should increase buffer size. */
10108c2ecf20Sopenharmony_ci				abort();
10118c2ecf20Sopenharmony_ci			}
10128c2ecf20Sopenharmony_ci		}
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
10168c2ecf20Sopenharmony_ci		       (int) rseq_gettid(), nr_abort, signals_delivered);
10178c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_unregister_current_thread())
10188c2ecf20Sopenharmony_ci		abort();
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	return NULL;
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci/* Simultaneous modification to a per-cpu buffer from many threads.  */
10248c2ecf20Sopenharmony_civoid test_percpu_memcpy_buffer(void)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	const int num_threads = opt_threads;
10278c2ecf20Sopenharmony_ci	int i, j, ret;
10288c2ecf20Sopenharmony_ci	uint64_t sum = 0, expected_sum = 0;
10298c2ecf20Sopenharmony_ci	struct percpu_memcpy_buffer buffer;
10308c2ecf20Sopenharmony_ci	pthread_t test_threads[num_threads];
10318c2ecf20Sopenharmony_ci	cpu_set_t allowed_cpus;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	memset(&buffer, 0, sizeof(buffer));
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	/* Generate list entries for every usable cpu. */
10368c2ecf20Sopenharmony_ci	sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
10378c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
10388c2ecf20Sopenharmony_ci		if (!CPU_ISSET(i, &allowed_cpus))
10398c2ecf20Sopenharmony_ci			continue;
10408c2ecf20Sopenharmony_ci		/* Worse-case is every item in same CPU. */
10418c2ecf20Sopenharmony_ci		buffer.c[i].array =
10428c2ecf20Sopenharmony_ci			malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE *
10438c2ecf20Sopenharmony_ci			       MEMCPY_BUFFER_ITEM_PER_CPU);
10448c2ecf20Sopenharmony_ci		assert(buffer.c[i].array);
10458c2ecf20Sopenharmony_ci		buffer.c[i].buflen = CPU_SETSIZE * MEMCPY_BUFFER_ITEM_PER_CPU;
10468c2ecf20Sopenharmony_ci		for (j = 1; j <= MEMCPY_BUFFER_ITEM_PER_CPU; j++) {
10478c2ecf20Sopenharmony_ci			expected_sum += 2 * j + 1;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci			/*
10508c2ecf20Sopenharmony_ci			 * We could theoretically put the word-sized
10518c2ecf20Sopenharmony_ci			 * "data" directly in the buffer. However, we
10528c2ecf20Sopenharmony_ci			 * want to model objects that would not fit
10538c2ecf20Sopenharmony_ci			 * within a single word, so allocate an object
10548c2ecf20Sopenharmony_ci			 * for each node.
10558c2ecf20Sopenharmony_ci			 */
10568c2ecf20Sopenharmony_ci			buffer.c[i].array[j - 1].data1 = j;
10578c2ecf20Sopenharmony_ci			buffer.c[i].array[j - 1].data2 = j + 1;
10588c2ecf20Sopenharmony_ci			buffer.c[i].offset++;
10598c2ecf20Sopenharmony_ci		}
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
10638c2ecf20Sopenharmony_ci		ret = pthread_create(&test_threads[i], NULL,
10648c2ecf20Sopenharmony_ci				     test_percpu_memcpy_buffer_thread,
10658c2ecf20Sopenharmony_ci				     &buffer);
10668c2ecf20Sopenharmony_ci		if (ret) {
10678c2ecf20Sopenharmony_ci			errno = ret;
10688c2ecf20Sopenharmony_ci			perror("pthread_create");
10698c2ecf20Sopenharmony_ci			abort();
10708c2ecf20Sopenharmony_ci		}
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
10748c2ecf20Sopenharmony_ci		ret = pthread_join(test_threads[i], NULL);
10758c2ecf20Sopenharmony_ci		if (ret) {
10768c2ecf20Sopenharmony_ci			errno = ret;
10778c2ecf20Sopenharmony_ci			perror("pthread_join");
10788c2ecf20Sopenharmony_ci			abort();
10798c2ecf20Sopenharmony_ci		}
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
10838c2ecf20Sopenharmony_ci		struct percpu_memcpy_buffer_node item;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci		if (!CPU_ISSET(i, &allowed_cpus))
10868c2ecf20Sopenharmony_ci			continue;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci		while (__percpu_memcpy_buffer_pop(&buffer, &item, i)) {
10898c2ecf20Sopenharmony_ci			sum += item.data1;
10908c2ecf20Sopenharmony_ci			sum += item.data2;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci		free(buffer.c[i].array);
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/*
10968c2ecf20Sopenharmony_ci	 * All entries should now be accounted for (unless some external
10978c2ecf20Sopenharmony_ci	 * actor is interfering with our allowed affinity while this
10988c2ecf20Sopenharmony_ci	 * test is running).
10998c2ecf20Sopenharmony_ci	 */
11008c2ecf20Sopenharmony_ci	assert(sum == expected_sum);
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_cistatic void test_signal_interrupt_handler(int signo)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	signals_delivered++;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int set_signal_handler(void)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	int ret = 0;
11118c2ecf20Sopenharmony_ci	struct sigaction sa;
11128c2ecf20Sopenharmony_ci	sigset_t sigset;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	ret = sigemptyset(&sigset);
11158c2ecf20Sopenharmony_ci	if (ret < 0) {
11168c2ecf20Sopenharmony_ci		perror("sigemptyset");
11178c2ecf20Sopenharmony_ci		return ret;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	sa.sa_handler = test_signal_interrupt_handler;
11218c2ecf20Sopenharmony_ci	sa.sa_mask = sigset;
11228c2ecf20Sopenharmony_ci	sa.sa_flags = 0;
11238c2ecf20Sopenharmony_ci	ret = sigaction(SIGUSR1, &sa, NULL);
11248c2ecf20Sopenharmony_ci	if (ret < 0) {
11258c2ecf20Sopenharmony_ci		perror("sigaction");
11268c2ecf20Sopenharmony_ci		return ret;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	printf_verbose("Signal handler set for SIGUSR1\n");
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	return ret;
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cistruct test_membarrier_thread_args {
11358c2ecf20Sopenharmony_ci	int stop;
11368c2ecf20Sopenharmony_ci	intptr_t percpu_list_ptr;
11378c2ecf20Sopenharmony_ci};
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci/* Worker threads modify data in their "active" percpu lists. */
11408c2ecf20Sopenharmony_civoid *test_membarrier_worker_thread(void *arg)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	struct test_membarrier_thread_args *args =
11438c2ecf20Sopenharmony_ci		(struct test_membarrier_thread_args *)arg;
11448c2ecf20Sopenharmony_ci	const int iters = opt_reps;
11458c2ecf20Sopenharmony_ci	int i;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	if (rseq_register_current_thread()) {
11488c2ecf20Sopenharmony_ci		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
11498c2ecf20Sopenharmony_ci			errno, strerror(errno));
11508c2ecf20Sopenharmony_ci		abort();
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	/* Wait for initialization. */
11548c2ecf20Sopenharmony_ci	while (!atomic_load(&args->percpu_list_ptr)) {}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	for (i = 0; i < iters; ++i) {
11578c2ecf20Sopenharmony_ci		int ret;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		do {
11608c2ecf20Sopenharmony_ci			int cpu = rseq_cpu_start();
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci			ret = rseq_offset_deref_addv(&args->percpu_list_ptr,
11638c2ecf20Sopenharmony_ci				sizeof(struct percpu_list_entry) * cpu, 1, cpu);
11648c2ecf20Sopenharmony_ci		} while (rseq_unlikely(ret));
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (rseq_unregister_current_thread()) {
11688c2ecf20Sopenharmony_ci		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
11698c2ecf20Sopenharmony_ci			errno, strerror(errno));
11708c2ecf20Sopenharmony_ci		abort();
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci	return NULL;
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_civoid test_membarrier_init_percpu_list(struct percpu_list *list)
11768c2ecf20Sopenharmony_ci{
11778c2ecf20Sopenharmony_ci	int i;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	memset(list, 0, sizeof(*list));
11808c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++) {
11818c2ecf20Sopenharmony_ci		struct percpu_list_node *node;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci		node = malloc(sizeof(*node));
11848c2ecf20Sopenharmony_ci		assert(node);
11858c2ecf20Sopenharmony_ci		node->data = 0;
11868c2ecf20Sopenharmony_ci		node->next = NULL;
11878c2ecf20Sopenharmony_ci		list->c[i].head = node;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_civoid test_membarrier_free_percpu_list(struct percpu_list *list)
11928c2ecf20Sopenharmony_ci{
11938c2ecf20Sopenharmony_ci	int i;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	for (i = 0; i < CPU_SETSIZE; i++)
11968c2ecf20Sopenharmony_ci		free(list->c[i].head);
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic int sys_membarrier(int cmd, int flags, int cpu_id)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	return syscall(__NR_membarrier, cmd, flags, cpu_id);
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci/*
12058c2ecf20Sopenharmony_ci * The manager thread swaps per-cpu lists that worker threads see,
12068c2ecf20Sopenharmony_ci * and validates that there are no unexpected modifications.
12078c2ecf20Sopenharmony_ci */
12088c2ecf20Sopenharmony_civoid *test_membarrier_manager_thread(void *arg)
12098c2ecf20Sopenharmony_ci{
12108c2ecf20Sopenharmony_ci	struct test_membarrier_thread_args *args =
12118c2ecf20Sopenharmony_ci		(struct test_membarrier_thread_args *)arg;
12128c2ecf20Sopenharmony_ci	struct percpu_list list_a, list_b;
12138c2ecf20Sopenharmony_ci	intptr_t expect_a = 0, expect_b = 0;
12148c2ecf20Sopenharmony_ci	int cpu_a = 0, cpu_b = 0;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	if (rseq_register_current_thread()) {
12178c2ecf20Sopenharmony_ci		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
12188c2ecf20Sopenharmony_ci			errno, strerror(errno));
12198c2ecf20Sopenharmony_ci		abort();
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* Init lists. */
12238c2ecf20Sopenharmony_ci	test_membarrier_init_percpu_list(&list_a);
12248c2ecf20Sopenharmony_ci	test_membarrier_init_percpu_list(&list_b);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	while (!atomic_load(&args->stop)) {
12298c2ecf20Sopenharmony_ci		/* list_a is "active". */
12308c2ecf20Sopenharmony_ci		cpu_a = rand() % CPU_SETSIZE;
12318c2ecf20Sopenharmony_ci		/*
12328c2ecf20Sopenharmony_ci		 * As list_b is "inactive", we should never see changes
12338c2ecf20Sopenharmony_ci		 * to list_b.
12348c2ecf20Sopenharmony_ci		 */
12358c2ecf20Sopenharmony_ci		if (expect_b != atomic_load(&list_b.c[cpu_b].head->data)) {
12368c2ecf20Sopenharmony_ci			fprintf(stderr, "Membarrier test failed\n");
12378c2ecf20Sopenharmony_ci			abort();
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		/* Make list_b "active". */
12418c2ecf20Sopenharmony_ci		atomic_store(&args->percpu_list_ptr, (intptr_t)&list_b);
12428c2ecf20Sopenharmony_ci		if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,
12438c2ecf20Sopenharmony_ci					MEMBARRIER_CMD_FLAG_CPU, cpu_a) &&
12448c2ecf20Sopenharmony_ci				errno != ENXIO /* missing CPU */) {
12458c2ecf20Sopenharmony_ci			perror("sys_membarrier");
12468c2ecf20Sopenharmony_ci			abort();
12478c2ecf20Sopenharmony_ci		}
12488c2ecf20Sopenharmony_ci		/*
12498c2ecf20Sopenharmony_ci		 * Cpu A should now only modify list_b, so the values
12508c2ecf20Sopenharmony_ci		 * in list_a should be stable.
12518c2ecf20Sopenharmony_ci		 */
12528c2ecf20Sopenharmony_ci		expect_a = atomic_load(&list_a.c[cpu_a].head->data);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		cpu_b = rand() % CPU_SETSIZE;
12558c2ecf20Sopenharmony_ci		/*
12568c2ecf20Sopenharmony_ci		 * As list_a is "inactive", we should never see changes
12578c2ecf20Sopenharmony_ci		 * to list_a.
12588c2ecf20Sopenharmony_ci		 */
12598c2ecf20Sopenharmony_ci		if (expect_a != atomic_load(&list_a.c[cpu_a].head->data)) {
12608c2ecf20Sopenharmony_ci			fprintf(stderr, "Membarrier test failed\n");
12618c2ecf20Sopenharmony_ci			abort();
12628c2ecf20Sopenharmony_ci		}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci		/* Make list_a "active". */
12658c2ecf20Sopenharmony_ci		atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a);
12668c2ecf20Sopenharmony_ci		if (sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,
12678c2ecf20Sopenharmony_ci					MEMBARRIER_CMD_FLAG_CPU, cpu_b) &&
12688c2ecf20Sopenharmony_ci				errno != ENXIO /* missing CPU*/) {
12698c2ecf20Sopenharmony_ci			perror("sys_membarrier");
12708c2ecf20Sopenharmony_ci			abort();
12718c2ecf20Sopenharmony_ci		}
12728c2ecf20Sopenharmony_ci		/* Remember a value from list_b. */
12738c2ecf20Sopenharmony_ci		expect_b = atomic_load(&list_b.c[cpu_b].head->data);
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	test_membarrier_free_percpu_list(&list_a);
12778c2ecf20Sopenharmony_ci	test_membarrier_free_percpu_list(&list_b);
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	if (rseq_unregister_current_thread()) {
12808c2ecf20Sopenharmony_ci		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
12818c2ecf20Sopenharmony_ci			errno, strerror(errno));
12828c2ecf20Sopenharmony_ci		abort();
12838c2ecf20Sopenharmony_ci	}
12848c2ecf20Sopenharmony_ci	return NULL;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci/* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */
12888c2ecf20Sopenharmony_ci#ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
12898c2ecf20Sopenharmony_civoid test_membarrier(void)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	const int num_threads = opt_threads;
12928c2ecf20Sopenharmony_ci	struct test_membarrier_thread_args thread_args;
12938c2ecf20Sopenharmony_ci	pthread_t worker_threads[num_threads];
12948c2ecf20Sopenharmony_ci	pthread_t manager_thread;
12958c2ecf20Sopenharmony_ci	int i, ret;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0, 0)) {
12988c2ecf20Sopenharmony_ci		perror("sys_membarrier");
12998c2ecf20Sopenharmony_ci		abort();
13008c2ecf20Sopenharmony_ci	}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	thread_args.stop = 0;
13038c2ecf20Sopenharmony_ci	thread_args.percpu_list_ptr = 0;
13048c2ecf20Sopenharmony_ci	ret = pthread_create(&manager_thread, NULL,
13058c2ecf20Sopenharmony_ci			test_membarrier_manager_thread, &thread_args);
13068c2ecf20Sopenharmony_ci	if (ret) {
13078c2ecf20Sopenharmony_ci		errno = ret;
13088c2ecf20Sopenharmony_ci		perror("pthread_create");
13098c2ecf20Sopenharmony_ci		abort();
13108c2ecf20Sopenharmony_ci	}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
13138c2ecf20Sopenharmony_ci		ret = pthread_create(&worker_threads[i], NULL,
13148c2ecf20Sopenharmony_ci				test_membarrier_worker_thread, &thread_args);
13158c2ecf20Sopenharmony_ci		if (ret) {
13168c2ecf20Sopenharmony_ci			errno = ret;
13178c2ecf20Sopenharmony_ci			perror("pthread_create");
13188c2ecf20Sopenharmony_ci			abort();
13198c2ecf20Sopenharmony_ci		}
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	for (i = 0; i < num_threads; i++) {
13248c2ecf20Sopenharmony_ci		ret = pthread_join(worker_threads[i], NULL);
13258c2ecf20Sopenharmony_ci		if (ret) {
13268c2ecf20Sopenharmony_ci			errno = ret;
13278c2ecf20Sopenharmony_ci			perror("pthread_join");
13288c2ecf20Sopenharmony_ci			abort();
13298c2ecf20Sopenharmony_ci		}
13308c2ecf20Sopenharmony_ci	}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	atomic_store(&thread_args.stop, 1);
13338c2ecf20Sopenharmony_ci	ret = pthread_join(manager_thread, NULL);
13348c2ecf20Sopenharmony_ci	if (ret) {
13358c2ecf20Sopenharmony_ci		errno = ret;
13368c2ecf20Sopenharmony_ci		perror("pthread_join");
13378c2ecf20Sopenharmony_ci		abort();
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci}
13408c2ecf20Sopenharmony_ci#else /* RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV */
13418c2ecf20Sopenharmony_civoid test_membarrier(void)
13428c2ecf20Sopenharmony_ci{
13438c2ecf20Sopenharmony_ci	fprintf(stderr, "rseq_offset_deref_addv is not implemented on this architecture. "
13448c2ecf20Sopenharmony_ci			"Skipping membarrier test.\n");
13458c2ecf20Sopenharmony_ci}
13468c2ecf20Sopenharmony_ci#endif
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_cistatic void show_usage(int argc, char **argv)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	printf("Usage : %s <OPTIONS>\n",
13518c2ecf20Sopenharmony_ci		argv[0]);
13528c2ecf20Sopenharmony_ci	printf("OPTIONS:\n");
13538c2ecf20Sopenharmony_ci	printf("	[-1 loops] Number of loops for delay injection 1\n");
13548c2ecf20Sopenharmony_ci	printf("	[-2 loops] Number of loops for delay injection 2\n");
13558c2ecf20Sopenharmony_ci	printf("	[-3 loops] Number of loops for delay injection 3\n");
13568c2ecf20Sopenharmony_ci	printf("	[-4 loops] Number of loops for delay injection 4\n");
13578c2ecf20Sopenharmony_ci	printf("	[-5 loops] Number of loops for delay injection 5\n");
13588c2ecf20Sopenharmony_ci	printf("	[-6 loops] Number of loops for delay injection 6\n");
13598c2ecf20Sopenharmony_ci	printf("	[-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n");
13608c2ecf20Sopenharmony_ci	printf("	[-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n");
13618c2ecf20Sopenharmony_ci	printf("	[-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n");
13628c2ecf20Sopenharmony_ci	printf("	[-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n");
13638c2ecf20Sopenharmony_ci	printf("	[-y] Yield\n");
13648c2ecf20Sopenharmony_ci	printf("	[-k] Kill thread with signal\n");
13658c2ecf20Sopenharmony_ci	printf("	[-s S] S: =0: disabled (default), >0: sleep time (ms)\n");
13668c2ecf20Sopenharmony_ci	printf("	[-t N] Number of threads (default 200)\n");
13678c2ecf20Sopenharmony_ci	printf("	[-r N] Number of repetitions per thread (default 5000)\n");
13688c2ecf20Sopenharmony_ci	printf("	[-d] Disable rseq system call (no initialization)\n");
13698c2ecf20Sopenharmony_ci	printf("	[-D M] Disable rseq for each M threads\n");
13708c2ecf20Sopenharmony_ci	printf("	[-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
13718c2ecf20Sopenharmony_ci	printf("	[-M] Push into buffer and memcpy buffer with memory barriers.\n");
13728c2ecf20Sopenharmony_ci	printf("	[-v] Verbose output.\n");
13738c2ecf20Sopenharmony_ci	printf("	[-h] Show this help.\n");
13748c2ecf20Sopenharmony_ci	printf("\n");
13758c2ecf20Sopenharmony_ci}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ciint main(int argc, char **argv)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	int i;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	for (i = 1; i < argc; i++) {
13828c2ecf20Sopenharmony_ci		if (argv[i][0] != '-')
13838c2ecf20Sopenharmony_ci			continue;
13848c2ecf20Sopenharmony_ci		switch (argv[i][1]) {
13858c2ecf20Sopenharmony_ci		case '1':
13868c2ecf20Sopenharmony_ci		case '2':
13878c2ecf20Sopenharmony_ci		case '3':
13888c2ecf20Sopenharmony_ci		case '4':
13898c2ecf20Sopenharmony_ci		case '5':
13908c2ecf20Sopenharmony_ci		case '6':
13918c2ecf20Sopenharmony_ci		case '7':
13928c2ecf20Sopenharmony_ci		case '8':
13938c2ecf20Sopenharmony_ci		case '9':
13948c2ecf20Sopenharmony_ci			if (argc < i + 2) {
13958c2ecf20Sopenharmony_ci				show_usage(argc, argv);
13968c2ecf20Sopenharmony_ci				goto error;
13978c2ecf20Sopenharmony_ci			}
13988c2ecf20Sopenharmony_ci			loop_cnt[argv[i][1] - '0'] = atol(argv[i + 1]);
13998c2ecf20Sopenharmony_ci			i++;
14008c2ecf20Sopenharmony_ci			break;
14018c2ecf20Sopenharmony_ci		case 'm':
14028c2ecf20Sopenharmony_ci			if (argc < i + 2) {
14038c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14048c2ecf20Sopenharmony_ci				goto error;
14058c2ecf20Sopenharmony_ci			}
14068c2ecf20Sopenharmony_ci			opt_modulo = atol(argv[i + 1]);
14078c2ecf20Sopenharmony_ci			if (opt_modulo < 0) {
14088c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14098c2ecf20Sopenharmony_ci				goto error;
14108c2ecf20Sopenharmony_ci			}
14118c2ecf20Sopenharmony_ci			i++;
14128c2ecf20Sopenharmony_ci			break;
14138c2ecf20Sopenharmony_ci		case 's':
14148c2ecf20Sopenharmony_ci			if (argc < i + 2) {
14158c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14168c2ecf20Sopenharmony_ci				goto error;
14178c2ecf20Sopenharmony_ci			}
14188c2ecf20Sopenharmony_ci			opt_sleep = atol(argv[i + 1]);
14198c2ecf20Sopenharmony_ci			if (opt_sleep < 0) {
14208c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14218c2ecf20Sopenharmony_ci				goto error;
14228c2ecf20Sopenharmony_ci			}
14238c2ecf20Sopenharmony_ci			i++;
14248c2ecf20Sopenharmony_ci			break;
14258c2ecf20Sopenharmony_ci		case 'y':
14268c2ecf20Sopenharmony_ci			opt_yield = 1;
14278c2ecf20Sopenharmony_ci			break;
14288c2ecf20Sopenharmony_ci		case 'k':
14298c2ecf20Sopenharmony_ci			opt_signal = 1;
14308c2ecf20Sopenharmony_ci			break;
14318c2ecf20Sopenharmony_ci		case 'd':
14328c2ecf20Sopenharmony_ci			opt_disable_rseq = 1;
14338c2ecf20Sopenharmony_ci			break;
14348c2ecf20Sopenharmony_ci		case 'D':
14358c2ecf20Sopenharmony_ci			if (argc < i + 2) {
14368c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14378c2ecf20Sopenharmony_ci				goto error;
14388c2ecf20Sopenharmony_ci			}
14398c2ecf20Sopenharmony_ci			opt_disable_mod = atol(argv[i + 1]);
14408c2ecf20Sopenharmony_ci			if (opt_disable_mod < 0) {
14418c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14428c2ecf20Sopenharmony_ci				goto error;
14438c2ecf20Sopenharmony_ci			}
14448c2ecf20Sopenharmony_ci			i++;
14458c2ecf20Sopenharmony_ci			break;
14468c2ecf20Sopenharmony_ci		case 't':
14478c2ecf20Sopenharmony_ci			if (argc < i + 2) {
14488c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14498c2ecf20Sopenharmony_ci				goto error;
14508c2ecf20Sopenharmony_ci			}
14518c2ecf20Sopenharmony_ci			opt_threads = atol(argv[i + 1]);
14528c2ecf20Sopenharmony_ci			if (opt_threads < 0) {
14538c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14548c2ecf20Sopenharmony_ci				goto error;
14558c2ecf20Sopenharmony_ci			}
14568c2ecf20Sopenharmony_ci			i++;
14578c2ecf20Sopenharmony_ci			break;
14588c2ecf20Sopenharmony_ci		case 'r':
14598c2ecf20Sopenharmony_ci			if (argc < i + 2) {
14608c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14618c2ecf20Sopenharmony_ci				goto error;
14628c2ecf20Sopenharmony_ci			}
14638c2ecf20Sopenharmony_ci			opt_reps = atoll(argv[i + 1]);
14648c2ecf20Sopenharmony_ci			if (opt_reps < 0) {
14658c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14668c2ecf20Sopenharmony_ci				goto error;
14678c2ecf20Sopenharmony_ci			}
14688c2ecf20Sopenharmony_ci			i++;
14698c2ecf20Sopenharmony_ci			break;
14708c2ecf20Sopenharmony_ci		case 'h':
14718c2ecf20Sopenharmony_ci			show_usage(argc, argv);
14728c2ecf20Sopenharmony_ci			goto end;
14738c2ecf20Sopenharmony_ci		case 'T':
14748c2ecf20Sopenharmony_ci			if (argc < i + 2) {
14758c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14768c2ecf20Sopenharmony_ci				goto error;
14778c2ecf20Sopenharmony_ci			}
14788c2ecf20Sopenharmony_ci			opt_test = *argv[i + 1];
14798c2ecf20Sopenharmony_ci			switch (opt_test) {
14808c2ecf20Sopenharmony_ci			case 's':
14818c2ecf20Sopenharmony_ci			case 'l':
14828c2ecf20Sopenharmony_ci			case 'i':
14838c2ecf20Sopenharmony_ci			case 'b':
14848c2ecf20Sopenharmony_ci			case 'm':
14858c2ecf20Sopenharmony_ci			case 'r':
14868c2ecf20Sopenharmony_ci				break;
14878c2ecf20Sopenharmony_ci			default:
14888c2ecf20Sopenharmony_ci				show_usage(argc, argv);
14898c2ecf20Sopenharmony_ci				goto error;
14908c2ecf20Sopenharmony_ci			}
14918c2ecf20Sopenharmony_ci			i++;
14928c2ecf20Sopenharmony_ci			break;
14938c2ecf20Sopenharmony_ci		case 'v':
14948c2ecf20Sopenharmony_ci			verbose = 1;
14958c2ecf20Sopenharmony_ci			break;
14968c2ecf20Sopenharmony_ci		case 'M':
14978c2ecf20Sopenharmony_ci			opt_mb = 1;
14988c2ecf20Sopenharmony_ci			break;
14998c2ecf20Sopenharmony_ci		default:
15008c2ecf20Sopenharmony_ci			show_usage(argc, argv);
15018c2ecf20Sopenharmony_ci			goto error;
15028c2ecf20Sopenharmony_ci		}
15038c2ecf20Sopenharmony_ci	}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	loop_cnt_1 = loop_cnt[1];
15068c2ecf20Sopenharmony_ci	loop_cnt_2 = loop_cnt[2];
15078c2ecf20Sopenharmony_ci	loop_cnt_3 = loop_cnt[3];
15088c2ecf20Sopenharmony_ci	loop_cnt_4 = loop_cnt[4];
15098c2ecf20Sopenharmony_ci	loop_cnt_5 = loop_cnt[5];
15108c2ecf20Sopenharmony_ci	loop_cnt_6 = loop_cnt[6];
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	if (set_signal_handler())
15138c2ecf20Sopenharmony_ci		goto error;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_register_current_thread())
15168c2ecf20Sopenharmony_ci		goto error;
15178c2ecf20Sopenharmony_ci	switch (opt_test) {
15188c2ecf20Sopenharmony_ci	case 's':
15198c2ecf20Sopenharmony_ci		printf_verbose("spinlock\n");
15208c2ecf20Sopenharmony_ci		test_percpu_spinlock();
15218c2ecf20Sopenharmony_ci		break;
15228c2ecf20Sopenharmony_ci	case 'l':
15238c2ecf20Sopenharmony_ci		printf_verbose("linked list\n");
15248c2ecf20Sopenharmony_ci		test_percpu_list();
15258c2ecf20Sopenharmony_ci		break;
15268c2ecf20Sopenharmony_ci	case 'b':
15278c2ecf20Sopenharmony_ci		printf_verbose("buffer\n");
15288c2ecf20Sopenharmony_ci		test_percpu_buffer();
15298c2ecf20Sopenharmony_ci		break;
15308c2ecf20Sopenharmony_ci	case 'm':
15318c2ecf20Sopenharmony_ci		printf_verbose("memcpy buffer\n");
15328c2ecf20Sopenharmony_ci		test_percpu_memcpy_buffer();
15338c2ecf20Sopenharmony_ci		break;
15348c2ecf20Sopenharmony_ci	case 'i':
15358c2ecf20Sopenharmony_ci		printf_verbose("counter increment\n");
15368c2ecf20Sopenharmony_ci		test_percpu_inc();
15378c2ecf20Sopenharmony_ci		break;
15388c2ecf20Sopenharmony_ci	case 'r':
15398c2ecf20Sopenharmony_ci		printf_verbose("membarrier\n");
15408c2ecf20Sopenharmony_ci		test_membarrier();
15418c2ecf20Sopenharmony_ci		break;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci	if (!opt_disable_rseq && rseq_unregister_current_thread())
15448c2ecf20Sopenharmony_ci		abort();
15458c2ecf20Sopenharmony_ciend:
15468c2ecf20Sopenharmony_ci	return 0;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_cierror:
15498c2ecf20Sopenharmony_ci	return -1;
15508c2ecf20Sopenharmony_ci}
1551