aboutsummaryrefslogtreecommitdiff
path: root/blockjob.c
diff options
context:
space:
mode:
Diffstat (limited to 'blockjob.c')
-rw-r--r--blockjob.c84
1 files changed, 67 insertions, 17 deletions
diff --git a/blockjob.c b/blockjob.c
index ff9a614531..0ed50b953b 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -37,6 +37,26 @@
#include "qemu/timer.h"
#include "qapi-event.h"
+/* Right now, this mutex is only needed to synchronize accesses to job->busy
+ * and job->sleep_timer, such as concurrent calls to block_job_do_yield and
+ * block_job_enter. */
+static QemuMutex block_job_mutex;
+
+static void block_job_lock(void)
+{
+ qemu_mutex_lock(&block_job_mutex);
+}
+
+static void block_job_unlock(void)
+{
+ qemu_mutex_unlock(&block_job_mutex);
+}
+
+static void __attribute__((__constructor__)) block_job_init(void)
+{
+ qemu_mutex_init(&block_job_mutex);
+}
+
static void block_job_event_cancelled(BlockJob *job);
static void block_job_event_completed(BlockJob *job, const char *msg);
@@ -152,6 +172,7 @@ void block_job_unref(BlockJob *job)
{
if (--job->refcnt == 0) {
BlockDriverState *bs = blk_bs(job->blk);
+ QLIST_REMOVE(job, job_list);
bs->job = NULL;
block_job_remove_all_bdrv(job);
blk_remove_aio_context_notifier(job->blk,
@@ -160,7 +181,7 @@ void block_job_unref(BlockJob *job)
blk_unref(job->blk);
error_free(job->blocker);
g_free(job->id);
- QLIST_REMOVE(job, job_list);
+ assert(!timer_pending(&job->sleep_timer));
g_free(job);
}
}
@@ -287,6 +308,13 @@ static void coroutine_fn block_job_co_entry(void *opaque)
job->driver->start(job);
}
+static void block_job_sleep_timer_cb(void *opaque)
+{
+ BlockJob *job = opaque;
+
+ block_job_enter(job);
+}
+
void block_job_start(BlockJob *job)
{
assert(job && !block_job_started(job) && job->paused &&
@@ -556,7 +584,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
info->type = g_strdup(BlockJobType_str(job->driver->job_type));
info->device = g_strdup(job->id);
info->len = job->len;
- info->busy = job->busy;
+ info->busy = atomic_read(&job->busy);
info->paused = job->pause_count > 0;
info->offset = job->offset;
info->speed = job->speed;
@@ -664,6 +692,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
job->paused = true;
job->pause_count = 1;
job->refcnt = 1;
+ aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
+ QEMU_CLOCK_REALTIME, SCALE_NS,
+ block_job_sleep_timer_cb, job);
error_setg(&job->blocker, "block device is in use by block job: %s",
BlockJobType_str(driver->job_type));
@@ -729,6 +760,26 @@ static bool block_job_should_pause(BlockJob *job)
return job->pause_count > 0;
}
+/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
+ * Reentering the job coroutine with block_job_enter() before the timer has
+ * expired is allowed and cancels the timer.
+ *
+ * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
+ * called explicitly. */
+static void block_job_do_yield(BlockJob *job, uint64_t ns)
+{
+ block_job_lock();
+ if (ns != -1) {
+ timer_mod(&job->sleep_timer, ns);
+ }
+ job->busy = false;
+ block_job_unlock();
+ qemu_coroutine_yield();
+
+ /* Set by block_job_enter before re-entering the coroutine. */
+ assert(job->busy);
+}
+
void coroutine_fn block_job_pause_point(BlockJob *job)
{
assert(job && block_job_started(job));
@@ -746,9 +797,7 @@ void coroutine_fn block_job_pause_point(BlockJob *job)
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
job->paused = true;
- job->busy = false;
- qemu_coroutine_yield(); /* wait for block_job_resume() */
- job->busy = true;
+ block_job_do_yield(job, -1);
job->paused = false;
}
@@ -778,9 +827,17 @@ void block_job_enter(BlockJob *job)
return;
}
- if (!job->busy) {
- bdrv_coroutine_enter(blk_bs(job->blk), job->co);
+ block_job_lock();
+ if (job->busy) {
+ block_job_unlock();
+ return;
}
+
+ assert(!job->deferred_to_main_loop);
+ timer_del(&job->sleep_timer);
+ job->busy = true;
+ block_job_unlock();
+ aio_co_wake(job->co);
}
bool block_job_is_cancelled(BlockJob *job)
@@ -788,7 +845,7 @@ bool block_job_is_cancelled(BlockJob *job)
return job->cancelled;
}
-void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
+void block_job_sleep_ns(BlockJob *job, int64_t ns)
{
assert(job->busy);
@@ -797,13 +854,8 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
return;
}
- /* We need to leave job->busy set here, because when we have
- * put a coroutine to 'sleep', we have scheduled it to run in
- * the future. We cannot enter that same coroutine again before
- * it wakes and runs, otherwise we risk double-entry or entry after
- * completion. */
if (!block_job_should_pause(job)) {
- co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
+ block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
}
block_job_pause_point(job);
@@ -818,11 +870,9 @@ void block_job_yield(BlockJob *job)
return;
}
- job->busy = false;
if (!block_job_should_pause(job)) {
- qemu_coroutine_yield();
+ block_job_do_yield(job, -1);
}
- job->busy = true;
block_job_pause_point(job);
}