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, \
}