Re: [RFC PATCH 04/12] rcu_txn: Add CONFIG_DEBUG_RCU_TXN

From: Puranjay Mohan

Date: Fri Oct 23 2026 - 14:23:42 EST


On Fri, Oct 16, 2026 at 09:33:11AM -0400, Mathieu Desnoyers wrote:
[...]

> @@ -261,6 +291,7 @@ static inline bool rcu_txn_is_proxy(const void *v, unsigned long tag)
> */
> static inline struct rcu_txn_record *__rcu_txn_untag(void *v, unsigned long tag)
> {
> + RCU_TXN_WARN_ON(!rcu_txn_is_proxy(v, tag));
> return (struct rcu_txn_record *)((unsigned long)v - tag);
> }

This can never fire: the only caller, rcu_txn_resolve(), has just
checked rcu_txn_is_proxy(v, tag). I'd drop it.

[...]

> + RCU_TXN_WARN_ON(__rcu_txn_holds_proxy(old_ptr, tag));
> + RCU_TXN_WARN_ON(__rcu_txn_holds_proxy(new_ptr, tag));

[...]

> @@ -715,6 +893,9 @@ static inline bool __rcu_txn_chain(struct rcu_txn *t, void __rcu **slot,
> r = __rcu_txn_find_ryw(t, slot);
> if (!r)
> return rcu_txn_record(t, slot, old_ptr, new_ptr, tag);
> + RCU_TXN_WARN_ON(r->tag != tag);
> + /* The caller must have read its own write. */
> + RCU_TXN_WARN_ON(r->ptr[1] != old_ptr);
> r->ptr[1] = new_ptr; /* committed old preserved */
> return true;
> }

The append path checks new_ptr for the tag, but the chain path doesn't.
With CONFIG_DEBUG_RCU_TXN, chaining a tagged value onto an
already-recorded slot of a non-disjoint handle and committing gives no
warning, and leaves the tagged value in the slot, where every later
reader treats it as a proxy. Passing the same slot, value and tag
through the append path warns once:

# rcu_txn_test_exp_chain_tagged: append path: 1 warnings
# rcu_txn_test_exp_chain_tagged: chain path + commit: 0 warnings, slot now 0000000000000301 (tagged: 1)

The same check in the chain path catches it:

--- a/include/linux/rcu_txn.h
+++ b/include/linux/rcu_txn.h
@@ -905,6 +905,7 @@ static inline bool __rcu_txn_chain(struct rcu_txn *t, void __rcu **slot,
RCU_TXN_WARN_ON(r->tag != tag);
/* The caller must have read its own write. */
RCU_TXN_WARN_ON(r->ptr[1] != old_ptr);
+ RCU_TXN_WARN_ON(__rcu_txn_holds_proxy(new_ptr, tag));
r->ptr[1] = new_ptr; /* committed old preserved */
return true;
}

Thanks,
Puranjay