svn commit: r355107 - in head/sys/dev/cxgbe: . common

Navdeep Parhar np at FreeBSD.org
Tue Nov 26 05:54:26 UTC 2019


Author: np
Date: Tue Nov 26 05:54:25 2019
New Revision: 355107
URL: https://svnweb.freebsd.org/changeset/base/355107

Log:
  cxgbe(4): Allow the driver to specify multiple FECs that the firmware
  should try in order to link up with the peer.
  
  Various FEC variables within the driver can now have multiple bits set
  instead of being powers of 2.  0 and -1 in the user knobs still mean no
  FEC and auto (driver decides) respectively for backward compatibility,
  but no-FEC and auto now have their own bits in the internal
  representation.  There is a new bit that can be set to request the FEC
  recommended by the cable/transceiver module.
  
  Add sysctls to display link related capabilities of the local side as
  well as the link partner.
  
  Note that all this needs a new firmware and the documentation for the
  driver FEC knobs will be updated after that firmware is added to the
  driver.
  
  MFC after:	1 week
  Sponsored by:	Chelsio Communications

Modified:
  head/sys/dev/cxgbe/common/common.h
  head/sys/dev/cxgbe/common/t4_hw.c
  head/sys/dev/cxgbe/t4_main.c

Modified: head/sys/dev/cxgbe/common/common.h
==============================================================================
--- head/sys/dev/cxgbe/common/common.h	Tue Nov 26 05:06:25 2019	(r355106)
+++ head/sys/dev/cxgbe/common/common.h	Tue Nov 26 05:54:25 2019	(r355107)
@@ -62,10 +62,21 @@ enum {
 };
 
 enum {
-	FEC_NONE      = 0,
-	FEC_RS        = 1 << 0,
-	FEC_BASER_RS  = 1 << 1,
-	FEC_AUTO      = 1 << 5,		/* M_FW_PORT_CAP32_FEC + 1 */
+	/*
+	 * Real FECs.  In the same order as the FEC portion of caps32 so that
+	 * the code can do (fec & M_FW_PORT_CAP32_FEC) to get all the real FECs.
+	 */
+	FEC_RS        = 1 << 0,	/* Reed-Solomon */
+	FEC_BASER_RS  = 1 << 1,	/* BASE-R, aka Firecode */
+	FEC_NONE      = 1 << 2,	/* no FEC */
+
+	/*
+	 * Pseudo FECs that translate to real FECs.  The firmware knows nothing
+	 * about these and they start at M_FW_PORT_CAP32_FEC + 1.  AUTO should
+	 * be set all by itself.
+	 */
+	FEC_AUTO      = 1 << 5,
+	FEC_MODULE    = 1 << 6,	/* FEC suggested by the cable/transceiver. */
 };
 
 enum t4_bar2_qtype { T4_BAR2_QTYPE_EGRESS, T4_BAR2_QTYPE_INGRESS };
@@ -409,20 +420,20 @@ struct trace_params {
 
 struct link_config {
 	/* OS-specific code owns all the requested_* fields. */
-	int8_t requested_aneg;		/* link autonegotiation */
-	int8_t requested_fc;		/* flow control */
-	int8_t requested_fec;		/* FEC */
-	u_int requested_speed;		/* speed (Mbps) */
+	int8_t requested_aneg;	/* link autonegotiation */
+	int8_t requested_fc;	/* flow control */
+	int8_t requested_fec;	/* FEC */
+	u_int requested_speed;	/* speed (Mbps) */
 
-	uint32_t supported;		/* link capabilities */
-	uint32_t advertising;		/* advertised capabilities */
-	uint32_t lp_advertising;	/* peer advertised capabilities */
-	uint32_t fec_hint;		/* use this fec */
-	u_int speed;			/* actual link speed (Mbps) */
-	int8_t fc;			/* actual link flow control */
-	int8_t fec;			/* actual FEC */
-	bool link_ok;			/* link up? */
-	uint8_t link_down_rc;		/* link down reason */
+	uint32_t pcaps;		/* link capabilities */
+	uint32_t acaps;		/* advertised capabilities */
+	uint32_t lpacaps;	/* peer advertised capabilities */
+	u_int speed;		/* actual link speed (Mbps) */
+	int8_t fc;		/* actual link flow control */
+	int8_t fec_hint;	/* cable/transceiver recommended fec */
+	int8_t fec;		/* actual FEC */
+	bool link_ok;		/* link up? */
+	uint8_t link_down_rc;	/* link down reason */
 };
 
 #include "adapter.h"
@@ -881,7 +892,7 @@ port_top_speed(const struct port_info *pi)
 {
 
 	/* Mbps -> Gbps */
-	return (fwcap_to_speed(pi->link_cfg.supported) / 1000);
+	return (fwcap_to_speed(pi->link_cfg.pcaps) / 1000);
 }
 
 #endif /* __CHELSIO_COMMON_H */

Modified: head/sys/dev/cxgbe/common/t4_hw.c
==============================================================================
--- head/sys/dev/cxgbe/common/t4_hw.c	Tue Nov 26 05:06:25 2019	(r355106)
+++ head/sys/dev/cxgbe/common/t4_hw.c	Tue Nov 26 05:54:25 2019	(r355107)
@@ -3841,6 +3841,43 @@ is_bt(struct port_info *pi)
 	    pi->port_type == FW_PORT_TYPE_BT_XAUI);
 }
 
