From owner-svn-src-head@freebsd.org Mon Jul 17 00:42:15 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 1CE2AC7D357; Mon, 17 Jul 2017 00:42:15 +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 mx1.freebsd.org (Postfix) with ESMTPS id DE38A6C527; Mon, 17 Jul 2017 00:42:14 +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 v6H0gE8W046911; Mon, 17 Jul 2017 00:42:14 GMT (envelope-from np@FreeBSD.org) Received: (from np@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v6H0gDMU046907; Mon, 17 Jul 2017 00:42:13 GMT (envelope-from np@FreeBSD.org) Message-Id: <201707170042.v6H0gDMU046907@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: np set sender to np@FreeBSD.org using -f From: Navdeep Parhar Date: Mon, 17 Jul 2017 00:42:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r321063 - 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: 321063 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.23 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: Mon, 17 Jul 2017 00:42:15 -0000 Author: np Date: Mon Jul 17 00:42:13 2017 New Revision: 321063 URL: https://svnweb.freebsd.org/changeset/base/321063 Log: cxgbe(4): Various link/media related improvements. - Deal with changes to port_type, and not just port_mod when a transceiver is changed. This fixes hot swapping of transceivers of different types (QSFP+ or QSA or QSFP28 in a QSFP28 port, SFP+ or SFP28 in a SFP28 port, etc.). - Always refresh media information for ifconfig if the port is down. The firmware does not generate tranceiver-change interrupts unless at least one VI is enabled on the physical port. Before this change ifconfig diplayed potentially stale information for ports that were administratively down. - Always recalculate and reapply L1 config on a transceiver change. - Display PAUSE settings in ifconfig. The driver sysctls for this continue to work as well. MFC after: 2 weeks Sponsored by: Chelsio Communications Modified: head/sys/dev/cxgbe/adapter.h 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/adapter.h ============================================================================== --- head/sys/dev/cxgbe/adapter.h Sun Jul 16 21:07:58 2017 (r321062) +++ head/sys/dev/cxgbe/adapter.h Mon Jul 17 00:42:13 2017 (r321063) @@ -1036,10 +1036,10 @@ adap2pinfo(struct adapter *sc, int idx) } static inline void -t4_os_set_hw_addr(struct adapter *sc, int idx, uint8_t hw_addr[]) +t4_os_set_hw_addr(struct port_info *pi, uint8_t hw_addr[]) { - bcopy(hw_addr, sc->port[idx]->vi[0].hw_addr, ETHER_ADDR_LEN); + bcopy(hw_addr, pi->vi[0].hw_addr, ETHER_ADDR_LEN); } static inline bool @@ -1089,24 +1089,6 @@ port_top_speed(const struct port_info *pi) } static inline int -port_top_speed_raw(const struct port_info *pi) -{ - - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100G) - return (FW_PORT_CAP_SPEED_100G); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_40G) - return (FW_PORT_CAP_SPEED_40G); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_25G) - return (FW_PORT_CAP_SPEED_25G); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G) - return (FW_PORT_CAP_SPEED_10G); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_1G) - return (FW_PORT_CAP_SPEED_1G); - - return (0); -} - -static inline int tx_resume_threshold(struct sge_eq *eq) { @@ -1142,8 +1124,8 @@ extern device_method_t cxgbe_methods[]; int t4_os_find_pci_capability(struct adapter *, int); int t4_os_pci_save_state(struct adapter *); int t4_os_pci_restore_state(struct adapter *); -void t4_os_portmod_changed(const struct adapter *, int); -void t4_os_link_changed(struct adapter *, int, int); +void t4_os_portmod_changed(struct port_info *, int, int, struct link_config *); +void t4_os_link_changed(struct port_info *, struct link_config *); void t4_iterate(void (*)(struct adapter *, void *), void *); void t4_init_devnames(struct adapter *); void t4_add_adapter(struct adapter *); Modified: head/sys/dev/cxgbe/common/common.h ============================================================================== --- head/sys/dev/cxgbe/common/common.h Sun Jul 16 21:07:58 2017 (r321062) +++ head/sys/dev/cxgbe/common/common.h Mon Jul 17 00:42:13 2017 (r321063) @@ -399,16 +399,18 @@ struct trace_params { }; struct link_config { + /* OS-specific code owns all the requested_* fields */ + unsigned char requested_aneg; /* link aneg user has requested */ + unsigned char requested_fc; /* flow control user has requested */ + unsigned char requested_fec; /* FEC user has requested */ + unsigned int requested_speed; /* speed user has requested */ + unsigned short supported; /* link capabilities */ unsigned short advertising; /* advertised capabilities */ unsigned short lp_advertising; /* peer advertised capabilities */ - unsigned int requested_speed; /* speed user has requested */ unsigned int speed; /* actual link speed */ - unsigned char requested_fc; /* flow control user has requested */ unsigned char fc; /* actual link flow control */ - unsigned char requested_fec; /* FEC user has requested */ unsigned char fec; /* actual FEC */ - unsigned char autoneg; /* autonegotiating? */ unsigned char link_ok; /* link up? */ unsigned char link_down_rc; /* link down reason */ }; @@ -766,6 +768,7 @@ int t4_sge_ctxt_rd_bd(struct adapter *adap, unsigned i u32 *data); int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox); const char *t4_link_down_rc_str(unsigned char link_down_rc); +int t4_update_port_info(struct port_info *pi); int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl); int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox, u32 addr, u32 val); int t4_sched_config(struct adapter *adapter, int type, int minmaxen, Modified: head/sys/dev/cxgbe/common/t4_hw.c ============================================================================== --- head/sys/dev/cxgbe/common/t4_hw.c Sun Jul 16 21:07:58 2017 (r321062) +++ head/sys/dev/cxgbe/common/t4_hw.c Mon Jul 17 00:42:13 2017 (r321063) @@ -3683,9 +3683,6 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_bu } } -#define ADVERT_MASK (V_FW_PORT_CAP_SPEED(M_FW_PORT_CAP_SPEED) | \ - FW_PORT_CAP_ANEG) - /** * t4_link_l1cfg - apply link configuration to MAC/PHY * @phy: the PHY to setup @@ -3704,7 +3701,7 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int m { struct fw_port_cmd c; unsigned int mdi = V_FW_PORT_CAP_MDI(FW_PORT_CAP_MDI_AUTO); - unsigned int fc, fec; + unsigned int aneg, fc, fec, speed; fc = 0; if (lc->requested_fc & PAUSE_RX) @@ -3714,12 +3711,41 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int m fec = 0; if (lc->requested_fec & FEC_RS) - fec |= FW_PORT_CAP_FEC_RS; - if (lc->requested_fec & FEC_BASER_RS) - fec |= FW_PORT_CAP_FEC_BASER_RS; - if (lc->requested_fec & FEC_RESERVED) - fec |= FW_PORT_CAP_FEC_RESERVED; + fec = FW_PORT_CAP_FEC_RS; + else if (lc->requested_fec & FEC_BASER_RS) + fec = FW_PORT_CAP_FEC_BASER_RS; + else if (lc->requested_fec & FEC_RESERVED) + fec = FW_PORT_CAP_FEC_RESERVED; + if (!(lc->supported & FW_PORT_CAP_ANEG) || + lc->requested_aneg == AUTONEG_DISABLE) { + aneg = 0; + switch (lc->requested_speed) { + case 100: + speed = FW_PORT_CAP_SPEED_100G; + break; + case 40: + speed = FW_PORT_CAP_SPEED_40G; + break; + case 25: + speed = FW_PORT_CAP_SPEED_25G; + break; + case 10: + speed = FW_PORT_CAP_SPEED_10G; + break; + case 1: + speed = FW_PORT_CAP_SPEED_1G; + break; + default: + return -EINVAL; + break; + } + } else { + aneg = FW_PORT_CAP_ANEG; + speed = lc->supported & + V_FW_PORT_CAP_SPEED(M_FW_PORT_CAP_SPEED); + } + memset(&c, 0, sizeof(c)); c.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) | F_FW_CMD_REQUEST | F_FW_CMD_EXEC | @@ -3727,21 +3753,9 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int m c.action_to_len16 = cpu_to_be32(V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG) | FW_LEN16(c)); + c.u.l1cfg.rcap = cpu_to_be32(aneg | speed | fc | fec | mdi); - if (!(lc->supported & FW_PORT_CAP_ANEG)) { - c.u.l1cfg.rcap = cpu_to_be32((lc->supported & ADVERT_MASK) | - fc | fec); - lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; - lc->fec = lc->requested_fec; - } else if (lc->autoneg == AUTONEG_DISABLE) { - c.u.l1cfg.rcap = cpu_to_be32(lc->requested_speed | - fc | fec | mdi); - lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; - lc->fec = lc->requested_fec; - } else - c.u.l1cfg.rcap = cpu_to_be32(lc->advertising | fc | fec | mdi); - - return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); + return t4_wr_mbox_ns(adap, mbox, &c, sizeof(c), NULL); } /** @@ -7474,7 +7488,91 @@ const char *t4_link_down_rc_str(unsigned char link_dow return reason[link_down_rc]; } +/* + * Updates all fields owned by the common code in port_info and link_config + * based on information provided by the firmware. Does not touch any + * requested_* field. + */ +static void handle_port_info(struct port_info *pi, const struct fw_port_info *p) +{ + struct link_config *lc = &pi->link_cfg; + int speed; + unsigned char fc, fec; + u32 stat = be32_to_cpu(p->lstatus_to_modtype); + + pi->port_type = G_FW_PORT_CMD_PTYPE(stat); + pi->mod_type = G_FW_PORT_CMD_MODTYPE(stat); + pi->mdio_addr = stat & F_FW_PORT_CMD_MDIOCAP ? + G_FW_PORT_CMD_MDIOADDR(stat) : -1; + + lc->supported = be16_to_cpu(p->pcap); + lc->advertising = be16_to_cpu(p->acap); + lc->lp_advertising = be16_to_cpu(p->lpacap); + lc->link_ok = (stat & F_FW_PORT_CMD_LSTATUS) != 0; + lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC(stat); + + speed = 0; + if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100M)) + speed = 100; + else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_1G)) + speed = 1000; + else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_10G)) + speed = 10000; + else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_25G)) + speed = 25000; + else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_40G)) + speed = 40000; + else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100G)) + speed = 100000; + lc->speed = speed; + + fc = 0; + if (stat & F_FW_PORT_CMD_RXPAUSE) + fc |= PAUSE_RX; + if (stat & F_FW_PORT_CMD_TXPAUSE) + fc |= PAUSE_TX; + lc->fc = fc; + + fec = 0; + if (lc->advertising & FW_PORT_CAP_FEC_RS) + fec |= FEC_RS; + if (lc->advertising & FW_PORT_CAP_FEC_BASER_RS) + fec |= FEC_BASER_RS; + if (lc->advertising & FW_PORT_CAP_FEC_RESERVED) + fec |= FEC_RESERVED; + lc->fec = fec; +} + /** + * t4_update_port_info - retrieve and update port information if changed + * @pi: the port_info + * + * We issue a Get Port Information Command to the Firmware and, if + * successful, we check to see if anything is different from what we + * last recorded and update things accordingly. + */ + int t4_update_port_info(struct port_info *pi) + { + struct fw_port_cmd port_cmd; + int ret; + + memset(&port_cmd, 0, sizeof port_cmd); + port_cmd.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) | + F_FW_CMD_REQUEST | F_FW_CMD_READ | + V_FW_PORT_CMD_PORTID(pi->tx_chan)); + port_cmd.action_to_len16 = cpu_to_be32( + V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_GET_PORT_INFO) | + FW_LEN16(port_cmd)); + ret = t4_wr_mbox_ns(pi->adapter, pi->adapter->mbox, + &port_cmd, sizeof(port_cmd), &port_cmd); + if (ret) + return ret; + + handle_port_info(pi, &port_cmd.u.info); + return 0; +} + +/** * t4_handle_fw_rpl - process a FW reply message * @adap: the adapter * @rpl: start of the FW message @@ -7490,52 +7588,31 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be6 if (opcode == FW_PORT_CMD && action == FW_PORT_ACTION_GET_PORT_INFO) { /* link/module state change message */ - int speed = 0, fc = 0, i; + int i, old_ptype, old_mtype; int chan = G_FW_PORT_CMD_PORTID(be32_to_cpu(p->op_to_portid)); struct port_info *pi = NULL; - struct link_config *lc; - u32 stat = be32_to_cpu(p->u.info.lstatus_to_modtype); - int link_ok = (stat & F_FW_PORT_CMD_LSTATUS) != 0; - u32 mod = G_FW_PORT_CMD_MODTYPE(stat); + struct link_config *lc, old_lc; - if (stat & F_FW_PORT_CMD_RXPAUSE) - fc |= PAUSE_RX; - if (stat & F_FW_PORT_CMD_TXPAUSE) - fc |= PAUSE_TX; - if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100G)) - speed = 100000; - for_each_port(adap, i) { pi = adap2pinfo(adap, i); if (pi->tx_chan == chan) break; } + lc = &pi->link_cfg; + old_lc = *lc; + old_ptype = pi->port_type; + old_mtype = pi->mod_type; - if (mod != pi->mod_type) { - pi->mod_type = mod; - t4_os_portmod_changed(adap, i); + handle_port_info(pi, &p->u.info); + if (old_ptype != pi->port_type || old_mtype != pi->mod_type) { + t4_os_portmod_changed(pi, old_ptype, old_mtype, + &old_lc); } - if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc) { /* something changed */ - if (!link_ok && lc->link_ok) - lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC(stat); - lc->link_ok = link_ok; - lc->speed = speed; - lc->fc = fc; - lc->supported = be16_to_cpu(p->u.info.pcap); - lc->lp_advertising = be16_to_cpu(p->u.info.lpacap); - t4_os_link_changed(adap, i, link_ok); + if (old_lc.link_ok != lc->link_ok || + old_lc.speed != lc->speed || + old_lc.fc != lc->fc) { + t4_os_link_changed(pi, &old_lc); } } else { CH_WARN_RATELIMIT(adap, "Unknown firmware reply %d\n", opcode); @@ -7566,48 +7643,6 @@ static void get_pci_mode(struct adapter *adapter, } } -/** - * init_link_config - initialize a link's SW state - * @lc: structure holding the link state - * @pcaps: supported link capabilities - * @acaps: advertised link capabilities - * - * Initializes the SW state maintained for each link, including the link's - * capabilities and default speed/flow-control/autonegotiation settings. - */ -static void init_link_config(struct link_config *lc, unsigned int pcaps, - unsigned int acaps) -{ - unsigned int fec; - - lc->supported = pcaps; - lc->lp_advertising = 0; - lc->requested_speed = 0; - lc->speed = 0; - lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - lc->link_ok = 0; - lc->link_down_rc = 255; - - fec = 0; - if (acaps & FW_PORT_CAP_FEC_RS) - fec |= FEC_RS; - if (acaps & FW_PORT_CAP_FEC_BASER_RS) - fec |= FEC_BASER_RS; - if (acaps & FW_PORT_CAP_FEC_RESERVED) - fec |= FEC_RESERVED; - fec &= G_FW_PORT_CAP_FEC(lc->supported); - lc->requested_fec = lc->fec = fec; - - if (lc->supported & FW_PORT_CAP_ANEG) { - lc->advertising = lc->supported & ADVERT_MASK; - lc->autoneg = AUTONEG_ENABLE; - lc->requested_fc |= PAUSE_AUTONEG; - } else { - lc->advertising = 0; - lc->autoneg = AUTONEG_DISABLE; - } -} - struct flash_desc { u32 vendor_and_model_id; u32 size_mb; @@ -8144,24 +8179,7 @@ int t4_port_init(struct adapter *adap, int mbox, int p if (!(adap->flags & IS_VF) || adap->params.vfres.r_caps & FW_CMD_CAP_PORT) { - c.op_to_portid = htonl(V_FW_CMD_OP(FW_PORT_CMD) | - F_FW_CMD_REQUEST | F_FW_CMD_READ | - V_FW_PORT_CMD_PORTID(j)); - c.action_to_len16 = htonl( - V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(c)); - ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); - if (ret) - return ret; - - ret = be32_to_cpu(c.u.info.lstatus_to_modtype); - p->mdio_addr = (ret & F_FW_PORT_CMD_MDIOCAP) ? - G_FW_PORT_CMD_MDIOADDR(ret) : -1; - p->port_type = G_FW_PORT_CMD_PTYPE(ret); - p->mod_type = G_FW_PORT_CMD_MODTYPE(ret); - - init_link_config(&p->link_cfg, be16_to_cpu(c.u.info.pcap), - be16_to_cpu(c.u.info.acap)); + t4_update_port_info(p); } ret = t4_alloc_vi(adap, mbox, j, pf, vf, 1, addr, &rss_size); @@ -8177,7 +8195,7 @@ int t4_port_init(struct adapter *adap, int mbox, int p p->rx_chan_map = t4_get_mps_bg_map(adap, j); p->lport = j; p->vi[0].rss_size = rss_size; - t4_os_set_hw_addr(adap, p->port_id, addr); + t4_os_set_hw_addr(p, addr); param = V_FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | V_FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_RSSINFO) | Modified: head/sys/dev/cxgbe/t4_main.c ============================================================================== --- head/sys/dev/cxgbe/t4_main.c Sun Jul 16 21:07:58 2017 (r321062) +++ head/sys/dev/cxgbe/t4_main.c Mon Jul 17 00:42:13 2017 (r321063) @@ -487,6 +487,7 @@ static int get_params__post_init(struct adapter *); static int set_params__post_init(struct adapter *); static void t4_set_desc(struct adapter *); static void build_medialist(struct port_info *, struct ifmedia *); +static void init_l1cfg(struct port_info *); static int cxgbe_init_synchronized(struct vi_info *); static int cxgbe_uninit_synchronized(struct vi_info *); static void quiesce_txq(struct adapter *, struct sge_txq *); @@ -955,7 +956,6 @@ t4_attach(device_t dev) n10g = n1g = 0; for_each_port(sc, i) { struct port_info *pi; - struct link_config *lc; pi = malloc(sizeof(*pi), M_CXGBE, M_ZERO | M_WAITOK); sc->port[i] = pi; @@ -984,28 +984,6 @@ t4_attach(device_t dev) goto done; } - lc = &pi->link_cfg; - lc->requested_fc &= ~(PAUSE_TX | PAUSE_RX); - lc->requested_fc |= t4_pause_settings; - if (t4_fec != -1) { - lc->requested_fec = t4_fec & - G_FW_PORT_CAP_FEC(lc->supported); - } - if (lc->supported & FW_PORT_CAP_ANEG && t4_autoneg != -1) { - lc->autoneg = t4_autoneg ? AUTONEG_ENABLE : - AUTONEG_DISABLE; - } - lc->requested_speed = port_top_speed_raw(pi); - - rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc); - if (rc != 0) { - device_printf(dev, "port %d l1cfg failed: %d\n", i, rc); - free(pi->vi, M_CXGBE); - free(pi, M_CXGBE); - sc->port[i] = NULL; - goto done; - } - snprintf(pi->lockname, sizeof(pi->lockname), "%sp%d", device_get_nameunit(dev), i); mtx_init(&pi->pi_lock, pi->lockname, 0, MTX_DEF); @@ -1480,7 +1458,6 @@ cxgbe_vi_attach(device_t dev, struct vi_info *vi) /* Initialize ifmedia for this VI */ ifmedia_init(&vi->media, IFM_IMASK, cxgbe_media_change, cxgbe_media_status); - build_medialist(vi->pi, &vi->media); vi->vlan_c = EVENTHANDLER_REGISTER(vlan_config, cxgbe_vlan_config, ifp, EVENTHANDLER_PRI_ANY); @@ -2022,32 +1999,63 @@ cxgbe_media_status(struct ifnet *ifp, struct ifmediare struct vi_info *vi = ifp->if_softc; struct port_info *pi = vi->pi; struct ifmedia_entry *cur; - int speed = pi->link_cfg.speed; + struct link_config *lc = &pi->link_cfg; + /* + * If all the interfaces are administratively down the firmware does not + * report transceiver changes. Refresh port info here so that ifconfig + * displays accurate information at all times. + */ + if (begin_synchronized_op(pi->adapter, NULL, SLEEP_OK | INTR_OK, + "t4med") == 0) { + PORT_LOCK(pi); + if (pi->up_vis == 0) { + t4_update_port_info(pi); + build_medialist(pi, &vi->media); + } + PORT_UNLOCK(pi); + end_synchronized_op(pi->adapter, 0); + } + cur = vi->media.ifm_cur; ifmr->ifm_status = IFM_AVALID; - if (!pi->link_cfg.link_ok) + if (lc->link_ok == 0) return; ifmr->ifm_status |= IFM_ACTIVE; + ifmr->ifm_active &= ~(IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE); + if (lc->fc & PAUSE_RX) + ifmr->ifm_active |= IFM_ETH_RXPAUSE; + if (lc->fc & PAUSE_TX) + ifmr->ifm_active |= IFM_ETH_TXPAUSE; /* active and current will differ iff current media is autoselect. */ if (IFM_SUBTYPE(cur->ifm_media) != IFM_AUTO) return; ifmr->ifm_active = IFM_ETHER | IFM_FDX; - if (speed == 10000) + if (lc->fc & PAUSE_RX) + ifmr->ifm_active |= IFM_ETH_RXPAUSE; + if (lc->fc & PAUSE_TX) + ifmr->ifm_active |= IFM_ETH_TXPAUSE; + switch (lc->speed) { + case 10000: ifmr->ifm_active |= IFM_10G_T; - else if (speed == 1000) + break; + case 1000: ifmr->ifm_active |= IFM_1000_T; - else if (speed == 100) + break; + case 100: ifmr->ifm_active |= IFM_100_TX; - else if (speed == 10) + break; + case 10: ifmr->ifm_active |= IFM_10_T; - else - KASSERT(0, ("%s: link up but speed unknown (%u)", __func__, - speed)); + break; + default: + device_printf(vi->dev, "link up but speed unknown (%u)\n", + lc->speed); + } } static int @@ -3590,11 +3598,18 @@ build_medialist(struct port_info *pi, struct ifmedia * { int m; - PORT_LOCK(pi); + PORT_LOCK_ASSERT_OWNED(pi); ifmedia_removeall(media); - m = IFM_ETHER | IFM_FDX; + /* + * XXX: Would it be better to ifmedia_add all 4 combinations of pause + * settings for every speed instead of just txpause|rxpause? ifconfig + * media display looks much better if autoselect is the only case where + * ifm_current is different from ifm_active. If the user picks anything + * except txpause|rxpause the display is ugly. + */ + m = IFM_ETHER | IFM_FDX | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE; switch(pi->port_type) { case FW_PORT_TYPE_BT_XFI: @@ -3770,8 +3785,53 @@ build_medialist(struct port_info *pi, struct ifmedia * ifmedia_set(media, m | IFM_UNKNOWN); break; } +} - PORT_UNLOCK(pi); +/* + * Update all the requested_* fields in the link config and then send a mailbox + * command to apply the settings. + */ +static void +init_l1cfg(struct port_info *pi) +{ + struct adapter *sc = pi->adapter; + struct link_config *lc = &pi->link_cfg; + int rc; + + ASSERT_SYNCHRONIZED_OP(sc); + + if (t4_autoneg != 0 && lc->supported & FW_PORT_CAP_ANEG) { + lc->requested_aneg = AUTONEG_ENABLE; + lc->requested_speed = 0; + } else { + lc->requested_aneg = AUTONEG_DISABLE; + lc->requested_speed = port_top_speed(pi); /* in Gbps */ + } + + lc->requested_fc = t4_pause_settings & (PAUSE_TX | PAUSE_RX); + + if (t4_fec != -1) { + lc->requested_fec = t4_fec & (FEC_RS | FEC_BASER_RS | + FEC_RESERVED); + } else { + /* Use the suggested value provided by the firmware in acaps */ + if (lc->advertising & FW_PORT_CAP_FEC_RS) + lc->requested_fec = FEC_RS; + else if (lc->advertising & FW_PORT_CAP_FEC_BASER_RS) + lc->requested_fec = FEC_BASER_RS; + else if (lc->advertising & FW_PORT_CAP_FEC_RESERVED) + lc->requested_fec = FEC_RESERVED; + else + lc->requested_fec = 0; + } + + rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc); + if (rc != 0) { + device_printf(pi->dev, "l1cfg failed: %d\n", rc); + } else { + lc->fc = lc->requested_fc; + lc->fec = lc->requested_fec; + } } #define FW_MAC_EXACT_CHUNK 7 @@ -4053,8 +4113,12 @@ cxgbe_init_synchronized(struct vi_info *vi) /* all ok */ PORT_LOCK(pi); + if (pi->up_vis++ == 0) { + t4_update_port_info(pi); + build_medialist(vi->pi, &vi->media); + init_l1cfg(pi); + } ifp->if_drv_flags |= IFF_DRV_RUNNING; - pi->up_vis++; if (pi->nvi > 1 || sc->flags & IS_VF) callout_reset(&vi->tick, hz, vi_tick, vi); @@ -4127,7 +4191,7 @@ cxgbe_uninit_synchronized(struct vi_info *vi) pi->link_cfg.link_ok = 0; pi->link_cfg.speed = 0; pi->link_cfg.link_down_rc = 255; - t4_os_link_changed(sc, pi->port_id, 0); + t4_os_link_changed(pi, NULL); return (0); } @@ -5813,6 +5877,9 @@ sysctl_pause_settings(SYSCTL_HANDLER_ARGS) lc->requested_fc &= ~(PAUSE_TX | PAUSE_RX); lc->requested_fc |= n; rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc); + if (rc == 0) { + lc->fc = lc->requested_fc; + } } end_synchronized_op(sc, 0); } @@ -5870,6 +5937,9 @@ sysctl_fec(SYSCTL_HANDLER_ARGS) lc->requested_fec = n & G_FW_PORT_CAP_FEC(lc->supported); rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc); + if (rc == 0) { + lc->fec = lc->requested_fec; + } } end_synchronized_op(sc, 0); } @@ -5886,7 +5956,7 @@ sysctl_autoneg(SYSCTL_HANDLER_ARGS) int rc, val, old; if (lc->supported & FW_PORT_CAP_ANEG) - val = lc->autoneg == AUTONEG_ENABLE ? 1 : 0; + val = lc->requested_aneg == AUTONEG_ENABLE ? 1 : 0; else val = -1; rc = sysctl_handle_int(oidp, &val, 0, req); @@ -5901,18 +5971,18 @@ sysctl_autoneg(SYSCTL_HANDLER_ARGS) val = AUTONEG_ENABLE; else return (EINVAL); - if (lc->autoneg == val) + if (lc->requested_aneg == val) return (0); /* no change */ rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK, "t4aneg"); if (rc) return (rc); - old = lc->autoneg; - lc->autoneg = val; + old = lc->requested_aneg; + lc->requested_aneg = val; rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc); if (rc != 0) - lc->autoneg = old; + lc->requested_aneg = old; end_synchronized_op(sc, 0); return (rc); } @@ -8874,9 +8944,9 @@ t4_os_pci_restore_state(struct adapter *sc) } void -t4_os_portmod_changed(const struct adapter *sc, int idx) +t4_os_portmod_changed(struct port_info *pi, int old_ptype, int old_mtype, + struct link_config *old_lc) { - struct port_info *pi = sc->port[idx]; struct vi_info *vi; struct ifnet *ifp; int v; @@ -8884,9 +8954,15 @@ t4_os_portmod_changed(const struct adapter *sc, int id NULL, "LR", "SR", "ER", "TWINAX", "active TWINAX", "LRM" }; + PORT_LOCK(pi); for_each_vi(pi, v, vi) { build_medialist(pi, &vi->media); } + PORT_UNLOCK(pi); + if (begin_synchronized_op(pi->adapter, vi, HOLD_LOCK, "t4mod") == 0) { + init_l1cfg(pi); + end_synchronized_op(pi->adapter, LOCK_HELD); + } ifp = pi->vi[0].ifp; if (pi->mod_type == FW_PORT_MOD_TYPE_NONE) @@ -8896,8 +8972,8 @@ t4_os_portmod_changed(const struct adapter *sc, int id else if (pi->mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED) if_printf(ifp, "unsupported transceiver inserted.\n"); else if (pi->mod_type > 0 && pi->mod_type < nitems(mod_str)) { - if_printf(ifp, "%s transceiver inserted.\n", - mod_str[pi->mod_type]); + if_printf(ifp, "%dGbps %s transceiver inserted.\n", + port_top_speed(pi), mod_str[pi->mod_type]); } else { if_printf(ifp, "transceiver (type %d) inserted.\n", pi->mod_type); @@ -8905,11 +8981,11 @@ t4_os_portmod_changed(const struct adapter *sc, int id } void -t4_os_link_changed(struct adapter *sc, int idx, int link_stat) +t4_os_link_changed(struct port_info *pi, struct link_config *old_lc) { - struct port_info *pi = sc->port[idx]; struct vi_info *vi; struct ifnet *ifp; + struct link_config *lc; int v; for_each_vi(pi, v, vi) { @@ -8917,8 +8993,9 @@ t4_os_link_changed(struct adapter *sc, int idx, int li if (ifp == NULL) continue; - if (link_stat) { - ifp->if_baudrate = IF_Mbps(pi->link_cfg.speed); + lc = &pi->link_cfg; + if (lc->link_ok) { + ifp->if_baudrate = IF_Mbps(lc->speed); if_link_state_change(ifp, LINK_STATE_UP); } else { if_link_state_change(ifp, LINK_STATE_DOWN);