[PATCH v6 08/13] net/enetc: refresh link speed on VF link-up interrupt

Gagandeep Singh g.singh at nxp.com
Tue Aug 11 09:40:27 CEST 2026


The interrupt-driven link-status path (enetc4_process_psi_msg) only
updated link_status when a PSI-to-VSI notification arrived; it never
re-queried the link speed from the PF.  As a result, after a link-down
followed by a link-up the cached link_speed showed the speed from the
previous session rather than the freshly negotiated one.

1. Introducing enetc4_decode_link_speed() - a shared helper that maps
   a PF-to-VF speed status code to the corresponding RTE_ETH_SPEED_NUM_*
   / RTE_ETH_LINK_*_DUPLEX values, handling both the current and the
   legacy (vf_link_legacy) 4-bit message layout.

2. Calling enetc4_vf_get_link_speed() inside enetc4_process_psi_msg()
   on ENETC_LINK_UP so the negotiated speed is fetched from the PF and
   decoded immediately, before rte_eth_linkstatus_set() is called and
   the LSC callback is fired.

Signed-off-by: Gagandeep Singh <g.singh at nxp.com>
---
 doc/guides/rel_notes/release_26_11.rst |   1 +
 drivers/net/enetc/enetc.h              |   2 +
 drivers/net/enetc/enetc4_ethdev.c      |   1 +
 drivers/net/enetc/enetc4_vf.c          | 257 +++++++++++++------------
 4 files changed, 141 insertions(+), 120 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index ec223a93ba..1765adc1bc 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -69,6 +69,7 @@ New Features
   * Added firmware version reporting for the ENETC4 VF.
   * Added register dump support for ENETC4 PF and VF.
   * Added ring parameters support for the ENETC4 VF (rxq_info_get / txq_info_get).
+  * Refreshed VF link speed on the link-up interrupt in the ENETC4 VF driver.
 
 Removed Items
 -------------
diff --git a/drivers/net/enetc/enetc.h b/drivers/net/enetc/enetc.h
index 4b563851b8..185fd8c4ee 100644
--- a/drivers/net/enetc/enetc.h
+++ b/drivers/net/enetc/enetc.h
@@ -5,6 +5,7 @@
 #ifndef _ENETC_H_
 #define _ENETC_H_
 
+#include <pthread.h>
 #include <rte_time.h>
 #include <ethdev_pci.h>
 