+static int8_t fwcap_to_fec(uint32_t caps, bool unset_means_none)
+{
+	int8_t fec = 0;
+
+	if ((caps & V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC)) == 0)
+		return (unset_means_none ? FEC_NONE : 0);
+
+	if (caps & FW_PORT_CAP32_FEC_RS)
+		fec |= FEC_RS;
+	if (caps & FW_PORT_CAP32_FEC_BASER_RS)
+		fec |= FEC_BASER_RS;
+	if (caps & FW_PORT_CAP32_FEC_NO_FEC)
+		fec |= FEC_NONE;
+
+	return (fec);
+}
+
+/*
+ * Note that 0 is not translated to NO_FEC.
+ */
+static uint32_t fec_to_fwcap(int8_t fec)
+{
+	uint32_t caps = 0;
+
+	/* Only real FECs allowed. */
+	MPASS((fec & ~M_FW_PORT_CAP32_FEC) == 0);
+
+	if (fec & FEC_RS)
+		caps |= FW_PORT_CAP32_FEC_RS;
+	if (fec & FEC_BASER_RS)
+		caps |= FW_PORT_CAP32_FEC_BASER_RS;
+	if (fec & FEC_NONE)
+		caps |= FW_PORT_CAP32_FEC_NO_FEC;
+
+	return (caps);
+}
+
 /**
  *	t4_link_l1cfg - apply link configuration to MAC/PHY
  *	@phy: the PHY to setup
@@ -3869,41 +3906,61 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int m
 	if (!(lc->requested_fc & PAUSE_AUTONEG))
 		fc |= FW_PORT_CAP32_FORCE_PAUSE;
 
-	fec = 0;
-	if (lc->requested_fec == FEC_AUTO)
-		fec = lc->fec_hint;
-	else {
-		if (lc->requested_fec & FEC_RS)
-			fec |= FW_PORT_CAP32_FEC_RS;
-		if (lc->requested_fec & FEC_BASER_RS)
-			fec |= FW_PORT_CAP32_FEC_BASER_RS;
-	}
-
 	if (lc->requested_aneg == AUTONEG_DISABLE)
 		aneg = 0;
 	else if (lc->requested_aneg == AUTONEG_ENABLE)
 		aneg = FW_PORT_CAP32_ANEG;
 	else
-		aneg = lc->supported & FW_PORT_CAP32_ANEG;
+		aneg = lc->pcaps & FW_PORT_CAP32_ANEG;
 
 	if (aneg) {
-		speed = lc->supported & V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
+		speed = lc->pcaps &
+		    V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
 	} else if (lc->requested_speed != 0)
 		speed = speed_to_fwcap(lc->requested_speed);
 	else
-		speed = fwcap_top_speed(lc->supported);
+		speed = fwcap_top_speed(lc->pcaps);
 
+	fec = 0;
+	if (fec_supported(lc->pcaps)) {
+		if (lc->requested_fec == FEC_AUTO) {
+			if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) {
+				if (speed & FW_PORT_CAP32_SPEED_100G) {
+					fec |= FW_PORT_CAP32_FEC_RS;
+					fec |= FW_PORT_CAP32_FEC_NO_FEC;
+				} else {
+					fec |= FW_PORT_CAP32_FEC_RS;
+					fec |= FW_PORT_CAP32_FEC_BASER_RS;
+					fec |= FW_PORT_CAP32_FEC_NO_FEC;
+				}
+			} else {
+				/* Set only 1b with old firmwares. */
+				fec |= fec_to_fwcap(lc->fec_hint);
+			}
+		} else {
+			fec |= fec_to_fwcap(lc->requested_fec &
+			    M_FW_PORT_CAP32_FEC);
+			if (lc->requested_fec & FEC_MODULE)
+				fec |= fec_to_fwcap(lc->fec_hint);
+		}
+
+		if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC)
+			fec |= FW_PORT_CAP32_FORCE_FEC;
+		else if (fec == FW_PORT_CAP32_FEC_NO_FEC)
+			fec = 0;
+	}
+
 	/* Force AN on for BT cards. */
 	if (is_bt(adap->port[adap->chan_map[port]]))
-		aneg = lc->supported & FW_PORT_CAP32_ANEG;
+		aneg = lc->pcaps & FW_PORT_CAP32_ANEG;
 
 	rcap = aneg | speed | fc | fec;
-	if ((rcap | lc->supported) != lc->supported) {
+	if ((rcap | lc->pcaps) != lc->pcaps) {
 #ifdef INVARIANTS
-		CH_WARN(adap, "rcap 0x%08x, pcap 0x%08x\n", rcap,
-		    lc->supported);
+		CH_WARN(adap, "rcap 0x%08x, pcap 0x%08x, removed 0x%x\n", rcap,
+		    lc->pcaps, rcap & (rcap ^ lc->pcaps));
 #endif
-		rcap &= lc->supported;
+		rcap &= lc->pcaps;
 	}
 	rcap |= mdi;
 
@@ -8470,7 +8527,6 @@ uint32_t fwcap_top_speed(uint32_t caps)
 	return 0;
 }
 
-
 /**
  *	lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities
  *	@lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value
@@ -8516,7 +8572,7 @@ static void handle_port_info(struct port_info *pi, con
     enum fw_port_action action, bool *mod_changed, bool *link_changed)
 {
 	struct link_config old_lc, *lc = &pi->link_cfg;
-	unsigned char fc, fec;
+	unsigned char fc;
 	u32 stat, linkattr;
 	int old_ptype, old_mtype;
 
@@ -8531,9 +8587,9 @@ static void handle_port_info(struct port_info *pi, con
 		pi->mdio_addr = stat & F_FW_PORT_CMD_MDIOCAP ?
 		    G_FW_PORT_CMD_MDIOADDR(stat) : -1;
 
-		lc->supported = fwcaps16_to_caps32(be16_to_cpu(p->u.info.pcap));
-		lc->advertising = fwcaps16_to_caps32(be16_to_cpu(p->u.info.acap));
-		lc->lp_advertising = fwcaps16_to_caps32(be16_to_cpu(p->u.info.lpacap));
+		lc->pcaps = fwcaps16_to_caps32(be16_to_cpu(p->u.info.pcap));
+		lc->acaps = fwcaps16_to_caps32(be16_to_cpu(p->u.info.acap));
+		lc->lpacaps = fwcaps16_to_caps32(be16_to_cpu(p->u.info.lpacap));
 		lc->link_ok = (stat & F_FW_PORT_CMD_LSTATUS) != 0;
 		lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC(stat);
 
@@ -8546,9 +8602,9 @@ static void handle_port_info(struct port_info *pi, con
 		pi->mdio_addr = stat & F_FW_PORT_CMD_MDIOCAP32 ?
 		    G_FW_PORT_CMD_MDIOADDR32(stat) : -1;
 
-		lc->supported = be32_to_cpu(p->u.info32.pcaps32);
-		lc->advertising = be32_to_cpu(p->u.info32.acaps32);
-		lc->lp_advertising = be32_to_cpu(p->u.info32.lpacaps32);
+		lc->pcaps = be32_to_cpu(p->u.info32.pcaps32);
+		lc->acaps = be32_to_cpu(p->u.info32.acaps32);
+		lc->lpacaps = be32_to_cpu(p->u.info32.lpacaps32);
 		lc->link_ok = (stat & F_FW_PORT_CMD_LSTATUS32) != 0;
 		lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC32(stat);
 
@@ -8559,6 +8615,7 @@ static void handle_port_info(struct port_info *pi, con
 	}
 
 	lc->speed = fwcap_to_speed(linkattr);
+	lc->fec = fwcap_to_fec(linkattr, true);
 
 	fc = 0;
 	if (linkattr & FW_PORT_CAP32_FC_RX)
@@ -8567,23 +8624,14 @@ static void handle_port_info(struct port_info *pi, con
 		fc |= PAUSE_TX;
 	lc->fc = fc;
 
-	fec = FEC_NONE;
-	if (linkattr & FW_PORT_CAP32_FEC_RS)
-		fec |= FEC_RS;
-	if (linkattr & FW_PORT_CAP32_FEC_BASER_RS)
-		fec |= FEC_BASER_RS;
-	lc->fec = fec;
-
 	if (mod_changed != NULL)
 		*mod_changed = false;
 	if (link_changed != NULL)
 		*link_changed = false;
 	if (old_ptype != pi->port_type || old_mtype != pi->mod_type ||
-	    old_lc.supported != lc->supported) {
-		if (pi->mod_type != FW_PORT_MOD_TYPE_NONE) {
-			lc->fec_hint = lc->advertising &
-			    V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC);
-		}
+	    old_lc.pcaps != lc->pcaps) {
+		if (pi->mod_type != FW_PORT_MOD_TYPE_NONE)
+			lc->fec_hint = fwcap_to_fec(lc->acaps, true);
 		if (mod_changed != NULL)
 			*mod_changed = true;
 	}

Modified: head/sys/dev/cxgbe/t4_main.c
==============================================================================
--- head/sys/dev/cxgbe/t4_main.c	Tue Nov 26 05:06:25 2019	(r355106)
+++ head/sys/dev/cxgbe/t4_main.c	Tue Nov 26 05:54:25 2019	(r355107)
@@ -678,6 +678,7 @@ static int sysctl_qsize_rxq(SYSCTL_HANDLER_ARGS);
 static int sysctl_qsize_txq(SYSCTL_HANDLER_ARGS);
 static int sysctl_pause_settings(SYSCTL_HANDLER_ARGS);
 static int sysctl_fec(SYSCTL_HANDLER_ARGS);
+static int sysctl_module_fec(SYSCTL_HANDLER_ARGS);
 static int sysctl_autoneg(SYSCTL_HANDLER_ARGS);
 static int sysctl_handle_t4_reg64(SYSCTL_HANDLER_ARGS);
 static int sysctl_temperature(SYSCTL_HANDLER_ARGS);
@@ -2393,7 +2394,7 @@ cxgbe_media_change(struct ifnet *ifp)
 	PORT_LOCK(pi);
 	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
 		/* ifconfig .. media autoselect */
