git: 448bcd01dc5e - main - cxgbe(4): internal knob for flexible control over FEC selection.

From: Navdeep Parhar <np_at_FreeBSD.org>
Date: Wed, 10 Nov 2021 23:23:04 UTC
The branch main has been updated by np:

URL: https://cgit.FreeBSD.org/src/commit/?id=448bcd01dc5edcbbf23975588f131e13cd09778f

commit 448bcd01dc5edcbbf23975588f131e13cd09778f
Author:     Navdeep Parhar <np@FreeBSD.org>
AuthorDate: 2021-11-10 19:38:54 +0000
Commit:     Navdeep Parhar <np@FreeBSD.org>
CommitDate: 2021-11-10 23:16:53 +0000

    cxgbe(4): internal knob for flexible control over FEC selection.
    
    Recent firmwares have support for autonomous FEC selection and a "force"
    knob to let the driver control this behavior (or not) in a fine grained
    manner. This change adds a driver knob so that all the different ways of
    configuring the link FEC can be exercised. Note that this controls the
    internal driver/firmware interaction for link configuration and is not
    meant for general use.
    
    MFC after:      1 week
    Sponsored by:   Chelsio Communications
---
 sys/dev/cxgbe/common/common.h |  2 ++
 sys/dev/cxgbe/common/t4_hw.c  | 30 +++++++++++++++++-----
 sys/dev/cxgbe/t4_main.c       | 60 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 85 insertions(+), 7 deletions(-)

diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h
index 50859e868b9d..bee4f58f693c 100644
--- a/sys/dev/cxgbe/common/common.h
+++ b/sys/dev/cxgbe/common/common.h
@@ -443,9 +443,11 @@ struct link_config {
 	int8_t requested_aneg;	/* link autonegotiation */
 	int8_t requested_fc;	/* flow control */
 	int8_t requested_fec;	/* FEC */
+	int8_t force_fec;	/* FORCE_FEC in L1_CFG32 command. */
 	u_int requested_speed;	/* speed (Mbps) */
 	uint32_t requested_caps;/* rcap in last l1cfg issued by the driver. */
 
+	/* These are populated with information from the firmware. */
 	uint32_t pcaps;		/* link capabilities */
 	uint32_t acaps;		/* advertised capabilities */
 	uint32_t lpacaps;	/* peer advertised capabilities */
diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c
index b9bf5df5ccc6..d6f85a1fcd34 100644
--- a/sys/dev/cxgbe/common/t4_hw.c
+++ b/sys/dev/cxgbe/common/t4_hw.c
@@ -3917,9 +3917,19 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
 		speed = fwcap_top_speed(lc->pcaps);
 
 	fec = 0;
+#ifdef INVARIANTS
+	if (lc->force_fec != 0)
+		MPASS(lc->pcaps & FW_PORT_CAP32_FORCE_FEC);
+#endif
 	if (fec_supported(speed)) {
 		if (lc->requested_fec == FEC_AUTO) {
-			if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) {
+			if (lc->force_fec > 0) {
+				/*
+				 * Must use FORCE_FEC even though requested FEC
+				 * is AUTO. Set all the FEC bits valid for the
+				 * speed and let the firmware pick one.
+				 */
+				fec |= 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;
@@ -3929,20 +3939,26 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
 					fec |= FW_PORT_CAP32_FEC_NO_FEC;
 				}
 			} else {
-				/* Set only 1b with old firmwares. */
+				/*
+				 * Set only 1b. Old firmwares can't deal with
+				 * multiple bits and new firmwares are free to
+				 * ignore this and try whatever FECs they want
+				 * because we aren't setting FORCE_FEC here.
+				 */
 				fec |= fec_to_fwcap(lc->fec_hint);
 			}
 		} else {
+			/*
+			 * User has explicitly requested some FEC(s). Set
+			 * FORCE_FEC unless prohibited from using it.
+			 */
+			if (lc->force_fec != 0)
+				fec |= FW_PORT_CAP32_FORCE_FEC;
 			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. */
diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c
index b8e4ff7e9c0f..44a9a1343016 100644
--- a/sys/dev/cxgbe/t4_main.c
+++ b/sys/dev/cxgbe/t4_main.c
@@ -517,6 +517,21 @@ static int t4_fec = -1;
 SYSCTL_INT(_hw_cxgbe, OID_AUTO, fec, CTLFLAG_RDTUN, &t4_fec, 0,
     "Forward Error Correction (bit 0 = RS, bit 1 = BASER_RS)");
 
+/*
+ * Controls when the driver sets the FORCE_FEC bit in the L1_CFG32 that it
+ * issues to the firmware.  If the firmware doesn't support FORCE_FEC then the
+ * driver runs as if this is set to 0.
+ * -1 to set FORCE_FEC iff requested_fec != AUTO. Multiple FEC bits are okay.
+ *  0 to never set FORCE_FEC. requested_fec = AUTO means use the hint from the
+ *    transceiver. Multiple FEC bits may not be okay but will be passed on to
+ *    the firmware anyway (may result in l1cfg errors with old firmwares).
+ *  1 to always set FORCE_FEC. Multiple FEC bits are okay. requested_fec = AUTO
+ *    means set all FEC bits that are valid for the speed.
+ */
+static int t4_force_fec = -1;
+SYSCTL_INT(_hw_cxgbe, OID_AUTO, force_fec, CTLFLAG_RDTUN, &t4_force_fec, 0,
+    "Controls the use of FORCE_FEC bit in L1 configuration.");
+
 /*
  * Link autonegotiation.
  * -1 to run with the firmware default.
@@ -775,6 +790,7 @@ static int sysctl_link_fec(SYSCTL_HANDLER_ARGS);
 static int sysctl_requested_fec(SYSCTL_HANDLER_ARGS);
 static int sysctl_module_fec(SYSCTL_HANDLER_ARGS);
 static int sysctl_autoneg(SYSCTL_HANDLER_ARGS);
+static int sysctl_force_fec(SYSCTL_HANDLER_ARGS);
 static int sysctl_handle_t4_reg64(SYSCTL_HANDLER_ARGS);
 static int sysctl_temperature(SYSCTL_HANDLER_ARGS);
 static int sysctl_vdd(SYSCTL_HANDLER_ARGS);
@@ -5703,6 +5719,7 @@ init_link_config(struct port_info *pi)
 	struct link_config *lc = &pi->link_cfg;
 
 	PORT_LOCK_ASSERT_OWNED(pi);
+	MPASS(lc->pcaps != 0);
 
 	lc->requested_caps = 0;
 	lc->requested_speed = 0;
@@ -5728,6 +5745,13 @@ init_link_config(struct port_info *pi)
 		if (lc->requested_fec == 0)
 			lc->requested_fec = FEC_AUTO;
 	}
+	lc->force_fec = 0;
+	if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) {
+		if (t4_force_fec < 0)
+			lc->force_fec = -1;
+		else if (t4_force_fec > 0)
+			lc->force_fec = 1;
+	}
 }
 
 /*
@@ -7774,6 +7798,9 @@ cxgbe_sysctls(struct port_info *pi)
 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, pi, 0,
 	    sysctl_autoneg, "I",
 	    "autonegotiation (-1 = not supported)");
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "force_fec",
+	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, pi, 0,
+	    sysctl_force_fec, "I", "when to use FORCE_FEC bit for link config");
 
 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rcaps", CTLFLAG_RD,
 	    &pi->link_cfg.requested_caps, 0, "L1 config requested by driver");
@@ -8492,6 +8519,39 @@ done:
 	return (rc);
 }
 
+static int
+sysctl_force_fec(SYSCTL_HANDLER_ARGS)
+{
+	struct port_info *pi = arg1;
+	struct adapter *sc = pi->adapter;
+	struct link_config *lc = &pi->link_cfg;
+	int rc, val;
+
+	val = lc->force_fec;
+	MPASS(val >= -1 && val <= 1);
+	rc = sysctl_handle_int(oidp, &val, 0, req);
+	if (rc != 0 || req->newptr == NULL)
+		return (rc);
+	if (!(lc->pcaps & FW_PORT_CAP32_FORCE_FEC))
+		return (ENOTSUP);
+	if (val < -1 || val > 1)
+		return (EINVAL);
+
+	rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK, "t4ff");
+	if (rc)
+		return (rc);
+	PORT_LOCK(pi);
+	lc->force_fec = val;
+	if (!hw_off_limits(sc)) {
+		fixup_link_config(pi);
+		if (pi->up_vis > 0)
+			rc = apply_link_config(pi);
+	}
+	PORT_UNLOCK(pi);
+	end_synchronized_op(sc, 0);
+	return (rc);
+}
+
 static int
 sysctl_handle_t4_reg64(SYSCTL_HANDLER_ARGS)
 {