[libvirt] [PATCH 1/2] util: keep/use a bitmap of in-use macvtap devices

Pavel Hrdina phrdina at redhat.com
Fri Jan 22 12:47:27 UTC 2016


On Thu, Jan 21, 2016 at 02:54:10PM -0500, Laine Stump wrote:

[...]

>  src/libvirt_private.syms    |   2 +
>  src/qemu/qemu_process.c     |  10 +-
>  src/util/virnetdevmacvlan.c | 438 +++++++++++++++++++++++++++++++++++++-------
>  src/util/virnetdevmacvlan.h |   5 +-
>  4 files changed, 390 insertions(+), 65 deletions(-)
> 

[...]

> diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
> index 496416e..20a821a 100644
> --- a/src/util/virnetdevmacvlan.c
> +++ b/src/util/virnetdevmacvlan.c

[...]

> + * virNetDevMacVLanReserveID:
> + *
> + *  @id: id 0 - MACVLAN_MAX_ID+1 to reserve (or -1 for "first free")
> + *  @flags: set VIR_NETDEV_MACVLAN_CREATE_WITH_TAP for macvtapN else macvlanN
> + *  @quietfail: don't log an error if this name is already in-use
> + *
> + *  Reserve the indicated ID in the appropriate bitmap, or find the
> + *  first free ID if @id is -1.
> + *
> + *  returns id or -1 to indicate failure
> + */
> +static int
> +virNetDevMacVLanReserveID(int id, unsigned int flags, bool quietfail)
> +{
> +    virBitmapPtr bitmap;
> +
> +    if (virNetDevMacVLanInitialize() < 0)
> +       return -1;
> +
> +    bitmap = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +        macvtapIDs :  macvlanIDs;
> +
> +    if (id > MACVLAN_MAX_ID) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("can't use name %s%d - out of range 0-%d"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX,
> +                       id, MACVLAN_MAX_ID);
> +        return -1;
> +    }
> +
> +    if (id < 0 &&
> +        (id = virBitmapNextClearBit(bitmap, 0)) < 0) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("no unused %s names available"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX);
> +        return -1;
> +    }
> +
> +    if (virBitmapIsBitSet(bitmap, id)) {
> +        if (quietfail) {
> +            VIR_INFO("couldn't reserve name %s%d - already in use",
> +                     (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                     MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +        } else {
> +            virReportError(VIR_ERR_INTERNAL_ERROR,
> +                           _("couldn't reserve name %s%d - already in use"),
> +                           (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                           MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +        }
> +        return -1;
> +    }
> +
> +    if (virBitmapSetBit(bitmap, id) < 0) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("couldn't mark %s%d as used"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +        return -1;
> +    }
> +    VIR_INFO("reserving device %s%d",
> +             (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +             MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +    return id;
> +}
> +
> +
> +/**
> + * virNetDevMacVLanReserveNextFreeID:
> + *
> + *  @id: id to start scanning at - return first free ID *after* this
> + *       id (use -1 to start looking at the beginning)
> + *  @flags: set VIR_NETDEV_MACVLAN_CREATE_WITH_TAP for macvtapN else macvlanN
> + *
> + *  Reserve the indicated ID in the appropriate bitmap, or find the
> + *  first free ID if @id is -1.
> + *

This comment isn't true.  The function takes the id, pass it to the
virBitmanNextClearBit and get a next free ID even if original id >= 0.  It
always tries to find next free id starting from the position specified by id.

The second issue I have with those function is that they are almost identical
and I think they can be easily merged together extending the
virNetDevMacVLanReserveID() like this:

static int
virNetDevMacVLanReserverID(int id,
                           unsigned int flags,
                           bool quietfail,
                           bool nextFree)
{

[...]

    if ((id < 0 || nextFree ) &&
        (id = virBitmapNextClearBit(bitmap, 0)) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("no unused %s names available"),
                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX);
        return -1;
    }

[...]

}

> + *  returns id or -1 to indicate failure
> + */
> +static int
> +virNetDevMacVLanReserveNextFreeID(int id, unsigned int flags)
> +{
> +    virBitmapPtr bitmap;
> +
> +    if (virNetDevMacVLanInitialize() < 0)
> +       return -1;
> +
> +    bitmap = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +        macvtapIDs :  macvlanIDs;
> +
> +    if (id > MACVLAN_MAX_ID) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("can't use name %s%d - out of range 0-%d"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX,
> +                       id, MACVLAN_MAX_ID);
> +        return -1;
> +    }
> +
> +    if ((id = virBitmapNextClearBit(bitmap, id)) < 0) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("no unused %s names available"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX);
> +        return -1;
> +    }
> +
> +    if (virBitmapIsBitSet(bitmap, id)) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("couldn't reserve name %s%d - already in use"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +        return -1;
> +    }
> +
> +    if (virBitmapSetBit(bitmap, id) < 0) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       _("couldn't mark %s%d as used"),
> +                       (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +                       MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +        return -1;
> +    }
> +    VIR_INFO("reserving device %s%d",
> +             (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> +             MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX, id);
> +    return id;
> +}
> +

[...]

> @@ -724,14 +1002,20 @@ virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname,
>   * virNetDevMacVLanCreateWithVPortProfile:
>   * Create an instance of a macvtap device and open its tap character
>   * device.
> - * @tgifname: Interface name that the macvtap is supposed to have. May
> - *    be NULL if this function is supposed to choose a name
> +
> + * @ifnameRequested: Interface name that the caller wants the macvtap
> + *    device to have, or NULL to pick the first available name
> + *    appropriate for the type (macvlan%d or macvtap%d). If the
> + *    suggested name fits one of those patterns, but is already in
> + *    use, we will fallback to finding the first available. If the
> + *    suggested name *doesn't* fit a pattern and the name is in use,
> + *    we will fail.
>   * @macaddress: The MAC address for the macvtap device
>   * @linkdev: The interface name of the NIC to connect to the external bridge
> - * @mode: int describing the mode for 'bridge', 'vepa', 'private' or 'passthru'.
> + * @mode: macvtap mode (VIR_NETDEV_MACVLAN_MODE_(BRIDGE|VEPA|PRIVATE|PASSTHRU)
>   * @vmuuid: The UUID of the VM the macvtap belongs to
>   * @virtPortProfile: pointer to object holding the virtual port profile data
> - * @res_ifname: Pointer to a string pointer where the actual name of the
> + * @ifnameResult: Pointer to a string pointer where the actual name of the
>   *     interface will be stored into if everything succeeded. It is up
>   *     to the caller to free the string.
>   * @tapfd: array of file descriptor return value for the new tap device
> @@ -744,39 +1028,36 @@ virNetDevMacVLanVPortProfileRegisterCallback(const char *ifname,
>   *
>   * Return 0 on success, -1 on error.
>   */
> -int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname,
> -                                           const virMacAddr *macaddress,
> -                                           const char *linkdev,
> -                                           virNetDevMacVLanMode mode,
> -                                           const unsigned char *vmuuid,
> -                                           virNetDevVPortProfilePtr virtPortProfile,
> -                                           char **res_ifname,
> -                                           virNetDevVPortProfileOp vmOp,
> -                                           char *stateDir,
> -                                           int *tapfd,
> -                                           size_t tapfdSize,
> -                                           unsigned int flags)
> +int
> +virNetDevMacVLanCreateWithVPortProfile(const char *ifnameRequested,
> +                                       const virMacAddr *macaddress,
> +                                       const char *linkdev,
> +                                       virNetDevMacVLanMode mode,
> +                                       const unsigned char *vmuuid,
> +                                       virNetDevVPortProfilePtr virtPortProfile,
> +                                       char **ifnameResult,
> +                                       virNetDevVPortProfileOp vmOp,
> +                                       char *stateDir,
> +                                       int *tapfd,
> +                                       size_t tapfdSize,
> +                                       unsigned int flags)
>  {
>      const char *type = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
>          "macvtap" : "macvlan";
> -    const char *prefix = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
> -        MACVTAP_NAME_PREFIX : MACVLAN_NAME_PREFIX;
>      const char *pattern = (flags & VIR_NETDEV_MACVLAN_CREATE_WITH_TAP) ?
>          MACVTAP_NAME_PATTERN : MACVLAN_NAME_PATTERN;
> -    int c, rc;
> +    int rc, reservedID = -1;
>      char ifname[IFNAMSIZ];
>      int retries, do_retry = 0;
>      uint32_t macvtapMode;
> -    const char *cr_ifname = NULL;
> +    const char *ifnameCreated = NULL;
>      int ret;
>      int vf = -1;
>      bool vnet_hdr = flags & VIR_NETDEV_MACVLAN_VNET_HDR;
>  
>      macvtapMode = modeMap[mode];
>  
> -    *res_ifname = NULL;
> -
> -    VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virNetDevVPortProfileOpTypeToString(vmOp));
> +    *ifnameResult = NULL;
>  
>      /** Note: When using PASSTHROUGH mode with MACVTAP devices the link
>       * device's MAC address must be set to the VMs MAC address. In
> @@ -803,52 +1084,81 @@ int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname,
>          }
>      }
>  
> -    if (tgifname) {
> -        if ((ret = virNetDevExists(tgifname)) < 0)
> -            return -1;
> +    if (ifnameRequested) {
> +        bool isAutoName
> +            = (STRPREFIX(ifnameRequested, MACVTAP_NAME_PREFIX) ||
> +               STRPREFIX(ifnameRequested, MACVLAN_NAME_PREFIX));
> +
> +        VIR_INFO("Requested macvtap device name: %s", ifnameRequested);
> +        virMutexLock(&virNetDevMacVLanCreateMutex);
>  
> +        if ((ret = virNetDevExists(ifnameRequested)) < 0) {
> +            virMutexUnlock(&virNetDevMacVLanCreateMutex);
> +            return -1;
> +        }
>          if (ret) {
> -            if (STRPREFIX(tgifname, prefix))
> +            if (isAutoName)
>                  goto create_name;
>              virReportSystemError(EEXIST,
> -                                 _("Unable to create macvlan device %s"), tgifname);
> +                                 _("Unable to create macvlan device %s"), ifnameRequested);
> +            virMutexUnlock(&virNetDevMacVLanCreateMutex);

Maybe use %s instead of macvlan as you've done in the new functions.

>              return -1;
>          }
> -        cr_ifname = tgifname;
> -        rc = virNetDevMacVLanCreate(tgifname, type, macaddress, linkdev,
> -                                    macvtapMode, &do_retry);
> -        if (rc < 0)
> +        if (isAutoName &&
> +            (reservedID = virNetDevMacVLanReserveName(ifnameRequested, true)) < 0) {
> +            reservedID = -1;
> +            goto create_name;
> +        }
> +
> +        if (virNetDevMacVLanCreate(ifnameRequested, type, macaddress,
> +                                   linkdev, macvtapMode, &do_retry) < 0) {
> +            if (isAutoName) {
> +                virNetDevMacVLanReleaseName(ifnameRequested);
> +                reservedID = -1;
> +                goto create_name;
> +            }
> +            virMutexUnlock(&virNetDevMacVLanCreateMutex);
>              return -1;
> -    } else {
> +        }
> +        /* virNetDevMacVLanCreate() was successful - use this name */
> +        ifnameCreated = ifnameRequested;
>   create_name:
> -        retries = 5;
> +        virMutexUnlock(&virNetDevMacVLanCreateMutex);
> +    }
> +
> +    retries = 8192;
> +    while (!ifnameCreated && retries) {
>          virMutexLock(&virNetDevMacVLanCreateMutex);
> -        for (c = 0; c < 8192; c++) {
> -            snprintf(ifname, sizeof(ifname), pattern, c);
> -            if ((ret = virNetDevExists(ifname)) < 0) {
> -                virMutexUnlock(&virNetDevMacVLanCreateMutex);
> +        if ((reservedID = virNetDevMacVLanReserveNextFreeID(reservedID, flags)) < 0) {
> +            virMutexUnlock(&virNetDevMacVLanCreateMutex);
> +            virReportSystemError(EEXIST, "%s",
> +                                 _("All macvlan device names are already being used"));
> +            return -1;

Isn't this error message redundant?  There will already be an error reported
by virNetDevMacVLanReserveNextFreeID().




More information about the libvir-list mailing list