Module Name: src Committed By: riastradh Date: Sat Aug 20 14:06:21 UTC 2022
Modified Files: src/sys/dev/usb: usbnet.c Log Message: usbnet(9): Split unp_stopping into stopped/txstopped/rxstopped. In practical terms this could be done with one variable and an atomic store, but serializing all access with a lock makes reasoning easier, and the locks have to be taken by the logic that queries the variables anyway, and the variables are set only under heavy-weight configuration changes anyway. What this accomplishes is disentangling lock order between rxlock and txlock: they are never taken at the same time, so no order is needed. I renamed unp_stopping to unp_stopped for a compiler-assisted audit to make sure I reviewed every case of it. To generate a diff of this commit: cvs rdiff -u -r1.101 -r1.102 src/sys/dev/usb/usbnet.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/usbnet.c diff -u src/sys/dev/usb/usbnet.c:1.101 src/sys/dev/usb/usbnet.c:1.102 --- src/sys/dev/usb/usbnet.c:1.101 Sat Aug 20 14:06:09 2022 +++ src/sys/dev/usb/usbnet.c Sat Aug 20 14:06:20 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: usbnet.c,v 1.101 2022/08/20 14:06:09 riastradh Exp $ */ +/* $NetBSD: usbnet.c,v 1.102 2022/08/20 14:06:20 riastradh Exp $ */ /* * Copyright (c) 2019 Matthew R. Green @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.101 2022/08/20 14:06:09 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.102 2022/08/20 14:06:20 riastradh Exp $"); #include <sys/param.h> #include <sys/kernel.h> @@ -58,7 +58,8 @@ struct usbnet_private { * - unp_txlock protects the tx path and its data * * the lock ordering is: - * ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock + * ifnet lock -> unp_core_lock -> unp_rxlock + * -> unp_txlock * -> unp_mcastlock * - ifnet lock is not needed for unp_core_lock, but if ifnet lock is * involved, it must be taken first @@ -79,7 +80,9 @@ struct usbnet_private { struct usbd_pipe *unp_ep[USBNET_ENDPT_MAX]; volatile bool unp_dying; - bool unp_stopping; + bool unp_stopped; + bool unp_rxstopped; + bool unp_txstopped; bool unp_attached; bool unp_ifp_attached; bool unp_link; @@ -360,7 +363,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, voi mutex_enter(&unp->unp_rxlock); - if (usbnet_isdying(un) || unp->unp_stopping || + if (usbnet_isdying(un) || unp->unp_rxstopped || status == USBD_INVAL || status == USBD_NOT_STARTED || status == USBD_CANCELLED) goto out; @@ -387,7 +390,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, voi usbnet_isowned_rx(un); done: - if (usbnet_isdying(un) || unp->unp_stopping) + if (usbnet_isdying(un) || unp->unp_rxstopped) goto out; mutex_exit(&unp->unp_rxlock); @@ -416,7 +419,7 @@ usbnet_txeof(struct usbd_xfer *xfer, voi unp->unp_number, status, (uintptr_t)xfer, 0); mutex_enter(&unp->unp_txlock); - if (unp->unp_stopping || usbnet_isdying(un)) { + if (unp->unp_txstopped || usbnet_isdying(un)) { mutex_exit(&unp->unp_txlock); return; } @@ -588,11 +591,11 @@ usbnet_if_start(struct ifnet *ifp) struct usbnet_private * const unp = un->un_pri; USBNETHIST_FUNC(); - USBNETHIST_CALLARGS("%jd: stopping %jd", - unp->unp_number, unp->unp_stopping, 0, 0); + USBNETHIST_CALLARGS("%jd: txstopped %jd", + unp->unp_number, unp->unp_txstopped, 0, 0); mutex_enter(&unp->unp_txlock); - if (!unp->unp_stopping) + if (!unp->unp_txstopped) usbnet_start_locked(ifp); mutex_exit(&unp->unp_txlock); } @@ -678,8 +681,8 @@ usbnet_rx_start_pipes(struct usbnet * co struct usbnet_private * const unp = un->un_pri; mutex_enter(&unp->unp_rxlock); - mutex_enter(&unp->unp_txlock); - unp->unp_stopping = false; + KASSERT(unp->unp_rxstopped); + unp->unp_rxstopped = false; for (size_t i = 0; i < un->un_rx_list_cnt; i++) { struct usbnet_chain *c = &cd->uncd_rx_chain[i]; @@ -689,7 +692,6 @@ usbnet_rx_start_pipes(struct usbnet * co usbd_transfer(c->unc_xfer); } - mutex_exit(&unp->unp_txlock); mutex_exit(&unp->unp_rxlock); } @@ -874,9 +876,17 @@ usbnet_init_rx_tx(struct usbnet * const mutex_exit(&unp->unp_mcastlock); } + /* Allow transmit. */ + mutex_enter(&unp->unp_txlock); + KASSERT(unp->unp_txstopped); + unp->unp_txstopped = false; + mutex_exit(&unp->unp_txlock); + /* Start up the receive pipe(s). */ usbnet_rx_start_pipes(un); + /* Kick off the watchdog/stats/mii tick. */ + unp->unp_stopped = false; callout_schedule(&unp->unp_stat_ch, hz); out: @@ -1094,17 +1104,21 @@ usbnet_stop(struct usbnet *un, struct if * Prevent new activity (rescheduling ticks, xfers, &c.) and * clear the watchdog timer. */ + unp->unp_stopped = true; + mutex_enter(&unp->unp_rxlock); + unp->unp_rxstopped = true; + mutex_exit(&unp->unp_rxlock); + mutex_enter(&unp->unp_txlock); - unp->unp_stopping = true; + unp->unp_txstopped = true; unp->unp_timer = 0; mutex_exit(&unp->unp_txlock); - mutex_exit(&unp->unp_rxlock); /* * Stop the timer first, then the task -- if the timer was * already firing, we stop the task or wait for it complete - * only after if last fired. Setting unp_stopping prevents the + * only after if last fired. Setting unp_stopped prevents the * timer task from being scheduled again. */ callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock); @@ -1233,7 +1247,7 @@ usbnet_tick_task(void *arg) uno_tick(un); mutex_enter(&unp->unp_core_lock); - if (!unp->unp_stopping && !usbnet_isdying(un)) + if (!unp->unp_stopped && !usbnet_isdying(un)) callout_schedule(&unp->unp_stat_ch, hz); mutex_exit(&unp->unp_core_lock); } @@ -1402,6 +1416,9 @@ usbnet_attach(struct usbnet *un) unp->unp_number = atomic_inc_uint_nv(&usbnet_number); + unp->unp_stopped = true; + unp->unp_rxstopped = true; + unp->unp_txstopped = true; unp->unp_attached = true; } @@ -1591,11 +1608,17 @@ usbnet_activate(device_t self, devact_t atomic_store_relaxed(&unp->unp_dying, true); + mutex_enter(&unp->unp_core_lock); + unp->unp_stopped = true; + mutex_exit(&unp->unp_core_lock); + mutex_enter(&unp->unp_rxlock); + unp->unp_rxstopped = true; + mutex_exit(&unp->unp_rxlock); + mutex_enter(&unp->unp_txlock); - unp->unp_stopping = true; + unp->unp_txstopped = true; mutex_exit(&unp->unp_txlock); - mutex_exit(&unp->unp_rxlock); return 0; default: