]> git.proxmox.com Git - mirror_qemu.git/blame - tests/unit/test-coroutine.c
Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.1-20210709' into staging
[mirror_qemu.git] / tests / unit / test-coroutine.c
CommitLineData
aa7ee42e
SH
1/*
2 * Coroutine tests
3 *
4 * Copyright IBM, Corp. 2011
5 *
6 * Authors:
7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
10 * See the COPYING.LIB file in the top-level directory.
11 *
12 */
13
681c28a3 14#include "qemu/osdep.h"
10817bf0
DB
15#include "qemu/coroutine.h"
16#include "qemu/coroutine_int.h"
e70372fc 17#include "qemu/lockable.h"
aa7ee42e
SH
18
19/*
20 * Check that qemu_in_coroutine() works
21 */
22
23static void coroutine_fn verify_in_coroutine(void *opaque)
24{
25 g_assert(qemu_in_coroutine());
26}
27
28static void test_in_coroutine(void)
29{
30 Coroutine *coroutine;
31
32 g_assert(!qemu_in_coroutine());
33
0b8b8753
PB
34 coroutine = qemu_coroutine_create(verify_in_coroutine, NULL);
35 qemu_coroutine_enter(coroutine);
aa7ee42e
SH
36}
37
38/*
39 * Check that qemu_coroutine_self() works
40 */
41
42static void coroutine_fn verify_self(void *opaque)
43{
7e70cdba
PB
44 Coroutine **p_co = opaque;
45 g_assert(qemu_coroutine_self() == *p_co);
aa7ee42e
SH
46}
47
48static void test_self(void)
49{
50 Coroutine *coroutine;
51
0b8b8753
PB
52 coroutine = qemu_coroutine_create(verify_self, &coroutine);
53 qemu_coroutine_enter(coroutine);
aa7ee42e
SH
54}
55
afe16f3f
SH
56/*
57 * Check that qemu_coroutine_entered() works
58 */
59
60static void coroutine_fn verify_entered_step_2(void *opaque)
61{
62 Coroutine *caller = (Coroutine *)opaque;
63
64 g_assert(qemu_coroutine_entered(caller));
65 g_assert(qemu_coroutine_entered(qemu_coroutine_self()));
66 qemu_coroutine_yield();
67
68 /* Once more to check it still works after yielding */
69 g_assert(qemu_coroutine_entered(caller));
70 g_assert(qemu_coroutine_entered(qemu_coroutine_self()));
afe16f3f
SH
71}
72
73static void coroutine_fn verify_entered_step_1(void *opaque)
74{
75 Coroutine *self = qemu_coroutine_self();
76 Coroutine *coroutine;
77
78 g_assert(qemu_coroutine_entered(self));
79
80 coroutine = qemu_coroutine_create(verify_entered_step_2, self);
81 g_assert(!qemu_coroutine_entered(coroutine));
82 qemu_coroutine_enter(coroutine);
83 g_assert(!qemu_coroutine_entered(coroutine));
84 qemu_coroutine_enter(coroutine);
85}
86
87static void test_entered(void)
88{
89 Coroutine *coroutine;
90
91 coroutine = qemu_coroutine_create(verify_entered_step_1, NULL);
92 g_assert(!qemu_coroutine_entered(coroutine));
93 qemu_coroutine_enter(coroutine);
94}
95
aa7ee42e
SH
96/*
97 * Check that coroutines may nest multiple levels
98 */
99
100typedef struct {
101 unsigned int n_enter; /* num coroutines entered */
102 unsigned int n_return; /* num coroutines returned */
103 unsigned int max; /* maximum level of nesting */
104} NestData;
105
106static void coroutine_fn nest(void *opaque)
107{
108 NestData *nd = opaque;
109
110 nd->n_enter++;
111
112 if (nd->n_enter < nd->max) {
113 Coroutine *child;
114
0b8b8753
PB
115 child = qemu_coroutine_create(nest, nd);
116 qemu_coroutine_enter(child);
aa7ee42e
SH
117 }
118
119 nd->n_return++;
120}
121
122static void test_nesting(void)
123{
124 Coroutine *root;
125 NestData nd = {
126 .n_enter = 0,
127 .n_return = 0,
128 .max = 128,
129 };
130
0b8b8753
PB
131 root = qemu_coroutine_create(nest, &nd);
132 qemu_coroutine_enter(root);
aa7ee42e
SH
133
134 /* Must enter and return from max nesting level */
135 g_assert_cmpint(nd.n_enter, ==, nd.max);
136 g_assert_cmpint(nd.n_return, ==, nd.max);
137}
138
139/*
140 * Check that yield/enter transfer control correctly
141 */
142
143static void coroutine_fn yield_5_times(void *opaque)
144{
145 bool *done = opaque;
146 int i;
147
148 for (i = 0; i < 5; i++) {
149 qemu_coroutine_yield();
150 }
151 *done = true;
152}
153
154static void test_yield(void)
155{
156 Coroutine *coroutine;
157 bool done = false;
158 int i = -1; /* one extra time to return from coroutine */
159
0b8b8753 160 coroutine = qemu_coroutine_create(yield_5_times, &done);
aa7ee42e 161 while (!done) {
0b8b8753 162 qemu_coroutine_enter(coroutine);
aa7ee42e
SH
163 i++;
164 }
165 g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
166}
167
7c2eed3e
SH
168static void coroutine_fn c2_fn(void *opaque)
169{
170 qemu_coroutine_yield();
171}
172
173static void coroutine_fn c1_fn(void *opaque)
174{
175 Coroutine *c2 = opaque;
0b8b8753 176 qemu_coroutine_enter(c2);
7c2eed3e
SH
177}
178
439b6e5e 179static void test_no_dangling_access(void)
7c2eed3e
SH
180{
181 Coroutine *c1;
182 Coroutine *c2;
980e6621 183 Coroutine tmp;
7c2eed3e 184
0b8b8753
PB
185 c2 = qemu_coroutine_create(c2_fn, NULL);
186 c1 = qemu_coroutine_create(c1_fn, c2);
7c2eed3e 187
0b8b8753 188 qemu_coroutine_enter(c1);
980e6621
KW
189
190 /* c1 shouldn't be used any more now; make sure we segfault if it is */
191 tmp = *c1;
7c2eed3e 192 memset(c1, 0xff, sizeof(Coroutine));
0b8b8753 193 qemu_coroutine_enter(c2);
980e6621
KW
194
195 /* Must restore the coroutine now to avoid corrupted pool */
196 *c1 = tmp;
7c2eed3e
SH
197}
198
439b6e5e
PB
199static bool locked;
200static int done;
201
202static void coroutine_fn mutex_fn(void *opaque)
203{
204 CoMutex *m = opaque;
205 qemu_co_mutex_lock(m);
206 assert(!locked);
207 locked = true;
208 qemu_coroutine_yield();
209 locked = false;
210 qemu_co_mutex_unlock(m);
211 done++;
212}
213
e70372fc
PB
214static void coroutine_fn lockable_fn(void *opaque)
215{
216 QemuLockable *x = opaque;
217 qemu_lockable_lock(x);
218 assert(!locked);
219 locked = true;
220 qemu_coroutine_yield();
221 locked = false;
222 qemu_lockable_unlock(x);
223 done++;
224}
225
439b6e5e
PB
226static void do_test_co_mutex(CoroutineEntry *entry, void *opaque)
227{
228 Coroutine *c1 = qemu_coroutine_create(entry, opaque);
229 Coroutine *c2 = qemu_coroutine_create(entry, opaque);
230
231 done = 0;
232 qemu_coroutine_enter(c1);
233 g_assert(locked);
234 qemu_coroutine_enter(c2);
235
236 /* Unlock queues c2. It is then started automatically when c1 yields or
237 * terminates.
238 */
239 qemu_coroutine_enter(c1);
240 g_assert_cmpint(done, ==, 1);
241 g_assert(locked);
242
243 qemu_coroutine_enter(c2);
244 g_assert_cmpint(done, ==, 2);
245 g_assert(!locked);
246}
247
248static void test_co_mutex(void)
249{
250 CoMutex m;
251
252 qemu_co_mutex_init(&m);
253 do_test_co_mutex(mutex_fn, &m);
254}
255
e70372fc
PB
256static void test_co_mutex_lockable(void)
257{
258 CoMutex m;
259 CoMutex *null_pointer = NULL;
260
261 qemu_co_mutex_init(&m);
262 do_test_co_mutex(lockable_fn, QEMU_MAKE_LOCKABLE(&m));
263
264 g_assert(QEMU_MAKE_LOCKABLE(null_pointer) == NULL);
265}
266
25bc2dae
PB
267static CoRwlock rwlock;
268
269/* Test that readers are properly sent back to the queue when upgrading,
270 * even if they are the sole readers. The test scenario is as follows:
271 *
272 *
273 * | c1 | c2 |
274 * |--------------+------------+
275 * | rdlock | |
276 * | yield | |
277 * | | wrlock |
278 * | | <queued> |
279 * | upgrade | |
280 * | <queued> | <dequeued> |
281 * | | unlock |
282 * | <dequeued> | |
283 * | unlock | |
284 */
285
286static void coroutine_fn rwlock_yield_upgrade(void *opaque)
287{
288 qemu_co_rwlock_rdlock(&rwlock);
289 qemu_coroutine_yield();
290
291 qemu_co_rwlock_upgrade(&rwlock);
292 qemu_co_rwlock_unlock(&rwlock);
293
294 *(bool *)opaque = true;
295}
296
297static void coroutine_fn rwlock_wrlock_yield(void *opaque)
298{
299 qemu_co_rwlock_wrlock(&rwlock);
300 qemu_coroutine_yield();
301
302 qemu_co_rwlock_unlock(&rwlock);
303 *(bool *)opaque = true;
304}
305
306static void test_co_rwlock_upgrade(void)
307{
308 bool c1_done = false;
309 bool c2_done = false;
310 Coroutine *c1, *c2;
311
312 qemu_co_rwlock_init(&rwlock);
313 c1 = qemu_coroutine_create(rwlock_yield_upgrade, &c1_done);
314 c2 = qemu_coroutine_create(rwlock_wrlock_yield, &c2_done);
315
316 qemu_coroutine_enter(c1);
317 qemu_coroutine_enter(c2);
318
319 /* c1 now should go to sleep. */
320 qemu_coroutine_enter(c1);
321 g_assert(!c1_done);
322
323 qemu_coroutine_enter(c2);
324 g_assert(c1_done);
325 g_assert(c2_done);
326}
327
b6489ac0
DE
328static void coroutine_fn rwlock_rdlock_yield(void *opaque)
329{
330 qemu_co_rwlock_rdlock(&rwlock);
331 qemu_coroutine_yield();
332
333 qemu_co_rwlock_unlock(&rwlock);
334 qemu_coroutine_yield();
335
336 *(bool *)opaque = true;
337}
338
339static void coroutine_fn rwlock_wrlock_downgrade(void *opaque)
340{
341 qemu_co_rwlock_wrlock(&rwlock);
342
343 qemu_co_rwlock_downgrade(&rwlock);
344 qemu_co_rwlock_unlock(&rwlock);
345 *(bool *)opaque = true;
346}
347
348static void coroutine_fn rwlock_rdlock(void *opaque)
349{
350 qemu_co_rwlock_rdlock(&rwlock);
351
352 qemu_co_rwlock_unlock(&rwlock);
353 *(bool *)opaque = true;
354}
355
356static void coroutine_fn rwlock_wrlock(void *opaque)
357{
358 qemu_co_rwlock_wrlock(&rwlock);
359
360 qemu_co_rwlock_unlock(&rwlock);
361 *(bool *)opaque = true;
362}
363
364/*
365 * Check that downgrading a reader-writer lock does not cause a hang.
366 *
367 * Four coroutines are used to produce a situation where there are
368 * both reader and writer hopefuls waiting to acquire an rwlock that
369 * is held by a reader.
370 *
371 * The correct sequence of operations we aim to provoke can be
372 * represented as:
373 *
374 * | c1 | c2 | c3 | c4 |
375 * |--------+------------+------------+------------|
376 * | rdlock | | | |
377 * | yield | | | |
378 * | | wrlock | | |
379 * | | <queued> | | |
380 * | | | rdlock | |
381 * | | | <queued> | |
382 * | | | | wrlock |
383 * | | | | <queued> |
384 * | unlock | | | |
385 * | yield | | | |
386 * | | <dequeued> | | |
387 * | | downgrade | | |
388 * | | | <dequeued> | |
389 * | | | unlock | |
390 * | | ... | | |
391 * | | unlock | | |
392 * | | | | <dequeued> |
393 * | | | | unlock |
394 */
395static void test_co_rwlock_downgrade(void)
396{
397 bool c1_done = false;
398 bool c2_done = false;
399 bool c3_done = false;
400 bool c4_done = false;
401 Coroutine *c1, *c2, *c3, *c4;
402
403 qemu_co_rwlock_init(&rwlock);
404
405 c1 = qemu_coroutine_create(rwlock_rdlock_yield, &c1_done);
406 c2 = qemu_coroutine_create(rwlock_wrlock_downgrade, &c2_done);
407 c3 = qemu_coroutine_create(rwlock_rdlock, &c3_done);
408 c4 = qemu_coroutine_create(rwlock_wrlock, &c4_done);
409
410 qemu_coroutine_enter(c1);
411 qemu_coroutine_enter(c2);
412 qemu_coroutine_enter(c3);
413 qemu_coroutine_enter(c4);
414
415 qemu_coroutine_enter(c1);
416
417 g_assert(c2_done);
418 g_assert(c3_done);
419 g_assert(c4_done);
420
421 qemu_coroutine_enter(c1);
422
423 g_assert(c1_done);
424}
425
aa7ee42e
SH
426/*
427 * Check that creation, enter, and return work
428 */
429
430static void coroutine_fn set_and_exit(void *opaque)
431{
432 bool *done = opaque;
433
434 *done = true;
435}
436
437static void test_lifecycle(void)
438{
439 Coroutine *coroutine;
440 bool done = false;
441
442 /* Create, enter, and return from coroutine */
0b8b8753
PB
443 coroutine = qemu_coroutine_create(set_and_exit, &done);
444 qemu_coroutine_enter(coroutine);
aa7ee42e
SH
445 g_assert(done); /* expect done to be true (first time) */
446
447 /* Repeat to check that no state affects this test */
448 done = false;
0b8b8753
PB
449 coroutine = qemu_coroutine_create(set_and_exit, &done);
450 qemu_coroutine_enter(coroutine);
aa7ee42e
SH
451 g_assert(done); /* expect done to be true (second time) */
452}
453
f8d1daea
CS
454
455#define RECORD_SIZE 10 /* Leave some room for expansion */
456struct coroutine_position {
457 int func;
458 int state;
459};
460static struct coroutine_position records[RECORD_SIZE];
461static unsigned record_pos;
462
463static void record_push(int func, int state)
464{
465 struct coroutine_position *cp = &records[record_pos++];
466 g_assert_cmpint(record_pos, <, RECORD_SIZE);
467 cp->func = func;
468 cp->state = state;
469}
470
471static void coroutine_fn co_order_test(void *opaque)
472{
473 record_push(2, 1);
474 g_assert(qemu_in_coroutine());
475 qemu_coroutine_yield();
476 record_push(2, 2);
477 g_assert(qemu_in_coroutine());
478}
479
480static void do_order_test(void)
481{
482 Coroutine *co;
483
0b8b8753 484 co = qemu_coroutine_create(co_order_test, NULL);
f8d1daea 485 record_push(1, 1);
0b8b8753 486 qemu_coroutine_enter(co);
f8d1daea
CS
487 record_push(1, 2);
488 g_assert(!qemu_in_coroutine());
0b8b8753 489 qemu_coroutine_enter(co);
f8d1daea
CS
490 record_push(1, 3);
491 g_assert(!qemu_in_coroutine());
492}
493
494static void test_order(void)
495{
496 int i;
497 const struct coroutine_position expected_pos[] = {
498 {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3}
499 };
500 do_order_test();
501 g_assert_cmpint(record_pos, ==, 5);
502 for (i = 0; i < record_pos; i++) {
503 g_assert_cmpint(records[i].func , ==, expected_pos[i].func );
504 g_assert_cmpint(records[i].state, ==, expected_pos[i].state);
505 }
506}
5e3840ce
SH
507/*
508 * Lifecycle benchmark
509 */
510
511static void coroutine_fn empty_coroutine(void *opaque)
512{
513 /* Do nothing */
514}
515
516static void perf_lifecycle(void)
517{
518 Coroutine *coroutine;
519 unsigned int i, max;
520 double duration;
521
522 max = 1000000;
523
524 g_test_timer_start();
525 for (i = 0; i < max; i++) {
0b8b8753
PB
526 coroutine = qemu_coroutine_create(empty_coroutine, NULL);
527 qemu_coroutine_enter(coroutine);
5e3840ce
SH
528 }
529 duration = g_test_timer_elapsed();
530
13ee9e30 531 g_test_message("Lifecycle %u iterations: %f s", max, duration);
5e3840ce
SH
532}
533
7e849a99
AB
534static void perf_nesting(void)
535{
536 unsigned int i, maxcycles, maxnesting;
537 double duration;
538
a9031675 539 maxcycles = 10000;
02700315 540 maxnesting = 1000;
7e849a99 541 Coroutine *root;
7e849a99
AB
542
543 g_test_timer_start();
544 for (i = 0; i < maxcycles; i++) {
a9031675
GK
545 NestData nd = {
546 .n_enter = 0,
547 .n_return = 0,
548 .max = maxnesting,
549 };
0b8b8753
PB
550 root = qemu_coroutine_create(nest, &nd);
551 qemu_coroutine_enter(root);
7e849a99
AB
552 }
553 duration = g_test_timer_elapsed();
554
13ee9e30 555 g_test_message("Nesting %u iterations of %u depth each: %f s",
7e849a99
AB
556 maxcycles, maxnesting, duration);
557}
558
2fcd15ea
GK
559/*
560 * Yield benchmark
561 */
562
563static void coroutine_fn yield_loop(void *opaque)
564{
565 unsigned int *counter = opaque;
566
567 while ((*counter) > 0) {
568 (*counter)--;
569 qemu_coroutine_yield();
570 }
571}
572
573static void perf_yield(void)
574{
575 unsigned int i, maxcycles;
576 double duration;
577
578 maxcycles = 100000000;
579 i = maxcycles;
0b8b8753 580 Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i);
2fcd15ea
GK
581
582 g_test_timer_start();
583 while (i > 0) {
0b8b8753 584 qemu_coroutine_enter(coroutine);
2fcd15ea
GK
585 }
586 duration = g_test_timer_elapsed();
587
13ee9e30 588 g_test_message("Yield %u iterations: %f s", maxcycles, duration);
2fcd15ea 589}
7e849a99 590
58803ce7
PB
591static __attribute__((noinline)) void dummy(unsigned *i)
592{
593 (*i)--;
594}
595
596static void perf_baseline(void)
597{
598 unsigned int i, maxcycles;
599 double duration;
600
601 maxcycles = 100000000;
602 i = maxcycles;
603
604 g_test_timer_start();
605 while (i > 0) {
606 dummy(&i);
607 }
608 duration = g_test_timer_elapsed();
609
13ee9e30 610 g_test_message("Function call %u iterations: %f s", maxcycles, duration);
58803ce7
PB
611}
612
61ff8cfb
ML
613static __attribute__((noinline)) void perf_cost_func(void *opaque)
614{
615 qemu_coroutine_yield();
616}
617
618static void perf_cost(void)
619{
620 const unsigned long maxcycles = 40000000;
621 unsigned long i = 0;
622 double duration;
623 unsigned long ops;
624 Coroutine *co;
625
626 g_test_timer_start();
627 while (i++ < maxcycles) {
0b8b8753
PB
628 co = qemu_coroutine_create(perf_cost_func, &i);
629 qemu_coroutine_enter(co);
630 qemu_coroutine_enter(co);
61ff8cfb
ML
631 }
632 duration = g_test_timer_elapsed();
633 ops = (long)(maxcycles / (duration * 1000));
634
635 g_test_message("Run operation %lu iterations %f s, %luK operations/s, "
636 "%luns per coroutine",
637 maxcycles,
638 duration, ops,
6d86ae08 639 (unsigned long)(1000000000.0 * duration / maxcycles));
61ff8cfb
ML
640}
641
aa7ee42e
SH
642int main(int argc, char **argv)
643{
644 g_test_init(&argc, &argv, NULL);
271b385e
SH
645
646 /* This test assumes there is a freelist and marks freed coroutine memory
647 * with a sentinel value. If there is no freelist this would legitimately
648 * crash, so skip it.
649 */
650 if (CONFIG_COROUTINE_POOL) {
439b6e5e 651 g_test_add_func("/basic/no-dangling-access", test_no_dangling_access);
271b385e
SH
652 }
653
aa7ee42e
SH
654 g_test_add_func("/basic/lifecycle", test_lifecycle);
655 g_test_add_func("/basic/yield", test_yield);
656 g_test_add_func("/basic/nesting", test_nesting);
657 g_test_add_func("/basic/self", test_self);
afe16f3f 658 g_test_add_func("/basic/entered", test_entered);
aa7ee42e 659 g_test_add_func("/basic/in_coroutine", test_in_coroutine);
f8d1daea 660 g_test_add_func("/basic/order", test_order);
439b6e5e 661 g_test_add_func("/locking/co-mutex", test_co_mutex);
e70372fc 662 g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable);
25bc2dae 663 g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade);
b6489ac0 664 g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade);
5e3840ce
SH
665 if (g_test_perf()) {
666 g_test_add_func("/perf/lifecycle", perf_lifecycle);
7e849a99 667 g_test_add_func("/perf/nesting", perf_nesting);
2fcd15ea 668 g_test_add_func("/perf/yield", perf_yield);
58803ce7 669 g_test_add_func("/perf/function-call", perf_baseline);
61ff8cfb 670 g_test_add_func("/perf/cost", perf_cost);
5e3840ce 671 }
aa7ee42e
SH
672 return g_test_run();
673}