AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/01 03:00 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "ANDROID_BINDER_IPC",
    "USERFAULTFD",
    "BPF_SYSCALL",
    "INET"
  ],
  "FocusSymbols": [
    "vma_start_read_unlocked",
    "lock_vma_under_rcu",
    "binder_alloc_free_page",
    "bpf_iter_task_vma_new"
  ],
  "KMSANReasoning": "The patch makes per-VMA locking unconditional on MMU configurations (removing CONFIG_PER_VMA_LOCK) and introduces a helper function `vma_start_read_unlocked()` to streamline VMA locking in binder, TCP zerocopy, and procfs task MMU walkers.\n\nThese changes strictly pertain to locking primitives, synchronization, and control flow for VMA refcounts and rwsems. Any potential bugs introduced (e.g., concurrency races, locking violations, or use-after-free from incorrect refcounts) are within the scope of LOCKDEP and KASAN. No new uninitialized stack/heap allocations, partial struct initializations, or copy-to-user info-leak paths are introduced. Therefore, dedicated KMSAN fuzzing is not warranted.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch removes the CONFIG_PER_VMA_LOCK option, making per-VMA locking unconditionally enabled for MMU systems, and introduces the new vma_start_read_unlocked() helper. It refactors lock acquisition across Android Binder, TCP zerocopy receive, userfaultfd, procfs task MMU maps, and BPF VMA iterators, directly changing concurrency and locking semantics in core MM and associated subsystems.",
  "WorthFuzzing": true
}

