diff options
-rw-r--r-- | block/trace-events | 1 | ||||
-rw-r--r-- | blockdev.c | 14 | ||||
-rw-r--r-- | blockjob.c | 60 | ||||
-rw-r--r-- | include/block/blockjob.h | 17 | ||||
-rw-r--r-- | qapi/block-core.json | 23 |
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. |