26ba25
From 6dca7cc6632a4b4632786e8a087289dff1ad6ef8 Mon Sep 17 00:00:00 2001
26ba25
From: Kevin Wolf <kwolf@redhat.com>
26ba25
Date: Wed, 10 Oct 2018 20:08:41 +0100
26ba25
Subject: [PATCH 10/49] block: Really pause block jobs on drain
26ba25
26ba25
RH-Author: Kevin Wolf <kwolf@redhat.com>
26ba25
Message-id: <20181010200843.6710-8-kwolf@redhat.com>
26ba25
Patchwork-id: 82587
26ba25
O-Subject: [RHEL-8 qemu-kvm PATCH 07/44] block: Really pause block jobs on drain
26ba25
Bugzilla: 1637976
26ba25
RH-Acked-by: Max Reitz <mreitz@redhat.com>
26ba25
RH-Acked-by: John Snow <jsnow@redhat.com>
26ba25
RH-Acked-by: Thomas Huth <thuth@redhat.com>
26ba25
26ba25
We already requested that block jobs be paused in .bdrv_drained_begin,
26ba25
but no guarantee was made that the job was actually inactive at the
26ba25
point where bdrv_drained_begin() returned.
26ba25
26ba25
This introduces a new callback BdrvChildRole.bdrv_drained_poll() and
26ba25
uses it to make bdrv_drain_poll() consider block jobs using the node to
26ba25
be drained.
26ba25
26ba25
For the test case to work as expected, we have to switch from
26ba25
block_job_sleep_ns() to qemu_co_sleep_ns() so that the test job is even
26ba25
considered active and must be waited for when draining the node.
26ba25
26ba25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26ba25
(cherry picked from commit 89bd030533e3592ca0a995450dcfc5d53e459e20)
26ba25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26ba25
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
26ba25
---
26ba25
 block.c                      |  9 +++++++++
26ba25
 block/io.c                   | 40 ++++++++++++++++++++++++++++++++++------
26ba25
 block/mirror.c               |  8 ++++++++
26ba25
 blockjob.c                   | 23 +++++++++++++++++++++++
26ba25
 include/block/block.h        |  8 ++++++++
26ba25
 include/block/block_int.h    |  7 +++++++
26ba25
 include/block/blockjob_int.h |  8 ++++++++
26ba25
 tests/test-bdrv-drain.c      | 18 ++++++++++--------
26ba25
 8 files changed, 107 insertions(+), 14 deletions(-)
26ba25
26ba25
diff --git a/block.c b/block.c
26ba25
index 10a1ece..0d9698a 100644
26ba25
--- a/block.c
26ba25
+++ b/block.c
26ba25
@@ -821,6 +821,12 @@ static void bdrv_child_cb_drained_begin(BdrvChild *child)
26ba25
     bdrv_drained_begin(bs);
26ba25
 }
26ba25
 
26ba25
+static bool bdrv_child_cb_drained_poll(BdrvChild *child)
26ba25
+{
26ba25
+    BlockDriverState *bs = child->opaque;
26ba25
+    return bdrv_drain_poll(bs, NULL);
26ba25
+}
26ba25
+
26ba25
 static void bdrv_child_cb_drained_end(BdrvChild *child)
