162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 262306a36Sopenharmony_ci#define _GNU_SOURCE 362306a36Sopenharmony_ci#include <assert.h> 462306a36Sopenharmony_ci#include <linux/membarrier.h> 562306a36Sopenharmony_ci#include <pthread.h> 662306a36Sopenharmony_ci#include <sched.h> 762306a36Sopenharmony_ci#include <stdatomic.h> 862306a36Sopenharmony_ci#include <stdint.h> 962306a36Sopenharmony_ci#include <stdio.h> 1062306a36Sopenharmony_ci#include <stdlib.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <syscall.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <poll.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include <signal.h> 1762306a36Sopenharmony_ci#include <errno.h> 1862306a36Sopenharmony_ci#include <stddef.h> 1962306a36Sopenharmony_ci#include <stdbool.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic inline pid_t rseq_gettid(void) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci return syscall(__NR_gettid); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define NR_INJECT 9 2762306a36Sopenharmony_cistatic int loop_cnt[NR_INJECT + 1]; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int loop_cnt_1 asm("asm_loop_cnt_1") __attribute__((used)); 3062306a36Sopenharmony_cistatic int loop_cnt_2 asm("asm_loop_cnt_2") __attribute__((used)); 3162306a36Sopenharmony_cistatic int loop_cnt_3 asm("asm_loop_cnt_3") __attribute__((used)); 3262306a36Sopenharmony_cistatic int loop_cnt_4 asm("asm_loop_cnt_4") __attribute__((used)); 3362306a36Sopenharmony_cistatic int loop_cnt_5 asm("asm_loop_cnt_5") __attribute__((used)); 3462306a36Sopenharmony_cistatic int loop_cnt_6 asm("asm_loop_cnt_6") __attribute__((used)); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int opt_modulo, verbose; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int opt_yield, opt_signal, opt_sleep, 3962306a36Sopenharmony_ci opt_disable_rseq, opt_threads = 200, 4062306a36Sopenharmony_ci opt_disable_mod = 0, opt_test = 's'; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic long long opt_reps = 5000; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic __thread __attribute__((tls_model("initial-exec"))) 4562306a36Sopenharmony_ciunsigned int signals_delivered; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#ifndef BENCHMARK 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic __thread __attribute__((tls_model("initial-exec"), unused)) 5062306a36Sopenharmony_ciunsigned int yield_mod_cnt, nr_abort; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define printf_verbose(fmt, ...) \ 5362306a36Sopenharmony_ci do { \ 5462306a36Sopenharmony_ci if (verbose) \ 5562306a36Sopenharmony_ci printf(fmt, ## __VA_ARGS__); \ 5662306a36Sopenharmony_ci } while (0) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#ifdef __i386__ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define INJECT_ASM_REG "eax" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 6362306a36Sopenharmony_ci , INJECT_ASM_REG 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 6662306a36Sopenharmony_ci "mov asm_loop_cnt_" #n ", %%" INJECT_ASM_REG "\n\t" \ 6762306a36Sopenharmony_ci "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \ 6862306a36Sopenharmony_ci "jz 333f\n\t" \ 6962306a36Sopenharmony_ci "222:\n\t" \ 7062306a36Sopenharmony_ci "dec %%" INJECT_ASM_REG "\n\t" \ 7162306a36Sopenharmony_ci "jnz 222b\n\t" \ 7262306a36Sopenharmony_ci "333:\n\t" 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#elif defined(__x86_64__) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define INJECT_ASM_REG_P "rax" 7762306a36Sopenharmony_ci#define INJECT_ASM_REG "eax" 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 8062306a36Sopenharmony_ci , INJECT_ASM_REG_P \ 8162306a36Sopenharmony_ci , INJECT_ASM_REG 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 8462306a36Sopenharmony_ci "lea asm_loop_cnt_" #n "(%%rip), %%" INJECT_ASM_REG_P "\n\t" \ 8562306a36Sopenharmony_ci "mov (%%" INJECT_ASM_REG_P "), %%" INJECT_ASM_REG "\n\t" \ 8662306a36Sopenharmony_ci "test %%" INJECT_ASM_REG ",%%" INJECT_ASM_REG "\n\t" \ 8762306a36Sopenharmony_ci "jz 333f\n\t" \ 8862306a36Sopenharmony_ci "222:\n\t" \ 8962306a36Sopenharmony_ci "dec %%" INJECT_ASM_REG "\n\t" \ 9062306a36Sopenharmony_ci "jnz 222b\n\t" \ 9162306a36Sopenharmony_ci "333:\n\t" 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#elif defined(__s390__) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define RSEQ_INJECT_INPUT \ 9662306a36Sopenharmony_ci , [loop_cnt_1]"m"(loop_cnt[1]) \ 9762306a36Sopenharmony_ci , [loop_cnt_2]"m"(loop_cnt[2]) \ 9862306a36Sopenharmony_ci , [loop_cnt_3]"m"(loop_cnt[3]) \ 9962306a36Sopenharmony_ci , [loop_cnt_4]"m"(loop_cnt[4]) \ 10062306a36Sopenharmony_ci , [loop_cnt_5]"m"(loop_cnt[5]) \ 10162306a36Sopenharmony_ci , [loop_cnt_6]"m"(loop_cnt[6]) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define INJECT_ASM_REG "r12" 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 10662306a36Sopenharmony_ci , INJECT_ASM_REG 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 10962306a36Sopenharmony_ci "l %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \ 11062306a36Sopenharmony_ci "ltr %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG "\n\t" \ 11162306a36Sopenharmony_ci "je 333f\n\t" \ 11262306a36Sopenharmony_ci "222:\n\t" \ 11362306a36Sopenharmony_ci "ahi %%" INJECT_ASM_REG ", -1\n\t" \ 11462306a36Sopenharmony_ci "jnz 222b\n\t" \ 11562306a36Sopenharmony_ci "333:\n\t" 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#elif defined(__ARMEL__) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define RSEQ_INJECT_INPUT \ 12062306a36Sopenharmony_ci , [loop_cnt_1]"m"(loop_cnt[1]) \ 12162306a36Sopenharmony_ci , [loop_cnt_2]"m"(loop_cnt[2]) \ 12262306a36Sopenharmony_ci , [loop_cnt_3]"m"(loop_cnt[3]) \ 12362306a36Sopenharmony_ci , [loop_cnt_4]"m"(loop_cnt[4]) \ 12462306a36Sopenharmony_ci , [loop_cnt_5]"m"(loop_cnt[5]) \ 12562306a36Sopenharmony_ci , [loop_cnt_6]"m"(loop_cnt[6]) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define INJECT_ASM_REG "r4" 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 13062306a36Sopenharmony_ci , INJECT_ASM_REG 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 13362306a36Sopenharmony_ci "ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \ 13462306a36Sopenharmony_ci "cmp " INJECT_ASM_REG ", #0\n\t" \ 13562306a36Sopenharmony_ci "beq 333f\n\t" \ 13662306a36Sopenharmony_ci "222:\n\t" \ 13762306a36Sopenharmony_ci "subs " INJECT_ASM_REG ", #1\n\t" \ 13862306a36Sopenharmony_ci "bne 222b\n\t" \ 13962306a36Sopenharmony_ci "333:\n\t" 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#elif defined(__AARCH64EL__) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define RSEQ_INJECT_INPUT \ 14462306a36Sopenharmony_ci , [loop_cnt_1] "Qo" (loop_cnt[1]) \ 14562306a36Sopenharmony_ci , [loop_cnt_2] "Qo" (loop_cnt[2]) \ 14662306a36Sopenharmony_ci , [loop_cnt_3] "Qo" (loop_cnt[3]) \ 14762306a36Sopenharmony_ci , [loop_cnt_4] "Qo" (loop_cnt[4]) \ 14862306a36Sopenharmony_ci , [loop_cnt_5] "Qo" (loop_cnt[5]) \ 14962306a36Sopenharmony_ci , [loop_cnt_6] "Qo" (loop_cnt[6]) 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define INJECT_ASM_REG RSEQ_ASM_TMP_REG32 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 15462306a36Sopenharmony_ci " ldr " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n" \ 15562306a36Sopenharmony_ci " cbz " INJECT_ASM_REG ", 333f\n" \ 15662306a36Sopenharmony_ci "222:\n" \ 15762306a36Sopenharmony_ci " sub " INJECT_ASM_REG ", " INJECT_ASM_REG ", #1\n" \ 15862306a36Sopenharmony_ci " cbnz " INJECT_ASM_REG ", 222b\n" \ 15962306a36Sopenharmony_ci "333:\n" 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#elif defined(__PPC__) 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#define RSEQ_INJECT_INPUT \ 16462306a36Sopenharmony_ci , [loop_cnt_1]"m"(loop_cnt[1]) \ 16562306a36Sopenharmony_ci , [loop_cnt_2]"m"(loop_cnt[2]) \ 16662306a36Sopenharmony_ci , [loop_cnt_3]"m"(loop_cnt[3]) \ 16762306a36Sopenharmony_ci , [loop_cnt_4]"m"(loop_cnt[4]) \ 16862306a36Sopenharmony_ci , [loop_cnt_5]"m"(loop_cnt[5]) \ 16962306a36Sopenharmony_ci , [loop_cnt_6]"m"(loop_cnt[6]) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci#define INJECT_ASM_REG "r18" 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 17462306a36Sopenharmony_ci , INJECT_ASM_REG 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 17762306a36Sopenharmony_ci "lwz %%" INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \ 17862306a36Sopenharmony_ci "cmpwi %%" INJECT_ASM_REG ", 0\n\t" \ 17962306a36Sopenharmony_ci "beq 333f\n\t" \ 18062306a36Sopenharmony_ci "222:\n\t" \ 18162306a36Sopenharmony_ci "subic. %%" INJECT_ASM_REG ", %%" INJECT_ASM_REG ", 1\n\t" \ 18262306a36Sopenharmony_ci "bne 222b\n\t" \ 18362306a36Sopenharmony_ci "333:\n\t" 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#elif defined(__mips__) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci#define RSEQ_INJECT_INPUT \ 18862306a36Sopenharmony_ci , [loop_cnt_1]"m"(loop_cnt[1]) \ 18962306a36Sopenharmony_ci , [loop_cnt_2]"m"(loop_cnt[2]) \ 19062306a36Sopenharmony_ci , [loop_cnt_3]"m"(loop_cnt[3]) \ 19162306a36Sopenharmony_ci , [loop_cnt_4]"m"(loop_cnt[4]) \ 19262306a36Sopenharmony_ci , [loop_cnt_5]"m"(loop_cnt[5]) \ 19362306a36Sopenharmony_ci , [loop_cnt_6]"m"(loop_cnt[6]) 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#define INJECT_ASM_REG "$5" 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 19862306a36Sopenharmony_ci , INJECT_ASM_REG 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 20162306a36Sopenharmony_ci "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \ 20262306a36Sopenharmony_ci "beqz " INJECT_ASM_REG ", 333f\n\t" \ 20362306a36Sopenharmony_ci "222:\n\t" \ 20462306a36Sopenharmony_ci "addiu " INJECT_ASM_REG ", -1\n\t" \ 20562306a36Sopenharmony_ci "bnez " INJECT_ASM_REG ", 222b\n\t" \ 20662306a36Sopenharmony_ci "333:\n\t" 20762306a36Sopenharmony_ci#elif defined(__riscv) 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#define RSEQ_INJECT_INPUT \ 21062306a36Sopenharmony_ci , [loop_cnt_1]"m"(loop_cnt[1]) \ 21162306a36Sopenharmony_ci , [loop_cnt_2]"m"(loop_cnt[2]) \ 21262306a36Sopenharmony_ci , [loop_cnt_3]"m"(loop_cnt[3]) \ 21362306a36Sopenharmony_ci , [loop_cnt_4]"m"(loop_cnt[4]) \ 21462306a36Sopenharmony_ci , [loop_cnt_5]"m"(loop_cnt[5]) \ 21562306a36Sopenharmony_ci , [loop_cnt_6]"m"(loop_cnt[6]) 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci#define INJECT_ASM_REG "t1" 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#define RSEQ_INJECT_CLOBBER \ 22062306a36Sopenharmony_ci , INJECT_ASM_REG 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#define RSEQ_INJECT_ASM(n) \ 22362306a36Sopenharmony_ci "lw " INJECT_ASM_REG ", %[loop_cnt_" #n "]\n\t" \ 22462306a36Sopenharmony_ci "beqz " INJECT_ASM_REG ", 333f\n\t" \ 22562306a36Sopenharmony_ci "222:\n\t" \ 22662306a36Sopenharmony_ci "addi " INJECT_ASM_REG "," INJECT_ASM_REG ", -1\n\t" \ 22762306a36Sopenharmony_ci "bnez " INJECT_ASM_REG ", 222b\n\t" \ 22862306a36Sopenharmony_ci "333:\n\t" 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci#else 23262306a36Sopenharmony_ci#error unsupported target 23362306a36Sopenharmony_ci#endif 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define RSEQ_INJECT_FAILED \ 23662306a36Sopenharmony_ci nr_abort++; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#define RSEQ_INJECT_C(n) \ 23962306a36Sopenharmony_ci{ \ 24062306a36Sopenharmony_ci int loc_i, loc_nr_loops = loop_cnt[n]; \ 24162306a36Sopenharmony_ci \ 24262306a36Sopenharmony_ci for (loc_i = 0; loc_i < loc_nr_loops; loc_i++) { \ 24362306a36Sopenharmony_ci rseq_barrier(); \ 24462306a36Sopenharmony_ci } \ 24562306a36Sopenharmony_ci if (loc_nr_loops == -1 && opt_modulo) { \ 24662306a36Sopenharmony_ci if (yield_mod_cnt == opt_modulo - 1) { \ 24762306a36Sopenharmony_ci if (opt_sleep > 0) \ 24862306a36Sopenharmony_ci poll(NULL, 0, opt_sleep); \ 24962306a36Sopenharmony_ci if (opt_yield) \ 25062306a36Sopenharmony_ci sched_yield(); \ 25162306a36Sopenharmony_ci if (opt_signal) \ 25262306a36Sopenharmony_ci raise(SIGUSR1); \ 25362306a36Sopenharmony_ci yield_mod_cnt = 0; \ 25462306a36Sopenharmony_ci } else { \ 25562306a36Sopenharmony_ci yield_mod_cnt++; \ 25662306a36Sopenharmony_ci } \ 25762306a36Sopenharmony_ci } \ 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci#else 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci#define printf_verbose(fmt, ...) 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci#endif /* BENCHMARK */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#include "rseq.h" 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic enum rseq_mo opt_mo = RSEQ_MO_RELAXED; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci#ifdef RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV 27162306a36Sopenharmony_ci#define TEST_MEMBARRIER 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int sys_membarrier(int cmd, int flags, int cpu_id) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci return syscall(__NR_membarrier, cmd, flags, cpu_id); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci#endif 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci#ifdef BUILDOPT_RSEQ_PERCPU_MM_CID 28062306a36Sopenharmony_ci# define RSEQ_PERCPU RSEQ_PERCPU_MM_CID 28162306a36Sopenharmony_cistatic 28262306a36Sopenharmony_ciint get_current_cpu_id(void) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci return rseq_current_mm_cid(); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_cistatic 28762306a36Sopenharmony_cibool rseq_validate_cpu_id(void) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return rseq_mm_cid_available(); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci# ifdef TEST_MEMBARRIER 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * Membarrier does not currently support targeting a mm_cid, so 29462306a36Sopenharmony_ci * issue the barrier on all cpus. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_cistatic 29762306a36Sopenharmony_ciint rseq_membarrier_expedited(int cpu) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, 30062306a36Sopenharmony_ci 0, 0); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci# endif /* TEST_MEMBARRIER */ 30362306a36Sopenharmony_ci#else 30462306a36Sopenharmony_ci# define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID 30562306a36Sopenharmony_cistatic 30662306a36Sopenharmony_ciint get_current_cpu_id(void) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return rseq_cpu_start(); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_cistatic 31162306a36Sopenharmony_cibool rseq_validate_cpu_id(void) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci return rseq_current_cpu_raw() >= 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci# ifdef TEST_MEMBARRIER 31662306a36Sopenharmony_cistatic 31762306a36Sopenharmony_ciint rseq_membarrier_expedited(int cpu) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci return sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, 32062306a36Sopenharmony_ci MEMBARRIER_CMD_FLAG_CPU, cpu); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci# endif /* TEST_MEMBARRIER */ 32362306a36Sopenharmony_ci#endif 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistruct percpu_lock_entry { 32662306a36Sopenharmony_ci intptr_t v; 32762306a36Sopenharmony_ci} __attribute__((aligned(128))); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistruct percpu_lock { 33062306a36Sopenharmony_ci struct percpu_lock_entry c[CPU_SETSIZE]; 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistruct test_data_entry { 33462306a36Sopenharmony_ci intptr_t count; 33562306a36Sopenharmony_ci} __attribute__((aligned(128))); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistruct spinlock_test_data { 33862306a36Sopenharmony_ci struct percpu_lock lock; 33962306a36Sopenharmony_ci struct test_data_entry c[CPU_SETSIZE]; 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistruct spinlock_thread_test_data { 34362306a36Sopenharmony_ci struct spinlock_test_data *data; 34462306a36Sopenharmony_ci long long reps; 34562306a36Sopenharmony_ci int reg; 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistruct inc_test_data { 34962306a36Sopenharmony_ci struct test_data_entry c[CPU_SETSIZE]; 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistruct inc_thread_test_data { 35362306a36Sopenharmony_ci struct inc_test_data *data; 35462306a36Sopenharmony_ci long long reps; 35562306a36Sopenharmony_ci int reg; 35662306a36Sopenharmony_ci}; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistruct percpu_list_node { 35962306a36Sopenharmony_ci intptr_t data; 36062306a36Sopenharmony_ci struct percpu_list_node *next; 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistruct percpu_list_entry { 36462306a36Sopenharmony_ci struct percpu_list_node *head; 36562306a36Sopenharmony_ci} __attribute__((aligned(128))); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistruct percpu_list { 36862306a36Sopenharmony_ci struct percpu_list_entry c[CPU_SETSIZE]; 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci#define BUFFER_ITEM_PER_CPU 100 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistruct percpu_buffer_node { 37462306a36Sopenharmony_ci intptr_t data; 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistruct percpu_buffer_entry { 37862306a36Sopenharmony_ci intptr_t offset; 37962306a36Sopenharmony_ci intptr_t buflen; 38062306a36Sopenharmony_ci struct percpu_buffer_node **array; 38162306a36Sopenharmony_ci} __attribute__((aligned(128))); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistruct percpu_buffer { 38462306a36Sopenharmony_ci struct percpu_buffer_entry c[CPU_SETSIZE]; 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci#define MEMCPY_BUFFER_ITEM_PER_CPU 100 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistruct percpu_memcpy_buffer_node { 39062306a36Sopenharmony_ci intptr_t data1; 39162306a36Sopenharmony_ci uint64_t data2; 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistruct percpu_memcpy_buffer_entry { 39562306a36Sopenharmony_ci intptr_t offset; 39662306a36Sopenharmony_ci intptr_t buflen; 39762306a36Sopenharmony_ci struct percpu_memcpy_buffer_node *array; 39862306a36Sopenharmony_ci} __attribute__((aligned(128))); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistruct percpu_memcpy_buffer { 40162306a36Sopenharmony_ci struct percpu_memcpy_buffer_entry c[CPU_SETSIZE]; 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* A simple percpu spinlock. Grabs lock on current cpu. */ 40562306a36Sopenharmony_cistatic int rseq_this_cpu_lock(struct percpu_lock *lock) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci int cpu; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci for (;;) { 41062306a36Sopenharmony_ci int ret; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci cpu = get_current_cpu_id(); 41362306a36Sopenharmony_ci if (cpu < 0) { 41462306a36Sopenharmony_ci fprintf(stderr, "pid: %d: tid: %d, cpu: %d: cid: %d\n", 41562306a36Sopenharmony_ci getpid(), (int) rseq_gettid(), rseq_current_cpu_raw(), cpu); 41662306a36Sopenharmony_ci abort(); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci ret = rseq_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU, 41962306a36Sopenharmony_ci &lock->c[cpu].v, 42062306a36Sopenharmony_ci 0, 1, cpu); 42162306a36Sopenharmony_ci if (rseq_likely(!ret)) 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci /* Retry if comparison fails or rseq aborts. */ 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * Acquire semantic when taking lock after control dependency. 42762306a36Sopenharmony_ci * Matches rseq_smp_store_release(). 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci rseq_smp_acquire__after_ctrl_dep(); 43062306a36Sopenharmony_ci return cpu; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void rseq_percpu_unlock(struct percpu_lock *lock, int cpu) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci assert(lock->c[cpu].v == 1); 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * Release lock, with release semantic. Matches 43862306a36Sopenharmony_ci * rseq_smp_acquire__after_ctrl_dep(). 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci rseq_smp_store_release(&lock->c[cpu].v, 0); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_civoid *test_percpu_spinlock_thread(void *arg) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct spinlock_thread_test_data *thread_data = arg; 44662306a36Sopenharmony_ci struct spinlock_test_data *data = thread_data->data; 44762306a36Sopenharmony_ci long long i, reps; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!opt_disable_rseq && thread_data->reg && 45062306a36Sopenharmony_ci rseq_register_current_thread()) 45162306a36Sopenharmony_ci abort(); 45262306a36Sopenharmony_ci reps = thread_data->reps; 45362306a36Sopenharmony_ci for (i = 0; i < reps; i++) { 45462306a36Sopenharmony_ci int cpu = rseq_this_cpu_lock(&data->lock); 45562306a36Sopenharmony_ci data->c[cpu].count++; 45662306a36Sopenharmony_ci rseq_percpu_unlock(&data->lock, cpu); 45762306a36Sopenharmony_ci#ifndef BENCHMARK 45862306a36Sopenharmony_ci if (i != 0 && !(i % (reps / 10))) 45962306a36Sopenharmony_ci printf_verbose("tid %d: count %lld\n", 46062306a36Sopenharmony_ci (int) rseq_gettid(), i); 46162306a36Sopenharmony_ci#endif 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", 46462306a36Sopenharmony_ci (int) rseq_gettid(), nr_abort, signals_delivered); 46562306a36Sopenharmony_ci if (!opt_disable_rseq && thread_data->reg && 46662306a36Sopenharmony_ci rseq_unregister_current_thread()) 46762306a36Sopenharmony_ci abort(); 46862306a36Sopenharmony_ci return NULL; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * A simple test which implements a sharded counter using a per-cpu 47362306a36Sopenharmony_ci * lock. Obviously real applications might prefer to simply use a 47462306a36Sopenharmony_ci * per-cpu increment; however, this is reasonable for a test and the 47562306a36Sopenharmony_ci * lock can be extended to synchronize more complicated operations. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_civoid test_percpu_spinlock(void) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci const int num_threads = opt_threads; 48062306a36Sopenharmony_ci int i, ret; 48162306a36Sopenharmony_ci uint64_t sum; 48262306a36Sopenharmony_ci pthread_t test_threads[num_threads]; 48362306a36Sopenharmony_ci struct spinlock_test_data data; 48462306a36Sopenharmony_ci struct spinlock_thread_test_data thread_data[num_threads]; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 48762306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 48862306a36Sopenharmony_ci thread_data[i].reps = opt_reps; 48962306a36Sopenharmony_ci if (opt_disable_mod <= 0 || (i % opt_disable_mod)) 49062306a36Sopenharmony_ci thread_data[i].reg = 1; 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci thread_data[i].reg = 0; 49362306a36Sopenharmony_ci thread_data[i].data = &data; 49462306a36Sopenharmony_ci ret = pthread_create(&test_threads[i], NULL, 49562306a36Sopenharmony_ci test_percpu_spinlock_thread, 49662306a36Sopenharmony_ci &thread_data[i]); 49762306a36Sopenharmony_ci if (ret) { 49862306a36Sopenharmony_ci errno = ret; 49962306a36Sopenharmony_ci perror("pthread_create"); 50062306a36Sopenharmony_ci abort(); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 50562306a36Sopenharmony_ci ret = pthread_join(test_threads[i], NULL); 50662306a36Sopenharmony_ci if (ret) { 50762306a36Sopenharmony_ci errno = ret; 50862306a36Sopenharmony_ci perror("pthread_join"); 50962306a36Sopenharmony_ci abort(); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci sum = 0; 51462306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) 51562306a36Sopenharmony_ci sum += data.c[i].count; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci assert(sum == (uint64_t)opt_reps * num_threads); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_civoid *test_percpu_inc_thread(void *arg) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct inc_thread_test_data *thread_data = arg; 52362306a36Sopenharmony_ci struct inc_test_data *data = thread_data->data; 52462306a36Sopenharmony_ci long long i, reps; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (!opt_disable_rseq && thread_data->reg && 52762306a36Sopenharmony_ci rseq_register_current_thread()) 52862306a36Sopenharmony_ci abort(); 52962306a36Sopenharmony_ci reps = thread_data->reps; 53062306a36Sopenharmony_ci for (i = 0; i < reps; i++) { 53162306a36Sopenharmony_ci int ret; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci do { 53462306a36Sopenharmony_ci int cpu; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci cpu = get_current_cpu_id(); 53762306a36Sopenharmony_ci ret = rseq_addv(RSEQ_MO_RELAXED, RSEQ_PERCPU, 53862306a36Sopenharmony_ci &data->c[cpu].count, 1, cpu); 53962306a36Sopenharmony_ci } while (rseq_unlikely(ret)); 54062306a36Sopenharmony_ci#ifndef BENCHMARK 54162306a36Sopenharmony_ci if (i != 0 && !(i % (reps / 10))) 54262306a36Sopenharmony_ci printf_verbose("tid %d: count %lld\n", 54362306a36Sopenharmony_ci (int) rseq_gettid(), i); 54462306a36Sopenharmony_ci#endif 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", 54762306a36Sopenharmony_ci (int) rseq_gettid(), nr_abort, signals_delivered); 54862306a36Sopenharmony_ci if (!opt_disable_rseq && thread_data->reg && 54962306a36Sopenharmony_ci rseq_unregister_current_thread()) 55062306a36Sopenharmony_ci abort(); 55162306a36Sopenharmony_ci return NULL; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_civoid test_percpu_inc(void) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci const int num_threads = opt_threads; 55762306a36Sopenharmony_ci int i, ret; 55862306a36Sopenharmony_ci uint64_t sum; 55962306a36Sopenharmony_ci pthread_t test_threads[num_threads]; 56062306a36Sopenharmony_ci struct inc_test_data data; 56162306a36Sopenharmony_ci struct inc_thread_test_data thread_data[num_threads]; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 56462306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 56562306a36Sopenharmony_ci thread_data[i].reps = opt_reps; 56662306a36Sopenharmony_ci if (opt_disable_mod <= 0 || (i % opt_disable_mod)) 56762306a36Sopenharmony_ci thread_data[i].reg = 1; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci thread_data[i].reg = 0; 57062306a36Sopenharmony_ci thread_data[i].data = &data; 57162306a36Sopenharmony_ci ret = pthread_create(&test_threads[i], NULL, 57262306a36Sopenharmony_ci test_percpu_inc_thread, 57362306a36Sopenharmony_ci &thread_data[i]); 57462306a36Sopenharmony_ci if (ret) { 57562306a36Sopenharmony_ci errno = ret; 57662306a36Sopenharmony_ci perror("pthread_create"); 57762306a36Sopenharmony_ci abort(); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 58262306a36Sopenharmony_ci ret = pthread_join(test_threads[i], NULL); 58362306a36Sopenharmony_ci if (ret) { 58462306a36Sopenharmony_ci errno = ret; 58562306a36Sopenharmony_ci perror("pthread_join"); 58662306a36Sopenharmony_ci abort(); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci sum = 0; 59162306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) 59262306a36Sopenharmony_ci sum += data.c[i].count; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci assert(sum == (uint64_t)opt_reps * num_threads); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_civoid this_cpu_list_push(struct percpu_list *list, 59862306a36Sopenharmony_ci struct percpu_list_node *node, 59962306a36Sopenharmony_ci int *_cpu) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci int cpu; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci for (;;) { 60462306a36Sopenharmony_ci intptr_t *targetptr, newval, expect; 60562306a36Sopenharmony_ci int ret; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci cpu = get_current_cpu_id(); 60862306a36Sopenharmony_ci /* Load list->c[cpu].head with single-copy atomicity. */ 60962306a36Sopenharmony_ci expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head); 61062306a36Sopenharmony_ci newval = (intptr_t)node; 61162306a36Sopenharmony_ci targetptr = (intptr_t *)&list->c[cpu].head; 61262306a36Sopenharmony_ci node->next = (struct percpu_list_node *)expect; 61362306a36Sopenharmony_ci ret = rseq_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU, 61462306a36Sopenharmony_ci targetptr, expect, newval, cpu); 61562306a36Sopenharmony_ci if (rseq_likely(!ret)) 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci /* Retry if comparison fails or rseq aborts. */ 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci if (_cpu) 62062306a36Sopenharmony_ci *_cpu = cpu; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* 62462306a36Sopenharmony_ci * Unlike a traditional lock-less linked list; the availability of a 62562306a36Sopenharmony_ci * rseq primitive allows us to implement pop without concerns over 62662306a36Sopenharmony_ci * ABA-type races. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_cistruct percpu_list_node *this_cpu_list_pop(struct percpu_list *list, 62962306a36Sopenharmony_ci int *_cpu) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct percpu_list_node *node = NULL; 63262306a36Sopenharmony_ci int cpu; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci for (;;) { 63562306a36Sopenharmony_ci struct percpu_list_node *head; 63662306a36Sopenharmony_ci intptr_t *targetptr, expectnot, *load; 63762306a36Sopenharmony_ci long offset; 63862306a36Sopenharmony_ci int ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci cpu = get_current_cpu_id(); 64162306a36Sopenharmony_ci targetptr = (intptr_t *)&list->c[cpu].head; 64262306a36Sopenharmony_ci expectnot = (intptr_t)NULL; 64362306a36Sopenharmony_ci offset = offsetof(struct percpu_list_node, next); 64462306a36Sopenharmony_ci load = (intptr_t *)&head; 64562306a36Sopenharmony_ci ret = rseq_cmpnev_storeoffp_load(RSEQ_MO_RELAXED, RSEQ_PERCPU, 64662306a36Sopenharmony_ci targetptr, expectnot, 64762306a36Sopenharmony_ci offset, load, cpu); 64862306a36Sopenharmony_ci if (rseq_likely(!ret)) { 64962306a36Sopenharmony_ci node = head; 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci if (ret > 0) 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci /* Retry if rseq aborts. */ 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci if (_cpu) 65762306a36Sopenharmony_ci *_cpu = cpu; 65862306a36Sopenharmony_ci return node; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* 66262306a36Sopenharmony_ci * __percpu_list_pop is not safe against concurrent accesses. Should 66362306a36Sopenharmony_ci * only be used on lists that are not concurrently modified. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_cistruct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct percpu_list_node *node; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci node = list->c[cpu].head; 67062306a36Sopenharmony_ci if (!node) 67162306a36Sopenharmony_ci return NULL; 67262306a36Sopenharmony_ci list->c[cpu].head = node->next; 67362306a36Sopenharmony_ci return node; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_civoid *test_percpu_list_thread(void *arg) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci long long i, reps; 67962306a36Sopenharmony_ci struct percpu_list *list = (struct percpu_list *)arg; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_register_current_thread()) 68262306a36Sopenharmony_ci abort(); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci reps = opt_reps; 68562306a36Sopenharmony_ci for (i = 0; i < reps; i++) { 68662306a36Sopenharmony_ci struct percpu_list_node *node; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci node = this_cpu_list_pop(list, NULL); 68962306a36Sopenharmony_ci if (opt_yield) 69062306a36Sopenharmony_ci sched_yield(); /* encourage shuffling */ 69162306a36Sopenharmony_ci if (node) 69262306a36Sopenharmony_ci this_cpu_list_push(list, node, NULL); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", 69662306a36Sopenharmony_ci (int) rseq_gettid(), nr_abort, signals_delivered); 69762306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_unregister_current_thread()) 69862306a36Sopenharmony_ci abort(); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return NULL; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/* Simultaneous modification to a per-cpu linked list from many threads. */ 70462306a36Sopenharmony_civoid test_percpu_list(void) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci const int num_threads = opt_threads; 70762306a36Sopenharmony_ci int i, j, ret; 70862306a36Sopenharmony_ci uint64_t sum = 0, expected_sum = 0; 70962306a36Sopenharmony_ci struct percpu_list list; 71062306a36Sopenharmony_ci pthread_t test_threads[num_threads]; 71162306a36Sopenharmony_ci cpu_set_t allowed_cpus; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci memset(&list, 0, sizeof(list)); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Generate list entries for every usable cpu. */ 71662306a36Sopenharmony_ci sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus); 71762306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 71862306a36Sopenharmony_ci if (!CPU_ISSET(i, &allowed_cpus)) 71962306a36Sopenharmony_ci continue; 72062306a36Sopenharmony_ci for (j = 1; j <= 100; j++) { 72162306a36Sopenharmony_ci struct percpu_list_node *node; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci expected_sum += j; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci node = malloc(sizeof(*node)); 72662306a36Sopenharmony_ci assert(node); 72762306a36Sopenharmony_ci node->data = j; 72862306a36Sopenharmony_ci node->next = list.c[i].head; 72962306a36Sopenharmony_ci list.c[i].head = node; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 73462306a36Sopenharmony_ci ret = pthread_create(&test_threads[i], NULL, 73562306a36Sopenharmony_ci test_percpu_list_thread, &list); 73662306a36Sopenharmony_ci if (ret) { 73762306a36Sopenharmony_ci errno = ret; 73862306a36Sopenharmony_ci perror("pthread_create"); 73962306a36Sopenharmony_ci abort(); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 74462306a36Sopenharmony_ci ret = pthread_join(test_threads[i], NULL); 74562306a36Sopenharmony_ci if (ret) { 74662306a36Sopenharmony_ci errno = ret; 74762306a36Sopenharmony_ci perror("pthread_join"); 74862306a36Sopenharmony_ci abort(); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 75362306a36Sopenharmony_ci struct percpu_list_node *node; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (!CPU_ISSET(i, &allowed_cpus)) 75662306a36Sopenharmony_ci continue; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci while ((node = __percpu_list_pop(&list, i))) { 75962306a36Sopenharmony_ci sum += node->data; 76062306a36Sopenharmony_ci free(node); 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* 76562306a36Sopenharmony_ci * All entries should now be accounted for (unless some external 76662306a36Sopenharmony_ci * actor is interfering with our allowed affinity while this 76762306a36Sopenharmony_ci * test is running). 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci assert(sum == expected_sum); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cibool this_cpu_buffer_push(struct percpu_buffer *buffer, 77362306a36Sopenharmony_ci struct percpu_buffer_node *node, 77462306a36Sopenharmony_ci int *_cpu) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci bool result = false; 77762306a36Sopenharmony_ci int cpu; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci for (;;) { 78062306a36Sopenharmony_ci intptr_t *targetptr_spec, newval_spec; 78162306a36Sopenharmony_ci intptr_t *targetptr_final, newval_final; 78262306a36Sopenharmony_ci intptr_t offset; 78362306a36Sopenharmony_ci int ret; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci cpu = get_current_cpu_id(); 78662306a36Sopenharmony_ci offset = RSEQ_READ_ONCE(buffer->c[cpu].offset); 78762306a36Sopenharmony_ci if (offset == buffer->c[cpu].buflen) 78862306a36Sopenharmony_ci break; 78962306a36Sopenharmony_ci newval_spec = (intptr_t)node; 79062306a36Sopenharmony_ci targetptr_spec = (intptr_t *)&buffer->c[cpu].array[offset]; 79162306a36Sopenharmony_ci newval_final = offset + 1; 79262306a36Sopenharmony_ci targetptr_final = &buffer->c[cpu].offset; 79362306a36Sopenharmony_ci ret = rseq_cmpeqv_trystorev_storev(opt_mo, RSEQ_PERCPU, 79462306a36Sopenharmony_ci targetptr_final, offset, targetptr_spec, 79562306a36Sopenharmony_ci newval_spec, newval_final, cpu); 79662306a36Sopenharmony_ci if (rseq_likely(!ret)) { 79762306a36Sopenharmony_ci result = true; 79862306a36Sopenharmony_ci break; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci /* Retry if comparison fails or rseq aborts. */ 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci if (_cpu) 80362306a36Sopenharmony_ci *_cpu = cpu; 80462306a36Sopenharmony_ci return result; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistruct percpu_buffer_node *this_cpu_buffer_pop(struct percpu_buffer *buffer, 80862306a36Sopenharmony_ci int *_cpu) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct percpu_buffer_node *head; 81162306a36Sopenharmony_ci int cpu; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci for (;;) { 81462306a36Sopenharmony_ci intptr_t *targetptr, newval; 81562306a36Sopenharmony_ci intptr_t offset; 81662306a36Sopenharmony_ci int ret; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci cpu = get_current_cpu_id(); 81962306a36Sopenharmony_ci /* Load offset with single-copy atomicity. */ 82062306a36Sopenharmony_ci offset = RSEQ_READ_ONCE(buffer->c[cpu].offset); 82162306a36Sopenharmony_ci if (offset == 0) { 82262306a36Sopenharmony_ci head = NULL; 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci head = RSEQ_READ_ONCE(buffer->c[cpu].array[offset - 1]); 82662306a36Sopenharmony_ci newval = offset - 1; 82762306a36Sopenharmony_ci targetptr = (intptr_t *)&buffer->c[cpu].offset; 82862306a36Sopenharmony_ci ret = rseq_cmpeqv_cmpeqv_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU, 82962306a36Sopenharmony_ci targetptr, offset, 83062306a36Sopenharmony_ci (intptr_t *)&buffer->c[cpu].array[offset - 1], 83162306a36Sopenharmony_ci (intptr_t)head, newval, cpu); 83262306a36Sopenharmony_ci if (rseq_likely(!ret)) 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci /* Retry if comparison fails or rseq aborts. */ 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci if (_cpu) 83762306a36Sopenharmony_ci *_cpu = cpu; 83862306a36Sopenharmony_ci return head; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci/* 84262306a36Sopenharmony_ci * __percpu_buffer_pop is not safe against concurrent accesses. Should 84362306a36Sopenharmony_ci * only be used on buffers that are not concurrently modified. 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_cistruct percpu_buffer_node *__percpu_buffer_pop(struct percpu_buffer *buffer, 84662306a36Sopenharmony_ci int cpu) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct percpu_buffer_node *head; 84962306a36Sopenharmony_ci intptr_t offset; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci offset = buffer->c[cpu].offset; 85262306a36Sopenharmony_ci if (offset == 0) 85362306a36Sopenharmony_ci return NULL; 85462306a36Sopenharmony_ci head = buffer->c[cpu].array[offset - 1]; 85562306a36Sopenharmony_ci buffer->c[cpu].offset = offset - 1; 85662306a36Sopenharmony_ci return head; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_civoid *test_percpu_buffer_thread(void *arg) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci long long i, reps; 86262306a36Sopenharmony_ci struct percpu_buffer *buffer = (struct percpu_buffer *)arg; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_register_current_thread()) 86562306a36Sopenharmony_ci abort(); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci reps = opt_reps; 86862306a36Sopenharmony_ci for (i = 0; i < reps; i++) { 86962306a36Sopenharmony_ci struct percpu_buffer_node *node; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci node = this_cpu_buffer_pop(buffer, NULL); 87262306a36Sopenharmony_ci if (opt_yield) 87362306a36Sopenharmony_ci sched_yield(); /* encourage shuffling */ 87462306a36Sopenharmony_ci if (node) { 87562306a36Sopenharmony_ci if (!this_cpu_buffer_push(buffer, node, NULL)) { 87662306a36Sopenharmony_ci /* Should increase buffer size. */ 87762306a36Sopenharmony_ci abort(); 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", 88362306a36Sopenharmony_ci (int) rseq_gettid(), nr_abort, signals_delivered); 88462306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_unregister_current_thread()) 88562306a36Sopenharmony_ci abort(); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return NULL; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci/* Simultaneous modification to a per-cpu buffer from many threads. */ 89162306a36Sopenharmony_civoid test_percpu_buffer(void) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci const int num_threads = opt_threads; 89462306a36Sopenharmony_ci int i, j, ret; 89562306a36Sopenharmony_ci uint64_t sum = 0, expected_sum = 0; 89662306a36Sopenharmony_ci struct percpu_buffer buffer; 89762306a36Sopenharmony_ci pthread_t test_threads[num_threads]; 89862306a36Sopenharmony_ci cpu_set_t allowed_cpus; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci memset(&buffer, 0, sizeof(buffer)); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* Generate list entries for every usable cpu. */ 90362306a36Sopenharmony_ci sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus); 90462306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 90562306a36Sopenharmony_ci if (!CPU_ISSET(i, &allowed_cpus)) 90662306a36Sopenharmony_ci continue; 90762306a36Sopenharmony_ci /* Worse-case is every item in same CPU. */ 90862306a36Sopenharmony_ci buffer.c[i].array = 90962306a36Sopenharmony_ci malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE * 91062306a36Sopenharmony_ci BUFFER_ITEM_PER_CPU); 91162306a36Sopenharmony_ci assert(buffer.c[i].array); 91262306a36Sopenharmony_ci buffer.c[i].buflen = CPU_SETSIZE * BUFFER_ITEM_PER_CPU; 91362306a36Sopenharmony_ci for (j = 1; j <= BUFFER_ITEM_PER_CPU; j++) { 91462306a36Sopenharmony_ci struct percpu_buffer_node *node; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci expected_sum += j; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* 91962306a36Sopenharmony_ci * We could theoretically put the word-sized 92062306a36Sopenharmony_ci * "data" directly in the buffer. However, we 92162306a36Sopenharmony_ci * want to model objects that would not fit 92262306a36Sopenharmony_ci * within a single word, so allocate an object 92362306a36Sopenharmony_ci * for each node. 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_ci node = malloc(sizeof(*node)); 92662306a36Sopenharmony_ci assert(node); 92762306a36Sopenharmony_ci node->data = j; 92862306a36Sopenharmony_ci buffer.c[i].array[j - 1] = node; 92962306a36Sopenharmony_ci buffer.c[i].offset++; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 93462306a36Sopenharmony_ci ret = pthread_create(&test_threads[i], NULL, 93562306a36Sopenharmony_ci test_percpu_buffer_thread, &buffer); 93662306a36Sopenharmony_ci if (ret) { 93762306a36Sopenharmony_ci errno = ret; 93862306a36Sopenharmony_ci perror("pthread_create"); 93962306a36Sopenharmony_ci abort(); 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 94462306a36Sopenharmony_ci ret = pthread_join(test_threads[i], NULL); 94562306a36Sopenharmony_ci if (ret) { 94662306a36Sopenharmony_ci errno = ret; 94762306a36Sopenharmony_ci perror("pthread_join"); 94862306a36Sopenharmony_ci abort(); 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 95362306a36Sopenharmony_ci struct percpu_buffer_node *node; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (!CPU_ISSET(i, &allowed_cpus)) 95662306a36Sopenharmony_ci continue; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci while ((node = __percpu_buffer_pop(&buffer, i))) { 95962306a36Sopenharmony_ci sum += node->data; 96062306a36Sopenharmony_ci free(node); 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci free(buffer.c[i].array); 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* 96662306a36Sopenharmony_ci * All entries should now be accounted for (unless some external 96762306a36Sopenharmony_ci * actor is interfering with our allowed affinity while this 96862306a36Sopenharmony_ci * test is running). 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_ci assert(sum == expected_sum); 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cibool this_cpu_memcpy_buffer_push(struct percpu_memcpy_buffer *buffer, 97462306a36Sopenharmony_ci struct percpu_memcpy_buffer_node item, 97562306a36Sopenharmony_ci int *_cpu) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci bool result = false; 97862306a36Sopenharmony_ci int cpu; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci for (;;) { 98162306a36Sopenharmony_ci intptr_t *targetptr_final, newval_final, offset; 98262306a36Sopenharmony_ci char *destptr, *srcptr; 98362306a36Sopenharmony_ci size_t copylen; 98462306a36Sopenharmony_ci int ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci cpu = get_current_cpu_id(); 98762306a36Sopenharmony_ci /* Load offset with single-copy atomicity. */ 98862306a36Sopenharmony_ci offset = RSEQ_READ_ONCE(buffer->c[cpu].offset); 98962306a36Sopenharmony_ci if (offset == buffer->c[cpu].buflen) 99062306a36Sopenharmony_ci break; 99162306a36Sopenharmony_ci destptr = (char *)&buffer->c[cpu].array[offset]; 99262306a36Sopenharmony_ci srcptr = (char *)&item; 99362306a36Sopenharmony_ci /* copylen must be <= 4kB. */ 99462306a36Sopenharmony_ci copylen = sizeof(item); 99562306a36Sopenharmony_ci newval_final = offset + 1; 99662306a36Sopenharmony_ci targetptr_final = &buffer->c[cpu].offset; 99762306a36Sopenharmony_ci ret = rseq_cmpeqv_trymemcpy_storev( 99862306a36Sopenharmony_ci opt_mo, RSEQ_PERCPU, 99962306a36Sopenharmony_ci targetptr_final, offset, 100062306a36Sopenharmony_ci destptr, srcptr, copylen, 100162306a36Sopenharmony_ci newval_final, cpu); 100262306a36Sopenharmony_ci if (rseq_likely(!ret)) { 100362306a36Sopenharmony_ci result = true; 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci /* Retry if comparison fails or rseq aborts. */ 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci if (_cpu) 100962306a36Sopenharmony_ci *_cpu = cpu; 101062306a36Sopenharmony_ci return result; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cibool this_cpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer, 101462306a36Sopenharmony_ci struct percpu_memcpy_buffer_node *item, 101562306a36Sopenharmony_ci int *_cpu) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci bool result = false; 101862306a36Sopenharmony_ci int cpu; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci for (;;) { 102162306a36Sopenharmony_ci intptr_t *targetptr_final, newval_final, offset; 102262306a36Sopenharmony_ci char *destptr, *srcptr; 102362306a36Sopenharmony_ci size_t copylen; 102462306a36Sopenharmony_ci int ret; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci cpu = get_current_cpu_id(); 102762306a36Sopenharmony_ci /* Load offset with single-copy atomicity. */ 102862306a36Sopenharmony_ci offset = RSEQ_READ_ONCE(buffer->c[cpu].offset); 102962306a36Sopenharmony_ci if (offset == 0) 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci destptr = (char *)item; 103262306a36Sopenharmony_ci srcptr = (char *)&buffer->c[cpu].array[offset - 1]; 103362306a36Sopenharmony_ci /* copylen must be <= 4kB. */ 103462306a36Sopenharmony_ci copylen = sizeof(*item); 103562306a36Sopenharmony_ci newval_final = offset - 1; 103662306a36Sopenharmony_ci targetptr_final = &buffer->c[cpu].offset; 103762306a36Sopenharmony_ci ret = rseq_cmpeqv_trymemcpy_storev(RSEQ_MO_RELAXED, RSEQ_PERCPU, 103862306a36Sopenharmony_ci targetptr_final, offset, destptr, srcptr, copylen, 103962306a36Sopenharmony_ci newval_final, cpu); 104062306a36Sopenharmony_ci if (rseq_likely(!ret)) { 104162306a36Sopenharmony_ci result = true; 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci /* Retry if comparison fails or rseq aborts. */ 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci if (_cpu) 104762306a36Sopenharmony_ci *_cpu = cpu; 104862306a36Sopenharmony_ci return result; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci/* 105262306a36Sopenharmony_ci * __percpu_memcpy_buffer_pop is not safe against concurrent accesses. Should 105362306a36Sopenharmony_ci * only be used on buffers that are not concurrently modified. 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_cibool __percpu_memcpy_buffer_pop(struct percpu_memcpy_buffer *buffer, 105662306a36Sopenharmony_ci struct percpu_memcpy_buffer_node *item, 105762306a36Sopenharmony_ci int cpu) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci intptr_t offset; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci offset = buffer->c[cpu].offset; 106262306a36Sopenharmony_ci if (offset == 0) 106362306a36Sopenharmony_ci return false; 106462306a36Sopenharmony_ci memcpy(item, &buffer->c[cpu].array[offset - 1], sizeof(*item)); 106562306a36Sopenharmony_ci buffer->c[cpu].offset = offset - 1; 106662306a36Sopenharmony_ci return true; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_civoid *test_percpu_memcpy_buffer_thread(void *arg) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci long long i, reps; 107262306a36Sopenharmony_ci struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_register_current_thread()) 107562306a36Sopenharmony_ci abort(); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci reps = opt_reps; 107862306a36Sopenharmony_ci for (i = 0; i < reps; i++) { 107962306a36Sopenharmony_ci struct percpu_memcpy_buffer_node item; 108062306a36Sopenharmony_ci bool result; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci result = this_cpu_memcpy_buffer_pop(buffer, &item, NULL); 108362306a36Sopenharmony_ci if (opt_yield) 108462306a36Sopenharmony_ci sched_yield(); /* encourage shuffling */ 108562306a36Sopenharmony_ci if (result) { 108662306a36Sopenharmony_ci if (!this_cpu_memcpy_buffer_push(buffer, item, NULL)) { 108762306a36Sopenharmony_ci /* Should increase buffer size. */ 108862306a36Sopenharmony_ci abort(); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", 109462306a36Sopenharmony_ci (int) rseq_gettid(), nr_abort, signals_delivered); 109562306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_unregister_current_thread()) 109662306a36Sopenharmony_ci abort(); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return NULL; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci/* Simultaneous modification to a per-cpu buffer from many threads. */ 110262306a36Sopenharmony_civoid test_percpu_memcpy_buffer(void) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci const int num_threads = opt_threads; 110562306a36Sopenharmony_ci int i, j, ret; 110662306a36Sopenharmony_ci uint64_t sum = 0, expected_sum = 0; 110762306a36Sopenharmony_ci struct percpu_memcpy_buffer buffer; 110862306a36Sopenharmony_ci pthread_t test_threads[num_threads]; 110962306a36Sopenharmony_ci cpu_set_t allowed_cpus; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci memset(&buffer, 0, sizeof(buffer)); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* Generate list entries for every usable cpu. */ 111462306a36Sopenharmony_ci sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus); 111562306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 111662306a36Sopenharmony_ci if (!CPU_ISSET(i, &allowed_cpus)) 111762306a36Sopenharmony_ci continue; 111862306a36Sopenharmony_ci /* Worse-case is every item in same CPU. */ 111962306a36Sopenharmony_ci buffer.c[i].array = 112062306a36Sopenharmony_ci malloc(sizeof(*buffer.c[i].array) * CPU_SETSIZE * 112162306a36Sopenharmony_ci MEMCPY_BUFFER_ITEM_PER_CPU); 112262306a36Sopenharmony_ci assert(buffer.c[i].array); 112362306a36Sopenharmony_ci buffer.c[i].buflen = CPU_SETSIZE * MEMCPY_BUFFER_ITEM_PER_CPU; 112462306a36Sopenharmony_ci for (j = 1; j <= MEMCPY_BUFFER_ITEM_PER_CPU; j++) { 112562306a36Sopenharmony_ci expected_sum += 2 * j + 1; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* 112862306a36Sopenharmony_ci * We could theoretically put the word-sized 112962306a36Sopenharmony_ci * "data" directly in the buffer. However, we 113062306a36Sopenharmony_ci * want to model objects that would not fit 113162306a36Sopenharmony_ci * within a single word, so allocate an object 113262306a36Sopenharmony_ci * for each node. 113362306a36Sopenharmony_ci */ 113462306a36Sopenharmony_ci buffer.c[i].array[j - 1].data1 = j; 113562306a36Sopenharmony_ci buffer.c[i].array[j - 1].data2 = j + 1; 113662306a36Sopenharmony_ci buffer.c[i].offset++; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 114162306a36Sopenharmony_ci ret = pthread_create(&test_threads[i], NULL, 114262306a36Sopenharmony_ci test_percpu_memcpy_buffer_thread, 114362306a36Sopenharmony_ci &buffer); 114462306a36Sopenharmony_ci if (ret) { 114562306a36Sopenharmony_ci errno = ret; 114662306a36Sopenharmony_ci perror("pthread_create"); 114762306a36Sopenharmony_ci abort(); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 115262306a36Sopenharmony_ci ret = pthread_join(test_threads[i], NULL); 115362306a36Sopenharmony_ci if (ret) { 115462306a36Sopenharmony_ci errno = ret; 115562306a36Sopenharmony_ci perror("pthread_join"); 115662306a36Sopenharmony_ci abort(); 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 116162306a36Sopenharmony_ci struct percpu_memcpy_buffer_node item; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (!CPU_ISSET(i, &allowed_cpus)) 116462306a36Sopenharmony_ci continue; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci while (__percpu_memcpy_buffer_pop(&buffer, &item, i)) { 116762306a36Sopenharmony_ci sum += item.data1; 116862306a36Sopenharmony_ci sum += item.data2; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci free(buffer.c[i].array); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* 117462306a36Sopenharmony_ci * All entries should now be accounted for (unless some external 117562306a36Sopenharmony_ci * actor is interfering with our allowed affinity while this 117662306a36Sopenharmony_ci * test is running). 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci assert(sum == expected_sum); 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cistatic void test_signal_interrupt_handler(int signo) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci signals_delivered++; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int set_signal_handler(void) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci int ret = 0; 118962306a36Sopenharmony_ci struct sigaction sa; 119062306a36Sopenharmony_ci sigset_t sigset; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ret = sigemptyset(&sigset); 119362306a36Sopenharmony_ci if (ret < 0) { 119462306a36Sopenharmony_ci perror("sigemptyset"); 119562306a36Sopenharmony_ci return ret; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci sa.sa_handler = test_signal_interrupt_handler; 119962306a36Sopenharmony_ci sa.sa_mask = sigset; 120062306a36Sopenharmony_ci sa.sa_flags = 0; 120162306a36Sopenharmony_ci ret = sigaction(SIGUSR1, &sa, NULL); 120262306a36Sopenharmony_ci if (ret < 0) { 120362306a36Sopenharmony_ci perror("sigaction"); 120462306a36Sopenharmony_ci return ret; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci printf_verbose("Signal handler set for SIGUSR1\n"); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci return ret; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci/* Test MEMBARRIER_CMD_PRIVATE_RESTART_RSEQ_ON_CPU membarrier command. */ 121362306a36Sopenharmony_ci#ifdef TEST_MEMBARRIER 121462306a36Sopenharmony_cistruct test_membarrier_thread_args { 121562306a36Sopenharmony_ci int stop; 121662306a36Sopenharmony_ci intptr_t percpu_list_ptr; 121762306a36Sopenharmony_ci}; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci/* Worker threads modify data in their "active" percpu lists. */ 122062306a36Sopenharmony_civoid *test_membarrier_worker_thread(void *arg) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct test_membarrier_thread_args *args = 122362306a36Sopenharmony_ci (struct test_membarrier_thread_args *)arg; 122462306a36Sopenharmony_ci const int iters = opt_reps; 122562306a36Sopenharmony_ci int i; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (rseq_register_current_thread()) { 122862306a36Sopenharmony_ci fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", 122962306a36Sopenharmony_ci errno, strerror(errno)); 123062306a36Sopenharmony_ci abort(); 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Wait for initialization. */ 123462306a36Sopenharmony_ci while (!atomic_load(&args->percpu_list_ptr)) {} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci for (i = 0; i < iters; ++i) { 123762306a36Sopenharmony_ci int ret; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci do { 124062306a36Sopenharmony_ci int cpu = get_current_cpu_id(); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci ret = rseq_offset_deref_addv(RSEQ_MO_RELAXED, RSEQ_PERCPU, 124362306a36Sopenharmony_ci &args->percpu_list_ptr, 124462306a36Sopenharmony_ci sizeof(struct percpu_list_entry) * cpu, 1, cpu); 124562306a36Sopenharmony_ci } while (rseq_unlikely(ret)); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (rseq_unregister_current_thread()) { 124962306a36Sopenharmony_ci fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", 125062306a36Sopenharmony_ci errno, strerror(errno)); 125162306a36Sopenharmony_ci abort(); 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci return NULL; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_civoid test_membarrier_init_percpu_list(struct percpu_list *list) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci int i; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci memset(list, 0, sizeof(*list)); 126162306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) { 126262306a36Sopenharmony_ci struct percpu_list_node *node; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci node = malloc(sizeof(*node)); 126562306a36Sopenharmony_ci assert(node); 126662306a36Sopenharmony_ci node->data = 0; 126762306a36Sopenharmony_ci node->next = NULL; 126862306a36Sopenharmony_ci list->c[i].head = node; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_civoid test_membarrier_free_percpu_list(struct percpu_list *list) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci int i; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci for (i = 0; i < CPU_SETSIZE; i++) 127762306a36Sopenharmony_ci free(list->c[i].head); 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci/* 128162306a36Sopenharmony_ci * The manager thread swaps per-cpu lists that worker threads see, 128262306a36Sopenharmony_ci * and validates that there are no unexpected modifications. 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_civoid *test_membarrier_manager_thread(void *arg) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci struct test_membarrier_thread_args *args = 128762306a36Sopenharmony_ci (struct test_membarrier_thread_args *)arg; 128862306a36Sopenharmony_ci struct percpu_list list_a, list_b; 128962306a36Sopenharmony_ci intptr_t expect_a = 0, expect_b = 0; 129062306a36Sopenharmony_ci int cpu_a = 0, cpu_b = 0; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (rseq_register_current_thread()) { 129362306a36Sopenharmony_ci fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", 129462306a36Sopenharmony_ci errno, strerror(errno)); 129562306a36Sopenharmony_ci abort(); 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* Init lists. */ 129962306a36Sopenharmony_ci test_membarrier_init_percpu_list(&list_a); 130062306a36Sopenharmony_ci test_membarrier_init_percpu_list(&list_b); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci while (!atomic_load(&args->stop)) { 130562306a36Sopenharmony_ci /* list_a is "active". */ 130662306a36Sopenharmony_ci cpu_a = rand() % CPU_SETSIZE; 130762306a36Sopenharmony_ci /* 130862306a36Sopenharmony_ci * As list_b is "inactive", we should never see changes 130962306a36Sopenharmony_ci * to list_b. 131062306a36Sopenharmony_ci */ 131162306a36Sopenharmony_ci if (expect_b != atomic_load(&list_b.c[cpu_b].head->data)) { 131262306a36Sopenharmony_ci fprintf(stderr, "Membarrier test failed\n"); 131362306a36Sopenharmony_ci abort(); 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci /* Make list_b "active". */ 131762306a36Sopenharmony_ci atomic_store(&args->percpu_list_ptr, (intptr_t)&list_b); 131862306a36Sopenharmony_ci if (rseq_membarrier_expedited(cpu_a) && 131962306a36Sopenharmony_ci errno != ENXIO /* missing CPU */) { 132062306a36Sopenharmony_ci perror("sys_membarrier"); 132162306a36Sopenharmony_ci abort(); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci /* 132462306a36Sopenharmony_ci * Cpu A should now only modify list_b, so the values 132562306a36Sopenharmony_ci * in list_a should be stable. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci expect_a = atomic_load(&list_a.c[cpu_a].head->data); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci cpu_b = rand() % CPU_SETSIZE; 133062306a36Sopenharmony_ci /* 133162306a36Sopenharmony_ci * As list_a is "inactive", we should never see changes 133262306a36Sopenharmony_ci * to list_a. 133362306a36Sopenharmony_ci */ 133462306a36Sopenharmony_ci if (expect_a != atomic_load(&list_a.c[cpu_a].head->data)) { 133562306a36Sopenharmony_ci fprintf(stderr, "Membarrier test failed\n"); 133662306a36Sopenharmony_ci abort(); 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci /* Make list_a "active". */ 134062306a36Sopenharmony_ci atomic_store(&args->percpu_list_ptr, (intptr_t)&list_a); 134162306a36Sopenharmony_ci if (rseq_membarrier_expedited(cpu_b) && 134262306a36Sopenharmony_ci errno != ENXIO /* missing CPU*/) { 134362306a36Sopenharmony_ci perror("sys_membarrier"); 134462306a36Sopenharmony_ci abort(); 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci /* Remember a value from list_b. */ 134762306a36Sopenharmony_ci expect_b = atomic_load(&list_b.c[cpu_b].head->data); 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci test_membarrier_free_percpu_list(&list_a); 135162306a36Sopenharmony_ci test_membarrier_free_percpu_list(&list_b); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (rseq_unregister_current_thread()) { 135462306a36Sopenharmony_ci fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", 135562306a36Sopenharmony_ci errno, strerror(errno)); 135662306a36Sopenharmony_ci abort(); 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci return NULL; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_civoid test_membarrier(void) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci const int num_threads = opt_threads; 136462306a36Sopenharmony_ci struct test_membarrier_thread_args thread_args; 136562306a36Sopenharmony_ci pthread_t worker_threads[num_threads]; 136662306a36Sopenharmony_ci pthread_t manager_thread; 136762306a36Sopenharmony_ci int i, ret; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci if (sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0, 0)) { 137062306a36Sopenharmony_ci perror("sys_membarrier"); 137162306a36Sopenharmony_ci abort(); 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci thread_args.stop = 0; 137562306a36Sopenharmony_ci thread_args.percpu_list_ptr = 0; 137662306a36Sopenharmony_ci ret = pthread_create(&manager_thread, NULL, 137762306a36Sopenharmony_ci test_membarrier_manager_thread, &thread_args); 137862306a36Sopenharmony_ci if (ret) { 137962306a36Sopenharmony_ci errno = ret; 138062306a36Sopenharmony_ci perror("pthread_create"); 138162306a36Sopenharmony_ci abort(); 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 138562306a36Sopenharmony_ci ret = pthread_create(&worker_threads[i], NULL, 138662306a36Sopenharmony_ci test_membarrier_worker_thread, &thread_args); 138762306a36Sopenharmony_ci if (ret) { 138862306a36Sopenharmony_ci errno = ret; 138962306a36Sopenharmony_ci perror("pthread_create"); 139062306a36Sopenharmony_ci abort(); 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci for (i = 0; i < num_threads; i++) { 139662306a36Sopenharmony_ci ret = pthread_join(worker_threads[i], NULL); 139762306a36Sopenharmony_ci if (ret) { 139862306a36Sopenharmony_ci errno = ret; 139962306a36Sopenharmony_ci perror("pthread_join"); 140062306a36Sopenharmony_ci abort(); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci atomic_store(&thread_args.stop, 1); 140562306a36Sopenharmony_ci ret = pthread_join(manager_thread, NULL); 140662306a36Sopenharmony_ci if (ret) { 140762306a36Sopenharmony_ci errno = ret; 140862306a36Sopenharmony_ci perror("pthread_join"); 140962306a36Sopenharmony_ci abort(); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci#else /* TEST_MEMBARRIER */ 141362306a36Sopenharmony_civoid test_membarrier(void) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci fprintf(stderr, "rseq_offset_deref_addv is not implemented on this architecture. " 141662306a36Sopenharmony_ci "Skipping membarrier test.\n"); 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci#endif 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic void show_usage(int argc, char **argv) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci printf("Usage : %s <OPTIONS>\n", 142362306a36Sopenharmony_ci argv[0]); 142462306a36Sopenharmony_ci printf("OPTIONS:\n"); 142562306a36Sopenharmony_ci printf(" [-1 loops] Number of loops for delay injection 1\n"); 142662306a36Sopenharmony_ci printf(" [-2 loops] Number of loops for delay injection 2\n"); 142762306a36Sopenharmony_ci printf(" [-3 loops] Number of loops for delay injection 3\n"); 142862306a36Sopenharmony_ci printf(" [-4 loops] Number of loops for delay injection 4\n"); 142962306a36Sopenharmony_ci printf(" [-5 loops] Number of loops for delay injection 5\n"); 143062306a36Sopenharmony_ci printf(" [-6 loops] Number of loops for delay injection 6\n"); 143162306a36Sopenharmony_ci printf(" [-7 loops] Number of loops for delay injection 7 (-1 to enable -m)\n"); 143262306a36Sopenharmony_ci printf(" [-8 loops] Number of loops for delay injection 8 (-1 to enable -m)\n"); 143362306a36Sopenharmony_ci printf(" [-9 loops] Number of loops for delay injection 9 (-1 to enable -m)\n"); 143462306a36Sopenharmony_ci printf(" [-m N] Yield/sleep/kill every modulo N (default 0: disabled) (>= 0)\n"); 143562306a36Sopenharmony_ci printf(" [-y] Yield\n"); 143662306a36Sopenharmony_ci printf(" [-k] Kill thread with signal\n"); 143762306a36Sopenharmony_ci printf(" [-s S] S: =0: disabled (default), >0: sleep time (ms)\n"); 143862306a36Sopenharmony_ci printf(" [-t N] Number of threads (default 200)\n"); 143962306a36Sopenharmony_ci printf(" [-r N] Number of repetitions per thread (default 5000)\n"); 144062306a36Sopenharmony_ci printf(" [-d] Disable rseq system call (no initialization)\n"); 144162306a36Sopenharmony_ci printf(" [-D M] Disable rseq for each M threads\n"); 144262306a36Sopenharmony_ci printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n"); 144362306a36Sopenharmony_ci printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n"); 144462306a36Sopenharmony_ci printf(" [-v] Verbose output.\n"); 144562306a36Sopenharmony_ci printf(" [-h] Show this help.\n"); 144662306a36Sopenharmony_ci printf("\n"); 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ciint main(int argc, char **argv) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci int i; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci for (i = 1; i < argc; i++) { 145462306a36Sopenharmony_ci if (argv[i][0] != '-') 145562306a36Sopenharmony_ci continue; 145662306a36Sopenharmony_ci switch (argv[i][1]) { 145762306a36Sopenharmony_ci case '1': 145862306a36Sopenharmony_ci case '2': 145962306a36Sopenharmony_ci case '3': 146062306a36Sopenharmony_ci case '4': 146162306a36Sopenharmony_ci case '5': 146262306a36Sopenharmony_ci case '6': 146362306a36Sopenharmony_ci case '7': 146462306a36Sopenharmony_ci case '8': 146562306a36Sopenharmony_ci case '9': 146662306a36Sopenharmony_ci if (argc < i + 2) { 146762306a36Sopenharmony_ci show_usage(argc, argv); 146862306a36Sopenharmony_ci goto error; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci loop_cnt[argv[i][1] - '0'] = atol(argv[i + 1]); 147162306a36Sopenharmony_ci i++; 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci case 'm': 147462306a36Sopenharmony_ci if (argc < i + 2) { 147562306a36Sopenharmony_ci show_usage(argc, argv); 147662306a36Sopenharmony_ci goto error; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci opt_modulo = atol(argv[i + 1]); 147962306a36Sopenharmony_ci if (opt_modulo < 0) { 148062306a36Sopenharmony_ci show_usage(argc, argv); 148162306a36Sopenharmony_ci goto error; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci i++; 148462306a36Sopenharmony_ci break; 148562306a36Sopenharmony_ci case 's': 148662306a36Sopenharmony_ci if (argc < i + 2) { 148762306a36Sopenharmony_ci show_usage(argc, argv); 148862306a36Sopenharmony_ci goto error; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci opt_sleep = atol(argv[i + 1]); 149162306a36Sopenharmony_ci if (opt_sleep < 0) { 149262306a36Sopenharmony_ci show_usage(argc, argv); 149362306a36Sopenharmony_ci goto error; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci i++; 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci case 'y': 149862306a36Sopenharmony_ci opt_yield = 1; 149962306a36Sopenharmony_ci break; 150062306a36Sopenharmony_ci case 'k': 150162306a36Sopenharmony_ci opt_signal = 1; 150262306a36Sopenharmony_ci break; 150362306a36Sopenharmony_ci case 'd': 150462306a36Sopenharmony_ci opt_disable_rseq = 1; 150562306a36Sopenharmony_ci break; 150662306a36Sopenharmony_ci case 'D': 150762306a36Sopenharmony_ci if (argc < i + 2) { 150862306a36Sopenharmony_ci show_usage(argc, argv); 150962306a36Sopenharmony_ci goto error; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci opt_disable_mod = atol(argv[i + 1]); 151262306a36Sopenharmony_ci if (opt_disable_mod < 0) { 151362306a36Sopenharmony_ci show_usage(argc, argv); 151462306a36Sopenharmony_ci goto error; 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci i++; 151762306a36Sopenharmony_ci break; 151862306a36Sopenharmony_ci case 't': 151962306a36Sopenharmony_ci if (argc < i + 2) { 152062306a36Sopenharmony_ci show_usage(argc, argv); 152162306a36Sopenharmony_ci goto error; 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci opt_threads = atol(argv[i + 1]); 152462306a36Sopenharmony_ci if (opt_threads < 0) { 152562306a36Sopenharmony_ci show_usage(argc, argv); 152662306a36Sopenharmony_ci goto error; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci i++; 152962306a36Sopenharmony_ci break; 153062306a36Sopenharmony_ci case 'r': 153162306a36Sopenharmony_ci if (argc < i + 2) { 153262306a36Sopenharmony_ci show_usage(argc, argv); 153362306a36Sopenharmony_ci goto error; 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci opt_reps = atoll(argv[i + 1]); 153662306a36Sopenharmony_ci if (opt_reps < 0) { 153762306a36Sopenharmony_ci show_usage(argc, argv); 153862306a36Sopenharmony_ci goto error; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci i++; 154162306a36Sopenharmony_ci break; 154262306a36Sopenharmony_ci case 'h': 154362306a36Sopenharmony_ci show_usage(argc, argv); 154462306a36Sopenharmony_ci goto end; 154562306a36Sopenharmony_ci case 'T': 154662306a36Sopenharmony_ci if (argc < i + 2) { 154762306a36Sopenharmony_ci show_usage(argc, argv); 154862306a36Sopenharmony_ci goto error; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci opt_test = *argv[i + 1]; 155162306a36Sopenharmony_ci switch (opt_test) { 155262306a36Sopenharmony_ci case 's': 155362306a36Sopenharmony_ci case 'l': 155462306a36Sopenharmony_ci case 'i': 155562306a36Sopenharmony_ci case 'b': 155662306a36Sopenharmony_ci case 'm': 155762306a36Sopenharmony_ci case 'r': 155862306a36Sopenharmony_ci break; 155962306a36Sopenharmony_ci default: 156062306a36Sopenharmony_ci show_usage(argc, argv); 156162306a36Sopenharmony_ci goto error; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci i++; 156462306a36Sopenharmony_ci break; 156562306a36Sopenharmony_ci case 'v': 156662306a36Sopenharmony_ci verbose = 1; 156762306a36Sopenharmony_ci break; 156862306a36Sopenharmony_ci case 'M': 156962306a36Sopenharmony_ci opt_mo = RSEQ_MO_RELEASE; 157062306a36Sopenharmony_ci break; 157162306a36Sopenharmony_ci default: 157262306a36Sopenharmony_ci show_usage(argc, argv); 157362306a36Sopenharmony_ci goto error; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci loop_cnt_1 = loop_cnt[1]; 157862306a36Sopenharmony_ci loop_cnt_2 = loop_cnt[2]; 157962306a36Sopenharmony_ci loop_cnt_3 = loop_cnt[3]; 158062306a36Sopenharmony_ci loop_cnt_4 = loop_cnt[4]; 158162306a36Sopenharmony_ci loop_cnt_5 = loop_cnt[5]; 158262306a36Sopenharmony_ci loop_cnt_6 = loop_cnt[6]; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (set_signal_handler()) 158562306a36Sopenharmony_ci goto error; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_register_current_thread()) 158862306a36Sopenharmony_ci goto error; 158962306a36Sopenharmony_ci if (!opt_disable_rseq && !rseq_validate_cpu_id()) { 159062306a36Sopenharmony_ci fprintf(stderr, "Error: cpu id getter unavailable\n"); 159162306a36Sopenharmony_ci goto error; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci switch (opt_test) { 159462306a36Sopenharmony_ci case 's': 159562306a36Sopenharmony_ci printf_verbose("spinlock\n"); 159662306a36Sopenharmony_ci test_percpu_spinlock(); 159762306a36Sopenharmony_ci break; 159862306a36Sopenharmony_ci case 'l': 159962306a36Sopenharmony_ci printf_verbose("linked list\n"); 160062306a36Sopenharmony_ci test_percpu_list(); 160162306a36Sopenharmony_ci break; 160262306a36Sopenharmony_ci case 'b': 160362306a36Sopenharmony_ci printf_verbose("buffer\n"); 160462306a36Sopenharmony_ci test_percpu_buffer(); 160562306a36Sopenharmony_ci break; 160662306a36Sopenharmony_ci case 'm': 160762306a36Sopenharmony_ci printf_verbose("memcpy buffer\n"); 160862306a36Sopenharmony_ci test_percpu_memcpy_buffer(); 160962306a36Sopenharmony_ci break; 161062306a36Sopenharmony_ci case 'i': 161162306a36Sopenharmony_ci printf_verbose("counter increment\n"); 161262306a36Sopenharmony_ci test_percpu_inc(); 161362306a36Sopenharmony_ci break; 161462306a36Sopenharmony_ci case 'r': 161562306a36Sopenharmony_ci printf_verbose("membarrier\n"); 161662306a36Sopenharmony_ci test_membarrier(); 161762306a36Sopenharmony_ci break; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci if (!opt_disable_rseq && rseq_unregister_current_thread()) 162062306a36Sopenharmony_ci abort(); 162162306a36Sopenharmony_ciend: 162262306a36Sopenharmony_ci return 0; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cierror: 162562306a36Sopenharmony_ci return -1; 162662306a36Sopenharmony_ci} 1627