|
|
357786 |
From 0a25884271e9d6a99f0775c3b0c631d006ba2fc2 Mon Sep 17 00:00:00 2001
|
|
|
357786 |
From: Kevin Wolf <kwolf@redhat.com>
|
|
|
357786 |
Date: Tue, 26 Jun 2018 09:48:09 +0200
|
|
|
357786 |
Subject: [PATCH 40/89] job: Move coroutine and related code to Job
|
|
|
357786 |
|
|
|
357786 |
RH-Author: Kevin Wolf <kwolf@redhat.com>
|
|
|
357786 |
Message-id: <20180626094856.6924-27-kwolf@redhat.com>
|
|
|
357786 |
Patchwork-id: 81113
|
|
|
357786 |
O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 26/73] job: Move coroutine and related code to Job
|
|
|
357786 |
Bugzilla: 1513543
|
|
|
357786 |
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
|
|
|
357786 |
RH-Acked-by: Max Reitz <mreitz@redhat.com>
|
|
|
357786 |
RH-Acked-by: Fam Zheng <famz@redhat.com>
|
|
|
357786 |
|
|
|
357786 |
This commit moves some core functions for dealing with the job coroutine
|
|
|
357786 |
from BlockJob to Job. This includes primarily entering the coroutine
|
|
|
357786 |
(both for the first and reentering) and yielding explicitly and at pause
|
|
|
357786 |
points.
|
|
|
357786 |
|
|
|
357786 |
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
|
|
357786 |
Reviewed-by: John Snow <jsnow@redhat.com>
|
|
|
357786 |
(cherry picked from commit da01ff7f38f52791f93fc3ca59afcfbb220f15af)
|
|
|
357786 |
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
|
|
357786 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
357786 |
---
|
|
|
357786 |
block/backup.c | 2 +-
|
|
|
357786 |
block/commit.c | 4 +-
|
|
|
357786 |
block/mirror.c | 22 ++---
|
|
|
357786 |
block/replication.c | 2 +-
|
|
|
357786 |
block/stream.c | 4 +-
|
|
|
357786 |
blockdev.c | 8 +-
|
|
|
357786 |
blockjob.c | 219 ++++++++-----------------------------------
|
|
|
357786 |
include/block/blockjob.h | 40 --------
|
|
|
357786 |
include/block/blockjob_int.h | 26 -----
|
|
|
357786 |
include/qemu/job.h | 76 +++++++++++++++
|
|
|
357786 |
job.c | 137 +++++++++++++++++++++++++++
|
|
|
357786 |
tests/test-bdrv-drain.c | 38 ++++----
|
|
|
357786 |
tests/test-blockjob-txn.c | 12 +--
|
|
|
357786 |
tests/test-blockjob.c | 14 +--
|
|
|
357786 |
14 files changed, 305 insertions(+), 299 deletions(-)
|
|
|
357786 |
|
|
|
357786 |
diff --git a/block/backup.c b/block/backup.c
|
|
|
357786 |
index 22dd368..7d9aad9 100644
|
|
|
357786 |
--- a/block/backup.c
|
|
|
357786 |
+++ b/block/backup.c
|
|
|
357786 |
@@ -528,8 +528,8 @@ static const BlockJobDriver backup_job_driver = {
|
|
|
357786 |
.instance_size = sizeof(BackupBlockJob),
|
|
|
357786 |
.job_type = JOB_TYPE_BACKUP,
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = backup_run,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = backup_run,
|
|
|
357786 |
.commit = backup_commit,
|
|
|
357786 |
.abort = backup_abort,
|
|
|
357786 |
.clean = backup_clean,
|
|
|
357786 |
diff --git a/block/commit.c b/block/commit.c
|
|
|
357786 |
index d326766..2fbc310 100644
|
|
|
357786 |
--- a/block/commit.c
|
|
|
357786 |
+++ b/block/commit.c
|
|
|
357786 |
@@ -220,8 +220,8 @@ static const BlockJobDriver commit_job_driver = {
|
|
|
357786 |
.instance_size = sizeof(CommitBlockJob),
|
|
|
357786 |
.job_type = JOB_TYPE_COMMIT,
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = commit_run,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = commit_run,
|
|
|
357786 |
};
|
|
|
357786 |
|
|
|
357786 |
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
|
|
357786 |
@@ -371,7 +371,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
|
|
357786 |
s->on_error = on_error;
|
|
|
357786 |
|
|
|
357786 |
trace_commit_start(bs, base, top, s);
|
|
|
357786 |
- block_job_start(&s->common);
|
|
|
357786 |
+ job_start(&s->common.job);
|
|
|
357786 |
return;
|
|
|
357786 |
|
|
|
357786 |
fail:
|
|
|
357786 |
diff --git a/block/mirror.c b/block/mirror.c
|
|
|
357786 |
index 90d4ac9..95fc807 100644
|
|
|
357786 |
--- a/block/mirror.c
|
|
|
357786 |
+++ b/block/mirror.c
|
|
|
357786 |
@@ -126,7 +126,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
|
|
357786 |
g_free(op);
|
|
|
357786 |
|
|
|
357786 |
if (s->waiting_for_io) {
|
|
|
357786 |
- qemu_coroutine_enter(s->common.co);
|
|
|
357786 |
+ qemu_coroutine_enter(s->common.job.co);
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
@@ -345,7 +345,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|
|
357786 |
mirror_wait_for_io(s);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
- block_job_pause_point(&s->common);
|
|
|
357786 |
+ job_pause_point(&s->common.job);
|
|
|
357786 |
|
|
|
357786 |
/* Find the number of consective dirty chunks following the first dirty
|
|
|
357786 |
* one, and wait for in flight requests in them. */
|
|
|
357786 |
@@ -597,7 +597,7 @@ static void mirror_throttle(MirrorBlockJob *s)
|
|
|
357786 |
s->last_pause_ns = now;
|
|
|
357786 |
block_job_sleep_ns(&s->common, 0);
|
|
|
357786 |
} else {
|
|
|
357786 |
- block_job_pause_point(&s->common);
|
|
|
357786 |
+ job_pause_point(&s->common.job);
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
@@ -786,7 +786,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|
|
357786 |
goto immediate_exit;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
- block_job_pause_point(&s->common);
|
|
|
357786 |
+ job_pause_point(&s->common.job);
|
|
|
357786 |
|
|
|
357786 |
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
|
|
357786 |
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
|
|
|
357786 |
@@ -957,9 +957,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
|
|
357786 |
block_job_enter(&s->common);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
-static void mirror_pause(BlockJob *job)
|
|
|
357786 |
+static void mirror_pause(Job *job)
|
|
|
357786 |
{
|
|
|
357786 |
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
|
|
357786 |
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
|
|
357786 |
|
|
|
357786 |
mirror_wait_for_all_io(s);
|
|
|
357786 |
}
|
|
|
357786 |
@@ -991,10 +991,10 @@ static const BlockJobDriver mirror_job_driver = {
|
|
|
357786 |
.instance_size = sizeof(MirrorBlockJob),
|
|
|
357786 |
.job_type = JOB_TYPE_MIRROR,
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = mirror_run,
|
|
|
357786 |
+ .pause = mirror_pause,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = mirror_run,
|
|
|
357786 |
.complete = mirror_complete,
|
|
|
357786 |
- .pause = mirror_pause,
|
|
|
357786 |
.attached_aio_context = mirror_attached_aio_context,
|
|
|
357786 |
.drain = mirror_drain,
|
|
|
357786 |
};
|
|
|
357786 |
@@ -1004,10 +1004,10 @@ static const BlockJobDriver commit_active_job_driver = {
|
|
|
357786 |
.instance_size = sizeof(MirrorBlockJob),
|
|
|
357786 |
.job_type = JOB_TYPE_COMMIT,
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = mirror_run,
|
|
|
357786 |
+ .pause = mirror_pause,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = mirror_run,
|
|
|
357786 |
.complete = mirror_complete,
|
|
|
357786 |
- .pause = mirror_pause,
|
|
|
357786 |
.attached_aio_context = mirror_attached_aio_context,
|
|
|
357786 |
.drain = mirror_drain,
|
|
|
357786 |
};
|
|
|
357786 |
@@ -1244,7 +1244,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
trace_mirror_start(bs, s, opaque);
|
|
|
357786 |
- block_job_start(&s->common);
|
|
|
357786 |
+ job_start(&s->common.job);
|
|
|
357786 |
return;
|
|
|
357786 |
|
|
|
357786 |
fail:
|
|
|
357786 |
diff --git a/block/replication.c b/block/replication.c
|
|
|
357786 |
index 6c0c718..3f7500e 100644
|
|
|
357786 |
--- a/block/replication.c
|
|
|
357786 |
+++ b/block/replication.c
|
|
|
357786 |
@@ -574,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|
|
357786 |
aio_context_release(aio_context);
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
break;
|
|
|
357786 |
default:
|
|
|
357786 |
aio_context_release(aio_context);
|
|
|
357786 |
diff --git a/block/stream.c b/block/stream.c
|
|
|
357786 |
index 0bba816..6d8b7b6 100644
|
|
|
357786 |
--- a/block/stream.c
|
|
|
357786 |
+++ b/block/stream.c
|
|
|
357786 |
@@ -213,8 +213,8 @@ static const BlockJobDriver stream_job_driver = {
|
|
|
357786 |
.instance_size = sizeof(StreamBlockJob),
|
|
|
357786 |
.job_type = JOB_TYPE_STREAM,
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = stream_run,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = stream_run,
|
|
|
357786 |
};
|
|
|
357786 |
|
|
|
357786 |
void stream_start(const char *job_id, BlockDriverState *bs,
|
|
|
357786 |
@@ -262,7 +262,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|
|
357786 |
|
|
|
357786 |
s->on_error = on_error;
|
|
|
357786 |
trace_stream_start(bs, base, s);
|
|
|
357786 |
- block_job_start(&s->common);
|
|
|
357786 |
+ job_start(&s->common.job);
|
|
|
357786 |
return;
|
|
|
357786 |
|
|
|
357786 |
fail:
|
|
|
357786 |
diff --git a/blockdev.c b/blockdev.c
|
|
|
357786 |
index 96a89cc..efb83c4 100644
|
|
|
357786 |
--- a/blockdev.c
|
|
|
357786 |
+++ b/blockdev.c
|
|
|
357786 |
@@ -1952,7 +1952,7 @@ static void drive_backup_commit(BlkActionState *common)
|
|
|
357786 |
aio_context_acquire(aio_context);
|
|
|
357786 |
|
|
|
357786 |
assert(state->job);
|
|
|
357786 |
- block_job_start(state->job);
|
|
|
357786 |
+ job_start(&state->job->job);
|
|
|
357786 |
|
|
|
357786 |
aio_context_release(aio_context);
|
|
|
357786 |
}
|
|
|
357786 |
@@ -2050,7 +2050,7 @@ static void blockdev_backup_commit(BlkActionState *common)
|
|
|
357786 |
aio_context_acquire(aio_context);
|
|
|
357786 |
|
|
|
357786 |
assert(state->job);
|
|
|
357786 |
- block_job_start(state->job);
|
|
|
357786 |
+ job_start(&state->job->job);
|
|
|
357786 |
|
|
|
357786 |
aio_context_release(aio_context);
|
|
|
357786 |
}
|
|
|
357786 |
@@ -3472,7 +3472,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp)
|
|
|
357786 |
BlockJob *job;
|
|
|
357786 |
job = do_drive_backup(arg, NULL, errp);
|
|
|
357786 |
if (job) {
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
@@ -3560,7 +3560,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
|
|
|
357786 |
BlockJob *job;
|
|
|
357786 |
job = do_blockdev_backup(arg, NULL, errp);
|
|
|
357786 |
if (job) {
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
diff --git a/blockjob.c b/blockjob.c
|
|
|
357786 |
index 3ede511..313b1ff 100644
|
|
|
357786 |
--- a/blockjob.c
|
|
|
357786 |
+++ b/blockjob.c
|
|
|
357786 |
@@ -36,30 +36,9 @@
|
|
|
357786 |
#include "qemu/coroutine.h"
|
|
|
357786 |
#include "qemu/timer.h"
|
|
|
357786 |
|
|
|
357786 |
-/* Right now, this mutex is only needed to synchronize accesses to job->busy
|
|
|
357786 |
- * and job->sleep_timer, such as concurrent calls to block_job_do_yield and
|
|
|
357786 |
- * block_job_enter. */
|
|
|
357786 |
-static QemuMutex block_job_mutex;
|
|
|
357786 |
-
|
|
|
357786 |
-static void block_job_lock(void)
|
|
|
357786 |
-{
|
|
|
357786 |
- qemu_mutex_lock(&block_job_mutex);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
-static void block_job_unlock(void)
|
|
|
357786 |
-{
|
|
|
357786 |
- qemu_mutex_unlock(&block_job_mutex);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
-static void __attribute__((__constructor__)) block_job_init(void)
|
|
|
357786 |
-{
|
|
|
357786 |
- qemu_mutex_init(&block_job_mutex);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
static void block_job_event_cancelled(BlockJob *job);
|
|
|
357786 |
static void block_job_event_completed(BlockJob *job, const char *msg);
|
|
|
357786 |
static int block_job_event_pending(BlockJob *job);
|
|
|
357786 |
-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
|
|
|
357786 |
|
|
|
357786 |
/* Transactional group of block jobs */
|
|
|
357786 |
struct BlockJobTxn {
|
|
|
357786 |
@@ -161,33 +140,27 @@ static void block_job_txn_del_job(BlockJob *job)
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
-/* Assumes the block_job_mutex is held */
|
|
|
357786 |
-static bool block_job_timer_pending(BlockJob *job)
|
|
|
357786 |
-{
|
|
|
357786 |
- return timer_pending(&job->sleep_timer);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
-/* Assumes the block_job_mutex is held */
|
|
|
357786 |
-static bool block_job_timer_not_pending(BlockJob *job)
|
|
|
357786 |
+/* Assumes the job_mutex is held */
|
|
|
357786 |
+static bool job_timer_not_pending(Job *job)
|
|
|
357786 |
{
|
|
|
357786 |
- return !block_job_timer_pending(job);
|
|
|
357786 |
+ return !timer_pending(&job->sleep_timer);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
static void block_job_pause(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
- job->pause_count++;
|
|
|
357786 |
+ job->job.pause_count++;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
static void block_job_resume(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
- assert(job->pause_count > 0);
|
|
|
357786 |
- job->pause_count--;
|
|
|
357786 |
- if (job->pause_count) {
|
|
|
357786 |
+ assert(job->job.pause_count > 0);
|
|
|
357786 |
+ job->job.pause_count--;
|
|
|
357786 |
+ if (job->job.pause_count) {
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
/* kick only if no timer is pending */
|
|
|
357786 |
- block_job_enter_cond(job, block_job_timer_not_pending);
|
|
|
357786 |
+ job_enter_cond(&job->job, job_timer_not_pending);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
static void block_job_attached_aio_context(AioContext *new_context,
|
|
|
357786 |
@@ -208,7 +181,7 @@ void block_job_free(Job *job)
|
|
|
357786 |
block_job_detach_aio_context, bjob);
|
|
|
357786 |
blk_unref(bjob->blk);
|
|
|
357786 |
error_free(bjob->blocker);
|
|
|
357786 |
- assert(!timer_pending(&bjob->sleep_timer));
|
|
|
357786 |
+ assert(!timer_pending(&bjob->job.sleep_timer));
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
static void block_job_attached_aio_context(AioContext *new_context,
|
|
|
357786 |
@@ -226,7 +199,7 @@ static void block_job_attached_aio_context(AioContext *new_context,
|
|
|
357786 |
|
|
|
357786 |
static void block_job_drain(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
- /* If job is !job->busy this kicks it into the next pause point. */
|
|
|
357786 |
+ /* If job is !job->job.busy this kicks it into the next pause point. */
|
|
|
357786 |
block_job_enter(job);
|
|
|
357786 |
|
|
|
357786 |
blk_drain(job->blk);
|
|
|
357786 |
@@ -244,7 +217,7 @@ static void block_job_detach_aio_context(void *opaque)
|
|
|
357786 |
|
|
|
357786 |
block_job_pause(job);
|
|
|
357786 |
|
|
|
357786 |
- while (!job->paused && !job->completed) {
|
|
|
357786 |
+ while (!job->job.paused && !job->completed) {
|
|
|
357786 |
block_job_drain(job);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
@@ -312,29 +285,11 @@ bool block_job_is_internal(BlockJob *job)
|
|
|
357786 |
return (job->job.id == NULL);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
-static bool block_job_started(BlockJob *job)
|
|
|
357786 |
-{
|
|
|
357786 |
- return job->co;
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
const BlockJobDriver *block_job_driver(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
return job->driver;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
-/**
|
|
|
357786 |
- * All jobs must allow a pause point before entering their job proper. This
|
|
|
357786 |
- * ensures that jobs can be paused prior to being started, then resumed later.
|
|
|
357786 |
- */
|
|
|
357786 |
-static void coroutine_fn block_job_co_entry(void *opaque)
|
|
|
357786 |
-{
|
|
|
357786 |
- BlockJob *job = opaque;
|
|
|
357786 |
-
|
|
|
357786 |
- assert(job && job->driver && job->driver->start);
|
|
|
357786 |
- block_job_pause_point(job);
|
|
|
357786 |
- job->driver->start(job);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
static void block_job_sleep_timer_cb(void *opaque)
|
|
|
357786 |
{
|
|
|
357786 |
BlockJob *job = opaque;
|
|
|
357786 |
@@ -342,24 +297,12 @@ static void block_job_sleep_timer_cb(void *opaque)
|
|
|
357786 |
block_job_enter(job);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
-void block_job_start(BlockJob *job)
|
|
|
357786 |
-{
|
|
|
357786 |
- assert(job && !block_job_started(job) && job->paused &&
|
|
|
357786 |
- job->driver && job->driver->start);
|
|
|
357786 |
- job->co = qemu_coroutine_create(block_job_co_entry, job);
|
|
|
357786 |
- job->pause_count--;
|
|
|
357786 |
- job->busy = true;
|
|
|
357786 |
- job->paused = false;
|
|
|
357786 |
- job_state_transition(&job->job, JOB_STATUS_RUNNING);
|
|
|
357786 |
- bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
static void block_job_decommission(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
assert(job);
|
|
|
357786 |
job->completed = true;
|
|
|
357786 |
- job->busy = false;
|
|
|
357786 |
- job->paused = false;
|
|
|
357786 |
+ job->job.busy = false;
|
|
|
357786 |
+ job->job.paused = false;
|
|
|
357786 |
job->job.deferred_to_main_loop = true;
|
|
|
357786 |
block_job_txn_del_job(job);
|
|
|
357786 |
job_state_transition(&job->job, JOB_STATUS_NULL);
|
|
|
357786 |
@@ -374,7 +317,7 @@ static void block_job_do_dismiss(BlockJob *job)
|
|
|
357786 |
static void block_job_conclude(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
|
|
|
357786 |
- if (job->auto_dismiss || !block_job_started(job)) {
|
|
|
357786 |
+ if (job->auto_dismiss || !job_started(&job->job)) {
|
|
|
357786 |
block_job_do_dismiss(job);
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
@@ -439,7 +382,7 @@ static int block_job_finalize_single(BlockJob *job)
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
/* Emit events only if we actually started */
|
|
|
357786 |
- if (block_job_started(job)) {
|
|
|
357786 |
+ if (job_started(&job->job)) {
|
|
|
357786 |
if (job_is_cancelled(&job->job)) {
|
|
|
357786 |
block_job_event_cancelled(job);
|
|
|
357786 |
} else {
|
|
|
357786 |
@@ -464,7 +407,7 @@ static void block_job_cancel_async(BlockJob *job, bool force)
|
|
|
357786 |
if (job->user_paused) {
|
|
|
357786 |
/* Do not call block_job_enter here, the caller will handle it. */
|
|
|
357786 |
job->user_paused = false;
|
|
|
357786 |
- job->pause_count--;
|
|
|
357786 |
+ job->job.pause_count--;
|
|
|
357786 |
}
|
|
|
357786 |
job->job.cancelled = true;
|
|
|
357786 |
/* To prevent 'force == false' overriding a previous 'force == true' */
|
|
|
357786 |
@@ -615,6 +558,12 @@ static void block_job_completed_txn_success(BlockJob *job)
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
+/* Assumes the job_mutex is held */
|
|
|
357786 |
+static bool job_timer_pending(Job *job)
|
|
|
357786 |
+{
|
|
|
357786 |
+ return timer_pending(&job->sleep_timer);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|
|
357786 |
{
|
|
|
357786 |
int64_t old_speed = job->speed;
|
|
|
357786 |
@@ -635,7 +584,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
/* kick only if a timer is pending */
|
|
|
357786 |
- block_job_enter_cond(job, block_job_timer_pending);
|
|
|
357786 |
+ job_enter_cond(&job->job, job_timer_pending);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
|
|
|
357786 |
@@ -654,7 +603,7 @@ void block_job_complete(BlockJob *job, Error **errp)
|
|
|
357786 |
if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
- if (job->pause_count || job_is_cancelled(&job->job) ||
|
|
|
357786 |
+ if (job->job.pause_count || job_is_cancelled(&job->job) ||
|
|
|
357786 |
!job->driver->complete)
|
|
|
357786 |
{
|
|
|
357786 |
error_setg(errp, "The active block job '%s' cannot be completed",
|
|
|
357786 |
@@ -708,7 +657,7 @@ bool block_job_user_paused(BlockJob *job)
|
|
|
357786 |
void block_job_user_resume(BlockJob *job, Error **errp)
|
|
|
357786 |
{
|
|
|
357786 |
assert(job);
|
|
|
357786 |
- if (!job->user_paused || job->pause_count <= 0) {
|
|
|
357786 |
+ if (!job->user_paused || job->job.pause_count <= 0) {
|
|
|
357786 |
error_setg(errp, "Can't resume a job that was not paused");
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
@@ -727,7 +676,7 @@ void block_job_cancel(BlockJob *job, bool force)
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
block_job_cancel_async(job, force);
|
|
|
357786 |
- if (!block_job_started(job)) {
|
|
|
357786 |
+ if (!job_started(&job->job)) {
|
|
|
357786 |
block_job_completed(job, -ECANCELED);
|
|
|
357786 |
} else if (job->job.deferred_to_main_loop) {
|
|
|
357786 |
block_job_completed_txn_abort(job);
|
|
|
357786 |
@@ -797,8 +746,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
|
|
|
357786 |
info->type = g_strdup(job_type_str(&job->job));
|
|
|
357786 |
info->device = g_strdup(job->job.id);
|
|
|
357786 |
info->len = job->len;
|
|
|
357786 |
- info->busy = atomic_read(&job->busy);
|
|
|
357786 |
- info->paused = job->pause_count > 0;
|
|
|
357786 |
+ info->busy = atomic_read(&job->job.busy);
|
|
|
357786 |
+ info->paused = job->job.pause_count > 0;
|
|
|
357786 |
info->offset = job->offset;
|
|
|
357786 |
info->speed = job->speed;
|
|
|
357786 |
info->io_status = job->iostatus;
|
|
|
357786 |
@@ -915,12 +864,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
|
|
357786 |
job->blk = blk;
|
|
|
357786 |
job->cb = cb;
|
|
|
357786 |
job->opaque = opaque;
|
|
|
357786 |
- job->busy = false;
|
|
|
357786 |
- job->paused = true;
|
|
|
357786 |
- job->pause_count = 1;
|
|
|
357786 |
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
|
|
|
357786 |
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
|
|
|
357786 |
- aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
|
|
|
357786 |
+ aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer,
|
|
|
357786 |
QEMU_CLOCK_REALTIME, SCALE_NS,
|
|
|
357786 |
block_job_sleep_timer_cb, job);
|
|
|
357786 |
|
|
|
357786 |
@@ -980,128 +926,41 @@ void block_job_completed(BlockJob *job, int ret)
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
-static bool block_job_should_pause(BlockJob *job)
|
|
|
357786 |
-{
|
|
|
357786 |
- return job->pause_count > 0;
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
-/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
|
|
|
357786 |
- * Reentering the job coroutine with block_job_enter() before the timer has
|
|
|
357786 |
- * expired is allowed and cancels the timer.
|
|
|
357786 |
- *
|
|
|
357786 |
- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
|
|
|
357786 |
- * called explicitly. */
|
|
|
357786 |
-static void block_job_do_yield(BlockJob *job, uint64_t ns)
|
|
|
357786 |
-{
|
|
|
357786 |
- block_job_lock();
|
|
|
357786 |
- if (ns != -1) {
|
|
|
357786 |
- timer_mod(&job->sleep_timer, ns);
|
|
|
357786 |
- }
|
|
|
357786 |
- job->busy = false;
|
|
|
357786 |
- block_job_unlock();
|
|
|
357786 |
- qemu_coroutine_yield();
|
|
|
357786 |
-
|
|
|
357786 |
- /* Set by block_job_enter before re-entering the coroutine. */
|
|
|
357786 |
- assert(job->busy);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
-void coroutine_fn block_job_pause_point(BlockJob *job)
|
|
|
357786 |
-{
|
|
|
357786 |
- assert(job && block_job_started(job));
|
|
|
357786 |
-
|
|
|
357786 |
- if (!block_job_should_pause(job)) {
|
|
|
357786 |
- return;
|
|
|
357786 |
- }
|
|
|
357786 |
- if (job_is_cancelled(&job->job)) {
|
|
|
357786 |
- return;
|
|
|
357786 |
- }
|
|
|
357786 |
-
|
|
|
357786 |
- if (job->driver->pause) {
|
|
|
357786 |
- job->driver->pause(job);
|
|
|
357786 |
- }
|
|
|
357786 |
-
|
|
|
357786 |
- if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) {
|
|
|
357786 |
- JobStatus status = job->job.status;
|
|
|
357786 |
- job_state_transition(&job->job, status == JOB_STATUS_READY
|
|
|
357786 |
- ? JOB_STATUS_STANDBY
|
|
|
357786 |
- : JOB_STATUS_PAUSED);
|
|
|
357786 |
- job->paused = true;
|
|
|
357786 |
- block_job_do_yield(job, -1);
|
|
|
357786 |
- job->paused = false;
|
|
|
357786 |
- job_state_transition(&job->job, status);
|
|
|
357786 |
- }
|
|
|
357786 |
-
|
|
|
357786 |
- if (job->driver->resume) {
|
|
|
357786 |
- job->driver->resume(job);
|
|
|
357786 |
- }
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
-/*
|
|
|
357786 |
- * Conditionally enter a block_job pending a call to fn() while
|
|
|
357786 |
- * under the block_job_lock critical section.
|
|
|
357786 |
- */
|
|
|
357786 |
-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
|
|
|
357786 |
-{
|
|
|
357786 |
- if (!block_job_started(job)) {
|
|
|
357786 |
- return;
|
|
|
357786 |
- }
|
|
|
357786 |
- if (job->job.deferred_to_main_loop) {
|
|
|
357786 |
- return;
|
|
|
357786 |
- }
|
|
|
357786 |
-
|
|
|
357786 |
- block_job_lock();
|
|
|
357786 |
- if (job->busy) {
|
|
|
357786 |
- block_job_unlock();
|
|
|
357786 |
- return;
|
|
|
357786 |
- }
|
|
|
357786 |
-
|
|
|
357786 |
- if (fn && !fn(job)) {
|
|
|
357786 |
- block_job_unlock();
|
|
|
357786 |
- return;
|
|
|
357786 |
- }
|
|
|
357786 |
-
|
|
|
357786 |
- assert(!job->job.deferred_to_main_loop);
|
|
|
357786 |
- timer_del(&job->sleep_timer);
|
|
|
357786 |
- job->busy = true;
|
|
|
357786 |
- block_job_unlock();
|
|
|
357786 |
- aio_co_wake(job->co);
|
|
|
357786 |
-}
|
|
|
357786 |
-
|
|
|
357786 |
void block_job_enter(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
- block_job_enter_cond(job, NULL);
|
|
|
357786 |
+ job_enter_cond(&job->job, NULL);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
void block_job_sleep_ns(BlockJob *job, int64_t ns)
|
|
|
357786 |
{
|
|
|
357786 |
- assert(job->busy);
|
|
|
357786 |
+ assert(job->job.busy);
|
|
|
357786 |
|
|
|
357786 |
/* Check cancellation *before* setting busy = false, too! */
|
|
|
357786 |
if (job_is_cancelled(&job->job)) {
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
- if (!block_job_should_pause(job)) {
|
|
|
357786 |
- block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
|
|
|
357786 |
+ if (!job_should_pause(&job->job)) {
|
|
|
357786 |
+ job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
- block_job_pause_point(job);
|
|
|
357786 |
+ job_pause_point(&job->job);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
void block_job_yield(BlockJob *job)
|
|
|
357786 |
{
|
|
|
357786 |
- assert(job->busy);
|
|
|
357786 |
+ assert(job->job.busy);
|
|
|
357786 |
|
|
|
357786 |
/* Check cancellation *before* setting busy = false, too! */
|
|
|
357786 |
if (job_is_cancelled(&job->job)) {
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
- if (!block_job_should_pause(job)) {
|
|
|
357786 |
- block_job_do_yield(job, -1);
|
|
|
357786 |
+ if (!job_should_pause(&job->job)) {
|
|
|
357786 |
+ job_do_yield(&job->job, -1);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
- block_job_pause_point(job);
|
|
|
357786 |
+ job_pause_point(&job->job);
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
void block_job_iostatus_reset(BlockJob *job)
|
|
|
357786 |
@@ -1109,7 +968,7 @@ void block_job_iostatus_reset(BlockJob *job)
|
|
|
357786 |
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
|
|
357786 |
return;
|
|
|
357786 |
}
|
|
|
357786 |
- assert(job->user_paused && job->pause_count > 0);
|
|
|
357786 |
+ assert(job->user_paused && job->job.pause_count > 0);
|
|
|
357786 |
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
|
|
|
357786 |
index 2a9e865..b60d919 100644
|
|
|
357786 |
--- a/include/block/blockjob.h
|
|
|
357786 |
+++ b/include/block/blockjob.h
|
|
|
357786 |
@@ -51,43 +51,18 @@ typedef struct BlockJob {
|
|
|
357786 |
BlockBackend *blk;
|
|
|
357786 |
|
|
|
357786 |
/**
|
|
|
357786 |
- * The coroutine that executes the job. If not NULL, it is
|
|
|
357786 |
- * reentered when busy is false and the job is cancelled.
|
|
|
357786 |
- */
|
|
|
357786 |
- Coroutine *co;
|
|
|
357786 |
-
|
|
|
357786 |
- /**
|
|
|
357786 |
* Set to true if the job should abort immediately without waiting
|
|
|
357786 |
* for data to be in sync.
|
|
|
357786 |
*/
|
|
|
357786 |
bool force;
|
|
|
357786 |
|
|
|
357786 |
/**
|
|
|
357786 |
- * Counter for pause request. If non-zero, the block job is either paused,
|
|
|
357786 |
- * or if busy == true will pause itself as soon as possible.
|
|
|
357786 |
- */
|
|
|
357786 |
- int pause_count;
|
|
|
357786 |
-
|
|
|
357786 |
- /**
|
|
|
357786 |
* Set to true if the job is paused by user. Can be unpaused with the
|
|
|
357786 |
* block-job-resume QMP command.
|
|
|
357786 |
*/
|
|
|
357786 |
bool user_paused;
|
|
|
357786 |
|
|
|
357786 |
/**
|
|
|
357786 |
- * Set to false by the job while the coroutine has yielded and may be
|
|
|
357786 |
- * re-entered by block_job_enter(). There may still be I/O or event loop
|
|
|
357786 |
- * activity pending. Accessed under block_job_mutex (in blockjob.c).
|
|
|
357786 |
- */
|
|
|
357786 |
- bool busy;
|
|
|
357786 |
-
|
|
|
357786 |
- /**
|
|
|
357786 |
- * Set to true by the job while it is in a quiescent state, where
|
|
|
357786 |
- * no I/O or event loop activity is pending.
|
|
|
357786 |
- */
|
|
|
357786 |
- bool paused;
|
|
|
357786 |
-
|
|
|
357786 |
- /**
|
|
|
357786 |
* Set to true when the job is ready to be completed.
|
|
|
357786 |
*/
|
|
|
357786 |
bool ready;
|
|
|
357786 |
@@ -125,12 +100,6 @@ typedef struct BlockJob {
|
|
|
357786 |
/** ret code passed to block_job_completed. */
|
|
|
357786 |
int ret;
|
|
|
357786 |
|
|
|
357786 |
- /**
|
|
|
357786 |
- * Timer that is used by @block_job_sleep_ns. Accessed under
|
|
|
357786 |
- * block_job_mutex (in blockjob.c).
|
|
|
357786 |
- */
|
|
|
357786 |
- QEMUTimer sleep_timer;
|
|
|
357786 |
-
|
|
|
357786 |
/** True if this job should automatically finalize itself */
|
|
|
357786 |
bool auto_finalize;
|
|
|
357786 |
|
|
|
357786 |
@@ -208,15 +177,6 @@ void block_job_remove_all_bdrv(BlockJob *job);
|
|
|
357786 |
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
|
|
|
357786 |
|
|
|
357786 |
/**
|
|
|
357786 |
- * block_job_start:
|
|
|
357786 |
- * @job: A job that has not yet been started.
|
|
|
357786 |
- *
|
|
|
357786 |
- * Begins execution of a block job.
|
|
|
357786 |
- * Takes ownership of one reference to the job object.
|
|
|
357786 |
- */
|
|
|
357786 |
-void block_job_start(BlockJob *job);
|
|
|
357786 |
-
|
|
|
357786 |
-/**
|
|
|
357786 |
* block_job_cancel:
|
|
|
357786 |
* @job: The job to be canceled.
|
|
|
357786 |
* @force: Quit a job without waiting for data to be in sync.
|
|
|
357786 |
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
|
|
|
357786 |
index 0c2f8de..0a614a8 100644
|
|
|
357786 |
--- a/include/block/blockjob_int.h
|
|
|
357786 |
+++ b/include/block/blockjob_int.h
|
|
|
357786 |
@@ -38,9 +38,6 @@ struct BlockJobDriver {
|
|
|
357786 |
/** Generic JobDriver callbacks and settings */
|
|
|
357786 |
JobDriver job_driver;
|
|
|
357786 |
|
|
|
357786 |
- /** Mandatory: Entrypoint for the Coroutine. */
|
|
|
357786 |
- CoroutineEntry *start;
|
|
|
357786 |
-
|
|
|
357786 |
/**
|
|
|
357786 |
* Optional callback for job types whose completion must be triggered
|
|
|
357786 |
* manually.
|
|
|
357786 |
@@ -85,20 +82,6 @@ struct BlockJobDriver {
|
|
|
357786 |
*/
|
|
|
357786 |
void (*clean)(BlockJob *job);
|
|
|
357786 |
|
|
|
357786 |
- /**
|
|
|
357786 |
- * If the callback is not NULL, it will be invoked when the job transitions
|
|
|
357786 |
- * into the paused state. Paused jobs must not perform any asynchronous
|
|
|
357786 |
- * I/O or event loop activity. This callback is used to quiesce jobs.
|
|
|
357786 |
- */
|
|
|
357786 |
- void coroutine_fn (*pause)(BlockJob *job);
|
|
|
357786 |
-
|
|
|
357786 |
- /**
|
|
|
357786 |
- * If the callback is not NULL, it will be invoked when the job transitions
|
|
|
357786 |
- * out of the paused state. Any asynchronous I/O or event loop activity
|
|
|
357786 |
- * should be restarted from this callback.
|
|
|
357786 |
- */
|
|
|
357786 |
- void coroutine_fn (*resume)(BlockJob *job);
|
|
|
357786 |
-
|
|
|
357786 |
/*
|
|
|
357786 |
* If the callback is not NULL, it will be invoked before the job is
|
|
|
357786 |
* resumed in a new AioContext. This is the place to move any resources
|
|
|
357786 |
@@ -196,15 +179,6 @@ void block_job_early_fail(BlockJob *job);
|
|
|
357786 |
void block_job_completed(BlockJob *job, int ret);
|
|
|
357786 |
|
|
|
357786 |
/**
|
|
|
357786 |
- * block_job_pause_point:
|
|
|
357786 |
- * @job: The job that is ready to pause.
|
|
|
357786 |
- *
|
|
|
357786 |
- * Pause now if block_job_pause() has been called. Block jobs that perform
|
|
|
357786 |
- * lots of I/O must call this between requests so that the job can be paused.
|
|
|
357786 |
- */
|
|
|
357786 |
-void coroutine_fn block_job_pause_point(BlockJob *job);
|
|
|
357786 |
-
|
|
|
357786 |
-/**
|
|
|
357786 |
* block_job_enter:
|
|
|
357786 |
* @job: The job to enter.
|
|
|
357786 |
*
|
|
|
357786 |
diff --git a/include/qemu/job.h b/include/qemu/job.h
|
|
|
357786 |
index 933e0ab..9dcff12 100644
|
|
|
357786 |
--- a/include/qemu/job.h
|
|
|
357786 |
+++ b/include/qemu/job.h
|
|
|
357786 |
@@ -28,6 +28,7 @@
|
|
|
357786 |
|
|
|
357786 |
#include "qapi/qapi-types-block-core.h"
|
|
|
357786 |
#include "qemu/queue.h"
|
|
|
357786 |
+#include "qemu/coroutine.h"
|
|
|
357786 |
|
|
|
357786 |
typedef struct JobDriver JobDriver;
|
|
|
357786 |
|
|
|
357786 |
@@ -51,6 +52,37 @@ typedef struct Job {
|
|
|
357786 |
AioContext *aio_context;
|
|
|
357786 |
|
|
|
357786 |
/**
|
|
|
357786 |
+ * The coroutine that executes the job. If not NULL, it is reentered when
|
|
|
357786 |
+ * busy is false and the job is cancelled.
|
|
|
357786 |
+ */
|
|
|
357786 |
+ Coroutine *co;
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
+ * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in
|
|
|
357786 |
+ * job.c).
|
|
|
357786 |
+ */
|
|
|
357786 |
+ QEMUTimer sleep_timer;
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
+ * Counter for pause request. If non-zero, the block job is either paused,
|
|
|
357786 |
+ * or if busy == true will pause itself as soon as possible.
|
|
|
357786 |
+ */
|
|
|
357786 |
+ int pause_count;
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
+ * Set to false by the job while the coroutine has yielded and may be
|
|
|
357786 |
+ * re-entered by block_job_enter(). There may still be I/O or event loop
|
|
|
357786 |
+ * activity pending. Accessed under block_job_mutex (in blockjob.c).
|
|
|
357786 |
+ */
|
|
|
357786 |
+ bool busy;
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
+ * Set to true by the job while it is in a quiescent state, where
|
|
|
357786 |
+ * no I/O or event loop activity is pending.
|
|
|
357786 |
+ */
|
|
|
357786 |
+ bool paused;
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
* Set to true if the job should cancel itself. The flag must
|
|
|
357786 |
* always be tested just before toggling the busy flag from false
|
|
|
357786 |
* to true. After a job has been cancelled, it should only yield
|
|
|
357786 |
@@ -75,6 +107,23 @@ struct JobDriver {
|
|
|
357786 |
/** Enum describing the operation */
|
|
|
357786 |
JobType job_type;
|
|
|
357786 |
|
|
|
357786 |
+ /** Mandatory: Entrypoint for the Coroutine. */
|
|
|
357786 |
+ CoroutineEntry *start;
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
+ * If the callback is not NULL, it will be invoked when the job transitions
|
|
|
357786 |
+ * into the paused state. Paused jobs must not perform any asynchronous
|
|
|
357786 |
+ * I/O or event loop activity. This callback is used to quiesce jobs.
|
|
|
357786 |
+ */
|
|
|
357786 |
+ void coroutine_fn (*pause)(Job *job);
|
|
|
357786 |
+
|
|
|
357786 |
+ /**
|
|
|
357786 |
+ * If the callback is not NULL, it will be invoked when the job transitions
|
|
|
357786 |
+ * out of the paused state. Any asynchronous I/O or event loop activity
|
|
|
357786 |
+ * should be restarted from this callback.
|
|
|
357786 |
+ */
|
|
|
357786 |
+ void coroutine_fn (*resume)(Job *job);
|
|
|
357786 |
+
|
|
|
357786 |
/** Called when the job is freed */
|
|
|
357786 |
void (*free)(Job *job);
|
|
|
357786 |
};
|
|
|
357786 |
@@ -103,6 +152,30 @@ void job_ref(Job *job);
|
|
|
357786 |
*/
|
|
|
357786 |
void job_unref(Job *job);
|
|
|
357786 |
|
|
|
357786 |
+/**
|
|
|
357786 |
+ * Conditionally enter the job coroutine if the job is ready to run, not
|
|
|
357786 |
+ * already busy and fn() returns true. fn() is called while under the job_lock
|
|
|
357786 |
+ * critical section.
|
|
|
357786 |
+ */
|
|
|
357786 |
+void job_enter_cond(Job *job, bool(*fn)(Job *job));
|
|
|
357786 |
+
|
|
|
357786 |
+/**
|
|
|
357786 |
+ * @job: A job that has not yet been started.
|
|
|
357786 |
+ *
|
|
|
357786 |
+ * Begins execution of a job.
|
|
|
357786 |
+ * Takes ownership of one reference to the job object.
|
|
|
357786 |
+ */
|
|
|
357786 |
+void job_start(Job *job);
|
|
|
357786 |
+
|
|
|
357786 |
+/**
|
|
|
357786 |
+ * @job: The job that is ready to pause.
|
|
|
357786 |
+ *
|
|
|
357786 |
+ * Pause now if job_pause() has been called. Jobs that perform lots of I/O
|
|
|
357786 |
+ * must call this between requests so that the job can be paused.
|
|
|
357786 |
+ */
|
|
|
357786 |
+void coroutine_fn job_pause_point(Job *job);
|
|
|
357786 |
+
|
|
|
357786 |
+
|
|
|
357786 |
/** Returns the JobType of a given Job. */
|
|
|
357786 |
JobType job_type(const Job *job);
|
|
|
357786 |
|
|
|
357786 |
@@ -153,5 +226,8 @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
|
|
|
357786 |
|
|
|
357786 |
/* TODO To be removed from the public interface */
|
|
|
357786 |
void job_state_transition(Job *job, JobStatus s1);
|
|
|
357786 |
+void coroutine_fn job_do_yield(Job *job, uint64_t ns);
|
|
|
357786 |
+bool job_should_pause(Job *job);
|
|
|
357786 |
+bool job_started(Job *job);
|
|
|
357786 |
|
|
|
357786 |
#endif
|
|
|
357786 |
diff --git a/job.c b/job.c
|
|
|
357786 |
index c5a37fb..78497fd 100644
|
|
|
357786 |
--- a/job.c
|
|
|
357786 |
+++ b/job.c
|
|
|
357786 |
@@ -60,6 +60,26 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
|
|
|
357786 |
[JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
|
|
|
357786 |
};
|
|
|
357786 |
|
|
|
357786 |
+/* Right now, this mutex is only needed to synchronize accesses to job->busy
|
|
|
357786 |
+ * and job->sleep_timer, such as concurrent calls to job_do_yield and
|
|
|
357786 |
+ * job_enter. */
|
|
|
357786 |
+static QemuMutex job_mutex;
|
|
|
357786 |
+
|
|
|
357786 |
+static void job_lock(void)
|
|
|
357786 |
+{
|
|
|
357786 |
+ qemu_mutex_lock(&job_mutex);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+static void job_unlock(void)
|
|
|
357786 |
+{
|
|
|
357786 |
+ qemu_mutex_unlock(&job_mutex);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+static void __attribute__((__constructor__)) job_init(void)
|
|
|
357786 |
+{
|
|
|
357786 |
+ qemu_mutex_init(&job_mutex);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
/* TODO Make static once the whole state machine is in job.c */
|
|
|
357786 |
void job_state_transition(Job *job, JobStatus s1)
|
|
|
357786 |
{
|
|
|
357786 |
@@ -101,6 +121,16 @@ bool job_is_cancelled(Job *job)
|
|
|
357786 |
return job->cancelled;
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
+bool job_started(Job *job)
|
|
|
357786 |
+{
|
|
|
357786 |
+ return job->co;
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+bool job_should_pause(Job *job)
|
|
|
357786 |
+{
|
|
|
357786 |
+ return job->pause_count > 0;
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
Job *job_next(Job *job)
|
|
|
357786 |
{
|
|
|
357786 |
if (!job) {
|
|
|
357786 |
@@ -143,6 +173,9 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
|
|
|
357786 |
job->id = g_strdup(job_id);
|
|
|
357786 |
job->refcnt = 1;
|
|
|
357786 |
job->aio_context = ctx;
|
|
|
357786 |
+ job->busy = false;
|
|
|
357786 |
+ job->paused = true;
|
|
|
357786 |
+ job->pause_count = 1;
|
|
|
357786 |
|
|
|
357786 |
job_state_transition(job, JOB_STATUS_CREATED);
|
|
|
357786 |
|
|
|
357786 |
@@ -172,6 +205,110 @@ void job_unref(Job *job)
|
|
|
357786 |
}
|
|
|
357786 |
}
|
|
|
357786 |
|
|
|
357786 |
+void job_enter_cond(Job *job, bool(*fn)(Job *job))
|
|
|
357786 |
+{
|
|
|
357786 |
+ if (!job_started(job)) {
|
|
|
357786 |
+ return;
|
|
|
357786 |
+ }
|
|
|
357786 |
+ if (job->deferred_to_main_loop) {
|
|
|
357786 |
+ return;
|
|
|
357786 |
+ }
|
|
|
357786 |
+
|
|
|
357786 |
+ job_lock();
|
|
|
357786 |
+ if (job->busy) {
|
|
|
357786 |
+ job_unlock();
|
|
|
357786 |
+ return;
|
|
|
357786 |
+ }
|
|
|
357786 |
+
|
|
|
357786 |
+ if (fn && !fn(job)) {
|
|
|
357786 |
+ job_unlock();
|
|
|
357786 |
+ return;
|
|
|
357786 |
+ }
|
|
|
357786 |
+
|
|
|
357786 |
+ assert(!job->deferred_to_main_loop);
|
|
|
357786 |
+ timer_del(&job->sleep_timer);
|
|
|
357786 |
+ job->busy = true;
|
|
|
357786 |
+ job_unlock();
|
|
|
357786 |
+ aio_co_wake(job->co);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
|
|
|
357786 |
+ * Reentering the job coroutine with block_job_enter() before the timer has
|
|
|
357786 |
+ * expired is allowed and cancels the timer.
|
|
|
357786 |
+ *
|
|
|
357786 |
+ * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
|
|
|
357786 |
+ * called explicitly. */
|
|
|
357786 |
+void coroutine_fn job_do_yield(Job *job, uint64_t ns)
|
|
|
357786 |
+{
|
|
|
357786 |
+ job_lock();
|
|
|
357786 |
+ if (ns != -1) {
|
|
|
357786 |
+ timer_mod(&job->sleep_timer, ns);
|
|
|
357786 |
+ }
|
|
|
357786 |
+ job->busy = false;
|
|
|
357786 |
+ job_unlock();
|
|
|
357786 |
+ qemu_coroutine_yield();
|
|
|
357786 |
+
|
|
|
357786 |
+ /* Set by job_enter_cond() before re-entering the coroutine. */
|
|
|
357786 |
+ assert(job->busy);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+void coroutine_fn job_pause_point(Job *job)
|
|
|
357786 |
+{
|
|
|
357786 |
+ assert(job && job_started(job));
|
|
|
357786 |
+
|
|
|
357786 |
+ if (!job_should_pause(job)) {
|
|
|
357786 |
+ return;
|
|
|
357786 |
+ }
|
|
|
357786 |
+ if (job_is_cancelled(job)) {
|
|
|
357786 |
+ return;
|
|
|
357786 |
+ }
|
|
|
357786 |
+
|
|
|
357786 |
+ if (job->driver->pause) {
|
|
|
357786 |
+ job->driver->pause(job);
|
|
|
357786 |
+ }
|
|
|
357786 |
+
|
|
|
357786 |
+ if (job_should_pause(job) && !job_is_cancelled(job)) {
|
|
|
357786 |
+ JobStatus status = job->status;
|
|
|
357786 |
+ job_state_transition(job, status == JOB_STATUS_READY
|
|
|
357786 |
+ ? JOB_STATUS_STANDBY
|
|
|
357786 |
+ : JOB_STATUS_PAUSED);
|
|
|
357786 |
+ job->paused = true;
|
|
|
357786 |
+ job_do_yield(job, -1);
|
|
|
357786 |
+ job->paused = false;
|
|
|
357786 |
+ job_state_transition(job, status);
|
|
|
357786 |
+ }
|
|
|
357786 |
+
|
|
|
357786 |
+ if (job->driver->resume) {
|
|
|
357786 |
+ job->driver->resume(job);
|
|
|
357786 |
+ }
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+/**
|
|
|
357786 |
+ * All jobs must allow a pause point before entering their job proper. This
|
|
|
357786 |
+ * ensures that jobs can be paused prior to being started, then resumed later.
|
|
|
357786 |
+ */
|
|
|
357786 |
+static void coroutine_fn job_co_entry(void *opaque)
|
|
|
357786 |
+{
|
|
|
357786 |
+ Job *job = opaque;
|
|
|
357786 |
+
|
|
|
357786 |
+ assert(job && job->driver && job->driver->start);
|
|
|
357786 |
+ job_pause_point(job);
|
|
|
357786 |
+ job->driver->start(job);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
+
|
|
|
357786 |
+void job_start(Job *job)
|
|
|
357786 |
+{
|
|
|
357786 |
+ assert(job && !job_started(job) && job->paused &&
|
|
|
357786 |
+ job->driver && job->driver->start);
|
|
|
357786 |
+ job->co = qemu_coroutine_create(job_co_entry, job);
|
|
|
357786 |
+ job->pause_count--;
|
|
|
357786 |
+ job->busy = true;
|
|
|
357786 |
+ job->paused = false;
|
|
|
357786 |
+ job_state_transition(job, JOB_STATUS_RUNNING);
|
|
|
357786 |
+ aio_co_enter(job->aio_context, job->co);
|
|
|
357786 |
+}
|
|
|
357786 |
+
|
|
|
357786 |
typedef struct {
|
|
|
357786 |
Job *job;
|
|
|
357786 |
JobDeferToMainLoopFn *fn;
|
|
|
357786 |
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
|
|
|
357786 |
index 4f8cba8..c9f2f9b 100644
|
|
|
357786 |
--- a/tests/test-bdrv-drain.c
|
|
|
357786 |
+++ b/tests/test-bdrv-drain.c
|
|
|
357786 |
@@ -524,8 +524,8 @@ BlockJobDriver test_job_driver = {
|
|
|
357786 |
.job_driver = {
|
|
|
357786 |
.instance_size = sizeof(TestBlockJob),
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = test_job_start,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = test_job_start,
|
|
|
357786 |
.complete = test_job_complete,
|
|
|
357786 |
};
|
|
|
357786 |
|
|
|
357786 |
@@ -549,47 +549,47 @@ static void test_blockjob_common(enum drain_type drain_type)
|
|
|
357786 |
job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL,
|
|
|
357786 |
0, 0, NULL, NULL, &error_abort);
|
|
|
357786 |
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 0);
|
|
|
357786 |
- g_assert_false(job->paused);
|
|
|
357786 |
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 0);
|
|
|
357786 |
+ g_assert_false(job->job.paused);
|
|
|
357786 |
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
|
|
|
357786 |
|
|
|
357786 |
do_drain_begin(drain_type, src);
|
|
|
357786 |
|
|
|
357786 |
if (drain_type == BDRV_DRAIN_ALL) {
|
|
|
357786 |
/* bdrv_drain_all() drains both src and target */
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 2);
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 2);
|
|
|
357786 |
} else {
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 1);
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 1);
|
|
|
357786 |
}
|
|
|
357786 |
/* XXX We don't wait until the job is actually paused. Is this okay? */
|
|
|
357786 |
- /* g_assert_true(job->paused); */
|
|
|
357786 |
- g_assert_false(job->busy); /* The job is paused */
|
|
|
357786 |
+ /* g_assert_true(job->job.paused); */
|
|
|
357786 |
+ g_assert_false(job->job.busy); /* The job is paused */
|
|
|
357786 |
|
|
|
357786 |
do_drain_end(drain_type, src);
|
|
|
357786 |
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 0);
|
|
|
357786 |
- g_assert_false(job->paused);
|
|
|
357786 |
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 0);
|
|
|
357786 |
+ g_assert_false(job->job.paused);
|
|
|
357786 |
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
|
|
|
357786 |
|
|
|
357786 |
do_drain_begin(drain_type, target);
|
|
|
357786 |
|
|
|
357786 |
if (drain_type == BDRV_DRAIN_ALL) {
|
|
|
357786 |
/* bdrv_drain_all() drains both src and target */
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 2);
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 2);
|
|
|
357786 |
} else {
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 1);
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 1);
|
|
|
357786 |
}
|
|
|
357786 |
/* XXX We don't wait until the job is actually paused. Is this okay? */
|
|
|
357786 |
- /* g_assert_true(job->paused); */
|
|
|
357786 |
- g_assert_false(job->busy); /* The job is paused */
|
|
|
357786 |
+ /* g_assert_true(job->job.paused); */
|
|
|
357786 |
+ g_assert_false(job->job.busy); /* The job is paused */
|
|
|
357786 |
|
|
|
357786 |
do_drain_end(drain_type, target);
|
|
|
357786 |
|
|
|
357786 |
- g_assert_cmpint(job->pause_count, ==, 0);
|
|
|
357786 |
- g_assert_false(job->paused);
|
|
|
357786 |
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
|
|
|
357786 |
+ g_assert_cmpint(job->job.pause_count, ==, 0);
|
|
|
357786 |
+ g_assert_false(job->job.paused);
|
|
|
357786 |
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
|
|
|
357786 |
|
|
|
357786 |
ret = block_job_complete_sync(job, &error_abort);
|
|
|
357786 |
g_assert_cmpint(ret, ==, 0);
|
|
|
357786 |
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
|
|
|
357786 |
index c03f966..323e154 100644
|
|
|
357786 |
--- a/tests/test-blockjob-txn.c
|
|
|
357786 |
+++ b/tests/test-blockjob-txn.c
|
|
|
357786 |
@@ -78,8 +78,8 @@ static const BlockJobDriver test_block_job_driver = {
|
|
|
357786 |
.job_driver = {
|
|
|
357786 |
.instance_size = sizeof(TestBlockJob),
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = test_block_job_run,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = test_block_job_run,
|
|
|
357786 |
};
|
|
|
357786 |
|
|
|
357786 |
/* Create a block job that completes with a given return code after a given
|
|
|
357786 |
@@ -125,7 +125,7 @@ static void test_single_job(int expected)
|
|
|
357786 |
|
|
|
357786 |
txn = block_job_txn_new();
|
|
|
357786 |
job = test_block_job_start(1, true, expected, &result, txn);
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
|
|
|
357786 |
if (expected == -ECANCELED) {
|
|
|
357786 |
block_job_cancel(job, false);
|
|
|
357786 |
@@ -165,8 +165,8 @@ static void test_pair_jobs(int expected1, int expected2)
|
|
|
357786 |
txn = block_job_txn_new();
|
|
|
357786 |
job1 = test_block_job_start(1, true, expected1, &result1, txn);
|
|
|
357786 |
job2 = test_block_job_start(2, true, expected2, &result2, txn);
|
|
|
357786 |
- block_job_start(job1);
|
|
|
357786 |
- block_job_start(job2);
|
|
|
357786 |
+ job_start(&job1->job);
|
|
|
357786 |
+ job_start(&job2->job);
|
|
|
357786 |
|
|
|
357786 |
/* Release our reference now to trigger as many nice
|
|
|
357786 |
* use-after-free bugs as possible.
|
|
|
357786 |
@@ -227,8 +227,8 @@ static void test_pair_jobs_fail_cancel_race(void)
|
|
|
357786 |
txn = block_job_txn_new();
|
|
|
357786 |
job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
|
|
|
357786 |
job2 = test_block_job_start(2, false, 0, &result2, txn);
|
|
|
357786 |
- block_job_start(job1);
|
|
|
357786 |
- block_job_start(job2);
|
|
|
357786 |
+ job_start(&job1->job);
|
|
|
357786 |
+ job_start(&job2->job);
|
|
|
357786 |
|
|
|
357786 |
block_job_cancel(job1, false);
|
|
|
357786 |
|
|
|
357786 |
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
|
|
|
357786 |
index 5f43bd7..1d18325 100644
|
|
|
357786 |
--- a/tests/test-blockjob.c
|
|
|
357786 |
+++ b/tests/test-blockjob.c
|
|
|
357786 |
@@ -199,8 +199,8 @@ static const BlockJobDriver test_cancel_driver = {
|
|
|
357786 |
.job_driver = {
|
|
|
357786 |
.instance_size = sizeof(CancelJob),
|
|
|
357786 |
.free = block_job_free,
|
|
|
357786 |
+ .start = cancel_job_start,
|
|
|
357786 |
},
|
|
|
357786 |
- .start = cancel_job_start,
|
|
|
357786 |
.complete = cancel_job_complete,
|
|
|
357786 |
};
|
|
|
357786 |
|
|
|
357786 |
@@ -254,7 +254,7 @@ static void test_cancel_running(void)
|
|
|
357786 |
|
|
|
357786 |
s = create_common(&job;;
|
|
|
357786 |
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
assert(job->job.status == JOB_STATUS_RUNNING);
|
|
|
357786 |
|
|
|
357786 |
cancel_common(s);
|
|
|
357786 |
@@ -267,7 +267,7 @@ static void test_cancel_paused(void)
|
|
|
357786 |
|
|
|
357786 |
s = create_common(&job;;
|
|
|
357786 |
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
assert(job->job.status == JOB_STATUS_RUNNING);
|
|
|
357786 |
|
|
|
357786 |
block_job_user_pause(job, &error_abort);
|
|
|
357786 |
@@ -284,7 +284,7 @@ static void test_cancel_ready(void)
|
|
|
357786 |
|
|
|
357786 |
s = create_common(&job;;
|
|
|
357786 |
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
assert(job->job.status == JOB_STATUS_RUNNING);
|
|
|
357786 |
|
|
|
357786 |
s->should_converge = true;
|
|
|
357786 |
@@ -301,7 +301,7 @@ static void test_cancel_standby(void)
|
|
|
357786 |
|
|
|
357786 |
s = create_common(&job;;
|
|
|
357786 |
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
assert(job->job.status == JOB_STATUS_RUNNING);
|
|
|
357786 |
|
|
|
357786 |
s->should_converge = true;
|
|
|
357786 |
@@ -322,7 +322,7 @@ static void test_cancel_pending(void)
|
|
|
357786 |
|
|
|
357786 |
s = create_common(&job;;
|
|
|
357786 |
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
assert(job->job.status == JOB_STATUS_RUNNING);
|
|
|
357786 |
|
|
|
357786 |
s->should_converge = true;
|
|
|
357786 |
@@ -346,7 +346,7 @@ static void test_cancel_concluded(void)
|
|
|
357786 |
|
|
|
357786 |
s = create_common(&job;;
|
|
|
357786 |
|
|
|
357786 |
- block_job_start(job);
|
|
|
357786 |
+ job_start(&job->job);
|
|
|
357786 |
assert(job->job.status == JOB_STATUS_RUNNING);
|
|
|
357786 |
|
|
|
357786 |
s->should_converge = true;
|
|
|
357786 |
--
|
|
|
357786 |
1.8.3.1
|
|
|
357786 |
|