1/1 2026/09/01 03:00 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 6de62710957b1bbdf4b16790469d862343524d69\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 1 03:00:36 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/arm/Kconfig b/arch/arm/Kconfig\nindex f10379dfe7a33..3075e5c60a25e 100644\n--- a/arch/arm/Kconfig\n+++ b/arch/arm/Kconfig\n@@ -42,7 +42,6 @@ config ARM\n \tselect ARCH_SUPPORTS_ATOMIC_RMW\n \tselect ARCH_SUPPORTS_CFI\n \tselect ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect ARCH_SUPPORTS_RT\n \tselect ARCH_USE_BUILTIN_BSWAP\n \tselect ARCH_USE_CMPXCHG_LOCKREF\ndiff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig\nindex b5a51b0ef9440..2bbeded33da0d 100644\n--- a/arch/arm64/Kconfig\n+++ b/arch/arm64/Kconfig\n@@ -81,7 +81,6 @@ config ARM64\n \tselect ARCH_HAS_PTE_PROTNONE\n \tselect ARCH_SUPPORTS_NUMA_BALANCING\n \tselect ARCH_SUPPORTS_PAGE_TABLE_CHECK\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE\n \tselect ARCH_SUPPORTS_RT\n \tselect ARCH_SUPPORTS_SCHED_SMT\ndiff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig\nindex a21f51e5815e9..9c5def7062222 100644\n--- a/arch/loongarch/Kconfig\n+++ b/arch/loongarch/Kconfig\n@@ -69,7 +69,6 @@ config LOONGARCH\n \tselect ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS\n \tselect ARCH_HAS_PTE_PROTNONE if 64BIT\n \tselect ARCH_SUPPORTS_NUMA_BALANCING if NUMA\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect ARCH_SUPPORTS_RT\n \tselect ARCH_SUPPORTS_SCHED_SMT if SMP\n \tselect ARCH_SUPPORTS_SCHED_MC  if SMP\ndiff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig\nindex b5ad7c173ef0c..dd8f6060fb7a2 100644\n--- a/arch/powerpc/platforms/powernv/Kconfig\n+++ b/arch/powerpc/platforms/powernv/Kconfig\n@@ -17,7 +17,6 @@ config PPC_POWERNV\n \tselect PPC_DOORBELL\n \tselect MMU_NOTIFIER\n \tselect FORCE_SMP\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect PPC_RADIX_BROADCAST_TLBIE if PPC_RADIX_MMU\n \tdefault y\n \ndiff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig\nindex 74910ce3a541c..7d125e288f6ef 100644\n--- a/arch/powerpc/platforms/pseries/Kconfig\n+++ b/arch/powerpc/platforms/pseries/Kconfig\n@@ -23,7 +23,6 @@ config PPC_PSERIES\n \tselect HOTPLUG_CPU\n \tselect FORCE_SMP\n \tselect SWIOTLB\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect PPC_RADIX_BROADCAST_TLBIE if PPC_RADIX_MMU\n \tdefault y\n \ndiff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig\nindex f8e26c4bed2ba..505eed4af932b 100644\n--- a/arch/riscv/Kconfig\n+++ b/arch/riscv/Kconfig\n@@ -72,7 +72,6 @@ config RISCV\n \tselect ARCH_SUPPORTS_LTO_CLANG_THIN\n \tselect ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS if 64BIT \u0026\u0026 MMU\n \tselect ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK if MMU\n \tselect ARCH_HAS_PTE_PROTNONE if MMU\n \tselect ARCH_SUPPORTS_RT\n \tselect ARCH_SUPPORTS_SHADOW_CALL_STACK if HAVE_SHADOW_CALL_STACK\ndiff --git a/arch/s390/Kconfig b/arch/s390/Kconfig\nindex 4b51bc6e8948d..b88b850421369 100644\n--- a/arch/s390/Kconfig\n+++ b/arch/s390/Kconfig\n@@ -156,7 +156,6 @@ config S390\n \tselect ARCH_HAS_PTE_PROTNONE\n \tselect ARCH_SUPPORTS_NUMA_BALANCING\n \tselect ARCH_SUPPORTS_PAGE_TABLE_CHECK\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect ARCH_USES_CFI_GENERIC_LLVM_PASS if CC_IS_CLANG\n \tselect ARCH_USE_BUILTIN_BSWAP\n \tselect ARCH_USE_CMPXCHG_LOCKREF\ndiff --git a/arch/x86/Kconfig b/arch/x86/Kconfig\nindex 15fd9ec5ecacb..ac92b3fd70c64 100644\n--- a/arch/x86/Kconfig\n+++ b/arch/x86/Kconfig\n@@ -27,7 +27,6 @@ config X86_64\n \tselect ARCH_HAS_GIGANTIC_PAGE\n \tselect ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS\n \tselect ARCH_SUPPORTS_INT128 if CC_HAS_INT128\n-\tselect ARCH_SUPPORTS_PER_VMA_LOCK\n \tselect ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE\n \tselect HAVE_ARCH_SOFT_DIRTY\n \tselect MODULES_USE_ELF_RELA\n@@ -1849,7 +1848,6 @@ config X86_USER_SHADOW_STACK\n \tbool \"X86 userspace shadow stack\"\n \tdepends on AS_WRUSS\n \tdepends on X86_64\n-\tdepends on PER_VMA_LOCK\n \tselect ARCH_USES_HIGH_VMA_FLAGS\n \tselect ARCH_HAS_USER_SHADOW_STACK\n \tselect X86_CET\ndiff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs\nindex 52ffbf3504e7f..71febd3d5b073 100644\n--- a/drivers/android/binder/page_range.rs\n+++ b/drivers/android/binder/page_range.rs\n@@ -439,22 +439,9 @@ unsafe fn use_page_slow(\u0026self, i: usize) -\u003e Result\u003c()\u003e {\n         // workqueue.\n         let mm = MmWithUser::into_mmput_async(self.mm.mmget_not_zero().ok_or(ESRCH)?);\n         {\n-            let vma_read;\n-            let mmap_read;\n-            let vma = if let Some(ret) = mm.lock_vma_under_rcu(vma_addr) {\n-                vma_read = ret;\n-                check_vma(\u0026vma_read, self)\n-            } else {\n-                mmap_read = mm.mmap_read_lock();\n-                mmap_read\n-                    .vma_lookup(vma_addr)\n-                    .and_then(|vma| check_vma(vma, self))\n-            };\n-\n-            match vma {\n-                Some(vma) =\u003e vma.vm_insert_page(user_page_addr, \u0026new_page)?,\n-                None =\u003e return Err(ESRCH),\n-            }\n+            let vma_read_guard = mm.vma_start_read_unlocked(vma_addr).ok_or(ESRCH)?;\n+            let vma = check_vma(\u0026vma_read_guard, self).ok_or(ESRCH)?;\n+            vma.vm_insert_page(user_page_addr, \u0026new_page)?;\n         }\n \n         let inner = self.lock.lock();\ndiff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c\nindex e4488ad86a655..d6eae0aa70854 100644\n--- a/drivers/android/binder_alloc.c\n+++ b/drivers/android/binder_alloc.c\n@@ -259,21 +259,14 @@ static int binder_page_insert(struct binder_alloc *alloc,\n \tstruct vm_area_struct *vma;\n \tint ret = -ESRCH;\n \n-\t/* attempt per-vma lock first */\n-\tvma = lock_vma_under_rcu(mm, addr);\n-\tif (vma) {\n-\t\tif (binder_alloc_is_mapped(alloc))\n-\t\t\tret = vm_insert_page(vma, addr, page);\n-\t\tvma_end_read(vma);\n+\tvma = vma_start_read_unlocked(mm, addr);\n+\tif (!vma)\n \t\treturn ret;\n-\t}\n \n-\t/* fall back to mmap_lock */\n-\tmmap_read_lock(mm);\n-\tvma = vma_lookup(mm, addr);\n-\tif (vma \u0026\u0026 binder_alloc_is_mapped(alloc))\n+\tif (binder_alloc_is_mapped(alloc))\n \t\tret = vm_insert_page(vma, addr, page);\n-\tmmap_read_unlock(mm);\n+\n+\tvma_end_read(vma);\n \n \treturn ret;\n }\n@@ -1142,7 +1135,6 @@ enum lru_status binder_alloc_free_page(struct list_head *item,\n \tstruct vm_area_struct *vma;\n \tstruct page *page_to_free;\n \tunsigned long page_addr;\n-\tint mm_locked = 0;\n \tsize_t index;\n \n \tif (!mmget_not_zero(mm))\n@@ -1151,27 +1143,25 @@ enum lru_status binder_alloc_free_page(struct list_head *item,\n \tindex = mdata-\u003epage_index;\n \tpage_addr = alloc-\u003evm_start + index * PAGE_SIZE;\n \n-\t/* attempt per-vma lock first */\n+\t/*\n+\t * Attempt per-vma lock. This is essentially a\n+\t * \"trylock\". It can fail even if the VMA exists\n+\t * for 'page_addr'.\n+\t */\n \tvma = lock_vma_under_rcu(mm, page_addr);\n \tif (!vma) {\n-\t\t/* fall back to mmap_lock */\n-\t\tif (!mmap_read_trylock(mm))\n-\t\t\tgoto err_mmap_read_lock_failed;\n-\t\tmm_locked = 1;\n-\t\tvma = vma_lookup(mm, page_addr);\n+\t\t/*\n+\t\t * If the vma exists, we can't continue because we cannot\n+\t\t * remove the page from the vma. However, if the vma was\n+\t\t * unmapped, it's okay to continue.\n+\t\t */\n+\t\tif (binder_alloc_is_mapped(alloc))\n+\t\t\tgoto err_vma_lock_failed;\n \t}\n \n \tif (!mutex_trylock(\u0026alloc-\u003emutex))\n \t\tgoto err_get_alloc_mutex_failed;\n \n-\t/*\n-\t * Since a binder_alloc can only be mapped once, we ensure\n-\t * the vma corresponds to this mapping by checking whether\n-\t * the binder_alloc is still mapped.\n-\t */\n-\tif (vma \u0026\u0026 !binder_alloc_is_mapped(alloc))\n-\t\tgoto err_invalid_vma;\n-\n \ttrace_binder_unmap_kernel_start(alloc, index);\n \n \tpage_to_free = alloc-\u003epages[index];\n@@ -1182,7 +1172,12 @@ enum lru_status binder_alloc_free_page(struct list_head *item,\n \tlist_lru_isolate(lru, item);\n \tspin_unlock(\u0026lru-\u003elock);\n \n-\tif (vma) {\n+\t/*\n+\t * Since a binder_alloc can only be mapped once, we ensure\n+\t * the vma corresponds to this mapping by checking whether\n+\t * the binder_alloc is still mapped.\n+\t */\n+\tif (vma \u0026\u0026 binder_alloc_is_mapped(alloc)) {\n \t\ttrace_binder_unmap_user_start(alloc, index);\n \n \t\tzap_vma_range(vma, page_addr, PAGE_SIZE);\n@@ -1191,23 +1186,17 @@ enum lru_status binder_alloc_free_page(struct list_head *item,\n \t}\n \n \tmutex_unlock(\u0026alloc-\u003emutex);\n-\tif (mm_locked)\n-\t\tmmap_read_unlock(mm);\n-\telse\n+\tif (vma)\n \t\tvma_end_read(vma);\n \tmmput_async(mm);\n \tbinder_free_page(page_to_free);\n \n \treturn LRU_REMOVED_RETRY;\n \n-err_invalid_vma:\n-\tmutex_unlock(\u0026alloc-\u003emutex);\n err_get_alloc_mutex_failed:\n-\tif (mm_locked)\n-\t\tmmap_read_unlock(mm);\n-\telse\n+\tif (vma)\n \t\tvma_end_read(vma);\n-err_mmap_read_lock_failed:\n+err_vma_lock_failed:\n \tmmput_async(mm);\n err_mmget:\n \treturn LRU_SKIP;\ndiff --git a/fs/proc/internal.h b/fs/proc/internal.h\nindex 04bd6c9e65a72..623bb43ede550 100644\n--- a/fs/proc/internal.h\n+++ b/fs/proc/internal.h\n@@ -385,10 +385,8 @@ struct mem_size_stats;\n \n struct proc_maps_locking_ctx {\n \tstruct mm_struct *mm;\n-#ifdef CONFIG_PER_VMA_LOCK\n \tbool mmap_locked;\n \tstruct vm_area_struct *locked_vma;\n-#endif\n };\n \n struct proc_maps_private {\ndiff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c\nindex 5c54aebe21182..e671b4fd8dedd 100644\n--- a/fs/proc/task_mmu.c\n+++ b/fs/proc/task_mmu.c\n@@ -130,8 +130,6 @@ static void release_task_mempolicy(struct proc_maps_private *priv)\n }\n #endif\n \n-#ifdef CONFIG_PER_VMA_LOCK\n-\n static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)\n {\n \tint ret = mmap_read_lock_killable(lock_ctx-\u003emm);\n@@ -233,46 +231,6 @@ static inline void reacquire_rcu(struct proc_maps_private *priv)\n \tvma_iter_set(\u0026priv-\u003eiter, priv-\u003elock_ctx.locked_vma-\u003evm_end);\n }\n \n-#else /* CONFIG_PER_VMA_LOCK */\n-\n-static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)\n-{\n-\treturn mmap_read_lock_killable(lock_ctx-\u003emm);\n-}\n-\n-static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)\n-{\n-\tmmap_read_unlock(lock_ctx-\u003emm);\n-}\n-\n-static inline bool lock_vma_range(struct seq_file *m,\n-\t\t\t\t  struct proc_maps_locking_ctx *lock_ctx)\n-{\n-\treturn lock_ctx_mm(lock_ctx) == 0;\n-}\n-\n-static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx)\n-{\n-\tunlock_ctx_mm(lock_ctx);\n-}\n-\n-static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv,\n-\t\t\t\t\t   loff_t last_pos)\n-{\n-\treturn vma_next(\u0026priv-\u003eiter);\n-}\n-\n-static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv,\n-\t\t\t\t\t loff_t pos)\n-{\n-\treturn false;\n-}\n-\n-static inline void drop_rcu(struct proc_maps_private *priv) {}\n-static inline void reacquire_rcu(struct proc_maps_private *priv) {}\n-\n-#endif /* CONFIG_PER_VMA_LOCK */\n-\n static struct vm_area_struct *proc_get_vma(struct seq_file *m, loff_t *ppos)\n {\n \tstruct proc_maps_private *priv = m-\u003eprivate;\n@@ -560,8 +518,6 @@ static int pid_maps_open(struct inode *inode, struct file *file)\n \t\tPROCMAP_QUERY_VMA_FLAGS\t\t\t\t\\\n )\n \n-#ifdef CONFIG_PER_VMA_LOCK\n-\n static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx)\n {\n \treset_lock_ctx(lock_ctx);\n@@ -612,26 +568,6 @@ static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ct\n \treturn vma;\n }\n \n-#else /* CONFIG_PER_VMA_LOCK */\n-\n-static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx)\n-{\n-\treturn mmap_read_lock_killable(lock_ctx-\u003emm);\n-}\n-\n-static void query_vma_teardown(struct proc_maps_locking_ctx *lock_ctx)\n-{\n-\tmmap_read_unlock(lock_ctx-\u003emm);\n-}\n-\n-static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ctx *lock_ctx,\n-\t\t\t\t\t\t     unsigned long addr)\n-{\n-\treturn find_vma(lock_ctx-\u003emm, addr);\n-}\n-\n-#endif  /* CONFIG_PER_VMA_LOCK */\n-\n static struct vm_area_struct *query_matching_vma(struct proc_maps_locking_ctx *lock_ctx,\n \t\t\t\t\t\t unsigned long addr, u32 flags)\n {\n@@ -1314,8 +1250,6 @@ static const struct mm_walk_ops smaps_shmem_walk_ops = {\n \t.walk_lock\t\t= PGWALK_RDLOCK,\n };\n \n-#ifdef CONFIG_PER_VMA_LOCK\n-\n static const struct mm_walk_ops smaps_walk_vma_lock_ops = {\n \t.pmd_entry\t\t= smaps_pte_range,\n \t.hugetlb_entry\t\t= smaps_hugetlb_range,\n@@ -1345,22 +1279,6 @@ get_smaps_shmem_walk_ops(struct proc_maps_private *priv)\n \treturn \u0026smaps_shmem_walk_vma_lock_ops;\n }\n \n-#else /* CONFIG_PER_VMA_LOCK */\n-\n-static inline const struct mm_walk_ops *\n-get_smaps_walk_ops(struct proc_maps_private *priv)\n-{\n-\treturn \u0026smaps_walk_ops;\n-}\n-\n-static inline const struct mm_walk_ops *\n-get_smaps_shmem_walk_ops(struct proc_maps_private *priv)\n-{\n-\treturn \u0026smaps_shmem_walk_ops;\n-}\n-\n-#endif /* CONFIG_PER_VMA_LOCK */\n-\n /*\n  * Gather mem stats from @vma with the indicated beginning\n  * address @start, and keep them in @mss.\n@@ -3497,7 +3415,6 @@ static const struct mm_walk_ops show_numa_ops = {\n \t.walk_lock = PGWALK_RDLOCK,\n };\n \n-#ifdef CONFIG_PER_VMA_LOCK\n static const struct mm_walk_ops show_numa_vma_lock_ops = {\n \t.hugetlb_entry = gather_hugetlb_stats,\n \t.pmd_entry = gather_pte_stats,\n@@ -3512,16 +3429,6 @@ get_show_numa_ops(struct proc_maps_private *priv)\n \treturn \u0026show_numa_vma_lock_ops;\n }\n \n-#else /* CONFIG_PER_VMA_LOCK */\n-\n-static inline const struct mm_walk_ops *\n-get_show_numa_ops(struct proc_maps_private *priv)\n-{\n-\treturn \u0026show_numa_ops;\n-}\n-\n-#endif /* CONFIG_PER_VMA_LOCK */\n-\n /*\n  * Display pages allocated per node and memory policy via /proc.\n  */\ndiff --git a/include/linux/mm.h b/include/linux/mm.h\nindex dd09c438fa23e..7aaa25abc241a 100644\n--- a/include/linux/mm.h\n+++ b/include/linux/mm.h\n@@ -928,7 +928,6 @@ static inline void vma_numab_state_free(struct vm_area_struct *vma) {}\n  * These must be here rather than mmap_lock.h as dependent on vm_fault type,\n  * declared in this header.\n  */\n-#ifdef CONFIG_PER_VMA_LOCK\n static inline void release_fault_lock(struct vm_fault *vmf)\n {\n \tif (vmf-\u003eflags \u0026 FAULT_FLAG_VMA_LOCK)\n@@ -944,17 +943,6 @@ static inline void assert_fault_locked(const struct vm_fault *vmf)\n \telse\n \t\tmmap_assert_locked(vmf-\u003evma-\u003evm_mm);\n }\n-#else\n-static inline void release_fault_lock(struct vm_fault *vmf)\n-{\n-\tmmap_read_unlock(vmf-\u003evma-\u003evm_mm);\n-}\n-\n-static inline void assert_fault_locked(const struct vm_fault *vmf)\n-{\n-\tmmap_assert_locked(vmf-\u003evma-\u003evm_mm);\n-}\n-#endif /* CONFIG_PER_VMA_LOCK */\n \n static inline bool mm_flags_test(int flag, const struct mm_struct *mm)\n {\ndiff --git a/include/linux/mm_types.h b/include/linux/mm_types.h\nindex 6d815f6440c94..5413bd10fff2c 100644\n--- a/include/linux/mm_types.h\n+++ b/include/linux/mm_types.h\n@@ -950,7 +950,6 @@ struct vm_area_struct {\n \t\tvma_flags_t flags;\n \t};\n \n-#ifdef CONFIG_PER_VMA_LOCK\n \t/*\n \t * Can only be written (using WRITE_ONCE()) while holding both:\n \t *  - mmap_lock (in write mode)\n@@ -966,7 +965,7 @@ struct vm_area_struct {\n \t * slowpath.\n \t */\n \tunsigned int vm_lock_seq;\n-#endif\n+\n \t/*\n \t * Low 32-bits of anonymous page offset.\n \t * See vma_start_anon_pgoff() comment for details.\n@@ -1003,7 +1002,6 @@ struct vm_area_struct {\n #ifdef CONFIG_NUMA_BALANCING\n \tstruct vma_numab_state *numab_state;\t/* NUMA Balancing state */\n #endif\n-#ifdef CONFIG_PER_VMA_LOCK\n \t/*\n \t * Used to keep track of firstly, whether the VMA is attached, secondly,\n \t * if attached, how many read locks are taken, and thirdly, if the\n@@ -1046,7 +1044,6 @@ struct vm_area_struct {\n #ifdef CONFIG_DEBUG_LOCK_ALLOC\n \tstruct lockdep_map vmlock_dep_map;\n #endif\n-#endif\n #ifdef CONFIG_64BIT\n \t/*\n \t * High 32-bits of anonymous page offset.\n@@ -1254,7 +1251,6 @@ struct mm_struct {\n \t\t\t\t\t  * init_mm.mmlist, and are protected\n \t\t\t\t\t  * by mmlist_lock\n \t\t\t\t\t  */\n-#ifdef CONFIG_PER_VMA_LOCK\n \t\tstruct rcuwait vma_writer_wait;\n \t\t/*\n \t\t * This field has lock-like semantics, meaning it is sometimes\n@@ -1274,7 +1270,7 @@ struct mm_struct {\n \t\t * mmap_lock.\n \t\t */\n \t\tseqcount_t mm_lock_seq;\n-#endif\n+\n \t\tstruct futex_mm_data\tfutex;\n \n \t\tunsigned long hiwater_rss; /* High-watermark of RSS usage */\ndiff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h\nindex bec0eab6ef035..00eae65b74bd6 100644\n--- a/include/linux/mmap_lock.h\n+++ b/include/linux/mmap_lock.h\n@@ -76,8 +76,6 @@ static inline void mmap_assert_write_locked(const struct mm_struct *mm)\n \trwsem_assert_held_write(\u0026mm-\u003emmap_lock);\n }\n \n-#ifdef CONFIG_PER_VMA_LOCK\n-\n #ifdef CONFIG_LOCKDEP\n #define __vma_lockdep_map(vma) (\u0026vma-\u003evmlock_dep_map)\n #else\n@@ -230,10 +228,14 @@ static inline void vma_refcount_put(struct vm_area_struct *vma)\n }\n \n /*\n- * Use only while holding mmap read lock which guarantees that locking will not\n- * fail (nobody can concurrently write-lock the vma). vma_start_read() should\n+ * Use only while holding mmap read lock which guarantees that vma lock is not\n+ * contended (nobody can concurrently write-lock the vma). vma_start_read() should\n  * not be used in such cases because it might fail due to mm_lock_seq overflow.\n  * This functionality is used to obtain vma read lock and drop the mmap read lock.\n+ *\n+ * VMA can't be detached while we are holding mmap lock, therefore in practice this\n+ * function can fail only when there are so many readers that vm_refcnt overflows.\n+ * The failure case is very unlikely and is already annotated as such internally.\n  */\n static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int subclass)\n {\n@@ -249,16 +251,23 @@ static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int\n }\n \n /*\n- * Use only while holding mmap read lock which guarantees that locking will not\n- * fail (nobody can concurrently write-lock the vma). vma_start_read() should\n+ * Use only while holding mmap read lock which guarantees that vma lock is not\n+ * contended (nobody can concurrently write-lock the vma). vma_start_read() should\n  * not be used in such cases because it might fail due to mm_lock_seq overflow.\n  * This functionality is used to obtain vma read lock and drop the mmap read lock.\n+ *\n+ * VMA can't be detached while we are holding mmap lock, therefore in practice this\n+ * function can fail only when there are so many readers that vm_refcnt overflows.\n+ * The failure case is very unlikely and is already annotated as such internally.\n  */\n static inline bool vma_start_read_locked(struct vm_area_struct *vma)\n {\n \treturn vma_start_read_locked_nested(vma, 0);\n }\n \n+struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,\n+\t\t\t\t\t       unsigned long address);\n+\n static inline void vma_end_read(struct vm_area_struct *vma)\n {\n \tvma_refcount_put(vma);\n@@ -297,6 +306,9 @@ int __vma_start_write(struct vm_area_struct *vma, int state);\n  */\n static inline void vma_start_write(struct vm_area_struct *vma)\n {\n+\tif (!IS_ENABLED(CONFIG_MMU))\n+\t\treturn;\n+\n \tif (__is_vma_write_locked(vma))\n \t\treturn;\n \n@@ -319,6 +331,9 @@ static inline void vma_start_write(struct vm_area_struct *vma)\n static inline __must_check\n int vma_start_write_killable(struct vm_area_struct *vma)\n {\n+\tif (!IS_ENABLED(CONFIG_MMU))\n+\t\treturn 0;\n+\n \tif (__is_vma_write_locked(vma))\n \t\treturn 0;\n \n@@ -331,6 +346,11 @@ int vma_start_write_killable(struct vm_area_struct *vma)\n  */\n static inline void vma_assert_write_locked(struct vm_area_struct *vma)\n {\n+\tif (!IS_ENABLED(CONFIG_MMU)) {\n+\t\tmmap_assert_write_locked(vma-\u003evm_mm);\n+\t\treturn;\n+\t}\n+\n \tVM_WARN_ON_ONCE_VMA(!__is_vma_write_locked(vma), vma);\n }\n \n@@ -343,6 +363,11 @@ static inline void vma_assert_locked(struct vm_area_struct *vma)\n {\n \tunsigned int refcnt;\n \n+\tif (!IS_ENABLED(CONFIG_MMU)) {\n+\t\tmmap_assert_locked(vma-\u003evm_mm);\n+\t\treturn;\n+\t}\n+\n \tif (IS_ENABLED(CONFIG_LOCKDEP)) {\n \t\tif (!lock_is_held(__vma_lockdep_map(vma)))\n \t\t\tvma_assert_write_locked(vma);\n@@ -432,6 +457,9 @@ static inline bool vma_is_attached(struct vm_area_struct *vma)\n  */\n static inline void vma_assert_attached(struct vm_area_struct *vma)\n {\n+\tif (!IS_ENABLED(CONFIG_MMU))\n+\t\treturn;\n+\n \tWARN_ON_ONCE(!vma_is_attached(vma));\n }\n \n@@ -442,6 +470,9 @@ static inline void vma_assert_detached(struct vm_area_struct *vma)\n \n static inline void vma_mark_attached(struct vm_area_struct *vma)\n {\n+\tif (!IS_ENABLED(CONFIG_MMU))\n+\t\treturn;\n+\n \tvma_assert_write_locked(vma);\n \tvma_assert_detached(vma);\n \trefcount_set_release(\u0026vma-\u003evm_refcnt, 1);\n@@ -451,6 +482,9 @@ void __vma_exclude_readers_for_detach(struct vm_area_struct *vma);\n \n static inline void vma_mark_detached(struct vm_area_struct *vma)\n {\n+\tif (!IS_ENABLED(CONFIG_MMU))\n+\t\treturn;\n+\n \tvma_assert_write_locked(vma);\n \tvma_assert_attached(vma);\n \n@@ -484,54 +518,6 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm,\n \t\t\t\t     struct vma_iterator *iter,\n \t\t\t\t     unsigned long address);\n \n-#else /* CONFIG_PER_VMA_LOCK */\n-\n-static inline void mm_lock_seqcount_init(struct mm_struct *mm) {}\n-static inline void mm_lock_seqcount_begin(struct mm_struct *mm) {}\n-static inline void mm_lock_seqcount_end(struct mm_struct *mm) {}\n-\n-static inline bool mmap_lock_speculate_try_begin(struct mm_struct *mm, unsigned int *seq)\n-{\n-\treturn false;\n-}\n-\n-static inline bool mmap_lock_speculate_retry(struct mm_struct *mm, unsigned int seq)\n-{\n-\treturn true;\n-}\n-static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt) {}\n-static inline void vma_end_read(struct vm_area_struct *vma) {}\n-static inline void vma_start_write(struct vm_area_struct *vma) {}\n-static inline __must_check\n-int vma_start_write_killable(struct vm_area_struct *vma) { return 0; }\n-static inline void vma_assert_write_locked(struct vm_area_struct *vma)\n-\t\t{ mmap_assert_write_locked(vma-\u003evm_mm); }\n-static inline bool vma_is_attached(struct vm_area_struct *vma)\n-\t\t{ return true; }\n-static inline void vma_assert_attached(struct vm_area_struct *vma) {}\n-static inline void vma_assert_detached(struct vm_area_struct *vma) {}\n-static inline void vma_mark_attached(struct vm_area_struct *vma) {}\n-static inline void vma_mark_detached(struct vm_area_struct *vma) {}\n-\n-static inline struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,\n-\t\tunsigned long address)\n-{\n-\treturn NULL;\n-}\n-\n-static inline void vma_assert_locked(struct vm_area_struct *vma)\n-{\n-\tmmap_assert_locked(vma-\u003evm_mm);\n-}\n-\n-static inline void vma_assert_stabilised(struct vm_area_struct *vma)\n-{\n-\t/* If no VMA locks, then either mmap lock suffices to stabilise. */\n-\tmmap_assert_locked(vma-\u003evm_mm);\n-}\n-\n-#endif /* CONFIG_PER_VMA_LOCK */\n-\n static inline void vma_assert_can_modify(struct vm_area_struct *vma)\n {\n \tif (vma_is_attached(vma))\ndiff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c\nindex a839041e0d008..8fb70c2a9c8e6 100644\n--- a/kernel/bpf/stackmap.c\n+++ b/kernel/bpf/stackmap.c\n@@ -272,13 +272,10 @@ struct stack_map_vma_lock {\n /*\n  * Acquire a stable read-side reference on the VMA covering @ip.\n  *\n- * With CONFIG_PER_VMA_LOCK=y this returns a VMA with its per-VMA read\n- * lock held and mmap_lock dropped, so the caller may sleep.\n- *\n- * With CONFIG_PER_VMA_LOCK=n it returns a VMA with mmap_lock still\n- * held; the caller must snapshot any fields it needs and pin vm_file\n- * with get_file() before stack_map_unlock_vma() drops mmap_lock, as\n- * the VMA may be split, merged, or freed after that.\n+ * On NOMMU configurations, returns with the mmap_lock held.  If the MMU\n+ * is enabled, the per-VMA lock will be held instead.  The lock\n+ * should be released with stack_map_unlock_vma() which will release the\n+ * appropriate lock.  Once the lock is released, the VMA may be freed.\n  *\n  * Returns NULL on failure, in which case no lock is held.\n  */\n@@ -288,7 +285,6 @@ stack_map_lock_vma(struct stack_map_vma_lock *lock, unsigned long ip)\n \tstruct mm_struct *mm = lock-\u003emm;\n \tstruct vm_area_struct *vma;\n \n-\t/* noop under !CONFIG_PER_VMA_LOCK */\n \tvma = lock_vma_under_rcu(mm, ip);\n \tif (vma) {\n \t\tlock-\u003evma = vma;\n@@ -308,21 +304,20 @@ stack_map_lock_vma(struct stack_map_vma_lock *lock, unsigned long ip)\n \t\treturn NULL;\n \t}\n \n-#ifdef CONFIG_PER_VMA_LOCK\n+#ifdef CONFIG_MMU\n \tif (!vma_start_read_locked(vma)) {\n \t\tmmap_read_unlock(mm);\n \t\treturn NULL;\n \t}\n \tmmap_read_unlock(mm);\n #endif\n-\n \tlock-\u003evma = vma;\n \treturn vma;\n }\n \n static void stack_map_unlock_vma(struct stack_map_vma_lock *lock)\n {\n-#ifdef CONFIG_PER_VMA_LOCK\n+#ifdef CONFIG_MMU\n \tvma_end_read(lock-\u003evma);\n #else\n \tmmap_read_unlock(lock-\u003emm);\ndiff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c\nindex 13e1aabe6f886..c65ba1dcd8667 100644\n--- a/kernel/bpf/task_iter.c\n+++ b/kernel/bpf/task_iter.c\n@@ -869,7 +869,7 @@ __bpf_kfunc int bpf_iter_task_vma_new(struct bpf_iter_task_vma *it,\n \tBUILD_BUG_ON(sizeof(struct bpf_iter_task_vma_kern) != sizeof(struct bpf_iter_task_vma));\n \tBUILD_BUG_ON(__alignof__(struct bpf_iter_task_vma_kern) != __alignof__(struct bpf_iter_task_vma));\n \n-\tif (!IS_ENABLED(CONFIG_PER_VMA_LOCK)) {\n+\tif (!IS_ENABLED(CONFIG_MMU)) {\n \t\tkit-\u003edata = NULL;\n \t\treturn -EOPNOTSUPP;\n \t}\ndiff --git a/kernel/fork.c b/kernel/fork.c\nindex 416758c8a3d43..22283bf849e15 100644\n--- a/kernel/fork.c\n+++ b/kernel/fork.c\n@@ -1083,9 +1083,7 @@ static void mmap_init_lock(struct mm_struct *mm)\n {\n \tinit_rwsem(\u0026mm-\u003emmap_lock);\n \tmm_lock_seqcount_init(mm);\n-#ifdef CONFIG_PER_VMA_LOCK\n \trcuwait_init(\u0026mm-\u003evma_writer_wait);\n-#endif\n }\n \n static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)\ndiff --git a/mm/Kconfig b/mm/Kconfig\nindex 604c58199acbf..ece5d37b4eb7c 100644\n--- a/mm/Kconfig\n+++ b/mm/Kconfig\n@@ -1430,18 +1430,6 @@ config LRU_GEN_WALKS_MMU\n \tdepends on LRU_GEN \u0026\u0026 ARCH_HAS_HW_PTE_YOUNG\n # }\n \n-config ARCH_SUPPORTS_PER_VMA_LOCK\n-       def_bool n\n-\n-config PER_VMA_LOCK\n-\tdef_bool y\n-\tdepends on ARCH_SUPPORTS_PER_VMA_LOCK \u0026\u0026 MMU \u0026\u0026 SMP\n-\thelp\n-\t  Allow per-vma locking during page fault handling.\n-\n-\t  This feature allows locking each virtual memory area separately when\n-\t  handling page faults instead of taking mmap_lock.\n-\n config LOCK_MM_AND_FIND_VMA\n \tbool\n \tdepends on !STACK_GROWSUP\ndiff --git a/mm/Kconfig.debug b/mm/Kconfig.debug\nindex 15dca19dd07da..9eaa25d1cf234 100644\n--- a/mm/Kconfig.debug\n+++ b/mm/Kconfig.debug\n@@ -310,7 +310,6 @@ config DEBUG_KMEMLEAK_VERBOSE\n \n config PER_VMA_LOCK_STATS\n \tbool \"Statistics for per-vma locks\"\n-\tdepends on PER_VMA_LOCK\n \thelp\n \t  Say Y here to enable success, retry and failure counters of page\n \t  faults handled under protection of per-vma locks. When enabled, the\ndiff --git a/mm/debug.c b/mm/debug.c\nindex 9a0297b3988d8..655e6bcc0e8d9 100644\n--- a/mm/debug.c\n+++ b/mm/debug.c\n@@ -157,17 +157,13 @@ void dump_vma(const struct vm_area_struct *vma)\n \tpr_emerg(\"vma %px start %px end %px mm %px\\n\"\n \t\t\"prot %lx anon_vma %px vm_ops %px\\n\"\n \t\t\"pgoff %lx file %px private_data %px\\n\"\n-#ifdef CONFIG_PER_VMA_LOCK\n \t\t\"refcnt %x\\n\"\n-#endif\n \t\t\"flags: %#lx(%pGv)\\n\",\n \t\tvma, (void *)vma-\u003evm_start, (void *)vma-\u003evm_end, vma-\u003evm_mm,\n \t\t(unsigned long)pgprot_val(vma-\u003evm_page_prot),\n \t\tvma-\u003eanon_vma, vma-\u003evm_ops, vma_start_pgoff(vma),\n \t\tvma-\u003evm_file, vma-\u003evm_private_data,\n-#ifdef CONFIG_PER_VMA_LOCK\n \t\trefcount_read(\u0026vma-\u003evm_refcnt),\n-#endif\n \t\tvma-\u003evm_flags, \u0026vma-\u003evm_flags);\n }\n EXPORT_SYMBOL(dump_vma);\ndiff --git a/mm/init-mm.c b/mm/init-mm.c\nindex 3e792aad76261..a1bb2c2d0284a 100644\n--- a/mm/init-mm.c\n+++ b/mm/init-mm.c\n@@ -39,10 +39,8 @@ struct mm_struct init_mm = {\n \t.page_table_lock =  __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),\n \t.arg_lock\t=  __SPIN_LOCK_UNLOCKED(init_mm.arg_lock),\n \t.mmlist\t\t= LIST_HEAD_INIT(init_mm.mmlist),\n-#ifdef CONFIG_PER_VMA_LOCK\n \t.vma_writer_wait = __RCUWAIT_INITIALIZER(init_mm.vma_writer_wait),\n \t.mm_lock_seq\t= SEQCNT_ZERO(init_mm.mm_lock_seq),\n-#endif\n #ifdef CONFIG_SCHED_MM_CID\n \t.mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(init_mm.mm_cid.lock),\n #endif\ndiff --git a/mm/memory.c b/mm/memory.c\nindex 8b0c2c735d3de..7bd660d48ff7c 100644\n--- a/mm/memory.c\n+++ b/mm/memory.c\n@@ -6817,7 +6817,6 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,\n \t\t\t\t !vma_is_cow_mapping(vma)))\n \t\t\treturn VM_FAULT_SIGSEGV;\n \t}\n-#ifdef CONFIG_PER_VMA_LOCK\n \t/*\n \t * Per-VMA locks can't be used with FAULT_FLAG_RETRY_NOWAIT because of\n \t * the assumption that lock is dropped on VM_FAULT_RETRY.\n@@ -6826,7 +6825,6 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,\n \t\t\t(FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)) ==\n \t\t\t(FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)))\n \t\treturn VM_FAULT_SIGSEGV;\n-#endif\n \n \treturn 0;\n }\ndiff --git a/mm/mmap_lock.c b/mm/mmap_lock.c\nindex 898c2ef1e9580..2f94ee0fdee2a 100644\n--- a/mm/mmap_lock.c\n+++ b/mm/mmap_lock.c\n@@ -43,9 +43,6 @@ void __mmap_lock_do_trace_released(struct mm_struct *mm, bool write)\n EXPORT_SYMBOL(__mmap_lock_do_trace_released);\n #endif /* CONFIG_TRACING */\n \n-#ifdef CONFIG_MMU\n-#ifdef CONFIG_PER_VMA_LOCK\n-\n /* State shared across __vma_[start, end]_exclude_readers. */\n struct vma_exclude_readers_state {\n \t/* Input parameters. */\n@@ -299,6 +296,8 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,\n \tMA_STATE(mas, \u0026mm-\u003emm_mt, address, address);\n \tstruct vm_area_struct *vma;\n \n+\tif (!IS_ENABLED(CONFIG_MMU))\n+\t\treturn NULL;\n retry:\n \trcu_read_lock();\n \tvma = mas_walk(\u0026mas);\n@@ -341,6 +340,41 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,\n \treturn NULL;\n }\n \n+/**\n+ * vma_start_read_unlocked() - Find the VMA covering 'address' and read-lock it.\n+ * @mm: the mm_struct of the address space to search\n+ * @address: address that the vma should contain\n+ *\n+ * The fast path does not take mmap_lock. Waits for writers to finish if the\n+ * VMA is being modified by taking mmap_lock.\n+ * Use when mmap_lock is not held, otherwise use vma_start_read_locked().\n+ * Nothing prevents VMAs being unmapped/mapped before or after the VMA is\n+ * looked up, if a stronger guarantee is required, take an mmap_lock.\n+ *\n+ * Return: If a VMA exists which spans @address, return that VMA, read-locked.\n+ * If no VMA is mapped there or, very unlikely, a reference count overflow\n+ * occurred, return NULL.\n+ */\n+struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,\n+\t\t\t\t\t       unsigned long address)\n+{\n+\tstruct vm_area_struct *vma;\n+\n+\t/* Fast path: return stable VMA covering 'address': */\n+\tvma = lock_vma_under_rcu(mm, address);\n+\tif (vma)\n+\t\treturn vma;\n+\n+\t/* Slow path: preclude VMA writers by temporarily getting mmap read lock. */\n+\tmmap_read_lock(mm);\n+\tvma = vma_lookup(mm, address);\n+\tif (vma \u0026\u0026 !vma_start_read_locked(vma))\n+\t\tvma = NULL;\n+\tmmap_read_unlock(mm);\n+\n+\treturn vma;\n+}\n+\n static struct vm_area_struct *lock_next_vma_under_mmap_lock(struct mm_struct *mm,\n \t\t\t\t\t\t\t    struct vma_iterator *vmi,\n \t\t\t\t\t\t\t    unsigned long from_addr)\n@@ -431,7 +465,6 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm,\n \n \treturn vma;\n }\n-#endif /* CONFIG_PER_VMA_LOCK */\n \n #ifdef CONFIG_LOCK_MM_AND_FIND_VMA\n #include \u003clinux/extable.h\u003e\n@@ -548,23 +581,3 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,\n \treturn NULL;\n }\n #endif /* CONFIG_LOCK_MM_AND_FIND_VMA */\n-\n-#else /* CONFIG_MMU */\n-\n-/*\n- * At least xtensa ends up having protection faults even with no\n- * MMU.. No stack expansion, at least.\n- */\n-struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,\n-\t\t\tunsigned long addr, struct pt_regs *regs)\n-{\n-\tstruct vm_area_struct *vma;\n-\n-\tmmap_read_lock(mm);\n-\tvma = vma_lookup(mm, addr);\n-\tif (!vma)\n-\t\tmmap_read_unlock(mm);\n-\treturn vma;\n-}\n-\n-#endif /* CONFIG_MMU */\ndiff --git a/mm/pagewalk.c b/mm/pagewalk.c\nindex cc07fcf50e87b..7411702a37f58 100644\n--- a/mm/pagewalk.c\n+++ b/mm/pagewalk.c\n@@ -444,7 +444,6 @@ static inline void process_mm_walk_lock(struct mm_struct *mm,\n static inline void process_vma_walk_lock(struct vm_area_struct *vma,\n \t\t\t\t\t enum page_walk_lock walk_lock)\n {\n-#ifdef CONFIG_PER_VMA_LOCK\n \tswitch (walk_lock) {\n \tcase PGWALK_WRLOCK:\n \t\tvma_start_write(vma);\n@@ -459,7 +458,6 @@ static inline void process_vma_walk_lock(struct vm_area_struct *vma,\n \t\t/* PGWALK_RDLOCK is handled by process_mm_walk_lock */\n \t\tbreak;\n \t}\n-#endif\n }\n \n /*\ndiff --git a/mm/rmap.c b/mm/rmap.c\nindex d1819fd699380..fed0362e0bd0e 100644\n--- a/mm/rmap.c\n+++ b/mm/rmap.c\n@@ -260,11 +260,9 @@ static void check_anon_vma_clone(struct vm_area_struct *dst,\n \t/* For the anon_vma to be compatible, it can only be singular. */\n \tVM_WARN_ON_ONCE(operation == VMA_OP_MERGE_UNFAULTED \u0026\u0026\n \t\t\t!list_is_singular(\u0026src-\u003eanon_vma_chain));\n-#ifdef CONFIG_PER_VMA_LOCK\n \t/* Only merging an unfaulted VMA leaves the destination attached. */\n \tVM_WARN_ON_ONCE(operation != VMA_OP_MERGE_UNFAULTED \u0026\u0026\n \t\t\tvma_is_attached(dst));\n-#endif\n }\n \n static void maybe_reuse_anon_vma(struct vm_area_struct *dst,\ndiff --git a/mm/userfaultfd.c b/mm/userfaultfd.c\nindex 74f04c323c50f..6ee933a3803cc 100644\n--- a/mm/userfaultfd.c\n+++ b/mm/userfaultfd.c\n@@ -122,7 +122,6 @@ struct vm_area_struct *find_vma_and_prepare_anon(struct mm_struct *mm,\n \treturn vma;\n }\n \n-#ifdef CONFIG_PER_VMA_LOCK\n /*\n  * uffd_lock_vma() - Lookup and lock vma corresponding to @address.\n  * @mm: mm to search vma in.\n@@ -130,8 +129,10 @@ struct vm_area_struct *find_vma_and_prepare_anon(struct mm_struct *mm,\n  *\n  * Should be called without holding mmap_lock.\n  *\n- * Return: A locked vma containing @address, -ENOENT if no vma is found, or\n- * -ENOMEM if anon_vma couldn't be allocated.\n+ * Return: A locked vma containing @address, -ENOENT if no vma is found,\n+ * -ENOMEM if anon_vma couldn't be allocated, or -EAGAIN if vma refcount\n+ * overflow happened due to high number of readers and the caller should\n+ * retry later.\n  */\n static struct vm_area_struct *uffd_lock_vma(struct mm_struct *mm,\n \t\t\t\t       unsigned long address)\n@@ -182,34 +183,6 @@ static void uffd_mfill_unlock(struct vm_area_struct *vma)\n \tvma_end_read(vma);\n }\n \n-#else\n-\n-static struct vm_area_struct *uffd_mfill_lock(struct mm_struct *dst_mm,\n-\t\t\t\t\t      unsigned long dst_start,\n-\t\t\t\t\t      unsigned long len)\n-{\n-\tstruct vm_area_struct *dst_vma;\n-\n-\tmmap_read_lock(dst_mm);\n-\tdst_vma = find_vma_and_prepare_anon(dst_mm, dst_start);\n-\tif (IS_ERR(dst_vma))\n-\t\tgoto out_unlock;\n-\n-\tif (validate_dst_vma(dst_vma, dst_start + len))\n-\t\treturn dst_vma;\n-\n-\tdst_vma = ERR_PTR(-ENOENT);\n-out_unlock:\n-\tmmap_read_unlock(dst_mm);\n-\treturn dst_vma;\n-}\n-\n-static void uffd_mfill_unlock(struct vm_area_struct *vma)\n-{\n-\tmmap_read_unlock(vma-\u003evm_mm);\n-}\n-#endif\n-\n static void mfill_put_vma(struct mfill_state *state)\n {\n \tif (!state-\u003evma)\n@@ -1850,7 +1823,6 @@ int find_vmas_mm_locked(struct mm_struct *mm,\n \treturn 0;\n }\n \n-#ifdef CONFIG_PER_VMA_LOCK\n static int uffd_move_lock(struct mm_struct *mm,\n \t\t\t  unsigned long dst_start,\n \t\t\t  unsigned long src_start,\n@@ -1925,31 +1897,6 @@ static void uffd_move_unlock(struct vm_area_struct *dst_vma,\n \t\tvma_end_read(dst_vma);\n }\n \n-#else\n-\n-static int uffd_move_lock(struct mm_struct *mm,\n-\t\t\t  unsigned long dst_start,\n-\t\t\t  unsigned long src_start,\n-\t\t\t  struct vm_area_struct **dst_vmap,\n-\t\t\t  struct vm_area_struct **src_vmap)\n-{\n-\tint err;\n-\n-\tmmap_read_lock(mm);\n-\terr = find_vmas_mm_locked(mm, dst_start, src_start, dst_vmap, src_vmap);\n-\tif (err)\n-\t\tmmap_read_unlock(mm);\n-\treturn err;\n-}\n-\n-static void uffd_move_unlock(struct vm_area_struct *dst_vma,\n-\t\t\t     struct vm_area_struct *src_vma)\n-{\n-\tmmap_assert_locked(src_vma-\u003evm_mm);\n-\tmmap_read_unlock(dst_vma-\u003evm_mm);\n-}\n-#endif\n-\n /**\n  * move_pages - move arbitrary anonymous pages of an existing vma\n  * @ctx: pointer to the userfaultfd context\ndiff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c\nindex 455441f1b6949..62defe70f3ceb 100644\n--- a/net/ipv4/tcp.c\n+++ b/net/ipv4/tcp.c\n@@ -2167,27 +2167,18 @@ static void tcp_zc_finalize_rx_tstamp(struct sock *sk,\n }\n \n static struct vm_area_struct *find_tcp_vma(struct mm_struct *mm,\n-\t\t\t\t\t   unsigned long address,\n-\t\t\t\t\t   bool *mmap_locked)\n+\t\t\t\t\t   unsigned long address)\n {\n-\tstruct vm_area_struct *vma = lock_vma_under_rcu(mm, address);\n+\tstruct vm_area_struct *vma = vma_start_read_unlocked(mm, address);\n \n-\tif (vma) {\n-\t\tif (vma-\u003evm_ops != \u0026tcp_vm_ops) {\n-\t\t\tvma_end_read(vma);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\t*mmap_locked = false;\n-\t\treturn vma;\n-\t}\n+\tif (!vma)\n+\t\treturn NULL;\n \n-\tmmap_read_lock(mm);\n-\tvma = vma_lookup(mm, address);\n-\tif (!vma || vma-\u003evm_ops != \u0026tcp_vm_ops) {\n-\t\tmmap_read_unlock(mm);\n+\tif (vma-\u003evm_ops != \u0026tcp_vm_ops) {\n+\t\tvma_end_read(vma);\n \t\treturn NULL;\n \t}\n-\t*mmap_locked = true;\n+\n \treturn vma;\n }\n \n@@ -2208,7 +2199,6 @@ static int tcp_zerocopy_receive(struct sock *sk,\n \tu32 seq = tp-\u003ecopied_seq;\n \tu32 total_bytes_to_map;\n \tint inq = tcp_inq(sk);\n-\tbool mmap_locked;\n \tint ret;\n \n \tzc-\u003ecopybuf_len = 0;\n@@ -2233,7 +2223,7 @@ static int tcp_zerocopy_receive(struct sock *sk,\n \t\treturn 0;\n \t}\n \n-\tvma = find_tcp_vma(current-\u003emm, address, \u0026mmap_locked);\n+\tvma = find_tcp_vma(current-\u003emm, address);\n \tif (!vma)\n \t\treturn -EINVAL;\n \n@@ -2315,10 +2305,7 @@ static int tcp_zerocopy_receive(struct sock *sk,\n \t\t\t\t\t\t   zc, total_bytes_to_map);\n \t}\n out:\n-\tif (mmap_locked)\n-\t\tmmap_read_unlock(current-\u003emm);\n-\telse\n-\t\tvma_end_read(vma);\n+\tvma_end_read(vma);\n \t/* Try to copy straggler data. */\n \tif (!ret)\n \t\tcopylen = tcp_zc_handle_leftover(zc, sk, skb, \u0026seq, copybuf_len, tss);\ndiff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs\nindex 4764d7b68f2a7..58bc1793fdaf5 100644\n--- a/rust/kernel/mm.rs\n+++ b/rust/kernel/mm.rs\n@@ -170,30 +170,48 @@ pub unsafe fn from_raw\u003c'a\u003e(ptr: *const bindings::mm_struct) -\u003e \u0026'a MmWithUser {\n     ///\n     /// This is an optimistic trylock operation, so it may fail if there is contention. In that\n     /// case, you should fall back to taking the mmap read lock.\n-    ///\n-    /// When per-vma locks are disabled, this always returns `None`.\n     #[inline]\n     pub fn lock_vma_under_rcu(\u0026self, vma_addr: usize) -\u003e Option\u003cVmaReadGuard\u003c'_\u003e\u003e {\n-        #[cfg(CONFIG_PER_VMA_LOCK)]\n-        {\n-            // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where\n-            // `mm_users` is non-zero.\n-            let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };\n-            if !vma.is_null() {\n-                return Some(VmaReadGuard {\n-                    // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a\n-                    // valid vma. The vma is stable for as long as the vma read lock is held.\n-                    vma: unsafe { VmaRef::from_raw(vma) },\n-                    _nts: NotThreadSafe,\n-                });\n-            }\n+        // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where\n+        // `mm_users` is non-zero.\n+        let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };\n+        if vma.is_null() {\n+            return None;\n         }\n+        Some(VmaReadGuard {\n+            // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a\n+            // valid vma. The vma is stable for as long as the vma read lock is held.\n+            vma: unsafe { VmaRef::from_raw(vma) },\n+            _nts: NotThreadSafe,\n+        })\n+    }\n \n-        // Silence warnings about unused variables.\n-        #[cfg(not(CONFIG_PER_VMA_LOCK))]\n-        let _ = vma_addr;\n-\n-        None\n+    /// Find the VMA covering 'address' and read-lock it.\n+    ///\n+    /// The fast path does not take mmap_lock. Waits for writers to finish if the\n+    /// VMA is being modified by taking mmap_lock.\n+    /// Use when mmap_lock is not held, otherwise use vma_start_read_locked().\n+    /// Nothing prevents VMAs being unmapped/mapped before or after the VMA is\n+    /// looked up, if a stronger guarantee is required, take an mmap_lock.\n+    ///\n+    /// Return: If a VMA exists which spans @address, return that VMA, read-locked.\n+    /// If no VMA is mapped there or, very unlikely, a reference count overflow\n+    /// occurred, return NULL.\n+    #[inline]\n+    pub fn vma_start_read_unlocked(\u0026self, vma_addr: usize) -\u003e Option\u003cVmaReadGuard\u003c'_\u003e\u003e {\n+        // SAFETY: We may invoke `vma_start_read_unlocked` because we know this `mm` has non-zero\n+        // `mm_users`.\n+        let vma = unsafe { bindings::vma_start_read_unlocked(self.as_raw(), vma_addr) };\n+        if vma.is_null() {\n+            return None;\n+        }\n+        // INVARIANT: We just acquired the VMA read lock.\n+        Some(VmaReadGuard {\n+            // SAFETY: If `vma_start_read_unlocked` returns a non-null ptr, then it points at a\n+            // valid vma. The vma is stable for as long as the vma read lock is held.\n+            vma: unsafe { VmaRef::from_raw(vma) },\n+            _nts: NotThreadSafe,\n+        })\n     }\n \n     /// Lock the mmap read lock.\ndiff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h\nindex 4c58487b764e9..57046d8ac81d8 100644\n--- a/tools/testing/vma/include/dup.h\n+++ b/tools/testing/vma/include/dup.h\n@@ -560,7 +560,6 @@ struct vm_area_struct {\n \t\tvma_flags_t flags;\n \t};\n \n-#ifdef CONFIG_PER_VMA_LOCK\n \t/*\n \t * Can only be written (using WRITE_ONCE()) while holding both:\n \t *  - mmap_lock (in write mode)\n@@ -576,7 +575,7 @@ struct vm_area_struct {\n \t * slowpath.\n \t */\n \tunsigned int vm_lock_seq;\n-#endif\n+\n \tunsigned int __vm_anon_pgoff_lo;\n \n \t/*\n@@ -610,10 +609,8 @@ struct vm_area_struct {\n #ifdef CONFIG_NUMA_BALANCING\n \tstruct vma_numab_state *numab_state;\t/* NUMA Balancing state */\n #endif\n-#ifdef CONFIG_PER_VMA_LOCK\n \t/* Unstable RCU readers are allowed to read this. */\n \trefcount_t vm_refcnt;\n-#endif\n #ifdef CONFIG_64BIT\n \tunsigned int __vm_anon_pgoff_hi;\n #endif\ndiff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h\nindex 8a48b231aa7ab..54d5c3360aa26 100644\n--- a/tools/testing/vma/vma_internal.h\n+++ b/tools/testing/vma/vma_internal.h\n@@ -15,7 +15,6 @@\n #include \u003cstdlib.h\u003e\n \n #define CONFIG_MMU\t\t1\n-#define CONFIG_PER_VMA_LOCK\t1\n \n #ifdef __CONCAT\n #undef __CONCAT\n"
}

