return ret;
}
+/* Called while holding the VM job lock, to implement a block job
+ * abort with pivot; this updates the VM definition as appropriate, on
+ * either success or failure. */
+static int
+qemuDomainBlockPivot(struct qemud_driver *driver, virDomainObjPtr vm,
+ const char *device, virDomainDiskDefPtr disk)
+{
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainBlockJobInfo info;
+ const char *format = virStorageFileFormatTypeToString(disk->mirrorFormat);
+
+ /* Probe the status, if needed. */
+ if (!disk->mirroring) {
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorBlockJob(priv->mon, device, NULL, 0, &info,
+ BLOCK_JOB_INFO, true);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (ret < 0)
+ goto cleanup;
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ goto cleanup;
+ }
+ if (ret == 1 && info.cur == info.end &&
+ info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY)
+ disk->mirroring = true;
+ }
+
+ if (!disk->mirroring) {
+ virReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+ _("disk '%s' not ready for pivot yet"),
+ disk->dst);
+ goto cleanup;
+ }
+
+ /* Attempt the pivot. */
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorDrivePivot(priv->mon, device, disk->mirror, format);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (ret == 0) {
+ /* XXX We want to revoke security labels and disk lease, as
+ * well as audit that revocation, before dropping the original
+ * source. But it gets tricky if both source and mirror share
+ * common backing files (we want to only revoke the non-shared
+ * portion of the chain, and is made more difficult by the
+ * fact that we aren't tracking the full chain ourselves; so
+ * for now, we leak the access to the original. */
+ VIR_FREE(disk->src);
+ disk->src = disk->mirror;
+ disk->format = disk->mirrorFormat;
+ disk->mirror = NULL;
+ disk->mirrorFormat = VIR_STORAGE_FILE_NONE;
+ disk->mirroring = false;
+ qemuDomainDetermineDiskChain(driver, disk, true);
+ } else {
+ /* On failure, qemu abandons the mirror, and reverts back to
+ * the source disk (RHEL 6.3 has a bug where the revert could
+ * cause catastrophic failure in qemu, but we don't need to
+ * worry about it here as it is not an upstream qemu problem. */
+ /* XXX should we be parsing the exact qemu error, or calling
+ * 'query-block', to see what state we really got left in
+ * before killing the mirroring job? And just as on the
+ * success case, there's security labeling to worry about. */
+ VIR_FREE(disk->mirror);
+ disk->mirrorFormat = VIR_STORAGE_FILE_NONE;
+ disk->mirroring = false;
+ }
+
+cleanup:
+ return ret;
+}
+
static int
qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const char *base,
unsigned long bandwidth, virDomainBlockJobInfoPtr info,
disk->dst);
goto cleanup;
}
+ if (mode == BLOCK_JOB_ABORT &&
+ (flags & VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT) &&
+ !(async && disk->mirror)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("pivot of disk '%s' requires an active copy job"),
+ disk->dst);
+ goto cleanup;
+ }
if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
goto cleanup;
goto endjob;
}
+ if (disk->mirror && mode == BLOCK_JOB_ABORT &&
+ (flags & VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT)) {
+ ret = qemuDomainBlockPivot(driver, vm, device, disk);
+ goto endjob;
+ }
+
qemuDomainObjEnterMonitorWithDriver(driver, vm);
/* XXX - libvirt should really be tracking the backing file chain
* itself, and validating that base is on the chain, rather than
info->cur == info->end && info->type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY)
disk->mirroring = true;
+ /* A successful block job cancelation stops any mirroring. */
+ if (mode == BLOCK_JOB_ABORT && disk->mirror) {
+ /* XXX We should also revoke security labels and disk lease on
+ * the mirror, and audit that fact, before dropping things. */
+ VIR_FREE(disk->mirror);
+ disk->mirrorFormat = VIR_STORAGE_FILE_NONE;
+ disk->mirroring = false;
+ }
+
/* With synchronous block cancel, we must synthesize an event, and
* we silently ignore the ABORT_ASYNC flag. With asynchronous
* block cancel, the event will come from qemu, but without the
static int
qemuDomainBlockJobAbort(virDomainPtr dom, const char *path, unsigned int flags)
{
- virCheckFlags(VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC, -1);
+ virCheckFlags(VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC |
+ VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT, -1);
return qemuDomainBlockJobImpl(dom, path, NULL, 0, NULL, BLOCK_JOB_ABORT,
flags);
}