From: Adam Litke <agl(a)us.ibm.com>
Without the VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC flag, libvirt will internally
poll using qemu's "query-block-jobs" API and will not return until the
operation has been completed. API users are advised that this operation
is unbounded and further interaction with the domain during this period
may block. Future patches may refactor things to allow other queries in
parallel with this polling. For older qemu (namely RHEL 6.2, which
backported an earlier version of block_job_cancel for the qed file format
but was always synchronous), we don't synthesize the completion event,
so instead we refuse to honor the async flag.
Signed-off-by: Adam Litke <agl(a)us.ibm.com>
Cc: Stefan Hajnoczi <stefanha(a)gmail.com>
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/qemu/qemu_driver.c | 67 ++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 59 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index d9e35be..077c161 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -11599,7 +11599,7 @@ cleanup:
static int
qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const char *base,
unsigned long bandwidth, virDomainBlockJobInfoPtr info,
- int mode)
+ int mode, unsigned int flags)
{
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm = NULL;
@@ -11621,6 +11621,18 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const
char *base,
if (!device) {
goto cleanup;
}
+ priv = vm->privateData;
+
+ /* Asynchronous job cancellation was introduced at the same time
+ * as drive-mirror jobs, for upstream qemu 1.1. */
+ if (mode == QEMU_JOB_ABORT &&
+ (flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC) &&
+ !qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_MIRROR)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Asynchronous cancel is not supported with "
+ "this QEMU binary"));
+ goto cleanup;
+ }
if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
goto cleanup;
@@ -11632,7 +11644,6 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const
char *base,
}
qemuDomainObjEnterMonitorWithDriver(driver, vm);
- priv = vm->privateData;
/* XXX - add a qemu capability check, since only qemu 1.1 or newer
* supports the base argument.
* XXX - libvirt should really be tracking the backing file chain
@@ -11641,6 +11652,44 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const
char *base,
ret = qemuMonitorBlockJob(priv->mon, device, base, bandwidth, info, mode);
qemuDomainObjExitMonitorWithDriver(driver, vm);
+ /* Qemu provides asynchronous block job cancellation, but without
+ * the VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC flag libvirt guarantees a
+ * synchronous operation. Provide this behavior by waiting here,
+ * so we don't get confused by newly scheduled block jobs.
+ */
+ if (ret == 0 && mode == BLOCK_JOB_ABORT &&
+ !(flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)) {
+ while (1) {
+ /* Poll every 50ms */
+ static struct timespec ts = { .tv_sec = 0,
+ .tv_nsec = 50 * 1000 * 1000ull };
+ virDomainBlockJobInfo dummy;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorBlockJob(priv->mon, device, NULL, 0, &dummy,
+ BLOCK_JOB_INFO);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (ret <= 0)
+ break;
+
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+
+ nanosleep(&ts, NULL);
+
+ qemuDriverLock(driver);
+ virDomainObjLock(vm);
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ ret = -1;
+ break;
+ }
+ }
+ }
+
endjob:
if (qemuDomainObjEndJob(driver, vm) == 0) {
vm = NULL;
@@ -11658,8 +11707,9 @@ cleanup:
static int
qemuDomainBlockJobAbort(virDomainPtr dom, const char *path, unsigned int flags)
{
- virCheckFlags(0, -1);
- return qemuDomainBlockJobImpl(dom, path, NULL, 0, NULL, BLOCK_JOB_ABORT);
+ virCheckFlags(VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC, -1);
+ return qemuDomainBlockJobImpl(dom, path, NULL, 0, NULL, BLOCK_JOB_ABORT,
+ flags);
}
static int
@@ -11667,7 +11717,8 @@ qemuDomainGetBlockJobInfo(virDomainPtr dom, const char *path,
virDomainBlockJobInfoPtr info, unsigned int flags)
{
virCheckFlags(0, -1);
- return qemuDomainBlockJobImpl(dom, path, NULL, 0, info, BLOCK_JOB_INFO);
+ return qemuDomainBlockJobImpl(dom, path, NULL, 0, info, BLOCK_JOB_INFO,
+ flags);
}
static int
@@ -11676,7 +11727,7 @@ qemuDomainBlockJobSetSpeed(virDomainPtr dom, const char *path,
{
virCheckFlags(0, -1);
return qemuDomainBlockJobImpl(dom, path, NULL, bandwidth, NULL,
- BLOCK_JOB_SPEED);
+ BLOCK_JOB_SPEED, flags);
}
static int
@@ -11687,10 +11738,10 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const
char *base,
virCheckFlags(0, -1);
ret = qemuDomainBlockJobImpl(dom, path, base, bandwidth, NULL,
- BLOCK_JOB_PULL);
+ BLOCK_JOB_PULL, flags);
if (ret == 0 && bandwidth != 0)
ret = qemuDomainBlockJobImpl(dom, path, NULL, bandwidth, NULL,
- BLOCK_JOB_SPEED);
+ BLOCK_JOB_SPEED, flags);
return ret;
}
--
1.7.7.6