The tx ring producer uses two cursors: - tbr->windex which is driver private. It lets producers reserve space concurrently without a lock - vbr->windex is the host visible commit cursor and has to advance in reservation order so the host never sees it point past unwritten data.
Convert the reservation CAS on tbr->windex to a weak compare exchange, and replace the smp_wmb plus CAS spin that published the commit with rte_wait_until_equal_32 on the previous producer followed by a release store. The release ordering on that store covers what the barrier did. The wait on the previous producer uses acquire ordering, not relaxed. A release store only carries the writes of the storing thread and anything it has a happens-before edge to. Producers publish in reservation order, so when this thread stores its own windex the host may then consume the data of every earlier producer as well. The acquire on the wait pairs with the earlier producer's release store, putting that producer's data writes into this thread's happens-before set so they are carried by this store too. vbr->windex stays volatile uint32_t in the packed bufring struct. The host is not a C11 thread. The atomic qualifier is applied by cast at the single publishing store; the (uintptr_t) launder there avoids a bogus misaligned atomic warning from the packed attribute, since windex sits at offset 0 of a page aligned struct. Signed-off-by: Stephen Hemminger <[email protected]> --- drivers/bus/vmbus/private.h | 2 +- drivers/bus/vmbus/vmbus_bufring.c | 40 +++++++++++++++++-------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/bus/vmbus/private.h b/drivers/bus/vmbus/private.h index 6efac86b77..6b7782724f 100644 --- a/drivers/bus/vmbus/private.h +++ b/drivers/bus/vmbus/private.h @@ -25,7 +25,7 @@ extern int vmbus_logtype_bus; struct vmbus_br { struct vmbus_bufring *vbr; uint32_t dsize; - uint32_t windex; /* next available location */ + RTE_ATOMIC(uint32_t) windex; /* next available location */ }; #define UIO_NAME_MAX 64 diff --git a/drivers/bus/vmbus/vmbus_bufring.c b/drivers/bus/vmbus/vmbus_bufring.c index fcb97287dc..dcd66390a3 100644 --- a/drivers/bus/vmbus/vmbus_bufring.c +++ b/drivers/bus/vmbus/vmbus_bufring.c @@ -15,7 +15,7 @@ #include <rte_tailq.h> #include <rte_log.h> #include <rte_malloc.h> -#include <rte_atomic.h> +#include <rte_stdatomic.h> #include <rte_memory.h> #include <rte_pause.h> #include <rte_bus_vmbus.h> @@ -121,17 +121,13 @@ vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen, total += iov[i].iov_len; total += sizeof(save_windex); + /* Get current free location */ + old_windex = rte_atomic_load_explicit(&tbr->windex, + rte_memory_order_relaxed); + /* Reserve space in ring */ do { - uint32_t avail; - - /* Get current free location */ - old_windex = tbr->windex; - - /* Prevent compiler reordering this with calculation */ - rte_compiler_barrier(); - - avail = vmbus_br_availwrite(tbr, old_windex); + uint32_t avail = vmbus_br_availwrite(tbr, old_windex); /* If not enough space in ring, then tell caller. */ if (avail <= total) @@ -139,8 +135,12 @@ vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen, next_windex = vmbus_br_idxinc(old_windex, total, ring_size); - /* Atomic update of next write_index for other threads */ - } while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex)); + /* Atomic update of next write_index for other threads + * Can use weak since easy to recompute and retry. + */ + } while (!rte_atomic_compare_exchange_weak_explicit( + &tbr->windex, &old_windex, next_windex, + rte_memory_order_acquire, rte_memory_order_relaxed)); /* Space from old..new is now reserved */ windex = old_windex; @@ -157,12 +157,15 @@ vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen, /* The region reserved should match region used */ RTE_ASSERT(windex == next_windex); - /* Ensure that data is available before updating host index */ - rte_smp_wmb(); + /* Wait for previous producer to publish their windex update */ + rte_wait_until_equal_32(&vbr->windex, old_windex, rte_memory_order_acquire); - /* Checkin for our reservation. wait for our turn to update host */ - while (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex)) - rte_pause(); + /* Publish our windex update; prior data writes ordered via release. + * windex is 4-byte aligned in practice (struct is page-aligned, windex + * at offset 0); cast launders the packed-struct alignment-1 attribute. + */ + rte_atomic_store_explicit((volatile __rte_atomic uint32_t *)(uintptr_t)&vbr->windex, + next_windex, rte_memory_order_release); /* If host had read all data before this, then need to signal */ *need_sig |= vmbus_txbr_need_signal(vbr, old_windex); @@ -224,7 +227,8 @@ vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip) return -EAGAIN; /* Record where host was when we started read (for debug) */ - rbr->windex = rbr->vbr->windex; + rte_atomic_store_explicit(&rbr->windex, rbr->vbr->windex, + rte_memory_order_relaxed); /* * Copy channel packet from RX bufring. -- 2.53.0

