[libvirt] [PATCH 5/5] blockjob: allow for fast-finishing job
Daniel Veillard
veillard at redhat.com
Thu Apr 12 01:29:17 UTC 2012
On Wed, Apr 11, 2012 at 05:40:38PM -0600, Eric Blake wrote:
> In my testing, I was able to provoke an odd block pull failure:
>
> $ virsh blockpull dom vda --bandwidth 10000
> error: Requested operation is not valid: No active operation on device: drive-virtio-disk0
>
> merely by using gdb to artifically wait to do the block job set speed
> until after the pull had already finished. But in reality, that should
> be a success, since the pull finished before we had a chance to set
> speed. Furthermore, using a double job lock is annoying; we can alter
> the speed as part of the same job that started the block pull for less
> likelihood of hitting the speed failure in the first place.
>
> * src/qemu/qemu_monitor.h (BLOCK_JOB_CMD): Add new mode.
> * src/qemu/qemu_driver.c (qemuDomainBlockRebase): Move secondary
> job call...
> (qemuDomainBlockJobImpl): ...here, for fewer locks.
> * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Change
> return value on new internal mode.
> ---
> src/qemu/qemu_driver.c | 13 +++++--------
> src/qemu/qemu_monitor.h | 3 ++-
> src/qemu/qemu_monitor_json.c | 31 ++++++++++++++++++++-----------
> 3 files changed, 27 insertions(+), 20 deletions(-)
>
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index bb6089b..e31f407 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -11654,6 +11654,9 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const char *base,
> * relying on qemu to do this. */
> ret = qemuMonitorBlockJob(priv->mon, device, base, bandwidth, info, mode,
> async);
> + if (ret == 0 && mode == BLOCK_JOB_PULL && bandwidth)
> + ret = qemuMonitorBlockJob(priv->mon, device, NULL, bandwidth, NULL,
> + BLOCK_JOB_SPEED_INTERNAL, async);
> qemuDomainObjExitMonitorWithDriver(driver, vm);
> if (ret < 0)
> goto endjob;
> @@ -11750,15 +11753,9 @@ static int
> qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
> unsigned long bandwidth, unsigned int flags)
> {
> - int ret;
> -
> virCheckFlags(0, -1);
> - ret = qemuDomainBlockJobImpl(dom, path, base, bandwidth, NULL,
> - BLOCK_JOB_PULL, flags);
> - if (ret == 0 && bandwidth != 0)
> - ret = qemuDomainBlockJobImpl(dom, path, NULL, bandwidth, NULL,
> - BLOCK_JOB_SPEED, flags);
> - return ret;
> + return qemuDomainBlockJobImpl(dom, path, base, bandwidth, NULL,
> + BLOCK_JOB_PULL, flags);
> }
>
> static int
> diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
> index dc02964..f3cdcdd 100644
> --- a/src/qemu/qemu_monitor.h
> +++ b/src/qemu/qemu_monitor.h
> @@ -530,7 +530,8 @@ typedef enum {
> BLOCK_JOB_ABORT = 0,
> BLOCK_JOB_INFO = 1,
> BLOCK_JOB_SPEED = 2,
> - BLOCK_JOB_PULL = 3,
> + BLOCK_JOB_SPEED_INTERNAL = 3,
> + BLOCK_JOB_PULL = 4,
> } BLOCK_JOB_CMD;
>
> int qemuMonitorBlockJob(qemuMonitorPtr mon,
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index 242889c..26e41ac 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -3452,6 +3452,7 @@ qemuMonitorJSONBlockJob(qemuMonitorPtr mon,
> cmd = qemuMonitorJSONMakeCommand(cmd_name, NULL);
> break;
> case BLOCK_JOB_SPEED:
> + case BLOCK_JOB_SPEED_INTERNAL:
> cmd_name = async ? "block-job-set-speed" : "block_job_set_speed";
> cmd = qemuMonitorJSONMakeCommand(cmd_name, "s:device",
> device, "U:value",
> @@ -3473,22 +3474,30 @@ qemuMonitorJSONBlockJob(qemuMonitorPtr mon,
> ret = qemuMonitorJSONCommand(mon, cmd, &reply);
>
> if (ret == 0 && virJSONValueObjectHasKey(reply, "error")) {
> - if (qemuMonitorJSONHasError(reply, "DeviceNotActive"))
> - qemuReportError(VIR_ERR_OPERATION_INVALID,
> - _("No active operation on device: %s"), device);
> - else if (qemuMonitorJSONHasError(reply, "DeviceInUse"))
> + ret = -1;
> + if (qemuMonitorJSONHasError(reply, "DeviceNotActive")) {
> + /* If a job completes before we get a chance to set the
> + * speed, we don't want to fail the original command. */
> + if (mode == BLOCK_JOB_SPEED_INTERNAL)
> + ret = 0;
> + else
> + qemuReportError(VIR_ERR_OPERATION_INVALID,
> + _("No active operation on device: %s"),
> + device);
> + } else if (qemuMonitorJSONHasError(reply, "DeviceInUse")){
> qemuReportError(VIR_ERR_OPERATION_FAILED,
> - _("Device %s in use"), device);
> - else if (qemuMonitorJSONHasError(reply, "NotSupported"))
> + _("Device %s in use"), device);
> + } else if (qemuMonitorJSONHasError(reply, "NotSupported")) {
> qemuReportError(VIR_ERR_OPERATION_INVALID,
> - _("Operation is not supported for device: %s"), device);
> - else if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
> + _("Operation is not supported for device: %s"),
> + device);
> + } else if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
> qemuReportError(VIR_ERR_OPERATION_INVALID,
> - _("Command '%s' is not found"), cmd_name);
> - else
> + _("Command '%s' is not found"), cmd_name);
> + } else {
> qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> _("Unexpected error"));
> - ret = -1;
> + }
> }
>
> if (ret == 0 && mode == BLOCK_JOB_INFO)
ACK, a race we can't really avoid at that level and that's the proper
handling. Again I'm wondering how we could automate testing for this ...
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
More information about the libvir-list
mailing list