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

Reply via email to