[PATCH 1/5] netdev: Introduce several helper functions for generating unique netdev name

Shi Lei shi_lei at massclouds.com
Tue Dec 8 06:46:26 UTC 2020


On 2020-12-08 at 07:37, Laine Stump wrote:
>First - thanks for taking this on, and doing it the right way! (i.e.
>refactoring my two changes down into a common set of functions to be
>reused, rather than duplicating the code). I can't really say why I
>didn't go the extra bit when I made the changes to virnetdevtap.c and
>virnetdevmacvlan.c - I really should have cleaned it up as you've done
>in patches 1 & 2 here.
>
>On 12/4/20 2:01 AM, Shi Lei wrote:
>> Extract ReserveName/GenerateName from netdevtap and netdevmacvlan as
>> common helper functions. Along with them, export Lock/Unlock functions
>> to protect these processes.
>>
>> Signed-off-by: Shi Lei <shi_lei at massclouds.com>
>> ---
>>   src/libvirt_private.syms |   4 ++
>>   src/util/virnetdev.c     | 140 +++++++++++++++++++++++++++++++++++++++
>>   src/util/virnetdev.h     |  33 +++++++++
>>   3 files changed, 177 insertions(+)
>>
>> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
>> index 2f640ef1..be3148c9 100644
>> --- a/src/libvirt_private.syms
>> +++ b/src/libvirt_private.syms
>> @@ -2551,6 +2551,7 @@ virNetDevDelMulti;
>>   virNetDevExists;
>>   virNetDevFeatureTypeFromString;
>>   virNetDevFeatureTypeToString;
>> +virNetDevGenerateName;
>>   virNetDevGetFeatures;
>>   virNetDevGetIndex;
>>   virNetDevGetLinkInfo;
>> @@ -2572,8 +2573,10 @@ virNetDevGetVLanID;
>>   virNetDevIfStateTypeFromString;
>>   virNetDevIfStateTypeToString;
>>   virNetDevIsVirtualFunction;
>> +virNetDevLockGenName;
>>   virNetDevPFGetVF;
>>   virNetDevReadNetConfig;
>> +virNetDevReserveName;
>>   virNetDevRunEthernetScript;
>>   virNetDevRxFilterFree;
>>   virNetDevRxFilterModeTypeFromString;
>> @@ -2594,6 +2597,7 @@ virNetDevSetRcvMulti;
>>   virNetDevSetRootQDisc;
>>   virNetDevSetupControl;
>>   virNetDevSysfsFile;
>> +virNetDevUnlockGenName;
>>   virNetDevValidateConfig;
>>   virNetDevVFInterfaceStats;
>>  
>> diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
>> index 5104bbe7..5ff8e35f 100644
>> --- a/src/util/virnetdev.c
>> +++ b/src/util/virnetdev.c
>> @@ -17,6 +17,7 @@
>>    */
>>  
>>   #include <config.h>
>> +#include <math.h>
>>  
>>   #include "virnetdev.h"
>>   #include "viralloc.h"
>> @@ -95,6 +96,22 @@ VIR_LOG_INIT("util.netdev");
>>       (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
>>   #endif
>>  
>> +VIR_ENUM_IMPL(virNetDevGenNameType,
>> +              VIR_NET_DEV_GEN_NAME_LAST,
>> +              "none",
>> +              "tap",
>> +              "macvtap",
>> +              "macvlan",
>> +);
>
>
>You've added VIR_ENUM_IMPL and VIR_ENUM_DECL, but haven't actually used
>the corresponding virNetDevGenNameTypeToString() and
>virNetDevGenNameTypeFromString() functions anywhere. Unless it will be
>used, I'd say it would be just as well to leave it out. (and if you do
>define it, probably use "vnet" for the tap device entry, since it's not
>really important that the device is a tap device, just that it's a
>network device whose name starts with "vnet").
> 

Okay.

