[RFC PATCH v4 2/2] net/bonding: restrict secondary control operations

Weijun Pan wpan3636 at gmail.com
Sun Aug 30 03:14:45 CEST 2026


Bonding configuration and LACP runtime state are owned by the primary
process. Install a reduced dev_ops table in secondary processes so
ethdev rejects control operations, and reject bonding control APIs
when called from a non-primary process.

Supported query and detach paths remain available, while LACP runtime
state queries are restricted to the primary process.

Bugzilla ID: 1900

Signed-off-by: Weijun Pan <wpan3636 at gmail.com>
---
v4:
- Use rte_eth_linkstatus_set() in link update.
- Document supported secondary query and detach paths.
- Move the release note to API Changes.
- Keep secondary private dump but skip LACP state.

 .../link_bonding_poll_mode_drv_lib.rst        | 30 ++++++++++
 doc/guides/rel_notes/release_26_11.rst        |  6 ++
 drivers/net/bonding/eth_bond_private.h        | 12 ++++
 drivers/net/bonding/rte_eth_bond_8023ad.c     | 48 +++++++++++++++
 drivers/net/bonding/rte_eth_bond_api.c        | 53 ++++++++++++++++-
 drivers/net/bonding/rte_eth_bond_pmd.c        | 58 +++++++++++--------
 6 files changed, 182 insertions(+), 25 deletions(-)

diff --git a/doc/guides/prog_guide/link_bonding_poll_mode_drv_lib.rst b/doc/guides/prog_guide/link_bonding_poll_mode_drv_lib.rst
index 2fa1ac4028..a3f197c8b5 100644
--- a/doc/guides/prog_guide/link_bonding_poll_mode_drv_lib.rst
+++ b/doc/guides/prog_guide/link_bonding_poll_mode_drv_lib.rst
@@ -254,6 +254,36 @@ Like all other PMD, all functions exported by a PMD are lock-free functions
 that are assumed not to be invoked in parallel on different logical cores to
 work on the same target object.
 
+Bonding device configuration and LACP runtime state are owned by the primary
+process. Secondary processes may attach to an existing bonding device for
+detach and supported query operations only.
+
+Supported secondary-process queries include device information, statistics,
+link status, RETA query, RSS hash configuration, bonding mode, member list,
+primary member, transmit policy, link monitoring configuration, and LACP
+configuration. Private dump is limited to shared bonding information and skips
+LACP runtime state in a secondary process.
+
+Control operations are restricted to the primary process. This includes
+configuring, starting or stopping the device, setting up queues, changing
+members, changing the bonding mode, selecting the primary member, changing the
+transmit policy, changing link monitoring or propagation delays, updating RSS,
+changing MAC addresses, changing MTU, configuring VLAN filters, changing
+promiscuous or all-multicast mode, resetting statistics, configuring
+``rte_flow`` rules, and changing 802.3ad settings, including aggregation
+selection, external collect/distribute/slow-Tx controls, and dedicated queue
+enable or disable.
+
+LACP runtime state queries, including ``rte_eth_bond_8023ad_member_info()``,
+``rte_eth_bond_8023ad_ext_collect_get()``, and
+``rte_eth_bond_8023ad_ext_distrib_get()``, are also restricted to the primary
+process.
+
+Rx and Tx are not supported on a bonding device in a secondary process;
+receive returns no packets and transmit drops packets. In a secondary process,
+``rte_eth_dev_stop()`` returns ``-ENOTSUP`` and ``rte_eth_dev_close()`` is the
+detach operation.
+
 It should also be noted that the PMD receive function should not be invoked
 directly on a member devices after they have been to a bonding device since
 packets read directly from the member device will no longer be available to the
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index c8cc86295d..60d8146d68 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -81,6 +81,12 @@ Removed Items
 API Changes
 -----------
 
+* **Restricted bonding device control to the primary process.**
+
+  Bonding device configuration and LACP runtime state operations are now
+  rejected in secondary processes. Secondary processes may detach and use
+  supported query operations only.
+
 .. This section should contain API changes. Sample format:
 
    * sample: Add a short 1-2 sentence description of the API change
diff --git a/drivers/net/bonding/eth_bond_private.h b/drivers/net/bonding/eth_bond_private.h
index 378bbba4e6..526bcd0363 100644
--- a/drivers/net/bonding/eth_bond_private.h
+++ b/drivers/net/bonding/eth_bond_private.h
@@ -7,12 +7,14 @@
 
 #include <stdint.h>
 #include <sys/queue.h>
