Blame SOURCES/dpdk-17.11-i40e-fix-link-status-timeout.patch

a6040a
From: Fan Zhang <roy.fan.zhang@intel.com>
a6040a
Subject: [dpdk-dev,v2] drivers/i40e: fix link update no wait
a6040a
Date: Thu,  8 Mar 2018 12:17:52 +0000
a6040a
a6040a
Fixes: 263333bbb7a9 ("i40e: fix link status timeout")
a6040a
Cc: cunming.liang@intel.com
a6040a
Cc: stable@dpdk.org
a6040a
a6040a
In i40e_dev_link_update() the driver obtains the link status
a6040a
info via admin queue command despite of "no_wait" flag. This
a6040a
requires relatively long time and may be a problem to some
a6040a
application such as ovs-dpdk
a6040a
(https://bugzilla.redhat.com/show_bug.cgi?id=1551761).
a6040a
a6040a
This patch aims to fix the problem by using a different
a6040a
approach of obtaining link status for i40e NIC without waiting.
a6040a
Instead of getting the link status via admin queue command,
a6040a
this patch reads the link status registers to accelerate the
a6040a
procedure.
a6040a
a6040a
Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
a6040a
Signed-off-by: Andrey Chilikin <andrey.chilikin@intel.com>
a6040a
Reviewed-by: Eelco Chaudron <echaudro@redhat.com>
a6040a
Tested-by: Eelco Chaudron <echaudro@redhat.com>
a6040a
---
a6040a
v2:
a6040a
- add ccs after fixline
a6040a
a6040a
 drivers/net/i40e/i40e_ethdev.c | 128 ++++++++++++++++++++++++++++++-----------
a6040a
 1 file changed, 95 insertions(+), 33 deletions(-)
a6040a
a6040a
diff --git openvswitch-2.7.4/drivers/net/i40e/i40e_ethdev.c openvswitch-2.7.4/drivers/net/i40e/i40e_ethdev.c
a6040a
index 508b4171c..968249ed1 100644
a6040a
--- openvswitch-2.7.4/drivers/net/i40e/i40e_ethdev.c
a6040a
+++ openvswitch-2.7.4/drivers/net/i40e/i40e_ethdev.c
a6040a
@@ -2437,77 +2437,140 @@ i40e_dev_set_link_down(struct rte_eth_dev *dev)
a6040a
 	return i40e_phy_conf_link(hw, abilities, speed, false);
a6040a
 }
a6040a
 
