[PATCH 22/39] mm/mlock: clear VMA_LOCKED_MASK over mmap callback
From: Lorenzo Stoakes (ARM)
Date: Tue Sep 08 2026 - 16:33:19 EST
Currently there's a confusing mess around VMA_LOCKED_BIT and
VMA_LOCKONFAULT_BIT.
It is permitted for drivers to set any flags they like, with the VMA
already possessing lock flags.
This results in the absurd situation of a VMA possessing both
VMA_SPECIAL_FLAGS and VMA_LOCKED_MASK flags, which is not permitted.
This has resulted in mlock_vma_folio() having a very silly check for this
scenario to work around it.
There is no need for this - just clear the flags before invoking the hook
and reinstate them afterwards if they are required.
Nothing relies upon this being set during the mmap operation.
mmap_prepare is unaffected by this so requires no fix.
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx>
---
mm/internal.h | 9 +--------
mm/vma.c | 14 ++++++++++++++
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/mm/internal.h b/mm/internal.h
index a86b9803a4c3..6e27d3b10c01 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -975,14 +975,7 @@ void mlock_folio(struct folio *folio);
static inline void mlock_vma_folio(struct folio *folio,
struct vm_area_struct *vma)
{
- /*
- * The VM_SPECIAL check here serves two purposes.
- * 1) VM_IO check prevents migration from double-counting during mlock.
- * 2) Although mmap_region() and mlock_fixup() take care that VM_LOCKED
- * is never left set on a VM_SPECIAL vma, there is an interval while
- * file->f_op->mmap() is using vm_insert_page(s), when VM_LOCKED may
- * still be set while VM_SPECIAL bits are added: so ignore it then.
- */
+ /* The VM_IO check prevents migration from double-counting during mlock. */
if (unlikely((vma->vm_flags & (VM_LOCKED|VM_SPECIAL)) == VM_LOCKED))
mlock_folio(folio);
}
diff --git a/mm/vma.c b/mm/vma.c
index 526428753218..cb0c4c625756 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -2594,6 +2594,11 @@ static int __mmap_new_file_vma(struct mmap_state *map,
if (!map->file->f_op->mmap)
return 0;
+ /*
+ * Driver-specified flags may make the lock flags invalid, so clear
+ * VMA_LOCKED_MASK and reinstate it afterwards if appropriate.
+ */
+ vma_clear_flags_mask(vma, VMA_LOCKED_MASK);
error = mmap_file(vma->vm_file, vma);
if (error) {
UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
@@ -2607,6 +2612,15 @@ static int __mmap_new_file_vma(struct mmap_state *map,
return error;
}
+ /* If VMA flags still valid for locked mask, reinstate. */
+ if (vma_supports_mlock(vma)) {
+ const vma_flags_t mask =
+ vma_flags_and_mask(&map->vma_flags,
+ VMA_LOCKED_MASK);
+
+ vma_set_flags_mask(vma, mask);
+ }
+
map->file = vma->vm_file;
map->vma_flags = vma->flags;
--
2.55.0