From dbac8889407e96c511d68779a12bc847b2d976b2 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 18:50:36 +0000
Subject: [PATCH 01/12] usbnet(9): Omit needless un->un_intr test in
 usbnet_pipe_intr.

un->un_intr can't change after attach, and we don't open the pipe if
it's null.  So no need to test it.
---
 sys/dev/usb/usbnet.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 3c86c7092d32..053182ee8bd2 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -439,9 +439,9 @@ usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	USBNETHIST_FUNC();
 	struct usbnet * const un = priv;
 	struct usbnet_private * const unp = un->un_pri;
-	struct usbnet_intr * const uni = un->un_intr;
+	struct usbnet_intr * const uni __unused = un->un_intr;
 
-	if (uni == NULL || usbnet_isdying(un) || unp->unp_stopping ||
+	if (usbnet_isdying(un) || unp->unp_stopping ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
 	    status == USBD_CANCELLED) {
 		USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx",

From 07706520b3224080ecd84cf147a77dc3803b85f0 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 18:52:29 +0000
Subject: [PATCH 02/12] usbnet(9): Don't touch unp_stopping in
 usbnet_pipe_intr.

This access was unprotected by a lock, but it's not necessary anyway:
usbnet_stop aborts the pipes, and the xfer doesn't call usbd_transfer
to reschedule itself -- it's an intr pipe, so it's rescheduled
internally by usbdi(9) in a way that usbd_abort_pipe atomically
prevents.
---
 sys/dev/usb/usbnet.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 053182ee8bd2..c001683b1af3 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -441,12 +441,12 @@ usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	struct usbnet_private * const unp = un->un_pri;
 	struct usbnet_intr * const uni __unused = un->un_intr;
 
-	if (usbnet_isdying(un) || unp->unp_stopping ||
+	if (usbnet_isdying(un) ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
 	    status == USBD_CANCELLED) {
-		USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx",
+		USBNETHIST_CALLARGS("%jd: uni %#jx dying %#jx status %#jx",
 		    unp->unp_number, (uintptr_t)uni,
-		    (usbnet_isdying(un) << 8) | unp->unp_stopping, status);
+		    usbnet_isdying(un), status);
 		return;
 	}
 

From d9d85b6e4031c4697ab55f6d6c2ca45903a83874 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 19:03:49 +0000
Subject: [PATCH 03/12] usbnet(9): Split unp_stopping into
 stopped/txstopped/rxstopped.

In practical terms this could be done with one variable and an atomic
store, but serializing all access with a lock makes reasoning easier,
and the locks have to be taken by the logic that queries the
variables anyway, and the variables are set only under heavy-weight
configuration changes anyway.

What this accomplishes is disentangling lock order between rxlock and
txlock: they are never taken at the same time, so no order is needed.

I renamed unp_stopping to unp_stopped for a compiler-assisted audit
to make sure I reviewed every case of it.
---
 sys/dev/usb/usbnet.c | 57 +++++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index c001683b1af3..d2e4fb421a7c 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -58,7 +58,8 @@ struct usbnet_private {
 	 * - unp_txlock protects the tx path and its data
 	 *
 	 * the lock ordering is:
-	 *	ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock
+	 *	ifnet lock -> unp_core_lock -> unp_rxlock
+	 *				    -> unp_txlock
 	 *				    -> unp_mcastlock
 	 * - ifnet lock is not needed for unp_core_lock, but if ifnet lock is
 	 *   involved, it must be taken first
@@ -79,7 +80,9 @@ struct usbnet_private {
 	struct usbd_pipe	*unp_ep[USBNET_ENDPT_MAX];
 
 	volatile bool		unp_dying;
-	bool			unp_stopping;
+	bool			unp_stopped;
+	bool			unp_rxstopped;
+	bool			unp_txstopped;
 	bool			unp_attached;
 	bool			unp_ifp_attached;
 	bool			unp_link;
@@ -341,7 +344,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 
 	mutex_enter(&unp->unp_rxlock);
 
-	if (usbnet_isdying(un) || unp->unp_stopping ||
+	if (usbnet_isdying(un) || unp->unp_rxstopped ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
 	    status == USBD_CANCELLED)
 		goto out;
@@ -368,7 +371,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	usbnet_isowned_rx(un);
 
 done:
-	if (usbnet_isdying(un) || unp->unp_stopping)
+	if (usbnet_isdying(un) || unp->unp_rxstopped)
 		goto out;
 
 	mutex_exit(&unp->unp_rxlock);
@@ -397,7 +400,7 @@ usbnet_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	    unp->unp_number, status, (uintptr_t)xfer, 0);
 
 	mutex_enter(&unp->unp_txlock);
-	if (unp->unp_stopping || usbnet_isdying(un)) {
+	if (unp->unp_txstopped || usbnet_isdying(un)) {
 		mutex_exit(&unp->unp_txlock);
 		return;
 	}
@@ -569,11 +572,11 @@ usbnet_if_start(struct ifnet *ifp)
 	struct usbnet_private * const unp = un->un_pri;
 
 	USBNETHIST_FUNC();
-	USBNETHIST_CALLARGS("%jd: stopping %jd",
-	    unp->unp_number, unp->unp_stopping, 0, 0);
+	USBNETHIST_CALLARGS("%jd: txstopped %jd",
+	    unp->unp_number, unp->unp_txstopped, 0, 0);
 
 	mutex_enter(&unp->unp_txlock);
-	if (!unp->unp_stopping)
+	if (!unp->unp_txstopped)
 		usbnet_start_locked(ifp);
 	mutex_exit(&unp->unp_txlock);
 }
@@ -659,8 +662,8 @@ usbnet_rx_start_pipes(struct usbnet * const un)
 	struct usbnet_private * const unp = un->un_pri;
 
 	mutex_enter(&unp->unp_rxlock);
-	mutex_enter(&unp->unp_txlock);
-	unp->unp_stopping = false;
+	KASSERT(unp->unp_rxstopped);
+	unp->unp_rxstopped = false;
 
 	for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
 		struct usbnet_chain *c = &cd->uncd_rx_chain[i];
@@ -670,7 +673,6 @@ usbnet_rx_start_pipes(struct usbnet * const un)
 		usbd_transfer(c->unc_xfer);
 	}
 
-	mutex_exit(&unp->unp_txlock);
 	mutex_exit(&unp->unp_rxlock);
 }
 
@@ -855,9 +857,17 @@ usbnet_init_rx_tx(struct usbnet * const un)
 		mutex_exit(&unp->unp_mcastlock);
 	}
 
+	/* Allow transmit.  */
+	mutex_enter(&unp->unp_txlock);
+	KASSERT(unp->unp_txstopped);
+	unp->unp_txstopped = false;
+	mutex_exit(&unp->unp_txlock);
+
 	/* Start up the receive pipe(s). */
 	usbnet_rx_start_pipes(un);
 
+	/* Kick off the watchdog/stats/mii tick.  */
+	unp->unp_stopped = false;
 	callout_schedule(&unp->unp_stat_ch, hz);
 
 out:
@@ -1075,17 +1085,21 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
 	 * clear the watchdog timer.
 	 */
+	unp->unp_stopped = true;
+
 	mutex_enter(&unp->unp_rxlock);
+	unp->unp_rxstopped = true;
+	mutex_exit(&unp->unp_rxlock);
+
 	mutex_enter(&unp->unp_txlock);
-	unp->unp_stopping = true;
+	unp->unp_txstopped = true;
 	unp->unp_timer = 0;
 	mutex_exit(&unp->unp_txlock);
-	mutex_exit(&unp->unp_rxlock);
 
 	/*
 	 * Stop the timer first, then the task -- if the timer was
 	 * already firing, we stop the task or wait for it complete
-	 * only after if last fired.  Setting unp_stopping prevents the
+	 * only after if last fired.  Setting unp_stopped prevents the
 	 * timer task from being scheduled again.
 	 */
 	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
@@ -1214,7 +1228,7 @@ usbnet_tick_task(void *arg)
 	uno_tick(un);
 
 	mutex_enter(&unp->unp_core_lock);
-	if (!unp->unp_stopping && !usbnet_isdying(un))
+	if (!unp->unp_stopped && !usbnet_isdying(un))
 		callout_schedule(&unp->unp_stat_ch, hz);
 	mutex_exit(&unp->unp_core_lock);
 }
