Hi,

Customer complains that routing video stream over OpenBSD stutters
when snmpd is runnning.  It looks like ioctl(SIOCGIFMEDIA) may be
the culprit, so I want to get it out of the netlock.

Start with a mutex around interface media list.

ok?

bluhm

Index: dev/ic/gem.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/dev/ic/gem.c,v
retrieving revision 1.126
diff -u -p -r1.126 gem.c
--- dev/ic/gem.c        12 Dec 2020 11:48:52 -0000      1.126
+++ dev/ic/gem.c        11 Jul 2022 06:16:15 -0000
@@ -332,6 +332,7 @@ gem_config(struct gem_softc *sc)
        }
 
        /* Check if we support GigE media. */
+       mtx_enter(&ifmedia_mtx);
        TAILQ_FOREACH(ifm, &sc->sc_media.ifm_list, ifm_list) {
                if (IFM_SUBTYPE(ifm->ifm_media) == IFM_1000_T ||
                    IFM_SUBTYPE(ifm->ifm_media) == IFM_1000_SX ||
@@ -341,6 +342,7 @@ gem_config(struct gem_softc *sc)
                        break;
                }
        }
+       mtx_leave(&ifmedia_mtx);
 
        /* Attach the interface. */
        if_attach(ifp);
Index: net/if_media.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_media.c,v
retrieving revision 1.33
diff -u -p -r1.33 if_media.c
--- net/if_media.c      10 Jul 2022 21:13:41 -0000      1.33
+++ net/if_media.c      11 Jul 2022 06:24:28 -0000
@@ -82,6 +82,7 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/malloc.h>
+#include <sys/mutex.h>
 
 #include <net/if.h>
 #ifdef IFMEDIA_DEBUG
@@ -102,6 +103,8 @@ int ifmedia_debug = 0;
 static void ifmedia_printword(uint64_t);
 #endif
 
+struct mutex ifmedia_mtx = MUTEX_INITIALIZER(IPL_NET);
+
 /*
  * Initialize if_media struct for a specific interface instance.
  */
@@ -110,6 +113,7 @@ ifmedia_init(struct ifmedia *ifm, uint64
     ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback)
 {
        TAILQ_INIT(&ifm->ifm_list);
+       ifm->ifm_nwords = 0;
        ifm->ifm_cur = NULL;
        ifm->ifm_media = 0;
        ifm->ifm_mask = dontcare_mask;          /* IF don't-care bits */
@@ -145,7 +149,10 @@ ifmedia_add(struct ifmedia *ifm, uint64_
        entry->ifm_data = data;
        entry->ifm_aux = aux;
 
+       mtx_enter(&ifmedia_mtx);
        TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list);
+       ifm->ifm_nwords++;
+       mtx_leave(&ifmedia_mtx);
 }
 
 /*
@@ -290,7 +297,6 @@ ifmedia_ioctl(struct ifnet *ifp, struct 
        case  SIOCGIFMEDIA:
        {
                struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
-               struct ifmedia_entry *ep;
                size_t nwords;
 
                if (ifmr->ifm_count < 0)
@@ -302,37 +308,54 @@ ifmedia_ioctl(struct ifnet *ifp, struct 
                ifmr->ifm_status = 0;
                (*ifm->ifm_status_cb)(ifp, ifmr);
 
-               /*
-                * Count them so we know a-priori how much is the max we'll
-                * need.
-                */
-               ep = TAILQ_FIRST(&ifm->ifm_list);
-               for (nwords = 0; ep != NULL; ep = TAILQ_NEXT(ep, ifm_list))
-                       nwords++;
+               mtx_enter(&ifmedia_mtx);
+               nwords = ifm->ifm_nwords;
+               mtx_leave(&ifmedia_mtx);
 
