[libvirt] [PATCHv2 01/15] blockjob: add API for async virDomainBlockJobAbort
Adam Litke
agl at us.ibm.com
Fri Apr 6 13:14:14 UTC 2012
On Thu, Apr 05, 2012 at 10:36:47PM -0600, Eric Blake wrote:
> From: Adam Litke <agl at us.ibm.com>
>
> Qemu has changed the semantics of the "block_job_cancel" API. The original
> qed implementation (pretty much only backported to RHEL 6.2 qemu) was
> synchronous (ie. upon command completion, the operation was guaranteed to
> be completely stopped). With the new semantics going into qemu 1.1 for
> qcow2, a "block_job_cancel" merely requests that the operation be cancelled
> and an event is triggered once the cancellation request has been honored.
>
> To adopt the new semantics while preserving compatibility the following
> updates are made to the virDomainBlockJob API:
>
> A new block job event type VIR_DOMAIN_BLOCK_JOB_CANCELLED is recognized by
> libvirt. Regardless of the flags used with virDomainBlockJobAbort, this event
> will be raised whenever it is received from qemu. This event indicates that a
> block job has been successfully cancelled. For now, libvirt does not try
> to synthesize this event if using an older qemu that did not generate it.
>
> A new extension flag VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC is added to the
> virDomainBlockJobAbort API. When enabled, this function will operate
> asynchronously (ie, it can return before the job has actually been cancelled).
> When the API is used in this mode, it is the responsibility of the caller to
> wait for a VIR_DOMAIN_BLOCK_JOB_CANCELLED event or poll via the
> virDomainGetBlockJobInfo API to check the cancellation status; this flag
> is an error if it is not known if the hypervisor supports asynchronous cancel.
>
> This patch also exposes the new flag through virsh.
>
> Signed-off-by: Adam Litke <agl at us.ibm.com>
> Cc: Stefan Hajnoczi <stefanha at gmail.com>
> Signed-off-by: Eric Blake <eblake at redhat.com>
Tested-by: Adam Litke <agl at us.ibm.com>
> ---
> include/libvirt/libvirt.h.in | 10 +++++++++
> src/libvirt.c | 10 ++++++++-
> src/qemu/qemu_monitor_json.c | 42 +++++++++++++++++++++++++++++++------
> tools/virsh.c | 46 ++++++++++++++++++++++++++---------------
> tools/virsh.pod | 9 +++++--
> 5 files changed, 89 insertions(+), 28 deletions(-)
>
> diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
> index 499dcd4..97ad99d 100644
> --- a/include/libvirt/libvirt.h.in
> +++ b/include/libvirt/libvirt.h.in
> @@ -1946,6 +1946,15 @@ typedef enum {
> #endif
> } virDomainBlockJobType;
>
> +/**
> + * virDomainBlockJobAbortFlags:
> + *
> + * VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC: Request only, do not wait for completion
> + */
> +typedef enum {
> + VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC = 1 << 0,
> +} virDomainBlockJobAbortFlags;
> +
> /* An iterator for monitoring block job operations */
> typedef unsigned long long virDomainBlockJobCursor;
>
> @@ -3617,6 +3626,7 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn,
> typedef enum {
> VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0,
> VIR_DOMAIN_BLOCK_JOB_FAILED = 1,
> + VIR_DOMAIN_BLOCK_JOB_CANCELED = 2,
>
> #ifdef VIR_ENUM_SENTINELS
> VIR_DOMAIN_BLOCK_JOB_LAST
> diff --git a/src/libvirt.c b/src/libvirt.c
> index 16d1fd5..af22232 100644
> --- a/src/libvirt.c
> +++ b/src/libvirt.c
> @@ -17902,7 +17902,7 @@ error:
> * virDomainBlockJobAbort:
> * @dom: pointer to domain object
> * @disk: path to the block device, or device shorthand
> - * @flags: extra flags; not used yet, so callers should always pass 0
> + * @flags: bitwise-OR of virDomainBlockJobAbortFlags
> *
> * Cancel the active block job on the given disk.
> *
> @@ -17913,6 +17913,14 @@ error:
> * can be found by calling virDomainGetXMLDesc() and inspecting
> * elements within //domain/devices/disk.
> *
> + * By default, this function performs a synchronous operation and the caller
> + * may assume that the operation has completed when 0 is returned. However,
> + * BlockJob operations may take a long time to complete, and during this time
> + * further domain interactions may be unresponsive. To avoid this problem,
> + * pass VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC in the @flags argument to enable
> + * asynchronous behavior. Either way, when the job has been cancelled, a
> + * BlockJob event will be emitted, with status VIR_DOMAIN_BLOCK_JOB_CANCELLED.
> + *
> * Returns -1 in case of failure, 0 when successful.
> */
> int virDomainBlockJobAbort(virDomainPtr dom, const char *disk,
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index 8eeb307..9a8da3a 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -58,13 +58,14 @@ static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr dat
> static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
> -static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandleSPICEConnect(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon, virJSONValuePtr data);
> static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr data);
> +static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data);
> +static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data);
>
> static struct {
> const char *type;
> @@ -80,13 +81,14 @@ static struct {
> { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
> { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
> { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
> - { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, },
> { "SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect, },
> { "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, },
> { "SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect, },
> { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, },
> { "WAKEUP", qemuMonitorJSONHandlePMWakeup, },
> { "SUSPEND", qemuMonitorJSONHandlePMSuspend, },
> + { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, },
> + { "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, },
> };
>
>
> @@ -754,13 +756,15 @@ static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValu
> qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
> }
>
> -static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data)
> +static void
> +qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon,
> + virJSONValuePtr data,
> + int event)
> {
> const char *device;
> const char *type_str;
> int type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
> unsigned long long offset, len;
> - int status = VIR_DOMAIN_BLOCK_JOB_FAILED;
>
> if ((device = virJSONValueObjectGetString(data, "device")) == NULL) {
> VIR_WARN("missing device in block job event");
> @@ -785,11 +789,19 @@ static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr da
> if (STREQ(type_str, "stream"))
> type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL;
>
> - if (offset != 0 && offset == len)
> - status = VIR_DOMAIN_BLOCK_JOB_COMPLETED;
> + switch (event) {
> + case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
> + /* Make sure the whole device has been processed */
> + if (offset != len)
> + event = VIR_DOMAIN_BLOCK_JOB_FAILED;
> + break;
> + case VIR_DOMAIN_BLOCK_JOB_FAILED:
> + case VIR_DOMAIN_BLOCK_JOB_CANCELED:
> + break;
> + }
>
> out:
> - qemuMonitorEmitBlockJob(mon, device, type, status);
> + qemuMonitorEmitBlockJob(mon, device, type, event);
> }
>
> static void
> @@ -832,6 +844,22 @@ qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon,
> qemuMonitorEmitPMSuspend(mon);
> }
>
> +static void
> +qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon,
> + virJSONValuePtr data)
> +{
> + qemuMonitorJSONHandleBlockJobImpl(mon, data,
> + VIR_DOMAIN_BLOCK_JOB_COMPLETED);
> +}
> +
> +static void
> +qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon,
> + virJSONValuePtr data)
> +{
> + qemuMonitorJSONHandleBlockJobImpl(mon, data,
> + VIR_DOMAIN_BLOCK_JOB_CANCELED);
> +}
> +
> int
> qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
> const char *cmd_str,
> diff --git a/tools/virsh.c b/tools/virsh.c
> index cfdd040..7180b83 100644
> --- a/tools/virsh.c
> +++ b/tools/virsh.c
> @@ -7525,6 +7525,7 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
> const char *name, *path;
> unsigned long bandwidth = 0;
> int ret = -1;
> + unsigned int flags = 0;
>
> if (!vshConnectionUsability(ctl, ctl->conn))
> goto cleanup;
> @@ -7541,7 +7542,9 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
> }
>
> if (mode == VSH_CMD_BLOCK_JOB_ABORT) {
> - ret = virDomainBlockJobAbort(dom, path, 0);
> + if (vshCommandOptBool(cmd, "async"))
> + flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
> + ret = virDomainBlockJobAbort(dom, path, flags);
> } else if (mode == VSH_CMD_BLOCK_JOB_INFO) {
> ret = virDomainGetBlockJobInfo(dom, path, info, 0);
> } else if (mode == VSH_CMD_BLOCK_JOB_SPEED) {
> @@ -7589,20 +7592,25 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
> }
>
> /*
> - * "blockjobinfo" command
> + * "blockjob" command
> */
> static const vshCmdInfo info_block_job[] = {
> - {"help", N_("Manage active block operations.")},
> - {"desc", N_("Manage active block operations.")},
> + {"help", N_("Manage active block operations")},
> + {"desc", N_("Query, adjust speed, or cancel active block operations.")},
> {NULL, NULL}
> };
>
> static const vshCmdOptDef opts_block_job[] = {
> {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
> {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path of disk")},
> - {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Abort the active job on the specified disk")},
> - {"info", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("Get active job information for the specified disk")},
> - {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("Set the Bandwidth limit in MB/s")},
> + {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE,
> + N_("Abort the active job on the specified disk")},
> + {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
> + N_("don't wait for --abort to complete")},
> + {"info", VSH_OT_BOOL, VSH_OFLAG_NONE,
> + N_("Get active job information for the specified disk")},
> + {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE,
> + N_("Set the Bandwidth limit in MB/s")},
> {NULL, 0, 0, NULL}
> };
>
> @@ -7613,19 +7621,23 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
> virDomainBlockJobInfo info;
> const char *type;
> int ret;
> + bool abortMode = vshCommandOptBool(cmd, "abort");
> + bool infoMode = vshCommandOptBool(cmd, "info");
> + bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
>
> - if (vshCommandOptBool (cmd, "abort")) {
> - mode = VSH_CMD_BLOCK_JOB_ABORT;
> - } else if (vshCommandOptBool (cmd, "info")) {
> - mode = VSH_CMD_BLOCK_JOB_INFO;
> - } else if (vshCommandOptBool (cmd, "bandwidth")) {
> - mode = VSH_CMD_BLOCK_JOB_SPEED;
> - } else {
> + if (abortMode + infoMode + bandwidth > 1) {
> vshError(ctl, "%s",
> _("One of --abort, --info, or --bandwidth is required"));
> return false;
> }
>
> + if (abortMode)
> + mode = VSH_CMD_BLOCK_JOB_ABORT;
> + else if (bandwidth)
> + mode = VSH_CMD_BLOCK_JOB_SPEED;
> + else
> + mode = VSH_CMD_BLOCK_JOB_INFO;
> +
> ret = blockJobImpl(ctl, cmd, &info, mode);
> if (ret < 0)
> return false;
> @@ -7634,13 +7646,13 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
> return true;
>
> if (info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL)
> - type = "Block Pull";
> + type = _("Block Pull");
> else
> - type = "Unknown job";
> + type = _("Unknown job");
>
> print_job_progress(type, info.end - info.cur, info.end);
> if (info.bandwidth != 0)
> - vshPrint(ctl, " Bandwidth limit: %lu MB/s\n", info.bandwidth);
> + vshPrint(ctl, _(" Bandwidth limit: %lu MB/s\n"), info.bandwidth);
> return true;
> }
>
> diff --git a/tools/virsh.pod b/tools/virsh.pod
> index a60e667..aac72d1 100644
> --- a/tools/virsh.pod
> +++ b/tools/virsh.pod
> @@ -686,13 +686,16 @@ Both I<--live> and I<--current> flags may be given, but I<--current> is
> exclusive. If no flag is specified, behavior is different depending
> on hypervisor.
>
> -=item B<blockjob> I<domain> I<path> [I<--abort>] [I<--info>] [I<bandwidth>]
> +=item B<blockjob> I<domain> I<path> { I<--abort> [I<--async>] |
> +[I<--info>] | I<bandwidth> }
>
> -Manage active block operations.
> +Manage active block operations. If no mode is chosen, I<--info> is assumed.
>
> I<path> specifies fully-qualified path of the disk.
> +
> If I<--abort> is specified, the active job on the specified disk will
> -be aborted.
> +be aborted. If I<--async> is also specified, this command will return
> +immediately, rather than waiting for the cancelation to complete.
> If I<--info> is specified, the active job information on the specified
> disk will be printed.
> I<bandwidth> can be used to set bandwidth limit for the active job.
> --
> 1.7.7.6
>
--
Adam Litke <agl at us.ibm.com>
IBM Linux Technology Center
More information about the libvir-list
mailing list