From 6a1ece141c92669d0e1e9f9f4883c88334197686 Mon Sep 17 00:00:00 2001 From: Nick Hudson Date: Fri, 29 Jul 2022 15:59:25 +0100 Subject: [PATCH 06/67] wm: use a workqueue to reset the interface when wm_watchdog determines it needs a reset. --- sys/dev/pci/if_wm.c | 75 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/sys/dev/pci/if_wm.c b/sys/dev/pci/if_wm.c index 78ebb22f5c40..e123005fa2f9 100644 --- a/sys/dev/pci/if_wm.c +++ b/sys/dev/pci/if_wm.c @@ -705,6 +705,10 @@ struct wm_softc { struct wm_phyop phy; struct wm_nvmop nvm; + + struct workqueue *sc_reset_wq; + struct work sc_reset_work; + #ifdef WM_DEBUG uint32_t sc_debug; #endif @@ -820,7 +824,7 @@ static void wm_attach(device_t, device_t, void *); static int wm_detach(device_t, int); static bool wm_suspend(device_t, const pmf_qual_t *); static bool wm_resume(device_t, const pmf_qual_t *); -static void wm_watchdog(struct ifnet *); +static bool wm_watchdog(struct ifnet *); static void wm_watchdog_txq(struct ifnet *, struct wm_txqueue *, uint16_t *); static void wm_watchdog_txq_locked(struct ifnet *, struct wm_txqueue *, @@ -914,6 +918,7 @@ static void wm_nq_send_common_locked(struct ifnet *, struct wm_txqueue *, static void wm_deferred_start_locked(struct wm_txqueue *); static void wm_handle_queue(void *); static void wm_handle_queue_work(struct work *, void *); +static void wm_handle_reset_work(struct work *, void *); /* Interrupt */ static bool wm_txeof(struct wm_txqueue *, u_int); static bool wm_rxeof(struct wm_rxqueue *, u_int); @@ -2218,7 +2223,18 @@ alloc_retry: WM_WORKQUEUE_FLAGS); if (error) { aprint_error_dev(sc->sc_dev, - "unable to create workqueue\n"); + "unable to create TxRx workqueue\n"); + goto out; + } + + snprintf(wqname, sizeof(wqname), "%sReset", device_xname(sc->sc_dev)); + error = workqueue_create(&sc->sc_reset_wq, wqname, + wm_handle_reset_work, sc, WM_WORKQUEUE_PRI, IPL_NET, + WM_WORKQUEUE_FLAGS); + if (error) { + workqueue_destroy(sc->sc_queue_wq); + aprint_error_dev(sc->sc_dev, + "unable to create reset workqueue\n"); goto out; } @@ -3510,8 +3526,9 @@ wm_detach(device_t self, int flags __unused) } pci_intr_release(sc->sc_pc, sc->sc_intrs, sc->sc_nintrs); - /* wm_stop() ensured that the workqueue is stopped. */ + /* wm_stop() ensured that the tx workqueue is stopped. */ workqueue_destroy(sc->sc_queue_wq); + workqueue_destroy(sc->sc_reset_wq); for (i = 0; i < sc->sc_nqueues; i++) softint_disestablish(sc->sc_queue[i].wmq_si); @@ -3594,11 +3611,11 @@ wm_resume(device_t self, const pmf_qual_t *qual) } /* - * wm_watchdog: [ifnet interface function] + * wm_watchdog: * - * Watchdog timer handler. + * Watchdog checker. */ -static void +static bool wm_watchdog(struct ifnet *ifp) { int qid; @@ -3611,17 +3628,38 @@ wm_watchdog(struct ifnet *ifp) wm_watchdog_txq(ifp, txq, &hang_queue); } - /* IF any of queues hanged up, reset the interface. */ - if (hang_queue != 0) { - (void)wm_init(ifp); + if (hang_queue == 0) + return true; - /* - * There are still some upper layer processing which call - * ifp->if_start(). e.g. ALTQ or one CPU system - */ - /* Try to get more packets going. */ - ifp->if_start(ifp); - } + workqueue_enqueue(sc->sc_reset_wq, &sc->sc_reset_work, NULL); + + return false; +} + +/* + * Perform an interface watchdog reset. + */ +static void +wm_handle_reset_work(struct work *work, void *arg) +{ + struct wm_softc * const sc = + container_of(work, struct wm_softc, sc_reset_work); + struct ifnet * const ifp = &sc->sc_ethercom.ec_if; + + /* Don't want ioctl operations to happen */ + IFNET_LOCK(ifp); + + /* reset the interface. */ + wm_init(ifp); + + /* + * There are still some upper layer processing which call + * ifp->if_start(). e.g. ALTQ or one CPU system + */ + /* Try to get more packets going. */ + ifp->if_start(ifp); + + IFNET_UNLOCK(ifp); } @@ -3854,9 +3892,10 @@ wm_tick(void *arg) splx(s); #endif - wm_watchdog(ifp); + bool ok = wm_watchdog(ifp); - callout_schedule(&sc->sc_tick_ch, hz); + if (ok) + callout_schedule(&sc->sc_tick_ch, hz); } static int -- 2.25.1