ae23c9
From c48802abf2f0912ce3c34775587f674b037939ac Mon Sep 17 00:00:00 2001
ae23c9
From: Kevin Wolf <kwolf@redhat.com>
ae23c9
Date: Wed, 10 Oct 2018 20:21:56 +0100
ae23c9
Subject: [PATCH 30/49] blockjob: Wake up BDS when job becomes idle
ae23c9
ae23c9
RH-Author: Kevin Wolf <kwolf@redhat.com>
ae23c9
Message-id: <20181010202213.7372-18-kwolf@redhat.com>
ae23c9
Patchwork-id: 82610
ae23c9
O-Subject: [RHEL-8 qemu-kvm PATCH 27/44] blockjob: Wake up BDS when job becomes idle
ae23c9
Bugzilla: 1637976
ae23c9
RH-Acked-by: Max Reitz <mreitz@redhat.com>
ae23c9
RH-Acked-by: John Snow <jsnow@redhat.com>
ae23c9
RH-Acked-by: Thomas Huth <thuth@redhat.com>
ae23c9
ae23c9
In the context of draining a BDS, the .drained_poll callback of block
ae23c9
jobs is called. If this returns true (i.e. there is still some activity
ae23c9
pending), the drain operation may call aio_poll() with blocking=true to
ae23c9
wait for completion.
ae23c9
ae23c9
As soon as the pending activity is completed and the job finally arrives
ae23c9
in a quiescent state (i.e. its coroutine either yields with busy=false
ae23c9
or terminates), the block job must notify the aio_poll() loop to wake
ae23c9
up, otherwise we get a deadlock if both are running in different
ae23c9
threads.
ae23c9
ae23c9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
ae23c9
Reviewed-by: Fam Zheng <famz@redhat.com>
ae23c9
Reviewed-by: Max Reitz <mreitz@redhat.com>
ae23c9
(cherry picked from commit 34dc97b9a0e592bc466bdb0bbfe45d77304a72b6)
ae23c9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
ae23c9
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
ae23c9
---
ae23c9
 blockjob.c               | 18 ++++++++++++++++++
ae23c9
 include/block/blockjob.h | 13 +++++++++++++
ae23c9
 include/qemu/job.h       |  3 +++
ae23c9
 job.c                    |  7 +++++++
ae23c9
 4 files changed, 41 insertions(+)
ae23c9
ae23c9
diff --git a/blockjob.c b/blockjob.c
ae23c9
index be5903a..8d27e8e 100644
ae23c9
--- a/blockjob.c
ae23c9
+++ b/blockjob.c
ae23c9
@@ -221,6 +221,22 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
ae23c9
     return 0;
ae23c9
 }
ae23c9
 
ae23c9
+void block_job_wakeup_all_bdrv(BlockJob *job)
ae23c9
+{
ae23c9
+    GSList *l;
ae23c9
+
ae23c9
+    for (l = job->nodes; l; l = l->next) {
ae23c9
+        BdrvChild *c = l->data;
ae23c9
+        bdrv_wakeup(c->bs);
ae23c9
+    }
ae23c9
+}
ae23c9
+
ae23c9
+static void block_job_on_idle(Notifier *n, void *opaque)
ae23c9
+{
ae23c9
+    BlockJob *job = opaque;
ae23c9
+    block_job_wakeup_all_bdrv(job);
ae23c9
+}
ae23c9
+
ae23c9
 bool block_job_is_internal(BlockJob *job)
ae23c9
 {
ae23c9
     return (job->job.id == NULL);
ae23c9
@@ -419,6 +435,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
ae23c9
     job->finalize_completed_notifier.notify = block_job_event_completed;
ae23c9
     job->pending_notifier.notify = block_job_event_pending;
ae23c9
     job->ready_notifier.notify = block_job_event_ready;
ae23c9
+    job->idle_notifier.notify = block_job_on_idle;
ae23c9
 
ae23c9
     notifier_list_add(&job->job.on_finalize_cancelled,
ae23c9
                       &job->finalize_cancelled_notifier);
ae23c9
@@ -426,6 +443,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
ae23c9
                       &job->finalize_completed_notifier);
ae23c9
     notifier_list_add(&job->job.on_pending, &job->pending_notifier);
