From owner-svn-src-head@freebsd.org Tue Nov 26 05:54:26 2019 Return-Path: Delivered-To: svn-src-head@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 307B61A9F63; Tue, 26 Nov 2019 05:54:26 +0000 (UTC) (envelope-from np@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47MY4y0rCdz4QTs; Tue, 26 Nov 2019 05:54:26 +0000 (UTC) (envelope-from np@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id EF9E420A4F; Tue, 26 Nov 2019 05:54:25 +0000 (UTC) (envelope-from np@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xAQ5sPVm044126; Tue, 26 Nov 2019 05:54:25 GMT (envelope-from np@FreeBSD.org) Received: (from np@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xAQ5sPQY044123; Tue, 26 Nov 2019 05:54:25 GMT (envelope-from np@FreeBSD.org) Message-Id: <201911260554.xAQ5sPQY044123@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: np set sender to np@FreeBSD.org using -f From: Navdeep Parhar Date: Tue, 26 Nov 2019 05:54:25 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r355107 - in head/sys/dev/cxgbe: . common X-SVN-Group: head X-SVN-Commit-Author: np X-SVN-Commit-Paths: in head/sys/dev/cxgbe: . common X-SVN-Commit-Revision: 355107 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 26 Nov 2019 05:54:26 -0000 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; }