Re: RCU warning off ublk_buf_cleanup() -> mas_for_each()

Jens Axboe
2026-04-21T18:04:07Z

On 4/21/26 11:47 AM, Jens Axboe wrote:
> Hi Ming,
>
> Ran into the below running tests on the current tree:
>
> =============================
> WARNING: suspicious RCU usage
> 7.0.0+ #16 Tainted: G N
> -----------------------------
> lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!

FWIW, here's what claude spits out on the maple tree usage for
ublk. Simply passing it on...

Issue 1: ublk_buf_cleanup — missing RCU/lock for mas_for_each (line 5489)

ublk_buf_cleanup iterates the tree using mas_for_each without holding either
rcu_read_lock or mas_lock. The mas_find() documentation explicitly states: "Must
hold rcu_read_lock or the write lock." Internally, mas_find → mas_next_slot →
mt_slot → rcu_dereference_check(slots[offset], mt_locked(mt)). With neither RCU nor
the tree lock held, this will fire a lockdep splat on CONFIG_PROVE_RCU=y kernels.

While functionally safe (exclusive access during device release), the API contract
is violated. Fix: wrap the iteration in rcu_read_lock/unlock, or use
mas_lock/unlock.

Issue 2: __ublk_ctrl_unreg_buf — unpin_user_pages under spinlock (line 5431-5455)

This function holds mas_lock (a spinlock — atomic context) while unpinning
potentially many pages in a loop. For a large registered buffer with many disjoint
PFN ranges, this holds the spinlock for an extended period. unpin_user_pages →
gup_put_folio → folio_put could also grab additional locks if the folio's refcount
drops to zero.

A cleaner pattern would be to collect entries, drop mas_lock, then unpin. Compare
with ublk_buf_cleanup which does the same unpinning work outside any lock — the
asymmetry is notable.

Issue 3: ublk_buf_cleanup — kfree without mas_erase (line 5504)

The cleanup function does kfree(range) during iteration without first calling
mas_erase(). This leaves dangling pointers in the tree nodes until mtree_destroy is
called on line 5506. Not a bug (no concurrent access, mtree_destroy doesn't
dereference stored entries), but it's inconsistent with ublk_buf_erase_ranges and
__ublk_ctrl_unreg_buf which both properly erase before freeing.

--
Jens Axboe