2/1 2026/09/01 03:00 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "ANDROID_BINDER_IPC",
    "USERFAULTFD",
    "BPF_SYSCALL",
    "INET"
  ],
  "FocusSymbols": [
    "vma_start_read_unlocked",
    "lock_vma_under_rcu",
    "binder_alloc_free_page",
    "bpf_iter_task_vma_new"
  ],
  "Reasoning": "The patch removes the CONFIG_PER_VMA_LOCK option, making per-VMA locking unconditionally enabled for MMU systems, and introduces the new vma_start_read_unlocked() helper. It refactors lock acquisition across Android Binder, TCP zerocopy receive, userfaultfd, procfs task MMU maps, and BPF VMA iterators, directly changing concurrency and locking semantics in core MM and associated subsystems.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 6de62710957b1bbdf4b16790469d862343524d69
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 1 03:00:36 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f10379dfe7a33..3075e5c60a25e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -42,7 +42,6 @@ config ARM
 	select ARCH_SUPPORTS_ATOMIC_RMW
 	select ARCH_SUPPORTS_CFI
 	select ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_RT
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b5a51b0ef9440..2bbeded33da0d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -81,7 +81,6 @@ config ARM64
 	select ARCH_HAS_PTE_PROTNONE
 	select ARCH_SUPPORTS_NUMA_BALANCING
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE
 	select ARCH_SUPPORTS_RT
 	select ARCH_SUPPORTS_SCHED_SMT
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index a21f51e5815e9..9c5def7062222 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -69,7 +69,6 @@ config LOONGARCH
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
 	select ARCH_HAS_PTE_PROTNONE if 64BIT
 	select ARCH_SUPPORTS_NUMA_BALANCING if NUMA
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_RT
 	select ARCH_SUPPORTS_SCHED_SMT if SMP
 	select ARCH_SUPPORTS_SCHED_MC  if SMP
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
index b5ad7c173ef0c..dd8f6060fb7a2 100644
--- a/arch/powerpc/platforms/powernv/Kconfig
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -17,7 +17,6 @@ config PPC_POWERNV
 	select PPC_DOORBELL
 	select MMU_NOTIFIER
 	select FORCE_SMP
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select PPC_RADIX_BROADCAST_TLBIE if PPC_RADIX_MMU
 	default y
 
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 74910ce3a541c..7d125e288f6ef 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -23,7 +23,6 @@ config PPC_PSERIES
 	select HOTPLUG_CPU
 	select FORCE_SMP
 	select SWIOTLB
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select PPC_RADIX_BROADCAST_TLBIE if PPC_RADIX_MMU
 	default y
 
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index f8e26c4bed2ba..505eed4af932b 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -72,7 +72,6 @@ config RISCV
 	select ARCH_SUPPORTS_LTO_CLANG_THIN
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS if 64BIT && MMU
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
-	select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
 	select ARCH_HAS_PTE_PROTNONE if MMU
 	select ARCH_SUPPORTS_RT
 	select ARCH_SUPPORTS_SHADOW_CALL_STACK if HAVE_SHADOW_CALL_STACK
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 4b51bc6e8948d..b88b850421369 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -156,7 +156,6 @@ config S390
 	select ARCH_HAS_PTE_PROTNONE
 	select ARCH_SUPPORTS_NUMA_BALANCING
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_USES_CFI_GENERIC_LLVM_PASS if CC_IS_CLANG
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 15fd9ec5ecacb..ac92b3fd70c64 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -27,7 +27,6 @@ config X86_64
 	select ARCH_HAS_GIGANTIC_PAGE
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
 	select ARCH_SUPPORTS_INT128 if CC_HAS_INT128
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE
 	select HAVE_ARCH_SOFT_DIRTY
 	select MODULES_USE_ELF_RELA
@@ -1849,7 +1848,6 @@ config X86_USER_SHADOW_STACK
 	bool "X86 userspace shadow stack"
 	depends on AS_WRUSS
 	depends on X86_64
-	depends on PER_VMA_LOCK
 	select ARCH_USES_HIGH_VMA_FLAGS
 	select ARCH_HAS_USER_SHADOW_STACK
 	select X86_CET
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index 52ffbf3504e7f..71febd3d5b073 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -439,22 +439,9 @@ unsafe fn use_page_slow(&self, i: usize) -> Result<()> {
         // workqueue.
         let mm = MmWithUser::into_mmput_async(self.mm.mmget_not_zero().ok_or(ESRCH)?);
         {
-            let vma_read;
-            let mmap_read;
-            let vma = if let Some(ret) = mm.lock_vma_under_rcu(vma_addr) {
-                vma_read = ret;
-                check_vma(&vma_read, self)
-            } else {
-                mmap_read = mm.mmap_read_lock();
-                mmap_read
-                    .vma_lookup(vma_addr)
-                    .and_then(|vma| check_vma(vma, self))
-            };
-
-            match vma {
-                Some(vma) => vma.vm_insert_page(user_page_addr, &new_page)?,
-                None => return Err(ESRCH),
-            }
+            let vma_read_guard = mm.vma_start_read_unlocked(vma_addr).ok_or(ESRCH)?;
+            let vma = check_vma(&vma_read_guard, self).ok_or(ESRCH)?;
+            vma.vm_insert_page(user_page_addr, &new_page)?;
         }
 
         let inner = self.lock.lock();
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index e4488ad86a655..d6eae0aa70854 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -259,21 +259,14 @@ static int binder_page_insert(struct binder_alloc *alloc,
 	struct vm_area_struct *vma;
 	int ret = -ESRCH;
 
-	/* attempt per-vma lock first */
-	vma = lock_vma_under_rcu(mm, addr);
-	if (vma) {
-		if (binder_alloc_is_mapped(alloc))
-			ret = vm_insert_page(vma, addr, page);
-		vma_end_read(vma);
+	vma = vma_start_read_unlocked(mm, addr);
+	if (!vma)
 		return ret;
-	}
 
-	/* fall back to mmap_lock */
-	mmap_read_lock(mm);
-	vma = vma_lookup(mm, addr);
-	if (vma && binder_alloc_is_mapped(alloc))
+	if (binder_alloc_is_mapped(alloc))
 		ret = vm_insert_page(vma, addr, page);
-	mmap_read_unlock(mm);
+
+	vma_end_read(vma);
 
 	return ret;
 }
@@ -1142,7 +1135,6 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	struct vm_area_struct *vma;
 	struct page *page_to_free;
 	unsigned long page_addr;
-	int mm_locked = 0;
 	size_t index;
 
 	if (!mmget_not_zero(mm))
@@ -1151,27 +1143,25 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	index = mdata->page_index;
 	page_addr = alloc->vm_start + index * PAGE_SIZE;
 
-	/* attempt per-vma lock first */
+	/*
+	 * Attempt per-vma lock. This is essentially a
+	 * "trylock". It can fail even if the VMA exists
+	 * for 'page_addr'.
+	 */
 	vma = lock_vma_under_rcu(mm, page_addr);
 	if (!vma) {
-		/* fall back to mmap_lock */
-		if (!mmap_read_trylock(mm))
-			goto err_mmap_read_lock_failed;
-		mm_locked = 1;
-		vma = vma_lookup(mm, page_addr);
+		/*
+		 * If the vma exists, we can't continue because we cannot
+		 * remove the page from the vma. However, if the vma was
+		 * unmapped, it's okay to continue.
+		 */
+		if (binder_alloc_is_mapped(alloc))
+			goto err_vma_lock_failed;
 	}
 
 	if (!mutex_trylock(&alloc->mutex))
 		goto err_get_alloc_mutex_failed;
 
-	/*
-	 * Since a binder_alloc can only be mapped once, we ensure
-	 * the vma corresponds to this mapping by checking whether
-	 * the binder_alloc is still mapped.
-	 */
-	if (vma && !binder_alloc_is_mapped(alloc))
-		goto err_invalid_vma;
-
 	trace_binder_unmap_kernel_start(alloc, index);
 
 	page_to_free = alloc->pages[index];
@@ -1182,7 +1172,12 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	list_lru_isolate(lru, item);
 	spin_unlock(&lru->lock);
 
-	if (vma) {
+	/*
+	 * Since a binder_alloc can only be mapped once, we ensure
+	 * the vma corresponds to this mapping by checking whether
+	 * the binder_alloc is still mapped.
+	 */
+	if (vma && binder_alloc_is_mapped(alloc)) {
 		trace_binder_unmap_user_start(alloc, index);
 
 		zap_vma_range(vma, page_addr, PAGE_SIZE);
@@ -1191,23 +1186,17 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	}
 
 	mutex_unlock(&alloc->mutex);
-	if (mm_locked)
-		mmap_read_unlock(mm);
-	else
+	if (vma)
 		vma_end_read(vma);
 	mmput_async(mm);
 	binder_free_page(page_to_free);
 
 	return LRU_REMOVED_RETRY;
 
-err_invalid_vma:
-	mutex_unlock(&alloc->mutex);
 err_get_alloc_mutex_failed:
-	if (mm_locked)
-		mmap_read_unlock(mm);
-	else
+	if (vma)
 		vma_end_read(vma);
-err_mmap_read_lock_failed:
+err_vma_lock_failed:
 	mmput_async(mm);
 err_mmget:
 	return LRU_SKIP;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 04bd6c9e65a72..623bb43ede550 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -385,10 +385,8 @@ struct mem_size_stats;
 
 struct proc_maps_locking_ctx {
 	struct mm_struct *mm;
-#ifdef CONFIG_PER_VMA_LOCK
 	bool mmap_locked;
 	struct vm_area_struct *locked_vma;
-#endif
 };
 
 struct proc_maps_private {
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 5c54aebe21182..e671b4fd8dedd 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -130,8 +130,6 @@ static void release_task_mempolicy(struct proc_maps_private *priv)
 }
 #endif
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)
 {
 	int ret = mmap_read_lock_killable(lock_ctx->mm);
@@ -233,46 +231,6 @@ static inline void reacquire_rcu(struct proc_maps_private *priv)
 	vma_iter_set(&priv->iter, priv->lock_ctx.locked_vma->vm_end);
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)
-{
-	return mmap_read_lock_killable(lock_ctx->mm);
-}
-
-static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)
-{
-	mmap_read_unlock(lock_ctx->mm);
-}
-
-static inline bool lock_vma_range(struct seq_file *m,
-				  struct proc_maps_locking_ctx *lock_ctx)
-{
-	return lock_ctx_mm(lock_ctx) == 0;
-}
-
-static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx)
-{
-	unlock_ctx_mm(lock_ctx);
-}
-
-static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv,
-					   loff_t last_pos)
-{
-	return vma_next(&priv->iter);
-}
-
-static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv,
-					 loff_t pos)
-{
-	return false;
-}
-
-static inline void drop_rcu(struct proc_maps_private *priv) {}
-static inline void reacquire_rcu(struct proc_maps_private *priv) {}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 static struct vm_area_struct *proc_get_vma(struct seq_file *m, loff_t *ppos)
 {
 	struct proc_maps_private *priv = m->private;
@@ -560,8 +518,6 @@ static int pid_maps_open(struct inode *inode, struct file *file)
 		PROCMAP_QUERY_VMA_FLAGS				\
 )
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx)
 {
 	reset_lock_ctx(lock_ctx);
@@ -612,26 +568,6 @@ static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ct
 	return vma;
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx)
-{
-	return mmap_read_lock_killable(lock_ctx->mm);
-}
-
-static void query_vma_teardown(struct proc_maps_locking_ctx *lock_ctx)
-{
-	mmap_read_unlock(lock_ctx->mm);
-}
-
-static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ctx *lock_ctx,
-						     unsigned long addr)
-{
-	return find_vma(lock_ctx->mm, addr);
-}
-
-#endif  /* CONFIG_PER_VMA_LOCK */
-
 static struct vm_area_struct *query_matching_vma(struct proc_maps_locking_ctx *lock_ctx,
 						 unsigned long addr, u32 flags)
 {
@@ -1314,8 +1250,6 @@ static const struct mm_walk_ops smaps_shmem_walk_ops = {
 	.walk_lock		= PGWALK_RDLOCK,
 };
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 static const struct mm_walk_ops smaps_walk_vma_lock_ops = {
 	.pmd_entry		= smaps_pte_range,
 	.hugetlb_entry		= smaps_hugetlb_range,
@@ -1345,22 +1279,6 @@ get_smaps_shmem_walk_ops(struct proc_maps_private *priv)
 	return &smaps_shmem_walk_vma_lock_ops;
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline const struct mm_walk_ops *
-get_smaps_walk_ops(struct proc_maps_private *priv)
-{
-	return &smaps_walk_ops;
-}
-
-static inline const struct mm_walk_ops *
-get_smaps_shmem_walk_ops(struct proc_maps_private *priv)
-{
-	return &smaps_shmem_walk_ops;
-}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 /*
  * Gather mem stats from @vma with the indicated beginning
  * address @start, and keep them in @mss.
@@ -3497,7 +3415,6 @@ static const struct mm_walk_ops show_numa_ops = {
 	.walk_lock = PGWALK_RDLOCK,
 };
 
-#ifdef CONFIG_PER_VMA_LOCK
 static const struct mm_walk_ops show_numa_vma_lock_ops = {
 	.hugetlb_entry = gather_hugetlb_stats,
 	.pmd_entry = gather_pte_stats,
@@ -3512,16 +3429,6 @@ get_show_numa_ops(struct proc_maps_private *priv)
 	return &show_numa_vma_lock_ops;
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline const struct mm_walk_ops *
-get_show_numa_ops(struct proc_maps_private *priv)
-{
-	return &show_numa_ops;
-}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 /*
  * Display pages allocated per node and memory policy via /proc.
  */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index dd09c438fa23e..7aaa25abc241a 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -928,7 +928,6 @@ static inline void vma_numab_state_free(struct vm_area_struct *vma) {}
  * These must be here rather than mmap_lock.h as dependent on vm_fault type,
  * declared in this header.
  */
-#ifdef CONFIG_PER_VMA_LOCK
 static inline void release_fault_lock(struct vm_fault *vmf)
 {
 	if (vmf->flags & FAULT_FLAG_VMA_LOCK)
@@ -944,17 +943,6 @@ static inline void assert_fault_locked(const struct vm_fault *vmf)
 	else
 		mmap_assert_locked(vmf->vma->vm_mm);
 }
-#else
-static inline void release_fault_lock(struct vm_fault *vmf)
-{
-	mmap_read_unlock(vmf->vma->vm_mm);
-}
-
-static inline void assert_fault_locked(const struct vm_fault *vmf)
-{
-	mmap_assert_locked(vmf->vma->vm_mm);
-}
-#endif /* CONFIG_PER_VMA_LOCK */
 
 static inline bool mm_flags_test(int flag, const struct mm_struct *mm)
 {
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 6d815f6440c94..5413bd10fff2c 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -950,7 +950,6 @@ struct vm_area_struct {
 		vma_flags_t flags;
 	};
 
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Can only be written (using WRITE_ONCE()) while holding both:
 	 *  - mmap_lock (in write mode)
@@ -966,7 +965,7 @@ struct vm_area_struct {
 	 * slowpath.
 	 */
 	unsigned int vm_lock_seq;
-#endif
+
 	/*
 	 * Low 32-bits of anonymous page offset.
 	 * See vma_start_anon_pgoff() comment for details.
@@ -1003,7 +1002,6 @@ struct vm_area_struct {
 #ifdef CONFIG_NUMA_BALANCING
 	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
 #endif
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Used to keep track of firstly, whether the VMA is attached, secondly,
 	 * if attached, how many read locks are taken, and thirdly, if the
@@ -1046,7 +1044,6 @@ struct vm_area_struct {
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	struct lockdep_map vmlock_dep_map;
 #endif
-#endif
 #ifdef CONFIG_64BIT
 	/*
 	 * High 32-bits of anonymous page offset.
@@ -1254,7 +1251,6 @@ struct mm_struct {
 					  * init_mm.mmlist, and are protected
 					  * by mmlist_lock
 					  */
-#ifdef CONFIG_PER_VMA_LOCK
 		struct rcuwait vma_writer_wait;
 		/*
 		 * This field has lock-like semantics, meaning it is sometimes
@@ -1274,7 +1270,7 @@ struct mm_struct {
 		 * mmap_lock.
 		 */
 		seqcount_t mm_lock_seq;
-#endif
+
 		struct futex_mm_data	futex;
 
 		unsigned long hiwater_rss; /* High-watermark of RSS usage */
diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h
index bec0eab6ef035..00eae65b74bd6 100644
--- a/include/linux/mmap_lock.h
+++ b/include/linux/mmap_lock.h
@@ -76,8 +76,6 @@ static inline void mmap_assert_write_locked(const struct mm_struct *mm)
 	rwsem_assert_held_write(&mm->mmap_lock);
 }
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 #ifdef CONFIG_LOCKDEP
 #define __vma_lockdep_map(vma) (&vma->vmlock_dep_map)
 #else
@@ -230,10 +228,14 @@ static inline void vma_refcount_put(struct vm_area_struct *vma)
 }
 
 /*
- * Use only while holding mmap read lock which guarantees that locking will not
- * fail (nobody can concurrently write-lock the vma). vma_start_read() should
+ * Use only while holding mmap read lock which guarantees that vma lock is not
+ * contended (nobody can concurrently write-lock the vma). vma_start_read() should
  * not be used in such cases because it might fail due to mm_lock_seq overflow.
  * This functionality is used to obtain vma read lock and drop the mmap read lock.
+ *
+ * VMA can't be detached while we are holding mmap lock, therefore in practice this
+ * function can fail only when there are so many readers that vm_refcnt overflows.
+ * The failure case is very unlikely and is already annotated as such internally.
  */
 static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int subclass)
 {
@@ -249,16 +251,23 @@ static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int
 }
 
 /*
- * Use only while holding mmap read lock which guarantees that locking will not
- * fail (nobody can concurrently write-lock the vma). vma_start_read() should
+ * Use only while holding mmap read lock which guarantees that vma lock is not
+ * contended (nobody can concurrently write-lock the vma). vma_start_read() should
  * not be used in such cases because it might fail due to mm_lock_seq overflow.
  * This functionality is used to obtain vma read lock and drop the mmap read lock.
+ *
+ * VMA can't be detached while we are holding mmap lock, therefore in practice this
+ * function can fail only when there are so many readers that vm_refcnt overflows.
+ * The failure case is very unlikely and is already annotated as such internally.
  */
 static inline bool vma_start_read_locked(struct vm_area_struct *vma)
 {
 	return vma_start_read_locked_nested(vma, 0);
 }
 
+struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,
+					       unsigned long address);
+
 static inline void vma_end_read(struct vm_area_struct *vma)
 {
 	vma_refcount_put(vma);
@@ -297,6 +306,9 @@ int __vma_start_write(struct vm_area_struct *vma, int state);
  */
 static inline void vma_start_write(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	if (__is_vma_write_locked(vma))
 		return;
 
@@ -319,6 +331,9 @@ static inline void vma_start_write(struct vm_area_struct *vma)
 static inline __must_check
 int vma_start_write_killable(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return 0;
+
 	if (__is_vma_write_locked(vma))
 		return 0;
 
@@ -331,6 +346,11 @@ int vma_start_write_killable(struct vm_area_struct *vma)
  */
 static inline void vma_assert_write_locked(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU)) {
+		mmap_assert_write_locked(vma->vm_mm);
+		return;
+	}
+
 	VM_WARN_ON_ONCE_VMA(!__is_vma_write_locked(vma), vma);
 }
 
@@ -343,6 +363,11 @@ static inline void vma_assert_locked(struct vm_area_struct *vma)
 {
 	unsigned int refcnt;
 
+	if (!IS_ENABLED(CONFIG_MMU)) {
+		mmap_assert_locked(vma->vm_mm);
+		return;
+	}
+
 	if (IS_ENABLED(CONFIG_LOCKDEP)) {
 		if (!lock_is_held(__vma_lockdep_map(vma)))
 			vma_assert_write_locked(vma);
@@ -432,6 +457,9 @@ static inline bool vma_is_attached(struct vm_area_struct *vma)
  */
 static inline void vma_assert_attached(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	WARN_ON_ONCE(!vma_is_attached(vma));
 }
 
@@ -442,6 +470,9 @@ static inline void vma_assert_detached(struct vm_area_struct *vma)
 
 static inline void vma_mark_attached(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	vma_assert_write_locked(vma);
 	vma_assert_detached(vma);
 	refcount_set_release(&vma->vm_refcnt, 1);
@@ -451,6 +482,9 @@ void __vma_exclude_readers_for_detach(struct vm_area_struct *vma);
 
 static inline void vma_mark_detached(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	vma_assert_write_locked(vma);
 	vma_assert_attached(vma);
 
@@ -484,54 +518,6 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm,
 				     struct vma_iterator *iter,
 				     unsigned long address);
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline void mm_lock_seqcount_init(struct mm_struct *mm) {}
-static inline void mm_lock_seqcount_begin(struct mm_struct *mm) {}
-static inline void mm_lock_seqcount_end(struct mm_struct *mm) {}
-
-static inline bool mmap_lock_speculate_try_begin(struct mm_struct *mm, unsigned int *seq)
-{
-	return false;
-}
-
-static inline bool mmap_lock_speculate_retry(struct mm_struct *mm, unsigned int seq)
-{
-	return true;
-}
-static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt) {}
-static inline void vma_end_read(struct vm_area_struct *vma) {}
-static inline void vma_start_write(struct vm_area_struct *vma) {}
-static inline __must_check
-int vma_start_write_killable(struct vm_area_struct *vma) { return 0; }
-static inline void vma_assert_write_locked(struct vm_area_struct *vma)
-		{ mmap_assert_write_locked(vma->vm_mm); }
-static inline bool vma_is_attached(struct vm_area_struct *vma)
-		{ return true; }
-static inline void vma_assert_attached(struct vm_area_struct *vma) {}
-static inline void vma_assert_detached(struct vm_area_struct *vma) {}
-static inline void vma_mark_attached(struct vm_area_struct *vma) {}
-static inline void vma_mark_detached(struct vm_area_struct *vma) {}
-
-static inline struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
-		unsigned long address)
-{
-	return NULL;
-}
-
-static inline void vma_assert_locked(struct vm_area_struct *vma)
-{
-	mmap_assert_locked(vma->vm_mm);
-}
-
-static inline void vma_assert_stabilised(struct vm_area_struct *vma)
-{
-	/* If no VMA locks, then either mmap lock suffices to stabilise. */
-	mmap_assert_locked(vma->vm_mm);
-}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 static inline void vma_assert_can_modify(struct vm_area_struct *vma)
 {
 	if (vma_is_attached(vma))
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index a839041e0d008..8fb70c2a9c8e6 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -272,13 +272,10 @@ struct stack_map_vma_lock {
 /*
  * Acquire a stable read-side reference on the VMA covering @ip.
  *
- * With CONFIG_PER_VMA_LOCK=y this returns a VMA with its per-VMA read
- * lock held and mmap_lock dropped, so the caller may sleep.
- *
- * With CONFIG_PER_VMA_LOCK=n it returns a VMA with mmap_lock still
- * held; the caller must snapshot any fields it needs and pin vm_file
- * with get_file() before stack_map_unlock_vma() drops mmap_lock, as
- * the VMA may be split, merged, or freed after that.
+ * On NOMMU configurations, returns with the mmap_lock held.  If the MMU
+ * is enabled, the per-VMA lock will be held instead.  The lock
+ * should be released with stack_map_unlock_vma() which will release the
+ * appropriate lock.  Once the lock is released, the VMA may be freed.
  *
  * Returns NULL on failure, in which case no lock is held.
  */
@@ -288,7 +285,6 @@ stack_map_lock_vma(struct stack_map_vma_lock *lock, unsigned long ip)
 	struct mm_struct *mm = lock->mm;
 	struct vm_area_struct *vma;
 
-	/* noop under !CONFIG_PER_VMA_LOCK */
 	vma = lock_vma_under_rcu(mm, ip);
 	if (vma) {
 		lock->vma = vma;
@@ -308,21 +304,20 @@ stack_map_lock_vma(struct stack_map_vma_lock *lock, unsigned long ip)
 		return NULL;
 	}
 
-#ifdef CONFIG_PER_VMA_LOCK
+#ifdef CONFIG_MMU
 	if (!vma_start_read_locked(vma)) {
 		mmap_read_unlock(mm);
 		return NULL;
 	}
 	mmap_read_unlock(mm);
 #endif
-
 	lock->vma = vma;
 	return vma;
 }
 
 static void stack_map_unlock_vma(struct stack_map_vma_lock *lock)
 {
-#ifdef CONFIG_PER_VMA_LOCK
+#ifdef CONFIG_MMU
 	vma_end_read(lock->vma);
 #else
 	mmap_read_unlock(lock->mm);
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index 13e1aabe6f886..c65ba1dcd8667 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -869,7 +869,7 @@ __bpf_kfunc int bpf_iter_task_vma_new(struct bpf_iter_task_vma *it,
 	BUILD_BUG_ON(sizeof(struct bpf_iter_task_vma_kern) != sizeof(struct bpf_iter_task_vma));
 	BUILD_BUG_ON(__alignof__(struct bpf_iter_task_vma_kern) != __alignof__(struct bpf_iter_task_vma));
 
-	if (!IS_ENABLED(CONFIG_PER_VMA_LOCK)) {
+	if (!IS_ENABLED(CONFIG_MMU)) {
 		kit->data = NULL;
 		return -EOPNOTSUPP;
 	}
diff --git a/kernel/fork.c b/kernel/fork.c
index 416758c8a3d43..22283bf849e15 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1083,9 +1083,7 @@ static void mmap_init_lock(struct mm_struct *mm)
 {
 	init_rwsem(&mm->mmap_lock);
 	mm_lock_seqcount_init(mm);
-#ifdef CONFIG_PER_VMA_LOCK
 	rcuwait_init(&mm->vma_writer_wait);
-#endif
 }
 
 static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
diff --git a/mm/Kconfig b/mm/Kconfig
index 604c58199acbf..ece5d37b4eb7c 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1430,18 +1430,6 @@ config LRU_GEN_WALKS_MMU
 	depends on LRU_GEN && ARCH_HAS_HW_PTE_YOUNG
 # }
 
-config ARCH_SUPPORTS_PER_VMA_LOCK
-       def_bool n
-
-config PER_VMA_LOCK
-	def_bool y
-	depends on ARCH_SUPPORTS_PER_VMA_LOCK && MMU && SMP
-	help
-	  Allow per-vma locking during page fault handling.
-
-	  This feature allows locking each virtual memory area separately when
-	  handling page faults instead of taking mmap_lock.
-
 config LOCK_MM_AND_FIND_VMA
 	bool
 	depends on !STACK_GROWSUP
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
index 15dca19dd07da..9eaa25d1cf234 100644
--- a/mm/Kconfig.debug
+++ b/mm/Kconfig.debug
@@ -310,7 +310,6 @@ config DEBUG_KMEMLEAK_VERBOSE
 
 config PER_VMA_LOCK_STATS
 	bool "Statistics for per-vma locks"
-	depends on PER_VMA_LOCK
 	help
 	  Say Y here to enable success, retry and failure counters of page
 	  faults handled under protection of per-vma locks. When enabled, the
diff --git a/mm/debug.c b/mm/debug.c
index 9a0297b3988d8..655e6bcc0e8d9 100644
--- a/mm/debug.c
+++ b/mm/debug.c
@@ -157,17 +157,13 @@ void dump_vma(const struct vm_area_struct *vma)
 	pr_emerg("vma %px start %px end %px mm %px\n"
 		"prot %lx anon_vma %px vm_ops %px\n"
 		"pgoff %lx file %px private_data %px\n"
-#ifdef CONFIG_PER_VMA_LOCK
 		"refcnt %x\n"
-#endif
 		"flags: %#lx(%pGv)\n",
 		vma, (void *)vma->vm_start, (void *)vma->vm_end, vma->vm_mm,
 		(unsigned long)pgprot_val(vma->vm_page_prot),
 		vma->anon_vma, vma->vm_ops, vma_start_pgoff(vma),
 		vma->vm_file, vma->vm_private_data,
-#ifdef CONFIG_PER_VMA_LOCK
 		refcount_read(&vma->vm_refcnt),
-#endif
 		vma->vm_flags, &vma->vm_flags);
 }
 EXPORT_SYMBOL(dump_vma);
diff --git a/mm/init-mm.c b/mm/init-mm.c
index 3e792aad76261..a1bb2c2d0284a 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -39,10 +39,8 @@ struct mm_struct init_mm = {
 	.page_table_lock =  __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
 	.arg_lock	=  __SPIN_LOCK_UNLOCKED(init_mm.arg_lock),
 	.mmlist		= LIST_HEAD_INIT(init_mm.mmlist),
-#ifdef CONFIG_PER_VMA_LOCK
 	.vma_writer_wait = __RCUWAIT_INITIALIZER(init_mm.vma_writer_wait),
 	.mm_lock_seq	= SEQCNT_ZERO(init_mm.mm_lock_seq),
-#endif
 #ifdef CONFIG_SCHED_MM_CID
 	.mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(init_mm.mm_cid.lock),
 #endif
diff --git a/mm/memory.c b/mm/memory.c
index 8b0c2c735d3de..7bd660d48ff7c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6817,7 +6817,6 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,
 				 !vma_is_cow_mapping(vma)))
 			return VM_FAULT_SIGSEGV;
 	}
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Per-VMA locks can't be used with FAULT_FLAG_RETRY_NOWAIT because of
 	 * the assumption that lock is dropped on VM_FAULT_RETRY.
@@ -6826,7 +6825,6 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,
 			(FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)) ==
 			(FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)))
 		return VM_FAULT_SIGSEGV;
-#endif
 
 	return 0;
 }
diff --git a/mm/mmap_lock.c b/mm/mmap_lock.c
index 898c2ef1e9580..2f94ee0fdee2a 100644
--- a/mm/mmap_lock.c
+++ b/mm/mmap_lock.c
@@ -43,9 +43,6 @@ void __mmap_lock_do_trace_released(struct mm_struct *mm, bool write)
 EXPORT_SYMBOL(__mmap_lock_do_trace_released);
 #endif /* CONFIG_TRACING */
 
-#ifdef CONFIG_MMU
-#ifdef CONFIG_PER_VMA_LOCK
-
 /* State shared across __vma_[start, end]_exclude_readers. */
 struct vma_exclude_readers_state {
 	/* Input parameters. */
@@ -299,6 +296,8 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
 	MA_STATE(mas, &mm->mm_mt, address, address);
 	struct vm_area_struct *vma;
 
+	if (!IS_ENABLED(CONFIG_MMU))
+		return NULL;
 retry:
 	rcu_read_lock();
 	vma = mas_walk(&mas);
@@ -341,6 +340,41 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
 	return NULL;
 }
 
+/**
+ * vma_start_read_unlocked() - Find the VMA covering 'address' and read-lock it.
+ * @mm: the mm_struct of the address space to search
+ * @address: address that the vma should contain
+ *
+ * The fast path does not take mmap_lock. Waits for writers to finish if the
+ * VMA is being modified by taking mmap_lock.
+ * Use when mmap_lock is not held, otherwise use vma_start_read_locked().
+ * Nothing prevents VMAs being unmapped/mapped before or after the VMA is
+ * looked up, if a stronger guarantee is required, take an mmap_lock.
+ *
+ * Return: If a VMA exists which spans @address, return that VMA, read-locked.
+ * If no VMA is mapped there or, very unlikely, a reference count overflow
+ * occurred, return NULL.
+ */
+struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,
+					       unsigned long address)
+{
+	struct vm_area_struct *vma;
+
+	/* Fast path: return stable VMA covering 'address': */
+	vma = lock_vma_under_rcu(mm, address);
+	if (vma)
+		return vma;
+
+	/* Slow path: preclude VMA writers by temporarily getting mmap read lock. */
+	mmap_read_lock(mm);
+	vma = vma_lookup(mm, address);
+	if (vma && !vma_start_read_locked(vma))
+		vma = NULL;
+	mmap_read_unlock(mm);
+
+	return vma;
+}
+
 static struct vm_area_struct *lock_next_vma_under_mmap_lock(struct mm_struct *mm,
 							    struct vma_iterator *vmi,
 							    unsigned long from_addr)
@@ -431,7 +465,6 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm,
 
 	return vma;
 }
-#endif /* CONFIG_PER_VMA_LOCK */
 
 #ifdef CONFIG_LOCK_MM_AND_FIND_VMA
 #include <linux/extable.h>
@@ -548,23 +581,3 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
 	return NULL;
 }
 #endif /* CONFIG_LOCK_MM_AND_FIND_VMA */
