From dba177ad7829cb70de1054b49b6ec9999bb595c2 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Wed, 16 Sep 2026 12:31:52 +0800 Subject: [PATCH] mm: mincore: use per-vma lock during page table walk do_mincore() performs a read-only, per-VMA residency query, making it a good candidate for per-VMA locking. Convert it to acquire the per-VMA lock, thereby reducing contention on the per-MM mmap_lock. Link: https://lore.kernel.org/20260916043153.2631696-1-wangkefeng.wang@huawei.com Signed-off-by: Kefeng Wang Signed-off-by: Andrew Morton Reviewed-by: Pedro Falcato Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes (ARM) Cc: Jann Horn Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Zi Yan --- mm/mincore.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/mm/mincore.c b/mm/mincore.c index c086836bc4bc..0fe50f8a7e62 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -235,24 +235,22 @@ static const struct mm_walk_ops mincore_walk_ops = { .pmd_entry = mincore_pte_range, .pte_hole = mincore_unmapped_range, .hugetlb_entry = mincore_hugetlb, - .walk_lock = PGWALK_RDLOCK, + .walk_lock = PGWALK_VMA_RDLOCK_VERIFY, }; /* * Do a chunk of "sys_mincore()". We've already checked - * all the arguments, we hold the mmap semaphore: we should + * all the arguments, we hold the VMA read lock: we should * just return the amount of info we're asked for. */ -static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *vec) +static long do_mincore(struct vm_area_struct *vma, unsigned long addr, + unsigned long pages, unsigned char *vec) { - struct vm_area_struct *vma; - unsigned long end; + unsigned long end = min(vma->vm_end, addr + (pages << PAGE_SHIFT)); int err; - vma = vma_lookup(current->mm, addr); - if (!vma) - return -ENOMEM; - end = min(vma->vm_end, addr + (pages << PAGE_SHIFT)); + vma_assert_locked(vma); + if (!can_do_mincore(vma)) { unsigned long pages = DIV_ROUND_UP(end - addr, PAGE_SIZE); memset(vec, 1, pages); @@ -319,13 +317,20 @@ SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len, retval = 0; while (pages) { + struct vm_area_struct *vma; + + vma = vma_start_read_unlocked(current->mm, start); + if (!vma) { + retval = -ENOMEM; + break; + } + /* * Do at most PAGE_SIZE entries per iteration, due to * the temporary buffer size. */ - mmap_read_lock(current->mm); - retval = do_mincore(start, min(pages, PAGE_SIZE), tmp); - mmap_read_unlock(current->mm); + retval = do_mincore(vma, start, min(pages, PAGE_SIZE), tmp); + vma_end_read(vma); if (retval <= 0) break;