On Tue, 8 Sep 2026 03:55:12 +0000
Mukul Katiyar <[email protected]> wrote:

> Hi all,
> 
> Sharing a userspace reader-writer lock that has been running in production in 
> a DPDK-based network function for several years and wanted to check if there 
> would be interest in contributing it to DPDK as rte_eprwlock.
> 
> The Enhanced Passive Reader-Writer (EPRW) lock eliminates atomic operations 
> on the reader fast path, giving near-flat per-reader performance as core 
> count grows. It is compatible with poll-mode lcore discipline — no heartbeat 
> or periodic refresh required from registered threads.
> 
> Details, correctness proof, memory ordering analysis (x86-TSO and ARM), and 
> performance evaluation against rte_rwlock and pthread_rwlock_t are in a 
> preprint at:
> https://zenodo.org/records/22636501
> 
> Would this be a useful addition to DPDK?
> 
> Regards,
> Mukul Katiyar
> Versa Networks
> 

As an exercise, also point Fable AI to do analysis of the paper versus current 
DPDK code.
Surprisingly AI is relatively good at understanding locking; probably because 
it has been
trained on a huge data set of academic papers.

Short version: this is a write-up of Versa fixing a bug in their own
userspace port of Liu's PRW lock. It is not a critique of rte_rwlock
and does not cite any DPDK primitive (DPDK is cited as "Intel
Corporation 2024", which tells you how closely they looked). "Outdated
DPDK" is too generous; there is no DPDK survey at all. The DPDK
relevance is only the deployment context.

Analysis:

1. What the algorithm actually is. Once the writer scan checks `RP[i]
== Present && TV[i] != GV`, the version counter only distinguishes
"spinning at boundary" from "inside CS". A three-state per-thread flag
(ABSENT/WAITING/ACTIVE) does the same job with no GV, no unlock
broadcast, no rollover. The only thing TV buys is a WAITING to ACTIVE
transition without a store+fence, because the writer's GV++ invalidates
the match for it. That is the contended path, so it does not affect
throughput. Strip that and you have a per-thread-flag rwlock: Hsieh and
Weihl 1992, Linux 2.4 brlock, Dice/Shavit read indicators,
percpu_rw_semaphore slow path. The "heartbeat" was self-inflicted: they
dropped the kernel IPI and did not add an offline state. `RP = Absent`
is `rte_rcu_qsbr_thread_offline()`.

2. The premise contradicts DPDK practice. Section 3 says per-lcore
periodic reporting is incompatible with poll-mode discipline.
`rte_rcu_qsbr_quiescent()` per loop iteration is exactly how lib/rcu is
used, with online/offline for idle threads. Section 10 admits the QSBR
analogy but not that DPDK ships it. liburcu (Desnoyers et al., TPDS
2012) is the canonical treatment of replacing kernel IPI/quiescence in
userspace and is not cited either. RCU also gives readers zero wait and
real reclamation; EPRW writers still spin on every in-CS reader.

3. No comparison, no numbers. Missing: rte_rwlock (4 bytes, WAIT bit so
writers cannot starve), rte_pflock (bounded wait both sides),
rte_seqlock/seqcount, rte_rcu_qsbr, rte_mcslock, rte_ticketlock.
Microbenchmarks "left for future work". EPRW has no fairness: a reader
spinning on W must catch a window between one writer's `W = Absent` and
the next writer's CAS, and back-to-back writers skip it forever since
its TV matches. That is the case pflock was added for.

4. Concrete defects:
   - Lemma 8.1 is wrong. `Try_Write_Lock` increments GV and on failure releases 
W without the broadcast. A caller retrying try_wlock against a reader stuck in 
its CS (preempted control thread, slow path) gets 65536 failures, GV wraps, 
`TV[i] == GV`, false boundary match, exclusion violated. The 16-bit variant is 
unsafe as published. Trivial fix, but the proof did not cover its own try path.
   - ARMv8 `Read_Lock`: the exit load of W has no acquire. The DMB after the RP 
store does not order that load against later CS loads, so a reader can see 
pre-write data. Same in `Try_Read_Lock`. Section 6 walks every barrier site and 
misses this one. Fine on TSO.
   - Plain-C data races: readers load GV while the writer does a non-atomic 
increment; TV[i] is stored by thread i and by the broadcasting writer. Works on 
hardware with aligned words, UB in C11. Any DPDK version would need 
rte_stdatomic relaxed ops anyway.
   - Compact mode packs 3-byte slots: 32 threads in 3 cache lines, so every 
read lock/unlock is a store to a shared line. That reintroduces the coherence 
traffic PRW exists to avoid. The 64-byte alignment in the original is the whole 
point. No measurement.
   - Write_Unlock broadcast invalidates N reader-owned lines per write on top 
of the scan. Fine for read-mostly, but it is why per-lcore-slot locks do not 
fit "hundreds of locks", which is their own compact-mode motivation.
   - `TV[tid] = GV` in the Write_Lock contention loop is dead: a plain 
contender has RP Absent, so the winner's scan skips it regardless. Only Upgrade 
needs it.
   - "Non-atomic upgrade" (return 1) is a read unlock followed by a write lock; 
data can change in between. The name invites misuse.
   - Reader fast path uses MFENCE. rte_smp_mb() uses `lock addl` on x86 for a 
reason; xchg for the RP store folds store and barrier.

Reply via email to