@@ -1383,6 +1397,9 @@ usbnet_attach(struct usbnet *un)
 
 	unp->unp_number = atomic_inc_uint_nv(&usbnet_number);
 
+	unp->unp_stopped = true;
+	unp->unp_rxstopped = true;
+	unp->unp_txstopped = true;
 	unp->unp_attached = true;
 }
 
@@ -1572,11 +1589,17 @@ usbnet_activate(device_t self, devact_t act)
 
 		atomic_store_relaxed(&unp->unp_dying, true);
 
+		mutex_enter(&unp->unp_core_lock);
+		unp->unp_stopped = true;
+		mutex_exit(&unp->unp_core_lock);
+
 		mutex_enter(&unp->unp_rxlock);
+		unp->unp_rxstopped = true;
+		mutex_exit(&unp->unp_rxlock);
+
 		mutex_enter(&unp->unp_txlock);
-		unp->unp_stopping = true;
+		unp->unp_txstopped = true;
 		mutex_exit(&unp->unp_txlock);
-		mutex_exit(&unp->unp_rxlock);
 
 		return 0;
 	default:

From 09ed62aaa49167c7bf3f33a38f81b848b2d66913 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 19:13:20 +0000
Subject: [PATCH 04/12] usbnet(9): Assert core lock is held on usbnet_set_link.

This is only allowed to be called via the uno_statchg callback, which
in turn is called only with the core lock held.  (usbnet_set_link is
also called internally in usbnet(9) with the core lock held.)
---
 sys/dev/usb/usbnet.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index d2e4fb421a7c..cac473607151 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1277,6 +1277,7 @@ out:	mutex_exit(&un->un_pri->unp_core_lock);
 void
 usbnet_set_link(struct usbnet *un, bool link)
 {
+	usbnet_isowned_core(un);
 	un->un_pri->unp_link = link;
 }
 