-		if (!(lc->supported & FW_PORT_CAP32_ANEG)) {
+		if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) {
 			rc = ENOTSUP; /* AN not supported by transceiver */
 			goto done;
 		}
@@ -4763,7 +4764,7 @@ set_current_media(struct port_info *pi)
 
 	lc = &pi->link_cfg;
 	if (lc->requested_aneg != AUTONEG_DISABLE &&
-	    lc->supported & FW_PORT_CAP32_ANEG) {
+	    lc->pcaps & FW_PORT_CAP32_ANEG) {
 		ifmedia_set(ifm, IFM_ETHER | IFM_AUTO);
 		return;
 	}
@@ -4820,7 +4821,7 @@ build_medialist(struct port_info *pi)
 	ifm = &pi->media;
 	ifmedia_removeall(ifm);
 	lc = &pi->link_cfg;
-	ss = G_FW_PORT_CAP32_SPEED(lc->supported); /* Supported Speeds */
+	ss = G_FW_PORT_CAP32_SPEED(lc->pcaps); /* Supported Speeds */
 	if (__predict_false(ss == 0)) {	/* not supposed to happen. */
 		MPASS(ss != 0);
 no_media:
@@ -4846,7 +4847,7 @@ no_media:
 	}
 	if (unknown > 0) /* Add one unknown for all unknown media types. */
 		ifmedia_add4(ifm, IFM_ETHER | IFM_FDX | IFM_UNKNOWN);
-	if (lc->supported & FW_PORT_CAP32_ANEG)
+	if (lc->pcaps & FW_PORT_CAP32_ANEG)
 		ifmedia_add(ifm, IFM_ETHER | IFM_AUTO, 0, NULL);
 
 	set_current_media(pi);
@@ -4874,14 +4875,16 @@ init_link_config(struct port_info *pi)
 	lc->requested_fc = t4_pause_settings & (PAUSE_TX | PAUSE_RX |
 	    PAUSE_AUTONEG);
 
