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