a6040a
-int
a6040a
-i40e_dev_link_update(struct rte_eth_dev *dev,
a6040a
-		     int wait_to_complete)
a6040a
+#define __rte_always_inline inline __attribute__((always_inline))
a6040a
+static __rte_always_inline void
a6040a
+update_link_no_wait(struct i40e_hw *hw, struct rte_eth_link *link)
a6040a
+{
a6040a
+/* Link status registers and values*/
a6040a
+#define I40E_PRTMAC_LINKSTA		0x001E2420
a6040a
+#define I40E_REG_LINK_UP		0x40000080
a6040a
+#define I40E_PRTMAC_MACC		0x001E24E0
a6040a
+#define I40E_REG_MACC_25GB		0x00020000
a6040a
+#define I40E_REG_SPEED_MASK		0x38000000
a6040a
+#define I40E_REG_SPEED_100MB		0x00000000
a6040a
+#define I40E_REG_SPEED_1GB		0x08000000
a6040a
+#define I40E_REG_SPEED_10GB		0x10000000
a6040a
+#define I40E_REG_SPEED_20GB		0x20000000
a6040a
+#define I40E_REG_SPEED_25_40GB		0x18000000
a6040a
+	uint32_t link_speed;
a6040a
+	uint32_t reg_val;
a6040a
+
a6040a
+	reg_val = I40E_READ_REG(hw, I40E_PRTMAC_LINKSTA);
a6040a
+	link_speed = reg_val & I40E_REG_SPEED_MASK;
a6040a
+	reg_val &= I40E_REG_LINK_UP;
a6040a
+	link->link_status = (reg_val == I40E_REG_LINK_UP) ? 1 : 0;
a6040a
+
a6040a
+	if (unlikely(link->link_status != 0))
a6040a
+		return;
a6040a
+
a6040a
+	/* Parse the link status */
a6040a
+	switch (link_speed) {
a6040a
+	case I40E_REG_SPEED_100MB:
a6040a
+		link->link_speed = ETH_SPEED_NUM_100M;
a6040a
+		break;
a6040a
+	case I40E_REG_SPEED_1GB:
a6040a
+		link->link_speed = ETH_SPEED_NUM_1G;
a6040a
+		break;
a6040a
+	case I40E_REG_SPEED_10GB:
a6040a
+		link->link_speed = ETH_SPEED_NUM_10G;
a6040a
+		break;
a6040a
+	case I40E_REG_SPEED_20GB:
a6040a
+		link->link_speed = ETH_SPEED_NUM_20G;
a6040a
+		break;
a6040a
+	case I40E_REG_SPEED_25_40GB:
a6040a
+		reg_val = I40E_READ_REG(hw, I40E_PRTMAC_MACC);
a6040a
+
a6040a
+		if (reg_val & I40E_REG_MACC_25GB)
a6040a
+			link->link_speed = ETH_SPEED_NUM_25G;
a6040a
+		else
a6040a
+			link->link_speed = ETH_SPEED_NUM_40G;
a6040a
+
a6040a
+		break;
a6040a
+	default:
a6040a
+		PMD_DRV_LOG(ERR, "Unknown link speed info %u", link_speed);
a6040a
+		break;
a6040a
+	}
a6040a
+}
a6040a
+
a6040a
+static __rte_always_inline void
a6040a
+update_link_wait(struct i40e_hw *hw, struct rte_eth_link *link,
a6040a
+	bool enable_lse)
a6040a
 {
a6040a
-#define CHECK_INTERVAL 100  /* 100ms */
a6040a
-#define MAX_REPEAT_TIME 10  /* 1s (10 * 100ms) in total */
a6040a
-	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
a6040a
+#define CHECK_INTERVAL             100  /* 100ms */
a6040a
+#define MAX_REPEAT_TIME            10  /* 1s (10 * 100ms) in total */
a6040a
+	uint32_t rep_cnt = MAX_REPEAT_TIME;
a6040a
 	struct i40e_link_status link_status;
a6040a
-	struct rte_eth_link link, old;
a6040a
 	int status;
a6040a
-	unsigned rep_cnt = MAX_REPEAT_TIME;
a6040a
-	bool enable_lse = dev->data->dev_conf.intr_conf.lsc ? true : false;
a6040a
 
a6040a
-	memset(&link, 0, sizeof(link));
a6040a
-	memset(&old, 0, sizeof(old));
a6040a
 	memset(&link_status, 0, sizeof(link_status));
a6040a
-	rte_i40e_dev_atomic_read_link_status(dev, &old;;
a6040a
 
a6040a
 	do {
a6040a
 		/* Get link status information from hardware */
a6040a
 		status = i40e_aq_get_link_info(hw, enable_lse,
a6040a
 						&link_status, NULL);
a6040a
-		if (status != I40E_SUCCESS) {
a6040a
-			link.link_speed = ETH_SPEED_NUM_100M;
a6040a
-			link.link_duplex = ETH_LINK_FULL_DUPLEX;
a6040a
+		if (unlikely(status != I40E_SUCCESS)) {
a6040a
+			link->link_speed = ETH_SPEED_NUM_100M;
a6040a
+			link->link_duplex = ETH_LINK_FULL_DUPLEX;
a6040a
 			PMD_DRV_LOG(ERR, "Failed to get link info");
a6040a
-			goto out;
a6040a
+			return;
a6040a
 		}
a6040a
 
a6040a
-		link.link_status = link_status.link_info & I40E_AQ_LINK_UP;
a6040a
-		if (!wait_to_complete || link.link_status)
a6040a
-			break;
a6040a
+		link->link_status = link_status.link_info & I40E_AQ_LINK_UP;
a6040a
+		if (unlikely(link->link_status != 0))
a6040a
+			return;
a6040a
 
a6040a
 		rte_delay_ms(CHECK_INTERVAL);
a6040a
 	} while (--rep_cnt);
a6040a
 
a6040a
-	if (!link.link_status)
a6040a
-		goto out;
a6040a
-
a6040a
-	/* i40e uses full duplex only */
a6040a
-	link.link_duplex = ETH_LINK_FULL_DUPLEX;
a6040a
-
a6040a
 	/* Parse the link status */
a6040a
 	switch (link_status.link_speed) {
a6040a
 	case I40E_LINK_SPEED_100MB:
a6040a
-		link.link_speed = ETH_SPEED_NUM_100M;
a6040a
+		link->link_speed = ETH_SPEED_NUM_100M;
a6040a
 		break;
a6040a
 	case I40E_LINK_SPEED_1GB:
a6040a
-		link.link_speed = ETH_SPEED_NUM_1G;
a6040a
+		link->link_speed = ETH_SPEED_NUM_1G;
a6040a
 		break;
a6040a
 	case I40E_LINK_SPEED_10GB:
a6040a
-		link.link_speed = ETH_SPEED_NUM_10G;
a6040a
+		link->link_speed = ETH_SPEED_NUM_10G;
a6040a
 		break;
a6040a
 	case I40E_LINK_SPEED_20GB:
a6040a
-		link.link_speed = ETH_SPEED_NUM_20G;
a6040a
+		link->link_speed = ETH_SPEED_NUM_20G;
a6040a
 		break;
a6040a
 	case I40E_LINK_SPEED_25GB:
a6040a
-		link.link_speed = ETH_SPEED_NUM_25G;
a6040a
+		link->link_speed = ETH_SPEED_NUM_25G;
a6040a
 		break;
a6040a
 	case I40E_LINK_SPEED_40GB:
a6040a
-		link.link_speed = ETH_SPEED_NUM_40G;
a6040a
+		link->link_speed = ETH_SPEED_NUM_40G;
a6040a
 		break;
a6040a
 	default:
a6040a
-		link.link_speed = ETH_SPEED_NUM_100M;
a6040a
+		link->link_speed = ETH_SPEED_NUM_100M;
a6040a
 		break;
a6040a
 	}
a6040a
+}
a6040a
+
a6040a
+int
a6040a
+i40e_dev_link_update(struct rte_eth_dev *dev,
a6040a
+		     int wait_to_complete)
a6040a
+{
a6040a
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
a6040a
+	struct rte_eth_link link, old;
a6040a
+	bool enable_lse = dev->data->dev_conf.intr_conf.lsc ? true : false;
a6040a
 
a6040a
+	memset(&link, 0, sizeof(link));
a6040a
+	memset(&old, 0, sizeof(old));
a6040a
+
a6040a
+	rte_i40e_dev_atomic_read_link_status(dev, &old;;
a6040a
+
a6040a
+	/* i40e uses full duplex only */
a6040a
+	link.link_duplex = ETH_LINK_FULL_DUPLEX;
a6040a
 	link.link_autoneg = !(dev->data->dev_conf.link_speeds &
a6040a
 			ETH_LINK_SPEED_FIXED);
a6040a
 
a6040a
-out:
a6040a
+	if (!wait_to_complete)
a6040a
+		update_link_no_wait(hw, &link);
a6040a
+	else
a6040a
+		update_link_wait(hw, &link, enable_lse);
a6040a
+
a6040a
 	rte_i40e_dev_atomic_write_link_status(dev, &link);
a6040a
 	if (link.link_status == old.link_status)
a6040a
 		return -1;