-	if (t4_fec == -1 || t4_fec & FEC_AUTO)
+	if (t4_fec & FEC_AUTO)
 		lc->requested_fec = FEC_AUTO;
-	else {
+	else if (t4_fec == 0)
 		lc->requested_fec = FEC_NONE;
-		if (t4_fec & FEC_RS)
-			lc->requested_fec |= FEC_RS;
-		if (t4_fec & FEC_BASER_RS)
-			lc->requested_fec |= FEC_BASER_RS;
+	else {
+		/* -1 is handled by the FEC_AUTO block above and not here. */
+		lc->requested_fec = t4_fec &
+		    (FEC_RS | FEC_BASER_RS | FEC_NONE | FEC_MODULE);
+		if (lc->requested_fec == 0)
+			lc->requested_fec = FEC_AUTO;
 	}
 }
 
@@ -4901,7 +4904,7 @@ fixup_link_config(struct port_info *pi)
 	/* Speed (when not autonegotiating) */
 	if (lc->requested_speed != 0) {
 		fwspeed = speed_to_fwcap(lc->requested_speed);
-		if ((fwspeed & lc->supported) == 0) {
+		if ((fwspeed & lc->pcaps) == 0) {
 			n++;
 			lc->requested_speed = 0;
 		}
@@ -4912,7 +4915,7 @@ fixup_link_config(struct port_info *pi)
 	    lc->requested_aneg == AUTONEG_DISABLE ||
 	    lc->requested_aneg == AUTONEG_AUTO);
 	if (lc->requested_aneg == AUTONEG_ENABLE &&
-	    !(lc->supported & FW_PORT_CAP32_ANEG)) {
+	    !(lc->pcaps & FW_PORT_CAP32_ANEG)) {
 		n++;
 		lc->requested_aneg = AUTONEG_AUTO;
 	}
@@ -4920,26 +4923,26 @@ fixup_link_config(struct port_info *pi)
 	/* Flow control */
 	MPASS((lc->requested_fc & ~(PAUSE_TX | PAUSE_RX | PAUSE_AUTONEG)) == 0);
 	if (lc->requested_fc & PAUSE_TX &&
-	    !(lc->supported & FW_PORT_CAP32_FC_TX)) {
+	    !(lc->pcaps & FW_PORT_CAP32_FC_TX)) {
 		n++;
 		lc->requested_fc &= ~PAUSE_TX;
 	}
 	if (lc->requested_fc & PAUSE_RX &&
-	    !(lc->supported & FW_PORT_CAP32_FC_RX)) {
+	    !(lc->pcaps & FW_PORT_CAP32_FC_RX)) {
 		n++;
 		lc->requested_fc &= ~PAUSE_RX;
 	}
 	if (!(lc->requested_fc & PAUSE_AUTONEG) &&
-	    !(lc->supported & FW_PORT_CAP32_FORCE_PAUSE)) {
+	    !(lc->pcaps & FW_PORT_CAP32_FORCE_PAUSE)) {
 		n++;
 		lc->requested_fc |= PAUSE_AUTONEG;
 	}
 
 	/* FEC */
 	if ((lc->requested_fec & FEC_RS &&
-	    !(lc->supported & FW_PORT_CAP32_FEC_RS)) ||
+	    !(lc->pcaps & FW_PORT_CAP32_FEC_RS)) ||
 	    (lc->requested_fec & FEC_BASER_RS &&
-	    !(lc->supported & FW_PORT_CAP32_FEC_BASER_RS))) {
+	    !(lc->pcaps & FW_PORT_CAP32_FEC_BASER_RS))) {
 		n++;
 		lc->requested_fec = FEC_AUTO;
 	}
@@ -4963,17 +4966,17 @@ apply_link_config(struct port_info *pi)
 	PORT_LOCK_ASSERT_OWNED(pi);
 
 	if (lc->requested_aneg == AUTONEG_ENABLE)
-		MPASS(lc->supported & FW_PORT_CAP32_ANEG);
+		MPASS(lc->pcaps & FW_PORT_CAP32_ANEG);
 	if (!(lc->requested_fc & PAUSE_AUTONEG))
-		MPASS(lc->supported & FW_PORT_CAP32_FORCE_PAUSE);
+		MPASS(lc->pcaps & FW_PORT_CAP32_FORCE_PAUSE);
 	if (lc->requested_fc & PAUSE_TX)
