diff --git a/share/man/man9/usbnet.9 b/share/man/man9/usbnet.9 index c23157073660..21e3ff59f3d6 100644 --- a/share/man/man9/usbnet.9 +++ b/share/man/man9/usbnet.9 @@ -47,6 +47,8 @@ .Fn usbnet_softc "struct usbnet *un" .Ft bool .Fn usbnet_havelink "struct usbnet *un" +.Ft int +.Fn usbnet_ispromisc "struct usbnet *un" .Ft bool .Fn usbnet_isdying "struct usbnet *un" .Ft void @@ -243,6 +245,20 @@ Returns pointer to this device softc. .It Fn usbnet_havelink un Returns true if link is active. +.It Fn usbnet_ispromisc un +True if +.Dv IFF_PROMISC +is enabled, false if not. +.Pp +May be used only in +.Dq uno_init +and +.Dq uno_mcast . +.Pp +Drivers must use this in +.Dq uno_mcast +instead of reading +.Li ifp->if_flags . .It Fn usbnet_isdying un Returns true if device is dying (has been pulled or deactivated, pending detach). diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c index 57b06a472546..355be8a7ca8a 100644 --- a/sys/dev/usb/if_aue.c +++ b/sys/dev/usb/if_aue.c @@ -619,7 +619,7 @@ aue_uno_mcast(struct ifnet *ifp) AUEHIST_FUNC(); AUEHIST_CALLARGSN(5, "aue%jd: enter", device_unit(un->un_dev), 0, 0, 0); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ETHER_LOCK(ec); allmulti: ec->ec_flags |= ETHER_F_ALLMULTI; @@ -962,7 +962,7 @@ aue_uno_init(struct ifnet *ifp) aue_csr_write_1(sc, AUE_PAR0 + i, eaddr[i]); /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) + if (usbnet_ispromisc(un)) AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); else AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); diff --git a/sys/dev/usb/if_axe.c b/sys/dev/usb/if_axe.c index 9b0b76d05151..d55cda1c0d47 100644 --- a/sys/dev/usb/if_axe.c +++ b/sys/dev/usb/if_axe.c @@ -452,7 +452,7 @@ axe_uno_mcast(struct ifnet *ifp) ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC | AXE_RXCMD_MULTICAST); ETHER_LOCK(ec); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ec->ec_flags |= ETHER_F_ALLMULTI; ETHER_UNLOCK(ec); /* run promisc. mode */ diff --git a/sys/dev/usb/if_axen.c b/sys/dev/usb/if_axen.c index 4ab976dd1cfa..b02a641af9b0 100644 --- a/sys/dev/usb/if_axen.c +++ b/sys/dev/usb/if_axen.c @@ -249,7 +249,7 @@ axen_uno_mcast(struct ifnet *ifp) rxmode &= ~(AXEN_RXCTL_ACPT_ALL_MCAST | AXEN_RXCTL_PROMISC | AXEN_RXCTL_ACPT_MCAST); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { DPRINTF(("%s: promisc\n", device_xname(un->un_dev))); rxmode |= AXEN_RXCTL_PROMISC; allmulti: diff --git a/sys/dev/usb/if_cue.c b/sys/dev/usb/if_cue.c index e46981457228..7cef84486570 100644 --- a/sys/dev/usb/if_cue.c +++ b/sys/dev/usb/if_cue.c @@ -366,10 +366,10 @@ cue_uno_mcast(struct ifnet *ifp) struct ether_multistep step; uint32_t h, i; - DPRINTFN(2,("%s: cue_setiff if_flags=%#x\n", - device_xname(un->un_dev), ifp->if_flags)); + DPRINTFN(2,("%s: cue_setiff promisc=%d\n", + device_xname(un->un_dev), usbnet_ispromisc(un))); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ETHER_LOCK(ec); allmulti: ec->ec_flags |= ETHER_F_ALLMULTI; @@ -405,10 +405,8 @@ allmulti: * Also include the broadcast address in the filter * so we can receive broadcast frames. */ - if (ifp->if_flags & IFF_BROADCAST) { - h = cue_crc(etherbroadcastaddr); - sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); - } + h = cue_crc(etherbroadcastaddr); + sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); cue_mem(un, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &sc->cue_mctab, CUE_MCAST_TABLE_LEN); @@ -636,7 +634,7 @@ cue_uno_init(struct ifnet *ifp) /* Enable RX logic. */ ctl = CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON; - if (ifp->if_flags & IFF_PROMISC) + if (usbnet_ispromisc(un)) ctl |= CUE_ETHCTL_PROMISC; cue_csr_write_1(un, CUE_ETHCTL, ctl); diff --git a/sys/dev/usb/if_kue.c b/sys/dev/usb/if_kue.c index 50411164221e..fa441f45b62b 100644 --- a/sys/dev/usb/if_kue.c +++ b/sys/dev/usb/if_kue.c @@ -330,12 +330,12 @@ kue_uno_mcast(struct ifnet *ifp) DPRINTFN(5,("%s: %s: enter\n", device_xname(un->un_dev), __func__)); /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) + if (usbnet_ispromisc(un)) sc->kue_rxfilt |= KUE_RXFILT_PROMISC; else sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC; - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ETHER_LOCK(ec); allmulti: ec->ec_flags |= ETHER_F_ALLMULTI; diff --git a/sys/dev/usb/if_mos.c b/sys/dev/usb/if_mos.c index 2f64a19ffcf9..c9e6875dcc63 100644 --- a/sys/dev/usb/if_mos.c +++ b/sys/dev/usb/if_mos.c @@ -477,7 +477,7 @@ mos_uno_mcast(struct ifnet *ifp) rxmode &= ~(MOS_CTL_ALLMULTI | MOS_CTL_RX_PROMISC); ETHER_LOCK(ec); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ec->ec_flags |= ETHER_F_ALLMULTI; ETHER_UNLOCK(ec); /* run promisc. mode */ diff --git a/sys/dev/usb/if_mue.c b/sys/dev/usb/if_mue.c index e19576151ae5..a0bf825e8457 100644 --- a/sys/dev/usb/if_mue.c +++ b/sys/dev/usb/if_mue.c @@ -1028,11 +1028,11 @@ mue_uno_mcast(struct ifnet *ifp) rxfilt |= MUE_RFE_CTL_BROADCAST; ETHER_LOCK(ec); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { rxfilt |= MUE_RFE_CTL_UNICAST; allmulti: rxfilt |= MUE_RFE_CTL_MULTICAST; ec->ec_flags |= ETHER_F_ALLMULTI; - if (ifp->if_flags & IFF_PROMISC) + if (usbnet_ispromisc(un)) DPRINTF(un, "promisc\n"); else DPRINTF(un, "allmulti\n"); diff --git a/sys/dev/usb/if_smsc.c b/sys/dev/usb/if_smsc.c index 305781e53517..74c2b57669dd 100644 --- a/sys/dev/usb/if_smsc.c +++ b/sys/dev/usb/if_smsc.c @@ -425,7 +425,7 @@ smsc_uno_mcast(struct ifnet *ifp) if (usbnet_isdying(un)) return; - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ETHER_LOCK(ec); allmulti: ec->ec_flags |= ETHER_F_ALLMULTI; diff --git a/sys/dev/usb/if_udav.c b/sys/dev/usb/if_udav.c index add37bc874ac..cc6389458dbc 100644 --- a/sys/dev/usb/if_udav.c +++ b/sys/dev/usb/if_udav.c @@ -482,7 +482,7 @@ udav_uno_init(struct ifnet *ifp) UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); /* If we want promiscuous mode, accept all physical frames. */ - if (ifp->if_flags & IFF_PROMISC) + if (usbnet_ispromisc(un)) UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC); else UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC); @@ -576,20 +576,12 @@ udav_uno_mcast(struct ifnet *ifp) return; } - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ETHER_LOCK(ec); ec->ec_flags |= ETHER_F_ALLMULTI; ETHER_UNLOCK(ec); UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL | UDAV_RCR_PRMSC); return; - } else if (ifp->if_flags & IFF_ALLMULTI) { /* XXX ??? Can't happen? */ - ETHER_LOCK(ec); -allmulti: - ec->ec_flags |= ETHER_F_ALLMULTI; - ETHER_UNLOCK(ec); - UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL); - UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_PRMSC); - return; } /* first, zot all the existing hash bits */ @@ -603,7 +595,11 @@ allmulti: while (enm != NULL) { if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) != 0) { - goto allmulti; + ec->ec_flags |= ETHER_F_ALLMULTI; + ETHER_UNLOCK(ec); + UDAV_SETBIT(un, UDAV_RCR, UDAV_RCR_ALL); + UDAV_CLRBIT(un, UDAV_RCR, UDAV_RCR_PRMSC); + return; } h = UDAV_CALCHASH(enm->enm_addrlo); diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c index f05d5b4f75d0..42d359db0d1f 100644 --- a/sys/dev/usb/if_ure.c +++ b/sys/dev/usb/if_ure.c @@ -352,7 +352,7 @@ ure_uno_mcast(struct ifnet *ifp) /* continue to accept my own DA and bcast frames */ ETHER_LOCK(ec); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ec->ec_flags |= ETHER_F_ALLMULTI; ETHER_UNLOCK(ec); /* run promisc. mode */ diff --git a/sys/dev/usb/if_url.c b/sys/dev/usb/if_url.c index 7a51a59f75cf..36624a9f8f1c 100644 --- a/sys/dev/usb/if_url.c +++ b/sys/dev/usb/if_url.c @@ -433,7 +433,7 @@ url_uno_mcast(struct ifnet *ifp) rcr &= ~(URL_RCR_AAP | URL_RCR_AAM | URL_RCR_AM); ETHER_LOCK(ec); - if (ifp->if_flags & IFF_PROMISC) { + if (usbnet_ispromisc(un)) { ec->ec_flags |= ETHER_F_ALLMULTI; ETHER_UNLOCK(ec); /* run promisc. mode */ diff --git a/sys/dev/usb/usbnet.c b/sys/dev/usb/usbnet.c index 3c86c7092d32..8724875a27fd 100644 --- a/sys/dev/usb/usbnet.c +++ b/sys/dev/usb/usbnet.c @@ -52,18 +52,17 @@ struct usbnet_cdata { struct usbnet_private { /* - * - unp_core_lock protects most of this structure, the public one, - * and the MII / media data. + * - unp_miilock protects the MII / media data and tick scheduling. * - unp_rxlock protects the rx path and its data * - unp_txlock protects the tx path and its data * * the lock ordering is: - * ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock - * -> unp_mcastlock - * - ifnet lock is not needed for unp_core_lock, but if ifnet lock is - * involved, it must be taken first + * ifnet lock -> unp_miilock + * -> unp_rxlock + * -> unp_txlock + * -> unp_mcastlock */ - kmutex_t unp_core_lock; + kmutex_t unp_miilock; kmutex_t unp_rxlock; kmutex_t unp_txlock; @@ -79,7 +78,9 @@ struct usbnet_private { struct usbd_pipe *unp_ep[USBNET_ENDPT_MAX]; volatile bool unp_dying; - bool unp_stopping; + bool unp_stopped; + bool unp_rxstopped; + bool unp_txstopped; bool unp_attached; bool unp_ifp_attached; bool unp_link; @@ -103,9 +104,9 @@ static void usbnet_isowned_rx(struct usbnet *); static void usbnet_isowned_tx(struct usbnet *); static inline void -usbnet_isowned_core(struct usbnet *un) +usbnet_isowned_mii(struct usbnet *un) { - KASSERT(mutex_owned(&un->un_pri->unp_core_lock)); + KASSERT(mutex_owned(&un->un_pri->unp_miilock)); } static int usbnet_modcmd(modcmd_t, void *); @@ -161,7 +162,7 @@ static void uno_stop(struct usbnet *un, struct ifnet *ifp, int disable) { KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname); - usbnet_isowned_core(un); + usbnet_isowned_mii(un); if (un->un_ops->uno_stop) (*un->un_ops->uno_stop)(ifp, disable); } @@ -204,21 +205,21 @@ uno_init(struct usbnet *un, struct ifnet *ifp) static int uno_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val) { - usbnet_isowned_core(un); + usbnet_isowned_mii(un); return (*un->un_ops->uno_read_reg)(un, phy, reg, val); } static int uno_write_reg(struct usbnet *un, int phy, int reg, uint16_t val) { - usbnet_isowned_core(un); + usbnet_isowned_mii(un); return (*un->un_ops->uno_write_reg)(un, phy, reg, val); } static void uno_mii_statchg(struct usbnet *un, struct ifnet *ifp) { - usbnet_isowned_core(un); + usbnet_isowned_mii(un); (*un->un_ops->uno_statchg)(ifp); } @@ -341,7 +342,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) mutex_enter(&unp->unp_rxlock); - if (usbnet_isdying(un) || unp->unp_stopping || + if (usbnet_isdying(un) || unp->unp_rxstopped || status == USBD_INVAL || status == USBD_NOT_STARTED || status == USBD_CANCELLED) goto out; @@ -368,7 +369,7 @@ usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) usbnet_isowned_rx(un); done: - if (usbnet_isdying(un) || unp->unp_stopping) + if (usbnet_isdying(un) || unp->unp_rxstopped) goto out; mutex_exit(&unp->unp_rxlock); @@ -397,7 +398,7 @@ usbnet_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status) unp->unp_number, status, (uintptr_t)xfer, 0); mutex_enter(&unp->unp_txlock); - if (unp->unp_stopping || usbnet_isdying(un)) { + if (unp->unp_txstopped || usbnet_isdying(un)) { mutex_exit(&unp->unp_txlock); return; } @@ -439,14 +440,14 @@ usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) USBNETHIST_FUNC(); struct usbnet * const un = priv; struct usbnet_private * const unp = un->un_pri; - struct usbnet_intr * const uni = un->un_intr; + struct usbnet_intr * const uni __unused = un->un_intr; - if (uni == NULL || usbnet_isdying(un) || unp->unp_stopping || + if (usbnet_isdying(un) || status == USBD_INVAL || status == USBD_NOT_STARTED || status == USBD_CANCELLED) { - USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx", + USBNETHIST_CALLARGS("%jd: uni %#jx dying %#jx status %#jx", unp->unp_number, (uintptr_t)uni, - (usbnet_isdying(un) << 8) | unp->unp_stopping, status); + usbnet_isdying(un), status); return; } @@ -569,11 +570,11 @@ usbnet_if_start(struct ifnet *ifp) struct usbnet_private * const unp = un->un_pri; USBNETHIST_FUNC(); - USBNETHIST_CALLARGS("%jd: stopping %jd", - unp->unp_number, unp->unp_stopping, 0, 0); + USBNETHIST_CALLARGS("%jd: txstopped %jd", + unp->unp_number, unp->unp_txstopped, 0, 0); mutex_enter(&unp->unp_txlock); - if (!unp->unp_stopping) + if (!unp->unp_txstopped) usbnet_start_locked(ifp); mutex_exit(&unp->unp_txlock); } @@ -659,8 +660,8 @@ usbnet_rx_start_pipes(struct usbnet * const un) struct usbnet_private * const unp = un->un_pri; mutex_enter(&unp->unp_rxlock); - mutex_enter(&unp->unp_txlock); - unp->unp_stopping = false; + KASSERT(unp->unp_rxstopped); + unp->unp_rxstopped = false; for (size_t i = 0; i < un->un_rx_list_cnt; i++) { struct usbnet_chain *c = &cd->uncd_rx_chain[i]; @@ -670,7 +671,6 @@ usbnet_rx_start_pipes(struct usbnet * const un) usbd_transfer(c->unc_xfer); } - mutex_exit(&unp->unp_txlock); mutex_exit(&unp->unp_rxlock); } @@ -811,8 +811,6 @@ usbnet_init_rx_tx(struct usbnet * const un) KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname); - usbnet_isowned_core(un); - if (usbnet_isdying(un)) { return EIO; } @@ -855,10 +853,20 @@ usbnet_init_rx_tx(struct usbnet * const un) mutex_exit(&unp->unp_mcastlock); } + /* Allow transmit. */ + mutex_enter(&unp->unp_txlock); + KASSERT(unp->unp_txstopped); + unp->unp_txstopped = false; + mutex_exit(&unp->unp_txlock); + /* Start up the receive pipe(s). */ usbnet_rx_start_pipes(un); + /* Kick off the watchdog/stats/mii tick. */ + mutex_enter(&unp->unp_miilock); + unp->unp_stopped = false; callout_schedule(&unp->unp_stat_ch, hz); + mutex_exit(&unp->unp_miilock); out: if (error) { @@ -871,10 +879,11 @@ out: * For devices without any media autodetection, treat success * here as an active link. */ - if (un->un_ops->uno_statchg == NULL) + if (un->un_ops->uno_statchg == NULL) { + mutex_enter(&unp->unp_miilock); usbnet_set_link(un, error == 0); - - usbnet_isowned_core(un); + mutex_exit(&unp->unp_miilock); + } return error; } @@ -888,8 +897,8 @@ usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val) struct usbnet * const un = device_private(dev); int err; - /* MII layer ensures core_lock is held. */ - usbnet_isowned_core(un); + /* MII layer ensures miilock is held. */ + usbnet_isowned_mii(un); if (usbnet_isdying(un)) { return EIO; @@ -912,8 +921,8 @@ usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val) struct usbnet * const un = device_private(dev); int err; - /* MII layer ensures core_lock is held. */ - usbnet_isowned_core(un); + /* MII layer ensures miilock is held. */ + usbnet_isowned_mii(un); if (usbnet_isdying(un)) { return EIO; @@ -935,8 +944,8 @@ usbnet_mii_statchg(struct ifnet *ifp) USBNETHIST_FUNC(); USBNETHIST_CALLED(); struct usbnet * const un = ifp->if_softc; - /* MII layer ensures core_lock is held. */ - usbnet_isowned_core(un); + /* MII layer ensures miilock is held. */ + usbnet_isowned_mii(un); uno_mii_statchg(un, ifp); } @@ -949,8 +958,8 @@ usbnet_media_upd(struct ifnet *ifp) struct usbnet_private * const unp = un->un_pri; struct mii_data * const mii = usbnet_mii(un); - /* ifmedia layer ensures core_lock is held. */ - usbnet_isowned_core(un); + /* ifmedia layer ensures miilock is held. */ + usbnet_isowned_mii(un); /* ifmedia changes only with IFNET_LOCK held. */ KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname); @@ -985,8 +994,22 @@ usbnet_ifflags_cb(struct ethercom *ec) const u_short changed = ifp->if_flags ^ unp->unp_if_flags; if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) { + mutex_enter(&unp->unp_mcastlock); unp->unp_if_flags = ifp->if_flags; - if ((changed & IFF_PROMISC) != 0) + mutex_exit(&unp->unp_mcastlock); + /* + * XXX Can we just do uno_mcast synchronously here + * instead of resetting the whole interface? + * + * Not yet, because some usbnet drivers (e.g., aue(4)) + * initialize the hardware differently in uno_init + * depending on IFF_PROMISC. But some (again, aue(4)) + * _also_ need to know whether IFF_PROMISC is set in + * uno_mcast and do something different with it there. + * Maybe the logic can be unified, but it will require + * an audit and testing of all the usbnet drivers. + */ + if (changed & IFF_PROMISC) rv = ENETRESET; } else { rv = ENETRESET; @@ -995,6 +1018,18 @@ usbnet_ifflags_cb(struct ethercom *ec) return rv; } +bool +usbnet_ispromisc(struct usbnet *un) +{ + struct ifnet * const ifp = usbnet_ifp(un); + struct usbnet_private * const unp = un->un_pri; + + KASSERTMSG(mutex_owned(&unp->unp_mcastlock) || IFNET_LOCKED(ifp), + "%s", ifp->if_xname); + + return unp->unp_if_flags & IFF_PROMISC; +} + static int usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data) { @@ -1053,17 +1088,16 @@ static void usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable) { struct usbnet_private * const unp = un->un_pri; + struct mii_data * const mii = usbnet_mii(un); USBNETHIST_FUNC(); USBNETHIST_CALLED(); KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname); - usbnet_isowned_core(un); /* * For drivers with hardware multicast filter update callbacks: * Prevent concurrent access to the hardware registers by - * multicast filter updates, which happens without IFNET_LOCK - * or the usbnet core lock. + * multicast filter updates, which happens without IFNET_LOCK. */ if (un->un_ops->uno_mcast) { mutex_enter(&unp->unp_mcastlock); @@ -1075,22 +1109,38 @@ usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable) * Prevent new activity (rescheduling ticks, xfers, &c.) and * clear the watchdog timer. */ + mutex_enter(&unp->unp_miilock); + unp->unp_stopped = true; + mutex_exit(&unp->unp_miilock); + mutex_enter(&unp->unp_rxlock); + unp->unp_rxstopped = true; + mutex_exit(&unp->unp_rxlock); + mutex_enter(&unp->unp_txlock); - unp->unp_stopping = true; + unp->unp_txstopped = true; unp->unp_timer = 0; mutex_exit(&unp->unp_txlock); - mutex_exit(&unp->unp_rxlock); /* * Stop the timer first, then the task -- if the timer was * already firing, we stop the task or wait for it complete - * only after if last fired. Setting unp_stopping prevents the + * only after it last fired. Setting unp_stopped prevents the * timer task from being scheduled again. */ - callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock); + callout_halt(&unp->unp_stat_ch, NULL); usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER, - &unp->unp_core_lock); + NULL); + + /* + * Now that we have stopped calling mii_tick, bring the MII + * state machine down. + */ + if (mii) { + mutex_enter(&unp->unp_miilock); + mii_down(mii); + mutex_exit(&unp->unp_miilock); + } /* Stop transfers. */ usbnet_ep_stop_pipes(un); @@ -1124,7 +1174,6 @@ static void usbnet_if_stop(struct ifnet *ifp, int disable) { struct usbnet * const un = ifp->if_softc; - struct usbnet_private * const unp = un->un_pri; KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname); @@ -1138,9 +1187,7 @@ usbnet_if_stop(struct ifnet *ifp, int disable) if ((ifp->if_flags & IFF_RUNNING) == 0) return; - mutex_enter(&unp->unp_core_lock); usbnet_stop(un, ifp, disable); - mutex_exit(&unp->unp_core_lock); } /* @@ -1201,22 +1248,20 @@ usbnet_tick_task(void *arg) if (timeout) usbnet_watchdog(ifp); + /* Call driver if requested. */ + uno_tick(un); + + mutex_enter(&unp->unp_miilock); DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0); if (mii) { - mutex_enter(&unp->unp_core_lock); mii_tick(mii); if (!unp->unp_link) (*mii->mii_statchg)(ifp); - mutex_exit(&unp->unp_core_lock); } - /* Call driver if requested. */ - uno_tick(un); - - mutex_enter(&unp->unp_core_lock); - if (!unp->unp_stopping && !usbnet_isdying(un)) + if (!unp->unp_stopped && !usbnet_isdying(un)) callout_schedule(&unp->unp_stat_ch, hz); - mutex_exit(&unp->unp_core_lock); + mutex_exit(&unp->unp_miilock); } static int @@ -1245,16 +1290,14 @@ usbnet_if_init(struct ifnet *ifp) if (ifp->if_flags & IFF_RUNNING) return 0; - mutex_enter(&un->un_pri->unp_core_lock); error = uno_init(un, ifp); if (error) - goto out; + return error; error = usbnet_init_rx_tx(un); if (error) - goto out; -out: mutex_exit(&un->un_pri->unp_core_lock); + return error; - return error; + return 0; } @@ -1263,6 +1306,7 @@ out: mutex_exit(&un->un_pri->unp_core_lock); void usbnet_set_link(struct usbnet *un, bool link) { + usbnet_isowned_mii(un); un->un_pri->unp_link = link; } @@ -1372,7 +1416,7 @@ usbnet_attach(struct usbnet *un) mutex_init(&unp->unp_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); mutex_init(&unp->unp_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&unp->unp_core_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&unp->unp_miilock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&unp->unp_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK); rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev), @@ -1383,6 +1427,9 @@ usbnet_attach(struct usbnet *un) unp->unp_number = atomic_inc_uint_nv(&usbnet_number); + unp->unp_stopped = true; + unp->unp_rxstopped = true; + unp->unp_txstopped = true; unp->unp_attached = true; } @@ -1406,7 +1453,7 @@ usbnet_attach_mii(struct usbnet *un, const struct usbnet_mii *unm) usbnet_ec(un)->ec_mii = mii; ifmedia_init_with_lock(&mii->mii_media, 0, - usbnet_media_upd, ether_mediastatus, &unp->unp_core_lock); + usbnet_media_upd, ether_mediastatus, &unp->unp_miilock); mii_attach(un->un_dev, mii, unm->un_mii_capmask, unm->un_mii_phyloc, unm->un_mii_offset, unm->un_mii_flags); @@ -1535,7 +1582,7 @@ usbnet_detach(device_t self, int flags) rnd_detach_source(&unp->unp_rndsrc); mutex_destroy(&unp->unp_mcastlock); - mutex_destroy(&unp->unp_core_lock); + mutex_destroy(&unp->unp_miilock); mutex_destroy(&unp->unp_rxlock); mutex_destroy(&unp->unp_txlock); @@ -1572,11 +1619,17 @@ usbnet_activate(device_t self, devact_t act) atomic_store_relaxed(&unp->unp_dying, true); + mutex_enter(&unp->unp_miilock); + unp->unp_stopped = true; + mutex_exit(&unp->unp_miilock); + mutex_enter(&unp->unp_rxlock); + unp->unp_rxstopped = true; + mutex_exit(&unp->unp_rxlock); + mutex_enter(&unp->unp_txlock); - unp->unp_stopping = true; + unp->unp_txstopped = true; mutex_exit(&unp->unp_txlock); - mutex_exit(&unp->unp_rxlock); return 0; default: diff --git a/sys/dev/usb/usbnet.h b/sys/dev/usb/usbnet.h index 1ffd3bf86374..3c6dd5dd763a 100644 --- a/sys/dev/usb/usbnet.h +++ b/sys/dev/usb/usbnet.h @@ -285,6 +285,7 @@ void *usbnet_softc(struct usbnet *); bool usbnet_havelink(struct usbnet *); bool usbnet_isdying(struct usbnet *); +bool usbnet_ispromisc(struct usbnet *); /* * Endpoint / rx/tx chain management: