|WARNING| [RFC, v5, 2/2] net/bonding: restrict secondary control operations

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Sun Aug 30 18:57:06 CEST 2026


Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/168632

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-08-30

# DPDK Patch Review

## Patch 1/2: net/bonding: skip unavailable member stats

### Errors

None.

### Warnings

None.

### Info

None.

---

## Patch 2/2: net/bonding: restrict secondary control operations

### Errors

**1. Resource leak on error path in `bond_ethdev_link_update()`**

The new `bond_link` variable is a stack-allocated structure.
After the loop that updates it, the function has three early-exit paths via `goto out`
before the final `rte_eth_linkstatus_set()` call.
If any of these error paths are taken, the caller receives stale link information
because `ethdev->data->dev_link` was not updated.

While this is not a traditional resource leak (no memory/fd/lock),
it is a correctness bug: the caller expects `rte_eth_link_get[_nowait]()` to fail
or return current link state, but instead gets whatever was in `ethdev->data->dev_link` before.

**Suggested fix**: On each `goto out` error path, call `rte_eth_linkstatus_set(ethdev, &bond_link)`
*before* the goto, or restructure to ensure the caller can distinguish error from success.
The current code unconditionally calls `rte_eth_linkstatus_set()` at the `out:` label,
but that means errors return 0 with potentially stale link state visible to the caller.

Example:

```c
if (ret < 0) {
	bond_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
	bond_link.link_status = RTE_ETH_LINK_DOWN;  /* mark link down on error */
	RTE_BOND_LOG(ERR, "Member (port %u) link get failed: %s",
		bond_ctx->active_members[idx], rte_strerror(-ret));
	rte_eth_linkstatus_set(ethdev, &bond_link);
	return ret;  /* propagate error to caller */
}
```

Or set `bond_link.link_status = RTE_ETH_LINK_DOWN` before each `goto out`
so the caller at least sees link-down state instead of stale data.

---

**2. Statistics accumulation on uninitialized `member_stats` when `rte_eth_stats_get()` fails (from Patch 1)**

Patch 1 adds a `continue` when `rte_eth_stats_get()` returns non-zero,
skipping the accumulation loop body.
However, `struct rte_eth_stats member_stats;` is declared at the top of `bond_ethdev_stats_get()`
and is **not initialized**.
If `rte_eth_stats_get()` fails, `member_stats` contains stack garbage.

The `continue` prevents accumulation of that garbage, so Patch 1 is correct.
However, if `rte_eth_stats_get()` succeeds *partially* (fills some fields but not others)
or if future code accidentally removes the `continue`, uninitialized fields will be accumulated.

**Suggested fix**: Initialize `member_stats` to zero at declaration:

```c
struct rte_eth_stats member_stats = {0};
```

or use `memset(&member_stats, 0, sizeof(member_stats));` before the loop.

This is a **minor correctness issue** (Patch 1 already skips the bad case)
but prevents future bugs if the code is modified.

---

### Warnings

**1. Release notes claim "Added support for querying ... from secondary processes" but documentation says "Rx and Tx are not supported"**

The release note says:

> Added support for querying and detaching bonding devices from secondary processes.

The prog_guide addition says:

> Rx and Tx are not supported on a bonding device in a secondary process;
> receive returns no packets and transmit drops packets.

This implies that the Rx/Tx data-plane burst functions silently fail in secondary processes,
but the patch does not add any code to enforce this or document where this behavior is implemented.

**Suggested fix**: Either:
- Explicitly set `eth_dev->rx_pkt_burst` and `eth_dev->tx_pkt_burst` to stub functions
  that return 0 / drop packets in `bond_probe()` for secondary processes, or
- Document that the existing code already does this (if true), or
- Clarify in the release notes that only control-plane queries are added,
  and data-plane was already unavailable.

---

**2. `bond_ethdev_stop()` not in `secondary_dev_ops` but documentation says "stop ... restricted to primary"**

The `secondary_dev_ops` table includes only:
- `dev_close`
- `dev_infos_get`
- `link_update`
- `stats_get`
- `reta_query`
- `rss_hash_conf_get`
- `eth_dev_priv_dump`

The documentation (prog_guide) says:

> Control operations are restricted to the primary process. This includes ... starting or stopping the device ...

and:

> In a secondary process, `rte_eth_dev_stop()` returns `-ENOTSUP` ...

However, `dev_stop` is **not** in `secondary_dev_ops`, which means `rte_eth_dev_stop()` will return `-ENOTSUP`
because the ethdev layer sees a NULL `dev_ops->dev_stop`.
This is correct behavior (matches the documentation), but it is implicit rather than explicit.

**Suggested clarification**: The code is correct (ethdev returns `-ENOTSUP` when the op is missing),
but it might be clearer to add an explicit stub:

```c
static int
bond_ethdev_stop_secondary(__rte_unused struct rte_eth_dev *dev)
{
	return -ENOTSUP;
}
```

and include `.dev_stop = bond_ethdev_stop_secondary` in `secondary_dev_ops`.
This makes the restriction self-documenting in the code rather than relying on ethdev's implicit NULL check.

Not required, but improves readability.

---

### Info

**1. New `bond_check_primary()` helper is clear and concise**

The `bond_check_primary()` inline helper consistently enforces primary-only restrictions
across all control APIs. This is good defensive programming.

---

**2. Private dump restriction is well-documented**

The code restricts LACP dump to primary processes:

```c
if (internals->mode == BONDING_MODE_8023AD &&
		rte_eal_process_type() == RTE_PROC_PRIMARY)
	dump_lacp(dev->data->port_id, f);
```

and the prog_guide documents:

> Private dump is limited to shared bonding information and skips LACP runtime state in a secondary process.

This is correct and matches LACP state ownership restrictions.

---

## Summary

**Correctness issues requiring fixes:**
1. `bond_ethdev_link_update()` error paths do not properly signal failure to caller
   (early `goto out` paths return 0 but leave stale link state)
2. `member_stats` in `bond_ethdev_stats_get()` should be zero-initialized
   to prevent future bugs (current patch 1 is safe, but fragile)

**Documentation/clarity improvements:**
1. Clarify Rx/Tx behavior in secondary processes (where is it enforced?)
2. Consider explicit `-ENOTSUP` stub for `dev_stop` in `secondary_dev_ops`
   (current implicit NULL handling is correct but less obvious)


More information about the test-report mailing list