5c27b6
From 61e603b025083ece8ebf12dc3664c0a5f613f400 Mon Sep 17 00:00:00 2001
5c27b6
Message-Id: <61e603b025083ece8ebf12dc3664c0a5f613f400@dist-git>
5c27b6
From: Laine Stump <laine@laine.org>
5c27b6
Date: Thu, 13 Apr 2017 14:29:37 -0400
5c27b6
Subject: [PATCH] util: try *really* hard to set the MAC address of an SRIOV VF
5c27b6
5c27b6
If an SRIOV VF has previously been used for VFIO device assignment,
5c27b6
the "admin MAC" that is stored in the PF driver's table of VF info
5c27b6
will have been set to the MAC address that the virtual machine wanted
5c27b6
the device to have. Setting the admin MAC for a VF also sets a flag in
5c27b6
the PF that is loosely called the "administratively set" flag. Once
5c27b6
that flag is set, it is no longer possible for the net driver of the
5c27b6
VF (either on the host or in a virtual machine) to directly set the
5c27b6
VF's MAC again; this flag isn't reset until the *PF* driver is
5c27b6
restarted, and that requires taking *all* VFs offline, so it's not
5c27b6
really feasible to do.
5c27b6
5c27b6
If the same SRIOV VF is later used for macvtap passthrough mode, the
5c27b6
VF's MAC address must be set, but normally we don't unbind the VF from
5c27b6
its host net driver (since we actually need the host net driver in
5c27b6
this case). Since setting the VF MAC directly will fail, in the past
5c27b6
"we" ("I") had tried to fix the problem by simply setting the admin MAC
5c27b6
(via the PF) instead. This *appeared* to work (and might have at one
5c27b6
time, due to promiscuous mode being turned on somewhere or something),
5c27b6
but it currently creates a non-working interface because only the
5c27b6
value for admin MAC is set to the desired value, *not* the actual MAC
5c27b6
that the VF is using.
5c27b6
5c27b6
Earlier patches in this series reverted that behavior, so that we once
5c27b6
again set the MAC of the VF itself for macvtap passthrough operation,
5c27b6
not the admin MAC. But that brings back the original bug - if the
5c27b6
interface has been used for VFIO device assignment, you can no longer
5c27b6
use it for macvtap passthrough.
5c27b6
5c27b6
This patch solves that problem by noticing when virNetDevSetMAC()
5c27b6
fails for a VF, and in that case it sets the desired MAC to the admin
5c27b6
MAC via the PF, then "bounces" the VF driver (by unbinding and the
5c27b6
immediately rebinding it to the VF). This causes the VF's MAC to be
5c27b6
reinitialized from the admin MAC, and everybody is happy (until the
5c27b6
*next* time someone wants to set the VF's MAC address, since the
5c27b6
"administratively set" bit is still turned on).
5c27b6
5c27b6
Resolves: https://bugzilla.redhat.com/1442040 (RHEL 7.3.z)
5c27b6
Resolves: https://bugzilla.redhat.com/1415609 (RHEL 7.4)
5c27b6
5c27b6
(cherry picked from commit 86556e167a634599ac9975bd5dad98e3ef9f7017)
5c27b6
---
5c27b6
 src/util/virnetdev.c | 102 +++++++++++++++++++++++++++++++++++++++++----------
5c27b6
 1 file changed, 83 insertions(+), 19 deletions(-)
5c27b6
5c27b6
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
5c27b6
index 0223877ff..a510ebc2e 100644
5c27b6
--- a/src/util/virnetdev.c
5c27b6
+++ b/src/util/virnetdev.c
5c27b6
@@ -1054,6 +1054,32 @@ virNetDevSysfsDeviceFile(char **pf_sysfs_device_link, const char *ifname,
5c27b6
     return 0;
5c27b6
 }
5c27b6
 
