Re: [RFC PATCH 10/12] rcu_txn: Test a commit from atomic context

From: Puranjay Mohan

Date: Fri Oct 23 2026 - 14:24:38 EST


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

> The test is only meaningful on a PREEMPT_RT kernel with lockdep,
> PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP. It passes there, and
> fails as expected when a spinlock_t is taken under the bit spinlock.
>
> The last two cases release a descriptor that installed no proxy, which
> has to be kfree_rcu() on PREEMPT_RT. They would pass with a kfree() as
> well: a kfree() under a bit spinlock on PREEMPT_RT was reported by
> neither lockdep nor the might_sleep() checks when tried.

This matters on !PREEMPT_RT too: there, a one-record commit or a cancel
under a raw spinlock calls kfree(), which lockdep can flag (see my reply
to patch 1). The test can't catch that as written.

[...]

> + /* One record. */
> + rcu_txn_init(&txn, GFP_KERNEL);
> + KUNIT_ASSERT_TRUE(test, rcu_txn_reserve(&txn, 2));
> + hlist_bl_lock(&bucket);
> + rcu_txn_record(&txn, &s1, T(20), T(40), TAG);
> + err = rcu_txn_commit(&txn);
> + hlist_bl_unlock(&bucket);

On !PREEMPT_RT, this case and the cancel after it are the two that reach
kfree(), and they only run under the bit spinlock, which lockdep doesn't
track. The raw spinlock case only commits two records, which go through
kfree_rcu(). Even under a raw spinlock, a single call rarely trips it,
because lockdep only sees barn->lock once the per-CPU sheaf is full.
Running the one-record and cancel cases under raw_spin_lock_irqsave() in
a loop would catch it. That is probably also why the kfree() you tried
on PREEMPT_RT went unreported, though I haven't checked RT.

[...]

> +/* Did the kernel report something since @tainted and @locks were sampled? */
> +static void expect_no_report(struct kunit *test, bool tainted, int locks)
> +{
> + KUNIT_EXPECT_EQ(test, test_taint(TAINT_WARN), tainted);
> + KUNIT_EXPECT_EQ(test, debug_locks, locks);
> +}

These compare against the state at the start of the test, so if
lockdep is already off, or TAINT_WARN already set, the matching check
can't fail. I ran into that: once one of my raw-spinlock cases
splatted, the two bulk cases after it passed even though each splats
when run alone. A kunit_skip() when PROVE_RAW_LOCK_NESTING is off, or
debug_locks is already 0, or TAINT_WARN is already set, would make that
visible.

Thanks,
Puranjay