@@ -123,6 +124,7 @@ struct enetc_eth_hw {
 	 * for PF kernel versions before 6.18.37. Set via vf_link_legacy devarg.
 	 */
 	uint8_t vf_link_legacy;
+	pthread_mutex_t vsi_lock; /* serializes all VSI-PSI mailbox transactions */
 };
 
 /*
diff --git a/drivers/net/enetc/enetc4_ethdev.c b/drivers/net/enetc/enetc4_ethdev.c
index ad6ef84214..3d86072593 100644
--- a/drivers/net/enetc/enetc4_ethdev.c
+++ b/drivers/net/enetc/enetc4_ethdev.c
@@ -844,6 +844,7 @@ enetc4_dev_close(struct rte_eth_dev *dev)
 		if (dev->data->dev_conf.intr_conf.lsc != 0)
 			enetc4_vf_dev_intr(dev, false);
 		ret = enetc4_vf_dev_stop(dev);
+		pthread_mutex_destroy(&hw->vsi_lock);
 	} else {
 		ret = enetc4_dev_stop(dev);
 	}
diff --git a/drivers/net/enetc/enetc4_vf.c b/drivers/net/enetc/enetc4_vf.c
index 3da84941a2..da41b999cb 100644
--- a/drivers/net/enetc/enetc4_vf.c
+++ b/drivers/net/enetc/enetc4_vf.c
@@ -323,9 +323,130 @@ enetc4_msg_get_psi_msg(struct enetc_hw *enetc_hw, struct enetc_psi_reply_msg *re
 	reply_msg->status = status;
 }
 
+/* Forward declaration: defined later in this file */
+static int enetc4_vf_get_link_speed(struct rte_eth_dev *dev,
+				     struct enetc_psi_reply_msg *reply_msg);
+
+/*
+ * Decode a PF-to-VF link-speed status code into the link_speed and
+ * link_duplex fields of *link.  vf_link_legacy selects the older
+ * 4-bit code layout used by kernel PFs before v6.18.37.
+ */
+static void
+enetc4_decode_link_speed(uint8_t status, bool vf_link_legacy,
+			 struct rte_eth_link *link)
+{
+	switch (status) {
+	case ENETC_SPEED_UNKNOWN:
+		ENETC_PMD_DEBUG("Speed unknown");
+		link->link_speed = RTE_ETH_SPEED_NUM_NONE;
+		break;
+	case ENETC_SPEED_10_HALF_DUPLEX:
+		link->link_speed = RTE_ETH_SPEED_NUM_10M;
+		link->link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+		break;
+	case ENETC_SPEED_10_FULL_DUPLEX:
+		link->link_speed = RTE_ETH_SPEED_NUM_10M;
+		link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+		break;
+	case ENETC_SPEED_100_HALF_DUPLEX:
+		link->link_speed = RTE_ETH_SPEED_NUM_100M;
+		link->link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+		break;
+	case ENETC_SPEED_100_FULL_DUPLEX:
+		link->link_speed = RTE_ETH_SPEED_NUM_100M;
+		link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+		break;
+	case ENETC_SPEED_1000:
+		link->link_speed = RTE_ETH_SPEED_NUM_1G;
+		link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+		break;
+	case ENETC_SPEED_2500:
+		link->link_speed = RTE_ETH_SPEED_NUM_2_5G;
+		link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+		break;
+	case ENETC_SPEED_5000:
+		link->link_speed = RTE_ETH_SPEED_NUM_5G;
+		link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+		break;
+	default:
+		if (vf_link_legacy) {
+			/* Legacy PF-to-VF message layout (older kernel PF):
+			 * speeds above 5Gbps use fixed 4-bit class codes.
+			 */
+			switch (status) {
+			case ENETC_SPEED_LEGACY_10G:
+				link->link_speed = RTE_ETH_SPEED_NUM_10G;
+				link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+				break;
+			case ENETC_SPEED_LEGACY_25G:
+				link->link_speed = RTE_ETH_SPEED_NUM_25G;
+				link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+				break;
+			case ENETC_SPEED_LEGACY_50G:
+				link->link_speed = RTE_ETH_SPEED_NUM_50G;
+				link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+				break;
+			case ENETC_SPEED_LEGACY_100G:
+				link->link_speed = RTE_ETH_SPEED_NUM_100G;
+				link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+				break;
+			case ENETC_SPEED_LEGACY_NOT_SUPPORTED:
+				ENETC_PMD_DEBUG("Speed not supported");
+				link->link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
+				break;
+			default:
+				ENETC_PMD_ERR("Unknown speed status");
+				link->link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
+				break;
+			}
+			break;
+		}
+		/* Any status here is > ENETC_SPEED_5000. Validate against
+		 * the set of speeds that the NETC IP is known to support.
+		 * An unrecognised code yields UNKNOWN rather than a
+		 * fabricated speed.
+		 */
+		switch ((status - ENETC_SPEED_5000) * 1000 + 5000) {
+		case RTE_ETH_SPEED_NUM_10G:
+			link->link_speed = RTE_ETH_SPEED_NUM_10G;
+			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+			break;
+		case RTE_ETH_SPEED_NUM_25G:
+			link->link_speed = RTE_ETH_SPEED_NUM_25G;
+			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+			break;
+		case RTE_ETH_SPEED_NUM_40G:
+			link->link_speed = RTE_ETH_SPEED_NUM_40G;
+			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+			break;
+		case RTE_ETH_SPEED_NUM_50G:
+			link->link_speed = RTE_ETH_SPEED_NUM_50G;
+			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+			break;
+		case RTE_ETH_SPEED_NUM_100G:
+			link->link_speed = RTE_ETH_SPEED_NUM_100G;
+			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+			break;
+		case RTE_ETH_SPEED_NUM_200G:
+			link->link_speed = RTE_ETH_SPEED_NUM_200G;
+			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+			break;
+		default:
+			ENETC_PMD_WARN("Unrecognized speed code 0x%x, "
+				       "reporting unknown", status);
+			link->link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
+			break;
+		}
+		break;
+	}
+}
+
 static void
 enetc4_process_psi_msg(struct rte_eth_dev *eth_dev, struct enetc_hw *enetc_hw)
 {
+	struct enetc_eth_hw *hw =
+		ENETC_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
 	struct enetc_psi_reply_msg *msg;
 	struct rte_eth_link link;
 	int ret = 0;
@@ -344,6 +465,15 @@ enetc4_process_psi_msg(struct rte_eth_dev *eth_dev, struct enetc_hw *enetc_hw)
 		case ENETC_LINK_UP:
 			ENETC_PMD_DEBUG("Link is up");
 			link.link_status = RTE_ETH_LINK_UP;
+			/* Re-query speed from PF so the cached value reflects
+			 * the current negotiated speed after link-up.
+			 */
+			memset(msg, 0, sizeof(*msg));
+			if (!enetc4_vf_get_link_speed(eth_dev, msg) &&
+			    msg->class_id == ENETC_CLASS_ID_LINK_SPEED)
+				enetc4_decode_link_speed(msg->status,
+							hw->vf_link_legacy,
+							&link);
 			break;
 		case ENETC_LINK_DOWN:
 			ENETC_PMD_DEBUG("Link is down");
@@ -379,6 +509,7 @@ enetc4_msg_vsi_send(struct enetc_eth_hw *hw, struct enetc_msg_swbd *msg)
 	int err = 0;
 	int vsimsgsr;
 
+	pthread_mutex_lock(&hw->vsi_lock);
 	enetc4_msg_vsi_write_msg(enetc_hw, msg);
 
 	do {
@@ -390,11 +521,13 @@ enetc4_msg_vsi_send(struct enetc_eth_hw *hw, struct enetc_msg_swbd *msg)
 
 	if (!timeout) {
 		ENETC_PMD_ERR("Message not processed by PSI");
+		pthread_mutex_unlock(&hw->vsi_lock);
 		return -ETIMEDOUT;
 	}
 	/* check for message delivery error */
 	if (vsimsgsr & ENETC4_VSIMSGSR_MS) {
 		ENETC_PMD_ERR("Transfer error when copying the data");
+		pthread_mutex_unlock(&hw->vsi_lock);
 		return -EIO;
 	}
 
@@ -441,6 +574,7 @@ enetc4_msg_vsi_send(struct enetc_eth_hw *hw, struct enetc_msg_swbd *msg)
 		}
 	}
 
