[libvirt] [PATCH 19/19] util: try *really* hard to set the MAC address of an SRIOV VF

Michal Privoznik mprivozn at redhat.com
Fri Mar 17 13:32:51 UTC 2017


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

No. this needs to be virPCIDeviceFree().

ACK with that fixed.

Michal




More information about the libvir-list mailing list