|SUCCESS| [v5,10/10] dts: add selective Rx tests

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Wed Jun 3 02:59:34 CEST 2026


Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/164834

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-06-03

# Review of Selective Rx patch series

Based on the patch series introducing selective Rx feature to DPDK, here are my
findings:

commit 39606359 (partial)
Author: Thomas Monjalon <thomas at monjalon.net>

    app/testpmd: print Rx split capabilities

This commit introduces new capability printing in testpmd.

> --- a/app/test-pmd/config.c
> +++ b/app/test-pmd/config.c
> @@ -790,6 +790,12 @@ rss_offload_types_display(uint64_t offload_types, uint16_t char_num_per_line)
>  	printf("\n");
>  }
>  
> +static void
> +print_bool_capa(const char *label, int value)
> +{
> +	printf("%s: %s\n", label, value ? "supported" : "not supported");
> +}

Does this function follow the implicit comparison rule? The coding style guide
requires explicit comparison with 0 for integer types. Should this be "value !=
0" instead?

> @@ -911,6 +917,16 @@ port_infos_display(portid_t port_id)
>  		dev_info.max_rx_pktlen);
>  	printf("Maximum configurable size of LRO aggregated packet: %u\n",
>  		dev_info.max_lro_pkt_size);
> +
> +	printf("Rx split:\n");
> +	printf("\tMax segments: %hu\n", dev_info.rx_seg_capa.max_nseg);
> +	if (dev_info.rx_seg_capa.max_nseg > 0) {
> +		print_bool_capa("\tMulti-pool", dev_info.rx_seg_capa.multi_pools);
> +		print_bool_capa("\tBuffer offset", dev_info.rx_seg_capa.offset_allowed);
> +		printf("\tOffset alignment: %u\n",
> +				RTE_BIT32(dev_info.rx_seg_capa.offset_align_log2));
> +	}

Does this alignment check handle the edge case where offset_align_log2 >= 32?
Would RTE_BIT32() overflow in that scenario?

---

commit 39606360 (partial)
Author: Gregory Etelson <getelson at nvidia.com>

    ethdev: introduce selective Rx

This commit adds the core selective Rx capability.

