Module Name: src Committed By: skrll Date: Sun Sep 4 08:50:26 UTC 2022
Modified Files: src/sys/dev/pci: if_bge.c if_bgevar.h Log Message: bge(4): fix the MP improvements and improve some more. - Have two locks sc_core_lock at IPL_NONE and sc_intr_lock at IPL_NET and use appropriately. - Use stopping flags instead of bge_if_flags so that bge_if_flags only needs to be protected by the sc_core_lock - Use ifmedia_init_with_lock and provide the sc_intr_lock. mii operatiions are done from the interrupt handler. - Fixup locking in bge_detach. - Rename bge_watchdog to bge_watchdog_tick to avoid confusion with the if_watchdog method. - Sprinkle some more asserts. To generate a diff of this commit: cvs rdiff -u -r1.385 -r1.386 src/sys/dev/pci/if_bge.c cvs rdiff -u -r1.39 -r1.40 src/sys/dev/pci/if_bgevar.h 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_bge.c diff -u src/sys/dev/pci/if_bge.c:1.385 src/sys/dev/pci/if_bge.c:1.386 --- src/sys/dev/pci/if_bge.c:1.385 Sun Sep 4 08:42:02 2022 +++ src/sys/dev/pci/if_bge.c Sun Sep 4 08:50:25 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bge.c,v 1.385 2022/09/04 08:42:02 skrll Exp $ */ +/* $NetBSD: if_bge.c,v 1.386 2022/09/04 08:50:25 skrll Exp $ */ /* * Copyright (c) 2001 Wind River Systems @@ -79,7 +79,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_bge.c,v 1.385 2022/09/04 08:42:02 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_bge.c,v 1.386 2022/09/04 08:50:25 skrll Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -205,8 +205,8 @@ static int bge_ioctl(struct ifnet *, u_l static int bge_init(struct ifnet *); static int bge_init_locked(struct ifnet *); static void bge_stop(struct ifnet *, int); -static void bge_stop_locked(struct ifnet *, int); -static bool bge_watchdog(struct ifnet *); +static void bge_stop_locked(struct ifnet *, bool); +static bool bge_watchdog_tick(struct ifnet *); static int bge_ifmedia_upd(struct ifnet *); static void bge_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void bge_handle_reset_work(struct work *, void *); @@ -1260,11 +1260,11 @@ bge_set_thresh(struct ifnet *ifp, int lv * occasionally cause glitches where Rx-interrupts are not * honoured for up to 10 seconds. jonat...@netbsd.org, 2003-04-05 */ - mutex_enter(sc->sc_core_lock); + mutex_enter(sc->sc_intr_lock); sc->bge_rx_coal_ticks = bge_rx_threshes[lvl].rx_ticks; sc->bge_rx_max_coal_bds = bge_rx_threshes[lvl].rx_max_bds; sc->bge_pending_rxintr_change = true; - mutex_exit(sc->sc_core_lock); + mutex_exit(sc->sc_intr_lock); } @@ -1456,14 +1456,14 @@ bge_jfree(struct mbuf *m, void *buf, siz if (i < 0 || i >= BGE_JSLOTS) panic("bge_jfree: asked to free buffer that we don't manage!"); - mutex_enter(sc->sc_core_lock); + mutex_enter(sc->sc_intr_lock); entry = SLIST_FIRST(&sc->bge_jinuse_listhead); if (entry == NULL) panic("bge_jfree: buffer not in use!"); entry->slot = i; SLIST_REMOVE_HEAD(&sc->bge_jinuse_listhead, jpool_entries); SLIST_INSERT_HEAD(&sc->bge_jfree_listhead, entry, jpool_entries); - mutex_exit(sc->sc_core_lock); + mutex_exit(sc->sc_intr_lock); if (__predict_true(m != NULL)) pool_cache_put(mb_cache, m); @@ -3297,6 +3297,9 @@ bge_attach(device_t parent, device_t sel return; } + sc->bge_stopping = false; + sc->bge_txrx_stopping = false; + /* Save various chip information. */ sc->bge_chipid = bge_chipid(pa); sc->bge_phy_addr = bge_phy_addr(sc); @@ -3868,7 +3871,8 @@ bge_attach(device_t parent, device_t sel else sc->bge_return_ring_cnt = BGE_RETURN_RING_CNT; - sc->sc_core_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); + sc->sc_core_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); + sc->sc_intr_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); /* Set up ifnet structure */ ifp = &sc->ethercom.ec_if; @@ -3943,8 +3947,9 @@ bge_attach(device_t parent, device_t sel struct ifmedia * const ifm = &sc->bge_ifmedia; sc->ethercom.ec_ifmedia = ifm; - ifmedia_init(ifm, IFM_IMASK, bge_ifmedia_upd, - bge_ifmedia_sts); + + ifmedia_init_with_lock(ifm, IFM_IMASK, + bge_ifmedia_upd, bge_ifmedia_sts, sc->sc_intr_lock); ifmedia_add(ifm, IFM_ETHER | IFM_1000_SX, 0, NULL); ifmedia_add(ifm, IFM_ETHER | IFM_1000_SX | IFM_FDX, 0, NULL); ifmedia_add(ifm, IFM_ETHER | IFM_AUTO, 0, NULL); @@ -3978,8 +3983,8 @@ bge_attach(device_t parent, device_t sel trys = 0; BGE_CLRBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP); sc->ethercom.ec_mii = mii; - ifmedia_init(&mii->mii_media, 0, bge_ifmedia_upd, - bge_ifmedia_sts); + ifmedia_init_with_lock(&mii->mii_media, 0, bge_ifmedia_upd, + bge_ifmedia_sts, sc->sc_intr_lock); mii_flags = MIIF_DOPAUSE; if (sc->bge_flags & BGEF_FIBER_MII) mii_flags |= MIIF_HAVEFIBER; @@ -4087,8 +4092,13 @@ bge_detach(device_t self, int flags __un struct bge_softc * const sc = device_private(self); struct ifnet * const ifp = &sc->ethercom.ec_if; + IFNET_LOCK(ifp); + /* Stop the interface. Callouts are stopped in it. */ bge_stop(ifp, 1); + sc->bge_detaching = true; + + IFNET_UNLOCK(ifp); mii_detach(&sc->bge_mii, MII_PHY_ANY, MII_OFFSET_ANY); @@ -4701,7 +4711,11 @@ bge_intr(void *xsc) if (BGE_IS_5717_PLUS(sc)) intrmask = 0; - mutex_enter(sc->sc_core_lock); + mutex_enter(sc->sc_intr_lock); + if (sc->bge_txrx_stopping) { + mutex_exit(sc->sc_intr_lock); + return 1; + } /* * It is possible for the interrupt to arrive before @@ -4723,7 +4737,7 @@ bge_intr(void *xsc) if (sc->bge_lasttag == statustag && (~pcistate & intrmask)) { BGE_EVCNT_INCR(sc->bge_ev_intr_spurious); - mutex_exit(sc->sc_core_lock); + mutex_exit(sc->sc_intr_lock); return 0; } sc->bge_lasttag = statustag; @@ -4731,7 +4745,7 @@ bge_intr(void *xsc) if (!(statusword & BGE_STATFLAG_UPDATED) && !(~pcistate & intrmask)) { BGE_EVCNT_INCR(sc->bge_ev_intr_spurious2); - mutex_exit(sc->sc_core_lock); + mutex_exit(sc->sc_intr_lock); return 0; } statustag = 0; @@ -4753,13 +4767,11 @@ bge_intr(void *xsc) BGE_STS_BIT(sc, BGE_STS_LINK_EVT)) bge_link_upd(sc); - if (sc->bge_if_flags & IFF_RUNNING) { - /* Check RX return ring producer/consumer */ - bge_rxeof(sc); + /* Check RX return ring producer/consumer */ + bge_rxeof(sc); - /* Check TX ring producer/consumer */ - bge_txeof(sc); - } + /* Check TX ring producer/consumer */ + bge_txeof(sc); if (sc->bge_pending_rxintr_change) { uint32_t rx_ticks = sc->bge_rx_coal_ticks; @@ -4780,10 +4792,9 @@ bge_intr(void *xsc) /* Re-enable interrupts. */ bge_writembx_flush(sc, BGE_MBX_IRQ0_LO, statustag); - if (sc->bge_if_flags & IFF_RUNNING) - if_schedule_deferred_start(ifp); + if_schedule_deferred_start(ifp); - mutex_exit(sc->sc_core_lock); + mutex_exit(sc->sc_intr_lock); return 1; } @@ -4820,6 +4831,10 @@ bge_tick(void *xsc) struct mii_data * const mii = &sc->bge_mii; mutex_enter(sc->sc_core_lock); + if (sc->bge_stopping) { + mutex_exit(sc->sc_core_lock); + return; + } if (BGE_IS_5705_PLUS(sc)) bge_stats_update_regs(sc); @@ -4840,15 +4855,17 @@ bge_tick(void *xsc) * IPMI/ASF mode or produce extra input errors. * (extra input errors was reported for bcm5701 & bcm5704). */ - if (!BGE_STS_BIT(sc, BGE_STS_LINK)) + if (!BGE_STS_BIT(sc, BGE_STS_LINK)) { + mutex_enter(sc->sc_intr_lock); mii_tick(mii); + mutex_exit(sc->sc_intr_lock); + } } bge_asf_driver_up(sc); - const bool ok = bge_watchdog(ifp); - - if (ok && !sc->bge_detaching) + const bool ok = bge_watchdog_tick(ifp); + if (ok) callout_schedule(&sc->bge_timeout, hz); mutex_exit(sc->sc_core_lock); @@ -5454,9 +5471,10 @@ bge_start(struct ifnet *ifp) { struct bge_softc * const sc = ifp->if_softc; - mutex_enter(sc->sc_core_lock); - bge_start_locked(ifp); - mutex_exit(sc->sc_core_lock); + mutex_enter(sc->sc_intr_lock); + if (!sc->bge_txrx_stopping) + bge_start_locked(ifp); + mutex_exit(sc->sc_intr_lock); } /* @@ -5473,8 +5491,7 @@ bge_start_locked(struct ifnet *ifp) int pkts = 0; int error; - if ((sc->bge_if_flags & IFF_RUNNING) != IFF_RUNNING) - return; + KASSERT(mutex_owned(sc->sc_intr_lock)); prodidx = sc->bge_tx_prodidx; @@ -5549,6 +5566,11 @@ bge_init(struct ifnet *ifp) { struct bge_softc * const sc = ifp->if_softc; + KASSERT(IFNET_LOCKED(ifp)); + + if (sc->bge_detaching) + return ENXIO; + mutex_enter(sc->sc_core_lock); int ret = bge_init_locked(ifp); mutex_exit(sc->sc_core_lock); @@ -5565,12 +5587,13 @@ bge_init_locked(struct ifnet *ifp) uint32_t mode, reg; int error = 0; + ASSERT_SLEEPABLE(); KASSERT(IFNET_LOCKED(ifp)); KASSERT(mutex_owned(sc->sc_core_lock)); KASSERT(ifp == &sc->ethercom.ec_if); /* Cancel pending I/O and flush buffers. */ - bge_stop_locked(ifp, 0); + bge_stop_locked(ifp, false); bge_stop_fw(sc); bge_sig_pre_reset(sc, BGE_RESET_START); @@ -5740,15 +5763,18 @@ bge_init_locked(struct ifnet *ifp) BGE_CLRBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); bge_writembx_flush(sc, BGE_MBX_IRQ0_LO, 0); - if ((error = bge_ifmedia_upd(ifp)) != 0) - goto out; + mutex_enter(sc->sc_intr_lock); + if ((error = bge_ifmedia_upd(ifp)) == 0) { + sc->bge_stopping = false; + sc->bge_txrx_stopping = false; - /* IFNET_LOCKED asserted above */ - ifp->if_flags |= IFF_RUNNING; + /* IFNET_LOCKED asserted above */ + ifp->if_flags |= IFF_RUNNING; - callout_schedule(&sc->bge_timeout, hz); + callout_schedule(&sc->bge_timeout, hz); + } + mutex_exit(sc->sc_intr_lock); -out: sc->bge_if_flags = ifp->if_flags; return error; @@ -5765,6 +5791,8 @@ bge_ifmedia_upd(struct ifnet *ifp) struct ifmedia * const ifm = &sc->bge_ifmedia; int rc; + KASSERT(mutex_owned(sc->sc_intr_lock)); + /* If this is a 1000baseX NIC, enable the TBI port. */ if (sc->bge_flags & BGEF_FIBER_TBI) { if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) @@ -5863,6 +5891,8 @@ bge_ifmedia_sts(struct ifnet *ifp, struc struct bge_softc * const sc = ifp->if_softc; struct mii_data * const mii = &sc->bge_mii; + KASSERT(mutex_owned(sc->sc_intr_lock)); + if (sc->bge_flags & BGEF_FIBER_TBI) { ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; @@ -6026,7 +6056,7 @@ bge_watchdog_check(struct bge_softc * co } static bool -bge_watchdog(struct ifnet *ifp) +bge_watchdog_tick(struct ifnet *ifp) { struct bge_softc * const sc = ifp->if_softc; @@ -6035,8 +6065,6 @@ bge_watchdog(struct ifnet *ifp) if (!sc->sc_trigger_reset && bge_watchdog_check(sc)) return true; - aprint_error_dev(sc->bge_dev, "watchdog timeout -- resetting\n"); - if (atomic_swap_uint(&sc->sc_reset_pending, 1) == 0) workqueue_enqueue(sc->sc_reset_wq, &sc->sc_reset_work, NULL); @@ -6052,6 +6080,8 @@ bge_handle_reset_work(struct work *work, struct bge_softc * const sc = arg; struct ifnet * const ifp = &sc->ethercom.ec_if; + printf("%s: watchdog timeout -- resetting\n", ifp->if_xname); + /* Don't want ioctl operations to happen */ IFNET_LOCK(ifp); @@ -6100,9 +6130,10 @@ bge_stop(struct ifnet *ifp, int disable) struct bge_softc * const sc = ifp->if_softc; ASSERT_SLEEPABLE(); + KASSERT(IFNET_LOCKED(ifp)); mutex_enter(sc->sc_core_lock); - bge_stop_locked(ifp, disable); + bge_stop_locked(ifp, disable ? true : false); mutex_exit(sc->sc_core_lock); } @@ -6111,17 +6142,21 @@ bge_stop(struct ifnet *ifp, int disable) * RX and TX lists. */ static void -bge_stop_locked(struct ifnet *ifp, int disable) +bge_stop_locked(struct ifnet *ifp, bool disable) { struct bge_softc * const sc = ifp->if_softc; + ASSERT_SLEEPABLE(); + KASSERT(IFNET_LOCKED(ifp)); KASSERT(mutex_owned(sc->sc_core_lock)); - if (disable) { - sc->bge_detaching = true; - callout_halt(&sc->bge_timeout, NULL); - } else - callout_stop(&sc->bge_timeout); + sc->bge_stopping = true; + + mutex_enter(sc->sc_intr_lock); + sc->bge_txrx_stopping = true; + mutex_exit(sc->sc_intr_lock); + + callout_halt(&sc->bge_timeout, sc->sc_core_lock); /* Disable host interrupts. */ BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); @@ -6206,8 +6241,11 @@ bge_stop_locked(struct ifnet *ifp, int d /* * Isolate/power down the PHY. */ - if (!(sc->bge_flags & BGEF_FIBER_TBI)) + if (!(sc->bge_flags & BGEF_FIBER_TBI)) { + mutex_enter(sc->sc_intr_lock); mii_down(&sc->bge_mii); + mutex_exit(sc->sc_intr_lock); + } sc->bge_tx_saved_considx = BGE_TXCONS_UNSET; @@ -6215,6 +6253,8 @@ bge_stop_locked(struct ifnet *ifp, int d BGE_STS_CLRBIT(sc, BGE_STS_LINK); ifp->if_flags &= ~IFF_RUNNING; + + sc->bge_if_flags = ifp->if_flags; } static void @@ -6226,6 +6266,8 @@ bge_link_upd(struct bge_softc *sc) uint16_t phyval; int link; + KASSERT(sc->sc_intr_lock); + /* Clear 'pending link event' flag */ BGE_STS_CLRBIT(sc, BGE_STS_LINK_EVT); Index: src/sys/dev/pci/if_bgevar.h diff -u src/sys/dev/pci/if_bgevar.h:1.39 src/sys/dev/pci/if_bgevar.h:1.40 --- src/sys/dev/pci/if_bgevar.h:1.39 Fri Sep 2 06:51:24 2022 +++ src/sys/dev/pci/if_bgevar.h Sun Sep 4 08:50:25 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bgevar.h,v 1.39 2022/09/02 06:51:24 skrll Exp $ */ +/* $NetBSD: if_bgevar.h,v 1.40 2022/09/04 08:50:25 skrll Exp $ */ /* * Copyright (c) 2001 Wind River Systems * Copyright (c) 1997, 1998, 1999, 2001 @@ -327,6 +327,8 @@ struct bge_softc { uint32_t bge_phy_flags; int bge_flowflags; time_t bge_tx_lastsent; + bool bge_stopping; + bool bge_txrx_stopping; bool bge_tx_sending; #ifdef BGE_EVENT_COUNTERS @@ -356,6 +358,7 @@ struct bge_softc { krndsource_t rnd_source; /* random source */ kmutex_t *sc_core_lock; /* lock for softc operations */ + kmutex_t *sc_intr_lock; /* lock for interrupt operations */ struct workqueue *sc_reset_wq; struct work sc_reset_work; volatile unsigned sc_reset_pending;