qemu/tests/test-blockjob.c
<<
>>
Prefs
   1/*
   2 * Blockjob tests
   3 *
   4 * Copyright Igalia, S.L. 2016
   5 *
   6 * Authors:
   7 *  Alberto Garcia   <berto@igalia.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
  19static const BlockJobDriver test_block_job_driver = {
  20    .instance_size = sizeof(BlockJob),
  21};
  22
  23static void block_job_cb(void *opaque, int ret)
  24{
  25}
  26
  27static BlockJob *mk_job(BlockBackend *blk, const char *id,
  28                        const BlockJobDriver *drv, bool should_succeed,
  29                        int flags)
  30{
  31    BlockJob *job;
  32    Error *errp = NULL;
  33
  34    job = block_job_create(id, drv, NULL, blk_bs(blk),
  35                           0, BLK_PERM_ALL, 0, flags, block_job_cb,
  36                           NULL, &errp);
  37    if (should_succeed) {
  38        g_assert_null(errp);
  39        g_assert_nonnull(job);
  40        if (id) {
  41            g_assert_cmpstr(job->id, ==, id);
  42        } else {
  43            g_assert_cmpstr(job->id, ==, blk_name(blk));
  44        }
  45    } else {
  46        g_assert_nonnull(errp);
  47        g_assert_null(job);
  48        error_free(errp);
  49    }
  50
  51    return job;
  52}
  53
  54static BlockJob *do_test_id(BlockBackend *blk, const char *id,
  55                            bool should_succeed)
  56{
  57    return mk_job(blk, id, &test_block_job_driver,
  58                  should_succeed, BLOCK_JOB_DEFAULT);
  59}
  60
  61/* This creates a BlockBackend (optionally with a name) with a
  62 * BlockDriverState inserted. */
  63static BlockBackend *create_blk(const char *name)
  64{
  65    /* No I/O is performed on this device */
  66    BlockBackend *blk = blk_new(0, BLK_PERM_ALL);
  67    BlockDriverState *bs;
  68
  69    bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
  70    g_assert_nonnull(bs);
  71
  72    blk_insert_bs(blk, bs, &error_abort);
  73    bdrv_unref(bs);
  74
  75    if (name) {
  76        Error *errp = NULL;
  77        monitor_add_blk(blk, name, &errp);
  78        g_assert_null(errp);
  79    }
  80
  81    return blk;
  82}
  83
  84/* This destroys the backend */
  85static void destroy_blk(BlockBackend *blk)
  86{
  87    if (blk_name(blk)[0] != '\0') {
  88        monitor_remove_blk(blk);
  89    }
  90
  91    blk_remove_bs(blk);
  92    blk_unref(blk);
  93}
  94
  95static void test_job_ids(void)
  96{
  97    BlockBackend *blk[3];
  98    BlockJob *job[3];
  99
 100    blk[0] = create_blk(NULL);
 101    blk[1] = create_blk("drive1");
 102    blk[2] = create_blk("drive2");
 103
 104    /* No job ID provided and the block backend has no name */
 105    job[0] = do_test_id(blk[0], NULL, false);
 106
 107    /* These are all invalid job IDs */
 108    job[0] = do_test_id(blk[0], "0id", false);
 109    job[0] = do_test_id(blk[0], "",    false);
 110    job[0] = do_test_id(blk[0], "   ", false);
 111    job[0] = do_test_id(blk[0], "123", false);
 112    job[0] = do_test_id(blk[0], "_id", false);
 113    job[0] = do_test_id(blk[0], "-id", false);
 114    job[0] = do_test_id(blk[0], ".id", false);
 115    job[0] = do_test_id(blk[0], "#id", false);
 116
 117    /* This one is valid */
 118    job[0] = do_test_id(blk[0], "id0", true);
 119
 120    /* We cannot have two jobs in the same BDS */
 121    do_test_id(blk[0], "id1", false);
 122
 123    /* Duplicate job IDs are not allowed */
 124    job[1] = do_test_id(blk[1], "id0", false);
 125
 126    /* But once job[0] finishes we can reuse its ID */
 127    block_job_early_fail(job[0]);
 128    job[1] = do_test_id(blk[1], "id0", true);
 129
 130    /* No job ID specified, defaults to the backend name ('drive1') */
 131    block_job_early_fail(job[1]);
 132    job[1] = do_test_id(blk[1], NULL, true);
 133
 134    /* Duplicate job ID */
 135    job[2] = do_test_id(blk[2], "drive1", false);
 136
 137    /* The ID of job[2] would default to 'drive2' but it is already in use */
 138    job[0] = do_test_id(blk[0], "drive2", true);
 139    job[2] = do_test_id(blk[2], NULL, false);
 140
 141    /* This one is valid */
 142    job[2] = do_test_id(blk[2], "id_2", true);
 143
 144    block_job_early_fail(job[0]);
 145    block_job_early_fail(job[1]);
 146    block_job_early_fail(job[2]);
 147
 148    destroy_blk(blk[0]);
 149    destroy_blk(blk[1]);
 150    destroy_blk(blk[2]);
 151}
 152
 153typedef struct CancelJob {
 154    BlockJob common;
 155    BlockBackend *blk;
 156    bool should_converge;
 157    bool should_complete;
 158    bool completed;
 159} CancelJob;
 160
 161static void cancel_job_completed(BlockJob *job, void *opaque)
 162{
 163    CancelJob *s = opaque;
 164    s->completed = true;
 165    block_job_completed(job, 0);
 166}
 167
 168static void cancel_job_complete(BlockJob *job, Error **errp)
 169{
 170    CancelJob *s = container_of(job, CancelJob, common);
 171    s->should_complete = true;
 172}
 173
 174static void coroutine_fn cancel_job_start(void *opaque)
 175{
 176    CancelJob *s = opaque;
 177
 178    while (!s->should_complete) {
 179        if (block_job_is_cancelled(&s->common)) {
 180            goto defer;
 181        }
 182
 183        if (!s->common.ready && s->should_converge) {
 184            block_job_event_ready(&s->common);
 185        }
 186
 187        block_job_sleep_ns(&s->common, 100000);
 188    }
 189
 190 defer:
 191    block_job_defer_to_main_loop(&s->common, cancel_job_completed, s);
 192}
 193
 194static const BlockJobDriver test_cancel_driver = {
 195    .instance_size = sizeof(CancelJob),
 196    .start         = cancel_job_start,
 197    .complete      = cancel_job_complete,
 198};
 199
 200static CancelJob *create_common(BlockJob **pjob)
 201{
 202    BlockBackend *blk;
 203    BlockJob *job;
 204    CancelJob *s;
 205
 206    blk = create_blk(NULL);
 207    job = mk_job(blk, "Steve", &test_cancel_driver, true,
 208                 BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
 209    block_job_ref(job);
 210    assert(job->status == BLOCK_JOB_STATUS_CREATED);
 211    s = container_of(job, CancelJob, common);
 212    s->blk = blk;
 213
 214    *pjob = job;
 215    return s;
 216}
 217
 218static void cancel_common(CancelJob *s)
 219{
 220    BlockJob *job = &s->common;
 221    BlockBackend *blk = s->blk;
 222    BlockJobStatus sts = job->status;
 223
 224    block_job_cancel_sync(job);
 225    if ((sts != BLOCK_JOB_STATUS_CREATED) &&
 226        (sts != BLOCK_JOB_STATUS_CONCLUDED)) {
 227        BlockJob *dummy = job;
 228        block_job_dismiss(&dummy, &error_abort);
 229    }
 230    assert(job->status == BLOCK_JOB_STATUS_NULL);
 231    block_job_unref(job);
 232    destroy_blk(blk);
 233}
 234
 235static void test_cancel_created(void)
 236{
 237    BlockJob *job;
 238    CancelJob *s;
 239
 240    s = create_common(&job);
 241    cancel_common(s);
 242}
 243
 244static void test_cancel_running(void)
 245{
 246    BlockJob *job;
 247    CancelJob *s;
 248
 249    s = create_common(&job);
 250
 251    block_job_start(job);
 252    assert(job->status == BLOCK_JOB_STATUS_RUNNING);
 253
 254    cancel_common(s);
 255}
 256
 257static void test_cancel_paused(void)
 258{
 259    BlockJob *job;
 260    CancelJob *s;
 261
 262    s = create_common(&job);
 263
 264    block_job_start(job);
 265    assert(job->status == BLOCK_JOB_STATUS_RUNNING);
 266
 267    block_job_user_pause(job, &error_abort);
 268    block_job_enter(job);
 269    assert(job->status == BLOCK_JOB_STATUS_PAUSED);
 270
 271    cancel_common(s);
 272}
 273
 274static void test_cancel_ready(void)
 275{
 276    BlockJob *job;
 277    CancelJob *s;
 278
 279    s = create_common(&job);
 280
 281    block_job_start(job);
 282    assert(job->status == BLOCK_JOB_STATUS_RUNNING);
 283
 284    s->should_converge = true;
 285    block_job_enter(job);
 286    assert(job->status == BLOCK_JOB_STATUS_READY);
 287
 288    cancel_common(s);
 289}
 290
 291static void test_cancel_standby(void)
 292{
 293    BlockJob *job;
 294    CancelJob *s;
 295
 296    s = create_common(&job);
 297
 298    block_job_start(job);
 299    assert(job->status == BLOCK_JOB_STATUS_RUNNING);
 300
 301    s->should_converge = true;
 302    block_job_enter(job);
 303    assert(job->status == BLOCK_JOB_STATUS_READY);
 304
 305    block_job_user_pause(job, &error_abort);
 306    block_job_enter(job);
 307    assert(job->status == BLOCK_JOB_STATUS_STANDBY);
 308
 309    cancel_common(s);
 310}
 311
 312static void test_cancel_pending(void)
 313{
 314    BlockJob *job;
 315    CancelJob *s;
 316
 317    s = create_common(&job);
 318
 319    block_job_start(job);
 320    assert(job->status == BLOCK_JOB_STATUS_RUNNING);
 321
 322    s->should_converge = true;
 323    block_job_enter(job);
 324    assert(job->status == BLOCK_JOB_STATUS_READY);
 325
 326    block_job_complete(job, &error_abort);
 327    block_job_enter(job);
 328    while (!s->completed) {
 329        aio_poll(qemu_get_aio_context(), true);
 330    }
 331    assert(job->status == BLOCK_JOB_STATUS_PENDING);
 332
 333    cancel_common(s);
 334}
 335
 336static void test_cancel_concluded(void)
 337{
 338    BlockJob *job;
 339    CancelJob *s;
 340
 341    s = create_common(&job);
 342
 343    block_job_start(job);
 344    assert(job->status == BLOCK_JOB_STATUS_RUNNING);
 345
 346    s->should_converge = true;
 347    block_job_enter(job);
 348    assert(job->status == BLOCK_JOB_STATUS_READY);
 349
 350    block_job_complete(job, &error_abort);
 351    block_job_enter(job);
 352    while (!s->completed) {
 353        aio_poll(qemu_get_aio_context(), true);
 354    }
 355    assert(job->status == BLOCK_JOB_STATUS_PENDING);
 356
 357    block_job_finalize(job, &error_abort);
 358    assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
 359
 360    cancel_common(s);
 361}
 362
 363int main(int argc, char **argv)
 364{
 365    qemu_init_main_loop(&error_abort);
 366    bdrv_init();
 367
 368    g_test_init(&argc, &argv, NULL);
 369    g_test_add_func("/blockjob/ids", test_job_ids);
 370    g_test_add_func("/blockjob/cancel/created", test_cancel_created);
 371    g_test_add_func("/blockjob/cancel/running", test_cancel_running);
 372    g_test_add_func("/blockjob/cancel/paused", test_cancel_paused);
 373    g_test_add_func("/blockjob/cancel/ready", test_cancel_ready);
 374    g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
 375    g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
 376    g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
 377    return g_test_run();
 378}
 379