Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 17 Jul 2017 00:42:13 +0000 (UTC)
From:      Navdeep Parhar <np@FreeBSD.org>
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
Message-ID:  <201707170042.v6H0gDMU046907@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201707170042.v6H0gDMU046907>