qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

test-blockjob-txn.c (6773B)


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