>
>
>> +static virNetDevGenName
>> +virNetDevGenNames[VIR_NET_DEV_GEN_NAME_LAST] = {
>> +    {0, NULL, VIR_MUTEX_INITIALIZER},
>
>
>So you have this extra entry in the table at [0] because the first enum
>value is VIR_NET_DEV_GEN_NAME_NONE. Since that value would never be
>possible, I think you should just make the first value in the enum be
>VIR_NET_DEV_GEN_NAME_VNET (instead of ..._TAP), and then you don't need
>the unused entry in the table.
> 

Okay.

>
>> +    {-1, VIR_NET_GENERATED_TAP_PREFIX, VIR_MUTEX_INITIALIZER},
>> +    {-1, VIR_NET_GENERATED_MACVTAP_PREFIX, VIR_MUTEX_INITIALIZER},
>> +    {-1, VIR_NET_GENERATED_MACVLAN_PREFIX, VIR_MUTEX_INITIALIZER},
>> +};
>> +
>>   typedef enum {
>>       VIR_MCAST_TYPE_INDEX_TOKEN,
>>       VIR_MCAST_TYPE_NAME_TOKEN,
>> @@ -3516,3 +3533,126 @@ virNetDevSetRootQDisc(const char *ifname,
>>  
>>       return 0;
>>   }
>> +
>> +
>> +/**
>> + * virNetDevReserveName:
>> + * @name: name of an existing network device
>> + * @type: type of the network device
>> + * @locked: whether this process is locked by the internal mutex
>> + *
>> + * Reserve a network device name, so that any new network device
>> + * created with an autogenerated name will use a number higher
>> + * than the number in the given device name.
>> + *
>> + * Returns nothing.
>> + */
>> +void
>> +virNetDevReserveName(const char *name,
>> +                     virNetDevGenNameType type,
>> +                     bool locked)
>> +{
>> +    unsigned int id;
>> +    const char *idstr = NULL;
>> +
>> +    if (type && STRPREFIX(name, virNetDevGenNames[type].prefix)) {
>
>
>If you're going to worry about range-checking type, make sure that it's
>< ..._LAST (0 will be a valid value if you follow my recommendation
>above to remove ..._NONE) 

Okay.

>
>
>> +
>> +        VIR_INFO("marking device in use: '%s'", name);
>> +
>> +        idstr = name + strlen(virNetDevGenNames[type].prefix);
>> +
>> +        if (virStrToLong_ui(idstr, NULL, 10, &id) >= 0) {
>> +            if (locked)
>> +                virMutexLock(&virNetDevGenNames[type].mutex);
>> +
>> +            if (virNetDevGenNames[type].lastID < (int)id)
>> +                virNetDevGenNames[type].lastID = id;
>> +
>> +            if (locked)
>> +                virMutexUnlock(&virNetDevGenNames[type].mutex);
>> +        }
>> +    }
>> +}
>> +
>> +
>> +/**
>> + * virNetDevGenerateName:
>> + * @ifname: pointer to pointer to string containing template
>> + *
>> + * generate a new (currently unused) name for a new network device based
>> + * on the templace string in @ifname - replace %d with the reserved id,
>> + * and keep trying new values until one is found
>> + * that doesn't already exist, or we've tried 10000 different
>> + * names. Once a usable name is found, replace the template with the
>> + * actual name.
>> + *
>> + * Returns 0 on success, -1 on failure.
>> + */
>> +int
>> +virNetDevGenerateName(char **ifname, virNetDevGenNameType type)
>> +{
>> +    int id;
>> +    const char *prefix = virNetDevGenNames[type].prefix;
>> +    double maxIDd = pow(10, IFNAMSIZ - 1 - strlen(prefix));
>> +    int maxID = INT_MAX;
>> +    int attempts = 0;
>> +
>> +    if (maxIDd <= (double)INT_MAX)
>> +        maxID = (int)maxIDd;
>> +
>> +    do {
>> +        g_autofree char *try = NULL;
>> +
>> +        id = ++virNetDevGenNames[type].lastID;
>> +
>> +        /* reset before overflow */
>> +        if (virNetDevGenNames[type].lastID >= maxID)
>> +            virNetDevGenNames[type].lastID = -1;
>> +
>> +        if (*ifname)
>> +            try = g_strdup_printf(*ifname, id);
>
>
>An interesting twist! I like the direction of this - instead of relying
>on the kernel to generate the names in the case of someone defining
>their device with, e.g. <target dev='myprefix%d'/>, you send it through
>our generator. Sending a user-provided string to printf as the format
>string makes me uneasy though. Also, what if ifname is an
>unparameterized name (e.g. "mytap"), and there is already a device named
>"mytap"? In that case, it will spin through the loop 10000 times trying
>the same device name, although we know from the start that it's not
>going to work.
>
>
>How about if, instead of blindly doing this, we check to see if the
>device name has a single "%" that is followed by "d"? If it's anything
>other than that (or NULL) then we just return with the string unchanged. 

Okay. In the meanwhile, I should modify the explanation of this functions.
>From now on, the *ifname can be a template, NULL or a user-defined name.

>
>
>> +        else
>> +            try = g_strdup_printf("%s%d", prefix, id);
>> +
>> +        if (!virNetDevExists(try)) {
>> +            g_free(*ifname);
>> +            *ifname = g_steal_pointer(&try);
>> +            return 0;
>> +        }
>> +    } while (++attempts < 10000);
>> +
>> +    virReportError(VIR_ERR_INTERNAL_ERROR,
>> +                   _("no unused %s names available"),
>> +                   prefix);
>> +    return -1;
>> +}
>> +
>> +
>> +/**
>> + * virNetDevLockGenName:
>> + * @type: type of the network device
>> + *
>> + * Lock the internal mutex for creation for this network device type.
>> + *
>> + * Returns nothing.
>> + */
>> +void
>> +virNetDevLockGenName(virNetDevGenNameType type)
>> +{
>> +    virMutexLock(&virNetDevGenNames[type].mutex);
>> +}
>> +
>> +
>> +/**
>> + * virNetDevUnlockGenName:
>> + * @type: type of the network device
>> + *
>> + * Unlock the internal mutex for creation for this network device type.
>> + *
>> + * Returns nothing.
>> + */
>> +void
>> +virNetDevUnlockGenName(virNetDevGenNameType type)
>> +{
>> +    virMutexUnlock(&virNetDevGenNames[type].mutex);
>> +}
>> diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
>> index 53e606c6..19f37b61 100644
>> --- a/src/util/virnetdev.h
>> +++ b/src/util/virnetdev.h
>> @@ -40,6 +40,12 @@ typedef void virIfreq;
>>    */
>>   #define VIR_NET_GENERATED_TAP_PREFIX "vnet"
>>  
>> +/* libvirt will start macvtap/macvlan interface names with one of
>> + * these prefixes when it auto-generates the name
>> + */
>> +#define VIR_NET_GENERATED_MACVTAP_PREFIX "macvtap"
>> +#define VIR_NET_GENERATED_MACVLAN_PREFIX "macvlan"
>> +
>>   typedef enum {
>>      VIR_NETDEV_RX_FILTER_MODE_NONE = 0,
>>      VIR_NETDEV_RX_FILTER_MODE_NORMAL,
>> @@ -145,6 +151,24 @@ struct _virNetDevCoalesce {
>>       uint32_t rate_sample_interval;
>>   };
>>  
>> +typedef enum {
>> +    VIR_NET_DEV_GEN_NAME_NONE = 0,
>
>
>See comment above about removing ..._NONE 

Okay.

>
>> +    VIR_NET_DEV_GEN_NAME_TAP,
>> +    VIR_NET_DEV_GEN_NAME_MACVTAP,
>> +    VIR_NET_DEV_GEN_NAME_MACVLAN,
>> +    VIR_NET_DEV_GEN_NAME_LAST
>> +} virNetDevGenNameType;
>> +
>> +VIR_ENUM_DECL(virNetDevGenNameType);
>
>
>Also about removing VIR_ENUM_DECL() (unless we end up with a reason for
>virNetDevGenNameType(To|From)String())
> 

Okay.

>
>> +
>> +typedef struct _virNetDevGenName virNetDevGenName;
>> +typedef virNetDevGenName *virNetDevGenNamePtr;
>> +struct _virNetDevGenName {
>> +    int lastID;         /* not "unsigned" because callers use %d */
>> +    const char *prefix;
>> +    virMutex mutex;
>> +};
>> +
>>  
>>   int virNetDevSetupControl(const char *ifname,
>>                             virIfreq *ifr)
>> @@ -321,3 +345,12 @@ int virNetDevVFInterfaceStats(virPCIDeviceAddressPtr vfAddr,
>>   ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
>>  
>>   G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetDevRxFilter, virNetDevRxFilterFree);
>> +
>> +void virNetDevReserveName(const char *name,
>> +                          virNetDevGenNameType type,
>> +                          bool locked);
>> +
>> +int virNetDevGenerateName(char **ifname, virNetDevGenNameType type);
>> +
>> +void virNetDevLockGenName(virNetDevGenNameType type);
>> +void virNetDevUnlockGenName(virNetDevGenNameType type);
>
>
>Otherwise this looks good!
>
>




More information about the libvir-list mailing list