tests: use SPDX identifiers
[urcu.git] / tests / benchmark / test_urcu_lfs.c
CommitLineData
ce29b371
MJ
1// SPDX-FileCopyrightText: 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
2// SPDX-FileCopyrightText: 2010 Paolo Bonzini <pbonzini@redhat.com>
3//
4// SPDX-License-Identifier: GPL-2.0-or-later
5
453629a9 6/*
cb8818f0 7 * Userspace RCU library - example lock-free stack
453629a9
MD
8 */
9
453629a9
MD
10#include <stdio.h>
11#include <pthread.h>
12#include <stdlib.h>
13#include <stdint.h>
14#include <stdbool.h>
15#include <string.h>
16#include <sys/types.h>
17#include <sys/wait.h>
18#include <unistd.h>
19#include <stdio.h>
453629a9
MD
20#include <errno.h>
21
22#include <urcu/arch.h>
01477510 23#include <urcu/assert.h>
bd252a04 24#include <urcu/tls-compat.h>
94df6318 25#include "thread-id.h"
65fcc7e9 26
453629a9
MD
27/* hardcoded number of CPUs */
28#define NR_CPUS 16384
29
453629a9
MD
30#ifndef DYNAMIC_LINK_TEST
31#define _LGPL_SOURCE
32#endif
33#include <urcu.h>
e5a7d709 34#include <urcu/cds.h>
453629a9 35
f9b5c2b6
MD
36#define POISON_PTR ((void *) 0x42UL)
37
111ce0c3
MD
38/*
39 * External synchronization used.
40 */
41enum test_sync {
42 TEST_SYNC_NONE = 0,
43 TEST_SYNC_RCU,
44};
45
46static enum test_sync test_sync;
47
453629a9
MD
48static volatile int test_go, test_stop;
49
50static unsigned long rduration;
51
52static unsigned long duration;
53
54/* read-side C.S. duration, in loops */
55static unsigned long wdelay;
56
ab0aacbe 57static inline void loop_sleep(unsigned long loops)
453629a9 58{
ab0aacbe 59 while (loops-- != 0)
06f22bdb 60 caa_cpu_relax();
453629a9
MD
61}
62
63static int verbose_mode;
64
111ce0c3
MD
65static int test_pop, test_pop_all;
66
453629a9
MD
67#define printf_verbose(fmt, args...) \
68 do { \
69 if (verbose_mode) \
111ce0c3 70 printf(fmt, ## args); \
453629a9
MD
71 } while (0)
72
73static unsigned int cpu_affinities[NR_CPUS];
74static unsigned int next_aff = 0;
75static int use_affinity = 0;
76
77pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
78
453629a9
MD
79static void set_affinity(void)
80{
2388c075 81#ifdef HAVE_SCHED_SETAFFINITY
453629a9 82 cpu_set_t mask;
95bc7fb9
MD
83 int cpu, ret;
84#endif /* HAVE_SCHED_SETAFFINITY */
453629a9
MD
85
86 if (!use_affinity)
87 return;
88
2388c075 89#ifdef HAVE_SCHED_SETAFFINITY
453629a9
MD
90 ret = pthread_mutex_lock(&affinity_mutex);
91 if (ret) {
92 perror("Error in pthread mutex lock");
93 exit(-1);
94 }
95 cpu = cpu_affinities[next_aff++];
96 ret = pthread_mutex_unlock(&affinity_mutex);
97 if (ret) {
98 perror("Error in pthread mutex unlock");
99 exit(-1);
100 }
101
102 CPU_ZERO(&mask);
103 CPU_SET(cpu, &mask);
453629a9 104 sched_setaffinity(0, sizeof(mask), &mask);
453629a9
MD
105#endif /* HAVE_SCHED_SETAFFINITY */
106}
107
108/*
109 * returns 0 if test should end.
110 */
111static int test_duration_dequeue(void)
112{
113 return !test_stop;
114}
115
116static int test_duration_enqueue(void)
117{
118 return !test_stop;
119}
120
bd252a04
MD
121static DEFINE_URCU_TLS(unsigned long long, nr_dequeues);
122static DEFINE_URCU_TLS(unsigned long long, nr_enqueues);
453629a9 123
bd252a04
MD
124static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues);
125static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues);
453629a9
MD
126
127static unsigned int nr_enqueuers;
128static unsigned int nr_dequeuers;
129
94aec122 130struct test {
cb8818f0 131 struct cds_lfs_node list;
94aec122
MD
132 struct rcu_head rcu;
133};
134
cb8818f0 135static struct cds_lfs_stack s;
453629a9 136
eb314468 137static void *thr_enqueuer(void *_count)
453629a9
MD
138{
139 unsigned long long *count = _count;
140
94df6318
MD
141 printf_verbose("thread_begin %s, tid %lu\n",
142 "enqueuer", urcu_get_thread_id());
453629a9
MD
143
144 set_affinity();
145
146 rcu_register_thread();
147
148 while (!test_go)
149 {
150 }
5481ddb3 151 cmm_smp_mb();
453629a9
MD
152
153 for (;;) {
94aec122 154 struct test *node = malloc(sizeof(*node));
453629a9
MD
155 if (!node)
156 goto fail;
cb8818f0
MD
157 cds_lfs_node_init(&node->list);
158 cds_lfs_push(&s, &node->list);
bd252a04 159 URCU_TLS(nr_successful_enqueues)++;
453629a9 160
a0b7f7ea 161 if (caa_unlikely(wdelay))
453629a9
MD
162 loop_sleep(wdelay);
163fail:
bd252a04 164 URCU_TLS(nr_enqueues)++;
a0b7f7ea 165 if (caa_unlikely(!test_duration_enqueue()))
453629a9
MD
166 break;
167 }
168
169 rcu_unregister_thread();
170
bd252a04
MD
171 count[0] = URCU_TLS(nr_enqueues);
172 count[1] = URCU_TLS(nr_successful_enqueues);
94df6318
MD
173 printf_verbose("enqueuer thread_end, tid %lu, "
174 "enqueues %llu successful_enqueues %llu\n",
175 urcu_get_thread_id(),
176 URCU_TLS(nr_enqueues),
177 URCU_TLS(nr_successful_enqueues));
453629a9
MD
178 return ((void*)1);
179
180}
181
94aec122
MD
182static
183void free_node_cb(struct rcu_head *head)
184{
185 struct test *node =
186 caa_container_of(head, struct test, rcu);
187 free(node);
188}
189
111ce0c3
MD
190static
191void do_test_pop(enum test_sync sync)
192{
193 struct cds_lfs_node *snode;
194
195 if (sync == TEST_SYNC_RCU)
196 rcu_read_lock();
197 snode = __cds_lfs_pop(&s);
198 if (sync == TEST_SYNC_RCU)
199 rcu_read_unlock();
200 if (snode) {
201 struct test *node;
202
f9b5c2b6 203 snode->next = POISON_PTR;
111ce0c3
MD
204 node = caa_container_of(snode,
205 struct test, list);
206 if (sync == TEST_SYNC_RCU)
207 call_rcu(&node->rcu, free_node_cb);
208 else
209 free(node);
210 URCU_TLS(nr_successful_dequeues)++;
211 }
212 URCU_TLS(nr_dequeues)++;
213}
214
215static
216void do_test_pop_all(enum test_sync sync)
217{
218 struct cds_lfs_node *snode;
219 struct cds_lfs_head *head;
220 struct cds_lfs_node *n;
221
222 head = __cds_lfs_pop_all(&s);
223 cds_lfs_for_each_safe(head, snode, n) {
224 struct test *node;
225
f9b5c2b6 226 snode->next = POISON_PTR;
111ce0c3
MD
227 node = caa_container_of(snode, struct test, list);
228 if (sync == TEST_SYNC_RCU)
229 call_rcu(&node->rcu, free_node_cb);
230 else
231 free(node);
232 URCU_TLS(nr_successful_dequeues)++;
233 URCU_TLS(nr_dequeues)++;
234 }
235
236}
237
eb314468 238static void *thr_dequeuer(void *_count)
453629a9
MD
239{
240 unsigned long long *count = _count;
f42bc7ef 241 unsigned int counter = 0;
453629a9 242
94df6318
MD
243 printf_verbose("thread_begin %s, tid %lu\n",
244 "dequeuer", urcu_get_thread_id());
453629a9
MD
245
246 set_affinity();
247
453629a9
MD
248 rcu_register_thread();
249
250 while (!test_go)
251 {
252 }
5481ddb3 253 cmm_smp_mb();
453629a9 254
01477510 255 urcu_posix_assert(test_pop || test_pop_all);
f3eb6ef1 256
111ce0c3 257 for (;;) {
111ce0c3
MD
258 if (test_pop && test_pop_all) {
259 /* both pop and pop all */
260 if (counter & 1)
261 do_test_pop(test_sync);
262 else
263 do_test_pop_all(test_sync);
264 counter++;
265 } else {
266 if (test_pop)
267 do_test_pop(test_sync);
268 else
269 do_test_pop_all(test_sync);
453629a9 270 }
111ce0c3 271
a0b7f7ea 272 if (caa_unlikely(!test_duration_dequeue()))
453629a9 273 break;
a0b7f7ea 274 if (caa_unlikely(rduration))
453629a9
MD
275 loop_sleep(rduration);
276 }
277
278 rcu_unregister_thread();
453629a9 279
94df6318
MD
280 printf_verbose("dequeuer thread_end, tid %lu, "
281 "dequeues %llu, successful_dequeues %llu\n",
282 urcu_get_thread_id(),
283 URCU_TLS(nr_dequeues),
284 URCU_TLS(nr_successful_dequeues));
bd252a04
MD
285 count[0] = URCU_TLS(nr_dequeues);
286 count[1] = URCU_TLS(nr_successful_dequeues);
453629a9
MD
287 return ((void*)2);
288}
289
bd23a6c6 290static void test_end(unsigned long long *nr_dequeues_l)
453629a9 291{
cb8818f0 292 struct cds_lfs_node *snode;
453629a9
MD
293
294 do {
bd23a6c6 295 snode = __cds_lfs_pop(&s);
94aec122
MD
296 if (snode) {
297 struct test *node;
298
299 node = caa_container_of(snode, struct test, list);
453629a9 300 free(node);
bd23a6c6 301 (*nr_dequeues_l)++;
453629a9 302 }
94aec122 303 } while (snode);
453629a9
MD
304}
305
70469b43 306static void show_usage(char **argv)
453629a9 307{
06637138
MD
308 printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) <OPTIONS>\n",
309 argv[0]);
310 printf("OPTIONS:\n");
311 printf(" [-d delay] (enqueuer period (in loops))\n");
312 printf(" [-c duration] (dequeuer period (in loops))\n");
313 printf(" [-v] (verbose output)\n");
314 printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
315 printf(" [-p] (test pop)\n");
316 printf(" [-P] (test pop_all, enabled by default)\n");
317 printf(" [-R] (use RCU external synchronization)\n");
318 printf(" Note: default: no external synchronization used.\n");
453629a9
MD
319 printf("\n");
320}
321
322int main(int argc, char **argv)
323{
324 int err;
325 pthread_t *tid_enqueuer, *tid_dequeuer;
326 void *tret;
327 unsigned long long *count_enqueuer, *count_dequeuer;
328 unsigned long long tot_enqueues = 0, tot_dequeues = 0;
329 unsigned long long tot_successful_enqueues = 0,
330 tot_successful_dequeues = 0;
331 unsigned long long end_dequeues = 0;
332 int i, a;
83e334d0 333 unsigned int i_thr;
453629a9
MD
334
335 if (argc < 4) {
70469b43 336 show_usage(argv);
453629a9
MD
337 return -1;
338 }
339
340 err = sscanf(argv[1], "%u", &nr_dequeuers);
341 if (err != 1) {
70469b43 342 show_usage(argv);
453629a9
MD
343 return -1;
344 }
345
346 err = sscanf(argv[2], "%u", &nr_enqueuers);
347 if (err != 1) {
70469b43 348 show_usage(argv);
453629a9
MD
349 return -1;
350 }
83e334d0 351
453629a9
MD
352 err = sscanf(argv[3], "%lu", &duration);
353 if (err != 1) {
70469b43 354 show_usage(argv);
453629a9
MD
355 return -1;
356 }
357
358 for (i = 4; i < argc; i++) {
359 if (argv[i][0] != '-')
360 continue;
361 switch (argv[i][1]) {
362 case 'a':
363 if (argc < i + 2) {
70469b43 364 show_usage(argv);
453629a9
MD
365 return -1;
366 }
367 a = atoi(argv[++i]);
368 cpu_affinities[next_aff++] = a;
369 use_affinity = 1;
370 printf_verbose("Adding CPU %d affinity\n", a);
371 break;
372 case 'c':
373 if (argc < i + 2) {
70469b43 374 show_usage(argv);
453629a9
MD
375 return -1;
376 }
377 rduration = atol(argv[++i]);
378 break;
379 case 'd':
380 if (argc < i + 2) {
70469b43 381 show_usage(argv);
453629a9
MD
382 return -1;
383 }
384 wdelay = atol(argv[++i]);
385 break;
386 case 'v':
387 verbose_mode = 1;
388 break;
111ce0c3
MD
389 case 'p':
390 test_pop = 1;
391 break;
392 case 'P':
393 test_pop_all = 1;
394 break;
395 case 'R':
396 test_sync = TEST_SYNC_RCU;
397 break;
453629a9
MD
398 }
399 }
400
111ce0c3
MD
401 /* activate pop_all test by default */
402 if (!test_pop && !test_pop_all)
403 test_pop_all = 1;
404
453629a9
MD
405 printf_verbose("running test for %lu seconds, %u enqueuers, "
406 "%u dequeuers.\n",
407 duration, nr_enqueuers, nr_dequeuers);
111ce0c3
MD
408 if (test_pop)
409 printf_verbose("pop test activated.\n");
410 if (test_pop_all)
411 printf_verbose("pop_all test activated.\n");
eb314468
MD
412 if (test_sync == TEST_SYNC_RCU)
413 printf_verbose("External sync: RCU.\n");
414 else
415 printf_verbose("External sync: none.\n");
453629a9
MD
416 printf_verbose("Writer delay : %lu loops.\n", rduration);
417 printf_verbose("Reader duration : %lu loops.\n", wdelay);
94df6318
MD
418 printf_verbose("thread %-6s, tid %lu\n",
419 "main", urcu_get_thread_id());
453629a9 420
9aa14175
MD
421 tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer));
422 tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer));
423 count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer));
424 count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer));
cb8818f0 425 cds_lfs_init(&s);
94aec122 426 err = create_all_cpu_call_rcu_data(0);
8bcbd94a
MD
427 if (err) {
428 printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
429 }
453629a9
MD
430
431 next_aff = 0;
432
83e334d0
MJ
433 for (i_thr = 0; i_thr < nr_enqueuers; i_thr++) {
434 err = pthread_create(&tid_enqueuer[i_thr], NULL, thr_enqueuer,
435 &count_enqueuer[2 * i_thr]);
453629a9
MD
436 if (err != 0)
437 exit(1);
438 }
83e334d0
MJ
439 for (i_thr = 0; i_thr < nr_dequeuers; i_thr++) {
440 err = pthread_create(&tid_dequeuer[i_thr], NULL, thr_dequeuer,
441 &count_dequeuer[2 * i_thr]);
453629a9
MD
442 if (err != 0)
443 exit(1);
444 }
445
5481ddb3 446 cmm_smp_mb();
453629a9
MD
447
448 test_go = 1;
449
83e334d0 450 for (i_thr = 0; i_thr < duration; i_thr++) {
453629a9 451 sleep(1);
4b665350
MD
452 if (verbose_mode) {
453 fwrite(".", sizeof(char), 1, stdout);
454 fflush(stdout);
455 }
453629a9
MD
456 }
457
458 test_stop = 1;
459
83e334d0
MJ
460 for (i_thr = 0; i_thr < nr_enqueuers; i_thr++) {
461 err = pthread_join(tid_enqueuer[i_thr], &tret);
453629a9
MD
462 if (err != 0)
463 exit(1);
83e334d0
MJ
464 tot_enqueues += count_enqueuer[2 * i_thr];
465 tot_successful_enqueues += count_enqueuer[2 * i_thr + 1];
453629a9 466 }
83e334d0
MJ
467 for (i_thr = 0; i_thr < nr_dequeuers; i_thr++) {
468 err = pthread_join(tid_dequeuer[i_thr], &tret);
453629a9
MD
469 if (err != 0)
470 exit(1);
83e334d0
MJ
471 tot_dequeues += count_dequeuer[2 * i_thr];
472 tot_successful_dequeues += count_dequeuer[2 * i_thr + 1];
453629a9 473 }
83e334d0 474
bd23a6c6 475 test_end(&end_dequeues);
453629a9
MD
476
477 printf_verbose("total number of enqueues : %llu, dequeues %llu\n",
478 tot_enqueues, tot_dequeues);
479 printf_verbose("total number of successful enqueues : %llu, "
480 "successful dequeues %llu\n",
481 tot_successful_enqueues, tot_successful_dequeues);
482 printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu "
483 "nr_dequeuers %3u "
484 "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu "
485 "successful enqueues %12llu successful dequeues %12llu "
486 "end_dequeues %llu nr_ops %12llu\n",
487 argv[0], duration, nr_enqueuers, wdelay,
488 nr_dequeuers, rduration, tot_enqueues, tot_dequeues,
489 tot_successful_enqueues,
490 tot_successful_dequeues, end_dequeues,
491 tot_enqueues + tot_dequeues);
492 if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues)
493 printf("WARNING! Discrepancy between nr succ. enqueues %llu vs "
494 "succ. dequeues + end dequeues %llu.\n",
495 tot_successful_enqueues,
496 tot_successful_dequeues + end_dequeues);
497
0938c541 498 free_all_cpu_call_rcu_data();
2af1c19e 499 cds_lfs_destroy(&s);
453629a9
MD
500 free(count_enqueuer);
501 free(count_dequeuer);
502 free(tid_enqueuer);
503 free(tid_dequeuer);
83e334d0 504
453629a9
MD
505 return 0;
506}
This page took 0.066469 seconds and 4 git commands to generate.