[libvirt] [RFC][PATCH v2 2/2] Automatically recreate macvtap interface and reconnect to qemu

Ed Swierk eswierk at aristanetworks.com
Tue Mar 9 00:29:59 UTC 2010


(See the PATCH 0 email for further discussion.)

---
The libvirt qemu driver creates a macvtap interface for each defined
"direct" network interface, but unlike a tap interface that is owned
by the process that creates it, a macvtap interface vanishes when the
underlying network device goes away.  When this happens, qemu is left
holding a file descriptor for a nonexistent tap interface.

This patch implements dynamically recreating a macvtap interface when
the underlying network device reappears, and reconnecting it to the
still-running qemu process.

It's a total hack job: after starting qemu, it spawns a thread
that wakes up once per second and attempts to create a new macvtap
interface for each defined direct interface.  If the macvtap
interface already exists (because it never disappeared) or can't
be created (because the underlying network device doesn't exist),
this is a no-op.  Only when the underlying network device reappears
does the reconnection complete.

At minimum, this should be done without the once-per-second polling.

Index: libvirt-0.7.7/src/qemu/qemu_driver.c
===================================================================
--- libvirt-0.7.7.orig/src/qemu/qemu_driver.c
+++ libvirt-0.7.7/src/qemu/qemu_driver.c
@@ -83,6 +83,7 @@
 #include "xml.h"
 #include "cpu/cpu.h"
 #include "macvtap.h"
+#include "threads.h"
 
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
@@ -107,6 +108,8 @@ struct _qemuDomainObjPrivate {
 
     qemuDomainPCIAddressSetPtr pciaddrs;
     int persistentAddrs;
+
+    pthread_t reconnectNetBackendsThread;
 };
 
 static int qemudShutdown(void);
@@ -151,6 +154,8 @@ static int qemudDomainDisconnectNetBacke
                                            virDomainObjPtr vm,
                                            virDomainNetDefPtr net);
 
+static void *qemudDomainReconnectNetBackends(void *opaque);
+
 static struct qemud_driver *qemu_driver = NULL;
 
 
@@ -2697,6 +2702,7 @@ static int qemudStartVMDaemon(virConnect
     char *pidfile = NULL;
     int logfile;
     qemuDomainObjPrivatePtr priv = vm->privateData;
+    pthread_attr_t threadAttr;
 
     struct qemudHookData hookData;
     hookData.conn = conn;
@@ -2950,6 +2956,15 @@ static int qemudStartVMDaemon(virConnect
     if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
         goto abort;
 
+    pthread_attr_init(&threadAttr);
+    pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+    if (pthread_create(&priv->reconnectNetBackendsThread, &threadAttr,
+                       qemudDomainReconnectNetBackends, vm->def->uuid)) {
+        pthread_attr_destroy(&threadAttr);
+        goto abort;
+    }
+    pthread_attr_destroy(&threadAttr);
+
     return 0;
 
 cleanup:
@@ -6248,7 +6263,7 @@ static int qemudDomainConnectNetBackend(
                                         unsigned long long qemuCmdFlags)
 {
     qemuDomainObjPrivatePtr priv = vm->privateData;
-    char *tapfd_name = NULL;
+    char *hostnet_name = NULL;
     int tapfd = -1;
     char *netstr = NULL;
     int ret = -1;
@@ -6286,27 +6301,30 @@ static int qemudDomainConnectNetBackend(
     if (tapfd < 0)
         return -1;
 
-    if (virAsprintf(&tapfd_name, "fd-%s", net->info.alias) < 0) {
+    if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0) {
         virReportOOMError();
         close(tapfd);
         return -1;
     }
 
     if (!(netstr = qemuBuildHostNetStr(net, ' ',
-                                       vlan, tapfd_name))) {
-        VIR_FREE(tapfd_name);
+                                       vlan, hostnet_name))) {
+        VIR_FREE(hostnet_name);
         close(tapfd);
         return -1;
     }
 
     qemuDomainObjEnterMonitorWithDriver(driver, vm);
 
-    if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0)
+    if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0)
+        VIR_INFO0("Did not remove existing host network");
+
+    if (qemuMonitorSendFileHandle(priv->mon, hostnet_name, tapfd) < 0)
         goto out;
 
     if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
-        if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0)
-            VIR_WARN(_("Failed to close tapfd with '%s'"), tapfd_name);
+        if (qemuMonitorCloseFileHandle(priv->mon, hostnet_name) < 0)
+            VIR_WARN(_("Failed to close tapfd with '%s'"), hostnet_name);
         goto out;
     }
 
@@ -6315,7 +6333,7 @@ static int qemudDomainConnectNetBackend(
 out:
     qemuDomainObjExitMonitorWithDriver(driver, vm);
     VIR_FREE(netstr);
-    VIR_FREE(tapfd_name);
+    VIR_FREE(hostnet_name);
     close(tapfd);
     return ret;
 }
@@ -9339,6 +9357,73 @@ out:
     return ret;
 }
 
