source: mainline/kernel/test/synch/rcu1.c@ 15f8079

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 15f8079 was 7850dda, checked in by Martin Decky <martin@…>, 8 years ago

riscv64: temporarily workaround GCC 7.1.0 internal compiler error

  • Property mode set to 100644
File size: 22.5 KB
Line 
1/*
2 * Copyright (c) 2012 Adam Hraska
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <assert.h>
30#include <test.h>
31#include <arch.h>
32#include <atomic.h>
33#include <print.h>
34#include <proc/thread.h>
35#include <macros.h>
36#include <str.h>
37#include <errno.h>
38#include <time/delay.h>
39
40#include <synch/rcu.h>
41
42
43#define MAX_THREADS 32
44
45static int one_idx = 0;
46static thread_t *thread[MAX_THREADS] = { NULL };
47
48typedef struct {
49 rcu_item_t rcu;
50 bool exited;
51} exited_t;
52
53/* Callback raced with preexisting readers. */
54#define ERACE 123
55/* Waited for too long for the callback to exit; consider it lost. */
56#define ECBLOST 432
57
58/*-------------------------------------------------------------------*/
59static void wait_for_cb_exit(size_t secs, exited_t *p, int *presult)
60{
61 size_t loops = 0;
62 /* 4 secs max */
63 size_t loop_ms_sec = 500;
64 size_t max_loops = ((secs * 1000 + loop_ms_sec - 1) / loop_ms_sec);
65
66 while (loops < max_loops && !p->exited) {
67 ++loops;
68 thread_usleep(loop_ms_sec * 1000);
69 TPRINTF(".");
70 }
71
72 if (!p->exited) {
73 *presult = ECBLOST;
74 }
75}
76
77static size_t get_thread_cnt(void)
78{
79 return min(MAX_THREADS, config.cpu_active * 4);
80}
81
82static void run_thread(size_t k, void (*func)(void*), void *arg)
83{
84 assert(thread[k] == NULL);
85
86 thread[k] = thread_create(func, arg, TASK, THREAD_FLAG_NONE,
87 "test-rcu-thread");
88
89 if(thread[k]) {
90 /* Distribute evenly. */
91 thread_wire(thread[k], &cpus[k % config.cpu_active]);
92 thread_ready(thread[k]);
93 }
94}
95
96static void run_all(void (*func)(void*))
97{
98 size_t thread_cnt = get_thread_cnt();
99
100 one_idx = 0;
101
102 for (size_t i = 0; i < thread_cnt; ++i) {
103 run_thread(i, func, NULL);
104 }
105}
106
107static void join_all(void)
108{
109 size_t thread_cnt = get_thread_cnt();
110
111 one_idx = 0;
112
113 for (size_t i = 0; i < thread_cnt; ++i) {
114 if (thread[i]) {
115 bool joined = false;
116 do {
117 int ret = thread_join_timeout(thread[i], 5 * 1000 * 1000, 0);
118 joined = (ret != ESYNCH_TIMEOUT);
119
120 if (ret == ESYNCH_OK_BLOCKED) {
121 TPRINTF("%zu threads remain\n", thread_cnt - i - 1);
122 }
123 } while (!joined);
124
125 thread_detach(thread[i]);
126 thread[i] = NULL;
127 }
128 }
129}
130
131static void run_one(void (*func)(void*), void *arg)
132{
133 assert(one_idx < MAX_THREADS);
134 run_thread(one_idx, func, arg);
135 ++one_idx;
136}
137
138
139static void join_one(void)
140{
141 assert(0 < one_idx && one_idx <= MAX_THREADS);
142
143 --one_idx;
144
145 if (thread[one_idx]) {
146 thread_join(thread[one_idx]);
147 thread_detach(thread[one_idx]);
148 thread[one_idx] = NULL;
149 }
150}
151
152/*-------------------------------------------------------------------*/
153
154
155static void nop_reader(void *arg)
156{
157 size_t nop_iters = (size_t)arg;
158
159 TPRINTF("Enter nop-reader\n");
160
161 for (size_t i = 0; i < nop_iters; ++i) {
162 rcu_read_lock();
163 rcu_read_unlock();
164 }
165
166 TPRINTF("Exit nop-reader\n");
167}
168
169static void get_seq(size_t from, size_t to, size_t steps, size_t *seq)
170{
171 assert(0 < steps && from <= to && 0 < to);
172 size_t inc = (to - from) / (steps - 1);
173
174 for (size_t i = 0; i < steps - 1; ++i) {
175 seq[i] = i * inc + from;
176 }
177
178 seq[steps - 1] = to;
179}
180
181static bool do_nop_readers(void)
182{
183 size_t seq[MAX_THREADS] = {0};
184 get_seq(100, 100000, get_thread_cnt(), seq);
185
186 TPRINTF("\nRun %zu thr: repeat empty no-op reader sections\n", get_thread_cnt());
187
188 for (size_t k = 0; k < get_thread_cnt(); ++k)
189 run_one(nop_reader, (void*)seq[k]);
190
191 TPRINTF("\nJoining %zu no-op readers\n", get_thread_cnt());
192 join_all();
193
194 return true;
195}
196
197/*-------------------------------------------------------------------*/
198
199
200
201static void long_reader(void *arg)
202{
203 const size_t iter_cnt = 100 * 1000 * 1000;
204 size_t nop_iters = (size_t)arg;
205 size_t outer_iters = iter_cnt / nop_iters;
206
207 TPRINTF("Enter long-reader\n");
208
209 for (size_t i = 0; i < outer_iters; ++i) {
210 rcu_read_lock();
211
212 for (volatile size_t k = 0; k < nop_iters; ++k) {
213 /* nop, but increment volatile k */
214 }
215
216 rcu_read_unlock();
217 }
218
219 TPRINTF("Exit long-reader\n");
220}
221
222static bool do_long_readers(void)
223{
224 size_t seq[MAX_THREADS] = {0};
225 get_seq(10, 1000 * 1000, get_thread_cnt(), seq);
226
227 TPRINTF("\nRun %zu thr: repeat long reader sections, will preempt, no cbs.\n",
228 get_thread_cnt());
229
230 for (size_t k = 0; k < get_thread_cnt(); ++k)
231 run_one(long_reader, (void*)seq[k]);
232
233 TPRINTF("\nJoining %zu readers with long reader sections.\n", get_thread_cnt());
234 join_all();
235
236 return true;
237}
238
239/*-------------------------------------------------------------------*/
240
241
242static atomic_t nop_callbacks_cnt = {0};
243/* Must be even. */
244static const int nop_updater_iters = 10000;
245
246static void count_cb(rcu_item_t *item)
247{
248 atomic_inc(&nop_callbacks_cnt);
249 free(item);
250}
251
252static void nop_updater(void *arg)
253{
254 for (int i = 0; i < nop_updater_iters; i += 2){
255 rcu_item_t *a = malloc(sizeof(rcu_item_t), FRAME_ATOMIC);
256 rcu_item_t *b = malloc(sizeof(rcu_item_t), FRAME_ATOMIC);
257
258 if (a && b) {
259 rcu_call(a, count_cb);
260 rcu_call(b, count_cb);
261 } else {
262 TPRINTF("[out-of-mem]\n");
263 free(a);
264 free(b);
265 return;
266 }
267 }
268}
269
270static bool do_nop_callbacks(void)
271{
272 atomic_set(&nop_callbacks_cnt, 0);
273
274 size_t exp_cnt = nop_updater_iters * get_thread_cnt();
275 size_t max_used_mem = sizeof(rcu_item_t) * exp_cnt;
276
277 TPRINTF("\nRun %zu thr: post %zu no-op callbacks (%zu B used), no readers.\n",
278 get_thread_cnt(), exp_cnt, max_used_mem);
279
280 run_all(nop_updater);
281 TPRINTF("\nJoining %zu no-op callback threads\n", get_thread_cnt());
282 join_all();
283
284 size_t loop_cnt = 0, max_loops = 15;
285
286 while (exp_cnt != atomic_get(&nop_callbacks_cnt) && loop_cnt < max_loops) {
287 ++loop_cnt;
288 TPRINTF(".");
289 thread_sleep(1);
290 }
291
292 return loop_cnt < max_loops;
293}
294
295/*-------------------------------------------------------------------*/
296
297typedef struct {
298 rcu_item_t rcu_item;
299 int cookie;
300} item_w_cookie_t;
301
302const int magic_cookie = 0x01234567;
303static int one_cb_is_done = 0;
304
305static void one_cb_done(rcu_item_t *item)
306{
307 assert( ((item_w_cookie_t *)item)->cookie == magic_cookie);
308 one_cb_is_done = 1;
309 TPRINTF("Callback()\n");
310 free(item);
311}
312
313static void one_cb_reader(void *arg)
314{
315 TPRINTF("Enter one-cb-reader\n");
316
317 rcu_read_lock();
318
319 item_w_cookie_t *item = malloc(sizeof(item_w_cookie_t), FRAME_ATOMIC);
320
321 if (item) {
322 item->cookie = magic_cookie;
323 rcu_call(&item->rcu_item, one_cb_done);
324 } else {
325 TPRINTF("\n[out-of-mem]\n");
326 }
327
328 thread_sleep(1);
329
330 rcu_read_unlock();
331
332 TPRINTF("Exit one-cb-reader\n");
333}
334
335static bool do_one_cb(void)
336{
337 one_cb_is_done = 0;
338
339 TPRINTF("\nRun a single reader that posts one callback.\n");
340 run_one(one_cb_reader, NULL);
341 join_one();
342
343 TPRINTF("\nJoined one-cb reader, wait for callback.\n");
344 size_t loop_cnt = 0;
345 size_t max_loops = 4; /* 200 ms total */
346
347 while (!one_cb_is_done && loop_cnt < max_loops) {
348 thread_usleep(50 * 1000);
349 ++loop_cnt;
350 }
351
352 return one_cb_is_done;
353}
354
355/*-------------------------------------------------------------------*/
356
357typedef struct {
358 size_t update_cnt;
359 size_t read_cnt;
360 size_t iters;
361} seq_work_t;
362
363typedef struct {
364 rcu_item_t rcu;
365 atomic_count_t start_time;
366} seq_item_t;
367
368
369static int seq_test_result = EOK;
370
371static atomic_t cur_time = {1};
372static atomic_count_t max_upd_done_time = {0};
373
374static void seq_cb(rcu_item_t *rcu_item)
375{
376 seq_item_t *item = member_to_inst(rcu_item, seq_item_t, rcu);
377
378 /* Racy but errs to the conservative side, so it is ok. */
379 if (max_upd_done_time < item->start_time) {
380 max_upd_done_time = item->start_time;
381
382 /* Make updated time visible */
383 memory_barrier();
384 }
385
386 free(item);
387}
388
389static void seq_func(void *arg)
390{
391 /*
392 * Temporarily workaround GCC 7.1.0 internal
393 * compiler error when compiling for riscv64.
394 */
395#ifndef KARCH_riscv64
396 seq_work_t *work = (seq_work_t*)arg;
397
398 /* Alternate between reader and updater roles. */
399 for (size_t k = 0; k < work->iters; ++k) {
400 /* Reader */
401 for (size_t i = 0; i < work->read_cnt; ++i) {
402 rcu_read_lock();
403 atomic_count_t start_time = atomic_postinc(&cur_time);
404
405 for (volatile size_t d = 0; d < 10 * i; ++d ){
406 /* no-op */
407 }
408
409 /* Get most recent max_upd_done_time. */
410 memory_barrier();
411
412 if (start_time < max_upd_done_time) {
413 seq_test_result = ERACE;
414 }
415
416 rcu_read_unlock();
417
418 if (seq_test_result != EOK)
419 return;
420 }
421
422 /* Updater */
423 for (size_t i = 0; i < work->update_cnt; ++i) {
424 seq_item_t *a = malloc(sizeof(seq_item_t), FRAME_ATOMIC);
425 seq_item_t *b = malloc(sizeof(seq_item_t), FRAME_ATOMIC);
426
427 if (a && b) {
428 a->start_time = atomic_postinc(&cur_time);
429 rcu_call(&a->rcu, seq_cb);
430
431 b->start_time = atomic_postinc(&cur_time);
432 rcu_call(&b->rcu, seq_cb);
433 } else {
434 TPRINTF("\n[out-of-mem]\n");
435 seq_test_result = ENOMEM;
436 free(a);
437 free(b);
438 return;
439 }
440 }
441
442 }
443#else
444 (void) seq_cb;
445#endif
446}
447
448static bool do_seq_check(void)
449{
450 seq_test_result = EOK;
451 max_upd_done_time = 0;
452 atomic_set(&cur_time, 1);
453
454 const size_t iters = 100;
455 const size_t total_cnt = 1000;
456 size_t read_cnt[MAX_THREADS] = {0};
457 seq_work_t item[MAX_THREADS];
458
459 size_t total_cbs = 0;
460 size_t max_used_mem = 0;
461
462 get_seq(0, total_cnt, get_thread_cnt(), read_cnt);
463
464
465 for (size_t i = 0; i < get_thread_cnt(); ++i) {
466 item[i].update_cnt = total_cnt - read_cnt[i];
467 item[i].read_cnt = read_cnt[i];
468 item[i].iters = iters;
469
470 total_cbs += 2 * iters * item[i].update_cnt;
471 }
472
473 max_used_mem = total_cbs * sizeof(seq_item_t);
474
475 const char *mem_suffix;
476 uint64_t mem_units;
477 bin_order_suffix(max_used_mem, &mem_units, &mem_suffix, false);
478
479 TPRINTF("\nRun %zu th: check callback completion time in readers. "
480 "%zu callbacks total (max %" PRIu64 " %s used). Be patient.\n",
481 get_thread_cnt(), total_cbs, mem_units, mem_suffix);
482
483 for (size_t i = 0; i < get_thread_cnt(); ++i) {
484 run_one(seq_func, &item[i]);
485 }
486
487 TPRINTF("\nJoining %zu seq-threads\n", get_thread_cnt());
488 join_all();
489
490 if (seq_test_result == ENOMEM) {
491 TPRINTF("\nErr: out-of mem\n");
492 } else if (seq_test_result == ERACE) {
493 TPRINTF("\nERROR: race detected!!\n");
494 }
495
496 return seq_test_result == EOK;
497}
498
499/*-------------------------------------------------------------------*/
500
501
502static void reader_unlocked(rcu_item_t *item)
503{
504 exited_t *p = (exited_t*)item;
505 p->exited = true;
506}
507
508static void reader_exit(void *arg)
509{
510 rcu_read_lock();
511 rcu_read_lock();
512 rcu_read_lock();
513 rcu_read_unlock();
514
515 rcu_call((rcu_item_t*)arg, reader_unlocked);
516
517 rcu_read_lock();
518 rcu_read_lock();
519
520 /* Exit without unlocking the rcu reader section. */
521}
522
523static bool do_reader_exit(void)
524{
525 TPRINTF("\nReader exits thread with rcu_lock\n");
526
527 exited_t *p = malloc(sizeof(exited_t), FRAME_ATOMIC);
528 if (!p) {
529 TPRINTF("[out-of-mem]\n");
530 return false;
531 }
532
533 p->exited = false;
534
535 run_one(reader_exit, p);
536 join_one();
537
538 int result = EOK;
539 wait_for_cb_exit(2 /* secs */, p, &result);
540
541 if (result != EOK) {
542 TPRINTF("Err: RCU locked up after exiting from within a reader\n");
543 /* Leak the mem. */
544 } else {
545 free(p);
546 }
547
548 return result == EOK;
549}
550
551/*-------------------------------------------------------------------*/
552
553/*-------------------------------------------------------------------*/
554
555typedef struct preempt_struct {
556 exited_t e;
557 int result;
558} preempt_t;
559
560
561static void preempted_unlocked(rcu_item_t *item)
562{
563 preempt_t *p = member_to_inst(item, preempt_t, e.rcu);
564 p->e.exited = true;
565 TPRINTF("Callback().\n");
566}
567
568static void preempted_reader_prev(void *arg)
569{
570 preempt_t *p = (preempt_t*)arg;
571 assert(!p->e.exited);
572
573 TPRINTF("reader_prev{ ");
574
575 rcu_read_lock();
576 scheduler();
577 rcu_read_unlock();
578
579 /*
580 * Start GP after exiting reader section w/ preemption.
581 * Just check that the callback does not lock up and is not lost.
582 */
583 rcu_call(&p->e.rcu, preempted_unlocked);
584
585 TPRINTF("}reader_prev\n");
586}
587
588static void preempted_reader_inside_cur(void *arg)
589{
590 preempt_t *p = (preempt_t*)arg;
591 assert(!p->e.exited);
592
593 TPRINTF("reader_inside_cur{ ");
594 /*
595 * Start a GP and try to finish the reader before
596 * the GP ends (including preemption).
597 */
598 rcu_call(&p->e.rcu, preempted_unlocked);
599
600 /* Give RCU threads a chance to start up. */
601 scheduler();
602 scheduler();
603
604 rcu_read_lock();
605 /* Come back as soon as possible to complete before GP ends. */
606 thread_usleep(2);
607 rcu_read_unlock();
608
609 TPRINTF("}reader_inside_cur\n");
610}
611
612
613static void preempted_reader_cur(void *arg)
614{
615 preempt_t *p = (preempt_t*)arg;
616 assert(!p->e.exited);
617
618 TPRINTF("reader_cur{ ");
619 rcu_read_lock();
620
621 /* Start GP. */
622 rcu_call(&p->e.rcu, preempted_unlocked);
623
624 /* Preempt while cur GP detection is running */
625 thread_sleep(1);
626
627 /* Err: exited before this reader completed. */
628 if (p->e.exited)
629 p->result = ERACE;
630
631 rcu_read_unlock();
632 TPRINTF("}reader_cur\n");
633}
634
635static void preempted_reader_next1(void *arg)
636{
637 preempt_t *p = (preempt_t*)arg;
638 assert(!p->e.exited);
639
640 TPRINTF("reader_next1{ ");
641 rcu_read_lock();
642
643 /* Preempt before cur GP detection starts. */
644 scheduler();
645
646 /* Start GP. */
647 rcu_call(&p->e.rcu, preempted_unlocked);
648
649 /* Err: exited before this reader completed. */
650 if (p->e.exited)
651 p->result = ERACE;
652
653 rcu_read_unlock();
654 TPRINTF("}reader_next1\n");
655}
656
657static void preempted_reader_next2(void *arg)
658{
659 preempt_t *p = (preempt_t*)arg;
660 assert(!p->e.exited);
661
662 TPRINTF("reader_next2{ ");
663 rcu_read_lock();
664
665 /* Preempt before cur GP detection starts. */
666 scheduler();
667
668 /* Start GP. */
669 rcu_call(&p->e.rcu, preempted_unlocked);
670
671 /*
672 * Preempt twice while GP is running after we've been known
673 * to hold up the GP just to make sure multiple preemptions
674 * are properly tracked if a reader is delaying the cur GP.
675 */
676 thread_sleep(1);
677 thread_sleep(1);
678
679 /* Err: exited before this reader completed. */
680 if (p->e.exited)
681 p->result = ERACE;
682
683 rcu_read_unlock();
684 TPRINTF("}reader_next2\n");
685}
686
687
688static bool do_one_reader_preempt(void (*f)(void*), const char *err)
689{
690 preempt_t *p = malloc(sizeof(preempt_t), FRAME_ATOMIC);
691 if (!p) {
692 TPRINTF("[out-of-mem]\n");
693 return false;
694 }
695
696 p->e.exited = false;
697 p->result = EOK;
698
699 run_one(f, p);
700 join_one();
701
702 /* Wait at most 4 secs. */
703 wait_for_cb_exit(4, &p->e, &p->result);
704
705 if (p->result == EOK) {
706 free(p);
707 return true;
708 } else {
709 TPRINTF("%s", err);
710 /* Leak a bit of mem. */
711 return false;
712 }
713}
714
715static bool do_reader_preempt(void)
716{
717 TPRINTF("\nReaders will be preempted.\n");
718
719 bool success = true;
720 bool ok = true;
721
722 ok = do_one_reader_preempt(preempted_reader_prev,
723 "Err: preempted_reader_prev()\n");
724 success = success && ok;
725
726 ok = do_one_reader_preempt(preempted_reader_inside_cur,
727 "Err: preempted_reader_inside_cur()\n");
728 success = success && ok;
729
730 ok = do_one_reader_preempt(preempted_reader_cur,
731 "Err: preempted_reader_cur()\n");
732 success = success && ok;
733
734 ok = do_one_reader_preempt(preempted_reader_next1,
735 "Err: preempted_reader_next1()\n");
736 success = success && ok;
737
738 ok = do_one_reader_preempt(preempted_reader_next2,
739 "Err: preempted_reader_next2()\n");
740 success = success && ok;
741
742 return success;
743}
744
745/*-------------------------------------------------------------------*/
746typedef struct {
747 bool reader_done;
748 bool reader_running;
749 bool synch_running;
750} synch_t;
751
752static void synch_reader(void *arg)
753{
754 synch_t *synch = (synch_t *) arg;
755
756 rcu_read_lock();
757
758 /* Order accesses of synch after the reader section begins. */
759 memory_barrier();
760
761 synch->reader_running = true;
762
763 while (!synch->synch_running) {
764 /* 0.5 sec */
765 delay(500 * 1000);
766 }
767
768 /* Run for 1 sec */
769 delay(1000 * 1000);
770 /* thread_join() propagates done to do_synch() */
771 synch->reader_done = true;
772
773 rcu_read_unlock();
774}
775
776
777static bool do_synch(void)
778{
779 TPRINTF("\nSynchronize with long reader\n");
780
781 synch_t *synch = malloc(sizeof(synch_t), FRAME_ATOMIC);
782
783 if (!synch) {
784 TPRINTF("[out-of-mem]\n");
785 return false;
786 }
787
788 synch->reader_done = false;
789 synch->reader_running = false;
790 synch->synch_running = false;
791
792 run_one(synch_reader, synch);
793
794 /* Wait for the reader to enter its critical section. */
795 scheduler();
796 while (!synch->reader_running) {
797 thread_usleep(500 * 1000);
798 }
799
800 synch->synch_running = true;
801
802 rcu_synchronize();
803 join_one();
804
805
806 if (synch->reader_done) {
807 free(synch);
808 return true;
809 } else {
810 TPRINTF("Err: synchronize() exited prematurely \n");
811 /* Leak some mem. */
812 return false;
813 }
814}
815
816/*-------------------------------------------------------------------*/
817typedef struct {
818 rcu_item_t rcu_item;
819 atomic_t done;
820} barrier_t;
821
822static void barrier_callback(rcu_item_t *item)
823{
824 barrier_t *b = member_to_inst(item, barrier_t, rcu_item);
825 atomic_set(&b->done, 1);
826}
827
828static bool do_barrier(void)
829{
830 TPRINTF("\nrcu_barrier: Wait for outstanding rcu callbacks to complete\n");
831
832 barrier_t *barrier = malloc(sizeof(barrier_t), FRAME_ATOMIC);
833
834 if (!barrier) {
835 TPRINTF("[out-of-mem]\n");
836 return false;
837 }
838
839 atomic_set(&barrier->done, 0);
840
841 rcu_call(&barrier->rcu_item, barrier_callback);
842 rcu_barrier();
843
844 if (1 == atomic_get(&barrier->done)) {
845 free(barrier);
846 return true;
847 } else {
848 TPRINTF("rcu_barrier() exited prematurely.\n");
849 /* Leak some mem. */
850 return false;
851 }
852}
853
854/*-------------------------------------------------------------------*/
855
856typedef struct {
857 size_t iters;
858 bool master;
859} stress_t;
860
861
862static void stress_reader(void *arg)
863{
864 bool *done = (bool*) arg;
865
866 while (!*done) {
867 rcu_read_lock();
868 rcu_read_unlock();
869
870 /*
871 * Do some work outside of the reader section so we are not always
872 * preempted in the reader section.
873 */
874 delay(5);
875 }
876}
877
878static void stress_cb(rcu_item_t *item)
879{
880 /* 5 us * 1000 * 1000 iters == 5 sec per updater thread */
881 delay(5);
882 free(item);
883}
884
885static void stress_updater(void *arg)
886{
887 stress_t *s = (stress_t *)arg;
888
889 for (size_t i = 0; i < s->iters; ++i) {
890 rcu_item_t *item = malloc(sizeof(rcu_item_t), FRAME_ATOMIC);
891
892 if (item) {
893 rcu_call(item, stress_cb);
894 } else {
895 TPRINTF("[out-of-mem]\n");
896 return;
897 }
898
899 /* Print a dot if we make a progress of 1% */
900 if (s->master && 0 == (i % (s->iters/100)))
901 TPRINTF(".");
902 }
903}
904
905static bool do_stress(void)
906{
907 size_t cb_per_thread = 1000 * 1000;
908 bool done = false;
909 stress_t master = { .iters = cb_per_thread, .master = true };
910 stress_t worker = { .iters = cb_per_thread, .master = false };
911
912 size_t thread_cnt = min(MAX_THREADS / 2, config.cpu_active);
913 /* Each cpu has one reader and one updater. */
914 size_t reader_cnt = thread_cnt;
915 size_t updater_cnt = thread_cnt;
916
917 size_t exp_upd_calls = updater_cnt * cb_per_thread;
918 size_t max_used_mem = exp_upd_calls * sizeof(rcu_item_t);
919
920 const char *mem_suffix;
921 uint64_t mem_units;
922 bin_order_suffix(max_used_mem, &mem_units, &mem_suffix, false);
923
924 TPRINTF("\nStress: Run %zu nop-readers and %zu updaters. %zu callbacks"
925 " total (max %" PRIu64 " %s used). Be very patient.\n",
926 reader_cnt, updater_cnt, exp_upd_calls, mem_units, mem_suffix);
927
928 for (size_t k = 0; k < reader_cnt; ++k) {
929 run_one(stress_reader, &done);
930 }
931
932 for (size_t k = 0; k < updater_cnt; ++k) {
933 run_one(stress_updater, k > 0 ? &worker : &master);
934 }
935
936 TPRINTF("\nJoining %zu stress updaters.\n", updater_cnt);
937
938 for (size_t k = 0; k < updater_cnt; ++k) {
939 join_one();
940 }
941
942 done = true;
943
944 TPRINTF("\nJoining %zu stress nop-readers.\n", reader_cnt);
945
946 join_all();
947 return true;
948}
949/*-------------------------------------------------------------------*/
950
951typedef struct {
952 rcu_item_t r;
953 size_t total_cnt;
954 size_t count_down;
955 bool expedite;
956} expedite_t;
957
958static void expedite_cb(rcu_item_t *arg)
959{
960 expedite_t *e = (expedite_t *)arg;
961
962 if (1 < e->count_down) {
963 --e->count_down;
964
965 if (0 == (e->count_down % (e->total_cnt/100))) {
966 TPRINTF("*");
967 }
968
969 _rcu_call(e->expedite, &e->r, expedite_cb);
970 } else {
971 /* Do not touch any of e's mem after we declare we're done with it. */
972 memory_barrier();
973 e->count_down = 0;
974 }
975}
976
977static void run_expedite(bool exp, size_t cnt)
978{
979 expedite_t e;
980 e.total_cnt = cnt;
981 e.count_down = cnt;
982 e.expedite = exp;
983
984 _rcu_call(e.expedite, &e.r, expedite_cb);
985
986 while (0 < e.count_down) {
987 thread_sleep(1);
988 TPRINTF(".");
989 }
990}
991
992static bool do_expedite(void)
993{
994 size_t exp_cnt = 1000 * 1000;
995 size_t normal_cnt = 1 * 1000;
996
997 TPRINTF("Expedited: sequence of %zu rcu_calls\n", exp_cnt);
998 run_expedite(true, exp_cnt);
999 TPRINTF("Normal/non-expedited: sequence of %zu rcu_calls\n", normal_cnt);
1000 run_expedite(false, normal_cnt);
1001 return true;
1002}
1003/*-------------------------------------------------------------------*/
1004
1005struct test_func {
1006 bool include;
1007 bool (*func)(void);
1008 const char *desc;
1009};
1010
1011
1012const char *test_rcu1(void)
1013{
1014 struct test_func test_func[] = {
1015 { 1, do_one_cb, "do_one_cb" },
1016 { 1, do_reader_preempt, "do_reader_preempt" },
1017 { 1, do_synch, "do_synch" },
1018 { 1, do_barrier, "do_barrier" },
1019 { 1, do_reader_exit, "do_reader_exit" },
1020 { 1, do_nop_readers, "do_nop_readers" },
1021 { 1, do_seq_check, "do_seq_check" },
1022 { 0, do_long_readers, "do_long_readers" },
1023 { 1, do_nop_callbacks, "do_nop_callbacks" },
1024 { 0, do_expedite, "do_expedite" },
1025 { 1, do_stress, "do_stress" },
1026 { 0, NULL, NULL }
1027 };
1028
1029 bool success = true;
1030 bool ok = true;
1031 uint64_t completed_gps = rcu_completed_gps();
1032 uint64_t delta_gps = 0;
1033
1034 for (int i = 0; test_func[i].func; ++i) {
1035 if (!test_func[i].include) {
1036 TPRINTF("\nSubtest %s() skipped.\n", test_func[i].desc);
1037 continue;
1038 } else {
1039 TPRINTF("\nRunning subtest %s.\n", test_func[i].desc);
1040 }
1041
1042 ok = test_func[i].func();
1043 success = success && ok;
1044
1045 delta_gps = rcu_completed_gps() - completed_gps;
1046 completed_gps += delta_gps;
1047
1048 if (ok) {
1049 TPRINTF("\nSubtest %s() ok (GPs: %" PRIu64 ").\n",
1050 test_func[i].desc, delta_gps);
1051 } else {
1052 TPRINTF("\nFailed: %s(). Pausing for 5 secs.\n", test_func[i].desc);
1053 thread_sleep(5);
1054 }
1055 }
1056
1057 if (success)
1058 return NULL;
1059 else
1060 return "One of the tests failed.";
1061}
Note: See TracBrowser for help on using the repository browser.