On Tue, Jun 30, 2020 at 03:48:22PM +0300, Vitaliy Makkoveev wrote: > On Tue, Jun 30, 2020 at 12:08:03PM +0200, Martin Pieuchot wrote: > > On 29/06/20(Mon) 11:59, Vitaliy Makkoveev wrote: > > > [...] > > > I reworked tool for reproduce. Now I avoided fork()/exec() route and it > > > takes couple of minutes to take panic on 4 cores. Also some screenshots > > > attached. > > > > Setting kern.pool_debug=2 makes the race reproducible in seconds.
Unfortunately you will catch splassert() caused by kern/sched_bsd.c:304. malloc() will call yield() while we are holding NET_LOCK(). I attached screenshot with splassertion to this mail. > > > > Could you turn this test into something committable in regress/? We can > > link it to the build once a fix is committed. > > > > We have 3 races with cloned interfaces: > 1. if_clone_create() vs if_clone_create() > 2. if_clone_destroy() vs if_clone_destroy() > 3. if_clone_destroy() vs the rest of stack > > It makes sences to commit unified test to regress/, so I suggest to wait > a little. The another solution. Diff below introduces per-`ifc' serialization for if_clone_create() and if_clone_destroy(). There is no index bitmap anymore. Diff fixes the following races: 1. if_clone_create() vs if_clone_create() 2. if_clone_destroy() vs if_clone_destroy() `ifc_create' will go the same lock path for each cloner, and `ifc_destroy' will go this path but in reverse order. It seems reasonable to allow simultaneous create/destroy for different cloners but since different instances of one cloner will block each other it's no reason have parallelism here. Updated test tool ---- cut begin ---- #include <sys/socket.h> #include <sys/select.h> #include <sys/ioctl.h> #include <net/if.h> #include <stdio.h> #include <stdlib.h> #include <err.h> #include <errno.h> #include <pthread.h> #include <string.h> #include <unistd.h> static struct ifreq ifr; static void *clone_create(void *arg) { int s; if((s=socket(AF_INET, SOCK_DGRAM, 0))<0) err(1, "socket()"); while(1){ if(ioctl(s, SIOCIFCREATE, &ifr)<0) if(errno==EINVAL) exit(1); } return NULL; } static void *clone_destroy(void *arg) { int s; if((s=socket(AF_INET, SOCK_DGRAM, 0))<0) err(1, "socket()"); while(1){ if(ioctl(s, SIOCIFDESTROY, &ifr)<0) if(errno==EINVAL) exit(1); } return NULL; } int main(int argc, char *argv[]) { pthread_t thr; int i; if(argc!=2){ fprintf(stderr, "usage: %s ifname\n", getprogname()); return 1; } if(getuid()!=0){ fprintf(stderr, "should be root\n"); return 1; } memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)); for(i=0; i<8*4; ++i){ if(pthread_create(&thr, NULL, clone_create, NULL)!=0) errx(1, "pthread_create(clone_create)"); if(pthread_create(&thr, NULL, clone_destroy, NULL)!=0) errx(1, "pthread_create(clone_destroy)"); } select(0, NULL, NULL, NULL, NULL); return 0; } ---- cut end ---- Index: sys/net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.611 diff -u -p -r1.611 if.c --- sys/net/if.c 30 Jun 2020 09:31:38 -0000 1.611 +++ sys/net/if.c 30 Jun 2020 20:41:50 -0000 @@ -155,6 +155,8 @@ int if_getgrouplist(caddr_t); void if_linkstate(struct ifnet *); void if_linkstate_task(void *); +int if_clone_lock(struct if_clone *); +void if_clone_unlock(struct if_clone *); int if_clone_list(struct if_clonereq *); struct if_clone *if_clone_lookup(const char *, int *); @@ -1244,27 +1246,35 @@ 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); - if (ifunit(name) != NULL) - return (EEXIST); + error = if_clone_lock(ifc); + if (error != 0) + return (error); + + if (ifunit(name) != NULL) { + error = (EEXIST); + goto unlock; + } - ret = (*ifc->ifc_create)(ifc, unit); + error = (*ifc->ifc_create)(ifc, unit); - if (ret != 0 || (ifp = ifunit(name)) == NULL) - return (ret); + if (error != 0 || (ifp = ifunit(name)) == NULL) + goto unlock; NET_LOCK(); if_addgroup(ifp, ifc->ifc_name); if (rdomain != 0) if_setrdomain(ifp, rdomain); NET_UNLOCK(); +unlock: + if_clone_unlock(ifc); - return (ret); + return (error); } /* @@ -1275,18 +1285,26 @@ if_clone_destroy(const char *name) { struct if_clone *ifc; struct ifnet *ifp; - int ret; + int error; ifc = if_clone_lookup(name, NULL); if (ifc == NULL) return (EINVAL); + error = if_clone_lock(ifc); + if (error != 0) + return (error); + ifp = ifunit(name); - if (ifp == NULL) - return (ENXIO); + if (ifp == NULL) { + error = (ENXIO); + goto unlock; + } - if (ifc->ifc_destroy == NULL) - return (EOPNOTSUPP); + if (ifc->ifc_destroy == NULL) { + error = (EOPNOTSUPP); + goto unlock; + } NET_LOCK(); if (ifp->if_flags & IFF_UP) { @@ -1296,9 +1314,56 @@ if_clone_destroy(const char *name) splx(s); } NET_UNLOCK(); - ret = (*ifc->ifc_destroy)(ifp); + error = (*ifc->ifc_destroy)(ifp); +unlock: + if_clone_unlock(ifc); + + return (error); +} + +/* + * Lock a clone network interface. + */ +int +if_clone_lock(struct if_clone *ifc) +{ + int error; + + rw_enter_write(&ifc->ifc_lock); + + while (ifc->ifc_flags & IFC_CREATE_LOCKED) { + ifc->ifc_flags |= IFC_CREATE_LOCKWAIT; + error = rwsleep_nsec(&ifc->ifc_flags, &ifc->ifc_lock, + PWAIT|PCATCH, "ifclk", INFSLP); + if(error != 0) { + ifc->ifc_flags &= ~IFC_CREATE_LOCKWAIT; + rw_exit_write(&ifc->ifc_lock); + return error; + } + } + ifc->ifc_flags |= IFC_CREATE_LOCKED; + ifc->ifc_flags &= ~IFC_CREATE_LOCKWAIT; + + rw_exit_write(&ifc->ifc_lock); + + return 0; +} + +/* + * Unlock a clone network interface. + */ +void +if_clone_unlock(struct if_clone *ifc) +{ + rw_enter_write(&ifc->ifc_lock); + + ifc->ifc_flags &= ~IFC_CREATE_LOCKED; + if (ifc->ifc_flags & IFC_CREATE_LOCKWAIT) { + ifc->ifc_flags &= ~IFC_CREATE_LOCKWAIT; + wakeup(&ifc->ifc_flags); + } - return (ret); + rw_exit_write(&ifc->ifc_lock); } /* 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 30 Jun 2020 20:41:50 -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> @@ -85,16 +86,23 @@ struct if_clone { LIST_ENTRY(if_clone) ifc_list; /* on list of cloners */ const char *ifc_name; /* name of device, e.g. `gif' */ size_t ifc_namelen; /* length of name */ + struct rwlock ifc_lock; /* lock for ifc_flags */ + u_int ifc_flags; /* flags */ int (*ifc_create)(struct if_clone *, int); int (*ifc_destroy)(struct ifnet *); }; +#define IFC_CREATE_LOCKED 0x1 +#define IFC_CREATE_LOCKWAIT 0x2 + #define IF_CLONE_INITIALIZER(name, create, destroy) \ { \ .ifc_list = { NULL, NULL }, \ .ifc_name = name, \ .ifc_namelen = sizeof(name) - 1, \ + .ifc_lock = RWLOCK_INITIALIZER("ifclk"), \ + .ifc_flags = 0, \ .ifc_create = create, \ .ifc_destroy = destroy, \ }