[PATCH 08/14] net/enetc: refresh link speed on VF link-up interrupt
Gagandeep Singh
g.singh at nxp.com
Thu Aug 6 13:28:30 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.
Fix this by:
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/enetc4_vf.c | 197 +++++++++++++------------
2 files changed, 107 insertions(+), 91 deletions(-)
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 9e333edf4f..eca0dc1dea 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -67,6 +67,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/enetc4_vf.c b/drivers/net/enetc/enetc4_vf.c
index 7623a66e98..58e38fe398 100644
--- a/drivers/net/enetc/enetc4_vf.c
+++ b/drivers/net/enetc/enetc4_vf.c
@@ -324,9 +324,99 @@ 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. Reverse the formula:
+ * SPEED = (status - ENETC_SPEED_5000) * 1000 + 5000 (in Mbps)
+ */
+ link->link_speed = (status - ENETC_SPEED_5000) * 1000 + 5000;
+ link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ 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;
@@ -345,6 +435,20 @@ 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.
+ */
+ rte_free(msg);
+ msg = rte_zmalloc(NULL, sizeof(*msg), RTE_CACHE_LINE_SIZE);
+ if (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);
+ } else {
+ ENETC_PMD_WARN("Failed to alloc msg for speed query");
+ }
break;
case ENETC_LINK_DOWN:
ENETC_PMD_DEBUG("Link is down");
@@ -1048,97 +1152,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).
- *
- * The PF only reports speeds that map to a well-known
- * RTE_ETH_SPEED_NUM_* value, so the computed value is a
- * valid DPDK speed. If a future speed not yet defined in
- * DPDK needs to be supported, the corresponding
- * RTE_ETH_SPEED_NUM_* value must first be added upstream.
- */
- link.link_speed = (reply_msg->status - ENETC_SPEED_5000)
- * 1000 + 5000;
- link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
- break;
- }
+ enetc4_decode_link_speed(reply_msg->status,
+ hw->vf_link_legacy, &link);
} else {
ENETC_PMD_ERR("Wrong reply message");
return -1;
More information about the dev
mailing list