Module Name: src Committed By: riastradh Date: Fri Aug 12 10:58:21 UTC 2022
Modified Files: src/sys/dev/pci: if_wm.c Log Message: wm: use a workqueue to reset the interface when wm_watchdog determines it needs a reset. Author: Nick Hudson <sk...@netbsd.org> Committer: Taylor R Campbell <riastr...@netbsd.org> To generate a diff of this commit: cvs rdiff -u -r1.760 -r1.761 src/sys/dev/pci/if_wm.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/pci/if_wm.c diff -u src/sys/dev/pci/if_wm.c:1.760 src/sys/dev/pci/if_wm.c:1.761 --- src/sys/dev/pci/if_wm.c:1.760 Fri Aug 12 10:57:06 2022 +++ src/sys/dev/pci/if_wm.c Fri Aug 12 10:58:21 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wm.c,v 1.760 2022/08/12 10:57:06 riastradh Exp $ */ +/* $NetBSD: if_wm.c,v 1.761 2022/08/12 10:58:21 riastradh Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -82,7 +82,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.760 2022/08/12 10:57:06 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.761 2022/08/12 10:58:21 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_net_mpsafe.h" @@ -706,10 +706,15 @@ struct wm_softc { struct wm_phyop phy; struct wm_nvmop nvm; + struct workqueue *sc_reset_wq; + struct work sc_reset_work; + volatile unsigned sc_reset_pending; + bool sc_dying; #ifdef WM_DEBUG uint32_t sc_debug; + bool sc_trigger_reset; #endif }; @@ -823,7 +828,7 @@ static void wm_attach(device_t, device_t 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 *, @@ -917,6 +922,7 @@ static void wm_nq_send_common_locked(str 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); @@ -2222,7 +2228,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_SOFTCLOCK, + WQ_MPSAFE); + if (error) { + workqueue_destroy(sc->sc_queue_wq); + aprint_error_dev(sc->sc_dev, + "unable to create reset workqueue\n"); goto out; } @@ -3517,8 +3534,9 @@ wm_detach(device_t self, int flags __unu } 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 workqueues are 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); @@ -3603,11 +3621,11 @@ wm_resume(device_t self, const pmf_qual_ } /* - * wm_watchdog: [ifnet interface function] + * wm_watchdog: * - * Watchdog timer handler. + * Watchdog checker. */ -static void +static bool wm_watchdog(struct ifnet *ifp) { int qid; @@ -3620,17 +3638,48 @@ 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); - - /* - * 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); +#ifdef WM_DEBUG + if (sc->sc_trigger_reset) { + /* debug operation, no need for atomicity or reliability */ + sc->sc_trigger_reset = 0; + hang_queue++; } +#endif + + if (hang_queue == 0) + return true; + + if (atomic_swap_uint(&sc->sc_reset_pending, 1) == 0) + 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 = arg; + 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); + + IFNET_UNLOCK(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); + + atomic_store_relaxed(&sc->sc_reset_pending, 0); } @@ -3863,9 +3912,8 @@ wm_tick(void *arg) splx(s); #endif - wm_watchdog(ifp); - - callout_schedule(&sc->sc_tick_ch, hz); + if (wm_watchdog(ifp)) + callout_schedule(&sc->sc_tick_ch, hz); } static int @@ -6468,6 +6516,12 @@ wm_init_sysctls(struct wm_softc *sc) wm_sysctl_debug, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (rv != 0) goto teardown; + rv = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE, + CTLTYPE_BOOL, "trigger_reset", + SYSCTL_DESCR("Trigger an interface reset"), + NULL, 0, &sc->sc_trigger_reset, 0, CTL_CREATE, CTL_EOL); + if (rv != 0) + goto teardown; #endif return; @@ -7110,6 +7164,7 @@ wm_stop(struct ifnet *ifp, int disable) */ for (int i = 0; i < sc->sc_nqueues; i++) workqueue_wait(sc->sc_queue_wq, &sc->sc_queue[i].wmq_cookie); + workqueue_wait(sc->sc_reset_wq, &sc->sc_reset_work); } static void