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-blockjob-txn.c (6761B)


      1/*
      2 * Blockjob transactions tests
      3 *
      4 * Copyright Red Hat, Inc. 2015
      5 *
      6 * Authors:
      7 *  Stefan Hajnoczi    <stefanha@redhat.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#include "qemu/osdep.h"
     14#include "qapi/error.h"
     15#include "qemu/main-loop.h"
     16#include "block/blockjob_int.h"
     17#include "sysemu/block-backend.h"
     18#include "qapi/qmp/qdict.h"
     19
     20typedef struct {
     21    BlockJob common;
     22    unsigned int iterations;
     23    bool use_timer;
     24    int rc;
     25    int *result;
     26} TestBlockJob;
     27
     28static void test_block_job_clean(Job *job)
     29{
     30    BlockJob *bjob = container_of(job, BlockJob, job);
     31    BlockDriverState *bs = blk_bs(bjob->blk);
     32
     33    bdrv_unref(bs);
     34}
     35
     36static int coroutine_fn test_block_job_run(Job *job, Error **errp)
     37{
     38    TestBlockJob *s = container_of(job, TestBlockJob, common.job);
     39
     40    while (s->iterations--) {
     41        if (s->use_timer) {
     42            job_sleep_ns(job, 0);
     43        } else {
     44            job_yield(job);
     45        }
     46
     47        if (job_is_cancelled(job)) {
     48            break;
     49        }
     50    }
     51
     52    return s->rc;
     53}
     54
     55typedef struct {
     56    TestBlockJob *job;
     57    int *result;
     58} TestBlockJobCBData;
     59
     60static void test_block_job_cb(void *opaque, int ret)
     61{
     62    TestBlockJobCBData *data = opaque;
     63    if (!ret && job_is_cancelled(&data->job->common.job)) {
     64        ret = -ECANCELED;
     65    }
     66    *data->result = ret;
     67    g_free(data);
     68}
     69
     70static const BlockJobDriver test_block_job_driver = {
     71    .job_driver = {
     72        .instance_size = sizeof(TestBlockJob),
     73        .free          = block_job_free,
     74        .user_resume   = block_job_user_resume,
     75        .run           = test_block_job_run,
     76        .clean         = test_block_job_clean,
     77    },
     78};
     79
     80/* Create a block job that completes with a given return code after a given
     81 * number of event loop iterations.  The return code is stored in the given
     82 * result pointer.
     83 *
     84 * The event loop iterations can either be handled automatically with a 0 delay
     85 * timer, or they can be stepped manually by entering the coroutine.
     86 */
     87static BlockJob *test_block_job_start(unsigned int iterations,
     88                                      bool use_timer,
     89                                      int rc, int *result, JobTxn *txn)
     90{
     91    BlockDriverState *bs;
     92    TestBlockJob *s;
     93    TestBlockJobCBData *data;
     94    static unsigned counter;
     95    char job_id[24];
     96
     97    data = g_new0(TestBlockJobCBData, 1);
     98
     99    QDict *opt = qdict_new();
    100    qdict_put_str(opt, "file.read-zeroes", "on");
    101    bs = bdrv_open("null-co://", NULL, opt, 0, &error_abort);
    102    g_assert_nonnull(bs);
    103
    104    snprintf(job_id, sizeof(job_id), "job%u", counter++);
    105    s = block_job_create(job_id, &test_block_job_driver, txn, bs,
    106                         0, BLK_PERM_ALL, 0, JOB_DEFAULT,
    107                         test_block_job_cb, data, &error_abort);
    108    s->iterations = iterations;
    109    s->use_timer = use_timer;
    110    s->rc = rc;
    111    s->result = result;
    112    data->job = s;
    113    data->result = result;
    114    return &s->common;
    115}
    116
    117static void test_single_job(int expected)
    118{
    119    BlockJob *job;
    120    JobTxn *txn;
    121    int result = -EINPROGRESS;
    122
    123    txn = job_txn_new();
    124    job = test_block_job_start(1, true, expected, &result, txn);
    125    job_start(&job->job);
    126
    127    if (expected == -ECANCELED) {
    128        job_cancel(&job->job, false);
    129    }
    130
    131    while (result == -EINPROGRESS) {
    132        aio_poll(qemu_get_aio_context(), true);
    133    }
    134    g_assert_cmpint(result, ==, expected);
    135
    136    job_txn_unref(txn);
    137}
    138
    139static void test_single_job_success(void)
    140{
    141    test_single_job(0);
    142}
    143
    144static void test_single_job_failure(void)
    145{
    146    test_single_job(-EIO);
    147}
    148
    149static void test_single_job_cancel(void)
    150{
    151    test_single_job(-ECANCELED);
    152}
    153
    154static void test_pair_jobs(int expected1, int expected2)
    155{
    156    BlockJob *job1;
    157    BlockJob *job2;
    158    JobTxn *txn;
    159    int result1 = -EINPROGRESS;
    160    int result2 = -EINPROGRESS;
    161
    162    txn = job_txn_new();
    163    job1 = test_block_job_start(1, true, expected1, &result1, txn);
    164    job2 = test_block_job_start(2, true, expected2, &result2, txn);
    165    job_start(&job1->job);
    166    job_start(&job2->job);
    167
    168    /* Release our reference now to trigger as many nice
    169     * use-after-free bugs as possible.
    170     */
    171    job_txn_unref(txn);
    172
    173    if (expected1 == -ECANCELED) {
    174        job_cancel(&job1->job, false);
    175    }
    176    if (expected2 == -ECANCELED) {
    177        job_cancel(&job2->job, false);
    178    }
    179
    180    while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
    181        aio_poll(qemu_get_aio_context(), true);
    182    }
    183
    184    /* Failure or cancellation of one job cancels the other job */
    185    if (expected1 != 0) {
    186        expected2 = -ECANCELED;
    187    } else if (expected2 != 0) {
    188        expected1 = -ECANCELED;
    189    }
    190
    191    g_assert_cmpint(result1, ==, expected1);
    192    g_assert_cmpint(result2, ==, expected2);
    193}
    194
    195static void test_pair_jobs_success(void)
    196{
    197    test_pair_jobs(0, 0);
    198}
    199
    200static void test_pair_jobs_failure(void)
    201{
    202    /* Test both orderings.  The two jobs run for a different number of
    203     * iterations so the code path is different depending on which job fails
    204     * first.
    205     */
    206    test_pair_jobs(-EIO, 0);
    207    test_pair_jobs(0, -EIO);
    208}
    209
    210static void test_pair_jobs_cancel(void)
    211{
    212    test_pair_jobs(-ECANCELED, 0);
    213    test_pair_jobs(0, -ECANCELED);
    214}
    215
    216static void test_pair_jobs_fail_cancel_race(void)
    217{
    218    BlockJob *job1;
    219    BlockJob *job2;
    220    JobTxn *txn;
    221    int result1 = -EINPROGRESS;
    222    int result2 = -EINPROGRESS;
    223
    224    txn = job_txn_new();
    225    job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
    226    job2 = test_block_job_start(2, false, 0, &result2, txn);
    227    job_start(&job1->job);
    228    job_start(&job2->job);
    229
    230    job_cancel(&job1->job, false);
    231
    232    /* Now make job2 finish before the main loop kicks jobs.  This simulates
    233     * the race between a pending kick and another job completing.
    234     */
    235    job_enter(&job2->job);
    236    job_enter(&job2->job);
    237
    238    while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
    239        aio_poll(qemu_get_aio_context(), true);
    240    }
    241
    242    g_assert_cmpint(result1, ==, -ECANCELED);
    243    g_assert_cmpint(result2, ==, -ECANCELED);
    244
    245    job_txn_unref(txn);
    246}
    247
    248int main(int argc, char **argv)
    249{
    250    qemu_init_main_loop(&error_abort);
    251    bdrv_init();
    252
    253    g_test_init(&argc, &argv, NULL);
    254    g_test_add_func("/single/success", test_single_job_success);
    255    g_test_add_func("/single/failure", test_single_job_failure);
    256    g_test_add_func("/single/cancel", test_single_job_cancel);
    257    g_test_add_func("/pair/success", test_pair_jobs_success);
    258    g_test_add_func("/pair/failure", test_pair_jobs_failure);
    259    g_test_add_func("/pair/cancel", test_pair_jobs_cancel);
    260    g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race);
    261    return g_test_run();
    262}