Module Name: src Committed By: knakahara Date: Tue Jan 30 08:15:47 UTC 2018
Modified Files: src/sys/dev/pci: if_wm.c Log Message: Make wm(4) watchdog MP-safe. There is almost no influence on performance. wm(4) does not use ifp->if_watchdog now, that is, it does not touch ifp->if_timer. It also uses own callout(wm_tick) as watchdog now. The watchdog uses per-queue counter to check timeout. So, global lock is not required. To generate a diff of this commit: cvs rdiff -u -r1.561 -r1.562 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.561 src/sys/dev/pci/if_wm.c:1.562 --- src/sys/dev/pci/if_wm.c:1.561 Mon Jan 29 04:17:32 2018 +++ src/sys/dev/pci/if_wm.c Tue Jan 30 08:15:47 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wm.c,v 1.561 2018/01/29 04:17:32 knakahara Exp $ */ +/* $NetBSD: if_wm.c,v 1.562 2018/01/30 08:15:47 knakahara Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -83,7 +83,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.561 2018/01/29 04:17:32 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.562 2018/01/30 08:15:47 knakahara Exp $"); #ifdef _KERNEL_OPT #include "opt_net_mpsafe.h" @@ -183,6 +183,11 @@ int wm_debug = WM_DEBUG_TX | WM_DEBUG_RX int wm_disable_msi = WM_DISABLE_MSI; int wm_disable_msix = WM_DISABLE_MSIX; +#ifndef WM_WATCHDOG_TIMEOUT +#define WM_WATCHDOG_TIMEOUT 5 +#endif +static int wm_watchdog_timeout = WM_WATCHDOG_TIMEOUT; + /* * Transmit descriptor list size. Due to errata, we can only have * 256 hardware descriptors in the ring on < 82544, but we use 4096 @@ -363,6 +368,9 @@ struct wm_txqueue { bool txq_stopping; + bool txq_watchdog; + time_t txq_lastsent; + uint32_t txq_packets; /* for AIM */ uint32_t txq_bytes; /* for AIM */ #ifdef WM_EVENT_COUNTERS @@ -680,8 +688,8 @@ 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 void wm_watchdog_txq(struct ifnet *, struct wm_txqueue *); -static void wm_watchdog_txq_locked(struct ifnet *, struct wm_txqueue *); +static void wm_watchdog_txq(struct ifnet *, struct wm_txqueue *, uint16_t *); +static void wm_watchdog_txq_locked(struct ifnet *, struct wm_txqueue *, uint16_t *); static void wm_tick(void *); static int wm_ifflags_cb(struct ethercom *); static int wm_ioctl(struct ifnet *, u_long, void *); @@ -2683,7 +2691,7 @@ alloc_retry: if (wm_is_using_multiqueue(sc)) ifp->if_transmit = wm_transmit; } - ifp->if_watchdog = wm_watchdog; + /* wm(4) doest not use ifp->if_watchdog, use wm_tick as watchdog. */ ifp->if_init = wm_init; ifp->if_stop = wm_stop; IFQ_SET_MAXLEN(&ifp->if_snd, max(WM_IFQUEUELEN, IFQ_MAXLEN)); @@ -2945,37 +2953,47 @@ wm_watchdog(struct ifnet *ifp) { int qid; struct wm_softc *sc = ifp->if_softc; + uint16_t hang_queue = 0; /* Max queue number of wm(4) is 82576's 16. */ for (qid = 0; qid < sc->sc_nqueues; qid++) { struct wm_txqueue *txq = &sc->sc_queue[qid].wmq_txq; - wm_watchdog_txq(ifp, txq); + wm_watchdog_txq(ifp, txq, &hang_queue); } - /* Reset the interface. */ - (void) wm_init(ifp); - /* - * There are still some upper layer processing which call - * ifp->if_start(). e.g. ALTQ or one CPU system + * IF any of queues hanged up, reset the interface. */ - /* Try to get more packets going. */ - ifp->if_start(ifp); + 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); + } } + static void -wm_watchdog_txq(struct ifnet *ifp, struct wm_txqueue *txq) +wm_watchdog_txq(struct ifnet *ifp, struct wm_txqueue *txq, uint16_t *hang) { mutex_enter(txq->txq_lock); - wm_watchdog_txq_locked(ifp, txq); + if (txq->txq_watchdog && + time_uptime - txq->txq_lastsent > wm_watchdog_timeout) { + wm_watchdog_txq_locked(ifp, txq, hang); + } mutex_exit(txq->txq_lock); } static void -wm_watchdog_txq_locked(struct ifnet *ifp, struct wm_txqueue *txq) +wm_watchdog_txq_locked(struct ifnet *ifp, struct wm_txqueue *txq, uint16_t *hang) { struct wm_softc *sc = ifp->if_softc; + struct wm_queue *wmq = container_of(txq, struct wm_queue, wmq_txq); KASSERT(mutex_owned(txq->txq_lock)); @@ -2984,6 +3002,8 @@ wm_watchdog_txq_locked(struct ifnet *ifp * before we report an error. */ wm_txeof(txq, UINT_MAX); + if (txq->txq_watchdog) + *hang |= __BIT(wmq->wmq_id); if (txq->txq_free != WM_NTXDESC(txq)) { #ifdef WM_DEBUG @@ -3044,8 +3064,13 @@ wm_tick(void *arg) WM_CORE_LOCK(sc); - if (sc->sc_core_stopping) - goto out; + if (sc->sc_core_stopping) { + WM_CORE_UNLOCK(sc); +#ifndef WM_MPSAFE + splx(s); +#endif + return; + } if (sc->sc_type >= WM_T_82542_2_1) { WM_EVCNT_ADD(&sc->sc_ev_rx_xon, CSR_READ(sc, WMREG_XONRXC)); @@ -3083,12 +3108,11 @@ wm_tick(void *arg) else wm_tbi_tick(sc); - callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc); -out: WM_CORE_UNLOCK(sc); -#ifndef WM_MPSAFE - splx(s); -#endif + + wm_watchdog(ifp); + + callout_reset(&sc->sc_tick_ch, hz, wm_tick, sc); } static int @@ -5976,6 +6000,7 @@ wm_stop_locked(struct ifnet *ifp, int di struct wm_queue *wmq = &sc->sc_queue[qidx]; struct wm_txqueue *txq = &wmq->wmq_txq; mutex_enter(txq->txq_lock); + txq->txq_watchdog = false; /* ensure watchdog disabled */ for (i = 0; i < WM_TXQUEUELEN(txq); i++) { txs = &txq->txq_soft[i]; if (txs->txs_mbuf != NULL) { @@ -5989,7 +6014,6 @@ wm_stop_locked(struct ifnet *ifp, int di /* Mark the interface as down and cancel the watchdog timer. */ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - ifp->if_timer = 0; if (disable) { for (i = 0; i < sc->sc_nqueues; i++) { @@ -6664,6 +6688,8 @@ wm_init_tx_queue(struct wm_softc *sc, st wm_init_tx_descs(sc, txq); wm_init_tx_regs(sc, wmq, txq); wm_init_tx_buffer(sc, txq); + + txq->txq_watchdog = false; } static void @@ -7427,7 +7453,8 @@ wm_send_common_locked(struct ifnet *ifp, if (txq->txq_free != ofree) { /* Set a watchdog timer in case the chip flakes out. */ - ifp->if_timer = 5; + txq->txq_lastsent = time_uptime; + txq->txq_watchdog = true; } } @@ -7999,7 +8026,8 @@ wm_nq_send_common_locked(struct ifnet *i if (sent) { /* Set a watchdog timer in case the chip flakes out. */ - ifp->if_timer = 5; + txq->txq_lastsent = time_uptime; + txq->txq_watchdog = true; } } @@ -8138,7 +8166,7 @@ wm_txeof(struct wm_txqueue *txq, u_int l * timer. */ if (txq->txq_sfree == WM_TXQUEUELEN(txq)) - ifp->if_timer = 0; + txq->txq_watchdog = false; return count; }