> --- a/lib/ethdev/rte_ethdev.c
> +++ b/lib/ethdev/rte_ethdev.c
> @@ -2129,7 +2129,7 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
>  			const struct rte_eth_dev_info *dev_info)
>  {
>  	const struct rte_eth_rxseg_capa *seg_capa = &dev_info->rx_seg_capa;
> -	struct rte_mempool *mp_first;
> +	struct rte_mempool *mp_first = NULL;

Does this initialization create a scenario where mp_first might remain NULL
through the entire function if all rx_seg[].mp are NULL? The function later
compares against mp_first without NULL checking. Can this cause a
NULL-pointer-based comparison issue?

> @@ -2148,13 +2148,17 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
>  	 * Check the sizes and offsets against buffer sizes
>  	 * for each segment specified in extended configuration.
>  	 */
> -	mp_first = rx_seg[0].mp;
>  	offset_mask = RTE_BIT32(seg_capa->offset_align_log2) - 1;
>  
>  	ptypes = NULL;
> @@ -2160,13 +2159,17 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
>  		uint32_t offset = rx_seg[seg_idx].offset;
>  		uint32_t proto_hdr = rx_seg[seg_idx].proto_hdr;
>  
> -		if (mpl == NULL) {
> -			RTE_ETHDEV_LOG_LINE(ERR, "null mempool pointer");
> -			ret = -EINVAL;
> -			goto out;
> +		if (mpl == NULL) { /* discarded segment */
> +			if (seg_capa->selective_rx == 0) { /* not supported */
> +				RTE_ETHDEV_LOG_LINE(ERR, "null mempool pointer");
> +				ret = -EINVAL;
> +				goto out;
> +			}
> +			continue; /* next checks are not relevant if no mempool */
>  		}

Does this logic protect against an all-NULL configuration slipping through? The
validation happens inside the loop, so if all segments have NULL mempools and
selective_rx is supported, would the function succeed?

> +	if (mp_first == NULL) {
> +		RTE_ETHDEV_LOG_LINE(ERR, "At least one Rx segment must have a mempool");
> +		ret = -EINVAL;
> +		goto out;
> +	}

Good catch - this check addresses the concern above by ensuring at least one
valid mempool exists.

---

commit 39606361 (partial)
Author: Gregory Etelson <getelson at nvidia.com>

    app/testpmd: support selective Rx

This commit adds testpmd support for selective Rx.

> --- a/app/test-pmd/testpmd.c
> +++ b/app/test-pmd/testpmd.c
> @@ -2731,6 +2731,16 @@ port_is_started(portid_t port_id)
>  	return 1;
>  }
>  
> +static struct rte_eth_rxseg_split *
> +next_rx_seg(union rte_eth_rxseg *segs, uint16_t *idx)
> +{
> +	if (*idx >= MAX_SEGS_BUFFER_SPLIT) {
> +		fprintf(stderr, "Too many segments (max %u)\n", MAX_SEGS_BUFFER_SPLIT);
> +		return NULL;
> +	}
> +	return &segs[(*idx)++].split;
> +}

Does this function handle the case where segs is NULL? While the callers
currently pass valid pointers, should there be a defensive check?

> @@ -2744,31 +2754,70 @@ rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
>  	uint32_t prev_hdrs = 0;
>  	int ret;
>  
> -	if ((rx_pkt_nb_segs > 1) &&
> -	    (rx_conf->offloads & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT)) {
> +	if (rx_pkt_nb_segs > 1 || rx_pkt_nb_offs > 0) {

[ ... ]

Does this block correctly initialize new_mpr when all segments have NULL
mempools? The code finds mp_first but also needs last_mp for determining buffer
sizes. Can last_mp remain NULL in some edge cases?

---

commit 39606365 (partial)
Author: Thomas Monjalon <thomas at monjalon.net>

    net/mlx5: support selective Rx

This commit adds selective Rx support to mlx5.

> --- a/drivers/net/mlx5/mlx5_rx.c
> +++ b/drivers/net/mlx5/mlx5_rx.c
> @@ -486,7 +486,7 @@ mlx5_rxq_initialize(struct mlx5_rxq_data *rxq)
>  					rxq->wqes)[i];
>  			addr = rte_pktmbuf_mtod(buf, uintptr_t);
>  			byte_count = DATA_LEN(buf);
> -			lkey = mlx5_rx_mb2mr(rxq, buf);
> +			lkey = buf->pool ? mlx5_rx_mb2mr(rxq, buf) : rxq->sh->null_mr->lkey;

Does this code safely access null_mr->lkey? If null_mr allocation failed during
initialization, does rxq->sh->null_mr potentially contain a NULL pointer that
would cause a crash here?

> @@ -1044,11 +1044,14 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
>  	const unsigned int sges_n = rxq->sges_n;
>  	struct rte_mbuf *pkt = NULL;
>  	struct rte_mbuf *seg = NULL;
> +	struct rte_mbuf *tail = NULL;

Does the addition of the tail pointer correctly handle all edge cases,
particularly when mixing real and discarded segments? The chaining logic seems
complex - can tail ever point to a discarded segment?

> @@ -1088,12 +1095,15 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
>  			rq_ci <<= sges_n;
>  			break;
>  		}
> +		}
>  		if (!pkt) {
> +		if (len == 0) { /* no CQE polled yet */

Does this nested condition structure maintain proper cleanup on all error
paths? When errors occur with both seg->pool checks and CQE polling, are all
allocated resources properly freed?

> +		if (seg->pool) { /* first real segment */
> +			pkt = seg;
> +			tail = seg;

Does this initialization handle the case where the first segment is discarded
but later segments are real? The logic seems to rely on seg->pool checks, but
the flow control with multiple nested conditions makes verification difficult.

---

commit 39606367 (partial)
Author: Thomas Monjalon <thomas at monjalon.net>

    common/mlx5: remove callbacks for MR registration

This refactoring removes function pointers for MR registration.

> --- a/drivers/common/mlx5/mlx5_common_mr.c
> +++ b/drivers/common/mlx5/mlx5_common_mr.c
> @@ -492,12 +492,12 @@ mlx5_mr_lookup_cache(struct mlx5_mr_share_cache *share_cache,
>   *   Pointer to MR to free.
>   */
>  void
> -mlx5_mr_free(struct mlx5_mr *mr, mlx5_dereg_mr_t dereg_mr_cb)
> +mlx5_mr_free(struct mlx5_mr *mr)
>  {
>  	if (mr == NULL)
>  		return;
>  	DRV_LOG(DEBUG, "freeing MR(%p):", (void *)mr);
> -	dereg_mr_cb(&mr->pmd_mr);
> +	mlx5_os_dereg_mr(&mr->pmd_mr);

Does this direct call to mlx5_os_dereg_mr maintain the same error handling
semantics as the callback? Are there any edge cases where the callback allowed
for custom cleanup that the direct call might miss?

---

commit 39606368 (partial)
Author: Thomas Monjalon <thomas at monjalon.net>

    dts: fix topology capability comparison

This fixes comparison operators in TopologyCapability.

> --- a/dts/framework/testbed_model/capability.py
> +++ b/dts/framework/testbed_model/capability.py
> @@ -574,7 +574,9 @@ def __gt__(self, other: Any) -> bool:
>          Returns:
>              :data:`True` if the instance's topology type is more complex than the compared object's.
>          """
> -        return other < self
> +        if not self.is_comparable_with(other):
> +            return False
> +        return self.topology_type > other.topology_type

Good fix. Does is_comparable_with() check for None? The original infinite
recursion bug is fixed, but verifying that is_comparable_with handles all edge
cases would strengthen this.

> @@ -586,7 +588,9 @@ def __le__(self, other: Any) -> bool:
>              :data:`True` if the instance's topology type is less complex or equal than
>              the compared object's.
>          """
> -        return not self > other
> +        if not self.is_comparable_with(other):
> +            return False
> +        return self.topology_type <= other.topology_type

Consistent with __gt__ fix. Does Python's comparison protocol guarantee that
all six comparison operators are now properly consistent?

---

General observations:

1. The series introduces selective Rx as an overlay on buffer split, reusing
   the same configuration structures with NULL mempool as a sentinel value.
   Does this design create any ambiguity in error messages or debugging
   scenarios?

2. The mlx5 implementation only supports selective Rx in the scalar SPRQ path
   mlx5_rx_burst(). Does the documentation clearly warn users that enabling
   selective Rx will force a potentially slower Rx path?

3. The testpmd changes automatically insert discard segments to fill gaps.
   Does this behavior align with user expectations, or should it require
   explicit opt-in?

4. Resource management: Are all null_mbuf allocations properly freed in error
   paths? The code paths in mlx5_rxq.c seem complex with multiple potential
   failure points.

5. The DTS test suite adds good coverage but focuses on positive cases and a
   few error cases. Does it test the interaction between selective Rx and
   other features like VLAN stripping, checksum offload, or RSS?


More information about the test-report mailing list