git: 87a1f9e8df1e - stable/13 - e1000: Try auto-negotiation for fixed 100 or 10 configuration

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Wed, 27 Nov 2024 04:16:45 UTC
The branch stable/13 has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=87a1f9e8df1ed75ff055fe68d93736f922c53de4

commit 87a1f9e8df1ed75ff055fe68d93736f922c53de4
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2024-11-23 12:27:44 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2024-11-27 04:16:33 +0000

    e1000: Try auto-negotiation for fixed 100 or 10 configuration
    
    This is a retread of https://reviews.freebsd.org/D34449 which I think
    will fix the issue for the remote side not supporting autoneg.  We now
    attempt an autoneg, and if that fails fall back to the current code
    that forces the link speed/duplex.
    
    The original intent of this patch is to inform the remote switch of
    duplex settings when we (the client) are specifying a fixed 10 or 100
    speed.  Otherwise it may get the duplex setting wrong.
    
    The tricky case is when the remote (switch) side is fixing its
    speed AND duplex while disabling autoneg and we (client) need to do
    the same, which still seems to be common enough at some ISPs.
    
    Original commit message follows:
    Currently if an e1000 interface is set to a fixed media configuration,
    for gigabit, it will participate in auto-negotiation as required by
    IEEE 802.3-2018 Clause 37. However, if set to fixed media configuration
    for 100 or 10, it does NOT participate in auto-negotiation.
    
    By my reading of Clauses 28 and 37, while auto-negotiation is optional
    for 100 and 10, it is not prohibited and is, in fact, "highly
    recommended".
    
    This patch enables auto-negotiation for fixed 100 and 10 media
    configuration, in a similar manner to that already performed for 1000.
    I.e., the patch enables advertising of just the manually configured
    settings with the goal of allowing the remote end to match the manually
    configured settings if it has them available.
    
    To be clear, this patch does NOT allow an em(4) interface that has been
    manually configured with specific media settings to respond to
    auto-negotiation by then configuring different parameters to those that
    were manually configured. The intent of this patch is to fully comply
    with the requirements of Clause 37, but for 100 and 10.
    
    The need for this has arisen on an em(4) link where the other end is
    under a different administrative control and is set to full
    auto-negotiation. Due to the cable length GigE is not working well. It
    is desired to set the em(4) end to "media 100baseTX mediatype
    full-duplex" which does work when both ends are configured that way.
    Currently, because em(4) does not participate in autoneg for this
    setting, the remote defaults to half-duplex - i.e., there's a duplex
    mismatch and things don't work. With this patch, em(4) would inform the
    remote that it has only 100baseTX full, the remote would match that and
    it will work.
    
    Tested by:      Natalino Picone <natalino.picone@nozominetworks.com>
    Tested by:      Franco Fichtner <franco@opnsense.org>
    Tested by:      J.R. Oldroyd <fbsd@opal.com> (previous version)
    Sponsored by:   Nozomi Networks
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D47336
    
    (cherry picked from commit bceec3d80a3caf9249e24247fb937674bf5b46b5)
---
 sys/dev/e1000/e1000_phy.c |  5 +++--
 sys/dev/e1000/if_em.c     | 44 ++++++++++++++++++++++++++++++++++++++------
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/sys/dev/e1000/e1000_phy.c b/sys/dev/e1000/e1000_phy.c
index 4e2afc43a684..53be6a48b671 100644
--- a/sys/dev/e1000/e1000_phy.c
+++ b/sys/dev/e1000/e1000_phy.c
@@ -1707,9 +1707,10 @@ s32 e1000_setup_copper_link_generic(struct e1000_hw *hw)
 		 * autonegotiation.
 		 */
 		ret_val = e1000_copper_link_autoneg(hw);
-		if (ret_val)
+		if (ret_val && !hw->mac.forced_speed_duplex)
 			return ret_val;
-	} else {
+	}
+	if (!hw->mac.autoneg || (ret_val && hw->mac.forced_speed_duplex)) {
 		/* PHY will be set to 10H, 10F, 100H or 100F
 		 * depending on user settings.
 		 */
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index d47d8d5e1fcc..1a5dbe2335ba 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -2001,7 +2001,18 @@ em_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr)
 	    (sc->hw.phy.media_type == e1000_media_type_internal_serdes)) {
 		if (sc->hw.mac.type == e1000_82545)
 			fiber_type = IFM_1000_LX;
-		ifmr->ifm_active |= fiber_type | IFM_FDX;
+		switch (sc->link_speed) {
+		case 10:
+			ifmr->ifm_active |= IFM_10_FL;
+			break;
+		case 100:
+			ifmr->ifm_active |= IFM_100_FX;
+			break;
+		case 1000:
+		default:
+			ifmr->ifm_active |= fiber_type | IFM_FDX;
+			break;
+		}
 	} else {
 		switch (sc->link_speed) {
 		case 10:
@@ -2014,11 +2025,12 @@ em_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr)
 			ifmr->ifm_active |= IFM_1000_T;
 			break;
 		}
-		if (sc->link_duplex == FULL_DUPLEX)
-			ifmr->ifm_active |= IFM_FDX;
-		else
-			ifmr->ifm_active |= IFM_HDX;
 	}
+
+	if (sc->link_duplex == FULL_DUPLEX)
+		ifmr->ifm_active |= IFM_FDX;
+	else
+		ifmr->ifm_active |= IFM_HDX;
 }
 
 /*********************************************************************
@@ -2052,6 +2064,26 @@ em_if_media_change(if_ctx_t ctx)
 		sc->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL;
 		break;
 	case IFM_100_TX:
+		sc->hw.mac.autoneg = DO_AUTO_NEG;
+		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) {
+			sc->hw.phy.autoneg_advertised = ADVERTISE_100_FULL;
+			sc->hw.mac.forced_speed_duplex = ADVERTISE_100_FULL;
+		} else {
+			sc->hw.phy.autoneg_advertised = ADVERTISE_100_HALF;
+			sc->hw.mac.forced_speed_duplex = ADVERTISE_100_HALF;
+		}
+		break;
+	case IFM_10_T:
+		sc->hw.mac.autoneg = DO_AUTO_NEG;
+		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) {
+			sc->hw.phy.autoneg_advertised = ADVERTISE_10_FULL;
+			sc->hw.mac.forced_speed_duplex = ADVERTISE_10_FULL;
+		} else {
+			sc->hw.phy.autoneg_advertised = ADVERTISE_10_HALF;
+			sc->hw.mac.forced_speed_duplex = ADVERTISE_10_HALF;
+		}
+		break;
+	case IFM_100_FX:
 		sc->hw.mac.autoneg = false;
 		sc->hw.phy.autoneg_advertised = 0;
 		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
@@ -2059,7 +2091,7 @@ em_if_media_change(if_ctx_t ctx)
 		else
 			sc->hw.mac.forced_speed_duplex = ADVERTISE_100_HALF;
 		break;
-	case IFM_10_T:
+	case IFM_10_FL:
 		sc->hw.mac.autoneg = false;
 		sc->hw.phy.autoneg_advertised = 0;
 		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)