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.
This commit is contained in:
martin 2023-09-04 17:57:49 +00:00
parent c9e6c99ebf
commit a6fbedfa1a
2 changed files with 130 additions and 28 deletions

View File

@ -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)
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)
} 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, uint32_t icr)
{
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, uint32_t icr)
DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> mii_pollstat\n",
device_xname(sc->sc_dev)));
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,22 +11948,22 @@ wm_gmii_i82544_readreg_locked(device_t dev, int phy, int reg, uint16_t *val)
{
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:
if (reg > BME1000_MAX_MULTI_PAGE_REG)
wm_gmii_mdic_writereg(dev, phy, IGPHY_PAGE_SELECT,
reg);
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;
}
}
*val = wm_gmii_mdic_readreg(dev, phy, reg & MII_ADDRMASK);
@ -11893,22 +11994,22 @@ wm_gmii_i82544_writereg_locked(device_t dev, int phy, int reg, uint16_t val)
{
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:
if (reg > BME1000_MAX_MULTI_PAGE_REG)
wm_gmii_mdic_writereg(dev, phy, IGPHY_PAGE_SELECT,
reg);
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;
}
}
wm_gmii_mdic_writereg(dev, phy, reg & MII_ADDRMASK, val);

View File

@ -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: