fix issues seen with transfers being reused before they are finished being used. adapt locking to the modern world. some what inspired by if_smsc.c: - axen_mii_lock is a normal mutex not rwlock - add locks for softc, rx and tx - add safe detach support - safe detach vs mii lock requires 2 methods to lock the MII lock, explicitly pass locking knowledge around - check axen_dying and new axen_stopping more often also: - rename axen_ax88179_eeprom() to axen_get_eaddr() and move the code here into the support the current method. - consolidate checks to reduce the number of error paths that need to release a resource - use const, __func__, more. special thanks to mlelstv and skrll for clearig up various confusion and providing examples. XXX: split up "also" into separate commit(s). Index: if_axen.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/if_axen.c,v retrieving revision 1.42 diff -p -u -r1.42 if_axen.c --- if_axen.c 18 Jun 2019 09:34:57 -0000 1.42 +++ if_axen.c 19 Jun 2019 13:09:18 -0000 @@ -36,7 +36,6 @@ __KERNEL_RCSID(0, "$NetBSD: if_axen.c,v #include #include #include -#include #include #include #include @@ -108,11 +107,16 @@ struct axen_softc { int axen_refcnt; bool axen_dying; + bool axen_stopping; bool axen_attached; struct usb_task axen_tick_task; - krwlock_t axen_mii_lock; + kmutex_t axen_lock; + kmutex_t axen_mii_lock; + kmutex_t axen_rxlock; + kmutex_t axen_txlock; + kcondvar_t axen_detachcv; int axen_link; @@ -167,9 +171,11 @@ static void axen_txeof(struct usbd_xfer static void axen_tick(void *); static void axen_tick_task(void *); static void axen_start(struct ifnet *); +static void axen_start_locked(struct ifnet *); static int axen_ioctl(struct ifnet *, u_long, void *); static int axen_init(struct ifnet *); static void axen_stop(struct ifnet *, int); +static void axen_stop_locked(struct ifnet *, int); static void axen_watchdog(struct ifnet *); static int axen_miibus_readreg(device_t, int, int, uint16_t *); static int axen_miibus_writereg(device_t, int, int, uint16_t); @@ -178,33 +184,54 @@ static int axen_cmd(struct axen_softc *, static int axen_ifmedia_upd(struct ifnet *); static void axen_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void axen_reset(struct axen_softc *); -#if 0 -static int axen_ax88179_eeprom(struct axen_softc *, void *); -#endif - -static void axen_iff(struct axen_softc *); -static void axen_lock_mii(struct axen_softc *); -static void axen_unlock_mii(struct axen_softc *); - +static int axen_get_eaddr(struct axen_softc *, void *); +static void axen_iff(struct axen_softc *, bool); static void axen_ax88179_init(struct axen_softc *); static void axen_setcoe(struct axen_softc *); -/* Get exclusive access to the MII registers */ +/* + * Access functions for MII. Take the MII lock to call axen_cmd(). + * Two forms: softc lock currently held or not. + */ static void axen_lock_mii(struct axen_softc *sc) { + mutex_enter(&sc->axen_lock); + sc->axen_refcnt++; + mutex_exit(&sc->axen_lock); + + mutex_enter(&sc->axen_mii_lock); +} + +static void +axen_lock_mii_sc_locked(struct axen_softc *sc) +{ + KASSERT(mutex_owned(&sc->axen_lock)); + sc->axen_refcnt++; - rw_enter(&sc->axen_mii_lock, RW_WRITER); + mutex_enter(&sc->axen_mii_lock); } static void axen_unlock_mii(struct axen_softc *sc) { - rw_exit(&sc->axen_mii_lock); + mutex_exit(&sc->axen_mii_lock); + mutex_enter(&sc->axen_lock); if (--sc->axen_refcnt < 0) - usb_detach_wakeupold(sc->axen_dev); + cv_broadcast(&sc->axen_detachcv); + mutex_exit(&sc->axen_lock); +} + +static void +axen_unlock_mii_sc_locked(struct axen_softc *sc) +{ + KASSERT(mutex_owned(&sc->axen_lock)); + + mutex_exit(&sc->axen_mii_lock); + if (--sc->axen_refcnt < 0) + cv_broadcast(&sc->axen_detachcv); } static int @@ -213,7 +240,7 @@ axen_cmd(struct axen_softc *sc, int cmd, usb_device_request_t req; usbd_status err; - KASSERT(rw_lock_held(&sc->axen_mii_lock)); + KASSERT(mutex_owned(&sc->axen_mii_lock)); if (sc->axen_dying) return 0; @@ -232,7 +259,7 @@ axen_cmd(struct axen_softc *sc, int cmd, cmd, val, AXEN_CMD_LEN(cmd))); if (err) { - DPRINTF(("axen_cmd err: cmd: %d, error: %d\n", cmd, err)); + DPRINTF(("%s: cmd: %d, error: %d\n", __func__, cmd, err)); return -1; } @@ -242,24 +269,23 @@ axen_cmd(struct axen_softc *sc, int cmd, static int axen_miibus_readreg(device_t dev, int phy, int reg, uint16_t *val) { - struct axen_softc *sc = device_private(dev); + struct axen_softc * const sc = device_private(dev); usbd_status err; uint16_t data; - if (sc->axen_dying) { - DPRINTF(("axen: dying\n")); + mutex_enter(&sc->axen_lock); + if (sc->axen_dying || sc->axen_phyno != phy) { + mutex_exit(&sc->axen_lock); return -1; } - - if (sc->axen_phyno != phy) - return -1; + mutex_exit(&sc->axen_lock); axen_lock_mii(sc); err = axen_cmd(sc, AXEN_CMD_MII_READ_REG, reg, phy, &data); axen_unlock_mii(sc); if (err) { - aprint_error_dev(sc->axen_dev, "read PHY failed\n"); + aprint_error_dev(sc->axen_dev, "read PHY failed: %d\n", err); return err; } @@ -277,25 +303,28 @@ axen_miibus_readreg(device_t dev, int ph static int axen_miibus_writereg(device_t dev, int phy, int reg, uint16_t val) { - struct axen_softc *sc = device_private(dev); + struct axen_softc * const sc = device_private(dev); usbd_status err; uint16_t uval; - if (sc->axen_dying) - return -1; - - if (sc->axen_phyno != phy) + mutex_enter(&sc->axen_lock); + if (sc->axen_dying || sc->axen_phyno != phy) { + mutex_exit(&sc->axen_lock); return -1; + } + mutex_exit(&sc->axen_lock); uval = htole16(val); + axen_lock_mii(sc); err = axen_cmd(sc, AXEN_CMD_MII_WRITE_REG, reg, phy, &uval); axen_unlock_mii(sc); + DPRINTFN(2, ("axen_miibus_writereg: phy 0x%x reg 0x%x val 0x%04hx\n", phy, reg, val)); if (err) { - aprint_error_dev(sc->axen_dev, "write PHY failed\n"); + aprint_error_dev(sc->axen_dev, "write PHY failed: %d\n", err); return err; } @@ -305,12 +334,15 @@ axen_miibus_writereg(device_t dev, int p static void axen_miibus_statchg(struct ifnet *ifp) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); int err; uint16_t val; uint16_t wval; + if (sc->axen_dying) + return; + sc->axen_link = 0; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { @@ -349,7 +381,7 @@ axen_miibus_statchg(struct ifnet *ifp) break; } - DPRINTF(("axen_miibus_statchg: val=0x%x\n", val)); + DPRINTF(("%s: val=0x%x\n", __func__, val)); wval = htole16(val); axen_lock_mii(sc); err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval); @@ -366,7 +398,7 @@ axen_miibus_statchg(struct ifnet *ifp) static int axen_ifmedia_upd(struct ifnet *ifp) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); int rc; @@ -390,7 +422,7 @@ axen_ifmedia_upd(struct ifnet *ifp) static void axen_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); mii_pollstat(mii); @@ -399,7 +431,7 @@ axen_ifmedia_sts(struct ifnet *ifp, stru } static void -axen_iff(struct axen_softc *sc) +axen_iff(struct axen_softc *sc, bool take_lock) { struct ifnet *ifp = GET_IFP(sc); struct ethercom *ec = &sc->axen_ec; @@ -413,10 +445,15 @@ axen_iff(struct axen_softc *sc) if (sc->axen_dying) return; + KASSERT(take_lock || mutex_owned(&sc->axen_lock)); + rxmode = 0; /* Enable receiver, set RX mode */ - axen_lock_mii(sc); + if (take_lock) + axen_lock_mii(sc); + else + axen_lock_mii_sc_locked(sc); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); rxmode = le16toh(wval); rxmode &= ~(AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_PROMISC | @@ -459,13 +496,17 @@ allmulti: ifp->if_flags |= IFF_ALLMULTI; axen_cmd(sc, AXEN_CMD_MAC_WRITE_FILTER, 8, AXEN_FILTER_MULTI, hashtbl); wval = htole16(rxmode); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); - axen_unlock_mii(sc); + if (take_lock) + axen_unlock_mii(sc); + else + axen_unlock_mii_sc_locked(sc); } static void axen_reset(struct axen_softc *sc) { + KASSERT(mutex_owned(&sc->axen_lock)); if (sc->axen_dying) return; /* XXX What to reset? */ @@ -474,15 +515,18 @@ axen_reset(struct axen_softc *sc) DELAY(1000); } -#if 0 /* not used */ #define AXEN_GPIO_WRITE(x, y) do { \ axen_cmd(sc, AXEN_CMD_WRITE_GPIO, 0, (x), NULL); \ usbd_delay_ms(sc->axen_udev, (y)); \ } while (/*CONSTCOND*/0) static int -axen_ax88179_eeprom(struct axen_softc *sc, void *addr) +axen_get_eaddr(struct axen_softc *sc, void *addr) { +#if 1 + return axen_cmd(sc, AXEN_CMD_MAC_READ_ETHER, 6, AXEN_CMD_MAC_NODE_ID, + addr); +#else int i, retry; uint8_t eeprom[20]; uint16_t csum; @@ -528,8 +572,8 @@ axen_ax88179_eeprom(struct axen_softc *s memcpy(addr, eeprom, ETHER_ADDR_LEN); return 0; -} #endif +} static void axen_ax88179_init(struct axen_softc *sc) @@ -695,7 +739,7 @@ axen_setcoe(struct axen_softc *sc) uint64_t enabled = ifp->if_capenable; uint8_t val; - axen_lock_mii(sc); + KASSERT(mutex_owned(&sc->axen_mii_lock)); val = AXEN_RXCOE_OFF; if (enabled & IFCAP_CSUM_IPv4_Rx) @@ -722,8 +766,6 @@ axen_setcoe(struct axen_softc *sc) if (enabled & IFCAP_CSUM_UDPv6_Tx) val |= AXEN_TXCOE_UDPv6; axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_TX_COE, &val); - - axen_unlock_mii(sc); } static int @@ -738,7 +780,7 @@ axen_match(device_t parent, cfdata_t mat static void axen_attach(device_t parent, device_t self, void *aux) { - struct axen_softc *sc = device_private(self); + struct axen_softc * const sc = device_private(self); struct usb_attach_arg *uaa = aux; struct usbd_device *dev = uaa->uaa_device; usbd_status err; @@ -770,7 +812,6 @@ axen_attach(device_t parent, device_t se sc->axen_flags = axen_lookup(uaa->uaa_vendor, uaa->uaa_product)->axen_flags; - rw_init(&sc->axen_mii_lock); usb_init_task(&sc->axen_tick_task, axen_tick_task, sc, 0); err = usbd_device2interface_handle(dev, AXEN_IFACE_IDX,&sc->axen_iface); @@ -820,24 +861,32 @@ axen_attach(device_t parent, device_t se } } + /* Set these up now for axen_cmd(). */ + mutex_init(&sc->axen_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->axen_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&sc->axen_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&sc->axen_mii_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&sc->axen_detachcv, "axendet"); + s = splnet(); sc->axen_phyno = AXEN_PHY_ID; DPRINTF(("%s: phyno %d\n", device_xname(self), sc->axen_phyno)); - /* - * Get station address. - */ -#if 0 /* read from eeprom */ - if (axen_ax88179_eeprom(sc, &eaddr)) { + /* Get station address. */ + axen_lock_mii(sc); + if (axen_get_eaddr(sc, &eaddr)) { + axen_unlock_mii(sc); printf("EEPROM checksum error\n"); + cv_destroy(&sc->axen_detachcv); + mutex_destroy(&sc->axen_lock); + mutex_destroy(&sc->axen_rxlock); + mutex_destroy(&sc->axen_txlock); + mutex_destroy(&sc->axen_mii_lock); return; } -#else /* use MAC command */ - axen_lock_mii(sc); - axen_cmd(sc, AXEN_CMD_MAC_READ_ETHER, 6, AXEN_CMD_MAC_NODE_ID, &eaddr); axen_unlock_mii(sc); -#endif + axen_ax88179_init(sc); /* @@ -912,10 +961,14 @@ axen_attach(device_t parent, device_t se static int axen_detach(device_t self, int flags) { - struct axen_softc *sc = device_private(self); + struct axen_softc * const sc = device_private(self); struct ifnet *ifp = GET_IFP(sc); int s; + mutex_enter(&sc->axen_lock); + sc->axen_dying = true; + mutex_exit(&sc->axen_lock); + DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); /* Detached before attached finished, so just bail out. */ @@ -924,23 +977,21 @@ axen_detach(device_t self, int flags) pmf_device_deregister(self); - sc->axen_dying = true; - callout_halt(&sc->axen_stat_ch, NULL); usb_rem_task_wait(sc->axen_udev, &sc->axen_tick_task, USB_TASKQ_DRIVER, NULL); - s = splusb(); - if (ifp->if_flags & IFF_RUNNING) axen_stop(ifp, 1); - callout_destroy(&sc->axen_stat_ch); - rnd_detach_source(&sc->rnd_source); - mii_detach(&sc->axen_mii, MII_PHY_ANY, MII_OFFSET_ANY); - ifmedia_delete_instance(&sc->axen_mii.mii_media, IFM_INST_ANY); - ether_ifdetach(ifp); - if_detach(ifp); + s = splusb(); + + mutex_enter(&sc->axen_lock); + sc->axen_refcnt--; + while (sc->axen_refcnt > 0) { + /* Wait for processes to go away */ + cv_wait(&sc->axen_detachcv, &sc->axen_lock); + } #ifdef DIAGNOSTIC if (sc->axen_ep[AXEN_ENDPT_TX] != NULL || @@ -949,17 +1000,26 @@ axen_detach(device_t self, int flags) aprint_debug_dev(self, "detach has active endpoints\n"); #endif + mutex_exit(&sc->axen_lock); + + callout_destroy(&sc->axen_stat_ch); + rnd_detach_source(&sc->rnd_source); + mii_detach(&sc->axen_mii, MII_PHY_ANY, MII_OFFSET_ANY); + ifmedia_delete_instance(&sc->axen_mii.mii_media, IFM_INST_ANY); + ether_ifdetach(ifp); + if_detach(ifp); + sc->axen_attached = false; - if (--sc->axen_refcnt >= 0) { - /* Wait for processes to go away. */ - usb_detach_waitold(sc->axen_dev); - } splx(s); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axen_udev,sc->axen_dev); - rw_destroy(&sc->axen_mii_lock); + cv_destroy(&sc->axen_detachcv); + mutex_destroy(&sc->axen_lock); + mutex_destroy(&sc->axen_mii_lock); + mutex_destroy(&sc->axen_rxlock); + mutex_destroy(&sc->axen_txlock); return 0; } @@ -967,7 +1027,7 @@ axen_detach(device_t self, int flags) static int axen_activate(device_t self, devact_t act) { - struct axen_softc *sc = device_private(self); + struct axen_softc * const sc = device_private(self); struct ifnet *ifp = GET_IFP(sc); DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); @@ -975,7 +1035,17 @@ axen_activate(device_t self, devact_t ac switch (act) { case DVACT_DEACTIVATE: if_deactivate(ifp); + + mutex_enter(&sc->axen_lock); sc->axen_dying = true; + mutex_exit(&sc->axen_lock); + + mutex_enter(&sc->axen_rxlock); + mutex_enter(&sc->axen_txlock); + sc->axen_stopping = true; + mutex_exit(&sc->axen_txlock); + mutex_exit(&sc->axen_rxlock); + return 0; default: return EOPNOTSUPP; @@ -1064,7 +1134,7 @@ static void axen_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status) { struct axen_chain *c = (struct axen_chain *)priv; - struct axen_softc *sc = c->axen_sc; + struct axen_softc * const sc = c->axen_sc; struct ifnet *ifp = GET_IFP(sc); uint8_t *buf = c->axen_buf; struct mbuf *m; @@ -1078,17 +1148,16 @@ axen_rxeof(struct usbd_xfer *xfer, void DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axen_dev), __func__)); - if (sc->axen_dying) - return; + mutex_enter(&sc->axen_rxlock); - if (!(ifp->if_flags & IFF_RUNNING)) + if (sc->axen_dying || sc->axen_stopping || + status == USBD_INVAL || status == USBD_NOT_STARTED || + status == USBD_CANCELLED || !(ifp->if_flags & IFF_RUNNING)) { + mutex_exit(&sc->axen_rxlock); return; + } if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_INVAL) - return; /* XXX plugged out or down */ - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) - return; if (usbd_ratecheck(&sc->axen_rx_notice)) aprint_error_dev(sc->axen_dev, "usb errors on rx: %s\n", usbd_errstr(status)); @@ -1184,11 +1253,19 @@ axen_rxeof(struct usbd_xfer *xfer, void m->m_pkthdr.csum_flags = axen_csum_flags_rx(ifp, pkt_hdr); memcpy(mtod(m, char *), buf + 2, pkt_len - 6); + mutex_exit(&sc->axen_rxlock); + /* push the packet up */ s = splnet(); if_percpuq_enqueue((ifp)->if_percpuq, (m)); splx(s); + mutex_enter(&sc->axen_rxlock); + if (sc->axen_stopping) { + mutex_exit(&sc->axen_rxlock); + return; + } + nextpkt: /* * prepare next packet @@ -1202,6 +1279,8 @@ nextpkt: } while(pkt_count > 0); done: + mutex_exit(&sc->axen_rxlock); + /* Setup new transfer. */ usbd_setup_xfer(xfer, c, c->axen_buf, sc->axen_rx_bufsz, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axen_rxeof); @@ -1263,87 +1342,112 @@ static void axen_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status) { struct axen_chain *c = (struct axen_chain *)priv; - struct axen_softc *sc = c->axen_sc; + struct axen_softc * const sc = c->axen_sc; struct axen_cdata *cd = &sc->axen_cdata; struct ifnet *ifp = GET_IFP(sc); int s; - if (sc->axen_dying) + mutex_enter(&sc->axen_txlock); + if (sc->axen_stopping || sc->axen_dying) { + mutex_exit(&sc->axen_txlock); return; + } s = splnet(); + KASSERT(cd->axen_tx_cnt > 0); cd->axen_tx_cnt--; - if (status != USBD_NORMAL_COMPLETION) { - if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { - splx(s); - return; - } + + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + + switch (status) { + case USBD_NOT_STARTED: + case USBD_CANCELLED: + break; + + case USBD_NORMAL_COMPLETION: + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + axen_start_locked(ifp); + break; + + default: + ifp->if_opackets++; + ifp->if_oerrors++; if (usbd_ratecheck(&sc->axen_tx_notice)) aprint_error_dev(sc->axen_dev, "usb error on tx: %s\n", usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->axen_ep[AXEN_ENDPT_TX]); - splx(s); - return; + break; } - ifp->if_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; - - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - axen_start(ifp); - - ifp->if_opackets++; splx(s); + mutex_exit(&sc->axen_txlock); } static void axen_tick(void *xsc) { - struct axen_softc *sc = xsc; + struct axen_softc * const sc = xsc; if (sc == NULL) return; + mutex_enter(&sc->axen_lock); + DPRINTFN(0xff,("%s: %s: enter\n", device_xname(sc->axen_dev),__func__)); - if (sc->axen_dying) + if (sc->axen_dying) { + mutex_exit(&sc->axen_lock); return; + } /* Perform periodic stuff in process context */ usb_add_task(sc->axen_udev, &sc->axen_tick_task, USB_TASKQ_DRIVER); + + mutex_exit(&sc->axen_lock); } static void axen_tick_task(void *xsc) { - int s; - struct axen_softc *sc; + struct axen_softc * const sc = xsc; struct ifnet *ifp; struct mii_data *mii; - - sc = xsc; + int s; if (sc == NULL) return; - if (sc->axen_dying) + mutex_enter(&sc->axen_lock); + if (sc->axen_dying) { + mutex_exit(&sc->axen_lock); return; + } ifp = GET_IFP(sc); mii = GET_MII(sc); if (mii == NULL) return; + sc->axen_refcnt++; + mutex_exit(&sc->axen_lock); + s = splnet(); mii_tick(mii); + if (sc->axen_link == 0) axen_miibus_statchg(ifp); - callout_schedule(&sc->axen_stat_ch, hz); + mutex_enter(&sc->axen_lock); + if (--sc->axen_refcnt < 0) + cv_broadcast(&sc->axen_detachcv); + if (!sc->axen_dying) + callout_schedule(&sc->axen_stat_ch, hz); + mutex_exit(&sc->axen_lock); splx(s); } @@ -1357,9 +1461,11 @@ axen_encap(struct axen_softc *sc, struct struct axen_sframe_hdr hdr; u_int length, boundary; + KASSERT(mutex_owned(&sc->axen_txlock)); + c = &sc->axen_cdata.axen_tx_chain[idx]; - /* XXX Is this need? */ + /* XXX Is this needed? wMaxPacketSize? */ switch (sc->axen_udev->ud_speed) { case USB_SPEED_SUPER: boundary = 4096; @@ -1405,13 +1511,15 @@ axen_encap(struct axen_softc *sc, struct } static void -axen_start(struct ifnet *ifp) +axen_start_locked(struct ifnet *ifp) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; struct mbuf *m; struct axen_cdata *cd = &sc->axen_cdata; int idx; + KASSERT(mutex_owned(&sc->axen_txlock)); + if (sc->axen_link == 0) return; @@ -1452,10 +1560,21 @@ axen_start(struct ifnet *ifp) ifp->if_timer = 5; } +static void +axen_start(struct ifnet *ifp) +{ + struct axen_softc * const sc = ifp->if_softc; + + mutex_enter(&sc->axen_txlock); + if (!sc->axen_stopping) + axen_start_locked(ifp); + mutex_exit(&sc->axen_txlock); +} + static int -axen_init(struct ifnet *ifp) +axen_init_locked(struct ifnet *ifp) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; struct axen_chain *c; usbd_status err; int i, s; @@ -1463,36 +1582,39 @@ axen_init(struct ifnet *ifp) uint16_t wval; uint8_t bval; + KASSERT(mutex_owned(&sc->axen_lock)); + + if (sc->axen_dying) + return EIO; + s = splnet(); - if (ifp->if_flags & IFF_RUNNING) - axen_stop(ifp, 0); + /* Cancel pending I/O */ + axen_stop_locked(ifp, 1); - /* - * Cancel pending I/O and free all RX/TX buffers. - */ + /* Reset the ethernet interface. */ axen_reset(sc); /* XXX: ? */ - axen_lock_mii(sc); + axen_lock_mii_sc_locked(sc); bval = 0x01; axen_cmd(sc, AXEN_CMD_MAC_WRITE, 1, AXEN_UNK_28, &bval); - axen_unlock_mii(sc); /* Configure offloading engine. */ axen_setcoe(sc); + axen_unlock_mii_sc_locked(sc); /* Program promiscuous mode and multicast filters. */ - axen_iff(sc); + axen_iff(sc, false); /* Enable receiver, set RX mode */ - axen_lock_mii(sc); + axen_lock_mii_sc_locked(sc); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); rxmode = le16toh(wval); rxmode |= AXEN_RXCTL_START; wval = htole16(rxmode); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); - axen_unlock_mii(sc); + axen_unlock_mii_sc_locked(sc); /* Open RX and TX pipes. */ err = usbd_open_pipe(sc->axen_iface, sc->axen_ed[AXEN_ENDPT_RX], @@ -1527,6 +1649,10 @@ axen_init(struct ifnet *ifp) return ENOBUFS; } + mutex_enter(&sc->axen_rxlock); + mutex_enter(&sc->axen_txlock); + sc->axen_stopping = false; + /* Start up the receive pipe. */ for (i = 0; i < AXEN_RX_LIST_CNT; i++) { c = &sc->axen_cdata.axen_rx_chain[i]; @@ -1536,6 +1662,10 @@ axen_init(struct ifnet *ifp) usbd_transfer(c->axen_xfer); } + mutex_exit(&sc->axen_txlock); + mutex_exit(&sc->axen_rxlock); + + /* Indicate we are up and running. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; @@ -1546,9 +1676,21 @@ axen_init(struct ifnet *ifp) } static int +axen_init(struct ifnet *ifp) +{ + struct axen_softc * const sc = ifp->if_softc; + + mutex_enter(&sc->axen_lock); + int ret = axen_init_locked(ifp); + mutex_exit(&sc->axen_lock); + + return ret; +} + +static int axen_ioctl(struct ifnet *ifp, u_long cmd, void *data) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; int s; int error = 0; @@ -1568,12 +1710,17 @@ axen_ioctl(struct ifnet *ifp, u_long cmd break; case IFF_UP | IFF_RUNNING: if ((ifp->if_flags ^ sc->axen_if_flags) == IFF_PROMISC) - axen_iff(sc); + axen_iff(sc, true); else axen_init(ifp); break; } + + mutex_enter(&sc->axen_rxlock); + mutex_enter(&sc->axen_txlock); sc->axen_if_flags = ifp->if_flags; + mutex_exit(&sc->axen_txlock); + mutex_exit(&sc->axen_rxlock); break; default: @@ -1584,10 +1731,12 @@ axen_ioctl(struct ifnet *ifp, u_long cmd switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: - axen_iff(sc); + axen_iff(sc, true); break; case SIOCSIFCAP: + mutex_enter(&sc->axen_lock); axen_setcoe(sc); + mutex_exit(&sc->axen_lock); break; default: break; @@ -1602,13 +1751,11 @@ axen_ioctl(struct ifnet *ifp, u_long cmd static void axen_watchdog(struct ifnet *ifp) { - struct axen_softc *sc; + struct axen_softc * const sc = ifp->if_softc; struct axen_chain *c; usbd_status stat; int s; - sc = ifp->if_softc; - ifp->if_oerrors++; aprint_error_dev(sc->axen_dev, "watchdog timeout\n"); @@ -1627,24 +1774,31 @@ axen_watchdog(struct ifnet *ifp) * RX and TX lists. */ static void -axen_stop(struct ifnet *ifp, int disable) +axen_stop_locked(struct ifnet *ifp, int disable) { - struct axen_softc *sc = ifp->if_softc; + struct axen_softc * const sc = ifp->if_softc; struct axen_chain *c; usbd_status err; int i; uint16_t rxmode, wval; + KASSERT(mutex_owned(&sc->axen_lock)); + mutex_enter(&sc->axen_rxlock); + mutex_enter(&sc->axen_txlock); + sc->axen_stopping = true; + mutex_exit(&sc->axen_txlock); + mutex_exit(&sc->axen_rxlock); + axen_reset(sc); /* Disable receiver, set RX mode */ - axen_lock_mii(sc); + axen_lock_mii_sc_locked(sc); axen_cmd(sc, AXEN_CMD_MAC_READ2, 2, AXEN_MAC_RXCTL, &wval); rxmode = le16toh(wval); rxmode &= ~AXEN_RXCTL_START; wval = htole16(rxmode); axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MAC_RXCTL, &wval); - axen_unlock_mii(sc); + axen_unlock_mii_sc_locked(sc); ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); @@ -1725,6 +1879,16 @@ axen_stop(struct ifnet *ifp, int disable } } +static void +axen_stop(struct ifnet *ifp, int disable) +{ + struct axen_softc * const sc = ifp->if_softc; + + mutex_enter(&sc->axen_lock); + axen_stop_locked(ifp, disable); + mutex_exit(&sc->axen_lock); +} + MODULE(MODULE_CLASS_DRIVER, if_axen, NULL); #ifdef _MODULE