|SUCCESS| [v2,2/2] ring: introduce peek API for soring
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Fri Apr 17 02:04:26 CEST 2026
Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/163161
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-04-17
# Review of DPDK SORING patch series
commit 1c849ee2a3f4 (based on patch identifiers)
Author: Konstantin Ananyev <konstantin.ananyev at huawei.com>
Subject: [PATCH v2 1/2] ring: make soring to finalize its own stage only
This patch modifies the soring finalize behavior to have each stage's
release() always attempt finalization instead of calling finalize() from
multiple locations.
> diff --git a/lib/ring/soring.c b/lib/ring/soring.c
> index 3b90521bdb..fc7fc55a21 100644
> --- a/lib/ring/soring.c
> +++ b/lib/ring/soring.c
> @@ -50,10 +50,8 @@
[ ... ]
> static inline uint32_t
> soring_dequeue(struct rte_soring *r, void *objs, void *meta,
> uint32_t num, enum rte_ring_queue_behavior behavior,
> @@ -293,22 +291,8 @@ soring_dequeue(struct rte_soring *r, void *objs, void *meta,
> st = r->cons.ht.sync_type;
>
> /* try to grab exactly @num elems first */
> - n = __rte_soring_move_cons_head(r, ns, num, RTE_RING_QUEUE_FIXED, st,
> + n = __rte_soring_move_cons_head(r, ns, num, behavior, st,
> &cons_head, &cons_next, &entries);
> - if (n == 0) {
> - /* try to finalize some elems from previous stage */
> - n = __rte_soring_stage_finalize(&r->stage[ns].sht, ns,
> - r->state, r->mask, 2 * num);
> - entries += n;
> -
> - /* repeat attempt to grab elems */
> - reqn = (behavior == RTE_RING_QUEUE_FIXED) ? num : 0;
> - if (entries >= reqn)
> - n = __rte_soring_move_cons_head(r, ns, num, behavior,
> - st, &cons_head, &cons_next, &entries);
> - else
> - n = 0;
> - }
Does this code change the semantic behavior when RTE_RING_QUEUE_VARIABLE
is passed? Previously, in the first call to __rte_soring_move_cons_head(),
the behavior was always forced to RTE_RING_QUEUE_FIXED. After this change,
if behavior is RTE_RING_QUEUE_VARIABLE, it's passed directly to
__rte_soring_move_cons_head(). Does this alter the dequeue behavior in a
way that could affect existing users?
[ ... ]
> soring_acquire(struct rte_soring *r, void *objs, void *meta,
> uint32_t stage, uint32_t num, enum rte_ring_queue_behavior behavior,
> uint32_t *ftoken, uint32_t *available)
> {
> - uint32_t avail, head, idx, n, next, reqn;
> + uint32_t avail, head, idx, n, next;
> struct soring_stage *pstg;
> struct soring_stage_headtail *cons;
>
> @@ -399,22 +383,7 @@ soring_acquire(struct rte_soring *r, void *objs, void *meta,
>
> /* try to grab exactly @num elems */
> n = __rte_soring_stage_move_head(cons, &pstg->ht, 0, num,
> - RTE_RING_QUEUE_FIXED, &head, &next, &avail);
> - if (n == 0) {
> - /* try to finalize some elems from previous stage */
> - n = __rte_soring_stage_finalize(&pstg->sht, stage - 1,
> - r->state, r->mask, 2 * num);
> - avail += n;
> -
> - /* repeat attempt to grab elems */
> - reqn = (behavior == RTE_RING_QUEUE_FIXED) ? num : 0;
> - if (avail >= reqn)
> - n = __rte_soring_stage_move_head(cons,
> - &pstg->ht, 0, num, behavior, &head,
> - &next, &avail);
> - else
> - n = 0;
> - }
> + behavior, &head, &next, &avail);
Same question here: does changing from forcing RTE_RING_QUEUE_FIXED to
passing the caller's behavior directly alter the semantics in a way that
could break existing code paths?
[ ... ]
> soring_release(struct rte_soring *r, const void *objs,
> const void *meta, uint32_t stage, uint32_t n, uint32_t ftoken)
> {
> - uint32_t idx, pos, tail;
> + uint32_t idx, pos;
> struct soring_stage *stg;
> union soring_state st;
>
> @@ -479,11 +448,8 @@ soring_release(struct rte_soring *r, const void *objs,
> rte_atomic_store_explicit(&r->state[idx].raw, st.raw,
> rte_memory_order_relaxed);
>
> - /* try to do finalize(), if appropriate */
> - tail = rte_atomic_load_explicit(&stg->sht.tail.pos,
> - rte_memory_order_relaxed);
> - if (tail == pos)
> - __rte_soring_stage_finalize(&stg->sht, stage, r->state, r->mask,
> + /* try to do finalize(), if possible */
> + __rte_soring_stage_finalize(&stg->sht, stage, r->state, r->mask,
> r->capacity);
The check for tail == pos has been removed. Does
__rte_soring_stage_finalize() handle the case where tail != pos correctly,
or could this cause unnecessary overhead or incorrect behavior when
attempting finalization?
commit 6a5b8c7d9e12 (based on patch identifiers)
Author: Konstantin Ananyev <konstantin.ananyev at huawei.com>
Subject: [PATCH v2 2/2] ring: introduce peek API for soring
This patch adds peek-style API for soring rings with ST and HTS sync
modes, allowing split enqueue/dequeue operations.
> diff --git a/app/test/test_soring_mt_stress.c b/app/test/test_soring_mt_stress.c
[ ... ]
> +static const struct test_case tests[] = {
> + {
> + .name = "MT_DEQENQ-MT_STG1-PRCS",
> + .func = test_sym_mt1,
> + .wfunc = test_worker_prcs,
> + },
[ ... ]
> diff --git a/app/test/test_soring_peek_stress.c b/app/test/test_soring_peek_stress.c
> new file mode 100644
> index 0000000000..6ac638f8d7
> --- /dev/null
> +++ b/app/test/test_soring_peek_stress.c
> @@ -0,0 +1,75 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2024 Huawei Technologies Co., Ltd
> + */
> +
> +#include "test_soring_stress_impl.h"
> +
> +static inline uint32_t
> +_st_ring_dequeue_burst(struct rte_soring *r, void **obj, uint32_t n,
> + uint32_t *avail)
> +{
> + uint32_t m;
> +
> + m = rte_soring_dequeue_burst_start(r, obj, n, avail);
> + if (m != 0)
> + rte_soring_dequeue_finish(r, m);
> + return m;
> +}
In _st_ring_dequeue_burst(), if rte_soring_dequeue_burst_start() returns
a non-zero value but then rte_soring_dequeue_finish() is not called (say,
due to an early return path), would this leave the ring in an inconsistent
state?
> +
> +static inline uint32_t
> +_st_ring_enqueue_bulk(struct rte_soring *r, void * const *obj, uint32_t n,
> + uint32_t *free)
> +{
> + uint32_t m;
> +
> + m = rte_soring_enqueue_bulk_start(r, n, free);
> + if (m != 0)
> + rte_soring_enqueue_finish(r, obj, m);
> + return m;
> +}
Similarly in _st_ring_enqueue_bulk(), if rte_soring_enqueue_bulk_start()
succeeds but rte_soring_enqueue_finish() is not called, does this leak
the reserved space in the ring?
[ ... ]
> diff --git a/lib/ring/rte_soring.h b/lib/ring/rte_soring.h
> index 95c3cc4080..16f042e612 100644
> --- a/lib/ring/rte_soring.h
> +++ b/lib/ring/rte_soring.h
[ ... ]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Complete to enqueue several objects plus metadata on the soring.
> + * Note that number of objects to enqueue should not exceed previous
> + * enqueue_start return value.
> + *
> + * @param r
> + * A pointer to the soring structure.
> + * @param objs
> + * A pointer to an array of objects to enqueue.
> + * Size of objects to enqueue must be the same value as 'elem_size' parameter
> + * used while creating the soring. Otherwise the results are undefined.
> + * @param meta
> + * A pointer to an array of metadata values for each object to enqueue.
> + * Note that if user not using object metadata values, then this parameter
> + * can be NULL.
> + * Size of elements in this array must be the same value as 'meta_size'
> + * parameter used while creating the soring. If user created the soring with
> + * 'meta_size' value equals zero, then 'meta' parameter should be NULL.
> + * Otherwise the results are undefined.
> + * @param n
> + * The number of objects to add in the soring from the 'objs'.
> + */
> +__rte_experimental
> +void
> +rte_soring_enqueux_finish(struct rte_soring *r, const void *objs,
> + const void *meta, uint32_t n);
The function name rte_soring_enqueux_finish appears to have a typo.
Should this be rte_soring_enqueue_finish or is the 'x' intentional to
differentiate the version that accepts metadata?
[ ... ]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Start to dequeue several objects plus metadata from the soring.
> + * Dequeues exactly requested number of objects or none.
> + * Note that user has to call appropriate dequeue_finish()
> + * to complete given dequeue operation and actually remove objects from
> + * the soring.
> + *
> + * @param r
> + * A pointer to the soring structure.
> + * @param objs
> + * A pointer to an array of objects to dequeue.
> + * Size of objects to enqueue must be the same value as 'elem_size' parameter
> + * used while creating the soring. Otherwise the results are undefined.
In the documentation for rte_soring_dequeux_bulk_start(), the param
description for objs says "Size of objects to enqueue" but should this
say "dequeue" instead?
[ ... ]
> diff --git a/lib/ring/soring.c b/lib/ring/soring.c
> index fc7fc55a21..de5b67071f 100644
> --- a/lib/ring/soring.c
> +++ b/lib/ring/soring.c
> @@ -247,6 +247,28 @@ __rte_soring_stage_move_head(struct soring_stage_headtail *d,
> return n;
> }
>
> +static inline void
> +__enqueue_elems(struct rte_soring *r, const void *objs, const void *meta,
> + uint32_t head, uint32_t n)
> +{
> + __rte_ring_do_enqueue_elems(&r[1], objs, r->size, head & r->mask,
> + r->esize, n);
> + if (meta != NULL)
> + __rte_ring_do_enqueue_elems(r->meta, meta, r->size,
> + head & r->mask, r->msize, n);
> +}
In __enqueue_elems(), could the compiler optimize the check for meta !=
NULL differently based on optimization level? Should this use
rte_likely() or rte_unlikely() to provide a hint?
[ ... ]
> +static inline uint32_t
> +soring_enqueue_start(struct rte_soring *r, uint32_t num,
> + enum rte_ring_queue_behavior behavior, uint32_t *free_space)
> +{
> + enum rte_ring_sync_type st;
> + uint32_t free, head, n, next;
> +
> + RTE_ASSERT(r != NULL && r->nb_stage > 0);
> +
> + st = r->prod.ht.sync_type;
> +
> + switch (st) {
> + case RTE_RING_SYNC_ST:
> + n = __rte_ring_headtail_move_head(&r->prod.ht, &r->cons.ht,
> + r->capacity, RTE_RING_SYNC_ST, num, behavior,
> + &head, &next, &free);
> + break;
> + case RTE_RING_SYNC_MT_HTS:
> + n = __rte_ring_hts_move_head(&r->prod.hts, &r->cons.ht,
> + r->capacity, num, behavior, &head, &free);
> + break;
> + default:
> + /* unsupported mode, shouldn't be here */
> + RTE_ASSERT(0);
> + free = 0;
> + n = 0;
> + }
In soring_enqueue_start() and similar functions, when the sync_type falls
into the default case, does RTE_ASSERT(0) provide adequate error handling
in production builds where assertions may be disabled?
[ ... ]
> +static inline void
> +soring_enqueue_finish(struct rte_soring *r, const void *objs, const void *meta,
> + uint32_t num)
> +{
> + enum rte_ring_sync_type st;
> + uint32_t n
More information about the test-report
mailing list