-
-#else /* CONFIG_MMU */
-
-/*
- * At least xtensa ends up having protection faults even with no
- * MMU.. No stack expansion, at least.
- */
-struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
-			unsigned long addr, struct pt_regs *regs)
-{
-	struct vm_area_struct *vma;
-
-	mmap_read_lock(mm);
-	vma = vma_lookup(mm, addr);
-	if (!vma)
-		mmap_read_unlock(mm);
-	return vma;
-}
-
-#endif /* CONFIG_MMU */
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index cc07fcf50e87b..7411702a37f58 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -444,7 +444,6 @@ static inline void process_mm_walk_lock(struct mm_struct *mm,
 static inline void process_vma_walk_lock(struct vm_area_struct *vma,
 					 enum page_walk_lock walk_lock)
 {
-#ifdef CONFIG_PER_VMA_LOCK
 	switch (walk_lock) {
 	case PGWALK_WRLOCK:
 		vma_start_write(vma);
@@ -459,7 +458,6 @@ static inline void process_vma_walk_lock(struct vm_area_struct *vma,
 		/* PGWALK_RDLOCK is handled by process_mm_walk_lock */
 		break;
 	}
-#endif
 }
 
 /*
diff --git a/mm/rmap.c b/mm/rmap.c
index d1819fd699380..fed0362e0bd0e 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -260,11 +260,9 @@ static void check_anon_vma_clone(struct vm_area_struct *dst,
 	/* For the anon_vma to be compatible, it can only be singular. */
 	VM_WARN_ON_ONCE(operation == VMA_OP_MERGE_UNFAULTED &&
 			!list_is_singular(&src->anon_vma_chain));
-#ifdef CONFIG_PER_VMA_LOCK
 	/* Only merging an unfaulted VMA leaves the destination attached. */
 	VM_WARN_ON_ONCE(operation != VMA_OP_MERGE_UNFAULTED &&
 			vma_is_attached(dst));
-#endif
 }
 
 static void maybe_reuse_anon_vma(struct vm_area_struct *dst,
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 74f04c323c50f..6ee933a3803cc 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -122,7 +122,6 @@ struct vm_area_struct *find_vma_and_prepare_anon(struct mm_struct *mm,
 	return vma;
 }
 
-#ifdef CONFIG_PER_VMA_LOCK
 /*
  * uffd_lock_vma() - Lookup and lock vma corresponding to @address.
  * @mm: mm to search vma in.
@@ -130,8 +129,10 @@ struct vm_area_struct *find_vma_and_prepare_anon(struct mm_struct *mm,
  *
  * Should be called without holding mmap_lock.
  *
- * Return: A locked vma containing @address, -ENOENT if no vma is found, or
- * -ENOMEM if anon_vma couldn't be allocated.
+ * Return: A locked vma containing @address, -ENOENT if no vma is found,
+ * -ENOMEM if anon_vma couldn't be allocated, or -EAGAIN if vma refcount
+ * overflow happened due to high number of readers and the caller should
+ * retry later.
  */
 static struct vm_area_struct *uffd_lock_vma(struct mm_struct *mm,
 				       unsigned long address)
@@ -182,34 +183,6 @@ static void uffd_mfill_unlock(struct vm_area_struct *vma)
 	vma_end_read(vma);
 }
 
-#else
-
-static struct vm_area_struct *uffd_mfill_lock(struct mm_struct *dst_mm,
-					      unsigned long dst_start,
-					      unsigned long len)
-{
-	struct vm_area_struct *dst_vma;
-
-	mmap_read_lock(dst_mm);
-	dst_vma = find_vma_and_prepare_anon(dst_mm, dst_start);
-	if (IS_ERR(dst_vma))
-		goto out_unlock;
-
-	if (validate_dst_vma(dst_vma, dst_start + len))
-		return dst_vma;
-
-	dst_vma = ERR_PTR(-ENOENT);
-out_unlock:
-	mmap_read_unlock(dst_mm);
-	return dst_vma;
-}
-
-static void uffd_mfill_unlock(struct vm_area_struct *vma)
-{
-	mmap_read_unlock(vma->vm_mm);
-}
-#endif
-
 static void mfill_put_vma(struct mfill_state *state)
 {
 	if (!state->vma)
@@ -1850,7 +1823,6 @@ int find_vmas_mm_locked(struct mm_struct *mm,
 	return 0;
 }
 
-#ifdef CONFIG_PER_VMA_LOCK
 static int uffd_move_lock(struct mm_struct *mm,
 			  unsigned long dst_start,
 			  unsigned long src_start,
@@ -1925,31 +1897,6 @@ static void uffd_move_unlock(struct vm_area_struct *dst_vma,
 		vma_end_read(dst_vma);
 }
 
-#else
-
-static int uffd_move_lock(struct mm_struct *mm,
-			  unsigned long dst_start,
-			  unsigned long src_start,
-			  struct vm_area_struct **dst_vmap,
-			  struct vm_area_struct **src_vmap)
-{
-	int err;
-
-	mmap_read_lock(mm);
-	err = find_vmas_mm_locked(mm, dst_start, src_start, dst_vmap, src_vmap);
-	if (err)
-		mmap_read_unlock(mm);
-	return err;
-}
-
-static void uffd_move_unlock(struct vm_area_struct *dst_vma,
-			     struct vm_area_struct *src_vma)
-{
-	mmap_assert_locked(src_vma->vm_mm);
-	mmap_read_unlock(dst_vma->vm_mm);
-}
-#endif
-
 /**
  * move_pages - move arbitrary anonymous pages of an existing vma
  * @ctx: pointer to the userfaultfd context
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 455441f1b6949..62defe70f3ceb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2167,27 +2167,18 @@ static void tcp_zc_finalize_rx_tstamp(struct sock *sk,
 }
 
 static struct vm_area_struct *find_tcp_vma(struct mm_struct *mm,
-					   unsigned long address,
-					   bool *mmap_locked)
+					   unsigned long address)
 {
-	struct vm_area_struct *vma = lock_vma_under_rcu(mm, address);
+	struct vm_area_struct *vma = vma_start_read_unlocked(mm, address);
 
-	if (vma) {
-		if (vma->vm_ops != &tcp_vm_ops) {
-			vma_end_read(vma);
-			return NULL;
-		}
-		*mmap_locked = false;
-		return vma;
-	}
+	if (!vma)
+		return NULL;
 
-	mmap_read_lock(mm);
-	vma = vma_lookup(mm, address);
-	if (!vma || vma->vm_ops != &tcp_vm_ops) {
-		mmap_read_unlock(mm);
+	if (vma->vm_ops != &tcp_vm_ops) {
+		vma_end_read(vma);
 		return NULL;
 	}
-	*mmap_locked = true;
+
 	return vma;
 }
 
@@ -2208,7 +2199,6 @@ static int tcp_zerocopy_receive(struct sock *sk,
 	u32 seq = tp->copied_seq;
 	u32 total_bytes_to_map;
 	int inq = tcp_inq(sk);
-	bool mmap_locked;
 	int ret;
 
 	zc->copybuf_len = 0;
@@ -2233,7 +2223,7 @@ static int tcp_zerocopy_receive(struct sock *sk,
 		return 0;
 	}
 
-	vma = find_tcp_vma(current->mm, address, &mmap_locked);
+	vma = find_tcp_vma(current->mm, address);
 	if (!vma)
 		return -EINVAL;
 
@@ -2315,10 +2305,7 @@ static int tcp_zerocopy_receive(struct sock *sk,
 						   zc, total_bytes_to_map);
 	}
 out:
-	if (mmap_locked)
-		mmap_read_unlock(current->mm);
-	else
-		vma_end_read(vma);
+	vma_end_read(vma);
 	/* Try to copy straggler data. */
 	if (!ret)
 		copylen = tcp_zc_handle_leftover(zc, sk, skb, &seq, copybuf_len, tss);
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
index 4764d7b68f2a7..58bc1793fdaf5 100644
--- a/rust/kernel/mm.rs
+++ b/rust/kernel/mm.rs
@@ -170,30 +170,48 @@ pub unsafe fn from_raw<'a>(ptr: *const bindings::mm_struct) -> &'a MmWithUser {
     ///
     /// This is an optimistic trylock operation, so it may fail if there is contention. In that
     /// case, you should fall back to taking the mmap read lock.
-    ///
-    /// When per-vma locks are disabled, this always returns `None`.
     #[inline]
     pub fn lock_vma_under_rcu(&self, vma_addr: usize) -> Option<VmaReadGuard<'_>> {
-        #[cfg(CONFIG_PER_VMA_LOCK)]
-        {
-            // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
-            // `mm_users` is non-zero.
-            let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
-            if !vma.is_null() {
-                return Some(VmaReadGuard {
-                    // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a
-                    // valid vma. The vma is stable for as long as the vma read lock is held.
-                    vma: unsafe { VmaRef::from_raw(vma) },
-                    _nts: NotThreadSafe,
-                });
-            }
+        // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
+        // `mm_users` is non-zero.
+        let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
+        if vma.is_null() {
+            return None;
         }
+        Some(VmaReadGuard {
+            // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a
+            // valid vma. The vma is stable for as long as the vma read lock is held.
+            vma: unsafe { VmaRef::from_raw(vma) },
+            _nts: NotThreadSafe,
+        })
+    }
 
-        // Silence warnings about unused variables.
-        #[cfg(not(CONFIG_PER_VMA_LOCK))]
-        let _ = vma_addr;
-
-        None
+    /// Find the VMA covering 'address' and read-lock it.
+    ///
+    /// The fast path does not take mmap_lock. Waits for writers to finish if the
+    /// VMA is being modified by taking mmap_lock.
+    /// Use when mmap_lock is not held, otherwise use vma_start_read_locked().
+    /// Nothing prevents VMAs being unmapped/mapped before or after the VMA is
+    /// looked up, if a stronger guarantee is required, take an mmap_lock.
+    ///
+    /// Return: If a VMA exists which spans @address, return that VMA, read-locked.
+    /// If no VMA is mapped there or, very unlikely, a reference count overflow
+    /// occurred, return NULL.
+    #[inline]
+    pub fn vma_start_read_unlocked(&self, vma_addr: usize) -> Option<VmaReadGuard<'_>> {
+        // SAFETY: We may invoke `vma_start_read_unlocked` because we know this `mm` has non-zero
+        // `mm_users`.
+        let vma = unsafe { bindings::vma_start_read_unlocked(self.as_raw(), vma_addr) };
+        if vma.is_null() {
+            return None;
+        }
+        // INVARIANT: We just acquired the VMA read lock.
+        Some(VmaReadGuard {
+            // SAFETY: If `vma_start_read_unlocked` returns a non-null ptr, then it points at a
+            // valid vma. The vma is stable for as long as the vma read lock is held.
+            vma: unsafe { VmaRef::from_raw(vma) },
+            _nts: NotThreadSafe,
+        })
     }
 
     /// Lock the mmap read lock.
diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
index 4c58487b764e9..57046d8ac81d8 100644
--- a/tools/testing/vma/include/dup.h
+++ b/tools/testing/vma/include/dup.h
@@ -560,7 +560,6 @@ struct vm_area_struct {
 		vma_flags_t flags;
 	};
 
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Can only be written (using WRITE_ONCE()) while holding both:
 	 *  - mmap_lock (in write mode)
@@ -576,7 +575,7 @@ struct vm_area_struct {
 	 * slowpath.
 	 */
 	unsigned int vm_lock_seq;
-#endif
+
 	unsigned int __vm_anon_pgoff_lo;
 
 	/*
@@ -610,10 +609,8 @@ struct vm_area_struct {
 #ifdef CONFIG_NUMA_BALANCING
 	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
 #endif
-#ifdef CONFIG_PER_VMA_LOCK
 	/* Unstable RCU readers are allowed to read this. */
 	refcount_t vm_refcnt;
-#endif
 #ifdef CONFIG_64BIT
 	unsigned int __vm_anon_pgoff_hi;
 #endif
diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h
index 8a48b231aa7ab..54d5c3360aa26 100644
--- a/tools/testing/vma/vma_internal.h
+++ b/tools/testing/vma/vma_internal.h
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 
 #define CONFIG_MMU		1
-#define CONFIG_PER_VMA_LOCK	1
 
 #ifdef __CONCAT
 #undef __CONCAT

3/2 2026/09/01 03:00 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18804
							output: 37
							thoughts: 1638
						

Thoughts:
**Analysis of a Kernel Patch for Fuzzing Potential**

Okay, let's dive into this patch. My immediate thought is to determine if it introduces changes significant enough to warrant fuzzing efforts. Let's start by inspecting the git diff. The removal of `ARCH_SUPPORTS_PER_VMA_LOCK` and `PER_VMA_LOCK` in the `Kconfig` files is a red flag. It implies that per-VMA locking, previously optional, is now a mandatory configuration, at least for MMU/SMP configurations and seems standard now. Let's verify this by checking the relevant header and source files like `include/linux/mmap_lock.h`, `mm/mmap_lock.c`, and so on.

The patch introduces a helper function, `vma_start_read_unlocked`, which, from what I'm seeing, seems to streamline VMA read locking. It looks like it tries a fast path with `lock_vma_under_rcu` and then falls back to acquiring `mmap_read_lock` if the fast path fails. The significant part is the refactoring of multiple locations, like `binder_page_insert`, `tcp_zerocopy_receive`, `task_mmu.c`, `userfaultfd.c`, and others, to use this new helper. It looks like the old conditional (`if (PER_VMA_LOCK)`) logic is removed.

Now, the question is, is this just a refactoring, or does it change the functional behavior? Let's zoom in on `vma_start_read_unlocked` in `mm/mmap_lock.c` and how it is used in the `tcp_zerocopy_receive` function. The key difference here is the lock handling. Previously, it would fall back to `mmap_read_lock` and hold it throughout the execution of `tcp_zerocopy_receive`. Now, it calls `vma_start_read_unlocked` which takes the per-vma read lock and then drops `mmap_lock` early with the call to `vma_end_read(vma)`.

This is a significant change in the locking model for memory management, TCP zero-copy, Binder, userfaultfd, and even BPF, which touches several crucial subsystems. The core MM and locking semantics, spanning diverse areas, are definitely being affected.

Based on the system prompt's criteria, this is *not* just documentation or Kconfig changes. It touches reachable core kernel logic (MM, network, Binder, proc, userfaultfd, and BPF), so I would conclude that `WorthFuzzing=true`.

Now, it's time to identify the relevant functions and configs and verify that they can actually be executed, using tools like `grepper` or other code search tools. Let's find the relevant symbols and configs and confirm the execution paths.



4/2 2026/09/01 03:00 tool grepper
0m Args:
{
  "Expression": "vma_start_read_unlocked",
  "PathPrefix": "mm/mmap_lock.c"
}

Results:
{
  "Output": "mm/mmap_lock.c=293=struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,\n--\nmm/mmap_lock.c-343-/**\nmm/mmap_lock.c:344: * vma_start_read_unlocked() - Find the VMA covering 'address' and read-lock it.\nmm/mmap_lock.c-345- * @mm: the mm_struct of the address space to search\n--\nmm/mmap_lock.c-357- */\nmm/mmap_lock.c:358:struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,\nmm/mmap_lock.c-359-\t\t\t\t\t       unsigned long address)\n"
}

5/2 2026/09/01 03:00 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 21317
							output: 29
							thoughts: 343
						

Thoughts:
**Analyzing the Impact of This Patch**

Okay, let's dive into this patch and map out its implications. First, I need to get a handle on the specific functions it touches. I've identified a list: `vma_start_read_unlocked` in `mm/mmap_lock.c`, `tcp_zerocopy_receive` in `net/ipv4/tcp.c`, several functions from the binder memory allocator, like `binder_page_insert` and `binder_alloc_free_page`, and a suite of VMA locking and iteration functions related to BPF, userfaultfd, and process memory maps. Specifically, I see `stack_map_lock_vma`, `bpf_iter_task_vma_new`, `uffd_lock_vma`, `query_matching_vma`, and `lock_vma_under_rcu`. The functions in `proc_get_vma` also catch my eye.

Now that I have a scope, I'll hone in on the key symbols, specifically those that are either exported, non-static, or otherwise of particular interest. This will help me prioritize which areas to scrutinize for potential interactions and impacts. I'll need to focus on files like `binder_alloc.c`, `net/ipv4/tcp.c`, `mm/mmap_lock.c`, and so forth. I'll make sure to note my progress.



6/2 2026/09/01 03:00 tool grepper
0m Args:
{
  "Expression": "binder_page_insert",
  "PathPrefix": "drivers/android/"
}

Results:
{
  "Output": "drivers/android/binder_alloc.c=234=static struct page *binder_page_lookup(struct binder_alloc *alloc,\n--\ndrivers/android/binder_alloc.c-253-\ndrivers/android/binder_alloc.c:254:static int binder_page_insert(struct binder_alloc *alloc,\ndrivers/android/binder_alloc.c-255-\t\t\t      unsigned long addr,\n--\ndrivers/android/binder_alloc.c=305=static int binder_install_single_page(struct binder_alloc *alloc,\n--\ndrivers/android/binder_alloc.c-320-\ndrivers/android/binder_alloc.c:321:\tret = binder_page_insert(alloc, addr, page);\ndrivers/android/binder_alloc.c-322-\tswitch (ret) {\n"
}

7/2 2026/09/01 03:00 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 22215
							output: 31
							thoughts: 57
						

Thoughts:
Okay, let's dive into this.

**Investigating Binder Memory Management**

Alright, I need to get a handle on how memory is being managed within this Binder implementation. My immediate thought is to start digging into a few key functions. Let's start with `binder_install_single_page`. Is that function handling single page allocations or related tasks?  I'll also want to look at `binder_alloc_new_buf` to understand how new buffers are created. Then, of course, I have to trace how memory gets freed, so `binder_alloc_free_page` seems essential. I need to understand its role.

Once I have a solid understanding of memory allocation and freeing within Binder, I will need to look at what calls `binder_alloc_free_page` and `binder_page_insert` so I can understand the logic and control flow of the Binder's memory management.



8/2 2026/09/01 03:00 tool grepper
0m Args:
{
  "Expression": "binder_alloc_free_page",
  "PathPrefix": "drivers/android/"
}

Results:
{
  "Output": "drivers/android/binder_alloc.c=1116=EXPORT_SYMBOL_IF_KUNIT(binder_alloc_vma_close);\n--\ndrivers/android/binder_alloc.c-1118-/**\ndrivers/android/binder_alloc.c:1119: * binder_alloc_free_page() - shrinker callback to free pages\ndrivers/android/binder_alloc.c-1120- * @item:   item to free\n--\ndrivers/android/binder_alloc.c-1126- */\ndrivers/android/binder_alloc.c:1127:enum lru_status binder_alloc_free_page(struct list_head *item,\ndrivers/android/binder_alloc.c-1128-\t\t\t\t       struct list_lru_one *lru,\n--\ndrivers/android/binder_alloc.c-1203-}\ndrivers/android/binder_alloc.c:1204:EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_page);\ndrivers/android/binder_alloc.c-1205-\n--\ndrivers/android/binder_alloc.c=1213=binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)\ndrivers/android/binder_alloc.c-1214-{\ndrivers/android/binder_alloc.c:1215:\treturn list_lru_walk(\u0026binder_freelist, binder_alloc_free_page,\ndrivers/android/binder_alloc.c-1216-\t\t\t    NULL, sc-\u003enr_to_scan);\n--\ndrivers/android/binder_alloc.h=107=struct binder_alloc {\n--\ndrivers/android/binder_alloc.h-123-\ndrivers/android/binder_alloc.h:124:enum lru_status binder_alloc_free_page(struct list_head *item,\ndrivers/android/binder_alloc.h-125-\t\t\t\t       struct list_lru_one *lru,\n--\ndrivers/android/tests/binder_alloc_kunit.c=204=static unsigned long binder_alloc_test_free_page(struct kunit *test,\n--\ndrivers/android/tests/binder_alloc_kunit.c-211-\twhile ((count = list_lru_count(alloc-\u003efreelist))) {\ndrivers/android/tests/binder_alloc_kunit.c:212:\t\tlist_lru_walk(alloc-\u003efreelist, binder_alloc_free_page,\ndrivers/android/tests/binder_alloc_kunit.c-213-\t\t\t      NULL, count);\n--\ndrivers/android/tests/binder_alloc_kunit.c=420=static void binder_alloc_test_mmap(struct kunit *test)\n--\ndrivers/android/tests/binder_alloc_kunit.c-446- * correctly allocated, put onto lru when buffers are freed, and\ndrivers/android/tests/binder_alloc_kunit.c:447: * are freed when binder_alloc_free_page() is called.\ndrivers/android/tests/binder_alloc_kunit.c-448- */\n"
}

9/2 2026/09/01 03:00 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 23268
							output: 32
							thoughts: 81
						

Thoughts:
Here's my summary, as though I'm thinking through this myself:

**Quick Audit of Potential Shared Functions**

Okay, so I'm digging into potential areas for further investigation. The `binder_alloc_free_page` function immediately sticks out – non-static, that's a key indicator for potential exposure. Now, let's broaden the scope. I'm wondering about `vma_start_read_unlocked` in `mm/mmap_lock.c`. Another non-static function – worth a second look, depending on the context. And just to round out my initial pass, let me peek into `net/ipv4/tcp.c` and take a look at `tcp_zerocopy_receive`. Let's see what that looks like.



10/2 2026/09/01 03:00 tool grepper
0m Args:
{
  "Expression": "tcp_zerocopy_receive",
  "PathPrefix": "net/ipv4/"
}

Results:
{
  "Output": "net/ipv4/tcp.c=1935=static void tcp_zerocopy_set_hint_for_skb(struct sock *sk,\nnet/ipv4/tcp.c:1936:\t\t\t\t\t  struct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-1937-\t\t\t\t\t  struct sk_buff *skb, u32 offset)\n--\nnet/ipv4/tcp.c=1975=static int receive_fallback_to_copy(struct sock *sk,\nnet/ipv4/tcp.c:1976:\t\t\t\t    struct tcp_zerocopy_receive *zc, int inq,\nnet/ipv4/tcp.c-1977-\t\t\t\t    struct scm_timestamping_internal *tss)\n--\nnet/ipv4/tcp.c-2010-\nnet/ipv4/tcp.c:2011:static int tcp_copy_straggler_data(struct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-2012-\t\t\t\t   struct sk_buff *skb, u32 copylen,\n--\nnet/ipv4/tcp.c-2034-\nnet/ipv4/tcp.c:2035:static int tcp_zc_handle_leftover(struct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-2036-\t\t\t\t  struct sock *sk,\n--\nnet/ipv4/tcp.c=2062=static int tcp_zerocopy_vm_insert_batch_error(struct vm_area_struct *vma,\n--\nnet/ipv4/tcp.c-2067-\t\t\t\t\t      u32 *seq,\nnet/ipv4/tcp.c:2068:\t\t\t\t\t      struct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-2069-\t\t\t\t\t      u32 total_bytes_to_map,\n--\nnet/ipv4/tcp.c=2110=static int tcp_zerocopy_vm_insert_batch(struct vm_area_struct *vma,\n--\nnet/ipv4/tcp.c-2115-\t\t\t\t\tu32 *seq,\nnet/ipv4/tcp.c:2116:\t\t\t\t\tstruct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-2117-\t\t\t\t\tu32 total_bytes_to_map)\n--\nnet/ipv4/tcp.c=2143=static void tcp_zc_finalize_rx_tstamp(struct sock *sk,\nnet/ipv4/tcp.c:2144:\t\t\t\t      struct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-2145-\t\t\t\t      struct scm_timestamping_internal *tss)\n--\nnet/ipv4/tcp.c=2169=static struct vm_area_struct *find_tcp_vma(struct mm_struct *mm,\n--\nnet/ipv4/tcp.c-2185-#define TCP_ZEROCOPY_PAGE_BATCH_SIZE 32\nnet/ipv4/tcp.c:2186:static int tcp_zerocopy_receive(struct sock *sk,\nnet/ipv4/tcp.c:2187:\t\t\t\tstruct tcp_zerocopy_receive *zc,\nnet/ipv4/tcp.c-2188-\t\t\t\tstruct scm_timestamping_internal *tss)\n--\nnet/ipv4/tcp.c=4468=int do_tcp_getsockopt(struct sock *sk, int level,\n--\nnet/ipv4/tcp.c-4730-\t\tstruct scm_timestamping_internal tss;\nnet/ipv4/tcp.c:4731:\t\tstruct tcp_zerocopy_receive zc = {};\nnet/ipv4/tcp.c-4732-\t\tint err;\n--\nnet/ipv4/tcp.c-4736-\t\tif (len \u003c 0 ||\nnet/ipv4/tcp.c:4737:\t\t    len \u003c offsetofend(struct tcp_zerocopy_receive, length))\nnet/ipv4/tcp.c-4738-\t\t\treturn -EINVAL;\n--\nnet/ipv4/tcp.c-4754-\t\tsockopt_lock_sock(sk);\nnet/ipv4/tcp.c:4755:\t\terr = tcp_zerocopy_receive(sk, \u0026zc, \u0026tss);\nnet/ipv4/tcp.c-4756-\t\terr = BPF_CGROUP_RUN_PROG_GETSOCKOPT_KERN(sk, level, optname,\n--\nnet/ipv4/tcp.c-4758-\t\tsockopt_release_sock(sk);\nnet/ipv4/tcp.c:4759:\t\tif (len \u003e= offsetofend(struct tcp_zerocopy_receive, msg_flags))\nnet/ipv4/tcp.c-4760-\t\t\tgoto zerocopy_rcv_cmsg;\nnet/ipv4/tcp.c-4761-\t\tswitch (len) {\nnet/ipv4/tcp.c:4762:\t\tcase offsetofend(struct tcp_zerocopy_receive, msg_flags):\nnet/ipv4/tcp.c-4763-\t\t\tgoto zerocopy_rcv_cmsg;\nnet/ipv4/tcp.c:4764:\t\tcase offsetofend(struct tcp_zerocopy_receive, msg_controllen):\nnet/ipv4/tcp.c:4765:\t\tcase offsetofend(struct tcp_zerocopy_receive, msg_control):\nnet/ipv4/tcp.c:4766:\t\tcase offsetofend(struct tcp_zerocopy_receive, flags):\nnet/ipv4/tcp.c:4767:\t\tcase offsetofend(struct tcp_zerocopy_receive, copybuf_len):\nnet/ipv4/tcp.c:4768:\t\tcase offsetofend(struct tcp_zerocopy_receive, copybuf_address):\nnet/ipv4/tcp.c:4769:\t\tcase offsetofend(struct tcp_zerocopy_receive, err):\nnet/ipv4/tcp.c-4770-\t\t\tgoto zerocopy_rcv_sk_err;\nnet/ipv4/tcp.c:4771:\t\tcase offsetofend(struct tcp_zerocopy_receive, inq):\nnet/ipv4/tcp.c-4772-\t\t\tgoto zerocopy_rcv_inq;\nnet/ipv4/tcp.c:4773:\t\tcase offsetofend(struct tcp_zerocopy_receive, length):\nnet/ipv4/tcp.c-4774-\t\tdefault:\n"
}

11/2 2026/09/01 03:00 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 25206
							output: 29
							thoughts: 470
						

Thoughts:
**My Investigation into Potential Code Interactions**

Okay, so `do_tcp_getsockopt` calls `tcp_zerocopy_receive`, which has kicked off this line of inquiry. My first thought is to dig into the BPF interactions, so I'm starting with `kernel/bpf/task_iter.c` and `kernel/bpf/stackmap.c`. Specifically, I'm noting that `bpf_iter_task_vma_new` in `task_iter.c` is a BPF kfunc – that's potentially significant.

Next, I need to understand how this might affect `userfaultfd`. Looking at `mm/userfaultfd.c`, I see `uffd_lock_vma` is called by `mfill_atomic`, which is further down the call stack to `userfaultfd_copy`, `userfaultfd_zeropage`, `userfaultfd_poison`, and related functions. And `uffd_move_lock` is used by `move_pages` which is related to `userfaultfd_move`, So I need to be careful with my locking.

I'm also checking `fs/proc/task_mmu.c`. Functions like `proc_get_vma` and `proc_maps_locking_ctx` are used in `/proc/[pid]/maps`, `/proc/[pid]/smaps`, `/proc/[pid]/numa_maps`, and `query_matching_vma`, which is related to the PROCMAP_QUERY ioctl, pidfd_getfd and maps. It's always worth checking proc.

Additionally, I'm examining `mm/mmap_lock.c` and noting the non-static functions `lock_vma_under_rcu` and `vma_start_read_unlocked` in this file, since they are relevant to VMA locking.

I'm then revisiting `kernel/bpf/stackmap.c` to look at `stack_map_lock_vma` and `stack_map_unlock_vma`, and confirming they are called by `bpf_get_stack` and stackmap functions.

Finally, a quick check of Kconfigs is in order. I need to figure out what configurations are relevant to the modified code. The first thing I've noticed is Android Binder:
`ANDROID_BINDER_IPC` and I need to check the Kconfig for Binder.



12/2 2026/09/01 03:01 tool grepper
0m Args:
{
  "Expression": "config ANDROID_BINDER",
  "PathPrefix": "drivers/android"
}

Results:
{
  "Output": "drivers/android/Kconfig=2=menu \"Android\"\ndrivers/android/Kconfig-3-\ndrivers/android/Kconfig:4:config ANDROID_BINDER_IPC\ndrivers/android/Kconfig-5-\tbool \"Android Binder IPC Driver\"\n--\ndrivers/android/Kconfig-16-\ndrivers/android/Kconfig:17:config ANDROID_BINDER_IPC_RUST\ndrivers/android/Kconfig-18-\tbool \"Rust version of Android Binder IPC Driver\"\n--\ndrivers/android/Kconfig-29-\ndrivers/android/Kconfig:30:config ANDROID_BINDERFS\ndrivers/android/Kconfig-31-\tbool \"Android Binderfs filesystem\"\n--\ndrivers/android/Kconfig-41-\ndrivers/android/Kconfig:42:config ANDROID_BINDER_DEVICES\ndrivers/android/Kconfig-43-\tstring \"Android Binder devices\"\n--\ndrivers/android/Kconfig-53-\ndrivers/android/Kconfig:54:config ANDROID_BINDER_ALLOC_KUNIT_TEST\ndrivers/android/Kconfig-55-\ttristate \"KUnit Tests for Android Binder Alloc\" if !KUNIT_ALL_TESTS\n"
}

13/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 26531
							output: 24
							thoughts: 32
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Investigating Userfaultfd Configuration Options**

Alright, time to dive in. I need to refresh my memory on the userfaultfd configuration options. Where are those typically defined, or rather, where do I *expect* to find them? My instincts are pointing me towards `fs/userfaultfd.c` – that's the obvious place for the core implementation, so any module-specific config would likely be alongside the source. But, you know how these things go, sometimes configurations are a bit more globally managed. Therefore, I should also double-check the global kernel configuration files for the potential options: the `mm/Kconfig` directory might contain memory management-related flags that influence userfaultfd behavior. And since it's an important subsystem feature, I can't overlook `init/Kconfig` for any early-boot or basic enablement configurations. Gotta be thorough. Let's start with those.



14/2 2026/09/01 03:01 tool grepper
0m Args:
{
  "Expression": "config USERFAULTFD",
  "PathPrefix": ""
}

Results:
{
  "Output": "mm/Kconfig=1372=config HAVE_ARCH_USERFAULTFD_MINOR\n--\nmm/Kconfig-1376-\nmm/Kconfig:1377:config USERFAULTFD_RWP\nmm/Kconfig-1378-\tdef_bool y\n--\nmm/Kconfig-1385-\nmm/Kconfig:1386:menuconfig USERFAULTFD\nmm/Kconfig-1387-\tbool \"Enable userfaultfd() system call\"\n"
}

15/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 26927
							output: 28
							thoughts: 394
						

Thoughts:
**My Current Investigative Path**

Okay, let's see what's on the menu today. First, I need to verify a few things regarding BPF configuration; let's check for `BPF_SYSCALL` along with the related stackmap and `task_iter` functionality. Then, let's pivot to the networking side of things. I'll need to confirm that `INET` and `NET` are enabled; this'll be important given some of the direction this is heading.

Next, I'll need to dig into memory management, particularly focusing on lock statistics. Let me check the `mm/Kconfig.debug` file for `PER_VMA_LOCK_STATS`. I see it's there; it enables counters for page faults handled under per-VMA locks. That's good to know.

I'm also going to quickly confirm that `CONFIG_MMU` is enabled, which should be standard but it's always good to verify. Just in case there's something else lurking, let's quickly scan the other configs again: `USERFAULTFD`, `ANDROID_BINDER_IPC`, `BPF_SYSCALL`, and `INET`.

Now, for some focus symbols. I prefer to zero in on non-static, non-inlined API entrypoints. That'll give me the best view of the system's "front door". Here are some I'm interested in:

*   `vma_start_read_unlocked` in `mm/mmap_lock.c` - a newly added core MM API function! That's going to be very interesting.
*   `lock_vma_under_rcu` in `mm/mmap_lock.c` - another core MM API function, so let's keep an eye on that as well.
*   `tcp_zerocopy_receive` in `net/ipv4/tcp.c` - This'll be relevant, given the initial networking focus.
*   `binder_alloc_free_page` in `drivers/android/binder_alloc.c` - Always good to check binder interactions.
*   `bpf_iter_task_vma_new` in `kernel/bpf/task_iter.c` - A BPF iterator, so this looks right.
*   `userfaultfd_copy` / `userfaultfd_move` in `mm/userfaultfd.c` or `fs/userfaultfd.c` - These are important to understand. I may want to focus on `userfaultfd_move` or `move_pages` in more detail later.



16/2 2026/09/01 03:01 tool grepper
0m Args:
{
  "Expression": "uffd_move_lock",
  "PathPrefix": "mm/"
}

Results:
{
  "Output": "mm/userfaultfd.c=1800=int find_vmas_mm_locked(struct mm_struct *mm,\n--\nmm/userfaultfd.c-1825-\nmm/userfaultfd.c:1826:static int uffd_move_lock(struct mm_struct *mm,\nmm/userfaultfd.c-1827-\t\t\t  unsigned long dst_start,\n--\nmm/userfaultfd.c=1967=static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start,\n--\nmm/userfaultfd.c-1985-\nmm/userfaultfd.c:1986:\terr = uffd_move_lock(mm, dst_start, src_start, \u0026dst_vma, \u0026src_vma);\nmm/userfaultfd.c-1987-\tif (err)\n"
}

17/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 28151
							output: 31
							thoughts: 15
						

Thoughts:
Here's my take on the thought process, as requested:

**Diving into `userfaultfd_move`**

Okay, time to get granular. I need to refresh my understanding of how `userfaultfd_move` is actually defined and declared. It's crucial for the next step, so I'm going to pull up the header files and/or the relevant source code. I need to make sure I have a solid grasp on the function signature, its parameters (types, especially), what it's expected to do, and any relevant macros or inline definitions involved. I'll need to double-check if there's any tricky pointer usage or alignment constraints I need to consider for whatever I'm troubleshooting, and cross-reference this with the documentation.



18/2 2026/09/01 03:01 tool grepper
0m Args:
{
  "Expression": "move_pages\\(",
  "PathPrefix": "mm/userfaultfd.c"
}

Results:
{
  "Output": "mm/userfaultfd.c=1892=static void uffd_move_unlock(struct vm_area_struct *dst_vma,\n--\nmm/userfaultfd.c-1909- *\nmm/userfaultfd.c:1910: * move_pages() remaps arbitrary anonymous pages atomically in zero\nmm/userfaultfd.c-1911- * copy. It only works on non shared anonymous pages because those can\n--\nmm/userfaultfd.c-1921- * storage or any other I/O device (MADV_DONTFORK in the source vma\nmm/userfaultfd.c:1922: * avoids move_pages() to fail with -EBUSY if the process forks before\nmm/userfaultfd.c:1923: * move_pages() is called), then it will call move_pages() to map the\nmm/userfaultfd.c-1924- * page in the faulting address in the destination vma.\n--\nmm/userfaultfd.c-1938- * the destination virtual memory range is not a whole unmapped hole,\nmm/userfaultfd.c:1939: * move_pages() will fail respectively with -ENOENT or -EEXIST. This\nmm/userfaultfd.c-1940- * provides a very strict behavior to avoid any chance of memory\n--\nmm/userfaultfd.c-1943- * time for any given faulting address. This means that if two threads\nmm/userfaultfd.c:1944: * try to both call move_pages() on the same destination address at the\nmm/userfaultfd.c-1945- * same time, the second thread will get an explicit error from this\n--\nmm/userfaultfd.c-1953- * an amount of bytes successfully moved. If the retval reports a\nmm/userfaultfd.c:1954: * \"short\" remap, the move_pages() command should be repeated by\nmm/userfaultfd.c-1955- * userland with src+retval, dst+reval, len-retval if it wants to know\n--\nmm/userfaultfd.c-1966- */\nmm/userfaultfd.c:1967:static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start,\nmm/userfaultfd.c-1968-\t\t   unsigned long src_start, unsigned long len, __u64 mode)\n--\nmm/userfaultfd.c=4504=static int userfaultfd_move(struct userfaultfd_ctx *ctx,\n--\nmm/userfaultfd.c-4543-\tif (mmget_not_zero(mm)) {\nmm/userfaultfd.c:4544:\t\tret = move_pages(ctx, uffdio_move.dst, uffdio_move.src,\nmm/userfaultfd.c-4545-\t\t\t\t uffdio_move.len, uffdio_move.mode);\n"
}

19/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 29097
							output: 36
							thoughts: 40
						

Thoughts:
Okay, let's dive into this. I need to quickly refresh my memory on `userfaultfd_move`. Is that function declared as static or not? The implications are pretty significant in this context. To be absolutely sure, I'll jump into the code. Line 4504 in `mm/userfaultfd.c` should have the answer. I'm heading there now.



20/2 2026/09/01 03:01 tool read-file
0m Args:
{
  "File": "mm/userfaultfd.c",
  "FirstLine": 4500,
  "LineCount": 20
}

Results:
{
  "Contents": "4500:\t\t */\n4501:\t\treturn (unsigned int)user_features | UFFD_FEATURE_INITIALIZED;\n4502:\t}\n4503:\t\n4504:\tstatic int userfaultfd_move(struct userfaultfd_ctx *ctx,\n4505:\t\t\t\t    unsigned long arg)\n4506:\t{\n4507:\t\t__s64 ret;\n4508:\t\tstruct uffdio_move uffdio_move;\n4509:\t\tstruct uffdio_move __user *user_uffdio_move;\n4510:\t\tstruct userfaultfd_wake_range range;\n4511:\t\tstruct mm_struct *mm = ctx-\u003emm;\n4512:\t\n4513:\t\tuser_uffdio_move = (struct uffdio_move __user *) arg;\n4514:\t\n4515:\t\tret = -EAGAIN;\n4516:\t\tif (unlikely(atomic_read(\u0026ctx-\u003emmap_changing))) {\n4517:\t\t\tif (unlikely(put_user(ret, \u0026user_uffdio_move-\u003emove)))\n4518:\t\t\t\treturn -EFAULT;\n4519:\t\t\tgoto out;\n"
}

21/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 29580
							output: 27
							thoughts: 47
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into userfaultfd's Entrypoints**

Okay, time to get into the nitty-gritty of `userfaultfd`. My first thought is to nail down those key entrypoints. Specifically, I need to confirm something I *think* I know about `userfaultfd_ioctl`: is it defined as `static`, or not? That's a crucial detail for understanding its scope and potential interactions. Now, let's compare what's going on in `fs/userfaultfd.c` and `mm/userfaultfd.c` to see if there's any important difference in implementation.



22/2 2026/09/01 03:01 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "userfaultfd_ioctl",
  "PathPrefix": "fs/"
}

Results:
{
  "error": "no matches"
}

23/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 29817
							output: 27
							thoughts: 15
						

Thoughts:
Here's my thought process, summarized as if I were thinking it:

**Digging into the `userfaultfd_ioctl` Kernel Implementation**

Okay, time to dive into this `userfaultfd_ioctl` beast. My immediate goal is to understand how it's implemented and where it's used within the kernel. Given my familiarity with kernel internals, I'll start with a broad search, probably using `grep` or a similar tool. I need to cast a wide net initially. I'll target the kernel source code directly – probably using the source tree of the kernel version I'm currently working with, or at least a recent mainline version. I'm thinking I'll start with something like a recursive search, looking for the string `userfaultfd_ioctl` to get a basic list of files and code paths that reference it. This initial search should give me a map of the landscape. From there, I can narrow down my focus. I'll be looking for the definition of the actual function itself, the `ioctl` handler, of course. Then I want to know what structures and functions it calls. Understanding the data structures involved, and the different `ioctl` commands that are defined, will give me a better grasp of the `userfaultfd` mechanism. My experience tells me that I need to be ready to trace through some function calls to see how the userfaultfd is handled, so I'll be keeping an eye out for interesting data structure definitions. Eventually I expect to fully understand how this `ioctl` is implemented, and how the underlying infrastructure works.



24/2 2026/09/01 03:01 tool grepper
0m Args:
{
  "Expression": "userfaultfd_ioctl",
  "PathPrefix": "mm/"
}

Results:
{
  "Output": "mm/khugepaged.c=2133=static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff)\n--\nmm/khugepaged.c-2189-\t\t * userfaultfd_wp() vmas.  But since the mmap_lock is not held,\nmm/khugepaged.c:2190:\t\t * it is still possible for a racing userfaultfd_ioctl() or\nmm/khugepaged.c-2191-\t\t * madvise() to have inserted ptes or markers.  Now that we hold\n--\nmm/userfaultfd.c=4574=static int userfaultfd_api(struct userfaultfd_ctx *ctx,\n--\nmm/userfaultfd.c-4631-\nmm/userfaultfd.c:4632:static long userfaultfd_ioctl(struct file *file, unsigned cmd,\nmm/userfaultfd.c-4633-\t\t\t      unsigned long arg)\n--\nmm/userfaultfd.c=4710=static const struct file_operations userfaultfd_fops = {\n--\nmm/userfaultfd.c-4716-\t.read_iter\t= userfaultfd_read_iter,\nmm/userfaultfd.c:4717:\t.unlocked_ioctl = userfaultfd_ioctl,\nmm/userfaultfd.c-4718-\t.compat_ioctl\t= compat_ptr_ioctl,\n"
}

25/2 2026/09/01 03:01 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 30558
							output: 195
							thoughts: 423
						

Thoughts:
**My Analysis of This Patch**

Okay, let's see what we're dealing with here. First, I need to identify the key functions this patch touches. Looks like we have four interesting ones: `vma_start_read_unlocked` (a new MM function in `mm/mmap_lock.c`), `lock_vma_under_rcu` (another MM API from the same file), `binder_alloc_free_page` (which is in `drivers/android/binder_alloc.c`), and `bpf_iter_task_vma_new` from `kernel/bpf/task_iter.c`. These are my focus symbols.

Next, I need to understand the configuration landscape. I'm noting the presence or absence of configs like `MMU`, `SMP`, `PER_VMA_LOCK_STATS`, `ANDROID_BINDER_IPC`, `USERFAULTFD`, `INET`, and `BPF_SYSCALL`.

Given those symbols and configurations, I'm going to review the essential configs. I'm focusing on `USERFAULTFD`, `ANDROID_BINDER_IPC`, `BPF_SYSCALL`, and `INET`.

Now, let's step back and put it all together. This patch seems to be about refactoring per-VMA locking. It's making it permanently enabled on MMU/SMP architectures and introduces `vma_start_read_unlocked()` to make lock acquisition easier. This implies changes to locking, and it's impacting several areas: Android Binder, TCP zerocopy, userfaultfd, procfs task MMU, and the BPF task iterator. So it is *definitely* something to fuzz.

Based on that, I'm calling `set-results` – ready to proceed with this analysis.



26/2 2026/09/01 03:01 tool set-results
0m Args:
{
  "EnableConfigs": [
    "ANDROID_BINDER_IPC",
    "USERFAULTFD",
    "BPF_SYSCALL",
    "INET"
  ],
  "FocusSymbols": [
    "vma_start_read_unlocked",
    "lock_vma_under_rcu",
    "binder_alloc_free_page",
    "bpf_iter_task_vma_new"
  ],
  "Reasoning": "The patch removes the CONFIG_PER_VMA_LOCK option, making per-VMA locking unconditionally enabled for MMU systems, and introduces the new vma_start_read_unlocked() helper. It refactors lock acquisition across Android Binder, TCP zerocopy receive, userfaultfd, procfs task MMU maps, and BPF VMA iterators, directly changing concurrency and locking semantics in core MM and associated subsystems.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "ANDROID_BINDER_IPC",
    "USERFAULTFD",
    "BPF_SYSCALL",
    "INET"
  ],
  "FocusSymbols": [
    "vma_start_read_unlocked",
    "lock_vma_under_rcu",
    "binder_alloc_free_page",
    "bpf_iter_task_vma_new"
  ],
  "Reasoning": "The patch removes the CONFIG_PER_VMA_LOCK option, making per-VMA locking unconditionally enabled for MMU systems, and introduces the new vma_start_read_unlocked() helper. It refactors lock acquisition across Android Binder, TCP zerocopy receive, userfaultfd, procfs task MMU maps, and BPF VMA iterators, directly changing concurrency and locking semantics in core MM and associated subsystems.",
  "WorthFuzzing": true
}

27/1 2026/09/01 03:01 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

28/2 2026/09/01 03:01 agent kmsan-evaluator
0m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch makes per-VMA locking unconditional on MMU configurations (removing CONFIG_PER_VMA_LOCK) and introduces a helper function `vma_start_read_unlocked()` to streamline VMA locking in binder, TCP zerocopy, and procfs task MMU walkers.\n\nThese changes strictly pertain to locking primitives, synchronization, and control flow for VMA refcounts and rwsems. Any potential bugs introduced (e.g., concurrency races, locking violations, or use-after-free from incorrect refcounts) are within the scope of LOCKDEP and KASAN. No new uninitialized stack/heap allocations, partial struct initializations, or copy-to-user info-leak paths are introduced. Therefore, dedicated KMSAN fuzzing is not warranted.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 6de62710957b1bbdf4b16790469d862343524d69
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 1 03:00:36 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f10379dfe7a33..3075e5c60a25e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -42,7 +42,6 @@ config ARM
 	select ARCH_SUPPORTS_ATOMIC_RMW
 	select ARCH_SUPPORTS_CFI
 	select ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_RT
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b5a51b0ef9440..2bbeded33da0d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -81,7 +81,6 @@ config ARM64
 	select ARCH_HAS_PTE_PROTNONE
 	select ARCH_SUPPORTS_NUMA_BALANCING
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE
 	select ARCH_SUPPORTS_RT
 	select ARCH_SUPPORTS_SCHED_SMT
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index a21f51e5815e9..9c5def7062222 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -69,7 +69,6 @@ config LOONGARCH
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
 	select ARCH_HAS_PTE_PROTNONE if 64BIT
 	select ARCH_SUPPORTS_NUMA_BALANCING if NUMA
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_RT
 	select ARCH_SUPPORTS_SCHED_SMT if SMP
 	select ARCH_SUPPORTS_SCHED_MC  if SMP
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
index b5ad7c173ef0c..dd8f6060fb7a2 100644
--- a/arch/powerpc/platforms/powernv/Kconfig
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -17,7 +17,6 @@ config PPC_POWERNV
 	select PPC_DOORBELL
 	select MMU_NOTIFIER
 	select FORCE_SMP
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select PPC_RADIX_BROADCAST_TLBIE if PPC_RADIX_MMU
 	default y
 
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 74910ce3a541c..7d125e288f6ef 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -23,7 +23,6 @@ config PPC_PSERIES
 	select HOTPLUG_CPU
 	select FORCE_SMP
 	select SWIOTLB
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select PPC_RADIX_BROADCAST_TLBIE if PPC_RADIX_MMU
 	default y
 
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index f8e26c4bed2ba..505eed4af932b 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -72,7 +72,6 @@ config RISCV
 	select ARCH_SUPPORTS_LTO_CLANG_THIN
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS if 64BIT && MMU
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
-	select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
 	select ARCH_HAS_PTE_PROTNONE if MMU
 	select ARCH_SUPPORTS_RT
 	select ARCH_SUPPORTS_SHADOW_CALL_STACK if HAVE_SHADOW_CALL_STACK
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 4b51bc6e8948d..b88b850421369 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -156,7 +156,6 @@ config S390
 	select ARCH_HAS_PTE_PROTNONE
 	select ARCH_SUPPORTS_NUMA_BALANCING
 	select ARCH_SUPPORTS_PAGE_TABLE_CHECK
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_USES_CFI_GENERIC_LLVM_PASS if CC_IS_CLANG
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 15fd9ec5ecacb..ac92b3fd70c64 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -27,7 +27,6 @@ config X86_64
 	select ARCH_HAS_GIGANTIC_PAGE
 	select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS
 	select ARCH_SUPPORTS_INT128 if CC_HAS_INT128
-	select ARCH_SUPPORTS_PER_VMA_LOCK
 	select ARCH_SUPPORTS_HUGE_PFNMAP if TRANSPARENT_HUGEPAGE
 	select HAVE_ARCH_SOFT_DIRTY
 	select MODULES_USE_ELF_RELA
@@ -1849,7 +1848,6 @@ config X86_USER_SHADOW_STACK
 	bool "X86 userspace shadow stack"
 	depends on AS_WRUSS
 	depends on X86_64
-	depends on PER_VMA_LOCK
 	select ARCH_USES_HIGH_VMA_FLAGS
 	select ARCH_HAS_USER_SHADOW_STACK
 	select X86_CET
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index 52ffbf3504e7f..71febd3d5b073 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -439,22 +439,9 @@ unsafe fn use_page_slow(&self, i: usize) -> Result<()> {
         // workqueue.
         let mm = MmWithUser::into_mmput_async(self.mm.mmget_not_zero().ok_or(ESRCH)?);
         {
-            let vma_read;
-            let mmap_read;
-            let vma = if let Some(ret) = mm.lock_vma_under_rcu(vma_addr) {
-                vma_read = ret;
-                check_vma(&vma_read, self)
-            } else {
-                mmap_read = mm.mmap_read_lock();
-                mmap_read
-                    .vma_lookup(vma_addr)
-                    .and_then(|vma| check_vma(vma, self))
-            };
-
-            match vma {
-                Some(vma) => vma.vm_insert_page(user_page_addr, &new_page)?,
-                None => return Err(ESRCH),
-            }
+            let vma_read_guard = mm.vma_start_read_unlocked(vma_addr).ok_or(ESRCH)?;
+            let vma = check_vma(&vma_read_guard, self).ok_or(ESRCH)?;
+            vma.vm_insert_page(user_page_addr, &new_page)?;
         }
 
         let inner = self.lock.lock();
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index e4488ad86a655..d6eae0aa70854 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -259,21 +259,14 @@ static int binder_page_insert(struct binder_alloc *alloc,
 	struct vm_area_struct *vma;
 	int ret = -ESRCH;
 
-	/* attempt per-vma lock first */
-	vma = lock_vma_under_rcu(mm, addr);
-	if (vma) {
-		if (binder_alloc_is_mapped(alloc))
-			ret = vm_insert_page(vma, addr, page);
-		vma_end_read(vma);
+	vma = vma_start_read_unlocked(mm, addr);
+	if (!vma)
 		return ret;
-	}
 
-	/* fall back to mmap_lock */
-	mmap_read_lock(mm);
-	vma = vma_lookup(mm, addr);
-	if (vma && binder_alloc_is_mapped(alloc))
+	if (binder_alloc_is_mapped(alloc))
 		ret = vm_insert_page(vma, addr, page);
-	mmap_read_unlock(mm);
+
+	vma_end_read(vma);
 
 	return ret;
 }
@@ -1142,7 +1135,6 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	struct vm_area_struct *vma;
 	struct page *page_to_free;
 	unsigned long page_addr;
-	int mm_locked = 0;
 	size_t index;
 
 	if (!mmget_not_zero(mm))
@@ -1151,27 +1143,25 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	index = mdata->page_index;
 	page_addr = alloc->vm_start + index * PAGE_SIZE;
 
-	/* attempt per-vma lock first */
+	/*
+	 * Attempt per-vma lock. This is essentially a
+	 * "trylock". It can fail even if the VMA exists
+	 * for 'page_addr'.
+	 */
 	vma = lock_vma_under_rcu(mm, page_addr);
 	if (!vma) {
-		/* fall back to mmap_lock */
-		if (!mmap_read_trylock(mm))
-			goto err_mmap_read_lock_failed;
-		mm_locked = 1;
-		vma = vma_lookup(mm, page_addr);
+		/*
+		 * If the vma exists, we can't continue because we cannot
+		 * remove the page from the vma. However, if the vma was
+		 * unmapped, it's okay to continue.
+		 */
+		if (binder_alloc_is_mapped(alloc))
+			goto err_vma_lock_failed;
 	}
 
 	if (!mutex_trylock(&alloc->mutex))
 		goto err_get_alloc_mutex_failed;
 
-	/*
-	 * Since a binder_alloc can only be mapped once, we ensure
-	 * the vma corresponds to this mapping by checking whether
-	 * the binder_alloc is still mapped.
-	 */
-	if (vma && !binder_alloc_is_mapped(alloc))
-		goto err_invalid_vma;
-
 	trace_binder_unmap_kernel_start(alloc, index);
 
 	page_to_free = alloc->pages[index];
@@ -1182,7 +1172,12 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	list_lru_isolate(lru, item);
 	spin_unlock(&lru->lock);
 
-	if (vma) {
+	/*
+	 * Since a binder_alloc can only be mapped once, we ensure
+	 * the vma corresponds to this mapping by checking whether
+	 * the binder_alloc is still mapped.
+	 */
+	if (vma && binder_alloc_is_mapped(alloc)) {
 		trace_binder_unmap_user_start(alloc, index);
 
 		zap_vma_range(vma, page_addr, PAGE_SIZE);
@@ -1191,23 +1186,17 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
 	}
 
 	mutex_unlock(&alloc->mutex);
-	if (mm_locked)
-		mmap_read_unlock(mm);
-	else
+	if (vma)
 		vma_end_read(vma);
 	mmput_async(mm);
 	binder_free_page(page_to_free);
 
 	return LRU_REMOVED_RETRY;
 
-err_invalid_vma:
-	mutex_unlock(&alloc->mutex);
 err_get_alloc_mutex_failed:
-	if (mm_locked)
-		mmap_read_unlock(mm);
-	else
+	if (vma)
 		vma_end_read(vma);
-err_mmap_read_lock_failed:
+err_vma_lock_failed:
 	mmput_async(mm);
 err_mmget:
 	return LRU_SKIP;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 04bd6c9e65a72..623bb43ede550 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -385,10 +385,8 @@ struct mem_size_stats;
 
 struct proc_maps_locking_ctx {
 	struct mm_struct *mm;
-#ifdef CONFIG_PER_VMA_LOCK
 	bool mmap_locked;
 	struct vm_area_struct *locked_vma;
-#endif
 };
 
 struct proc_maps_private {
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 5c54aebe21182..e671b4fd8dedd 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -130,8 +130,6 @@ static void release_task_mempolicy(struct proc_maps_private *priv)
 }
 #endif
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)
 {
 	int ret = mmap_read_lock_killable(lock_ctx->mm);
@@ -233,46 +231,6 @@ static inline void reacquire_rcu(struct proc_maps_private *priv)
 	vma_iter_set(&priv->iter, priv->lock_ctx.locked_vma->vm_end);
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)
-{
-	return mmap_read_lock_killable(lock_ctx->mm);
-}
-
-static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx)
-{
-	mmap_read_unlock(lock_ctx->mm);
-}
-
-static inline bool lock_vma_range(struct seq_file *m,
-				  struct proc_maps_locking_ctx *lock_ctx)
-{
-	return lock_ctx_mm(lock_ctx) == 0;
-}
-
-static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx)
-{
-	unlock_ctx_mm(lock_ctx);
-}
-
-static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv,
-					   loff_t last_pos)
-{
-	return vma_next(&priv->iter);
-}
-
-static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv,
-					 loff_t pos)
-{
-	return false;
-}
-
-static inline void drop_rcu(struct proc_maps_private *priv) {}
-static inline void reacquire_rcu(struct proc_maps_private *priv) {}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 static struct vm_area_struct *proc_get_vma(struct seq_file *m, loff_t *ppos)
 {
 	struct proc_maps_private *priv = m->private;
@@ -560,8 +518,6 @@ static int pid_maps_open(struct inode *inode, struct file *file)
 		PROCMAP_QUERY_VMA_FLAGS				\
 )
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx)
 {
 	reset_lock_ctx(lock_ctx);
@@ -612,26 +568,6 @@ static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ct
 	return vma;
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx)
-{
-	return mmap_read_lock_killable(lock_ctx->mm);
-}
-
-static void query_vma_teardown(struct proc_maps_locking_ctx *lock_ctx)
-{
-	mmap_read_unlock(lock_ctx->mm);
-}
-
-static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ctx *lock_ctx,
-						     unsigned long addr)
-{
-	return find_vma(lock_ctx->mm, addr);
-}
-
-#endif  /* CONFIG_PER_VMA_LOCK */
-
 static struct vm_area_struct *query_matching_vma(struct proc_maps_locking_ctx *lock_ctx,
 						 unsigned long addr, u32 flags)
 {
@@ -1314,8 +1250,6 @@ static const struct mm_walk_ops smaps_shmem_walk_ops = {
 	.walk_lock		= PGWALK_RDLOCK,
 };
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 static const struct mm_walk_ops smaps_walk_vma_lock_ops = {
 	.pmd_entry		= smaps_pte_range,
 	.hugetlb_entry		= smaps_hugetlb_range,
@@ -1345,22 +1279,6 @@ get_smaps_shmem_walk_ops(struct proc_maps_private *priv)
 	return &smaps_shmem_walk_vma_lock_ops;
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline const struct mm_walk_ops *
-get_smaps_walk_ops(struct proc_maps_private *priv)
-{
-	return &smaps_walk_ops;
-}
-
-static inline const struct mm_walk_ops *
-get_smaps_shmem_walk_ops(struct proc_maps_private *priv)
-{
-	return &smaps_shmem_walk_ops;
-}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 /*
  * Gather mem stats from @vma with the indicated beginning
  * address @start, and keep them in @mss.
@@ -3497,7 +3415,6 @@ static const struct mm_walk_ops show_numa_ops = {
 	.walk_lock = PGWALK_RDLOCK,
 };
 
-#ifdef CONFIG_PER_VMA_LOCK
 static const struct mm_walk_ops show_numa_vma_lock_ops = {
 	.hugetlb_entry = gather_hugetlb_stats,
 	.pmd_entry = gather_pte_stats,
@@ -3512,16 +3429,6 @@ get_show_numa_ops(struct proc_maps_private *priv)
 	return &show_numa_vma_lock_ops;
 }
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline const struct mm_walk_ops *
-get_show_numa_ops(struct proc_maps_private *priv)
-{
-	return &show_numa_ops;
-}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 /*
  * Display pages allocated per node and memory policy via /proc.
  */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index dd09c438fa23e..7aaa25abc241a 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -928,7 +928,6 @@ static inline void vma_numab_state_free(struct vm_area_struct *vma) {}
  * These must be here rather than mmap_lock.h as dependent on vm_fault type,
  * declared in this header.
  */
-#ifdef CONFIG_PER_VMA_LOCK
 static inline void release_fault_lock(struct vm_fault *vmf)
 {
 	if (vmf->flags & FAULT_FLAG_VMA_LOCK)
@@ -944,17 +943,6 @@ static inline void assert_fault_locked(const struct vm_fault *vmf)
 	else
 		mmap_assert_locked(vmf->vma->vm_mm);
 }
-#else
-static inline void release_fault_lock(struct vm_fault *vmf)
-{
-	mmap_read_unlock(vmf->vma->vm_mm);
-}
-
-static inline void assert_fault_locked(const struct vm_fault *vmf)
-{
-	mmap_assert_locked(vmf->vma->vm_mm);
-}
-#endif /* CONFIG_PER_VMA_LOCK */
 
 static inline bool mm_flags_test(int flag, const struct mm_struct *mm)
 {
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 6d815f6440c94..5413bd10fff2c 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -950,7 +950,6 @@ struct vm_area_struct {
 		vma_flags_t flags;
 	};
 
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Can only be written (using WRITE_ONCE()) while holding both:
 	 *  - mmap_lock (in write mode)
@@ -966,7 +965,7 @@ struct vm_area_struct {
 	 * slowpath.
 	 */
 	unsigned int vm_lock_seq;
-#endif
+
 	/*
 	 * Low 32-bits of anonymous page offset.
 	 * See vma_start_anon_pgoff() comment for details.
@@ -1003,7 +1002,6 @@ struct vm_area_struct {
 #ifdef CONFIG_NUMA_BALANCING
 	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
 #endif
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Used to keep track of firstly, whether the VMA is attached, secondly,
 	 * if attached, how many read locks are taken, and thirdly, if the
@@ -1046,7 +1044,6 @@ struct vm_area_struct {
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	struct lockdep_map vmlock_dep_map;
 #endif
-#endif
 #ifdef CONFIG_64BIT
 	/*
 	 * High 32-bits of anonymous page offset.
@@ -1254,7 +1251,6 @@ struct mm_struct {
 					  * init_mm.mmlist, and are protected
 					  * by mmlist_lock
 					  */
-#ifdef CONFIG_PER_VMA_LOCK
 		struct rcuwait vma_writer_wait;
 		/*
 		 * This field has lock-like semantics, meaning it is sometimes
@@ -1274,7 +1270,7 @@ struct mm_struct {
 		 * mmap_lock.
 		 */
 		seqcount_t mm_lock_seq;
-#endif
+
 		struct futex_mm_data	futex;
 
 		unsigned long hiwater_rss; /* High-watermark of RSS usage */
diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h
index bec0eab6ef035..00eae65b74bd6 100644
--- a/include/linux/mmap_lock.h
+++ b/include/linux/mmap_lock.h
@@ -76,8 +76,6 @@ static inline void mmap_assert_write_locked(const struct mm_struct *mm)
 	rwsem_assert_held_write(&mm->mmap_lock);
 }
 
-#ifdef CONFIG_PER_VMA_LOCK
-
 #ifdef CONFIG_LOCKDEP
 #define __vma_lockdep_map(vma) (&vma->vmlock_dep_map)
 #else
@@ -230,10 +228,14 @@ static inline void vma_refcount_put(struct vm_area_struct *vma)
 }
 
 /*
- * Use only while holding mmap read lock which guarantees that locking will not
- * fail (nobody can concurrently write-lock the vma). vma_start_read() should
+ * Use only while holding mmap read lock which guarantees that vma lock is not
+ * contended (nobody can concurrently write-lock the vma). vma_start_read() should
  * not be used in such cases because it might fail due to mm_lock_seq overflow.
  * This functionality is used to obtain vma read lock and drop the mmap read lock.
+ *
+ * VMA can't be detached while we are holding mmap lock, therefore in practice this
+ * function can fail only when there are so many readers that vm_refcnt overflows.
+ * The failure case is very unlikely and is already annotated as such internally.
  */
 static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int subclass)
 {
@@ -249,16 +251,23 @@ static inline bool vma_start_read_locked_nested(struct vm_area_struct *vma, int
 }
 
 /*
- * Use only while holding mmap read lock which guarantees that locking will not
- * fail (nobody can concurrently write-lock the vma). vma_start_read() should
+ * Use only while holding mmap read lock which guarantees that vma lock is not
+ * contended (nobody can concurrently write-lock the vma). vma_start_read() should
  * not be used in such cases because it might fail due to mm_lock_seq overflow.
  * This functionality is used to obtain vma read lock and drop the mmap read lock.
+ *
+ * VMA can't be detached while we are holding mmap lock, therefore in practice this
+ * function can fail only when there are so many readers that vm_refcnt overflows.
+ * The failure case is very unlikely and is already annotated as such internally.
  */
 static inline bool vma_start_read_locked(struct vm_area_struct *vma)
 {
 	return vma_start_read_locked_nested(vma, 0);
 }
 
+struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,
+					       unsigned long address);
+
 static inline void vma_end_read(struct vm_area_struct *vma)
 {
 	vma_refcount_put(vma);
@@ -297,6 +306,9 @@ int __vma_start_write(struct vm_area_struct *vma, int state);
  */
 static inline void vma_start_write(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	if (__is_vma_write_locked(vma))
 		return;
 
@@ -319,6 +331,9 @@ static inline void vma_start_write(struct vm_area_struct *vma)
 static inline __must_check
 int vma_start_write_killable(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return 0;
+
 	if (__is_vma_write_locked(vma))
 		return 0;
 
@@ -331,6 +346,11 @@ int vma_start_write_killable(struct vm_area_struct *vma)
  */
 static inline void vma_assert_write_locked(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU)) {
+		mmap_assert_write_locked(vma->vm_mm);
+		return;
+	}
+
 	VM_WARN_ON_ONCE_VMA(!__is_vma_write_locked(vma), vma);
 }
 
@@ -343,6 +363,11 @@ static inline void vma_assert_locked(struct vm_area_struct *vma)
 {
 	unsigned int refcnt;
 
+	if (!IS_ENABLED(CONFIG_MMU)) {
+		mmap_assert_locked(vma->vm_mm);
+		return;
+	}
+
 	if (IS_ENABLED(CONFIG_LOCKDEP)) {
 		if (!lock_is_held(__vma_lockdep_map(vma)))
 			vma_assert_write_locked(vma);
@@ -432,6 +457,9 @@ static inline bool vma_is_attached(struct vm_area_struct *vma)
  */
 static inline void vma_assert_attached(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	WARN_ON_ONCE(!vma_is_attached(vma));
 }
 
@@ -442,6 +470,9 @@ static inline void vma_assert_detached(struct vm_area_struct *vma)
 
 static inline void vma_mark_attached(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	vma_assert_write_locked(vma);
 	vma_assert_detached(vma);
 	refcount_set_release(&vma->vm_refcnt, 1);
@@ -451,6 +482,9 @@ void __vma_exclude_readers_for_detach(struct vm_area_struct *vma);
 
 static inline void vma_mark_detached(struct vm_area_struct *vma)
 {
+	if (!IS_ENABLED(CONFIG_MMU))
+		return;
+
 	vma_assert_write_locked(vma);
 	vma_assert_attached(vma);
 
@@ -484,54 +518,6 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm,
 				     struct vma_iterator *iter,
 				     unsigned long address);
 
-#else /* CONFIG_PER_VMA_LOCK */
-
-static inline void mm_lock_seqcount_init(struct mm_struct *mm) {}
-static inline void mm_lock_seqcount_begin(struct mm_struct *mm) {}
-static inline void mm_lock_seqcount_end(struct mm_struct *mm) {}
-
-static inline bool mmap_lock_speculate_try_begin(struct mm_struct *mm, unsigned int *seq)
-{
-	return false;
-}
-
-static inline bool mmap_lock_speculate_retry(struct mm_struct *mm, unsigned int seq)
-{
-	return true;
-}
-static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt) {}
-static inline void vma_end_read(struct vm_area_struct *vma) {}
-static inline void vma_start_write(struct vm_area_struct *vma) {}
-static inline __must_check
-int vma_start_write_killable(struct vm_area_struct *vma) { return 0; }
-static inline void vma_assert_write_locked(struct vm_area_struct *vma)
-		{ mmap_assert_write_locked(vma->vm_mm); }
-static inline bool vma_is_attached(struct vm_area_struct *vma)
-		{ return true; }
-static inline void vma_assert_attached(struct vm_area_struct *vma) {}
-static inline void vma_assert_detached(struct vm_area_struct *vma) {}
-static inline void vma_mark_attached(struct vm_area_struct *vma) {}
-static inline void vma_mark_detached(struct vm_area_struct *vma) {}
-
-static inline struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
-		unsigned long address)
-{
-	return NULL;
-}
-
-static inline void vma_assert_locked(struct vm_area_struct *vma)
-{
-	mmap_assert_locked(vma->vm_mm);
-}
-
-static inline void vma_assert_stabilised(struct vm_area_struct *vma)
-{
-	/* If no VMA locks, then either mmap lock suffices to stabilise. */
-	mmap_assert_locked(vma->vm_mm);
-}
-
-#endif /* CONFIG_PER_VMA_LOCK */
-
 static inline void vma_assert_can_modify(struct vm_area_struct *vma)
 {
 	if (vma_is_attached(vma))
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index a839041e0d008..8fb70c2a9c8e6 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -272,13 +272,10 @@ struct stack_map_vma_lock {
 /*
  * Acquire a stable read-side reference on the VMA covering @ip.
  *
- * With CONFIG_PER_VMA_LOCK=y this returns a VMA with its per-VMA read
- * lock held and mmap_lock dropped, so the caller may sleep.
- *
- * With CONFIG_PER_VMA_LOCK=n it returns a VMA with mmap_lock still
- * held; the caller must snapshot any fields it needs and pin vm_file
- * with get_file() before stack_map_unlock_vma() drops mmap_lock, as
- * the VMA may be split, merged, or freed after that.
+ * On NOMMU configurations, returns with the mmap_lock held.  If the MMU
+ * is enabled, the per-VMA lock will be held instead.  The lock
+ * should be released with stack_map_unlock_vma() which will release the
+ * appropriate lock.  Once the lock is released, the VMA may be freed.
  *
  * Returns NULL on failure, in which case no lock is held.
  */
@@ -288,7 +285,6 @@ stack_map_lock_vma(struct stack_map_vma_lock *lock, unsigned long ip)
 	struct mm_struct *mm = lock->mm;
 	struct vm_area_struct *vma;
 
-	/* noop under !CONFIG_PER_VMA_LOCK */
 	vma = lock_vma_under_rcu(mm, ip);
 	if (vma) {
 		lock->vma = vma;
@@ -308,21 +304,20 @@ stack_map_lock_vma(struct stack_map_vma_lock *lock, unsigned long ip)
 		return NULL;
 	}
 
-#ifdef CONFIG_PER_VMA_LOCK
+#ifdef CONFIG_MMU
 	if (!vma_start_read_locked(vma)) {
 		mmap_read_unlock(mm);
 		return NULL;
 	}
 	mmap_read_unlock(mm);
 #endif
-
 	lock->vma = vma;
 	return vma;
 }
 
 static void stack_map_unlock_vma(struct stack_map_vma_lock *lock)
 {
-#ifdef CONFIG_PER_VMA_LOCK
+#ifdef CONFIG_MMU
 	vma_end_read(lock->vma);
 #else
 	mmap_read_unlock(lock->mm);
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index 13e1aabe6f886..c65ba1dcd8667 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -869,7 +869,7 @@ __bpf_kfunc int bpf_iter_task_vma_new(struct bpf_iter_task_vma *it,
 	BUILD_BUG_ON(sizeof(struct bpf_iter_task_vma_kern) != sizeof(struct bpf_iter_task_vma));
 	BUILD_BUG_ON(__alignof__(struct bpf_iter_task_vma_kern) != __alignof__(struct bpf_iter_task_vma));
 
-	if (!IS_ENABLED(CONFIG_PER_VMA_LOCK)) {
+	if (!IS_ENABLED(CONFIG_MMU)) {
 		kit->data = NULL;
 		return -EOPNOTSUPP;
 	}
diff --git a/kernel/fork.c b/kernel/fork.c
index 416758c8a3d43..22283bf849e15 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1083,9 +1083,7 @@ static void mmap_init_lock(struct mm_struct *mm)
 {
 	init_rwsem(&mm->mmap_lock);
 	mm_lock_seqcount_init(mm);
-#ifdef CONFIG_PER_VMA_LOCK
 	rcuwait_init(&mm->vma_writer_wait);
-#endif
 }
 
 static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
diff --git a/mm/Kconfig b/mm/Kconfig
index 604c58199acbf..ece5d37b4eb7c 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1430,18 +1430,6 @@ config LRU_GEN_WALKS_MMU
 	depends on LRU_GEN && ARCH_HAS_HW_PTE_YOUNG
 # }
 
-config ARCH_SUPPORTS_PER_VMA_LOCK
-       def_bool n
-
-config PER_VMA_LOCK
-	def_bool y
-	depends on ARCH_SUPPORTS_PER_VMA_LOCK && MMU && SMP
-	help
-	  Allow per-vma locking during page fault handling.
-
-	  This feature allows locking each virtual memory area separately when
-	  handling page faults instead of taking mmap_lock.
-
 config LOCK_MM_AND_FIND_VMA
 	bool
 	depends on !STACK_GROWSUP
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
index 15dca19dd07da..9eaa25d1cf234 100644
--- a/mm/Kconfig.debug
+++ b/mm/Kconfig.debug
@@ -310,7 +310,6 @@ config DEBUG_KMEMLEAK_VERBOSE
 
 config PER_VMA_LOCK_STATS
 	bool "Statistics for per-vma locks"
-	depends on PER_VMA_LOCK
 	help
 	  Say Y here to enable success, retry and failure counters of page
 	  faults handled under protection of per-vma locks. When enabled, the
diff --git a/mm/debug.c b/mm/debug.c
index 9a0297b3988d8..655e6bcc0e8d9 100644
--- a/mm/debug.c
+++ b/mm/debug.c
@@ -157,17 +157,13 @@ void dump_vma(const struct vm_area_struct *vma)
 	pr_emerg("vma %px start %px end %px mm %px\n"
 		"prot %lx anon_vma %px vm_ops %px\n"
 		"pgoff %lx file %px private_data %px\n"
-#ifdef CONFIG_PER_VMA_LOCK
 		"refcnt %x\n"
-#endif
 		"flags: %#lx(%pGv)\n",
 		vma, (void *)vma->vm_start, (void *)vma->vm_end, vma->vm_mm,
 		(unsigned long)pgprot_val(vma->vm_page_prot),
 		vma->anon_vma, vma->vm_ops, vma_start_pgoff(vma),
 		vma->vm_file, vma->vm_private_data,
-#ifdef CONFIG_PER_VMA_LOCK
 		refcount_read(&vma->vm_refcnt),
-#endif
 		vma->vm_flags, &vma->vm_flags);
 }
 EXPORT_SYMBOL(dump_vma);
diff --git a/mm/init-mm.c b/mm/init-mm.c
index 3e792aad76261..a1bb2c2d0284a 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -39,10 +39,8 @@ struct mm_struct init_mm = {
 	.page_table_lock =  __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
 	.arg_lock	=  __SPIN_LOCK_UNLOCKED(init_mm.arg_lock),
 	.mmlist		= LIST_HEAD_INIT(init_mm.mmlist),
-#ifdef CONFIG_PER_VMA_LOCK
 	.vma_writer_wait = __RCUWAIT_INITIALIZER(init_mm.vma_writer_wait),
 	.mm_lock_seq	= SEQCNT_ZERO(init_mm.mm_lock_seq),
-#endif
 #ifdef CONFIG_SCHED_MM_CID
 	.mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(init_mm.mm_cid.lock),
 #endif
diff --git a/mm/memory.c b/mm/memory.c
index 8b0c2c735d3de..7bd660d48ff7c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6817,7 +6817,6 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,
 				 !vma_is_cow_mapping(vma)))
 			return VM_FAULT_SIGSEGV;
 	}
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Per-VMA locks can't be used with FAULT_FLAG_RETRY_NOWAIT because of
 	 * the assumption that lock is dropped on VM_FAULT_RETRY.
@@ -6826,7 +6825,6 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma,
 			(FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)) ==
 			(FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)))
 		return VM_FAULT_SIGSEGV;
-#endif
 
 	return 0;
 }
