For now, disk migration via block copy job is not implemented. But
when we do implement it, we have to deal with the fact that qemu does
not provide an easy way to re-start a qemu process with mirroring
still intact (it _might_ be possible by using qemu -S then an
initial 'drive-mirror' with disk reuse before starting the domain,
but that gets hairy). Even something like 'virDomainSave' becomes
hairy, if you realize the implications that 'virDomainRestore' would
be stuck with recreating the same mirror layout.
But if we step back and look at the bigger picture, we realize that
the initial client of live storage migration via disk mirroring is
oVirt, which always uses transient domains, and that if a transient
domain is destroyed while a mirror exists, oVirt can easily restart
the storage migration by creating a new domain that visits just the
source storage, with no loss in data.
We can make life a lot easier by being cowards, and forbidding
certain operations on a domain. This patch guarantees that we
never get in a state where we would have to restart a domain with
a mirroring block copy, by preventing saves, snapshots, and hot
unplug of a disk in use.
* src/conf/domain_conf.h (virDomainHasDiskMirror): New prototype.
* src/conf/domain_conf.c (virDomainHasDiskMirror): New function.
* src/libvirt_private.syms (domain_conf.h): Export it.
* src/qemu/qemu_driver.c (qemuDomainSaveInternal)
(qemuDomainSnapshotCreateXML, qemuDomainRevertToSnapshot)
(qemuDomainBlockJobImpl): Prevent dangerous actions while block
copy is already in action.
* src/qemu/qemu_hotplug.c (qemuDomainDetachDiskDevice): Likewise.
---
src/conf/domain_conf.c | 12 ++++++++++++
src/conf/domain_conf.h | 1 +
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 25 ++++++++++++++++++++++++-
src/qemu/qemu_hotplug.c | 7 +++++++
5 files changed, 45 insertions(+), 1 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 83b9655..df1c27a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7199,6 +7199,18 @@ virDomainDiskRemoveByName(virDomainDefPtr def, const char *name)
return virDomainDiskRemove(def, i);
}
+/* Return true if VM has at least one disk involved in a current block
+ * copy job (that is, with a <mirror> element in the disk xml). */
+bool
+virDomainHasDiskMirror(virDomainObjPtr vm)
+{
+ int i;
+ for (i = 0; i < vm->def->ndisks; i++)
+ if (vm->def->disks[i]->mirror)
+ return true;
+ return false;
+}
+
int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net)
{
if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index d4b0338..0c03e1c 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1962,6 +1962,7 @@ virDomainDiskDefPtr
virDomainDiskRemove(virDomainDefPtr def, size_t i);
virDomainDiskDefPtr
virDomainDiskRemoveByName(virDomainDefPtr def, const char *name);
+bool virDomainHasDiskMirror(virDomainObjPtr vm);
int virDomainNetIndexByMac(virDomainDefPtr def, const unsigned char *mac);
int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index a90f8a0..570940d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -354,6 +354,7 @@ virDomainGraphicsSpiceZlibCompressionTypeFromString;
virDomainGraphicsSpiceZlibCompressionTypeToString;
virDomainGraphicsTypeFromString;
virDomainGraphicsTypeToString;
+virDomainHasDiskMirror;
virDomainHostdevDefAlloc;
virDomainHostdevDefClear;
virDomainHostdevDefFree;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f5b3406..53189b5 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2558,6 +2558,11 @@ qemuDomainSaveInternal(struct qemud_driver *driver, virDomainPtr
dom,
"%s", _("domain is marked for auto
destroy"));
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_BLOCK_COPY_ACTIVE, "%s",
+ _("domain has active block copy job"));
+ goto cleanup;
+ }
memset(&header, 0, sizeof(header));
memcpy(header.magic, QEMUD_SAVE_PARTIAL, sizeof(header.magic));
@@ -10264,6 +10269,12 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
"%s", _("domain is marked for auto
destroy"));
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_BLOCK_COPY_ACTIVE, "%s",
+ _("domain has active block copy job"));
+ goto cleanup;
+ }
+
if (!vm->persistent && (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) {
qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot halt after transient domain snapshot"));
@@ -10871,6 +10882,11 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr
snapshot,
_("no domain with matching uuid '%s'"),
uuidstr);
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_BLOCK_COPY_ACTIVE, "%s",
+ _("domain has active block copy job"));
+ goto cleanup;
+ }
snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name);
if (!snap) {
@@ -11609,6 +11625,7 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const
char *base,
char uuidstr[VIR_UUID_STRING_BUFLEN];
char *device = NULL;
int ret = -1;
+ int idx;
qemuDriverLock(driver);
virUUIDFormat(dom->uuid, uuidstr);
@@ -11619,10 +11636,16 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, const
char *base,
goto cleanup;
}
- device = qemuDiskPathToAlias(vm, path, NULL);
+ device = qemuDiskPathToAlias(vm, path, &idx);
if (!device) {
goto cleanup;
}
+ if (mode == BLOCK_JOB_PULL && vm->def->disks[idx]->mirror) {
+ qemuReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+ _("disk '%s' already in active block copy
job"),
+ vm->def->disks[idx]->dst);
+ goto cleanup;
+ }
if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
goto cleanup;
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 857b980..98fa8f8 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1721,6 +1721,13 @@ int qemuDomainDetachDiskDevice(struct qemud_driver *driver,
detach = vm->def->disks[i];
+ if (detach->mirror) {
+ qemuReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+ _("disk '%s' is in an active block copy job"),
+ detach->dst);
+ goto cleanup;
+ }
+
if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=
0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
--
1.7.7.6