c8c376
From 6ad2b5b70219158aa873058b4d4b1d94c481640d Mon Sep 17 00:00:00 2001
c8c376
Message-Id: <6ad2b5b70219158aa873058b4d4b1d94c481640d@dist-git>
c8c376
From: Michal Privoznik <mprivozn@redhat.com>
c8c376
Date: Tue, 8 Nov 2016 13:42:13 +0100
c8c376
Subject: [PATCH] qemu_hotplug: Support interface type of vhost-user hotplug
c8c376
c8c376
RHEL-7.3: https://bugzilla.redhat.com/show_bug.cgi?id=1366108
c8c376
RHEL-7.3.z: https://bugzilla.redhat.com/show_bug.cgi?id=1392032
c8c376
c8c376
There are couple of things that needs to be done in order to
c8c376
allow vhost-user hotplug. Firstly, vhost-user requires a chardev
c8c376
which is connected to vhost-user bridge and through which qemu
c8c376
communicates with the bridge (no acutal guest traffic is sent
c8c376
through there, just some metadata). In order to generate proper
c8c376
chardev alias, we must assign device alias way sooner.
c8c376
c8c376
Then, because we are plugging the chardev first, we need to do
c8c376
the proper undo if something fails - that is remove netdev too.
c8c376
We don't want anything to be left over in case attach fails at
c8c376
some point.
c8c376
c8c376
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
c8c376
(cherry picked from commit ff89d5cbcf266e780b4ff93960969b3a6c2e9a01)
c8c376
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
c8c376
---
c8c376
 src/qemu/qemu_hotplug.c | 71 +++++++++++++++++++++++++++++++++++++++++--------
c8c376
 1 file changed, 60 insertions(+), 11 deletions(-)
c8c376
c8c376
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
c8c376
index 011152762..8acbf370b 100644
c8c376
--- a/src/qemu/qemu_hotplug.c
c8c376
+++ b/src/qemu/qemu_hotplug.c
c8c376
@@ -902,6 +902,10 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
     virNetDevBandwidthPtr actualBandwidth;
c8c376
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
c8c376
     size_t i;
c8c376
+    char *charDevAlias = NULL;
c8c376
+    bool charDevPlugged = false;
c8c376
+    bool netdevPlugged = false;
c8c376
+    bool hostPlugged = false;
c8c376
 
c8c376
     /* preallocate new slot for device */
c8c376
     if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets + 1) < 0)
c8c376
@@ -940,6 +944,9 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
         return -1;
c8c376
     }
c8c376
 
c8c376
+    if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0)
c8c376
+        goto cleanup;
c8c376
+
c8c376
     switch (actualType) {
c8c376
     case VIR_DOMAIN_NET_TYPE_BRIDGE:
c8c376
     case VIR_DOMAIN_NET_TYPE_NETWORK:
c8c376
@@ -1014,8 +1021,18 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
         goto cleanup;
c8c376
         break;
c8c376
 
c8c376
-    case VIR_DOMAIN_NET_TYPE_USER:
c8c376
     case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
c8c376
+        if (!qemuDomainSupportsNetdev(vm->def, priv->qemuCaps, net)) {
c8c376
+            virReportError(VIR_ERR_INTERNAL_ERROR,
c8c376
+                           "%s", _("Netdev support unavailable"));
c8c376
+            goto cleanup;
c8c376
+        }
c8c376
+
c8c376
+        if (virAsprintf(&charDevAlias, "char%s", net->info.alias) < 0)
c8c376
+            goto cleanup;
c8c376
+        break;
c8c376
+
c8c376
+    case VIR_DOMAIN_NET_TYPE_USER:
c8c376
     case VIR_DOMAIN_NET_TYPE_SERVER:
c8c376
     case VIR_DOMAIN_NET_TYPE_CLIENT:
c8c376
     case VIR_DOMAIN_NET_TYPE_MCAST:
c8c376
@@ -1051,9 +1068,6 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
             goto cleanup;
c8c376
     }
c8c376
 
c8c376
-    if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0)
c8c376
-        goto cleanup;
c8c376
-
c8c376
     if (qemuDomainMachineIsS390CCW(vm->def) &&
c8c376
         virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VIRTIO_CCW)) {
c8c376
         net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW;
c8c376
@@ -1111,23 +1125,36 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
     }
c8c376
 
c8c376
     qemuDomainObjEnterMonitor(driver, vm);
c8c376
+
c8c376
+    if (actualType == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
c8c376
+        if (qemuMonitorAttachCharDev(priv->mon, charDevAlias, net->data.vhostuser) < 0) {
c8c376
+            ignore_value(qemuDomainObjExitMonitor(driver, vm));
c8c376
+            virDomainAuditNet(vm, NULL, net, "attach", false);
c8c376
+            goto cleanup;
c8c376
+        }
c8c376
+        charDevPlugged = true;
c8c376
+    }
c8c376
+
c8c376
     if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV)) {
c8c376
         if (qemuMonitorAddNetdev(priv->mon, netstr,
c8c376
                                  tapfd, tapfdName, tapfdSize,
c8c376
                                  vhostfd, vhostfdName, vhostfdSize) < 0) {
c8c376
             ignore_value(qemuDomainObjExitMonitor(driver, vm));
c8c376
             virDomainAuditNet(vm, NULL, net, "attach", false);
c8c376
-            goto cleanup;
c8c376
+            goto try_remove;
c8c376
         }
c8c376
+        netdevPlugged = true;
c8c376
     } else {
c8c376
         if (qemuMonitorAddHostNetwork(priv->mon, netstr,
c8c376
                                       tapfd, tapfdName, tapfdSize,
c8c376
                                       vhostfd, vhostfdName, vhostfdSize) < 0) {
c8c376
             ignore_value(qemuDomainObjExitMonitor(driver, vm));
c8c376
             virDomainAuditNet(vm, NULL, net, "attach", false);
c8c376
-            goto cleanup;
c8c376
+            goto try_remove;
c8c376
         }
c8c376
+        hostPlugged = true;
c8c376
     }