-		MPASS(lc->supported & FW_PORT_CAP32_FC_TX);
+		MPASS(lc->pcaps & FW_PORT_CAP32_FC_TX);
 	if (lc->requested_fc & PAUSE_RX)
-		MPASS(lc->supported & FW_PORT_CAP32_FC_RX);
+		MPASS(lc->pcaps & FW_PORT_CAP32_FC_RX);
 	if (lc->requested_fec & FEC_RS)
-		MPASS(lc->supported & FW_PORT_CAP32_FEC_RS);
+		MPASS(lc->pcaps & FW_PORT_CAP32_FEC_RS);
 	if (lc->requested_fec & FEC_BASER_RS)
-		MPASS(lc->supported & FW_PORT_CAP32_FEC_BASER_RS);
+		MPASS(lc->pcaps & FW_PORT_CAP32_FEC_BASER_RS);
 #endif
 	rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc);
 	if (rc != 0) {
@@ -6750,11 +6753,21 @@ cxgbe_sysctls(struct port_info *pi)
     "PAUSE settings (bit 0 = rx_pause, 1 = tx_pause, 2 = pause_autoneg)");
 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "fec",
 	    CTLTYPE_STRING | CTLFLAG_RW, pi, 0, sysctl_fec, "A",
-	    "Forward Error Correction (bit 0 = RS, bit 1 = BASER_RS)");
+	    "FECs to use (bit 0 = RS, 1 = FC, 2 = none, 5 = auto, 6 = module)");
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "module_fec",
+	    CTLTYPE_STRING, pi, 0, sysctl_module_fec, "A",
+	    "FEC recommended by the cable/transceiver");
 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "autoneg",
 	    CTLTYPE_INT | CTLFLAG_RW, pi, 0, sysctl_autoneg, "I",
 	    "autonegotiation (-1 = not supported)");
 
+	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcaps", CTLFLAG_RD,
+	    &pi->link_cfg.pcaps, 0, "port capabilities");
+	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "acaps", CTLFLAG_RD,
+	    &pi->link_cfg.acaps, 0, "advertised capabilities");
+	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "lpacaps", CTLFLAG_RD,
+	    &pi->link_cfg.lpacaps, 0, "link partner advertised capabilities");
+
 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "max_speed", CTLFLAG_RD, NULL,
 	    port_top_speed(pi), "max speed (in Gbps)");
 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "mps_bg_map", CTLFLAG_RD, NULL,
@@ -7275,7 +7288,8 @@ sysctl_fec(SYSCTL_HANDLER_ARGS)
 
 	if (req->newptr == NULL) {
 		struct sbuf *sb;
-		static char *bits = "\20\1RS\2BASE-R\3RSVD1\4RSVD2\5RSVD3\6AUTO";
+		static char *bits = "\20\1RS-FEC\2FC-FEC\3NO-FEC\4RSVD2"
+		    "\5RSVD3\6auto\7module";
 
 		rc = sysctl_wire_old_buffer(req, 0);
 		if (rc != 0)
@@ -7291,19 +7305,20 @@ sysctl_fec(SYSCTL_HANDLER_ARGS)
 		 */
 		if (lc->link_ok) {
 			sbuf_printf(sb, "%b", (lc->fec & M_FW_PORT_CAP32_FEC) |
-			    (lc->requested_fec & FEC_AUTO), bits);
+			    (lc->requested_fec & (FEC_AUTO | FEC_MODULE)),
+			    bits);
 		} else {
 			sbuf_printf(sb, "%b", lc->requested_fec, bits);
 		}
 		rc = sbuf_finish(sb);
 		sbuf_delete(sb);
 	} else {
-		char s[3];
+		char s[8];
 		int n;
 
 		snprintf(s, sizeof(s), "%d",
 		    lc->requested_fec == FEC_AUTO ? -1 :
-		    lc->requested_fec & M_FW_PORT_CAP32_FEC);
+		    lc->requested_fec & (M_FW_PORT_CAP32_FEC | FEC_MODULE));
 
 		rc = sysctl_handle_string(oidp, s, sizeof(s), req);
 		if (rc != 0)
@@ -7312,12 +7327,8 @@ sysctl_fec(SYSCTL_HANDLER_ARGS)
 		n = strtol(&s[0], NULL, 0);
 		if (n < 0 || n & FEC_AUTO)
 			n = FEC_AUTO;
