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} CancelJob;
 164
 165static void cancel_job_complete(Job *job, Error **errp)
 166{
 167    CancelJob *s = container_of(job, CancelJob, common.job);
 168    s->should_complete = true;
 169}
 170
 171static int coroutine_fn cancel_job_run(Job *job, Error **errp)
 172{
 173    CancelJob *s = container_of(job, CancelJob, common.job);
 174
 175    while (!s->should_complete) {
 176        if (job_is_cancelled(&s->common.job)) {
 177            return 0;
 178        }
 179
 180        if (!job_is_ready(&s->common.job) && s->should_converge) {
 181            job_transition_to_ready(&s->common.job);
 182        }
 183
 184        job_sleep_ns(&s->common.job, 100000);
 185    }
 186
 187    return 0;
 188}
 189
 190static const BlockJobDriver test_cancel_driver = {
 191    .job_driver = {
 192        .instance_size = sizeof(CancelJob),
 193        .free          = block_job_free,
 194        .user_resume   = block_job_user_resume,
 195        .drain         = block_job_drain,
 196        .run           = cancel_job_run,
 197        .complete      = cancel_job_complete,
 198    },
 199};
 200
 201static CancelJob *create_common(Job **pjob)
 202{
 203    BlockBackend *blk;
 204    Job *job;
 205    BlockJob *bjob;
 206    CancelJob *s;
 207
 208    blk = create_blk(NULL);
 209    bjob = mk_job(blk, "Steve", &test_cancel_driver, true,
 210                  JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
 211    job = &bjob->job;
 212    job_ref(job);
 213    assert(job->status == JOB_STATUS_CREATED);
 214    s = container_of(bjob, CancelJob, common);
 215    s->blk = blk;
 216
 217    *pjob = job;
 218    return s;
 219}
 220
 221static void cancel_common(CancelJob *s)
 222{
 223    BlockJob *job = &s->common;
 224    BlockBackend *blk = s->blk;
 225    JobStatus sts = job->job.status;
 226    AioContext *ctx;
 227
 228    ctx = job->job.aio_context;
 229    aio_context_acquire(ctx);
 230
 231    job_cancel_sync(&job->job);
 232    if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
 233        Job *dummy = &job->job;
 234        job_dismiss(&dummy, &error_abort);
 235    }
 236    assert(job->job.status == JOB_STATUS_NULL);
 237    job_unref(&job->job);
 238    destroy_blk(blk);
 239
 240    aio_context_release(ctx);
 241}
 242
 243static void test_cancel_created(void)
 244{
 245    Job *job;
 246    CancelJob *s;
 247
 248    s = create_common(&job);
 249    cancel_common(s);
 250}
 251
 252static void test_cancel_running(void)
 253{
 254    Job *job;
 255    CancelJob *s;
 256
 257    s = create_common(&job);
 258
 259    job_start(job);
 260    assert(job->status == JOB_STATUS_RUNNING);
 261
 262    cancel_common(s);
 263}
 264
 265static void test_cancel_paused(void)
 266{
 267    Job *job;
 268    CancelJob *s;
 269
 270    s = create_common(&job);
 271
 272    job_start(job);
 273    assert(job->status == JOB_STATUS_RUNNING);
 274
 275    job_user_pause(job, &error_abort);
 276    job_enter(job);
 277    assert(job->status == JOB_STATUS_PAUSED);
 278
 279    cancel_common(s);
 280}
 281
 282static void test_cancel_ready(void)
 283{
 284    Job *job;
 285    CancelJob *s;
 286
 287    s = create_common(&job);
 288
 289    job_start(job);
 290    assert(job->status == JOB_STATUS_RUNNING);
 291
 292    s->should_converge = true;
 293    job_enter(job);
 294    assert(job->status == JOB_STATUS_READY);
 295
 296    cancel_common(s);
 297}
 298
 299static void test_cancel_standby(void)
 300{
 301    Job *job;
 302    CancelJob *s;
 303
 304    s = create_common(&job);
 305
 306    job_start(job);
 307    assert(job->status == JOB_STATUS_RUNNING);
 308
 309    s->should_converge = true;
 310    job_enter(job);
 311    assert(job->status == JOB_STATUS_READY);
 312
 313    job_user_pause(job, &error_abort);
 314    job_enter(job);
 315    assert(job->status == JOB_STATUS_STANDBY);
 316
 317    cancel_common(s);
 318}
 319
 320static void test_cancel_pending(void)
 321{
 322    Job *job;
 323    CancelJob *s;
 324
 325    s = create_common(&job);
 326
 327    job_start(job);
 328    assert(job->status == JOB_STATUS_RUNNING);
 329
 330    s->should_converge = true;
 331    job_enter(job);
 332    assert(job->status == JOB_STATUS_READY);
 333
 334    job_complete(job, &error_abort);
 335    job_enter(job);
 336    while (!job->deferred_to_main_loop) {
 337        aio_poll(qemu_get_aio_context(), true);
 338    }
 339    assert(job->status == JOB_STATUS_READY);
 340    aio_poll(qemu_get_aio_context(), true);
 341    assert(job->status == JOB_STATUS_PENDING);
 342
 343    cancel_common(s);
 344}
 345
 346static void test_cancel_concluded(void)
 347{
 348    Job *job;
 349    CancelJob *s;
 350
 351    s = create_common(&job);
 352
 353    job_start(job);
 354    assert(job->status == JOB_STATUS_RUNNING);
 355
 356    s->should_converge = true;
 357    job_enter(job);
 358    assert(job->status == JOB_STATUS_READY);
 359
 360    job_complete(job, &error_abort);
 361    job_enter(job);
 362    while (!job->deferred_to_main_loop) {
 363        aio_poll(qemu_get_aio_context(), true);
 364    }
 365    assert(job->status == JOB_STATUS_READY);
 366    aio_poll(qemu_get_aio_context(), true);
 367    assert(job->status == JOB_STATUS_PENDING);
 368
 369    job_finalize(job, &error_abort);
 370    assert(job->status == JOB_STATUS_CONCLUDED);
 371
 372    cancel_common(s);
 373}
 374
 375int main(int argc, char **argv)
 376{
 377    qemu_init_main_loop(&error_abort);
 378    bdrv_init();
 379
 380    g_test_init(&argc, &argv, NULL);
 381    g_test_add_func("/blockjob/ids", test_job_ids);
 382    g_test_add_func("/blockjob/cancel/created", test_cancel_created);
 383    g_test_add_func("/blockjob/cancel/running", test_cancel_running);
 384    g_test_add_func("/blockjob/cancel/paused", test_cancel_paused);
 385    g_test_add_func("/blockjob/cancel/ready", test_cancel_ready);
 386    g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
 387    g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
 388    g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
 389    return g_test_run();
 390}
 391