QEMU already supports migration using a file descriptor that is passed
to QEMU. As of now, libvirt, uses this path via the
tunnelled migration approach, where libvirt creates a set of fds from
a pipe, passes those fds to QEMU, and migrates the VM.

This patch introduces supports for fd based live migrations in libvirt,
where the fds are opened and given to libvirt by the client. Clients are
expected to pass the FDs on both source and destination libvirt, via the
virDomainFDStore API, with the "fd" key being the domain UUID. If the
URI of the migration has been set to "fd", libvirt will try to look for
an FD passed to it via the client meant for migrating the VM, and will
pass on that FD to QEMU and trigger the migration.

Signed-off-by: Tejus GK <[email protected]>
---
 src/qemu/qemu_migration.c | 114 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 112 insertions(+), 2 deletions(-)

diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 0cd417c15d..66e1236e6c 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -38,6 +38,7 @@
 #include "qemu_security.h"
 #include "qemu_slirp.h"
 #include "qemu_block.h"
+#include "qemu_fd.h"
 #include "qemu_tpm.h"
 #include "qemu_vhost_user.h"
 
@@ -3137,6 +3138,8 @@ qemuMigrationDstPrepare(virQEMUDriver *driver,
 
     if (tunnel) {
         migrateFrom = g_strdup("stdio");
+    } else if (g_strcmp0(protocol, "fd") == 0) {
+        migrateFrom = g_strdup("fd");
     } else if (g_strcmp0(protocol, "unix") == 0) {
         migrateFrom = g_strdup_printf("%s:%s", protocol, listenAddress);
     } else {
@@ -3301,6 +3304,8 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver,
     unsigned int startFlags;
     bool relabel = false;
     bool tunnel = !!st;
+    bool useDestFD = STREQ_NULLABLE(protocol, "fd");
+    int destfd = -1;
     int ret = -1;
     int rv;
 
@@ -3319,6 +3324,37 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver,
         virPipe(dataFD) < 0)
         goto error;
 
+    if (useDestFD) {
+        qemuFDTuple *fdtuple = NULL;
+        const char *fdName = vm->def->uuid;
+
+        VIR_WITH_MUTEX_LOCK_GUARD(&driver->lock) {
+            fdtuple = g_hash_table_lookup(driver->domainFDs, fdName);
+            if (!fdtuple) {
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                               _("no file descriptor stored for migration of 
domain '%1$s'"),
+                               fdName);
+                goto error;
+            }
+
+            if (fdtuple->nfds != 1) {
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                               _("expected a single file descriptor for 
migration of domain '%1$s'"),
+                               fdName);
+                goto error;
+            }
+
+            if ((destfd = dup(fdtuple->fds[0])) < 0) {
+                virReportSystemError(errno,
+                                     _("failed to duplicate migration FD for 
domain '%1$s'"),
+                                     fdName);
+                goto error;
+            }
+
+            g_hash_table_remove(driver->domainFDs, fdName);
+        }
+    }
+
     startFlags = VIR_QEMU_PROCESS_START_AUTODESTROY;
 
     if (qemuProcessInit(driver, vm, mig->cpu, VIR_ASYNC_JOB_MIGRATION_IN,
@@ -3328,7 +3364,7 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver,
 
     if (!(incoming = qemuMigrationDstPrepare(driver, vm, tunnel, protocol,
                                              listenAddress, port,
-                                             &dataFD[0])))
+                                             useDestFD ? destfd : dataFD[0])))
         goto error;
 
     qemuMigrationDstPrepareDiskSeclabels(vm, migrate_disks, flags);
@@ -3437,6 +3473,7 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver,
 
  cleanup:
     qemuProcessIncomingDefFree(incoming);
+    VIR_FORCE_CLOSE(destfd);
     VIR_FORCE_CLOSE(dataFD[0]);
     VIR_FORCE_CLOSE(dataFD[1]);
     virObjectEventStateQueue(driver->domainEventState, event);
@@ -3974,7 +4011,8 @@ qemuMigrationDstPrepareDirect(virQEMUDriver *driver,
 
         if (STRNEQ(uri->scheme, "tcp") &&
             STRNEQ(uri->scheme, "rdma") &&
-            STRNEQ(uri->scheme, "unix")) {
+            STRNEQ(uri->scheme, "unix") &&
+            STRNEQ(uri->scheme, "fd")) {
             virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
                            _("unsupported scheme %1$s in migration URI %2$s"),
                            uri->scheme, uri_in);
@@ -3984,6 +4022,9 @@ qemuMigrationDstPrepareDirect(virQEMUDriver *driver,
         if (STREQ(uri->scheme, "unix")) {
             autoPort = false;
             listenAddress = uri->path;
+        } else if (STREQ(uri->scheme, "fd")) {
+            autoPort = false;
+            listenAddress = NULL;
         } else {
             if (uri->server == NULL) {
                 virReportError(VIR_ERR_INVALID_ARG,
@@ -5411,6 +5452,75 @@ qemuMigrationSrcPerformNative(virQEMUDriver *driver,
             spec.destType = MIGRATION_DEST_CONNECT_SOCKET;
 
         spec.dest.socket.path = uribits->path;
+    } else if (STREQ(uribits->scheme, "fd")) {
+
+        if (flags & VIR_MIGRATE_PARALLEL) {
+            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                           _("FD-based migration is not supported with 
multi-fd migrations"));
+            return -1;
+        }
+
+        if (flags & VIR_MIGRATE_POSTCOPY) {
+            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                           _("FD-based migration is not supported with 
post-copy migration"));
+            return -1;
+        }
+        if (flags & VIR_MIGRATE_POSTCOPY_RESUME) {
+            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                           _("FD-based migration is not supported with 
post-copy resume"));
+            return -1;
+        }
+        if (flags & VIR_MIGRATE_ZEROCOPY) {
+            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                           _("FD-based migration is not supported with 
zero-copy migration"));
+            return -1;
+        }
+
+        if (flags & VIR_MIGRATE_TLS) {
+            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                           _("FD-based migration is not supported with TLS 
migration"));
+            return -1;
+        }
+
+        qemuFDTuple *fdtuple = NULL;
+        const char *fdName = vm->def->uuid;
+        int srcfd = -1;
+
+        VIR_WITH_MUTEX_LOCK_GUARD(&driver->lock) {
+            fdtuple = g_hash_table_lookup(driver->domainFDs, fdName);
+            if (!fdtuple) {
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                                _("no file descriptor stored for migration of 
domain '%1$s'"),
+                                fdName);
+                return -1;
+            }
+
+            if (fdtuple->nfds != 1) {
+                virReportError(VIR_ERR_OPERATION_INVALID,
+                                _("expected a single file descriptor for 
migration of domain '%1$s'"),
+                                fdName);
+                return -1;
+            }
+
+            if ((srcfd = dup(fdtuple->fds[0])) < 0) {
+                virReportSystemError(errno,
+                                        _("failed to duplicate migration FD 
for domain '%1$s'"),
+                                        fdName);
+                return -1;
+            }
+
+            g_hash_table_remove(driver->domainFDs, fdName);
+        }
+
+        if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, 
srcfd) < 0) {
+            VIR_FORCE_CLOSE(srcfd);
+            return -1;
+        }
+
+        spec.destType = MIGRATION_DEST_FD;
+        spec.dest.fd.qemu = srcfd;
+        spec.dest.fd.local = -1;
+
     } else {
         /* RDMA, multi-fd, and postcopy-preempt migration require QEMU to
          * connect to the destination itself.
-- 
2.43.7

Reply via email to