param_test.c 30 KB

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