From e9cba453f4d767b0bd05315864061ff7fe9f6bf9 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 19:23:51 +0000
Subject: [PATCH 05/12] usbnet(9): Call uno_tick before mii stuff.

The one driver that uses it, cue(4), uses it just for statistics
gathering; hard to imagine that order could be important here.  But
this will allow for some simplification of the surrounding code.
---
 sys/dev/usb/usbnet.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index cac473607151..9bf200f1ed6d 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1215,6 +1215,9 @@ usbnet_tick_task(void *arg)
 	if (timeout)
 		usbnet_watchdog(ifp);
 
+	/* Call driver if requested. */
+	uno_tick(un);
+
 	DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
 	if (mii) {
 		mutex_enter(&unp->unp_core_lock);
@@ -1224,9 +1227,6 @@ usbnet_tick_task(void *arg)
 		mutex_exit(&unp->unp_core_lock);
 	}
 
-	/* Call driver if requested. */
-	uno_tick(un);
-
 	mutex_enter(&unp->unp_core_lock);
 	if (!unp->unp_stopped && !usbnet_isdying(un))
 		callout_schedule(&unp->unp_stat_ch, hz);

From ef32c4cdc7e4cf12b88ead1a0a55353a440d708d Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 19:26:21 +0000
Subject: [PATCH 06/12] usbnet(9): Simplify core lock use in usbnet_tick_task.

---
 sys/dev/usb/usbnet.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 9bf200f1ed6d..1c1c4c5a8386 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1218,16 +1218,14 @@ usbnet_tick_task(void *arg)
 	/* Call driver if requested. */
 	uno_tick(un);
 
+	mutex_enter(&unp->unp_core_lock);
 	DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
 	if (mii) {
-		mutex_enter(&unp->unp_core_lock);
 		mii_tick(mii);
 		if (!unp->unp_link)
 			(*mii->mii_statchg)(ifp);
-		mutex_exit(&unp->unp_core_lock);
 	}
 
-	mutex_enter(&unp->unp_core_lock);
 	if (!unp->unp_stopped && !usbnet_isdying(un))
 		callout_schedule(&unp->unp_stat_ch, hz);
 	mutex_exit(&unp->unp_core_lock);

From 742ec9b0568b041b024949d774c82c23f0fba12e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Mon, 15 Aug 2022 09:54:34 +0000
Subject: [PATCH 07/12] usbnet(9): Call mii_down once we've finished with
 mii_tick.

---
 sys/dev/usb/usbnet.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 1c1c4c5a8386..686f4ace0200 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1063,6 +1063,7 @@ static void
 usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 {
 	struct usbnet_private * const unp = un->un_pri;
+	struct mii_data * const mii = usbnet_mii(un);
 
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 
@@ -1106,6 +1107,13 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
 	    &unp->unp_core_lock);
 
+	/*
+	 * Now that we have stopped calling mii_tick, bring the MII
+	 * state machine down.
+	 */
+	if (mii)
+		mii_down(mii);
+
 	/* Stop transfers. */
 	usbnet_ep_stop_pipes(un);
 

From 2f00f4f8108f8e4b34d17d50045af871889e9cb1 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 19:52:57 +0000
Subject: [PATCH 08/12] usbnet(9): Limit scope of core lock to mii and tick
 scheduling.

Bringing the interface up or down is serialized by IFNET_LOCK, and we
prevent further mii callbacks with mii_down, so there's no need for
another lock to serialize uno_init, uno_stop, and the mii callbacks.
---
 sys/dev/usb/usbnet.c | 51 +++++++++++++++++++++-----------------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 686f4ace0200..9c46ca6b9151 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -52,17 +52,15 @@ struct usbnet_cdata {
 
 struct usbnet_private {
 	/*
-	 * - unp_core_lock protects most of this structure, the public one,
-	 *   and the MII / media data.
+	 * - unp_core_lock protects the MII / media data and tick scheduling.
 	 * - unp_rxlock protects the rx path and its data
 	 * - unp_txlock protects the tx path and its data
 	 *
 	 * the lock ordering is:
-	 *	ifnet lock -> unp_core_lock -> unp_rxlock
-	 *				    -> unp_txlock
-	 *				    -> unp_mcastlock
-	 * - ifnet lock is not needed for unp_core_lock, but if ifnet lock is
-	 *   involved, it must be taken first
+	 *	ifnet lock -> unp_core_lock
+	 *		   -> unp_rxlock
+	 *		   -> unp_txlock
+	 *		   -> unp_mcastlock
 	 */
 	kmutex_t		unp_core_lock;
 	kmutex_t		unp_rxlock;
@@ -813,8 +811,6 @@ usbnet_init_rx_tx(struct usbnet * const un)
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un)) {
 		return EIO;
 	}
@@ -867,8 +863,10 @@ usbnet_init_rx_tx(struct usbnet * const un)
 	usbnet_rx_start_pipes(un);
 
 	/* Kick off the watchdog/stats/mii tick.  */
+	mutex_enter(&unp->unp_core_lock);
 	unp->unp_stopped = false;
 	callout_schedule(&unp->unp_stat_ch, hz);