26ba25
 {
26ba25
     BlockDriverState *bs = child->opaque;
26ba25
@@ -905,6 +911,7 @@ const BdrvChildRole child_file = {
26ba25
     .get_parent_desc = bdrv_child_get_parent_desc,
26ba25
     .inherit_options = bdrv_inherited_options,
26ba25
     .drained_begin   = bdrv_child_cb_drained_begin,
26ba25
+    .drained_poll    = bdrv_child_cb_drained_poll,
26ba25
     .drained_end     = bdrv_child_cb_drained_end,
26ba25
     .attach          = bdrv_child_cb_attach,
26ba25
     .detach          = bdrv_child_cb_detach,
26ba25
@@ -929,6 +936,7 @@ const BdrvChildRole child_format = {
26ba25
     .get_parent_desc = bdrv_child_get_parent_desc,
26ba25
     .inherit_options = bdrv_inherited_fmt_options,
26ba25
     .drained_begin   = bdrv_child_cb_drained_begin,
26ba25
+    .drained_poll    = bdrv_child_cb_drained_poll,
26ba25
     .drained_end     = bdrv_child_cb_drained_end,
26ba25
     .attach          = bdrv_child_cb_attach,
26ba25
     .detach          = bdrv_child_cb_detach,
26ba25
@@ -1048,6 +1056,7 @@ const BdrvChildRole child_backing = {
26ba25
     .detach          = bdrv_backing_detach,
26ba25
     .inherit_options = bdrv_backing_options,
26ba25
     .drained_begin   = bdrv_child_cb_drained_begin,
26ba25
+    .drained_poll    = bdrv_child_cb_drained_poll,
26ba25
     .drained_end     = bdrv_child_cb_drained_end,
26ba25
     .inactivate      = bdrv_child_cb_inactivate,
26ba25
     .update_filename = bdrv_backing_update_filename,
26ba25
diff --git a/block/io.c b/block/io.c
26ba25
index 4d332c3..e260394 100644
26ba25
--- a/block/io.c
26ba25
+++ b/block/io.c
26ba25
@@ -69,6 +69,23 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
26ba25
     }
26ba25
 }
26ba25
 
26ba25
+static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore)
26ba25
+{
26ba25
+    BdrvChild *c, *next;
26ba25
+    bool busy = false;
26ba25
+
26ba25
+    QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
26ba25
+        if (c == ignore) {
26ba25
+            continue;
26ba25
+        }
26ba25
+        if (c->role->drained_poll) {
26ba25
+            busy |= c->role->drained_poll(c);
26ba25
+        }
26ba25
+    }
26ba25
+
26ba25
+    return busy;
26ba25
+}
26ba25
+
26ba25
 static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
26ba25
 {
26ba25
     dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer);
26ba25
@@ -182,21 +199,32 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
26ba25
 }
26ba25
 
26ba25
 /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
26ba25
-static bool bdrv_drain_poll(BlockDriverState *bs)
26ba25
+bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent)
26ba25
+{
26ba25
+    if (bdrv_parent_drained_poll(bs, ignore_parent)) {
26ba25
+        return true;
26ba25
+    }
26ba25
+
26ba25
+    return atomic_read(&bs->in_flight);
26ba25
+}
26ba25
+
26ba25
+static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
26ba25
+                                      BdrvChild *ignore_parent)
26ba25
 {
26ba25
     /* Execute pending BHs first and check everything else only after the BHs
26ba25
      * have executed. */
26ba25
     while (aio_poll(bs->aio_context, false));
26ba25
-    return atomic_read(&bs->in_flight);
26ba25
+
26ba25
+    return bdrv_drain_poll(bs, ignore_parent);
26ba25
 }
26ba25
 
26ba25
-static bool bdrv_drain_recurse(BlockDriverState *bs)
26ba25
+static bool bdrv_drain_recurse(BlockDriverState *bs, BdrvChild *parent)
26ba25
 {
26ba25
     BdrvChild *child, *tmp;
26ba25
     bool waited;
26ba25
 
26ba25
     /* Wait for drained requests to finish */
26ba25
-    waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll(bs));
26ba25
+    waited = BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent));
26ba25
 
26ba25
     QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
26ba25
         BlockDriverState *bs = child->bs;
26ba25
@@ -213,7 +241,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
26ba25
              */
26ba25
             bdrv_ref(bs);
26ba25
         }
26ba25
-        waited |= bdrv_drain_recurse(bs);
26ba25
+        waited |= bdrv_drain_recurse(bs, child);
26ba25
         if (in_main_loop) {
26ba25
             bdrv_unref(bs);
26ba25
         }
26ba25
@@ -289,7 +317,7 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
26ba25
 
26ba25
     bdrv_parent_drained_begin(bs, parent);
26ba25
     bdrv_drain_invoke(bs, true);
26ba25
-    bdrv_drain_recurse(bs);
26ba25
+    bdrv_drain_recurse(bs, parent);
26ba25
 
26ba25
     if (recursive) {
26ba25
         bs->recursive_quiesce_counter++;
26ba25
diff --git a/block/mirror.c b/block/mirror.c
26ba25
index 313e6e9..4b27f71 100644
26ba25
--- a/block/mirror.c
26ba25
+++ b/block/mirror.c
26ba25
@@ -976,6 +976,12 @@ static void mirror_pause(Job *job)
26ba25
     mirror_wait_for_all_io(s);
26ba25
 }
26ba25
 
26ba25
+static bool mirror_drained_poll(BlockJob *job)
26ba25
+{
26ba25
+    MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
26ba25
+    return !!s->in_flight;
26ba25
+}
26ba25
+
26ba25
 static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context)
26ba25
 {
26ba25
     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
26ba25
@@ -1011,6 +1017,7 @@ static const BlockJobDriver mirror_job_driver = {
26ba25
         .pause                  = mirror_pause,
26ba25
         .complete               = mirror_complete,
26ba25
     },
26ba25
+    .drained_poll           = mirror_drained_poll,
26ba25
     .attached_aio_context   = mirror_attached_aio_context,
26ba25
     .drain                  = mirror_drain,
26ba25
 };
26ba25
@@ -1028,6 +1035,7 @@ static const BlockJobDriver commit_active_job_driver = {
26ba25
         .pause                  = mirror_pause,
26ba25
         .complete               = mirror_complete,
26ba25
     },
26ba25
+    .drained_poll           = mirror_drained_poll,
26ba25
     .attached_aio_context   = mirror_attached_aio_context,
26ba25
     .drain                  = mirror_drain,
26ba25
 };
26ba25
diff --git a/blockjob.c b/blockjob.c
26ba25
index 0306533..be5903a 100644
26ba25
--- a/blockjob.c
26ba25
+++ b/blockjob.c
26ba25
@@ -155,6 +155,28 @@ static void child_job_drained_begin(BdrvChild *c)
26ba25
     job_pause(&job->job);
26ba25
 }
26ba25
 
26ba25
+static bool child_job_drained_poll(BdrvChild *c)
26ba25
+{
26ba25
+    BlockJob *bjob = c->opaque;
26ba25
+    Job *job = &bjob->job;
26ba25
+    const BlockJobDriver *drv = block_job_driver(bjob);
26ba25
+
26ba25
+    /* An inactive or completed job doesn't have any pending requests. Jobs
26ba25
+     * with !job->busy are either already paused or have a pause point after
26ba25
+     * being reentered, so no job driver code will run before they pause. */
26ba25
+    if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) {
26ba25
+        return false;
26ba25
+    }
26ba25
+
26ba25
+    /* Otherwise, assume that it isn't fully stopped yet, but allow the job to
26ba25
+     * override this assumption. */
26ba25
+    if (drv->drained_poll) {
26ba25
+        return drv->drained_poll(bjob);
26ba25
+    } else {
26ba25
+        return true;
26ba25
+    }
26ba25
+}
26ba25
+
26ba25
 static void child_job_drained_end(BdrvChild *c)
26ba25
 {
26ba25
     BlockJob *job = c->opaque;
26ba25
@@ -164,6 +186,7 @@ static void child_job_drained_end(BdrvChild *c)
26ba25
 static const BdrvChildRole child_job = {
26ba25
     .get_parent_desc    = child_job_get_parent_desc,
26ba25
     .drained_begin      = child_job_drained_begin,
26ba25
+    .drained_poll       = child_job_drained_poll,
26ba25
     .drained_end        = child_job_drained_end,
26ba25
     .stay_at_node       = true,
26ba25
 };
26ba25
diff --git a/include/block/block.h b/include/block/block.h
26ba25
index 8f87eea..8c91d4c 100644
26ba25
--- a/include/block/block.h
26ba25
+++ b/include/block/block.h
26ba25
@@ -596,6 +596,14 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
26ba25
 void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
26ba25
 
26ba25
 /**
26ba25
+ * bdrv_drain_poll:
26ba25
+ *
26ba25
+ * Poll for pending requests in @bs and its parents (except for
26ba25
+ * @ignore_parent). This is part of bdrv_drained_begin.
26ba25
+ */
26ba25
+bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent);
26ba25
+
26ba25
+/**
26ba25
  * bdrv_drained_begin:
26ba25
  *
26ba25
  * Begin a quiesced section for exclusive access to the BDS, by disabling
26ba25
diff --git a/include/block/block_int.h b/include/block/block_int.h
26ba25
index 341cbe8..beeacde 100644
26ba25
--- a/include/block/block_int.h
26ba25
+++ b/include/block/block_int.h
26ba25
@@ -610,6 +610,13 @@ struct BdrvChildRole {
26ba25
     void (*drained_begin)(BdrvChild *child);
26ba25
     void (*drained_end)(BdrvChild *child);
26ba25
 
26ba25
+    /*
26ba25
+     * Returns whether the parent has pending requests for the child. This
26ba25
+     * callback is polled after .drained_begin() has been called until all
26ba25
+     * activity on the child has stopped.
26ba25
+     */
26ba25
+    bool (*drained_poll)(BdrvChild *child);
26ba25
+
26ba25
     /* Notifies the parent that the child has been activated/inactivated (e.g.
26ba25
      * when migration is completing) and it can start/stop requesting
26ba25
      * permissions and doing I/O on it. */
