Add support for the mapped-ram migration capability on restore.
Using mapped-ram with QEMU to restore an image requires the same
steps as saving:
- The 'mapped-ram' migration capability must be set to true
- The 'multifd' migration capability must be set to true and
the 'multifd-channels' migration parameter must set to a
value >= 1
- QEMU must be provided an fdset containing the migration fd(s)
- The 'migrate-incoming' qmp command is invoked with a URI referencing
the fdset and an offset where to start reading the data stream, e.g.
{"execute":"migrate-incoming",
"arguments":{"uri":"file:/dev/fdset/0,offset=0x119eb"}}
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
src/qemu/qemu_driver.c | 26 +++++++++++++-----
src/qemu/qemu_migration.c | 11 ++++----
src/qemu/qemu_process.c | 58 +++++++++++++++++++++++++++++++--------
src/qemu/qemu_process.h | 15 ++++++----
src/qemu/qemu_saveimage.c | 9 ++++--
src/qemu/qemu_saveimage.h | 2 ++
src/qemu/qemu_snapshot.c | 8 +++---
7 files changed, 92 insertions(+), 37 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 87d75b6baa..6d0f52951c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1611,7 +1611,7 @@ static virDomainPtr qemuDomainCreateXML(virConnectPtr conn,
}
if (qemuProcessStart(conn, driver, vm, NULL, VIR_ASYNC_JOB_START,
- NULL, -1, NULL, NULL,
+ NULL, -1, NULL, NULL, NULL,
VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
start_flags) < 0) {
virDomainAuditStart(vm, "booted", false);
@@ -5774,6 +5774,8 @@ qemuDomainRestoreInternal(virConnectPtr conn,
virFileWrapperFd *wrapperFd = NULL;
bool hook_taint = false;
bool reset_nvram = false;
+ bool mapped_ram = false;
+ g_autoptr(qemuMigrationParams) restoreParams = NULL;
virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE |
VIR_DOMAIN_SAVE_RUNNING |
@@ -5786,9 +5788,13 @@ qemuDomainRestoreInternal(virConnectPtr conn,
if (qemuSaveImageGetMetadata(driver, NULL, path, &def, &data, false) < 0)
goto cleanup;
+ mapped_ram = data->header.features & QEMU_SAVE_FEATURE_MAPPED_RAM;
+ if (!(restoreParams = qemuMigrationParamsForSave(mapped_ram)))
+ return -1;
+
fd = qemuSaveImageOpen(driver, path,
(flags & VIR_DOMAIN_SAVE_BYPASS_CACHE) != 0,
- &wrapperFd, false);
+ mapped_ram, &wrapperFd, false);
if (fd < 0)
goto cleanup;
@@ -5842,7 +5848,7 @@ qemuDomainRestoreInternal(virConnectPtr conn,
if (qemuProcessBeginJob(vm, VIR_DOMAIN_JOB_OPERATION_RESTORE, flags) < 0)
goto cleanup;
- ret = qemuSaveImageStartVM(conn, driver, vm, &fd, data, path,
+ ret = qemuSaveImageStartVM(conn, driver, vm, &fd, data, path, restoreParams,
false, reset_nvram, VIR_ASYNC_JOB_START);
qemuProcessEndJob(vm);
@@ -5957,7 +5963,7 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path,
if (qemuSaveImageGetMetadata(driver, NULL, path, &def, &data, false) < 0)
goto cleanup;
- fd = qemuSaveImageOpen(driver, path, 0, NULL, false);
+ fd = qemuSaveImageOpen(driver, path, 0, false, NULL, false);
if (fd < 0)
goto cleanup;
@@ -6098,6 +6104,8 @@ qemuDomainObjRestore(virConnectPtr conn,
g_autofree char *xmlout = NULL;
virQEMUSaveData *data = NULL;
virFileWrapperFd *wrapperFd = NULL;
+ bool mapped_ram = false;
+ g_autoptr(qemuMigrationParams) restoreParams = NULL;
ret = qemuSaveImageGetMetadata(driver, NULL, path, &def, &data, true);
if (ret < 0) {
@@ -6106,7 +6114,11 @@ qemuDomainObjRestore(virConnectPtr conn,
goto cleanup;
}
- fd = qemuSaveImageOpen(driver, path, bypass_cache, &wrapperFd, false);
+ mapped_ram = data->header.features & QEMU_SAVE_FEATURE_MAPPED_RAM;
+ if (!(restoreParams = qemuMigrationParamsForSave(mapped_ram)))
+ return -1;
+
+ fd = qemuSaveImageOpen(driver, path, bypass_cache, mapped_ram, &wrapperFd,
false);
if (fd < 0)
goto cleanup;
@@ -6148,7 +6160,7 @@ qemuDomainObjRestore(virConnectPtr conn,
virDomainObjAssignDef(vm, &def, true, NULL);
- ret = qemuSaveImageStartVM(conn, driver, vm, &fd, data, path,
+ ret = qemuSaveImageStartVM(conn, driver, vm, &fd, data, path, restoreParams,
start_paused, reset_nvram, asyncJob);
cleanup:
@@ -6349,7 +6361,7 @@ qemuDomainObjStart(virConnectPtr conn,
}
ret = qemuProcessStart(conn, driver, vm, NULL, asyncJob,
- NULL, -1, NULL, NULL,
+ NULL, -1, NULL, NULL, NULL,
VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags);
virDomainAuditStart(vm, "booted", ret >= 0);
if (ret >= 0) {
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index fd897b4ed1..35d3e26908 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -2953,9 +2953,8 @@ qemuMigrationDstPrepare(virDomainObj *vm,
const char *protocol,
const char *listenAddress,
unsigned short port,
- int fd)
+ int *fd)
{
- qemuDomainObjPrivate *priv = vm->privateData;
g_autofree char *migrateFrom = NULL;
if (tunnel) {
@@ -3009,8 +3008,8 @@ qemuMigrationDstPrepare(virDomainObj *vm,
migrateFrom = g_strdup_printf(incFormat, protocol, listenAddress, port);
}
- return qemuProcessIncomingDefNew(priv->qemuCaps, listenAddress,
- migrateFrom, fd, NULL);
+ return qemuProcessIncomingDefNew(vm, listenAddress,
+ migrateFrom, fd, NULL, NULL);
}
@@ -3154,7 +3153,7 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver,
if (!(incoming = qemuMigrationDstPrepare(vm, tunnel, protocol,
listenAddress, port,
- dataFD[0])))
+ &dataFD[0])))
goto error;
if (qemuProcessPrepareDomain(driver, vm, startFlags) < 0)
@@ -3524,7 +3523,7 @@ qemuMigrationDstPrepareResume(virQEMUDriver *driver,
priv->origname = g_strdup(origname);
if (!(incoming = qemuMigrationDstPrepare(vm, false, protocol,
- listenAddress, port, -1)))
+ listenAddress, port, NULL)))
goto cleanup;
if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_MIGRATION_IN) < 0)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 1d7f214212..b02cd84aff 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -4680,6 +4680,7 @@ qemuProcessIncomingDefFree(qemuProcessIncomingDef *inc)
g_free(inc->address);
g_free(inc->uri);
+ qemuFDPassFree(inc->fdPassMigrate);
g_free(inc);
}
@@ -4693,26 +4694,54 @@ qemuProcessIncomingDefFree(qemuProcessIncomingDef *inc)
* qemuProcessIncomingDefFree will NOT close it.
*/
qemuProcessIncomingDef *
-qemuProcessIncomingDefNew(virQEMUCaps *qemuCaps,
+qemuProcessIncomingDefNew(virDomainObj *vm,
const char *listenAddress,
const char *migrateFrom,
- int fd,
- const char *path)
+ int *fd,
+ const char *path,
+ virQEMUSaveData *data)
{
+ qemuDomainObjPrivate *priv = vm->privateData;
qemuProcessIncomingDef *inc = NULL;
- if (qemuMigrationDstCheckProtocol(qemuCaps, migrateFrom) < 0)
+ if (qemuMigrationDstCheckProtocol(priv->qemuCaps, migrateFrom) < 0)
return NULL;
inc = g_new0(qemuProcessIncomingDef, 1);
inc->address = g_strdup(listenAddress);
- inc->uri = qemuMigrationDstGetURI(migrateFrom, fd);
+ if (data) {
+ size_t offset = sizeof(virQEMUSaveHeader) + data->header.data_len;
+
+ if (data->header.features & QEMU_SAVE_FEATURE_MAPPED_RAM) {
+ unsigned int fdsetId;
+
+ inc->fdPassMigrate = qemuFDPassNew("migrate", priv);
+ qemuFDPassAddFD(inc->fdPassMigrate, fd, "-buffered-fd");
+ qemuFDPassGetId(inc->fdPassMigrate, &fdsetId);
+ inc->uri = g_strdup_printf("file:/dev/fdset/%u,offset=%#lx",
fdsetId, offset);
+ } else {
+ g_autofree char *tmp = g_new(char, offset);
+
+ /* HACK:
+ * We provide qemu the offset in case of mapped-ram, but must set
+ * the file offset for the legacy save format. Unfortunately we
+ * can't lseek when the fd is wrapped by virFileWrapperFd, so
+ * we do a needless read instead.
+ */
+ if (saferead(*fd, tmp, offset) != offset)
+ VIR_DEBUG("failed to read from save file");
+ inc->uri = qemuMigrationDstGetURI(migrateFrom, *fd);
+ }
+ } else {
+ inc->uri = qemuMigrationDstGetURI(migrateFrom, *fd);
+ }
+
if (!inc->uri)
goto error;
- inc->fd = fd;
+ inc->fd = *fd;
inc->path = path;
return inc;
@@ -7782,8 +7811,11 @@ qemuProcessLaunch(virConnectPtr conn,
&nnicindexes, &nicindexes)))
goto cleanup;
- if (incoming && incoming->fd != -1)
- virCommandPassFD(cmd, incoming->fd, 0);
+ if (incoming) {
+ if (incoming->fd != -1)
+ virCommandPassFD(cmd, incoming->fd, 0);
+ qemuFDPassTransferCommand(incoming->fdPassMigrate, cmd);
+ }
/* now that we know it is about to start call the hook if present */
if (qemuProcessStartHook(driver, vm,
@@ -8195,6 +8227,7 @@ qemuProcessStart(virConnectPtr conn,
int migrateFd,
const char *migratePath,
virDomainMomentObj *snapshot,
+ qemuMigrationParams *migParams,
virNetDevVPortProfileOp vmop,
unsigned int flags)
{
@@ -8248,7 +8281,7 @@ qemuProcessStart(virConnectPtr conn,
relabel = true;
if (incoming) {
- if (qemuMigrationDstRun(vm, incoming->uri, asyncJob, NULL, 0) < 0)
+ if (qemuMigrationDstRun(vm, incoming->uri, asyncJob, migParams, 0) < 0)
goto stop;
} else {
/* Refresh state of devices from QEMU. During migration this happens
@@ -8302,6 +8335,7 @@ qemuProcessStart(virConnectPtr conn,
* @path: path to memory state file
* @snapshot: internal snapshot to load when starting QEMU process or NULL
* @data: data from memory state file or NULL
+ * @migParams: Migration params to use on restore or NULL
* @asyncJob: type of asynchronous job
* @start_flags: flags to start QEMU process with
* @reason: audit log reason
@@ -8328,6 +8362,7 @@ qemuProcessStartWithMemoryState(virConnectPtr conn,
const char *path,
virDomainMomentObj *snapshot,
virQEMUSaveData *data,
+ qemuMigrationParams *migParams,
virDomainAsyncJob asyncJob,
unsigned int start_flags,
const char *reason,
@@ -8342,8 +8377,7 @@ qemuProcessStartWithMemoryState(virConnectPtr conn,
int rc = 0;
int ret = -1;
- incoming = qemuProcessIncomingDefNew(priv->qemuCaps, NULL, "stdio",
- *fd, path);
+ incoming = qemuProcessIncomingDefNew(vm, NULL, "stdio", fd, path, data);
if (!incoming)
return -1;
@@ -8369,7 +8403,7 @@ qemuProcessStartWithMemoryState(virConnectPtr conn,
if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL,
asyncJob, incoming, *fd, path, snapshot,
- VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
+ migParams, VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
start_flags) == 0)
*started = true;
diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
index e48d53dc46..93699da8bd 100644
--- a/src/qemu/qemu_process.h
+++ b/src/qemu/qemu_process.h
@@ -54,14 +54,17 @@ struct _qemuProcessIncomingDef {
char *address; /* address where QEMU is supposed to listen */
char *uri; /* used when calling migrate-incoming QMP command */
int fd; /* for fd:N URI */
+ qemuFDPass *fdPassMigrate; /* for file:/dev/fdset/n,offset=x URI */
const char *path; /* path associated with fd */
};
-qemuProcessIncomingDef *qemuProcessIncomingDefNew(virQEMUCaps *qemuCaps,
- const char *listenAddress,
- const char *migrateFrom,
- int fd,
- const char *path);
+qemuProcessIncomingDef *qemuProcessIncomingDefNew(virDomainObj *vm,
+ const char *listenAddress,
+ const char *migrateFrom,
+ int *fd,
+ const char *path,
+ virQEMUSaveData *data);
+
void qemuProcessIncomingDefFree(qemuProcessIncomingDef *inc);
int qemuProcessBeginJob(virDomainObj *vm,
@@ -88,6 +91,7 @@ int qemuProcessStart(virConnectPtr conn,
int stdin_fd,
const char *stdin_path,
virDomainMomentObj *snapshot,
+ qemuMigrationParams *migParams,
virNetDevVPortProfileOp vmop,
unsigned int flags);
@@ -98,6 +102,7 @@ int qemuProcessStartWithMemoryState(virConnectPtr conn,
const char *path,
virDomainMomentObj *snapshot,
virQEMUSaveData *data,
+ qemuMigrationParams *migParams,
virDomainAsyncJob asyncJob,
unsigned int start_flags,
const char *reason,
diff --git a/src/qemu/qemu_saveimage.c b/src/qemu/qemu_saveimage.c
index 125064ab66..b99e0de1ff 100644
--- a/src/qemu/qemu_saveimage.c
+++ b/src/qemu/qemu_saveimage.c
@@ -711,6 +711,7 @@ qemuSaveImageGetMetadata(virQEMUDriver *driver,
* @driver: qemu driver data
* @path: path of the save image
* @bypass_cache: bypass cache when opening the file
+ * @mapped_ram: Image contains mapped-ram save format
* @wrapperFd: returns the file wrapper structure
* @open_write: open the file for writing (for updates)
*
@@ -720,6 +721,7 @@ int
qemuSaveImageOpen(virQEMUDriver *driver,
const char *path,
bool bypass_cache,
+ bool mapped_ram,
virFileWrapperFd **wrapperFd,
bool open_write)
{
@@ -741,7 +743,7 @@ qemuSaveImageOpen(virQEMUDriver *driver,
if ((fd = qemuDomainOpenFile(cfg, NULL, path, oflags, NULL)) < 0)
return -1;
- if (bypass_cache &&
+ if (!mapped_ram && bypass_cache &&
!(*wrapperFd = virFileWrapperFdNew(&fd, path,
VIR_FILE_WRAPPER_BYPASS_CACHE)))
return -1;
@@ -760,6 +762,7 @@ qemuSaveImageStartVM(virConnectPtr conn,
int *fd,
virQEMUSaveData *data,
const char *path,
+ qemuMigrationParams *restoreParams,
bool start_paused,
bool reset_nvram,
virDomainAsyncJob asyncJob)
@@ -776,8 +779,8 @@ qemuSaveImageStartVM(virConnectPtr conn,
start_flags |= VIR_QEMU_PROCESS_START_RESET_NVRAM;
if (qemuProcessStartWithMemoryState(conn, driver, vm, fd, path, NULL, data,
- asyncJob, start_flags, "restored",
- &started) < 0) {
+ restoreParams, asyncJob, start_flags,
+ "restored", &started) < 0) {
goto cleanup;
}
diff --git a/src/qemu/qemu_saveimage.h b/src/qemu/qemu_saveimage.h
index e101fdba6e..2007784e88 100644
--- a/src/qemu/qemu_saveimage.h
+++ b/src/qemu/qemu_saveimage.h
@@ -69,6 +69,7 @@ qemuSaveImageStartVM(virConnectPtr conn,
int *fd,
virQEMUSaveData *data,
const char *path,
+ qemuMigrationParams *restoreParams,
bool start_paused,
bool reset_nvram,
virDomainAsyncJob asyncJob)
@@ -87,6 +88,7 @@ int
qemuSaveImageOpen(virQEMUDriver *driver,
const char *path,
bool bypass_cache,
+ bool mapped_ram,
virFileWrapperFd **wrapperFd,
bool open_write);
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index 7d05ce76f4..79c20d48f4 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -2123,7 +2123,7 @@ qemuSnapshotRevertExternalPrepare(virDomainObj *vm,
return -1;
memdata->fd = qemuSaveImageOpen(driver, memdata->path,
- false, NULL, false);
+ false, false, NULL, false);
if (memdata->fd < 0)
return -1;
@@ -2366,7 +2366,7 @@ qemuSnapshotRevertActive(virDomainObj *vm,
if (qemuProcessStartWithMemoryState(snapshot->domain->conn, driver, vm,
&memdata.fd, memdata.path, loadSnap,
- memdata.data, VIR_ASYNC_JOB_SNAPSHOT,
+ memdata.data, NULL, VIR_ASYNC_JOB_SNAPSHOT,
start_flags, "from-snapshot",
&started) < 0) {
if (started) {
@@ -2520,7 +2520,7 @@ qemuSnapshotRevertInactive(virDomainObj *vm,
rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL,
VIR_ASYNC_JOB_SNAPSHOT, NULL, -1, NULL, NULL,
- VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
+ NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
start_flags);
virDomainAuditStart(vm, "from-snapshot", rc >= 0);
if (rc < 0) {
@@ -2997,7 +2997,7 @@ qemuSnapshotDeleteExternalPrepare(virDomainObj *vm,
if (!virDomainObjIsActive(vm)) {
if (qemuProcessStart(NULL, driver, vm, NULL, VIR_ASYNC_JOB_SNAPSHOT,
- NULL, -1, NULL, NULL,
+ NULL, -1, NULL, NULL, NULL,
VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
VIR_QEMU_PROCESS_START_PAUSED) < 0) {
return -1;
--
2.35.3