Re: [RFC PATCH 00/12] RCU pseudo-transactions

From: Puranjay Mohan

Date: Fri Oct 23 2026 - 14:21:16 EST


Hi Mathieu,

On Fri, Oct 16, 2026 at 09:33:07AM -0400, Mathieu Desnoyers wrote:
> I'd specifically like feedback on:
>
> - Where this should live (kernel/rcu/, lib/?),
>
> - Naming and API shape (descriptor + records),
>
> - Whether the Bloom filter threshold should be a config knob,
>
> - Subsystems where this could apply.

I went through the series and ran the KUnit suite on arm64 (QEMU/KVM)
with PROVE_LOCKING, PROVE_RAW_LOCK_NESTING and DEBUG_RCU_TXN. All 59
cases pass, and herd7 agrees with all 21 litmus tests. The
install/flip/settle ordering and the descriptor lifetime look right to
me.

I found two bugs, details in the replies to the patches:

- patch 1: a one-record commit or a cancel frees the descriptor with
kfree() on !PREEMPT_RT, which lockdep can flag as an invalid wait
context under a raw spinlock;

- patch 3: rcu_txn_load() on a consumed handle with an armed Bloom
filter dereferences a NULL descriptor.

Moves in patch 6 can also make a reader skip or revisit nodes, or loop
in another list instead of terminating if the node moves there. In
patch 5, deleting a node twice can silently corrupt the bucket.

On your questions:

Location: either works for me, but CONFIG_RCU_TXN is in lib/Kconfig and
CONFIG_DEBUG_RCU_TXN in kernel/rcu/Kconfig.debug; I'd keep them
together.

Naming: no strong opinion.

Bloom filter knob: I wouldn't add one. Outside the KUnit tests, the
only transactions that reach eight records are the mutex sample's
evicting insertions, which reserve 32 records anyway. Meanwhile the
room reserved for the filter pushes descriptors of 8 to 10 records from
512 to 1024 bytes. I'd drop patch 3 until something needs it.

API shape: every rcu_txn_list add, delete and replace allocates a
descriptor and frees it with kfree_rcu(), so all of them can fail with
-ENOMEM, delete included. That is hard to use on teardown paths, and
reserving first only moves the failure. Since a descriptor can be
reused a grace period after its commit, have you considered a small
cache of them, tagged with start_poll_synchronize_rcu() and checked with
poll_state_synchronize_rcu()? Separately, a one-record transaction
never installs a proxy but still allocates a descriptor, 256 bytes
unless reserved smaller; keeping the first record inline in struct
rcu_txn would avoid that.

Subsystems: in dcache the hash buckets are hlist_bl, with bit 0 of the
head as the lock, and d_unhashed() tests for a NULL pprev.
rcu_txn_hlist hard-codes bit 0 as its tag and has no unhashed state, so
I'm curious how your userspace port deals with both. Also, the only
in-tree user of list_bidir_prev_rcu(), kernel/nstree.c, walks either way
but never forward and then back in one walk, so a user that needs that
guarantee would help make the case for patch 6.

I can send my KUnit reproducers as patches if they're useful.

Thanks,
Puranjay