Commit 89bd0305 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

block: Really pause block jobs on drain



We already requested that block jobs be paused in .bdrv_drained_begin,
but no guarantee was made that the job was actually inactive at the
point where bdrv_drained_begin() returned.

This introduces a new callback BdrvChildRole.bdrv_drained_poll() and
uses it to make bdrv_drain_poll() consider block jobs using the node to
be drained.

For the test case to work as expected, we have to switch from
block_job_sleep_ns() to qemu_co_sleep_ns() so that the test job is even
considered active and must be waited for when draining the node.

Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 1cc8e54a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -821,6 +821,12 @@ static void bdrv_child_cb_drained_begin(BdrvChild *child)
    bdrv_drained_begin(bs);
}

static bool bdrv_child_cb_drained_poll(BdrvChild *child)
{
    BlockDriverState *bs = child->opaque;
    return bdrv_drain_poll(bs, NULL);
}

static void bdrv_child_cb_drained_end(BdrvChild *child)
{
    BlockDriverState *bs = child->opaque;
@@ -905,6 +911,7 @@ const BdrvChildRole child_file = {
    .get_parent_desc = bdrv_child_get_parent_desc,
    .inherit_options = bdrv_inherited_options,
    .drained_begin   = bdrv_child_cb_drained_begin,
    .drained_poll    = bdrv_child_cb_drained_poll,
    .drained_end     = bdrv_child_cb_drained_end,
    .attach          = bdrv_child_cb_attach,
    .detach          = bdrv_child_cb_detach,
@@ -929,6 +936,7 @@ const BdrvChildRole child_format = {
    .get_parent_desc = bdrv_child_get_parent_desc,
    .inherit_options = bdrv_inherited_fmt_options,
    .drained_begin   = bdrv_child_cb_drained_begin,
    .drained_poll    = bdrv_child_cb_drained_poll,
    .drained_end     = bdrv_child_cb_drained_end,
    .attach          = bdrv_child_cb_attach,
    .detach          = bdrv_child_cb_detach,
@@ -1048,6 +1056,7 @@ const BdrvChildRole child_backing = {
    .detach          = bdrv_backing_detach,
    .inherit_options = bdrv_backing_options,
    .drained_begin   = bdrv_child_cb_drained_begin,
    .drained_poll    = bdrv_child_cb_drained_poll,
    .drained_end     = bdrv_child_cb_drained_end,
    .inactivate      = bdrv_child_cb_inactivate,
    .update_filename = bdrv_backing_update_filename,
+34 −6
Original line number Diff line number Diff line
@@ -69,6 +69,23 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
    }
}

static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore)
{
    BdrvChild *c, *next;
    bool busy = false;

    QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
        if (c == ignore) {
            continue;
        }
        if (c->role->drained_poll) {
            busy |= c->role->drained_poll(c);
        }
    }

    return busy;
}

static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
{
    dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer);
@@ -183,21 +200,32 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
}

/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
static bool bdrv_drain_poll(BlockDriverState *bs)
bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent)
{
    if (bdrv_parent_drained_poll(bs, ignore_parent)) {
        return true;
    }

    return atomic_read(&bs->in_flight);
}

static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
                                      BdrvChild *ignore_parent)
{
    /* Execute pending BHs first and check everything else only after the BHs
     * have executed. */
    while (aio_poll(bs->aio_context, false));
    return atomic_read(&bs->in_flight);

    return bdrv_drain_poll(bs, ignore_parent);
}

static bool bdrv_drain_recurse(BlockDriverState *bs)
static bool bdrv_drain_recurse(BlockDriverState *bs, BdrvChild *parent)
{
    BdrvChild *child, *tmp;
    bool waited;

    /* Wait for drained requests to finish */
    waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll(bs));
    waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent));

    QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
        BlockDriverState *bs = child->bs;
@@ -214,7 +242,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
             */
            bdrv_ref(bs);
        }
        waited |= bdrv_drain_recurse(bs);
        waited |= bdrv_drain_recurse(bs, child);
        if (in_main_loop) {
            bdrv_unref(bs);
        }
@@ -290,7 +318,7 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,

    bdrv_parent_drained_begin(bs, parent);
    bdrv_drain_invoke(bs, true);
    bdrv_drain_recurse(bs);
    bdrv_drain_recurse(bs, parent);

    if (recursive) {
        bs->recursive_quiesce_counter++;
+8 −0
Original line number Diff line number Diff line
@@ -964,6 +964,12 @@ static void mirror_pause(Job *job)
    mirror_wait_for_all_io(s);
}

static bool mirror_drained_poll(BlockJob *job)
{
    MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
    return !!s->in_flight;
}

static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context)
{
    MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
@@ -997,6 +1003,7 @@ static const BlockJobDriver mirror_job_driver = {
        .pause                  = mirror_pause,
        .complete               = mirror_complete,
    },
    .drained_poll           = mirror_drained_poll,
    .attached_aio_context   = mirror_attached_aio_context,
    .drain                  = mirror_drain,
};
@@ -1012,6 +1019,7 @@ static const BlockJobDriver commit_active_job_driver = {
        .pause                  = mirror_pause,
        .complete               = mirror_complete,
    },
    .drained_poll           = mirror_drained_poll,
    .attached_aio_context   = mirror_attached_aio_context,
    .drain                  = mirror_drain,
};
+23 −0
Original line number Diff line number Diff line
@@ -155,6 +155,28 @@ static void child_job_drained_begin(BdrvChild *c)
    job_pause(&job->job);
}

static bool child_job_drained_poll(BdrvChild *c)
{
    BlockJob *bjob = c->opaque;
    Job *job = &bjob->job;
    const BlockJobDriver *drv = block_job_driver(bjob);

    /* An inactive or completed job doesn't have any pending requests. Jobs
     * with !job->busy are either already paused or have a pause point after
     * being reentered, so no job driver code will run before they pause. */
    if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) {
        return false;
    }

    /* Otherwise, assume that it isn't fully stopped yet, but allow the job to
     * override this assumption. */
    if (drv->drained_poll) {
        return drv->drained_poll(bjob);
    } else {
        return true;
    }
}

static void child_job_drained_end(BdrvChild *c)
{
    BlockJob *job = c->opaque;
@@ -164,6 +186,7 @@ static void child_job_drained_end(BdrvChild *c)
static const BdrvChildRole child_job = {
    .get_parent_desc    = child_job_get_parent_desc,
    .drained_begin      = child_job_drained_begin,
    .drained_poll       = child_job_drained_poll,
    .drained_end        = child_job_drained_end,
    .stay_at_node       = true,
};
+8 −0
Original line number Diff line number Diff line
@@ -567,6 +567,14 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
 */
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);

/**
 * bdrv_drain_poll:
 *
 * Poll for pending requests in @bs and its parents (except for
 * @ignore_parent). This is part of bdrv_drained_begin.
 */
bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent);

/**
 * bdrv_drained_begin:
 *
Loading