> On 11 Jul 2022, at 13:02, Alexander Bluhm <[email protected]> wrote:
>
> 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?
>
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 */
> };
>