5c27b6
+
5c27b6
+static virPCIDevicePtr
5c27b6
+virNetDevGetPCIDevice(const char *devName)
5c27b6
+{
5c27b6
+    char *vfSysfsDevicePath = NULL;
5c27b6
+    virPCIDeviceAddressPtr vfPCIAddr = NULL;
5c27b6
+    virPCIDevicePtr vfPCIDevice = NULL;
5c27b6
+
5c27b6
+    if (virNetDevSysfsFile(&vfSysfsDevicePath, devName, "device") < 0)
5c27b6
+        goto cleanup;
5c27b6
+
5c27b6
+    vfPCIAddr = virPCIGetDeviceAddressFromSysfsLink(vfSysfsDevicePath);
5c27b6
+    if (!vfPCIAddr)
5c27b6
+        goto cleanup;
5c27b6
+
5c27b6
+    vfPCIDevice = virPCIDeviceNew(vfPCIAddr->domain, vfPCIAddr->bus,
5c27b6
+                                  vfPCIAddr->slot, vfPCIAddr->function);
5c27b6
+
5c27b6
+ cleanup:
5c27b6
+    VIR_FREE(vfSysfsDevicePath);
5c27b6
+    VIR_FREE(vfPCIAddr);
5c27b6
+
5c27b6
+    return vfPCIDevice;
5c27b6
+}
5c27b6
+
5c27b6
+
5c27b6
 /**
5c27b6
  * virNetDevGetVirtualFunctions:
5c27b6
  *
5c27b6
@@ -2001,6 +2027,7 @@ virNetDevSetNetConfig(const char *linkdev, int vf,
5c27b6
     char *pfDevOrig = NULL;
5c27b6
     char *vfDevOrig = NULL;
5c27b6
     int vlanTag = -1;
5c27b6
+    virPCIDevicePtr vfPCIDevice = NULL;
5c27b6
 
5c27b6
     if (vf >= 0) {
5c27b6
         /* linkdev is the PF */
5c27b6
@@ -2096,6 +2123,8 @@ virNetDevSetNetConfig(const char *linkdev, int vf,
5c27b6
     }
5c27b6
 
