tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

usbnet improvements



The attached patch set fixes a deadlock in usbnet and considerably
simplifies the API.  This changes both the API and ABI so it'll
require a kernel bump.  Attached is an incremental patch set to make
review and bisection easier, and a giant diff for testing.

Review and testing welcome!

I have successfully tested aue(4), ure(4), and url(4), with dhcpcd,
mdnsd, ifconfig down up in a loop, and detaching/reattaching
physically, all at the same time.  (I also tested ping and data
transfer too!  But mostly this patch set affects the init/stop paths,
not the tx/rx paths.)  So far I haven't won the game of `crash or
deadlock the system' this way!

It wouldn't hurt to test more of those devices, of course, but I also
don't have (working) devices handy for the following:

axe(4), axen(4), cdce(4), cue(4), kue(4), mos(4), mue(4), smsc(4),
udav(4), upl(4), urndis(4)


DEADLOCK.  The deadlock happens when a device such as ure(4) is
unplugged:

1. usbnet_detach holds the usbnet core lock, awaits softclock `lock'
   (i.e., softints running sequentially at each level on a single CPU)
   for callout to wake kpause in ure_reset

2. callout holds softclock `lock', awaits softnet_lock in
   tcp_timer_keep, frag6_fasttimo, &c.

3. soclose holds softnet_lock, awaits usbnet core lock in ure_ioctl
   for SIOCDELMULTI

This patch set breaks the deadlock by handling hardware multicast
filter updates for SIOCADDMULTI/SIOCDELMULTI through a new usbnet
driver operation, uno_mcast, rather than through uno_ioctl, which
doesn't need the usbnet core lock or IFNET_LOCK.

The job of uno_mcast is to commit the ethersubr(9) multicast address
list to any hardware filter.  usbnet(9) serializes calls to uno_mcast,
and only while the interface is up.  After uno_stop, SIOCADDMULTI and
SIOCDELMULTI will still update the ethersubr(9) list, but usbnet(9)
won't bother calling uno_mcast until the next if_init, at which time
it will atomically call uno_mcast and begin accepting concurrent
multicast updates.

Note: uno_mcast is issued synchronously.  However, the patch set makes
it asynchronous temporarily, so we can pull it up to netbsd-9, because
a delayed multicast filter update is better than a deadlock; later in
the patch set the synchronous updates are restored with a different
locking ABI.

(Could the deadlock have been done more simply, by teaching the
various *_uno_ioctl routines to not take the usbnet core lock for
SIOCADDMULTI/SIOCDELMULTI?  They could do that -- but then they race
with init/stop, which is a whole nother can of worms.  This patch set
addresses that in a much more regimented flow of driver callbacks.)


API CHANGES.  There is now a rigid timeline on which usbnet(9) issues
driver callbacks (time flows downward; comma means `may happen in
parallel'; => means `invoked synchronously as a subroutine'):

usbnet_attach
(driver has exclusive access to everything here, no locking needed)
usbnet_attach_ifp
uno_ioctl, uno_read_reg or uno_write_reg or uno_statchg
ifp->if_init
    => uno_init
    => uno_mcast
uno_tx_prepare, uno_rx_loop, uno_tick, uno_intr, uno_ioctl, uno_mcast,
  uno_read_reg or uno_write_reg or uno_statchg
ifp->if_stop
    => uno_stop
uno_ioctl, uno_read_reg or uno_write_reg or uno_statchg
usbnet_detach

Notes:

- Repeated if_init while IFF_RUNNING will be ignored, so uno_init is
  only called on the interface-up transition from !IFF_RUNNING to
  IFF_RUNNING, and not again until after stopping.

- Similarly, repeated if_stop while !IFF_RUNNING will be ignored, so
  uno_stop is only called on the interface-down transition from
  IFF_RUNNING to !IFF_RUNNING (and then only when the device is not
  detaching -- there's no point in trying to write device registers to
  reset the hardware if it's physically gone!), and not again until
  after reinitialization.

- uno_init, uno_stop, and uno_ioctl are always called with IFNET_LOCK
  held, the long-term interface configuration lock.

- Each of

  . uno_tx_prepare
  . uno_rx_loop
  . uno_tick
  . uno_intr
  . uno_ioctl
  . uno_mcast
  . uno_read_reg or uno_write_reg or uno_statchg

  independently will only ever be called in serial -- that is, e.g.,
  there will never be two concurrent calls to uno_mcast for the same
  usbnet device, or two concurrent calls to uno_rx_loop.  But there
  may be concurrent calls to uno_rx_loop and uno_mcast -- one of each.

- The MII callbacks uno_read_reg, uno_write_reg, and uno_statchg are
  serialized among themselves -- there is never an uno_read_reg and
  uno_write_reg at the same time on the same usbnet device, for
  instance.  However, they may run in parallel with anything else,
  except uno_init or uno_stop, at any time between usbnet_attach_ifp
  and usbnet_detach, irrespective of whether the interface is up or
  down.


API DELETIONS.  The following usbnet API functions are now gone:

- usbnet_set_dying -- only one driver, url(4), ever used this, and
  only on attach failure, which usbnet already gracefully handles

- usbnet_lock_{core,tx,rx} &c. -- these locks are now internal to
  usbnet.c; drivers have no need to take them

- usbnet_busy, usbnet_unbusy -- nothing relied on usbnet reference
  counting; usbnet waits for the users of all resources to drain by
  some other mechanism -- callout_halt, usb_rem_task_wait, if_detach,
  &c. -- before destroying them, following the rigid timeline.

- usbnet_init_rx_tx -- now handled internally by usbnet's if_init
  function; this only contained driver-independent logic to set up
  pipes and callouts and, after changes for the hardware multicast
  filter updates, was used the same way by all drivers as the last
  thing done by uno_init.

- usbnet_stop -- now handled internally by usbnet's if_stop function;
  contained driver-independent logic to tear down pipes and callouts,
  along with a call to uno_stop.  The driver-independent logic now
  rigidly follows the timeline above; there is no need for drivers to
  invoke it directly.


BUG IN MUE(4). Currently it is possible for uno_ioctl and uno_mcast to
run concurrently.  This means there is a bug in mue(4), because they
touch the same registers.  Two alternatives are:

1. Make uno_ioctl take the same lock inside usbnet as uno_mcast.

   => I'm reluctant because I don't know if some ioctls might
      reasonably be blocking, which is not allowed for
      SIOCADDMULTI/SIOCDELMULTI to wait for.  But currently I don't
      think this poses a problem with the usbnet drivers in tree --
      I'm just worried about deadlocks introduced by future drivers
      with fancier ioctls.

2. Create a new lock inside mue(4) for mue_uno_cast and
   mue_sethwcsum_locked.

   => Makes things a little more complicated in the driver -- but the
      complication of additional interactions is limited to this
      driver, so maybe not so bad.

Preferences?


BUG IN SMSC(4).  Currently there is a minor bug in smsc(4) because
smsc_setoe_locked, which services SIOCSIFCAP to enable or disable
hardware checksumming, races with smsc_uno_rx_loop over
sc->sc_coe_ctrl.  Here are two way we might address this:

1. Create a lock that smsc_setoe_locked and smsc_uno_rx_loop agree on
   to cover sc->sc_coe_ctrl or manage it with atomics, and if the rx
   already happened (because the hardware packet delivery isn't
   beholden to any kmutex_t we create), just accept that we might lose
   some packets to checksum errors.

2. Make SIOCSIFCAP take the interface down, set the checksum cap, and
   then bring the interface back up again.

I'm inclined to prefer option (1); option (2) means we'll still drop
some packets -- probably more! -- and it's more intrusive into the
flow of logic.

Preferences?
From e24059791cc751bb80c37ad6ef62b4d2467856cd Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 00:21:10 +0000
Subject: [PATCH 01/75] usbnet: Defer hardware multicast filter updates to USB
 task.

Breaks deadlock:

- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
  wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI

This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.

With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.

This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession.  This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
---
 sys/dev/usb/usbnet.c | 98 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 95 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 1712d2f687f0..9fa061b3cea0 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -72,6 +72,7 @@ struct usbnet_private {
 
 	struct ethercom		unp_ec;
 	struct mii_data		unp_mii;
+	struct usb_task		unp_mcasttask;
 	struct usb_task		unp_ticktask;
 	struct callout		unp_stat_ch;
 	struct usbd_pipe	*unp_ep[USBNET_ENDPT_MAX];
@@ -1035,12 +1036,64 @@ usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		return uno_override_ioctl(un, ifp, cmd, data);
 
 	error = ether_ioctl(ifp, cmd, data);
-	if (error == ENETRESET)
-		error = uno_ioctl(un, ifp, cmd, data);
+	if (error == ENETRESET) {
+		switch (cmd) {
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+			usb_add_task(un->un_udev, &unp->unp_mcasttask,
+			    USB_TASKQ_DRIVER);
+			error = 0;
+			break;
+		default:
+			error = uno_ioctl(un, ifp, cmd, data);
+		}
+	}
 
 	return error;
 }
 
+static void
+usbnet_mcast_task(void *arg)
+{
+	USBNETHIST_FUNC();
+	struct usbnet * const un = arg;
+	struct usbnet_private * const unp = un->un_pri;
+	struct ifnet * const ifp = usbnet_ifp(un);
+	bool dying;
+	struct ifreq ifr;
+
+	USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
+
+	/*
+	 * If we're detaching, we must check unp_dying _before_
+	 * touching IFNET_LOCK -- the ifnet may have been detached by
+	 * the time this task runs.  This is racy -- unp_dying may be
+	 * set immediately after we test it -- but nevertheless safe,
+	 * because usbnet_detach waits for the task to complete before
+	 * issuing if_detach, and necessary, so that we don't touch
+	 * IFNET_LOCK after if_detach.  See usbnet_detach for details.
+	 */
+	mutex_enter(&unp->unp_core_lock);
+	dying = unp->unp_dying;
+	mutex_exit(&unp->unp_core_lock);
+	if (dying)
+		return;
+
+	/*
+	 * Pass a bogus ifr with SIOCDELMULTI -- the goal is to just
+	 * notify the driver to reprogram any hardware multicast
+	 * filter, according to what's already stored in the ethercom.
+	 * None of the drivers actually examine this argument, so it
+	 * doesn't change the ABI as far as they can tell.
+	 */
+	IFNET_LOCK(ifp);
+	if (ifp->if_flags & IFF_RUNNING) {
+		memset(&ifr, 0, sizeof(ifr));
+		(void)uno_ioctl(un, ifp, SIOCDELMULTI, &ifr);
+	}
+	IFNET_UNLOCK(ifp);
+}
+
 /*
  * Generic stop network function:
  *	- mark as stopping
@@ -1373,7 +1426,10 @@ usbnet_attach(struct usbnet *un,
 	un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP);
 	struct usbnet_private * const unp = un->un_pri;
 
-	usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un, USB_TASKQ_MPSAFE);
+	usb_init_task(&unp->unp_mcasttask, usbnet_mcast_task, un,
+	    USB_TASKQ_MPSAFE);
+	usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un,
+	    USB_TASKQ_MPSAFE);
 	callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE);
 	callout_setfunc(&unp->unp_stat_ch, usbnet_tick, un);
 
@@ -1506,6 +1562,8 @@ usbnet_detach(device_t self, int flags)
 	callout_halt(&unp->unp_stat_ch, NULL);
 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
 	    NULL);
+	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
+	    NULL);
 
 	mutex_enter(&unp->unp_core_lock);
 	unp->unp_refcnt--;
@@ -1534,6 +1592,40 @@ usbnet_detach(device_t self, int flags)
 	}
 	usbnet_ec(un)->ec_mii = NULL;
 
+	/*
+	 * We have already waited for the multicast task to complete.
+	 * Unfortunately, until if_detach, nothing has prevented it
+	 * from running again -- another thread might issue if_mcast_op
+	 * between the time of our first usb_rem_task_wait and the time
+	 * we actually get around to if_detach.
+	 *
+	 * Fortunately, the first usb_rem_task_wait ensures that if the
+	 * task is scheduled again, it will witness our setting of
+	 * unp_dying to true[*].  So after that point, if the task is
+	 * scheduled again, it will decline to touch IFNET_LOCK and do
+	 * nothing.  But we still need to wait for it to complete.
+	 *
+	 * It would be nice if we could write
+	 *
+	 *	if_pleasestopissuingmcastopsthanks(ifp);
+	 *	usb_rem_task_wait(..., &unp->unp_mcasttask, ...);
+	 *	if_detach(ifp);
+	 *
+	 * and then we would need only one usb_rem_task_wait.
+	 *
+	 * Unfortunately, there is no such operation available in
+	 * sys/net at the moment, and it would require a bit of
+	 * coordination with if_mcast_op and doifioctl probably under a
+	 * new lock.  So we'll use this kludge until that mechanism is
+	 * invented.
+	 *
+	 * [*] This is not exactly a documented property of the API,
+	 * but it is implied by the single lock in the task queue
+	 * serializing changes to the task state.
+	 */
+	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
+	    NULL);
+
 	cv_destroy(&unp->unp_detachcv);
 	mutex_destroy(&unp->unp_core_lock);
 	mutex_destroy(&unp->unp_rxlock);

From 020e1a8b0d96d7ec05f571b09f0ce63fdbca59da Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 22:10:56 +0000
Subject: [PATCH 02/75] usbnet: Simplify usbnet_isdying.

usbnet_detach (or its caller) stops all users before it returns.

If un->un_pri is null at this point, there's a bug -- something
didn't wait for everything to finish before calling usbnet_detach.
---
 sys/dev/usb/usbnet.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 9fa061b3cea0..9c694b588346 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1322,7 +1322,7 @@ usbnet_havelink(struct usbnet *un)
 bool
 usbnet_isdying(struct usbnet *un)
 {
-	return un->un_pri == NULL || un->un_pri->unp_dying;
+	return un->un_pri->unp_dying;
 }
 
 

From 6b4d05adfbc53e6684b13b1fe51a8c103177689e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 22:56:01 +0000
Subject: [PATCH 03/75] usbnet: Set and clear IFF_RUNNING slightly earlier and
 later.

- Set IFF_RUNNING before any calls to usbnet_rxeof are possible.
- Don't clear IFF_RUNNING until all transfers have been aborted.
---
 sys/dev/usb/usbnet.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 9c694b588346..1f58ff87f1d3 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -850,9 +850,6 @@ usbnet_init_rx_tx(struct usbnet * const un)
 		goto out;
 	}
 
-	/* Start up the receive pipe(s). */
-	usbnet_rx_start_pipes(un);
-
 	/* Indicate we are up and running. */
 #if 0
 	/* XXX if_mcast_op() can call this without ifnet locked */
@@ -860,6 +857,9 @@ usbnet_init_rx_tx(struct usbnet * const un)
 #endif
 	ifp->if_flags |= IFF_RUNNING;
 
+	/* Start up the receive pipe(s). */
+	usbnet_rx_start_pipes(un);
+
 	callout_schedule(&unp->unp_stat_ch, hz);
 
 out:
@@ -1126,14 +1126,6 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 
 	uno_stop(un, ifp, disable);
 
-	/*
-	 * XXXSMP Would like to
-	 *	KASSERT(IFNET_LOCKED(ifp))
-	 * here but the locking order is:
-	 *	ifnet -> core_lock -> rxlock -> txlock
-	 * and core_lock is already held.
-	 */
-	ifp->if_flags &= ~IFF_RUNNING;
 	unp->unp_timer = 0;
 
 	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
@@ -1150,6 +1142,15 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	/* Close pipes. */
 	usbnet_ep_close_pipes(un);
 
+	/*
+	 * XXXSMP Would like to
+	 *	KASSERT(IFNET_LOCKED(ifp))
+	 * here but the locking order is:
+	 *	ifnet -> core_lock -> rxlock -> txlock
+	 * and core_lock is already held.
+	 */
+	ifp->if_flags &= ~IFF_RUNNING;
+
 	usbnet_unbusy(un);
 }
 

From 0b26f18a099f9230d561d9be78a99e61ccea1671 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 22:59:10 +0000
Subject: [PATCH 04/75] usbnet: Take IFNET_LOCK around access to if_flags in
 usbnet_detach.

This is not stable without IFNET_LOCK.  Extraneous calls to
usbnet_stop arising from this race might be harmless, but let's
render it unnecessary to even think about that.
---
 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 1f58ff87f1d3..d35385f1cdbd 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1554,11 +1554,11 @@ usbnet_detach(device_t self, int flags)
 	unp->unp_dying = true;
 	mutex_exit(&unp->unp_core_lock);
 
+	IFNET_LOCK(ifp);
 	if (ifp->if_flags & IFF_RUNNING) {
-		IFNET_LOCK(ifp);
 		usbnet_if_stop(ifp, 1);
-		IFNET_UNLOCK(ifp);
 	}
+	IFNET_UNLOCK(ifp);
 
 	callout_halt(&unp->unp_stat_ch, NULL);
 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,

From 29f96ceaf28641b3ff57f7698d241484d140f7f6 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 23:01:27 +0000
Subject: [PATCH 05/75] usbnet: Ensure ifp->if_softc is initialized _before_
 publishing ifp.

Otherwise other parts of the system might start using ifp the moment
we if_register it, and trip over null if_softc.
---
 sys/dev/usb/usbnet.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index d35385f1cdbd..83fcf11b7ebc 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -80,6 +80,7 @@ struct usbnet_private {
 	bool			unp_dying;
 	bool			unp_stopping;
 	bool			unp_attached;
+	bool			unp_ifp_attached;
 	bool			unp_link;
 
 	int			unp_refcnt;
@@ -1492,7 +1493,9 @@ usbnet_attach_ifp(struct usbnet *un,
 	struct ifnet * const ifp = usbnet_ifp(un);
 
 	KASSERT(unp->unp_attached);
+	KASSERT(!unp->unp_ifp_attached);
 
+	ifp->if_softc = un;
 	strlcpy(ifp->if_xname, device_xname(un->un_dev), IFNAMSIZ);
 	ifp->if_flags = if_flags;
 	ifp->if_extflags = IFEF_MPSAFE | if_extflags;
@@ -1511,6 +1514,7 @@ usbnet_attach_ifp(struct usbnet *un,
 	if (ifp->_if_input == NULL)
 		ifp->if_percpuq = if_percpuq_create(ifp);
 	if_register(ifp);
+	unp->unp_ifp_attached = true;
 
 	/*
 	 * If ethernet address is all zero, skip ether_ifattach() and
@@ -1528,7 +1532,6 @@ usbnet_attach_ifp(struct usbnet *un,
 
 	/* Now ready, and attached. */
 	IFQ_SET_READY(&ifp->if_snd);
-	ifp->if_softc = un;
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev);
 
@@ -1584,7 +1587,7 @@ usbnet_detach(device_t self, int flags)
 		mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
 		ifmedia_fini(&mii->mii_media);
 	}
-	if (ifp->if_softc) {
+	if (unp->unp_ifp_attached) {
 		if (!usbnet_empty_eaddr(un))
 			ether_ifdetach(ifp);
 		else

From b280ff680210e7862047a67d5817dbd6f36423a0 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 23:08:03 +0000
Subject: [PATCH 06/75] usbnet: Ensure access to unp_timer is protected by
 unp_txlock.

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

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 83fcf11b7ebc..de231d9bd799 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1127,7 +1127,9 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 
 	uno_stop(un, ifp, disable);
 
+	mutex_enter(&unp->unp_txlock);
 	unp->unp_timer = 0;
+	mutex_exit(&unp->unp_txlock);
 
 	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
@@ -1239,7 +1241,10 @@ usbnet_tick_task(void *arg)
 	usbnet_busy(un);
 	mutex_exit(&unp->unp_core_lock);
 
-	if (unp->unp_timer != 0 && --unp->unp_timer == 0)
+	mutex_enter(&unp->unp_txlock);
+	const bool timeout = unp->unp_timer != 0 && --unp->unp_timer == 0;
+	mutex_exit(&unp->unp_txlock);
+	if (timeout)
 		usbnet_watchdog(ifp);
 
 	DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);

From 6ec6a12b39d0c2dbd15c187365b2e82fcaac8190 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 23:34:52 +0000
Subject: [PATCH 07/75] usbnet: Assert IFNET_LOCKED on if_flags change
 callbacks.

- if_init
- if_stop
- ethersubr(9) ifflags_cb
---
 sys/dev/usb/usbnet.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index de231d9bd799..c2b0d55a6bc9 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1006,6 +1006,8 @@ usbnet_ifflags_cb(struct ethercom *ec)
 	struct usbnet_private * const unp = un->un_pri;
 	int rv = 0;
 
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+
 	mutex_enter(&unp->unp_core_lock);
 
 	const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
@@ -1163,6 +1165,8 @@ 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);
+
 	mutex_enter(&unp->unp_core_lock);
 	usbnet_stop(un, ifp, disable);
 	mutex_exit(&unp->unp_core_lock);
@@ -1272,6 +1276,8 @@ usbnet_if_init(struct ifnet *ifp)
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
 
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+
 	return uno_init(un, ifp);
 }
 

From 3d5f1fe36e4b90523619dee4ffb5f0f682d8c906 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 23:52:41 +0000
Subject: [PATCH 08/75] usbnet: Assert IFNET_LOCKED in usbnet_init_rx_tx,
 usbnet_stop.

Exception: urndis(4) abuses this API to start this logic before the
ifp is actually initialized.  So for the sake of urndis(4), until
sense can be beaten into it, allow the !unp_ifp_attached case to run
without IFNET_LOCK.
---
 sys/dev/usb/usbnet.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index c2b0d55a6bc9..9b475f07ff88 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -151,6 +151,8 @@ fail:
 static void
 uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 {
+	KASSERTMSG(!un->un_pri->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	usbnet_isowned_core(un);
 	if (un->un_ops->uno_stop)
 		(*un->un_ops->uno_stop)(ifp, disable);
@@ -820,6 +822,9 @@ usbnet_init_rx_tx(struct usbnet * const un)
 	usbd_status err;
 	int error = 0;
 
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
+
 	usbnet_isowned_core(un);
 
 	if (unp->unp_dying) {
@@ -1117,6 +1122,8 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	usbnet_isowned_core(un);
 
 	usbnet_busy(un);
@@ -1147,13 +1154,9 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	/* Close pipes. */
 	usbnet_ep_close_pipes(un);
 
-	/*
-	 * XXXSMP Would like to
-	 *	KASSERT(IFNET_LOCKED(ifp))
-	 * here but the locking order is:
-	 *	ifnet -> core_lock -> rxlock -> txlock
-	 * and core_lock is already held.
-	 */
+	/* Everything is quesced now. */
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	ifp->if_flags &= ~IFF_RUNNING;
 
 	usbnet_unbusy(un);

From 96fc4b6a31cf5ab7ca6d1131511c0d10171e9c5d Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 22:50:37 +0000
Subject: [PATCH 09/75] usbnet: Don't check if_flags for IFF_RUNNING in
 usbnet_rxeof.

This can only run after we start the pipes in usbnet_init_rx_tx, and
before we abort the pipes in usbnet_stop, during which time if_flags
& IFF_RUNNING is stably set.
---
 sys/dev/usb/usbnet.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 9b475f07ff88..7123c7c95879 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -337,7 +337,6 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	struct usbnet_chain * const c = priv;
 	struct usbnet * const un = c->unc_un;
 	struct usbnet_private * const unp = un->un_pri;
-	struct ifnet * const ifp = usbnet_ifp(un);
 	uint32_t total_len;
 
 	USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
@@ -347,7 +346,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 
 	if (unp->unp_dying || unp->unp_stopping ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
-	    status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING))
+	    status == USBD_CANCELLED)
 		goto out;
 
 	if (status != USBD_NORMAL_COMPLETION) {

From cf99aa9b94195805eb61e8d92f2fdf0e80d95322 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 22:52:49 +0000
Subject: [PATCH 10/75] usbnet: Don't check if_flags for IFF_RUNNING in
 usbnet_pipe_intr.

The one user of this interface in tree, aue(4), doesn't care --
if_statinc is safe whether IFF_RUNNING or not.
---
 sys/dev/usb/usbnet.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 7123c7c95879..846faf9f5404 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -443,11 +443,10 @@ usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	struct usbnet * const un = priv;
 	struct usbnet_private * const unp = un->un_pri;
 	struct usbnet_intr * const uni = un->un_intr;
-	struct ifnet * const ifp = usbnet_ifp(un);
 
 	if (uni == NULL || unp->unp_dying || unp->unp_stopping ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
-	    status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) {
+	    status == USBD_CANCELLED) {
 		USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx",
 		    unp->unp_number, (uintptr_t)uni,
 		    (unp->unp_dying << 8) | unp->unp_stopping, status);

From 53a1dd9c29da08467a5cae1de2ef7a44d3ddb6f2 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 25 Dec 2021 12:52:21 +0000
Subject: [PATCH 11/75] usbnet: Omit needless unp == NULL test in
 usbnet_tick_task.

The task is never scheduled until after un->un_pri is initialized,
and un->un_pri isn't nulled until after the callout and task are
quiescent.
---
 sys/dev/usb/usbnet.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 846faf9f5404..e1dca2a3980e 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1227,9 +1227,6 @@ usbnet_tick_task(void *arg)
 	struct usbnet * const un = arg;
 	struct usbnet_private * const unp = un->un_pri;
 
-	if (unp == NULL)
-		return;
-
 	USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
 
 	mutex_enter(&unp->unp_core_lock);

From 9b371e6461cbb3a63eef68b920d251e49ecb6416 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 25 Dec 2021 20:31:02 +0000
Subject: [PATCH 12/75] axen(4), mue(4), smsc(4): Omit irrelevant cases in
 ioctl.

SIOCSIFFLAGS and SIOCSETHERCAP always end up in ether_ioctl_reinit,
which triggers the same logic to reprogram the multicast filters
anyway.
---
 sys/dev/usb/if_axen.c | 2 --
 sys/dev/usb/if_mue.c  | 2 --
 sys/dev/usb/if_smsc.c | 2 --
 3 files changed, 6 deletions(-)

diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index bf91d56b356d..fe433722d185 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -559,8 +559,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	usbnet_busy(un);
 
 	switch (cmd) {
-	case SIOCSIFFLAGS:
-	case SIOCSETHERCAP:
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
 		axen_setiff_locked(un);
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 559b9eff2569..16a52e4c6ed4 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1273,8 +1273,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	usbnet_busy(un);
 
 	switch (cmd) {
-	case SIOCSIFFLAGS:
-	case SIOCSETHERCAP:
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
 		mue_setiff_locked(un);
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 6cf37e507d1c..12eaf3fa3cc6 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -757,8 +757,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	usbnet_busy(un);
 
 	switch (cmd) {
-	case SIOCSIFFLAGS:
-	case SIOCSETHERCAP:
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
 		smsc_setiff_locked(un);

From f47977214e4479d9b2cfc6c29f2a328d390256bc Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 00:52:09 +0000
Subject: [PATCH 13/75] usbnet: Remove usbnet_set_dying.

Not necessary for the one caller that did it (url(4)): usbnet_detach
handles failed attach just fine without it.
---
 sys/dev/usb/if_url.c | 8 +-------
 sys/dev/usb/usbnet.c | 6 ------
 sys/dev/usb/usbnet.h | 1 -
 3 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 764b9c654682..dbb57bf66ca7 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -256,18 +256,12 @@ url_attach(device_t parent, device_t self, void *aux)
 	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");
-		goto bad;
+		return;
 	}
 
 	/* initialize interface information */
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
 	    0, &unm);
-
-	return;
-
- bad:
-	usbnet_set_dying(un, true);
-	return;
 }
 
 /* read/write memory */
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index e1dca2a3980e..a8f912b0ad54 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1288,12 +1288,6 @@ usbnet_set_link(struct usbnet *un, bool link)
 	un->un_pri->unp_link = link;
 }
 
-void
-usbnet_set_dying(struct usbnet *un, bool link)
-{
-	un->un_pri->unp_dying = link;
-}
-
 struct ifnet *
 usbnet_ifp(struct usbnet *un)
 {
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 15e9dfc7b351..1faa822887c5 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -284,7 +284,6 @@ struct usbnet {
 /* Various accessors. */
 
 void usbnet_set_link(struct usbnet *, bool);
-void usbnet_set_dying(struct usbnet *, bool);
 
 struct ifnet *usbnet_ifp(struct usbnet *);
 struct ethercom *usbnet_ec(struct usbnet *);

From d06385d3455030d5b65c8ca16ae801d7a641addb Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:17:06 +0000
Subject: [PATCH 14/75] usbnet: Fix ordering of actions in usbnet_stop.

Make sure all software activity is quiescent (callouts and tasks,
including ifmedia and mii callbacks -- anything that might trigger
register access) before asking the driver to stop the hardware.  This
way, the driver uno_stop routine is guaranteed exclusive access to
the registers.

This will also enable us to simplify the callouts and tasks so they
don't have to check the software state -- to be done in a separate
commit.
---
 sys/dev/usb/usbnet.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index a8f912b0ad54..1b516402527d 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1132,16 +1132,29 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	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
+	 * timer task from being scheduled again.
+	 */
+	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
+	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
+	    &unp->unp_core_lock);
+
+	/*
+	 * Now that the software is quiescent, ask the driver to stop
+	 * the hardware.  The driver's uno_stop routine now has
+	 * exclusive access to any registers that might previously have
+	 * been used by to ifmedia, mii, or ioctl callbacks.
+	 */
 	uno_stop(un, ifp, disable);
 
+	/* Clear the watchdog timer.  */
 	mutex_enter(&unp->unp_txlock);
 	unp->unp_timer = 0;
 	mutex_exit(&unp->unp_txlock);
 
-	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
-	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
-	    &unp->unp_core_lock);
-
 	/* Stop transfers. */
 	usbnet_ep_stop_pipes(un);
 

From 46d1e507c84a35e5afa48a705c4192c2e33e952b Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:22:46 +0000
Subject: [PATCH 15/75] usbnet: Assert IFNET_LOCKED in usbnet_media_upd.

This ensures, if the device is being initialized or stopped,
usbnet_media_upd will not run until it's done, so the reset sequence
has exclusive access to the device registers used by mii.
---
 sys/dev/usb/usbnet.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 1b516402527d..89041f6ec949 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -983,6 +983,9 @@ usbnet_media_upd(struct ifnet *ifp)
 	/* ifmedia layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
+	/* ifmedia changes only with IFNET_LOCK held.  */
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+
 	if (unp->unp_dying)
 		return EIO;
 

From 2a0ffa7a4b779d8cb776a246941866288780b18b Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:29:16 +0000
Subject: [PATCH 16/75] usbnet: Refuse to bring interfaces back up once dying.

Make this happen uniformly across all usbnet drivers, not on a
per-driver basis.

This ensures new activity on the interface can't happen by the time
we have stopped existing activity and waited for it to complete.
---
 sys/dev/usb/usbnet.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 89041f6ec949..a831fc6eba62 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1289,9 +1289,20 @@ usbnet_if_init(struct ifnet *ifp)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
+	bool dying;
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
+	/*
+	 * Prevent anyone from bringing the interface back up once
+	 * we're detaching.
+	 */
+	mutex_enter(&un->un_pri->unp_core_lock);
+	dying = un->un_pri->unp_dying;
+	mutex_exit(&un->un_pri->unp_core_lock);
+	if (dying)
+		return EIO;
+
 	return uno_init(un, ifp);
 }
 
@@ -1572,10 +1583,18 @@ usbnet_detach(device_t self, int flags)
 	struct ifnet * const ifp = usbnet_ifp(un);
 	struct mii_data * const mii = usbnet_mii(un);
 
+	/*
+	 * Prevent new activity.  After we stop the interface, it
+	 * cannot be brought back up.
+	 */
 	mutex_enter(&unp->unp_core_lock);
 	unp->unp_dying = true;
 	mutex_exit(&unp->unp_core_lock);
 
+	/*
+	 * If we're still running on the network, stop and wait for all
+	 * asynchronous activity to finish.
+	 */
 	IFNET_LOCK(ifp);
 	if (ifp->if_flags & IFF_RUNNING) {
 		usbnet_if_stop(ifp, 1);

From a29d30c747a4861896c0b278bf9a7f6b3a5f2a7d Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:32:42 +0000
Subject: [PATCH 17/75] usbnet: Omit needless callout_halt and
 usb_rem_task_wait.

The callout and tasks cannot be pending at this point -- it is a bug
if usbnet_if_stop failed to quiesce everything, so turn these into
KASSERTs.
---
 sys/dev/usb/usbnet.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index a831fc6eba62..630290b91626 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1601,9 +1601,13 @@ usbnet_detach(device_t self, int flags)
 	}
 	IFNET_UNLOCK(ifp);
 
-	callout_halt(&unp->unp_stat_ch, NULL);
-	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
-	    NULL);
+	/*
+	 * The callout and tick task can't be scheduled anew at this
+	 * point, and usbnet_if_stop has waited for them to complete.
+	 */
+	KASSERT(!callout_pending(&unp->unp_stat_ch));
+	KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask));
+
 	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
 	    NULL);
 

From e83d2cff081dbbfd0d7ba494a04e83f90a9eda43 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:47:10 +0000
Subject: [PATCH 18/75] usbnet: Detach interface and mii before waiting for
 refcnt to drain.

All outstanding software activity under usbnet's control -- which is
all that participates in the refcnting -- should be quiesced by
stopping and detaching everything.
---
 sys/dev/usb/usbnet.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 630290b91626..f072bebc9e83 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1611,20 +1611,6 @@ usbnet_detach(device_t self, int flags)
 	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
 	    NULL);
 
-	mutex_enter(&unp->unp_core_lock);
-	unp->unp_refcnt--;
-	while (unp->unp_refcnt >= 0) {
-		/* Wait for processes to go away */
-		cv_wait(&unp->unp_detachcv, &unp->unp_core_lock);
-	}
-	mutex_exit(&unp->unp_core_lock);
-
-	usbnet_rx_list_free(un);
-	usbnet_tx_list_free(un);
-
-	callout_destroy(&unp->unp_stat_ch);
-	rnd_detach_source(&unp->unp_rndsrc);
-
 	if (mii) {
 		mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
 		ifmedia_fini(&mii->mii_media);
@@ -1672,6 +1658,20 @@ usbnet_detach(device_t self, int flags)
 	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
 	    NULL);
 
+	mutex_enter(&unp->unp_core_lock);
+	unp->unp_refcnt--;
+	while (unp->unp_refcnt >= 0) {
+		/* Wait for processes to go away */
+		cv_wait(&unp->unp_detachcv, &unp->unp_core_lock);
+	}
+	mutex_exit(&unp->unp_core_lock);
+
+	usbnet_rx_list_free(un);
+	usbnet_tx_list_free(un);
+
+	callout_destroy(&unp->unp_stat_ch);
+	rnd_detach_source(&unp->unp_rndsrc);
+
 	cv_destroy(&unp->unp_detachcv);
 	mutex_destroy(&unp->unp_core_lock);
 	mutex_destroy(&unp->unp_rxlock);

From a75c018a4b477a0806862b283a1686cd51aa1061 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:49:56 +0000
Subject: [PATCH 19/75] usbnet: Make detach order reverse attach order, for
 unp_stat_ch.

No functional change intended.
---
 sys/dev/usb/usbnet.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index f072bebc9e83..470db472a7c5 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1669,7 +1669,6 @@ usbnet_detach(device_t self, int flags)
 	usbnet_rx_list_free(un);
 	usbnet_tx_list_free(un);
 
-	callout_destroy(&unp->unp_stat_ch);
 	rnd_detach_source(&unp->unp_rndsrc);
 
 	cv_destroy(&unp->unp_detachcv);
@@ -1677,6 +1676,8 @@ usbnet_detach(device_t self, int flags)
 	mutex_destroy(&unp->unp_rxlock);
 	mutex_destroy(&unp->unp_txlock);
 
+	callout_destroy(&unp->unp_stat_ch);
+
 	pmf_device_deregister(un->un_dev);
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev, un->un_dev);

From e9c4e2154a43f6b63a33b41acd114e9a7e6d6b5c Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:50:32 +0000
Subject: [PATCH 20/75] usbnet: Don't issue a detach event if we never issued
 an attach one.

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

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 470db472a7c5..33c78dcc85d9 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1680,7 +1680,14 @@ usbnet_detach(device_t self, int flags)
 
 	pmf_device_deregister(un->un_dev);
 
-	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev, un->un_dev);
+	/*
+	 * Notify userland that we're going away, if we arrived in the
+	 * first place.
+	 */
+	if (unp->unp_ifp_attached) {
+		usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev,
+		    un->un_dev);
+	}
 
 	kmem_free(unp, sizeof(*unp));
 	un->un_pri = NULL;

From 63821288610e3bd3a89bf88770e6fd7b4f916260 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:50:59 +0000
Subject: [PATCH 21/75] usbnet: Uncomment and fix assertion for ifp->if_flags
 |= IFF_RUNNING.

We always hold IFNET_LOCK for ioctls that end up here -- the ones
that don't hold it are only SIOCADDMULTI/SIOCDELMULTI, which don't
end up here.  However, urndis(4) throws a spanner in the works by
doing weird device initialization.
---
 sys/dev/usb/usbnet.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 33c78dcc85d9..b3ad4ba90f01 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -855,10 +855,9 @@ usbnet_init_rx_tx(struct usbnet * const un)
 	}
 
 	/* Indicate we are up and running. */
-#if 0
-	/* XXX if_mcast_op() can call this without ifnet locked */
-	KASSERT(ifp->if_softc == NULL || IFNET_LOCKED(ifp));
-#endif
+	/* XXX urndis calls usbnet_init_rx_tx before usbnet_attach_ifp.  */
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	ifp->if_flags |= IFF_RUNNING;
 
 	/* Start up the receive pipe(s). */

From c936920caad9808ed90432634fe3103d6e91cea2 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 24 Dec 2021 22:07:39 +0000
Subject: [PATCH 22/75] usbnet: Omit needless tests in usbnet_tick.

It's harmless for us to schedule the tick task even if unp_dying or
unp_stopping is set by now, because usbnet_stop will just wait for it
to finish anyway, and the callout can't be scheduled again until the
interface is done stopping and is brought back up again.

No need for unp == NULL test -- un->un_pri is initialized well before
this callout can be scheduled, and is nulled out only at the end of
usbnet_detach, at which point we have already halted this callout.
---
 sys/dev/usb/usbnet.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index b3ad4ba90f01..183bd5af37c6 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1203,10 +1203,8 @@ usbnet_tick(void *arg)
 
 	USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
 
-	if (unp != NULL && !unp->unp_stopping && !unp->unp_dying) {
-		/* Perform periodic stuff in process context */
-		usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
-	}
+	/* Perform periodic stuff in process context */
+	usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
 }
 
 static void

From da13dcc9623947c354742cc901d0af4ff65f0754 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:54:38 +0000
Subject: [PATCH 23/75] usbnet: Omit needless locking/busying/testing in
 usbnet_tick_task.

usbnet_stop waits for the task to complete before resetting the
hardware, and usbnet_detach waits for usbnet_stop to complete before
destroying anything, so there's no need for any of this.
---
 sys/dev/usb/usbnet.c | 15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 183bd5af37c6..4568379f0db7 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1239,22 +1239,10 @@ usbnet_tick_task(void *arg)
 	USBNETHIST_FUNC();
 	struct usbnet * const un = arg;
 	struct usbnet_private * const unp = un->un_pri;
-
-	USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
-
-	mutex_enter(&unp->unp_core_lock);
-	if (unp->unp_stopping || unp->unp_dying) {
-		mutex_exit(&unp->unp_core_lock);
-		return;
-	}
-
 	struct ifnet * const ifp = usbnet_ifp(un);
 	struct mii_data * const mii = usbnet_mii(un);
 
-	KASSERT(ifp != NULL);	/* embedded member */
-
-	usbnet_busy(un);
-	mutex_exit(&unp->unp_core_lock);
+	USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
 
 	mutex_enter(&unp->unp_txlock);
 	const bool timeout = unp->unp_timer != 0 && --unp->unp_timer == 0;
@@ -1275,7 +1263,6 @@ usbnet_tick_task(void *arg)
 	uno_tick(un);
 
 	mutex_enter(&unp->unp_core_lock);
-	usbnet_unbusy(un);
 	if (!unp->unp_stopping && !unp->unp_dying)
 		callout_schedule(&unp->unp_stat_ch, hz);
 	mutex_exit(&unp->unp_core_lock);

From fff239aec712dba2c39abc86839aea0547561ca5 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 10:06:50 +0000
Subject: [PATCH 24/75] usbnet: Clear watchdog timer before stopping hardware.

No need to take the lock again -- which might not be necessary
because the callout and task have completed, but let's obviate the
need to think about that.
---
 sys/dev/usb/usbnet.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 4568379f0db7..a84512b557e4 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1128,9 +1128,14 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 
 	usbnet_busy(un);
 
+	/*
+	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
+	 * clear the watchdog timer.
+	 */
 	mutex_enter(&unp->unp_rxlock);
 	mutex_enter(&unp->unp_txlock);
 	unp->unp_stopping = true;
+	unp->unp_timer = 0;
 	mutex_exit(&unp->unp_txlock);
 	mutex_exit(&unp->unp_rxlock);
 
@@ -1152,11 +1157,6 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 */
 	uno_stop(un, ifp, disable);
 
-	/* Clear the watchdog timer.  */
-	mutex_enter(&unp->unp_txlock);
-	unp->unp_timer = 0;
-	mutex_exit(&unp->unp_txlock);
-
 	/* Stop transfers. */
 	usbnet_ep_stop_pipes(un);
 

From 30272ec1f8f87c7ccd58dc92b3b32bf4315c2553 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Wed, 29 Dec 2021 22:34:05 +0000
Subject: [PATCH 25/75] usbnet: Avoid IFNET_LOCK on detach if we never attached
 the ifp.

---
 sys/dev/usb/usbnet.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index a84512b557e4..22b87068e38d 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1578,12 +1578,17 @@ usbnet_detach(device_t self, int flags)
 	/*
 	 * If we're still running on the network, stop and wait for all
 	 * asynchronous activity to finish.
+	 *
+	 * If usbnet_attach_ifp never ran, IFNET_LOCK won't work, but
+	 * no activity is possible, so just skip this part.
 	 */
-	IFNET_LOCK(ifp);
-	if (ifp->if_flags & IFF_RUNNING) {
-		usbnet_if_stop(ifp, 1);
+	if (unp->unp_ifp_attached) {
+		IFNET_LOCK(ifp);
+		if (ifp->if_flags & IFF_RUNNING) {
+			usbnet_if_stop(ifp, 1);
+		}
+		IFNET_UNLOCK(ifp);
 	}
-	IFNET_UNLOCK(ifp);
 
 	/*
 	 * The callout and tick task can't be scheduled anew at this

From 3f48d4679c67146aa28f08f92d9286138b612e66 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 09:34:22 +0000
Subject: [PATCH 26/75] cue(4): Return real error code, not -1, on init when
 detaching.

---
 sys/dev/usb/if_cue.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index e730297a939d..587c430a5d15 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -622,7 +622,7 @@ cue_init_locked(struct ifnet *ifp)
 	DPRINTFN(10,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
 	if (usbnet_isdying(un))
-		return -1;
+		return ENXIO;
 
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);

From ee3f467ca72b73309d0f78e1950a344c2468ea4c Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 09:48:31 +0000
Subject: [PATCH 27/75] usbnet: Don't waste time calling uno_stop if device is
 detaching.

The hardware is most likely gone, so trying to write to its registers
(and, in some cases, wait until a timeout for a device to reset) is a
waste of time.  Even if it was detached only in software with drvctl,
reattaching it will reset the device anyway.
---
 sys/dev/usb/usbnet.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 22b87068e38d..e04c3589f2b5 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1154,8 +1154,13 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 * the hardware.  The driver's uno_stop routine now has
 	 * exclusive access to any registers that might previously have
 	 * been used by to ifmedia, mii, or ioctl callbacks.
+	 *
+	 * Don't bother if the device is being detached, though -- if
+	 * it's been unplugged then there's no point in trying to touch
+	 * the registers.
 	 */
-	uno_stop(un, ifp, disable);
+	if (!unp->unp_dying)
+		uno_stop(un, ifp, disable);
 
 	/* Stop transfers. */
 	usbnet_ep_stop_pipes(un);

From 2d3f0ffb4eb8c43841b3b1637239f3d20916bd2e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 09:51:35 +0000
Subject: [PATCH 28/75] usbnet: Impart blame on whose ifnet is unlocked in
 uno_init.

---
 sys/dev/usb/usbnet.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index e04c3589f2b5..b938c70a8202 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -181,7 +181,7 @@ uno_override_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 static int
 uno_init(struct usbnet *un, struct ifnet *ifp)
 {
-	KASSERT(IFNET_LOCKED(ifp));
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 	return (*un->un_ops->uno_init)(ifp);
 }
 

From 3f50ad8ab20a345546a0f2d559cc61b606227396 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 09:53:09 +0000
Subject: [PATCH 29/75] usbnet: Assert ioctl locking.

---
 sys/dev/usb/usbnet.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index b938c70a8202..ace8f78da9d6 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -161,11 +161,15 @@ uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 static int
 uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 {
-	/*
-	 * There are cases where IFNET_LOCK will not be held when we
-	 * are called (e.g. add/delete multicast address), so we can't
-	 * assert it.
-	 */
+
+	switch (cmd) {
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		break;
+	default:
+		KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+	}
+
 	if (un->un_ops->uno_ioctl)
 		return (*un->un_ops->uno_ioctl)(ifp, cmd, data);
 	return 0;
@@ -174,7 +178,15 @@ uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 static int
 uno_override_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 {
-	/* See above. */
+
+	switch (cmd) {
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		break;
+	default:
+		KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+	}
+
 	return (*un->un_ops->uno_override_ioctl)(ifp, cmd, data);
 }
 

From 63717dcf98c09b88c13c488142b7bc91d6e12661 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 10:17:02 +0000
Subject: [PATCH 30/75] usbnet: Enter uno_init with the core lock held.

This reduces code in all drivers except urndis(4) and aue(4).

However, it's still safe for urndis to drop the core lock because the
ifnet is locked, and the ifnet lock covers the DOWN->UP (uno_init)
and UP->DOWN (uno_stop) transitions.
---
 sys/dev/usb/if_aue.c    | 5 +++--
 sys/dev/usb/if_axe.c    | 2 --
 sys/dev/usb/if_axen.c   | 2 --
 sys/dev/usb/if_cdce.c   | 2 --
 sys/dev/usb/if_cue.c    | 2 --
 sys/dev/usb/if_kue.c    | 2 --
 sys/dev/usb/if_mos.c    | 2 --
 sys/dev/usb/if_mue.c    | 2 --
 sys/dev/usb/if_smsc.c   | 2 --
 sys/dev/usb/if_udav.c   | 5 -----
 sys/dev/usb/if_upl.c    | 2 --
 sys/dev/usb/if_ure.c    | 2 --
 sys/dev/usb/if_url.c    | 2 --
 sys/dev/usb/if_urndis.c | 9 ++++++++-
 sys/dev/usb/usbnet.c    | 7 ++++++-
 15 files changed, 17 insertions(+), 31 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 89e2c5f55447..b72eff0349c4 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -1000,11 +1000,9 @@ aue_uno_init(struct ifnet *ifp)
 	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	rv = aue_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return rv;
 }
@@ -1012,6 +1010,7 @@ aue_uno_init(struct ifnet *ifp)
 static int
 aue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
+	struct usbnet * const un = ifp->if_softc;
 
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGSN(5, "aue%jd: enter cmd %#jx data %#jx",
@@ -1021,7 +1020,9 @@ aue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	switch (cmd) {
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
+		usbnet_lock_core(un);
 		aue_uno_init(ifp);
+		usbnet_unlock_core(un);
 		break;
 	default:
 		break;
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index eba67386e770..78a4a6546f70 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1317,11 +1317,9 @@ axe_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	int ret = axe_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index fe433722d185..7d90fa1259f9 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -935,11 +935,9 @@ axen_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	int ret = axen_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 3edf73d856ca..4785d9a4cc9e 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -261,7 +261,6 @@ cdce_uno_init(struct ifnet *ifp)
 	struct usbnet		*un = ifp->if_softc;
 	int rv;
 
-	usbnet_lock_core(un);
 	if (usbnet_isdying(un))
 		rv = EIO;
 	else {
@@ -269,7 +268,6 @@ cdce_uno_init(struct ifnet *ifp)
 		rv = usbnet_init_rx_tx(un);
 		usbnet_set_link(un, rv == 0);
 	}
-	usbnet_unlock_core(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 587c430a5d15..191f31eb9eca 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -673,11 +673,9 @@ cue_uno_init(struct ifnet *ifp)
 	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	rv = cue_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 172bc93fa301..6d8af3cd0735 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -633,11 +633,9 @@ kue_uno_init(struct ifnet *ifp)
 	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	rv = kue_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 347104b7b0df..987bb0484710 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -760,11 +760,9 @@ mos_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	int ret = mos_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 16a52e4c6ed4..81534e865688 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1255,11 +1255,9 @@ mue_uno_init(struct ifnet *ifp)
 	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	rv = mue_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 12eaf3fa3cc6..4361532efc39 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -552,11 +552,9 @@ smsc_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	int ret = smsc_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index a38a83a53df0..e22e1f305876 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -478,10 +478,7 @@ udav_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_lock_core(un);
-
 	if (usbnet_isdying(un)) {
-		usbnet_unlock_core(un);
 		return EIO;
 	}
 
@@ -522,7 +519,6 @@ udav_uno_init(struct ifnet *ifp)
 
 	if (rc != 0) {
 		usbnet_unbusy(un);
-		usbnet_unlock_core(un);
 		return rc;
 	}
 
@@ -532,7 +528,6 @@ udav_uno_init(struct ifnet *ifp)
 		rc = usbnet_init_rx_tx(un);
 
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return rc;
 }
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index fb99702f160e..cf4ba9de209f 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -257,12 +257,10 @@ upl_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	int rv;
 
-	usbnet_lock_core(un);
 	if (usbnet_isdying(un))
 		rv = EIO;
 	else
 		rv = usbnet_init_rx_tx(un);
-	usbnet_unlock_core(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index ea5b3023dd31..db75961fc88e 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -456,11 +456,9 @@ ure_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	int ret = ure_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index dbb57bf66ca7..af766fcd455d 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -408,11 +408,9 @@ url_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
 	usbnet_busy(un);
 	int ret = url_init_locked(ifp);
 	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index 381207450d72..ff4da0414739 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -883,8 +883,15 @@ static int
 urndis_uno_init(struct ifnet *ifp)
 {
 	struct usbnet *un = ifp->if_softc;
+	int error;
 
-	return urndis_init_un(ifp, un);
+	KASSERT(IFNET_LOCKED(ifp));
+
+	usbnet_unlock_core(un);
+	error = urndis_init_un(ifp, un);
+	usbnet_lock_core(un);
+
+	return error;
 }
 
 static int
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index ace8f78da9d6..80db2a9464a5 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1291,6 +1291,7 @@ usbnet_if_init(struct ifnet *ifp)
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
 	bool dying;
+	int error;
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
@@ -1304,7 +1305,11 @@ usbnet_if_init(struct ifnet *ifp)
 	if (dying)
 		return EIO;
 
-	return uno_init(un, ifp);
+	mutex_enter(&un->un_pri->unp_core_lock);
+	error = uno_init(un, ifp);
+	mutex_exit(&un->un_pri->unp_core_lock);
+
+	return error;
 }
 
 

From 63fb3e3df00737561c0a91e0580619c2a9237703 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 26 Dec 2021 01:56:30 +0000
Subject: [PATCH 31/75] usbnet: Print diagnostic about refcnt stragglers.

I don't think there can be any, but this message, if printed, would
falsify my hypothesis!
---
 sys/dev/usb/usbnet.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 80db2a9464a5..784f8459b801 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1671,6 +1671,10 @@ usbnet_detach(device_t self, int flags)
 
 	mutex_enter(&unp->unp_core_lock);
 	unp->unp_refcnt--;
+	if (unp->unp_refcnt >= 0) {
+		aprint_error_dev(un->un_dev, "%d stragglers\n",
+		    unp->unp_refcnt + 1);
+	}
 	while (unp->unp_refcnt >= 0) {
 		/* Wait for processes to go away */
 		cv_wait(&unp->unp_detachcv, &unp->unp_core_lock);

From 2f39eef4f188471023d55c33ab566f942f68ca16 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 11:51:41 +0000
Subject: [PATCH 32/75] usbnet: Use atomic_load/store_relaxed for unp_dying.

This way we don't need to hold the core lock to avoid upsetting
sanitizers (which probably find the current code upsetting), and we
can use it to exit early from timeout loops that run under the core
lock (which is probably not necessary for them to do anyway, but
let's worry about that later).
---
 sys/dev/usb/usbnet.c | 42 ++++++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 784f8459b801..4b78d50f9247 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -77,7 +77,7 @@ struct usbnet_private {
 	struct callout		unp_stat_ch;
 	struct usbd_pipe	*unp_ep[USBNET_ENDPT_MAX];
 
-	bool			unp_dying;
+	volatile bool		unp_dying;
 	bool			unp_stopping;
 	bool			unp_attached;
 	bool			unp_ifp_attached;
@@ -356,7 +356,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 
 	mutex_enter(&unp->unp_rxlock);
 
-	if (unp->unp_dying || unp->unp_stopping ||
+	if (usbnet_isdying(un) || unp->unp_stopping ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
 	    status == USBD_CANCELLED)
 		goto out;
@@ -383,7 +383,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	usbnet_isowned_rx(un);
 
 done:
-	if (unp->unp_dying || unp->unp_stopping)
+	if (usbnet_isdying(un) || unp->unp_stopping)
 		goto out;
 
 	mutex_exit(&unp->unp_rxlock);
@@ -412,7 +412,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 || unp->unp_dying) {
+	if (unp->unp_stopping || usbnet_isdying(un)) {
 		mutex_exit(&unp->unp_txlock);
 		return;
 	}
@@ -456,12 +456,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 = un->un_intr;
 
-	if (uni == NULL || unp->unp_dying || unp->unp_stopping ||
+	if (uni == NULL || 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",
 		    unp->unp_number, (uintptr_t)uni,
-		    (unp->unp_dying << 8) | unp->unp_stopping, status);
+		    (usbnet_isdying(un) << 8) | unp->unp_stopping, status);
 		return;
 	}
 
@@ -837,7 +837,7 @@ usbnet_init_rx_tx(struct usbnet * const un)
 
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying) {
+	if (usbnet_isdying(un)) {
 		return EIO;
 	}
 
@@ -918,13 +918,12 @@ usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 {
 	USBNETHIST_FUNC();
 	struct usbnet * const un = device_private(dev);
-	struct usbnet_private * const unp = un->un_pri;
 	int err;
 
 	/* MII layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying) {
+	if (usbnet_isdying(un)) {
 		return EIO;
 	}
 
@@ -934,7 +933,7 @@ usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 
 	if (err) {
 		USBNETHIST_CALLARGS("%jd: read PHY failed: %jd",
-		    unp->unp_number, err, 0, 0);
+		    un->un_pri->unp_number, err, 0, 0);
 		return err;
 	}
 
@@ -946,13 +945,12 @@ usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 {
 	USBNETHIST_FUNC();
 	struct usbnet * const un = device_private(dev);
-	struct usbnet_private * const unp = un->un_pri;
 	int err;
 
 	/* MII layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying) {
+	if (usbnet_isdying(un)) {
 		return EIO;
 	}
 
@@ -962,7 +960,7 @@ usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 
 	if (err) {
 		USBNETHIST_CALLARGS("%jd: write PHY failed: %jd",
-		    unp->unp_number, err, 0, 0);
+		    un->un_pri->unp_number, err, 0, 0);
 		return err;
 	}
 
@@ -997,7 +995,7 @@ usbnet_media_upd(struct ifnet *ifp)
 	/* ifmedia changes only with IFNET_LOCK held.  */
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
-	if (unp->unp_dying)
+	if (usbnet_isdying(un))
 		return EIO;
 
 	unp->unp_link = false;
@@ -1085,7 +1083,7 @@ usbnet_mcast_task(void *arg)
 	USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
 
 	/*
-	 * If we're detaching, we must check unp_dying _before_
+	 * If we're detaching, we must check usbnet_isdying _before_
 	 * touching IFNET_LOCK -- the ifnet may have been detached by
 	 * the time this task runs.  This is racy -- unp_dying may be
 	 * set immediately after we test it -- but nevertheless safe,
@@ -1094,7 +1092,7 @@ usbnet_mcast_task(void *arg)
 	 * IFNET_LOCK after if_detach.  See usbnet_detach for details.
 	 */
 	mutex_enter(&unp->unp_core_lock);
-	dying = unp->unp_dying;
+	dying = usbnet_isdying(un);
 	mutex_exit(&unp->unp_core_lock);
 	if (dying)
 		return;
@@ -1171,7 +1169,7 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	 * it's been unplugged then there's no point in trying to touch
 	 * the registers.
 	 */
-	if (!unp->unp_dying)
+	if (!usbnet_isdying(un))
 		uno_stop(un, ifp, disable);
 
 	/* Stop transfers. */
@@ -1280,7 +1278,7 @@ usbnet_tick_task(void *arg)
 	uno_tick(un);
 
 	mutex_enter(&unp->unp_core_lock);
-	if (!unp->unp_stopping && !unp->unp_dying)
+	if (!unp->unp_stopping && !usbnet_isdying(un))
 		callout_schedule(&unp->unp_stat_ch, hz);
 	mutex_exit(&unp->unp_core_lock);
 }
@@ -1300,7 +1298,7 @@ usbnet_if_init(struct ifnet *ifp)
 	 * we're detaching.
 	 */
 	mutex_enter(&un->un_pri->unp_core_lock);
-	dying = un->un_pri->unp_dying;
+	dying = usbnet_isdying(un);
 	mutex_exit(&un->un_pri->unp_core_lock);
 	if (dying)
 		return EIO;
@@ -1360,7 +1358,7 @@ usbnet_havelink(struct usbnet *un)
 bool
 usbnet_isdying(struct usbnet *un)
 {
-	return un->un_pri->unp_dying;
+	return atomic_load_relaxed(&un->un_pri->unp_dying);
 }
 
 
@@ -1594,7 +1592,7 @@ usbnet_detach(device_t self, int flags)
 	 * cannot be brought back up.
 	 */
 	mutex_enter(&unp->unp_core_lock);
-	unp->unp_dying = true;
+	atomic_store_relaxed(&unp->unp_dying, true);
 	mutex_exit(&unp->unp_core_lock);
 
 	/*
@@ -1723,7 +1721,7 @@ usbnet_activate(device_t self, devact_t act)
 		if_deactivate(ifp);
 
 		mutex_enter(&unp->unp_core_lock);
-		unp->unp_dying = true;
+		atomic_store_relaxed(&unp->unp_dying, true);
 		mutex_exit(&unp->unp_core_lock);
 
 		mutex_enter(&unp->unp_rxlock);

From 975b36220a5f1370621d75b749da70a931f20e7e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 12:23:28 +0000
Subject: [PATCH 33/75] usbnet: Omit needless locking around usbnet_isdying.

Now that is tested and set with atomic_load/store, there is no need
to hold the lock -- which means we can set it while the core lock is
held during, e.g., a reset sequence, and use that to interrupt the
sequence so it doesn't get stuck waiting to time out when the device
is physically removed.
---
 sys/dev/usb/usbnet.c | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 4b78d50f9247..4ea0859904b9 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1075,12 +1075,11 @@ usbnet_mcast_task(void *arg)
 {
 	USBNETHIST_FUNC();
 	struct usbnet * const un = arg;
-	struct usbnet_private * const unp = un->un_pri;
 	struct ifnet * const ifp = usbnet_ifp(un);
-	bool dying;
 	struct ifreq ifr;
 
-	USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
+	USBNETHIST_CALLARGSN(10, "%jd: enter",
+	    un->un_pri->unp_number, 0, 0, 0);
 
 	/*
 	 * If we're detaching, we must check usbnet_isdying _before_
@@ -1091,10 +1090,7 @@ usbnet_mcast_task(void *arg)
 	 * issuing if_detach, and necessary, so that we don't touch
 	 * IFNET_LOCK after if_detach.  See usbnet_detach for details.
 	 */
-	mutex_enter(&unp->unp_core_lock);
-	dying = usbnet_isdying(un);
-	mutex_exit(&unp->unp_core_lock);
-	if (dying)
+	if (usbnet_isdying(un))
 		return;
 
 	/*
@@ -1288,7 +1284,6 @@ usbnet_if_init(struct ifnet *ifp)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
-	bool dying;
 	int error;
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
@@ -1297,10 +1292,7 @@ usbnet_if_init(struct ifnet *ifp)
 	 * Prevent anyone from bringing the interface back up once
 	 * we're detaching.
 	 */
-	mutex_enter(&un->un_pri->unp_core_lock);
-	dying = usbnet_isdying(un);
-	mutex_exit(&un->un_pri->unp_core_lock);
-	if (dying)
+	if (usbnet_isdying(un))
 		return EIO;
 
 	mutex_enter(&un->un_pri->unp_core_lock);
@@ -1591,9 +1583,7 @@ usbnet_detach(device_t self, int flags)
 	 * Prevent new activity.  After we stop the interface, it
 	 * cannot be brought back up.
 	 */
-	mutex_enter(&unp->unp_core_lock);
 	atomic_store_relaxed(&unp->unp_dying, true);
-	mutex_exit(&unp->unp_core_lock);
 
 	/*
 	 * If we're still running on the network, stop and wait for all
@@ -1720,9 +1710,7 @@ usbnet_activate(device_t self, devact_t act)
 	case DVACT_DEACTIVATE:
 		if_deactivate(ifp);
 
-		mutex_enter(&unp->unp_core_lock);
 		atomic_store_relaxed(&unp->unp_dying, true);
-		mutex_exit(&unp->unp_core_lock);
 
 		mutex_enter(&unp->unp_rxlock);
 		mutex_enter(&unp->unp_txlock);

From 59a9877487bf5db2385798a09981f34b42e7c314 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 11:36:01 +0000
Subject: [PATCH 34/75] usbnet drivers: Stop timeout loops early if device is
 detaching.

---
 sys/dev/usb/if_aue.c  |  6 ++++++
 sys/dev/usb/if_mos.c  |  4 ++++
 sys/dev/usb/if_mue.c  |  2 ++
 sys/dev/usb/if_smsc.c |  2 ++
 sys/dev/usb/if_udav.c |  2 ++
 sys/dev/usb/if_ure.c  | 10 ++++++++++
 sys/dev/usb/if_url.c  |  2 ++
 7 files changed, 28 insertions(+)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index b72eff0349c4..b4d0dc36b7b8 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -483,6 +483,8 @@ aue_uno_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
 
 	for (i = 0; i < AUE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
 			break;
 	}
@@ -524,6 +526,8 @@ aue_uno_mii_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
 	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
 
 	for (i = 0; i < AUE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
 			break;
 	}
@@ -680,6 +684,8 @@ aue_reset(struct aue_softc *sc)
 	AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
 
 	for (i = 0; i < AUE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
 			break;
 	}
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 987bb0484710..e76c9f11893a 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -364,6 +364,8 @@ mos_uno_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 	    MOS_PHYSTS_PENDING);
 
 	for (i = 0; i < MOS_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (mos_reg_read_1(un, MOS_PHY_STS) & MOS_PHYSTS_READY)
 			break;
 	}
@@ -396,6 +398,8 @@ mos_uno_mii_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
 	    MOS_PHYSTS_PENDING);
 
 	for (i = 0; i < MOS_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (mos_reg_read_1(un, MOS_PHY_STS) & MOS_PHYSTS_READY)
 			break;
 	}
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 81534e865688..034bacc1f587 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -200,6 +200,8 @@ mue_wait_for_bits(struct usbnet *un, uint32_t reg,
 	int ntries;
 
 	for (ntries = 0; ntries < 1000; ntries++) {
+		if (usbnet_isdying(un))
+			return 1;
 		val = mue_csr_read(un, reg);
 		if ((val & set) || !(val & clear))
 			return 0;
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 4361532efc39..de6e4395be5b 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -264,6 +264,8 @@ smsc_wait_for_bits(struct usbnet *un, uint32_t reg, uint32_t bits)
 	int err, i;
 
 	for (i = 0; i < 100; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if ((err = smsc_readreg(un, reg, &val)) != 0)
 			return err;
 		if (!(val & bits))
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index e22e1f305876..13e5971a4bd6 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -568,6 +568,8 @@ udav_chip_init(struct usbnet *un)
 	UDAV_SETBIT(un, UDAV_NCR, UDAV_NCR_RST);
 
 	for (int i = 0; i < UDAV_TX_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(udav_csr_read1(un, UDAV_NCR) & UDAV_NCR_RST))
 			break;
 		delay(10);	/* XXX */
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index db75961fc88e..6ad61447dc3a 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -396,6 +396,8 @@ ure_reset(struct usbnet *un)
 	ure_write_1(un, URE_PLA_CR, URE_MCU_TYPE_PLA, URE_CR_RST);
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(ure_read_1(un, URE_PLA_CR, URE_MCU_TYPE_PLA) &
 		    URE_CR_RST))
 			break;
@@ -541,6 +543,8 @@ ure_rtl8153_init(struct usbnet *un)
 	    URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2));
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (ure_read_2(un, URE_PLA_BOOT_CTRL, URE_MCU_TYPE_PLA) &
 		    URE_AUTOLOAD_DONE)
 			break;
@@ -550,6 +554,8 @@ ure_rtl8153_init(struct usbnet *un)
 		URE_PRINTF(un, "timeout waiting for chip autoload\n");
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		val = ure_ocp_reg_read(un, URE_OCP_PHY_STATUS) &
 		    URE_PHY_STAT_MASK;
 		if (val == URE_PHY_STAT_LAN_ON || val == URE_PHY_STAT_PWRDN)
@@ -748,6 +754,8 @@ ure_init_fifo(struct usbnet *un)
 	    ure_read_2(un, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) &
 	    ~URE_MCU_BORW_EN);
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (ure_read_1(un, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
 		    URE_LINK_LIST_READY)
 			break;
@@ -759,6 +767,8 @@ ure_init_fifo(struct usbnet *un)
 	    ure_read_2(un, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) |
 	    URE_RE_INIT_LL);
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (ure_read_1(un, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
 		    URE_LINK_LIST_READY)
 			break;
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index af766fcd455d..28674302ab26 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -428,6 +428,8 @@ url_reset(struct usbnet *un)
 	URL_SETBIT(un, URL_CR, URL_CR_SOFT_RST);
 
 	for (i = 0; i < URL_TX_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(url_csr_read_1(un, URL_CR) & URL_CR_SOFT_RST))
 			break;
 		delay(10);	/* XXX */

From 43bfc55db3a0bac9c31b74e4351f75b0d04e1c61 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:12:07 +0000
Subject: [PATCH 35/75] usbnet: Split multicast filter reprogramming into
 separate operation.

---
 sys/dev/usb/if_aue.c  | 31 +++++++++++++------------------
 sys/dev/usb/if_axe.c  | 19 +++++--------------
 sys/dev/usb/if_axen.c | 20 ++++++++++++++++----
 sys/dev/usb/if_cue.c  | 19 +++++--------------
 sys/dev/usb/if_kue.c  | 19 +++++--------------
 sys/dev/usb/if_mos.c  | 19 +++++--------------
 sys/dev/usb/if_mue.c  | 20 ++++++++++++++++----
 sys/dev/usb/if_smsc.c | 20 ++++++++++++++++----
 sys/dev/usb/if_udav.c | 19 +++++--------------
 sys/dev/usb/if_ure.c  | 19 +++++--------------
 sys/dev/usb/if_url.c  | 19 +++++--------------
 sys/dev/usb/usbnet.c  | 24 +++++++++---------------
 sys/dev/usb/usbnet.h  | 14 ++++++++++----
 13 files changed, 115 insertions(+), 147 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index b4d0dc36b7b8..c47324b59848 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -240,7 +240,7 @@ CFATTACH_DECL_NEW(aue, sizeof(struct aue_softc), aue_match, aue_attach,
 static void aue_reset_pegasus_II(struct aue_softc *);
 
 static void aue_uno_stop(struct ifnet *, int);
-static int aue_uno_ioctl(struct ifnet *, u_long, void *);
+static void aue_uno_mcast(struct ifnet *);
 static int aue_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int aue_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void aue_uno_mii_statchg(struct ifnet *);
@@ -252,7 +252,7 @@ static void aue_uno_intr(struct usbnet *, usbd_status);
 
 static const struct usbnet_ops aue_ops = {
 	.uno_stop = aue_uno_stop,
-	.uno_ioctl = aue_uno_ioctl,
+	.uno_mcast = aue_uno_mcast,
 	.uno_read_reg = aue_uno_mii_read_reg,
 	.uno_write_reg = aue_uno_mii_write_reg,
 	.uno_statchg = aue_uno_mii_statchg,
@@ -1013,28 +1013,23 @@ aue_uno_init(struct ifnet *ifp)
 	return rv;
 }
 
-static int
-aue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+aue_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
 	AUEHIST_FUNC();
-	AUEHIST_CALLARGSN(5, "aue%jd: enter cmd %#jx data %#jx",
+	AUEHIST_CALLARGSN(5, "aue%jd: enter",
 	    device_unit(((struct usbnet *)(ifp->if_softc))->un_dev),
-	    cmd, (uintptr_t)data, 0);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		usbnet_lock_core(un);
-		aue_uno_init(ifp);
-		usbnet_unlock_core(un);
-		break;
-	default:
-		break;
-	}
+	    0, 0, 0);
 
-	return 0;
+	/*
+	 * XXX I feel like this is pretty heavy-handed!  Maybe we could
+	 * make do with aue_setiff_locked instead?
+	 */
+	usbnet_lock_core(un);
+	aue_uno_init(ifp);
+	usbnet_unlock_core(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 78a4a6546f70..20ca7b276760 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -259,7 +259,7 @@ CFATTACH_DECL_NEW(axe, sizeof(struct axe_softc),
 	axe_match, axe_attach, usbnet_detach, usbnet_activate);
 
 static void	axe_uno_stop(struct ifnet *, int);
-static int	axe_uno_ioctl(struct ifnet *, u_long, void *);
+static void	axe_uno_mcast(struct ifnet *);
 static int	axe_uno_init(struct ifnet *);
 static int	axe_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	axe_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
@@ -276,7 +276,7 @@ static void	axe_ax88772b_init(struct axe_softc *);
 
 static const struct usbnet_ops axe_ops = {
 	.uno_stop = axe_uno_stop,
-	.uno_ioctl = axe_uno_ioctl,
+	.uno_mcast = axe_uno_mcast,
 	.uno_read_reg = axe_uno_mii_read_reg,
 	.uno_write_reg = axe_uno_mii_write_reg,
 	.uno_statchg = axe_uno_mii_statchg,
@@ -1324,27 +1324,18 @@ axe_uno_init(struct ifnet *ifp)
 	return ret;
 }
 
-static int
-axe_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+axe_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		axe_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
+	axe_rcvfilt_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 7d90fa1259f9..3c12ad38c0a7 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -80,6 +80,7 @@ static void	axen_ax88179_init(struct usbnet *);
 
 static void	axen_uno_stop(struct ifnet *, int);
 static int	axen_uno_ioctl(struct ifnet *, u_long, void *);
+static void	axen_uno_mcast(struct ifnet *);
 static int	axen_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	axen_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void	axen_uno_mii_statchg(struct ifnet *);
@@ -92,6 +93,7 @@ static int	axen_uno_init(struct ifnet *);
 static const struct usbnet_ops axen_ops = {
 	.uno_stop = axen_uno_stop,
 	.uno_ioctl = axen_uno_ioctl,
+	.uno_mcast = axen_uno_mcast,
 	.uno_read_reg = axen_uno_mii_read_reg,
 	.uno_write_reg = axen_uno_mii_write_reg,
 	.uno_statchg = axen_uno_mii_statchg,
@@ -559,10 +561,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	usbnet_busy(un);
 
 	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		axen_setiff_locked(un);
-		break;
 	case SIOCSIFCAP:
 		axen_setoe_locked(un);
 		break;
@@ -576,6 +574,20 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return 0;
 }
 
+static void
+axen_uno_mcast(struct ifnet *ifp)
+{
+	struct usbnet * const un = ifp->if_softc;
+
+	usbnet_lock_core(un);
+	usbnet_busy(un);
+
+	axen_setiff_locked(un);
+
+	usbnet_unbusy(un);
+	usbnet_unlock_core(un);
+}
+
 static int
 axen_match(device_t parent, cfdata_t match, void *aux)
 {
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 191f31eb9eca..54a6c8535e12 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -141,14 +141,14 @@ CFATTACH_DECL_NEW(cue, sizeof(struct cue_softc), cue_match, cue_attach,
 static unsigned cue_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
 static void cue_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
-static int cue_uno_ioctl(struct ifnet *, u_long, void *);
+static void cue_uno_mcast(struct ifnet *);
 static void cue_uno_stop(struct ifnet *, int);
 static int cue_uno_init(struct ifnet *);
 static void cue_uno_tick(struct usbnet *);
 
 static const struct usbnet_ops cue_ops = {
 	.uno_stop = cue_uno_stop,
-	.uno_ioctl = cue_uno_ioctl,
+	.uno_mcast = cue_uno_mcast,
 	.uno_tx_prepare = cue_uno_tx_prepare,
 	.uno_rx_loop = cue_uno_rx_loop,
 	.uno_init = cue_uno_init,
@@ -680,27 +680,18 @@ cue_uno_init(struct ifnet *ifp)
 	return rv;
 }
 
-static int
-cue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+cue_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		cue_setiff_locked(un);
-		break;
-	default:
-		break;
-	}
+	cue_setiff_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 /* Stop and reset the adapter.  */
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 6d8af3cd0735..6c2f417e74df 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -174,11 +174,11 @@ CFATTACH_DECL_NEW(kue, sizeof(struct kue_softc), kue_match, kue_attach,
 static void kue_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static unsigned kue_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
-static int kue_uno_ioctl(struct ifnet *, u_long, void *);
+static void kue_uno_mcast(struct ifnet *);
 static int kue_uno_init(struct ifnet *);
 
 static const struct usbnet_ops kue_ops = {
-	.uno_ioctl = kue_uno_ioctl,
+	.uno_mcast = kue_uno_mcast,
 	.uno_tx_prepare = kue_uno_tx_prepare,
 	.uno_rx_loop = kue_uno_rx_loop,
 	.uno_init = kue_uno_init,
@@ -640,27 +640,18 @@ kue_uno_init(struct ifnet *ifp)
 	return rv;
 }
 
-static int
-kue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+kue_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		kue_setiff_locked(un);
-		break;
-	default:
-		break;
-	}
+	kue_setiff_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 #ifdef _MODULE
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index e76c9f11893a..26d048b20310 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -145,7 +145,7 @@ CFATTACH_DECL_NEW(mos, sizeof(struct usbnet),
 static void mos_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static unsigned mos_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
-static int mos_uno_ioctl(struct ifnet *, u_long, void *);
+static void mos_uno_mcast(struct ifnet *);
 static int mos_uno_init(struct ifnet *);
 static void mos_chip_init(struct usbnet *);
 static void mos_uno_stop(struct ifnet *ifp, int disable);
@@ -164,7 +164,7 @@ static int mos_write_mcast(struct usbnet *, uint8_t *);
 
 static const struct usbnet_ops mos_ops = {
 	.uno_stop = mos_uno_stop,
-	.uno_ioctl = mos_uno_ioctl,
+	.uno_mcast = mos_uno_mcast,
 	.uno_read_reg = mos_uno_mii_read_reg,
 	.uno_write_reg = mos_uno_mii_write_reg,
 	.uno_statchg = mos_uno_mii_statchg,
@@ -771,27 +771,18 @@ mos_uno_init(struct ifnet *ifp)
 	return ret;
 }
 
-static int
-mos_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+mos_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		mos_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
+	mos_rcvfilt_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 void
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 034bacc1f587..fe14f4422d8f 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -99,6 +99,7 @@ static void	mue_reset(struct usbnet *);
 
 static void	mue_uno_stop(struct ifnet *, int);
 static int	mue_uno_ioctl(struct ifnet *, u_long, void *);
+static void	mue_uno_mcast(struct ifnet *);
 static int	mue_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	mue_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void	mue_uno_mii_statchg(struct ifnet *);
@@ -111,6 +112,7 @@ static int	mue_uno_init(struct ifnet *);
 static const struct usbnet_ops mue_ops = {
 	.uno_stop = mue_uno_stop,
 	.uno_ioctl = mue_uno_ioctl,
+	.uno_mcast = mue_uno_mcast,
 	.uno_read_reg = mue_uno_mii_read_reg,
 	.uno_write_reg = mue_uno_mii_write_reg,
 	.uno_statchg = mue_uno_mii_statchg,
@@ -1273,10 +1275,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	usbnet_busy(un);
 
 	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		mue_setiff_locked(un);
-		break;
 	case SIOCSIFCAP:
 		mue_sethwcsum_locked(un);
 		break;
@@ -1293,6 +1291,20 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return 0;
 }
 
+static void
+mue_uno_mcast(struct ifnet *ifp)
+{
+	struct usbnet * const un = ifp->if_softc;
+
+	usbnet_lock_core(un);
+	usbnet_busy(un);
+
+	mue_setiff_locked(un);
+
+	usbnet_unbusy(un);
+	usbnet_unlock_core(un);
+}
+
 static void
 mue_reset(struct usbnet *un)
 {
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index de6e4395be5b..d749af747e09 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -187,6 +187,7 @@ static int	 smsc_uno_miibus_readreg(struct usbnet *, int, int, uint16_t *);
 static int	 smsc_uno_miibus_writereg(struct usbnet *, int, int, uint16_t);
 
 static int	 smsc_uno_ioctl(struct ifnet *, u_long, void *);
+static void	 smsc_uno_mcast(struct ifnet *);
 static unsigned	 smsc_uno_tx_prepare(struct usbnet *, struct mbuf *,
 		     struct usbnet_chain *);
 static void	 smsc_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
@@ -195,6 +196,7 @@ static void	 smsc_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
 static const struct usbnet_ops smsc_ops = {
 	.uno_stop = smsc_uno_stop,
 	.uno_ioctl = smsc_uno_ioctl,
+	.uno_mcast = smsc_uno_mcast,
 	.uno_read_reg = smsc_uno_miibus_readreg,
 	.uno_write_reg = smsc_uno_miibus_writereg,
 	.uno_statchg = smsc_uno_miibus_statchg,
@@ -757,10 +759,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	usbnet_busy(un);
 
 	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		smsc_setiff_locked(un);
-		break;
 	case SIOCSIFCAP:
 		smsc_setoe_locked(un);
 		break;
@@ -774,6 +772,20 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return 0;
 }
 
+static void
+smsc_uno_mcast(struct ifnet *ifp)
+{
+	struct usbnet * const un = ifp->if_softc;
+
+	usbnet_lock_core(un);
+	usbnet_busy(un);
+
+	smsc_setiff_locked(un);
+
+	usbnet_unbusy(un);
+	usbnet_unlock_core(un);
+}
+
 static int
 smsc_match(device_t parent, cfdata_t match, void *aux)
 {
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 13e5971a4bd6..45f68f89eb32 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -69,7 +69,7 @@ static unsigned udav_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				    struct usbnet_chain *);
 static void udav_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static void udav_uno_stop(struct ifnet *, int);
-static int udav_uno_ioctl(struct ifnet *, u_long, void *);
+static void udav_uno_mcast(struct ifnet *);
 static int udav_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int udav_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void udav_uno_mii_statchg(struct ifnet *);
@@ -132,7 +132,7 @@ static const struct udav_type {
 
 static const struct usbnet_ops udav_ops = {
 	.uno_stop = udav_uno_stop,
-	.uno_ioctl = udav_uno_ioctl,
+	.uno_mcast = udav_uno_mcast,
 	.uno_read_reg = udav_uno_mii_read_reg,
 	.uno_write_reg = udav_uno_mii_write_reg,
 	.uno_statchg = udav_uno_mii_statchg,
@@ -718,27 +718,18 @@ udav_uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
 	usbnet_enqueue(un, buf, pkt_len, 0, 0, 0);
 }
 
-static int
-udav_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+udav_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		udav_setiff_locked(un);
-		break;
-	default:
-		break;
-	}
+	udav_setiff_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 6ad61447dc3a..480bc05e1e6d 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -86,7 +86,7 @@ static void	ure_disable_teredo(struct usbnet *);
 static void	ure_init_fifo(struct usbnet *);
 
 static void	ure_uno_stop(struct ifnet *, int);
-static int	ure_uno_ioctl(struct ifnet *, u_long, void *);
+static void	ure_uno_mcast(struct ifnet *);
 static int	ure_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	ure_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void	ure_uno_miibus_statchg(struct ifnet *);
@@ -104,7 +104,7 @@ CFATTACH_DECL_NEW(ure, sizeof(struct usbnet), ure_match, ure_attach,
 
 static const struct usbnet_ops ure_ops = {
 	.uno_stop = ure_uno_stop,
-	.uno_ioctl = ure_uno_ioctl,
+	.uno_mcast = ure_uno_mcast,
 	.uno_read_reg = ure_uno_mii_read_reg,
 	.uno_write_reg = ure_uno_mii_write_reg,
 	.uno_statchg = ure_uno_miibus_statchg,
@@ -802,27 +802,18 @@ ure_init_fifo(struct usbnet *un)
 	    URE_TXFIFO_THR_NORMAL);
 }
 
-static int
-ure_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+ure_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		ure_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
+	ure_rcvfilt_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 static int
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 28674302ab26..907d759f9ad8 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -77,7 +77,7 @@ static unsigned	url_uno_tx_prepare(struct usbnet *, struct mbuf *,
 static void url_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static int url_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int url_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
-static int url_uno_ioctl(struct ifnet *, u_long, void *);
+static void url_uno_mcast(struct ifnet *);
 static void url_uno_stop(struct ifnet *, int);
 static void url_uno_mii_statchg(struct ifnet *);
 static int url_uno_init(struct ifnet *);
@@ -93,7 +93,7 @@ static int url_mem(struct usbnet *, int, int, void *, int);
 
 static const struct usbnet_ops url_ops = {
 	.uno_stop = url_uno_stop,
-	.uno_ioctl = url_uno_ioctl,
+	.uno_mcast = url_uno_mcast,
 	.uno_read_reg = url_uno_mii_read_reg,
 	.uno_write_reg = url_uno_mii_write_reg,
 	.uno_statchg = url_uno_mii_statchg,
@@ -559,27 +559,18 @@ static void url_intr(void)
 }
 #endif
 
-static int
-url_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+static void
+url_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
 	usbnet_busy(un);
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		url_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
+	url_rcvfilt_locked(un);
 
 	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
-
-	return 0;
 }
 
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 4ea0859904b9..80911908fa66 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -162,13 +162,9 @@ static int
 uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 {
 
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		break;
-	default:
-		KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
-	}
+	KASSERTMSG(cmd != SIOCADDMULTI, "%s", ifp->if_xname);
+	KASSERTMSG(cmd != SIOCDELMULTI, "%s", ifp->if_xname);
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
 	if (un->un_ops->uno_ioctl)
 		return (*un->un_ops->uno_ioctl)(ifp, cmd, data);
@@ -1076,7 +1072,6 @@ usbnet_mcast_task(void *arg)
 	USBNETHIST_FUNC();
 	struct usbnet * const un = arg;
 	struct ifnet * const ifp = usbnet_ifp(un);
-	struct ifreq ifr;
 
 	USBNETHIST_CALLARGSN(10, "%jd: enter",
 	    un->un_pri->unp_number, 0, 0, 0);
@@ -1094,16 +1089,15 @@ usbnet_mcast_task(void *arg)
 		return;
 
 	/*
-	 * Pass a bogus ifr with SIOCDELMULTI -- the goal is to just
-	 * notify the driver to reprogram any hardware multicast
-	 * filter, according to what's already stored in the ethercom.
-	 * None of the drivers actually examine this argument, so it
-	 * doesn't change the ABI as far as they can tell.
+	 * If the hardware is running, ask the driver to reprogram the
+	 * multicast filter.  If the hardware is not running, the
+	 * driver is responsible for programming the multicast filter
+	 * as part of its uno_init routine to bring the hardware up.
 	 */
 	IFNET_LOCK(ifp);
 	if (ifp->if_flags & IFF_RUNNING) {
-		memset(&ifr, 0, sizeof(ifr));
-		(void)uno_ioctl(un, ifp, SIOCDELMULTI, &ifr);
+		if (un->un_ops->uno_mcast)
+			(*un->un_ops->uno_mcast)(ifp);
 	}
 	IFNET_UNLOCK(ifp);
 }
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 1faa822887c5..fc9c578453a5 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -131,6 +131,8 @@ enum usbnet_ep {
 typedef void (*usbnet_stop_cb)(struct ifnet *, int);
 /* Interface ioctl callback. */
 typedef int (*usbnet_ioctl_cb)(struct ifnet *, u_long, void *);
+/* Reprogram multicast filters callback. */
+typedef void (*usbnet_mcast_cb)(struct ifnet *);
 /* Initialise device callback. */
 typedef int (*usbnet_init_cb)(struct ifnet *);
 
@@ -170,16 +172,20 @@ typedef void (*usbnet_intr_cb)(struct usbnet *, usbd_status);
  * Note that when CORE_LOCK is held, IFNET_LOCK may or may not also
  * be held.
  *
- * Note that the IFNET_LOCK **may not be held** for some ioctl
- * operations (add/delete multicast addresses, for example).
+ * Note that the IFNET_LOCK **may not be held** for some the ioctl
+ * commands SIOCADDMULTI/SIOCDELMULTI.  These commands are only passed
+ * explicitly to uno_override_ioctl; for all other devices, they are
+ * handled inside usbnet by scheduling a task to asynchronously call
+ * uno_mcast with IFNET_LOCK held.
  *
  * Busy reference counts are maintained across calls to: uno_stop,
  * uno_read_reg, uno_write_reg, uno_statchg, and uno_tick.
  */
 struct usbnet_ops {
 	usbnet_stop_cb		uno_stop;		/* C */
-	usbnet_ioctl_cb		uno_ioctl;		/* I (maybe) */
-	usbnet_ioctl_cb		uno_override_ioctl;	/* I (maybe) */
+	usbnet_ioctl_cb		uno_ioctl;		/* I */
+	usbnet_ioctl_cb		uno_override_ioctl;	/* I (except mcast) */
+	usbnet_mcast_cb		uno_mcast;		/* I */
 	usbnet_init_cb		uno_init;		/* I */
 	usbnet_mii_read_reg_cb	uno_read_reg;		/* C */
 	usbnet_mii_write_reg_cb uno_write_reg;		/* C */

From f0764746f2240d2e4ff3a11d9f06c20693d2a57f Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:32:54 +0000
Subject: [PATCH 36/75] usbnet drivers: No need for usbnet_busy in uno_init.

This callback always runs with the IFNET_LOCK held, and the interface
cannot be detached until the IFNET_LOCK is released, so there is no
need to hang onto a reference count here.  (None of the usbnet
drivers touch the IFNET_LOCK except to verify it is held sometimes.)
---
 sys/dev/usb/if_aue.c  | 3 ---
 sys/dev/usb/if_axe.c  | 4 ----
 sys/dev/usb/if_axen.c | 4 ----
 sys/dev/usb/if_cue.c  | 3 ---
 sys/dev/usb/if_kue.c  | 3 ---
 sys/dev/usb/if_mos.c  | 4 ----
 sys/dev/usb/if_mue.c  | 3 ---
 sys/dev/usb/if_smsc.c | 4 ----
 sys/dev/usb/if_udav.c | 5 -----
 sys/dev/usb/if_ure.c  | 4 ----
 sys/dev/usb/if_url.c  | 4 ----
 11 files changed, 41 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index c47324b59848..058c8ae923be 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -1003,12 +1003,9 @@ aue_init_locked(struct ifnet *ifp)
 static int
 aue_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_busy(un);
 	rv = aue_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 20ca7b276760..252f28ef8b14 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1315,11 +1315,7 @@ axe_init_locked(struct ifnet *ifp)
 static int
 axe_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_busy(un);
 	int ret = axe_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 3c12ad38c0a7..f8199ca2f1fc 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -945,11 +945,7 @@ axen_init_locked(struct ifnet *ifp)
 static int
 axen_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_busy(un);
 	int ret = axen_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 54a6c8535e12..6c2db667ba82 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -670,12 +670,9 @@ cue_init_locked(struct ifnet *ifp)
 static int
 cue_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_busy(un);
 	rv = cue_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 6c2f417e74df..7d2a03e14c8c 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -630,12 +630,9 @@ kue_init_locked(struct ifnet *ifp)
 static int
 kue_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_busy(un);
 	rv = kue_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 26d048b20310..03601b9e752b 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -762,11 +762,7 @@ mos_init_locked(struct ifnet *ifp)
 static int
 mos_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_busy(un);
 	int ret = mos_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index fe14f4422d8f..e41fe6b6b091 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1256,12 +1256,9 @@ mue_init_locked(struct ifnet *ifp)
 static int
 mue_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const	un = ifp->if_softc;
 	int rv;
 
-	usbnet_busy(un);
 	rv = mue_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index d749af747e09..718638357b93 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -554,11 +554,7 @@ smsc_reset(struct smsc_softc *sc)
 static int
 smsc_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_busy(un);
 	int ret = smsc_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 45f68f89eb32..fe70dfb17012 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -486,8 +486,6 @@ udav_uno_init(struct ifnet *ifp)
 	if (ifp->if_flags & IFF_RUNNING)
 		usbnet_stop(un, ifp, 1);
 
-	usbnet_busy(un);
-
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
 	udav_csr_write(un, UDAV_PAR, eaddr, ETHER_ADDR_LEN);
 
@@ -518,7 +516,6 @@ udav_uno_init(struct ifnet *ifp)
 		rc = 0;
 
 	if (rc != 0) {
-		usbnet_unbusy(un);
 		return rc;
 	}
 
@@ -527,8 +524,6 @@ udav_uno_init(struct ifnet *ifp)
 	else
 		rc = usbnet_init_rx_tx(un);
 
-	usbnet_unbusy(un);
-
 	return rc;
 }
 
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 480bc05e1e6d..669677836f8d 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -456,11 +456,7 @@ ure_init_locked(struct ifnet *ifp)
 static int
 ure_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_busy(un);
 	int ret = ure_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return ret;
 }
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 907d759f9ad8..b0f48c5249c8 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -406,11 +406,7 @@ url_init_locked(struct ifnet *ifp)
 static int
 url_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_busy(un);
 	int ret = url_init_locked(ifp);
-	usbnet_unbusy(un);
 
 	return ret;
 }

From cf33d1711c00a3442d9d45df68a3ef2f448af17c Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:35:32 +0000
Subject: [PATCH 37/75] usbnet drivers: No need for usbnet_busy in uno_mcast.

This callback always runs with IFNET_LOCK held, and during a task
that usbnet_detach prevents scheduling anew and waits for finishing
before completing the detach, so there is no need to hang onto a
reference count here.
---
 sys/dev/usb/if_axe.c  | 2 --
 sys/dev/usb/if_axen.c | 2 --
 sys/dev/usb/if_cue.c  | 2 --
 sys/dev/usb/if_kue.c  | 2 --
 sys/dev/usb/if_mos.c  | 2 --
 sys/dev/usb/if_mue.c  | 2 --
 sys/dev/usb/if_smsc.c | 2 --
 sys/dev/usb/if_udav.c | 2 --
 sys/dev/usb/if_ure.c  | 2 --
 sys/dev/usb/if_url.c  | 2 --
 10 files changed, 20 deletions(-)

diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 252f28ef8b14..5f7663e435e0 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1326,11 +1326,9 @@ axe_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	axe_rcvfilt_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index f8199ca2f1fc..96f17df1b3cc 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -580,11 +580,9 @@ axen_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	axen_setiff_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 6c2db667ba82..646cef9178b5 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -683,11 +683,9 @@ cue_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const	un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	cue_setiff_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 7d2a03e14c8c..c02f40a77dcf 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -643,11 +643,9 @@ kue_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const	un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	kue_setiff_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 03601b9e752b..1c27bcb6f308 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -773,11 +773,9 @@ mos_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	mos_rcvfilt_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index e41fe6b6b091..663ef0f49c2a 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1294,11 +1294,9 @@ mue_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	mue_setiff_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 718638357b93..4e75cf65a499 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -774,11 +774,9 @@ smsc_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	smsc_setiff_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index fe70dfb17012..f0c23cfea3ae 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -719,11 +719,9 @@ udav_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	udav_setiff_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 669677836f8d..45b18f48438c 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -804,11 +804,9 @@ ure_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	ure_rcvfilt_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index b0f48c5249c8..cbc125bd08ea 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -561,11 +561,9 @@ url_uno_mcast(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	url_rcvfilt_locked(un);
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 

From 84af54f8304ddd7c28cbd286b943b5be13d95296 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:37:56 +0000
Subject: [PATCH 38/75] usbnet drivers: No need for usbnet_busy in uno_ioctl.

This callback always runs with the IFNET_LOCK held, and the interface
cannot be detached until the IFNET_LOCK is released, so there is no
need to hang onto a reference count here.  (None of the subnet
drivers touch the IFNET_LOCK except to verify it is held sometimes.)
---
 sys/dev/usb/if_axen.c | 2 --
 sys/dev/usb/if_mue.c  | 2 --
 sys/dev/usb/if_smsc.c | 2 --
 3 files changed, 6 deletions(-)

diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 96f17df1b3cc..44bb96944b7a 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -558,7 +558,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	switch (cmd) {
 	case SIOCSIFCAP:
@@ -568,7 +567,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 
 	return 0;
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 663ef0f49c2a..d6ab282aa2d7 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1269,7 +1269,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	switch (cmd) {
 	case SIOCSIFCAP:
@@ -1282,7 +1281,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 
 	return 0;
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 4e75cf65a499..d61315fed14d 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -752,7 +752,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	struct usbnet * const un = ifp->if_softc;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	switch (cmd) {
 	case SIOCSIFCAP:
@@ -762,7 +761,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 
 	return 0;

From 50737f7f8b63d7038b92e448bcfdb01fe1bcb179 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:39:06 +0000
Subject: [PATCH 39/75] usbnet drivers: No need for usbnet_busy during attach.

usbnet_detach cannot run until the attach routine has finished
(unless a driver goes out of its way to tie its shoelaces together
and explicitly call it during the attach routine, which none of them
do), so there is no need to hang onto a reference count that we
release before attach returns.
---
 sys/dev/usb/if_axe.c  | 4 ----
 sys/dev/usb/if_axen.c | 6 ------
 sys/dev/usb/if_smsc.c | 2 --
 sys/dev/usb/if_udav.c | 2 --
 sys/dev/usb/if_url.c  | 2 --
 5 files changed, 16 deletions(-)

diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 5f7663e435e0..5f65f8d53085 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -930,10 +930,8 @@ axe_attach(device_t parent, device_t self, void *aux)
 
 	/* We need the PHYID for init dance in some cases */
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 	if (axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, &sc->axe_phyaddrs)) {
 		aprint_error_dev(self, "failed to read phyaddrs\n");
-		usbnet_unbusy(un);
 		usbnet_unlock_core(un);
 		return;
 	}
@@ -964,13 +962,11 @@ axe_attach(device_t parent, device_t self, void *aux)
 	} else {
 		if (axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs)) {
 			aprint_error_dev(self, "failed to read ipg\n");
-			usbnet_unbusy(un);
 			usbnet_unlock_core(un);
 			return;
 		}
 	}
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 
 	if (!AXE_IS_172(un))
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 44bb96944b7a..ae07b34b0daa 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -368,7 +368,6 @@ axen_ax88179_init(struct usbnet *un)
 	uint8_t val;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	/* XXX: ? */
 	axen_cmd(un, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val);
@@ -452,7 +451,6 @@ axen_ax88179_init(struct usbnet *un)
 	default:
 		aprint_error_dev(un->un_dev, "unknown uplink bus:0x%02x\n",
 		    val);
-		usbnet_unbusy(un);
 		usbnet_unlock_core(un);
 		return;
 	}
@@ -512,7 +510,6 @@ axen_ax88179_init(struct usbnet *un)
 	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0000);
 #endif
 
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 }
 
@@ -682,14 +679,11 @@ axen_attach(device_t parent, device_t self, void *aux)
 
 	/* Get station address.  */
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 	if (axen_get_eaddr(un, &un->un_eaddr)) {
-		usbnet_unbusy(un);
 		usbnet_unlock_core(un);
 		printf("EEPROM checksum error\n");
 		return;
 	}
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 
 	axen_ax88179_init(un);
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index d61315fed14d..a48336f8b972 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -884,7 +884,6 @@ smsc_attach(device_t parent, device_t self, void *aux)
 	un->un_phyno = 1;
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 	/*
 	 * Attempt to get the mac address, if an EEPROM is not attached this
 	 * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
@@ -912,7 +911,6 @@ smsc_attach(device_t parent, device_t self, void *aux)
 			un->un_eaddr[0] = (uint8_t)((mac_l) & 0xff);
 		}
 	}
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index f0c23cfea3ae..77f9bd353c5e 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -240,14 +240,12 @@ udav_attach(device_t parent, device_t self, void *aux)
 	usbnet_attach(un, "udavdet");
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 // 	/* reset the adapter */
 // 	udav_reset(un);
 
 	/* Get Ethernet Address */
 	err = udav_csr_read(un, UDAV_PAR, un->un_eaddr, ETHER_ADDR_LEN);
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index cbc125bd08ea..82f1772f180f 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -244,7 +244,6 @@ url_attach(device_t parent, device_t self, void *aux)
 	usbnet_attach(un, "urldet");
 
 	usbnet_lock_core(un);
-	usbnet_busy(un);
 
 	/* reset the adapter */
 	url_reset(un);
@@ -252,7 +251,6 @@ url_attach(device_t parent, device_t self, void *aux)
 	/* Get Ethernet Address */
 	err = url_mem(un, URL_CMD_READMEM, URL_IDR0, (void *)un->un_eaddr,
 		      ETHER_ADDR_LEN);
-	usbnet_unbusy(un);
 	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");

From 66f6815df914d44a4e67bbb2e16d28617854601b Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:46:11 +0000
Subject: [PATCH 40/75] usbnet: No need for usbnet_busy in usbnet_init_rx_tx or
 usbnet_stop.

These run with IFNET_LOCK held, and the interface cannot be detached
until the IFNET_LOCK is released, so there is no need to hang onto a
reference count here.
---
 sys/dev/usb/usbnet.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 80911908fa66..64221164a70c 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -837,8 +837,6 @@ usbnet_init_rx_tx(struct usbnet * const un)
 		return EIO;
 	}
 
-	usbnet_busy(un);
-
 	/* Open RX and TX pipes. */
 	err = usbnet_ep_open_pipes(un);
 	if (err) {
@@ -879,7 +877,6 @@ out:
 		usbnet_tx_list_fini(un);
 		usbnet_ep_close_pipes(un);
 	}
-	usbnet_unbusy(un);
 
 	usbnet_isowned_core(un);
 
@@ -1126,8 +1123,6 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	    "%s", ifp->if_xname);
 	usbnet_isowned_core(un);
 
-	usbnet_busy(un);
-
 	/*
 	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
 	 * clear the watchdog timer.
@@ -1176,8 +1171,6 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
 	    "%s", ifp->if_xname);
 	ifp->if_flags &= ~IFF_RUNNING;
-
-	usbnet_unbusy(un);
 }
 
 static void

From b3eb4ac8e894c8a30e528a15b23cd59346e08185 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:48:26 +0000
Subject: [PATCH 41/75] usbnet: No need for usbnet_busy in mii callbacks.

After mii_detach, these have all completed and no new ones can be
made, and detach doesn't start destroying anything until after
mii_detach has returned, so there is no need to hang onto a reference
count here.
---
 sys/dev/usb/usbnet.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 64221164a70c..03c33cda6caf 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -920,10 +920,7 @@ usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 		return EIO;
 	}
 
-	usbnet_busy(un);
 	err = uno_read_reg(un, phy, reg, val);
-	usbnet_unbusy(un);
-
 	if (err) {
 		USBNETHIST_CALLARGS("%jd: read PHY failed: %jd",
 		    un->un_pri->unp_number, err, 0, 0);
@@ -947,10 +944,7 @@ usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 		return EIO;
 	}
 
-	usbnet_busy(un);
 	err = uno_write_reg(un, phy, reg, val);
-	usbnet_unbusy(un);
-
 	if (err) {
 		USBNETHIST_CALLARGS("%jd: write PHY failed: %jd",
 		    un->un_pri->unp_number, err, 0, 0);
@@ -969,9 +963,7 @@ usbnet_mii_statchg(struct ifnet *ifp)
 	/* MII layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	usbnet_busy(un);
 	uno_mii_statchg(un, ifp);
-	usbnet_unbusy(un);
 }
 
 static int

From 63eb9fdaf2f2cadf40259da867ff2d91b3b64b5a Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 13:51:35 +0000
Subject: [PATCH 42/75] usbnet: usbnet_busy is no longer referenced; release
 it!

---
 sys/dev/usb/usbnet.c | 38 --------------------------------------
 sys/dev/usb/usbnet.h |  6 ------
 2 files changed, 44 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 03c33cda6caf..7b5cc90fe0e9 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -56,7 +56,6 @@ struct usbnet_private {
 	 *   and the MII / media data.
 	 * - unp_rxlock protects the rx path and its data
 	 * - unp_txlock protects the tx path and its data
-	 * - unp_detachcv handles detach vs open references
 	 *
 	 * the lock ordering is:
 	 *	ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock
@@ -66,7 +65,6 @@ struct usbnet_private {
 	kmutex_t		unp_core_lock;
 	kmutex_t		unp_rxlock;
 	kmutex_t		unp_txlock;
-	kcondvar_t		unp_detachcv;
 
 	struct usbnet_cdata	unp_cdata;
 
@@ -83,7 +81,6 @@ struct usbnet_private {
 	bool			unp_ifp_attached;
 	bool			unp_link;
 
-	int			unp_refcnt;
 	int			unp_timer;
 	unsigned short		unp_if_flags;
 	unsigned		unp_number;
@@ -883,27 +880,6 @@ out:
 	return error;
 }
 
-void
-usbnet_busy(struct usbnet *un)
-{
-	struct usbnet_private * const unp = un->un_pri;
-
-	usbnet_isowned_core(un);
-
-	unp->unp_refcnt++;
-}
-
-void
-usbnet_unbusy(struct usbnet *un)
-{
-	struct usbnet_private * const unp = un->un_pri;
-
-	usbnet_isowned_core(un);
-
-	if (--unp->unp_refcnt < 0)
-		cv_broadcast(&unp->unp_detachcv);
-}
-
 /* MII management. */
 
 int
@@ -1443,7 +1419,6 @@ 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);
-	cv_init(&unp->unp_detachcv, detname);
 
 	rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
 	    RND_TYPE_NET, RND_FLAG_DEFAULT);
@@ -1636,24 +1611,11 @@ usbnet_detach(device_t self, int flags)
 	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
 	    NULL);
 
-	mutex_enter(&unp->unp_core_lock);
-	unp->unp_refcnt--;
-	if (unp->unp_refcnt >= 0) {
-		aprint_error_dev(un->un_dev, "%d stragglers\n",
-		    unp->unp_refcnt + 1);
-	}
-	while (unp->unp_refcnt >= 0) {
-		/* Wait for processes to go away */
-		cv_wait(&unp->unp_detachcv, &unp->unp_core_lock);
-	}
-	mutex_exit(&unp->unp_core_lock);
-
 	usbnet_rx_list_free(un);
 	usbnet_tx_list_free(un);
 
 	rnd_detach_source(&unp->unp_rndsrc);
 
-	cv_destroy(&unp->unp_detachcv);
 	mutex_destroy(&unp->unp_core_lock);
 	mutex_destroy(&unp->unp_rxlock);
 	mutex_destroy(&unp->unp_txlock);
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index fc9c578453a5..10597c14d7b5 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -177,9 +177,6 @@ typedef void (*usbnet_intr_cb)(struct usbnet *, usbd_status);
  * explicitly to uno_override_ioctl; for all other devices, they are
  * handled inside usbnet by scheduling a task to asynchronously call
  * uno_mcast with IFNET_LOCK held.
- *
- * Busy reference counts are maintained across calls to: uno_stop,
- * uno_read_reg, uno_write_reg, uno_statchg, and uno_tick.
  */
 struct usbnet_ops {
 	usbnet_stop_cb		uno_stop;		/* C */
@@ -314,9 +311,6 @@ usbnet_isowned_core(struct usbnet *un)
 	KASSERT(mutex_owned(usbnet_mutex_core(un)));
 }
 
-void	usbnet_busy(struct usbnet *);
-void	usbnet_unbusy(struct usbnet *);
-
 void	usbnet_lock_rx(struct usbnet *);
 void	usbnet_unlock_rx(struct usbnet *);
 kmutex_t *usbnet_mutex_rx(struct usbnet *);

From 424beef55157e6be23a66eaea4ed6f9fccb1f6af Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 14:02:30 +0000
Subject: [PATCH 43/75] usbnet: Make the tx/rx locks private to usbnet.c.

Suffice it for the drivers to know that uno_tx_prepare and
uno_rx_loop have exclusive access to the chain, and, for tx,
exclusive access to the mbuf.
---
 sys/dev/usb/if_axe.c  |  2 --
 sys/dev/usb/if_cdce.c |  2 --
 sys/dev/usb/usbnet.c  | 41 ++++++++++-------------------------------
 sys/dev/usb/usbnet.h  | 18 ------------------
 4 files changed, 10 insertions(+), 53 deletions(-)

diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 5f65f8d53085..ea22523cc651 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1136,8 +1136,6 @@ axe_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 	size_t hdr_len = 0, tlr_len = 0;
 	int length, boundary;
 
-	usbnet_isowned_tx(un);
-
 	if (!AXE_IS_172(un)) {
 		/*
 		 * Copy the mbuf data into a contiguous buffer, leaving two
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 4785d9a4cc9e..c7ee6bef2792 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -277,8 +277,6 @@ cdce_uno_rx_loop(struct usbnet * un, struct usbnet_chain *c, uint32_t total_len)
 {
 	struct ifnet		*ifp = usbnet_ifp(un);
 
-	usbnet_isowned_rx(un);
-
 	/* Strip off CRC added by Zaurus, if present */
 	if (un->un_flags & CDCE_ZAURUS && total_len > 4)
 		total_len -= 4;
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 7b5cc90fe0e9..f780e98cee9f 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -96,6 +96,9 @@ struct usbnet_private {
 
 volatile unsigned usbnet_number;
 
+static void usbnet_isowned_rx(struct usbnet *);
+static void usbnet_isowned_tx(struct usbnet *);
+
 static int usbnet_modcmd(modcmd_t, void *);
 
 #ifdef USB_DEBUG
@@ -1323,46 +1326,22 @@ usbnet_unlock_core(struct usbnet *un)
 	mutex_exit(&un->un_pri->unp_core_lock);
 }
 
-kmutex_t *
+kmutex_t*
 usbnet_mutex_core(struct usbnet *un)
 {
 	return &un->un_pri->unp_core_lock;
 }
 
-void
-usbnet_lock_rx(struct usbnet *un)
-{
-	mutex_enter(&un->un_pri->unp_rxlock);
-}
-
-void
-usbnet_unlock_rx(struct usbnet *un)
-{
-	mutex_exit(&un->un_pri->unp_rxlock);
-}
-
-kmutex_t *
-usbnet_mutex_rx(struct usbnet *un)
-{
-	return &un->un_pri->unp_rxlock;
-}
-
-void
-usbnet_lock_tx(struct usbnet *un)
-{
-	mutex_enter(&un->un_pri->unp_txlock);
-}
-
-void
-usbnet_unlock_tx(struct usbnet *un)
+static void
+usbnet_isowned_rx(struct usbnet *un)
 {
-	mutex_exit(&un->un_pri->unp_txlock);
+	KASSERT(mutex_owned(&un->un_pri->unp_rxlock));
 }
 
-kmutex_t *
-usbnet_mutex_tx(struct usbnet *un)
+static void
+usbnet_isowned_tx(struct usbnet *un)
 {
-	return &un->un_pri->unp_txlock;
+	KASSERT(mutex_owned(&un->un_pri->unp_txlock));
 }
 
 /* Autoconf management. */
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 10597c14d7b5..9ce537d7b849 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -311,24 +311,6 @@ usbnet_isowned_core(struct usbnet *un)
 	KASSERT(mutex_owned(usbnet_mutex_core(un)));
 }
 
-void	usbnet_lock_rx(struct usbnet *);
-void	usbnet_unlock_rx(struct usbnet *);
-kmutex_t *usbnet_mutex_rx(struct usbnet *);
-static __inline__ void
-usbnet_isowned_rx(struct usbnet *un)
-{
-	KASSERT(mutex_owned(usbnet_mutex_rx(un)));
-}
-
-void	usbnet_lock_tx(struct usbnet *);
-void	usbnet_unlock_tx(struct usbnet *);
-kmutex_t *usbnet_mutex_tx(struct usbnet *);
-static __inline__ void
-usbnet_isowned_tx(struct usbnet *un)
-{
-	KASSERT(mutex_owned(usbnet_mutex_tx(un)));
-}
-
 /*
  * Endpoint / rx/tx chain management:
  *

From a5171358f7e37ae2ef08a526bca9571d61a37431 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 14:09:23 +0000
Subject: [PATCH 44/75] usbnet: No need for the core lock in usbnet_ifflags_cb.

The only state this touches is unp_if_flags, and all paths touching
it also hold IFNET_LOCK -- not to mention this is the only path that
touches unp_if_flags in the first place!
---
 sys/dev/usb/usbnet.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index f780e98cee9f..abe51ceee46d 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -987,8 +987,6 @@ usbnet_ifflags_cb(struct ethercom *ec)
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
-	mutex_enter(&unp->unp_core_lock);
-
 	const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
 	if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) {
 		unp->unp_if_flags = ifp->if_flags;
@@ -998,8 +996,6 @@ usbnet_ifflags_cb(struct ethercom *ec)
 		rv = ENETRESET;
 	}
 
-	mutex_exit(&unp->unp_core_lock);
-
 	return rv;
 }
 

From 7d6385d772c8b1d4e63e7545978f32a656527945 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 14:26:56 +0000
Subject: [PATCH 45/75] usbnet drivers: Omit needless uno_init locked
 subroutines.

uno_init is now called with the core lock already held so there is no
need for a separate locked subroutine.
---
 sys/dev/usb/if_aue.c  | 12 +-----------
 sys/dev/usb/if_axe.c  | 10 +---------
 sys/dev/usb/if_axen.c | 10 +---------
 sys/dev/usb/if_cue.c  | 12 +-----------
 sys/dev/usb/if_kue.c  | 12 +-----------
 sys/dev/usb/if_mos.c  | 10 +---------
 sys/dev/usb/if_mue.c  | 12 +-----------
 sys/dev/usb/if_smsc.c |  9 ---------
 sys/dev/usb/if_ure.c  | 10 +---------
 sys/dev/usb/if_url.c  | 10 +---------
 10 files changed, 9 insertions(+), 98 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 058c8ae923be..9cc622a1368c 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -954,7 +954,7 @@ aue_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-aue_init_locked(struct ifnet *ifp)
+aue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	struct aue_softc	*sc = usbnet_softc(un);
@@ -1000,16 +1000,6 @@ aue_init_locked(struct ifnet *ifp)
 	return rv;
 }
 
-static int
-aue_uno_init(struct ifnet *ifp)
-{
-	int rv;
-
-	rv = aue_init_locked(ifp);
-
-	return rv;
-}
-
 static void
 aue_uno_mcast(struct ifnet *ifp)
 {
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index ea22523cc651..1286dcf944fb 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1210,7 +1210,7 @@ axe_csum_cfg(struct axe_softc *sc)
 }
 
 static int
-axe_init_locked(struct ifnet *ifp)
+axe_uno_init(struct ifnet *ifp)
 {
 	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
@@ -1306,14 +1306,6 @@ axe_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-axe_uno_init(struct ifnet *ifp)
-{
-	int ret = axe_init_locked(ifp);
-
-	return ret;
-}
-
 static void
 axe_uno_mcast(struct ifnet *ifp)
 {
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index ae07b34b0daa..6db947dd38e2 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -894,7 +894,7 @@ axen_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-axen_init_locked(struct ifnet *ifp)
+axen_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	uint16_t rxmode;
@@ -932,14 +932,6 @@ axen_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-axen_uno_init(struct ifnet *ifp)
-{
-	int ret = axen_init_locked(ifp);
-
-	return ret;
-}
-
 static void
 axen_uno_stop(struct ifnet *ifp, int disable)
 {
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 646cef9178b5..6fc62f924e10 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -613,7 +613,7 @@ cue_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-cue_init_locked(struct ifnet *ifp)
+cue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	int			i, ctl;
@@ -667,16 +667,6 @@ cue_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-cue_uno_init(struct ifnet *ifp)
-{
-	int rv;
-
-	rv = cue_init_locked(ifp);
-
-	return rv;
-}
-
 static void
 cue_uno_mcast(struct ifnet *ifp)
 {
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index c02f40a77dcf..265c981f4d41 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -591,7 +591,7 @@ kue_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-kue_init_locked(struct ifnet *ifp)
+kue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	struct kue_softc	*sc = usbnet_softc(un);
@@ -627,16 +627,6 @@ kue_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-kue_uno_init(struct ifnet *ifp)
-{
-	int rv;
-
-	rv = kue_init_locked(ifp);
-
-	return rv;
-}
-
 static void
 kue_uno_mcast(struct ifnet *ifp)
 {
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 1c27bcb6f308..2e0799a15ebd 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -723,7 +723,7 @@ mos_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-mos_init_locked(struct ifnet *ifp)
+mos_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	u_int8_t		rxmode;
@@ -759,14 +759,6 @@ mos_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-mos_uno_init(struct ifnet *ifp)
-{
-	int ret = mos_init_locked(ifp);
-
-	return ret;
-}
-
 static void
 mos_uno_mcast(struct ifnet *ifp)
 {
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index d6ab282aa2d7..3f360bded2f6 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1223,7 +1223,7 @@ mue_uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
 }
 
 static int
-mue_init_locked(struct ifnet *ifp)
+mue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
@@ -1253,16 +1253,6 @@ mue_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-mue_uno_init(struct ifnet *ifp)
-{
-	int rv;
-
-	rv = mue_init_locked(ifp);
-
-	return rv;
-}
-
 static int
 mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index a48336f8b972..7fac2e4a2497 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -174,7 +174,6 @@ static int	 smsc_chip_init(struct usbnet *);
 static int	 smsc_setmacaddress(struct usbnet *, const uint8_t *);
 
 static int	 smsc_uno_init(struct ifnet *);
-static int	 smsc_init_locked(struct ifnet *);
 static void	 smsc_uno_stop(struct ifnet *, int);
 
 static void	 smsc_reset(struct smsc_softc *);
@@ -553,14 +552,6 @@ smsc_reset(struct smsc_softc *sc)
 
 static int
 smsc_uno_init(struct ifnet *ifp)
-{
-	int ret = smsc_init_locked(ifp);
-
-	return ret;
-}
-
-static int
-smsc_init_locked(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 45b18f48438c..7a49d558a6c7 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -408,7 +408,7 @@ ure_reset(struct usbnet *un)
 }
 
 static int
-ure_init_locked(struct ifnet *ifp)
+ure_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	uint8_t eaddr[8];
@@ -453,14 +453,6 @@ ure_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-ure_uno_init(struct ifnet *ifp)
-{
-	int ret = ure_init_locked(ifp);
-
-	return ret;
-}
-
 static void
 ure_uno_stop(struct ifnet *ifp, int disable __unused)
 {
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 82f1772f180f..b525a7a02f85 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -363,7 +363,7 @@ url_csr_write_4(struct usbnet *un, int reg, int aval)
 }
 
 static int
-url_init_locked(struct ifnet *ifp)
+url_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	const u_char *eaddr;
@@ -401,14 +401,6 @@ url_init_locked(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static int
-url_uno_init(struct ifnet *ifp)
-{
-	int ret = url_init_locked(ifp);
-
-	return ret;
-}
-
 static void
 url_reset(struct usbnet *un)
 {

From 7762faeed6ef9ab4778f533aaa8f860f5a26c445 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 23:17:18 +0000
Subject: [PATCH 46/75] usbnet: Take the core lock around uno_mcast.

Every driver does this already.  This will enable us to change the
lock that serializes access to the registers so we can go back to
doing this synchronously in SIOCADDMULTI/SIOCDELMULTI.
---
 sys/dev/usb/if_aue.c  | 3 ---
 sys/dev/usb/if_axe.c  | 4 ----
 sys/dev/usb/if_axen.c | 4 ----
 sys/dev/usb/if_cue.c  | 4 ----
 sys/dev/usb/if_kue.c  | 4 ----
 sys/dev/usb/if_mos.c  | 4 ----
 sys/dev/usb/if_mue.c  | 4 ----
 sys/dev/usb/if_smsc.c | 4 ----
 sys/dev/usb/if_udav.c | 4 ----
 sys/dev/usb/if_ure.c  | 4 ----
 sys/dev/usb/if_url.c  | 4 ----
 sys/dev/usb/usbnet.c  | 5 ++++-
 12 files changed, 4 insertions(+), 44 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 9cc622a1368c..6fa49edb4962 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -1003,7 +1003,6 @@ aue_uno_init(struct ifnet *ifp)
 static void
 aue_uno_mcast(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
 
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGSN(5, "aue%jd: enter",
@@ -1014,9 +1013,7 @@ aue_uno_mcast(struct ifnet *ifp)
 	 * XXX I feel like this is pretty heavy-handed!  Maybe we could
 	 * make do with aue_setiff_locked instead?
 	 */
-	usbnet_lock_core(un);
 	aue_uno_init(ifp);
-	usbnet_unlock_core(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 1286dcf944fb..224eac042771 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1311,11 +1311,7 @@ axe_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	axe_rcvfilt_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 6db947dd38e2..0e3334022a4e 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -574,11 +574,7 @@ axen_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	axen_setiff_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 static int
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 6fc62f924e10..828ea1085584 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -672,11 +672,7 @@ cue_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	cue_setiff_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 /* Stop and reset the adapter.  */
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 265c981f4d41..048a718095a3 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -632,11 +632,7 @@ kue_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	kue_setiff_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 #ifdef _MODULE
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 2e0799a15ebd..df70e79b535a 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -764,11 +764,7 @@ mos_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	mos_rcvfilt_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 void
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 3f360bded2f6..201a4dc3f856 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1281,11 +1281,7 @@ mue_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	mue_setiff_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 7fac2e4a2497..429c2b939899 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -762,11 +762,7 @@ smsc_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	smsc_setiff_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 static int
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 77f9bd353c5e..3528653f3794 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -716,11 +716,7 @@ udav_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	udav_setiff_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 7a49d558a6c7..37e3611f9c87 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -795,11 +795,7 @@ ure_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	ure_rcvfilt_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 static int
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index b525a7a02f85..35bb1e7016f2 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -550,11 +550,7 @@ url_uno_mcast(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	url_rcvfilt_locked(un);
-
-	usbnet_unlock_core(un);
 }
 
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index abe51ceee46d..31562ec087a7 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1060,8 +1060,11 @@ usbnet_mcast_task(void *arg)
 	 */
 	IFNET_LOCK(ifp);
 	if (ifp->if_flags & IFF_RUNNING) {
-		if (un->un_ops->uno_mcast)
+		if (un->un_ops->uno_mcast) {
+			mutex_enter(&un->un_pri->unp_core_lock);
 			(*un->un_ops->uno_mcast)(ifp);
+			mutex_exit(&un->un_pri->unp_core_lock);
+		}
 	}
 	IFNET_UNLOCK(ifp);
 }

From 518017e19b7029364ed6d08c14dacc6b04e94514 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 23:36:08 +0000
Subject: [PATCH 47/75] aue(4): Reduce aue_uno_mcast from aue_uno_init to
 aue_setiff_locked.

This operation only needs to update the hardware to reflect
SIOCADDMULTI/SIOCDELMULTI.  Not clear that everything in aue(4) needs
to be reset -- in fact I'm pretty sure that's undesirable!

WARNING: I have not tested this with a real aue(4) device.
---
 sys/dev/usb/if_aue.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 6fa49edb4962..9292d1e73e0c 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -1009,11 +1009,7 @@ aue_uno_mcast(struct ifnet *ifp)
 	    device_unit(((struct usbnet *)(ifp->if_softc))->un_dev),
 	    0, 0, 0);
 
-	/*
-	 * XXX I feel like this is pretty heavy-handed!  Maybe we could
-	 * make do with aue_setiff_locked instead?
-	 */
-	aue_uno_init(ifp);
+	aue_setiff_locked(ifp);
 }
 
 static void

From 7be56f315b265208db8c4aadec0592f97a7a48b7 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Thu, 30 Dec 2021 23:42:29 +0000
Subject: [PATCH 48/75] usbnet drivers: Omit needless uno_mcast locked
 subroutines.

uno_mcast is now called with the core lock already held so there is
no need for a separate locked subroutine.
---
 sys/dev/usb/if_aue.c  | 18 +++---------------
 sys/dev/usb/if_axe.c  | 14 +++-----------
 sys/dev/usb/if_axen.c | 14 +++-----------
 sys/dev/usb/if_cue.c  | 14 +++-----------
 sys/dev/usb/if_kue.c  | 14 +++-----------
 sys/dev/usb/if_mos.c  | 14 +++-----------
 sys/dev/usb/if_mue.c  | 17 ++++-------------
 sys/dev/usb/if_smsc.c | 14 +++-----------
 sys/dev/usb/if_udav.c | 15 +++------------
 sys/dev/usb/if_ure.c  | 14 +++-----------
 sys/dev/usb/if_url.c  | 17 ++++-------------
 11 files changed, 35 insertions(+), 130 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 9292d1e73e0c..7d1994cfb451 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -611,10 +611,10 @@ aue_crc(void *addrv)
 }
 
 static void
-aue_setiff_locked(struct usbnet *un)
+aue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet * const un = ifp->if_softc;
 	struct aue_softc * const sc = usbnet_softc(un);
-	struct ifnet * const	ifp = usbnet_ifp(un);
 	struct ethercom *	ec = usbnet_ec(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
@@ -988,7 +988,7 @@ aue_uno_init(struct ifnet *ifp)
 	rv = usbnet_init_rx_tx(un);
 
 	/* Load the multicast filter. */
-	aue_setiff_locked(un);
+	aue_uno_mcast(ifp);
 
 	/* Enable RX and TX */
 	aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
@@ -1000,18 +1000,6 @@ aue_uno_init(struct ifnet *ifp)
 	return rv;
 }
 
-static void
-aue_uno_mcast(struct ifnet *ifp)
-{
-
-	AUEHIST_FUNC();
-	AUEHIST_CALLARGSN(5, "aue%jd: enter",
-	    device_unit(((struct usbnet *)(ifp->if_softc))->un_dev),
-	    0, 0, 0);
-
-	aue_setiff_locked(ifp);
-}
-
 static void
 aue_uno_stop(struct ifnet *ifp, int disable)
 {
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 224eac042771..cb42c4ce17a3 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -426,11 +426,11 @@ axe_uno_mii_statchg(struct ifnet *ifp)
 }
 
 static void
-axe_rcvfilt_locked(struct usbnet *un)
+axe_uno_mcast(struct ifnet *ifp)
 {
 	AXEHIST_FUNC(); AXEHIST_CALLED();
+	struct usbnet * const un = ifp->if_softc;
 	struct axe_softc * const sc = usbnet_softc(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -1301,19 +1301,11 @@ axe_uno_init(struct ifnet *ifp)
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
 
 	/* Accept multicast frame or run promisc. mode */
-	axe_rcvfilt_locked(un);
+	axe_uno_mcast(ifp);
 
 	return usbnet_init_rx_tx(un);
 }
 
-static void
-axe_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	axe_rcvfilt_locked(un);
-}
-
 static void
 axe_uno_stop(struct ifnet *ifp, int disable)
 {
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 0e3334022a4e..55ffb9335e3b 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -225,9 +225,9 @@ axen_uno_mii_statchg(struct ifnet *ifp)
 }
 
 static void
-axen_setiff_locked(struct usbnet *un)
+axen_uno_mcast(struct ifnet *ifp)
 {
-	struct ifnet * const ifp = usbnet_ifp(un);
+	struct usbnet * const un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -569,14 +569,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return 0;
 }
 
-static void
-axen_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	axen_setiff_locked(un);
-}
-
 static int
 axen_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -916,7 +908,7 @@ axen_uno_init(struct ifnet *ifp)
 	axen_setoe_locked(un);
 
 	/* Program promiscuous mode and multicast filters. */
-	axen_setiff_locked(un);
+	axen_uno_mcast(ifp);
 
 	/* Enable receiver, set RX mode */
 	axen_cmd(un, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval);
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 828ea1085584..cf85b72d3d57 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -357,11 +357,11 @@ cue_crc(const char *addr)
 }
 
 static void
-cue_setiff_locked(struct usbnet *un)
+cue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet		*un = ifp->if_softc;
 	struct cue_softc	*sc = usbnet_softc(un);
 	struct ethercom		*ec = usbnet_ec(un);
-	struct ifnet		*ifp = usbnet_ifp(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
 	uint32_t		h, i;
@@ -648,7 +648,7 @@ cue_uno_init(struct ifnet *ifp)
 	cue_csr_write_1(un, CUE_ETHCTL, ctl);
 
 	/* Load the multicast filter. */
-	cue_setiff_locked(un);
+	cue_uno_mcast(ifp);
 
 	/*
 	 * Set the number of RX and TX buffers that we want
@@ -667,14 +667,6 @@ cue_uno_init(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static void
-cue_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const	un = ifp->if_softc;
-
-	cue_setiff_locked(un);
-}
-
 /* Stop and reset the adapter.  */
 static void
 cue_uno_stop(struct ifnet *ifp, int disable)
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 048a718095a3..be075ed5e65f 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -318,11 +318,11 @@ kue_load_fw(struct usbnet *un)
 }
 
 static void
-kue_setiff_locked(struct usbnet *un)
+kue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet *		un = ifp->if_softc;
 	struct ethercom *	ec = usbnet_ec(un);
 	struct kue_softc *	sc = usbnet_softc(un);
-	struct ifnet * const	ifp = usbnet_ifp(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
 	int			i;
@@ -622,19 +622,11 @@ kue_uno_init(struct ifnet *ifp)
 	kue_setword(un, KUE_CMD_SET_URB_SIZE, 64);
 
 	/* Load the multicast filter. */
-	kue_setiff_locked(un);
+	kue_uno_mcast(ifp);
 
 	return usbnet_init_rx_tx(un);
 }
 
-static void
-kue_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const	un = ifp->if_softc;
-
-	kue_setiff_locked(un);
-}
-
 #ifdef _MODULE
 #include "ioconf.c"
 #endif
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index df70e79b535a..afe02a06ed22 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -458,9 +458,9 @@ mos_uno_mii_statchg(struct ifnet *ifp)
 }
 
 static void
-mos_rcvfilt_locked(struct usbnet *un)
+mos_uno_mcast(struct ifnet *ifp)
 {
-	struct ifnet		*ifp = usbnet_ifp(un);
+	struct usbnet		*un = ifp->if_softc;
 	struct ethercom		*ec = usbnet_ec(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
@@ -748,7 +748,7 @@ mos_uno_init(struct ifnet *ifp)
 	mos_reg_write_1(un, MOS_IPG1, ipgs[1]);
 
 	/* Accept multicast frame or run promisc. mode */
-	mos_rcvfilt_locked(un);
+	mos_uno_mcast(ifp);
 
 	/* Enable receiver and transmitter, bridge controls speed/duplex mode */
 	rxmode = mos_reg_read_1(un, MOS_CTL);
@@ -759,14 +759,6 @@ mos_uno_init(struct ifnet *ifp)
 	return usbnet_init_rx_tx(un);
 }
 
-static void
-mos_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	mos_rcvfilt_locked(un);
-}
-
 void
 mos_uno_stop(struct ifnet *ifp, int disable)
 {
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 201a4dc3f856..0f5c29421afc 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -92,14 +92,13 @@ static int	mue_chip_init(struct usbnet *);
 static void	mue_set_macaddr(struct usbnet *);
 static int	mue_get_macaddr(struct usbnet *, prop_dictionary_t);
 static int	mue_prepare_tso(struct usbnet *, struct mbuf *);
-static void	mue_setiff_locked(struct usbnet *);
+static void	mue_uno_mcast(struct ifnet *);
 static void	mue_sethwcsum_locked(struct usbnet *);
 static void	mue_setmtu_locked(struct usbnet *);
 static void	mue_reset(struct usbnet *);
 
 static void	mue_uno_stop(struct ifnet *, int);
 static int	mue_uno_ioctl(struct ifnet *, u_long, void *);
-static void	mue_uno_mcast(struct ifnet *);
 static int	mue_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	mue_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void	mue_uno_mii_statchg(struct ifnet *);
@@ -997,10 +996,10 @@ mue_prepare_tso(struct usbnet *un, struct mbuf *m)
 }
 
 static void
-mue_setiff_locked(struct usbnet *un)
+mue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet *un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	const uint8_t *enaddr = CLLADDR(ifp->if_sadl);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -1242,7 +1241,7 @@ mue_uno_init(struct ifnet *ifp)
 	mue_set_macaddr(un);
 
 	/* Load the multicast filter. */
-	mue_setiff_locked(un);
+	mue_uno_mcast(ifp);
 
 	/* TCP/UDP checksum offload engines. */
 	mue_sethwcsum_locked(un);
@@ -1276,14 +1275,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return 0;
 }
 
-static void
-mue_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	mue_setiff_locked(un);
-}
-
 static void
 mue_reset(struct usbnet *un)
 {
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 429c2b939899..57c4f08b77f0 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -411,11 +411,11 @@ smsc_hash(uint8_t addr[ETHER_ADDR_LEN])
 }
 
 static void
-smsc_setiff_locked(struct usbnet *un)
+smsc_uno_mcast(struct ifnet *ifp)
 {
 	USMSCHIST_FUNC(); USMSCHIST_CALLED();
+	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -568,7 +568,7 @@ smsc_uno_init(struct ifnet *ifp)
 	smsc_reset(sc);
 
 	/* Load the multicast filter. */
-	smsc_setiff_locked(un);
+	smsc_uno_mcast(ifp);
 
 	/* TCP/UDP checksum offload engines. */
 	smsc_setoe_locked(un);
@@ -757,14 +757,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return 0;
 }
 
-static void
-smsc_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	smsc_setiff_locked(un);
-}
-
 static int
 smsc_match(device_t parent, cfdata_t match, void *aux)
 {
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 3528653f3794..791939e5d358 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -74,7 +74,6 @@ static int udav_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int udav_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void udav_uno_mii_statchg(struct ifnet *);
 static int udav_uno_init(struct ifnet *);
-static void udav_setiff_locked(struct usbnet *);
 static void udav_reset(struct usbnet *);
 
 static int udav_csr_read(struct usbnet *, int, void *, int);
@@ -501,7 +500,7 @@ udav_uno_init(struct ifnet *ifp)
 		UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 
 	/* Load the multicast filter */
-	udav_setiff_locked(un);
+	udav_uno_mcast(ifp);
 
 	/* Enable RX */
 	UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_RXEN);
@@ -576,10 +575,10 @@ udav_chip_init(struct usbnet *un)
 	(ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1))
 
 static void
-udav_setiff_locked(struct usbnet *un)
+udav_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet * const un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
 	uint8_t hashes[8];
@@ -711,14 +710,6 @@ udav_uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
 	usbnet_enqueue(un, buf, pkt_len, 0, 0, 0);
 }
 
-static void
-udav_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	udav_setiff_locked(un);
-}
-
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
 static void
 udav_uno_stop(struct ifnet *ifp, int disable)
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 37e3611f9c87..8d6da56a603a 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -331,10 +331,10 @@ ure_uno_miibus_statchg(struct ifnet *ifp)
 }
 
 static void
-ure_rcvfilt_locked(struct usbnet *un)
+ure_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet *un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
-	struct ifnet *ifp = usbnet_ifp(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
 	uint32_t mchash[2] = { 0, 0 };
@@ -448,7 +448,7 @@ ure_uno_init(struct ifnet *ifp)
 	    ~URE_RXDY_GATED_EN);
 
 	/* Accept multicast frame or run promisc. mode. */
-	ure_rcvfilt_locked(un);
+	ure_uno_mcast(ifp);
 
 	return usbnet_init_rx_tx(un);
 }
@@ -790,14 +790,6 @@ ure_init_fifo(struct usbnet *un)
 	    URE_TXFIFO_THR_NORMAL);
 }
 
-static void
-ure_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	ure_rcvfilt_locked(un);
-}
-
 static int
 ure_match(device_t parent, cfdata_t match, void *aux)
 {
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 35bb1e7016f2..df856a563bc4 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -77,11 +77,10 @@ static unsigned	url_uno_tx_prepare(struct usbnet *, struct mbuf *,
 static void url_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static int url_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int url_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
-static void url_uno_mcast(struct ifnet *);
 static void url_uno_stop(struct ifnet *, int);
 static void url_uno_mii_statchg(struct ifnet *);
 static int url_uno_init(struct ifnet *);
-static void url_rcvfilt_locked(struct usbnet *);
+static void url_uno_mcast(struct ifnet *);
 static void url_reset(struct usbnet *);
 
 static int url_csr_read_1(struct usbnet *, int);
@@ -393,7 +392,7 @@ url_uno_init(struct ifnet *ifp)
 	URL_SETBIT2(un, URL_RCR, URL_RCR_TAIL | URL_RCR_AD | URL_RCR_AB);
 
 	/* Accept multicast frame or run promisc. mode */
-	url_rcvfilt_locked(un);
+	url_uno_mcast(ifp);
 
 	/* Enable RX and TX */
 	URL_SETBIT(un, URL_CR, URL_CR_TE | URL_CR_RE);
@@ -425,9 +424,9 @@ url_reset(struct usbnet *un)
 }
 
 static void
-url_rcvfilt_locked(struct usbnet *un)
+url_uno_mcast(struct ifnet *ifp)
 {
-	struct ifnet * const ifp = usbnet_ifp(un);
+	struct usbnet * const un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -545,14 +544,6 @@ static void url_intr(void)
 }
 #endif
 
-static void
-url_uno_mcast(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	url_rcvfilt_locked(un);
-}
-
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
 static void
 url_uno_stop(struct ifnet *ifp, int disable)

From 6033fe59838bf0780d418be033ff30e801275c3e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 31 Dec 2021 00:48:54 +0000
Subject: [PATCH 49/75] usbnet drivers: Stop abusing ifp->if_flags &
 IFF_ALLMULTI.

This legacy flag is a figment of userland's imagination.  The actual
kernel state is ec->ec_flags & ETHER_F_ALLMULTI, protected by the
ETHER_LOCK, so that multicast filter updates -- which run without
IFNET_LOCK -- need not attempt to write racily to ifp->if_flags.
---
 sys/dev/usb/if_aue.c  | 12 ++++++------
 sys/dev/usb/if_cue.c  |  8 ++++----
 sys/dev/usb/if_kue.c  |  8 ++++----
 sys/dev/usb/if_mue.c  |  9 ++++-----
 sys/dev/usb/if_smsc.c |  8 +++++---
 sys/dev/usb/if_udav.c | 12 ++++++++----
 6 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 7d1994cfb451..bd9226a3ba67 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -627,21 +627,20 @@ aue_uno_mcast(struct ifnet *ifp)
 	usbnet_isowned_core(un);
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
 		return;
 	}
 
-	AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
-
 	/* now program new ones */
 	ETHER_LOCK(ec);
 	ETHER_FIRST_MULTI(step, ec, enm);
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo,
 		    enm->enm_addrhi, ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -649,13 +648,14 @@ allmulti:
 		hashtbl[h >> 3] |= 1 << (h & 0x7);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
+	AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+
 	/* write the hashtable */
 	for (i = 0; i < 8; i++)
 		aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]);
-
-	ifp->if_flags &= ~IFF_ALLMULTI;
 }
 
 static void
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index cf85b72d3d57..a3902bc54679 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -370,8 +370,10 @@ cue_uno_mcast(struct ifnet *ifp)
 	    device_xname(un->un_dev), ifp->if_flags));
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		for (i = 0; i < CUE_MCAST_TABLE_LEN; i++)
 			sc->cue_mctab[i] = 0xFF;
 		cue_mem(un, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
@@ -389,7 +391,6 @@ allmulti:
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo,
 		    enm->enm_addrhi, ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -397,10 +398,9 @@ allmulti:
 		sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
-	ifp->if_flags &= ~IFF_ALLMULTI;
-
 	/*
 	 * Also include the broadcast address in the filter
 	 * so we can receive broadcast frames.
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index be075ed5e65f..15f32d81409f 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -336,8 +336,10 @@ kue_uno_mcast(struct ifnet *ifp)
 		sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC;
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI|KUE_RXFILT_PROMISC;
 		sc->kue_rxfilt &= ~KUE_RXFILT_MULTICAST;
 		kue_setword(un, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
@@ -353,7 +355,6 @@ allmulti:
 		if (i == KUE_MCFILTCNT(sc) ||
 		    memcmp(enm->enm_addrlo, enm->enm_addrhi,
 		    ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -361,10 +362,9 @@ allmulti:
 		ETHER_NEXT_MULTI(step, enm);
 		i++;
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
-	ifp->if_flags &= ~IFF_ALLMULTI;
-
 	sc->kue_rxfilt |= KUE_RXFILT_MULTICAST;
 	kue_ctl(un, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS,
 	    i, sc->kue_mcfilters, i * ETHER_ADDR_LEN);
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 0f5c29421afc..c63911009771 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1023,10 +1023,11 @@ mue_uno_mcast(struct ifnet *ifp)
 	/* Always accept broadcast frames. */
 	rxfilt |= MUE_RFE_CTL_BROADCAST;
 
+	ETHER_LOCK(ec);
 	if (ifp->if_flags & IFF_PROMISC) {
 		rxfilt |= MUE_RFE_CTL_UNICAST;
 allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
 		if (ifp->if_flags & IFF_PROMISC)
 			DPRINTF(un, "promisc\n");
 		else
@@ -1036,7 +1037,6 @@ allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 		pfiltbl[0][0] = MUE_ENADDR_HI(enaddr) | MUE_ADDR_FILTX_VALID;
 		pfiltbl[0][1] = MUE_ENADDR_LO(enaddr);
 		i = 1;
-		ETHER_LOCK(ec);
 		ETHER_FIRST_MULTI(step, ec, enm);
 		while (enm != NULL) {
 			if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
@@ -1044,7 +1044,6 @@ allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 				memset(pfiltbl, 0, sizeof(pfiltbl));
 				memset(hashtbl, 0, sizeof(hashtbl));
 				rxfilt &= ~MUE_RFE_CTL_MULTICAST_HASH;
-				ETHER_UNLOCK(ec);
 				goto allmulti;
 			}
 			if (i < MUE_NUM_ADDR_FILTX) {
@@ -1062,14 +1061,14 @@ allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 			i++;
 			ETHER_NEXT_MULTI(step, enm);
 		}
-		ETHER_UNLOCK(ec);
+		ec->ec_flags &= ~ETHER_F_ALLMULTI;
 		rxfilt |= MUE_RFE_CTL_PERFECT;
-		ifp->if_flags &= ~IFF_ALLMULTI;
 		if (rxfilt & MUE_RFE_CTL_MULTICAST_HASH)
 			DPRINTF(un, "perfect filter and hash tables\n");
 		else
 			DPRINTF(un, "perfect filter\n");
 	}
+	ETHER_UNLOCK(ec);
 
 	for (i = 0; i < MUE_NUM_ADDR_FILTX; i++) {
 		hireg = (un->un_flags & LAN7500) ?
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 57c4f08b77f0..751d0c982eb7 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -427,8 +427,11 @@ smsc_uno_mcast(struct ifnet *ifp)
 	if (usbnet_isdying(un))
 		return;
 
-	if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		DPRINTF("receive all multicast enabled", 0, 0, 0, 0);
 		sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
 		sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT;
@@ -443,7 +446,6 @@ allmulti:
 	ETHER_FIRST_MULTI(step, ec, enm);
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -451,6 +453,7 @@ allmulti:
 		hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
 	/* Debug */
@@ -463,7 +466,6 @@ allmulti:
 	/* Write the hash table and mac control registers */
 
 	//XXX should we be doing this?
-	ifp->if_flags &= ~IFF_ALLMULTI;
 	smsc_writereg(un, SMSC_HASHH, hashtbl[1]);
 	smsc_writereg(un, SMSC_HASHL, hashtbl[0]);
 	smsc_writereg(un, SMSC_MAC_CSR, sc->sc_mac_csr);
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 791939e5d358..32978d4d2dd5 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -598,11 +598,16 @@ udav_uno_mcast(struct ifnet *ifp)
 	}
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 		return;
-	} else if (ifp->if_flags & IFF_ALLMULTI) {
+	} else if (ifp->if_flags & IFF_ALLMULTI) { /* XXX ??? Can't happen? */
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_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;
@@ -619,7 +624,6 @@ allmulti:
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
 		    ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -627,10 +631,10 @@ allmulti:
 		hashes[h>>3] |= 1 << (h & 0x7);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
 	/* disable all multicast */
-	ifp->if_flags &= ~IFF_ALLMULTI;
 	UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL);
 
 	/* write hash value to the register */

From 3c095499ef9bf9ed73881b0a7f3cdbd16ddddb10 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 00:49:08 +0000
Subject: [PATCH 50/75] usbnet: Apply hardware multicast filter updates
 synchronously again.

To make this work:

1. Do it only under a new lock, unp_mcastlock.  This lock lives at
   IPL_SOFTCLOCK so it can be taken from network stack callouts.  It
   is forbidden to acquire the usbnet core lock under unp_mcastlock.

2. Do it only after usbnet_init_rx_tx and before usbnet_stop; if
   issued at any other time, drop the update on the floor.

3. Make usbnet_init_rx_tx apply any pending multicast filter updates
   under the lock before setting the flag that allows SIOCADDMULTI or
   SIOCDELMULTI to apply the updates.

4. Remove core lock asserts from various drivers' register access
   routines.  This is necessary because the multicast filter updates
   are done with register reads/writes, but _cannot_ take the core
   lock when the caller holds softnet_lock.

This now programs the hardware multicast filter redundantly in many
drivers which already explicitly call *_uno_mcast from the *_uno_init
routines.  This is probably harmless, but it will likely be better to
remove the explicit calls.
---
 sys/dev/usb/if_aue.c  |  10 ----
 sys/dev/usb/if_axe.c  |   2 -
 sys/dev/usb/if_axen.c |   4 --
 sys/dev/usb/if_smsc.c |   6 --
 sys/dev/usb/if_udav.c |   7 ---
 sys/dev/usb/if_ure.c  |   2 -
 sys/dev/usb/if_url.c  |   4 --
 sys/dev/usb/usbnet.c  | 124 +++++++++++++++---------------------------
 8 files changed, 43 insertions(+), 116 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index bd9226a3ba67..60e1551bfa77 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -284,8 +284,6 @@ aue_csr_read_1(struct aue_softc *sc, int reg)
 	usbd_status		err;
 	uByte			val = 0;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -315,8 +313,6 @@ aue_csr_read_2(struct aue_softc *sc, int reg)
 	usbd_status		err;
 	uWord			val;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -346,8 +342,6 @@ aue_csr_write_1(struct aue_softc *sc, int reg, int aval)
 	usbd_status		err;
 	uByte			val;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -378,8 +372,6 @@ aue_csr_write_2(struct aue_softc *sc, int reg, int aval)
 	usbd_status		err;
 	uWord			val;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -624,8 +616,6 @@ aue_uno_mcast(struct ifnet *ifp)
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGSN(5, "aue%jd: enter", device_unit(un->un_dev), 0, 0, 0);
 
-	usbnet_isowned_core(un);
-
 	if (ifp->if_flags & IFF_PROMISC) {
 		ETHER_LOCK(ec);
 allmulti:
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index cb42c4ce17a3..a62af4a7d4eb 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -293,8 +293,6 @@ axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return -1;
 
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 55ffb9335e3b..910ef4602199 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -108,8 +108,6 @@ axen_cmd(struct usbnet *un, int cmd, int index, int val, void *buf)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -239,8 +237,6 @@ axen_uno_mcast(struct ifnet *ifp)
 	if (usbnet_isdying(un))
 		return;
 
-	usbnet_isowned_core(un);
-
 	rxmode = 0;
 
 	/* Enable receiver, set RX mode */
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 751d0c982eb7..7de0a5e1fb27 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -211,8 +211,6 @@ smsc_readreg(struct usbnet *un, uint32_t off, uint32_t *data)
 	uint32_t buf;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -238,8 +236,6 @@ smsc_writereg(struct usbnet *un, uint32_t off, uint32_t data)
 	uint32_t buf;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -422,8 +418,6 @@ smsc_uno_mcast(struct ifnet *ifp)
 	uint32_t hashtbl[2] = { 0, 0 };
 	uint32_t hash;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 32978d4d2dd5..fb443e0afb33 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -364,7 +364,6 @@ udav_csr_read(struct usbnet *un, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
 	KASSERT(!usbnet_isdying(un));
 
 	DPRINTFN(0x200,
@@ -395,7 +394,6 @@ udav_csr_write(struct usbnet *un, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
 	KASSERT(!usbnet_isdying(un));
 
 	DPRINTFN(0x200,
@@ -424,8 +422,6 @@ udav_csr_read1(struct usbnet *un, int offset)
 {
 	uint8_t val = 0;
 
-	usbnet_isowned_core(un);
-
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
@@ -442,7 +438,6 @@ udav_csr_write1(struct usbnet *un, int offset, unsigned char ch)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
 	KASSERT(!usbnet_isdying(un));
 
 	DPRINTFN(0x200,
@@ -586,8 +581,6 @@ udav_uno_mcast(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 8d6da56a603a..7a2ebd311ded 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -340,8 +340,6 @@ ure_uno_mcast(struct ifnet *ifp)
 	uint32_t mchash[2] = { 0, 0 };
 	uint32_t h = 0, rxmode;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index df856a563bc4..9dbf779aea31 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -268,8 +268,6 @@ url_mem(struct usbnet *un, int cmd, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
@@ -435,8 +433,6 @@ url_uno_mcast(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 31562ec087a7..1c8210d07a8b 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -59,6 +59,7 @@ struct usbnet_private {
 	 *
 	 * 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
 	 */
@@ -66,11 +67,13 @@ struct usbnet_private {
 	kmutex_t		unp_rxlock;
 	kmutex_t		unp_txlock;
 
+	kmutex_t		unp_mcastlock;
+	bool			unp_mcastactive;
+
 	struct usbnet_cdata	unp_cdata;
 
 	struct ethercom		unp_ec;
 	struct mii_data		unp_mii;
-	struct usb_task		unp_mcasttask;
 	struct usb_task		unp_ticktask;
 	struct callout		unp_stat_ch;
 	struct usbd_pipe	*unp_ep[USBNET_ENDPT_MAX];
@@ -866,6 +869,17 @@ usbnet_init_rx_tx(struct usbnet * const un)
 	    "%s", ifp->if_xname);
 	ifp->if_flags |= IFF_RUNNING;
 
+	/*
+	 * If the hardware has a multicast filter, program it and then
+	 * allow updates to it while we're running.
+	 */
+	if (un->un_ops->uno_mcast) {
+		mutex_enter(&unp->unp_mcastlock);
+		(*un->un_ops->uno_mcast)(ifp);
+		unp->unp_mcastactive = true;
+		mutex_exit(&unp->unp_mcastlock);
+	}
+
 	/* Start up the receive pipe(s). */
 	usbnet_rx_start_pipes(un);
 
@@ -1018,8 +1032,20 @@ usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		switch (cmd) {
 		case SIOCADDMULTI:
 		case SIOCDELMULTI:
-			usb_add_task(un->un_udev, &unp->unp_mcasttask,
-			    USB_TASKQ_DRIVER);
+			/*
+			 * If there's a hardware multicast filter, and
+			 * it has been programmed by usbnet_init_rx_tx
+			 * and is active, update it now.  Otherwise,
+			 * drop the update on the floor -- it will be
+			 * observed by usbnet_init_rx_tx next time we
+			 * bring the interface up.
+			 */
+			if (un->un_ops->uno_mcast) {
+				mutex_enter(&unp->unp_mcastlock);
+				if (unp->unp_mcastactive)
+					(*un->un_ops->uno_mcast)(ifp);
+				mutex_exit(&unp->unp_mcastlock);
+			}
 			error = 0;
 			break;
 		default:
@@ -1030,45 +1056,6 @@ usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 	return error;
 }
 
-static void
-usbnet_mcast_task(void *arg)
-{
-	USBNETHIST_FUNC();
-	struct usbnet * const un = arg;
-	struct ifnet * const ifp = usbnet_ifp(un);
-
-	USBNETHIST_CALLARGSN(10, "%jd: enter",
-	    un->un_pri->unp_number, 0, 0, 0);
-
-	/*
-	 * If we're detaching, we must check usbnet_isdying _before_
-	 * touching IFNET_LOCK -- the ifnet may have been detached by
-	 * the time this task runs.  This is racy -- unp_dying may be
-	 * set immediately after we test it -- but nevertheless safe,
-	 * because usbnet_detach waits for the task to complete before
-	 * issuing if_detach, and necessary, so that we don't touch
-	 * IFNET_LOCK after if_detach.  See usbnet_detach for details.
-	 */
-	if (usbnet_isdying(un))
-		return;
-
-	/*
-	 * If the hardware is running, ask the driver to reprogram the
-	 * multicast filter.  If the hardware is not running, the
-	 * driver is responsible for programming the multicast filter
-	 * as part of its uno_init routine to bring the hardware up.
-	 */
-	IFNET_LOCK(ifp);
-	if (ifp->if_flags & IFF_RUNNING) {
-		if (un->un_ops->uno_mcast) {
-			mutex_enter(&un->un_pri->unp_core_lock);
-			(*un->un_ops->uno_mcast)(ifp);
-			mutex_exit(&un->un_pri->unp_core_lock);
-		}
-	}
-	IFNET_UNLOCK(ifp);
-}
-
 /*
  * Generic stop network function:
  *	- mark as stopping
@@ -1093,6 +1080,18 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	    "%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.
+	 */
+	if (un->un_ops->uno_mcast) {
+		mutex_enter(&unp->unp_mcastlock);
+		unp->unp_mcastactive = false;
+		mutex_exit(&unp->unp_mcastlock);
+	}
+
 	/*
 	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
 	 * clear the watchdog timer.
@@ -1387,8 +1386,6 @@ usbnet_attach(struct usbnet *un,
 	un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP);
 	struct usbnet_private * const unp = un->un_pri;
 
-	usb_init_task(&unp->unp_mcasttask, usbnet_mcast_task, un,
-	    USB_TASKQ_MPSAFE);
 	usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un,
 	    USB_TASKQ_MPSAFE);
 	callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE);
@@ -1397,6 +1394,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_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 
 	rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
 	    RND_TYPE_NET, RND_FLAG_DEFAULT);
@@ -1539,9 +1537,6 @@ usbnet_detach(device_t self, int flags)
 	KASSERT(!callout_pending(&unp->unp_stat_ch));
 	KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask));
 
-	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
-	    NULL);
-
 	if (mii) {
 		mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
 		ifmedia_fini(&mii->mii_media);
@@ -1555,45 +1550,12 @@ usbnet_detach(device_t self, int flags)
 	}
 	usbnet_ec(un)->ec_mii = NULL;
 
-	/*
-	 * We have already waited for the multicast task to complete.
-	 * Unfortunately, until if_detach, nothing has prevented it
-	 * from running again -- another thread might issue if_mcast_op
-	 * between the time of our first usb_rem_task_wait and the time
-	 * we actually get around to if_detach.
-	 *
-	 * Fortunately, the first usb_rem_task_wait ensures that if the
-	 * task is scheduled again, it will witness our setting of
-	 * unp_dying to true[*].  So after that point, if the task is
-	 * scheduled again, it will decline to touch IFNET_LOCK and do
-	 * nothing.  But we still need to wait for it to complete.
-	 *
-	 * It would be nice if we could write
-	 *
-	 *	if_pleasestopissuingmcastopsthanks(ifp);
-	 *	usb_rem_task_wait(..., &unp->unp_mcasttask, ...);
-	 *	if_detach(ifp);
-	 *
-	 * and then we would need only one usb_rem_task_wait.
-	 *
-	 * Unfortunately, there is no such operation available in
-	 * sys/net at the moment, and it would require a bit of
-	 * coordination with if_mcast_op and doifioctl probably under a
-	 * new lock.  So we'll use this kludge until that mechanism is
-	 * invented.
-	 *
-	 * [*] This is not exactly a documented property of the API,
-	 * but it is implied by the single lock in the task queue
-	 * serializing changes to the task state.
-	 */
-	usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
-	    NULL);
-
 	usbnet_rx_list_free(un);
 	usbnet_tx_list_free(un);
 
 	rnd_detach_source(&unp->unp_rndsrc);
 
+	mutex_destroy(&unp->unp_mcastlock);
 	mutex_destroy(&unp->unp_core_lock);
 	mutex_destroy(&unp->unp_rxlock);
 	mutex_destroy(&unp->unp_txlock);

From 0e5ade8e28aaac7911f1395428eaf7b1c88f31b9 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 00:57:08 +0000
Subject: [PATCH 51/75] usbnet drivers: Omit redundant multicast filter update
 on init.

---
 sys/dev/usb/if_aue.c  | 3 ---
 sys/dev/usb/if_axe.c  | 3 ---
 sys/dev/usb/if_axen.c | 3 ---
 sys/dev/usb/if_cue.c  | 3 ---
 sys/dev/usb/if_kue.c  | 3 ---
 sys/dev/usb/if_mos.c  | 3 ---
 sys/dev/usb/if_mue.c  | 3 ---
 sys/dev/usb/if_smsc.c | 3 ---
 sys/dev/usb/if_udav.c | 3 ---
 sys/dev/usb/if_ure.c  | 3 ---
 sys/dev/usb/if_url.c  | 3 ---
 11 files changed, 33 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 60e1551bfa77..4c46c11a9862 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -977,9 +977,6 @@ aue_uno_init(struct ifnet *ifp)
 
 	rv = usbnet_init_rx_tx(un);
 
-	/* Load the multicast filter. */
-	aue_uno_mcast(ifp);
-
 	/* Enable RX and TX */
 	aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
 	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index a62af4a7d4eb..641148ae9af4 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1298,9 +1298,6 @@ axe_uno_init(struct ifnet *ifp)
 
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
 
-	/* Accept multicast frame or run promisc. mode */
-	axe_uno_mcast(ifp);
-
 	return usbnet_init_rx_tx(un);
 }
 
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 910ef4602199..d70a8ef553d6 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -903,9 +903,6 @@ axen_uno_init(struct ifnet *ifp)
 	/* Configure offloading engine. */
 	axen_setoe_locked(un);
 
-	/* Program promiscuous mode and multicast filters. */
-	axen_uno_mcast(ifp);
-
 	/* Enable receiver, set RX mode */
 	axen_cmd(un, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval);
 	rxmode = le16toh(wval);
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index a3902bc54679..de78422bead6 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -647,9 +647,6 @@ cue_uno_init(struct ifnet *ifp)
 		ctl |= CUE_ETHCTL_PROMISC;
 	cue_csr_write_1(un, CUE_ETHCTL, ctl);
 
-	/* Load the multicast filter. */
-	cue_uno_mcast(ifp);
-
 	/*
 	 * Set the number of RX and TX buffers that we want
 	 * to reserve inside the ASIC.
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 15f32d81409f..76ada5e2c2fc 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -621,9 +621,6 @@ kue_uno_init(struct ifnet *ifp)
 #endif
 	kue_setword(un, KUE_CMD_SET_URB_SIZE, 64);
 
-	/* Load the multicast filter. */
-	kue_uno_mcast(ifp);
-
 	return usbnet_init_rx_tx(un);
 }
 
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index afe02a06ed22..8c9483c16bc4 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -747,9 +747,6 @@ mos_uno_init(struct ifnet *ifp)
 	mos_reg_write_1(un, MOS_IPG0, ipgs[0]);
 	mos_reg_write_1(un, MOS_IPG1, ipgs[1]);
 
-	/* Accept multicast frame or run promisc. mode */
-	mos_uno_mcast(ifp);
-
 	/* Enable receiver and transmitter, bridge controls speed/duplex mode */
 	rxmode = mos_reg_read_1(un, MOS_CTL);
 	rxmode |= MOS_CTL_RX_ENB | MOS_CTL_TX_ENB | MOS_CTL_BS_ENB;
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index c63911009771..0e9c23ac14a5 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1239,9 +1239,6 @@ mue_uno_init(struct ifnet *ifp)
 	/* Set MAC address. */
 	mue_set_macaddr(un);
 
-	/* Load the multicast filter. */
-	mue_uno_mcast(ifp);
-
 	/* TCP/UDP checksum offload engines. */
 	mue_sethwcsum_locked(un);
 
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 7de0a5e1fb27..4662dcf1a4ee 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -563,9 +563,6 @@ smsc_uno_init(struct ifnet *ifp)
 	/* Reset the ethernet interface. */
 	smsc_reset(sc);
 
-	/* Load the multicast filter. */
-	smsc_uno_mcast(ifp);
-
 	/* TCP/UDP checksum offload engines. */
 	smsc_setoe_locked(un);
 
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index fb443e0afb33..be74ad58ca0f 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -494,9 +494,6 @@ udav_uno_init(struct ifnet *ifp)
 	else
 		UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 
-	/* Load the multicast filter */
-	udav_uno_mcast(ifp);
-
 	/* Enable RX */
 	UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_RXEN);
 
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 7a2ebd311ded..307cdd71d007 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -445,9 +445,6 @@ ure_uno_init(struct ifnet *ifp)
 	    ure_read_2(un, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) &
 	    ~URE_RXDY_GATED_EN);
 
-	/* Accept multicast frame or run promisc. mode. */
-	ure_uno_mcast(ifp);
-
 	return usbnet_init_rx_tx(un);
 }
 
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 9dbf779aea31..0efb6afb2a7f 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -389,9 +389,6 @@ url_uno_init(struct ifnet *ifp)
 	/* Init receive control register */
 	URL_SETBIT2(un, URL_RCR, URL_RCR_TAIL | URL_RCR_AD | URL_RCR_AB);
 
-	/* Accept multicast frame or run promisc. mode */
-	url_uno_mcast(ifp);
-
 	/* Enable RX and TX */
 	URL_SETBIT(un, URL_CR, URL_CR_TE | URL_CR_RE);
 

From da42d6be0b1f683e009767d00c843c11837b6a22 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 01:23:12 +0000
Subject: [PATCH 52/75] aue(4): Enable rx/tx registers on init before
 usbnet_init_rx_tx.

This way, we still have exclusive access to the registers before
calls to aue_uno_mcast can start happening without the usbnet core
lock.
---
 sys/dev/usb/if_aue.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 4c46c11a9862..70b97c038c62 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -975,13 +975,13 @@ aue_uno_init(struct ifnet *ifp)
 	else
 		AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
 
-	rv = usbnet_init_rx_tx(un);
-
 	/* Enable RX and TX */
 	aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
 	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
 	AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
 
+	rv = usbnet_init_rx_tx(un);
+
 	//mii_mediachg(mii);
 
 	return rv;

From b6751c6d1dc3ed999c03c94f8302052bd0dd5fc3 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 01:26:28 +0000
Subject: [PATCH 53/75] aue(4): Simplify.  No functional change.

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

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 70b97c038c62..b2af18bd825f 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -948,7 +948,7 @@ aue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	struct aue_softc	*sc = usbnet_softc(un);
-	int			i, rv;
+	int			i;
 	const u_char		*eaddr;
 
 	AUEHIST_FUNC();
@@ -980,11 +980,7 @@ aue_uno_init(struct ifnet *ifp)
 	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
 	AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
 
-	rv = usbnet_init_rx_tx(un);
-
-	//mii_mediachg(mii);
-
-	return rv;
+	return usbnet_init_rx_tx(un);
 }
 
 static void

From 2dd96907652eac90c9f929dcc061f4047a19b3a6 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 01:41:50 +0000
Subject: [PATCH 54/75] udav(4): Stop asserting !usbnet_isdying.

This can change at any moment; no software lock can prevent the
device from being detached.  Any test of it is necessarily
best-effort just to avoid wasting time later on waiting for requests
to fail or time out.
---
 sys/dev/usb/if_udav.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index be74ad58ca0f..4585f90a279e 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -364,7 +364,8 @@ udav_csr_read(struct usbnet *un, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	KASSERT(!usbnet_isdying(un));
+	if (usbnet_isdying(un))
+		return USBD_IOERROR;
 
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
@@ -394,7 +395,8 @@ udav_csr_write(struct usbnet *un, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	KASSERT(!usbnet_isdying(un));
+	if (usbnet_isdying(un))
+		return USBD_IOERROR;
 
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
@@ -438,7 +440,8 @@ udav_csr_write1(struct usbnet *un, int offset, unsigned char ch)
 	usb_device_request_t req;
 	usbd_status err;
 
-	KASSERT(!usbnet_isdying(un));
+	if (usbnet_isdying(un))
+		return USBD_IOERROR;
 
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));

From a879550d9563232c766ca91ea499735fcf666509 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 01:46:34 +0000
Subject: [PATCH 55/75] usbnet drivers: Assert IFNET_LOCKED in if ioctl
 routines.

These only happen either during the transition up or down (init or
stop), or while that transition is excluded (ioctl).

This may be called from ioctl or from init, which both hold the ifnet
lock.

XXX smsc_setoe_locked should maybe trigger reinit because the rx loop
behaves differently depending on whether checksumming is enabled.

XXX mue_sethwcsum_locked needs to exclude mcast updates.
---
 sys/dev/usb/if_axen.c | 2 +-
 sys/dev/usb/if_mue.c  | 4 ++++
 sys/dev/usb/if_smsc.c | 2 +-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index d70a8ef553d6..92127d30dfb0 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -516,7 +516,7 @@ axen_setoe_locked(struct usbnet *un)
 	uint64_t enabled = ifp->if_capenable;
 	uint8_t val;
 
-	usbnet_isowned_core(un);
+	KASSERT(IFNET_LOCKED(ifp));
 
 	val = AXEN_RXCOE_OFF;
 	if (enabled & IFCAP_CSUM_IPv4_Rx)
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 0e9c23ac14a5..8747b24e55b2 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1091,6 +1091,8 @@ mue_sethwcsum_locked(struct usbnet *un)
 	struct ifnet * const ifp = usbnet_ifp(un);
 	uint32_t reg, val;
 
+	KASSERT(IFNET_LOCKED(ifp));
+
 	reg = (un->un_flags & LAN7500) ? MUE_7500_RFE_CTL : MUE_7800_RFE_CTL;
 	val = mue_csr_read(un, reg);
 
@@ -1123,6 +1125,8 @@ mue_setmtu_locked(struct usbnet *un)
 	struct ifnet * const ifp = usbnet_ifp(un);
 	uint32_t val;
 
+	KASSERT(IFNET_LOCKED(ifp));
+
 	/* Set the maximum frame size. */
 	MUE_CLRBIT(un, MUE_MAC_RX, MUE_MAC_RX_RXEN);
 	val = mue_csr_read(un, MUE_MAC_RX);
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 4662dcf1a4ee..9119d796335a 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -473,7 +473,7 @@ smsc_setoe_locked(struct usbnet *un)
 	uint32_t val;
 	int err;
 
-	usbnet_isowned_core(un);
+	KASSERT(IFNET_LOCKED(ifp));
 
 	err = smsc_readreg(un, SMSC_COE_CTRL, &val);
 	if (err != 0) {

From ffd661d3a22d8614c48437e76a7bb72be442138b Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 9 Jan 2022 00:37:15 +0000
Subject: [PATCH 56/75] axen(4): Use axen mii read/write reg routines, not
 usbnet ones.

The usbnet wrappers don't add anything important.  We already test
usbnet_isdying in axen_cmd, and that's already a best-effort thing
(which should probably be done better by having usbd_do_request fail
promptly if detaching anyway).
---
 sys/dev/usb/if_axen.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 92127d30dfb0..cfe5d92591dc 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -492,18 +492,18 @@ axen_ax88179_init(struct usbnet *un)
 #define GMII_PHY_PAGE_SEL	0x1e
 #define GMII_PHY_PAGE_SEL	0x1f
 #define GMII_PAGE_EXT		0x0007
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE_SEL,
+	axen_uno_mii_write_reg(un, un->un_phyno, GMII_PHY_PAGE_SEL,
 	    GMII_PAGE_EXT);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE,
+	axen_uno_mii_write_reg(un, un->un_phyno, GMII_PHY_PAGE,
 	    0x002c);
 #endif
 
 #if 1 /* XXX: phy hack ? */
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0005);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x0C, 0x0000);
-	usbnet_mii_readreg(un->un_dev, un->un_phyno, 0x0001, &wval);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x01, wval | 0x0080);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0000);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x1F, 0x0005);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x0C, 0x0000);
+	axen_uno_mii_read_reg(un, un->un_phyno, 0x0001, &wval);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x01, wval | 0x0080);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x1F, 0x0000);
 #endif
 
 	usbnet_unlock_core(un);

From 90751fd2025e003e6631f7ee7f7f247292a2feb0 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 9 Jan 2022 00:39:16 +0000
Subject: [PATCH 57/75] axen(4): Avoid undefined behaviour if mii read reg
 fails.

Some callers don't check the error code.
---
 sys/dev/usb/if_axen.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index cfe5d92591dc..983bec5b4edf 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -141,8 +141,10 @@ axen_uno_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 		return EINVAL;
 
 	usbd_status err = axen_cmd(un, AXEN_CMD_MII_READ_REG, reg, phy, &data);
-	if (err)
+	if (err) {
+		*val = 0;
 		return EIO;
+	}
 
 	*val = le16toh(data);
 	if (reg == MII_BMSR)

From 528fb836dd24347af03772dd934c80eac628f3bd Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 9 Jan 2022 00:39:47 +0000
Subject: [PATCH 58/75] usbnet: Make usbnet_mii_readreg/writereg/statchg
 private to usbnet.c.

No drivers need to use these.
---
 sys/dev/usb/usbnet.c | 6 +++---
 sys/dev/usb/usbnet.h | 5 -----
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 1c8210d07a8b..2bf19e2a1662 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -899,7 +899,7 @@ out:
 
 /* MII management. */
 
-int
+static int
 usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 {
 	USBNETHIST_FUNC();
@@ -923,7 +923,7 @@ usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 	return 0;
 }
 
-int
+static int
 usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 {
 	USBNETHIST_FUNC();
@@ -947,7 +947,7 @@ usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 	return 0;
 }
 
-void
+static void
 usbnet_mii_statchg(struct ifnet *ifp)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 9ce537d7b849..5737aefd4f61 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -326,11 +326,6 @@ usbnet_isowned_core(struct usbnet *un)
  */
 int	usbnet_init_rx_tx(struct usbnet * const);
 
-/* MII. */
-int	usbnet_mii_readreg(device_t, int, int, uint16_t *);
-int	usbnet_mii_writereg(device_t, int, int, uint16_t);
-void	usbnet_mii_statchg(struct ifnet *);
-
 /* interrupt handling */
 void	usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int,
 		       uint32_t, int);

From 2e0102505abb7473b8b5557fdc4a478a0c7eefff Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 01:43:32 +0000
Subject: [PATCH 59/75] usbnet drivers: Omit needless usbnet core lock and
 assertions.

During attach, the caller has exclusive access to the usbnet until
usbnet_attach_ifp.  At other times, register access is serialized
either by the usbnet multicast lock or by IFNET_LOCK.
---
 sys/dev/usb/if_aue.c  |  6 ------
 sys/dev/usb/if_axe.c  |  9 ---------
 sys/dev/usb/if_axen.c | 15 ---------------
 sys/dev/usb/if_cue.c  |  4 ----
 sys/dev/usb/if_mue.c  |  4 ----
 sys/dev/usb/if_smsc.c | 11 -----------
 sys/dev/usb/if_udav.c |  5 -----
 sys/dev/usb/if_ure.c  |  6 ------
 sys/dev/usb/if_url.c  |  5 -----
 9 files changed, 65 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index b2af18bd825f..aead96ed9c27 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -432,8 +432,6 @@ aue_read_mac(struct usbnet *un)
 	int			off = 0;
 	int			word;
 
-	usbnet_isowned_core(un);
-
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGS("aue%jd: enter",
 	    device_unit(un->un_dev), 0, 0, 0);
@@ -846,14 +844,10 @@ aue_attach(device_t parent, device_t self, void *aux)
 	/* First level attach. */
 	usbnet_attach(un, "auedet");
 
-	usbnet_lock_core(un);
-
 	/* Reset the adapter and get station address from the EEPROM.  */
 	aue_reset(sc);
 	aue_read_mac(un);
 
-	usbnet_unlock_core(un);
-
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
 	    0, &unm);
 }
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 641148ae9af4..00af7ea7f867 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -510,8 +510,6 @@ static void
 axe_reset(struct usbnet *un)
 {
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
@@ -927,10 +925,8 @@ axe_attach(device_t parent, device_t self, void *aux)
 	usbnet_attach(un, "axedet");
 
 	/* We need the PHYID for init dance in some cases */
-	usbnet_lock_core(un);
 	if (axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, &sc->axe_phyaddrs)) {
 		aprint_error_dev(self, "failed to read phyaddrs\n");
-		usbnet_unlock_core(un);
 		return;
 	}
 
@@ -960,13 +956,10 @@ axe_attach(device_t parent, device_t self, void *aux)
 	} else {
 		if (axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs)) {
 			aprint_error_dev(self, "failed to read ipg\n");
-			usbnet_unlock_core(un);
 			return;
 		}
 	}
 
-	usbnet_unlock_core(un);
-
 	if (!AXE_IS_172(un))
 		usbnet_ec(un)->ec_capabilities = ETHERCAP_VLAN_MTU;
 	if (un->un_flags & AX772B) {
@@ -1215,8 +1208,6 @@ axe_uno_init(struct ifnet *ifp)
 	struct axe_softc * const sc = usbnet_softc(un);
 	int rxmode;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return EIO;
 
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 983bec5b4edf..93333a2b360a 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -293,7 +293,6 @@ allmulti:
 static void
 axen_reset(struct usbnet *un)
 {
-	usbnet_isowned_core(un);
 	if (usbnet_isdying(un))
 		return;
 	/* XXX What to reset? */
@@ -365,8 +364,6 @@ axen_ax88179_init(struct usbnet *un)
 	uint16_t wval;
 	uint8_t val;
 
-	usbnet_lock_core(un);
-
 	/* XXX: ? */
 	axen_cmd(un, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val);
 	DPRINTFN(5, ("AXEN_CMD_MAC_READ(0x05): 0x%02x\n", val));
@@ -449,7 +446,6 @@ axen_ax88179_init(struct usbnet *un)
 	default:
 		aprint_error_dev(un->un_dev, "unknown uplink bus:0x%02x\n",
 		    val);
-		usbnet_unlock_core(un);
 		return;
 	}
 	axen_cmd(un, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl);
@@ -507,8 +503,6 @@ axen_ax88179_init(struct usbnet *un)
 	axen_uno_mii_write_reg(un, un->un_phyno, 0x01, wval | 0x0080);
 	axen_uno_mii_write_reg(un, un->un_phyno, 0x1F, 0x0000);
 #endif
-
-	usbnet_unlock_core(un);
 }
 
 static void
@@ -552,8 +546,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	switch (cmd) {
 	case SIOCSIFCAP:
 		axen_setoe_locked(un);
@@ -562,8 +554,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
@@ -664,13 +654,10 @@ axen_attach(device_t parent, device_t self, void *aux)
 	DPRINTF(("%s: phyno %d\n", device_xname(self), un->un_phyno));
 
 	/* Get station address.  */
-	usbnet_lock_core(un);
 	if (axen_get_eaddr(un, &un->un_eaddr)) {
-		usbnet_unlock_core(un);
 		printf("EEPROM checksum error\n");
 		return;
 	}
-	usbnet_unlock_core(un);
 
 	axen_ax88179_init(un);
 
@@ -887,8 +874,6 @@ axen_uno_init(struct ifnet *ifp)
 	uint16_t wval;
 	uint8_t bval;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return EIO;
 
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index de78422bead6..951836df49de 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -545,8 +545,6 @@ cue_uno_tick(struct usbnet *un)
 {
 	struct ifnet		*ifp = usbnet_ifp(un);
 
-	usbnet_lock_core(un);
-
 	net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
 	if (cue_csr_read_2(un, CUE_RX_FRAMEERR))
 		if_statinc_ref(nsr, if_ierrors);
@@ -558,8 +556,6 @@ cue_uno_tick(struct usbnet *un)
 	if_statadd_ref(nsr, if_collisions,
 	    cue_csr_read_2(un, CUE_TX_EXCESSCOLL));
 	IF_STAT_PUTREF(ifp);
-
-	usbnet_unlock_core(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 8747b24e55b2..21151a37588f 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1257,8 +1257,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	switch (cmd) {
 	case SIOCSIFCAP:
 		mue_sethwcsum_locked(un);
@@ -1270,8 +1268,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 9119d796335a..089b7b166d14 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -535,7 +535,6 @@ smsc_reset(struct smsc_softc *sc)
 {
 	struct usbnet * const un = &sc->smsc_un;
 
-	usbnet_isowned_core(un);
 	if (usbnet_isdying(un))
 		return;
 
@@ -552,8 +551,6 @@ smsc_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return EIO;
 
@@ -587,8 +584,6 @@ smsc_chip_init(struct usbnet *un)
 	int burst_cap;
 	int err;
 
-	usbnet_isowned_core(un);
-
 	/* Enter H/W config mode */
 	smsc_writereg(un, SMSC_HW_CFG, SMSC_HW_CFG_LRST);
 
@@ -735,8 +730,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-
 	switch (cmd) {
 	case SIOCSIFCAP:
 		smsc_setoe_locked(un);
@@ -745,8 +738,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
@@ -855,7 +846,6 @@ smsc_attach(device_t parent, device_t self, void *aux)
 	/* Setup some of the basics */
 	un->un_phyno = 1;
 
-	usbnet_lock_core(un);
 	/*
 	 * Attempt to get the mac address, if an EEPROM is not attached this
 	 * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
@@ -883,7 +873,6 @@ smsc_attach(device_t parent, device_t self, void *aux)
 			un->un_eaddr[0] = (uint8_t)((mac_l) & 0xff);
 		}
 	}
-	usbnet_unlock_core(un);
 
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
 	    0, &unm);
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 4585f90a279e..e24a622b9738 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -238,14 +238,11 @@ udav_attach(device_t parent, device_t self, void *aux)
 
 	usbnet_attach(un, "udavdet");
 
-	usbnet_lock_core(un);
-
 // 	/* reset the adapter */
 // 	udav_reset(un);
 
 	/* Get Ethernet Address */
 	err = udav_csr_read(un, UDAV_PAR, un->un_eaddr, ETHER_ADDR_LEN);
-	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");
 		return;
@@ -522,7 +519,6 @@ udav_uno_init(struct ifnet *ifp)
 static void
 udav_reset(struct usbnet *un)
 {
-    	usbnet_isowned_core(un);
 
 	if (usbnet_isdying(un))
 		return;
@@ -535,7 +531,6 @@ udav_reset(struct usbnet *un)
 static void
 udav_chip_init(struct usbnet *un)
 {
-	usbnet_isowned_core(un);
 
 	/* Select PHY */
 #if 1
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 307cdd71d007..be04f691e030 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -389,8 +389,6 @@ ure_reset(struct usbnet *un)
 {
 	int i;
 
-	usbnet_isowned_core(un);
-
 	ure_write_1(un, URE_PLA_CR, URE_MCU_TYPE_PLA, URE_CR_RST);
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
@@ -411,8 +409,6 @@ ure_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	uint8_t eaddr[8];
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return EIO;
 
@@ -895,7 +891,6 @@ ure_attach(device_t parent, device_t self, void *aux)
 	    (un->un_flags != 0) ? "" : "unknown ",
 	    ver);
 
-	usbnet_lock_core(un);
 	if (un->un_flags & URE_FLAG_8152)
 		ure_rtl8152_init(un);
 	else
@@ -908,7 +903,6 @@ ure_attach(device_t parent, device_t self, void *aux)
 	else
 		ure_read_mem(un, URE_PLA_BACKUP, URE_MCU_TYPE_PLA, eaddr,
 		    sizeof(eaddr));
-	usbnet_unlock_core(un);
 	if (ETHER_IS_ZERO(eaddr)) {
 		maclo = 0x00f2 | (cprng_strong32() & 0xffff0000);
 		machi = cprng_strong32() & 0xffff;
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 0efb6afb2a7f..8ea014769504 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -242,15 +242,12 @@ url_attach(device_t parent, device_t self, void *aux)
 	/* Set these up now for url_mem().  */
 	usbnet_attach(un, "urldet");
 
-	usbnet_lock_core(un);
-
 	/* reset the adapter */
 	url_reset(un);
 
 	/* Get Ethernet Address */
 	err = url_mem(un, URL_CMD_READMEM, URL_IDR0, (void *)un->un_eaddr,
 		      ETHER_ADDR_LEN);
-	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");
 		return;
@@ -368,8 +365,6 @@ url_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return EIO;
 

From b8a5c9b0c6beec1536ee08567aadc139cb1f60c7 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 02:01:16 +0000
Subject: [PATCH 60/75] urndis(4): Delete some crazy logic that I think is
 unnecessary.

XXX definitely need to test this one
---
 sys/dev/usb/if_urndis.c | 30 ++++--------------------------
 1 file changed, 4 insertions(+), 26 deletions(-)

diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index ff4da0414739..e14df42ef03f 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -866,16 +866,6 @@ urndis_init_un(struct ifnet *ifp, struct usbnet *un)
 	if (err != RNDIS_STATUS_SUCCESS)
 		return EIO;
 
-	usbnet_lock_core(un);
-	if (usbnet_isdying(un))
-		err = EIO;
-	else {
-		usbnet_stop(un, ifp, 1);
-		err = usbnet_init_rx_tx(un);
-		usbnet_set_link(un, err == 0);
-	}
-	usbnet_unlock_core(un);
-
 	return err;
 }
 
@@ -887,9 +877,11 @@ urndis_uno_init(struct ifnet *ifp)
 
 	KASSERT(IFNET_LOCKED(ifp));
 
-	usbnet_unlock_core(un);
 	error = urndis_init_un(ifp, un);
-	usbnet_lock_core(un);
+	if (error)
+		return EIO;	/* XXX */
+	error = usbnet_init_rx_tx(un);
+	usbnet_set_link(un, error == 0);
 
 	return error;
 }
@@ -1064,9 +1056,6 @@ urndis_attach(device_t parent, device_t self, void *aux)
 	    &buf, &bufsz) != RNDIS_STATUS_SUCCESS) {
 		aprint_error("%s: unable to get hardware address\n",
 		    DEVNAME(un));
-		usbnet_lock_core(un);
-		usbnet_stop(un, ifp, 1);
-		usbnet_unlock_core(un);
 		return;
 	}
 
@@ -1077,9 +1066,6 @@ urndis_attach(device_t parent, device_t self, void *aux)
 		aprint_error("%s: invalid address\n", DEVNAME(un));
 		if (buf && bufsz)
 			kmem_free(buf, bufsz);
-		usbnet_lock_core(un);
-		usbnet_stop(un, ifp, 1);
-		usbnet_unlock_core(un);
 		return;
 	}
 
@@ -1090,17 +1076,9 @@ urndis_attach(device_t parent, device_t self, void *aux)
 	if (urndis_ctrl_set(un, OID_GEN_CURRENT_PACKET_FILTER, &filter,
 	    sizeof(filter)) != RNDIS_STATUS_SUCCESS) {
 		aprint_error("%s: unable to set data filters\n", DEVNAME(un));
-		usbnet_lock_core(un);
-		usbnet_stop(un, ifp, 1);
-		usbnet_unlock_core(un);
 		return;
 	}
 
-	/* Turn off again now it has been identified. */
-	usbnet_lock_core(un);
-	usbnet_stop(un, ifp, 1);
-	usbnet_unlock_core(un);
-
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
             0, NULL);
 }

From 6c8c93efe09a4dff18df945e460f34bcd75c44bd Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 7 Jan 2022 02:01:44 +0000
Subject: [PATCH 61/75] usbnet: Delete the core lock from the API.

Init/stop and ioctl happen under IFNET_LOCK.  Multicast updates only
happen after init and before stop.  Core lock is no longer a relevant
part of the API.  Internally, it serves essentially just to lock out
asynchronous mii activity during init/stop.
---
 sys/dev/usb/usbnet.c | 30 ++++++++++++------------------
 sys/dev/usb/usbnet.h | 14 --------------
 2 files changed, 12 insertions(+), 32 deletions(-)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 2bf19e2a1662..a2c3f5be364e 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -102,6 +102,18 @@ volatile unsigned usbnet_number;
 static void usbnet_isowned_rx(struct usbnet *);
 static void usbnet_isowned_tx(struct usbnet *);
 
+static kmutex_t *
+usbnet_mutex_core(struct usbnet *un)
+{
+	return &un->un_pri->unp_core_lock;
+}
+
+static __inline__ void
+usbnet_isowned_core(struct usbnet *un)
+{
+	KASSERT(mutex_owned(usbnet_mutex_core(un)));
+}
+
 static int usbnet_modcmd(modcmd_t, void *);
 
 #ifdef USB_DEBUG
@@ -1312,24 +1324,6 @@ usbnet_isdying(struct usbnet *un)
 
 /* Locking. */
 
-void
-usbnet_lock_core(struct usbnet *un)
-{
-	mutex_enter(&un->un_pri->unp_core_lock);
-}
-
-void
-usbnet_unlock_core(struct usbnet *un)
-{
-	mutex_exit(&un->un_pri->unp_core_lock);
-}
-
-kmutex_t*
-usbnet_mutex_core(struct usbnet *un)
-{
-	return &un->un_pri->unp_core_lock;
-}
-
 static void
 usbnet_isowned_rx(struct usbnet *un)
 {
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 5737aefd4f61..550d211e13a6 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -297,20 +297,6 @@ void *usbnet_softc(struct usbnet *);
 bool usbnet_havelink(struct usbnet *);
 bool usbnet_isdying(struct usbnet *);
 
-
-/*
- * Locking.  Note that the isowned() are implemented here so that
- * empty-KASSERT() causes them to be elided for non-DIAG builds.
- */
-void	usbnet_lock_core(struct usbnet *);
-void	usbnet_unlock_core(struct usbnet *);
-kmutex_t *usbnet_mutex_core(struct usbnet *);
-static __inline__ void
-usbnet_isowned_core(struct usbnet *un)
-{
-	KASSERT(mutex_owned(usbnet_mutex_core(un)));
-}
-
 /*
  * Endpoint / rx/tx chain management:
  *

From 0453a0a4ac57ae99e229f4f6494e8ef55690935c Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:04:12 +0000
Subject: [PATCH 62/75] usbnet drivers: Omit needless isdying tests in
 *_uno_init.

usbnet(9) already checks this immediately before calling *_uno_init.
---
 sys/dev/usb/if_aue.c  |  3 ---
 sys/dev/usb/if_axe.c  |  3 ---
 sys/dev/usb/if_axen.c |  3 ---
 sys/dev/usb/if_cdce.c | 10 +++-------
 sys/dev/usb/if_cue.c  |  3 ---
 sys/dev/usb/if_kue.c  |  3 ---
 sys/dev/usb/if_mos.c  |  3 ---
 sys/dev/usb/if_mue.c  |  5 -----
 sys/dev/usb/if_smsc.c |  3 ---
 sys/dev/usb/if_udav.c |  4 ----
 sys/dev/usb/if_upl.c  |  5 +----
 sys/dev/usb/if_ure.c  |  3 ---
 sys/dev/usb/if_url.c  |  3 ---
 13 files changed, 4 insertions(+), 47 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index aead96ed9c27..f9bc263609a7 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -949,9 +949,6 @@ aue_uno_init(struct ifnet *ifp)
 	AUEHIST_CALLARGSN(5, "aue%jd: enter link=%jd",
 	    device_unit(un->un_dev), usbnet_havelink(un), 0, 0);
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
 	if (ifp->if_flags & IFF_RUNNING)
 		return 0;
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 00af7ea7f867..688d25f4b771 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1208,9 +1208,6 @@ axe_uno_init(struct ifnet *ifp)
 	struct axe_softc * const sc = usbnet_softc(un);
 	int rxmode;
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);
 
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 93333a2b360a..18892ac4b837 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -874,9 +874,6 @@ axen_uno_init(struct ifnet *ifp)
 	uint16_t wval;
 	uint8_t bval;
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);
 
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index c7ee6bef2792..96c70139625c 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -261,13 +261,9 @@ cdce_uno_init(struct ifnet *ifp)
 	struct usbnet		*un = ifp->if_softc;
 	int rv;
 
-	if (usbnet_isdying(un))
-		rv = EIO;
-	else {
-		usbnet_stop(un, ifp, 1);
-		rv = usbnet_init_rx_tx(un);
-		usbnet_set_link(un, rv == 0);
-	}
+	usbnet_stop(un, ifp, 1);
+	rv = usbnet_init_rx_tx(un);
+	usbnet_set_link(un, rv == 0);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 951836df49de..c6a2d7060fb0 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -617,9 +617,6 @@ cue_uno_init(struct ifnet *ifp)
 
 	DPRINTFN(10,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
-	if (usbnet_isdying(un))
-		return ENXIO;
-
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);
 
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 76ada5e2c2fc..f2c2bbb66280 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -599,9 +599,6 @@ kue_uno_init(struct ifnet *ifp)
 
 	DPRINTFN(5,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);
 
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 8c9483c16bc4..5abbfc55a41c 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -729,9 +729,6 @@ mos_uno_init(struct ifnet *ifp)
 	u_int8_t		rxmode;
 	unsigned char		ipgs[2];
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);
 
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 21151a37588f..882588bb8bd3 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1229,11 +1229,6 @@ mue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	if (usbnet_isdying(un)) {
-		DPRINTF(un, "dying\n");
-		return EIO;
-	}
-
 	/* Cancel pending I/O and free all TX/RX buffers. */
 	if (ifp->if_flags & IFF_RUNNING)
 		usbnet_stop(un, ifp, 1);
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 089b7b166d14..c48051153c42 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -551,9 +551,6 @@ smsc_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
 	usbnet_stop(un, ifp, 1);
 
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index e24a622b9738..23e7b2d7b91c 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -470,10 +470,6 @@ udav_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	if (usbnet_isdying(un)) {
-		return EIO;
-	}
-
 	/* Cancel pending I/O and free all TX/RX buffers */
 	if (ifp->if_flags & IFF_RUNNING)
 		usbnet_stop(un, ifp, 1);
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index cf4ba9de209f..f90f644cf6b8 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -257,10 +257,7 @@ upl_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	int rv;
 
-	if (usbnet_isdying(un))
-		rv = EIO;
-	else
-		rv = usbnet_init_rx_tx(un);
+	rv = usbnet_init_rx_tx(un);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index be04f691e030..80856be4b7c6 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -409,9 +409,6 @@ ure_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	uint8_t eaddr[8];
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O. */
 	if (ifp->if_flags & IFF_RUNNING)
 		usbnet_stop(un, ifp, 1);
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 8ea014769504..ca18b0d7cc68 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -365,9 +365,6 @@ url_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O and free all TX/RX buffers */
 	usbnet_stop(un, ifp, 1);
 

From b964c1e2f4b194c68a74c7858cca4384de8bf088 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:10:08 +0000
Subject: [PATCH 63/75] usbnet: Do nothing on if_init/stop if already in the
 target state.

The network stack _shouldn't_ ever call us if so, but I'm not yet
sure it _won't_.
---
 sys/dev/usb/usbnet.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index a2c3f5be364e..08f99fc2727c 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1162,6 +1162,16 @@ usbnet_if_stop(struct ifnet *ifp, int disable)
 
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
+	/*
+	 * If we're already stopped, nothing to do.
+	 *
+	 * XXX This should be an assertion, but it may require some
+	 * analysis -- and possibly some tweaking -- of sys/net to
+	 * ensure.
+	 */
+	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);
@@ -1263,6 +1273,16 @@ usbnet_if_init(struct ifnet *ifp)
 	if (usbnet_isdying(un))
 		return EIO;
 
+	/*
+	 * If we're already running, nothing to do.
+	 *
+	 * XXX This should be an assertion, but it may require some
+	 * analysis -- and possibly some tweaking -- of sys/net to
+	 * ensure.
+	 */
+	if (ifp->if_flags & IFF_RUNNING)
+		return 0;
+
 	mutex_enter(&un->un_pri->unp_core_lock);
 	error = uno_init(un, ifp);
 	mutex_exit(&un->un_pri->unp_core_lock);

From a4e706d5ca1943e9db2e4353682471696b90ebbd Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:13:46 +0000
Subject: [PATCH 64/75] usbnet drivers: Prune dead IFF_RUNNING branches in
 *_uno_init.

usbnet(9) guarantees !IFF_RUNNING now before calling it.
---
 sys/dev/usb/if_aue.c    | 4 ----
 sys/dev/usb/if_mue.c    | 4 ----
 sys/dev/usb/if_udav.c   | 4 ----
 sys/dev/usb/if_ure.c    | 4 ----
 sys/dev/usb/if_urndis.c | 3 ---
 5 files changed, 19 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index f9bc263609a7..281a015825b2 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -949,10 +949,6 @@ aue_uno_init(struct ifnet *ifp)
 	AUEHIST_CALLARGSN(5, "aue%jd: enter link=%jd",
 	    device_unit(un->un_dev), usbnet_havelink(un), 0, 0);
 
-	/* Cancel pending I/O */
-	if (ifp->if_flags & IFF_RUNNING)
-		return 0;
-
 	/* Reset the interface. */
 	aue_reset(sc);
 
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 882588bb8bd3..0e966839595b 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1229,10 +1229,6 @@ mue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	/* Cancel pending I/O and free all TX/RX buffers. */
-	if (ifp->if_flags & IFF_RUNNING)
-		usbnet_stop(un, ifp, 1);
-
 	mue_reset(un);
 
 	/* Set MAC address. */
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 23e7b2d7b91c..1dbbb86ac70b 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -470,10 +470,6 @@ udav_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	/* Cancel pending I/O and free all TX/RX buffers */
-	if (ifp->if_flags & IFF_RUNNING)
-		usbnet_stop(un, ifp, 1);
-
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
 	udav_csr_write(un, UDAV_PAR, eaddr, ETHER_ADDR_LEN);
 
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 80856be4b7c6..d7ad5b8c7ac8 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -409,10 +409,6 @@ ure_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	uint8_t eaddr[8];
 
-	/* Cancel pending I/O. */
-	if (ifp->if_flags & IFF_RUNNING)
-		usbnet_stop(un, ifp, 1);
-
 	/* Set MAC address. */
 	memset(eaddr, 0, sizeof(eaddr));
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index e14df42ef03f..593d817e077b 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -859,9 +859,6 @@ urndis_init_un(struct ifnet *ifp, struct usbnet *un)
 {
 	int 			 err;
 
-	if (ifp->if_flags & IFF_RUNNING)
-		return 0;
-
 	err = urndis_ctrl_init(un);
 	if (err != RNDIS_STATUS_SUCCESS)
 		return EIO;

From 89128929402d6db743be8f47a492089c57573cb6 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:19:31 +0000
Subject: [PATCH 65/75] usbnet drivers: From *_uno_init, call *_uno_stop, not
 usbnet_stop.

Make usbnet_stop private now that no drivers use it.

None of the driver-independent logic in usbnet_stop has any effect at
this point because we are guaranteed not to be running, so only the
driver-dependent logic in *_uno_stop (at most) is needed.

For drivers with no *_uno_stop, just omit the call to usbnet_stop
altogether.

Some of this logic is obviously redundant with the subsequent call to
*_reset -- to be addressed in a subsequent commit.
---
 sys/dev/usb/if_axe.c  | 2 +-
 sys/dev/usb/if_axen.c | 2 +-
 sys/dev/usb/if_cdce.c | 1 -
 sys/dev/usb/if_cue.c  | 2 +-
 sys/dev/usb/if_kue.c  | 3 ---
 sys/dev/usb/if_mos.c  | 2 +-
 sys/dev/usb/if_smsc.c | 2 +-
 sys/dev/usb/if_url.c  | 2 +-
 sys/dev/usb/usbnet.c  | 4 +---
 sys/dev/usb/usbnet.h  | 3 ---
 10 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 688d25f4b771..679c9fbf0c25 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1209,7 +1209,7 @@ axe_uno_init(struct ifnet *ifp)
 	int rxmode;
 
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	axe_uno_stop(ifp, 1);
 
 	/* Reset the ethernet interface. */
 	axe_reset(un);
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 18892ac4b837..331ba9424db7 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -875,7 +875,7 @@ axen_uno_init(struct ifnet *ifp)
 	uint8_t bval;
 
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	axen_uno_stop(ifp, 1);
 
 	/* Reset the ethernet interface. */
 	axen_reset(un);
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 96c70139625c..0b658fb0d2f0 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -261,7 +261,6 @@ cdce_uno_init(struct ifnet *ifp)
 	struct usbnet		*un = ifp->if_softc;
 	int rv;
 
-	usbnet_stop(un, ifp, 1);
 	rv = usbnet_init_rx_tx(un);
 	usbnet_set_link(un, rv == 0);
 
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index c6a2d7060fb0..188f2fa173de 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -618,7 +618,7 @@ cue_uno_init(struct ifnet *ifp)
 	DPRINTFN(10,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	cue_uno_stop(ifp, 1);
 
 	/* Reset the interface. */
 #if 1
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index f2c2bbb66280..3e63405dc650 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -599,9 +599,6 @@ kue_uno_init(struct ifnet *ifp)
 
 	DPRINTFN(5,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
-	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
-
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
 	/* Set MAC address */
 	kue_ctl(un, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, eaddr, ETHER_ADDR_LEN);
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 5abbfc55a41c..3ffa6ea01158 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -730,7 +730,7 @@ mos_uno_init(struct ifnet *ifp)
 	unsigned char		ipgs[2];
 
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	mos_uno_stop(ifp, 1);
 
 	/* Reset the ethernet interface. */
 	mos_reset(un);
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index c48051153c42..353b7c837d76 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -552,7 +552,7 @@ smsc_uno_init(struct ifnet *ifp)
 	struct smsc_softc * const sc = usbnet_softc(un);
 
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	smsc_uno_stop(ifp, 1);
 
 	/* Reset the ethernet interface. */
 	smsc_reset(sc);
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index ca18b0d7cc68..d67d78a38e8a 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -366,7 +366,7 @@ url_uno_init(struct ifnet *ifp)
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
 	/* Cancel pending I/O and free all TX/RX buffers */
-	usbnet_stop(un, ifp, 1);
+	url_uno_stop(ifp, 1);
 
 	eaddr = CLLADDR(ifp->if_sadl);
 	for (i = 0; i < ETHER_ADDR_LEN; i++)
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 08f99fc2727c..709aca405de4 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1077,11 +1077,9 @@ usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
  *	- free RX and TX resources
  *	- close pipes
  *
- * usbnet_stop() is exported for drivers to use, expects lock held.
- *
  * usbnet_if_stop() is for the if_stop handler.
  */
-void
+static void
 usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 {
 	struct usbnet_private * const unp = un->un_pri;
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 550d211e13a6..b7d333fb2455 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -324,9 +324,6 @@ void	usbnet_attach_ifp(struct usbnet *, unsigned, unsigned,
 int	usbnet_detach(device_t, int);
 int	usbnet_activate(device_t, devact_t);
 
-/* stop backend */
-void	usbnet_stop(struct usbnet *, struct ifnet *, int);
-
 /* module hook up */
 
 #ifdef _MODULE

From 204ba56b3f9604db5ae888efe443227cb4d2331f Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:25:51 +0000
Subject: [PATCH 66/75] usbnet drivers: Omit redundant device reset via
 *_uno_stop on init.

Only those drivers where *_uno_stop is just *_reset, and *_uno_init
immediately calls *_reset afterward, are affected.
---
 sys/dev/usb/if_axe.c  | 3 ---
 sys/dev/usb/if_mos.c  | 3 ---
 sys/dev/usb/if_smsc.c | 3 ---
 3 files changed, 9 deletions(-)

diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 679c9fbf0c25..5c38c36fb0ad 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1208,9 +1208,6 @@ axe_uno_init(struct ifnet *ifp)
 	struct axe_softc * const sc = usbnet_softc(un);
 	int rxmode;
 
-	/* Cancel pending I/O */
-	axe_uno_stop(ifp, 1);
-
 	/* Reset the ethernet interface. */
 	axe_reset(un);
 
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 3ffa6ea01158..cfcdce4197b4 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -729,9 +729,6 @@ mos_uno_init(struct ifnet *ifp)
 	u_int8_t		rxmode;
 	unsigned char		ipgs[2];
 
-	/* Cancel pending I/O */
-	mos_uno_stop(ifp, 1);
-
 	/* Reset the ethernet interface. */
 	mos_reset(un);
 
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 353b7c837d76..f6d5c90603aa 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -551,9 +551,6 @@ smsc_uno_init(struct ifnet *ifp)
 	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
 
-	/* Cancel pending I/O */
-	smsc_uno_stop(ifp, 1);
-
 	/* Reset the ethernet interface. */
 	smsc_reset(sc);
 

From 18c92693095de4561c725a68e361e4c85bfb1e28 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:29:54 +0000
Subject: [PATCH 67/75] url(4): Inline call to url_uno_stop -- alias for
 url_reset.

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

diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index d67d78a38e8a..c8db7a235ebe 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -365,8 +365,7 @@ url_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	/* Cancel pending I/O and free all TX/RX buffers */
-	url_uno_stop(ifp, 1);
+	url_reset(un);
 
 	eaddr = CLLADDR(ifp->if_sadl);
 	for (i = 0; i < ETHER_ADDR_LEN; i++)

From 11afd78f309fb9dfc050dc0c7c2da752f03cc6d6 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:39:45 +0000
Subject: [PATCH 68/75] usbnet: Handle usbnet_set_link for drivers with no
 media detect.

---
 sys/dev/usb/if_cdce.c   | 1 -
 sys/dev/usb/if_urndis.c | 1 -
 sys/dev/usb/usbnet.c    | 7 +++++++
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 0b658fb0d2f0..e077091de930 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -262,7 +262,6 @@ cdce_uno_init(struct ifnet *ifp)
 	int rv;
 
 	rv = usbnet_init_rx_tx(un);
-	usbnet_set_link(un, rv == 0);
 
 	return rv;
 }
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index 593d817e077b..3178bcfa53ce 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -878,7 +878,6 @@ urndis_uno_init(struct ifnet *ifp)
 	if (error)
 		return EIO;	/* XXX */
 	error = usbnet_init_rx_tx(un);
-	usbnet_set_link(un, error == 0);
 
 	return error;
 }
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 709aca405de4..48f54f1bb41d 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -904,6 +904,13 @@ out:
 		usbnet_ep_close_pipes(un);
 	}
 
+	/*
+	 * For devices without any media autodetection, treat success
+	 * here as an active link.
+	 */
+	if (un->un_ops->uno_statchg == NULL)
+		usbnet_set_link(un, error == 0);
+
 	usbnet_isowned_core(un);
 
 	return error;

From 6039380dc7ff7c30696efdd624f56b5ea2fcbb54 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:42:27 +0000
Subject: [PATCH 69/75] usbnet drivers: Simplify return of usbnet_init_rx_tx.

---
 sys/dev/usb/if_cdce.c   | 5 +----
 sys/dev/usb/if_udav.c   | 6 ++----
 sys/dev/usb/if_upl.c    | 5 +----
 sys/dev/usb/if_urndis.c | 3 +--
 4 files changed, 5 insertions(+), 14 deletions(-)

diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index e077091de930..13804e66b801 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -259,11 +259,8 @@ static int
 cdce_uno_init(struct ifnet *ifp)
 {
 	struct usbnet		*un = ifp->if_softc;
-	int rv;
 
-	rv = usbnet_init_rx_tx(un);
-
-	return rv;
+	return usbnet_init_rx_tx(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 1dbbb86ac70b..ac4671797bca 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -501,11 +501,9 @@ udav_uno_init(struct ifnet *ifp)
 	}
 
 	if (usbnet_isdying(un))
-		rc = EIO;
-	else
-		rc = usbnet_init_rx_tx(un);
+		return EIO;
 
-	return rc;
+	return usbnet_init_rx_tx(un);
 }
 
 static void
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index f90f644cf6b8..7f58fe587838 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -255,11 +255,8 @@ static int
 upl_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
-	int rv;
 
-	rv = usbnet_init_rx_tx(un);
-
-	return rv;
+	return usbnet_init_rx_tx(un);
 }
 
 static int
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index 3178bcfa53ce..5d7088b53008 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -877,9 +877,8 @@ urndis_uno_init(struct ifnet *ifp)
 	error = urndis_init_un(ifp, un);
 	if (error)
 		return EIO;	/* XXX */
-	error = usbnet_init_rx_tx(un);
 
-	return error;
+	return usbnet_init_rx_tx(un);
 }
 
 static int

From 23bebe58170244f36cf19a03bed41eca7db71ca2 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:48:21 +0000
Subject: [PATCH 70/75] usbnet: Factor usbnet_init_rx_tx out into
 usbnet_if_init.

Make it private; no need for drivers to call it any more.
---
 sys/dev/usb/if_aue.c    | 2 +-
 sys/dev/usb/if_axe.c    | 2 +-
 sys/dev/usb/if_axen.c   | 2 +-
 sys/dev/usb/if_cdce.c   | 3 +--
 sys/dev/usb/if_cue.c    | 2 +-
 sys/dev/usb/if_kue.c    | 2 +-
 sys/dev/usb/if_mos.c    | 2 +-
 sys/dev/usb/if_mue.c    | 2 +-
 sys/dev/usb/if_smsc.c   | 2 +-
 sys/dev/usb/if_udav.c   | 2 +-
 sys/dev/usb/if_upl.c    | 3 +--
 sys/dev/usb/if_ure.c    | 2 +-
 sys/dev/usb/if_url.c    | 2 +-
 sys/dev/usb/if_urndis.c | 2 +-
 sys/dev/usb/usbnet.c    | 9 +++++++--
 sys/dev/usb/usbnet.h    | 3 ---
 16 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 281a015825b2..82394beb3319 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -967,7 +967,7 @@ aue_uno_init(struct ifnet *ifp)
 	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
 	AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index 5c38c36fb0ad..a249ec6e3b29 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -1283,7 +1283,7 @@ axe_uno_init(struct ifnet *ifp)
 
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 331ba9424db7..14b63a9a7353 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -894,7 +894,7 @@ axen_uno_init(struct ifnet *ifp)
 	wval = htole16(rxmode);
 	axen_cmd(un, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 13804e66b801..14085b22400d 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -258,9 +258,8 @@ cdce_attach(device_t parent, device_t self, void *aux)
 static int
 cdce_uno_init(struct ifnet *ifp)
 {
-	struct usbnet		*un = ifp->if_softc;
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 188f2fa173de..93e7a5c84f0e 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -654,7 +654,7 @@ cue_uno_init(struct ifnet *ifp)
 	/* Program the LED operation. */
 	cue_csr_write_1(un, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 /* Stop and reset the adapter.  */
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 3e63405dc650..1d003c34ad31 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -615,7 +615,7 @@ kue_uno_init(struct ifnet *ifp)
 #endif
 	kue_setword(un, KUE_CMD_SET_URB_SIZE, 64);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 #ifdef _MODULE
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index cfcdce4197b4..0afb0647bfb7 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -747,7 +747,7 @@ mos_uno_init(struct ifnet *ifp)
 	rxmode &= ~(MOS_CTL_SLEEP);
 	mos_reg_write_1(un, MOS_CTL, rxmode);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 void
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 0e966839595b..ef6306b8160c 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -1240,7 +1240,7 @@ mue_uno_init(struct ifnet *ifp)
 	/* Set MTU. */
 	mue_setmtu_locked(un);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static int
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index f6d5c90603aa..7501eb356099 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -557,7 +557,7 @@ smsc_uno_init(struct ifnet *ifp)
 	/* TCP/UDP checksum offload engines. */
 	smsc_setoe_locked(un);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index ac4671797bca..241ce7cfc111 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -503,7 +503,7 @@ udav_uno_init(struct ifnet *ifp)
 	if (usbnet_isdying(un))
 		return EIO;
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index 7f58fe587838..b5eb8d3f05bd 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -254,9 +254,8 @@ upl_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 static int
 upl_uno_init(struct ifnet *ifp)
 {
-	struct usbnet * const un = ifp->if_softc;
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static int
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index d7ad5b8c7ac8..4488285cd643 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -434,7 +434,7 @@ ure_uno_init(struct ifnet *ifp)
 	    ure_read_2(un, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) &
 	    ~URE_RXDY_GATED_EN);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index c8db7a235ebe..461cad903c40 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -383,7 +383,7 @@ url_uno_init(struct ifnet *ifp)
 	/* Enable RX and TX */
 	URL_SETBIT(un, URL_CR, URL_CR_TE | URL_CR_RE);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index 5d7088b53008..22cf550632f2 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -878,7 +878,7 @@ urndis_uno_init(struct ifnet *ifp)
 	if (error)
 		return EIO;	/* XXX */
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static int
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 48f54f1bb41d..7af2ff020ff0 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -834,7 +834,7 @@ usbnet_ep_stop_pipes(struct usbnet * const un)
 	return err;
 }
 
-int
+static int
 usbnet_init_rx_tx(struct usbnet * const un)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
@@ -1290,7 +1290,12 @@ usbnet_if_init(struct ifnet *ifp)
 
 	mutex_enter(&un->un_pri->unp_core_lock);
 	error = uno_init(un, ifp);
-	mutex_exit(&un->un_pri->unp_core_lock);
+	if (error)
+		goto out;
+	error = usbnet_init_rx_tx(un);
+	if (error)
+		goto out;
+out:	mutex_exit(&un->un_pri->unp_core_lock);
 
 	return error;
 }
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index b7d333fb2455..2ca1d690ca80 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -307,10 +307,7 @@ bool usbnet_isdying(struct usbnet *);
  * usbnet_detach() frees the rx/tx chains
  *
  * Setup un_ed[] with valid end points before calling usbnet_attach().
- * Call usbnet_init_rx_tx() to initialise pipes, which will be open
- * upon success.
  */
-int	usbnet_init_rx_tx(struct usbnet * const);
 
 /* interrupt handling */
 void	usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int,

From 6f801c8a8d2a6871809477992be83fb698c05b87 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 03:52:30 +0000
Subject: [PATCH 71/75] usbnet: Omit empty uno_init functions.

---
 sys/dev/usb/if_cdce.c | 9 ---------
 sys/dev/usb/if_upl.c  | 9 ---------
 sys/dev/usb/usbnet.c  | 2 +-
 3 files changed, 1 insertion(+), 19 deletions(-)

diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 14085b22400d..bf865f952709 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -82,12 +82,10 @@ static void	cdce_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
 				 uint32_t);
 static unsigned	cdce_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				    struct usbnet_chain *);
-static int	cdce_uno_init(struct ifnet *);
 
 static const struct usbnet_ops cdce_ops = {
 	.uno_tx_prepare = cdce_uno_tx_prepare,
 	.uno_rx_loop = cdce_uno_rx_loop,
-	.uno_init = cdce_uno_init,
 };
 
 static int
@@ -255,13 +253,6 @@ cdce_attach(device_t parent, device_t self, void *aux)
             0, NULL);
 }
 
-static int
-cdce_uno_init(struct ifnet *ifp)
-{
-
-	return 0;
-}
-
 static void
 cdce_uno_rx_loop(struct usbnet * un, struct usbnet_chain *c, uint32_t total_len)
 {
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index b5eb8d3f05bd..ef20dd690e43 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -111,10 +111,8 @@ static void upl_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static unsigned upl_uno_tx_prepare(struct usbnet *, struct mbuf *,
 			       struct usbnet_chain *);
 static int upl_uno_ioctl(struct ifnet *, u_long, void *);
-static int upl_uno_init(struct ifnet *);
 
 static const struct usbnet_ops upl_ops = {
-	.uno_init = upl_uno_init,
 	.uno_tx_prepare = upl_uno_tx_prepare,
 	.uno_rx_loop = upl_uno_rx_loop,
 	.uno_ioctl = upl_uno_ioctl,
@@ -251,13 +249,6 @@ upl_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 	return total_len;
 }
 
-static int
-upl_uno_init(struct ifnet *ifp)
-{
-
-	return 0;
-}
-
 static int
 upl_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 7af2ff020ff0..c68f4cbf7d75 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -205,7 +205,7 @@ static int
 uno_init(struct usbnet *un, struct ifnet *ifp)
 {
 	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
-	return (*un->un_ops->uno_init)(ifp);
+	return un->un_ops->uno_init ? (*un->un_ops->uno_init)(ifp) : 0;
 }
 
 static int

From a73b6c0b119d699157a458e463814ca856a156b4 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 8 Jan 2022 04:09:04 +0000
Subject: [PATCH 72/75] usbnet: Omit needless detachcv name parameter to
 usbnet_attach.

---
 sys/dev/usb/if_aue.c    | 2 +-
 sys/dev/usb/if_axe.c    | 2 +-
 sys/dev/usb/if_axen.c   | 2 +-
 sys/dev/usb/if_cdce.c   | 2 +-
 sys/dev/usb/if_cue.c    | 2 +-
 sys/dev/usb/if_kue.c    | 2 +-
 sys/dev/usb/if_mos.c    | 2 +-
 sys/dev/usb/if_mue.c    | 2 +-
 sys/dev/usb/if_smsc.c   | 2 +-
 sys/dev/usb/if_udav.c   | 2 +-
 sys/dev/usb/if_upl.c    | 2 +-
 sys/dev/usb/if_ure.c    | 2 +-
 sys/dev/usb/if_url.c    | 2 +-
 sys/dev/usb/if_urndis.c | 2 +-
 sys/dev/usb/usbnet.c    | 3 +--
 sys/dev/usb/usbnet.h    | 2 +-
 16 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 82394beb3319..e45707c0a9c1 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -842,7 +842,7 @@ aue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* First level attach. */
-	usbnet_attach(un, "auedet");
+	usbnet_attach(un);
 
 	/* Reset the adapter and get station address from the EEPROM.  */
 	aue_reset(sc);
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index a249ec6e3b29..c6a0cc11ee85 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -922,7 +922,7 @@ axe_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for axe_cmd().  */
-	usbnet_attach(un, "axedet");
+	usbnet_attach(un);
 
 	/* We need the PHYID for init dance in some cases */
 	if (axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, &sc->axe_phyaddrs)) {
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index 14b63a9a7353..2cf58eec5bd5 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -648,7 +648,7 @@ axen_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for axen_cmd().  */
-	usbnet_attach(un, "axendet");
+	usbnet_attach(un);
 
 	un->un_phyno = AXEN_PHY_ID;
 	DPRINTF(("%s: phyno %d\n", device_xname(self), un->un_phyno));
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index bf865f952709..452be10fc77e 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -248,7 +248,7 @@ cdce_attach(device_t parent, device_t self, void *aux)
 		un->un_eaddr[5] = (uint8_t)(device_unit(un->un_dev));
 	}
 
-	usbnet_attach(un, "cdcedet");
+	usbnet_attach(un);
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
             0, NULL);
 }
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index 93e7a5c84f0e..d6eaa2852123 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -525,7 +525,7 @@ cue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* First level attach. */
-	usbnet_attach(un, "cuedet");
+	usbnet_attach(un);
 
 #if 0
 	/* Reset the adapter. */
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 1d003c34ad31..b34ac031559c 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -494,7 +494,7 @@ kue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* First level attach, so kue_ctl() works. */
-	usbnet_attach(un, "kuedet");
+	usbnet_attach(un);
 
 	/* Read ethernet descriptor */
 	err = kue_ctl(un, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 0afb0647bfb7..b89087007259 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -636,7 +636,7 @@ mos_attach(device_t parent, device_t self, void *aux)
 		aprint_normal_dev(self, "MCS7832\n");
 
 	/* Set these up now for register access. */
-	usbnet_attach(un, "mosdet");
+	usbnet_attach(un);
 
 	mos_chip_init(un);
 
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index ef6306b8160c..68cb400be022 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -852,7 +852,7 @@ mue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for mue_cmd().  */
-	usbnet_attach(un, "muedet");
+	usbnet_attach(un);
 
 	un->un_phyno = 1;
 
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 7501eb356099..ab4827f580f9 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -822,7 +822,7 @@ smsc_attach(device_t parent, device_t self, void *aux)
 		}
 	}
 
-	usbnet_attach(un, "smscdet");
+	usbnet_attach(un);
 
 #ifdef notyet
 	/*
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index 241ce7cfc111..f4dfe968f2c9 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -236,7 +236,7 @@ udav_attach(device_t parent, device_t self, void *aux)
 	/* Not supported yet. */
 	un->un_ed[USBNET_ENDPT_INTR] = 0;
 
-	usbnet_attach(un, "udavdet");
+	usbnet_attach(un);
 
 // 	/* reset the adapter */
 // 	udav_reset(un);
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index ef20dd690e43..770c3a244152 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -205,7 +205,7 @@ upl_attach(device_t parent, device_t self, void *aux)
 		return;
 	}
 
-	usbnet_attach(un, "upldet");
+	usbnet_attach(un);
 
 	/* Initialize interface info.*/
 	struct ifnet *ifp = usbnet_ifp(un);
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index 4488285cd643..6d1afeedd36a 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -851,7 +851,7 @@ ure_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for ure_ctl().  */
-	usbnet_attach(un, "uredet");
+	usbnet_attach(un);
 
 	un->un_phyno = 0;
 
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 461cad903c40..1d9d76b6d727 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -240,7 +240,7 @@ url_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for url_mem().  */
-	usbnet_attach(un, "urldet");
+	usbnet_attach(un);
 
 	/* reset the adapter */
 	url_reset(un);
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index 22cf550632f2..08a82be22c5d 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -1042,7 +1042,7 @@ urndis_attach(device_t parent, device_t self, void *aux)
 	ifp->if_watchdog = urndis_watchdog;
 #endif
 
-	usbnet_attach(un, "urndisdet");
+	usbnet_attach(un);
 
 	struct ifnet *ifp = usbnet_ifp(un);
 	urndis_init_un(ifp, un);
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index c68f4cbf7d75..c5951bf60b11 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1390,8 +1390,7 @@ usbnet_empty_eaddr(struct usbnet * const un)
  */
 
 void
-usbnet_attach(struct usbnet *un,
-	      const char *detname)	/* detach cv name */
+usbnet_attach(struct usbnet *un)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 2ca1d690ca80..b489bcea8987 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -315,7 +315,7 @@ void	usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int,
 void	usbnet_input(struct usbnet * const, uint8_t *, size_t);
 
 /* autoconf */
-void	usbnet_attach(struct usbnet *un, const char *);
+void	usbnet_attach(struct usbnet *);
 void	usbnet_attach_ifp(struct usbnet *, unsigned, unsigned,
 			  const struct usbnet_mii *);
 int	usbnet_detach(device_t, int);

From 4653f4e6a63da808f12e8b982e5e9132022afeac Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 9 Jan 2022 00:42:51 +0000
Subject: [PATCH 73/75] usbnet: Fix type of struct usbnet::un_ed according to
 plan.

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

diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index b489bcea8987..75e9492e0310 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -262,11 +262,8 @@ struct usbnet {
 	/*
 	 * This section should be filled in before calling
 	 * usbnet_attach_ifp().
-	 *
-	 * XXX This should be of type "uByte".  enum usbnet_ep
-	 * is the index.  Fix this in a kernel version bump.
 	 */
-	enum usbnet_ep		un_ed[USBNET_ENDPT_MAX];
+	uByte			un_ed[USBNET_ENDPT_MAX];
 
 	/* MII specific. Not used without MII. */
 	int			un_phyno;

From 0826138ebe0fef18db04ab78f949612ced52a737 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 9 Jan 2022 00:47:39 +0000
Subject: [PATCH 74/75] usbnet: On if_stop, abort xfers before resetting
 hardware.

uno_stop is supposed to have exclusive access to the hardware; this
ensures that any concurrent uno_rx_loop has completed before we enter
uno_stop.
---
 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 c5951bf60b11..1f57e11ea39e 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -1130,6 +1130,9 @@ 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);
 
+	/* Stop transfers. */
+	usbnet_ep_stop_pipes(un);
+
 	/*
 	 * Now that the software is quiescent, ask the driver to stop
 	 * the hardware.  The driver's uno_stop routine now has
@@ -1143,9 +1146,6 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	if (!usbnet_isdying(un))
 		uno_stop(un, ifp, disable);
 
-	/* Stop transfers. */
-	usbnet_ep_stop_pipes(un);
-
 	/* Free RX/TX resources. */
 	usbnet_rx_list_fini(un);
 	usbnet_tx_list_fini(un);

From 8b94b493b22db546f7011ca999a325f2c97ff43d Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sun, 9 Jan 2022 00:53:17 +0000
Subject: [PATCH 75/75] usbnet: Update some comments.

---
 sys/dev/usb/usbnet.h | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 75e9492e0310..bdd34a52367c 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -58,19 +58,12 @@
  *     cases), but provides a normal handler with callback to handle
  *     ENETRESET conditions that should be sufficient for most users
  *   - start uses usbnet transmit prepare callback (uno_tx_prepare)
- * - interface init and stop have helper functions
- *   - device specific init should use usbnet_init_rx_tx() to open pipes
- *     to the device and setup the rx/tx chains for use after any device
- *     specific setup
- *   - usbnet_stop() must be called with the un_lock held, and will
- *     call the device-specific usbnet stop callback, which enables the
- *     standard init calls stop idiom.
  * - interrupt handling:
- *   - for rx, usbnet_init_rx_tx() will enable the receive pipes and
+ *   - for rx, usbnet will enable the receive pipes and
  *     call the rx_loop callback to handle device specific processing of
  *     packets, which can use usbnet_enqueue() to provide data to the
  *     higher layers
- *   - for tx, usbnet_start (if_start) will pull entries out of the
+ *   - for tx, usbnet will pull entries out of the
  *     transmit queue and use the transmit prepare callback (uno_tx_prepare)
  *     for the given mbuf.  the usb callback will use usbnet_txeof() for
  *     the transmit completion function (internal to usbnet)
@@ -175,8 +168,7 @@ typedef void (*usbnet_intr_cb)(struct usbnet *, usbd_status);
  * Note that the IFNET_LOCK **may not be held** for some the ioctl
  * commands SIOCADDMULTI/SIOCDELMULTI.  These commands are only passed
  * explicitly to uno_override_ioctl; for all other devices, they are
- * handled inside usbnet by scheduling a task to asynchronously call
- * uno_mcast with IFNET_LOCK held.
+ * handled by uno_mcast (also without IFNET_LOCK).
  */
 struct usbnet_ops {
 	usbnet_stop_cb		uno_stop;		/* C */
@@ -297,10 +289,20 @@ bool usbnet_isdying(struct usbnet *);
 /*
  * Endpoint / rx/tx chain management:
  *
- * usbnet_attach() initialises usbnet and allocates rx and tx chains
- * usbnet_init_rx_tx() open pipes, initialises the rx/tx chains for use
- * usbnet_stop() stops pipes, cleans (not frees) rx/tx chains, locked
- *               version assumes un_lock is held
+ * 1. usbnet_attach() initialises usbnet and allocates rx and tx chains
+ *
+ * 2. On if_init, usbnet:
+ *    - calls uno_init to initialize hardware
+ *    - open pipes
+ *    - initialises the rx/tx chains for use
+ *    - calls uno_mcast to program hardware multicast filter
+ *
+ * 3. On if_stop, usbnet:
+ *    - stops pipes
+ *    - calls uno_stop to stop hardware (unless we're detaching anyway)
+ *    - cleans (not frees) rx/tx chains
+ *    - closes pipes
+ *
  * usbnet_detach() frees the rx/tx chains
  *
  * Setup un_ed[] with valid end points before calling usbnet_attach().
diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 89e2c5f55447..e45707c0a9c1 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -240,7 +240,7 @@ CFATTACH_DECL_NEW(aue, sizeof(struct aue_softc), aue_match, aue_attach,
 static void aue_reset_pegasus_II(struct aue_softc *);
 
 static void aue_uno_stop(struct ifnet *, int);
-static int aue_uno_ioctl(struct ifnet *, u_long, void *);
+static void aue_uno_mcast(struct ifnet *);
 static int aue_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int aue_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void aue_uno_mii_statchg(struct ifnet *);
@@ -252,7 +252,7 @@ static void aue_uno_intr(struct usbnet *, usbd_status);
 
 static const struct usbnet_ops aue_ops = {
 	.uno_stop = aue_uno_stop,
-	.uno_ioctl = aue_uno_ioctl,
+	.uno_mcast = aue_uno_mcast,
 	.uno_read_reg = aue_uno_mii_read_reg,
 	.uno_write_reg = aue_uno_mii_write_reg,
 	.uno_statchg = aue_uno_mii_statchg,
@@ -284,8 +284,6 @@ aue_csr_read_1(struct aue_softc *sc, int reg)
 	usbd_status		err;
 	uByte			val = 0;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -315,8 +313,6 @@ aue_csr_read_2(struct aue_softc *sc, int reg)
 	usbd_status		err;
 	uWord			val;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -346,8 +342,6 @@ aue_csr_write_1(struct aue_softc *sc, int reg, int aval)
 	usbd_status		err;
 	uByte			val;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -378,8 +372,6 @@ aue_csr_write_2(struct aue_softc *sc, int reg, int aval)
 	usbd_status		err;
 	uWord			val;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -440,8 +432,6 @@ aue_read_mac(struct usbnet *un)
 	int			off = 0;
 	int			word;
 
-	usbnet_isowned_core(un);
-
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGS("aue%jd: enter",
 	    device_unit(un->un_dev), 0, 0, 0);
@@ -483,6 +473,8 @@ aue_uno_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
 
 	for (i = 0; i < AUE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
 			break;
 	}
@@ -524,6 +516,8 @@ aue_uno_mii_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
 	aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
 
 	for (i = 0; i < AUE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
 			break;
 	}
@@ -607,10 +601,10 @@ aue_crc(void *addrv)
 }
 
 static void
-aue_setiff_locked(struct usbnet *un)
+aue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet * const un = ifp->if_softc;
 	struct aue_softc * const sc = usbnet_softc(un);
-	struct ifnet * const	ifp = usbnet_ifp(un);
 	struct ethercom *	ec = usbnet_ec(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
@@ -620,24 +614,21 @@ aue_setiff_locked(struct usbnet *un)
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGSN(5, "aue%jd: enter", device_unit(un->un_dev), 0, 0, 0);
 
-	usbnet_isowned_core(un);
-
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
 		return;
 	}
 
-	AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
-
 	/* now program new ones */
 	ETHER_LOCK(ec);
 	ETHER_FIRST_MULTI(step, ec, enm);
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo,
 		    enm->enm_addrhi, ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -645,13 +636,14 @@ allmulti:
 		hashtbl[h >> 3] |= 1 << (h & 0x7);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
+	AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+
 	/* write the hashtable */
 	for (i = 0; i < 8; i++)
 		aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]);
-
-	ifp->if_flags &= ~IFF_ALLMULTI;
 }
 
 static void
@@ -680,6 +672,8 @@ aue_reset(struct aue_softc *sc)
 	AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
 
 	for (i = 0; i < AUE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
 			break;
 	}
@@ -848,16 +842,12 @@ aue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* First level attach. */
-	usbnet_attach(un, "auedet");
-
-	usbnet_lock_core(un);
+	usbnet_attach(un);
 
 	/* Reset the adapter and get station address from the EEPROM.  */
 	aue_reset(sc);
 	aue_read_mac(un);
 
-	usbnet_unlock_core(un);
-
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
 	    0, &unm);
 }
@@ -948,24 +938,17 @@ aue_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-aue_init_locked(struct ifnet *ifp)
+aue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	struct aue_softc	*sc = usbnet_softc(un);
-	int			i, rv;
+	int			i;
 	const u_char		*eaddr;
 
 	AUEHIST_FUNC();
 	AUEHIST_CALLARGSN(5, "aue%jd: enter link=%jd",
 	    device_unit(un->un_dev), usbnet_havelink(un), 0, 0);
 
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O */
-	if (ifp->if_flags & IFF_RUNNING)
-		return 0;
-
 	/* Reset the interface. */
 	aue_reset(sc);
 
@@ -979,54 +962,11 @@ aue_init_locked(struct ifnet *ifp)
 	else
 		AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
 
-	rv = usbnet_init_rx_tx(un);
-
-	/* Load the multicast filter. */
-	aue_setiff_locked(un);
-
 	/* Enable RX and TX */
 	aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
 	AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
 	AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
 
-	//mii_mediachg(mii);
-
-	return rv;
-}
-
-static int
-aue_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const	un = ifp->if_softc;
-	int rv;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	rv = aue_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return rv;
-}
-
-static int
-aue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-
-	AUEHIST_FUNC();
-	AUEHIST_CALLARGSN(5, "aue%jd: enter cmd %#jx data %#jx",
-	    device_unit(((struct usbnet *)(ifp->if_softc))->un_dev),
-	    cmd, (uintptr_t)data, 0);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		aue_uno_init(ifp);
-		break;
-	default:
-		break;
-	}
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c
index eba67386e770..c6a0cc11ee85 100644
--- a/sys/dev/usb/if_axe.c
+++ b/sys/dev/usb/if_axe.c
@@ -259,7 +259,7 @@ CFATTACH_DECL_NEW(axe, sizeof(struct axe_softc),
 	axe_match, axe_attach, usbnet_detach, usbnet_activate);
 
 static void	axe_uno_stop(struct ifnet *, int);
-static int	axe_uno_ioctl(struct ifnet *, u_long, void *);
+static void	axe_uno_mcast(struct ifnet *);
 static int	axe_uno_init(struct ifnet *);
 static int	axe_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	axe_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
@@ -276,7 +276,7 @@ static void	axe_ax88772b_init(struct axe_softc *);
 
 static const struct usbnet_ops axe_ops = {
 	.uno_stop = axe_uno_stop,
-	.uno_ioctl = axe_uno_ioctl,
+	.uno_mcast = axe_uno_mcast,
 	.uno_read_reg = axe_uno_mii_read_reg,
 	.uno_write_reg = axe_uno_mii_write_reg,
 	.uno_statchg = axe_uno_mii_statchg,
@@ -293,8 +293,6 @@ axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return -1;
 
@@ -426,11 +424,11 @@ axe_uno_mii_statchg(struct ifnet *ifp)
 }
 
 static void
-axe_rcvfilt_locked(struct usbnet *un)
+axe_uno_mcast(struct ifnet *ifp)
 {
 	AXEHIST_FUNC(); AXEHIST_CALLED();
+	struct usbnet * const un = ifp->if_softc;
 	struct axe_softc * const sc = usbnet_softc(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -512,8 +510,6 @@ static void
 axe_reset(struct usbnet *un)
 {
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
@@ -926,15 +922,11 @@ axe_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for axe_cmd().  */
-	usbnet_attach(un, "axedet");
+	usbnet_attach(un);
 
 	/* We need the PHYID for init dance in some cases */
-	usbnet_lock_core(un);
-	usbnet_busy(un);
 	if (axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, &sc->axe_phyaddrs)) {
 		aprint_error_dev(self, "failed to read phyaddrs\n");
-		usbnet_unbusy(un);
-		usbnet_unlock_core(un);
 		return;
 	}
 
@@ -964,15 +956,10 @@ axe_attach(device_t parent, device_t self, void *aux)
 	} else {
 		if (axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs)) {
 			aprint_error_dev(self, "failed to read ipg\n");
-			usbnet_unbusy(un);
-			usbnet_unlock_core(un);
 			return;
 		}
 	}
 
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	if (!AXE_IS_172(un))
 		usbnet_ec(un)->ec_capabilities = ETHERCAP_VLAN_MTU;
 	if (un->un_flags & AX772B) {
@@ -1140,8 +1127,6 @@ axe_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 	size_t hdr_len = 0, tlr_len = 0;
 	int length, boundary;
 
-	usbnet_isowned_tx(un);
-
 	if (!AXE_IS_172(un)) {
 		/*
 		 * Copy the mbuf data into a contiguous buffer, leaving two
@@ -1216,21 +1201,13 @@ axe_csum_cfg(struct axe_softc *sc)
 }
 
 static int
-axe_init_locked(struct ifnet *ifp)
+axe_uno_init(struct ifnet *ifp)
 {
 	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
 	struct axe_softc * const sc = usbnet_softc(un);
 	int rxmode;
 
-	usbnet_isowned_core(un);
-
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
-
 	/* Reset the ethernet interface. */
 	axe_reset(un);
 
@@ -1306,46 +1283,6 @@ axe_init_locked(struct ifnet *ifp)
 
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
 
-	/* Accept multicast frame or run promisc. mode */
-	axe_rcvfilt_locked(un);
-
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-axe_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	int ret = axe_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return ret;
-}
-
-static int
-axe_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		axe_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c
index bf91d56b356d..2cf58eec5bd5 100644
--- a/sys/dev/usb/if_axen.c
+++ b/sys/dev/usb/if_axen.c
@@ -80,6 +80,7 @@ static void	axen_ax88179_init(struct usbnet *);
 
 static void	axen_uno_stop(struct ifnet *, int);
 static int	axen_uno_ioctl(struct ifnet *, u_long, void *);
+static void	axen_uno_mcast(struct ifnet *);
 static int	axen_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	axen_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void	axen_uno_mii_statchg(struct ifnet *);
@@ -92,6 +93,7 @@ static int	axen_uno_init(struct ifnet *);
 static const struct usbnet_ops axen_ops = {
 	.uno_stop = axen_uno_stop,
 	.uno_ioctl = axen_uno_ioctl,
+	.uno_mcast = axen_uno_mcast,
 	.uno_read_reg = axen_uno_mii_read_reg,
 	.uno_write_reg = axen_uno_mii_write_reg,
 	.uno_statchg = axen_uno_mii_statchg,
@@ -106,8 +108,6 @@ axen_cmd(struct usbnet *un, int cmd, int index, int val, void *buf)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -141,8 +141,10 @@ axen_uno_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 		return EINVAL;
 
 	usbd_status err = axen_cmd(un, AXEN_CMD_MII_READ_REG, reg, phy, &data);
-	if (err)
+	if (err) {
+		*val = 0;
 		return EIO;
+	}
 
 	*val = le16toh(data);
 	if (reg == MII_BMSR)
@@ -223,9 +225,9 @@ axen_uno_mii_statchg(struct ifnet *ifp)
 }
 
 static void
-axen_setiff_locked(struct usbnet *un)
+axen_uno_mcast(struct ifnet *ifp)
 {
-	struct ifnet * const ifp = usbnet_ifp(un);
+	struct usbnet * const un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -237,8 +239,6 @@ axen_setiff_locked(struct usbnet *un)
 	if (usbnet_isdying(un))
 		return;
 
-	usbnet_isowned_core(un);
-
 	rxmode = 0;
 
 	/* Enable receiver, set RX mode */
@@ -293,7 +293,6 @@ allmulti:
 static void
 axen_reset(struct usbnet *un)
 {
-	usbnet_isowned_core(un);
 	if (usbnet_isdying(un))
 		return;
 	/* XXX What to reset? */
@@ -365,9 +364,6 @@ axen_ax88179_init(struct usbnet *un)
 	uint16_t wval;
 	uint8_t val;
 
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
 	/* XXX: ? */
 	axen_cmd(un, AXEN_CMD_MAC_READ, 1, AXEN_UNK_05, &val);
 	DPRINTFN(5, ("AXEN_CMD_MAC_READ(0x05): 0x%02x\n", val));
@@ -450,8 +446,6 @@ axen_ax88179_init(struct usbnet *un)
 	default:
 		aprint_error_dev(un->un_dev, "unknown uplink bus:0x%02x\n",
 		    val);
-		usbnet_unbusy(un);
-		usbnet_unlock_core(un);
 		return;
 	}
 	axen_cmd(un, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl);
@@ -496,22 +490,19 @@ axen_ax88179_init(struct usbnet *un)
 #define GMII_PHY_PAGE_SEL	0x1e
 #define GMII_PHY_PAGE_SEL	0x1f
 #define GMII_PAGE_EXT		0x0007
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE_SEL,
+	axen_uno_mii_write_reg(un, un->un_phyno, GMII_PHY_PAGE_SEL,
 	    GMII_PAGE_EXT);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, GMII_PHY_PAGE,
+	axen_uno_mii_write_reg(un, un->un_phyno, GMII_PHY_PAGE,
 	    0x002c);
 #endif
 
 #if 1 /* XXX: phy hack ? */
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0005);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x0C, 0x0000);
-	usbnet_mii_readreg(un->un_dev, un->un_phyno, 0x0001, &wval);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x01, wval | 0x0080);
-	usbnet_mii_writereg(un->un_dev, un->un_phyno, 0x1F, 0x0000);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x1F, 0x0005);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x0C, 0x0000);
+	axen_uno_mii_read_reg(un, un->un_phyno, 0x0001, &wval);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x01, wval | 0x0080);
+	axen_uno_mii_write_reg(un, un->un_phyno, 0x1F, 0x0000);
 #endif
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 }
 
 static void
@@ -521,7 +512,7 @@ axen_setoe_locked(struct usbnet *un)
 	uint64_t enabled = ifp->if_capenable;
 	uint8_t val;
 
-	usbnet_isowned_core(un);
+	KASSERT(IFNET_LOCKED(ifp));
 
 	val = AXEN_RXCOE_OFF;
 	if (enabled & IFCAP_CSUM_IPv4_Rx)
@@ -555,16 +546,7 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
 	switch (cmd) {
-	case SIOCSIFFLAGS:
-	case SIOCSETHERCAP:
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		axen_setiff_locked(un);
-		break;
 	case SIOCSIFCAP:
 		axen_setoe_locked(un);
 		break;
@@ -572,9 +554,6 @@ axen_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
@@ -669,22 +648,16 @@ axen_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for axen_cmd().  */
-	usbnet_attach(un, "axendet");
+	usbnet_attach(un);
 
 	un->un_phyno = AXEN_PHY_ID;
 	DPRINTF(("%s: phyno %d\n", device_xname(self), un->un_phyno));
 
 	/* Get station address.  */
-	usbnet_lock_core(un);
-	usbnet_busy(un);
 	if (axen_get_eaddr(un, &un->un_eaddr)) {
-		usbnet_unbusy(un);
-		usbnet_unlock_core(un);
 		printf("EEPROM checksum error\n");
 		return;
 	}
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	axen_ax88179_init(un);
 
@@ -894,20 +867,15 @@ axen_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-axen_init_locked(struct ifnet *ifp)
+axen_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	uint16_t rxmode;
 	uint16_t wval;
 	uint8_t bval;
 
-	usbnet_isowned_core(un);
-
-	if (usbnet_isdying(un))
-		return EIO;
-
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	axen_uno_stop(ifp, 1);
 
 	/* Reset the ethernet interface. */
 	axen_reset(un);
@@ -919,9 +887,6 @@ axen_init_locked(struct ifnet *ifp)
 	/* Configure offloading engine. */
 	axen_setoe_locked(un);
 
-	/* Program promiscuous mode and multicast filters. */
-	axen_setiff_locked(un);
-
 	/* Enable receiver, set RX mode */
 	axen_cmd(un, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval);
 	rxmode = le16toh(wval);
@@ -929,21 +894,7 @@ axen_init_locked(struct ifnet *ifp)
 	wval = htole16(rxmode);
 	axen_cmd(un, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval);
 
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-axen_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	int ret = axen_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return ret;
+	return 0;
 }
 
 static void
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c
index 3edf73d856ca..452be10fc77e 100644
--- a/sys/dev/usb/if_cdce.c
+++ b/sys/dev/usb/if_cdce.c
@@ -82,12 +82,10 @@ static void	cdce_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
 				 uint32_t);
 static unsigned	cdce_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				    struct usbnet_chain *);
-static int	cdce_uno_init(struct ifnet *);
 
 static const struct usbnet_ops cdce_ops = {
 	.uno_tx_prepare = cdce_uno_tx_prepare,
 	.uno_rx_loop = cdce_uno_rx_loop,
-	.uno_init = cdce_uno_init,
 };
 
 static int
@@ -250,37 +248,16 @@ cdce_attach(device_t parent, device_t self, void *aux)
 		un->un_eaddr[5] = (uint8_t)(device_unit(un->un_dev));
 	}
 
-	usbnet_attach(un, "cdcedet");
+	usbnet_attach(un);
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
             0, NULL);
 }
 
-static int
-cdce_uno_init(struct ifnet *ifp)
-{
-	struct usbnet		*un = ifp->if_softc;
-	int rv;
-
-	usbnet_lock_core(un);
-	if (usbnet_isdying(un))
-		rv = EIO;
-	else {
-		usbnet_stop(un, ifp, 1);
-		rv = usbnet_init_rx_tx(un);
-		usbnet_set_link(un, rv == 0);
-	}
-	usbnet_unlock_core(un);
-
-	return rv;
-}
-
 static void
 cdce_uno_rx_loop(struct usbnet * un, struct usbnet_chain *c, uint32_t total_len)
 {
 	struct ifnet		*ifp = usbnet_ifp(un);
 
-	usbnet_isowned_rx(un);
-
 	/* Strip off CRC added by Zaurus, if present */
 	if (un->un_flags & CDCE_ZAURUS && total_len > 4)
 		total_len -= 4;
diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c
index e730297a939d..d6eaa2852123 100644
--- a/sys/dev/usb/if_cue.c
+++ b/sys/dev/usb/if_cue.c
@@ -141,14 +141,14 @@ CFATTACH_DECL_NEW(cue, sizeof(struct cue_softc), cue_match, cue_attach,
 static unsigned cue_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
 static void cue_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
-static int cue_uno_ioctl(struct ifnet *, u_long, void *);
+static void cue_uno_mcast(struct ifnet *);
 static void cue_uno_stop(struct ifnet *, int);
 static int cue_uno_init(struct ifnet *);
 static void cue_uno_tick(struct usbnet *);
 
 static const struct usbnet_ops cue_ops = {
 	.uno_stop = cue_uno_stop,
-	.uno_ioctl = cue_uno_ioctl,
+	.uno_mcast = cue_uno_mcast,
 	.uno_tx_prepare = cue_uno_tx_prepare,
 	.uno_rx_loop = cue_uno_rx_loop,
 	.uno_init = cue_uno_init,
@@ -357,11 +357,11 @@ cue_crc(const char *addr)
 }
 
 static void
-cue_setiff_locked(struct usbnet *un)
+cue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet		*un = ifp->if_softc;
 	struct cue_softc	*sc = usbnet_softc(un);
 	struct ethercom		*ec = usbnet_ec(un);
-	struct ifnet		*ifp = usbnet_ifp(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
 	uint32_t		h, i;
@@ -370,8 +370,10 @@ cue_setiff_locked(struct usbnet *un)
 	    device_xname(un->un_dev), ifp->if_flags));
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		for (i = 0; i < CUE_MCAST_TABLE_LEN; i++)
 			sc->cue_mctab[i] = 0xFF;
 		cue_mem(un, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
@@ -389,7 +391,6 @@ allmulti:
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo,
 		    enm->enm_addrhi, ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -397,10 +398,9 @@ allmulti:
 		sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
-	ifp->if_flags &= ~IFF_ALLMULTI;
-
 	/*
 	 * Also include the broadcast address in the filter
 	 * so we can receive broadcast frames.
@@ -525,7 +525,7 @@ cue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* First level attach. */
-	usbnet_attach(un, "cuedet");
+	usbnet_attach(un);
 
 #if 0
 	/* Reset the adapter. */
@@ -545,8 +545,6 @@ cue_uno_tick(struct usbnet *un)
 {
 	struct ifnet		*ifp = usbnet_ifp(un);
 
-	usbnet_lock_core(un);
-
 	net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
 	if (cue_csr_read_2(un, CUE_RX_FRAMEERR))
 		if_statinc_ref(nsr, if_ierrors);
@@ -558,8 +556,6 @@ cue_uno_tick(struct usbnet *un)
 	if_statadd_ref(nsr, if_collisions,
 	    cue_csr_read_2(un, CUE_TX_EXCESSCOLL));
 	IF_STAT_PUTREF(ifp);
-
-	usbnet_unlock_core(un);
 }
 
 static void
@@ -613,7 +609,7 @@ cue_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-cue_init_locked(struct ifnet *ifp)
+cue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	int			i, ctl;
@@ -621,11 +617,8 @@ cue_init_locked(struct ifnet *ifp)
 
 	DPRINTFN(10,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
-	if (usbnet_isdying(un))
-		return -1;
-
 	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
+	cue_uno_stop(ifp, 1);
 
 	/* Reset the interface. */
 #if 1
@@ -647,9 +640,6 @@ cue_init_locked(struct ifnet *ifp)
 		ctl |= CUE_ETHCTL_PROMISC;
 	cue_csr_write_1(un, CUE_ETHCTL, ctl);
 
-	/* Load the multicast filter. */
-	cue_setiff_locked(un);
-
 	/*
 	 * Set the number of RX and TX buffers that we want
 	 * to reserve inside the ASIC.
@@ -664,44 +654,6 @@ cue_init_locked(struct ifnet *ifp)
 	/* Program the LED operation. */
 	cue_csr_write_1(un, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
 
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-cue_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const	un = ifp->if_softc;
-	int rv;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	rv = cue_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return rv;
-}
-
-static int
-cue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const	un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		cue_setiff_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c
index 172bc93fa301..b34ac031559c 100644
--- a/sys/dev/usb/if_kue.c
+++ b/sys/dev/usb/if_kue.c
@@ -174,11 +174,11 @@ CFATTACH_DECL_NEW(kue, sizeof(struct kue_softc), kue_match, kue_attach,
 static void kue_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static unsigned kue_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
-static int kue_uno_ioctl(struct ifnet *, u_long, void *);
+static void kue_uno_mcast(struct ifnet *);
 static int kue_uno_init(struct ifnet *);
 
 static const struct usbnet_ops kue_ops = {
-	.uno_ioctl = kue_uno_ioctl,
+	.uno_mcast = kue_uno_mcast,
 	.uno_tx_prepare = kue_uno_tx_prepare,
 	.uno_rx_loop = kue_uno_rx_loop,
 	.uno_init = kue_uno_init,
@@ -318,11 +318,11 @@ kue_load_fw(struct usbnet *un)
 }
 
 static void
-kue_setiff_locked(struct usbnet *un)
+kue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet *		un = ifp->if_softc;
 	struct ethercom *	ec = usbnet_ec(un);
 	struct kue_softc *	sc = usbnet_softc(un);
-	struct ifnet * const	ifp = usbnet_ifp(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
 	int			i;
@@ -336,8 +336,10 @@ kue_setiff_locked(struct usbnet *un)
 		sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC;
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI|KUE_RXFILT_PROMISC;
 		sc->kue_rxfilt &= ~KUE_RXFILT_MULTICAST;
 		kue_setword(un, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
@@ -353,7 +355,6 @@ allmulti:
 		if (i == KUE_MCFILTCNT(sc) ||
 		    memcmp(enm->enm_addrlo, enm->enm_addrhi,
 		    ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -361,10 +362,9 @@ allmulti:
 		ETHER_NEXT_MULTI(step, enm);
 		i++;
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
-	ifp->if_flags &= ~IFF_ALLMULTI;
-
 	sc->kue_rxfilt |= KUE_RXFILT_MULTICAST;
 	kue_ctl(un, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS,
 	    i, sc->kue_mcfilters, i * ETHER_ADDR_LEN);
@@ -494,7 +494,7 @@ kue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* First level attach, so kue_ctl() works. */
-	usbnet_attach(un, "kuedet");
+	usbnet_attach(un);
 
 	/* Read ethernet descriptor */
 	err = kue_ctl(un, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
@@ -591,7 +591,7 @@ kue_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-kue_init_locked(struct ifnet *ifp)
+kue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const	un = ifp->if_softc;
 	struct kue_softc	*sc = usbnet_softc(un);
@@ -599,12 +599,6 @@ kue_init_locked(struct ifnet *ifp)
 
 	DPRINTFN(5,("%s: %s: enter\n", device_xname(un->un_dev),__func__));
 
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
-
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
 	/* Set MAC address */
 	kue_ctl(un, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, eaddr, ETHER_ADDR_LEN);
@@ -621,47 +615,6 @@ kue_init_locked(struct ifnet *ifp)
 #endif
 	kue_setword(un, KUE_CMD_SET_URB_SIZE, 64);
 
-	/* Load the multicast filter. */
-	kue_setiff_locked(un);
-
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-kue_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const	un = ifp->if_softc;
-	int rv;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	rv = kue_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return rv;
-}
-
-static int
-kue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const	un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		kue_setiff_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c
index 347104b7b0df..b89087007259 100644
--- a/sys/dev/usb/if_mos.c
+++ b/sys/dev/usb/if_mos.c
@@ -145,7 +145,7 @@ CFATTACH_DECL_NEW(mos, sizeof(struct usbnet),
 static void mos_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static unsigned mos_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
-static int mos_uno_ioctl(struct ifnet *, u_long, void *);
+static void mos_uno_mcast(struct ifnet *);
 static int mos_uno_init(struct ifnet *);
 static void mos_chip_init(struct usbnet *);
 static void mos_uno_stop(struct ifnet *ifp, int disable);
@@ -164,7 +164,7 @@ static int mos_write_mcast(struct usbnet *, uint8_t *);
 
 static const struct usbnet_ops mos_ops = {
 	.uno_stop = mos_uno_stop,
-	.uno_ioctl = mos_uno_ioctl,
+	.uno_mcast = mos_uno_mcast,
 	.uno_read_reg = mos_uno_mii_read_reg,
 	.uno_write_reg = mos_uno_mii_write_reg,
 	.uno_statchg = mos_uno_mii_statchg,
@@ -364,6 +364,8 @@ mos_uno_mii_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
 	    MOS_PHYSTS_PENDING);
 
 	for (i = 0; i < MOS_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (mos_reg_read_1(un, MOS_PHY_STS) & MOS_PHYSTS_READY)
 			break;
 	}
@@ -396,6 +398,8 @@ mos_uno_mii_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
 	    MOS_PHYSTS_PENDING);
 
 	for (i = 0; i < MOS_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if (mos_reg_read_1(un, MOS_PHY_STS) & MOS_PHYSTS_READY)
 			break;
 	}
@@ -454,9 +458,9 @@ mos_uno_mii_statchg(struct ifnet *ifp)
 }
 
 static void
-mos_rcvfilt_locked(struct usbnet *un)
+mos_uno_mcast(struct ifnet *ifp)
 {
-	struct ifnet		*ifp = usbnet_ifp(un);
+	struct usbnet		*un = ifp->if_softc;
 	struct ethercom		*ec = usbnet_ec(un);
 	struct ether_multi	*enm;
 	struct ether_multistep	step;
@@ -632,7 +636,7 @@ mos_attach(device_t parent, device_t self, void *aux)
 		aprint_normal_dev(self, "MCS7832\n");
 
 	/* Set these up now for register access. */
-	usbnet_attach(un, "mosdet");
+	usbnet_attach(un);
 
 	mos_chip_init(un);
 
@@ -719,18 +723,12 @@ mos_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 }
 
 static int
-mos_init_locked(struct ifnet *ifp)
+mos_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	u_int8_t		rxmode;
 	unsigned char		ipgs[2];
 
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
-
 	/* Reset the ethernet interface. */
 	mos_reset(un);
 
@@ -743,52 +741,12 @@ mos_init_locked(struct ifnet *ifp)
 	mos_reg_write_1(un, MOS_IPG0, ipgs[0]);
 	mos_reg_write_1(un, MOS_IPG1, ipgs[1]);
 
-	/* Accept multicast frame or run promisc. mode */
-	mos_rcvfilt_locked(un);
-
 	/* Enable receiver and transmitter, bridge controls speed/duplex mode */
 	rxmode = mos_reg_read_1(un, MOS_CTL);
 	rxmode |= MOS_CTL_RX_ENB | MOS_CTL_TX_ENB | MOS_CTL_BS_ENB;
 	rxmode &= ~(MOS_CTL_SLEEP);
 	mos_reg_write_1(un, MOS_CTL, rxmode);
 
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-mos_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	int ret = mos_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return ret;
-}
-
-static int
-mos_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		mos_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c
index 559b9eff2569..68cb400be022 100644
--- a/sys/dev/usb/if_mue.c
+++ b/sys/dev/usb/if_mue.c
@@ -92,7 +92,7 @@ static int	mue_chip_init(struct usbnet *);
 static void	mue_set_macaddr(struct usbnet *);
 static int	mue_get_macaddr(struct usbnet *, prop_dictionary_t);
 static int	mue_prepare_tso(struct usbnet *, struct mbuf *);
-static void	mue_setiff_locked(struct usbnet *);
+static void	mue_uno_mcast(struct ifnet *);
 static void	mue_sethwcsum_locked(struct usbnet *);
 static void	mue_setmtu_locked(struct usbnet *);
 static void	mue_reset(struct usbnet *);
@@ -111,6 +111,7 @@ static int	mue_uno_init(struct ifnet *);
 static const struct usbnet_ops mue_ops = {
 	.uno_stop = mue_uno_stop,
 	.uno_ioctl = mue_uno_ioctl,
+	.uno_mcast = mue_uno_mcast,
 	.uno_read_reg = mue_uno_mii_read_reg,
 	.uno_write_reg = mue_uno_mii_write_reg,
 	.uno_statchg = mue_uno_mii_statchg,
@@ -200,6 +201,8 @@ mue_wait_for_bits(struct usbnet *un, uint32_t reg,
 	int ntries;
 
 	for (ntries = 0; ntries < 1000; ntries++) {
+		if (usbnet_isdying(un))
+			return 1;
 		val = mue_csr_read(un, reg);
 		if ((val & set) || !(val & clear))
 			return 0;
@@ -849,7 +852,7 @@ mue_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for mue_cmd().  */
-	usbnet_attach(un, "muedet");
+	usbnet_attach(un);
 
 	un->un_phyno = 1;
 
@@ -993,10 +996,10 @@ mue_prepare_tso(struct usbnet *un, struct mbuf *m)
 }
 
 static void
-mue_setiff_locked(struct usbnet *un)
+mue_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet *un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	const uint8_t *enaddr = CLLADDR(ifp->if_sadl);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -1020,10 +1023,11 @@ mue_setiff_locked(struct usbnet *un)
 	/* Always accept broadcast frames. */
 	rxfilt |= MUE_RFE_CTL_BROADCAST;
 
+	ETHER_LOCK(ec);
 	if (ifp->if_flags & IFF_PROMISC) {
 		rxfilt |= MUE_RFE_CTL_UNICAST;
 allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
-		ifp->if_flags |= IFF_ALLMULTI;
+		ec->ec_flags |= ETHER_F_ALLMULTI;
 		if (ifp->if_flags & IFF_PROMISC)
 			DPRINTF(un, "promisc\n");
 		else
@@ -1033,7 +1037,6 @@ allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 		pfiltbl[0][0] = MUE_ENADDR_HI(enaddr) | MUE_ADDR_FILTX_VALID;
 		pfiltbl[0][1] = MUE_ENADDR_LO(enaddr);
 		i = 1;
-		ETHER_LOCK(ec);
 		ETHER_FIRST_MULTI(step, ec, enm);
 		while (enm != NULL) {
 			if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
@@ -1041,7 +1044,6 @@ allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 				memset(pfiltbl, 0, sizeof(pfiltbl));
 				memset(hashtbl, 0, sizeof(hashtbl));
 				rxfilt &= ~MUE_RFE_CTL_MULTICAST_HASH;
-				ETHER_UNLOCK(ec);
 				goto allmulti;
 			}
 			if (i < MUE_NUM_ADDR_FILTX) {
@@ -1059,14 +1061,14 @@ allmulti:	rxfilt |= MUE_RFE_CTL_MULTICAST;
 			i++;
 			ETHER_NEXT_MULTI(step, enm);
 		}
-		ETHER_UNLOCK(ec);
+		ec->ec_flags &= ~ETHER_F_ALLMULTI;
 		rxfilt |= MUE_RFE_CTL_PERFECT;
-		ifp->if_flags &= ~IFF_ALLMULTI;
 		if (rxfilt & MUE_RFE_CTL_MULTICAST_HASH)
 			DPRINTF(un, "perfect filter and hash tables\n");
 		else
 			DPRINTF(un, "perfect filter\n");
 	}
+	ETHER_UNLOCK(ec);
 
 	for (i = 0; i < MUE_NUM_ADDR_FILTX; i++) {
 		hireg = (un->un_flags & LAN7500) ?
@@ -1089,6 +1091,8 @@ mue_sethwcsum_locked(struct usbnet *un)
 	struct ifnet * const ifp = usbnet_ifp(un);
 	uint32_t reg, val;
 
+	KASSERT(IFNET_LOCKED(ifp));
+
 	reg = (un->un_flags & LAN7500) ? MUE_7500_RFE_CTL : MUE_7800_RFE_CTL;
 	val = mue_csr_read(un, reg);
 
@@ -1121,6 +1125,8 @@ mue_setmtu_locked(struct usbnet *un)
 	struct ifnet * const ifp = usbnet_ifp(un);
 	uint32_t val;
 
+	KASSERT(IFNET_LOCKED(ifp));
+
 	/* Set the maximum frame size. */
 	MUE_CLRBIT(un, MUE_MAC_RX, MUE_MAC_RX_RXEN);
 	val = mue_csr_read(un, MUE_MAC_RX);
@@ -1219,49 +1225,22 @@ mue_uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
 }
 
 static int
-mue_init_locked(struct ifnet *ifp)
+mue_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	if (usbnet_isdying(un)) {
-		DPRINTF(un, "dying\n");
-		return EIO;
-	}
-
-	/* Cancel pending I/O and free all TX/RX buffers. */
-	if (ifp->if_flags & IFF_RUNNING)
-		usbnet_stop(un, ifp, 1);
-
 	mue_reset(un);
 
 	/* Set MAC address. */
 	mue_set_macaddr(un);
 
-	/* Load the multicast filter. */
-	mue_setiff_locked(un);
-
 	/* TCP/UDP checksum offload engines. */
 	mue_sethwcsum_locked(un);
 
 	/* Set MTU. */
 	mue_setmtu_locked(un);
 
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-mue_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const	un = ifp->if_softc;
-	int rv;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	rv = mue_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return rv;
+	return 0;
 }
 
 static int
@@ -1269,16 +1248,7 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
 	switch (cmd) {
-	case SIOCSIFFLAGS:
-	case SIOCSETHERCAP:
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		mue_setiff_locked(un);
-		break;
 	case SIOCSIFCAP:
 		mue_sethwcsum_locked(un);
 		break;
@@ -1289,9 +1259,6 @@ mue_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c
index 6cf37e507d1c..ab4827f580f9 100644
--- a/sys/dev/usb/if_smsc.c
+++ b/sys/dev/usb/if_smsc.c
@@ -174,7 +174,6 @@ static int	 smsc_chip_init(struct usbnet *);
 static int	 smsc_setmacaddress(struct usbnet *, const uint8_t *);
 
 static int	 smsc_uno_init(struct ifnet *);
-static int	 smsc_init_locked(struct ifnet *);
 static void	 smsc_uno_stop(struct ifnet *, int);
 
 static void	 smsc_reset(struct smsc_softc *);
@@ -187,6 +186,7 @@ static int	 smsc_uno_miibus_readreg(struct usbnet *, int, int, uint16_t *);
 static int	 smsc_uno_miibus_writereg(struct usbnet *, int, int, uint16_t);
 
 static int	 smsc_uno_ioctl(struct ifnet *, u_long, void *);
+static void	 smsc_uno_mcast(struct ifnet *);
 static unsigned	 smsc_uno_tx_prepare(struct usbnet *, struct mbuf *,
 		     struct usbnet_chain *);
 static void	 smsc_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
@@ -195,6 +195,7 @@ static void	 smsc_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
 static const struct usbnet_ops smsc_ops = {
 	.uno_stop = smsc_uno_stop,
 	.uno_ioctl = smsc_uno_ioctl,
+	.uno_mcast = smsc_uno_mcast,
 	.uno_read_reg = smsc_uno_miibus_readreg,
 	.uno_write_reg = smsc_uno_miibus_writereg,
 	.uno_statchg = smsc_uno_miibus_statchg,
@@ -210,8 +211,6 @@ smsc_readreg(struct usbnet *un, uint32_t off, uint32_t *data)
 	uint32_t buf;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -237,8 +236,6 @@ smsc_writereg(struct usbnet *un, uint32_t off, uint32_t data)
 	uint32_t buf;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return 0;
 
@@ -264,6 +261,8 @@ smsc_wait_for_bits(struct usbnet *un, uint32_t reg, uint32_t bits)
 	int err, i;
 
 	for (i = 0; i < 100; i++) {
+		if (usbnet_isdying(un))
+			return ENXIO;
 		if ((err = smsc_readreg(un, reg, &val)) != 0)
 			return err;
 		if (!(val & bits))
@@ -408,24 +407,25 @@ smsc_hash(uint8_t addr[ETHER_ADDR_LEN])
 }
 
 static void
-smsc_setiff_locked(struct usbnet *un)
+smsc_uno_mcast(struct ifnet *ifp)
 {
 	USMSCHIST_FUNC(); USMSCHIST_CALLED();
+	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
 	uint32_t hashtbl[2] = { 0, 0 };
 	uint32_t hash;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
-	if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
 allmulti:
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		DPRINTF("receive all multicast enabled", 0, 0, 0, 0);
 		sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
 		sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT;
@@ -440,7 +440,6 @@ allmulti:
 	ETHER_FIRST_MULTI(step, ec, enm);
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -448,6 +447,7 @@ allmulti:
 		hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
 	/* Debug */
@@ -460,7 +460,6 @@ allmulti:
 	/* Write the hash table and mac control registers */
 
 	//XXX should we be doing this?
-	ifp->if_flags &= ~IFF_ALLMULTI;
 	smsc_writereg(un, SMSC_HASHH, hashtbl[1]);
 	smsc_writereg(un, SMSC_HASHL, hashtbl[0]);
 	smsc_writereg(un, SMSC_MAC_CSR, sc->sc_mac_csr);
@@ -474,7 +473,7 @@ smsc_setoe_locked(struct usbnet *un)
 	uint32_t val;
 	int err;
 
-	usbnet_isowned_core(un);
+	KASSERT(IFNET_LOCKED(ifp));
 
 	err = smsc_readreg(un, SMSC_COE_CTRL, &val);
 	if (err != 0) {
@@ -536,7 +535,6 @@ smsc_reset(struct smsc_softc *sc)
 {
 	struct usbnet * const un = &sc->smsc_un;
 
-	usbnet_isowned_core(un);
 	if (usbnet_isdying(un))
 		return;
 
@@ -549,42 +547,17 @@ smsc_reset(struct smsc_softc *sc)
 
 static int
 smsc_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	int ret = smsc_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return ret;
-}
-
-static int
-smsc_init_locked(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	struct smsc_softc * const sc = usbnet_softc(un);
 
-	usbnet_isowned_core(un);
-
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O */
-	usbnet_stop(un, ifp, 1);
-
 	/* Reset the ethernet interface. */
 	smsc_reset(sc);
 
-	/* Load the multicast filter. */
-	smsc_setiff_locked(un);
-
 	/* TCP/UDP checksum offload engines. */
 	smsc_setoe_locked(un);
 
-	return usbnet_init_rx_tx(un);
+	return 0;
 }
 
 static void
@@ -605,8 +578,6 @@ smsc_chip_init(struct usbnet *un)
 	int burst_cap;
 	int err;
 
-	usbnet_isowned_core(un);
-
 	/* Enter H/W config mode */
 	smsc_writereg(un, SMSC_HW_CFG, SMSC_HW_CFG_LRST);
 
@@ -753,16 +724,7 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 	struct usbnet * const un = ifp->if_softc;
 
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
 	switch (cmd) {
-	case SIOCSIFFLAGS:
-	case SIOCSETHERCAP:
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		smsc_setiff_locked(un);
-		break;
 	case SIOCSIFCAP:
 		smsc_setoe_locked(un);
 		break;
@@ -770,9 +732,6 @@ smsc_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		break;
 	}
 
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
 	return 0;
 }
 
@@ -863,7 +822,7 @@ smsc_attach(device_t parent, device_t self, void *aux)
 		}
 	}
 
-	usbnet_attach(un, "smscdet");
+	usbnet_attach(un);
 
 #ifdef notyet
 	/*
@@ -881,8 +840,6 @@ smsc_attach(device_t parent, device_t self, void *aux)
 	/* Setup some of the basics */
 	un->un_phyno = 1;
 
-	usbnet_lock_core(un);
-	usbnet_busy(un);
 	/*
 	 * Attempt to get the mac address, if an EEPROM is not attached this
 	 * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
@@ -910,8 +867,6 @@ smsc_attach(device_t parent, device_t self, void *aux)
 			un->un_eaddr[0] = (uint8_t)((mac_l) & 0xff);
 		}
 	}
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
 	    0, &unm);
diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c
index a38a83a53df0..f4dfe968f2c9 100644
--- a/sys/dev/usb/if_udav.c
+++ b/sys/dev/usb/if_udav.c
@@ -69,12 +69,11 @@ static unsigned udav_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				    struct usbnet_chain *);
 static void udav_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static void udav_uno_stop(struct ifnet *, int);
-static int udav_uno_ioctl(struct ifnet *, u_long, void *);
+static void udav_uno_mcast(struct ifnet *);
 static int udav_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int udav_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void udav_uno_mii_statchg(struct ifnet *);
 static int udav_uno_init(struct ifnet *);
-static void udav_setiff_locked(struct usbnet *);
 static void udav_reset(struct usbnet *);
 
 static int udav_csr_read(struct usbnet *, int, void *, int);
@@ -132,7 +131,7 @@ static const struct udav_type {
 
 static const struct usbnet_ops udav_ops = {
 	.uno_stop = udav_uno_stop,
-	.uno_ioctl = udav_uno_ioctl,
+	.uno_mcast = udav_uno_mcast,
 	.uno_read_reg = udav_uno_mii_read_reg,
 	.uno_write_reg = udav_uno_mii_write_reg,
 	.uno_statchg = udav_uno_mii_statchg,
@@ -237,18 +236,13 @@ udav_attach(device_t parent, device_t self, void *aux)
 	/* Not supported yet. */
 	un->un_ed[USBNET_ENDPT_INTR] = 0;
 
-	usbnet_attach(un, "udavdet");
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
+	usbnet_attach(un);
 
 // 	/* reset the adapter */
 // 	udav_reset(un);
 
 	/* Get Ethernet Address */
 	err = udav_csr_read(un, UDAV_PAR, un->un_eaddr, ETHER_ADDR_LEN);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");
 		return;
@@ -367,8 +361,8 @@ udav_csr_read(struct usbnet *un, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-	KASSERT(!usbnet_isdying(un));
+	if (usbnet_isdying(un))
+		return USBD_IOERROR;
 
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
@@ -398,8 +392,8 @@ udav_csr_write(struct usbnet *un, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-	KASSERT(!usbnet_isdying(un));
+	if (usbnet_isdying(un))
+		return USBD_IOERROR;
 
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
@@ -427,8 +421,6 @@ udav_csr_read1(struct usbnet *un, int offset)
 {
 	uint8_t val = 0;
 
-	usbnet_isowned_core(un);
-
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
@@ -445,8 +437,8 @@ udav_csr_write1(struct usbnet *un, int offset, unsigned char ch)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-	KASSERT(!usbnet_isdying(un));
+	if (usbnet_isdying(un))
+		return USBD_IOERROR;
 
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
@@ -478,19 +470,6 @@ udav_uno_init(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_lock_core(un);
-
-	if (usbnet_isdying(un)) {
-		usbnet_unlock_core(un);
-		return EIO;
-	}
-
-	/* Cancel pending I/O and free all TX/RX buffers */
-	if (ifp->if_flags & IFF_RUNNING)
-		usbnet_stop(un, ifp, 1);
-
-	usbnet_busy(un);
-
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
 	udav_csr_write(un, UDAV_PAR, eaddr, ETHER_ADDR_LEN);
 
@@ -507,9 +486,6 @@ udav_uno_init(struct ifnet *ifp)
 	else
 		UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 
-	/* Load the multicast filter */
-	udav_setiff_locked(un);
-
 	/* Enable RX */
 	UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_RXEN);
 
@@ -521,26 +497,18 @@ udav_uno_init(struct ifnet *ifp)
 		rc = 0;
 
 	if (rc != 0) {
-		usbnet_unbusy(un);
-		usbnet_unlock_core(un);
 		return rc;
 	}
 
 	if (usbnet_isdying(un))
-		rc = EIO;
-	else
-		rc = usbnet_init_rx_tx(un);
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
+		return EIO;
 
-	return rc;
+	return 0;
 }
 
 static void
 udav_reset(struct usbnet *un)
 {
-    	usbnet_isowned_core(un);
 
 	if (usbnet_isdying(un))
 		return;
@@ -553,7 +521,6 @@ udav_reset(struct usbnet *un)
 static void
 udav_chip_init(struct usbnet *un)
 {
-	usbnet_isowned_core(un);
 
 	/* Select PHY */
 #if 1
@@ -573,6 +540,8 @@ udav_chip_init(struct usbnet *un)
 	UDAV_SETBIT(un, UDAV_NCR, UDAV_NCR_RST);
 
 	for (int i = 0; i < UDAV_TX_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(udav_csr_read1(un, UDAV_NCR) & UDAV_NCR_RST))
 			break;
 		delay(10);	/* XXX */
@@ -586,10 +555,10 @@ udav_chip_init(struct usbnet *un)
 	(ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1))
 
 static void
-udav_setiff_locked(struct usbnet *un)
+udav_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet * const un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
-	struct ifnet * const ifp = usbnet_ifp(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
 	uint8_t hashes[8];
@@ -597,8 +566,6 @@ udav_setiff_locked(struct usbnet *un)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
@@ -609,11 +576,16 @@ udav_setiff_locked(struct usbnet *un)
 	}
 
 	if (ifp->if_flags & IFF_PROMISC) {
+		ETHER_LOCK(ec);
+		ec->ec_flags |= ETHER_F_ALLMULTI;
+		ETHER_UNLOCK(ec);
 		UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC);
 		return;
-	} else if (ifp->if_flags & IFF_ALLMULTI) {
+	} else if (ifp->if_flags & IFF_ALLMULTI) { /* XXX ??? Can't happen? */
+		ETHER_LOCK(ec);
 allmulti:
-		ifp->if_flags |= IFF_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;
@@ -630,7 +602,6 @@ allmulti:
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
 		    ETHER_ADDR_LEN) != 0) {
-			ETHER_UNLOCK(ec);
 			goto allmulti;
 		}
 
@@ -638,10 +609,10 @@ allmulti:
 		hashes[h>>3] |= 1 << (h & 0x7);
 		ETHER_NEXT_MULTI(step, enm);
 	}
+	ec->ec_flags &= ~ETHER_F_ALLMULTI;
 	ETHER_UNLOCK(ec);
 
 	/* disable all multicast */
-	ifp->if_flags &= ~IFF_ALLMULTI;
 	UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL);
 
 	/* write hash value to the register */
@@ -721,29 +692,6 @@ udav_uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
 	usbnet_enqueue(un, buf, pkt_len, 0, 0, 0);
 }
 
-static int
-udav_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		udav_setiff_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return 0;
-}
-
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
 static void
 udav_uno_stop(struct ifnet *ifp, int disable)
diff --git a/sys/dev/usb/if_upl.c b/sys/dev/usb/if_upl.c
index fb99702f160e..770c3a244152 100644
--- a/sys/dev/usb/if_upl.c
+++ b/sys/dev/usb/if_upl.c
@@ -111,10 +111,8 @@ static void upl_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static unsigned upl_uno_tx_prepare(struct usbnet *, struct mbuf *,
 			       struct usbnet_chain *);
 static int upl_uno_ioctl(struct ifnet *, u_long, void *);
-static int upl_uno_init(struct ifnet *);
 
 static const struct usbnet_ops upl_ops = {
-	.uno_init = upl_uno_init,
 	.uno_tx_prepare = upl_uno_tx_prepare,
 	.uno_rx_loop = upl_uno_rx_loop,
 	.uno_ioctl = upl_uno_ioctl,
@@ -207,7 +205,7 @@ upl_attach(device_t parent, device_t self, void *aux)
 		return;
 	}
 
-	usbnet_attach(un, "upldet");
+	usbnet_attach(un);
 
 	/* Initialize interface info.*/
 	struct ifnet *ifp = usbnet_ifp(un);
@@ -251,22 +249,6 @@ upl_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
 	return total_len;
 }
 
-static int
-upl_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-	int rv;
-
-	usbnet_lock_core(un);
-	if (usbnet_isdying(un))
-		rv = EIO;
-	else
-		rv = usbnet_init_rx_tx(un);
-	usbnet_unlock_core(un);
-
-	return rv;
-}
-
 static int
 upl_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
index ea5b3023dd31..6d1afeedd36a 100644
--- a/sys/dev/usb/if_ure.c
+++ b/sys/dev/usb/if_ure.c
@@ -86,7 +86,7 @@ static void	ure_disable_teredo(struct usbnet *);
 static void	ure_init_fifo(struct usbnet *);
 
 static void	ure_uno_stop(struct ifnet *, int);
-static int	ure_uno_ioctl(struct ifnet *, u_long, void *);
+static void	ure_uno_mcast(struct ifnet *);
 static int	ure_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int	ure_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
 static void	ure_uno_miibus_statchg(struct ifnet *);
@@ -104,7 +104,7 @@ CFATTACH_DECL_NEW(ure, sizeof(struct usbnet), ure_match, ure_attach,
 
 static const struct usbnet_ops ure_ops = {
 	.uno_stop = ure_uno_stop,
-	.uno_ioctl = ure_uno_ioctl,
+	.uno_mcast = ure_uno_mcast,
 	.uno_read_reg = ure_uno_mii_read_reg,
 	.uno_write_reg = ure_uno_mii_write_reg,
 	.uno_statchg = ure_uno_miibus_statchg,
@@ -331,17 +331,15 @@ ure_uno_miibus_statchg(struct ifnet *ifp)
 }
 
 static void
-ure_rcvfilt_locked(struct usbnet *un)
+ure_uno_mcast(struct ifnet *ifp)
 {
+	struct usbnet *un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
-	struct ifnet *ifp = usbnet_ifp(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
 	uint32_t mchash[2] = { 0, 0 };
 	uint32_t h = 0, rxmode;
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
@@ -391,11 +389,11 @@ ure_reset(struct usbnet *un)
 {
 	int i;
 
-	usbnet_isowned_core(un);
-
 	ure_write_1(un, URE_PLA_CR, URE_MCU_TYPE_PLA, URE_CR_RST);
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(ure_read_1(un, URE_PLA_CR, URE_MCU_TYPE_PLA) &
 		    URE_CR_RST))
 			break;
@@ -406,20 +404,11 @@ ure_reset(struct usbnet *un)
 }
 
 static int
-ure_init_locked(struct ifnet *ifp)
+ure_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	uint8_t eaddr[8];
 
-	usbnet_isowned_core(un);
-
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O. */
-	if (ifp->if_flags & IFF_RUNNING)
-		usbnet_stop(un, ifp, 1);
-
 	/* Set MAC address. */
 	memset(eaddr, 0, sizeof(eaddr));
 	memcpy(eaddr, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
@@ -445,24 +434,7 @@ ure_init_locked(struct ifnet *ifp)
 	    ure_read_2(un, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) &
 	    ~URE_RXDY_GATED_EN);
 
-	/* Accept multicast frame or run promisc. mode. */
-	ure_rcvfilt_locked(un);
-
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-ure_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	int ret = ure_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return ret;
+	return 0;
 }
 
 static void
@@ -543,6 +515,8 @@ ure_rtl8153_init(struct usbnet *un)
 	    URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2));
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (ure_read_2(un, URE_PLA_BOOT_CTRL, URE_MCU_TYPE_PLA) &
 		    URE_AUTOLOAD_DONE)
 			break;
@@ -552,6 +526,8 @@ ure_rtl8153_init(struct usbnet *un)
 		URE_PRINTF(un, "timeout waiting for chip autoload\n");
 
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		val = ure_ocp_reg_read(un, URE_OCP_PHY_STATUS) &
 		    URE_PHY_STAT_MASK;
 		if (val == URE_PHY_STAT_LAN_ON || val == URE_PHY_STAT_PWRDN)
@@ -750,6 +726,8 @@ ure_init_fifo(struct usbnet *un)
 	    ure_read_2(un, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) &
 	    ~URE_MCU_BORW_EN);
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (ure_read_1(un, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
 		    URE_LINK_LIST_READY)
 			break;
@@ -761,6 +739,8 @@ ure_init_fifo(struct usbnet *un)
 	    ure_read_2(un, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) |
 	    URE_RE_INIT_LL);
 	for (i = 0; i < URE_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (ure_read_1(un, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
 		    URE_LINK_LIST_READY)
 			break;
@@ -794,29 +774,6 @@ ure_init_fifo(struct usbnet *un)
 	    URE_TXFIFO_THR_NORMAL);
 }
 
-static int
-ure_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		ure_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return 0;
-}
-
 static int
 ure_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -894,7 +851,7 @@ ure_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for ure_ctl().  */
-	usbnet_attach(un, "uredet");
+	usbnet_attach(un);
 
 	un->un_phyno = 0;
 
@@ -927,7 +884,6 @@ ure_attach(device_t parent, device_t self, void *aux)
 	    (un->un_flags != 0) ? "" : "unknown ",
 	    ver);
 
-	usbnet_lock_core(un);
 	if (un->un_flags & URE_FLAG_8152)
 		ure_rtl8152_init(un);
 	else
@@ -940,7 +896,6 @@ ure_attach(device_t parent, device_t self, void *aux)
 	else
 		ure_read_mem(un, URE_PLA_BACKUP, URE_MCU_TYPE_PLA, eaddr,
 		    sizeof(eaddr));
-	usbnet_unlock_core(un);
 	if (ETHER_IS_ZERO(eaddr)) {
 		maclo = 0x00f2 | (cprng_strong32() & 0xffff0000);
 		machi = cprng_strong32() & 0xffff;
diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c
index 764b9c654682..1d9d76b6d727 100644
--- a/sys/dev/usb/if_url.c
+++ b/sys/dev/usb/if_url.c
@@ -77,11 +77,10 @@ static unsigned	url_uno_tx_prepare(struct usbnet *, struct mbuf *,
 static void url_uno_rx_loop(struct usbnet *, struct usbnet_chain *, uint32_t);
 static int url_uno_mii_read_reg(struct usbnet *, int, int, uint16_t *);
 static int url_uno_mii_write_reg(struct usbnet *, int, int, uint16_t);
-static int url_uno_ioctl(struct ifnet *, u_long, void *);
 static void url_uno_stop(struct ifnet *, int);
 static void url_uno_mii_statchg(struct ifnet *);
 static int url_uno_init(struct ifnet *);
-static void url_rcvfilt_locked(struct usbnet *);
+static void url_uno_mcast(struct ifnet *);
 static void url_reset(struct usbnet *);
 
 static int url_csr_read_1(struct usbnet *, int);
@@ -93,7 +92,7 @@ static int url_mem(struct usbnet *, int, int, void *, int);
 
 static const struct usbnet_ops url_ops = {
 	.uno_stop = url_uno_stop,
-	.uno_ioctl = url_uno_ioctl,
+	.uno_mcast = url_uno_mcast,
 	.uno_read_reg = url_uno_mii_read_reg,
 	.uno_write_reg = url_uno_mii_write_reg,
 	.uno_statchg = url_uno_mii_statchg,
@@ -241,10 +240,7 @@ url_attach(device_t parent, device_t self, void *aux)
 	}
 
 	/* Set these up now for url_mem().  */
-	usbnet_attach(un, "urldet");
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
+	usbnet_attach(un);
 
 	/* reset the adapter */
 	url_reset(un);
@@ -252,22 +248,14 @@ url_attach(device_t parent, device_t self, void *aux)
 	/* Get Ethernet Address */
 	err = url_mem(un, URL_CMD_READMEM, URL_IDR0, (void *)un->un_eaddr,
 		      ETHER_ADDR_LEN);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
 	if (err) {
 		aprint_error_dev(self, "read MAC address failed\n");
-		goto bad;
+		return;
 	}
 
 	/* initialize interface information */
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
 	    0, &unm);
-
-	return;
-
- bad:
-	usbnet_set_dying(un, true);
-	return;
 }
 
 /* read/write memory */
@@ -277,8 +265,6 @@ url_mem(struct usbnet *un, int cmd, int offset, void *buf, int len)
 	usb_device_request_t req;
 	usbd_status err;
 
-	usbnet_isowned_core(un);
-
 	DPRINTFN(0x200,
 		("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
@@ -371,7 +357,7 @@ url_csr_write_4(struct usbnet *un, int reg, int aval)
 }
 
 static int
-url_init_locked(struct ifnet *ifp)
+url_uno_init(struct ifnet *ifp)
 {
 	struct usbnet * const un = ifp->if_softc;
 	const u_char *eaddr;
@@ -379,13 +365,7 @@ url_init_locked(struct ifnet *ifp)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_isowned_core(un);
-
-	if (usbnet_isdying(un))
-		return EIO;
-
-	/* Cancel pending I/O and free all TX/RX buffers */
-	usbnet_stop(un, ifp, 1);
+	url_reset(un);
 
 	eaddr = CLLADDR(ifp->if_sadl);
 	for (i = 0; i < ETHER_ADDR_LEN; i++)
@@ -400,27 +380,10 @@ url_init_locked(struct ifnet *ifp)
 	/* Init receive control register */
 	URL_SETBIT2(un, URL_RCR, URL_RCR_TAIL | URL_RCR_AD | URL_RCR_AB);
 
-	/* Accept multicast frame or run promisc. mode */
-	url_rcvfilt_locked(un);
-
 	/* Enable RX and TX */
 	URL_SETBIT(un, URL_CR, URL_CR_TE | URL_CR_RE);
 
-	return usbnet_init_rx_tx(un);
-}
-
-static int
-url_uno_init(struct ifnet *ifp)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-	int ret = url_init_locked(ifp);
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return ret;
+	return 0;
 }
 
 static void
@@ -436,6 +399,8 @@ url_reset(struct usbnet *un)
 	URL_SETBIT(un, URL_CR, URL_CR_SOFT_RST);
 
 	for (i = 0; i < URL_TX_TIMEOUT; i++) {
+		if (usbnet_isdying(un))
+			return;
 		if (!(url_csr_read_1(un, URL_CR) & URL_CR_SOFT_RST))
 			break;
 		delay(10);	/* XXX */
@@ -445,9 +410,9 @@ url_reset(struct usbnet *un)
 }
 
 static void
-url_rcvfilt_locked(struct usbnet *un)
+url_uno_mcast(struct ifnet *ifp)
 {
-	struct ifnet * const ifp = usbnet_ifp(un);
+	struct usbnet * const un = ifp->if_softc;
 	struct ethercom *ec = usbnet_ec(un);
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -456,8 +421,6 @@ url_rcvfilt_locked(struct usbnet *un)
 
 	DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__));
 
-	usbnet_isowned_core(un);
-
 	if (usbnet_isdying(un))
 		return;
 
@@ -565,29 +528,6 @@ static void url_intr(void)
 }
 #endif
 
-static int
-url_uno_ioctl(struct ifnet *ifp, u_long cmd, void *data)
-{
-	struct usbnet * const un = ifp->if_softc;
-
-	usbnet_lock_core(un);
-	usbnet_busy(un);
-
-	switch (cmd) {
-	case SIOCADDMULTI:
-	case SIOCDELMULTI:
-		url_rcvfilt_locked(un);
-		break;
-	default:
-		break;
-	}
-
-	usbnet_unbusy(un);
-	usbnet_unlock_core(un);
-
-	return 0;
-}
-
 /* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
 static void
 url_uno_stop(struct ifnet *ifp, int disable)
diff --git a/sys/dev/usb/if_urndis.c b/sys/dev/usb/if_urndis.c
index 381207450d72..08a82be22c5d 100644
--- a/sys/dev/usb/if_urndis.c
+++ b/sys/dev/usb/if_urndis.c
@@ -859,23 +859,10 @@ urndis_init_un(struct ifnet *ifp, struct usbnet *un)
 {
 	int 			 err;
 
-	if (ifp->if_flags & IFF_RUNNING)
-		return 0;
-
 	err = urndis_ctrl_init(un);
 	if (err != RNDIS_STATUS_SUCCESS)
 		return EIO;
 
-	usbnet_lock_core(un);
-	if (usbnet_isdying(un))
-		err = EIO;
-	else {
-		usbnet_stop(un, ifp, 1);
-		err = usbnet_init_rx_tx(un);
-		usbnet_set_link(un, err == 0);
-	}
-	usbnet_unlock_core(un);
-
 	return err;
 }
 
@@ -883,8 +870,15 @@ static int
 urndis_uno_init(struct ifnet *ifp)
 {
 	struct usbnet *un = ifp->if_softc;
+	int error;
+
+	KASSERT(IFNET_LOCKED(ifp));
 
-	return urndis_init_un(ifp, un);
+	error = urndis_init_un(ifp, un);
+	if (error)
+		return EIO;	/* XXX */
+
+	return 0;
 }
 
 static int
@@ -1048,7 +1042,7 @@ urndis_attach(device_t parent, device_t self, void *aux)
 	ifp->if_watchdog = urndis_watchdog;
 #endif
 
-	usbnet_attach(un, "urndisdet");
+	usbnet_attach(un);
 
 	struct ifnet *ifp = usbnet_ifp(un);
 	urndis_init_un(ifp, un);
@@ -1057,9 +1051,6 @@ urndis_attach(device_t parent, device_t self, void *aux)
 	    &buf, &bufsz) != RNDIS_STATUS_SUCCESS) {
 		aprint_error("%s: unable to get hardware address\n",
 		    DEVNAME(un));
-		usbnet_lock_core(un);
-		usbnet_stop(un, ifp, 1);
-		usbnet_unlock_core(un);
 		return;
 	}
 
@@ -1070,9 +1061,6 @@ urndis_attach(device_t parent, device_t self, void *aux)
 		aprint_error("%s: invalid address\n", DEVNAME(un));
 		if (buf && bufsz)
 			kmem_free(buf, bufsz);
-		usbnet_lock_core(un);
-		usbnet_stop(un, ifp, 1);
-		usbnet_unlock_core(un);
 		return;
 	}
 
@@ -1083,17 +1071,9 @@ urndis_attach(device_t parent, device_t self, void *aux)
 	if (urndis_ctrl_set(un, OID_GEN_CURRENT_PACKET_FILTER, &filter,
 	    sizeof(filter)) != RNDIS_STATUS_SUCCESS) {
 		aprint_error("%s: unable to set data filters\n", DEVNAME(un));
-		usbnet_lock_core(un);
-		usbnet_stop(un, ifp, 1);
-		usbnet_unlock_core(un);
 		return;
 	}
 
-	/* Turn off again now it has been identified. */
-	usbnet_lock_core(un);
-	usbnet_stop(un, ifp, 1);
-	usbnet_unlock_core(un);
-
 	usbnet_attach_ifp(un, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST,
             0, NULL);
 }
diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c
index 1712d2f687f0..1f57e11ea39e 100644
--- a/sys/dev/usb/usbnet.c
+++ b/sys/dev/usb/usbnet.c
@@ -56,17 +56,19 @@ struct usbnet_private {
 	 *   and the MII / media data.
 	 * - unp_rxlock protects the rx path and its data
 	 * - unp_txlock protects the tx path and its data
-	 * - unp_detachcv handles detach vs open references
 	 *
 	 * 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
 	 */
 	kmutex_t		unp_core_lock;
 	kmutex_t		unp_rxlock;
 	kmutex_t		unp_txlock;
-	kcondvar_t		unp_detachcv;
+
+	kmutex_t		unp_mcastlock;
+	bool			unp_mcastactive;
 
 	struct usbnet_cdata	unp_cdata;
 
@@ -76,12 +78,12 @@ struct usbnet_private {
 	struct callout		unp_stat_ch;
 	struct usbd_pipe	*unp_ep[USBNET_ENDPT_MAX];
 
-	bool			unp_dying;
+	volatile bool		unp_dying;
 	bool			unp_stopping;
 	bool			unp_attached;
+	bool			unp_ifp_attached;
 	bool			unp_link;
 
-	int			unp_refcnt;
 	int			unp_timer;
 	unsigned short		unp_if_flags;
 	unsigned		unp_number;
@@ -97,6 +99,21 @@ struct usbnet_private {
 
 volatile unsigned usbnet_number;
 
+static void usbnet_isowned_rx(struct usbnet *);
+static void usbnet_isowned_tx(struct usbnet *);
+
+static kmutex_t *
+usbnet_mutex_core(struct usbnet *un)
+{
+	return &un->un_pri->unp_core_lock;
+}
+
+static __inline__ void
+usbnet_isowned_core(struct usbnet *un)
+{
+	KASSERT(mutex_owned(usbnet_mutex_core(un)));
+}
+
 static int usbnet_modcmd(modcmd_t, void *);
 
 #ifdef USB_DEBUG
@@ -149,6 +166,8 @@ fail:
 static void
 uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 {
+	KASSERTMSG(!un->un_pri->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	usbnet_isowned_core(un);
 	if (un->un_ops->uno_stop)
 		(*un->un_ops->uno_stop)(ifp, disable);
@@ -157,11 +176,11 @@ uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 static int
 uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 {
-	/*
-	 * There are cases where IFNET_LOCK will not be held when we
-	 * are called (e.g. add/delete multicast address), so we can't
-	 * assert it.
-	 */
+
+	KASSERTMSG(cmd != SIOCADDMULTI, "%s", ifp->if_xname);
+	KASSERTMSG(cmd != SIOCDELMULTI, "%s", ifp->if_xname);
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+
 	if (un->un_ops->uno_ioctl)
 		return (*un->un_ops->uno_ioctl)(ifp, cmd, data);
 	return 0;
@@ -170,15 +189,23 @@ uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 static int
 uno_override_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
 {
-	/* See above. */
+
+	switch (cmd) {
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		break;
+	default:
+		KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+	}
+
 	return (*un->un_ops->uno_override_ioctl)(ifp, cmd, data);
 }
 
 static int
 uno_init(struct usbnet *un, struct ifnet *ifp)
 {
-	KASSERT(IFNET_LOCKED(ifp));
-	return (*un->un_ops->uno_init)(ifp);
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+	return un->un_ops->uno_init ? (*un->un_ops->uno_init)(ifp) : 0;
 }
 
 static int
@@ -333,7 +360,6 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	struct usbnet_chain * const c = priv;
 	struct usbnet * const un = c->unc_un;
 	struct usbnet_private * const unp = un->un_pri;
-	struct ifnet * const ifp = usbnet_ifp(un);
 	uint32_t total_len;
 
 	USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
@@ -341,9 +367,9 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 
 	mutex_enter(&unp->unp_rxlock);
 
-	if (unp->unp_dying || unp->unp_stopping ||
+	if (usbnet_isdying(un) || unp->unp_stopping ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
-	    status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING))
+	    status == USBD_CANCELLED)
 		goto out;
 
 	if (status != USBD_NORMAL_COMPLETION) {
@@ -368,7 +394,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	usbnet_isowned_rx(un);
 
 done:
-	if (unp->unp_dying || unp->unp_stopping)
+	if (usbnet_isdying(un) || unp->unp_stopping)
 		goto out;
 
 	mutex_exit(&unp->unp_rxlock);
@@ -397,7 +423,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 || unp->unp_dying) {
+	if (unp->unp_stopping || usbnet_isdying(un)) {
 		mutex_exit(&unp->unp_txlock);
 		return;
 	}
@@ -440,14 +466,13 @@ usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
 	struct usbnet * const un = priv;
 	struct usbnet_private * const unp = un->un_pri;
 	struct usbnet_intr * const uni = un->un_intr;
-	struct ifnet * const ifp = usbnet_ifp(un);
 
-	if (uni == NULL || unp->unp_dying || unp->unp_stopping ||
+	if (uni == NULL || usbnet_isdying(un) || unp->unp_stopping ||
 	    status == USBD_INVAL || status == USBD_NOT_STARTED ||
-	    status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) {
+	    status == USBD_CANCELLED) {
 		USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx",
 		    unp->unp_number, (uintptr_t)uni,
-		    (unp->unp_dying << 8) | unp->unp_stopping, status);
+		    (usbnet_isdying(un) << 8) | unp->unp_stopping, status);
 		return;
 	}
 
@@ -809,7 +834,7 @@ usbnet_ep_stop_pipes(struct usbnet * const un)
 	return err;
 }
 
-int
+static int
 usbnet_init_rx_tx(struct usbnet * const un)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
@@ -818,14 +843,15 @@ usbnet_init_rx_tx(struct usbnet * const un)
 	usbd_status err;
 	int error = 0;
 
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
+
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying) {
+	if (usbnet_isdying(un)) {
 		return EIO;
 	}
 
-	usbnet_busy(un);
-
 	/* Open RX and TX pipes. */
 	err = usbnet_ep_open_pipes(un);
 	if (err) {
@@ -849,16 +875,26 @@ usbnet_init_rx_tx(struct usbnet * const un)
 		goto out;
 	}
 
-	/* Start up the receive pipe(s). */
-	usbnet_rx_start_pipes(un);
-
 	/* Indicate we are up and running. */
-#if 0
-	/* XXX if_mcast_op() can call this without ifnet locked */
-	KASSERT(ifp->if_softc == NULL || IFNET_LOCKED(ifp));
-#endif
+	/* XXX urndis calls usbnet_init_rx_tx before usbnet_attach_ifp.  */
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	ifp->if_flags |= IFF_RUNNING;
 
+	/*
+	 * If the hardware has a multicast filter, program it and then
+	 * allow updates to it while we're running.
+	 */
+	if (un->un_ops->uno_mcast) {
+		mutex_enter(&unp->unp_mcastlock);
+		(*un->un_ops->uno_mcast)(ifp);
+		unp->unp_mcastactive = true;
+		mutex_exit(&unp->unp_mcastlock);
+	}
+
+	/* Start up the receive pipe(s). */
+	usbnet_rx_start_pipes(un);
+
 	callout_schedule(&unp->unp_stat_ch, hz);
 
 out:
@@ -867,93 +903,70 @@ out:
 		usbnet_tx_list_fini(un);
 		usbnet_ep_close_pipes(un);
 	}
-	usbnet_unbusy(un);
-
-	usbnet_isowned_core(un);
 
-	return error;
-}
-
-void
-usbnet_busy(struct usbnet *un)
-{
-	struct usbnet_private * const unp = un->un_pri;
-
-	usbnet_isowned_core(un);
-
-	unp->unp_refcnt++;
-}
-
-void
-usbnet_unbusy(struct usbnet *un)
-{
-	struct usbnet_private * const unp = un->un_pri;
+	/*
+	 * For devices without any media autodetection, treat success
+	 * here as an active link.
+	 */
+	if (un->un_ops->uno_statchg == NULL)
+		usbnet_set_link(un, error == 0);
 
 	usbnet_isowned_core(un);
 
-	if (--unp->unp_refcnt < 0)
-		cv_broadcast(&unp->unp_detachcv);
+	return error;
 }
 
 /* MII management. */
 
-int
+static int
 usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
 {
 	USBNETHIST_FUNC();
 	struct usbnet * const un = device_private(dev);
-	struct usbnet_private * const unp = un->un_pri;
 	int err;
 
 	/* MII layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying) {
+	if (usbnet_isdying(un)) {
 		return EIO;
 	}
 
-	usbnet_busy(un);
 	err = uno_read_reg(un, phy, reg, val);
-	usbnet_unbusy(un);
-
 	if (err) {
 		USBNETHIST_CALLARGS("%jd: read PHY failed: %jd",
-		    unp->unp_number, err, 0, 0);
+		    un->un_pri->unp_number, err, 0, 0);
 		return err;
 	}
 
 	return 0;
 }
 
-int
+static int
 usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
 {
 	USBNETHIST_FUNC();
 	struct usbnet * const un = device_private(dev);
-	struct usbnet_private * const unp = un->un_pri;
 	int err;
 
 	/* MII layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying) {
+	if (usbnet_isdying(un)) {
 		return EIO;
 	}
 
-	usbnet_busy(un);
 	err = uno_write_reg(un, phy, reg, val);
-	usbnet_unbusy(un);
-
 	if (err) {
 		USBNETHIST_CALLARGS("%jd: write PHY failed: %jd",
-		    unp->unp_number, err, 0, 0);
+		    un->un_pri->unp_number, err, 0, 0);
 		return err;
 	}
 
 	return 0;
 }
 
-void
+static void
 usbnet_mii_statchg(struct ifnet *ifp)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
@@ -962,9 +975,7 @@ usbnet_mii_statchg(struct ifnet *ifp)
 	/* MII layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	usbnet_busy(un);
 	uno_mii_statchg(un, ifp);
-	usbnet_unbusy(un);
 }
 
 static int
@@ -978,7 +989,10 @@ usbnet_media_upd(struct ifnet *ifp)
 	/* ifmedia layer ensures core_lock is held. */
 	usbnet_isowned_core(un);
 
-	if (unp->unp_dying)
+	/* ifmedia changes only with IFNET_LOCK held.  */
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+
+	if (usbnet_isdying(un))
 		return EIO;
 
 	unp->unp_link = false;
@@ -1004,7 +1018,7 @@ usbnet_ifflags_cb(struct ethercom *ec)
 	struct usbnet_private * const unp = un->un_pri;
 	int rv = 0;
 
-	mutex_enter(&unp->unp_core_lock);
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
 
 	const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
 	if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) {
@@ -1015,8 +1029,6 @@ usbnet_ifflags_cb(struct ethercom *ec)
 		rv = ENETRESET;
 	}
 
-	mutex_exit(&unp->unp_core_lock);
-
 	return rv;
 }
 
@@ -1035,8 +1047,30 @@ usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 		return uno_override_ioctl(un, ifp, cmd, data);
 
 	error = ether_ioctl(ifp, cmd, data);
-	if (error == ENETRESET)
-		error = uno_ioctl(un, ifp, cmd, data);
+	if (error == ENETRESET) {
+		switch (cmd) {
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+			/*
+			 * If there's a hardware multicast filter, and
+			 * it has been programmed by usbnet_init_rx_tx
+			 * and is active, update it now.  Otherwise,
+			 * drop the update on the floor -- it will be
+			 * observed by usbnet_init_rx_tx next time we
+			 * bring the interface up.
+			 */
+			if (un->un_ops->uno_mcast) {
+				mutex_enter(&unp->unp_mcastlock);
+				if (unp->unp_mcastactive)
+					(*un->un_ops->uno_mcast)(ifp);
+				mutex_exit(&unp->unp_mcastlock);
+			}
+			error = 0;
+			break;
+		default:
+			error = uno_ioctl(un, ifp, cmd, data);
+		}
+	}
 
 	return error;
 }
@@ -1050,39 +1084,48 @@ usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
  *	- free RX and TX resources
  *	- close pipes
  *
- * usbnet_stop() is exported for drivers to use, expects lock held.
- *
  * usbnet_if_stop() is for the if_stop handler.
  */
-void
+static void
 usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 {
 	struct usbnet_private * const unp = un->un_pri;
 
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
 	usbnet_isowned_core(un);
 
-	usbnet_busy(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.
+	 */
+	if (un->un_ops->uno_mcast) {
+		mutex_enter(&unp->unp_mcastlock);
+		unp->unp_mcastactive = false;
+		mutex_exit(&unp->unp_mcastlock);
+	}
 
+	/*
+	 * Prevent new activity (rescheduling ticks, xfers, &c.) and
+	 * clear the watchdog timer.
+	 */
 	mutex_enter(&unp->unp_rxlock);
 	mutex_enter(&unp->unp_txlock);
 	unp->unp_stopping = true;
+	unp->unp_timer = 0;
 	mutex_exit(&unp->unp_txlock);
 	mutex_exit(&unp->unp_rxlock);
 
-	uno_stop(un, ifp, disable);
-
 	/*
-	 * XXXSMP Would like to
-	 *	KASSERT(IFNET_LOCKED(ifp))
-	 * here but the locking order is:
-	 *	ifnet -> core_lock -> rxlock -> txlock
-	 * and core_lock is already held.
+	 * 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
+	 * timer task from being scheduled again.
 	 */
-	ifp->if_flags &= ~IFF_RUNNING;
-	unp->unp_timer = 0;
-
 	callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
 	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
 	    &unp->unp_core_lock);
@@ -1090,6 +1133,19 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	/* Stop transfers. */
 	usbnet_ep_stop_pipes(un);
 
+	/*
+	 * Now that the software is quiescent, ask the driver to stop
+	 * the hardware.  The driver's uno_stop routine now has
+	 * exclusive access to any registers that might previously have
+	 * been used by to ifmedia, mii, or ioctl callbacks.
+	 *
+	 * Don't bother if the device is being detached, though -- if
+	 * it's been unplugged then there's no point in trying to touch
+	 * the registers.
+	 */
+	if (!usbnet_isdying(un))
+		uno_stop(un, ifp, disable);
+
 	/* Free RX/TX resources. */
 	usbnet_rx_list_fini(un);
 	usbnet_tx_list_fini(un);
@@ -1097,7 +1153,10 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
 	/* Close pipes. */
 	usbnet_ep_close_pipes(un);
 
-	usbnet_unbusy(un);
+	/* Everything is quesced now. */
+	KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
+	    "%s", ifp->if_xname);
+	ifp->if_flags &= ~IFF_RUNNING;
 }
 
 static void
@@ -1106,6 +1165,18 @@ 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);
+
+	/*
+	 * If we're already stopped, nothing to do.
+	 *
+	 * XXX This should be an assertion, but it may require some
+	 * analysis -- and possibly some tweaking -- of sys/net to
+	 * ensure.
+	 */
+	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);
@@ -1126,10 +1197,8 @@ usbnet_tick(void *arg)
 
 	USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
 
-	if (unp != NULL && !unp->unp_stopping && !unp->unp_dying) {
-		/* Perform periodic stuff in process context */
-		usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
-	}
+	/* Perform periodic stuff in process context */
+	usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
 }
 
 static void
@@ -1164,27 +1233,15 @@ usbnet_tick_task(void *arg)
 	USBNETHIST_FUNC();
 	struct usbnet * const un = arg;
 	struct usbnet_private * const unp = un->un_pri;
-
-	if (unp == NULL)
-		return;
-
-	USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
-
-	mutex_enter(&unp->unp_core_lock);
-	if (unp->unp_stopping || unp->unp_dying) {
-		mutex_exit(&unp->unp_core_lock);
-		return;
-	}
-
 	struct ifnet * const ifp = usbnet_ifp(un);
 	struct mii_data * const mii = usbnet_mii(un);
 
-	KASSERT(ifp != NULL);	/* embedded member */
-
-	usbnet_busy(un);
-	mutex_exit(&unp->unp_core_lock);
+	USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
 
-	if (unp->unp_timer != 0 && --unp->unp_timer == 0)
+	mutex_enter(&unp->unp_txlock);
+	const bool timeout = unp->unp_timer != 0 && --unp->unp_timer == 0;
+	mutex_exit(&unp->unp_txlock);
+	if (timeout)
 		usbnet_watchdog(ifp);
 
 	DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
@@ -1200,8 +1257,7 @@ usbnet_tick_task(void *arg)
 	uno_tick(un);
 
 	mutex_enter(&unp->unp_core_lock);
-	usbnet_unbusy(un);
-	if (!unp->unp_stopping && !unp->unp_dying)
+	if (!unp->unp_stopping && !usbnet_isdying(un))
 		callout_schedule(&unp->unp_stat_ch, hz);
 	mutex_exit(&unp->unp_core_lock);
 }
@@ -1211,8 +1267,37 @@ usbnet_if_init(struct ifnet *ifp)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 	struct usbnet * const un = ifp->if_softc;
+	int error;
+
+	KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
+
+	/*
+	 * Prevent anyone from bringing the interface back up once
+	 * we're detaching.
+	 */
+	if (usbnet_isdying(un))
+		return EIO;
+
+	/*
+	 * If we're already running, nothing to do.
+	 *
+	 * XXX This should be an assertion, but it may require some
+	 * analysis -- and possibly some tweaking -- of sys/net to
+	 * ensure.
+	 */
+	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;
+	error = usbnet_init_rx_tx(un);
+	if (error)
+		goto out;
+out:	mutex_exit(&un->un_pri->unp_core_lock);
 
-	return uno_init(un, ifp);
+	return error;
 }
 
 
@@ -1224,12 +1309,6 @@ usbnet_set_link(struct usbnet *un, bool link)
 	un->un_pri->unp_link = link;
 }
 
-void
-usbnet_set_dying(struct usbnet *un, bool link)
-{
-	un->un_pri->unp_dying = link;
-}
-
 struct ifnet *
 usbnet_ifp(struct usbnet *un)
 {
@@ -1269,64 +1348,22 @@ usbnet_havelink(struct usbnet *un)
 bool
 usbnet_isdying(struct usbnet *un)
 {
-	return un->un_pri == NULL || un->un_pri->unp_dying;
+	return atomic_load_relaxed(&un->un_pri->unp_dying);
 }
 
 
 /* Locking. */
 
-void
-usbnet_lock_core(struct usbnet *un)
-{
-	mutex_enter(&un->un_pri->unp_core_lock);
-}
-
-void
-usbnet_unlock_core(struct usbnet *un)
-{
-	mutex_exit(&un->un_pri->unp_core_lock);
-}
-
-kmutex_t *
-usbnet_mutex_core(struct usbnet *un)
-{
-	return &un->un_pri->unp_core_lock;
-}
-
-void
-usbnet_lock_rx(struct usbnet *un)
-{
-	mutex_enter(&un->un_pri->unp_rxlock);
-}
-
-void
-usbnet_unlock_rx(struct usbnet *un)
-{
-	mutex_exit(&un->un_pri->unp_rxlock);
-}
-
-kmutex_t *
-usbnet_mutex_rx(struct usbnet *un)
-{
-	return &un->un_pri->unp_rxlock;
-}
-
-void
-usbnet_lock_tx(struct usbnet *un)
-{
-	mutex_enter(&un->un_pri->unp_txlock);
-}
-
-void
-usbnet_unlock_tx(struct usbnet *un)
+static void
+usbnet_isowned_rx(struct usbnet *un)
 {
-	mutex_exit(&un->un_pri->unp_txlock);
+	KASSERT(mutex_owned(&un->un_pri->unp_rxlock));
 }
 
-kmutex_t *
-usbnet_mutex_tx(struct usbnet *un)
+static void
+usbnet_isowned_tx(struct usbnet *un)
 {
-	return &un->un_pri->unp_txlock;
+	KASSERT(mutex_owned(&un->un_pri->unp_txlock));
 }
 
 /* Autoconf management. */
@@ -1353,8 +1390,7 @@ usbnet_empty_eaddr(struct usbnet * const un)
  */
 
 void
-usbnet_attach(struct usbnet *un,
-	      const char *detname)	/* detach cv name */
+usbnet_attach(struct usbnet *un)
 {
 	USBNETHIST_FUNC(); USBNETHIST_CALLED();
 
@@ -1373,14 +1409,15 @@ usbnet_attach(struct usbnet *un,
 	un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP);
 	struct usbnet_private * const unp = un->un_pri;
 
-	usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un, USB_TASKQ_MPSAFE);
+	usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un,
+	    USB_TASKQ_MPSAFE);
 	callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE);
 	callout_setfunc(&unp->unp_stat_ch, usbnet_tick, 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);
-	cv_init(&unp->unp_detachcv, detname);
+	mutex_init(&unp->unp_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 
 	rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
 	    RND_TYPE_NET, RND_FLAG_DEFAULT);
@@ -1435,7 +1472,9 @@ usbnet_attach_ifp(struct usbnet *un,
 	struct ifnet * const ifp = usbnet_ifp(un);
 
 	KASSERT(unp->unp_attached);
+	KASSERT(!unp->unp_ifp_attached);
 
+	ifp->if_softc = un;
 	strlcpy(ifp->if_xname, device_xname(un->un_dev), IFNAMSIZ);
 	ifp->if_flags = if_flags;
 	ifp->if_extflags = IFEF_MPSAFE | if_extflags;
@@ -1454,6 +1493,7 @@ usbnet_attach_ifp(struct usbnet *un,
 	if (ifp->_if_input == NULL)
 		ifp->if_percpuq = if_percpuq_create(ifp);
 	if_register(ifp);
+	unp->unp_ifp_attached = true;
 
 	/*
 	 * If ethernet address is all zero, skip ether_ifattach() and
@@ -1471,7 +1511,6 @@ usbnet_attach_ifp(struct usbnet *un,
 
 	/* Now ready, and attached. */
 	IFQ_SET_READY(&ifp->if_snd);
-	ifp->if_softc = un;
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev);
 
@@ -1493,39 +1532,39 @@ usbnet_detach(device_t self, int flags)
 	struct ifnet * const ifp = usbnet_ifp(un);
 	struct mii_data * const mii = usbnet_mii(un);
 
-	mutex_enter(&unp->unp_core_lock);
-	unp->unp_dying = true;
-	mutex_exit(&unp->unp_core_lock);
+	/*
+	 * Prevent new activity.  After we stop the interface, it
+	 * cannot be brought back up.
+	 */
+	atomic_store_relaxed(&unp->unp_dying, true);
 
-	if (ifp->if_flags & IFF_RUNNING) {
+	/*
+	 * If we're still running on the network, stop and wait for all
+	 * asynchronous activity to finish.
+	 *
+	 * If usbnet_attach_ifp never ran, IFNET_LOCK won't work, but
+	 * no activity is possible, so just skip this part.
+	 */
+	if (unp->unp_ifp_attached) {
 		IFNET_LOCK(ifp);
-		usbnet_if_stop(ifp, 1);
+		if (ifp->if_flags & IFF_RUNNING) {
+			usbnet_if_stop(ifp, 1);
+		}
 		IFNET_UNLOCK(ifp);
 	}
 
-	callout_halt(&unp->unp_stat_ch, NULL);
-	usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
-	    NULL);
-
-	mutex_enter(&unp->unp_core_lock);
-	unp->unp_refcnt--;
-	while (unp->unp_refcnt >= 0) {
-		/* Wait for processes to go away */
-		cv_wait(&unp->unp_detachcv, &unp->unp_core_lock);
-	}
-	mutex_exit(&unp->unp_core_lock);
-
-	usbnet_rx_list_free(un);
-	usbnet_tx_list_free(un);
-
-	callout_destroy(&unp->unp_stat_ch);
-	rnd_detach_source(&unp->unp_rndsrc);
+	/*
+	 * The callout and tick task can't be scheduled anew at this
+	 * point, and usbnet_if_stop has waited for them to complete.
+	 */
+	KASSERT(!callout_pending(&unp->unp_stat_ch));
+	KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask));
 
 	if (mii) {
 		mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
 		ifmedia_fini(&mii->mii_media);
 	}
-	if (ifp->if_softc) {
+	if (unp->unp_ifp_attached) {
 		if (!usbnet_empty_eaddr(un))
 			ether_ifdetach(ifp);
 		else
@@ -1534,14 +1573,28 @@ usbnet_detach(device_t self, int flags)
 	}
 	usbnet_ec(un)->ec_mii = NULL;
 
-	cv_destroy(&unp->unp_detachcv);
+	usbnet_rx_list_free(un);
+	usbnet_tx_list_free(un);
+
+	rnd_detach_source(&unp->unp_rndsrc);
+
+	mutex_destroy(&unp->unp_mcastlock);
 	mutex_destroy(&unp->unp_core_lock);
 	mutex_destroy(&unp->unp_rxlock);
 	mutex_destroy(&unp->unp_txlock);
 
+	callout_destroy(&unp->unp_stat_ch);
+
 	pmf_device_deregister(un->un_dev);
 
-	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev, un->un_dev);
+	/*
+	 * Notify userland that we're going away, if we arrived in the
+	 * first place.
+	 */
+	if (unp->unp_ifp_attached) {
+		usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev,
+		    un->un_dev);
+	}
 
 	kmem_free(unp, sizeof(*unp));
 	un->un_pri = NULL;
@@ -1561,9 +1614,7 @@ usbnet_activate(device_t self, devact_t act)
 	case DVACT_DEACTIVATE:
 		if_deactivate(ifp);
 
-		mutex_enter(&unp->unp_core_lock);
-		unp->unp_dying = true;
-		mutex_exit(&unp->unp_core_lock);
+		atomic_store_relaxed(&unp->unp_dying, true);
 
 		mutex_enter(&unp->unp_rxlock);
 		mutex_enter(&unp->unp_txlock);
diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h
index 15e9dfc7b351..bdd34a52367c 100644
--- a/sys/dev/usb/usbnet.h
+++ b/sys/dev/usb/usbnet.h
@@ -58,19 +58,12 @@
  *     cases), but provides a normal handler with callback to handle
  *     ENETRESET conditions that should be sufficient for most users
  *   - start uses usbnet transmit prepare callback (uno_tx_prepare)
- * - interface init and stop have helper functions
- *   - device specific init should use usbnet_init_rx_tx() to open pipes
- *     to the device and setup the rx/tx chains for use after any device
- *     specific setup
- *   - usbnet_stop() must be called with the un_lock held, and will
- *     call the device-specific usbnet stop callback, which enables the
- *     standard init calls stop idiom.
  * - interrupt handling:
- *   - for rx, usbnet_init_rx_tx() will enable the receive pipes and
+ *   - for rx, usbnet will enable the receive pipes and
  *     call the rx_loop callback to handle device specific processing of
  *     packets, which can use usbnet_enqueue() to provide data to the
  *     higher layers
- *   - for tx, usbnet_start (if_start) will pull entries out of the
+ *   - for tx, usbnet will pull entries out of the
  *     transmit queue and use the transmit prepare callback (uno_tx_prepare)
  *     for the given mbuf.  the usb callback will use usbnet_txeof() for
  *     the transmit completion function (internal to usbnet)
@@ -131,6 +124,8 @@ enum usbnet_ep {
 typedef void (*usbnet_stop_cb)(struct ifnet *, int);
 /* Interface ioctl callback. */
 typedef int (*usbnet_ioctl_cb)(struct ifnet *, u_long, void *);
+/* Reprogram multicast filters callback. */
+typedef void (*usbnet_mcast_cb)(struct ifnet *);
 /* Initialise device callback. */
 typedef int (*usbnet_init_cb)(struct ifnet *);
 
@@ -170,16 +165,16 @@ typedef void (*usbnet_intr_cb)(struct usbnet *, usbd_status);
  * Note that when CORE_LOCK is held, IFNET_LOCK may or may not also
  * be held.
  *
- * Note that the IFNET_LOCK **may not be held** for some ioctl
- * operations (add/delete multicast addresses, for example).
- *
- * Busy reference counts are maintained across calls to: uno_stop,
- * uno_read_reg, uno_write_reg, uno_statchg, and uno_tick.
+ * Note that the IFNET_LOCK **may not be held** for some the ioctl
+ * commands SIOCADDMULTI/SIOCDELMULTI.  These commands are only passed
+ * explicitly to uno_override_ioctl; for all other devices, they are
+ * handled by uno_mcast (also without IFNET_LOCK).
  */
 struct usbnet_ops {
 	usbnet_stop_cb		uno_stop;		/* C */
-	usbnet_ioctl_cb		uno_ioctl;		/* I (maybe) */
-	usbnet_ioctl_cb		uno_override_ioctl;	/* I (maybe) */
+	usbnet_ioctl_cb		uno_ioctl;		/* I */
+	usbnet_ioctl_cb		uno_override_ioctl;	/* I (except mcast) */
+	usbnet_mcast_cb		uno_mcast;		/* I */
 	usbnet_init_cb		uno_init;		/* I */
 	usbnet_mii_read_reg_cb	uno_read_reg;		/* C */
 	usbnet_mii_write_reg_cb uno_write_reg;		/* C */
@@ -259,11 +254,8 @@ struct usbnet {
 	/*
 	 * This section should be filled in before calling
 	 * usbnet_attach_ifp().
-	 *
-	 * XXX This should be of type "uByte".  enum usbnet_ep
-	 * is the index.  Fix this in a kernel version bump.
 	 */
-	enum usbnet_ep		un_ed[USBNET_ENDPT_MAX];
+	uByte			un_ed[USBNET_ENDPT_MAX];
 
 	/* MII specific. Not used without MII. */
 	int			un_phyno;
@@ -284,7 +276,6 @@ struct usbnet {
 /* Various accessors. */
 
 void usbnet_set_link(struct usbnet *, bool);
-void usbnet_set_dying(struct usbnet *, bool);
 
 struct ifnet *usbnet_ifp(struct usbnet *);
 struct ethercom *usbnet_ec(struct usbnet *);
@@ -295,60 +286,27 @@ void *usbnet_softc(struct usbnet *);
 bool usbnet_havelink(struct usbnet *);
 bool usbnet_isdying(struct usbnet *);
 
-
-/*
- * Locking.  Note that the isowned() are implemented here so that
- * empty-KASSERT() causes them to be elided for non-DIAG builds.
- */
-void	usbnet_lock_core(struct usbnet *);
-void	usbnet_unlock_core(struct usbnet *);
-kmutex_t *usbnet_mutex_core(struct usbnet *);
-static __inline__ void
-usbnet_isowned_core(struct usbnet *un)
-{
-	KASSERT(mutex_owned(usbnet_mutex_core(un)));
-}
-
-void	usbnet_busy(struct usbnet *);
-void	usbnet_unbusy(struct usbnet *);
-
-void	usbnet_lock_rx(struct usbnet *);
-void	usbnet_unlock_rx(struct usbnet *);
-kmutex_t *usbnet_mutex_rx(struct usbnet *);
-static __inline__ void
-usbnet_isowned_rx(struct usbnet *un)
-{
-	KASSERT(mutex_owned(usbnet_mutex_rx(un)));
-}
-
-void	usbnet_lock_tx(struct usbnet *);
-void	usbnet_unlock_tx(struct usbnet *);
-kmutex_t *usbnet_mutex_tx(struct usbnet *);
-static __inline__ void
-usbnet_isowned_tx(struct usbnet *un)
-{
-	KASSERT(mutex_owned(usbnet_mutex_tx(un)));
-}
-
 /*
  * Endpoint / rx/tx chain management:
  *
- * usbnet_attach() initialises usbnet and allocates rx and tx chains
- * usbnet_init_rx_tx() open pipes, initialises the rx/tx chains for use
- * usbnet_stop() stops pipes, cleans (not frees) rx/tx chains, locked
- *               version assumes un_lock is held
+ * 1. usbnet_attach() initialises usbnet and allocates rx and tx chains
+ *
+ * 2. On if_init, usbnet:
+ *    - calls uno_init to initialize hardware
+ *    - open pipes
+ *    - initialises the rx/tx chains for use
+ *    - calls uno_mcast to program hardware multicast filter
+ *
+ * 3. On if_stop, usbnet:
+ *    - stops pipes
+ *    - calls uno_stop to stop hardware (unless we're detaching anyway)
+ *    - cleans (not frees) rx/tx chains
+ *    - closes pipes
+ *
  * usbnet_detach() frees the rx/tx chains
  *
  * Setup un_ed[] with valid end points before calling usbnet_attach().
- * Call usbnet_init_rx_tx() to initialise pipes, which will be open
- * upon success.
  */
-int	usbnet_init_rx_tx(struct usbnet * const);
-
-/* MII. */
-int	usbnet_mii_readreg(device_t, int, int, uint16_t *);
-int	usbnet_mii_writereg(device_t, int, int, uint16_t);
-void	usbnet_mii_statchg(struct ifnet *);
 
 /* interrupt handling */
 void	usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int,
@@ -356,15 +314,12 @@ void	usbnet_enqueue(struct usbnet * const, uint8_t *, size_t, int,
 void	usbnet_input(struct usbnet * const, uint8_t *, size_t);
 
 /* autoconf */
-void	usbnet_attach(struct usbnet *un, const char *);
+void	usbnet_attach(struct usbnet *);
 void	usbnet_attach_ifp(struct usbnet *, unsigned, unsigned,
 			  const struct usbnet_mii *);
 int	usbnet_detach(device_t, int);
 int	usbnet_activate(device_t, devact_t);
 
-/* stop backend */
-void	usbnet_stop(struct usbnet *, struct ifnet *, int);
-
 /* module hook up */
 
 #ifdef _MODULE


Home | Main Index | Thread Index | Old Index