+	pthread_mutex_unlock(&hw->vsi_lock);
 	return err;
 }
 
@@ -1051,126 +1185,8 @@ enetc4_vf_link_update(struct rte_eth_dev *dev, int wait_to_complete __rte_unused
 	}
 
 	if (reply_msg->class_id == ENETC_CLASS_ID_LINK_SPEED) {
-		switch (reply_msg->status) {
-		case ENETC_SPEED_UNKNOWN:
-			ENETC_PMD_DEBUG("Speed unknown");
-			link.link_speed = RTE_ETH_SPEED_NUM_NONE;
-			break;
-		case ENETC_SPEED_10_HALF_DUPLEX:
-			link.link_speed = RTE_ETH_SPEED_NUM_10M;
-			link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
-			break;
-		case ENETC_SPEED_10_FULL_DUPLEX:
-			link.link_speed = RTE_ETH_SPEED_NUM_10M;
-			link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-			break;
-		case ENETC_SPEED_100_HALF_DUPLEX:
-			link.link_speed = RTE_ETH_SPEED_NUM_100M;
-			link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
-			break;
-		case ENETC_SPEED_100_FULL_DUPLEX:
-			link.link_speed = RTE_ETH_SPEED_NUM_100M;
-			link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-			break;
-		case ENETC_SPEED_1000:
-			link.link_speed = RTE_ETH_SPEED_NUM_1G;
-			link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-			break;
-		case ENETC_SPEED_2500:
-			link.link_speed = RTE_ETH_SPEED_NUM_2_5G;
-			link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-			break;
-		case ENETC_SPEED_5000:
-			link.link_speed = RTE_ETH_SPEED_NUM_5G;
-			link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-			break;
-		default:
-			if (hw->vf_link_legacy) {
-				/* Legacy PF-to-VF message layout (older kernel
-				 * PF): speeds greater than 5Gbps are encoded
-				 * with fixed 4-bit class codes rather than the
-				 * formula below.
-				 */
-				switch (reply_msg->status) {
-				case ENETC_SPEED_LEGACY_10G:
-					link.link_speed = RTE_ETH_SPEED_NUM_10G;
-					link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-					break;
-				case ENETC_SPEED_LEGACY_25G:
-					link.link_speed = RTE_ETH_SPEED_NUM_25G;
-					link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-					break;
-				case ENETC_SPEED_LEGACY_50G:
-					link.link_speed = RTE_ETH_SPEED_NUM_50G;
-					link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-					break;
-				case ENETC_SPEED_LEGACY_100G:
-					link.link_speed = RTE_ETH_SPEED_NUM_100G;
-					link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-					break;
-				case ENETC_SPEED_LEGACY_NOT_SUPPORTED:
-					ENETC_PMD_DEBUG("Speed not supported");
-					link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
-					break;
-				default:
-					ENETC_PMD_ERR("Unknown speed status");
-					link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
-					break;
-				}
-				break;
-			}
-
-			/* Any status reaching here is greater than
-			 * ENETC_SPEED_5000, as all values from 0x0 to
-			 * ENETC_SPEED_5000 are handled by the cases above. Speeds
-			 * greater than 5Gbps are not enumerated and follow the
-			 * formula:
-			 *
-			 *   SPEED = (link_speed - 5000) / 1000 + ENETC_SPEED_5000
-			 *
-			 * where link_speed is in Mbps. Reverse it here to get the
-			 * actual link speed (RTE_ETH_SPEED_NUM_* values are in Mbps).
-			 *
-			 * Validate the computed value against the set of speeds
-			 * that the NETC IP is known to support (> 5Gbps).
-			 * An unrecognised code yields UNKNOWN rather than a
-			 * fabricated speed.
-			 */
-			switch ((reply_msg->status - ENETC_SPEED_5000)
-				* 1000 + 5000) {
-			case RTE_ETH_SPEED_NUM_10G:
-				link.link_speed = RTE_ETH_SPEED_NUM_10G;
-				link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-				break;
-			case RTE_ETH_SPEED_NUM_25G:
-				link.link_speed = RTE_ETH_SPEED_NUM_25G;
-				link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-				break;
-			case RTE_ETH_SPEED_NUM_40G:
-				link.link_speed = RTE_ETH_SPEED_NUM_40G;
-				link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-				break;
-			case RTE_ETH_SPEED_NUM_50G:
-				link.link_speed = RTE_ETH_SPEED_NUM_50G;
-				link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-				break;
-			case RTE_ETH_SPEED_NUM_100G:
-				link.link_speed = RTE_ETH_SPEED_NUM_100G;
-				link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-				break;
-			case RTE_ETH_SPEED_NUM_200G:
-				link.link_speed = RTE_ETH_SPEED_NUM_200G;
-				link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-				break;
-			default:
-				ENETC_PMD_WARN("Unrecognized speed code 0x%x, "
-					       "reporting unknown",
-					       reply_msg->status);
-				link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
-				break;
-			}
-			break;
-		}
+		enetc4_decode_link_speed(reply_msg->status,
+					 hw->vf_link_legacy, &link);
 	} else {
 		ENETC_PMD_ERR("Wrong reply message");
 		return -1;
@@ -1741,6 +1757,7 @@ enetc4_vf_dev_init(struct rte_eth_dev *eth_dev)
 	}
 
 	enetc4_dev_hw_init(eth_dev);
+	pthread_mutex_init(&hw->vsi_lock, NULL);
 
 	hw->nc_mode = 0;
 	enetc4_vf_get_devarg_nc(eth_dev);
-- 
2.25.1



More information about the dev mailing list