Signed-off-by: Jim Fehlig <jfehlig@xxxxxxxx> --- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_migration.c | 79 ++++++++++++++++++++++++++++ src/qemu/qemu_migration.h | 7 +++ src/qemu/qemu_monitor.c | 32 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++ src/qemu/qemu_saveimage.c | 105 ++++++++++++++++++++++++++++++-------- src/qemu/qemu_saveimage.h | 1 + src/qemu/qemu_snapshot.c | 2 +- 8 files changed, 208 insertions(+), 24 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f9761242d2..34f37210d9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2696,7 +2696,7 @@ qemuDomainSaveInternal(virQEMUDriver *driver, if (!(cookie = qemuDomainSaveCookieNew(vm))) goto endjob; - if (!(data = virQEMUSaveDataNew(driver, xml, cookie, was_running, compressed))) + if (!(data = virQEMUSaveDataNew(driver, vm, xml, cookie, was_running, compressed))) goto endjob; xml = NULL; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 1faab5dd23..3110ef2621 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -7072,6 +7072,85 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainObj *vm, } +int +qemuMigrationSrcToMappedFile(virQEMUDriver *driver, virDomainObj *vm, + int fd, + virDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivate *priv = vm->privateData; + g_autoptr(qemuMigrationParams) saveParams = NULL; + unsigned long saveMigBandwidth = priv->migMaxBandwidth; + int rc; + int ret = -1; + virErrorPtr orig_err = NULL; + + if (qemuMigrationSetDBusVMState(driver, vm) < 0) + return -1; + + if (!(saveParams = qemuMigrationParamsForMappedSave())) + return -1; + + /* Increase migration bandwidth to unlimited since target is a file. + * Failure to change migration speed is not fatal. */ + if (qemuMigrationParamsSetULL(saveParams, + QEMU_MIGRATION_PARAM_MAX_BANDWIDTH, + QEMU_DOMAIN_MIG_BANDWIDTH_MAX * 1024 * 1024) < 0) + return -1; + + if (qemuMigrationParamsApply(vm, asyncJob, saveParams, 0) < 0) + return -1; + + priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit")); + /* nothing to tear down */ + return -1; + } + + if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) + goto cleanup; + + rc = qemuMonitorMigrateToFdSet(vm, 0, fd); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto cleanup; + + rc = qemuMigrationSrcWaitForCompletion(vm, asyncJob, NULL, 0); + + if (rc < 0) { + if (rc == -2) { + virErrorPreserveLast(&orig_err); + if (virDomainObjIsActive(vm)) + qemuMigrationSrcCancel(vm, asyncJob, true); + } + goto cleanup; + } + + qemuDomainEventEmitJobCompleted(driver, vm); + ret = 0; + + cleanup: + if (ret < 0 && !orig_err) + virErrorPreserveLast(&orig_err); + + /* Restore max migration bandwidth */ + if (virDomainObjIsActive(vm)) { + if (qemuMigrationParamsSetULL(saveParams, + QEMU_MIGRATION_PARAM_MAX_BANDWIDTH, + saveMigBandwidth * 1024 * 1024) == 0) + ignore_value(qemuMigrationParamsApply(vm, asyncJob, + saveParams, 0)); + priv->migMaxBandwidth = saveMigBandwidth; + } + + virErrorRestore(&orig_err); + + return ret; +} + + /** * This function is supposed to be used only to while reconnecting to a domain * with an active migration job. diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index ed62fd4a91..f845a0198b 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -241,6 +241,13 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainAsyncJob asyncJob) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; +int +qemuMigrationSrcToMappedFile(virQEMUDriver *driver, + virDomainObj *vm, + int fd, + virDomainAsyncJob asyncJob) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; + int qemuMigrationSrcCancelUnattended(virDomainObj *vm, virDomainJobObj *oldJob); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 34e2ccab97..4c92bd740a 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2237,6 +2237,38 @@ qemuMonitorMigrateToFd(qemuMonitor *mon, } +int +qemuMonitorMigrateToFdSet(virDomainObj *vm, + unsigned int flags, + int fd) +{ + qemuDomainObjPrivate *priv = vm->privateData; + qemuMonitor *mon = priv->mon; + off_t offset; + g_autoptr(qemuFDPass) fdPassMigrate = NULL; + unsigned int setId; + g_autofree char *uri = NULL; + + VIR_DEBUG("fd=%d flags=0x%x", fd, flags); + + QEMU_CHECK_MONITOR(mon); + + if ((offset = lseek(fd, 0, SEEK_CUR)) == -1) + return -1; + + fdPassMigrate = qemuFDPassNew("migrate", priv); + qemuFDPassAddFD(fdPassMigrate, &fd, "-fd"); + qemuFDPassTransferMonitor(fdPassMigrate, mon); + + if (qemuFDPassGetId(fdPassMigrate, &setId) < 0) + return -1; + + uri = g_strdup_printf("file:/dev/fdset/%u,offset=%#lx", setId, offset); + + return qemuMonitorJSONMigrate(mon, flags, uri); +} + + int qemuMonitorMigrateToHost(qemuMonitor *mon, unsigned int flags, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6e81945201..c477def138 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -843,6 +843,10 @@ int qemuMonitorMigrateToFd(qemuMonitor *mon, unsigned int flags, int fd); +int qemuMonitorMigrateToFdSet(virDomainObj *vm, + unsigned int flags, + int fd); + int qemuMonitorMigrateToHost(qemuMonitor *mon, unsigned int flags, const char *protocol, diff --git a/src/qemu/qemu_saveimage.c b/src/qemu/qemu_saveimage.c index 30085dc7bc..8f28770086 100644 --- a/src/qemu/qemu_saveimage.c +++ b/src/qemu/qemu_saveimage.c @@ -96,6 +96,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virQEMUSaveData, virQEMUSaveDataFree); */ virQEMUSaveData * virQEMUSaveDataNew(virQEMUDriver *driver, + virDomainObj *vm, char *domXML, qemuDomainSaveCookie *cookieObj, bool running, @@ -122,6 +123,10 @@ virQEMUSaveDataNew(virQEMUDriver *driver, goto error; } header->version = cfg->saveImageVersion; + /* Enable mapped-ram feature if available and save version >= 3 */ + if (header->version >= QEMU_SAVE_VERSION && + qemuMigrationCapsGet(vm, QEMU_MIGRATION_CAP_MAPPED_RAM)) + header->features |= QEMU_SAVE_FEATURE_MAPPED_RAM; header->was_running = running ? 1 : 0; header->compressed = compressed; @@ -369,6 +374,79 @@ qemuSaveImageDecompressionStop(virCommand *cmd, } +static int +qemuSaveImageCreateSequential(virQEMUDriver *driver, + virDomainObj *vm, + const char *path, + int fd, + virQEMUSaveData *data, + virCommand *compressor, + unsigned int flags, + virDomainAsyncJob asyncJob) +{ + int ret = -1; + virFileWrapperFd *wrapperFd = NULL; + unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING; + + if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) + wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE; + + if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags))) + goto cleanup; + + if (virQEMUSaveDataWrite(data, fd, path) < 0) + goto cleanup; + + /* Perform the migration */ + if (qemuMigrationSrcToFile(driver, vm, fd, compressor, asyncJob) < 0) + goto cleanup; + + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, _("unable to close %1$s"), path); + goto cleanup; + } + + if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FORCE_CLOSE(fd); + if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0) + ret = -1; + virFileWrapperFdFree(wrapperFd); + + return ret; +} + + +static int +qemuSaveImageCreateMapped(virQEMUDriver *driver, + virDomainObj *vm, + const char *path, + int fd, + virQEMUSaveData *data, + unsigned int flags, + virDomainAsyncJob asyncJob) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + + /* mapped-ram does not support directIO */ + if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("bypass cache unsupported by this system")); + return -1; + } + + if (virQEMUSaveDataWrite(data, fd, path) < 0) + return -1; + + /* Perform the migration */ + return qemuMigrationSrcToMappedFile(driver, vm, fd, asyncJob); +} + + /* Helper function to execute a migration to file with a correct save header * the caller needs to make sure that the processors are stopped and do all other * actions besides saving memory */ @@ -386,12 +464,9 @@ qemuSaveImageCreate(virQEMUDriver *driver, int ret = -1; int fd = -1; int directFlag = 0; - virFileWrapperFd *wrapperFd = NULL; - unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING; /* Obtain the file handle. */ if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) { - wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE; directFlag = virFileDirectFdFlag(); if (directFlag < 0) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", @@ -409,14 +484,12 @@ qemuSaveImageCreate(virQEMUDriver *driver, if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) < 0) goto cleanup; - if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags))) - goto cleanup; + if (data->header.features & QEMU_SAVE_FEATURE_MAPPED_RAM) + ret = qemuSaveImageCreateMapped(driver, vm, path, fd, data, flags, asyncJob); + else + ret = qemuSaveImageCreateSequential(driver, vm, path, fd, data, compressor, flags, asyncJob); - if (virQEMUSaveDataWrite(data, fd, path) < 0) - goto cleanup; - - /* Perform the migration */ - if (qemuMigrationSrcToFile(driver, vm, fd, compressor, asyncJob) < 0) + if (ret < 0) goto cleanup; /* Touch up file header to mark image complete. */ @@ -425,14 +498,6 @@ qemuSaveImageCreate(virQEMUDriver *driver, * up to seek backwards on wrapperFd. The reopened fd will * trigger a single page of file system cache pollution, but * that's acceptable. */ - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, _("unable to close %1$s"), path); - goto cleanup; - } - - if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0) - goto cleanup; - if ((fd = qemuDomainOpenFile(cfg, vm->def, path, O_WRONLY, NULL)) < 0 || virQEMUSaveDataFinish(data, &fd, path) < 0) goto cleanup; @@ -441,10 +506,6 @@ qemuSaveImageCreate(virQEMUDriver *driver, cleanup: VIR_FORCE_CLOSE(fd); - if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0) - ret = -1; - virFileWrapperFdFree(wrapperFd); - if (ret < 0 && needUnlink) unlink(path); diff --git a/src/qemu/qemu_saveimage.h b/src/qemu/qemu_saveimage.h index 63ad5508ed..81d93bf33c 100644 --- a/src/qemu/qemu_saveimage.h +++ b/src/qemu/qemu_saveimage.h @@ -124,6 +124,7 @@ virQEMUSaveDataWrite(virQEMUSaveData *data, virQEMUSaveData * virQEMUSaveDataNew(virQEMUDriver *driver, + virDomainObj *vm, char *domXML, qemuDomainSaveCookie *cookieObj, bool running, diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 1d75208814..1e9e0e31d7 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -1390,7 +1390,7 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driver, !(snapdef->cookie = (virObject *) qemuDomainSaveCookieNew(vm))) goto cleanup; - if (!(data = virQEMUSaveDataNew(driver, xml, + if (!(data = virQEMUSaveDataNew(driver, vm, xml, (qemuDomainSaveCookie *) snapdef->cookie, resume, compressed))) goto cleanup; -- 2.44.0