+	mutex_exit(&unp->unp_core_lock);
 
 out:
 	if (error) {
@@ -881,10 +879,11 @@ out:
 	 * For devices without any media autodetection, treat success
 	 * here as an active link.
 	 */
-	if (un->un_ops->uno_statchg == NULL)
+	if (un->un_ops->uno_statchg == NULL) {
+		mutex_enter(&unp->unp_core_lock);
 		usbnet_set_link(un, error == 0);
-
-	usbnet_isowned_core(un);
+		mutex_exit(&unp->unp_core_lock);
+	}
 
 	return error;
 }
@@ -1068,13 +1067,11 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
-	usbnet_isowned_core(un);
 
 	/*
 	 * For drivers with hardware multicast filter update callbacks:
 	 * Prevent concurrent access to the hardware registers by
-	 * multicast filter updates, which happens without IFNET_LOCK
-	 * or the usbnet core lock.
+	 * multicast filter updates, which happens without IFNET_LOCK.
 	 */
 	if (un->un_ops->uno_mcast) {
 		mutex_enter(&unp->unp_mcastlock);
@@ -1086,7 +1083,9 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
 	 * clear the watchdog timer.
 	 */
+	mutex_enter(&unp->unp_core_lock);
 	unp->unp_stopped = true;
+	mutex_exit(&unp->unp_core_lock);
 
 	mutex_enter(&unp->unp_rxlock);
 	unp->unp_rxstopped = true;
@@ -1100,19 +1099,22 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	/*
 	 * Stop the timer first, then the task -- if the timer was
 	 * already firing, we stop the task or wait for it complete
-	 * only after if last fired.  Setting unp_stopped prevents the
+	 * only after it last fired.  Setting unp_stopped prevents the
 	 * timer task from being scheduled again.
 	 */
-	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
+	callout_halt(&unp->unp_stat_ch, NULL);
 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
-	    &unp->unp_core_lock);
+	    NULL);
 
 	/*
 	 * Now that we have stopped calling mii_tick, bring the MII
 	 * state machine down.
 	 */
-	if (mii)
+	if (mii) {
+		mutex_enter(&unp->unp_core_lock);
 		mii_down(mii);
+		mutex_exit(&unp->unp_core_lock);
+	}
 
 	/* Stop transfers. */
 	usbnet_ep_stop_pipes(un);
@@ -1146,7 +1148,6 @@ static void
 usbnet_if_stop(struct ifnet *ifp, int disable)
 {
 	struct usbnet * const un = ifp->if_softc;
-	struct usbnet_private * const unp = un->un_pri;
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
@@ -1160,9 +1161,7 @@ usbnet_if_stop(struct ifnet *ifp, int disable)
 	if ((ifp->if_flags & IFF_RUNNING) == 0)
 		return;
 
-	mutex_enter(&unp->unp_core_lock);
 	usbnet_stop(un, ifp, disable);
-	mutex_exit(&unp->unp_core_lock);
 }
 
 /*
@@ -1265,16 +1264,14 @@ usbnet_if_init(struct ifnet *ifp)
 	if (ifp->if_flags & IFF_RUNNING)
 		return 0;
 
-	mutex_enter(&un->un_pri->unp_core_lock);
 	error = uno_init(un, ifp);
 	if (error)
-		goto out;
+		return error;
 	error = usbnet_init_rx_tx(un);
 	if (error)
-		goto out;
-out:	mutex_exit(&un->un_pri->unp_core_lock);
+		return error;
 
-	return error;
+	return 0;
 }
 
 

From 5c185fede29af65416c636dced106cc90cb23e85 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 14 Aug 2022 20:12:52 +0000
Subject: [PATCH 09/12] usbnet(9): Rename core lock -> mii lock.

No functional change intended.
---
 sys/dev/usb/usbnet.c | 66 ++++++++++++++++++++++----------------------
 1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 9c46ca6b9151..f763973336ab 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -52,17 +52,17 @@ struct usbnet_cdata {
 
 struct usbnet_private {
 	/*
-	 * - unp_core_lock protects the MII / media data and tick scheduling.
+	 * - unp_miilock protects the MII / media data and tick scheduling.
 	 * - unp_rxlock protects the rx path and its data
 	 * - unp_txlock protects the tx path and its data
 	 *
 	 * the lock ordering is:
-	 *	ifnet lock -> unp_core_lock
+	 *	ifnet lock -> unp_miilock
 	 *		   -> unp_rxlock
 	 *		   -> unp_txlock
 	 *		   -> unp_mcastlock
 	 */
-	kmutex_t		unp_core_lock;
+	kmutex_t		unp_miilock;
 	kmutex_t		unp_rxlock;
 	kmutex_t		unp_txlock;
 
@@ -104,9 +104,9 @@ static void usbnet_isowned_rx(struct usbnet *);
 static void usbnet_isowned_tx(struct usbnet *);
 
 static inline void