+#include <stdbool.h>
 
 #include <ethdev_driver.h>
 #include <rte_flow.h>
 #include <rte_spinlock.h>
 #include <rte_bitmap.h>
 #include <rte_flow_driver.h>
+#include <rte_eal.h>
 
 #include "rte_eth_bond.h"
 #include "eth_bond_8023ad_private.h"
@@ -212,6 +214,16 @@ find_member_by_id(uint16_t *members, uint16_t members_count, uint16_t member_id)
 	return pos;
 }
 
+static inline int
+bond_check_primary(const char *op, int err)
+{
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return 0;
+
+	RTE_BOND_LOG(ERR, "%s not supported in non-primary process", op);
+	return err;
+}
+
 int
 valid_port_id(uint16_t port_id);
 
diff --git a/drivers/net/bonding/rte_eth_bond_8023ad.c b/drivers/net/bonding/rte_eth_bond_8023ad.c
index d1f30229d0..65f417a444 100644
--- a/drivers/net/bonding/rte_eth_bond_8023ad.c
+++ b/drivers/net/bonding/rte_eth_bond_8023ad.c
@@ -1436,6 +1436,11 @@ rte_eth_bond_8023ad_agg_selection_set(uint16_t port_id,
 	struct rte_eth_dev *bond_dev;
 	struct bond_dev_private *internals;
 	struct mode8023ad_private *mode4;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(port_id) != 0)
 		return -EINVAL;