+static void *qemudDomainReconnectNetBackends(void *opaque)
+{
+    virConnectPtr conn = virConnectOpen("qemu:///system");
+    struct qemud_driver *driver = conn->privateData;
+    const unsigned char *uuid = opaque;
+
+    while (1) {
+        virDomainObjPtr vm;
+        unsigned long long qemuCmdFlags;
+        int i;
+
+        usleep(1000000);
+
+        VIR_DEBUG(_("qemuDomainReconnectNetBackends (%p %p)"),
+                  conn, driver);
+
+        qemuDriverLock(driver);
+        vm = virDomainFindByUUID(&driver->domains, uuid);
+        if (!vm) {
+            VIR_DEBUG0("qemuDomainReconnectNetBackends done");
+            qemuDriverUnlock(driver);
+            break;
+        }
+
+        VIR_DEBUG(_("qemuDomainReconnectNetBackends (%s)"),
+                  vm->def->name);
+
+        if (qemudExtractVersionInfo(vm->def->emulator,
+                                    NULL,
+                                    &qemuCmdFlags) < 0)
+            goto cleanup;
+
+        if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
+            goto cleanup;
+
+        if (!virDomainObjIsActive(vm))
+            goto endjob;
+
+        for (i = 0 ; i < vm->def->nnets ; i++) {
+            virDomainNetDefPtr net = vm->def->nets[i];
+
+            if (net->type != VIR_DOMAIN_NET_TYPE_DIRECT)
+                continue;
+
+            if (qemudDomainConnectNetBackend(conn, driver, vm,
+                                             net, qemuCmdFlags) == 0) {
+                VIR_WARN(_("Reconnected interface %s (%s) for domain %s"),
+                           net->data.direct.linkdev, net->ifname, vm->def->name);
+            } else {
+                VIR_DEBUG(_("Unable to reconnect interface %s for domain %s"),
+                           net->data.direct.linkdev, vm->def->name);
+            }
+        }
+
+    endjob:
+        if (qemuDomainObjEndJob(vm) == 0)
+            vm = NULL;
+
+    cleanup:
+        if (vm)
+            virDomainObjUnlock(vm);
+        qemuDriverUnlock(driver);
+    }
+
+    return NULL;
+}
+
 static int
 qemuCPUCompare(virConnectPtr conn,
                const char *xmlDesc,
Index: libvirt-0.7.7/src/util/macvtap.c
===================================================================
--- libvirt-0.7.7.orig/src/util/macvtap.c
+++ libvirt-0.7.7/src/util/macvtap.c
@@ -215,10 +215,12 @@ getIfIndex(virConnectPtr conn,
     if (ioctl(fd, SIOCGIFINDEX, &ifreq) >= 0)
         *idx = ifreq.ifr_ifindex;
     else {
+#if 0
         if (conn)
             ReportError(conn, VIR_ERR_INTERNAL_ERROR,
                         _("interface %s does not exist"),
                         ifname);
+#endif
         rc = ENODEV;
     }
 
@@ -747,11 +749,13 @@ create_name:
 
     rc = ifUp(cr_ifname, 1);
     if (rc != 0) {
+#if 0
         virReportSystemError(errno,
                              _("cannot 'up' interface %s -- another "
                              "macvtap device may be 'up' and have the same "
                              "MAC address"),
                              cr_ifname);
+#endif
         rc = -1;
         goto link_del_exit;
     }
Index: libvirt-0.7.7/src/qemu/qemu_conf.c
===================================================================
--- libvirt-0.7.7.orig/src/qemu/qemu_conf.c
+++ libvirt-0.7.7/src/qemu/qemu_conf.c
@@ -3854,18 +3854,21 @@ int qemudBuildCommandLine(virConnectPtr 
                                                   net->data.direct.linkdev,
                                                   net->data.direct.mode,
                                                   qemuCmdFlags);
-                if (tapfd < 0)
-                    goto error;
-
-                if (VIR_REALLOC_N(*tapfds, (*ntapfds)+1) < 0) {
-                    close(tapfd);
-                    goto no_memory;
-                }
+                if (tapfd < 0) {
+                    VIR_WARN(_("Unable to connect interface %s for domain %s"),
+                             net->data.direct.linkdev, def->name);
+                    tapfd_name[0] = 0;
+                } else {
+                    if (VIR_REALLOC_N(*tapfds, (*ntapfds)+1) < 0) {
+                        close(tapfd);
+                        goto no_memory;
+                    }
 
-                (*tapfds)[(*ntapfds)++] = tapfd;
+                    (*tapfds)[(*ntapfds)++] = tapfd;
 
-                if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name))
-                    goto no_memory;
+                    if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name))
+                        goto no_memory;
+                }
             }
 
             /* Possible combinations:
@@ -3876,8 +3879,9 @@ int qemudBuildCommandLine(virConnectPtr 
              *
              * NB, no support for -netdev without use of -device
              */
-            if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
-                (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+            if ((tapfd_name[0] != 0) &&
+                ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+                 (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))) {
                 ADD_ARG_LIT("-netdev");
                 if (!(host = qemuBuildHostNetStr(net, ',',
                                                  vlan, tapfd_name)))
@@ -3895,8 +3899,9 @@ int qemudBuildCommandLine(virConnectPtr 
                     goto error;
                 ADD_ARG(nic);
             }
-            if (!((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
-                  (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))) {
+            if ((tapfd_name[0] != 0) &&
+                (!((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+                   (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)))) {
                 ADD_ARG_LIT("-net");
                 if (!(host = qemuBuildHostNetStr(net, ',',
                                                  vlan, tapfd_name)))





More information about the libvir-list mailing list