diff --git a/mm/mmap_lock.c b/mm/mmap_lock.c
index 898c2ef1e9580..2f94ee0fdee2a 100644
--- a/mm/mmap_lock.c
+++ b/mm/mmap_lock.c
@@ -43,9 +43,6 @@ void __mmap_lock_do_trace_released(struct mm_struct *mm, bool write)
 EXPORT_SYMBOL(__mmap_lock_do_trace_released);
 #endif /* CONFIG_TRACING */
 
-#ifdef CONFIG_MMU
-#ifdef CONFIG_PER_VMA_LOCK
-
 /* State shared across __vma_[start, end]_exclude_readers. */
 struct vma_exclude_readers_state {
 	/* Input parameters. */
@@ -299,6 +296,8 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
 	MA_STATE(mas, &mm->mm_mt, address, address);
 	struct vm_area_struct *vma;
 
+	if (!IS_ENABLED(CONFIG_MMU))
+		return NULL;
 retry:
 	rcu_read_lock();
 	vma = mas_walk(&mas);
@@ -341,6 +340,41 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm,
 	return NULL;
 }
 
+/**
+ * vma_start_read_unlocked() - Find the VMA covering 'address' and read-lock it.
+ * @mm: the mm_struct of the address space to search
+ * @address: address that the vma should contain
+ *
+ * The fast path does not take mmap_lock. Waits for writers to finish if the
+ * VMA is being modified by taking mmap_lock.
+ * Use when mmap_lock is not held, otherwise use vma_start_read_locked().
+ * Nothing prevents VMAs being unmapped/mapped before or after the VMA is
+ * looked up, if a stronger guarantee is required, take an mmap_lock.
+ *
+ * Return: If a VMA exists which spans @address, return that VMA, read-locked.
+ * If no VMA is mapped there or, very unlikely, a reference count overflow
+ * occurred, return NULL.
+ */
+struct vm_area_struct *vma_start_read_unlocked(struct mm_struct *mm,
+					       unsigned long address)
+{
+	struct vm_area_struct *vma;
+
+	/* Fast path: return stable VMA covering 'address': */
+	vma = lock_vma_under_rcu(mm, address);
+	if (vma)
+		return vma;
+
+	/* Slow path: preclude VMA writers by temporarily getting mmap read lock. */
+	mmap_read_lock(mm);
+	vma = vma_lookup(mm, address);
+	if (vma && !vma_start_read_locked(vma))
+		vma = NULL;
+	mmap_read_unlock(mm);
+
+	return vma;
+}
+
 static struct vm_area_struct *lock_next_vma_under_mmap_lock(struct mm_struct *mm,
 							    struct vma_iterator *vmi,
 							    unsigned long from_addr)
