diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f197d7c7de..4ed82ed570 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2164,6 +2164,12 @@ qemuBuildDisksCommandLine(virCommand *cmd, for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; + /* transient disks with shared backing image will be hotplugged after + * the VM is started */ + if (disk->transient && + disk->transientShareBacking == VIR_TRISTATE_BOOL_YES) + continue; + if (qemuBuildDiskCommandLine(cmd, def, disk, qemuCaps) < 0) return -1; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1b22bd1256..9b069fe7ce 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7016,7 +7016,8 @@ qemuProcessSetupDisksTransientSnapshot(virDomainObj *vm, virDomainDiskDef *domdisk = vm->def->disks[i]; g_autoptr(virDomainSnapshotDiskDef) snapdisk = NULL; - if (!domdisk->transient) + if (!domdisk->transient || + domdisk->transientShareBacking == VIR_TRISTATE_BOOL_YES) continue; /* validation code makes sure that we do this only for local disks @@ -7048,6 +7049,45 @@ qemuProcessSetupDisksTransientSnapshot(virDomainObj *vm, } +static int +qemuProcessSetupDisksTransientHotplug(virDomainObj *vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivate *priv = vm->privateData; + bool hasHotpluggedDisk = false; + size_t i; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDef *domdisk = vm->def->disks[i]; + + if (!domdisk->transient || + domdisk->transientShareBacking != VIR_TRISTATE_BOOL_YES) + continue; + + if (qemuDomainAttachDiskGeneric(priv->driver, vm, domdisk, asyncJob) < 0) + return -1; + + hasHotpluggedDisk = true; + } + + /* in order to allow booting from such disks we need to issue a system-reset + * so that the firmware tables recording bootable devices are regerated */ + if (hasHotpluggedDisk) { + int rc; + + if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0) + return -1; + + rc = qemuMonitorSystemReset(priv->mon); + + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0) + return -1; + } + + return 0; +} + + static int qemuProcessSetupDisksTransient(virDomainObj *vm, qemuDomainAsyncJob asyncJob) @@ -7060,6 +7100,9 @@ qemuProcessSetupDisksTransient(virDomainObj *vm, if (qemuProcessSetupDisksTransientSnapshot(vm, asyncJob) < 0) return -1; + if (qemuProcessSetupDisksTransientHotplug(vm, asyncJob) < 0) + return -1; + return 0; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index fb8af31cc4..669c45b3c5 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2980,6 +2980,30 @@ qemuValidateDomainDeviceDefDiskTransient(const virDomainDiskDef *disk, return -1; } + if (disk->transientShareBacking == VIR_TRISTATE_BOOL_YES) { + /* sharing the backing file requires hotplug of the disk in the qemu driver */ + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_USB: + case VIR_DOMAIN_DISK_BUS_VIRTIO: + case VIR_DOMAIN_DISK_BUS_SCSI: + break; + + case VIR_DOMAIN_DISK_BUS_IDE: + case VIR_DOMAIN_DISK_BUS_FDC: + case VIR_DOMAIN_DISK_BUS_XEN: + case VIR_DOMAIN_DISK_BUS_UML: + case VIR_DOMAIN_DISK_BUS_SATA: + case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NONE: + case VIR_DOMAIN_DISK_BUS_LAST: + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk bus '%s' doesn't support transiend disk backing image sharing"), + virDomainDiskBusTypeToString(disk->bus)); + return -1; + } + } + return 0; }