tests: Add job commit by drained_end test
Signed-off-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
804db8ea00
commit
8e4428106a
|
@ -1527,6 +1527,122 @@ static void test_set_aio_context(void)
|
||||||
iothread_join(b);
|
iothread_join(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct TestDropBackingBlockJob {
|
||||||
|
BlockJob common;
|
||||||
|
bool should_complete;
|
||||||
|
bool *did_complete;
|
||||||
|
} TestDropBackingBlockJob;
|
||||||
|
|
||||||
|
static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp)
|
||||||
|
{
|
||||||
|
TestDropBackingBlockJob *s =
|
||||||
|
container_of(job, TestDropBackingBlockJob, common.job);
|
||||||
|
|
||||||
|
while (!s->should_complete) {
|
||||||
|
job_sleep_ns(job, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_drop_backing_job_commit(Job *job)
|
||||||
|
{
|
||||||
|
TestDropBackingBlockJob *s =
|
||||||
|
container_of(job, TestDropBackingBlockJob, common.job);
|
||||||
|
|
||||||
|
bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort);
|
||||||
|
|
||||||
|
*s->did_complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const BlockJobDriver test_drop_backing_job_driver = {
|
||||||
|
.job_driver = {
|
||||||
|
.instance_size = sizeof(TestDropBackingBlockJob),
|
||||||
|
.free = block_job_free,
|
||||||
|
.user_resume = block_job_user_resume,
|
||||||
|
.drain = block_job_drain,
|
||||||
|
.run = test_drop_backing_job_run,
|
||||||
|
.commit = test_drop_backing_job_commit,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a child node with three parent nodes on it, and then runs a
|
||||||
|
* block job on the final one, parent-node-2.
|
||||||
|
*
|
||||||
|
* (TODO: parent-node-0 currently serves no purpose, but will as of a
|
||||||
|
* follow-up patch.)
|
||||||
|
*
|
||||||
|
* The job is then asked to complete before a section where the child
|
||||||
|
* is drained.
|
||||||
|
*
|
||||||
|
* Ending this section will undrain the child's parents, first
|
||||||
|
* parent-node-2, then parent-node-1, then parent-node-0 -- the parent
|
||||||
|
* list is in reverse order of how they were added. Ending the drain
|
||||||
|
* on parent-node-2 will resume the job, thus completing it and
|
||||||
|
* scheduling job_exit().
|
||||||
|
*
|
||||||
|
* Ending the drain on parent-node-1 will poll the AioContext, which
|
||||||
|
* lets job_exit() and thus test_drop_backing_job_commit() run. That
|
||||||
|
* function removes the child as parent-node-2's backing file.
|
||||||
|
*
|
||||||
|
* In old (and buggy) implementations, there are two problems with
|
||||||
|
* that:
|
||||||
|
* (A) bdrv_drain_invoke() polls for every node that leaves the
|
||||||
|
* drained section. This means that job_exit() is scheduled
|
||||||
|
* before the child has left the drained section. Its
|
||||||
|
* quiesce_counter is therefore still 1 when it is removed from
|
||||||
|
* parent-node-2.
|
||||||
|
*
|
||||||
|
* (B) bdrv_replace_child_noperm() calls drained_end() on the old
|
||||||
|
* child's parents as many times as the child is quiesced. This
|
||||||
|
* means it will call drained_end() on parent-node-2 once.
|
||||||
|
* Because parent-node-2 is no longer quiesced at this point, this
|
||||||
|
* will fail.
|
||||||
|
*
|
||||||
|
* bdrv_replace_child_noperm() therefore must call drained_end() on
|
||||||
|
* the parent only if it really is still drained because the child is
|
||||||
|
* drained.
|
||||||
|
*/
|
||||||
|
static void test_blockjob_commit_by_drained_end(void)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs_child, *bs_parents[3];
|
||||||
|
TestDropBackingBlockJob *job;
|
||||||
|
bool job_has_completed = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bs_child = bdrv_new_open_driver(&bdrv_test, "child-node", BDRV_O_RDWR,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
char name[32];
|
||||||
|
snprintf(name, sizeof(name), "parent-node-%i", i);
|
||||||
|
bs_parents[i] = bdrv_new_open_driver(&bdrv_test, name, BDRV_O_RDWR,
|
||||||
|
&error_abort);
|
||||||
|
bdrv_set_backing_hd(bs_parents[i], bs_child, &error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
job = block_job_create("job", &test_drop_backing_job_driver, NULL,
|
||||||
|
bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
job->did_complete = &job_has_completed;
|
||||||
|
|
||||||
|
job_start(&job->common.job);
|
||||||
|
|
||||||
|
job->should_complete = true;
|
||||||
|
bdrv_drained_begin(bs_child);
|
||||||
|
g_assert(!job_has_completed);
|
||||||
|
bdrv_drained_end(bs_child);
|
||||||
|
g_assert(job_has_completed);
|
||||||
|
|
||||||
|
bdrv_unref(bs_parents[0]);
|
||||||
|
bdrv_unref(bs_parents[1]);
|
||||||
|
bdrv_unref(bs_parents[2]);
|
||||||
|
bdrv_unref(bs_child);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1610,6 +1726,9 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context);
|
g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context);
|
||||||
|
|
||||||
|
g_test_add_func("/bdrv-drain/blockjob/commit_by_drained_end",
|
||||||
|
test_blockjob_commit_by_drained_end);
|
||||||
|
|
||||||
ret = g_test_run();
|
ret = g_test_run();
|
||||||
qemu_event_destroy(&done_event);
|
qemu_event_destroy(&done_event);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue