Blame SOURCES/kvm-block-Really-pause-block-jobs-on-drain.patch

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