Fix: rcuja merge fixes
[userspace-rcu.git] / tests / regression / test_urcu_ja_range.c
CommitLineData
91233b5b
MD
1/*
2 * test_urcu_ja_range.c
3 *
4 * Userspace RCU library - test program
5 *
6 * Copyright 2009-2012 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#define _GNU_SOURCE
24#include "test_urcu_ja_range.h"
1badda43 25#include "../common/debug-yield.h"
91233b5b
MD
26#include <inttypes.h>
27#include <stdint.h>
28
29DEFINE_URCU_TLS(unsigned int, rand_lookup);
30DEFINE_URCU_TLS(unsigned long, nr_add);
31DEFINE_URCU_TLS(unsigned long, nr_addexist);
32DEFINE_URCU_TLS(unsigned long, nr_del);
33DEFINE_URCU_TLS(unsigned long, nr_delnoent);
34DEFINE_URCU_TLS(unsigned long, lookup_fail);
35DEFINE_URCU_TLS(unsigned long, lookup_ok);
36
37struct cds_ja *test_ja;
38
39volatile int test_go, test_stop;
40
41unsigned long wdelay;
42
43unsigned long duration;
44
45/* read-side C.S. duration, in loops */
46unsigned long rduration;
47
48unsigned long init_populate;
49int add_only;
50
51unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset;
52unsigned long init_pool_size = DEFAULT_RAND_POOL,
53 lookup_pool_size = DEFAULT_RAND_POOL,
54 write_pool_size = DEFAULT_RAND_POOL;
55int validate_lookup;
56int sanity_test;
57unsigned int key_bits = 32;
58
59int count_pipe[2];
60
61int verbose_mode;
62
63unsigned int cpu_affinities[NR_CPUS];
64unsigned int next_aff = 0;
65int use_affinity = 0;
66
67pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
68
69DEFINE_URCU_TLS(unsigned long long, nr_writes);
70DEFINE_URCU_TLS(unsigned long long, nr_reads);
71
72unsigned int nr_readers;
73unsigned int nr_writers;
74
22789e8f 75static unsigned int add_ratio = 50, range_max_len = 0;
91233b5b
MD
76static uint64_t key_mul = 1ULL;
77
78static int add_unique, add_replace;
79
80static pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
81
82static int leak_detection;
83static unsigned long test_nodes_allocated, test_nodes_freed;
84
85void set_affinity(void)
86{
87 cpu_set_t mask;
88 int cpu;
89 int ret;
90
91 if (!use_affinity)
92 return;
93
94#if HAVE_SCHED_SETAFFINITY
95 ret = pthread_mutex_lock(&affinity_mutex);
96 if (ret) {
97 perror("Error in pthread mutex lock");
98 exit(-1);
99 }
100 cpu = cpu_affinities[next_aff++];
101 ret = pthread_mutex_unlock(&affinity_mutex);
102 if (ret) {
103 perror("Error in pthread mutex unlock");
104 exit(-1);
105 }
106 CPU_ZERO(&mask);
107 CPU_SET(cpu, &mask);
108#if SCHED_SETAFFINITY_ARGS == 2
109 sched_setaffinity(0, &mask);
110#else
111 sched_setaffinity(0, sizeof(mask), &mask);
112#endif
113#endif /* HAVE_SCHED_SETAFFINITY */
114}
115
116void rcu_copy_mutex_lock(void)
117{
118 int ret;
119 ret = pthread_mutex_lock(&rcu_copy_mutex);
120 if (ret) {
121 perror("Error in pthread mutex lock");
122 exit(-1);
123 }
124}
125
126void rcu_copy_mutex_unlock(void)
127{
128 int ret;
129
130 ret = pthread_mutex_unlock(&rcu_copy_mutex);
131 if (ret) {
132 perror("Error in pthread mutex unlock");
133 exit(-1);
134 }
135}
136
137void show_usage(int argc, char **argv)
138{
139 printf("Usage : %s nr_readers nr_writers duration (s)\n", argv[0]);
140#ifdef DEBUG_YIELD
141 printf(" [-r] [-w] (yield reader and/or writer)\n");
142#endif
143 printf(" [-d delay] (writer period (us))\n");
144 printf(" [-c duration] (reader C.S. duration (in loops))\n");
145 printf(" [-v] (verbose output)\n");
146 printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
147 printf(" [-u] Add unique keys.\n");
148 printf(" [-s] Replace existing keys.\n");
149printf(" [not -u nor -s] Add entries (supports redundant keys).\n");
150 printf(" [-r ratio] Add ratio (in %% of add+removal).\n");
151 printf(" [-k] Populate init nodes.\n");
152 printf(" [-R offset] Lookup pool offset.\n");
153 printf(" [-S offset] Write pool offset.\n");
154 printf(" [-T offset] Init pool offset.\n");
155 printf(" [-M size] Lookup pool size.\n");
156 printf(" [-N size] Write pool size.\n");
157 printf(" [-O size] Init pool size.\n");
158 printf(" [-V] Validate lookups of init values (use with filled init pool, same lookup range, with different write range).\n");
159 printf(" [-t] Do sanity test.\n");
160 printf(" [-B] Key bits for multithread test (default: 32).\n");
161 printf(" [-m factor] Key multiplication factor.\n");
162 printf(" [-l] Memory leak detection.\n");
22789e8f 163 printf(" [-L len] Range max len.\n");
91233b5b
MD
164 printf("\n\n");
165}
166
167enum urcu_ja_addremove {
168 AR_RANDOM = 0,
169 AR_ADD = 1,
170 AR_REMOVE = -1,
171}; /* 1: add, -1 remove, 0: random */
172
173static enum urcu_ja_addremove addremove; /* 1: add, -1 remove, 0: random */
174
175static
176void test_ja_rw_sigusr1_handler(int signo)
177{
178 switch (addremove) {
179 case AR_ADD:
180 printf("Add/Remove: random.\n");
181 addremove = AR_RANDOM;
182 break;
183 case AR_RANDOM:
184 printf("Add/Remove: remove only.\n");
185 addremove = AR_REMOVE;
186 break;
187 case AR_REMOVE:
188 printf("Add/Remove: add only.\n");
189 addremove = AR_ADD;
190 break;
191 }
192}
193
194static
195void *test_ja_rw_thr_reader(void *_count)
196{
197 unsigned long long *count = _count;
198 struct cds_ja_range *range;
199 uint64_t key;
200
5bcf8326
MD
201 printf_verbose("thread_begin %s, tid %lu\n",
202 "reader", urcu_get_thread_id());
91233b5b 203
5bcf8326 204 URCU_TLS(rand_lookup) = (unsigned int) urcu_get_thread_id() ^ time(NULL);
b10c8ba8 205
91233b5b
MD
206 set_affinity();
207
208 rcu_register_thread();
209
210 while (!test_go)
211 {
212 }
213 cmm_smp_mb();
214
215 for (;;) {
216 rcu_read_lock();
217
b10c8ba8
MD
218 key = (uint64_t) rand_r(&URCU_TLS(rand_lookup));
219 key += (uint64_t) rand_r(&URCU_TLS(rand_lookup)) << 32ULL;
220 key = (key % lookup_pool_size) + lookup_pool_offset;
91233b5b 221 key *= key_mul;
b10c8ba8 222
91233b5b
MD
223 range = cds_ja_range_lookup(test_ja, key);
224 if (!range) {
225 if (validate_lookup) {
226 printf("[ERROR] Lookup cannot find initial node.\n");
227 exit(-1);
228 }
229 URCU_TLS(lookup_fail)++;
230 } else {
231 range = cds_ja_range_lock(range);
232 if (!range) {
233 if (validate_lookup) {
234 printf("[ERROR] Lookup cannot find initial node.\n");
235 exit(-1);
236 }
237 } else {
238 URCU_TLS(lookup_ok)++;
239 cds_ja_range_unlock(range);
240 }
241 }
242 rcu_debug_yield_read();
243 if (caa_unlikely(rduration))
244 loop_sleep(rduration);
245 rcu_read_unlock();
246 URCU_TLS(nr_reads)++;
247 if (caa_unlikely(!test_duration_read()))
248 break;
249 if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0))
250 rcu_quiescent_state();
251 }
252
253 rcu_unregister_thread();
254
255 *count = URCU_TLS(nr_reads);
5bcf8326
MD
256 printf_verbose("thread_end %s, tid %lu\n",
257 "reader", urcu_get_thread_id());
258 printf_verbose("read tid : %lu, lookupfail %lu, lookupok %lu\n",
259 urcu_get_thread_id(), URCU_TLS(lookup_fail),
91233b5b
MD
260 URCU_TLS(lookup_ok));
261 return ((void*)1);
262}
263
264static
265int is_add(void)
266{
267 return ((unsigned int) rand_r(&URCU_TLS(rand_lookup)) % 100) < add_ratio;
268}
269
270static
271void *test_ja_rw_thr_writer(void *_count)
272{
273 struct wr_count *count = _count;
274 int ret;
275
5bcf8326
MD
276 printf_verbose("thread_begin %s, tid %lu\n",
277 "writer", urcu_get_thread_id());
91233b5b 278
5bcf8326 279 URCU_TLS(rand_lookup) = (unsigned int) urcu_get_thread_id() ^ time(NULL);
b10c8ba8 280
91233b5b
MD
281 set_affinity();
282
283 rcu_register_thread();
284
285 while (!test_go)
286 {
287 }
288 cmm_smp_mb();
289
290 for (;;) {
291 if ((addremove == AR_ADD)
292 || (addremove == AR_RANDOM && is_add())) {
293 struct cds_ja_range *range;
294 uint64_t start, end, tmp;
295
b10c8ba8
MD
296 start = (uint64_t) rand_r(&URCU_TLS(rand_lookup));
297 start += (uint64_t) rand_r(&URCU_TLS(rand_lookup)) << 32ULL;
298 start = (start % write_pool_size) + write_pool_offset;
299
300 end = (uint64_t) rand_r(&URCU_TLS(rand_lookup));
301 end += (uint64_t) rand_r(&URCU_TLS(rand_lookup)) << 32ULL;
302 end = (end % write_pool_size) + write_pool_offset;
303
91233b5b
MD
304 start *= key_mul;
305 end *= key_mul;
306 if (start > end) {
307 tmp = start;
308 start = end;
309 end = tmp;
310 }
22789e8f
MD
311 if (end - start > range_max_len) {
312 end = start + range_max_len;
313 }
91233b5b 314 rcu_read_lock();
cd7eb7a3
MD
315 ret = cds_ja_range_add(test_ja, start, end, NULL);
316 if (ret) {
317 if (ret == -EEXIST) {
318 URCU_TLS(nr_addexist)++;
319 } else {
320 assert(0);
321 }
91233b5b
MD
322 } else {
323 URCU_TLS(nr_add)++;
324 }
325 rcu_read_unlock();
326 } else {
327 struct cds_ja_range *range;
328 uint64_t key;
329
330 /* May delete */
b10c8ba8
MD
331 key = (uint64_t) rand_r(&URCU_TLS(rand_lookup));
332 key += (uint64_t) rand_r(&URCU_TLS(rand_lookup)) << 32ULL;
333 key = (key % write_pool_size) + write_pool_offset;
91233b5b
MD
334 key *= key_mul;
335
336 rcu_read_lock();
337
338 range = cds_ja_range_lookup(test_ja, key);
339 if (range) {
340 ret = cds_ja_range_del(test_ja, range);
341 if (!ret) {
342 URCU_TLS(nr_del)++;
343 } else {
344 URCU_TLS(nr_delnoent)++;
345 }
346 } else {
347 URCU_TLS(nr_delnoent)++;
348 }
349 rcu_read_unlock();
350 }
351
352 URCU_TLS(nr_writes)++;
353 if (caa_unlikely(!test_duration_write()))
354 break;
355 if (caa_unlikely(wdelay))
356 loop_sleep(wdelay);
357 if (caa_unlikely((URCU_TLS(nr_writes) & ((1 << 10) - 1)) == 0))
358 rcu_quiescent_state();
359 }
360
361 rcu_unregister_thread();
362
5bcf8326
MD
363 printf_verbose("thread_end %s, tid %lu\n",
364 "writer", urcu_get_thread_id());
365 printf_verbose("info tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, "
366 "nr_delnoent %lu\n", urcu_get_thread_id(),
367 URCU_TLS(nr_add),
91233b5b
MD
368 URCU_TLS(nr_addexist), URCU_TLS(nr_del),
369 URCU_TLS(nr_delnoent));
370 count->update_ops = URCU_TLS(nr_writes);
371 count->add = URCU_TLS(nr_add);
372 count->add_exist = URCU_TLS(nr_addexist);
373 count->remove = URCU_TLS(nr_del);
374 return ((void*)2);
375}
376
377static
378int do_mt_populate_ja(void)
379{
380 uint64_t iter;
381 int ret;
382
383 if (!init_populate)
384 return 0;
385
386 printf("Starting rw test\n");
387
388 for (iter = init_pool_offset; iter < init_pool_offset + init_pool_size; iter++) {
389 struct cds_ja_range *range;
390 uint64_t key;
391
392 /* note: only inserting ulong keys */
393 key = (unsigned long) iter;
394 key *= key_mul;
395 rcu_read_lock();
cd7eb7a3 396 ret = cds_ja_range_add(test_ja, key, key, NULL);
91233b5b
MD
397 URCU_TLS(nr_add)++;
398 URCU_TLS(nr_writes)++;
399 rcu_read_unlock();
012ca80d
MD
400 /* Hash table resize only occurs in call_rcu thread */
401 if (!(iter % 100))
402 rcu_quiescent_state();
cd7eb7a3
MD
403 if (ret) {
404 fprintf(stderr, "Error (%d) adding range %" PRIu64 "\n",
405 ret, key);
91233b5b
MD
406 assert(0);
407 }
408 }
409 return 0;
410}
411
412static
413int do_mt_test(void)
414{
415 pthread_t *tid_reader, *tid_writer;
416 void *tret;
417 int ret, i, err;
418 unsigned long long *count_reader;
419 struct wr_count *count_writer;
420 unsigned long long tot_reads = 0, tot_writes = 0,
421 tot_add = 0, tot_add_exist = 0, tot_remove = 0;
422 unsigned int remain;
423
424 tid_reader = malloc(sizeof(*tid_reader) * nr_readers);
425 tid_writer = malloc(sizeof(*tid_writer) * nr_writers);
426 count_reader = malloc(sizeof(*count_reader) * nr_readers);
427 count_writer = malloc(sizeof(*count_writer) * nr_writers);
428
63d2de6a
MD
429 printf("Allocating %u-bit Judy Array for ranges\n",
430 key_bits);
431 test_ja = cds_ja_range_new(key_bits);
91233b5b
MD
432 if (!test_ja) {
433 printf("Error allocating judy array.\n");
434 ret = -1;
435 goto end;
436 }
437
438 do_mt_populate_ja();
439
440 next_aff = 0;
441
442 for (i = 0; i < nr_readers; i++) {
443 err = pthread_create(&tid_reader[i],
444 NULL, test_ja_rw_thr_reader,
445 &count_reader[i]);
446 if (err != 0)
447 exit(1);
448 }
449 for (i = 0; i < nr_writers; i++) {
450 err = pthread_create(&tid_writer[i],
451 NULL, test_ja_rw_thr_writer,
452 &count_writer[i]);
453 if (err != 0)
454 exit(1);
455 }
456
457 cmm_smp_mb();
458
459 test_go = 1;
460
461 rcu_thread_offline_qsbr();
462
463 remain = duration;
464 do {
465 remain = sleep(remain);
466 } while (remain > 0);
467
468 test_stop = 1;
469
470 for (i = 0; i < nr_readers; i++) {
471 err = pthread_join(tid_reader[i], &tret);
472 if (err != 0)
473 exit(1);
474 tot_reads += count_reader[i];
475 }
476 for (i = 0; i < nr_writers; i++) {
477 err = pthread_join(tid_writer[i], &tret);
478 if (err != 0)
479 exit(1);
480 tot_writes += count_writer[i].update_ops;
481 tot_add += count_writer[i].add;
482 tot_add_exist += count_writer[i].add_exist;
483 tot_remove += count_writer[i].remove;
484 }
485 rcu_thread_online_qsbr();
486
9a655aa8
MD
487 ret = cds_ja_range_validate(test_ja);
488 assert(!ret);
489
91233b5b
MD
490 ret = cds_ja_range_destroy(test_ja, NULL);
491 if (ret) {
492 fprintf(stderr, "Error destroying judy array\n");
493 goto end;
494 }
495
496 free(tid_reader);
497 free(tid_writer);
498 free(count_reader);
499 free(count_writer);
500 ret = 0;
501end:
502 return ret;
503}
504
505int main(int argc, char **argv)
506{
507 int i, j, a, ret, err;
508 uint64_t key;
509 struct sigaction act;
510
511 if (argc < 4) {
512 show_usage(argc, argv);
513 return -1;
514 }
515
516 err = sscanf(argv[1], "%u", &nr_readers);
517 if (err != 1) {
518 show_usage(argc, argv);
519 return -1;
520 }
521
522 err = sscanf(argv[2], "%u", &nr_writers);
523 if (err != 1) {
524 show_usage(argc, argv);
525 return -1;
526 }
527
528 err = sscanf(argv[3], "%lu", &duration);
529 if (err != 1) {
530 show_usage(argc, argv);
531 return -1;
532 }
533
534 for (i = 4; i < argc; i++) {
535 if (argv[i][0] != '-')
536 continue;
537 switch (argv[i][1]) {
538#ifdef DEBUG_YIELD
539 case 'r':
540 yield_active |= YIELD_READ;
541 break;
542 case 'w':
543 yield_active |= YIELD_WRITE;
544 break;
545#endif
546 case 'a':
547 if (argc < i + 2) {
548 show_usage(argc, argv);
549 return -1;
550 }
551 a = atoi(argv[++i]);
552 cpu_affinities[next_aff++] = a;
553 use_affinity = 1;
554 printf_verbose("Adding CPU %d affinity\n", a);
555 break;
556 case 'c':
557 if (argc < i + 2) {
558 show_usage(argc, argv);
559 return -1;
560 }
561 rduration = atol(argv[++i]);
562 break;
563 case 'd':
564 if (argc < i + 2) {
565 show_usage(argc, argv);
566 return -1;
567 }
568 wdelay = atol(argv[++i]);
569 break;
570 case 'v':
571 verbose_mode = 1;
572 break;
573 case 'r':
574 add_ratio = atoi(argv[++i]);
575 break;
576 case 'k':
577 init_populate = 1;
578 break;
579 case 'R':
580 lookup_pool_offset = atol(argv[++i]);
581 break;
582 case 'S':
583 write_pool_offset = atol(argv[++i]);
584 break;
585 case 'T':
586 init_pool_offset = atol(argv[++i]);
587 break;
588 case 'M':
589 lookup_pool_size = atol(argv[++i]);
590 break;
591 case 'N':
592 write_pool_size = atol(argv[++i]);
593 break;
594 case 'O':
595 init_pool_size = atol(argv[++i]);
596 break;
597 case 'V':
598 validate_lookup = 1;
599 break;
600 case 't':
601 sanity_test = 1;
602 break;
603 case 'B':
604 key_bits = atol(argv[++i]);
605 break;
606 case 'm':
607 key_mul = atoll(argv[++i]);
608 break;
609 case 'u':
610 add_unique = 1;
611 break;
612 case 's':
613 add_replace = 1;
614 break;
615 case 'l':
616 leak_detection = 1;
617 break;
22789e8f
MD
618 case 'L':
619 range_max_len = atol(argv[++i]);
620 break;
91233b5b
MD
621 }
622 }
623
624 printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
625 duration, nr_readers, nr_writers);
626 printf_verbose("Writer delay : %lu loops.\n", wdelay);
627 printf_verbose("Reader duration : %lu loops.\n", rduration);
628 printf_verbose("Add ratio: %u%%.\n", add_ratio);
629 printf_verbose("Mode:%s%s.\n",
630 " add/remove",
631 add_unique ? " uniquify" : ( add_replace ? " replace" : " insert"));
632 printf_verbose("Key multiplication factor: %" PRIu64 ".\n", key_mul);
633 printf_verbose("Init pool size offset %lu size %lu.\n",
634 init_pool_offset, init_pool_size);
635 printf_verbose("Lookup pool size offset %lu size %lu.\n",
636 lookup_pool_offset, lookup_pool_size);
637 printf_verbose("Update pool size offset %lu size %lu.\n",
638 write_pool_offset, write_pool_size);
1badda43 639 printf_verbose("Range max len: %u.\n",
22789e8f 640 range_max_len);
91233b5b
MD
641 if (validate_lookup)
642 printf_verbose("Validating lookups.\n");
643 if (leak_detection)
644 printf_verbose("Memory leak dection activated.\n");
5bcf8326
MD
645 printf_verbose("thread %-6s, tid %lu\n",
646 "main", urcu_get_thread_id());
91233b5b
MD
647
648 memset(&act, 0, sizeof(act));
649 ret = sigemptyset(&act.sa_mask);
650 if (ret == -1) {
651 perror("sigemptyset");
652 return -1;
653 }
654 act.sa_handler = test_ja_rw_sigusr1_handler;
655 act.sa_flags = SA_RESTART;
656 ret = sigaction(SIGUSR1, &act, NULL);
657 if (ret == -1) {
658 perror("sigaction");
659 return -1;
660 }
661
662 err = create_all_cpu_call_rcu_data(0);
663 if (err) {
664 printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
665 }
666
667 rcu_register_thread();
668
669 ret = do_mt_test();
670
671 /* Wait for in-flight call_rcu free to complete for leak detection */
672 rcu_barrier();
673
674 rcu_unregister_thread();
675 free_all_cpu_call_rcu_data();
676
677 if (ret) {
678 printf("Test ended with error: %d\n", ret);
679 }
680 return ret;
681}
This page took 0.04994 seconds and 4 git commands to generate.