@@ -1508,6 +1513,11 @@ rte_eth_bond_8023ad_setup(uint16_t port_id,
 {
 	struct rte_eth_dev *bond_dev;
 	int err;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	err = bond_8023ad_setup_validate(port_id, conf);
 	if (err != 0)
@@ -1531,6 +1541,11 @@ rte_eth_bond_8023ad_member_info(uint16_t port_id, uint16_t member_id,
 	struct rte_eth_dev *bond_dev;
 	struct bond_dev_private *internals;
 	struct port *port;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	if (info == NULL || valid_bonding_port_id(port_id) != 0 ||
 			rte_eth_bond_mode_get(port_id) != BONDING_MODE_8023AD)
@@ -1592,6 +1607,11 @@ rte_eth_bond_8023ad_ext_collect(uint16_t port_id, uint16_t member_id,
 {
 	struct port *port;
 	int res;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	res = bond_8023ad_ext_validate(port_id, member_id);
 	if (res != 0)
@@ -1614,6 +1634,11 @@ rte_eth_bond_8023ad_ext_distrib(uint16_t port_id, uint16_t member_id,
 {
 	struct port *port;
 	int res;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	res = bond_8023ad_ext_validate(port_id, member_id);
 	if (res != 0)
@@ -1636,6 +1661,10 @@ rte_eth_bond_8023ad_ext_distrib_get(uint16_t port_id, uint16_t member_id)
 	struct port *port;
 	int err;
 
+	err = bond_check_primary(__func__, -ENOTSUP);
+	if (err != 0)
+		return err;
+
 	err = bond_8023ad_ext_validate(port_id, member_id);
 	if (err != 0)
 		return err;
@@ -1651,6 +1680,10 @@ rte_eth_bond_8023ad_ext_collect_get(uint16_t port_id, uint16_t member_id)
 	struct port *port;
 	int err;
 
+	err = bond_check_primary(__func__, -ENOTSUP);
+	if (err != 0)
+		return err;
+
 	err = bond_8023ad_ext_validate(port_id, member_id);
 	if (err != 0)
 		return err;
@@ -1666,6 +1699,11 @@ rte_eth_bond_8023ad_ext_slowtx(uint16_t port_id, uint16_t member_id,
 {
 	struct port *port;
 	int res;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	res = bond_8023ad_ext_validate(port_id, member_id);
 	if (res != 0)
@@ -1727,6 +1765,11 @@ rte_eth_bond_8023ad_dedicated_queues_enable(uint16_t port)
 {
 	struct rte_eth_dev *dev;
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(port) != 0)
 		return -EINVAL;
@@ -1756,6 +1799,11 @@ rte_eth_bond_8023ad_dedicated_queues_disable(uint16_t port)
 {
 	struct rte_eth_dev *dev;
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -ENOTSUP);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(port) != 0)
 		return -EINVAL;
diff --git a/drivers/net/bonding/rte_eth_bond_api.c b/drivers/net/bonding/rte_eth_bond_api.c
index d9b6f1c417..029e141d89 100644
--- a/drivers/net/bonding/rte_eth_bond_api.c
+++ b/drivers/net/bonding/rte_eth_bond_api.c
@@ -159,6 +159,10 @@ rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id)
 	char devargs[52];
 	int ret;
 
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
+
 	if (name == NULL) {
 		RTE_BOND_LOG(ERR, "Invalid name specified");
 		return -EINVAL;
@@ -640,9 +644,12 @@ rte_eth_bond_member_add(uint16_t bonding_port_id, uint16_t member_port_id)
 {
 	struct rte_eth_dev *bonding_eth_dev;
 	struct bond_dev_private *internals;
-
 	int retval;
 
+	retval = bond_check_primary(__func__, -1);
+	if (retval != 0)
+		return retval;
+
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
 
@@ -781,6 +788,10 @@ rte_eth_bond_member_remove(uint16_t bonding_port_id, uint16_t member_port_id)
 	struct bond_dev_private *internals;
 	int retval;
 
+	retval = bond_check_primary(__func__, -1);
+	if (retval != 0)
+		return retval;
+
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
 
@@ -801,6 +812,11 @@ int
 rte_eth_bond_mode_set(uint16_t bonding_port_id, uint8_t mode)
 {
 	struct rte_eth_dev *bonding_eth_dev;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -833,6 +849,11 @@ int
 rte_eth_bond_primary_set(uint16_t bonding_port_id, uint16_t member_port_id)
 {
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -923,6 +944,11 @@ rte_eth_bond_mac_address_set(uint16_t bonding_port_id,
 {
 	struct rte_eth_dev *bonding_eth_dev;
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -949,6 +975,11 @@ rte_eth_bond_mac_address_reset(uint16_t bonding_port_id)
 {
 	struct rte_eth_dev *bonding_eth_dev;
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -990,6 +1021,11 @@ int
 rte_eth_bond_xmit_policy_set(uint16_t bonding_port_id, uint8_t policy)
 {
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -1035,6 +1071,11 @@ int
 rte_eth_bond_link_monitoring_set(uint16_t bonding_port_id, uint32_t internal_ms)
 {
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -1064,6 +1105,11 @@ rte_eth_bond_link_down_prop_delay_set(uint16_t bonding_port_id,
 
 {
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
@@ -1092,6 +1138,11 @@ rte_eth_bond_link_up_prop_delay_set(uint16_t bonding_port_id, uint32_t delay_ms)
 
 {
 	struct bond_dev_private *internals;
+	int ret;
+
+	ret = bond_check_primary(__func__, -1);
+	if (ret != 0)
+		return ret;
 
 	if (valid_bonding_port_id(bonding_port_id) != 0)
 		return -1;
diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c
index 6f10dbb0c7..bba93a5638 100644
--- a/drivers/net/bonding/rte_eth_bond_pmd.c
+++ b/drivers/net/bonding/rte_eth_bond_pmd.c
@@ -2558,22 +2558,24 @@ bond_ethdev_link_update(struct rte_eth_dev *ethdev, int wait_to_complete)
 
 	struct bond_dev_private *bond_ctx;
 	struct rte_eth_link member_link;
+	struct rte_eth_link bond_link;
 
 	bool one_link_update_succeeded;
 	uint32_t idx;
 	int ret;
 
-	bond_ctx = ethdev->data->dev_private;
+	rte_eth_linkstatus_get(ethdev, &bond_link);
+	bond_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
 
-	ethdev->data->dev_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
+	bond_ctx = ethdev->data->dev_private;
 
 	if (ethdev->data->dev_started == 0 ||
 			bond_ctx->active_member_count == 0) {
-		ethdev->data->dev_link.link_status = RTE_ETH_LINK_DOWN;
-		return 0;
+		bond_link.link_status = RTE_ETH_LINK_DOWN;
+		goto out;
 	}
 
-	ethdev->data->dev_link.link_status = RTE_ETH_LINK_UP;
+	bond_link.link_status = RTE_ETH_LINK_UP;
 
 	if (wait_to_complete)
 		link_update = rte_eth_link_get;
@@ -2586,7 +2588,7 @@ bond_ethdev_link_update(struct rte_eth_dev *ethdev, int wait_to_complete)
 		 * Setting link speed to UINT32_MAX to ensure we pick up the
 		 * value of the first active member
 		 */
-		ethdev->data->dev_link.link_speed = UINT32_MAX;
+		bond_link.link_speed = UINT32_MAX;
 
 		/**
 		 * link speed is minimum value of all the members link speed as
@@ -2597,19 +2599,16 @@ bond_ethdev_link_update(struct rte_eth_dev *ethdev, int wait_to_complete)
 			ret = link_update(bond_ctx->active_members[idx],
 					  &member_link);
 			if (ret < 0) {
-				ethdev->data->dev_link.link_speed =
-					RTE_ETH_SPEED_NUM_NONE;
+				bond_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
 				RTE_BOND_LOG(ERR,
 					"Member (port %u) link get failed: %s",
 					bond_ctx->active_members[idx],
 					rte_strerror(-ret));
-				return 0;
+				goto out;
 			}
 
-			if (member_link.link_speed <
-					ethdev->data->dev_link.link_speed)
-				ethdev->data->dev_link.link_speed =
-						member_link.link_speed;
+			if (member_link.link_speed < bond_link.link_speed)
+				bond_link.link_speed = member_link.link_speed;
 		}
 		break;
 	case BONDING_MODE_ACTIVE_BACKUP:
@@ -2619,15 +2618,15 @@ bond_ethdev_link_update(struct rte_eth_dev *ethdev, int wait_to_complete)
 			RTE_BOND_LOG(ERR, "Member (port %u) link get failed: %s",
 				bond_ctx->current_primary_port,
 				rte_strerror(-ret));
-			return 0;
+			goto out;
 		}
 
-		ethdev->data->dev_link.link_speed = member_link.link_speed;
+		bond_link.link_speed = member_link.link_speed;
 		break;
 	case BONDING_MODE_8023AD:
-		ethdev->data->dev_link.link_autoneg =
+		bond_link.link_autoneg =
 				bond_ctx->mode4.member_link.link_autoneg;
-		ethdev->data->dev_link.link_duplex =
+		bond_link.link_duplex =
 				bond_ctx->mode4.member_link.link_duplex;
 		/* fall through */
 		/* to update link speed */
@@ -2640,7 +2639,7 @@ bond_ethdev_link_update(struct rte_eth_dev *ethdev, int wait_to_complete)
 		 * In theses mode the maximum theoretical link speed is the sum
 		 * of all the members
 		 */
-		ethdev->data->dev_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
+		bond_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
 		one_link_update_succeeded = false;
 
 		for (idx = 0; idx < bond_ctx->active_member_count; idx++) {
@@ -2655,17 +2654,17 @@ bond_ethdev_link_update(struct rte_eth_dev *ethdev, int wait_to_complete)
 			}
 
 			one_link_update_succeeded = true;
-			ethdev->data->dev_link.link_speed +=
-					member_link.link_speed;
+			bond_link.link_speed += member_link.link_speed;
 		}
 
 		if (!one_link_update_succeeded) {
 			RTE_BOND_LOG(ERR, "All members link get failed");
-			return 0;
+			goto out;
 		}
 	}
 
-
+out:
+	rte_eth_linkstatus_set(ethdev, &bond_link);
 	return 0;
 }
 
@@ -3641,12 +3640,23 @@ bond_ethdev_priv_dump(struct rte_eth_dev *dev, FILE *f)
 	const struct bond_dev_private *internals = dev->data->dev_private;
 
 	dump_basic(dev, f);
-	if (internals->mode == BONDING_MODE_8023AD)
+	if (internals->mode == BONDING_MODE_8023AD &&
+			rte_eal_process_type() == RTE_PROC_PRIMARY)
 		dump_lacp(dev->data->port_id, f);
 
 	return 0;
 }
 
+static const struct eth_dev_ops secondary_dev_ops = {
+	.dev_close         = bond_ethdev_close,
+	.dev_infos_get     = bond_ethdev_info,
+	.link_update       = bond_ethdev_link_update,
+	.stats_get         = bond_ethdev_stats_get,
+	.reta_query        = bond_ethdev_rss_reta_query,
+	.rss_hash_conf_get = bond_ethdev_rss_hash_conf_get,
+	.eth_dev_priv_dump = bond_ethdev_priv_dump,
+};
+
 const struct eth_dev_ops default_dev_ops = {
 	.dev_start            = bond_ethdev_start,
 	.dev_stop             = bond_ethdev_stop,
@@ -3831,7 +3841,7 @@ bond_probe(struct rte_vdev_device *dev)
 			return -1;
 		}
 
-		eth_dev->dev_ops = &default_dev_ops;
+		eth_dev->dev_ops = &secondary_dev_ops;
 		eth_dev->device = &dev->device;
 
 		/*
-- 
2.34.1



More information about the dev mailing list