From: Masayoshi Mizuma <m.mizuma@xxxxxxxxxxxxxx> Add qemuHotplugCreateDisksTransient() to make <transient/> disk sharable. The procedure is followings. First, create the overlay disk with the original disk is set as the backingStore. Then, blockdev-del the StorageProgs and FormatProgs of the disk. That's because to fix the bootindex of the disk. Lastly, device_add the disks. Signed-off-by: Masayoshi Mizuma <m.mizuma@xxxxxxxxxxxxxx> --- src/qemu/qemu_command.c | 3 + src/qemu/qemu_hotplug.c | 285 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 3 + 3 files changed, 291 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1ec302d4ac..81a27703c5 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2139,6 +2139,9 @@ qemuBuildDisksCommandLine(virCommandPtr cmd, bootCD = 0; break; case VIR_DOMAIN_DISK_DEVICE_DISK: + /* to use bootindex later for transient disk */ + disk->info.bootIndex = bootDisk; + G_GNUC_FALLTHROUGH; case VIR_DOMAIN_DISK_DEVICE_LUN: bootindex = bootDisk; bootDisk = 0; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index a2535949b7..5d0445538d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6710,3 +6710,288 @@ qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, virBitmapFree(livevcpus); return ret; } + +struct _qemuHotplugTransientDiskContext { + virDomainDeviceDefPtr trandev; + virDomainDiskDefPtr *domdisk; + size_t ndd; +}; + +typedef struct _qemuHotplugTransientDiskContext qemuHotplugTransientDiskContext; +typedef qemuHotplugTransientDiskContext *qemuHotplugTransientDiskContextPtr; + +static qemuHotplugTransientDiskContextPtr +qemuHotplugTransientDiskContextNew(size_t ndisks) +{ + qemuHotplugTransientDiskContextPtr ret = g_new0(qemuHotplugTransientDiskContext, 1); + + ret->trandev = g_new0(virDomainDeviceDef, ndisks); + ret->domdisk = g_new0(virDomainDiskDefPtr, ndisks); + + return ret; +} + +static void +qemuHotplugTransientDiskCleanup(virDomainDeviceDefPtr data, + virDomainDiskDefPtr *domdisk) +{ + VIR_FREE(data); + VIR_FREE(domdisk); + + return; +} + +static void +qemuHotplugTransientDiskContextCleanup(qemuHotplugTransientDiskContextPtr hptctxt) +{ + if (!hptctxt) + return; + + qemuHotplugTransientDiskCleanup(hptctxt->trandev, hptctxt->domdisk); + + g_free(hptctxt); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuHotplugTransientDiskContext, qemuHotplugTransientDiskContextCleanup); + +static int +qemuHotplugDiskPrepareOneBlockdev(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + virDomainDiskDefPtr domdisk, + virDomainDiskDefPtr trandisk, + GHashTable *blockNamedNodeData, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + g_autoptr(qemuBlockStorageSourceChainData) data = NULL; + g_autoptr(virStorageSource) terminator = NULL; + + terminator = virStorageSourceNew(); + + if (qemuDomainPrepareStorageSourceBlockdev(trandisk, trandisk->src, + priv, cfg) < 0) + return -1; + + if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdevTop(trandisk->src, + terminator, + priv->qemuCaps))) + return -1; + + if (qemuBlockStorageSourceCreateDetectSize(blockNamedNodeData, + trandisk->src, domdisk->src) < 0) + return -1; + + if (qemuBlockStorageSourceCreate(vm, trandisk->src, domdisk->src, + NULL, data->srcdata[0], + asyncJob) < 0) + return -1; + + /* blockdev-del the transient disk src. The disk is blockdev-add'ed + * while the disk is hot-added */ + if (qemuBlockStorageSourceDetachOneBlockdev(driver, vm, + asyncJob, trandisk->src) < 0) + return -1; + + return 0; +} + +static int +qemuHotplugDiskTransientPrepareOne(virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + virDomainDiskDefPtr domdisk, + virDomainDiskDefPtr trandisk, + GHashTable *blockNamedNodeData, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + bool supportsCreate; + + if (qemuDomainStorageSourceValidateDepth(trandisk->src, 1, trandisk->dst) < 0) + return -1; + + if (virStorageSourceInitChainElement(trandisk->src, domdisk->src, false) < 0) + return -1; + + trandisk->src->readonly = false; + supportsCreate = virStorageSourceSupportsCreate(trandisk->src); + + if (supportsCreate) { + if (qemuDomainStorageFileInit(driver, vm, trandisk->src, NULL) < 0) + return -1; + + if (virStorageSourceCreate(trandisk->src) < 0) { + virReportSystemError(errno, _("failed to create image file '%s'"), + NULLSTR(trandisk->src->path)); + return -1; + } + } + + if (qemuDomainStorageSourceAccessAllow(driver, vm, trandisk->src, + false, true, true) < 0) + return -1; + + if (qemuHotplugDiskPrepareOneBlockdev(driver, vm, cfg, domdisk, trandisk, + blockNamedNodeData, asyncJob) < 0) + return -1; + + return 0; +} + +static qemuHotplugTransientDiskContextPtr +qemuHotplugDiskPrepareDisksTransient(virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + GHashTable *blockNamedNodeData, + qemuDomainAsyncJob asyncJob) +{ + g_autoptr(qemuHotplugTransientDiskContext) hptctxt = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + size_t i; + + hptctxt = qemuHotplugTransientDiskContextNew(vm->def->ndisks); + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr domdisk = vm->def->disks[i]; + virDomainDiskDefPtr trandisk; + virDomainDeviceDefPtr trandev; + + if (!(trandisk = virDomainDiskDefNew(driver->xmlopt))) + return NULL; + + trandev = hptctxt->trandev + hptctxt->ndd; + trandev->type = VIR_DOMAIN_DEVICE_DISK; + + memcpy(&trandisk->info, &domdisk->info, sizeof(virDomainDeviceInfo)); + trandisk->info.alias = NULL; + trandisk->info.romfile = NULL; + + if (domdisk->transient) { + trandisk->src = virStorageSourceNew(); + trandisk->src->type = VIR_STORAGE_TYPE_FILE; + trandisk->src->format = VIR_STORAGE_FILE_QCOW2; + trandisk->src->path = g_strdup_printf("%s.TRANSIENT-%s", + domdisk->src->path, vm->def->name); + + if (!(trandisk->src->backingStore = + virStorageSourceCopy(domdisk->src, false))) + return NULL; + + if (virFileExists(trandisk->src->path)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("Overlay file '%s' for transient disk '%s' already exists"), + trandisk->src->path, domdisk->dst); + return NULL; + } + + if (qemuHotplugDiskTransientPrepareOne(vm, cfg, domdisk, trandisk, + blockNamedNodeData, + asyncJob) < 0) + return NULL; + } else { + /* The disks without transient option will be device_add as well + * because to fix the bootindex */ + if (!(trandisk->src = virStorageSourceCopy(domdisk->src, false))) + return NULL; + } + + trandisk->bus = domdisk->bus; + trandisk->dst = g_strdup(domdisk->dst); + trandev->data.disk = trandisk; + + hptctxt->domdisk[hptctxt->ndd] = domdisk; + + hptctxt->ndd++; + } + + return g_steal_pointer(&hptctxt); +} + +static int +qemuHotplugDiskTransientCreate(virDomainObjPtr vm, + qemuHotplugTransientDiskContextPtr hptctxt, + virQEMUDriverConfigPtr cfg, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + const char *alias = NULL; + size_t i; + int ret; + + for (i = 0; i < hptctxt->ndd; i++) { + virDomainDeviceDefPtr trandev = hptctxt->trandev + i; + virDomainDiskDefPtr domdisk = hptctxt->domdisk[i]; + bool transient = domdisk->transient; + + /* transient disk doesn't support disk hotplug. Disable it here temporarily + * to remove it */ + if (transient) + domdisk->transient = false; + + /* blockdev-del StorageProgs and FormatProps of domdisk so that + * qemuDomainAttachDeviceDiskLiveInternal() can blockdev-add without + * write lock issue */ + if (qemuDomainRemoveDiskDevice(driver, vm, domdisk, asyncJob) < 0) + return -1; + + ret = qemuDomainAttachDeviceDiskLiveInternal(driver, vm, trandev, asyncJob); + if (!ret) { + alias = trandev->data.disk->info.alias; + if (transient) { + trandev->data.disk->transient = true; + } + } else { + VIR_DEBUG("Failed to attach disk %s (transient: %d) with disk hotplug.", + trandev->data.disk->dst, transient); + return -1; + } + + if (alias) { + virObjectEventPtr event; + event = virDomainEventDeviceAddedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->domainEventState, event); + } + + if (qemuDomainUpdateDeviceList(driver, vm, asyncJob) < 0) + return -1; + } + + if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0) + return -1; + + return 0; +} + +int +qemuHotplugCreateDisksTransient(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(qemuHotplugTransientDiskContext) hptctxt = NULL; + g_autoptr(GHashTable) blockNamedNodeData = NULL; + + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_PCIE_ROOT_PORT_HOTPLUG)) { + + VIR_DEBUG("prepare transient disks with disk hotplug"); + + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, asyncJob))) + return -1; + + if (!(hptctxt = qemuHotplugDiskPrepareDisksTransient(vm, cfg, + blockNamedNodeData, + asyncJob))) + return -1; + + if (qemuHotplugDiskTransientCreate(vm, hptctxt, cfg, asyncJob) < 0) + return -1; + } + + priv->inhibitDiskTransientDelete = false; + + return 0; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 6287c5b5e8..baef2dba42 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -160,3 +160,6 @@ int qemuHotplugAttachDBusVMState(virQEMUDriverPtr driver, int qemuHotplugRemoveDBusVMState(virQEMUDriverPtr driver, virDomainObjPtr vm, qemuDomainAsyncJob asyncJob); + +int qemuHotplugCreateDisksTransient(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob); -- 2.27.0