Skip to content

Commit 284fd53

Browse files
Ming Leikawasaki
authored andcommitted
ublk: fix maple tree lockdep warning and unpin under spinlock
Fix two issues in the shmem buffer maple tree usage: 1) ublk_buf_cleanup() iterates the tree with mas_for_each() without holding rcu_read_lock or mas_lock, triggering a lockdep splat on CONFIG_PROVE_RCU kernels. Add mas_lock/unlock around the iteration. 2) __ublk_ctrl_unreg_buf() calls unpin_user_pages() under mas_lock (a spinlock). unpin_user_pages can be expensive for large buffers and may take additional locks if folio refcount drops to zero. Restructure to drop mas_lock before unpinning, re-acquiring it to continue iteration. Both functions now use the same pattern: erase under lock, drop lock, unpin and free, re-lock to continue. Extract ublk_unpin_range_pages() helper to share the page unpinning loop. Reported-by: Jens Axboe <[email protected]> Closes: https://lore.kernel.org/linux-block/[email protected]/ Cc: Liam R. Howlett <[email protected]> Signed-off-by: Ming Lei <[email protected]> Tested-by: Shin'ichiro Kawasaki <[email protected]>
1 parent 3b54e52 commit 284fd53

1 file changed

Lines changed: 39 additions & 24 deletions

File tree

drivers/block/ublk_drv.c

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5413,16 +5413,39 @@ static int ublk_ctrl_reg_buf(struct ublk_device *ub,
54135413
return ret;
54145414
}
54155415

5416+
static void ublk_unpin_range_pages(unsigned long base_pfn,
5417+
unsigned long nr_pages)
5418+
{
5419+
#define UBLK_UNPIN_BATCH 32
5420+
struct page *pages[UBLK_UNPIN_BATCH];
5421+
unsigned long off;
5422+
5423+
for (off = 0; off < nr_pages; ) {
5424+
unsigned int batch = min_t(unsigned long,
5425+
nr_pages - off, UBLK_UNPIN_BATCH);
5426+
unsigned int j;
5427+
5428+
for (j = 0; j < batch; j++)
5429+
pages[j] = pfn_to_page(base_pfn + off + j);
5430+
unpin_user_pages(pages, batch);
5431+
off += batch;
5432+
}
5433+
}
5434+
5435+
/*
5436+
* Drop mas_lock during iteration to avoid unpinning pages under spinlock.
5437+
* Safe because callers hold ub->mutex (via ublk_lock_buf_tree), preventing
5438+
* concurrent tree modifications.
5439+
*/
54165440
static int __ublk_ctrl_unreg_buf(struct ublk_device *ub, int buf_index)
54175441
{
54185442
MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
54195443
struct ublk_buf_range *range;
5420-
struct page *pages[32];
54215444
int ret = -ENOENT;
54225445

54235446
mas_lock(&mas);
54245447
mas_for_each(&mas, range, ULONG_MAX) {
5425-
unsigned long base, nr, off;
5448+
unsigned long base, nr;
54265449

54275450
if (range->buf_index != buf_index)
54285451
continue;
@@ -5431,18 +5454,12 @@ static int __ublk_ctrl_unreg_buf(struct ublk_device *ub, int buf_index)
54315454
base = mas.index;
54325455
nr = mas.last - base + 1;
54335456
mas_erase(&mas);
5457+
mas_unlock(&mas);
54345458

5435-
for (off = 0; off < nr; ) {
5436-
unsigned int batch = min_t(unsigned long,
5437-
nr - off, 32);
5438-
unsigned int j;
5439-
5440-
for (j = 0; j < batch; j++)
5441-
pages[j] = pfn_to_page(base + off + j);
5442-
unpin_user_pages(pages, batch);
5443-
off += batch;
5444-
}
5459+
ublk_unpin_range_pages(base, nr);
54455460
kfree(range);
5461+
5462+
mas_lock(&mas);
54465463
}
54475464
mas_unlock(&mas);
54485465

@@ -5472,29 +5489,27 @@ static int ublk_ctrl_unreg_buf(struct ublk_device *ub,
54725489
return ret;
54735490
}
54745491

5492+
/*
5493+
* Drop mas_lock during iteration to avoid unpinning pages under spinlock.
5494+
* Safe because this is called from device release with exclusive access.
5495+
*/
54755496
static void ublk_buf_cleanup(struct ublk_device *ub)
54765497
{
54775498
MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
54785499
struct ublk_buf_range *range;
5479-
struct page *pages[32];
54805500

5501+
mas_lock(&mas);
54815502
mas_for_each(&mas, range, ULONG_MAX) {
54825503
unsigned long base = mas.index;
54835504
unsigned long nr = mas.last - base + 1;
5484-
unsigned long off;
54855505

5486-
for (off = 0; off < nr; ) {
5487-
unsigned int batch = min_t(unsigned long,
5488-
nr - off, 32);
5489-
unsigned int j;
5490-
5491-
for (j = 0; j < batch; j++)
5492-
pages[j] = pfn_to_page(base + off + j);
5493-
unpin_user_pages(pages, batch);
5494-
off += batch;
5495-
}
5506+
mas_erase(&mas);
5507+
mas_unlock(&mas);
5508+
ublk_unpin_range_pages(base, nr);
54965509
kfree(range);
5510+
mas_lock(&mas);
54975511
}
5512+
mas_unlock(&mas);
54985513
mtree_destroy(&ub->buf_tree);
54995514
ida_destroy(&ub->buf_ida);
55005515
}

0 commit comments

Comments
 (0)