* [RFC v2 04/11] net/bonding: use stdatomic
From: Stephen Hemminger @ 2026-05-21 18:04 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Chas Williams, Min Hu (Connor)
In-Reply-To: <20260521180706.678377-1-stephen@networkplumber.org>
The old rte_atomic16 and rte_atomic64 functions are deprecated.
Replace with rte_stdatomic for managing warning and timer flags.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
drivers/net/bonding/eth_bond_8023ad_private.h | 6 ++--
drivers/net/bonding/rte_eth_bond_8023ad.c | 35 ++++++++-----------
2 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/drivers/net/bonding/eth_bond_8023ad_private.h b/drivers/net/bonding/eth_bond_8023ad_private.h
index ab7d15f81a..dd3cf3ed26 100644
--- a/drivers/net/bonding/eth_bond_8023ad_private.h
+++ b/drivers/net/bonding/eth_bond_8023ad_private.h
@@ -9,7 +9,7 @@
#include <rte_ether.h>
#include <rte_byteorder.h>
-#include <rte_atomic.h>
+#include <rte_stdatomic.h>
#include <rte_flow.h>
#include "rte_eth_bond_8023ad.h"
@@ -140,10 +140,10 @@ struct port {
/** Timer which is also used as mutex. If is 0 (not running) RX marker
* packet might be responded. Otherwise shall be dropped. It is zeroed in
* mode 4 callback function after expire. */
- volatile uint64_t rx_marker_timer;
+ RTE_ATOMIC(uint64_t) rx_marker_timer;
uint64_t warning_timer;
- volatile uint16_t warnings_to_show;
+ RTE_ATOMIC(uint16_t) warnings_to_show;
/** Memory pool used to allocate slow queues */
struct rte_mempool *slow_pool;
diff --git a/drivers/net/bonding/rte_eth_bond_8023ad.c b/drivers/net/bonding/rte_eth_bond_8023ad.c
index ba88f6d261..cc7e4af2b9 100644
--- a/drivers/net/bonding/rte_eth_bond_8023ad.c
+++ b/drivers/net/bonding/rte_eth_bond_8023ad.c
@@ -171,27 +171,17 @@ timer_is_running(uint64_t *timer)
static void
set_warning_flags(struct port *port, uint16_t flags)
{
- int retval;
- uint16_t old;
- uint16_t new_flag = 0;
-
- do {
- old = port->warnings_to_show;
- new_flag = old | flags;
- retval = rte_atomic16_cmpset(&port->warnings_to_show, old, new_flag);
- } while (unlikely(retval == 0));
+ rte_atomic_fetch_or_explicit(&port->warnings_to_show, flags, rte_memory_order_relaxed);
}
static void
show_warnings(uint16_t member_id)
{
struct port *port = &bond_mode_8023ad_ports[member_id];
- uint8_t warnings;
-
- do {
- warnings = port->warnings_to_show;
- } while (rte_atomic16_cmpset(&port->warnings_to_show, warnings, 0) == 0);
+ uint16_t warnings;
+ warnings = rte_atomic_exchange_explicit(&port->warnings_to_show, 0,
+ rte_memory_order_relaxed);
if (!warnings)
return;
@@ -1337,7 +1327,6 @@ bond_mode_8023ad_handle_slow_pkt(struct bond_dev_private *internals,
struct port *port = &bond_mode_8023ad_ports[member_id];
struct marker_header *m_hdr;
uint64_t marker_timer, old_marker_timer;
- int retval;
uint8_t wrn, subtype;
/* If packet is a marker, we send response now by reusing given packet
* and update only source MAC, destination MAC is multicast so don't
@@ -1354,17 +1343,19 @@ bond_mode_8023ad_handle_slow_pkt(struct bond_dev_private *internals,
}
/* Setup marker timer. Do it in loop in case concurrent access. */
+ old_marker_timer = rte_atomic_load_explicit(&port->rx_marker_timer,
+ rte_memory_order_relaxed);
do {
- old_marker_timer = port->rx_marker_timer;
if (!timer_is_expired(&old_marker_timer)) {
wrn = WRN_RX_MARKER_TO_FAST;
goto free_out;
}
timer_set(&marker_timer, mode4->rx_marker_timeout);
- retval = rte_atomic64_cmpset(&port->rx_marker_timer,
- old_marker_timer, marker_timer);
- } while (unlikely(retval == 0));
+
+ } while (!rte_atomic_compare_exchange_weak_explicit(&port->rx_marker_timer,
+ &old_marker_timer, marker_timer,
+ rte_memory_order_seq_cst, rte_memory_order_relaxed));
m_hdr->marker.tlv_type_marker = MARKER_TLV_TYPE_RESP;
rte_eth_macaddr_get(member_id, &m_hdr->eth_hdr.src_addr);
@@ -1372,7 +1363,8 @@ bond_mode_8023ad_handle_slow_pkt(struct bond_dev_private *internals,
if (internals->mode4.dedicated_queues.enabled == 0) {
if (rte_ring_enqueue(port->tx_ring, pkt) != 0) {
/* reset timer */
- port->rx_marker_timer = 0;
+ rte_atomic_store_explicit(&port->rx_marker_timer, 0,
+ rte_memory_order_release);
wrn = WRN_TX_QUEUE_FULL;
goto free_out;
}
@@ -1386,7 +1378,8 @@ bond_mode_8023ad_handle_slow_pkt(struct bond_dev_private *internals,
&pkt, tx_count);
if (tx_count != 1) {
/* reset timer */
- port->rx_marker_timer = 0;
+ rte_atomic_store_explicit(&port->rx_marker_timer, 0,
+ rte_memory_order_release);
wrn = WRN_TX_QUEUE_FULL;
goto free_out;
}
--
2.53.0
^ permalink raw reply related
* [RFC v2 03/11] ring: use C11 atomic operations for MP/SP head/tail
From: Stephen Hemminger @ 2026-05-21 18:04 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger, Konstantin Ananyev, Wathsala Vithanage
In-Reply-To: <20260521180706.678377-1-stephen@networkplumber.org>
Last caller of rte_atomic32_cmpset() in lib/, blocking deprecation
of the rte_atomicNN_*() family.
Replace cmpset with rte_atomic_compare_exchange_weak_explicit(),
and convert head/tail loads/stores from implicit seq_cst to explicit
acquire/release. Matches the HTS/RTS pattern.
Acquire-load of d->head orders the subsequent load of s->tail (was
rte_smp_rmb()). Acquire-load of s->tail pairs with the release-store
of the counterpart tail in __rte_ring_update_tail(), which subsumes
the previous wmb/rmb barriers.
Weak CAS avoids arm64's hidden inner retry; the outer do-while already
loops. CAS orderings relaxed: no data published by the reservation.
The now-unused 'enqueue' parameter of __rte_ring_update_tail() is
removed; both call sites updated.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/ring/rte_ring_generic_pvt.h | 65 +++++++++++++++++++++++----------
1 file changed, 45 insertions(+), 20 deletions(-)
diff --git a/lib/ring/rte_ring_generic_pvt.h b/lib/ring/rte_ring_generic_pvt.h
index affd2d5ba7..84570fd5fc 100644
--- a/lib/ring/rte_ring_generic_pvt.h
+++ b/lib/ring/rte_ring_generic_pvt.h
@@ -23,21 +23,24 @@
*/
static __rte_always_inline void
__rte_ring_update_tail(struct rte_ring_headtail *ht, uint32_t old_val,
- uint32_t new_val, uint32_t single, uint32_t enqueue)
+ uint32_t new_val, uint32_t single,
+ uint32_t enqueue __rte_unused)
{
- if (enqueue)
- rte_smp_wmb();
- else
- rte_smp_rmb();
/*
* If there are other enqueues/dequeues in progress that preceded us,
* we need to wait for them to complete
*/
if (!single)
- rte_wait_until_equal_32((volatile uint32_t *)(uintptr_t)&ht->tail, old_val,
- rte_memory_order_relaxed);
-
- ht->tail = new_val;
+ rte_wait_until_equal_32((volatile uint32_t *)(uintptr_t)&ht->tail,
+ old_val, rte_memory_order_relaxed);
+ /*
+ * R0: Release store on the tail. Pairs with the acquire load of the
+ * counterpart's tail at A0 in __rte_ring_headtail_move_head() on the
+ * other side. Ensures slot operations performed by this thread (writes
+ * for enqueue, reads for dequeue) become visible before the new tail
+ * value is observed by the other side.
+ */
+ rte_atomic_store_explicit(&ht->tail, new_val, rte_memory_order_release);
}
/**
@@ -76,25 +79,35 @@ __rte_ring_headtail_move_head(struct rte_ring_headtail *d,
{
unsigned int max = n;
int success;
+ uint32_t tail;
do {
/* Reset n to the initial burst count */
n = max;
- *old_head = d->head;
+ /*
+ * Acquire on d->head and acquire on s->tail below together prevent
+ * the two loads from being reordered (was rte_smp_rmb()) and
+ * re-establish ordering after a failed CAS on retry.
+ */
+ *old_head = rte_atomic_load_explicit(&d->head,
+ rte_memory_order_acquire);
- /* add rmb barrier to avoid load/load reorder in weak
- * memory model. It is noop on x86
+ /*
+ * A0: Acquire load on the counterpart's tail. Pairs with the
+ * release store at R0 in __rte_ring_update_tail(), ensuring slot
+ * operations on the other side are visible before this thread
+ * accesses the reserved slots.
*/
- rte_smp_rmb();
+ tail = rte_atomic_load_explicit(&s->tail, rte_memory_order_acquire);
/*
* The subtraction is done between two unsigned 32bits value
* (the result is always modulo 32 bits even if we have
- * *old_head > s->tail). So 'entries' is always between 0
+ * *old_head > tail). So 'entries' is always between 0
* and capacity (which is < size).
*/
- *entries = (capacity + s->tail - *old_head);
+ *entries = (capacity + tail - *old_head);
/* check that we have enough room in ring */
if (unlikely(n > *entries))
@@ -106,12 +119,24 @@ __rte_ring_headtail_move_head(struct rte_ring_headtail *d,
*new_head = *old_head + n;
if (is_st) {
- d->head = *new_head;
+ rte_atomic_store_explicit(&d->head, *new_head, rte_memory_order_relaxed);
success = 1;
- } else
- success = rte_atomic32_cmpset(
- (uint32_t *)(uintptr_t)&d->head,
- *old_head, *new_head);
+ } else {
+ /*
+ * Weak CAS: the outer do-while handles spurious
+ * failures, so we avoid the strong variant's
+ * internal retry (which on arm64 wraps the LL/SC
+ * pair in a hidden inner loop).
+ *
+ * Relaxed on both success and failure: this CAS
+ * does not publish data. Slot data visibility is
+ * provided by the acquire loads above and the
+ * release store of tail in __rte_ring_update_tail().
+ */
+ success = rte_atomic_compare_exchange_weak_explicit(
+ &d->head, old_head, *new_head,
+ rte_memory_order_relaxed, rte_memory_order_relaxed);
+ }
} while (unlikely(success == 0));
return n;
}
--
2.53.0
^ permalink raw reply related
* [RFC v2 02/11] eal: reimplement rte_smp_*mb with rte_atomic_thread_fence
From: Stephen Hemminger @ 2026-05-21 18:04 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, Wathsala Vithanage, Bibo Mao,
David Christensen, Sun Yuechi, Bruce Richardson,
Konstantin Ananyev
In-Reply-To: <20260521180706.678377-1-stephen@networkplumber.org>
The rte_smp_mb(), rte_smp_wmb() and rte_smp_rmb() functions were
flagged as deprecated by commit 3ec965b6de12 ("doc: update atomic
operation deprecation") in 2021 but nothing came of it.
Reimplement them as inline wrappers over rte_atomic_thread_fence()
and drop the deprecation notice.
The API is preserved; only the implementation changes.
Generated code is unchanged on x86 (seq_cst keeps the lock-addl
trick, release/acquire collapse to a compiler barrier under TSO).
On arm64, release/acquire emit dmb ish instead of dmb ishst/ishld;
the difference is below measurement noise.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
doc/guides/rel_notes/deprecation.rst | 8 --
lib/eal/arm/include/rte_atomic_32.h | 6 --
lib/eal/arm/include/rte_atomic_64.h | 6 --
lib/eal/include/generic/rte_atomic.h | 130 +++++--------------------
lib/eal/loongarch/include/rte_atomic.h | 6 --
lib/eal/ppc/include/rte_atomic.h | 6 --
lib/eal/riscv/include/rte_atomic.h | 6 --
lib/eal/x86/include/rte_atomic.h | 33 +++----
8 files changed, 37 insertions(+), 164 deletions(-)
diff --git a/doc/guides/rel_notes/deprecation.rst b/doc/guides/rel_notes/deprecation.rst
index 35c9b4e06c..2190419f79 100644
--- a/doc/guides/rel_notes/deprecation.rst
+++ b/doc/guides/rel_notes/deprecation.rst
@@ -47,14 +47,6 @@ Deprecation Notices
operations must be used for patches that need to be merged in 20.08 onwards.
This change will not introduce any performance degradation.
-* rte_smp_*mb: These APIs provide full barrier functionality. However, many
- use cases do not require full barriers. To support such use cases, DPDK has
- adopted atomic operations from
- https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html. These
- operations and a new wrapper ``rte_atomic_thread_fence`` instead of
- ``__atomic_thread_fence`` must be used for patches that need to be merged in
- 20.08 onwards. This change will not introduce any performance degradation.
-
* lib: will fix extending some enum/define breaking the ABI. There are multiple
samples in DPDK that enum/define terminated with a ``.*MAX.*`` value which is
used by iterators, and arrays holding these values are sized with this
diff --git a/lib/eal/arm/include/rte_atomic_32.h b/lib/eal/arm/include/rte_atomic_32.h
index 696a539fef..4115271091 100644
--- a/lib/eal/arm/include/rte_atomic_32.h
+++ b/lib/eal/arm/include/rte_atomic_32.h
@@ -17,12 +17,6 @@ extern "C" {
#define rte_rmb() __sync_synchronize()
-#define rte_smp_mb() rte_mb()
-
-#define rte_smp_wmb() rte_wmb()
-
-#define rte_smp_rmb() rte_rmb()
-
#define rte_io_mb() rte_mb()
#define rte_io_wmb() rte_wmb()
diff --git a/lib/eal/arm/include/rte_atomic_64.h b/lib/eal/arm/include/rte_atomic_64.h
index 9f790238df..604e777bcd 100644
--- a/lib/eal/arm/include/rte_atomic_64.h
+++ b/lib/eal/arm/include/rte_atomic_64.h
@@ -20,12 +20,6 @@ extern "C" {
#define rte_rmb() asm volatile("dmb oshld" : : : "memory")
-#define rte_smp_mb() asm volatile("dmb ish" : : : "memory")
-
-#define rte_smp_wmb() asm volatile("dmb ishst" : : : "memory")
-
-#define rte_smp_rmb() asm volatile("dmb ishld" : : : "memory")
-
#define rte_io_mb() rte_mb()
#define rte_io_wmb() rte_wmb()
diff --git a/lib/eal/include/generic/rte_atomic.h b/lib/eal/include/generic/rte_atomic.h
index 292e52fade..1b04b43cbb 100644
--- a/lib/eal/include/generic/rte_atomic.h
+++ b/lib/eal/include/generic/rte_atomic.h
@@ -59,55 +59,25 @@ static inline void rte_rmb(void);
*
* Guarantees that the LOAD and STORE operations that precede the
* rte_smp_mb() call are globally visible across the lcores
- * before the LOAD and STORE operations that follows it.
- *
- * @note
- * This function is deprecated.
- * It provides similar synchronization primitive as atomic fence,
- * but has different syntax and memory ordering semantic. Hence
- * deprecated for the simplicity of memory ordering semantics in use.
- *
- * rte_atomic_thread_fence(rte_memory_order_acq_rel) should be used instead.
+ * before the LOAD and STORE operations that follow it.
*/
static inline void rte_smp_mb(void);
/**
* Write memory barrier between lcores
*
- * Guarantees that the STORE operations that precede the
- * rte_smp_wmb() call are globally visible across the lcores
- * before the STORE operations that follows it.
- *
- * @note
- * This function is deprecated.
- * It provides similar synchronization primitive as atomic fence,
- * but has different syntax and memory ordering semantic. Hence
- * deprecated for the simplicity of memory ordering semantics in use.
- *
- * rte_atomic_thread_fence(rte_memory_order_release) should be used instead.
- * The fence also guarantees LOAD operations that precede the call
- * are globally visible across the lcores before the STORE operations
- * that follows it.
+ * Guarantees that the LOAD and STORE operations that precede the
+ * rte_smp_wmb() call are globally visible across the lcores before
+ * any STORE operations that follow it.
*/
static inline void rte_smp_wmb(void);
/**
* Read memory barrier between lcores
*
- * Guarantees that the LOAD operations that precede the
- * rte_smp_rmb() call are globally visible across the lcores
- * before the LOAD operations that follows it.
- *
- * @note
- * This function is deprecated.
- * It provides similar synchronization primitive as atomic fence,
- * but has different syntax and memory ordering semantic. Hence
- * deprecated for the simplicity of memory ordering semantics in use.
- *
- * rte_atomic_thread_fence(rte_memory_order_acquire) should be used instead.
- * The fence also guarantees LOAD operations that precede the call
- * are globally visible across the lcores before the STORE operations
- * that follows it.
+ * Guarantees that any LOAD operations that precede the rte_smp_rmb()
+ * call complete before LOAD and STORE operations that follow it
+ * become globally visible.
*/
static inline void rte_smp_rmb(void);
///@}
@@ -164,6 +134,24 @@ static inline void rte_io_rmb(void);
*/
static inline void rte_atomic_thread_fence(rte_memory_order memorder);
+static __rte_always_inline void
+rte_smp_mb(void)
+{
+ rte_atomic_thread_fence(rte_memory_order_seq_cst);
+}
+
+static __rte_always_inline void
+rte_smp_wmb(void)
+{
+ rte_atomic_thread_fence(rte_memory_order_release);
+}
+
+static __rte_always_inline void
+rte_smp_rmb(void)
+{
+ rte_atomic_thread_fence(rte_memory_order_acquire);
+}
+
/*------------------------- 16 bit atomic operations -------------------------*/
#ifndef RTE_TOOLCHAIN_MSVC
@@ -184,9 +172,6 @@ static inline void rte_atomic_thread_fence(rte_memory_order memorder);
* @return
* Non-zero on success; 0 on failure.
*/
-static inline int
-rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src);
-
static inline int
rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src)
{
@@ -303,9 +288,6 @@ rte_atomic16_sub(rte_atomic16_t *v, int16_t dec)
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic16_inc(rte_atomic16_t *v);
-
static inline void
rte_atomic16_inc(rte_atomic16_t *v)
{
@@ -318,9 +300,6 @@ rte_atomic16_inc(rte_atomic16_t *v)
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic16_dec(rte_atomic16_t *v);
-
static inline void
rte_atomic16_dec(rte_atomic16_t *v)
{
@@ -379,8 +358,6 @@ rte_atomic16_sub_return(rte_atomic16_t *v, int16_t dec)
* @return
* True if the result after the increment operation is 0; false otherwise.
*/
-static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v);
-
static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v)
{
return rte_atomic_fetch_add_explicit((volatile __rte_atomic int16_t *)&v->cnt, 1,
@@ -398,8 +375,6 @@ static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v)
* @return
* True if the result after the decrement operation is 0; false otherwise.
*/
-static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v);
-
static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v)
{
return rte_atomic_fetch_sub_explicit((volatile __rte_atomic int16_t *)&v->cnt, 1,
@@ -417,8 +392,6 @@ static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v)
* @return
* 0 if failed; else 1, success.
*/
-static inline int rte_atomic16_test_and_set(rte_atomic16_t *v);
-
static inline int rte_atomic16_test_and_set(rte_atomic16_t *v)
{
return rte_atomic16_cmpset((volatile uint16_t *)&v->cnt, 0, 1);
@@ -453,9 +426,6 @@ static inline void rte_atomic16_clear(rte_atomic16_t *v)
* @return
* Non-zero on success; 0 on failure.
*/
-static inline int
-rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src);
-
static inline int
rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
{
@@ -572,9 +542,6 @@ rte_atomic32_sub(rte_atomic32_t *v, int32_t dec)
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic32_inc(rte_atomic32_t *v);
-
static inline void
rte_atomic32_inc(rte_atomic32_t *v)
{
@@ -587,9 +554,6 @@ rte_atomic32_inc(rte_atomic32_t *v)
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic32_dec(rte_atomic32_t *v);
-
static inline void
rte_atomic32_dec(rte_atomic32_t *v)
{
@@ -648,8 +612,6 @@ rte_atomic32_sub_return(rte_atomic32_t *v, int32_t dec)
* @return
* True if the result after the increment operation is 0; false otherwise.
*/
-static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v);
-
static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v)
{
return rte_atomic_fetch_add_explicit((volatile __rte_atomic int32_t *)&v->cnt, 1,
@@ -667,8 +629,6 @@ static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v)
* @return
* True if the result after the decrement operation is 0; false otherwise.
*/
-static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v);
-
static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v)
{
return rte_atomic_fetch_sub_explicit((volatile __rte_atomic int32_t *)&v->cnt, 1,
@@ -686,8 +646,6 @@ static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v)
* @return
* 0 if failed; else 1, success.
*/
-static inline int rte_atomic32_test_and_set(rte_atomic32_t *v);
-
static inline int rte_atomic32_test_and_set(rte_atomic32_t *v)
{
return rte_atomic32_cmpset((volatile uint32_t *)&v->cnt, 0, 1);
@@ -721,9 +679,6 @@ static inline void rte_atomic32_clear(rte_atomic32_t *v)
* @return
* Non-zero on success; 0 on failure.
*/
-static inline int
-rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src);
-
static inline int
rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
{
@@ -770,9 +725,6 @@ typedef struct {
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic64_init(rte_atomic64_t *v);
-
static inline void
rte_atomic64_init(rte_atomic64_t *v)
{
@@ -798,9 +750,6 @@ rte_atomic64_init(rte_atomic64_t *v)
* @return
* The value of the counter.
*/
-static inline int64_t
-rte_atomic64_read(rte_atomic64_t *v);
-
static inline int64_t
rte_atomic64_read(rte_atomic64_t *v)
{
@@ -828,9 +777,6 @@ rte_atomic64_read(rte_atomic64_t *v)
* @param new_value
* The new value of the counter.
*/
-static inline void
-rte_atomic64_set(rte_atomic64_t *v, int64_t new_value);
-
static inline void
rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
{
@@ -856,9 +802,6 @@ rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
* @param inc
* The value to be added to the counter.
*/
-static inline void
-rte_atomic64_add(rte_atomic64_t *v, int64_t inc);
-
static inline void
rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
{
@@ -874,9 +817,6 @@ rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
* @param dec
* The value to be subtracted from the counter.
*/
-static inline void
-rte_atomic64_sub(rte_atomic64_t *v, int64_t dec);
-
static inline void
rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
{
@@ -890,9 +830,6 @@ rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic64_inc(rte_atomic64_t *v);
-
static inline void
rte_atomic64_inc(rte_atomic64_t *v)
{
@@ -905,9 +842,6 @@ rte_atomic64_inc(rte_atomic64_t *v)
* @param v
* A pointer to the atomic counter.
*/
-static inline void
-rte_atomic64_dec(rte_atomic64_t *v);
-
static inline void
rte_atomic64_dec(rte_atomic64_t *v)
{
@@ -927,9 +861,6 @@ rte_atomic64_dec(rte_atomic64_t *v)
* @return
* The value of v after the addition.
*/
-static inline int64_t
-rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc);
-
static inline int64_t
rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
{
@@ -950,9 +881,6 @@ rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
* @return
* The value of v after the subtraction.
*/
-static inline int64_t
-rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec);
-
static inline int64_t
rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
{
@@ -971,8 +899,6 @@ rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
* @return
* True if the result after the addition is 0; false otherwise.
*/
-static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v);
-
static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
{
return rte_atomic64_add_return(v, 1) == 0;
@@ -989,8 +915,6 @@ static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
* @return
* True if the result after subtraction is 0; false otherwise.
*/
-static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v);
-
static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
{
return rte_atomic64_sub_return(v, 1) == 0;
@@ -1007,8 +931,6 @@ static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
* @return
* 0 if failed; else 1, success.
*/
-static inline int rte_atomic64_test_and_set(rte_atomic64_t *v);
-
static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
{
return rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 0, 1);
@@ -1020,8 +942,6 @@ static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
* @param v
* A pointer to the atomic counter.
*/
-static inline void rte_atomic64_clear(rte_atomic64_t *v);
-
static inline void rte_atomic64_clear(rte_atomic64_t *v)
{
rte_atomic64_set(v, 0);
diff --git a/lib/eal/loongarch/include/rte_atomic.h b/lib/eal/loongarch/include/rte_atomic.h
index 785a452c9e..a789e3ab4d 100644
--- a/lib/eal/loongarch/include/rte_atomic.h
+++ b/lib/eal/loongarch/include/rte_atomic.h
@@ -18,12 +18,6 @@ extern "C" {
#define rte_rmb() rte_mb()
-#define rte_smp_mb() rte_mb()
-
-#define rte_smp_wmb() rte_mb()
-
-#define rte_smp_rmb() rte_mb()
-
#define rte_io_mb() rte_mb()
#define rte_io_wmb() rte_mb()
diff --git a/lib/eal/ppc/include/rte_atomic.h b/lib/eal/ppc/include/rte_atomic.h
index 64f4c3d670..0e64db2a35 100644
--- a/lib/eal/ppc/include/rte_atomic.h
+++ b/lib/eal/ppc/include/rte_atomic.h
@@ -24,12 +24,6 @@ extern "C" {
#define rte_rmb() asm volatile("sync" : : : "memory")
-#define rte_smp_mb() rte_mb()
-
-#define rte_smp_wmb() rte_wmb()
-
-#define rte_smp_rmb() rte_rmb()
-
#define rte_io_mb() rte_mb()
#define rte_io_wmb() rte_wmb()
diff --git a/lib/eal/riscv/include/rte_atomic.h b/lib/eal/riscv/include/rte_atomic.h
index 061b175f33..04c40e4e9b 100644
--- a/lib/eal/riscv/include/rte_atomic.h
+++ b/lib/eal/riscv/include/rte_atomic.h
@@ -23,12 +23,6 @@ extern "C" {
#define rte_rmb() asm volatile("fence r, r" : : : "memory")
-#define rte_smp_mb() rte_mb()
-
-#define rte_smp_wmb() rte_wmb()
-
-#define rte_smp_rmb() rte_rmb()
-
#define rte_io_mb() asm volatile("fence iorw, iorw" : : : "memory")
#define rte_io_wmb() asm volatile("fence orw, ow" : : : "memory")
diff --git a/lib/eal/x86/include/rte_atomic.h b/lib/eal/x86/include/rte_atomic.h
index 4f05302c9f..f4d39ce4fe 100644
--- a/lib/eal/x86/include/rte_atomic.h
+++ b/lib/eal/x86/include/rte_atomic.h
@@ -23,10 +23,6 @@
#define rte_rmb() _mm_lfence()
-#define rte_smp_wmb() rte_compiler_barrier()
-
-#define rte_smp_rmb() rte_compiler_barrier()
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -63,20 +59,6 @@ extern "C" {
* So below we use that technique for rte_smp_mb() implementation.
*/
-static __rte_always_inline void
-rte_smp_mb(void)
-{
-#ifdef RTE_TOOLCHAIN_MSVC
- _mm_mfence();
-#else
-#ifdef RTE_ARCH_I686
- asm volatile("lock addl $0, -128(%%esp); " ::: "memory");
-#else
- asm volatile("lock addl $0, -128(%%rsp); " ::: "memory");
-#endif
-#endif
-}
-
#define rte_io_mb() rte_mb()
#define rte_io_wmb() rte_compiler_barrier()
@@ -93,10 +75,19 @@ rte_smp_mb(void)
static __rte_always_inline void
rte_atomic_thread_fence(rte_memory_order memorder)
{
- if (memorder == rte_memory_order_seq_cst)
- rte_smp_mb();
- else
+ if (memorder == rte_memory_order_seq_cst) {
+#ifdef RTE_TOOLCHAIN_MSVC
+ _mm_mfence();
+#else
+#ifdef RTE_ARCH_I686
+ asm volatile("lock addl $0, -128(%%esp); " ::: "memory");
+#else
+ asm volatile("lock addl $0, -128(%%rsp); " ::: "memory");
+#endif
+#endif
+ } else {
__rte_atomic_thread_fence(memorder);
+ }
}
#ifdef __cplusplus
--
2.53.0
^ permalink raw reply related
* [RFC v2 01/11] eal: use intrinsics for rte_atomic on all platforms
From: Stephen Hemminger @ 2026-05-21 18:04 UTC (permalink / raw)
To: dev
Cc: Stephen Hemminger, Wathsala Vithanage, Bibo Mao,
David Christensen, Sun Yuechi, Bruce Richardson,
Konstantin Ananyev
In-Reply-To: <20260521180706.678377-1-stephen@networkplumber.org>
Next step is to deprecate the rte_atomicNN_*() family. Rather than
maintaining both the inline asm and intrinsic fallbacks, drop the
asm paths and use intrinsics everywhere.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
lib/eal/arm/include/rte_atomic_32.h | 4 -
lib/eal/arm/include/rte_atomic_64.h | 4 -
lib/eal/include/generic/rte_atomic.h | 76 +---------
lib/eal/loongarch/include/rte_atomic.h | 4 -
lib/eal/ppc/include/rte_atomic.h | 173 -----------------------
lib/eal/riscv/include/rte_atomic.h | 4 -
lib/eal/x86/include/rte_atomic.h | 172 ----------------------
lib/eal/x86/include/rte_atomic_32.h | 188 -------------------------
lib/eal/x86/include/rte_atomic_64.h | 157 ---------------------
9 files changed, 6 insertions(+), 776 deletions(-)
diff --git a/lib/eal/arm/include/rte_atomic_32.h b/lib/eal/arm/include/rte_atomic_32.h
index 0b9a0dfa30..696a539fef 100644
--- a/lib/eal/arm/include/rte_atomic_32.h
+++ b/lib/eal/arm/include/rte_atomic_32.h
@@ -5,10 +5,6 @@
#ifndef _RTE_ATOMIC_ARM32_H_
#define _RTE_ATOMIC_ARM32_H_
-#ifndef RTE_FORCE_INTRINSICS
-# error Platform must be built with RTE_FORCE_INTRINSICS
-#endif
-
#include "generic/rte_atomic.h"
#ifdef __cplusplus
diff --git a/lib/eal/arm/include/rte_atomic_64.h b/lib/eal/arm/include/rte_atomic_64.h
index 181bb60929..9f790238df 100644
--- a/lib/eal/arm/include/rte_atomic_64.h
+++ b/lib/eal/arm/include/rte_atomic_64.h
@@ -6,10 +6,6 @@
#ifndef _RTE_ATOMIC_ARM64_H_
#define _RTE_ATOMIC_ARM64_H_
-#ifndef RTE_FORCE_INTRINSICS
-# error Platform must be built with RTE_FORCE_INTRINSICS
-#endif
-
#include "generic/rte_atomic.h"
#include <rte_branch_prediction.h>
#include <rte_debug.h>
diff --git a/lib/eal/include/generic/rte_atomic.h b/lib/eal/include/generic/rte_atomic.h
index 0a4f3f8528..292e52fade 100644
--- a/lib/eal/include/generic/rte_atomic.h
+++ b/lib/eal/include/generic/rte_atomic.h
@@ -187,13 +187,11 @@ static inline void rte_atomic_thread_fence(rte_memory_order memorder);
static inline int
rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src);
-#ifdef RTE_FORCE_INTRINSICS
static inline int
rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src)
{
return __sync_bool_compare_and_swap(dst, exp, src);
}
-#endif
/**
* Atomic exchange.
@@ -211,15 +209,11 @@ rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src)
* The original value at that location
*/
static inline uint16_t
-rte_atomic16_exchange(volatile uint16_t *dst, uint16_t val);
-
-#ifdef RTE_FORCE_INTRINSICS
-static inline uint16_t
rte_atomic16_exchange(volatile uint16_t *dst, uint16_t val)
{
- return rte_atomic_exchange_explicit(dst, val, rte_memory_order_seq_cst);
+ return rte_atomic_exchange_explicit((volatile __rte_atomic uint16_t *)dst,
+ val, rte_memory_order_seq_cst);
}
-#endif
/**
* The atomic counter structure.
@@ -312,13 +306,11 @@ rte_atomic16_sub(rte_atomic16_t *v, int16_t dec)
static inline void
rte_atomic16_inc(rte_atomic16_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic16_inc(rte_atomic16_t *v)
{
rte_atomic16_add(v, 1);
}
-#endif
/**
* Atomically decrement a counter by one.
@@ -329,13 +321,11 @@ rte_atomic16_inc(rte_atomic16_t *v)
static inline void
rte_atomic16_dec(rte_atomic16_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic16_dec(rte_atomic16_t *v)
{
rte_atomic16_sub(v, 1);
}
-#endif
/**
* Atomically add a 16-bit value to a counter and return the result.
@@ -391,13 +381,11 @@ rte_atomic16_sub_return(rte_atomic16_t *v, int16_t dec)
*/
static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v)
{
return rte_atomic_fetch_add_explicit((volatile __rte_atomic int16_t *)&v->cnt, 1,
rte_memory_order_seq_cst) + 1 == 0;
}
-#endif
/**
* Atomically decrement a 16-bit counter by one and test.
@@ -412,13 +400,11 @@ static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v)
*/
static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v)
{
return rte_atomic_fetch_sub_explicit((volatile __rte_atomic int16_t *)&v->cnt, 1,
rte_memory_order_seq_cst) - 1 == 0;
}
-#endif
/**
* Atomically test and set a 16-bit atomic counter.
@@ -433,12 +419,10 @@ static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v)
*/
static inline int rte_atomic16_test_and_set(rte_atomic16_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic16_test_and_set(rte_atomic16_t *v)
{
return rte_atomic16_cmpset((volatile uint16_t *)&v->cnt, 0, 1);
}
-#endif
/**
* Atomically set a 16-bit counter to 0.
@@ -472,13 +456,11 @@ static inline void rte_atomic16_clear(rte_atomic16_t *v)
static inline int
rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src);
-#ifdef RTE_FORCE_INTRINSICS
static inline int
rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
{
return __sync_bool_compare_and_swap(dst, exp, src);
}
-#endif
/**
* Atomic exchange.
@@ -496,15 +478,11 @@ rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
* The original value at that location
*/
static inline uint32_t
-rte_atomic32_exchange(volatile uint32_t *dst, uint32_t val);
-
-#ifdef RTE_FORCE_INTRINSICS
-static inline uint32_t
rte_atomic32_exchange(volatile uint32_t *dst, uint32_t val)
{
- return rte_atomic_exchange_explicit(dst, val, rte_memory_order_seq_cst);
+ return rte_atomic_exchange_explicit((volatile __rte_atomic uint32_t *)dst,
+ val, rte_memory_order_seq_cst);
}
-#endif
/**
* The atomic counter structure.
@@ -597,13 +575,11 @@ rte_atomic32_sub(rte_atomic32_t *v, int32_t dec)
static inline void
rte_atomic32_inc(rte_atomic32_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic32_inc(rte_atomic32_t *v)
{
rte_atomic32_add(v, 1);
}
-#endif
/**
* Atomically decrement a counter by one.
@@ -614,13 +590,11 @@ rte_atomic32_inc(rte_atomic32_t *v)
static inline void
rte_atomic32_dec(rte_atomic32_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic32_dec(rte_atomic32_t *v)
{
rte_atomic32_sub(v,1);
}
-#endif
/**
* Atomically add a 32-bit value to a counter and return the result.
@@ -676,13 +650,11 @@ rte_atomic32_sub_return(rte_atomic32_t *v, int32_t dec)
*/
static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v)
{
return rte_atomic_fetch_add_explicit((volatile __rte_atomic int32_t *)&v->cnt, 1,
rte_memory_order_seq_cst) + 1 == 0;
}
-#endif
/**
* Atomically decrement a 32-bit counter by one and test.
@@ -697,13 +669,11 @@ static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v)
*/
static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v)
{
return rte_atomic_fetch_sub_explicit((volatile __rte_atomic int32_t *)&v->cnt, 1,
rte_memory_order_seq_cst) - 1 == 0;
}
-#endif
/**
* Atomically test and set a 32-bit atomic counter.
@@ -718,12 +688,10 @@ static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v)
*/
static inline int rte_atomic32_test_and_set(rte_atomic32_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic32_test_and_set(rte_atomic32_t *v)
{
return rte_atomic32_cmpset((volatile uint32_t *)&v->cnt, 0, 1);
}
-#endif
/**
* Atomically set a 32-bit counter to 0.
@@ -756,13 +724,11 @@ static inline void rte_atomic32_clear(rte_atomic32_t *v)
static inline int
rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src);
-#ifdef RTE_FORCE_INTRINSICS
static inline int
rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
{
return __sync_bool_compare_and_swap(dst, exp, src);
}
-#endif
/**
* Atomic exchange.
@@ -780,15 +746,11 @@ rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
* The original value at that location
*/
static inline uint64_t
-rte_atomic64_exchange(volatile uint64_t *dst, uint64_t val);
-
-#ifdef RTE_FORCE_INTRINSICS
-static inline uint64_t
rte_atomic64_exchange(volatile uint64_t *dst, uint64_t val)
{
- return rte_atomic_exchange_explicit(dst, val, rte_memory_order_seq_cst);
+ return rte_atomic_exchange_explicit((volatile __rte_atomic uint64_t *)dst,
+ val, rte_memory_order_seq_cst);
}
-#endif
/**
* The atomic counter structure.
@@ -811,7 +773,6 @@ typedef struct {
static inline void
rte_atomic64_init(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic64_init(rte_atomic64_t *v)
{
@@ -828,7 +789,6 @@ rte_atomic64_init(rte_atomic64_t *v)
}
#endif
}
-#endif
/**
* Atomically read a 64-bit counter.
@@ -841,7 +801,6 @@ rte_atomic64_init(rte_atomic64_t *v)
static inline int64_t
rte_atomic64_read(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int64_t
rte_atomic64_read(rte_atomic64_t *v)
{
@@ -860,7 +819,6 @@ rte_atomic64_read(rte_atomic64_t *v)
return tmp;
#endif
}
-#endif
/**
* Atomically set a 64-bit counter.
@@ -873,7 +831,6 @@ rte_atomic64_read(rte_atomic64_t *v)
static inline void
rte_atomic64_set(rte_atomic64_t *v, int64_t new_value);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
{
@@ -890,7 +847,6 @@ rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
}
#endif
}
-#endif
/**
* Atomically add a 64-bit value to a counter.
@@ -903,14 +859,12 @@ rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
static inline void
rte_atomic64_add(rte_atomic64_t *v, int64_t inc);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
{
rte_atomic_fetch_add_explicit((volatile __rte_atomic int64_t *)&v->cnt, inc,
rte_memory_order_seq_cst);
}
-#endif
/**
* Atomically subtract a 64-bit value from a counter.
@@ -923,14 +877,12 @@ rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
static inline void
rte_atomic64_sub(rte_atomic64_t *v, int64_t dec);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
{
rte_atomic_fetch_sub_explicit((volatile __rte_atomic int64_t *)&v->cnt, dec,
rte_memory_order_seq_cst);
}
-#endif
/**
* Atomically increment a 64-bit counter by one and test.
@@ -941,13 +893,11 @@ rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
static inline void
rte_atomic64_inc(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic64_inc(rte_atomic64_t *v)
{
rte_atomic64_add(v, 1);
}
-#endif
/**
* Atomically decrement a 64-bit counter by one and test.
@@ -958,13 +908,11 @@ rte_atomic64_inc(rte_atomic64_t *v)
static inline void
rte_atomic64_dec(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void
rte_atomic64_dec(rte_atomic64_t *v)
{
rte_atomic64_sub(v, 1);
}
-#endif
/**
* Add a 64-bit value to an atomic counter and return the result.
@@ -982,14 +930,12 @@ rte_atomic64_dec(rte_atomic64_t *v)
static inline int64_t
rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc);
-#ifdef RTE_FORCE_INTRINSICS
static inline int64_t
rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
{
return rte_atomic_fetch_add_explicit((volatile __rte_atomic int64_t *)&v->cnt, inc,
rte_memory_order_seq_cst) + inc;
}
-#endif
/**
* Subtract a 64-bit value from an atomic counter and return the result.
@@ -1007,14 +953,12 @@ rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
static inline int64_t
rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec);
-#ifdef RTE_FORCE_INTRINSICS
static inline int64_t
rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
{
return rte_atomic_fetch_sub_explicit((volatile __rte_atomic int64_t *)&v->cnt, dec,
rte_memory_order_seq_cst) - dec;
}
-#endif
/**
* Atomically increment a 64-bit counter by one and test.
@@ -1029,12 +973,10 @@ rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
*/
static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
{
return rte_atomic64_add_return(v, 1) == 0;
}
-#endif
/**
* Atomically decrement a 64-bit counter by one and test.
@@ -1049,12 +991,10 @@ static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
*/
static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
{
return rte_atomic64_sub_return(v, 1) == 0;
}
-#endif
/**
* Atomically test and set a 64-bit atomic counter.
@@ -1069,12 +1009,10 @@ static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
*/
static inline int rte_atomic64_test_and_set(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
{
return rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 0, 1);
}
-#endif
/**
* Atomically set a 64-bit counter to 0.
@@ -1084,12 +1022,10 @@ static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
*/
static inline void rte_atomic64_clear(rte_atomic64_t *v);
-#ifdef RTE_FORCE_INTRINSICS
static inline void rte_atomic64_clear(rte_atomic64_t *v)
{
rte_atomic64_set(v, 0);
}
-#endif
#endif
diff --git a/lib/eal/loongarch/include/rte_atomic.h b/lib/eal/loongarch/include/rte_atomic.h
index c8066a4612..785a452c9e 100644
--- a/lib/eal/loongarch/include/rte_atomic.h
+++ b/lib/eal/loongarch/include/rte_atomic.h
@@ -5,10 +5,6 @@
#ifndef RTE_ATOMIC_LOONGARCH_H
#define RTE_ATOMIC_LOONGARCH_H
-#ifndef RTE_FORCE_INTRINSICS
-# error Platform must be built with RTE_FORCE_INTRINSICS
-#endif
-
#include <rte_common.h>
#include "generic/rte_atomic.h"
diff --git a/lib/eal/ppc/include/rte_atomic.h b/lib/eal/ppc/include/rte_atomic.h
index 10acc238f9..64f4c3d670 100644
--- a/lib/eal/ppc/include/rte_atomic.h
+++ b/lib/eal/ppc/include/rte_atomic.h
@@ -43,179 +43,6 @@ rte_atomic_thread_fence(rte_memory_order memorder)
}
/*------------------------- 16 bit atomic operations -------------------------*/
-#ifndef RTE_FORCE_INTRINSICS
-static inline int
-rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src)
-{
- return rte_atomic_compare_exchange_strong_explicit(dst, &exp, src, rte_memory_order_acquire,
- rte_memory_order_acquire) ? 1 : 0;
-}
-
-static inline int rte_atomic16_test_and_set(rte_atomic16_t *v)
-{
- return rte_atomic16_cmpset((volatile uint16_t *)&v->cnt, 0, 1);
-}
-
-static inline void
-rte_atomic16_inc(rte_atomic16_t *v)
-{
- rte_atomic_fetch_add_explicit(&v->cnt, 1, rte_memory_order_acquire);
-}
-
-static inline void
-rte_atomic16_dec(rte_atomic16_t *v)
-{
- rte_atomic_fetch_sub_explicit(&v->cnt, 1, rte_memory_order_acquire);
-}
-
-static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v)
-{
- return rte_atomic_fetch_add_explicit(&v->cnt, 1, rte_memory_order_acquire) + 1 == 0;
-}
-
-static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v)
-{
- return rte_atomic_fetch_sub_explicit(&v->cnt, 1, rte_memory_order_acquire) - 1 == 0;
-}
-
-static inline uint16_t
-rte_atomic16_exchange(volatile uint16_t *dst, uint16_t val)
-{
- return __atomic_exchange_2(dst, val, rte_memory_order_seq_cst);
-}
-
-/*------------------------- 32 bit atomic operations -------------------------*/
-
-static inline int
-rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
-{
- return rte_atomic_compare_exchange_strong_explicit(dst, &exp, src, rte_memory_order_acquire,
- rte_memory_order_acquire) ? 1 : 0;
-}
-
-static inline int rte_atomic32_test_and_set(rte_atomic32_t *v)
-{
- return rte_atomic32_cmpset((volatile uint32_t *)&v->cnt, 0, 1);
-}
-
-static inline void
-rte_atomic32_inc(rte_atomic32_t *v)
-{
- rte_atomic_fetch_add_explicit(&v->cnt, 1, rte_memory_order_acquire);
-}
-
-static inline void
-rte_atomic32_dec(rte_atomic32_t *v)
-{
- rte_atomic_fetch_sub_explicit(&v->cnt, 1, rte_memory_order_acquire);
-}
-
-static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v)
-{
- return rte_atomic_fetch_add_explicit(&v->cnt, 1, rte_memory_order_acquire) + 1 == 0;
-}
-
-static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v)
-{
- return rte_atomic_fetch_sub_explicit(&v->cnt, 1, rte_memory_order_acquire) - 1 == 0;
-}
-
-static inline uint32_t
-rte_atomic32_exchange(volatile uint32_t *dst, uint32_t val)
-{
- return __atomic_exchange_4(dst, val, rte_memory_order_seq_cst);
-}
-
-/*------------------------- 64 bit atomic operations -------------------------*/
-
-static inline int
-rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
-{
- return rte_atomic_compare_exchange_strong_explicit(dst, &exp, src, rte_memory_order_acquire,
- rte_memory_order_acquire) ? 1 : 0;
-}
-
-static inline void
-rte_atomic64_init(rte_atomic64_t *v)
-{
- v->cnt = 0;
-}
-
-static inline int64_t
-rte_atomic64_read(rte_atomic64_t *v)
-{
- return v->cnt;
-}
-
-static inline void
-rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
-{
- v->cnt = new_value;
-}
-
-static inline void
-rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
-{
- rte_atomic_fetch_add_explicit(&v->cnt, inc, rte_memory_order_acquire);
-}
-
-static inline void
-rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
-{
- rte_atomic_fetch_sub_explicit(&v->cnt, dec, rte_memory_order_acquire);
-}
-
-static inline void
-rte_atomic64_inc(rte_atomic64_t *v)
-{
- rte_atomic_fetch_add_explicit(&v->cnt, 1, rte_memory_order_acquire);
-}
-
-static inline void
-rte_atomic64_dec(rte_atomic64_t *v)
-{
- rte_atomic_fetch_sub_explicit(&v->cnt, 1, rte_memory_order_acquire);
-}
-
-static inline int64_t
-rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
-{
- return rte_atomic_fetch_add_explicit(&v->cnt, inc, rte_memory_order_acquire) + inc;
-}
-
-static inline int64_t
-rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
-{
- return rte_atomic_fetch_sub_explicit(&v->cnt, dec, rte_memory_order_acquire) - dec;
-}
-
-static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
-{
- return rte_atomic_fetch_add_explicit(&v->cnt, 1, rte_memory_order_acquire) + 1 == 0;
-}
-
-static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
-{
- return rte_atomic_fetch_sub_explicit(&v->cnt, 1, rte_memory_order_acquire) - 1 == 0;
-}
-
-static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
-{
- return rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 0, 1);
-}
-
-static inline void rte_atomic64_clear(rte_atomic64_t *v)
-{
- v->cnt = 0;
-}
-
-static inline uint64_t
-rte_atomic64_exchange(volatile uint64_t *dst, uint64_t val)
-{
- return __atomic_exchange_8(dst, val, rte_memory_order_seq_cst);
-}
-
-#endif
#ifdef __cplusplus
}
diff --git a/lib/eal/riscv/include/rte_atomic.h b/lib/eal/riscv/include/rte_atomic.h
index 66346ad474..061b175f33 100644
--- a/lib/eal/riscv/include/rte_atomic.h
+++ b/lib/eal/riscv/include/rte_atomic.h
@@ -8,10 +8,6 @@
#ifndef RTE_ATOMIC_RISCV_H
#define RTE_ATOMIC_RISCV_H
-#ifndef RTE_FORCE_INTRINSICS
-# error Platform must be built with RTE_FORCE_INTRINSICS
-#endif
-
#include <stdint.h>
#include <rte_common.h>
#include <rte_config.h>
diff --git a/lib/eal/x86/include/rte_atomic.h b/lib/eal/x86/include/rte_atomic.h
index e071e4234e..4f05302c9f 100644
--- a/lib/eal/x86/include/rte_atomic.h
+++ b/lib/eal/x86/include/rte_atomic.h
@@ -111,178 +111,6 @@ rte_atomic_thread_fence(rte_memory_order memorder)
extern "C" {
#endif
-#ifndef RTE_FORCE_INTRINSICS
-static inline int
-rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src)
-{
- uint8_t res;
-
- asm volatile(
- MPLOCKED
- "cmpxchgw %[src], %[dst];"
- "sete %[res];"
- : [res] "=a" (res), /* output */
- [dst] "=m" (*dst)
- : [src] "r" (src), /* input */
- "a" (exp),
- "m" (*dst)
- : "memory"); /* no-clobber list */
- return res;
-}
-
-static inline uint16_t
-rte_atomic16_exchange(volatile uint16_t *dst, uint16_t val)
-{
- asm volatile(
- MPLOCKED
- "xchgw %0, %1;"
- : "=r" (val), "=m" (*dst)
- : "0" (val), "m" (*dst)
- : "memory"); /* no-clobber list */
- return val;
-}
-
-static inline int rte_atomic16_test_and_set(rte_atomic16_t *v)
-{
- return rte_atomic16_cmpset((volatile uint16_t *)&v->cnt, 0, 1);
-}
-
-static inline void
-rte_atomic16_inc(rte_atomic16_t *v)
-{
- asm volatile(
- MPLOCKED
- "incw %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : "m" (v->cnt) /* input */
- );
-}
-
-static inline void
-rte_atomic16_dec(rte_atomic16_t *v)
-{
- asm volatile(
- MPLOCKED
- "decw %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : "m" (v->cnt) /* input */
- );
-}
-
-static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v)
-{
- uint8_t ret;
-
- asm volatile(
- MPLOCKED
- "incw %[cnt] ; "
- "sete %[ret]"
- : [cnt] "+m" (v->cnt), /* output */
- [ret] "=qm" (ret)
- );
- return ret != 0;
-}
-
-static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v)
-{
- uint8_t ret;
-
- asm volatile(MPLOCKED
- "decw %[cnt] ; "
- "sete %[ret]"
- : [cnt] "+m" (v->cnt), /* output */
- [ret] "=qm" (ret)
- );
- return ret != 0;
-}
-
-/*------------------------- 32 bit atomic operations -------------------------*/
-
-static inline int
-rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
-{
- uint8_t res;
-
- asm volatile(
- MPLOCKED
- "cmpxchgl %[src], %[dst];"
- "sete %[res];"
- : [res] "=a" (res), /* output */
- [dst] "=m" (*dst)
- : [src] "r" (src), /* input */
- "a" (exp),
- "m" (*dst)
- : "memory"); /* no-clobber list */
- return res;
-}
-
-static inline uint32_t
-rte_atomic32_exchange(volatile uint32_t *dst, uint32_t val)
-{
- asm volatile(
- MPLOCKED
- "xchgl %0, %1;"
- : "=r" (val), "=m" (*dst)
- : "0" (val), "m" (*dst)
- : "memory"); /* no-clobber list */
- return val;
-}
-
-static inline int rte_atomic32_test_and_set(rte_atomic32_t *v)
-{
- return rte_atomic32_cmpset((volatile uint32_t *)&v->cnt, 0, 1);
-}
-
-static inline void
-rte_atomic32_inc(rte_atomic32_t *v)
-{
- asm volatile(
- MPLOCKED
- "incl %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : "m" (v->cnt) /* input */
- );
-}
-
-static inline void
-rte_atomic32_dec(rte_atomic32_t *v)
-{
- asm volatile(
- MPLOCKED
- "decl %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : "m" (v->cnt) /* input */
- );
-}
-
-static inline int rte_atomic32_inc_and_test(rte_atomic32_t *v)
-{
- uint8_t ret;
-
- asm volatile(
- MPLOCKED
- "incl %[cnt] ; "
- "sete %[ret]"
- : [cnt] "+m" (v->cnt), /* output */
- [ret] "=qm" (ret)
- );
- return ret != 0;
-}
-
-static inline int rte_atomic32_dec_and_test(rte_atomic32_t *v)
-{
- uint8_t ret;
-
- asm volatile(MPLOCKED
- "decl %[cnt] ; "
- "sete %[ret]"
- : [cnt] "+m" (v->cnt), /* output */
- [ret] "=qm" (ret)
- );
- return ret != 0;
-}
-
-#endif /* !RTE_FORCE_INTRINSICS */
#ifdef __cplusplus
}
diff --git a/lib/eal/x86/include/rte_atomic_32.h b/lib/eal/x86/include/rte_atomic_32.h
index 0f25863aa5..37d139f30d 100644
--- a/lib/eal/x86/include/rte_atomic_32.h
+++ b/lib/eal/x86/include/rte_atomic_32.h
@@ -20,193 +20,5 @@
/*------------------------- 64 bit atomic operations -------------------------*/
-#ifndef RTE_FORCE_INTRINSICS
-static inline int
-rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
-{
- uint8_t res;
- union {
- struct {
- uint32_t l32;
- uint32_t h32;
- };
- uint64_t u64;
- } _exp, _src;
-
- _exp.u64 = exp;
- _src.u64 = src;
-
-#ifndef __PIC__
- asm volatile (
- MPLOCKED
- "cmpxchg8b (%[dst]);"
- "setz %[res];"
- : [res] "=a" (res) /* result in eax */
- : [dst] "S" (dst), /* esi */
- "b" (_src.l32), /* ebx */
- "c" (_src.h32), /* ecx */
- "a" (_exp.l32), /* eax */
- "d" (_exp.h32) /* edx */
- : "memory" ); /* no-clobber list */
-#else
- asm volatile (
- "xchgl %%ebx, %%edi;\n"
- MPLOCKED
- "cmpxchg8b (%[dst]);"
- "setz %[res];"
- "xchgl %%ebx, %%edi;\n"
- : [res] "=a" (res) /* result in eax */
- : [dst] "S" (dst), /* esi */
- "D" (_src.l32), /* ebx */
- "c" (_src.h32), /* ecx */
- "a" (_exp.l32), /* eax */
- "d" (_exp.h32) /* edx */
- : "memory" ); /* no-clobber list */
-#endif
-
- return res;
-}
-
-static inline uint64_t
-rte_atomic64_exchange(volatile uint64_t *dest, uint64_t val)
-{
- uint64_t old;
-
- do {
- old = *dest;
- } while (rte_atomic64_cmpset(dest, old, val) == 0);
-
- return old;
-}
-
-static inline void
-rte_atomic64_init(rte_atomic64_t *v)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, 0);
- }
-}
-
-static inline int64_t
-rte_atomic64_read(rte_atomic64_t *v)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- /* replace the value by itself */
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, tmp);
- }
- return tmp;
-}
-
-static inline void
-rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, new_value);
- }
-}
-
-static inline void
-rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, tmp + inc);
- }
-}
-
-static inline void
-rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, tmp - dec);
- }
-}
-
-static inline void
-rte_atomic64_inc(rte_atomic64_t *v)
-{
- rte_atomic64_add(v, 1);
-}
-
-static inline void
-rte_atomic64_dec(rte_atomic64_t *v)
-{
- rte_atomic64_sub(v, 1);
-}
-
-static inline int64_t
-rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, tmp + inc);
- }
-
- return tmp + inc;
-}
-
-static inline int64_t
-rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
-{
- int success = 0;
- uint64_t tmp;
-
- while (success == 0) {
- tmp = v->cnt;
- success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt,
- tmp, tmp - dec);
- }
-
- return tmp - dec;
-}
-
-static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
-{
- return rte_atomic64_add_return(v, 1) == 0;
-}
-
-static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
-{
- return rte_atomic64_sub_return(v, 1) == 0;
-}
-
-static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
-{
- return rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 0, 1);
-}
-
-static inline void rte_atomic64_clear(rte_atomic64_t *v)
-{
- rte_atomic64_set(v, 0);
-}
-#endif
#endif /* _RTE_ATOMIC_I686_H_ */
diff --git a/lib/eal/x86/include/rte_atomic_64.h b/lib/eal/x86/include/rte_atomic_64.h
index 0a7a2131e0..1cd12695a2 100644
--- a/lib/eal/x86/include/rte_atomic_64.h
+++ b/lib/eal/x86/include/rte_atomic_64.h
@@ -22,163 +22,6 @@
/*------------------------- 64 bit atomic operations -------------------------*/
-#ifndef RTE_FORCE_INTRINSICS
-static inline int
-rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src)
-{
- uint8_t res;
-
-
- asm volatile(
- MPLOCKED
- "cmpxchgq %[src], %[dst];"
- "sete %[res];"
- : [res] "=a" (res), /* output */
- [dst] "=m" (*dst)
- : [src] "r" (src), /* input */
- "a" (exp),
- "m" (*dst)
- : "memory"); /* no-clobber list */
-
- return res;
-}
-
-static inline uint64_t
-rte_atomic64_exchange(volatile uint64_t *dst, uint64_t val)
-{
- asm volatile(
- MPLOCKED
- "xchgq %0, %1;"
- : "=r" (val), "=m" (*dst)
- : "0" (val), "m" (*dst)
- : "memory"); /* no-clobber list */
- return val;
-}
-
-static inline void
-rte_atomic64_init(rte_atomic64_t *v)
-{
- v->cnt = 0;
-}
-
-static inline int64_t
-rte_atomic64_read(rte_atomic64_t *v)
-{
- return v->cnt;
-}
-
-static inline void
-rte_atomic64_set(rte_atomic64_t *v, int64_t new_value)
-{
- v->cnt = new_value;
-}
-
-static inline void
-rte_atomic64_add(rte_atomic64_t *v, int64_t inc)
-{
- asm volatile(
- MPLOCKED
- "addq %[inc], %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : [inc] "ir" (inc), /* input */
- "m" (v->cnt)
- );
-}
-
-static inline void
-rte_atomic64_sub(rte_atomic64_t *v, int64_t dec)
-{
- asm volatile(
- MPLOCKED
- "subq %[dec], %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : [dec] "ir" (dec), /* input */
- "m" (v->cnt)
- );
-}
-
-static inline void
-rte_atomic64_inc(rte_atomic64_t *v)
-{
- asm volatile(
- MPLOCKED
- "incq %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : "m" (v->cnt) /* input */
- );
-}
-
-static inline void
-rte_atomic64_dec(rte_atomic64_t *v)
-{
- asm volatile(
- MPLOCKED
- "decq %[cnt]"
- : [cnt] "=m" (v->cnt) /* output */
- : "m" (v->cnt) /* input */
- );
-}
-
-static inline int64_t
-rte_atomic64_add_return(rte_atomic64_t *v, int64_t inc)
-{
- int64_t prev = inc;
-
- asm volatile(
- MPLOCKED
- "xaddq %[prev], %[cnt]"
- : [prev] "+r" (prev), /* output */
- [cnt] "=m" (v->cnt)
- : "m" (v->cnt) /* input */
- );
- return prev + inc;
-}
-
-static inline int64_t
-rte_atomic64_sub_return(rte_atomic64_t *v, int64_t dec)
-{
- return rte_atomic64_add_return(v, -dec);
-}
-
-static inline int rte_atomic64_inc_and_test(rte_atomic64_t *v)
-{
- uint8_t ret;
-
- asm volatile(
- MPLOCKED
- "incq %[cnt] ; "
- "sete %[ret]"
- : [cnt] "+m" (v->cnt), /* output */
- [ret] "=qm" (ret)
- );
-
- return ret != 0;
-}
-
-static inline int rte_atomic64_dec_and_test(rte_atomic64_t *v)
-{
- uint8_t ret;
-
- asm volatile(
- MPLOCKED
- "decq %[cnt] ; "
- "sete %[ret]"
- : [cnt] "+m" (v->cnt), /* output */
- [ret] "=qm" (ret)
- );
- return ret != 0;
-}
-
-static inline int rte_atomic64_test_and_set(rte_atomic64_t *v)
-{
- return rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 0, 1);
-}
-
-static inline void rte_atomic64_clear(rte_atomic64_t *v)
-{
- v->cnt = 0;
-}
-#endif
/*------------------------ 128 bit atomic operations -------------------------*/
--
2.53.0
^ permalink raw reply related
* [RFC v2 00/11] prepare deprecation of rte_atomicNN_*() family
From: Stephen Hemminger @ 2026-05-21 18:04 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
In-Reply-To: <20260521042043.1590536-1-stephen@networkplumber.org>
The goal is to land every deprecation currently listed in the release
notes by the 26.11 ABI bump. Functions to be removed in 26.11 need
to be marked __rte_deprecated by 26.07, with all in-tree users
converted off them first so CI stays clean.
This is the first step. After this series there are no remaining
in-tree users of rte_atomic64. Expected follow-ups:
- convert remaining rte_atomic32 users (dpaa/fslmc, netvsc, vmbus,
sw_evdev, txgbe, ifc, hinic, bnx2x, vhost)
- convert remaining rte_atomic16 users (dpaa/fslmc, qman)
- mark the rte_atomicNN_*() family __rte_deprecated
- remove the legacy test_atomic.c
- remove the API itself at 26.11
Patch 1 deletes the inline-asm atomic fallbacks across arm, ppc,
loongarch, riscv, and x86 now that RTE_FORCE_INTRINSICS has been
the default everywhere for years. Largest patch by line count and
the one most worth review attention.
Patch 2 retires the rte_smp_*mb deprecation notice (open since 2021)
by reimplementing those APIs as inline wrappers over
rte_atomic_thread_fence; the API is preserved for readability.
Patch 3 is the load-bearing change for lib/: the last caller of
rte_atomic32_cmpset() is converted, with explicit acquire/release
orderings matching the existing HTS/RTS ring pattern.
Driver conversions (patches 4-11) match each rte_atomic64 use to its
best fit rather than blanket seq_cst: software stats become plain
assignment (DPDK convention, torn reads accepted); CAS loops setting
a flag collapse to fetch_or or exchange; open-coded link-status CAS
in net/pfe and net/sfc moves to the existing rte_eth_linkstatus
helpers; genuine synchronization stays atomic with explicit ordering.
v2 - fix clang build
- replace rte_atomic64 in more drivers
- incorporate feedback on rte_smp and ring
- drop zxdh change (only caused by intrinsics in spinlock)
Stephen Hemminger (11):
eal: use intrinsics for rte_atomic on all platforms
eal: reimplement rte_smp_*mb with rte_atomic_thread_fence
ring: use C11 atomic operations for MP/SP head/tail
net/bonding: use stdatomic
net/nbl: remove unused rte_atomic16 field
net/ena: replace use of rte_atomicNN
net/failsafe: convert to stdatomic
net/enic: do not use deprecated rte_atomic64
net/pfe: use ethdev linkstatus helpers
net/sfc: replace rte_atomic with stdatomic
crypto/ccp: replace use of rte_atomic64 with stdatomic
doc/guides/rel_notes/deprecation.rst | 8 -
drivers/crypto/ccp/ccp_crypto.c | 11 +-
drivers/crypto/ccp/ccp_crypto.h | 2 +-
drivers/crypto/ccp/ccp_dev.c | 10 +-
drivers/crypto/ccp/ccp_dev.h | 4 +-
drivers/net/bonding/eth_bond_8023ad_private.h | 6 +-
drivers/net/bonding/rte_eth_bond_8023ad.c | 35 ++-
drivers/net/ena/base/ena_plat_dpdk.h | 14 +-
drivers/net/ena/ena_ethdev.c | 21 +-
drivers/net/ena/ena_ethdev.h | 7 +-
drivers/net/enic/enic.h | 6 +-
drivers/net/enic/enic_compat.h | 1 -
drivers/net/enic/enic_main.c | 17 +-
drivers/net/enic/enic_rxtx.c | 14 +-
drivers/net/enic/enic_rxtx_vec_avx2.c | 4 +-
drivers/net/failsafe/failsafe_ops.c | 12 +-
drivers/net/failsafe/failsafe_private.h | 29 +--
drivers/net/failsafe/failsafe_rxtx.c | 2 +-
drivers/net/nbl/nbl_hw/nbl_resource.h | 1 -
drivers/net/pfe/pfe_ethdev.c | 32 +--
drivers/net/sfc/sfc.c | 9 +-
drivers/net/sfc/sfc.h | 4 +-
drivers/net/sfc/sfc_port.c | 7 +-
drivers/net/sfc/sfc_stats.h | 2 +-
lib/eal/arm/include/rte_atomic_32.h | 10 -
lib/eal/arm/include/rte_atomic_64.h | 10 -
lib/eal/include/generic/rte_atomic.h | 206 +++---------------
lib/eal/loongarch/include/rte_atomic.h | 10 -
lib/eal/ppc/include/rte_atomic.h | 179 ---------------
lib/eal/riscv/include/rte_atomic.h | 10 -
lib/eal/x86/include/rte_atomic.h | 205 +----------------
lib/eal/x86/include/rte_atomic_32.h | 188 ----------------
lib/eal/x86/include/rte_atomic_64.h | 157 -------------
lib/ring/rte_ring_generic_pvt.h | 65 ++++--
34 files changed, 190 insertions(+), 1108 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH 00/10] e1000 base code update
From: Bruce Richardson @ 2026-05-21 16:47 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev
In-Reply-To: <20260520125256.354336-1-ciara.loftus@intel.com>
On Wed, May 20, 2026 at 12:52:37PM +0000, Ciara Loftus wrote:
> Update the e1000 base code. The changes are mostly fixes, refactoring,
> and code cleanup.
>
> Ciara Loftus (1):
> net/e1000/base: update version info
>
> Dima Ruinskiy (2):
> net/e1000/base: fix coding style issues
> net/e1000/base: propagate PHY control register write error
>
> Lukasz Czapnik (1):
> net/e1000/base: fix possible variable overflow
>
> Mateusz Fryze (1):
> net/e1000/base: auto-negotiation status for 1000BASE-T
>
> Menachem Fogel (1):
> net/e1000/base: fix NVM loop bounds and pointer access
>
> Vitaly Lifshits (4):
> net/e1000/base: refactor K1 exit timeout configuration
> net/e1000/base: fix typo in e1000 base code
> net/e1000/base: reclassify MAC type
> net/e1000/base: clear DPG enable bit post MAC reset
>
Series-acked-by: Bruce Richardson <bruce.richardson@intel.com>
Applied to next-net-intel.
Thanks,
/Bruce
^ permalink raw reply
* Re: [PATCH 05/10] net/e1000/base: clear DPG enable bit post MAC reset
From: Bruce Richardson @ 2026-05-21 16:42 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, Vitaly Lifshits
In-Reply-To: <20260520125256.354336-6-ciara.loftus@intel.com>
On Wed, May 20, 2026 at 12:52:42PM +0000, Ciara Loftus wrote:
> From: Vitaly Lifshits <vitaly.lifshits@intel.com>
>
> The GbE autonomous power gating feature was added to support G3 to S5
> flow. However, this changed the reset value of DPG_EN bit to 1, causing
> a possible autonomous transition to power gating state during D0. This
> might result in undefined errors such as: packet loss, packet corruption
> and Tx/Rx hangs. Therefore, clear DPG_EN bit after hardware reset flow.
>
> Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
> Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> ---
Seems like a bug fix needing backport. Will add fixes tag and CC stable on
apply.
Fixes: 5a32a257f957 ("e1000: more NICs in base driver")
> drivers/net/intel/e1000/base/e1000_defines.h | 1 +
> drivers/net/intel/e1000/base/e1000_ich8lan.c | 7 +++++++
> 2 files changed, 8 insertions(+)
>
> diff --git a/drivers/net/intel/e1000/base/e1000_defines.h b/drivers/net/intel/e1000/base/e1000_defines.h
> index eb93675823..6c710300a6 100644
> --- a/drivers/net/intel/e1000/base/e1000_defines.h
> +++ b/drivers/net/intel/e1000/base/e1000_defines.h
> @@ -36,6 +36,7 @@
>
> /* Extended Device Control */
> #define E1000_CTRL_EXT_LPCD 0x00000004 /* LCD Power Cycle Done */
> +#define E1000_CTRL_EXT_DPG_EN 0x00000008 /* Dynamic Power Gating Enable */
> #define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* SW Definable Pin 4 data */
> #define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* SW Definable Pin 6 data */
> #define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* SW Definable Pin 3 data */
> diff --git a/drivers/net/intel/e1000/base/e1000_ich8lan.c b/drivers/net/intel/e1000/base/e1000_ich8lan.c
> index 6190052368..96b9ad6a70 100644
> --- a/drivers/net/intel/e1000/base/e1000_ich8lan.c
> +++ b/drivers/net/intel/e1000/base/e1000_ich8lan.c
> @@ -5107,6 +5107,13 @@ STATIC s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
> reg |= E1000_KABGTXD_BGSQLBIAS;
> E1000_WRITE_REG(hw, E1000_KABGTXD, reg);
>
> + if (hw->mac.type >= e1000_pch_ptp) {
> + DEBUGOUT("Clearing DPG EN bit post reset\n");
> + reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
> + reg &= ~E1000_CTRL_EXT_DPG_EN;
> + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
> + }
> +
> return E1000_SUCCESS;
> }
>
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH] net/mlx5: remove nonsensical flow action class_id checks
From: Dariusz Sosnowski @ 2026-05-21 16:23 UTC (permalink / raw)
To: Adrian Schollmeyer
Cc: Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
Matan Azrad, Michael Baum, dev, Michael Pfeiffer, stable
In-Reply-To: <20260520132533.159996-1-a.schollmeyer@syseleven.de>
On Wed, May 20, 2026 at 03:25:32PM +0200, Adrian Schollmeyer wrote:
> From: Michael Pfeiffer <m.pfeiffer@syseleven.de>
>
> For a MODIFY_FIELD action, flow_hw_validate_action_modify_field() is
> invoked and enforces class_id == 0 in the action's source and
> destination, if the modified field is none of
> RTE_FLOW_FIELD_GENEVE_OPT_*, as the value is used solely for GENEVE
> fields.
>
> However, this check is flawed due to the way rte_flow_field_data is
> initialized. As it consists of unions and anonymous structs as members,
> empty initialization of this struct or initializing just the tag_index
> only guarantees initialization of the first union member, while the
> remaining member's default initialization behavior is unspecified.
> Therefore, depending on the compiler type, version and configuration,
> the remaining members may either be default-initialized as well or
> contain bytes from uninitialized memory. This causes the check to fail
> depending on how the struct is initialized wherever it is used.
>
> For example, rte_flow_configure() sometimes fails on mlx5 under these
> circumstances with an error "destination class id is not supported"
> during creation of representor tagging rules, as these internally use
> MODIFY_FIELD actions in the following call stack:
>
> 1. rte_flow_configure
> 2. mlx5_flow_port_configure
> 3. flow_hw_configure
> 4. __flow_hw_configure
> 5. flow_hw_setup_tx_repr_tagging
> 6. flow_hw_create_tx_repr_tag_jump_acts_tmpl
> --> various rte_flow_action_modify_field are initialized here, but
> class_id remains uninitialized
> 7. __flow_hw_actions_template_create
> 8. mlx5_flow_hw_actions_validate
> 9. flow_hw_validate_action_modify_field
> --> invoked with class_id containing uninitialized bytes and
> non-GENEVE field type
>
> Remove the two checks for class_id in the non-GENEVE case, as this field
> is unused for these actions and avoids additional implicit dependencies
> on the correct ordering of union members.
>
> Fixes: 1caa89ec1891 ("net/mlx5: support GENEVE options modification")
> Cc: stable@dpdk.org
>
> Signed-off-by: Michael Pfeiffer <m.pfeiffer@syseleven.de>
> Signed-off-by: Adrian Schollmeyer <a.schollmeyer@syseleven.de>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
Thank you for the contribution.
Best regards,
Dariusz Sosnowski
^ permalink raw reply
* Re: [PATCH v2 0/6] net/gve: add hardware timestamping support
From: Joshua Washington @ 2026-05-21 16:10 UTC (permalink / raw)
To: Mark Blasko; +Cc: Stephen Hemminger, dev
In-Reply-To: <CANULgnJv8LyoLZhGfn7gPxo-=2Z3ZSD8jCv9jAf9bCu4RZCaiQ@mail.gmail.com>
On Mon, May 18, 2026 at 11:44 AM Mark Blasko <blasko@google.com> wrote:
>
> On Sun, May 17, 2026 at 4:15 PM Stephen Hemminger
> <stephen@networkplumber.org> wrote:
> >
> > Warning: (unchanged from v1) gve_read_clock and the periodic
> > gve_read_nic_clock alarm callback both issue
> > GVE_ADMINQ_REPORT_NIC_TIMESTAMP into the single shared DMA buffer
> > priv->nic_ts_report, then read it after gve_adminq_execute_cmd
> > has released adminq_lock. If gve_read_clock is preempted between
> > gve_adminq_report_nic_timestamp returning and the be64_to_cpu
> > read, the alarm callback can memset() and reissue its own
> > command, so the user thread will read either zero or another
> > command's response. The simplest fix is for gve_read_clock to
> > return the cached priv->last_read_nic_timestamp instead of
> > issuing a fresh adminq command - the 250ms periodic sync keeps
> > it fresh enough for .read_clock semantics. Once the dev_op
> > registration is restored this race becomes reachable.
>
> I want to make sure I fully understand the API contract here. Is the
> fact that .read_clock does not require a fresh device read documented
> in the DPDK specification, or is this based on typical application
> use cases? If the latter, what are those typical use cases?
Looking at the documentation for the user-exposed rte_eth_read_clock()
API, it seems that this API is intended to be quite accurate on a
read, or at least more accurate than 250ms of granularity would allow.
This is the example given in the documentation:
> * E.g, a simple heuristic to derivate the frequency would be:
> * uint64_t start, end;
> * rte_eth_read_clock(port, start);
> * rte_delay_ms(100);
> * rte_eth_read_clock(port, end);
> * double freq = (end - start) * 10;
> *
> * Compute a common reference with:
> * uint64_t base_time_sec = current_time();
> * uint64_t base_clock;
> * rte_eth_read_clock(port, base_clock);
> *
> * Then, convert the raw mbuf timestamp with:
> * base_time_sec + (double)(*timestamp_dynfield(mbuf) - base_clock) / freq;
read_clock() having 250ms granularity would completely nullify this
use case, as it is entirely possible for both clock reads to hold the
same value. And even if they _didn't_ hold the same value, they would
end up being 250ms-worth of NIC cycles apart, instead of a more
accurate representation of the delta over that period of time -- not
very useful.
--
Joshua Washington
^ permalink raw reply
* Re: [RFC 3/7] ring: use C11 atomic operations for MP/SP head/tail
From: Wathsala Vithanage @ 2026-05-21 15:57 UTC (permalink / raw)
To: Stephen Hemminger, dev; +Cc: Konstantin Ananyev
In-Reply-To: <20260521042043.1590536-4-stephen@networkplumber.org>
[-- Attachment #1: Type: text/plain, Size: 5398 bytes --]
Already looks good. I have one minor suggestion.
In |rte_ring_c11_pvt.h| (and in the MCS lock code as well), we introduced
a comment style that annotates load-acquire and store-release
operations as |An| and |Rm|, respectively. Each |An| comment refers to the
corresponding |Rm| it synchronizes with, and vice versa, while also
describing
the intent of the pairing.
--wathsala
On 5/20/26 23:17, Stephen Hemminger wrote:
> Last caller of rte_atomic32_cmpset() in lib/, blocking deprecation
> of the rte_atomicNN_*() family.
>
> Replace cmpset with rte_atomic_compare_exchange_weak_explicit(),
> and convert head/tail loads/stores from implicit seq_cst to explicit
> acquire/release. Matches the HTS/RTS pattern.
>
> Acquire-load of d->head orders the subsequent load of s->tail (was
> rte_smp_rmb()). Acquire-load of s->tail pairs with the release-store
> of the counterpart tail in __rte_ring_update_tail(), which subsumes
> the previous wmb/rmb barriers.
>
> Weak CAS avoids arm64's hidden inner retry; the outer do-while already
> loops. CAS orderings relaxed: no data published by the reservation.
>
> The now-unused 'enqueue' parameter of __rte_ring_update_tail() is
> removed; both call sites updated.
>
> Signed-off-by: Stephen Hemminger<stephen@networkplumber.org>
> ---
> lib/ring/rte_ring_generic_pvt.h | 64 +++++++++++++++++++++++----------
> 1 file changed, 45 insertions(+), 19 deletions(-)
>
> diff --git a/lib/ring/rte_ring_generic_pvt.h b/lib/ring/rte_ring_generic_pvt.h
> index affd2d5ba7..9497f6737b 100644
> --- a/lib/ring/rte_ring_generic_pvt.h
> +++ b/lib/ring/rte_ring_generic_pvt.h
> @@ -23,21 +23,25 @@
> */
> static __rte_always_inline void
> __rte_ring_update_tail(struct rte_ring_headtail *ht, uint32_t old_val,
> - uint32_t new_val, uint32_t single, uint32_t enqueue)
> + uint32_t new_val, uint32_t single,
> + uint32_t enqueue __rte_unused)
> {
> - if (enqueue)
> - rte_smp_wmb();
> - else
> - rte_smp_rmb();
> /*
> * If there are other enqueues/dequeues in progress that preceded us,
> * we need to wait for them to complete
> */
> if (!single)
> - rte_wait_until_equal_32((volatile uint32_t *)(uintptr_t)&ht->tail, old_val,
> - rte_memory_order_relaxed);
> + rte_wait_until_equal_32((volatile uint32_t *)(uintptr_t)&ht->tail,
> + old_val, rte_memory_order_relaxed);
>
> - ht->tail = new_val;
> + /*
> + * Release ordering on the tail store ensures that the slot reads
> + * (dequeue) or writes (enqueue) performed by this thread are visible
> + * to the other side before the new tail value is observed.
> + * Pairs with the acquire load of the counterpart's tail in
> + * __rte_ring_headtail_move_head().
> + */
> + rte_atomic_store_explicit(&ht->tail, new_val, rte_memory_order_release);
> }
>
> /**
> @@ -76,25 +80,35 @@ __rte_ring_headtail_move_head(struct rte_ring_headtail *d,
> {
> unsigned int max = n;
> int success;
> + uint32_t tail;
>
> do {
> /* Reset n to the initial burst count */
> n = max;
>
> - *old_head = d->head;
> + /*
> + * Acquire load: orders this load before the load of s->tail
> + * below (replaces rte_smp_rmb() in the previous version) and
> + * re-establishes ordering after a failed CAS on retry.
> + */
> + *old_head = rte_atomic_load_explicit(&d->head,
> + rte_memory_order_acquire);
>
> - /* add rmb barrier to avoid load/load reorder in weak
> - * memory model. It is noop on x86
> + /*
> + * Acquire load on the counterpart's tail pairs with the
> + * release store in __rte_ring_update_tail() on the other
> + * side, ensuring slot operations performed there are visible
> + * before the caller accesses the reserved slots.
> */
> - rte_smp_rmb();
> + tail = rte_atomic_load_explicit(&s->tail, rte_memory_order_acquire);
>
> /*
> * The subtraction is done between two unsigned 32bits value
> * (the result is always modulo 32 bits even if we have
> - * *old_head > s->tail). So 'entries' is always between 0
> + * *old_head > tail). So 'entries' is always between 0
> * and capacity (which is < size).
> */
> - *entries = (capacity + s->tail - *old_head);
> + *entries = (capacity + tail - *old_head);
>
> /* check that we have enough room in ring */
> if (unlikely(n > *entries))
> @@ -106,12 +120,24 @@ __rte_ring_headtail_move_head(struct rte_ring_headtail *d,
>
> *new_head = *old_head + n;
> if (is_st) {
> - d->head = *new_head;
> + rte_atomic_store_explicit(&d->head, *new_head, rte_memory_order_relaxed);
> success = 1;
> - } else
> - success = rte_atomic32_cmpset(
> - (uint32_t *)(uintptr_t)&d->head,
> - *old_head, *new_head);
> + } else {
> + /*
> + * Weak CAS: the outer do-while handles spurious
> + * failures, so we avoid the strong variant's
> + * internal retry (which on arm64 wraps the LL/SC
> + * pair in a hidden inner loop).
> + *
> + * Relaxed on both success and failure: this CAS
> + * does not publish data. Slot data visibility is
> + * provided by the acquire loads above and the
> + * release store of tail in __rte_ring_update_tail().
> + */
> + success = rte_atomic_compare_exchange_weak_explicit(
> + &d->head, old_head, *new_head,
> + rte_memory_order_relaxed, rte_memory_order_relaxed);
> + }
> } while (unlikely(success == 0));
> return n;
> }
[-- Attachment #2: Type: text/html, Size: 6093 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/2] ethdev: add telemetry endpoint for list names
From: Bruce Richardson @ 2026-05-21 15:44 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Morten Brørup, fengchengwen, thomas, dev, andrew.rybchenko
In-Reply-To: <ag8jF4E2RnQcFrYu@bricha3-mobl1.ger.corp.intel.com>
On Thu, May 21, 2026 at 04:21:59PM +0100, Bruce Richardson wrote:
> On Thu, May 21, 2026 at 07:37:16AM -0700, Stephen Hemminger wrote:
> > On Thu, 21 May 2026 14:40:47 +0200 Morten Brørup
> > <mb@smartsharesystems.com> wrote:
> >
> > > > From: fengchengwen [mailto:fengchengwen@huawei.com] Sent: Thursday,
> > > > 21 May 2026 14.25
> > > >
> > > > Thanks for the feedback.
> > > >
> > > > I intend to keep the current dict format. This concise ID-name
> > > > mapping is quite helpful and easy to read especially when there are
> > > > massive ports, which is exactly the main purpose why I submitted
> > > > this patch.
> > > >
> > > > In my opinion, adopting OData-style query would require
> > > > architecture- level refactoring of telemetry framework, which is
> > > > way too heavy for this simple requirement.
> > >
> > > Agree. Refactoring the telemetry framework is different task, not
> > > related to this patch.
> > >
> > > > For complex query demands, we can implement them by extending the
> > > > upper-layer Python telemetry script instead.
> > > >
> > > > So I suggest we keep this simple form here.
> > >
> > > If it is generally acceptable for DPDK telemetry that a request for a
> > > list does not return a list type, but returns an object type with
> > > "index": "value" fields instead, then Series-acked-by: Morten Brørup
> > > <mb@smartsharesystems.com>
> >
> > It is necessary since port list may have holes due to hotplug or the
> > ownership API. It would be good to have a more complete query function
> > that returns more about each ethdev. I wouldn't worry about the size
> > of the response. This is JSON and it is meant to be read by scripts not
> > directly by humans.
>
> That's where I think we have a difference - if the output is meant to be
> read by scripts, we should not need extra commands like this, since the
> information is already available via existing commands. The only
> compelling reason that I can see for adding a new command to return a set
> of ethdev names is for human interactive use.
>
> Personally, I think the output should be just used by other scripts, but
> it does appear that this quick-and-dirty telemetry script is being used
> extensively for human interactive querying. Therefore, I'm working on
> extending the script to make it more useful for such cases. I'd prefer to
> add the extra smarts into the script rather than seeing a proliferation
> of endpoints providing the same data in different formats.
>
Here [1] is my proposed generalised solution, quickly prototyped with copilot,
composed of two parts: firstly, support for querying values across a range
of ports using a foreach command, and then secondly, support for aliases to
make the use of those foreach commands easier on the user. It's not
intended as a mergable set of patches as-is, but rather to demonstrate how
we might be able to come up with a more general solution (that keeps to the
DRY principle) entirely within the python script rather than extending the
C code.
/Bruce
[1] https://patches.dpdk.org/project/dpdk/list/?series=38197
^ permalink raw reply
* Re: [RFC 2/7] eal: reimplement rte_smp_*mb with rte_atomic_thread_fence
From: Wathsala Vithanage @ 2026-05-21 15:43 UTC (permalink / raw)
To: Stephen Hemminger, dev
Cc: Bibo Mao, David Christensen, Sun Yuechi, Bruce Richardson,
Konstantin Ananyev
In-Reply-To: <20260521042043.1590536-3-stephen@networkplumber.org>
Hi Stephen,
Suggesting minor changes to comments on acquire and
release fences..
> +/** @name SMP Memory Barrier
> + */
> +///@{
> +/**
> + * General memory barrier between lcores
> + *
> + * Guarantees that the LOAD and STORE operations that precede the
> + * rte_smp_mb() call are globally visible across the lcores
> + * before the LOAD and STORE operations that follows it.
> + */
> +static __rte_always_inline void
> +rte_smp_mb(void)
> +{
> + rte_atomic_thread_fence(rte_memory_order_seq_cst);
> +}
> +
> +/**
> + * Write memory barrier between lcores
> + *
> + * Guarantees that the STORE operations that precede the
> + * rte_smp_wmb() call are globally visible across the lcores
> + * before the STORE operations that follows it.
> + */
> +static __rte_always_inline void
> +rte_smp_wmb(void)
> +{
> + rte_atomic_thread_fence(rte_memory_order_release);
> +}
Release fences order STORE | STORE, and LOAD | STORE.
Therefor, the comment should be "Guarantees that LOAD
and STORE operations that precede the rte_smp_wmb() call
are globally observed before STORE operations that follows it."
> +
> +/**
> + * Read memory barrier between lcores
> + *
> + * Guarantees that the LOAD operations that precede the
> + * rte_smp_rmb() call are globally visible across the lcores
> + * before the LOAD operations that follows it.
> + */
> +static __rte_always_inline void
> +rte_smp_rmb(void)
> +{
> + rte_atomic_thread_fence(rte_memory_order_acquire);
> +}
Acquire fences order LOAD | LOAD and LOAD | STORE.
Thus, the comment should be "Guarantees that the LOAD
operations that precede the rte_smp_rmb() call observe
global state before LOAD and STORE operations that
follows it"
--wathsala
^ permalink raw reply
* [PATCH 2/2] usertools/telemetry: support using aliases for long commands
From: Bruce Richardson @ 2026-05-21 15:39 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
In-Reply-To: <20260521153913.82634-1-bruce.richardson@intel.com>
Similarly to how shell aliases work, allow specifying of short alias
commands for dpdk-telemetry.py script. The aliases are read from
"$HOME/.dpdk_telemetry_aliases" at startup.
Some examples of use from the docs. Alias file contents:
# Basic shortcuts
ls=/ethdev/list
names=FOREACH i /ethdev/list /ethdev/info,$i .name
q=quit
Alias use:
--> ls
{"/ethdev/list": [0, 1]}
--> names
[{"i": 0, "name": "0000:
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
doc/guides/howto/telemetry.rst | 34 +++++++++++++++
usertools/dpdk-telemetry.py | 75 ++++++++++++++++++++++++++++++++--
2 files changed, 105 insertions(+), 4 deletions(-)
diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst
index 4bf48c635e..072289aec8 100644
--- a/doc/guides/howto/telemetry.rst
+++ b/doc/guides/howto/telemetry.rst
@@ -130,6 +130,40 @@ and query information using the telemetry client python script.
- With loop variable: returns an array of objects containing the loop
variable field and requested value fields.
+ * Use command aliases.
+
+ The telemetry script can load aliases at startup from::
+
+ $HOME/.dpdk_telemetry_aliases
+
+ Each alias entry must be in ``alias=command`` format.
+ Empty lines and lines starting with ``#`` are ignored.
+
+ Example alias file::
+
+ # Basic shortcuts
+ ls=/ethdev/list
+ names=FOREACH i /ethdev/list /ethdev/info,$i .name
+ q=quit
+
+ Alias behavior is intentionally similar to shell aliases:
+
+ - The first token of the entered input is checked for an alias match.
+ - If matched, that first token is replaced with its expansion.
+ - Alias expansion is recursive (aliases can expand to other aliases).
+ - Expansion has a safety limit to prevent infinite loops.
+
+ Examples::
+
+ --> ls
+ {"/ethdev/list": [0, 1]}
+
+ --> names
+ [{"i": 0, "name": "0000:16:00.0"}, {"i": 1, "name": "0000:16:00.1"}]
+
+ --> q
+ # exits the client
+
Connecting to Different DPDK Processes
--------------------------------------
diff --git a/usertools/dpdk-telemetry.py b/usertools/dpdk-telemetry.py
index 2de10cff69..8b976160e0 100755
--- a/usertools/dpdk-telemetry.py
+++ b/usertools/dpdk-telemetry.py
@@ -21,6 +21,70 @@
SOCKET_NAME = "dpdk_telemetry.{}".format(TELEMETRY_VERSION)
DEFAULT_PREFIX = "rte"
CMDS = []
+ALIASES = {}
+ALIAS_FILE = ".dpdk_telemetry_aliases"
+MAX_ALIAS_EXPANSIONS = 32
+
+
+def load_aliases():
+ """Load aliases from $HOME/.dpdk_telemetry_aliases"""
+ aliases = {}
+ home = os.environ.get("HOME")
+ if not home:
+ return aliases
+
+ alias_path = os.path.join(home, ALIAS_FILE)
+ if not os.path.isfile(alias_path):
+ return aliases
+
+ try:
+ with open(alias_path) as alias_file:
+ for line_num, line in enumerate(alias_file, start=1):
+ entry = line.strip()
+ if not entry or entry.startswith("#"):
+ continue
+ if "=" not in entry:
+ print(
+ "Warning: ignoring malformed alias at {}:{}".format(alias_path, line_num),
+ file=sys.stderr,
+ )
+ continue
+ name, command = entry.split("=", 1)
+ name = name.strip()
+ command = command.strip()
+ if not name or not command:
+ print(
+ "Warning: ignoring malformed alias at {}:{}".format(alias_path, line_num),
+ file=sys.stderr,
+ )
+ continue
+ aliases[name] = command
+ except OSError as e:
+ print("Warning: failed to read {}: {}".format(alias_path, e), file=sys.stderr)
+
+ return aliases
+
+
+def expand_aliases(text, aliases):
+ """Expand aliases similarly to shell aliases on the first token"""
+ expanded = text
+ for _ in range(MAX_ALIAS_EXPANSIONS):
+ stripped = expanded.lstrip()
+ if not stripped:
+ return expanded
+
+ parts = stripped.split(None, 1)
+ first = parts[0]
+ rest = parts[1] if len(parts) > 1 else ""
+
+ if first not in aliases:
+ return expanded
+
+ alias_value = aliases[first]
+ expanded = "{} {}".format(alias_value, rest).strip() if rest else alias_value
+
+ print("Warning: alias expansion limit reached", file=sys.stderr)
+ return expanded
def send_command(sock, cmd, output_buf_len, echo=False, pretty=False):
@@ -262,10 +326,12 @@ def handle_socket(args, path):
# interactive prompt
try:
- text = input(prompt).strip()
- while text != "quit":
- handle_command(sock, output_buf_len, text, pretty=prompt)
+ while True:
text = input(prompt).strip()
+ expanded = expand_aliases(text, ALIASES)
+ if expanded == "quit":
+ break
+ handle_command(sock, output_buf_len, expanded, pretty=prompt)
except EOFError:
pass
finally:
@@ -274,7 +340,7 @@ def handle_socket(args, path):
def readline_complete(text, state):
"""Find any matching commands from the list based on user input"""
- all_cmds = ["quit"] + CMDS
+ all_cmds = ["quit"] + list(ALIASES.keys()) + CMDS
if text:
matches = [c for c in all_cmds if c.startswith(text)]
else:
@@ -304,6 +370,7 @@ def readline_complete(text, state):
help="List all possible file-prefixes and exit",
)
args = parser.parse_args()
+ALIASES = load_aliases()
if args.list:
list_fp()
sys.exit(0)
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] usertools/telemetry: add a FOREACH command
From: Bruce Richardson @ 2026-05-21 15:39 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
In-Reply-To: <20260521153913.82634-1-bruce.richardson@intel.com>
To simplify querying data from multiple devices, e.g. across ethdevs, or
dmadevs, add a FOREACH command to the python script, allowing you to
run, e.g. /ethdev/list, and then run a second command for each item in
the list, gathering the relevant output values, optionally including an
index counter.
Simple examples are given in the documentation:
--> FOREACH /ethdev/list /ethdev/stats .opackets
[0, 0]
--> FOREACH /ethdev/list /ethdev/stats .ipackets .opackets
[{"ipackets": 0, "opackets": 0}, {"ipackets": 0, "opackets": 0}]
--> FOREACH i /ethdev/list /ethdev/info,$i .name
[{"i": 0, "name": "0000:16:00.0"}, {"i": 1, "name": "0000:16:00.1"}]
--> FOREACH i /ethdev/list /ethdev/stats,$i .ipackets .opackets
[{"i": 0, "ipackets": 0, "opackets": 0}, {"i": 1, "ipackets": 0, "opackets": 0}]
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
doc/guides/howto/telemetry.rst | 42 +++++++++++
usertools/dpdk-telemetry.py | 128 ++++++++++++++++++++++++++++++++-
2 files changed, 167 insertions(+), 3 deletions(-)
diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst
index 0464c431fe..4bf48c635e 100644
--- a/doc/guides/howto/telemetry.rst
+++ b/doc/guides/howto/telemetry.rst
@@ -88,6 +88,48 @@ and query information using the telemetry client python script.
{"/help": {"/ethdev/xstats": "Returns the extended stats for a port.
Parameters: int port_id"}}
+ * Run a compound query using ``FOREACH``.
+
+ The ``FOREACH`` command runs a list command, iterates each returned item,
+ runs a second command for each item, and emits combined JSON output.
+
+ Start with the simplest form (no loop variable)::
+
+ FOREACH /<list_cmd> /<iter_cmd> .<field> [.<field> ...]
+
+ To include numbered output, use a loop variable::
+
+ FOREACH <var> /<list_cmd> /<iter_cmd_with_$var> .<field> [.<field> ...]
+
+ Notes:
+
+ - Field selectors are whitespace-separated tokens, each starting with ``.``.
+ - In no-variable mode, the iter command is called as ``/<iter_cmd>,<item>``.
+ - In loop-variable mode, use ``$<var>`` in the iter command where the
+ item value should be substituted.
+
+ Examples::
+
+ --> FOREACH /ethdev/list /ethdev/stats .opackets
+ [0, 0]
+
+ --> FOREACH /ethdev/list /ethdev/stats .ipackets .opackets
+ [{"ipackets": 0, "opackets": 0}, {"ipackets": 0, "opackets": 0}]
+
+ --> FOREACH i /ethdev/list /ethdev/info,$i .name
+ [{"i": 0, "name": "0000:16:00.0"}, {"i": 1, "name": "0000:16:00.1"}]
+
+ --> FOREACH i /ethdev/list /ethdev/stats,$i .ipackets .opackets
+ [{"i": 0, "ipackets": 0, "opackets": 0}, {"i": 1, "ipackets": 0, "opackets": 0}]
+
+ Output behavior:
+
+ - Without loop variable and one field: returns an array of values.
+ - Without loop variable and multiple fields: returns an array of objects
+ containing named value fields.
+ - With loop variable: returns an array of objects containing the loop
+ variable field and requested value fields.
+
Connecting to Different DPDK Processes
--------------------------------------
diff --git a/usertools/dpdk-telemetry.py b/usertools/dpdk-telemetry.py
index 09258a1f7e..2de10cff69 100755
--- a/usertools/dpdk-telemetry.py
+++ b/usertools/dpdk-telemetry.py
@@ -23,6 +23,130 @@
CMDS = []
+def send_command(sock, cmd, output_buf_len, echo=False, pretty=False):
+ """Send a telemetry command and return the parsed JSON reply"""
+ sock.send(cmd.encode())
+ return read_socket(sock, output_buf_len, echo, pretty)
+
+
+def get_cmd_payload(reply, cmd):
+ """Return the payload for a command response if present"""
+ if isinstance(reply, dict) and len(reply) == 1:
+ return next(iter(reply.values()))
+ return None
+
+
+def get_path_value(payload, path):
+ """Resolve a dotted path (e.g. '.name' or '.a.b') from a JSON payload"""
+ if not path:
+ return payload
+
+ keys = [k for k in path.lstrip(".").split(".") if k]
+ val = payload
+ for key in keys:
+ if not isinstance(val, dict) or key not in val:
+ return None
+ val = val[key]
+ return val
+
+
+def parse_selectors(selector_text):
+ """Parse whitespace-separated dotted selectors"""
+ selectors = selector_text.split()
+ if not selectors:
+ print("Invalid FOREACH syntax: missing selector")
+ return None
+ if any(not selector.startswith(".") for selector in selectors):
+ print("Invalid FOREACH syntax: selector must start with '.'")
+ return None
+ return selectors
+
+
+def parse_foreach(text):
+ """Parse FOREACH [<var>] /<cmd> /<parameterized cmd> .<value> [.<value> ...]"""
+ try:
+ tokens = text.split(None, 3)
+ except ValueError:
+ print("Invalid FOREACH syntax")
+ return None
+
+ if len(tokens) != 4:
+ print("Invalid FOREACH syntax")
+ return None
+
+ _, arg1, arg2, arg3 = tokens
+ if arg1.startswith("/"):
+ var_name = None
+ list_cmd = arg1
+ iter_cmd = arg2
+ selector_text = arg3
+ else:
+ var_name = arg1
+ list_cmd = arg2
+ try:
+ iter_cmd, selector_text = arg3.split(None, 1)
+ except ValueError:
+ print("Invalid FOREACH syntax")
+ return None
+
+ if not list_cmd.startswith("/") or not iter_cmd.startswith("/"):
+ print("Invalid FOREACH syntax: commands must start with '/'")
+ return None
+
+ selectors = parse_selectors(selector_text)
+ if selectors is None:
+ return None
+
+ return var_name, list_cmd, iter_cmd, selectors
+
+
+def build_foreach_result(item, var_name, payload, selectors):
+ """Build one FOREACH result entry based on selector count and index mode"""
+ values = {selector.lstrip("."): get_path_value(payload, selector) for selector in selectors}
+
+ if var_name is None and len(selectors) == 1:
+ return next(iter(values.values()))
+ if var_name is None:
+ return values
+
+ return {var_name: item, **values}
+
+
+def handle_foreach(sock, output_buf_len, text, pretty=False):
+ """Handle FOREACH queries and print telemetry-like JSON array output"""
+ parsed = parse_foreach(text)
+ if parsed is None:
+ return
+ var_name, list_cmd, iter_cmd, selectors = parsed
+
+ list_reply = send_command(sock, list_cmd, output_buf_len)
+ values = get_cmd_payload(list_reply, list_cmd)
+ if not isinstance(values, list):
+ print("FOREACH source command did not return a JSON array")
+ return
+
+ output = []
+ for item in values:
+ if var_name is None:
+ cmd = "{},{}".format(iter_cmd, item)
+ else:
+ cmd = iter_cmd.replace("$" + var_name, str(item))
+ item_reply = send_command(sock, cmd, output_buf_len)
+ item_payload = get_cmd_payload(item_reply, cmd)
+ output.append(build_foreach_result(item, var_name, item_payload, selectors))
+
+ indent = 2 if pretty else None
+ print(json.dumps(output, indent=indent))
+
+
+def handle_command(sock, output_buf_len, text, pretty=False):
+ """Execute a user command if recognized"""
+ if text.startswith("/"):
+ send_command(sock, text, output_buf_len, echo=True, pretty=pretty)
+ elif text.startswith("FOREACH "):
+ handle_foreach(sock, output_buf_len, text, pretty)
+
+
def read_socket(sock, buf_len, echo=True, pretty=False):
"""Read data from socket and return it in JSON format"""
reply = sock.recv(buf_len).decode()
@@ -140,9 +264,7 @@ def handle_socket(args, path):
try:
text = input(prompt).strip()
while text != "quit":
- if text.startswith("/"):
- sock.send(text.encode())
- read_socket(sock, output_buf_len, pretty=prompt)
+ handle_command(sock, output_buf_len, text, pretty=prompt)
text = input(prompt).strip()
except EOFError:
pass
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] extend interactive telemetry script
From: Bruce Richardson @ 2026-05-21 15:39 UTC (permalink / raw)
To: dev; +Cc: Bruce Richardson
To simplify interactive telemetry script for general use, i.e. not from
other scripts, we can add two new features to it:
1. Support for FOREACH to allow gathering a set of output values across
a list of ports or devices, e.g. ethdevs or rawdevs.
2. Support having predefined aliases in a file in the user's home
directory to simplify the use of more complicated FOREACH commands.
Putting these together, we can create new commands such as "eth_names".
bruce@host:$ cat ~/.dpdk_telemetry_aliases
eth_names=FOREACH index /ethdev/list /ethdev/info,$index .name
bruce@host:$ echo eth_names | ./usertools/dpdk-telemetry.py | jq
[
{
"index": 0,
"name": "0000:16:00.0"
},
{
"index": 1,
"name": "0000:16:00.1"
}
]
Bruce Richardson (2):
usertools/telemetry: add a FOREACH command
usertools/telemetry: support using aliases for long commands
doc/guides/howto/telemetry.rst | 76 +++++++++++++
usertools/dpdk-telemetry.py | 201 ++++++++++++++++++++++++++++++++-
2 files changed, 271 insertions(+), 6 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH] Windows: fix core count on NUMA with more than 64 cores per node
From: Dmitry Kozlyuk @ 2026-05-21 15:26 UTC (permalink / raw)
To: David Marchand, Andre Muezerie; +Cc: dev, Gena Tertychnyi
In-Reply-To: <CAJFAV8yhmQ8hEpMmUWW77kAyuJXb-Ru6z1kBnHLSBhcZ_LfEhg@mail.gmail.com>
Fixes: b70a9b788625 ("eal: get/set thread affinity per thread identifier")
Cc: stable@dpdk.org
Acked-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
I believe it really fixes my commit b8a36b086625 ("eal/windows: improve
CPU and
NUMA node detection"), but the patch will only apply to Tyler's commit
and it's far enough for any LTS.
Nit: EAL_NUMA_GROUP_COUNT is not needed because GroupCount handling
is source-compatible between MinGW and Windows SDK,
and there is logic to handle GroupCount == 0 for older Windows anyway.
^ permalink raw reply
* Re: [PATCH v2 2/2] ethdev: add telemetry endpoint for list names
From: Bruce Richardson @ 2026-05-21 15:21 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Morten Brørup, fengchengwen, thomas, dev, andrew.rybchenko
In-Reply-To: <20260521073716.3a788175@phoenix.local>
On Thu, May 21, 2026 at 07:37:16AM -0700, Stephen Hemminger wrote:
> On Thu, 21 May 2026 14:40:47 +0200
> Morten Brørup <mb@smartsharesystems.com> wrote:
>
> > > From: fengchengwen [mailto:fengchengwen@huawei.com]
> > > Sent: Thursday, 21 May 2026 14.25
> > >
> > > Thanks for the feedback.
> > >
> > > I intend to keep the current dict format. This concise ID-name mapping
> > > is quite
> > > helpful and easy to read especially when there are massive ports, which
> > > is exactly
> > > the main purpose why I submitted this patch.
> > >
> > > In my opinion, adopting OData-style query would require architecture-
> > > level
> > > refactoring of telemetry framework, which is way too heavy for this
> > > simple requirement.
> >
> > Agree.
> > Refactoring the telemetry framework is different task, not related to this patch.
> >
> > > For complex query demands, we can implement them by extending the
> > > upper-layer Python
> > > telemetry script instead.
> > >
> > > So I suggest we keep this simple form here.
> >
> > If it is generally acceptable for DPDK telemetry that a request for a list does not return a list type, but returns an object type with "index": "value" fields instead, then
> > Series-acked-by: Morten Brørup <mb@smartsharesystems.com>
>
> It is necessary since port list may have holes due to hotplug or the ownership API.
> It would be good to have a more complete query function that returns more about each ethdev.
> I wouldn't worry about the size of the response. This is JSON and it is meant to be read by scripts not directly by humans.
That's where I think we have a difference - if the output is meant to be
read by scripts, we should not need extra commands like this, since the
information is already available via existing commands. The only compelling
reason that I can see for adding a new command to return a set of ethdev
names is for human interactive use.
Personally, I think the output should be just used by other scripts, but
it does appear that this quick-and-dirty telemetry script is being used
extensively for human interactive querying. Therefore, I'm working on
extending the script to make it more useful for such cases. I'd prefer to
add the extra smarts into the script rather than seeing a proliferation of
endpoints providing the same data in different formats.
/Bruce
^ permalink raw reply
* Re: [PATCH v19 00/11]net/sxe2: fix logic errors and address feedback
From: Thomas Monjalon @ 2026-05-21 15:16 UTC (permalink / raw)
To: Jie Liu; +Cc: stephen, dev
In-Reply-To: <20260520021809.4019054-1-liujie5@linkdatatechnology.com>
Hello,
20/05/2026 04:17, liujie5@linkdatatechnology.com:
> common/sxe2: add sxe2 basic structures
Are you planning to add a crypto or compress driver?
This is usually the reason to have a common library.
If you don't intend to share some code between different driver,
then you should not have a common library.
^ permalink raw reply
* Re: [PATCH v6 3/3] test: add test for af_packet
From: Thomas Monjalon @ 2026-05-21 14:54 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
In-Reply-To: <20260429182055.398105-4-stephen@networkplumber.org>
29/04/2026 20:19, Stephen Hemminger:
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <linux/if.h>
> +#include <linux/if_tun.h>
> +#include <linux/sockios.h>
> +#include <pthread.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/socket.h>
There is a compilation failure for ppc about sockaddr.
It seems sys/socket.h must be included before linux/if.h.
I'll do the change while pulling.
^ permalink raw reply
* Re: [PATCH] Windows: fix core count on NUMA with more than 64 cores per node
From: David Marchand @ 2026-05-21 14:51 UTC (permalink / raw)
To: Dmitry Kozlyuk, Andre Muezerie; +Cc: dev, Gena Tertychnyi
In-Reply-To: <20260513235518.408895-1-andremue@linux.microsoft.com>
Hello,
On Thu, 14 May 2026 at 01:56, Andre Muezerie
<andremue@linux.microsoft.com> wrote:
>
> From: Gena Tertychnyi <genter@microsoft.com>
>
> Fix specific to Windows NUMA machines with more than 64 cores per node
> (e.g. 2 NUMAs with 128 cores each):
>
> - NumaNode.GroupMasks[] array is used instead of NumaNode.GroupMask.
> - RelationAll is used instead of RelationNumaNode when calling
> GetLogicalProcessorInformationEx because RelationAll returns the full
> multi-group NUMA affinity as RelationNumaNode returns only the NUMA
> node's primary group.
Probably worth a Fixes: tag.
>
> Signed-off-by: Gena Tertychnyi <genter@microsoft.com>
> Signed-off-by: Andre Muezerie <andremue@linux.microsoft.com>
Dmitry, any objection?
--
David Marchand
^ permalink raw reply
* Re: [PATCH v2 2/2] ethdev: add telemetry endpoint for list names
From: Stephen Hemminger @ 2026-05-21 14:37 UTC (permalink / raw)
To: Morten Brørup
Cc: fengchengwen, Bruce Richardson, thomas, dev, andrew.rybchenko
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F6588F@smartserver.smartshare.dk>
On Thu, 21 May 2026 14:40:47 +0200
Morten Brørup <mb@smartsharesystems.com> wrote:
> > From: fengchengwen [mailto:fengchengwen@huawei.com]
> > Sent: Thursday, 21 May 2026 14.25
> >
> > Thanks for the feedback.
> >
> > I intend to keep the current dict format. This concise ID-name mapping
> > is quite
> > helpful and easy to read especially when there are massive ports, which
> > is exactly
> > the main purpose why I submitted this patch.
> >
> > In my opinion, adopting OData-style query would require architecture-
> > level
> > refactoring of telemetry framework, which is way too heavy for this
> > simple requirement.
>
> Agree.
> Refactoring the telemetry framework is different task, not related to this patch.
>
> > For complex query demands, we can implement them by extending the
> > upper-layer Python
> > telemetry script instead.
> >
> > So I suggest we keep this simple form here.
>
> If it is generally acceptable for DPDK telemetry that a request for a list does not return a list type, but returns an object type with "index": "value" fields instead, then
> Series-acked-by: Morten Brørup <mb@smartsharesystems.com>
It is necessary since port list may have holes due to hotplug or the ownership API.
It would be good to have a more complete query function that returns more about each ethdev.
I wouldn't worry about the size of the response. This is JSON and it is meant to be read by scripts not directly by humans.
^ permalink raw reply
* Re: [PATCH] dma/ae4dma: add AMD AE4DMA DMA PMD
From: David Marchand @ 2026-05-21 14:28 UTC (permalink / raw)
To: Raghavendra Ningoji
Cc: dev, thomas, rjarry, Bhagyada.Modali, Selwin.Sebastian,
Chengwen Feng
In-Reply-To: <20260518181856.1228373-1-raghavendra.ningoji@amd.com>
Hello,
On Mon, 18 May 2026 at 20:19, Raghavendra Ningoji
<raghavendra.ningoji@amd.com> wrote:
>
> Add a new dmadev poll-mode driver for the AMD AE4DMA hardware DMA
> engine. An AE4DMA engine exposes 16 hardware command queues, each
> with a 32-entry descriptor ring; the PMD maps each hardware channel
> to its own dmadev with a single virtual channel, so a PCI function
> appears as 16 dmadevs named "<pci-bdf>-ch0" .. "<pci-bdf>-ch15".
>
> Driver characteristics:
>
> - Memory-to-memory copy operations only (RTE_DMA_CAPA_MEM_TO_MEM).
> - Completion is detected via the hardware's per-queue read_idx
> register, which the engine advances as it processes descriptors.
> The descriptor status / err_code bytes are read only to classify
> each drained slot as success or failure.
> - vchan_status reports IDLE/ACTIVE based on HW read_idx vs write_idx
> and HALTED_ERROR when the queue is not enabled.
> - depends on bus_pci and dmadev.
>
> Signed-off-by: Raghavendra Ningoji <raghavendra.ningoji@amd.com>
Cc: Chengwen Feng (DMAdev maintainer)
- The patch is big, splitting it into logical patches introducing one
feature at a time would help.
See for example how the latest DMA driver was submitted:
5a9c32a89c - dma/hisi_acc: introduce HiSilicon SoC accelerator driver
(7 months ago) <Chengwen Feng>
2557ad8f8a - dma/hisi_acc: add control path operations (7 months ago)
<Chengwen Feng>
b58c4435ea - dma/hisi_acc: add data path operations (7 months ago)
<Chengwen Feng>
- Please fix the below warnings raised by checkpatches.sh, and run
this script before submitting a new revision.
### [PATCH] dma/ae4dma: add AMD AE4DMA DMA PMD
WARNING:TYPO_SPELLING: 're-use' may be misspelled - perhaps 'reuse'?
#199: FILE: drivers/dma/ae4dma/ae4dma_dmadev.c:42:
+ AE4DMA_PMD_INFO("re-use memzone already "
^^^^^^
total: 0 errors, 1 warnings, 1160 lines checked
Warning in drivers/dma/ae4dma/ae4dma_internal.h:
Prefer RTE_LOG_LINE/RTE_LOG_DP_LINE
Warning in drivers/dma/ae4dma/ae4dma_internal.h:
Do not use variadic argument pack in macros
Please use __rte_cache_aligned only for struct or union types alignment.
- Please also checks the copyright years.
For new code (from upstream pov), this should be 2026.
- Globally in those changes, rte_iova_t should probably be used
instead of phys_addr_t.
> ---
> MAINTAINERS | 5 +
> doc/guides/dmadevs/ae4dma.rst | 75 +++
> doc/guides/dmadevs/index.rst | 1 +
> doc/guides/rel_notes/release_26_07.rst | 7 +
> drivers/dma/ae4dma/ae4dma_dmadev.c | 742 +++++++++++++++++++++++++
> drivers/dma/ae4dma/ae4dma_hw_defs.h | 164 ++++++
> drivers/dma/ae4dma/ae4dma_internal.h | 117 ++++
> drivers/dma/ae4dma/meson.build | 7 +
> drivers/dma/meson.build | 1 +
> usertools/dpdk-devbind.py | 5 +-
> 10 files changed, 1123 insertions(+), 1 deletion(-)
> create mode 100644 doc/guides/dmadevs/ae4dma.rst
> create mode 100644 drivers/dma/ae4dma/ae4dma_dmadev.c
> create mode 100644 drivers/dma/ae4dma/ae4dma_hw_defs.h
> create mode 100644 drivers/dma/ae4dma/ae4dma_internal.h
> create mode 100644 drivers/dma/ae4dma/meson.build
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9143d028bc..0b5a6e08d8 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1361,6 +1361,11 @@ F: doc/guides/compressdevs/features/zsda.ini
> DMAdev Drivers
> --------------
>
> +AMD AE4DMA
> +M: Bhagyada Modali <Bhagyada.Modali@amd.com>
No capital letter in the mail address section.
> +F: drivers/dma/ae4dma/
> +F: doc/guides/dmadevs/ae4dma.rst
> +
> Intel IDXD - EXPERIMENTAL
> M: Bruce Richardson <bruce.richardson@intel.com>
> M: Kevin Laatz <kevin.laatz@intel.com>
Please also add an entry in the .mailmap file as it is your first contribution.
> diff --git a/drivers/dma/ae4dma/ae4dma_dmadev.c b/drivers/dma/ae4dma/ae4dma_dmadev.c
> new file mode 100644
> index 0000000000..eb6ea88f55
> --- /dev/null
> +++ b/drivers/dma/ae4dma/ae4dma_dmadev.c
> @@ -0,0 +1,742 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2021-2025 Advanced Micro Devices, Inc. All rights reserved.
> + */
> +
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +#include <rte_bus_pci.h>
> +#include <bus_pci_driver.h>
> +#include <rte_dmadev_pmd.h>
> +#include <rte_malloc.h>
> +
> +#include "ae4dma_internal.h"
> +
> +/*
> + * One dmadev per AE4DMA hardware channel; each dmadev has exactly one
> + * virtual channel. The HW's per-queue register block must be densely
> + * packed right after the engine-common config register at BAR0+0; the
> + * build-time check below catches an accidental layout change.
> + */
> +static_assert(sizeof(struct ae4dma_hwq_regs) == 32,
> + "ae4dma_hwq_regs stride changed; per-queue offset math will break");
> +
> +RTE_LOG_REGISTER_DEFAULT(ae4dma_pmd_logtype, INFO);
> +
> +#define AE4DMA_PMD_NAME dmadev_ae4dma
> +#define AE4DMA_PMD_NAME_STR RTE_STR(AE4DMA_PMD_NAME)
AE4DMA_PMD_NAME_STR is not used at all.
Avoid introducing the macro AE4DMA_PMD_NAME, the value is not supposed
to change.
> +
> +static const struct rte_memzone *
> +ae4dma_queue_dma_zone_reserve(const char *queue_name,
> + uint32_t queue_size, int socket_id)
> +{
> + const struct rte_memzone *mz;
> +
> + mz = rte_memzone_lookup(queue_name);
> + if (mz != 0) {
mz != NULL
> + if (((size_t)queue_size <= mz->len) &&
> + ((socket_id == SOCKET_ID_ANY) ||
> + (socket_id == mz->socket_id))) {
> + AE4DMA_PMD_INFO("re-use memzone already "
> + "allocated for %s", queue_name);
> + return mz;
> + }
> + AE4DMA_PMD_ERR("Incompatible memzone already "
> + "allocated %s, size %u, socket %d. "
> + "Requested size %u, socket %u",
> + queue_name, (uint32_t)mz->len,
> + mz->socket_id, queue_size, socket_id);
> + return NULL;
> + }
> + return rte_memzone_reserve_aligned(queue_name, queue_size,
> + socket_id, RTE_MEMZONE_IOVA_CONTIG, queue_size);
> +}
> +
> +/* Configure a device. */
> +static int
> +ae4dma_dev_configure(struct rte_dma_dev *dev __rte_unused,
> + const struct rte_dma_conf *dev_conf,
> + uint32_t conf_sz)
> +{
> + if (sizeof(struct rte_dma_conf) != conf_sz)
> + return -EINVAL;
> +
> + if (dev_conf->nb_vchans != 1)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +/* Setup a virtual channel for AE4DMA, only 1 vchan is supported per dmadev. */
> +static int
> +ae4dma_vchan_setup(struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
> + const struct rte_dma_vchan_conf *qconf, uint32_t qconf_sz)
> +{
> + struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint16_t max_desc = qconf->nb_desc;
> +
> + if (sizeof(struct rte_dma_vchan_conf) != qconf_sz)
> + return -EINVAL;
> +
> + if (max_desc < 2)
> + return -EINVAL;
> +
> + if (!rte_is_power_of_2(max_desc))
> + max_desc = rte_align32pow2(max_desc);
> +
> + if (max_desc > AE4DMA_DESCRIPTORS_PER_CMDQ) {
> + AE4DMA_PMD_DEBUG("DMA dev %u nb_desc clamped to %u",
> + dev->data->dev_id, AE4DMA_DESCRIPTORS_PER_CMDQ);
> + max_desc = AE4DMA_DESCRIPTORS_PER_CMDQ;
> + }
> +
> + cmd_q->qcfg = *qconf;
> + cmd_q->qcfg.nb_desc = max_desc;
> +
> + /* Ensure all counters are reset, if reconfiguring/restarting device. */
> + memset(&cmd_q->stats, 0, sizeof(cmd_q->stats));
> + return 0;
> +}
> +
> +/* Start a configured device. */
> +static int
> +ae4dma_dev_start(struct rte_dma_dev *dev)
> +{
> + struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> +
> + if (nb == 0)
> + return -EBUSY;
> +
> + /* Program ring depth expected by hardware. */
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->max_idx, nb);
> + return 0;
> +}
> +
> +/* Stop a configured device. */
> +static int
> +ae4dma_dev_stop(struct rte_dma_dev *dev)
> +{
> + struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> +
> + if (cmd_q->hwq_regs != NULL)
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->control_reg.control_raw,
> + AE4DMA_CMD_QUEUE_DISABLE);
> + return 0;
> +}
> +
> +/* Get device information of a device. */
> +static int
> +ae4dma_dev_info_get(const struct rte_dma_dev *dev, struct rte_dma_info *info,
> + uint32_t size)
> +{
> + if (size < sizeof(*info))
> + return -EINVAL;
> + info->dev_name = dev->device->name;
> + info->dev_capa = RTE_DMA_CAPA_MEM_TO_MEM;
> + info->max_vchans = 1;
> + info->min_desc = 2;
> + info->max_desc = AE4DMA_DESCRIPTORS_PER_CMDQ;
> + info->nb_vchans = 1;
> + return 0;
> +}
> +
> +/* Close a configured device. */
> +static int
> +ae4dma_dev_close(struct rte_dma_dev *dev)
> +{
> + struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> +
> + if (cmd_q->hwq_regs != NULL)
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->control_reg.control_raw,
> + AE4DMA_CMD_QUEUE_DISABLE);
> +
> + if (cmd_q->memz_name[0] != '\0') {
> + const struct rte_memzone *mz = rte_memzone_lookup(cmd_q->memz_name);
> +
> + if (mz != NULL)
> + rte_memzone_free(mz);
> + }
> + cmd_q->qbase_desc = NULL;
> + cmd_q->qbase_addr = NULL;
> + cmd_q->qbase_phys_addr = 0;
> + return 0;
> +}
> +
> +/* trigger h/w to process enqued desc:doorbell - by next_write */
> +static inline void
> +__submit(struct ae4dma_dmadev *ae4dma)
> +{
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint16_t write_idx = cmd_q->next_write;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> +
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->write_idx, write_idx);
> + if (nb != 0)
> + cmd_q->stats.submitted += (uint16_t)((cmd_q->next_write - cmd_q->last_write +
> + nb) % nb);
> + cmd_q->last_write = cmd_q->next_write;
> +}
> +
> +static int
> +ae4dma_submit(void *dev_private, uint16_t vchan __rte_unused)
> +{
> + struct ae4dma_dmadev *ae4dma = dev_private;
> +
> + __submit(ae4dma);
> + return 0;
> +}
> +
> +/* Write descriptor for enqueue (copy only). */
> +static inline int
> +__write_desc_copy(void *dev_private, rte_iova_t src, phys_addr_t dst,
> + uint32_t len, uint64_t flags)
> +{
> + struct ae4dma_dmadev *ae4dma = dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + struct ae4dma_desc *dma_desc;
> + uint16_t ret;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> + uint16_t write = cmd_q->next_write;
> +
> + if (nb == 0)
> + return -EINVAL;
> +
> + /* Reserve one slot to distinguish full from empty (power-of-two ring). */
> + if ((uint32_t)cmd_q->ring_buff_count >= (uint32_t)(nb - 1))
> + return -ENOSPC;
> +
> + dma_desc = &cmd_q->qbase_desc[write];
> + memset(dma_desc, 0, sizeof(*dma_desc));
> + dma_desc->length = len;
> + dma_desc->src_hi = upper_32_bits(src);
> + dma_desc->src_lo = lower_32_bits(src);
> + dma_desc->dst_hi = upper_32_bits(dst);
> + dma_desc->dst_lo = lower_32_bits(dst);
> + cmd_q->ring_buff_count++;
> + cmd_q->next_write = (uint16_t)((write + 1) % nb);
> + ret = write;
> + if (flags & RTE_DMA_OP_FLAG_SUBMIT)
> + __submit(ae4dma);
> + return ret;
> +}
> +
> +/* Enqueue a copy operation onto the ae4dma device. */
> +static int
> +ae4dma_enqueue_copy(void *dev_private, uint16_t vchan __rte_unused,
> + rte_iova_t src, rte_iova_t dst, uint32_t length, uint64_t flags)
> +{
> + return __write_desc_copy(dev_private, src, dst, length, flags);
> +}
> +
> +/* Dump DMA device info. */
> +static int
> +ae4dma_dev_dump(const struct rte_dma_dev *dev, FILE *f)
> +{
> + struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + struct ae4dma_cmd_queue *cmd_q;
> + void *ae4dma_mmio_base_addr = (uint8_t *)ae4dma->io_regs;
> +
> + cmd_q = &ae4dma->cmd_q;
> + fprintf(f, "cmd_q->id = %" PRIx64 "\n", cmd_q->id);
> + fprintf(f, "cmd_q->qidx = %" PRIx64 "\n", cmd_q->qidx);
> + fprintf(f, "cmd_q->qsize = %" PRIx64 "\n", cmd_q->qsize);
> + fprintf(f, "mmio_base_addr = %p\n", ae4dma_mmio_base_addr);
> + fprintf(f, "queues per ae4dma engine = %d\n", AE4DMA_READ_REG_OFFSET(
> + ae4dma_mmio_base_addr, AE4DMA_COMMON_CONFIG_OFFSET));
> + fprintf(f, "== Private Data ==\n");
> + fprintf(f, " Config: { ring_size: %u }\n", cmd_q->qcfg.nb_desc);
> + fprintf(f, " Ring virt: %p\tphys: %#" PRIx64 "\n",
> + (void *)cmd_q->qbase_desc,
> + (uint64_t)cmd_q->qbase_phys_addr);
> + fprintf(f, " Next write: %u\n", cmd_q->next_write);
> + fprintf(f, " Next read: %u\n", cmd_q->next_read);
> + fprintf(f, " current queue depth: %u\n", cmd_q->ring_buff_count);
> + fprintf(f, " }\n");
> + fprintf(f, " Key Stats { submitted: %" PRIu64 ", comp: %" PRIu64 ", failed: %" PRIu64 " }\n",
> + cmd_q->stats.submitted,
> + cmd_q->stats.completed,
> + cmd_q->stats.errors);
> + return 0;
> +}
> +
> +/* Translates AE4DMA ChanERRs to DMA error codes. */
> +static inline enum rte_dma_status_code
> +__translate_status_ae4dma_to_dma(enum ae4dma_dma_err status)
> +{
> + AE4DMA_PMD_DEBUG("ae4dma desc status = %d", status);
> +
> + switch (status) {
> + case AE4DMA_DMA_ERR_NO_ERR:
> + return RTE_DMA_STATUS_SUCCESSFUL;
> + case AE4DMA_DMA_ERR_INV_LEN:
> + return RTE_DMA_STATUS_INVALID_LENGTH;
> + case AE4DMA_DMA_ERR_INV_SRC:
> + return RTE_DMA_STATUS_INVALID_SRC_ADDR;
> + case AE4DMA_DMA_ERR_INV_DST:
> + return RTE_DMA_STATUS_INVALID_DST_ADDR;
> + case AE4DMA_DMA_ERR_INV_ALIGN:
> + /* Name matches DPDK public enum spelling. */
> + return RTE_DMA_STATUS_DATA_POISION;
> + case AE4DMA_DMA_ERR_INV_HEADER:
> + case AE4DMA_DMA_ERR_INV_STATUS:
> + return RTE_DMA_STATUS_ERROR_UNKNOWN;
> + default:
> + return RTE_DMA_STATUS_ERROR_UNKNOWN;
> + }
> +}
> +
> +/*
> + * Scan HW queue for completed descriptors (non-blocking).
> + *
> + * The AE4DMA engine signals completion by advancing the per-queue
> + * `read_idx` register; it does not (reliably) write a status value
> + * back into the descriptor. We therefore use the HW `read_idx`
> + * register as the source of truth and only inspect the descriptor's
> + * `dw1.err_code` byte to classify each completion as success or
> + * failure.
> + *
> + * @param cmd_q
> + * The AE4DMA command queue.
> + * @param max_ops
> + * Maximum descriptors to process this call.
> + * @param[out] failed_count
> + * Number of completed descriptors that did not report success.
> + * @return
> + * Number of descriptors completed (success + failure), <= max_ops.
> + */
> +static inline uint16_t
> +ae4dma_scan_hwq(struct ae4dma_cmd_queue *cmd_q, uint16_t max_ops,
> + uint16_t *failed_count)
> +{
> + volatile struct ae4dma_desc *hw_desc;
> + uint16_t events_count = 0, fails = 0;
> + uint16_t tail;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> + uint16_t mask;
> + uint16_t hw_read_idx;
> + uint16_t in_flight;
> + uint16_t scan_cap;
> +
> + if (nb == 0 || cmd_q->ring_buff_count == 0) {
> + *failed_count = 0;
> + return 0;
> + }
> + mask = nb - 1;
> +
> + hw_read_idx = (uint16_t)(AE4DMA_READ_REG(&cmd_q->hwq_regs->read_idx) & mask);
> + tail = cmd_q->next_read;
> +
> + /*
> + * Descriptors completed since our last visit live in the
> + * half-open ring range [tail, hw_read_idx). If HW hasn't
> + * moved we have nothing to do.
> + */
> + in_flight = (uint16_t)((hw_read_idx - tail) & mask);
> + if (in_flight == 0) {
> + *failed_count = 0;
> + return 0;
> + }
> +
> + scan_cap = max_ops;
> + if (scan_cap > AE4DMA_DESCRIPTORS_PER_CMDQ)
> + scan_cap = AE4DMA_DESCRIPTORS_PER_CMDQ;
> + if (scan_cap > in_flight)
> + scan_cap = in_flight;
> + if (scan_cap > cmd_q->ring_buff_count)
> + scan_cap = (uint16_t)cmd_q->ring_buff_count;
> +
> + while (events_count < scan_cap) {
> + uint8_t hw_status;
> + uint8_t hw_err;
> +
> + hw_desc = &cmd_q->qbase_desc[tail];
> + hw_status = hw_desc->dw1.status;
> + hw_err = hw_desc->dw1.err_code;
> +
> + /*
> + * read_idx advancing is the definitive completion
> + * signal. The per-descriptor status byte is informational
> + * and may not yet be written when we observe it:
> + *
> + * AE4DMA_DMA_DESC_ERROR (4)
> + * Hard failure - err_code names the precise cause.
> + * AE4DMA_DMA_DESC_COMPLETED (3) or 0
> + * Success.
> + * AE4DMA_DMA_DESC_VALIDATED (1) / _PROCESSED (2)
> + * Benign race: HW had not finished updating the
> + * status byte at the instant we read it. Since
> + * read_idx has moved past this slot, treat it as
> + * success unless err_code says otherwise.
> + *
> + * A non-zero err_code is treated as a failure regardless
> + * of the observed status value.
> + */
> + if (hw_status == AE4DMA_DMA_DESC_ERROR ||
> + hw_err != AE4DMA_DMA_ERR_NO_ERR) {
> + fails++;
> + AE4DMA_PMD_WARN("Desc failed: status=%u err=%u",
> + hw_status, hw_err);
> + }
> + cmd_q->status[events_count] = (enum ae4dma_dma_err)hw_err;
> + cmd_q->ring_buff_count--;
> + events_count++;
> + tail = (tail + 1) & mask;
> + }
> +
> + cmd_q->stats.completed += events_count;
> + cmd_q->stats.errors += fails;
> + cmd_q->next_read = tail;
> + *failed_count = fails;
> + return events_count;
> +}
> +
> +/* Returns successful operations count and sets error flag if any errors. */
> +static uint16_t
> +ae4dma_completed(void *dev_private, uint16_t vchan __rte_unused,
> + const uint16_t max_ops, uint16_t *last_idx, bool *has_error)
> +{
> + struct ae4dma_dmadev *ae4dma = dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint16_t cpl_count, sl_count;
> + uint16_t err_count = 0;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> +
> + *has_error = false;
> +
> + cpl_count = ae4dma_scan_hwq(cmd_q, max_ops, &err_count);
> +
> + if (cpl_count > max_ops)
> + cpl_count = max_ops;
> +
> + if (cpl_count > 0 && last_idx != NULL)
> + *last_idx = (uint16_t)((cmd_q->next_read - 1 + nb) % nb);
> +
> + sl_count = cpl_count - err_count;
> + if (err_count)
> + *has_error = true;
> +
> + return sl_count;
> +}
> +
> +static uint16_t
> +ae4dma_completed_status(void *dev_private, uint16_t vchan __rte_unused,
> + uint16_t max_ops, uint16_t *last_idx,
> + enum rte_dma_status_code *status)
> +{
> + struct ae4dma_dmadev *ae4dma = dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint16_t cpl_count;
> + uint16_t i;
> + uint16_t err_count = 0;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> +
> + cpl_count = ae4dma_scan_hwq(cmd_q, max_ops, &err_count);
> +
> + if (cpl_count > max_ops)
> + cpl_count = max_ops;
> +
> + if (cpl_count > 0 && last_idx != NULL)
> + *last_idx = (uint16_t)((cmd_q->next_read - 1 + nb) % nb);
> +
> + if (likely(err_count == 0)) {
> + for (i = 0; i < cpl_count; i++)
> + status[i] = RTE_DMA_STATUS_SUCCESSFUL;
> + } else {
> + for (i = 0; i < cpl_count; i++)
> + status[i] = __translate_status_ae4dma_to_dma(cmd_q->status[i]);
> + }
> +
> + return cpl_count;
> +}
> +
> +/* Get the remaining capacity of the ring. */
> +static uint16_t
> +ae4dma_burst_capacity(const void *dev_private, uint16_t vchan __rte_unused)
> +{
> + const struct ae4dma_dmadev *ae4dma = dev_private;
> + const struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint16_t nb = cmd_q->qcfg.nb_desc;
> + uint16_t mask;
> + uint16_t read_idx = cmd_q->next_read;
> + uint16_t write_idx = cmd_q->next_write;
> + uint16_t used;
> +
> + if (nb < 2 || !rte_is_power_of_2(nb))
> + return 0;
> +
> + mask = nb - 1;
> + used = (uint16_t)((write_idx - read_idx) & mask);
> + /* One slot reserved (same rule as enqueue). */
> + if (used >= nb - 1)
> + return 0;
> + return (uint16_t)(nb - 1 - used);
> +}
> +
> +/* Retrieve the generic stats of a DMA device. */
> +static int
> +ae4dma_stats_get(const struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
> + struct rte_dma_stats *rte_stats, uint32_t size)
> +{
> + const struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + const struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + const struct rte_dma_stats *stats = &cmd_q->stats;
> +
> + if (size < sizeof(*rte_stats))
> + return -EINVAL;
> + if (rte_stats == NULL)
> + return -EINVAL;
> +
> + *rte_stats = *stats;
> + return 0;
> +}
> +
> +/* Reset the generic stat counters for the DMA device. */
> +static int
> +ae4dma_stats_reset(struct rte_dma_dev *dev, uint16_t vchan __rte_unused)
> +{
> + struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> +
> + memset(&cmd_q->stats, 0, sizeof(cmd_q->stats));
> + return 0;
> +}
> +
> +/*
> + * Report channel state to the dmadev framework.
> + *
> + * RTE_DMA_VCHAN_HALTED_ERROR - HW queue is disabled (never started, or
> + * stopped via dev_stop()).
> + * RTE_DMA_VCHAN_IDLE - HW has caught up: read_idx == write_idx,
> + * no descriptors in flight.
> + * RTE_DMA_VCHAN_ACTIVE - HW still has descriptors to process.
> + */
> +static int
> +ae4dma_vchan_status(const struct rte_dma_dev *dev, uint16_t vchan __rte_unused,
> + enum rte_dma_vchan_status *status)
> +{
> + const struct ae4dma_dmadev *ae4dma = dev->fp_obj->dev_private;
> + const struct ae4dma_cmd_queue *cmd_q = &ae4dma->cmd_q;
> + uint32_t ctrl, hw_read, hw_write;
> +
> + if (cmd_q->hwq_regs == NULL) {
> + *status = RTE_DMA_VCHAN_HALTED_ERROR;
> + return 0;
> + }
> +
> + ctrl = AE4DMA_READ_REG(&cmd_q->hwq_regs->control_reg.control_raw);
> + if ((ctrl & AE4DMA_CMD_QUEUE_ENABLE) == 0) {
> + *status = RTE_DMA_VCHAN_HALTED_ERROR;
> + return 0;
> + }
> +
> + hw_read = AE4DMA_READ_REG(&cmd_q->hwq_regs->read_idx);
> + hw_write = AE4DMA_READ_REG(&cmd_q->hwq_regs->write_idx);
> +
> + *status = (hw_read == hw_write) ? RTE_DMA_VCHAN_IDLE
> + : RTE_DMA_VCHAN_ACTIVE;
> + return 0;
> +}
> +
> +static int
> +ae4dma_add_queue(struct ae4dma_dmadev *dev, uint8_t qn, const char *pci_name)
> +{
> + uint32_t dma_addr_lo, dma_addr_hi;
> + struct ae4dma_cmd_queue *cmd_q;
> + const struct rte_memzone *q_mz;
> +
> + if (dev == NULL)
> + return -EINVAL;
dev can't be NULL.
The only caller passes a pointer that was already dereferenced.
> +
> + dev->io_regs = dev->pci->mem_resource[AE4DMA_PCIE_BAR].addr;
> +
> + cmd_q = &dev->cmd_q;
> + cmd_q->id = qn;
> + cmd_q->qidx = 0;
> + cmd_q->qsize = AE4DMA_QUEUE_SIZE(AE4DMA_QUEUE_DESC_SIZE);
> + cmd_q->hwq_regs = (volatile struct ae4dma_hwq_regs *)dev->io_regs + (qn + 1);
> +
> + /*
> + * Memzone name must be globally unique. Embed PCI BDF so multiple
> + * PCI functions probed concurrently don't collide.
> + */
> + snprintf(cmd_q->memz_name, sizeof(cmd_q->memz_name),
> + "ae4dma_%s_q%u", pci_name, (unsigned int)qn);
> +
> + q_mz = ae4dma_queue_dma_zone_reserve(cmd_q->memz_name,
> + cmd_q->qsize, rte_socket_id());
> + if (q_mz == NULL) {
> + AE4DMA_PMD_ERR("memzone reserve failed for %s", cmd_q->memz_name);
> + return -ENOMEM;
> + }
> +
> + cmd_q->qbase_addr = (void *)q_mz->addr;
> + cmd_q->qbase_desc = (struct ae4dma_desc *)q_mz->addr;
> + cmd_q->qbase_phys_addr = q_mz->iova;
> +
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->max_idx, AE4DMA_DESCRIPTORS_PER_CMDQ);
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->control_reg.control_raw,
> + AE4DMA_CMD_QUEUE_ENABLE);
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->intr_status_reg.intr_status_raw,
> + AE4DMA_DISABLE_INTR);
> + cmd_q->next_write = (uint16_t)AE4DMA_READ_REG(&cmd_q->hwq_regs->write_idx);
> + cmd_q->next_read = (uint16_t)AE4DMA_READ_REG(&cmd_q->hwq_regs->read_idx);
> + cmd_q->ring_buff_count = 0;
> +
> + dma_addr_lo = low32_value(cmd_q->qbase_phys_addr);
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->qbase_lo, dma_addr_lo);
> + dma_addr_hi = high32_value(cmd_q->qbase_phys_addr);
> + AE4DMA_WRITE_REG(&cmd_q->hwq_regs->qbase_hi, dma_addr_hi);
> +
> + return 0;
> +}
> +
> +static void
> +ae4dma_channel_dev_name(char *out, size_t outlen, const char *pci_name,
> + unsigned int ch)
> +{
> + snprintf(out, outlen, "%s-ch%u", pci_name, ch);
> +}
> +
> +/* Create a dmadev(dpdk DMA device) */
> +static int
> +ae4dma_dmadev_create(const char *name, struct rte_pci_device *dev, uint8_t qn)
> +{
> + static const struct rte_dma_dev_ops ae4dma_dmadev_ops = {
> + .dev_close = ae4dma_dev_close,
> + .dev_configure = ae4dma_dev_configure,
> + .dev_dump = ae4dma_dev_dump,
> + .dev_info_get = ae4dma_dev_info_get,
> + .dev_start = ae4dma_dev_start,
> + .dev_stop = ae4dma_dev_stop,
> + .stats_get = ae4dma_stats_get,
> + .stats_reset = ae4dma_stats_reset,
> + .vchan_status = ae4dma_vchan_status,
> + .vchan_setup = ae4dma_vchan_setup,
> + };
> +
> + struct rte_dma_dev *dmadev = NULL;
> + struct ae4dma_dmadev *ae4dma = NULL;
> + char hwq_dev_name[RTE_DEV_NAME_MAX_LEN];
> +
> + if (!name) {
> + AE4DMA_PMD_ERR("Invalid name of the device!");
> + return -EINVAL;
> + }
> + memset(hwq_dev_name, 0, sizeof(hwq_dev_name));
> + ae4dma_channel_dev_name(hwq_dev_name, sizeof(hwq_dev_name), name, qn);
> +
> + dmadev = rte_dma_pmd_allocate(hwq_dev_name, dev->device.numa_node,
> + sizeof(struct ae4dma_dmadev));
> + if (dmadev == NULL) {
> + AE4DMA_PMD_ERR("Unable to allocate dma device");
> + return -ENOMEM;
> + }
> + dmadev->device = &dev->device;
> + dmadev->fp_obj->dev_private = dmadev->data->dev_private;
> + dmadev->dev_ops = &ae4dma_dmadev_ops;
> +
> + dmadev->fp_obj->burst_capacity = ae4dma_burst_capacity;
> + dmadev->fp_obj->completed = ae4dma_completed;
> + dmadev->fp_obj->completed_status = ae4dma_completed_status;
> + dmadev->fp_obj->copy = ae4dma_enqueue_copy;
> + dmadev->fp_obj->submit = ae4dma_submit;
> + /* fill capability not advertised: leave fp_obj->fill as zero-initialised. */
> +
> + ae4dma = dmadev->data->dev_private;
> + ae4dma->dmadev = dmadev;
> + ae4dma->pci = dev;
> +
> + if (ae4dma_add_queue(ae4dma, qn, name) != 0)
> + goto init_error;
> + return 0;
> +
> +init_error:
> + AE4DMA_PMD_ERR("driver %s(): failed", __func__);
> + rte_dma_pmd_release(hwq_dev_name);
> + return -EFAULT;
ENOMEM or the value returned from ae4dma_add_queue.
> +}
> +
[snip]
> diff --git a/drivers/dma/ae4dma/ae4dma_hw_defs.h b/drivers/dma/ae4dma/ae4dma_hw_defs.h
> new file mode 100644
> index 0000000000..235819778e
> --- /dev/null
> +++ b/drivers/dma/ae4dma/ae4dma_hw_defs.h
> @@ -0,0 +1,164 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2024 Advanced Micro Devices, Inc. All rights reserved.
> + */
> +
> +#ifndef __AE4DMA_HW_DEFS_H__
> +#define __AE4DMA_HW_DEFS_H__
> +
> +#include <rte_bus_pci.h>
> +#include <rte_byteorder.h>
> +#include <rte_io.h>
> +#include <rte_pci.h>
> +#include <rte_memzone.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#define AE4DMA_BIT(nr) (1UL << (nr))
> +
> +#define AE4DMA_BITS_PER_LONG (__SIZEOF_LONG__ * 8)
> +#define AE4DMA_GENMASK(h, l) \
> + (((~0UL) << (l)) & (~0UL >> (AE4DMA_BITS_PER_LONG - 1 - (h))))
We have rte_bitops.h macros for bit manipulations, please reuse.
> +
> +/* ae4dma device details */
> +#define AMD_VENDOR_ID 0x1022
> +#define AE4DMA_DEVICE_ID 0x149b
> +#define AE4DMA_PCIE_BAR 0
> +
[snip]
> diff --git a/usertools/dpdk-devbind.py b/usertools/dpdk-devbind.py
> index 93f2383dff..ec6d6713b4 100755
> --- a/usertools/dpdk-devbind.py
> +++ b/usertools/dpdk-devbind.py
> @@ -86,6 +86,9 @@
> cn9k_ree = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f4',
> 'SVendor': None, 'SDevice': None}
>
> +amd_ae4dma = {'Class': '08', 'Vendor': '1022', 'Device': '149b',
> + 'SVendor': None, 'SDevice': None}
> +
Indent looks odd.
> virtio_blk = {'Class': '01', 'Vendor': "1af4", 'Device': '1001,1042',
> 'SVendor': None, 'SDevice': None}
>
> @@ -95,7 +98,7 @@
> network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
> baseband_devices = [acceleration_class]
> crypto_devices = [encryption_class, intel_processor_class]
> -dma_devices = [cnxk_dma, hisilicon_dma,
> +dma_devices = [amd_ae4dma, cnxk_dma, hisilicon_dma,
> intel_idxd_gnrd, intel_idxd_dmr, intel_idxd_spr,
> intel_ioat_bdw, intel_ioat_icx, intel_ioat_skx,
> odm_dma]
--
David Marchand
^ permalink raw reply
* Re: [PATCH v14 3/6] devtools: add compare-reviews.sh for multi-provider analysis
From: Thomas Monjalon @ 2026-05-21 14:17 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev, Aaron Conole
In-Reply-To: <20260414211012.951613-4-stephen@networkplumber.org>
14/04/2026 23:08, Stephen Hemminger:
> +#!/bin/bash
I'm not sure why bash is required for this script.
It could be just /bin/sh.
[...]
> + [[ -n "$ANTHROPIC_API_KEY" ]] && available="${available}anthropic,"
If you replace double brackets with single ones,
you should be close to a POSIX shell script.
> + [[ -z "${2:-}" || "$2" == -* ]] && error "$1 requires an argument"
and replace == with simple = in tests.
^ permalink raw reply
* Re: [PATCH v14 1/6] doc: add AGENTS.md for AI code review tools
From: Thomas Monjalon @ 2026-05-21 14:15 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev, Aaron Conole
In-Reply-To: <20260414211012.951613-2-stephen@networkplumber.org>
14/04/2026 23:08, Stephen Hemminger:
> Provide structured guidelines for AI tools reviewing DPDK
> patches. Focuses on correctness bug detection (resource leaks,
> use-after-free, race conditions), C coding style, forbidden
> tokens, API conventions, and severity classifications.
>
> Mechanical checks already handled by checkpatches.sh (SPDX
> format, commit message formatting, tag ordering) are excluded
> to avoid redundant and potentially contradictory findings.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> Acked-by: Aaron Conole <aconole@redhat.com>
> ---
> AGENTS.md | 2170 +++++++++++++++++++++++++++++++++++++++++++++++++++++
There are big chances that we are going to update this file in future.
Please help easy updates by following this guideline from
doc/guides/contributing/documentation.rst
* Each sentence should start on a new line.
Multiple sentences, which are not separated by a blank line,
are joined automatically into paragraphs.
* Wrap sentences at punctuation points, for example, at a comma.
If no punctuation, put the newline at a logical point in the sentence,
for example, at the end of a clause before an "and" or "but".
^ permalink raw reply
* Re: [PATCH v14 0/6] Add AGENTS.md and scripts for AI code review
From: Thomas Monjalon @ 2026-05-21 14:12 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev, Aaron Conole
In-Reply-To: <20260414211012.951613-1-stephen@networkplumber.org>
14/04/2026 23:08, Stephen Hemminger:
> devtools/analyze-patch.py | 1509 ++++++++++++++++
> devtools/compare-reviews.sh | 263 +++
> devtools/review-doc.py | 1184 +++++++++++++
Should we move these scripts in a sub-directory devtools/ai/ ?
Still about the naming,
compare-reviews.sh is for analyze-patch.py,
so I suggest this consistent naming:
review-patch.py
compare-patch-reviews.sh
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox