static void test_pair_jobs_fail_cancel_race(void) { BlockJob *job1; BlockJob *job2; BlockJobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; txn = block_job_txn_new(); job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); job2 = test_block_job_start(2, false, 0, &result2, txn); block_job_start(job1); block_job_start(job2); block_job_cancel(job1, false); /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. */ block_job_enter(job2); block_job_enter(job2); while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } g_assert_cmpint(result1, ==, -ECANCELED); g_assert_cmpint(result2, ==, -ECANCELED); block_job_txn_unref(txn); }
/* Create a block job that completes with a given return code after a given * number of event loop iterations. The return code is stored in the given * result pointer. * * The event loop iterations can either be handled automatically with a 0 delay * timer, or they can be stepped manually by entering the coroutine. */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, int rc, int *result) { BlockDriverState *bs; TestBlockJob *s; TestBlockJobCBData *data; static unsigned counter; char job_id[24]; data = g_new0(TestBlockJobCBData, 1); bs = bdrv_new(); snprintf(job_id, sizeof(job_id), "job%u", counter++); s = block_job_create(job_id, &test_block_job_driver, bs, 0, BLOCK_JOB_DEFAULT, test_block_job_cb, data, &error_abort); s->iterations = iterations; s->use_timer = use_timer; s->rc = rc; s->result = result; data->job = s; data->result = result; block_job_start(&s->common); return &s->common; }
static void test_pair_jobs(int expected1, int expected2) { BlockJob *job1; BlockJob *job2; BlockJobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; txn = block_job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1, txn); job2 = test_block_job_start(2, true, expected2, &result2, txn); block_job_start(job1); block_job_start(job2); /* Release our reference now to trigger as many nice * use-after-free bugs as possible. */ block_job_txn_unref(txn); if (expected1 == -ECANCELED) { block_job_cancel(job1, false); } if (expected2 == -ECANCELED) { block_job_cancel(job2, false); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } /* Failure or cancellation of one job cancels the other job */ if (expected1 != 0) { expected2 = -ECANCELED; } else if (expected2 != 0) { expected1 = -ECANCELED; } g_assert_cmpint(result1, ==, expected1); g_assert_cmpint(result2, ==, expected2); }
static void test_single_job(int expected) { BlockJob *job; BlockJobTxn *txn; int result = -EINPROGRESS; txn = block_job_txn_new(); job = test_block_job_start(1, true, expected, &result, txn); block_job_start(job); if (expected == -ECANCELED) { block_job_cancel(job, false); } while (result == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } g_assert_cmpint(result, ==, expected); block_job_txn_unref(txn); }
static void mirror_start_job(const char *job_id, BlockDriverState *bs, int creation_flags, BlockDriverState *target, const char *replaces, int64_t speed, uint32_t granularity, int64_t buf_size, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, BlockCompletionFunc *cb, void *opaque, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base, bool auto_complete, const char *filter_node_name, bool is_mirror, Error **errp) { MirrorBlockJob *s; BlockDriverState *mirror_top_bs; bool target_graph_mod; bool target_is_backing; Error *local_err = NULL; int ret; if (granularity == 0) { granularity = bdrv_get_default_bitmap_granularity(target); } assert(is_power_of_2(granularity)); if (buf_size < 0) { error_setg(errp, "Invalid parameter 'buf-size'"); return; } if (buf_size == 0) { buf_size = DEFAULT_MIRROR_BUF_SIZE; } /* In the case of active commit, add dummy driver to provide consistent * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, filter_node_name, BDRV_O_RDWR, errp); if (mirror_top_bs == NULL) { return; } if (!filter_node_name) { mirror_top_bs->implicit = true; } mirror_top_bs->total_sectors = bs->total_sectors; mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep * it alive until block_job_create() succeeds even if bs has no parent. */ bdrv_ref(mirror_top_bs); bdrv_drained_begin(bs); bdrv_append(mirror_top_bs, bs, &local_err); bdrv_drained_end(bs); if (local_err) { bdrv_unref(mirror_top_bs); error_propagate(errp, local_err); return; } /* Make sure that the source is not resized while the job is running */ s = block_job_create(job_id, driver, NULL, mirror_top_bs, BLK_PERM_CONSISTENT_READ, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed, creation_flags, cb, opaque, errp); if (!s) { goto fail; } /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); s->source = bs; s->mirror_top_bs = mirror_top_bs; /* No resize for the target either; while the mirror is still running, a * consistent read isn't necessarily possible. We could possibly allow * writes and graph modifications, though it would likely defeat the * purpose of a mirror, so leave them blocked for now. * * In the case of active commit, things look a bit different, though, * because the target is an already populated backing file in active use. * We can allow anything except resize there.*/ target_is_backing = bdrv_chain_contains(bs, target); target_graph_mod = (backing_mode != MIRROR_LEAVE_BACKING_CHAIN); s->target = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE | (target_graph_mod ? BLK_PERM_GRAPH_MOD : 0), BLK_PERM_WRITE_UNCHANGED | (target_is_backing ? BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD : 0)); ret = blk_insert_bs(s->target, target, errp); if (ret < 0) { goto fail; } if (is_mirror) { /* XXX: Mirror target could be a NBD server of target QEMU in the case * of non-shared block migration. To allow migration completion, we * have to allow "inactivate" of the target BB. When that happens, we * know the job is drained, and the vcpus are stopped, so no write * operation will be performed. Block layer already has assertions to * ensure that. */ blk_set_force_allow_inactivate(s->target); } s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; s->base = base; s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); s->unmap = unmap; if (auto_complete) { s->should_complete = true; } s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); if (!s->dirty_bitmap) { goto fail; } /* Required permissions are already taken with blk_new() */ block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL, &error_abort); /* In commit_active_start() all intermediate nodes disappear, so * any jobs in them must be blocked */ if (target_is_backing) { BlockDriverState *iter; for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) { /* XXX BLK_PERM_WRITE needs to be allowed so we don't block * ourselves at s->base (if writes are blocked for a node, they are * also blocked for its backing file). The other options would be a * second filter driver above s->base (== target). */ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, errp); if (ret < 0) { goto fail; } } } trace_mirror_start(bs, s, opaque); block_job_start(&s->common); return; fail: if (s) { /* Make sure this BDS does not go away until we have completed the graph * changes below */ bdrv_ref(mirror_top_bs); g_free(s->replaces); blk_unref(s->target); block_job_early_fail(&s->common); } bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, &error_abort); bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); bdrv_unref(mirror_top_bs); }