Module Name: src Committed By: martin Date: Mon Sep 4 17:57:50 UTC 2023
Modified Files: src/sys/dev/pci [netbsd-8]: if_wm.c if_wmvar.h Log Message: Pull up following revision(s) (requested by msaitoh in ticket #1894): sys/dev/pci/if_wmvar.h: revision 1.50 sys/dev/pci/if_wm.c: revision 1.783,1.784 via patch Delay sending LINK_STATE_UP to prevent dropping packets on I35[04] and I21[01]. Some (not all) systems use I35[04] or I21[01] don't send packet soon after linkup. The MAC send a packet to the PHY and any error is not observed. This behavior causes a problem that gratuitous ARP and/or IPv6 DAD packet are silently dropped. To avoid this problem, don't call mii_pollstat() here which will send LINK_STATE_UP notification to the upper layer. Instead, mii_pollstat() will be called in wm_gmii_mediastatus() or mii_tick() will be called in wm_tick(). Note that the similar workaround is in Linux's igb driver though it's only for I21[01]. OK'd by hikaru@ and knakahara@. Fix #ifdef WM_DEBUG code in wm_gmii_i82544_{read,write}reg_locked. To generate a diff of this commit: cvs rdiff -u -r1.508.4.50 -r1.508.4.51 src/sys/dev/pci/if_wm.c cvs rdiff -u -r1.33.6.10 -r1.33.6.11 src/sys/dev/pci/if_wmvar.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_wm.c diff -u src/sys/dev/pci/if_wm.c:1.508.4.50 src/sys/dev/pci/if_wm.c:1.508.4.51 --- src/sys/dev/pci/if_wm.c:1.508.4.50 Tue Jun 27 18:36:53 2023 +++ src/sys/dev/pci/if_wm.c Mon Sep 4 17:57:49 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wm.c,v 1.508.4.50 2023/06/27 18:36:53 martin Exp $ */ +/* $NetBSD: if_wm.c,v 1.508.4.51 2023/09/04 17:57:49 martin 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.508.4.50 2023/06/27 18:36:53 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.508.4.51 2023/09/04 17:57:49 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_net_mpsafe.h" @@ -547,7 +547,7 @@ struct wm_softc { #define WM_MEDIATYPE_COPPER 0x02 #define WM_MEDIATYPE_SERDES 0x03 /* Internal SERDES */ int sc_funcid; /* unit number of the chip (0 to 3) */ - int sc_flags; /* flags; see below */ + u_int sc_flags; /* flags; see below */ int sc_if_flags; /* last if_flags */ int sc_flowflags; /* 802.3x flow control flags */ int sc_align_tweak; @@ -717,6 +717,7 @@ struct wm_softc { int sc_tbi_linkup; /* TBI link status */ int sc_tbi_serdes_anegticks; /* autonegotiation ticks */ int sc_tbi_serdes_ticks; /* tbi ticks */ + struct timeval sc_linkup_delay_time; /* delay LINK_STATE_UP */ int sc_mchash_type; /* multicast filter offset */ @@ -3046,6 +3047,23 @@ alloc_retry: || (sc->sc_type == WM_T_I210) || (sc->sc_type == WM_T_I211)) sc->sc_flags |= WM_F_CRC_STRIP; + /* + * Workaround for some chips to delay sending LINK_STATE_UP. + * Some systems can't send packet soon after linkup. See also + * wm_linkintr_gmii(), wm_tick() and wm_gmii_mediastatus(). + */ + switch (sc->sc_type) { + case WM_T_I350: + case WM_T_I354: + case WM_T_I210: + case WM_T_I211: + if (sc->sc_mediatype == WM_MEDIATYPE_COPPER) + sc->sc_flags |= WM_F_DELAY_LINKUP; + break; + default: + break; + } + /* Set device properties (macflags) */ prop_dictionary_set_uint32(dict, "macflags", sc->sc_flags); @@ -3834,9 +3852,29 @@ wm_tick(void *arg) wm_update_stats(sc); - if (sc->sc_flags & WM_F_HAS_MII) - mii_tick(&sc->sc_mii); - else if ((sc->sc_type >= WM_T_82575) && (sc->sc_type <= WM_T_I211) + if (sc->sc_flags & WM_F_HAS_MII) { + bool dotick = true; + + /* + * Workaround for some chips to delay sending LINK_STATE_UP. + * See also wm_linkintr_gmii() and wm_gmii_mediastatus(). + */ + if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) { + struct timeval now; + + getmicrotime(&now); + if (timercmp(&now, &sc->sc_linkup_delay_time, <)) + dotick = false; + else if (sc->sc_linkup_delay_time.tv_sec != 0) { + /* Simplify by checking tv_sec only. */ + + sc->sc_linkup_delay_time.tv_sec = 0; + sc->sc_linkup_delay_time.tv_usec = 0; + } + } + if (dotick) + mii_tick(&sc->sc_mii); + } else if ((sc->sc_type >= WM_T_82575) && (sc->sc_type <= WM_T_I211) && (sc->sc_mediatype == WM_MEDIATYPE_SERDES)) wm_serdes_tick(sc); else @@ -10169,6 +10207,7 @@ wm_linkintr_gmii(struct wm_softc *sc, ui { uint32_t status, reg; bool link; + bool dopoll = true; KASSERT(WM_CORE_LOCKED(sc)); @@ -10214,7 +10253,42 @@ wm_linkintr_gmii(struct wm_softc *sc, ui DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> mii_pollstat\n", device_xname(sc->sc_dev))); - mii_pollstat(&sc->sc_mii); + if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) { + if (link) { + /* Wait 1 second. */ + dopoll = false; + getmicrotime(&sc->sc_linkup_delay_time); + sc->sc_linkup_delay_time.tv_sec += 1; + } else if (sc->sc_linkup_delay_time.tv_sec != 0) { + /* + * Simplify by checking tv_sec only. It's enough. + * + * Currently, it's not required to clear the time. + * It's just to know the timer is stopped + * (for debugging). + */ + + sc->sc_linkup_delay_time.tv_sec = 0; + sc->sc_linkup_delay_time.tv_usec = 0; + } + } + + /* + * Call mii_pollstat(). + * + * Some (not all) systems use I35[04] or I21[01] don't send packet soon + * after linkup. The MAC send a packet to the PHY and any error is not + * observed. This behavior causes a problem that gratuitous ARP and/or + * IPv6 DAD packet are silently dropped. To avoid this problem, don't + * call mii_pollstat() here which will send LINK_STATE_UP notification + * to the upper layer. Instead, mii_pollstat() will be called in + * wm_gmii_mediastatus() or mii_tick() will be called in wm_tick(). + */ + if (dopoll) + mii_pollstat(&sc->sc_mii); + + /* Do some workarounds soon after link status is changed. */ + if (sc->sc_type == WM_T_82543) { int miistatus, active; @@ -11598,8 +11672,35 @@ static void wm_gmii_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { struct wm_softc *sc = ifp->if_softc; + struct ethercom *ec = &sc->sc_ethercom; + struct mii_data *mii; + bool dopoll = true; + + KASSERT(ec->ec_mii != NULL); + mii = ec->ec_mii; + if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) { + struct timeval now; + + getmicrotime(&now); + if (timercmp(&now, &sc->sc_linkup_delay_time, <)) + dopoll = false; + else if (sc->sc_linkup_delay_time.tv_sec != 0) { + /* Simplify by checking tv_sec only. It's enough. */ + + sc->sc_linkup_delay_time.tv_sec = 0; + sc->sc_linkup_delay_time.tv_usec = 0; + } + } + + /* + * Don't call mii_pollstat() while doing workaround. + * See also wm_linkintr_gmii() and wm_tick(). + */ + if (dopoll) + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; - ether_mediastatus(ifp, ifmr); ifmr->ifm_active = (ifmr->ifm_active & ~IFM_ETH_FMASK) | sc->sc_flowflags; } @@ -11847,21 +11948,21 @@ wm_gmii_i82544_readreg_locked(device_t d { struct wm_softc *sc = device_private(dev); - if (reg > BME1000_MAX_MULTI_PAGE_REG) { - switch (sc->sc_phytype) { - case WMPHY_IGP: - case WMPHY_IGP_2: - case WMPHY_IGP_3: + switch (sc->sc_phytype) { + case WMPHY_IGP: + case WMPHY_IGP_2: + case WMPHY_IGP_3: + if (reg > BME1000_MAX_MULTI_PAGE_REG) wm_gmii_mdic_writereg(dev, phy, IGPHY_PAGE_SELECT, reg); - break; - default: + break; + default: #ifdef WM_DEBUG + if ((reg >> MII_ADDRBITS) != 0) device_printf(dev, "%s: PHYTYPE = 0x%x, addr = %02x\n", __func__, sc->sc_phytype, reg); #endif - break; - } + break; } *val = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK); @@ -11893,21 +11994,21 @@ wm_gmii_i82544_writereg_locked(device_t { struct wm_softc *sc = device_private(dev); - if (reg > BME1000_MAX_MULTI_PAGE_REG) { - switch (sc->sc_phytype) { - case WMPHY_IGP: - case WMPHY_IGP_2: - case WMPHY_IGP_3: + switch (sc->sc_phytype) { + case WMPHY_IGP: + case WMPHY_IGP_2: + case WMPHY_IGP_3: + if (reg > BME1000_MAX_MULTI_PAGE_REG) wm_gmii_mdic_writereg(dev, phy, IGPHY_PAGE_SELECT, reg); - break; - default: + break; + default: #ifdef WM_DEBUG + if ((reg >> MII_ADDRBITS) != 0) device_printf(dev, "%s: PHYTYPE == 0x%x, addr = %02x", __func__, sc->sc_phytype, reg); #endif - break; - } + break; } wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val); Index: src/sys/dev/pci/if_wmvar.h diff -u src/sys/dev/pci/if_wmvar.h:1.33.6.10 src/sys/dev/pci/if_wmvar.h:1.33.6.11 --- src/sys/dev/pci/if_wmvar.h:1.33.6.10 Tue Jun 27 18:36:53 2023 +++ src/sys/dev/pci/if_wmvar.h Mon Sep 4 17:57:49 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wmvar.h,v 1.33.6.10 2023/06/27 18:36:53 martin Exp $ */ +/* $NetBSD: if_wmvar.h,v 1.33.6.11 2023/09/04 17:57:49 martin Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -100,6 +100,7 @@ #define WM_F_SFP 0x10000000 /* SFP */ #define WM_F_MAS 0x20000000 /* Media Auto Sense */ #define WM_F_CRC_STRIP 0x40000000 /* CRC strip */ +#define WM_F_DELAY_LINKUP 0x80000000 /* delay LINK_STATE_UP */ #define WM_FLAGS "\20" \ "\1" "HAS_MII" "\2" "LOCK_EECD" "\3" "_B02" "\4" "_B03" \ @@ -109,7 +110,7 @@ "\21" "NEWQUEUE" "\22" "ASF_FIRM" "\23" "ARC_SUBSYS" "\24" "AMT" \ "\25" "MANAGE" "\26" "WOL" "\27" "EEE" "\30" "ATTACHED" \ "\31" "MDIC_WA" "\32" "PCS_DIS_AUTONEGO" "\33" "PLLWA" "\34" "CLSEMWA" \ - "\35" "SFP" "\36" "MAS" "\37" "CRC_STRIP" + "\35" "SFP" "\36" "MAS" "\37" "CRC_STRIP" "\40" "DELAY_LINKUP" /* * Variations of Intel gigabit Ethernet controller: