@@ -336,6 +336,13 @@ static bool backup_cancel(Job *job, bool force)
return true;
}
+static bool backup_change(Job *job, JobChangeOptions *opts, Error **errp)
+{
+ BlockJob *bjob = container_of(job, BlockJob, job);
+
+ return block_job_change(bjob, &opts->u.backup, errp);
+}
+
static const BlockJobDriver backup_job_driver = {
.job_driver = {
.instance_size = sizeof(BackupBlockJob),
@@ -348,6 +355,7 @@ static const BlockJobDriver backup_job_driver = {
.clean = backup_clean,
.pause = backup_pause,
.cancel = backup_cancel,
+ .change = backup_change,
},
.set_speed = backup_set_speed,
};
@@ -204,6 +204,13 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
return 0;
}
+static bool commit_change(Job *job, JobChangeOptions *opts, Error **errp)
+{
+ BlockJob *bjob = container_of(job, BlockJob, job);
+
+ return block_job_change(bjob, &opts->u.commit, errp);
+}
+
static const BlockJobDriver commit_job_driver = {
.job_driver = {
.instance_size = sizeof(CommitBlockJob),
@@ -213,7 +220,8 @@ static const BlockJobDriver commit_job_driver = {
.run = commit_run,
.prepare = commit_prepare,
.abort = commit_abort,
- .clean = commit_clean
+ .clean = commit_clean,
+ .change = commit_change,
},
};
@@ -1251,41 +1251,45 @@ static bool commit_active_cancel(Job *job, bool force)
return force || !job_is_ready(job);
}
-static void mirror_change(Job *job, JobChangeOptions *opts, Error **errp)
+static bool mirror_change(Job *job, JobChangeOptions *opts, Error **errp)
{
+ BlockJob *bjob = container_of(job, BlockJob, job);
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
JobChangeOptionsMirror *change_opts = &opts->u.mirror;
- MirrorCopyMode current;
-
- /*
- * The implementation relies on the fact that copy_mode is only written
- * under the BQL. Otherwise, further synchronization would be required.
- */
+ MirrorCopyMode old_mode;
GLOBAL_STATE_CODE();
- if (!change_opts->has_copy_mode) {
- /* Nothing to do */
- return;
- }
+ if (change_opts->has_copy_mode) {
+ old_mode = qatomic_read(&s->copy_mode);
- if (qatomic_read(&s->copy_mode) == change_opts->copy_mode) {
- return;
- }
+ if (old_mode != change_opts->copy_mode) {
+ if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) {
+ error_setg(errp, "Change to copy mode '%s' is not implemented",
+ MirrorCopyMode_str(change_opts->copy_mode));
+ return false;
+ }
- if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) {
- error_setg(errp, "Change to copy mode '%s' is not implemented",
- MirrorCopyMode_str(change_opts->copy_mode));
- return;
+ if (old_mode != MIRROR_COPY_MODE_BACKGROUND) {
+ error_setg(errp, "Expected current copy mode '%s', got '%s'",
+ MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND),
+ MirrorCopyMode_str(old_mode));
+ return false;
+ }
+ }
}
- current = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND,
- change_opts->copy_mode);
- if (current != MIRROR_COPY_MODE_BACKGROUND) {
- error_setg(errp, "Expected current copy mode '%s', got '%s'",
- MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND),
- MirrorCopyMode_str(current));
+ if (!block_job_change(bjob, qapi_JobChangeOptionsMirror_base(change_opts),
+ errp))
+ {
+ return false;
}
+
+ old_mode = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND,
+ change_opts->copy_mode);
+ assert(old_mode == MIRROR_COPY_MODE_BACKGROUND);
+
+ return true;
}
static void mirror_query(BlockJob *job, BlockJobInfo *info)
@@ -1315,6 +1319,13 @@ static const BlockJobDriver mirror_job_driver = {
.query = mirror_query,
};
+static bool commit_active_change(Job *job, JobChangeOptions *opts, Error **errp)
+{
+ BlockJob *bjob = container_of(job, BlockJob, job);
+
+ return block_job_change(bjob, &opts->u.commit, errp);
+}
+
static const BlockJobDriver commit_active_job_driver = {
.job_driver = {
.instance_size = sizeof(MirrorBlockJob),
@@ -1327,6 +1338,7 @@ static const BlockJobDriver commit_active_job_driver = {
.pause = mirror_pause,
.complete = mirror_complete,
.cancel = commit_active_cancel,
+ .change = commit_active_change,
},
.drained_poll = mirror_drained_poll,
};
@@ -239,6 +239,13 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
return error;
}
+static bool stream_change(Job *job, JobChangeOptions *opts, Error **errp)
+{
+ BlockJob *bjob = container_of(job, BlockJob, job);
+
+ return block_job_change(bjob, &opts->u.stream, errp);
+}
+
static const BlockJobDriver stream_job_driver = {
.job_driver = {
.instance_size = sizeof(StreamBlockJob),
@@ -248,6 +255,7 @@ static const BlockJobDriver stream_job_driver = {
.prepare = stream_prepare,
.clean = stream_clean,
.user_resume = block_job_user_resume,
+ .change = stream_change,
},
};
@@ -312,6 +312,19 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
return block_job_set_speed_locked(job, speed, errp);
}
+bool block_job_change(BlockJob *job, JobChangeOptionsBlockJob *opts,
+ Error **errp)
+{
+ GLOBAL_STATE_CODE();
+
+ if (!opts->has_speed) {
+ /* Nothing to do */
+ return true;
+ }
+
+ return block_job_set_speed(job, opts->speed, errp);
+}
+
void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n)
{
IO_CODE();
@@ -224,4 +224,11 @@ bool block_job_is_internal(BlockJob *job);
*/
const BlockJobDriver *block_job_driver(BlockJob *job);
+/**
+ * Common part of .change handler for block-jobs.
+ * Applies changes described in opts to the job.
+ */
+bool block_job_change(BlockJob *job, JobChangeOptionsBlockJob *opts,
+ Error **errp);
+
#endif
@@ -313,7 +313,7 @@ struct JobDriver {
*
* Note that this can already be called before the job coroutine is running.
*/
- void (*change)(Job *job, JobChangeOptions *opts, Error **errp);
+ bool (*change)(Job *job, JobChangeOptions *opts, Error **errp);
/**
* Called when the job is freed.
@@ -3062,6 +3062,17 @@
{ 'command': 'block-job-finalize', 'data': { 'id': 'str' },
'allow-preconfig': true }
+##
+# @JobChangeOptionsBlockJob:
+#
+# @speed: Change job speed (in bytes per second). Supported for
+# for backup, mirror, commit and stream jobs.
+#
+# Since: 9.1
+##
+{ 'struct': 'JobChangeOptionsBlockJob',
+ 'data': { '*speed' : 'uint64' } }
+
##
# @JobChangeOptionsMirror:
#
@@ -3071,12 +3082,17 @@
# Since: 8.2
##
{ 'struct': 'JobChangeOptionsMirror',
+ 'base': 'JobChangeOptionsBlockJob',
'data': { '*copy-mode' : 'MirrorCopyMode' } }
+
##
# @JobChangeOptions:
#
-# Block job options that can be changed after job creation.
+# Job options that can be changed after job creation. When option is
+# not specified the corresponding job parameter remains unchanged.
+# The change is transactional: on success all changes are applied
+# successfully, on failure nothing is changed.
#
# @id: The job identifier
#
@@ -3093,7 +3109,10 @@
'base': { 'id': 'str',
'*type': { 'type': 'JobType', 'features': ['deprecated'] } },
'discriminator': 'JobType',
- 'data': { 'mirror': 'JobChangeOptionsMirror' } }
+ 'data': { 'mirror': 'JobChangeOptionsMirror',
+ 'backup': 'JobChangeOptionsBlockJob',
+ 'stream': 'JobChangeOptionsBlockJob',
+ 'commit': 'JobChangeOptionsBlockJob' } }
##
# @block-job-change:
Allow change job speed through job-change command. Old block-job-set-speed would be deprecated soon. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- block/backup.c | 8 ++++++ block/commit.c | 10 ++++++- block/mirror.c | 60 ++++++++++++++++++++++++---------------- block/stream.c | 8 ++++++ blockjob.c | 13 +++++++++ include/block/blockjob.h | 7 +++++ include/qemu/job.h | 2 +- qapi/block-core.json | 23 +++++++++++++-- 8 files changed, 103 insertions(+), 28 deletions(-)