5c27b6
     if (MAC) {
5c27b6
+        int setMACrc;
5c27b6
+
5c27b6
         if (!linkdev) {
5c27b6
             virReportError(VIR_ERR_INTERNAL_ERROR,
5c27b6
                            _("VF %d of PF '%s' is not bound to a net driver, "
5c27b6
@@ -2104,27 +2133,61 @@ virNetDevSetNetConfig(const char *linkdev, int vf,
5c27b6
             goto cleanup;
5c27b6
         }
5c27b6
 
5c27b6
-        if (virNetDevSetMAC(linkdev, MAC) < 0) {
5c27b6
-            /* This may have failed due to the "administratively
5c27b6
-             * set" flag being set in the PF for this VF. For now
5c27b6
-             * we will just fail, but in the future we should
5c27b6
-             * attempt to set the VF MAC via the PF.
5c27b6
+        setMACrc = virNetDevSetMACInternal(linkdev, MAC, !!pfDevOrig);
5c27b6
+        if (setMACrc < 0) {
5c27b6
+            bool allowRetry = false;
5c27b6
+            int retries = 100;
5c27b6
+
5c27b6
+            /* if pfDevOrig == NULL, this isn't a VF, so we've failed */
5c27b6
+            if (!pfDevOrig || errno != EADDRNOTAVAIL)
5c27b6
+                goto cleanup;
5c27b6
+
5c27b6
+            /* Otherwise this is a VF, and virNetDevSetMAC failed with
5c27b6
+             * EADDRNOTAVAIL, which could be due to the
5c27b6
+             * "administratively set" flag being set in the PF for
5c27b6
+             * this VF.  When this happens, we can attempt to use an
5c27b6
+             * alternate method to set the VF MAC: first set it into
5c27b6
+             * the admin MAC for this VF in the PF, then unbind/rebind
5c27b6
+             * the VF from its net driver. This causes the VF's MAC to
5c27b6
+             * be initialized to whatever was stored in the admin MAC.
5c27b6
              */
5c27b6
-            goto cleanup;
5c27b6
+
5c27b6
+            if (virNetDevSetVfConfig(pfDevName, vf,
5c27b6
+                                     MAC, vlanTag, &allowRetry) < 0) {
5c27b6
+                goto cleanup;
5c27b6
+            }
5c27b6
+
5c27b6
+            /* admin MAC is set, now we need to construct a virPCIDevice
5c27b6
+             * object so we can call virPCIDeviceRebind()
5c27b6
+             */
5c27b6
+            if (!(vfPCIDevice = virNetDevGetPCIDevice(linkdev)))
5c27b6
+                goto cleanup;
5c27b6
+
5c27b6
+            /* Rebind the device. This should set the proper MAC address */
5c27b6
+            if (virPCIDeviceRebind(vfPCIDevice) < 0)
5c27b6
+                goto cleanup;
5c27b6
+
5c27b6
+            /* Wait until virNetDevGetIndex for the VF netdev returns success.
5c27b6
+             * This indicates that the device is ready to be used. If we don't
5c27b6
+             * wait, then upcoming operations on the VF may fail.
5c27b6
+             */
5c27b6
+            while (retries-- > 0 && !virNetDevExists(linkdev))
5c27b6
+               usleep(1000);
5c27b6
         }
5c27b6
-        if (pfDevOrig) {
5c27b6
-            /* if pfDevOrig is set, it means that the caller was
5c27b6
-             * *really* only interested in setting the MAC of the VF
5c27b6
-             * itself, *not* the admin MAC via the PF. In those cases,
5c27b6
-             * the adminMAC was only provided in case we need to set
5c27b6
-             * the VF's MAC by temporarily unbinding/rebinding the
5c27b6
-             * VF's net driver with the admin MAC set to the desired
5c27b6
-             * MAC, and then want to restore the admin MAC to its
5c27b6
-             * original setting when we're finished. We would only
5c27b6
-             * need to do that if the virNetDevSetMAC() above had
5c27b6
-             * failed; since it didn't, we don't need to set the
5c27b6
-             * adminMAC, so we are NULLing it out here to avoid that
5c27b6
-             * below.
5c27b6
+
5c27b6
+        if (pfDevOrig && setMACrc == 0) {
5c27b6
+            /* if pfDevOrig is set, it that the caller was *really*
5c27b6
+             * only interested in setting the MAC of the VF itself,
5c27b6
+             * *not* the admin MAC via the PF. In those cases, the
5c27b6
+             * adminMAC was only provided in case we need to set the
5c27b6
+             * VF's MAC by temporarily unbinding/rebinding the VF's
5c27b6
+             * net driver with the admin MAC set to the desired MAC,
5c27b6
+             * and then want to restore the admin MAC to its original
5c27b6
+             * setting when we're finished. We would only need to do
5c27b6
+             * that if the virNetDevSetMAC() above had failed; since
5c27b6
+             * setMACrc == 0, we know it didn't fail and we don't need
5c27b6
+             * to set the adminMAC, so we are NULLing it out here to
5c27b6
+             * avoid that below.
5c27b6
 
5c27b6
              * (NB: since setting the admin MAC sets the
5c27b6
              * "administratively set" flag for the VF in the PF's
5c27b6
@@ -2168,6 +2231,7 @@ virNetDevSetNetConfig(const char *linkdev, int vf,
5c27b6
  cleanup:
5c27b6
     VIR_FREE(pfDevOrig);
5c27b6
     VIR_FREE(vfDevOrig);
5c27b6
+    virPCIDeviceFree(vfPCIDevice);
5c27b6
     return ret;
5c27b6
 }
5c27b6
 
5c27b6
-- 
5c27b6
2.12.2
5c27b6