ae23c9
     notifier_list_add(&job->job.on_ready, &job->ready_notifier);
ae23c9
+    notifier_list_add(&job->job.on_idle, &job->idle_notifier);
ae23c9
 
ae23c9
     error_setg(&job->blocker, "block device is in use by block job: %s",
ae23c9
                job_type_str(&job->job));
ae23c9
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
ae23c9
index 32c00b7..2290bbb 100644
ae23c9
--- a/include/block/blockjob.h
ae23c9
+++ b/include/block/blockjob.h
ae23c9
@@ -70,6 +70,9 @@ typedef struct BlockJob {
ae23c9
     /** Called when the job transitions to READY */
ae23c9
     Notifier ready_notifier;
ae23c9
 
ae23c9
+    /** Called when the job coroutine yields or terminates */
ae23c9
+    Notifier idle_notifier;
ae23c9
+
ae23c9
     /** BlockDriverStates that are involved in this block job */
ae23c9
     GSList *nodes;
ae23c9
 } BlockJob;
ae23c9
@@ -119,6 +122,16 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
ae23c9
 void block_job_remove_all_bdrv(BlockJob *job);
ae23c9
 
ae23c9
 /**
ae23c9
+ * block_job_wakeup_all_bdrv:
ae23c9
+ * @job: The block job
ae23c9
+ *
ae23c9
+ * Calls bdrv_wakeup() for all BlockDriverStates that have been added to the
ae23c9
+ * job. This function is to be called whenever child_job_drained_poll() would
ae23c9
+ * go from true to false to notify waiting drain requests.
ae23c9
+ */
ae23c9
+void block_job_wakeup_all_bdrv(BlockJob *job);
ae23c9
+
ae23c9
+/**
ae23c9
  * block_job_set_speed:
ae23c9
  * @job: The job to set the speed for.
ae23c9
  * @speed: The new value
ae23c9
diff --git a/include/qemu/job.h b/include/qemu/job.h
ae23c9
index fdaa06f..407d549 100644
ae23c9
--- a/include/qemu/job.h
ae23c9
+++ b/include/qemu/job.h
ae23c9
@@ -156,6 +156,9 @@ typedef struct Job {
ae23c9
     /** Notifiers called when the job transitions to READY */
ae23c9
     NotifierList on_ready;
ae23c9
 
ae23c9
+    /** Notifiers called when the job coroutine yields or terminates */
ae23c9
+    NotifierList on_idle;
ae23c9
+
ae23c9
     /** Element of the list of jobs */
ae23c9
     QLIST_ENTRY(Job) job_list;
ae23c9
 
ae23c9
diff --git a/job.c b/job.c
ae23c9
index db53163..5a0ccc7 100644
ae23c9
--- a/job.c
ae23c9
+++ b/job.c
ae23c9
@@ -397,6 +397,11 @@ static void job_event_ready(Job *job)
ae23c9
     notifier_list_notify(&job->on_ready, job);
ae23c9
 }
ae23c9
 
ae23c9
+static void job_event_idle(Job *job)
ae23c9
+{
ae23c9
+    notifier_list_notify(&job->on_idle, job);
ae23c9
+}
ae23c9
+
ae23c9
 void job_enter_cond(Job *job, bool(*fn)(Job *job))
ae23c9
 {
ae23c9
     if (!job_started(job)) {
ae23c9
@@ -442,6 +447,7 @@ static void coroutine_fn job_do_yield(Job *job, uint64_t ns)
ae23c9
         timer_mod(&job->sleep_timer, ns);
ae23c9
     }
ae23c9
     job->busy = false;
ae23c9
+    job_event_idle(job);
ae23c9
     job_unlock();
ae23c9
     qemu_coroutine_yield();
ae23c9
 
ae23c9
@@ -860,6 +866,7 @@ static void coroutine_fn job_co_entry(void *opaque)
ae23c9
     assert(job && job->driver && job->driver->run);
ae23c9
     job_pause_point(job);
ae23c9
     job->ret = job->driver->run(job, &job->err);
ae23c9
+    job_event_idle(job);
ae23c9
     job->deferred_to_main_loop = true;
ae23c9
     aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job);
ae23c9
 }
ae23c9
-- 
ae23c9
1.8.3.1
ae23c9