Sorry, I found a memory leak. Updated diff below.

Index: sys/net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
retrieving revision 1.610
diff -u -p -r1.610 if.c
--- sys/net/if.c        22 Jun 2020 09:45:13 -0000      1.610
+++ sys/net/if.c        26 Jun 2020 17:19:41 -0000
@@ -157,6 +157,8 @@ void        if_linkstate_task(void *);
 
 int    if_clone_list(struct if_clonereq *);
 struct if_clone        *if_clone_lookup(const char *, int *);
+int    if_clone_alloc_unit(struct if_clone *, int);
+void   if_clone_rele_unit(struct if_clone *, int);
 
 int    if_group_egress_build(void);
 
@@ -1244,19 +1246,23 @@ if_clone_create(const char *name, int rd
 {
        struct if_clone *ifc;
        struct ifnet *ifp;
-       int unit, ret;
+       int unit, error;
 
        ifc = if_clone_lookup(name, &unit);
        if (ifc == NULL)
                return (EINVAL);
+       error = if_clone_alloc_unit(ifc, unit);
+       if (error != 0)
+               return (error);
 
-       if (ifunit(name) != NULL)
-               return (EEXIST);
-
-       ret = (*ifc->ifc_create)(ifc, unit);
+       if (ifunit(name) != NULL) {
+               error = (EEXIST);
+               goto rele;
+       }
 
-       if (ret != 0 || (ifp = ifunit(name)) == NULL)
-               return (ret);
+       error = (*ifc->ifc_create)(ifc, unit);
+       if (error != 0 || (ifp = ifunit(name)) == NULL)
+               return (0);
 
        NET_LOCK();
        if_addgroup(ifp, ifc->ifc_name);
@@ -1264,7 +1270,10 @@ if_clone_create(const char *name, int rd
                if_setrdomain(ifp, rdomain);
        NET_UNLOCK();
 
-       return (ret);
+       return (0);
+rele:
+       if_clone_rele_unit(ifc, unit);
+       return (error);
 }
 
 /*
@@ -1275,9 +1284,9 @@ if_clone_destroy(const char *name)
 {
        struct if_clone *ifc;
        struct ifnet *ifp;
-       int ret;
+       int ret, unit;
 
-       ifc = if_clone_lookup(name, NULL);
+       ifc = if_clone_lookup(name, &unit);
        if (ifc == NULL)
                return (EINVAL);
 
@@ -1297,6 +1306,7 @@ if_clone_destroy(const char *name)
        }
        NET_UNLOCK();
        ret = (*ifc->ifc_destroy)(ifp);
+       if_clone_rele_unit(ifc, unit);
 
        return (ret);
 }
@@ -1342,12 +1352,95 @@ if_clone_lookup(const char *name, int *u
                unit = (unit * 10) + (*cp++ - '0');
        }
 
-       if (unitp != NULL)
-               *unitp = unit;
+       *unitp = unit;
        return (ifc);
 }
 
 /*
+ * Allocate unit for cloned network interface.
+ */
+int if_clone_alloc_unit(struct if_clone *ifc, int unit)
+{
+       int word, bit, ret;
+
+       word = unit / (sizeof(*ifc->ifc_map) * 8);
+       bit = unit % (sizeof(*ifc->ifc_map) * 8);
+
+       rw_enter_write(&ifc->ifc_lock);
+
+       if(word >= ifc->ifc_map_size) {
+               u_long *map;
+               int size;
+
+               size = word + 1;
+               map = mallocarray(size, sizeof(*map), M_TEMP, M_WAITOK |
+                   M_ZERO);
+
+               if (ifc->ifc_map != NULL) {
+                       memcpy(map, ifc->ifc_map, ifc->ifc_map_size);
+                       free(ifc->ifc_map, M_TEMP,
+                           ifc->ifc_map_size * sizeof(*map));
+               }
+
+               ifc->ifc_map = map;
+               ifc->ifc_map_size = size;
+       }
+
+       if (ifc->ifc_map[word] & (1UL << bit))
+               ret = EEXIST;
+       else {
+               ifc->ifc_map[word] |= (1UL << bit);
+               ret = 0;
+       }
+
+       rw_exit_write(&ifc->ifc_lock);
+
+       return ret;
+}
+
+/*
+ * Release allocated unit for cloned network interface.
+ */
+void if_clone_rele_unit(struct if_clone *ifc, int unit)
+{
+       int word, bit;
+
+       word = unit / (sizeof(*ifc->ifc_map) * 8);
+       bit = unit % (sizeof(*ifc->ifc_map) * 8);
+
+       rw_enter_write(&ifc->ifc_lock);
+       KASSERT(word < ifc->ifc_map_size);
+
+       ifc->ifc_map[word] &= ~(1UL << bit);
+
+       if (ifc->ifc_map[word] == 0) {
+               u_long *map;
+               int size;
+
+               size = ifc->ifc_map_size - 2;
+               while (size>=0) {
+                       if (ifc->ifc_map[size] != 0)
+                               break;
+                       --size;
+               }
+               if (size<0) {
+                       size = 0;
+                       map = NULL;
+               } else {
+                       size += 1;
+                       map = mallocarray(size, sizeof(*map), M_TEMP,
+                           M_WAITOK);
+                       memcpy(map, ifc->ifc_map, size);
+               }
+
+               free(ifc->ifc_map, M_TEMP, ifc->ifc_map_size * sizeof(*map));
+               ifc->ifc_map = map;
+               ifc->ifc_map_size = size;
+       }
+       rw_exit_write(&ifc->ifc_lock);
+}
+
+/*
  * Register a network interface cloner.
  */
 void
@@ -1360,6 +1453,7 @@ if_clone_attach(struct if_clone *ifc)
         * initialization, the if_cloners becomes immutable.
         */
        KASSERT(pdevinit_done == 0);
+       rw_init(&ifc->ifc_lock, "icflck");
        LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
        if_cloners_count++;
 }
Index: sys/net/if_var.h
===================================================================
RCS file: /cvs/src/sys/net/if_var.h,v
retrieving revision 1.105
diff -u -p -r1.105 if_var.h
--- sys/net/if_var.h    12 May 2020 08:49:54 -0000      1.105
+++ sys/net/if_var.h    26 Jun 2020 17:19:41 -0000
@@ -45,6 +45,7 @@
 #include <sys/task.h>
 #include <sys/time.h>
 #include <sys/timeout.h>
+#include <sys/rwlock.h>
 
 #include <net/ifq.h>
 
@@ -86,6 +87,10 @@ struct if_clone {
        const char              *ifc_name;      /* name of device, e.g. `gif' */
        size_t                   ifc_namelen;   /* length of name */
 
+       struct rwlock           ifc_lock;       /* lock for map */
+       u_long                  *ifc_map;       /* units map */
+       int                     ifc_map_size;   /* units map size */
+
        int                     (*ifc_create)(struct if_clone *, int);
        int                     (*ifc_destroy)(struct ifnet *);
 };
@@ -95,6 +100,8 @@ struct if_clone {
   .ifc_list    = { NULL, NULL },                                       \
   .ifc_name    = name,                                                 \
   .ifc_namelen = sizeof(name) - 1,                                     \
+  .ifc_map     = NULL,                                                 \
+  .ifc_map_size        = 0,                                                    
\
   .ifc_create  = create,                                               \
   .ifc_destroy = destroy,                                              \
 }

Reply via email to