c8c376
+
c8c376
     if (qemuDomainObjExitMonitor(driver, vm) < 0)
c8c376
         goto cleanup;
c8c376
 
c8c376
@@ -1230,6 +1257,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
     }
c8c376
     VIR_FREE(vhostfd);
c8c376
     VIR_FREE(vhostfdName);
c8c376
+    VIR_FREE(charDevAlias);
c8c376
     virObjectUnref(cfg);
c8c376
 
c8c376
     return ret;
c8c376
@@ -1244,7 +1272,11 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
             if (virAsprintf(&netdev_name, "host%s", net->info.alias) < 0)
c8c376
                 goto cleanup;
c8c376
             qemuDomainObjEnterMonitor(driver, vm);
c8c376
-            if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0)
c8c376
+            if (charDevPlugged &&
c8c376
+                qemuMonitorDetachCharDev(priv->mon, charDevAlias) < 0)
c8c376
+                VIR_WARN("Failed to remove associated chardev %s", charDevAlias);
c8c376
+            if (netdevPlugged &&
c8c376
+                qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0)
c8c376
                 VIR_WARN("Failed to remove network backend for netdev %s",
c8c376
                          netdev_name);
c8c376
             ignore_value(qemuDomainObjExitMonitor(driver, vm));
c8c376
@@ -1257,7 +1289,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
c8c376
         if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0)
c8c376
             goto cleanup;
c8c376
         qemuDomainObjEnterMonitor(driver, vm);
c8c376
-        if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0)
c8c376
+        if (hostPlugged &&
c8c376
+            qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0)
c8c376
             VIR_WARN("Failed to remove network backend for vlan %d, net %s",
c8c376
                      vlan, hostnet_name);
c8c376
         ignore_value(qemuDomainObjExitMonitor(driver, vm));
c8c376
@@ -3357,10 +3390,12 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
c8c376
     virNetDevVPortProfilePtr vport;
c8c376
     virObjectEventPtr event;
c8c376
     char *hostnet_name = NULL;
c8c376
+    char *charDevAlias = NULL;
c8c376
     size_t i;
c8c376
     int ret = -1;
c8c376
+    int actualType = virDomainNetGetActualType(net);
c8c376
 
c8c376
-    if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
c8c376
+    if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
c8c376
         /* this function handles all hostdev and netdev cleanup */
c8c376
         ret = qemuDomainRemoveHostDevice(driver, vm,
c8c376
                                          virDomainNetGetActualHostdev(net));
c8c376
@@ -3370,9 +3405,11 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
c8c376
     VIR_DEBUG("Removing network interface %s from domain %p %s",
c8c376
               net->info.alias, vm, vm->def->name);
c8c376
 
c8c376
-    if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0)
c8c376
+    if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0 ||
c8c376
+        virAsprintf(&charDevAlias, "char%s", net->info.alias) < 0)
c8c376
         goto cleanup;
c8c376
 
c8c376
+
c8c376
     qemuDomainObjEnterMonitor(driver, vm);
c8c376
     if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV)) {
c8c376
         if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
c8c376
@@ -3395,6 +3432,17 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
c8c376
             goto cleanup;
c8c376
         }
c8c376
     }
c8c376
+
c8c376
+    if (actualType == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
c8c376
+        /* vhostuser has a chardev too */
c8c376
+        if (qemuMonitorDetachCharDev(priv->mon, charDevAlias) < 0) {
c8c376
+            /* well, this is a messy situation. Guest visible PCI device has
c8c376
+             * been removed, netdev too but chardev not. The best seems to be
c8c376
+             * to just ignore the error and carry on.
c8c376
+             */
c8c376
+        }
c8c376
+    }
c8c376
+
c8c376
     if (qemuDomainObjExitMonitor(driver, vm) < 0)
c8c376
         goto cleanup;
c8c376
 
c8c376
@@ -3419,7 +3467,7 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
c8c376
                                                   &net->mac));
c8c376
     }
c8c376
 
c8c376
-    if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) {
c8c376
+    if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
c8c376
         ignore_value(virNetDevMacVLanDeleteWithVPortProfile(
c8c376
                          net->ifname, &net->mac,
c8c376
                          virDomainNetGetActualDirectDev(net),
c8c376
@@ -3445,6 +3493,7 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
c8c376
 
c8c376
  cleanup:
c8c376
     virObjectUnref(cfg);
c8c376
+    VIR_FREE(charDevAlias);
c8c376
     VIR_FREE(hostnet_name);
c8c376
     return ret;
c8c376
 }
c8c376
-- 
c8c376
2.11.0
c8c376