[Qemu-devel] [PATCH v2 36/40] job: Add lifecycle QMP commands

Kevin Wolf posted 40 patches 7 years, 5 months ago
[Qemu-devel] [PATCH v2 36/40] job: Add lifecycle QMP commands
Posted by Kevin Wolf 7 years, 5 months ago
This adds QMP commands that control the transition between states of the
job lifecycle.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 qapi/job.json |  99 +++++++++++++++++++++++++++++++++++++++++++
 job-qmp.c     | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 MAINTAINERS   |   1 +
 Makefile.objs |   1 +
 trace-events  |   9 ++++
 5 files changed, 244 insertions(+)
 create mode 100644 job-qmp.c

diff --git a/qapi/job.json b/qapi/job.json
index 9fbdd0ccd9..b84dc6c820 100644
--- a/qapi/job.json
+++ b/qapi/job.json
@@ -106,3 +106,102 @@
 { 'event': 'JOB_STATUS_CHANGE',
   'data': { 'id': 'str',
             'status': 'JobStatus' } }
+
+##
+# @job-pause:
+#
+# Pause an active job.
+#
+# This command returns immediately after marking the active job for pausing.
+# Pausing an already paused job is an error.
+#
+# The job will pause as soon as possible, which means transitioning into the
+# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The
+# corresponding JOB_STATUS_CHANGE event will be emitted.
+#
+# Cancelling a paused job automatically resumes it.
+#
+# @id: The job identifier.
+#
+# Since: 2.13
+##
+{ 'command': 'job-pause', 'data': { 'id': 'str' } }
+
+##
+# @job-resume:
+#
+# Resume a paused job.
+#
+# This command returns immediately after resuming a paused job. Resuming an
+# already running job is an error.
+#
+# @id : The job identifier.
+#
+# Since: 2.13
+##
+{ 'command': 'job-resume', 'data': { 'id': 'str' } }
+
+##
+# @job-cancel:
+#
+# Instruct an active background job to cancel at the next opportunity.
+# This command returns immediately after marking the active job for
+# cancellation.
+#
+# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE
+# event. Usually, the status will change to ABORTING, but it is possible that
+# a job successfully completes (e.g. because it was almost done and there was
+# no opportunity to cancel earlier than completing the job) and transitions to
+# PENDING instead.
+#
+# @id: The job identifier.
+#
+# Since: 2.13
+##
+{ 'command': 'job-cancel', 'data': { 'id': 'str' } }
+
+
+##
+# @job-complete:
+#
+# Manually trigger completion of an active job in the READY state.
+#
+# @id: The job identifier.
+#
+# Since: 2.13
+##
+{ 'command': 'job-complete', 'data': { 'id': 'str' } }
+
+##
+# @job-dismiss:
+#
+# Deletes a job that is in the CONCLUDED state. This command only needs to be
+# run explicitly for jobs that don't have automatic dismiss enabled.
+#
+# This command will refuse to operate on any job that has not yet reached its
+# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY
+# event, job-cancel or job-complete will still need to be used as appropriate.
+#
+# @id: The job identifier.
+#
+# Since: 2.13
+##
+{ 'command': 'job-dismiss', 'data': { 'id': 'str' } }
+
+##
+# @job-finalize:
+#
+# Instructs all jobs in a transaction (or a single job if it is not part of any
+# transaction) to finalize any graph changes and do any necessary cleanup. This
+# command requires that all involved jobs are in the PENDING state.
+#
+# 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 identifier of any job in the transaction, or of a job that is not
+#      part of any transaction.
+#
+# Since: 2.13
+##
+{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
diff --git a/job-qmp.c b/job-qmp.c
new file mode 100644
index 0000000000..b2e18cfd9c
--- /dev/null
+++ b/job-qmp.c
@@ -0,0 +1,134 @@
+/*
+ * QMP interface for background jobs
+ *
+ * Copyright (c) 2011 IBM Corp.
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/job.h"
+#include "qapi/qapi-commands-job.h"
+#include "qapi/error.h"
+#include "trace-root.h"
+
+/* Get a job using its ID and acquire its AioContext */
+static Job *find_job(const char *id, AioContext **aio_context, Error **errp)
+{
+    Job *job;
+
+    *aio_context = NULL;
+
+    job = job_get(id);
+    if (!job) {
+        error_setg(errp, "Job not found");
+        return NULL;
+    }
+
+    *aio_context = job->aio_context;
+    aio_context_acquire(*aio_context);
+
+    return job;
+}
+
+void qmp_job_cancel(const char *id, Error **errp)
+{
+    AioContext *aio_context;
+    Job *job = find_job(id, &aio_context, errp);
+
+    if (!job) {
+        return;
+    }
+
+    trace_qmp_job_cancel(job);
+    job_user_cancel(job, true, errp);
+    aio_context_release(aio_context);
+}
+
+void qmp_job_pause(const char *id, Error **errp)
+{
+    AioContext *aio_context;
+    Job *job = find_job(id, &aio_context, errp);
+
+    if (!job) {
+        return;
+    }
+
+    trace_qmp_job_pause(job);
+    job_user_pause(job, errp);
+    aio_context_release(aio_context);
+}
+
+void qmp_job_resume(const char *id, Error **errp)
+{
+    AioContext *aio_context;
+    Job *job = find_job(id, &aio_context, errp);
+
+    if (!job) {
+        return;
+    }
+
+    trace_qmp_job_resume(job);
+    job_user_resume(job, errp);
+    aio_context_release(aio_context);
+}
+
+void qmp_job_complete(const char *id, Error **errp)
+{
+    AioContext *aio_context;
+    Job *job = find_job(id, &aio_context, errp);
+
+    if (!job) {
+        return;
+    }
+
+    trace_qmp_job_complete(job);
+    job_complete(job, errp);
+    aio_context_release(aio_context);
+}
+
+void qmp_job_finalize(const char *id, Error **errp)
+{
+    AioContext *aio_context;
+    Job *job = find_job(id, &aio_context, errp);
+
+    if (!job) {
+        return;
+    }
+
+    trace_qmp_job_finalize(job);
+    job_finalize(job, errp);
+    aio_context_release(aio_context);
+}
+
+void qmp_job_dismiss(const char *id, Error **errp)
+{
+    AioContext *aio_context;
+    Job *job = find_job(id, &aio_context, errp);
+
+    if (!job) {
+        return;
+    }
+
+    trace_qmp_job_dismiss(job);
+    job_dismiss(&job, errp);
+    aio_context_release(aio_context);
+}
diff --git a/MAINTAINERS b/MAINTAINERS
index 9fba3307d9..4215295f05 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1370,6 +1370,7 @@ S: Supported
 F: blockjob.c
 F: include/block/blockjob.h
 F: job.c
+F: job-qmp.c
 F: include/block/job.h
 F: block/backup.c
 F: block/commit.c
diff --git a/Makefile.objs b/Makefile.objs
index 3df8d58e49..c6c3554203 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -97,6 +97,7 @@ io-obj-y = io/
 ifeq ($(CONFIG_SOFTMMU),y)
 common-obj-y = blockdev.o blockdev-nbd.o block/
 common-obj-y += bootdevice.o iothread.o
+common-obj-y += job-qmp.o
 common-obj-y += net/
 common-obj-y += qdev-monitor.o device-hotplug.o
 common-obj-$(CONFIG_WIN32) += os-win32.o
diff --git a/trace-events b/trace-events
index ef7579a285..c445f54773 100644
--- a/trace-events
+++ b/trace-events
@@ -109,6 +109,15 @@ job_state_transition(void *job,  int ret, const char *legal, const char *s0, con
 job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
 job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
 
+# job-qmp.c
+qmp_job_cancel(void *job) "job %p"
+qmp_job_pause(void *job) "job %p"
+qmp_job_resume(void *job) "job %p"
+qmp_job_complete(void *job) "job %p"
+qmp_job_finalize(void *job) "job %p"
+qmp_job_dismiss(void *job) "job %p"
+
+
 ### Guest events, keep at bottom
 
 
-- 
2.13.6


Re: [Qemu-devel] [PATCH v2 36/40] job: Add lifecycle QMP commands
Posted by Eric Blake 7 years, 5 months ago
On 05/18/2018 08:21 AM, Kevin Wolf wrote:
> This adds QMP commands that control the transition between states of the
> job lifecycle.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>   qapi/job.json |  99 +++++++++++++++++++++++++++++++++++++++++++
>   job-qmp.c     | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   MAINTAINERS   |   1 +
>   Makefile.objs |   1 +
>   trace-events  |   9 ++++
>   5 files changed, 244 insertions(+)
>   create mode 100644 job-qmp.c
> 
> +##
> +# @job-dismiss:
> +#
> +# Deletes a job that is in the CONCLUDED state. This command only needs to be
> +# run explicitly for jobs that don't have automatic dismiss enabled.

Did we decide whether it is valid to expect a job with automatic dismiss 
enabled (old-style block jobs) to use the new job control commands?  Or 
would it be reasonable to require that 'job-dismiss' is an error on jobs 
with auto-dismiss enabled (as in, if you're going to use new style jobs, 
you are guaranteed to also have auto-dismiss false, because we don't 
expose a way to change that flag in new-style jobs; and if you use old 
style jobs, all management of the job should be done through the old 
interfaces).

> +# This command will refuse to operate on any job that has not yet reached its
> +# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY
> +# event, job-cancel or job-complete will still need to be used as appropriate.
> +#
> +# @id: The job identifier.
> +#
> +# Since: 2.13
> +##
> +{ 'command': 'job-dismiss', 'data': { 'id': 'str' } }
> +


-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

Re: [Qemu-devel] [PATCH v2 36/40] job: Add lifecycle QMP commands
Posted by Kevin Wolf 7 years, 5 months ago
Am 18.05.2018 um 20:12 hat Eric Blake geschrieben:
> On 05/18/2018 08:21 AM, Kevin Wolf wrote:
> > This adds QMP commands that control the transition between states of the
> > job lifecycle.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >   qapi/job.json |  99 +++++++++++++++++++++++++++++++++++++++++++
> >   job-qmp.c     | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >   MAINTAINERS   |   1 +
> >   Makefile.objs |   1 +
> >   trace-events  |   9 ++++
> >   5 files changed, 244 insertions(+)
> >   create mode 100644 job-qmp.c
> > 
> > +##
> > +# @job-dismiss:
> > +#
> > +# Deletes a job that is in the CONCLUDED state. This command only needs to be
> > +# run explicitly for jobs that don't have automatic dismiss enabled.
> 
> Did we decide whether it is valid to expect a job with automatic dismiss
> enabled (old-style block jobs) to use the new job control commands?

"job control commands" is a pretty broad term, so I'd say yes, it's
expected that you can use job-* commands on any job. For example,
job-pause/resume make perfect sense on a job with auto-dismiss=true.

> Or would it be reasonable to require that 'job-dismiss' is an error on
> jobs with auto-dismiss enabled

This, too, but only because it implicitly follows from the condition
specified above: You'll never catch a job in the CONCLUDED state when
you have auto-dismiss=true because it will immediately move on to NULL.

> (as in, if you're going to use new style jobs, you are guaranteed to
> also have auto-dismiss false, because we don't expose a way to change
> that flag in new-style jobs; and if you use old style jobs, all
> management of the job should be done through the old interfaces).

I'm not completely convinced that auto-dismiss=false is the only
"correct" setting. That might be the case for libvirt, but possibly not
for simple ad-hoc scripts with lower requirements.

We don't have any new jobs yet, so whether we expose the auto-* flags
there is a decision yet to be made.

Kevin

Re: [Qemu-devel] [PATCH v2 36/40] job: Add lifecycle QMP commands
Posted by John Snow 7 years, 5 months ago

On 05/18/2018 09:21 AM, Kevin Wolf wrote:
> +{ 'command': 'job-complete', 'data': { 'id': 'str' } }

Do we have to name it this? I've always disliked how "complete" is used
as both a verb and an adjective in our code (hence why I used such
bizarre phrasings like "concluded" in my jobs patches) ...

I don't necessarily have a better suggestion, but now's the chance to
ask the question.

I guess you don't have a better suggestion either, otherwise you'd have
changed it.

--js