Module Name: src
Committed By: martin
Date: Mon Sep 4 17:55:24 UTC 2023
Modified Files:
src/sys/dev/pci [netbsd-9]: if_wm.c if_wmvar.h
Log Message:
Pull up following revision(s) (requested by msaitoh in ticket #344):
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.645.2.16 -r1.645.2.17 src/sys/dev/pci/if_wm.c
cvs rdiff -u -r1.44.4.5 -r1.44.4.6 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.645.2.16 src/sys/dev/pci/if_wm.c:1.645.2.17
--- src/sys/dev/pci/if_wm.c:1.645.2.16 Tue Jun 27 18:32:46 2023
+++ src/sys/dev/pci/if_wm.c Mon Sep 4 17:55:24 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: if_wm.c,v 1.645.2.16 2023/06/27 18:32:46 martin Exp $ */
+/* $NetBSD: if_wm.c,v 1.645.2.17 2023/09/04 17:55:24 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.645.2.16 2023/06/27 18:32:46 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.645.2.17 2023/09/04 17:55:24 martin Exp $");
#ifdef _KERNEL_OPT
#include "opt_net_mpsafe.h"
@@ -550,7 +550,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_ec_capenable; /* last ec_capenable */
int sc_flowflags; /* 802.3x flow control flags */
@@ -722,6 +722,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 */
@@ -3084,6 +3085,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);
@@ -3877,9 +3895,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
@@ -10266,6 +10304,7 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
device_t dev = sc->sc_dev;
uint32_t status, reg;
bool link;
+ bool dopoll = true;
int rv;
KASSERT(WM_CORE_LOCKED(sc));
@@ -10312,7 +10351,46 @@ wm_linkintr_gmii(struct wm_softc *sc, ui
DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> mii_pollstat\n",
device_xname(dev)));
- mii_pollstat(&sc->sc_mii);
+ if ((sc->sc_flags & WM_F_DELAY_LINKUP) != 0) {
+ if (link) {
+ /*
+ * To workaround the problem, it's required to wait
+ * several hundred miliseconds. The time depend
+ * on the environment. Wait 1 second for the safety.
+ */
+ 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;
@@ -11817,8 +11895,40 @@ 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;
+
+ /*
+ * In normal drivers, ether_mediastatus() is called here.
+ * To avoid calling mii_pollstat(), ether_mediastatus() is open coded.
+ */
+ 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;
}
@@ -12073,23 +12183,25 @@ wm_gmii_i82544_readreg_locked(device_t d
struct wm_softc *sc = device_private(dev);
int rv;
- 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) {
rv = wm_gmii_mdic_writereg(dev, phy,
IGPHY_PAGE_SELECT, reg);
if (rv != 0)
return rv;
- break;
- default:
+ }
+ break;
+ default:
#ifdef WM_DEBUG
- device_printf(dev, "%s: PHYTYPE = 0x%x, addr = %02x\n",
+ if ((reg >> MII_ADDRBITS) != 0)
+ device_printf(dev,
+ "%s: PHYTYPE = 0x%x, addr = 0x%02x\n",
__func__, sc->sc_phytype, reg);
#endif
- break;
- }
+ break;
}
return wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK, val);
@@ -12124,23 +12236,25 @@ wm_gmii_i82544_writereg_locked(device_t
struct wm_softc *sc = device_private(dev);
int rv;
- 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) {
rv = wm_gmii_mdic_writereg(dev, phy,
IGPHY_PAGE_SELECT, reg);
if (rv != 0)
return rv;
- break;
- default:
+ }
+ break;
+ default:
#ifdef WM_DEBUG
- device_printf(dev, "%s: PHYTYPE == 0x%x, addr = %02x",
+ if ((reg >> MII_ADDRBITS) != 0)
+ device_printf(dev,
+ "%s: PHYTYPE == 0x%x, addr = 0x%02x",
__func__, sc->sc_phytype, reg);
#endif
- break;
- }
+ break;
}
return 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.44.4.5 src/sys/dev/pci/if_wmvar.h:1.44.4.6
--- src/sys/dev/pci/if_wmvar.h:1.44.4.5 Tue Jun 27 18:32:46 2023
+++ src/sys/dev/pci/if_wmvar.h Mon Sep 4 17:55:24 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: if_wmvar.h,v 1.44.4.5 2023/06/27 18:32:46 martin Exp $ */
+/* $NetBSD: if_wmvar.h,v 1.44.4.6 2023/09/04 17:55:24 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: