summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/trace-events1
-rw-r--r--blockdev.c14
-rw-r--r--blockjob.c60
-rw-r--r--include/block/blockjob.h17
-rw-r--r--qapi/block-core.json23
5 files changed, 98 insertions, 17 deletions
diff --git a/block/trace-events b/block/trace-events
index 1c6edb0b5a..f8c50b4063 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -53,6 +53,7 @@ qmp_block_job_cancel(void *job) "job %p"
qmp_block_job_pause(void *job) "job %p"
qmp_block_job_resume(void *job) "job %p"
qmp_block_job_complete(void *job) "job %p"
+qmp_block_job_finalize(void *job) "job %p"
qmp_block_job_dismiss(void *job) "job %p"
qmp_block_stream(void *bs, void *job) "bs %p job %p"
diff --git a/blockdev.c b/blockdev.c
index fc35d7c7bb..96bbac6c53 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3872,6 +3872,20 @@ void qmp_block_job_complete(const char *device, Error **errp)
aio_context_release(aio_context);
}
+void qmp_block_job_finalize(const char *id, Error **errp)
+{
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(id, &aio_context, errp);
+
+ if (!job) {
+ return;
+ }
+
+ trace_qmp_block_job_finalize(job);
+ block_job_finalize(job, errp);
+ aio_context_release(aio_context);
+}
+
void qmp_block_job_dismiss(const char *id, Error **errp)
{
AioContext *aio_context;
diff --git a/blockjob.c b/blockjob.c
index 0a22b924fe..d6b8bea514 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -65,6 +65,7 @@ bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
[BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
[BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
[BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
+ [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
[BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
};
@@ -449,7 +450,7 @@ static void block_job_clean(BlockJob *job)
}
}
-static int block_job_completed_single(BlockJob *job)
+static int block_job_finalize_single(BlockJob *job)
{
assert(job->completed);
@@ -590,18 +591,36 @@ static void block_job_completed_txn_abort(BlockJob *job)
assert(other_job->cancelled);
block_job_finish_sync(other_job, NULL, NULL);
}
- block_job_completed_single(other_job);
+ block_job_finalize_single(other_job);
aio_context_release(ctx);
}
block_job_txn_unref(txn);
}
+static int block_job_needs_finalize(BlockJob *job)
+{
+ return !job->auto_finalize;
+}
+
+static void block_job_do_finalize(BlockJob *job)
+{
+ int rc;
+ assert(job && job->txn);
+
+ /* prepare the transaction to complete */
+ rc = block_job_txn_apply(job->txn, block_job_prepare, true);
+ if (rc) {
+ block_job_completed_txn_abort(job);
+ } else {
+ block_job_txn_apply(job->txn, block_job_finalize_single, true);
+ }
+}
+
static void block_job_completed_txn_success(BlockJob *job)
{
BlockJobTxn *txn = job->txn;
BlockJob *other_job;
- int rc = 0;
block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
@@ -616,16 +635,12 @@ static void block_job_completed_txn_success(BlockJob *job)
assert(other_job->ret == 0);
}
- /* Jobs may require some prep-work to complete without failure */
- rc = block_job_txn_apply(txn, block_job_prepare, true);
- if (rc) {
- block_job_completed_txn_abort(job);
- return;
- }
-
- /* We are the last completed job, commit the transaction. */
block_job_txn_apply(txn, block_job_event_pending, false);
- block_job_txn_apply(txn, block_job_completed_single, true);
+
+ /* If no jobs need manual finalization, automatically do so */
+ if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
+ block_job_do_finalize(job);
+ }
}
/* Assumes the block_job_mutex is held */
@@ -677,6 +692,15 @@ void block_job_complete(BlockJob *job, Error **errp)
job->driver->complete(job, errp);
}
+void block_job_finalize(BlockJob *job, Error **errp)
+{
+ assert(job && job->id && job->txn);
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
+ return;
+ }
+ block_job_do_finalize(job);
+}
+
void block_job_dismiss(BlockJob **jobptr, Error **errp)
{
BlockJob *job = *jobptr;
@@ -727,11 +751,15 @@ void block_job_cancel(BlockJob *job)
{
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
block_job_do_dismiss(job);
- } else if (block_job_started(job)) {
- block_job_cancel_async(job);
- block_job_enter(job);
- } else {
+ return;
+ }
+ block_job_cancel_async(job);
+ if (!block_job_started(job)) {
block_job_completed(job, -ECANCELED);
+ } else if (job->deferred_to_main_loop) {
+ block_job_completed_txn_abort(job);
+ } else {
+ block_job_enter(job);
}
}
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 7c8d51effa..978274ed2b 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -244,6 +244,23 @@ void block_job_cancel(BlockJob *job);
*/
void block_job_complete(BlockJob *job, Error **errp);
+
+/**
+ * block_job_finalize:
+ * @job: The job to fully commit and finish.
+ * @errp: Error object.
+ *
+ * For jobs that have finished their work and are pending
+ * awaiting explicit acknowledgement to commit their work,
+ * This will commit that work.
+ *
+ * FIXME: Make the below statement universally true:
+ * For jobs that support the manual workflow mode, all graph
+ * changes that occur as a result will occur after this command
+ * and before a successful reply.
+ */
+void block_job_finalize(BlockJob *job, Error **errp);
+
/**
* block_job_dismiss:
* @job: The job to be dismissed.
diff --git a/qapi/block-core.json b/qapi/block-core.json
index a3e64bb392..53ec9c31e9 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -975,10 +975,13 @@
#
# @dismiss: see @block-job-dismiss
#
+# @finalize: see @block-job-finalize
+#
# Since: 2.12
##
{ 'enum': 'BlockJobVerb',
- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss' ] }
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
+ 'finalize' ] }
##
# @BlockJobStatus:
@@ -2278,6 +2281,24 @@
{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } }
##
+# @block-job-finalize:
+#
+# Once a job that has manual=true reaches the pending state, it can be
+# instructed to finalize any graph changes and do any necessary cleanup
+# via this command.
+# For jobs in a transaction, instructing one job to finalize will force
+# ALL jobs in the transaction to finalize, so it is only necessary to instruct
+# a single member job to finalize.
+#
+# @id: The job identifier.
+#
+# Returns: Nothing on success
+#
+# Since: 2.12
+##
+{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } }
+
+##
# @BlockdevDiscardOptions:
#
# Determines how to handle discard requests.