patch 'net/bnxt: avoid link flap when setting flow control' has been queued to stable release 24.11.7
luca.boccassi at gmail.com
luca.boccassi at gmail.com
Mon Jul 6 12:20:08 CEST 2026
Hi,
FYI, your patch has been queued to stable release 24.11.7
Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 07/05/26. So please
shout if anyone has objections.
Also note that after the patch there's a diff of the upstream commit vs the
patch applied to the branch. This will indicate if there was any rebasing
needed to apply to the stable branch. If there were code changes for rebasing
(ie: not only metadata diffs), please double check that the rebase was
correctly done.
Queued patches are on a temporary branch at:
https://github.com/bluca/dpdk-stable
This queued commit can be viewed at:
https://github.com/bluca/dpdk-stable/commit/b01c8043becdc71681c6564da35c681b1fb4a290
Thanks.
Luca Boccassi
---
>From b01c8043becdc71681c6564da35c681b1fb4a290 Mon Sep 17 00:00:00 2001
From: Chenna Arnoori <chenna.arnoori at broadcom.com>
Date: Tue, 16 Jun 2026 10:01:29 -0600
Subject: [PATCH] net/bnxt: avoid link flap when setting flow control
[ upstream commit 119b7929678aceae3e24f5de4764f4df243adf9f ]
When OVS-DPDK reconciles port state, it calls flow_ctrl_get followed
by flow_ctrl_set if the returned configuration differs from its
zsh:1: command not found: q
had auto_pause set (including the AUTONEG_PAUSE bit 0x4), even
though no pause autoneg was actually requested. This mismatch
caused OVS to repeatedly call flow_ctrl_set, which triggered a
full link reconfig with PHY reset, flapping the link every time
any interface change occurred on the system.
Two problems were fixed. First, flow_ctrl_set was clearing all
autoneg bits instead of only the flow-control autoneg bit,
which also wiped the speed autoneg state. Second, the pause
set path was abandoning its own HWRM request and calling the
full link config function, which built a separate request
without the pause fields that set_pause_common would have
computed.
Port the kernel bnxt_en driver's approach: add a
set_link_common helper that merges link and speed fields into
an existing HWRM request, and rework set_pause to build a
single combined request with both pause and link fields when
a full reconfig is needed. When only pause changes without an
auto-to-force transition, a pause-only PHY config is sent
without a full link reprogram.
When a reconfig is required, set_link_common reproduces the
behaviour of the full link config path: it validates the
requested speeds, honours the speeds-v2 (P7) field layout, and
keeps the 10GBase-T must-autoneg guard, so the combined request
programs the same fields the standalone link config would have.
Fixes: 8aaf473bbed6 ("net/bnxt: add flow control operations")
Signed-off-by: Chenna Arnoori <chenna.arnoori at broadcom.com>
---
drivers/net/bnxt/bnxt.h | 10 ++
drivers/net/bnxt/bnxt_ethdev.c | 12 +-
drivers/net/bnxt/bnxt_hwrm.c | 220 +++++++++++++++++++++++++++++++++
drivers/net/bnxt/bnxt_hwrm.h | 1 +
4 files changed, 241 insertions(+), 2 deletions(-)
diff --git a/drivers/net/bnxt/bnxt.h b/drivers/net/bnxt/bnxt.h
index a83fddf9e5..37c81d0288 100644
--- a/drivers/net/bnxt/bnxt.h
+++ b/drivers/net/bnxt/bnxt.h
@@ -88,6 +88,8 @@
#define HWRM_VERSION_1_9_1 0x10901
#define HWRM_VERSION_1_9_2 0x10903
#define HWRM_VERSION_1_10_2_13 0x10a020d
+/* Minimum spec version that supports AUTONEG_PAUSE bit in auto_pause field */
+#define HWRM_SPEC_CODE_AUTONEG_PAUSE 0x10201
#define BNXT_MAX_MTU 9574
#define BNXT_NUM_VLANS 2
@@ -331,6 +333,14 @@ struct bnxt_link_info {
uint8_t active_lanes;
uint8_t option_flags;
uint16_t pmd_speed_lanes;
+ /* Bitmask tracking which autoneg modes are active */
+ uint8_t autoneg;
+#define BNXT_AUTONEG_SPEED 1 /* speed autoneg enabled */
+#define BNXT_AUTONEG_FLOW_CTRL 2 /* pause/flow-ctrl autoneg enabled */
+ /* True after autoneg->forced FC transition; cleared once set_pause
+ * sends the combined pause+link HWRM request successfully.
+ */
+ bool link_reconfig_needed;
};
#define BNXT_COS_QUEUE_COUNT 8
diff --git a/drivers/net/bnxt/bnxt_ethdev.c b/drivers/net/bnxt/bnxt_ethdev.c
index b921ee17ad..eec9003d69 100644
--- a/drivers/net/bnxt/bnxt_ethdev.c
+++ b/drivers/net/bnxt/bnxt_ethdev.c
@@ -2494,7 +2494,7 @@ static int bnxt_flow_ctrl_get_op(struct rte_eth_dev *dev,
return rc;
memset(fc_conf, 0, sizeof(*fc_conf));
- if (bp->link_info->auto_pause)
+ if (bp->link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)
fc_conf->autoneg = 1;
switch (bp->link_info->pause) {
case 0:
@@ -2530,6 +2530,14 @@ static int bnxt_flow_ctrl_set_op(struct rte_eth_dev *dev,
return -ENOTSUP;
}
+ if (fc_conf->autoneg) {
+ bp->link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL;
+ } else {
+ if (bp->link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)
+ bp->link_info->link_reconfig_needed = true;
+ bp->link_info->autoneg &= ~BNXT_AUTONEG_FLOW_CTRL;
+ }
+
switch (fc_conf->mode) {
case RTE_ETH_FC_NONE:
bp->link_info->auto_pause = 0;
@@ -2571,7 +2579,7 @@ static int bnxt_flow_ctrl_set_op(struct rte_eth_dev *dev,
}
break;
}
- return bnxt_set_hwrm_link_config(bp, true);
+ return bnxt_hwrm_set_pause(bp);
}
/* Add UDP tunneling port */
diff --git a/drivers/net/bnxt/bnxt_hwrm.c b/drivers/net/bnxt/bnxt_hwrm.c
index 1cc389511a..ca7e0f4ebf 100644
--- a/drivers/net/bnxt/bnxt_hwrm.c
+++ b/drivers/net/bnxt/bnxt_hwrm.c
@@ -1849,6 +1849,26 @@ static int bnxt_hwrm_port_phy_qcfg(struct bnxt *bp,
link_info->auto_pause = resp->auto_pause;
link_info->force_pause = resp->force_pause;
link_info->auto_mode = resp->auto_mode;
+
+ if (link_info->auto_mode != HWRM_PORT_PHY_QCFG_OUTPUT_AUTO_MODE_NONE) {
+ link_info->autoneg = BNXT_AUTONEG_SPEED;
+ if (bp->hwrm_spec_code >= HWRM_SPEC_CODE_AUTONEG_PAUSE) {
+ if (link_info->auto_pause &
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_AUTONEG_PAUSE)
+ link_info->autoneg |=
+ BNXT_AUTONEG_FLOW_CTRL;
+ } else {
+ /* Firmware < HWRM_SPEC_CODE_AUTONEG_PAUSE does not
+ * expose the AUTONEG_PAUSE bit. Infer flow-ctrl
+ * autoneg from any non-zero auto_pause configuration.
+ */
+ if (link_info->auto_pause)
+ link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL;
+ }
+ } else {
+ link_info->autoneg = 0;
+ }
+
link_info->phy_type = resp->phy_type;
link_info->media_type = resp->media_type;
@@ -4060,6 +4080,206 @@ link_down:
return rc;
}
+static void
+bnxt_hwrm_set_pause_common(struct bnxt *bp,
+ struct hwrm_port_phy_cfg_input *req)
+{
+ struct bnxt_link_info *link_info = bp->link_info;
+
+ if (link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL) {
+ if (bp->hwrm_spec_code >= HWRM_SPEC_CODE_AUTONEG_PAUSE)
+ req->auto_pause =
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_AUTONEG_PAUSE;
+ if (link_info->auto_pause &
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_RX)
+ req->auto_pause |=
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_RX;
+ if (link_info->auto_pause &
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_TX)
+ req->auto_pause |=
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_TX;
+ req->enables |=
+ rte_cpu_to_le_32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_PAUSE);
+ } else {
+ if (link_info->force_pause &
+ HWRM_PORT_PHY_CFG_INPUT_FORCE_PAUSE_RX)
+ req->force_pause |=
+ HWRM_PORT_PHY_CFG_INPUT_FORCE_PAUSE_RX;
+ if (link_info->force_pause &
+ HWRM_PORT_PHY_CFG_INPUT_FORCE_PAUSE_TX)
+ req->force_pause |=
+ HWRM_PORT_PHY_CFG_INPUT_FORCE_PAUSE_TX;
+ req->enables |=
+ rte_cpu_to_le_32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_FORCE_PAUSE);
+ if (bp->hwrm_spec_code >= HWRM_SPEC_CODE_AUTONEG_PAUSE) {
+ req->auto_pause = req->force_pause;
+ req->enables |=
+ rte_cpu_to_le_32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_PAUSE);
+ }
+ }
+}
+
+static int
+bnxt_hwrm_set_link_common(struct bnxt *bp, struct hwrm_port_phy_cfg_input *req)
+{
+ struct rte_eth_conf *dev_conf = &bp->eth_dev->data->dev_conf;
+ struct bnxt_link_info *link_info = bp->link_info;
+ uint16_t autoneg, speed;
+ uint32_t en = 0;
+
+ autoneg = bnxt_check_eth_link_autoneg(dev_conf->link_speeds);
+
+ if (BNXT_CHIP_P5_P7(bp) &&
+ dev_conf->link_speeds & RTE_ETH_LINK_SPEED_40G)
+ autoneg = 0;
+
+ if (autoneg == 1 && BNXT_CHIP_P5(bp) &&
+ link_info->auto_mode == 0 &&
+ link_info->force_pam4_link_speed ==
+ HWRM_PORT_PHY_CFG_INPUT_FORCE_PAM4_LINK_SPEED_200GB)
+ autoneg = 0;
+
+ speed = bnxt_parse_eth_link_speed(bp, dev_conf->link_speeds,
+ link_info);
+ req->flags |=
+ rte_cpu_to_le_32(HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESET_PHY);
+
+ if (autoneg == 0 &&
+ (link_info->phy_type == HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASET ||
+ link_info->phy_type == HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASETE ||
+ link_info->media_type == HWRM_PORT_PHY_QCFG_OUTPUT_MEDIA_TYPE_TP)) {
+ PMD_DRV_LOG_LINE(ERR, "10GBase-T devices must autoneg");
+ return -EINVAL;
+ }
+
+ if (BNXT_LINK_SPEEDS_V2(bp)) {
+ /* speeds-v2 firmware uses a separate field layout */
+ if (autoneg == 1) {
+ req->flags |= rte_cpu_to_le_32
+ (HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESTART_AUTONEG);
+ req->auto_mode =
+ HWRM_PORT_PHY_CFG_INPUT_AUTO_MODE_SPEED_MASK;
+ req->auto_link_speeds2_mask = rte_cpu_to_le_16
+ (bnxt_parse_eth_link_speed_mask(bp,
+ dev_conf->link_speeds));
+ en |= HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_LINK_SPEEDS2_MASK |
+ HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_MODE;
+ } else {
+ req->flags |= rte_cpu_to_le_32
+ (HWRM_PORT_PHY_CFG_INPUT_FLAGS_FORCE);
+ if (speed) {
+ req->force_link_speeds2 =
+ rte_cpu_to_le_16(speed);
+ en |= HWRM_PORT_PHY_CFG_INPUT_ENABLES_FORCE_LINK_SPEEDS2;
+ }
+ }
+ } else if (autoneg == 1 &&
+ (link_info->support_auto_speeds ||
+ link_info->support_pam4_auto_speeds)) {
+ req->flags |= rte_cpu_to_le_32
+ (HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESTART_AUTONEG);
+ req->auto_link_speed_mask = rte_cpu_to_le_16
+ (bnxt_parse_eth_link_speed_mask(bp,
+ dev_conf->link_speeds));
+ req->auto_link_pam4_speed_mask =
+ rte_cpu_to_le_16(link_info->auto_pam4_link_speed_mask);
+ en |= HWRM_PORT_PHY_CFG_IN_EN_AUTO_LINK_SPEED_MASK |
+ HWRM_PORT_PHY_CFG_IN_EN_AUTO_PAM4_LINK_SPD_MASK |
+ HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_MODE;
+ req->auto_mode = HWRM_PORT_PHY_CFG_INPUT_AUTO_MODE_SPEED_MASK;
+ } else {
+ req->flags |= rte_cpu_to_le_32
+ (HWRM_PORT_PHY_CFG_INPUT_FLAGS_FORCE);
+
+ /* No requested speed: keep the PHY's current speed rather
+ * than forcing 0.
+ */
+ if (!speed) {
+ if (link_info->force_pam4_link_speed)
+ speed = link_info->force_pam4_link_speed;
+ else if (link_info->force_link_speed)
+ speed = link_info->force_link_speed;
+ else if (link_info->auto_pam4_link_speed_mask)
+ speed = link_info->auto_pam4_link_speed_mask;
+ else if (link_info->support_pam4_speeds)
+ speed = link_info->support_pam4_speeds;
+ else
+ speed = link_info->auto_link_speed;
+ /* Auto PAM4 link speed is zero, but auto_link_speed is
+ * not. Use the auto_link_speed.
+ */
+ if (link_info->auto_link_speed != 0 &&
+ link_info->auto_pam4_link_speed_mask == 0)
+ speed = link_info->auto_link_speed;
+ }
+
+ if (speed) {
+ if (link_info->link_signal_mode) {
+ req->force_pam4_link_speed =
+ rte_cpu_to_le_16(speed);
+ en |= HWRM_PORT_PHY_CFG_IN_EN_FORCE_PAM4_LINK_SPEED;
+ } else {
+ req->force_link_speed =
+ rte_cpu_to_le_16(speed);
+ }
+ }
+ }
+
+ req->auto_duplex = bnxt_parse_eth_link_duplex(dev_conf->link_speeds);
+ en |= HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_DUPLEX;
+ req->enables |= rte_cpu_to_le_32(en);
+
+ return 0;
+}
+
+int bnxt_hwrm_set_pause(struct bnxt *bp)
+{
+ struct hwrm_port_phy_cfg_input req = {0};
+ struct hwrm_port_phy_cfg_output *resp = bp->hwrm_cmd_resp_addr;
+ struct bnxt_link_info *link_info = bp->link_info;
+ bool reconfig;
+ int rc;
+
+ /* Full reprogram on FC autoneg, or the autoneg->forced transition */
+ reconfig = (link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL) ||
+ link_info->link_reconfig_needed;
+
+ /* Validate speeds up front, as bnxt_set_hwrm_link_config() does */
+ if (reconfig) {
+ rc = bnxt_validate_link_speed(bp);
+ if (rc)
+ return rc;
+ }
+
+ HWRM_PREP(&req, HWRM_PORT_PHY_CFG, BNXT_USE_CHIMP_MB);
+
+ bnxt_hwrm_set_pause_common(bp, &req);
+
+ if (reconfig) {
+ rc = bnxt_hwrm_set_link_common(bp, &req);
+ if (rc) {
+ HWRM_UNLOCK();
+ return rc;
+ }
+ }
+
+ rc = bnxt_hwrm_send_message(bp, &req, sizeof(req), BNXT_USE_CHIMP_MB);
+
+ /* HWRM_CHECK_RESULT() returns on any transport or firmware error,
+ * so the state updates below are reached only on success.
+ */
+ HWRM_CHECK_RESULT();
+
+ if (!(link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)) {
+ link_info->pause = link_info->force_pause;
+ link_info->auto_pause = 0;
+ }
+ link_info->link_reconfig_needed = false;
+
+ HWRM_UNLOCK();
+ return rc;
+}
+
static int bnxt_set_hwrm_link_config_v2(struct bnxt *bp, bool link_up)
{
struct rte_eth_conf *dev_conf = &bp->eth_dev->data->dev_conf;
diff --git a/drivers/net/bnxt/bnxt_hwrm.h b/drivers/net/bnxt/bnxt_hwrm.h
index ecb6335b3d..d3ad56c320 100644
--- a/drivers/net/bnxt/bnxt_hwrm.h
+++ b/drivers/net/bnxt/bnxt_hwrm.h
@@ -261,6 +261,7 @@ void bnxt_free_hwrm_rx_ring(struct bnxt *bp, int queue_index);
int bnxt_alloc_hwrm_resources(struct bnxt *bp);
int bnxt_get_hwrm_link_config(struct bnxt *bp, struct rte_eth_link *link);
int bnxt_set_hwrm_link_config(struct bnxt *bp, bool link_up);
+int bnxt_hwrm_set_pause(struct bnxt *bp);
int bnxt_hwrm_func_qcfg(struct bnxt *bp, uint16_t *mtu);
int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp);
int bnxt_hwrm_func_reserve_vf_resc(struct bnxt *bp, bool test);
--
2.47.3
---
Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- - 2026-07-03 12:55:47.313960716 +0100
+++ 0016-net-bnxt-avoid-link-flap-when-setting-flow-control.patch 2026-07-03 12:55:46.602572200 +0100
@@ -1 +1 @@
-From 119b7929678aceae3e24f5de4764f4df243adf9f Mon Sep 17 00:00:00 2001
+From b01c8043becdc71681c6564da35c681b1fb4a290 Mon Sep 17 00:00:00 2001
@@ -5,0 +6,2 @@
+[ upstream commit 119b7929678aceae3e24f5de4764f4df243adf9f ]
+
@@ -38 +39,0 @@
-Cc: stable at dpdk.org
@@ -49 +50 @@
-index 5ae4e620a4..0500414ae0 100644
+index a83fddf9e5..37c81d0288 100644
@@ -61 +62 @@
-@@ -333,6 +335,14 @@ struct bnxt_link_info {
+@@ -331,6 +333,14 @@ struct bnxt_link_info {
@@ -77 +78 @@
-index a2429561b9..8e8ead8f61 100644
+index b921ee17ad..eec9003d69 100644
@@ -80 +81 @@
-@@ -2571,7 +2571,7 @@ static int bnxt_flow_ctrl_get_op(struct rte_eth_dev *dev,
+@@ -2494,7 +2494,7 @@ static int bnxt_flow_ctrl_get_op(struct rte_eth_dev *dev,
@@ -89 +90 @@
-@@ -2607,6 +2607,14 @@ static int bnxt_flow_ctrl_set_op(struct rte_eth_dev *dev,
+@@ -2530,6 +2530,14 @@ static int bnxt_flow_ctrl_set_op(struct rte_eth_dev *dev,
@@ -104 +105 @@
-@@ -2648,7 +2656,7 @@ static int bnxt_flow_ctrl_set_op(struct rte_eth_dev *dev,
+@@ -2571,7 +2579,7 @@ static int bnxt_flow_ctrl_set_op(struct rte_eth_dev *dev,
@@ -114 +115 @@
-index 4a5b83d29c..66237bc219 100644
+index 1cc389511a..ca7e0f4ebf 100644
@@ -117 +118 @@
-@@ -1852,6 +1852,26 @@ static int bnxt_hwrm_port_phy_qcfg(struct bnxt *bp,
+@@ -1849,6 +1849,26 @@ static int bnxt_hwrm_port_phy_qcfg(struct bnxt *bp,
@@ -144 +145 @@
-@@ -4044,6 +4064,206 @@ link_down:
+@@ -4060,6 +4080,206 @@ link_down:
@@ -352 +353 @@
-index fc56223ab4..3034803023 100644
+index ecb6335b3d..d3ad56c320 100644
More information about the stable
mailing list