@@ -431,7 +465,6 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm,
 
 	return vma;
 }
-#endif /* CONFIG_PER_VMA_LOCK */
 
 #ifdef CONFIG_LOCK_MM_AND_FIND_VMA
 #include <linux/extable.h>
@@ -548,23 +581,3 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
 	return NULL;
 }
 #endif /* CONFIG_LOCK_MM_AND_FIND_VMA */
-
-#else /* CONFIG_MMU */
-
-/*
- * At least xtensa ends up having protection faults even with no
- * MMU.. No stack expansion, at least.
- */
-struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
-			unsigned long addr, struct pt_regs *regs)
-{
-	struct vm_area_struct *vma;
-
-	mmap_read_lock(mm);
-	vma = vma_lookup(mm, addr);
-	if (!vma)
-		mmap_read_unlock(mm);
-	return vma;
-}
-
-#endif /* CONFIG_MMU */
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index cc07fcf50e87b..7411702a37f58 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -444,7 +444,6 @@ static inline void process_mm_walk_lock(struct mm_struct *mm,
 static inline void process_vma_walk_lock(struct vm_area_struct *vma,
 					 enum page_walk_lock walk_lock)
 {
-#ifdef CONFIG_PER_VMA_LOCK
 	switch (walk_lock) {
 	case PGWALK_WRLOCK:
 		vma_start_write(vma);
@@ -459,7 +458,6 @@ static inline void process_vma_walk_lock(struct vm_area_struct *vma,
 		/* PGWALK_RDLOCK is handled by process_mm_walk_lock */
 		break;
 	}
-#endif
 }
 
 /*
diff --git a/mm/rmap.c b/mm/rmap.c
index d1819fd699380..fed0362e0bd0e 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -260,11 +260,9 @@ static void check_anon_vma_clone(struct vm_area_struct *dst,
 	/* For the anon_vma to be compatible, it can only be singular. */
 	VM_WARN_ON_ONCE(operation == VMA_OP_MERGE_UNFAULTED &&
 			!list_is_singular(&src->anon_vma_chain));
-#ifdef CONFIG_PER_VMA_LOCK
 	/* Only merging an unfaulted VMA leaves the destination attached. */
 	VM_WARN_ON_ONCE(operation != VMA_OP_MERGE_UNFAULTED &&
 			vma_is_attached(dst));
-#endif
 }
 
 static void maybe_reuse_anon_vma(struct vm_area_struct *dst,
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 74f04c323c50f..6ee933a3803cc 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -122,7 +122,6 @@ struct vm_area_struct *find_vma_and_prepare_anon(struct mm_struct *mm,
 	return vma;
 }
 
-#ifdef CONFIG_PER_VMA_LOCK
 /*
  * uffd_lock_vma() - Lookup and lock vma corresponding to @address.
  * @mm: mm to search vma in.
@@ -130,8 +129,10 @@ struct vm_area_struct *find_vma_and_prepare_anon(struct mm_struct *mm,
  *
  * Should be called without holding mmap_lock.
  *
- * Return: A locked vma containing @address, -ENOENT if no vma is found, or
- * -ENOMEM if anon_vma couldn't be allocated.
+ * Return: A locked vma containing @address, -ENOENT if no vma is found,
+ * -ENOMEM if anon_vma couldn't be allocated, or -EAGAIN if vma refcount
+ * overflow happened due to high number of readers and the caller should
+ * retry later.
  */
 static struct vm_area_struct *uffd_lock_vma(struct mm_struct *mm,
 				       unsigned long address)
@@ -182,34 +183,6 @@ static void uffd_mfill_unlock(struct vm_area_struct *vma)
 	vma_end_read(vma);
 }
 
-#else
-
-static struct vm_area_struct *uffd_mfill_lock(struct mm_struct *dst_mm,
-					      unsigned long dst_start,
-					      unsigned long len)
-{
-	struct vm_area_struct *dst_vma;
-
-	mmap_read_lock(dst_mm);
-	dst_vma = find_vma_and_prepare_anon(dst_mm, dst_start);
-	if (IS_ERR(dst_vma))
-		goto out_unlock;
-
-	if (validate_dst_vma(dst_vma, dst_start + len))
-		return dst_vma;
-
-	dst_vma = ERR_PTR(-ENOENT);
-out_unlock:
-	mmap_read_unlock(dst_mm);
-	return dst_vma;
-}
-
-static void uffd_mfill_unlock(struct vm_area_struct *vma)
-{
-	mmap_read_unlock(vma->vm_mm);
-}
-#endif
-
 static void mfill_put_vma(struct mfill_state *state)
 {
 	if (!state->vma)
@@ -1850,7 +1823,6 @@ int find_vmas_mm_locked(struct mm_struct *mm,
 	return 0;
 }
 
-#ifdef CONFIG_PER_VMA_LOCK
 static int uffd_move_lock(struct mm_struct *mm,
 			  unsigned long dst_start,
 			  unsigned long src_start,
@@ -1925,31 +1897,6 @@ static void uffd_move_unlock(struct vm_area_struct *dst_vma,
 		vma_end_read(dst_vma);
 }
 
-#else
-
-static int uffd_move_lock(struct mm_struct *mm,
-			  unsigned long dst_start,
-			  unsigned long src_start,
-			  struct vm_area_struct **dst_vmap,
-			  struct vm_area_struct **src_vmap)
-{
-	int err;
-
-	mmap_read_lock(mm);
-	err = find_vmas_mm_locked(mm, dst_start, src_start, dst_vmap, src_vmap);
-	if (err)
-		mmap_read_unlock(mm);
-	return err;
-}
-
-static void uffd_move_unlock(struct vm_area_struct *dst_vma,
-			     struct vm_area_struct *src_vma)
-{
-	mmap_assert_locked(src_vma->vm_mm);
-	mmap_read_unlock(dst_vma->vm_mm);
-}
-#endif
-
 /**
  * move_pages - move arbitrary anonymous pages of an existing vma
  * @ctx: pointer to the userfaultfd context
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 455441f1b6949..62defe70f3ceb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2167,27 +2167,18 @@ static void tcp_zc_finalize_rx_tstamp(struct sock *sk,
 }
 
 static struct vm_area_struct *find_tcp_vma(struct mm_struct *mm,
-					   unsigned long address,
-					   bool *mmap_locked)
+					   unsigned long address)
 {
-	struct vm_area_struct *vma = lock_vma_under_rcu(mm, address);
+	struct vm_area_struct *vma = vma_start_read_unlocked(mm, address);
 
-	if (vma) {
-		if (vma->vm_ops != &tcp_vm_ops) {
-			vma_end_read(vma);
-			return NULL;
-		}
-		*mmap_locked = false;
-		return vma;
-	}
+	if (!vma)
+		return NULL;
 
-	mmap_read_lock(mm);
-	vma = vma_lookup(mm, address);
-	if (!vma || vma->vm_ops != &tcp_vm_ops) {
-		mmap_read_unlock(mm);
+	if (vma->vm_ops != &tcp_vm_ops) {
+		vma_end_read(vma);
 		return NULL;
 	}
-	*mmap_locked = true;
+
 	return vma;
 }
 
@@ -2208,7 +2199,6 @@ static int tcp_zerocopy_receive(struct sock *sk,
 	u32 seq = tp->copied_seq;
 	u32 total_bytes_to_map;
 	int inq = tcp_inq(sk);
-	bool mmap_locked;
 	int ret;
 
 	zc->copybuf_len = 0;
@@ -2233,7 +2223,7 @@ static int tcp_zerocopy_receive(struct sock *sk,
 		return 0;
 	}
 
-	vma = find_tcp_vma(current->mm, address, &mmap_locked);
+	vma = find_tcp_vma(current->mm, address);
 	if (!vma)
 		return -EINVAL;
 
@@ -2315,10 +2305,7 @@ static int tcp_zerocopy_receive(struct sock *sk,
 						   zc, total_bytes_to_map);
 	}
 out:
-	if (mmap_locked)
-		mmap_read_unlock(current->mm);
-	else
-		vma_end_read(vma);
+	vma_end_read(vma);
 	/* Try to copy straggler data. */
 	if (!ret)
 		copylen = tcp_zc_handle_leftover(zc, sk, skb, &seq, copybuf_len, tss);
diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs
index 4764d7b68f2a7..58bc1793fdaf5 100644
--- a/rust/kernel/mm.rs
+++ b/rust/kernel/mm.rs
@@ -170,30 +170,48 @@ pub unsafe fn from_raw<'a>(ptr: *const bindings::mm_struct) -> &'a MmWithUser {
     ///
     /// This is an optimistic trylock operation, so it may fail if there is contention. In that
     /// case, you should fall back to taking the mmap read lock.
-    ///
-    /// When per-vma locks are disabled, this always returns `None`.
     #[inline]
     pub fn lock_vma_under_rcu(&self, vma_addr: usize) -> Option<VmaReadGuard<'_>> {
-        #[cfg(CONFIG_PER_VMA_LOCK)]
-        {
-            // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
-            // `mm_users` is non-zero.
-            let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
-            if !vma.is_null() {
-                return Some(VmaReadGuard {
-                    // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a
-                    // valid vma. The vma is stable for as long as the vma read lock is held.
-                    vma: unsafe { VmaRef::from_raw(vma) },
-                    _nts: NotThreadSafe,
-                });
-            }
+        // SAFETY: Calling `bindings::lock_vma_under_rcu` is always okay given an mm where
+        // `mm_users` is non-zero.
+        let vma = unsafe { bindings::lock_vma_under_rcu(self.as_raw(), vma_addr) };
+        if vma.is_null() {
+            return None;
         }
+        Some(VmaReadGuard {
+            // SAFETY: If `lock_vma_under_rcu` returns a non-null ptr, then it points at a
+            // valid vma. The vma is stable for as long as the vma read lock is held.
+            vma: unsafe { VmaRef::from_raw(vma) },
+            _nts: NotThreadSafe,
+        })
+    }
 
-        // Silence warnings about unused variables.
-        #[cfg(not(CONFIG_PER_VMA_LOCK))]
-        let _ = vma_addr;
-
-        None
+    /// Find the VMA covering 'address' and read-lock it.
+    ///
+    /// The fast path does not take mmap_lock. Waits for writers to finish if the
+    /// VMA is being modified by taking mmap_lock.
+    /// Use when mmap_lock is not held, otherwise use vma_start_read_locked().
+    /// Nothing prevents VMAs being unmapped/mapped before or after the VMA is
+    /// looked up, if a stronger guarantee is required, take an mmap_lock.
+    ///
+    /// Return: If a VMA exists which spans @address, return that VMA, read-locked.
+    /// If no VMA is mapped there or, very unlikely, a reference count overflow
+    /// occurred, return NULL.
+    #[inline]
+    pub fn vma_start_read_unlocked(&self, vma_addr: usize) -> Option<VmaReadGuard<'_>> {
+        // SAFETY: We may invoke `vma_start_read_unlocked` because we know this `mm` has non-zero
+        // `mm_users`.
+        let vma = unsafe { bindings::vma_start_read_unlocked(self.as_raw(), vma_addr) };
+        if vma.is_null() {
+            return None;
+        }
+        // INVARIANT: We just acquired the VMA read lock.
+        Some(VmaReadGuard {
+            // SAFETY: If `vma_start_read_unlocked` returns a non-null ptr, then it points at a
+            // valid vma. The vma is stable for as long as the vma read lock is held.
+            vma: unsafe { VmaRef::from_raw(vma) },
+            _nts: NotThreadSafe,
+        })
     }
 
     /// Lock the mmap read lock.
diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
index 4c58487b764e9..57046d8ac81d8 100644
--- a/tools/testing/vma/include/dup.h
+++ b/tools/testing/vma/include/dup.h
@@ -560,7 +560,6 @@ struct vm_area_struct {
 		vma_flags_t flags;
 	};
 
-#ifdef CONFIG_PER_VMA_LOCK
 	/*
 	 * Can only be written (using WRITE_ONCE()) while holding both:
 	 *  - mmap_lock (in write mode)
@@ -576,7 +575,7 @@ struct vm_area_struct {
 	 * slowpath.
 	 */
 	unsigned int vm_lock_seq;
-#endif
+
 	unsigned int __vm_anon_pgoff_lo;
 
 	/*
@@ -610,10 +609,8 @@ struct vm_area_struct {
 #ifdef CONFIG_NUMA_BALANCING
 	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
 #endif
-#ifdef CONFIG_PER_VMA_LOCK
 	/* Unstable RCU readers are allowed to read this. */
 	refcount_t vm_refcnt;
-#endif
 #ifdef CONFIG_64BIT
 	unsigned int __vm_anon_pgoff_hi;
 #endif
diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h
index 8a48b231aa7ab..54d5c3360aa26 100644
--- a/tools/testing/vma/vma_internal.h
+++ b/tools/testing/vma/vma_internal.h
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 
 #define CONFIG_MMU		1
-#define CONFIG_PER_VMA_LOCK	1
 
 #ifdef __CONCAT
 #undef __CONCAT

29/3 2026/09/01 03:01 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18801
							output: 187
							thoughts: 1750
						

Thoughts:
**KMSAN Evaluation: Patch for Unconditional Per-VMA Locking**

Alright, let's dissect this patch. My immediate goal is to determine if we need to throw KMSAN at it, or if KASAN, LOCKDEP, and our usual kernel debugging tools are sufficient.

First, I see the core change is the removal of `CONFIG_PER_VMA_LOCK` and making per-vma locking an unconditional part of the MMU.  This simplifies things, eliminating arch-specific defines and conditional compilation.  The introduction of `vma_start_read_unlocked(mm, addr)` looks like a new helper to streamline VMA locking.  It uses RCU for a fast path, falling back to read locking if necessary.

Now, let's dig into the details:  This `vma_start_read_unlocked` function uses both a fast path via RCU and a fallback that takes `mmap_read_lock`. The fallback uses the slow path, but that's expected.

I see it's touching several areas, including `binder_alloc`, `tcp_zerocopy_receive`, and parts of `procfs`, and several core mm files. My initial concern is always around uninitialized memory. Let's make sure nothing is leaking to user space.

I'm checking the changes to `binder_alloc` and `tcp_zerocopy_receive` carefully.  The key change seems to be using the new helper function in place of duplicated locking patterns. Also, the removal of the `#ifdef CONFIG_PER_VMA_LOCK` makes things more straightforward. I need to make sure this doesn't introduce any subtle issues with uninitialized memory.

Let's do a quick checklist to rule out the potential issues:

1.  Kernel structures sent to user space where fields/padding are uninitialized? No, nothing new is leaking.
2.  Conditional logic/branching depending on uninitialized variables? Nope. Variables are assigned.
3.  Allocation or initialization of complex data structures with uninitialized fields? No new allocations that are left uninitialized.
4.  Bounds checks / lengths / info leaks? Looks safe here.

The primary risk here is locking-related.  That's where KASAN and LOCKDEP shine.  We are talking about possible locking bugs, deadlocks, races, or use-after-free scenarios.

To double-check this, let's look at `stack_map_lock_vma` in `kernel/bpf/stackmap.c` and `tcp_zerocopy_receive` in `net/ipv4/tcp.c`. It's a quick sanity check to look for anything that uses `vma_start_read_unlocked`. All good.

I'm also paying close attention to the `proc_maps_locking_ctx` changes. I need to ensure that the newly unconditional struct members `mmap_locked` and `locked_vma` are initialized properly. `reset_lock_ctx` does the job, initializing them to `false` and `NULL`, as expected.

Now, let's consider the KMSAN angle explicitly. KMSAN is *the* tool for catching uninitialized memory reads and information leaks. The patch, at its heart, is about streamlining VMA locking. It's not creating new allocations, modifying existing ones in a way that would introduce uninitialized fields, or sending uninitialized memory to user space. Any errors will be around the concurrency and memory management changes.

Therefore, `NeedsKMSAN = false`.

I'll put the reasoning in contrast:

*   KASAN/LOCKDEP:  Well-suited for catching race conditions, concurrency bugs, use-after-free errors, memory refcount problems, and locking order issues associated with these VMA changes.
*   KMSAN: Not needed. The changes do *not* introduce or modify uninitialized stack/heap allocations, structure copying to userspace, or uninitialized memory branches.

Ready to call `set-results`.



30/3 2026/09/01 03:01 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch makes per-VMA locking unconditional on MMU configurations (removing CONFIG_PER_VMA_LOCK) and introduces a helper function `vma_start_read_unlocked()` to streamline VMA locking in binder, TCP zerocopy, and procfs task MMU walkers.\n\nThese changes strictly pertain to locking primitives, synchronization, and control flow for VMA refcounts and rwsems. Any potential bugs introduced (e.g., concurrency races, locking violations, or use-after-free from incorrect refcounts) are within the scope of LOCKDEP and KASAN. No new uninitialized stack/heap allocations, partial struct initializations, or copy-to-user info-leak paths are introduced. Therefore, dedicated KMSAN fuzzing is not warranted.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch makes per-VMA locking unconditional on MMU configurations (removing CONFIG_PER_VMA_LOCK) and introduces a helper function `vma_start_read_unlocked()` to streamline VMA locking in binder, TCP zerocopy, and procfs task MMU walkers.\n\nThese changes strictly pertain to locking primitives, synchronization, and control flow for VMA refcounts and rwsems. Any potential bugs introduced (e.g., concurrency races, locking violations, or use-after-free from incorrect refcounts) are within the scope of LOCKDEP and KASAN. No new uninitialized stack/heap allocations, partial struct initializations, or copy-to-user info-leak paths are introduced. Therefore, dedicated KMSAN fuzzing is not warranted.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)