|SUCCESS| [v6,10/10] dts: add selective Rx tests
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Wed Jun 3 03:01:13 CEST 2026
Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/164844
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-06-03
I'll review this DPDK patch series focusing on correctness, coding style, API compliance, and guideline adherence.
---
**Review of PATCH v6 series: selective Rx feature for DPDK**
**Commit:** (git sha from series not provided in isolation, assuming v6 iteration)
**Author:** Thomas Monjalon <thomas at monjalon.net>, Gregory Etelson <getelson at nvidia.com>
**Subject:** Multiple patches introducing selective Rx feature
---
### PATCH v6 01/10: app/testpmd: print Rx split capabilities
**Summary:** Adds printing of `rte_eth_rxseg_capa` fields to `show port info`.
> diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
> index c950793aaf..55d1c6d696 100644
> --- 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 match the existing style for boolean capability prints? The existing code might use different patterns. This is acceptable but should be consistent with similar code.
> @@ -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 code handle the case where max_nseg is 0? It looks fine, but the alignment calculation RTE_BIT32 with offset_align_log2 could overflow with bad data. Consider bounds checking offset_align_log2 before the bit shift.
---
### PATCH v6 02/10: ethdev: introduce selective Rx
**Summary:** Introduces selective Rx capability allowing NULL mempool segments.
> +/**
> + * Memory pool to allocate segment from.
> + *
> + * NULL means discarded segment.
> + * Length of discarded segment is not reflected in mbuf packet length
> + * and not accounted in ibytes statistics.
> + * @see rte_eth_rxseg_capa::selective_rx
> + */
> +struct rte_mempool *mp;
Good documentation of NULL semantics.
> @@ -2148,7 +2148,6 @@ 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;
Does this create a potential shift overflow? If offset_align_log2 is 32, RTE_BIT32(32) is undefined behavior. Should validate offset_align_log2 < 32.
> @@ -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 skip validation for NULL segments even when selective_rx is supported? The offset and length checks below are skipped. Could a driver accept invalid offset/length in a NULL segment that causes issues? Might need to at least validate offset + length doesn't exceed max_rx_pktlen.
> @@ -2233,6 +2236,11 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
> if (ret != 0)
> goto out;
> }
> + if (mp_first == NULL) {
> + RTE_ETHDEV_LOG_LINE(ERR, "At least one Rx segment must have a mempool");
> + ret = -EINVAL;
> + goto out;
> + }
Good catch requiring at least one real segment.
---
### PATCH v6 03/10: app/testpmd: support selective Rx
**Summary:** Adds selective Rx support in testpmd using rxoffs and rxpkts.
> +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;
> +}
Good bounds checking.
> @@ -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) {
> + struct rte_eth_dev_info dev_info;
> + uint16_t seg_idx = 0;
> + uint16_t next_offset = 0;
> +
> + ret = rte_eth_dev_info_get(port_id, &dev_info);
> + if (ret != 0)
> + return ret;
Does this handle dev_info uninitialized on error? rte_eth_dev_info_get might not clear dev_info on failure. The immediate return is safe, but later code uses dev_info.
> + /* multi-segment configuration */
> + for (i = 0; i < rx_pkt_nb_segs; i++) {
> + struct rte_eth_rxseg_split *rx_seg;
> + uint16_t seg_offset;
> +
> + if (i < rx_pkt_nb_offs)
> + seg_offset = rx_pkt_seg_offsets[i];
> + else
> + seg_offset = rx_pkt_nb_offs > 0 ? next_offset : 0;
> +
> + /* Insert selective Rx discard segment if there's a gap */
> + if (seg_offset > next_offset) {
Does this handle seg_offset < next_offset (going backwards)? This would indicate a configuration error. Should validate seg_offset >= next_offset.
> + rx_seg = next_rx_seg(rx_useg, &seg_idx);
> + if (rx_seg == NULL)
> + return -EINVAL;
> + rx_seg->offset = next_offset;
> + rx_seg->length = seg_offset - next_offset;
> + rx_seg->mp = NULL;
> + next_offset = seg_offset;
> + }
Does seg_offset - next_offset overflow? If seg_offset wraps, this could be very large. Already checked above, so safe if validation added.
> + /* Add trailing selective Rx discard segment up to max packet length */
> + if (rx_pkt_nb_offs > 0 && next_offset < dev_info.max_rx_pktlen) {
> + struct rte_eth_rxseg_split *rx_seg = next_rx_seg(rx_useg, &seg_idx);
> + if (rx_seg == NULL)
> + return -EINVAL;
> + rx_seg->offset = next_offset;
> + rx_seg->length = dev_info.max_rx_pktlen - next_offset;
> + rx_seg->mp = NULL;
> + }
Does this correctly handle the case where dev_info.max_rx_pktlen is 0? That would be an invalid device, but defensive coding suggests checking.
---
### PATCH v6 04/10: common/mlx5: add null MR functions
**Summary:** Adds functions to allocate/free null Memory Region.
> +RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
> +struct mlx5_pmd_mr *
> +mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
> +{
> + struct ibv_mr *ibv_mr;
> + struct mlx5_pmd_mr *null_mr;
> +
> + null_mr = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*null_mr), 0, dev->numa_node);
> + if (!null_mr)
> + return NULL;
Does mlx5_malloc log errors? Missing error log here might make debugging harder.
> + ibv_mr = mlx5_glue->alloc_null_mr(pd);
> + if (!ibv_mr) {
> + mlx5_free(null_mr);
> + return NULL;
> + }
Same here, no error logging. Should add DRV_LOG(ERR).
---
### PATCH v6 05/10: net/mlx5: fix Rx split segment counter type
**Summary:** Changes rxseg_n from uint32_t to uint16_t.
Looks good. Removes struct padding.
---
### PATCH v6 06/10: net/mlx5: support selective Rx
**Summary:** Implements selective Rx in mlx5_rx_burst().
> +static struct rte_mbuf *
> +mlx5_alloc_null_mbuf(uint32_t data_len)
> +{
> + size_t alloc_size = sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM +
> + rte_align32pow2(data_len);
> + struct rte_mbuf *m;
> +
> + m = mlx5_malloc(MLX5_MEM_ZERO, alloc_size, 0, SOCKET_ID_ANY);
> + if (m == NULL)
> + return NULL;
Does rte_align32pow2(data_len) handle data_len == 0? Might return 1, which is fine.
> + m->buf_addr = RTE_PTR_ADD(m, sizeof(*m));
> + m->buf_len = alloc_size - sizeof(*m);
> + rte_mbuf_iova_set(m, rte_mem_virt2iova(m->buf_addr));
Does rte_mem_virt2iova fail for mlx5_malloc memory? If it returns RTE_BAD_IOVA, the driver might crash. Should validate.
> + m->data_off = RTE_PKTMBUF_HEADROOM;
> + m->refcnt = 1;
> + m->nb_segs = 1;
> + m->port = RTE_MBUF_PORT_INVALID;
> + m->pool = NULL;
> + return m;
> +}
Reasonable fake mbuf construction.
> @@ -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 tail need initialization? It's set before use if pkt exists, but clarity would help.
> @@ -1056,14 +1059,18 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
> struct rte_mbuf *rep = (*rxq->elts)[idx];
> - volatile struct mlx5_mini_cqe8 *mcqe = NULL;
>
> - if (pkt)
> - NEXT(seg) = rep;
> + if (pkt) {
> + if (rep->pool)
> + NEXT(tail) = rep;
> + else
> + --NB_SEGS(pkt);
> + }
Does this correctly handle NB_SEGS underflow? If pkt starts with NB_SEGS == 1 and we decrement for the first discard segment, it becomes 0. Then if we try to decrement again, it wraps to 65535. Should check NB_SEGS(pkt) > 0 before decrementing.
[ ... ]
> @@ -1167,20 +1178,37 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
> /* If there's only one MR, no need to replace LKey in WQE. */
> if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
> wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
> + }
> if (len > DATA_LEN(seg)) {
> + if (seg->pool)
> + data_seg_len += DATA_LEN(seg);
> len -= DATA_LEN(seg);
> + if (pkt)
> ++NB_SEGS(pkt);
> ++rq_ci;
> continue;
> }
> + if (seg->pool) { /* last segment */
> DATA_LEN(seg) = len;
> + data_seg_len += len;
> + }
> + if (unlikely(!pkt)) { /* no real segment found, skip packet */
Does this leak rep if pkt is NULL? The rep was allocated earlier if seg->pool was set. Need to check the control flow more carefully.
Actually, looking
More information about the test-report
mailing list