From: Wathsala Vithanage <wathsala.vithanage@arm.com>
To: stable@dpdk.org
Cc: ola.liljedahlat@arm.com, honnappa.nagarahalli@arm.com,
dhruv.tripathi@arm.com, konstantin.ananyev@huawei.com,
wathsala.vithanage@arm.com
Subject: [PATCH 24.11 2/3] ring: establish a safe partial order in hts-ring
Date: Tue, 2 Dec 2025 20:39:27 +0000 [thread overview]
Message-ID: <20251202203936.511315-2-wathsala.vithanage@arm.com> (raw)
In-Reply-To: <20251202203936.511315-1-wathsala.vithanage@arm.com>
[ upstream commit 66d5f962780694f6aebf000907fc3ce7a72584f9 ]
Enforce a safe partial order by making the CAS and the preceding head
load use release and acquire semantics. This creates a pairwise
happens-before relationship between threads of the same role.
Combine the two load-acquire operations of ht.raw, which were previously
split across the two paths of a conditional branch, into
__rte_ring_hts_head_wait. This simplifies the branching logic and makes
the synchronization behavior easier to understand.
Add comments to explain synchronizes with edges in detail.
Signed-off-by: Wathsala Vithanage <wathsala.vithanage at arm.com>
Signed-off-by: Ola Liljedahl <ola.liljedahl at arm.com>
---
lib/ring/rte_ring_hts_elem_pvt.h | 96 +++++++++++++++++++++++---------
1 file changed, 71 insertions(+), 25 deletions(-)
diff --git a/lib/ring/rte_ring_hts_elem_pvt.h b/lib/ring/rte_ring_hts_elem_pvt.h
index 91f5eeccb9..3c27197b6b 100644
--- a/lib/ring/rte_ring_hts_elem_pvt.h
+++ b/lib/ring/rte_ring_hts_elem_pvt.h
@@ -32,22 +32,40 @@ __rte_ring_hts_update_tail(struct rte_ring_hts_headtail *ht, uint32_t old_tail,
RTE_SET_USED(enqueue);
tail = old_tail + num;
+
+ /*
+ * R0: Release the tail update. Establishes a synchronization edge with
+ * the load-acquire at A1/A3. This release ensures that all updates to
+ * *ht and the ring array made by this thread become visible to the
+ * opposing thread once the tail value written here is observed.
+ */
rte_atomic_store_explicit(&ht->ht.pos.tail, tail, rte_memory_order_release);
}
/**
- * @internal waits till tail will become equal to head.
- * Means no writer/reader is active for that ring.
- * Suppose to work as serialization point.
+ * @internal
+ * Waits until the tail becomes equal to the head.
+ * This indicates that another thread has finished its transaction, and there
+ * is a chance that we could be the next writer or reader in line.
+ *
+ * Returns ht.raw at this point. The value may be imprecise, since another
+ * thread might change the state before we observe ht.raw, but that does not
+ * matter. The function __rte_ring_hts_move_head() can detect and recall this
+ * function when it reaches the linearization point (CAS).
*/
-static __rte_always_inline void
+static __rte_always_inline union __rte_ring_hts_pos
__rte_ring_hts_head_wait(const struct rte_ring_hts_headtail *ht,
- union __rte_ring_hts_pos *p)
+ int memorder)
{
- while (p->pos.head != p->pos.tail) {
+ union __rte_ring_hts_pos p;
+ p.raw = rte_atomic_load_explicit(&ht->ht.raw, memorder);
+
+ while (p.pos.head != p.pos.tail) {
rte_pause();
- p->raw = rte_atomic_load_explicit(&ht->ht.raw, rte_memory_order_acquire);
+ p.raw = rte_atomic_load_explicit(&ht->ht.raw, memorder);
}
+
+ return p;
}
/**
@@ -58,13 +76,11 @@ __rte_ring_hts_move_prod_head(struct rte_ring *r, unsigned int num,
enum rte_ring_queue_behavior behavior, uint32_t *old_head,
uint32_t *free_entries)
{
- uint32_t n;
+ uint32_t n, cons_tail;
union __rte_ring_hts_pos np, op;
const uint32_t capacity = r->capacity;
- op.raw = rte_atomic_load_explicit(&r->hts_prod.ht.raw, rte_memory_order_acquire);
-
do {
/* Reset n to the initial burst count */
n = num;
@@ -74,7 +90,20 @@ __rte_ring_hts_move_prod_head(struct rte_ring *r, unsigned int num,
* make sure that we read prod head/tail *before*
* reading cons tail.
*/
- __rte_ring_hts_head_wait(&r->hts_prod, &op);
+ /*
+ * A0: Synchronizes with the CAS at R1.
+ * Establishes a happens-before relationship with a thread of the same
+ * type that released the ht.raw, ensuring this thread observes all of
+ * its memory effects needed to maintain a safe partial order.
+ */
+ op = __rte_ring_hts_head_wait(&r->hts_prod, rte_memory_order_acquire);
+
+ /*
+ * A1: Establish a synchronizes-with edge using a store-release at R0.
+ * This ensures that all memory effects from the preceding opposing
+ * thread are observed.
+ */
+ cons_tail = rte_atomic_load_explicit(&r->cons.tail, rte_memory_order_acquire);
/*
* The subtraction is done between two unsigned 32bits value
@@ -82,7 +111,7 @@ __rte_ring_hts_move_prod_head(struct rte_ring *r, unsigned int num,
* *old_head > cons_tail). So 'free_entries' is always between 0
* and capacity (which is < size).
*/
- *free_entries = capacity + r->cons.tail - op.pos.head;
+ *free_entries = capacity + cons_tail - op.pos.head;
/* check that we have enough room in ring */
if (unlikely(n > *free_entries))
@@ -96,13 +125,16 @@ __rte_ring_hts_move_prod_head(struct rte_ring *r, unsigned int num,
np.pos.head = op.pos.head + n;
/*
- * this CAS(ACQUIRE, ACQUIRE) serves as a hoist barrier to prevent:
- * - OOO reads of cons tail value
- * - OOO copy of elems from the ring
+ * R1: Establishes a synchronizes-with edge with the load-acquire
+ * of ht.raw at A0. This makes sure that the store-release to the
+ * tail by this thread, if it was of the opposite type, becomes
+ * visible to another thread of the current type. That thread will
+ * then observe the updates in the same order, keeping a safe
+ * partial order.
*/
} while (rte_atomic_compare_exchange_strong_explicit(&r->hts_prod.ht.raw,
(uint64_t *)(uintptr_t)&op.raw, np.raw,
- rte_memory_order_acquire, rte_memory_order_acquire) == 0);
+ rte_memory_order_release, rte_memory_order_relaxed) == 0);
*old_head = op.pos.head;
return n;
@@ -116,11 +148,9 @@ __rte_ring_hts_move_cons_head(struct rte_ring *r, unsigned int num,
enum rte_ring_queue_behavior behavior, uint32_t *old_head,
uint32_t *entries)
{
- uint32_t n;
+ uint32_t n, prod_tail;
union __rte_ring_hts_pos np, op;
- op.raw = rte_atomic_load_explicit(&r->hts_cons.ht.raw, rte_memory_order_acquire);
-
/* move cons.head atomically */
do {
/* Restore n as it may change every loop */
@@ -131,14 +161,27 @@ __rte_ring_hts_move_cons_head(struct rte_ring *r, unsigned int num,
* make sure that we read cons head/tail *before*
* reading prod tail.
*/
- __rte_ring_hts_head_wait(&r->hts_cons, &op);
+ /*
+ * A2: Synchronizes with the CAS at R2.
+ * Establishes a happens-before relationship with a thread of the same
+ * type that released the ht.raw, ensuring this thread observes all of
+ * its memory effects needed to maintain a safe partial order.
+ */
+ op = __rte_ring_hts_head_wait(&r->hts_cons, rte_memory_order_acquire);
+
+ /*
+ * A3: Establish a synchronizes-with edge using a store-release at R0.
+ * This ensures that all memory effects from the preceding opposing
+ * thread are observed.
+ */
+ prod_tail = rte_atomic_load_explicit(&r->prod.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
* cons_head > prod_tail). So 'entries' is always between 0
* and size(ring)-1.
*/
- *entries = r->prod.tail - op.pos.head;
+ *entries = prod_tail - op.pos.head;
/* Set the actual entries for dequeue */
if (n > *entries)
@@ -151,13 +194,16 @@ __rte_ring_hts_move_cons_head(struct rte_ring *r, unsigned int num,
np.pos.head = op.pos.head + n;
/*
- * this CAS(ACQUIRE, ACQUIRE) serves as a hoist barrier to prevent:
- * - OOO reads of prod tail value
- * - OOO copy of elems from the ring
+ * R2: Establishes a synchronizes-with edge with the load-acquire
+ * of ht.raw at A2. This makes sure that the store-release to the
+ * tail by this thread, if it was of the opposite type, becomes
+ * visible to another thread of the current type. That thread will
+ * then observe the updates in the same order, keeping a safe
+ * partial order.
*/
} while (rte_atomic_compare_exchange_strong_explicit(&r->hts_cons.ht.raw,
(uint64_t *)(uintptr_t)&op.raw, np.raw,
- rte_memory_order_acquire, rte_memory_order_acquire) == 0);
+ rte_memory_order_release, rte_memory_order_relaxed) == 0);
*old_head = op.pos.head;
return n;
--
2.43.0
next prev parent reply other threads:[~2025-12-02 20:40 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-02 20:39 [PATCH 24.11 1/3] ring: establish safe partial order in default mode Wathsala Vithanage
2025-12-02 20:39 ` Wathsala Vithanage [this message]
2025-12-02 20:39 ` [PATCH 24.11 3/3] ring: establish safe partial order in RTS mode Wathsala Vithanage
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251202203936.511315-2-wathsala.vithanage@arm.com \
--to=wathsala.vithanage@arm.com \
--cc=dhruv.tripathi@arm.com \
--cc=honnappa.nagarahalli@arm.com \
--cc=konstantin.ananyev@huawei.com \
--cc=ola.liljedahlat@arm.com \
--cc=stable@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).