-usbnet_isowned_core(struct usbnet *un)
+usbnet_isowned_mii(struct usbnet *un)
 {
-	KASSERT(mutex_owned(&un->un_pri->unp_core_lock));
+	KASSERT(mutex_owned(&un->un_pri->unp_miilock));
 }
 
 static int usbnet_modcmd(modcmd_t, void *);
@@ -162,7 +162,7 @@ static void
 uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 {
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
-	usbnet_isowned_core(un);
+	usbnet_isowned_mii(un);
 	if (un->un_ops->uno_stop)
 		(*un->un_ops->uno_stop)(ifp, disable);
 }
@@ -205,21 +205,21 @@ uno_init(struct usbnet *un, struct ifnet *ifp)
 static int
 uno_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 {
-	usbnet_isowned_core(un);
+	usbnet_isowned_mii(un);
 	return (*un->un_ops->uno_read_reg)(un, phy, reg, val);
 }
 
 static int
 uno_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
 {
-	usbnet_isowned_core(un);
+	usbnet_isowned_mii(un);
 	return (*un->un_ops->uno_write_reg)(un, phy, reg, val);
 }
 
 static void
 uno_mii_statchg(struct usbnet *un, struct ifnet *ifp)
 {
-	usbnet_isowned_core(un);
+	usbnet_isowned_mii(un);
 	(*un->un_ops->uno_statchg)(ifp);
 }
 
@@ -863,10 +863,10 @@ usbnet_init_rx_tx(struct usbnet * const un)
 	usbnet_rx_start_pipes(un);
 
 	/* Kick off the watchdog/stats/mii tick.  */
-	mutex_enter(&unp->unp_core_lock);
+	mutex_enter(&unp->unp_miilock);
 	unp->unp_stopped = false;
 	callout_schedule(&unp->unp_stat_ch, hz);
