From patchwork Wed May 9 16:26:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 10390073 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5D84660153 for ; Wed, 9 May 2018 16:48:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4B9C627F60 for ; Wed, 9 May 2018 16:48:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3EC6C27F81; Wed, 9 May 2018 16:48:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3380427F60 for ; Wed, 9 May 2018 16:48:07 +0000 (UTC) Received: from localhost ([::1]:57645 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fGSG6-00028K-DK for patchwork-qemu-devel@patchwork.kernel.org; Wed, 09 May 2018 12:48:06 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52437) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fGRwO-0005eh-Op for qemu-devel@nongnu.org; Wed, 09 May 2018 12:27:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fGRwM-0002Gv-5k for qemu-devel@nongnu.org; Wed, 09 May 2018 12:27:44 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:50608 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fGRwG-00022Q-65; Wed, 09 May 2018 12:27:36 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9601B4059FE1; Wed, 9 May 2018 16:27:35 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-117-193.ams2.redhat.com [10.36.117.193]) by smtp.corp.redhat.com (Postfix) with ESMTP id 47D3F2023433; Wed, 9 May 2018 16:27:34 +0000 (UTC) From: Kevin Wolf To: qemu-block@nongnu.org Date: Wed, 9 May 2018 18:26:15 +0200 Message-Id: <20180509162637.15575-21-kwolf@redhat.com> In-Reply-To: <20180509162637.15575-1-kwolf@redhat.com> References: <20180509162637.15575-1-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Wed, 09 May 2018 16:27:35 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Wed, 09 May 2018 16:27:35 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'kwolf@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH 20/42] job: Move pause/resume functions to Job 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, qemu-devel@nongnu.org, jcody@redhat.com, armbru@redhat.com, mreitz@redhat.com, jsnow@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP While we already moved the state related to job pausing to Job, the functions to do were still BlockJob only. This commit moves them over to Job. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Reviewed-by: John Snow --- include/block/blockjob.h | 32 ------------------- include/block/blockjob_int.h | 7 +++++ include/qemu/job.h | 37 ++++++++++++++++++++++ block/backup.c | 1 + block/commit.c | 1 + block/mirror.c | 2 ++ block/stream.c | 1 + blockdev.c | 6 ++-- blockjob.c | 73 ++++++++++---------------------------------- job.c | 51 +++++++++++++++++++++++++++++++ tests/test-bdrv-drain.c | 1 + tests/test-blockjob-txn.c | 1 + tests/test-blockjob.c | 6 ++-- 13 files changed, 125 insertions(+), 94 deletions(-) diff --git a/include/block/blockjob.h b/include/block/blockjob.h index c0448206e2..ce136ff2ac 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -57,12 +57,6 @@ typedef struct BlockJob { bool force; /** - * Set to true if the job is paused by user. Can be unpaused with the - * block-job-resume QMP command. - */ - bool user_paused; - - /** * Set to true when the job is ready to be completed. */ bool ready; @@ -248,32 +242,6 @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining); BlockJobInfo *block_job_query(BlockJob *job, Error **errp); /** - * block_job_user_pause: - * @job: The job to be paused. - * - * Asynchronously pause the specified job. - * Do not allow a resume until a matching call to block_job_user_resume. - */ -void block_job_user_pause(BlockJob *job, Error **errp); - -/** - * block_job_paused: - * @job: The job to query. - * - * Returns true if the job is user-paused. - */ -bool block_job_user_paused(BlockJob *job); - -/** - * block_job_user_resume: - * @job: The job to be resumed. - * - * Resume the specified job. - * Must be paired with a preceding block_job_user_pause. - */ -void block_job_user_resume(BlockJob *job, Error **errp); - -/** * block_job_user_cancel: * @job: The job to be cancelled. * @force: Quit a job without waiting for data to be in sync. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 8937f5b163..7e705ae0e9 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -134,6 +134,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, void block_job_free(Job *job); /** + * block_job_user_resume: + * Callback to be used for JobDriver.user_resume in all block jobs. Resets the + * iostatus when the user resumes @job. + */ +void block_job_user_resume(Job *job); + +/** * block_job_yield: * @job: The job that calls the function. * diff --git a/include/qemu/job.h b/include/qemu/job.h index ddfa824315..0ba6cb3161 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -83,6 +83,12 @@ typedef struct Job { bool paused; /** + * Set to true if the job is paused by user. Can be unpaused with the + * block-job-resume QMP command. + */ + bool user_paused; + + /** * Set to true if the job should cancel itself. The flag must * always be tested just before toggling the busy flag from false * to true. After a job has been cancelled, it should only yield @@ -124,6 +130,12 @@ struct JobDriver { */ void coroutine_fn (*resume)(Job *job); + /** + * Called when the job is resumed by the user (i.e. user_paused becomes + * false). .user_resume is called before .resume. + */ + void (*user_resume)(Job *job); + /** Called when the job is freed */ void (*free)(Job *job); }; @@ -203,6 +215,31 @@ const char *job_type_str(Job *job); bool job_is_cancelled(Job *job); /** + * Request @job to pause at the next pause point. Must be paired with + * job_resume(). If the job is supposed to be resumed by user action, call + * job_user_pause() instead. + */ +void job_pause(Job *job); + +/** Resumes a @job paused with job_pause. */ +void job_resume(Job *job); + +/** + * Asynchronously pause the specified @job. + * Do not allow a resume until a matching call to job_user_resume. + */ +void job_user_pause(Job *job, Error **errp); + +/** Returns true if the job is user-paused. */ +bool job_user_paused(Job *job); + +/** + * Resume the specified @job. + * Must be paired with a preceding job_user_pause. + */ +void job_user_resume(Job *job, Error **errp); + +/** * Get the next element from the list of block jobs after @job, or the * first one if @job is %NULL. * diff --git a/block/backup.c b/block/backup.c index f3a4f7c898..4d011d5a5c 100644 --- a/block/backup.c +++ b/block/backup.c @@ -528,6 +528,7 @@ static const BlockJobDriver backup_job_driver = { .instance_size = sizeof(BackupBlockJob), .job_type = JOB_TYPE_BACKUP, .free = block_job_free, + .user_resume = block_job_user_resume, .start = backup_run, }, .commit = backup_commit, diff --git a/block/commit.c b/block/commit.c index 1c6cb6c298..c4a98e5804 100644 --- a/block/commit.c +++ b/block/commit.c @@ -220,6 +220,7 @@ static const BlockJobDriver commit_job_driver = { .instance_size = sizeof(CommitBlockJob), .job_type = JOB_TYPE_COMMIT, .free = block_job_free, + .user_resume = block_job_user_resume, .start = commit_run, }, }; diff --git a/block/mirror.c b/block/mirror.c index 6d54729557..80f068ca75 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -991,6 +991,7 @@ static const BlockJobDriver mirror_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_MIRROR, .free = block_job_free, + .user_resume = block_job_user_resume, .start = mirror_run, .pause = mirror_pause, }, @@ -1004,6 +1005,7 @@ static const BlockJobDriver commit_active_job_driver = { .instance_size = sizeof(MirrorBlockJob), .job_type = JOB_TYPE_COMMIT, .free = block_job_free, + .user_resume = block_job_user_resume, .start = mirror_run, .pause = mirror_pause, }, diff --git a/block/stream.c b/block/stream.c index 1faab02086..e81b488a22 100644 --- a/block/stream.c +++ b/block/stream.c @@ -214,6 +214,7 @@ static const BlockJobDriver stream_job_driver = { .job_type = JOB_TYPE_STREAM, .free = block_job_free, .start = stream_run, + .user_resume = block_job_user_resume, }, }; diff --git a/blockdev.c b/blockdev.c index c551fdf39a..3533c0dd6a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3844,7 +3844,7 @@ void qmp_block_job_cancel(const char *device, force = false; } - if (block_job_user_paused(job) && !force) { + if (job_user_paused(&job->job) && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); goto out; @@ -3866,7 +3866,7 @@ void qmp_block_job_pause(const char *device, Error **errp) } trace_qmp_block_job_pause(job); - block_job_user_pause(job, errp); + job_user_pause(&job->job, errp); aio_context_release(aio_context); } @@ -3880,7 +3880,7 @@ void qmp_block_job_resume(const char *device, Error **errp) } trace_qmp_block_job_resume(job); - block_job_user_resume(job, errp); + job_user_resume(&job->job, errp); aio_context_release(aio_context); } diff --git a/blockjob.c b/blockjob.c index 342ec74f66..a2b6bfc975 100644 --- a/blockjob.c +++ b/blockjob.c @@ -141,21 +141,6 @@ static void block_job_txn_del_job(BlockJob *job) } } -static void block_job_pause(BlockJob *job) -{ - job->job.pause_count++; -} - -static void block_job_resume(BlockJob *job) -{ - assert(job->job.pause_count > 0); - job->job.pause_count--; - if (job->job.pause_count) { - return; - } - block_job_enter(job); -} - static void block_job_attached_aio_context(AioContext *new_context, void *opaque); static void block_job_detach_aio_context(void *opaque); @@ -186,7 +171,7 @@ static void block_job_attached_aio_context(AioContext *new_context, job->driver->attached_aio_context(job, new_context); } - block_job_resume(job); + job_resume(&job->job); } static void block_job_drain(BlockJob *job) @@ -207,7 +192,7 @@ static void block_job_detach_aio_context(void *opaque) /* In case the job terminates during aio_poll()... */ job_ref(&job->job); - block_job_pause(job); + job_pause(&job->job); while (!job->job.paused && !job->completed) { block_job_drain(job); @@ -226,13 +211,13 @@ static char *child_job_get_parent_desc(BdrvChild *c) static void child_job_drained_begin(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_pause(job); + job_pause(&job->job); } static void child_job_drained_end(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_resume(job); + job_resume(&job->job); } static const BdrvChildRole child_job = { @@ -389,9 +374,9 @@ static void block_job_cancel_async(BlockJob *job, bool force) if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { block_job_iostatus_reset(job); } - if (job->user_paused) { + if (job->job.user_paused) { /* Do not call block_job_enter here, the caller will handle it. */ - job->user_paused = false; + job->job.user_paused = false; job->job.pause_count--; } job->job.cancelled = true; @@ -621,39 +606,6 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp) *jobptr = NULL; } -void block_job_user_pause(BlockJob *job, Error **errp) -{ - if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) { - return; - } - if (job->user_paused) { - error_setg(errp, "Job is already paused"); - return; - } - job->user_paused = true; - block_job_pause(job); -} - -bool block_job_user_paused(BlockJob *job) -{ - return job->user_paused; -} - -void block_job_user_resume(BlockJob *job, Error **errp) -{ - assert(job); - if (!job->user_paused || job->job.pause_count <= 0) { - error_setg(errp, "Can't resume a job that was not paused"); - return; - } - if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) { - return; - } - block_job_iostatus_reset(job); - job->user_paused = false; - block_job_resume(job); -} - void block_job_cancel(BlockJob *job, bool force) { if (job->job.status == JOB_STATUS_CONCLUDED) { @@ -844,6 +796,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, assert(is_block_job(&job->job)); assert(job->job.driver->free == &block_job_free); + assert(job->job.driver->user_resume == &block_job_user_resume); job->driver = driver; job->blk = blk; @@ -934,10 +887,16 @@ void block_job_iostatus_reset(BlockJob *job) if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { return; } - assert(job->user_paused && job->job.pause_count > 0); + assert(job->job.user_paused && job->job.pause_count > 0); job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } +void block_job_user_resume(Job *job) +{ + BlockJob *bjob = container_of(job, BlockJob, job); + block_job_iostatus_reset(bjob); +} + void block_job_event_ready(BlockJob *job) { job_state_transition(&job->job, JOB_STATUS_READY); @@ -984,9 +943,9 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, action, &error_abort); } if (action == BLOCK_ERROR_ACTION_STOP) { - block_job_pause(job); + job_pause(&job->job); /* make the pause user visible, which will be resumed from QMP. */ - job->user_paused = true; + job->job.user_paused = true; block_job_iostatus_set_err(job, error); } return action; diff --git a/job.c b/job.c index 932ccea105..94ad01a51a 100644 --- a/job.c +++ b/job.c @@ -341,6 +341,57 @@ void job_start(Job *job) aio_co_enter(job->aio_context, job->co); } +void job_pause(Job *job) +{ + job->pause_count++; +} + +void job_resume(Job *job) +{ + assert(job->pause_count > 0); + job->pause_count--; + if (job->pause_count) { + return; + } + job_enter(job); +} + +void job_user_pause(Job *job, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) { + return; + } + if (job->user_paused) { + error_setg(errp, "Job is already paused"); + return; + } + job->user_paused = true; + job_pause(job); +} + +bool job_user_paused(Job *job) +{ + return job->user_paused; +} + +void job_user_resume(Job *job, Error **errp) +{ + assert(job); + if (!job->user_paused || job->pause_count <= 0) { + error_setg(errp, "Can't resume a job that was not paused"); + return; + } + if (job_apply_verb(job, JOB_VERB_RESUME, errp)) { + return; + } + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + job->user_paused = false; + job_resume(job); +} + + typedef struct { Job *job; JobDeferToMainLoopFn *fn; diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 50232f5eaf..c993512f66 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -524,6 +524,7 @@ BlockJobDriver test_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, + .user_resume = block_job_user_resume, .start = test_job_start, }, .complete = test_job_complete, diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 0e6162bc71..93d1ff0859 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -78,6 +78,7 @@ static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(TestBlockJob), .free = block_job_free, + .user_resume = block_job_user_resume, .start = test_block_job_run, }, }; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index b329bd5274..ceb59600ed 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -20,6 +20,7 @@ static const BlockJobDriver test_block_job_driver = { .job_driver = { .instance_size = sizeof(BlockJob), .free = block_job_free, + .user_resume = block_job_user_resume, }, }; @@ -199,6 +200,7 @@ static const BlockJobDriver test_cancel_driver = { .job_driver = { .instance_size = sizeof(CancelJob), .free = block_job_free, + .user_resume = block_job_user_resume, .start = cancel_job_start, }, .complete = cancel_job_complete, @@ -270,7 +272,7 @@ static void test_cancel_paused(void) job_start(&job->job); assert(job->job.status == JOB_STATUS_RUNNING); - block_job_user_pause(job, &error_abort); + job_user_pause(&job->job, &error_abort); block_job_enter(job); assert(job->job.status == JOB_STATUS_PAUSED); @@ -308,7 +310,7 @@ static void test_cancel_standby(void) block_job_enter(job); assert(job->job.status == JOB_STATUS_READY); - block_job_user_pause(job, &error_abort); + job_user_pause(&job->job, &error_abort); block_job_enter(job); assert(job->job.status == JOB_STATUS_STANDBY);