From nobody Tue Feb 10 05:10:38 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1517019573639858.65983806474; Fri, 26 Jan 2018 18:19:33 -0800 (PST) Received: from localhost ([::1]:44162 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1efG5a-0006Ue-Uu for importer@patchew.org; Fri, 26 Jan 2018 21:19:30 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37437) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1efFsB-0001B0-4d for qemu-devel@nongnu.org; Fri, 26 Jan 2018 21:05:43 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1efFs9-0008UW-J3 for qemu-devel@nongnu.org; Fri, 26 Jan 2018 21:05:39 -0500 Received: from mx1.redhat.com ([209.132.183.28]:43398) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1efFs5-0008Qf-U1; Fri, 26 Jan 2018 21:05:34 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3715255371; Sat, 27 Jan 2018 02:05:33 +0000 (UTC) Received: from probe.bos.redhat.com (dhcp-17-231.bos.redhat.com [10.18.17.231]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5C8785457C; Sat, 27 Jan 2018 02:05:32 +0000 (UTC) From: John Snow To: qemu-block@nongnu.org Date: Fri, 26 Jan 2018 21:05:13 -0500 Message-Id: <20180127020515.27137-13-jsnow@redhat.com> In-Reply-To: <20180127020515.27137-1-jsnow@redhat.com> References: <20180127020515.27137-1-jsnow@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Sat, 27 Jan 2018 02:05:33 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC v3 12/14] blockjobs: add block-job-finalize X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, John Snow , pkrempa@redhat.com, jtc@redhat.com, qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: John Snow --- block/trace-events | 1 + blockdev.c | 14 ++++++++++ blockjob.c | 72 +++++++++++++++++++++++++++++++++++++++-----= ---- include/block/blockjob.h | 17 ++++++++++++ qapi/block-core.json | 18 ++++++++++++ 5 files changed, 109 insertions(+), 13 deletions(-) diff --git a/block/trace-events b/block/trace-events index 8f61566770..d3be349489 100644 --- a/block/trace-events +++ b/block/trace-events @@ -46,6 +46,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" =20 diff --git a/blockdev.c b/blockdev.c index 5e8edff322..d387ef6ec0 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3849,6 +3849,20 @@ void qmp_block_job_complete(const char *device, Erro= r **errp) aio_context_release(aio_context); } =20 +void qmp_block_job_finalize(const char *id, Error **errp) +{ + AioContext *aio_context; + BlockJob *job =3D 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 d31b65273c..b8d6dd3bb4 100644 --- a/blockjob.c +++ b/blockjob.c @@ -61,6 +61,7 @@ enum BlockJobVerb { BLOCK_JOB_VERB_RESUME, BLOCK_JOB_VERB_SET_SPEED, BLOCK_JOB_VERB_COMPLETE, + BLOCK_JOB_VERB_FINALIZE, BLOCK_JOB_VERB_DISMISS, BLOCK_JOB_VERB__MAX }; @@ -72,6 +73,7 @@ bool BlockJobVerb[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__= MAX] =3D { [BLOCK_JOB_VERB_RESUME] =3D {0, 0, 0, 1, 0, 0, 0, 0}, [BLOCK_JOB_VERB_SET_SPEED] =3D {0, 1, 1, 1, 1, 0, 0, 0}, [BLOCK_JOB_VERB_COMPLETE] =3D {0, 0, 0, 0, 1, 0, 0, 0}, + [BLOCK_JOB_VERB_FINALIZE] =3D {0, 0, 0, 0, 0, 0, 1, 0}, [BLOCK_JOB_VERB_DISMISS] =3D {0, 0, 0, 0, 0, 0, 0, 1}, }; =20 @@ -459,6 +461,15 @@ static void block_job_completed_single(BlockJob *job) block_job_unref(job); } =20 +static void block_job_await_finalization(BlockJob *job) +{ + if (!job->manual) { + block_job_completed_single(job); + } else { + block_job_event_pending(job); + } +} + static void block_job_cancel_async(BlockJob *job) { if (job->iostatus !=3D BLOCK_DEVICE_IO_STATUS_OK) { @@ -558,6 +569,19 @@ static void block_job_completed_txn_abort(BlockJob *jo= b) block_job_txn_unref(txn); } =20 +static void block_job_txn_apply(BlockJobTxn *txn, void fn(BlockJob *)) +{ + AioContext *ctx; + BlockJob *job, *next; + + QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { + ctx =3D blk_get_aio_context(job->blk); + aio_context_acquire(ctx); + fn(job); + aio_context_release(ctx); + } +} + static void block_job_completed_txn_success(BlockJob *job) { AioContext *ctx; @@ -590,14 +614,9 @@ static void block_job_completed_txn_success(BlockJob *= job) } } =20 - /* We are the last completed job, commit the transaction. */ - QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { - ctx =3D blk_get_aio_context(other_job->blk); - aio_context_acquire(ctx); - assert(other_job->ret =3D=3D 0); - block_job_completed_single(other_job); - aio_context_release(ctx); - } + /* We are the last completed job, either commit the transaction + * or prepare for finalization via user intervention. */ + block_job_txn_apply(txn, block_job_await_finalization); } =20 /* Assumes the block_job_mutex is held */ @@ -606,6 +625,15 @@ static bool block_job_timer_pending(BlockJob *job) return timer_pending(&job->sleep_timer); } =20 +static void block_job_txn_completed(BlockJob *job, int ret) +{ + if (ret < 0 || block_job_is_cancelled(job)) { + block_job_completed_txn_abort(job); + } else { + block_job_completed_txn_success(job); + } +} + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { Error *local_err =3D NULL; @@ -644,6 +672,27 @@ void block_job_complete(BlockJob *job, Error **errp) job->driver->complete(job, errp); } =20 +void block_job_finalize(BlockJob *job, Error **errp) +{ + assert(job->id); + if (!job->manual) { + error_setg(errp, "The block job '%s' was not started with " + "\'manual\': true, and so cannot be finalized as it wil= l" + "do so automatically upon finishing its task", job->id); + return; + } else if (job->status !=3D BLOCK_JOB_STATUS_PENDING) { + error_setg(errp, "The active block job '%s' is not yet awaiting " + "finalization and cannot be finalized", job->id); + return; + } + + if (!job->txn) { + block_job_completed_single(job); + } else { + block_job_txn_apply(job->txn, block_job_completed_single); + } +} + void block_job_dismiss(BlockJob **jobptr, Error **errp) { BlockJob *job =3D *jobptr; @@ -807,7 +856,6 @@ static void block_job_event_waiting(BlockJob *job) &error_abort); } =20 -__attribute__((__unused__)) /* FIXME */ static void block_job_event_pending(BlockJob *job) { if (block_job_is_internal(job) || !job->manual) { @@ -952,12 +1000,10 @@ void block_job_completed(BlockJob *job, int ret) job->completed =3D true; job->ret =3D ret; if (!job->txn) { - block_job_completed_single(job); - } else if (ret < 0 || block_job_is_cancelled(job)) { - block_job_completed_txn_abort(job); + block_job_await_finalization(job); } else { block_job_event_waiting(job); - block_job_completed_txn_success(job); + block_job_txn_completed(job, ret); } } =20 diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 5f73fc8831..188853ca77 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); =20 + +/** + * 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 1f2eb39810..bd4458bf57 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2219,6 +2219,24 @@ ## { 'command': 'block-job-dismiss', 'data': { 'id': 'str' } } =20 +## +# @block-job-finalize: +# +# Once a job that has manual=3Dtrue 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 inst= ruct +# 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: # --=20 2.14.3