-	mutex_exit(&unp->unp_core_lock);
+	mutex_exit(&unp->unp_miilock);
 
 out:
 	if (error) {
@@ -880,9 +880,9 @@ out:
 	 * here as an active link.
 	 */
 	if (un->un_ops->uno_statchg == NULL) {
-		mutex_enter(&unp->unp_core_lock);
+		mutex_enter(&unp->unp_miilock);
 		usbnet_set_link(un, error == 0);
-		mutex_exit(&unp->unp_core_lock);
+		mutex_exit(&unp->unp_miilock);
 	}
 
 	return error;
@@ -897,8 +897,8 @@ usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 	struct usbnet * const un = device_private(dev);
 	int err;
 
-	/* MII layer ensures core_lock is held. */
-	usbnet_isowned_core(un);
+	/* MII layer ensures miilock is held. */
+	usbnet_isowned_mii(un);
 
 	if (usbnet_isdying(un)) {
 		return EIO;
@@ -921,8 +921,8 @@ usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 	struct usbnet * const un = device_private(dev);
 	int err;
 
-	/* MII layer ensures core_lock is held. */
-	usbnet_isowned_core(un);
+	/* MII layer ensures miilock is held. */
+	usbnet_isowned_mii(un);
 
 	if (usbnet_isdying(un)) {
 		return EIO;
@@ -944,8 +944,8 @@ usbnet_mii_statchg(struct ifnet *ifp)
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
 
-	/* MII layer ensures core_lock is held. */
-	usbnet_isowned_core(un);
+	/* MII layer ensures miilock is held. */
+	usbnet_isowned_mii(un);
 
 	uno_mii_statchg(un, ifp);
 }
@@ -958,8 +958,8 @@ usbnet_media_upd(struct ifnet *ifp)
 	struct usbnet_private * const unp = un->un_pri;
 	struct mii_data * const mii = usbnet_mii(un);
 
-	/* ifmedia layer ensures core_lock is held. */
-	usbnet_isowned_core(un);
+	/* ifmedia layer ensures miilock is held. */
+	usbnet_isowned_mii(un);
 
 	/* ifmedia changes only with IFNET_LOCK held.  */
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
@@ -1083,9 +1083,9 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
 	 * clear the watchdog timer.
 	 */
-	mutex_enter(&unp->unp_core_lock);
+	mutex_enter(&unp->unp_miilock);
 	unp->unp_stopped = true;
-	mutex_exit(&unp->unp_core_lock);
+	mutex_exit(&unp->unp_miilock);
 
 	mutex_enter(&unp->unp_rxlock);
 	unp->unp_rxstopped = true;
@@ -1111,9 +1111,9 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 * state machine down.
 	 */
 	if (mii) {
-		mutex_enter(&unp->unp_core_lock);
+		mutex_enter(&unp->unp_miilock);
 		mii_down(mii);
-		mutex_exit(&unp->unp_core_lock);
+		mutex_exit(&unp->unp_miilock);
 	}
 
 	/* Stop transfers. */
@@ -1225,7 +1225,7 @@ usbnet_tick_task(void *arg)
 	/* Call driver if requested. */
 	uno_tick(un);
 
-	mutex_enter(&unp->unp_core_lock);
+	mutex_enter(&unp->unp_miilock);
 	DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
 	if (mii) {
 		mii_tick(mii);
@@ -1235,7 +1235,7 @@ usbnet_tick_task(void *arg)
 
 	if (!unp->unp_stopped && !usbnet_isdying(un))
 		callout_schedule(&unp->unp_stat_ch, hz);
-	mutex_exit(&unp->unp_core_lock);
+	mutex_exit(&unp->unp_miilock);
 }
 
 static int
@@ -1280,7 +1280,7 @@ usbnet_if_init(struct ifnet *ifp)
 void
 usbnet_set_link(struct usbnet *un, bool link)
 {
-	usbnet_isowned_core(un);
+	usbnet_isowned_mii(un);
 	un->un_pri->unp_link = link;
 }
 
@@ -1390,7 +1390,7 @@ usbnet_attach(struct usbnet *un)
 
 	mutex_init(&unp->unp_txlock, MUTEX_DEFAULT, IPL_SOFTUSB);
 	mutex_init(&unp->unp_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB);
-	mutex_init(&unp->unp_core_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&unp->unp_miilock, MUTEX_DEFAULT, IPL_NONE);
 	mutex_init(&unp->unp_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 
 	rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
@@ -1427,7 +1427,7 @@ usbnet_attach_mii(struct usbnet *un, const struct usbnet_mii *unm)
 
 	usbnet_ec(un)->ec_mii = mii;
 	ifmedia_init_with_lock(&mii->mii_media, 0,
-	    usbnet_media_upd, ether_mediastatus, &unp->unp_core_lock);
+	    usbnet_media_upd, ether_mediastatus, &unp->unp_miilock);
 	mii_attach(un->un_dev, mii, unm->un_mii_capmask, unm->un_mii_phyloc,
 	    unm->un_mii_offset, unm->un_mii_flags);
 
@@ -1556,7 +1556,7 @@ usbnet_detach(device_t self, int flags)
 	rnd_detach_source(&unp->unp_rndsrc);
 
 	mutex_destroy(&unp->unp_mcastlock);
-	mutex_destroy(&unp->unp_core_lock);
+	mutex_destroy(&unp->unp_miilock);
 	mutex_destroy(&unp->unp_rxlock);
 	mutex_destroy(&unp->unp_txlock);
 
@@ -1593,9 +1593,9 @@ usbnet_activate(device_t self, devact_t act)
 
 		atomic_store_relaxed(&unp->unp_dying, true);
 
-		mutex_enter(&unp->unp_core_lock);
+		mutex_enter(&unp->unp_miilock);
 		unp->unp_stopped = true;
-		mutex_exit(&unp->unp_core_lock);
+		mutex_exit(&unp->unp_miilock);
 
 		mutex_enter(&unp->unp_rxlock);
 		unp->unp_rxstopped = true;

From 240b67937e4fbe20c4a37faaf9f234abb3942d98 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sat, 20 Aug 2022 10:51:50 +0000
Subject: [PATCH 10/12] usbnet(9): New usbnet_ispromisc(un).

Replaces ifp->if_flags & IFF_PROMISC in multicast filter updates.
---
 share/man/man9/usbnet.9 | 16 ++++++++++++++++
 sys/dev/usb/if_aue.c    |  4 ++--
 sys/dev/usb/if_axe.c    |  2 +-
 sys/dev/usb/if_axen.c   |  2 +-
 sys/dev/usb/if_cue.c    |  8 ++++----
 sys/dev/usb/if_kue.c    |  4 ++--
 sys/dev/usb/if_mos.c    |  2 +-
 sys/dev/usb/if_mue.c    |  4 ++--
 sys/dev/usb/if_smsc.c   |  2 +-
 sys/dev/usb/if_udav.c   |  4 ++--
 sys/dev/usb/if_ure.c    |  2 +-
 sys/dev/usb/if_url.c    |  2 +-
 sys/dev/usb/usbnet.c    | 28 +++++++++++++++++++++++++++-
 sys/dev/usb/usbnet.h    |  1 +
 14 files changed, 62 insertions(+), 19 deletions(-)

diff --git a/share/man/man9/usbnet.9 b/share/man/man9/usbnet.9
index c23157073660..21e3ff59f3d6 100644
--- a/share/man/man9/usbnet.9
+++ b/share/man/man9/usbnet.9
@@ -47,6 +47,8 @@
 .Fn usbnet_softc "struct usbnet *un"
 .Ft bool
 .Fn usbnet_havelink "struct usbnet *un"
+.Ft int
+.Fn usbnet_ispromisc "struct usbnet *un"
 .Ft bool
 .Fn usbnet_isdying "struct usbnet *un"
 .Ft void
@@ -243,6 +245,20 @@ Returns pointer to this
 device softc.
 .It Fn usbnet_havelink un
 Returns true if link is active.
+.It Fn usbnet_ispromisc un
+True if
+.Dv IFF_PROMISC
+is enabled, false if not.
+.Pp
+May be used only in
+.Dq uno_init
+and
+.Dq uno_mcast .
+.Pp
+Drivers must use this in
+.Dq uno_mcast
+instead of reading
+.Li ifp->if_flags .
 .It Fn usbnet_isdying un
 Returns true if device is dying (has been pulled or deactivated,
 pending detach).
diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 57b06a472546..355be8a7ca8a 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -619,7 +619,7 @@ aue_uno_mcast(struct ifnet *ifp)
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGSN(5, "aue%jd: enter", device_unit(un->un_dev), 0, 0, 0);
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ETHER_LOCK(ec);
 allmulti:
 		ec->ec_flags |= ETHER_F_ALLMULTI;
@@ -962,7 +962,7 @@ aue_uno_init(struct ifnet *ifp)
 		aue_csr_write_1(sc, AUE_PAR0 + i, eaddr[i]);
 
 	 /* If we want promiscuous mode, set the allframes bit. */
-	if (ifp->if_flags & IFF_PROMISC)
+	if (usbnet_ispromisc(un))
 		AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
 	else
 		AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 9b0b76d05151..d55cda1c0d47 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -452,7 +452,7 @@ axe_uno_mcast(struct ifnet *ifp)
 	    ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC | AXE_RXCMD_MULTICAST);
 
 	ETHER_LOCK(ec);
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ec->ec_flags |= ETHER_F_ALLMULTI;
 		ETHER_UNLOCK(ec);
 		/* run promisc. mode */
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 4ab976dd1cfa..b02a641af9b0 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -249,7 +249,7 @@ axen_uno_mcast(struct ifnet *ifp)
 	rxmode &= ~(AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_PROMISC |
 	    AXEN_RXCTL_ACPT_MCAST);
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		DPRINTF(("%s: promisc\n", device_xname(un->un_dev)));
 		rxmode |= AXEN_RXCTL_PROMISC;
 allmulti:
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index e46981457228..1574c9155ee3 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -366,10 +366,10 @@ cue_uno_mcast(struct ifnet *ifp)
 	struct ether_multistep	step;
 	uint32_t		h, i;
 
-	DPRINTFN(2,("%s: cue_setiff if_flags=%#x\n",
-	    device_xname(un->un_dev), ifp->if_flags));
+	DPRINTFN(2,("%s: cue_setiff promisc=%d\n",
+	    device_xname(un->un_dev), usbnet_ispromisc(un)));
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ETHER_LOCK(ec);
 allmulti:
 		ec->ec_flags |= ETHER_F_ALLMULTI;
@@ -636,7 +636,7 @@ cue_uno_init(struct ifnet *ifp)
 
 	/* Enable RX logic. */
 	ctl = CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON;
-	if (ifp->if_flags & IFF_PROMISC)
+	if (usbnet_ispromisc(un))
 		ctl |= CUE_ETHCTL_PROMISC;
 	cue_csr_write_1(un, CUE_ETHCTL, ctl);
 
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 50411164221e..fa441f45b62b 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -330,12 +330,12 @@ kue_uno_mcast(struct ifnet *ifp)
 	DPRINTFN(5,("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
 	 /* If we want promiscuous mode, set the allframes bit. */
-	if (ifp->if_flags & IFF_PROMISC)
+	if (usbnet_ispromisc(un))
 		sc->kue_rxfilt |= KUE_RXFILT_PROMISC;
 	else
 		sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC;
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ETHER_LOCK(ec);
 allmulti:
 		ec->ec_flags |= ETHER_F_ALLMULTI;
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 2f64a19ffcf9..c9e6875dcc63 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -477,7 +477,7 @@ mos_uno_mcast(struct ifnet *ifp)
 	rxmode &= ~(MOS_CTL_ALLMULTI | MOS_CTL_RX_PROMISC);
 
 	ETHER_LOCK(ec);
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ec->ec_flags |= ETHER_F_ALLMULTI;
 		ETHER_UNLOCK(ec);
 		/* run promisc. mode */
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index e19576151ae5..a0bf825e8457 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1028,11 +1028,11 @@ mue_uno_mcast(struct ifnet *ifp)
 	rxfilt |= MUE_RFE_CTL_BROADCAST;
 
 	ETHER_LOCK(ec);
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		rxfilt |= MUE_RFE_CTL_UNICAST;
 allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 		ec->ec_flags |= ETHER_F_ALLMULTI;
-		if (ifp->if_flags & IFF_PROMISC)
+		if (usbnet_ispromisc(un))
 			DPRINTF(un, "promisc\n");
 		else
 			DPRINTF(un, "allmulti\n");
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 305781e53517..74c2b57669dd 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -425,7 +425,7 @@ smsc_uno_mcast(struct ifnet *ifp)
 	if (usbnet_isdying(un))
 		return;
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ETHER_LOCK(ec);
 allmulti:
 		ec->ec_flags |= ETHER_F_ALLMULTI;
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index add37bc874ac..98bac3bf3fb9 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -482,7 +482,7 @@ udav_uno_init(struct ifnet *ifp)
 	UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
 
 	/* If we want promiscuous mode, accept all physical frames. */
-	if (ifp->if_flags & IFF_PROMISC)
+	if (usbnet_ispromisc(un))
 		UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 	else
 		UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
@@ -576,7 +576,7 @@ udav_uno_mcast(struct ifnet *ifp)
 		return;
 	}
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ETHER_LOCK(ec);
 		ec->ec_flags |= ETHER_F_ALLMULTI;
 		ETHER_UNLOCK(ec);
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index f05d5b4f75d0..42d359db0d1f 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -352,7 +352,7 @@ ure_uno_mcast(struct ifnet *ifp)
 	/* continue to accept my own DA and bcast frames */
 
 	ETHER_LOCK(ec);
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ec->ec_flags |= ETHER_F_ALLMULTI;
 		ETHER_UNLOCK(ec);
 		/* run promisc. mode */
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 7a51a59f75cf..36624a9f8f1c 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -433,7 +433,7 @@ url_uno_mcast(struct ifnet *ifp)
 	rcr &= ~(URL_RCR_AAP | URL_RCR_AAM | URL_RCR_AM);
 
 	ETHER_LOCK(ec);
-	if (ifp->if_flags & IFF_PROMISC) {
+	if (usbnet_ispromisc(un)) {
 		ec->ec_flags |= ETHER_F_ALLMULTI;
 		ETHER_UNLOCK(ec);
 		/* run promisc. mode */
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index f763973336ab..8724875a27fd 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -994,8 +994,22 @@ usbnet_ifflags_cb(struct ethercom *ec)
 
 	const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
 	if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) {
+		mutex_enter(&unp->unp_mcastlock);
 		unp->unp_if_flags = ifp->if_flags;
-		if ((changed & IFF_PROMISC) != 0)
+		mutex_exit(&unp->unp_mcastlock);
+		/*
+		 * XXX Can we just do uno_mcast synchronously here
+		 * instead of resetting the whole interface?
+		 *
+		 * Not yet, because some usbnet drivers (e.g., aue(4))
+		 * initialize the hardware differently in uno_init
+		 * depending on IFF_PROMISC.  But some (again, aue(4))
+		 * _also_ need to know whether IFF_PROMISC is set in
+		 * uno_mcast and do something different with it there.
+		 * Maybe the logic can be unified, but it will require
+		 * an audit and testing of all the usbnet drivers.
+		 */
+		if (changed & IFF_PROMISC)
 			rv = ENETRESET;
 	} else {
 		rv = ENETRESET;
@@ -1004,6 +1018,18 @@ usbnet_ifflags_cb(struct ethercom *ec)
 	return rv;
 }
 
+bool
+usbnet_ispromisc(struct usbnet *un)
+{
+	struct ifnet * const ifp = usbnet_ifp(un);
+	struct usbnet_private * const unp = un->un_pri;
+
+	KASSERTMSG(mutex_owned(&unp->unp_mcastlock) || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
+
+	return unp->unp_if_flags & IFF_PROMISC;
+}
+
 static int
 usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 1ffd3bf86374..3c6dd5dd763a 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -285,6 +285,7 @@ void *usbnet_softc(struct usbnet *);
 
 bool usbnet_havelink(struct usbnet *);
 bool usbnet_isdying(struct usbnet *);
+bool usbnet_ispromisc(struct usbnet *);
 
 /*
  * Endpoint / rx/tx chain management:

From 948230067873df1393f66be7d8721e3a21915ddd Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sat, 20 Aug 2022 10:52:27 +0000
Subject: [PATCH 11/12] cue(4): Prune dead branch: IFF_BROADCAST is always set
 here.

---
 sys/dev/usb/if_cue.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 1574c9155ee3..7cef84486570 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -405,10 +405,8 @@ allmulti:
 	 * Also include the broadcast address in the filter
 	 * so we can receive broadcast frames.
 	 */
-	if (ifp->if_flags & IFF_BROADCAST) {
-		h = cue_crc(etherbroadcastaddr);
-		sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
-	}
+	h = cue_crc(etherbroadcastaddr);
+	sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
 
 	cue_mem(un, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
 	    &sc->cue_mctab, CUE_MCAST_TABLE_LEN);

From dfa361fe7f6f9b4262ea80180e96d6946598c645 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sat, 20 Aug 2022 10:53:37 +0000
Subject: [PATCH 12/12] udav(4): Prune dead branch: legacy IFF_ALLMULTI is
 never set here.

---
 sys/dev/usb/if_udav.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 98bac3bf3fb9..cc6389458dbc 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -582,14 +582,6 @@ udav_uno_mcast(struct ifnet *ifp)
 		ETHER_UNLOCK(ec);
 		UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 		return;
-	} else if (ifp->if_flags & IFF_ALLMULTI) { /* XXX ??? Can't happen? */
-		ETHER_LOCK(ec);
-allmulti:
-		ec->ec_flags |= ETHER_F_ALLMULTI;
-		ETHER_UNLOCK(ec);
-		UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL);
-		UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_PRMSC);
-		return;
 	}
 
 	/* first, zot all the existing hash bits */
@@ -603,7 +595,11 @@ allmulti:
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
 		    ETHER_ADDR_LEN) != 0) {
-			goto allmulti;
+			ec->ec_flags |= ETHER_F_ALLMULTI;
+			ETHER_UNLOCK(ec);
+			UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL);
+			UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_PRMSC);
+			return;
 		}
 
 		h = UDAV_CALCHASH(enm->enm_addrlo);