git: ee7f62faa893 - main - e6000sw: stop / drain the taskqueue (and tick) during detach

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Sun, 27 Apr 2025 18:38:10 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=ee7f62faa893bd9bbe6650716579072724c427cb

commit ee7f62faa893bd9bbe6650716579072724c427cb
Author:     Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2025-04-25 18:41:13 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2025-04-27 18:05:05 +0000

    e6000sw: stop / drain the taskqueue (and tick) during detach
    
    Although the tick isn't running every hz right now, when it /is/
    running at hz, the shutdown path will race with an existing running
    tick routine, causing unpredictable panics.
    
    * Introduce a shutdown flag which will abort doing the tick work if set
    * set the shutdown flag and start cancel/draining the taskqueue during
      detach.
    
    Differential Revision:  https://reviews.freebsd.org/D50030
---
 sys/dev/etherswitch/e6000sw/e6000sw.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c
index 85900cebc303..be02edb3d5e6 100644
--- a/sys/dev/etherswitch/e6000sw/e6000sw.c
+++ b/sys/dev/etherswitch/e6000sw/e6000sw.c
@@ -89,6 +89,7 @@ typedef struct e6000sw_softc {
 	device_t		miibus[E6000SW_MAX_PORTS];
 	struct taskqueue	*sc_tq;
 	struct timeout_task	sc_tt;
+	bool			is_shutdown;
 
 	int			vlans[E6000SW_NUM_VLANS];
 	uint32_t		swid;
@@ -851,13 +852,18 @@ e6000sw_detach(device_t dev)
 
 	sc = device_get_softc(dev);
 
+	E6000SW_LOCK(sc);
+	sc->is_shutdown = true;
+	if (sc->sc_tq != NULL) {
+		while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_tt, NULL) != 0)
+			taskqueue_drain_timeout(sc->sc_tq, &sc->sc_tt);
+	}
+	E6000SW_UNLOCK(sc);
+
 	error = bus_generic_detach(dev);
 	if (error != 0)
 		return (error);
 
-	if (device_is_attached(dev))
-		taskqueue_drain_timeout(sc->sc_tq, &sc->sc_tt);
-
 	if (sc->sc_tq != NULL)
 		taskqueue_free(sc->sc_tq);
 
@@ -1584,6 +1590,12 @@ e6000sw_tick(void *arg, int p __unused)
 	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
 
 	E6000SW_LOCK(sc);
+
+	if (sc->is_shutdown) {
+		E6000SW_UNLOCK(sc);
+		return;
+	}
+
 	for (port = 0; port < sc->num_ports; port++) {
 		/* Tick only on PHY ports */
 		if (!e6000sw_is_portenabled(sc, port) ||