Skip to content

Commit 8cb251c

Browse files
ming1kawasaki
authored andcommitted
ublk: avoid unpinning pages under maple tree spinlock
ublk_shmem_remove_ranges() calls unpin_user_pages() while holding the maple tree spinlock (mas_lock). Although unpin_user_pages() is safe in atomic context, holding the spinlock across potentially many page unpinning operations is not ideal. Split into __ublk_shmem_remove_ranges() which erases up to 64 ranges under mas_lock, collecting base_pfn and nr_pages into a temporary xarray. Then drop the lock and unpin pages outside spinlock context. ublk_shmem_remove_ranges() loops until all matching ranges are processed. Signed-off-by: Ming Lei <[email protected]>
1 parent 987505e commit 8cb251c

1 file changed

Lines changed: 46 additions & 10 deletions

File tree

drivers/block/ublk_drv.c

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5433,32 +5433,68 @@ static void ublk_unpin_range_pages(unsigned long base_pfn,
54335433
}
54345434

54355435
/*
5436-
* Remove ranges from the maple tree matching buf_index, unpin pages
5437-
* and free range structs. If buf_index < 0, remove all ranges.
5436+
* Inner loop: erase up to UBLK_REMOVE_BATCH matching ranges under
5437+
* mas_lock, collecting them into an xarray. Then drop the lock and
5438+
* unpin pages + free ranges outside spinlock context.
5439+
*
5440+
* Returns true if the tree walk completed, false if more ranges remain.
5441+
* Xarray key is the base PFN, value encodes nr_pages via xa_mk_value().
54385442
*/
5439-
static int ublk_shmem_remove_ranges(struct ublk_device *ub, int buf_index)
5443+
#define UBLK_REMOVE_BATCH 64
5444+
5445+
static bool __ublk_shmem_remove_ranges(struct ublk_device *ub,
5446+
int buf_index, int *ret)
54405447
{
54415448
MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
54425449
struct ublk_buf_range *range;
5443-
int ret = -ENOENT;
5450+
struct xarray to_unpin;
5451+
unsigned long idx;
5452+
unsigned int count = 0;
5453+
bool done = false;
5454+
void *entry;
5455+
5456+
xa_init(&to_unpin);
54445457

54455458
mas_lock(&mas);
54465459
mas_for_each(&mas, range, ULONG_MAX) {
5447-
unsigned long base, nr;
5460+
unsigned long nr;
54485461

54495462
if (buf_index >= 0 && range->buf_index != buf_index)
54505463
continue;
54515464

5452-
ret = 0;
5453-
base = mas.index;
5454-
nr = mas.last - base + 1;
5465+
*ret = 0;
5466+
nr = mas.last - mas.index + 1;
5467+
if (xa_err(xa_store(&to_unpin, mas.index,
5468+
xa_mk_value(nr), GFP_ATOMIC)))
5469+
goto unlock;
54555470
mas_erase(&mas);
5456-
5457-
ublk_unpin_range_pages(base, nr);
54585471
kfree(range);
5472+
if (++count >= UBLK_REMOVE_BATCH)
5473+
goto unlock;
54595474
}
5475+
done = true;
5476+
unlock:
54605477
mas_unlock(&mas);
54615478

5479+
xa_for_each(&to_unpin, idx, entry)
5480+
ublk_unpin_range_pages(idx, xa_to_value(entry));
5481+
xa_destroy(&to_unpin);
5482+
5483+
return done;
5484+
}
5485+
5486+
/*
5487+
* Remove ranges from the maple tree matching buf_index, unpin pages
5488+
* and free range structs. If buf_index < 0, remove all ranges.
5489+
* Processes ranges in batches to avoid holding the maple tree spinlock
5490+
* across potentially expensive page unpinning.
5491+
*/
5492+
static int ublk_shmem_remove_ranges(struct ublk_device *ub, int buf_index)
5493+
{
5494+
int ret = -ENOENT;
5495+
5496+
while (!__ublk_shmem_remove_ranges(ub, buf_index, &ret))
5497+
cond_resched();
54625498
return ret;
54635499
}
54645500

0 commit comments

Comments
 (0)