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(qemu_get_aio_context(), 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 can have two jobs in the same BDS */
 126    job[1] = do_test_id(blk[0], "id1", true);
 127    job_early_fail(&job[1]->job);
 128
 129    /* Duplicate job IDs are not allowed */
 130    job[1] = do_test_id(blk[1], "id0", false);
 131
 132    /* But once job[0] finishes we can reuse its ID */
 133    job_early_fail(&job[0]->job);
 134    job[1] = do_test_id(blk[1], "id0", true);
 135
 136    /* No job ID specified, defaults to the backend name ('drive1') */
 137    job_early_fail(&job[1]->job);
 138    job[1] = do_test_id(blk[1], NULL, true);
 139
 140    /* Duplicate job ID */
 141    job[2] = do_test_id(blk[2], "drive1", false);
 142
 143    /* The ID of job[2] would default to 'drive2' but it is already in use */
 144    job[0] = do_test_id(blk[0], "drive2", true);
 145    job[2] = do_test_id(blk[2], NULL, false);
 146
 147    /* This one is valid */
 148    job[2] = do_test_id(blk[2], "id_2", true);
 149
 150    job_early_fail(&job[0]->job);
 151    job_early_fail(&job[1]->job);
 152    job_early_fail(&job[2]->job);
 153
 154    destroy_blk(blk[0]);
 155    destroy_blk(blk[1]);
 156    destroy_blk(blk[2]);
 157}
 158
 159typedef struct CancelJob {
 160    BlockJob common;
 161    BlockBackend *blk;
 162    bool should_converge;
 163    bool should_complete;
 164} CancelJob;
 165
 166static void cancel_job_complete(Job *job, Error **errp)
 167{
 168    CancelJob *s = container_of(job, CancelJob, common.job);
 169    s->should_complete = true;
 170}
 171
 172static int coroutine_fn cancel_job_run(Job *job, Error **errp)
 173{
 174    CancelJob *s = container_of(job, CancelJob, common.job);
 175
 176    while (!s->should_complete) {
 177        if (job_is_cancelled(&s->common.job)) {
 178            return 0;
 179        }
 180
 181        if (!job_is_ready(&s->common.job) && s->should_converge) {
 182            job_transition_to_ready(&s->common.job);
 183        }
 184
 185        job_sleep_ns(&s->common.job, 100000);
 186    }
 187
 188    return 0;
 189}
 190
 191static const BlockJobDriver test_cancel_driver = {
 192    .job_driver = {
 193        .instance_size = sizeof(CancelJob),
 194        .free          = block_job_free,
 195        .user_resume   = block_job_user_resume,
 196        .drain         = block_job_drain,
 197        .run           = cancel_job_run,
 198        .complete      = cancel_job_complete,
 199    },
 200};
 201
 202static CancelJob *create_common(Job **pjob)
 203{
 204    BlockBackend *blk;
 205    Job *job;
 206    BlockJob *bjob;
 207    CancelJob *s;
 208
 209    blk = create_blk(NULL);
 210    bjob = mk_job(blk, "Steve", &test_cancel_driver, true,
 211                  JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
 212    job = &bjob->job;
 213    job_ref(job);
 214    assert(job->status == JOB_STATUS_CREATED);
 215    s = container_of(bjob, CancelJob, common);
 216    s->blk = blk;
 217
 218    *pjob = job;
 219    return s;
 220}
 221
 222static void cancel_common(CancelJob *s)
 223{
 224    BlockJob *job = &s->common;
 225    BlockBackend *blk = s->blk;
 226    JobStatus sts = job->job.status;
 227    AioContext *ctx;
 228
 229    ctx = job->job.aio_context;
 230    aio_context_acquire(ctx);
 231
 232    job_cancel_sync(&job->job);
 233    if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
 234        Job *dummy = &job->job;
 235        job_dismiss(&dummy, &error_abort);
 236    }
 237    assert(job->job.status == JOB_STATUS_NULL);
 238    job_unref(&job->job);
 239    destroy_blk(blk);
 240
 241    aio_context_release(ctx);
 242}
 243
 244static void test_cancel_created(void)
 245{
 246    Job *job;
 247    CancelJob *s;
 248
 249    s = create_common(&job);
 250    cancel_common(s);
 251}
 252
 253static void test_cancel_running(void)
 254{
 255    Job *job;
 256    CancelJob *s;
 257
 258    s = create_common(&job);
 259
 260    job_start(job);
 261    assert(job->status == JOB_STATUS_RUNNING);
 262
 263    cancel_common(s);
 264}
 265
 266static void test_cancel_paused(void)
 267{
 268    Job *job;
 269    CancelJob *s;
 270
 271    s = create_common(&job);
 272
 273    job_start(job);
 274    assert(job->status == JOB_STATUS_RUNNING);
 275
 276    job_user_pause(job, &error_abort);
 277    job_enter(job);
 278    assert(job->status == JOB_STATUS_PAUSED);
 279
 280    cancel_common(s);
 281}
 282
 283static void test_cancel_ready(void)
 284{
 285    Job *job;
 286    CancelJob *s;
 287
 288    s = create_common(&job);
 289
 290    job_start(job);
 291    assert(job->status == JOB_STATUS_RUNNING);
 292
 293    s->should_converge = true;
 294    job_enter(job);
 295    assert(job->status == JOB_STATUS_READY);
 296
 297    cancel_common(s);
 298}
 299
 300static void test_cancel_standby(void)
 301{
 302    Job *job;
 303    CancelJob *s;
 304
 305    s = create_common(&job);
 306
 307    job_start(job);
 308    assert(job->status == JOB_STATUS_RUNNING);
 309
 310    s->should_converge = true;
 311    job_enter(job);
 312    assert(job->status == JOB_STATUS_READY);
 313
 314    job_user_pause(job, &error_abort);
 315    job_enter(job);
 316    assert(job->status == JOB_STATUS_STANDBY);
 317
 318    cancel_common(s);
 319}
 320
 321static void test_cancel_pending(void)
 322{
 323    Job *job;
 324    CancelJob *s;
 325
 326    s = create_common(&job);
 327
 328    job_start(job);
 329    assert(job->status == JOB_STATUS_RUNNING);
 330
 331    s->should_converge = true;
 332    job_enter(job);
 333    assert(job->status == JOB_STATUS_READY);
 334
 335    job_complete(job, &error_abort);
 336    job_enter(job);
 337    while (!job->deferred_to_main_loop) {
 338        aio_poll(qemu_get_aio_context(), true);
 339    }
 340    assert(job->status == JOB_STATUS_READY);
 341    aio_poll(qemu_get_aio_context(), true);
 342    assert(job->status == JOB_STATUS_PENDING);
 343
 344    cancel_common(s);
 345}
 346
 347static void test_cancel_concluded(void)
 348{
 349    Job *job;
 350    CancelJob *s;
 351
 352    s = create_common(&job);
 353
 354    job_start(job);
 355    assert(job->status == JOB_STATUS_RUNNING);
 356
 357    s->should_converge = true;
 358    job_enter(job);
 359    assert(job->status == JOB_STATUS_READY);
 360
 361    job_complete(job, &error_abort);
 362    job_enter(job);
 363    while (!job->deferred_to_main_loop) {
 364        aio_poll(qemu_get_aio_context(), true);
 365    }
 366    assert(job->status == JOB_STATUS_READY);
 367    aio_poll(qemu_get_aio_context(), true);
 368    assert(job->status == JOB_STATUS_PENDING);
 369
 370    job_finalize(job, &error_abort);
 371    assert(job->status == JOB_STATUS_CONCLUDED);
 372
 373    cancel_common(s);
 374}
 375
 376int main(int argc, char **argv)
 377{
 378    qemu_init_main_loop(&error_abort);
 379    bdrv_init();
 380
 381    g_test_init(&argc, &argv, NULL);
 382    g_test_add_func("/blockjob/ids", test_job_ids);
 383    g_test_add_func("/blockjob/cancel/created", test_cancel_created);
 384    g_test_add_func("/blockjob/cancel/running", test_cancel_running);
 385    g_test_add_func("/blockjob/cancel/paused", test_cancel_paused);
 386    g_test_add_func("/blockjob/cancel/ready", test_cancel_ready);
 387    g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
 388    g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
 389    g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
 390    return g_test_run();
 391}
 392