-               if (ifmr->ifm_count != 0) {
-                       size_t minwords, ksiz;
+               if (ifmr->ifm_count == 0) {
+                       ifmr->ifm_count = nwords;
+                       return (0);
+               }
+
+               do {
+                       struct ifmedia_entry *ife;
                        uint64_t *kptr;
+                       size_t ksiz;
 
-                       minwords = nwords > (size_t)ifmr->ifm_count ?
-                           (size_t)ifmr->ifm_count : nwords;
                        kptr = mallocarray(nwords, sizeof(*kptr), M_TEMP,
                            M_WAITOK | M_ZERO);
                        ksiz = nwords * sizeof(*kptr);
+
+                       mtx_enter(&ifmedia_mtx);
+                       /* Madia list might grow during malloc(). */
+                       if (nwords < ifm->ifm_nwords) {
+                               nwords = ifm->ifm_nwords;
+                               mtx_leave(&ifmedia_mtx);
+                               free(kptr, M_TEMP, ksiz);
+                               continue;
+                       }
+                       /* Request memory too small, set error and ifm_count. */
+                       if (ifmr->ifm_count < ifm->ifm_nwords) {
+                               nwords = ifm->ifm_nwords;
+                               mtx_leave(&ifmedia_mtx);
+                               free(kptr, M_TEMP, ksiz);
+                               error = E2BIG;
+                               break;
+                       }
                        /*
                         * Get the media words from the interface's list.
                         */
-                       ep = TAILQ_FIRST(&ifm->ifm_list);
-                       for (nwords = 0; ep != NULL && nwords < minwords;
-                           ep = TAILQ_NEXT(ep, ifm_list))
-                               kptr[nwords++] = ep->ifm_media;
-                       if (ep == NULL)
-                               error = copyout(kptr, ifmr->ifm_ulist,
-                                   nwords * sizeof(*kptr));
-                       else
-                               error = E2BIG;
+                       nwords = 0;
+                       TAILQ_FOREACH(ife, &ifm->ifm_list, ifm_list) {
+                               kptr[nwords++] = ife->ifm_media;
+                       }
+                       KASSERT(nwords == ifm->ifm_nwords);
+                       mtx_leave(&ifmedia_mtx);
+
+                       error = copyout(kptr, ifmr->ifm_ulist,
+                           nwords * sizeof(*kptr));
                        free(kptr, M_TEMP, ksiz);
-               }
+               } while (0);
                ifmr->ifm_count = nwords;
                break;
        }
@@ -355,6 +378,7 @@ ifmedia_match(struct ifmedia *ifm, uint6
        match = NULL;
        mask = ~mask;
 
+       mtx_enter(&ifmedia_mtx);
        TAILQ_FOREACH(next, &ifm->ifm_list, ifm_list) {
                if ((next->ifm_media & mask) == (target & mask)) {
                        if (match) {
@@ -368,6 +392,7 @@ ifmedia_match(struct ifmedia *ifm, uint6
                        match = next;
                }
        }
+       mtx_leave(&ifmedia_mtx);
 
        return (match);
 }
@@ -379,13 +404,25 @@ void
 ifmedia_delete_instance(struct ifmedia *ifm, uint64_t inst)
 {
        struct ifmedia_entry *ife, *nife;
+       struct ifmedia_list ifmlist;
+
+       TAILQ_INIT(&ifmlist);
 
+       mtx_enter(&ifmedia_mtx);
        TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) {
                if (inst == IFM_INST_ANY ||
                    inst == IFM_INST(ife->ifm_media)) {
                        TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list);
-                       free(ife, M_IFADDR, sizeof *ife);
+                       ifm->ifm_nwords--;
+                       TAILQ_INSERT_TAIL(&ifmlist, ife, ifm_list);
                }
+       }
+       mtx_leave(&ifmedia_mtx);
+
+       /* Do not hold mutex longer than necessary, call free() without. */
+       while((ife = TAILQ_FIRST(&ifmlist)) != NULL) {
+               TAILQ_REMOVE(&ifmlist, ife, ifm_list);
+               free(ife, M_IFADDR, sizeof *ife);
        }
 }
 
Index: net/if_media.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_media.h,v
retrieving revision 1.43
diff -u -p -r1.43 if_media.h
--- net/if_media.h      10 Jul 2022 21:13:41 -0000      1.43
+++ net/if_media.h      11 Jul 2022 06:16:15 -0000
@@ -71,6 +71,7 @@
 #ifdef _KERNEL
 
 struct ifnet;
+extern struct mutex ifmedia_mtx;
 
 #include <sys/queue.h>
 /*
@@ -80,6 +81,11 @@ typedef      int (*ifm_change_cb_t)(struct if
 typedef        void (*ifm_stat_cb_t)(struct ifnet *, struct ifmediareq *);
 
 /*
+ * Locks used to protect struct members in this file:
+ *     M       ifmedia_mtx             interface media mutex
+ */
+
+/*
  * In-kernel representation of a single supported media type.
  */
 struct ifmedia_entry {
@@ -89,6 +95,7 @@ struct ifmedia_entry {
        void    *ifm_aux;       /* for driver-specific use */
 };
 
+TAILQ_HEAD(ifmedia_list, ifmedia_entry);
 /*
  * One of these goes into a network interface's softc structure.
  * It is used to keep general media state.
@@ -97,7 +104,8 @@ struct ifmedia {
        uint64_t        ifm_mask;       /* mask of changes we don't care about 
*/
        uint64_t        ifm_media;      /* current user-set media word */
        struct ifmedia_entry *ifm_cur;  /* currently selected media */
-       TAILQ_HEAD(, ifmedia_entry) ifm_list; /* list of all supported media */
+       struct ifmedia_list ifm_list;   /* [M] list of all supported media */
+       size_t          ifm_nwords;     /* [M] number of ifm_list entries */
        ifm_change_cb_t ifm_change_cb;  /* media change driver callback */
        ifm_stat_cb_t   ifm_status_cb;  /* media status driver callback */
 };

Reply via email to