-		else {
-			if (n & ~M_FW_PORT_CAP32_FEC)
-				return (EINVAL);/* some other bit is set too */
-			if (!powerof2(n))
-				return (EINVAL);/* one bit can be set at most */
-		}
+		else if (n & ~(M_FW_PORT_CAP32_FEC | FEC_MODULE))
+			return (EINVAL);/* some other bit is set too */
 
 		rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK,
 		    "t4fec");
@@ -7327,15 +7338,17 @@ sysctl_fec(SYSCTL_HANDLER_ARGS)
 		old = lc->requested_fec;
 		if (n == FEC_AUTO)
 			lc->requested_fec = FEC_AUTO;
-		else if (n == 0)
+		else if (n == 0 || n == FEC_NONE)
 			lc->requested_fec = FEC_NONE;
 		else {
-			if ((lc->supported | V_FW_PORT_CAP32_FEC(n)) !=
-			    lc->supported) {
+			if ((lc->pcaps |
+			    V_FW_PORT_CAP32_FEC(n & M_FW_PORT_CAP32_FEC)) !=
+			    lc->pcaps) {
 				rc = ENOTSUP;
 				goto done;
 			}
-			lc->requested_fec = n;
+			lc->requested_fec = n & (M_FW_PORT_CAP32_FEC |
+			    FEC_MODULE);
 		}
 		fixup_link_config(pi);
 		if (pi->up_vis > 0) {
@@ -7355,6 +7368,56 @@ done:
 }
 
 static int
+sysctl_module_fec(SYSCTL_HANDLER_ARGS)
+{
+	struct port_info *pi = arg1;
+	struct adapter *sc = pi->adapter;
+	struct link_config *lc = &pi->link_cfg;
+	int rc;
+	int8_t fec;
+	struct sbuf *sb;
+	static char *bits = "\20\1RS-FEC\2FC-FEC\3NO-FEC\4RSVD2\5RSVD3";
+
+	rc = sysctl_wire_old_buffer(req, 0);
+	if (rc != 0)
+		return (rc);
+
+	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
+	if (sb == NULL)
+		return (ENOMEM);
+
+	if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4mfec") != 0)
+		return (EBUSY);
+	PORT_LOCK(pi);
+	if (pi->up_vis == 0) {
+		/*
+		 * If all the interfaces are administratively down the firmware
+		 * does not report transceiver changes.  Refresh port info here.
+		 * This is the only reason we have a synchronized op in this
+		 * function.  Just PORT_LOCK would have been enough otherwise.
+		 */
+		t4_update_port_info(pi);
+	}
+
+	fec = lc->fec_hint;
+	if (pi->mod_type == FW_PORT_MOD_TYPE_NONE ||
+	    !fec_supported(lc->pcaps)) {
+		sbuf_printf(sb, "n/a");
+	} else {
+		if (fec == 0)
+			fec = FEC_NONE;
+		sbuf_printf(sb, "%b", fec & M_FW_PORT_CAP32_FEC, bits);
+	}
+	rc = sbuf_finish(sb);
+	sbuf_delete(sb);
+
+	PORT_UNLOCK(pi);
+	end_synchronized_op(sc, 0);
+
+	return (rc);
+}
+
+static int
 sysctl_autoneg(SYSCTL_HANDLER_ARGS)
 {
 	struct port_info *pi = arg1;
@@ -7362,7 +7425,7 @@ sysctl_autoneg(SYSCTL_HANDLER_ARGS)
 	struct link_config *lc = &pi->link_cfg;
 	int rc, val;
 
-	if (lc->supported & FW_PORT_CAP32_ANEG)
+	if (lc->pcaps & FW_PORT_CAP32_ANEG)
 		val = lc->requested_aneg == AUTONEG_DISABLE ? 0 : 1;
 	else
 		val = -1;
@@ -7381,7 +7444,7 @@ sysctl_autoneg(SYSCTL_HANDLER_ARGS)
 	if (rc)
 		return (rc);
 	PORT_LOCK(pi);
-	if (val == AUTONEG_ENABLE && !(lc->supported & FW_PORT_CAP32_ANEG)) {
+	if (val == AUTONEG_ENABLE && !(lc->pcaps & FW_PORT_CAP32_ANEG)) {
 		rc = ENOTSUP;
 		goto done;
 	}


More information about the svn-src-head mailing list