* [RFC PATCH] net/bonding: reject control operations in secondary
@ 2026-07-08 17:42 Weijun Pan
2026-07-22 23:10 ` Stephen Hemminger
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Weijun Pan @ 2026-07-08 17:42 UTC (permalink / raw)
To: Chas Williams, Min Hu (Connor), Anatoly Burakov; +Cc: dev, Weijun Pan
The bonding PMD installs safe secondary burst functions when real
secondary datapath support is not available. This avoids crashes, but
secondary processes must not be able to change bonding control-plane
state if the primary process is the owner of that state.
Reject bonding control-plane operations from secondary processes. This
keeps bonding configuration and LACP state owned by the primary process
and is a prerequisite for future limited secondary datapath support.
Bugzilla ID: 1900
Signed-off-by: Weijun Pan <wpan3636@gmail.com>
---
drivers/net/bonding/rte_eth_bond_8023ad.c | 31 ++++++++++++
drivers/net/bonding/rte_eth_bond_api.c | 47 +++++++++++++++++
drivers/net/bonding/rte_eth_bond_pmd.c | 61 +++++++++++++++++++++++
3 files changed, 139 insertions(+)
diff --git a/drivers/net/bonding/rte_eth_bond_8023ad.c b/drivers/net/bonding/rte_eth_bond_8023ad.c
index d1f30229d0..685255ffc3 100644
--- a/drivers/net/bonding/rte_eth_bond_8023ad.c
+++ b/drivers/net/bonding/rte_eth_bond_8023ad.c
@@ -1428,11 +1428,24 @@ rte_eth_bond_8023ad_conf_get(uint16_t port_id,
return 0;
}
+static int
+bond_8023ad_primary_only(const char *op)
+{
+ if (rte_eal_process_type() != RTE_PROC_SECONDARY)
+ return 0;
+
+ RTE_BOND_LOG(ERR, "%s not supported in secondary process", op);
+ return -ENOTSUP;
+}
+
RTE_EXPORT_SYMBOL(rte_eth_bond_8023ad_agg_selection_set)
int
rte_eth_bond_8023ad_agg_selection_set(uint16_t port_id,
enum rte_bond_8023ad_agg_selection agg_selection)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct rte_eth_dev *bond_dev;
struct bond_dev_private *internals;
struct mode8023ad_private *mode4;
@@ -1506,6 +1519,9 @@ int
rte_eth_bond_8023ad_setup(uint16_t port_id,
struct rte_eth_bond_8023ad_conf *conf)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct rte_eth_dev *bond_dev;
int err;
@@ -1590,6 +1606,9 @@ int
rte_eth_bond_8023ad_ext_collect(uint16_t port_id, uint16_t member_id,
int enabled)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct port *port;
int res;
@@ -1612,6 +1631,9 @@ int
rte_eth_bond_8023ad_ext_distrib(uint16_t port_id, uint16_t member_id,
int enabled)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct port *port;
int res;
@@ -1664,6 +1686,9 @@ int
rte_eth_bond_8023ad_ext_slowtx(uint16_t port_id, uint16_t member_id,
struct rte_mbuf *lacp_pkt)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct port *port;
int res;
@@ -1725,6 +1750,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_8023ad_dedicated_queues_enable)
int
rte_eth_bond_8023ad_dedicated_queues_enable(uint16_t port)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct rte_eth_dev *dev;
struct bond_dev_private *internals;
@@ -1754,6 +1782,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_8023ad_dedicated_queues_disable)
int
rte_eth_bond_8023ad_dedicated_queues_disable(uint16_t port)
{
+ if (bond_8023ad_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct rte_eth_dev *dev;
struct bond_dev_private *internals;
diff --git a/drivers/net/bonding/rte_eth_bond_api.c b/drivers/net/bonding/rte_eth_bond_api.c
index d9b6f1c417..3b754bf8e0 100644
--- a/drivers/net/bonding/rte_eth_bond_api.c
+++ b/drivers/net/bonding/rte_eth_bond_api.c
@@ -150,10 +150,24 @@ deactivate_member(struct rte_eth_dev *eth_dev, uint16_t port_id)
}
}
+static int
+bond_api_primary_only(const char *op)
+{
+ if (rte_eal_process_type() != RTE_PROC_SECONDARY)
+ return 0;
+
+ RTE_BOND_LOG(ERR, "%s not supported in secondary process", op);
+ return -1;
+}
+
+
RTE_EXPORT_SYMBOL(rte_eth_bond_create)
int
rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct bond_dev_private *internals;
struct rte_eth_dev *bond_dev;
char devargs[52];
@@ -193,6 +207,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_free)
int
rte_eth_bond_free(const char *name)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
return rte_vdev_uninit(name);
}
@@ -638,6 +655,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_member_add)
int
rte_eth_bond_member_add(uint16_t bonding_port_id, uint16_t member_port_id)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct rte_eth_dev *bonding_eth_dev;
struct bond_dev_private *internals;
@@ -777,6 +797,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_member_remove)
int
rte_eth_bond_member_remove(uint16_t bonding_port_id, uint16_t member_port_id)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct rte_eth_dev *bonding_eth_dev;
struct bond_dev_private *internals;
int retval;
@@ -800,6 +823,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_mode_set)
int
rte_eth_bond_mode_set(uint16_t bonding_port_id, uint8_t mode)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct rte_eth_dev *bonding_eth_dev;
if (valid_bonding_port_id(bonding_port_id) != 0)
@@ -832,6 +858,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_primary_set)
int
rte_eth_bond_primary_set(uint16_t bonding_port_id, uint16_t member_port_id)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct bond_dev_private *internals;
if (valid_bonding_port_id(bonding_port_id) != 0)
@@ -921,6 +950,9 @@ int
rte_eth_bond_mac_address_set(uint16_t bonding_port_id,
struct rte_ether_addr *mac_addr)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct rte_eth_dev *bonding_eth_dev;
struct bond_dev_private *internals;
@@ -947,6 +979,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_mac_address_reset)
int
rte_eth_bond_mac_address_reset(uint16_t bonding_port_id)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct rte_eth_dev *bonding_eth_dev;
struct bond_dev_private *internals;
@@ -989,6 +1024,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_xmit_policy_set)
int
rte_eth_bond_xmit_policy_set(uint16_t bonding_port_id, uint8_t policy)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct bond_dev_private *internals;
if (valid_bonding_port_id(bonding_port_id) != 0)
@@ -1034,6 +1072,9 @@ RTE_EXPORT_SYMBOL(rte_eth_bond_link_monitoring_set)
int
rte_eth_bond_link_monitoring_set(uint16_t bonding_port_id, uint32_t internal_ms)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct bond_dev_private *internals;
if (valid_bonding_port_id(bonding_port_id) != 0)
@@ -1063,6 +1104,9 @@ rte_eth_bond_link_down_prop_delay_set(uint16_t bonding_port_id,
uint32_t delay_ms)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct bond_dev_private *internals;
if (valid_bonding_port_id(bonding_port_id) != 0)
@@ -1091,6 +1135,9 @@ int
rte_eth_bond_link_up_prop_delay_set(uint16_t bonding_port_id, uint32_t delay_ms)
{
+ if (bond_api_primary_only(__func__) != 0)
+ return -1;
+
struct bond_dev_private *internals;
if (valid_bonding_port_id(bonding_port_id) != 0)
diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c
index 6a4f997b5a..b6e38d4a60 100644
--- a/drivers/net/bonding/rte_eth_bond_pmd.c
+++ b/drivers/net/bonding/rte_eth_bond_pmd.c
@@ -2052,9 +2052,22 @@ bond_ethdev_primary_set(struct bond_dev_private *internals,
static int
bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev);
+static int
+bond_ethdev_primary_only(const char *op)
+{
+ if (rte_eal_process_type() != RTE_PROC_SECONDARY)
+ return 0;
+
+ RTE_BOND_LOG(ERR, "%s not supported in secondary process", op);
+ return -ENOTSUP;
+}
+
static int
bond_ethdev_start(struct rte_eth_dev *eth_dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals;
int i;
@@ -2186,6 +2199,9 @@ bond_ethdev_free_queues(struct rte_eth_dev *dev)
int
bond_ethdev_stop(struct rte_eth_dev *eth_dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals = eth_dev->data->dev_private;
uint16_t i;
int ret;
@@ -2400,6 +2416,9 @@ bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
static int
bond_ethdev_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
int res;
uint16_t i;
struct bond_dev_private *internals = dev->data->dev_private;
@@ -2431,6 +2450,9 @@ bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id,
uint16_t nb_rx_desc, unsigned int socket_id __rte_unused,
const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)
rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue),
0, dev->data->numa_node);
@@ -2455,6 +2477,9 @@ bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id,
uint16_t nb_tx_desc, unsigned int socket_id __rte_unused,
const struct rte_eth_txconf *tx_conf)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *)
rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue),
0, dev->data->numa_node);
@@ -2697,6 +2722,9 @@ bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats,
static int
bond_ethdev_stats_reset(struct rte_eth_dev *dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals = dev->data->dev_private;
int i;
int err;
@@ -2714,6 +2742,9 @@ bond_ethdev_stats_reset(struct rte_eth_dev *dev)
static int
bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals = eth_dev->data->dev_private;
int i;
int ret = 0;
@@ -2768,6 +2799,9 @@ bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev)
static int
bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals = dev->data->dev_private;
int i;
int ret = 0;
@@ -2871,6 +2905,9 @@ bond_ethdev_promiscuous_update(struct rte_eth_dev *dev)
static int
bond_ethdev_allmulticast_enable(struct rte_eth_dev *eth_dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals = eth_dev->data->dev_private;
int i;
int ret = 0;
@@ -2925,6 +2962,9 @@ bond_ethdev_allmulticast_enable(struct rte_eth_dev *eth_dev)
static int
bond_ethdev_allmulticast_disable(struct rte_eth_dev *eth_dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct bond_dev_private *internals = eth_dev->data->dev_private;
int i;
int ret = 0;
@@ -3186,6 +3226,9 @@ static int
bond_ethdev_rss_reta_update(struct rte_eth_dev *dev,
struct rte_eth_rss_reta_entry64 *reta_conf, uint16_t reta_size)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
unsigned i, j;
int result = 0;
int member_reta_size;
@@ -3246,6 +3289,9 @@ static int
bond_ethdev_rss_hash_update(struct rte_eth_dev *dev,
struct rte_eth_rss_conf *rss_conf)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
int i, result = 0;
struct bond_dev_private *internals = dev->data->dev_private;
struct rte_eth_rss_conf bond_rss_conf;
@@ -3295,6 +3341,9 @@ bond_ethdev_rss_hash_conf_get(struct rte_eth_dev *dev,
static int
bond_ethdev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct rte_eth_dev *member_eth_dev;
struct bond_dev_private *internals = dev->data->dev_private;
int ret, i;
@@ -3324,6 +3373,9 @@ static int
bond_ethdev_mac_address_set(struct rte_eth_dev *dev,
struct rte_ether_addr *addr)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
if (mac_address_set(dev, addr)) {
RTE_BOND_LOG(ERR, "Failed to update MAC address");
return -EINVAL;
@@ -3345,6 +3397,9 @@ bond_ethdev_mac_addr_add(struct rte_eth_dev *dev,
struct rte_ether_addr *mac_addr,
__rte_unused uint32_t index, uint32_t vmdq)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
struct rte_eth_dev *member_eth_dev;
struct bond_dev_private *internals = dev->data->dev_private;
int ret, i;
@@ -3381,6 +3436,9 @@ bond_ethdev_mac_addr_add(struct rte_eth_dev *dev,
static void
bond_ethdev_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return;
+
struct rte_eth_dev *member_eth_dev;
struct bond_dev_private *internals = dev->data->dev_private;
int i;
@@ -3965,6 +4023,9 @@ bond_remove(struct rte_vdev_device *dev)
static int
bond_ethdev_configure(struct rte_eth_dev *dev)
{
+ if (bond_ethdev_primary_only(__func__) != 0)
+ return -ENOTSUP;
+
const char *name = dev->device->name;
struct bond_dev_private *internals = dev->data->dev_private;
struct rte_kvargs *kvlist = internals->kvlist;
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [RFC PATCH] net/bonding: reject control operations in secondary
2026-07-08 17:42 [RFC PATCH] net/bonding: reject control operations in secondary Weijun Pan
@ 2026-07-22 23:10 ` Stephen Hemminger
2026-07-26 17:32 ` Stephen Hemminger
2026-08-23 15:16 ` [RFC PATCH v2] net/bonding: restrict secondary control operations Weijun Pan
2 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-07-22 23:10 UTC (permalink / raw)
To: Weijun Pan; +Cc: Chas Williams, Min Hu (Connor), Anatoly Burakov, dev
On Wed, 8 Jul 2026 12:42:04 -0500
Weijun Pan <wpan3636@gmail.com> wrote:
> +static int
> +bond_ethdev_primary_only(const char *op)
> +{
> + if (rte_eal_process_type() != RTE_PROC_SECONDARY)
> + return 0;
> +
> + RTE_BOND_LOG(ERR, "%s not supported in secondary process", op);
> + return -ENOTSUP;
> +}
> +
Minor nit, the function returns 0 or -ENOTSUP but then caller
always ends up just checking for 0. It might look cleaner as boolean
function or wrap the whole thing as a macro like ethdev does.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC PATCH] net/bonding: reject control operations in secondary
2026-07-08 17:42 [RFC PATCH] net/bonding: reject control operations in secondary Weijun Pan
2026-07-22 23:10 ` Stephen Hemminger
@ 2026-07-26 17:32 ` Stephen Hemminger
2026-08-23 15:16 ` [RFC PATCH v2] net/bonding: restrict secondary control operations Weijun Pan
2 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-07-26 17:32 UTC (permalink / raw)
To: Weijun Pan; +Cc: Chas Williams, Min Hu (Connor), Anatoly Burakov, dev
On Wed, 8 Jul 2026 12:42:04 -0500
Weijun Pan <wpan3636@gmail.com> wrote:
> The bonding PMD installs safe secondary burst functions when real
> secondary datapath support is not available. This avoids crashes, but
> secondary processes must not be able to change bonding control-plane
> state if the primary process is the owner of that state.
>
> Reject bonding control-plane operations from secondary processes. This
> keeps bonding configuration and LACP state owned by the primary process
> and is a prerequisite for future limited secondary datapath support.
>
> Bugzilla ID: 1900
>
> Signed-off-by: Weijun Pan <wpan3636@gmail.com>
> ---
This is the right path, but AI review found things that need work.
Review: [RFC PATCH] net/bonding: reject control operations in secondary
Message-Id: <20260708174204.72574-1-wpan3636@gmail.com>
Verified against DPDK main (38f72e500b3b). Applies cleanly, builds clean
with -Dwerror=true, checkpatches.sh reports no issues.
The intent is right, but the checks are placed one layer too low, which
means the stated goal -- "secondary processes must not be able to change
bonding control-plane state" -- is not actually met for dev_configure,
and one legitimate secondary path is broken.
Errors
------
1. rte_eth_bond_pmd.c: bond_ethdev_configure()
Returning -ENOTSUP from the dev_configure op does not prevent the
secondary from mutating shared state -- it makes it worse.
rte_eth_dev_configure() has already done this before it calls the PMD
(lib/ethdev/rte_ethdev.c):
1364 memcpy(&orig_conf, &dev->data->dev_conf, ...)
1587 diag = eth_dev_rx_queue_config(dev, nb_rx_q);
1596 diag = eth_dev_tx_queue_config(dev, nb_tx_q);
1606 diag = dev->dev_ops->dev_configure(dev);
1646 reset_queues:
1647 eth_dev_rx_queue_config(dev, 0);
1648 eth_dev_tx_queue_config(dev, 0);
On the -ENOTSUP the code jumps to reset_queues, and
eth_dev_rx_queue_config(dev, 0) calls dev_ops->rx_queue_release() on
every queue and rte_free()s dev->data->rx_queues, then sets
nb_rx_queues = 0 (lib/ethdev/ethdev_private.c:455). Those are the
primary's queues in shared memory. bond_ethdev_rx_queue_release() /
bond_ethdev_tx_queue_release() are not gated by this patch, so they
run.
Net effect: a stray rte_eth_dev_configure() from a secondary now
destroys the primary's Rx/Tx queue arrays instead of being rejected.
2. rte_eth_bond_api.c: rte_eth_bond_free()
This blocks the only supported way for a secondary to detach its
local port. rte_vdev_uninit() is process-local and works in a
secondary; bond_remove() has a deliberate secondary branch
(rte_eth_bond_pmd.c:4003):
if (rte_eal_process_type() != RTE_PROC_PRIMARY)
return rte_eth_dev_release_port(eth_dev);
That branch is now unreachable through the public API. Compare
bond_ethdev_close() ten lines away (2310), which returns 0 rather
than an error for non-primary, and the comment in
rte_eth_dev_close() explaining that a secondary must be able to
close to release its process-private resources. Teardown paths need
to stay callable; only reconfiguration should be rejected.
Warnings
--------
3. Structural: use a separate ops table rather than 17 in-function
checks.
bond_probe() already installs the ops table for the secondary
explicitly (rte_eth_bond_pmd.c:3889):
eth_dev->dev_ops = &default_dev_ops;
ethdev already returns -ENOTSUP for every one of these ops when the
pointer is NULL -- dev_configure (1347), dev_start (1801),
rx_queue_setup (2307), promiscuous_enable (3027), mtu_set (4400),
reta_update (5007), mac_addr_add (5415), mac_addr_remove (5479),
and so on -- and it does so *before* touching dev->data, which is
exactly what fixes finding 1. Defining
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,
};
and assigning it in the secondary branch of bond_probe() gets you
identical semantics with one hunk instead of eighteen, and no
runtime check on the primary's path.
4. Wrong polarity. All three helpers test
rte_eal_process_type() != RTE_PROC_SECONDARY
The established idiom is != RTE_PROC_PRIMARY: 357 occurrences under
drivers/ against 3 of the form used here, and both existing checks
in this driver (bond_ethdev_close at 2310, bond_remove at 4003) use
the primary form. As written, RTE_PROC_AUTO and RTE_PROC_INVALID
fall through to the permissive path.
5. Three byte-identical static helpers in three files
(bond_8023ad_primary_only, bond_api_primary_only,
bond_ethdev_primary_only). One static inline in
eth_bond_private.h.
6. The helper's return value is computed and discarded:
if (bond_8023ad_primary_only(__func__) != 0)
return -ENOTSUP;
Either propagate it (ret = ...; if (ret != 0) return ret;) or make
the helper return bool and name it accordingly.
7. Coverage gaps. flow_ops_get is not gated, so rte_flow_create() /
rte_flow_destroy() from a secondary still mutate bonding state.
rx_queue_release / tx_queue_release are ungated while the
corresponding setup ops are gated -- see finding 1 for why that
combination bites.
8. No documentation. This changes the observable behaviour of exported
API, but doc/guides/prog_guide/link_bonding_poll_mode_drv_lib.rst
says nothing about multi-process, and
doc/guides/rel_notes/release_26_07.rst is not touched. The guide
needs a short section stating which operations are primary-only.
9. Bugzilla ID 1900 is cited with no Fixes: tag and no
Cc: stable@dpdk.org. If 1900 is a crash being fixed, both are
needed. If this is groundwork for secondary datapath support, say
so and drop the implication.
Info
----
10. Statements are inserted ahead of the declarations in every touched
function. Legal under c11, but it inverts the DPDK function layout
(declarations, blank line, statements) throughout. Folding the
check into the existing declaration block would avoid it; the ops
table in finding 3 avoids it entirely. Also, rte_eth_bond_api.c
gains a double blank line after bond_api_primary_only().
11. rte_eth_dev_stop() resets that process's fast-path ops to the dummy
functions before calling dev_stop (rte_ethdev.c:1819). With
bond_ethdev_stop() now returning -ENOTSUP, a secondary that calls
stop ends up with a dead local datapath, dev_started still 1, and
no way to restart since dev_start is also gated.
Given findings 1 and 2, no Reviewed-by on this revision.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH v2] net/bonding: restrict secondary control operations
2026-07-08 17:42 [RFC PATCH] net/bonding: reject control operations in secondary Weijun Pan
2026-07-22 23:10 ` Stephen Hemminger
2026-07-26 17:32 ` Stephen Hemminger
@ 2026-08-23 15:16 ` Weijun Pan
2026-08-23 15:43 ` Stephen Hemminger
` (2 more replies)
2 siblings, 3 replies; 9+ messages in thread
From: Weijun Pan @ 2026-08-23 15:16 UTC (permalink / raw)
To: Chas Williams, Min Hu (Connor), Anatoly Burakov; +Cc: dev, Weijun Pan
The bonding PMD currently supports secondary attach with safe fallback
burst functions, but bonding configuration and LACP state are owned by
the primary process.
Use a secondary-specific dev_ops table so unsupported operations are
rejected by ethdev before PMD callbacks can mutate shared device state.
Also reject bonding-specific control APIs from non-primary processes,
while keeping secondary detach and query paths available.
This keeps secondary process behavior safe while leaving room for future
limited datapath support.
Bugzilla ID: 1900
Signed-off-by: Weijun Pan <wpan3636@gmail.com>
---
.../link_bonding_poll_mode_drv_lib.rst | 13 ++++++
doc/guides/rel_notes/release_26_11.rst | 6 +++
drivers/net/bonding/eth_bond_private.h | 8 ++++
drivers/net/bonding/rte_eth_bond_8023ad.c | 45 +++++++++++++++++++
drivers/net/bonding/rte_eth_bond_api.c | 44 ++++++++++++++++++
drivers/net/bonding/rte_eth_bond_pmd.c | 12 ++++-
6 files changed, 127 insertions(+), 1 deletion(-)
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..7ecb05d407 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,19 @@ 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 is owned by the primary process. Secondary
+processes may attach to an existing bonding device for query and detach
+operations, but must not change bonding configuration or device state.
+
+In a secondary process, bonding control operations such as configuring,
+starting or stopping the device, setting up queues, changing members,
+changing the bonding mode, updating RSS, changing MAC addresses, changing
+MTU, or configuring ``rte_flow`` rules are not supported.
+
+Secondary process datapath support is limited and bonding mode specific.
+Applications should not rely on secondary processes for bonding datapath
+operation unless support for the selected mode is explicitly documented.
+
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..8df012c99b 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,12 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Updated bonding PMD secondary process handling.**
+
+ Bonding devices now use a secondary-process device operations table to
+ keep query and detach paths available while rejecting unsupported control
+ operations before shared ethdev state can be modified. Bonding-specific
+ control APIs are also restricted to the primary process.
Removed Items
-------------
diff --git a/drivers/net/bonding/eth_bond_private.h b/drivers/net/bonding/eth_bond_private.h
index 378bbba4e6..750e5925df 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,12 @@ find_member_by_id(uint16_t *members, uint16_t members_count, uint16_t member_id)
return pos;
}
+static inline bool
+bond_process_is_primary(void)
+{
+ return rte_eal_process_type() == RTE_PROC_PRIMARY;
+}
+
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..e3d02f0539 100644
--- a/drivers/net/bonding/rte_eth_bond_8023ad.c
+++ b/drivers/net/bonding/rte_eth_bond_8023ad.c
@@ -1428,6 +1428,16 @@ rte_eth_bond_8023ad_conf_get(uint16_t port_id,
return 0;
}
+static int
+bond_8023ad_check_primary(const char *op)
+{
+ if (bond_process_is_primary())
+ return 0;
+
+ RTE_BOND_LOG(ERR, "%s not supported in non-primary process", op);
+ return -ENOTSUP;
+}
+
RTE_EXPORT_SYMBOL(rte_eth_bond_8023ad_agg_selection_set)
int
rte_eth_bond_8023ad_agg_selection_set(uint16_t port_id,
@@ -1436,6 +1446,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_8023ad_check_primary(__func__);
+ if (ret != 0)
+ return ret;
if (valid_bonding_port_id(port_id) != 0)
return -EINVAL;
@@ -1508,6 +1523,11 @@ rte_eth_bond_8023ad_setup(uint16_t port_id,
{
struct rte_eth_dev *bond_dev;
int err;
+ int ret;
+
+ ret = bond_8023ad_check_primary(__func__);
+ if (ret != 0)
+ return ret;
err = bond_8023ad_setup_validate(port_id, conf);
if (err != 0)
@@ -1592,6 +1612,11 @@ rte_eth_bond_8023ad_ext_collect(uint16_t port_id, uint16_t member_id,
{
struct port *port;
int res;
+ int ret;
+
+ ret = bond_8023ad_check_primary(__func__);
+ if (ret != 0)
+ return ret;
res = bond_8023ad_ext_validate(port_id, member_id);
if (res != 0)
@@ -1614,6 +1639,11 @@ rte_eth_bond_8023ad_ext_distrib(uint16_t port_id, uint16_t member_id,
{
struct port *port;
int res;
+ int ret;
+
+ ret = bond_8023ad_check_primary(__func__);
+ if (ret != 0)
+ return ret;
res = bond_8023ad_ext_validate(port_id, member_id);
if (res != 0)
@@ -1666,6 +1696,11 @@ rte_eth_bond_8023ad_ext_slowtx(uint16_t port_id, uint16_t member_id,
{
struct port *port;
int res;
+ int ret;
+
+ ret = bond_8023ad_check_primary(__func__);
+ if (ret != 0)
+ return ret;
res = bond_8023ad_ext_validate(port_id, member_id);
if (res != 0)
@@ -1727,6 +1762,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_8023ad_check_primary(__func__);
+ if (ret != 0)
+ return ret;
if (valid_bonding_port_id(port) != 0)
return -EINVAL;
@@ -1756,6 +1796,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_8023ad_check_primary(__func__);
+ 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..c4f055bfa5 100644
--- a/drivers/net/bonding/rte_eth_bond_api.c
+++ b/drivers/net/bonding/rte_eth_bond_api.c
@@ -150,6 +150,17 @@ deactivate_member(struct rte_eth_dev *eth_dev, uint16_t port_id)
}
}
+static int
+bond_api_check_primary(const char *op)
+{
+ if (bond_process_is_primary())
+ return 0;
+
+ RTE_BOND_LOG(ERR, "%s not supported in non-primary process", op);
+ return -1;
+}
+
+
RTE_EXPORT_SYMBOL(rte_eth_bond_create)
int
rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id)
@@ -159,6 +170,9 @@ rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id)
char devargs[52];
int ret;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (name == NULL) {
RTE_BOND_LOG(ERR, "Invalid name specified");
return -EINVAL;
@@ -643,6 +657,9 @@ rte_eth_bond_member_add(uint16_t bonding_port_id, uint16_t member_port_id)
int retval;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -781,6 +798,9 @@ rte_eth_bond_member_remove(uint16_t bonding_port_id, uint16_t member_port_id)
struct bond_dev_private *internals;
int retval;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -802,6 +822,9 @@ rte_eth_bond_mode_set(uint16_t bonding_port_id, uint8_t mode)
{
struct rte_eth_dev *bonding_eth_dev;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -834,6 +857,9 @@ rte_eth_bond_primary_set(uint16_t bonding_port_id, uint16_t member_port_id)
{
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -924,6 +950,9 @@ rte_eth_bond_mac_address_set(uint16_t bonding_port_id,
struct rte_eth_dev *bonding_eth_dev;
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -950,6 +979,9 @@ rte_eth_bond_mac_address_reset(uint16_t bonding_port_id)
struct rte_eth_dev *bonding_eth_dev;
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -991,6 +1023,9 @@ rte_eth_bond_xmit_policy_set(uint16_t bonding_port_id, uint8_t policy)
{
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -1036,6 +1071,9 @@ rte_eth_bond_link_monitoring_set(uint16_t bonding_port_id, uint32_t internal_ms)
{
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -1065,6 +1103,9 @@ rte_eth_bond_link_down_prop_delay_set(uint16_t bonding_port_id,
{
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
if (valid_bonding_port_id(bonding_port_id) != 0)
return -1;
@@ -1093,6 +1134,9 @@ rte_eth_bond_link_up_prop_delay_set(uint16_t bonding_port_id, uint32_t delay_ms)
{
struct bond_dev_private *internals;
+ if (bond_api_check_primary(__func__) != 0)
+ return -1;
+
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 6a4f997b5a..f7fb562cee 100644
--- a/drivers/net/bonding/rte_eth_bond_pmd.c
+++ b/drivers/net/bonding/rte_eth_bond_pmd.c
@@ -3644,6 +3644,16 @@ bond_ethdev_priv_dump(struct rte_eth_dev *dev, FILE *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,
@@ -3828,7 +3838,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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [RFC PATCH v2] net/bonding: restrict secondary control operations
2026-08-23 15:16 ` [RFC PATCH v2] net/bonding: restrict secondary control operations Weijun Pan
@ 2026-08-23 15:43 ` Stephen Hemminger
2026-08-24 2:39 ` Weijun Pan
2026-08-24 16:15 ` Stephen Hemminger
2026-08-26 16:10 ` [RFC PATCH v3] " Weijun Pan
2 siblings, 1 reply; 9+ messages in thread
From: Stephen Hemminger @ 2026-08-23 15:43 UTC (permalink / raw)
To: Weijun Pan; +Cc: Chas Williams, Min Hu (Connor), Anatoly Burakov, dev
On Sun, 23 Aug 2026 10:16:25 -0500
Weijun Pan <wpan3636@gmail.com> wrote:
> 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..7ecb05d407 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,19 @@ 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 is owned by the primary process. Secondary
> +processes may attach to an existing bonding device for query and detach
> +operations, but must not change bonding configuration or device state.
> +
> +In a secondary process, bonding control operations such as configuring,
> +starting or stopping the device, setting up queues, changing members,
> +changing the bonding mode, updating RSS, changing MAC addresses, changing
> +MTU, or configuring ``rte_flow`` rules are not supported.
> +
> +Secondary process datapath support is limited and bonding mode specific.
> +Applications should not rely on secondary processes for bonding datapath
> +operation unless support for the selected mode is explicitly documented.
> +
> 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..8df012c99b 100644
> --- a/doc/guides/rel_notes/release_26_11.rst
> +++ b/doc/guides/rel_notes/release_26_11.rst
> @@ -55,6 +55,12 @@ New Features
> Also, make sure to start the actual text at the margin.
> =======================================================
>
> +* **Updated bonding PMD secondary process handling.**
> +
> + Bonding devices now use a secondary-process device operations table to
> + keep query and detach paths available while rejecting unsupported control
> + operations before shared ethdev state can be modified. Bonding-specific
> + control APIs are also restricted to the primary process.
>
> Removed Items
Mind if I shorten this text, it reads like AI autogenerated verbosity.
I.e 10x longer than needed. Release notes especially should be succinct.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC PATCH v2] net/bonding: restrict secondary control operations
2026-08-23 15:43 ` Stephen Hemminger
@ 2026-08-24 2:39 ` Weijun Pan
0 siblings, 0 replies; 9+ messages in thread
From: Weijun Pan @ 2026-08-24 2:39 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Chas Williams, Min Hu (Connor), Anatoly Burakov, dev
Sure, please feel free to shorten it.
Thanks,
Weijun
On Sun, Aug 23, 2026 at 10:43 AM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Sun, 23 Aug 2026 10:16:25 -0500
> Weijun Pan <wpan3636@gmail.com> wrote:
>
> > 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..7ecb05d407 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,19 @@ 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 is owned by the primary process. Secondary
> > +processes may attach to an existing bonding device for query and detach
> > +operations, but must not change bonding configuration or device state.
> > +
> > +In a secondary process, bonding control operations such as configuring,
> > +starting or stopping the device, setting up queues, changing members,
> > +changing the bonding mode, updating RSS, changing MAC addresses, changing
> > +MTU, or configuring ``rte_flow`` rules are not supported.
> > +
> > +Secondary process datapath support is limited and bonding mode specific.
> > +Applications should not rely on secondary processes for bonding datapath
> > +operation unless support for the selected mode is explicitly documented.
> > +
> > 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..8df012c99b 100644
> > --- a/doc/guides/rel_notes/release_26_11.rst
> > +++ b/doc/guides/rel_notes/release_26_11.rst
> > @@ -55,6 +55,12 @@ New Features
> > Also, make sure to start the actual text at the margin.
> > =======================================================
> >
> > +* **Updated bonding PMD secondary process handling.**
> > +
> > + Bonding devices now use a secondary-process device operations table to
> > + keep query and detach paths available while rejecting unsupported control
> > + operations before shared ethdev state can be modified. Bonding-specific
> > + control APIs are also restricted to the primary process.
> >
> > Removed Items
>
> Mind if I shorten this text, it reads like AI autogenerated verbosity.
> I.e 10x longer than needed. Release notes especially should be succinct.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC PATCH v2] net/bonding: restrict secondary control operations
2026-08-23 15:16 ` [RFC PATCH v2] net/bonding: restrict secondary control operations Weijun Pan
2026-08-23 15:43 ` Stephen Hemminger
@ 2026-08-24 16:15 ` Stephen Hemminger
2026-08-26 16:10 ` [RFC PATCH v3] " Weijun Pan
2 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-08-24 16:15 UTC (permalink / raw)
To: Weijun Pan; +Cc: Chas Williams, Min Hu (Connor), Anatoly Burakov, dev
On Sun, 23 Aug 2026 10:16:25 -0500
Weijun Pan <wpan3636@gmail.com> wrote:
> The bonding PMD currently supports secondary attach with safe fallback
> burst functions, but bonding configuration and LACP state are owned by
> the primary process.
>
> Use a secondary-specific dev_ops table so unsupported operations are
> rejected by ethdev before PMD callbacks can mutate shared device state.
> Also reject bonding-specific control APIs from non-primary processes,
> while keeping secondary detach and query paths available.
>
> This keeps secondary process behavior safe while leaving room for future
> limited datapath support.
>
> Bugzilla ID: 1900
>
> Signed-off-by: Weijun Pan <wpan3636@gmail.com>
> ---
More indepth AI review with Fable saw some possible issues.
Review: [RFC PATCH v2] net/bonding: restrict secondary control operations
Applies cleanly to main, builds with -Dwerror=true. The secondary
dev_ops table is a good approach; everything left in it is read-only
against shared memory, and bond_ethdev_close() already returns early
for non-primary so dev_close stays safe.
Warning: prog guide describes a secondary datapath that does not exist
"Secondary process datapath support is limited and bonding mode
specific ... unless support for the selected mode is explicitly
documented."
bond_probe() installs bond_ethdev_rx_secondary() (returns 0) and
bond_ethdev_tx_secondary() (frees and returns nb_pkts) for every
mode. There is no mode with datapath support. Say that plainly:
Rx and Tx are not supported on a bonding device in a secondary
process; receive returns no packets and transmit drops them.
Warning: LACP query paths return zeroed state in a secondary process
bond_mode_8023ad_ports[] is a plain global array, populated only in
the primary. rte_eth_bond_8023ad_member_info(), _ext_collect_get()
and _ext_distrib_get() validate against shared internals (which
pass), then read actor/partner state from the secondary's untouched
copy and return all zeros with rc 0. bond_ethdev_priv_dump(), kept
in secondary_dev_ops, prints the same zeros through dump_lacp().
Since the patch's premise is that query paths are safe in a
secondary, either give these three the same bond_8023ad_check_primary()
guard (and drop eth_dev_priv_dump from secondary_dev_ops or make
dump_lacp() skip in secondary), or document that LACP per-member
state is only visible to the primary.
Warning: rte_eth_bond_api.c: two blank lines after
bond_api_check_primary(). checkpatch will flag it.
Info: three spellings of the same test
rte_eth_bond_pmd.c already open-codes rte_eal_process_type() in
bond_ethdev_mode_set(), bond_ethdev_close(), bond_probe() and
bond_remove(). This patch adds bond_process_is_primary() plus two
near-identical logging wrappers with different return values (-1
and -ENOTSUP). The return values match each file's conventions, so
not wrong, but one helper in eth_bond_private.h taking the error
code would remove the duplication.
Info: link_update writes shared state from the secondary
bond_ethdev_link_update() assigns ethdev->data->dev_link fields
directly. Keeping it in secondary_dev_ops means a secondary calling
rte_eth_link_get() races the primary on that shared struct.
Pre-existing behaviour, but worth a thought given the patch's
"must not change device state" rule.
----------------------------------------------------------------------
Suggested commit message (the Bugzilla entry has the background;
no need to restate it):
net/bonding: restrict secondary control operations
Bonding configuration and LACP state are owned by the primary
process. Install a reduced dev_ops table in secondary processes so
ethdev rejects control operations, and reject the bonding control
API when called from a non-primary process. Query and detach remain
available.
Bugzilla ID: 1900
----------------------------------------------------------------------
Suggested release note:
* **Restricted bonding device control to the primary process.**
Secondary processes can query and detach a bonding device but can no
longer change its configuration.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH v3] net/bonding: restrict secondary control operations
2026-08-23 15:16 ` [RFC PATCH v2] net/bonding: restrict secondary control operations Weijun Pan
2026-08-23 15:43 ` Stephen Hemminger
2026-08-24 16:15 ` Stephen Hemminger
@ 2026-08-26 16:10 ` Weijun Pan
2026-08-26 17:53 ` Stephen Hemminger
2 siblings, 1 reply; 9+ messages in thread
From: Weijun Pan @ 2026-08-26 16:10 UTC (permalink / raw)
To: Chas Williams, Min Hu (Connor), Anatoly Burakov; +Cc: dev, Weijun Pan
Bonding configuration and LACP state are owned by the primary
process. Install a reduced dev_ops table in secondary processes so
ethdev rejects control operations, and reject the bonding control
API when called from a non-primary process. Query and detach remain
available.
Bugzilla ID: 1900
Signed-off-by: Weijun Pan <wpan3636@gmail.com>
---
v3:
- Document that secondary bonding Rx/Tx are unsupported.
- Reject secondary LACP runtime state queries.
- Remove private dump from secondary dev_ops.
- Use a common primary-process helper.
- Shorten release notes.
.../link_bonding_poll_mode_drv_lib.rst | 13 +++++
doc/guides/rel_notes/release_26_11.rst | 4 ++
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 | 11 +++-
6 files changed, 139 insertions(+), 2 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..8e602e51b1 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,19 @@ 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
+supported query and detach operations, but control operations are restricted
+to the primary process.
+
+In a secondary process, bonding control operations such as configuring,
+starting or stopping the device, setting up queues, changing members,
+changing the bonding mode, updating RSS, changing MAC addresses, changing
+MTU, or configuring ``rte_flow`` rules are not supported.
+
+Rx and Tx are not supported on a bonding device in a secondary process;
+receive returns no packets and transmit drops packets.
+
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..1d03e3ee9b 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,10 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Restricted bonding device control to the primary process.**
+
+ Supported query and detach paths remain available to secondary processes,
+ while bonding device configuration changes are rejected.
Removed Items
-------------
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 6a4f997b5a..b250d01445 100644
--- a/drivers/net/bonding/rte_eth_bond_pmd.c
+++ b/drivers/net/bonding/rte_eth_bond_pmd.c
@@ -3644,6 +3644,15 @@ bond_ethdev_priv_dump(struct rte_eth_dev *dev, FILE *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,
+};
+
const struct eth_dev_ops default_dev_ops = {
.dev_start = bond_ethdev_start,
.dev_stop = bond_ethdev_stop,
@@ -3828,7 +3837,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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [RFC PATCH v3] net/bonding: restrict secondary control operations
2026-08-26 16:10 ` [RFC PATCH v3] " Weijun Pan
@ 2026-08-26 17:53 ` Stephen Hemminger
0 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-08-26 17:53 UTC (permalink / raw)
To: Weijun Pan; +Cc: Chas Williams, Min Hu (Connor), Anatoly Burakov, dev
On Wed, 26 Aug 2026 11:10:09 -0500
Weijun Pan <wpan3636@gmail.com> wrote:
> Bonding configuration and LACP state are owned by the primary
> process. Install a reduced dev_ops table in secondary processes so
> ethdev rejects control operations, and reject the bonding control
> API when called from a non-primary process. Query and detach remain
> available.
>
> Bugzilla ID: 1900
>
> Signed-off-by: Weijun Pan <wpan3636@gmail.com>
> ---
I would like to see link update use rte_eth_linkstatus_set() helper.
And do another check that documentation matches reality after this patch.
Overly verbose (sorry) AI review was:
Applies cleanly to main (d55ccd4), diffstat matches, and builds with
-Dwerror=true for net/bonding. checkpatches.sh is clean.
Fixed since v2
--------------
- rte_eth_bond_8023ad_member_info(), _ext_collect_get() and
_ext_distrib_get() are now rejected in a secondary, so they no
longer return all-zero LACP state read out of the process-local
bond_mode_8023ad_ports[].
- eth_dev_priv_dump is no longer in the secondary ops table, so
dump_lacp() cannot walk that array from a secondary either.
- The two near-identical primary-process helpers are collapsed into
bond_check_primary().
- The stray double blank line in rte_eth_bond_member_add() is gone.
- The programmer's guide now states the secondary datapath is a
stub, which matches bond_ethdev_rx_secondary() and
bond_ethdev_tx_secondary().
The set of API gated in this version looks right to me. Every
mutator in rte_eth_bond_api.c and rte_eth_bond_8023ad.c is covered,
rte_eth_bond_free() is deliberately left open for detach, and the
getters that are left ungated (mode_get, primary_get, members_get,
xmit_policy_get, 8023ad_conf_get, 8023ad_agg_selection_get, ...) all
read bond_dev_private, which is in shared memory. Omitting dev_stop
from secondary_dev_ops is also correct: rte_eth_dev_stop() writes
dev->data->dev_started, and rte_eth_dev_close() has its own
secondary path so detach still works.
Warnings
--------
1. link_update is retained in secondary_dev_ops, so dev_link is now
written from two processes without using the ethdev accessors.
bond_ethdev_link_update() assigns ethdev->data->dev_link.* field by
field, and dev_link lives in the shared rte_eth_dev_data. With
.link_update present, any rte_eth_link_get() in a secondary
republishes the primary's link record:
if (dev->data->dev_conf.intr_conf.lsc && dev->data->dev_started)
rte_eth_linkstatus_get(dev, eth_link);
else {
if (dev->dev_ops->link_update == NULL)
return -ENOTSUP;
dev->dev_ops->link_update(dev, 1);
*eth_link = dev->data->dev_link;
}
Readers on the LSC path use rte_eth_linkstatus_get(), a 64-bit
atomic load, so a reader can observe a torn combination of
link_status, link_speed and link_duplex.
The driver should use rte_eth_linkstatus_set() rather than touching
dev->data->dev_link directly. That is one atomic exchange of the
whole word, which closes the window on the primary path as well and
makes the secondary's recomputation harmless -- it derives the same
value from the same shared member state. Build the result locally
and publish once:
struct rte_eth_link link;
rte_eth_linkstatus_get(ethdev, &link);
link.link_speed = RTE_ETH_SPEED_NUM_NONE;
if (ethdev->data->dev_started == 0 ||
bond_ctx->active_member_count == 0) {
link.link_status = RTE_ETH_LINK_DOWN;
goto out;
}
link.link_status = RTE_ETH_LINK_UP;
...
out:
rte_eth_linkstatus_set(ethdev, &link);
return 0;
Seeding from rte_eth_linkstatus_get() keeps link_duplex and
link_autoneg for the modes that never set them; the interior
"return 0" paths become "goto out".
The rest of the driver has the same problem and is worth a
preparatory patch: bond_ethdev_start() and bond_ethdev_stop() set
link_status directly (rte_eth_bond_pmd.c:2068, 2220), as does
bond_ethdev_lsc_event_callback() at 3101, and
bond_ethdev_member_link_status_change() writes link_autoneg and
link_duplex at 1439. Nothing in bonding currently uses the
accessors.
2. The documentation does not match what the code now rejects.
The new prog_guide paragraph lists configure, start/stop, queue
setup, member changes, mode, RSS, MAC, MTU and rte_flow. It omits
everything else the patch turns into an error in a secondary:
- all of rte_eth_bond_8023ad_* except conf_get and
agg_selection_get, including member_info(),
ext_collect_get() and ext_distrib_get(), which are queries
- dedicated queue enable/disable
- xmit policy, link monitoring interval, link up/down
propagation delays, primary member selection
- stats reset, promiscuous and allmulticast, VLAN filter,
private dump
The commit message and the release note both say "query ... remain
available", and the guide says "supported query and detach
operations", so a reader would reasonably conclude
rte_eth_bond_8023ad_member_info() still works in a secondary. It
no longer does. Please say explicitly which queries survive
(device info, stats, link, RETA and RSS hash config, bonding mode,
members, and the LACP configuration) and that LACP runtime state
queries do not.
Worth stating the supported teardown too: rte_eth_dev_stop()
returns -ENOTSUP in a secondary and rte_eth_dev_close() is the
detach call. A secondary that does the usual stop-then-close will
now see an error from the stop.
3. Release note is in the wrong section.
This changes the behaviour of already-exported functions -- calls
that previously returned 0 now return -ENOTSUP or -1 -- so it
belongs under "API Changes", not "New Features". Same comment as
on v2.
4. bond_ethdev_stats_get() ignores the rte_eth_stats_get() return.
Pre-existing, but the patch puts .stats_get in secondary_dev_ops,
which makes the failure case realistic:
struct rte_eth_stats member_stats;
...
for (i = 0; i < internals->member_count; i++) {
rte_eth_stats_get(internals->members[i].port_id,
&member_stats);
stats->ipackets += member_stats.ipackets;
eth_stats_qstats_get() runs RTE_ETH_VALID_PORTID_OR_ERR_RET before
the memset, so on -ENODEV member_stats is left untouched. On the
first iteration that is uninitialized stack; on later iterations it
is the previous member's counters, double-counted. A secondary
that attached to the bonding device but did not probe the member
ports (blocklist, or members added by the primary after the
secondary started) hits exactly this.
ret = rte_eth_stats_get(internals->members[i].port_id,
&member_stats);
if (ret != 0)
continue;
Better as a separate fix ahead of this patch, since it is not
secondary-specific.
Info
----
- Three of the 8023ad functions declare a second int purely for the
new check while an existing one is right there:
struct port *port;
int res;
int ret;
ret = bond_check_primary(__func__, -ENOTSUP);
if (ret != 0)
return ret;
ext_distrib_get() and ext_collect_get() reuse err instead, which
reads better. Reusing res in ext_collect(), ext_distrib() and
ext_slowtx() would drop three declarations and make the series
consistent.
- rte_eth_bond_api.c passes -1 and rte_eth_bond_8023ad.c passes
-ENOTSUP. Each matches its own file's existing convention, so this
is defensible, but a caller cannot distinguish "wrong process type"
from "bad port id" in the api.c cases. -ENOTSUP everywhere would
be clearer if you are willing to change those return values.
- The <stdbool.h> addition to eth_bond_private.h is unrelated to this
change; bool is already used at line 184 and was working by
transitive include. It is a real fix, just not this patch's.
- Dropping eth_dev_priv_dump entirely costs some debuggability. Only
dump_lacp() touches the process-local array; dump_basic() reads
bond_dev_private, which is shared. Keeping .eth_dev_priv_dump in
secondary_dev_ops and skipping the LACP section in a secondary
would let a secondary still dump mode, members and offloads.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-26 17:53 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 17:42 [RFC PATCH] net/bonding: reject control operations in secondary Weijun Pan
2026-07-22 23:10 ` Stephen Hemminger
2026-07-26 17:32 ` Stephen Hemminger
2026-08-23 15:16 ` [RFC PATCH v2] net/bonding: restrict secondary control operations Weijun Pan
2026-08-23 15:43 ` Stephen Hemminger
2026-08-24 2:39 ` Weijun Pan
2026-08-24 16:15 ` Stephen Hemminger
2026-08-26 16:10 ` [RFC PATCH v3] " Weijun Pan
2026-08-26 17:53 ` Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox