cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

test-coroutine.c (16465B)


      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
     14#include "qemu/osdep.h"
     15#include "qemu/coroutine.h"
     16#include "qemu/coroutine_int.h"
     17#include "qemu/lockable.h"
     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
     34    coroutine = qemu_coroutine_create(verify_in_coroutine, NULL);
     35    qemu_coroutine_enter(coroutine);
     36}
     37
     38/*
     39 * Check that qemu_coroutine_self() works
     40 */
     41
     42static void coroutine_fn verify_self(void *opaque)
     43{
     44    Coroutine **p_co = opaque;
     45    g_assert(qemu_coroutine_self() == *p_co);
     46}
     47
     48static void test_self(void)
     49{
     50    Coroutine *coroutine;
     51
     52    coroutine = qemu_coroutine_create(verify_self, &coroutine);
     53    qemu_coroutine_enter(coroutine);
     54}
     55
     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()));
     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
     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
    115        child = qemu_coroutine_create(nest, nd);
    116        qemu_coroutine_enter(child);
    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
    131    root = qemu_coroutine_create(nest, &nd);
    132    qemu_coroutine_enter(root);
    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
    160    coroutine = qemu_coroutine_create(yield_5_times, &done);
    161    while (!done) {
    162        qemu_coroutine_enter(coroutine);
    163        i++;
    164    }
    165    g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
    166}
    167
    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;
    176    qemu_coroutine_enter(c2);
    177}
    178
    179static void test_no_dangling_access(void)
    180{
    181    Coroutine *c1;
    182    Coroutine *c2;
    183    Coroutine tmp;
    184
    185    c2 = qemu_coroutine_create(c2_fn, NULL);
    186    c1 = qemu_coroutine_create(c1_fn, c2);
    187
    188    qemu_coroutine_enter(c1);
    189
    190    /* c1 shouldn't be used any more now; make sure we segfault if it is */
    191    tmp = *c1;
    192    memset(c1, 0xff, sizeof(Coroutine));
    193    qemu_coroutine_enter(c2);
    194
    195    /* Must restore the coroutine now to avoid corrupted pool */
    196    *c1 = tmp;
    197}
    198
    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
    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
    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
    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
    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
    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
    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 */
    443    coroutine = qemu_coroutine_create(set_and_exit, &done);
    444    qemu_coroutine_enter(coroutine);
    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;
    449    coroutine = qemu_coroutine_create(set_and_exit, &done);
    450    qemu_coroutine_enter(coroutine);
    451    g_assert(done); /* expect done to be true (second time) */
    452}
    453
    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
    484    co = qemu_coroutine_create(co_order_test, NULL);
    485    record_push(1, 1);
    486    qemu_coroutine_enter(co);
    487    record_push(1, 2);
    488    g_assert(!qemu_in_coroutine());
    489    qemu_coroutine_enter(co);
    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}
    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++) {
    526        coroutine = qemu_coroutine_create(empty_coroutine, NULL);
    527        qemu_coroutine_enter(coroutine);
    528    }
    529    duration = g_test_timer_elapsed();
    530
    531    g_test_message("Lifecycle %u iterations: %f s", max, duration);
    532}
    533
    534static void perf_nesting(void)
    535{
    536    unsigned int i, maxcycles, maxnesting;
    537    double duration;
    538
    539    maxcycles = 10000;
    540    maxnesting = 1000;
    541    Coroutine *root;
    542
    543    g_test_timer_start();
    544    for (i = 0; i < maxcycles; i++) {
    545        NestData nd = {
    546            .n_enter  = 0,
    547            .n_return = 0,
    548            .max      = maxnesting,
    549        };
    550        root = qemu_coroutine_create(nest, &nd);
    551        qemu_coroutine_enter(root);
    552    }
    553    duration = g_test_timer_elapsed();
    554
    555    g_test_message("Nesting %u iterations of %u depth each: %f s",
    556        maxcycles, maxnesting, duration);
    557}
    558
    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;
    580    Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i);
    581
    582    g_test_timer_start();
    583    while (i > 0) {
    584        qemu_coroutine_enter(coroutine);
    585    }
    586    duration = g_test_timer_elapsed();
    587
    588    g_test_message("Yield %u iterations: %f s", maxcycles, duration);
    589}
    590
    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
    610    g_test_message("Function call %u iterations: %f s", maxcycles, duration);
    611}
    612
    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) {
    628        co = qemu_coroutine_create(perf_cost_func, &i);
    629        qemu_coroutine_enter(co);
    630        qemu_coroutine_enter(co);
    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,
    639                   (unsigned long)(1000000000.0 * duration / maxcycles));
    640}
    641
    642int main(int argc, char **argv)
    643{
    644    g_test_init(&argc, &argv, NULL);
    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) {
    651        g_test_add_func("/basic/no-dangling-access", test_no_dangling_access);
    652    }
    653
    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);
    658    g_test_add_func("/basic/entered", test_entered);
    659    g_test_add_func("/basic/in_coroutine", test_in_coroutine);
    660    g_test_add_func("/basic/order", test_order);
    661    g_test_add_func("/locking/co-mutex", test_co_mutex);
    662    g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable);
    663    g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade);
    664    g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade);
    665    if (g_test_perf()) {
    666        g_test_add_func("/perf/lifecycle", perf_lifecycle);
    667        g_test_add_func("/perf/nesting", perf_nesting);
    668        g_test_add_func("/perf/yield", perf_yield);
    669        g_test_add_func("/perf/function-call", perf_baseline);
    670        g_test_add_func("/perf/cost", perf_cost);
    671    }
    672    return g_test_run();
    673}