26ba25
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
26ba25
index 5cd50c6..e4a318d 100644
26ba25
--- a/include/block/blockjob_int.h
26ba25
+++ b/include/block/blockjob_int.h
26ba25
@@ -39,6 +39,14 @@ struct BlockJobDriver {
26ba25
     JobDriver job_driver;
26ba25
 
26ba25
     /*
26ba25
+     * Returns whether the job has pending requests for the child or will
26ba25
+     * submit new requests before the next pause point. This callback is polled
26ba25
+     * in the context of draining a job node after requesting that the job be
26ba25
+     * paused, until all activity on the child has stopped.
26ba25
+     */
26ba25
+    bool (*drained_poll)(BlockJob *job);
26ba25
+
26ba25
+    /*
26ba25
      * If the callback is not NULL, it will be invoked before the job is
26ba25
      * resumed in a new AioContext.  This is the place to move any resources
26ba25
      * besides job->blk to the new AioContext.
26ba25
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
26ba25
index f5d85c9..49786ea 100644
26ba25
--- a/tests/test-bdrv-drain.c
26ba25
+++ b/tests/test-bdrv-drain.c
26ba25
@@ -681,7 +681,11 @@ static int coroutine_fn test_job_run(Job *job, Error **errp)
26ba25
 
26ba25
     job_transition_to_ready(&s->common.job);
26ba25
     while (!s->should_complete) {
26ba25
-        job_sleep_ns(&s->common.job, 100000);
26ba25
+        /* Avoid block_job_sleep_ns() because it marks the job as !busy. We
26ba25
+         * want to emulate some actual activity (probably some I/O) here so
26ba25
+         * that drain has to wait for this acitivity to stop. */
26ba25
+        qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
26ba25
+        job_pause_point(&s->common.job);
26ba25
     }
26ba25
 
26ba25
     return 0;
26ba25
@@ -728,7 +732,7 @@ static void test_blockjob_common(enum drain_type drain_type)
26ba25
 
26ba25
     g_assert_cmpint(job->job.pause_count, ==, 0);
26ba25
     g_assert_false(job->job.paused);
26ba25
-    g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
26ba25
+    g_assert_true(job->job.busy); /* We're in job_sleep_ns() */
26ba25
 
26ba25
     do_drain_begin(drain_type, src);
26ba25
 
26ba25
@@ -738,15 +742,14 @@ static void test_blockjob_common(enum drain_type drain_type)
26ba25
     } else {
26ba25
         g_assert_cmpint(job->job.pause_count, ==, 1);
26ba25
     }
26ba25
-    /* XXX We don't wait until the job is actually paused. Is this okay? */
26ba25
-    /* g_assert_true(job->job.paused); */
26ba25
+    g_assert_true(job->job.paused);
26ba25
     g_assert_false(job->job.busy); /* The job is paused */
26ba25
 
26ba25
     do_drain_end(drain_type, src);
26ba25
 
26ba25
     g_assert_cmpint(job->job.pause_count, ==, 0);
26ba25
     g_assert_false(job->job.paused);
26ba25
-    g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
26ba25
+    g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */
26ba25
 
26ba25
     do_drain_begin(drain_type, target);
26ba25
 
26ba25
@@ -756,15 +759,14 @@ static void test_blockjob_common(enum drain_type drain_type)
26ba25
     } else {
26ba25
         g_assert_cmpint(job->job.pause_count, ==, 1);
26ba25
     }
26ba25
-    /* XXX We don't wait until the job is actually paused. Is this okay? */
26ba25
-    /* g_assert_true(job->job.paused); */
26ba25
+    g_assert_true(job->job.paused);
26ba25
     g_assert_false(job->job.busy); /* The job is paused */
26ba25
 
26ba25
     do_drain_end(drain_type, target);
26ba25
 
26ba25
     g_assert_cmpint(job->job.pause_count, ==, 0);
26ba25
     g_assert_false(job->job.paused);
26ba25
-    g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
26ba25
+    g_assert_true(job->job.busy); /* We're in job_sleep_ns() */
26ba25
 
26ba25
     ret = job_complete_sync(&job->job, &error_abort);
26ba25
     g_assert_cmpint(ret, ==, 0);
26ba25
-- 
26ba25
1.8.3.1
26ba25