Re: [RFC PATCH v4 1/3] mm: allow page faults to request VMA-lock
From: Hongru Zhang
Date: Wed Aug 05 2026 - 10:14:41 EST
> On Tue, Aug 04, 2026 at 05:52:19PM +0800, Hongru Zhang wrote:
> > From: Hongru Zhang <zhanghongru@xxxxxxxxxx>
> >
> > Page faults handled under the per-VMA lock currently fall back to the
> > mmap_lock path whenever handle_mm_fault() returns VM_FAULT_RETRY. This
> > means that lower-level fault handlers have no way to tell the
> > architecture fault handler that the retry can safely continue under the
> > per-VMA lock.
> >
> > Add VM_FAULT_MAY_USE_VMA_LOCK as an advisory bit that can be returned
>
> I don't love that name or that faulting retry behaviour is _modified_ by a
> value that indicates fault resolution state... ugh.
>
> It's kinda confusing things 'VM_FAULT_RETRY' is 'you have to retry this
> fault'.
>
> 'VM_FAULT_MAY_...' is starting to bring in effectively configuration
> options into it and that's kinda horrible.
>
> I mean is there any reason we shouldn't ALWAYS do this if a VMA lock was
> used?
>
> It's not too expensive to do a single retry with the VMA lock before
> falling back to the mmap lock.
vm_fault_t do_swap_page(struct vm_fault *vmf)
{
...
entry = softleaf_from_pte(vmf->orig_pte);
if (unlikely(!softleaf_is_swap(entry))) {
...
} else if (softleaf_is_device_private(entry)) {
if (vmf->flags & FAULT_FLAG_VMA_LOCK) {
/*
* migrate_to_ram is not yet ready to operate
* under VMA lock.
*/
vma_end_read(vma);
ret = VM_FAULT_RETRY;
goto out;
}
...
}
This is one example why I made the v4 retry opt-in. Device-private entries
currently use VM_FAULT_RETRY to force a fallback from the VMA-lock path to
the mmap_lock path, because migrate_to_ram() is not VMA-lock-ready yet. An
unconditional VMA-lock retry would not make this path progress under the VMA
lock; it would only add one bounded extra VMA-lock attempt before falling
back to mmap_lock.
>
> So maybe simplify like that?
>
> And like that this series becomes a single patch right?
>
Agreed. I'll try the "always retry once under VMA lock" approach and report
performance results in a later email.
> Though it seems the reticence is that you might end up waiting with the VMA
> lock held.
>
> If that's really critical then I'd drop this whole thing of referencing the
> VMA flag in the name altogether, it's confusing and you're left wondering
> why it's needed when the fault is already specified as allowing a VMA lock
> via FAULT_FLAG_VMA_LOCK.
>
> It's not at all clear it's _only_ for the arch-fault-handlers.
>
> I'm curious anyway as to where this waiting is actually happening? Is the
> waiting not _already_ happening with the VMA lock held on first attempt? Or
> if not there then where? [ the fault code is such a mess don't really have
> time to figure it out myself ].
>
> Anyway if we _have_ to have this flag, then something that actually matches
> the semantics here is better like:
>
> VM_FAULT_RETRY_WONT_BLOCK
>
> Which is informational, doesn't add any confusion about the VMA flag, and
> makes things a lot more self-documenting about what's going on here.
>
> > together with VM_FAULT_RETRY. Architecture fault handlers use this bit
> > to allow at most one retry under the per-VMA lock.
> >
> > This preserves the existing mmap_lock fallback behaviour for fault
> > handlers that continue to return VM_FAULT_RETRY without
> > VM_FAULT_MAY_USE_VMA_LOCK: major retries still enter the mmap_lock path
> > with FAULT_FLAG_TRIED set, while minor retries still enter it as a fresh
> > first attempt.
>
> Yup this was a concern I think I raised in a previous version that you'd
> get infinite VMA lock retries.
>
> >
> > The difference is limited to fault handlers that return VM_FAULT_RETRY
> > with VM_FAULT_MAY_USE_VMA_LOCK. For them, both major and minor retries
> > enter the VMA-lock retry with FAULT_FLAG_TRIED set. For major faults
> > this follows the existing mmap_lock retry handling, but the retried
> > fault runs under the VMA lock rather than the mmap_lock. For minor
> > faults this replaces the fresh mmap_lock retry with a VMA-lock retry
> > that has FAULT_FLAG_TRIED set. This can avoid an extra retry round for
> > short waits. The cost is that a retried fault that blocks for an extended
> > period may wait while holding the VMA lock. Fault handlers should return
>
> Where does it wait?
For filemap, the main wait I had in mind is in lock_folio_maybe_drop_mmap().
On the first attempt, maybe_unlock_mmap_for_io() can drop the fault lock
before waiting. On the VMA-lock retry, FAULT_FLAG_TRIED is already set, so
maybe_unlock_mmap_for_io() can no longer drop it. If folio_trylock() fails,
__folio_lock()/__folio_lock_killable() may then wait while the VMA lock is
still held.
There is also a less frequent not-uptodate path: after
maybe_unlock_mmap_for_io() keeps the fault lock on a FAULT_FLAG_TRIED
retry, filemap_read_folio() can wait for the synchronous read to complete.
I instrumented both places locally. The folio-lock wait path was common in
the shared-file sequential workload; the read_folio path also happened,
but much less frequently and mainly in the shared-file random cases.
>
> > VM_FAULT_RETRY with VM_FAULT_MAY_USE_VMA_LOCK when that tradeoff is
> > preferable to falling back to mmap_lock immediately.
>
> This sentence is completely unreadable :) Too. Many. Words.
>
> It's really hard to follow too. Was this AI-generated? Totally understand
> if it's to help with prose in general but what we end up with really does
> have to be clear.
>
> Something like:
>
> Minor faults are retried indefinitely with the mmap lock held,
> which guarantees some forward progress, however this isn't the case
> with VMA locks, so set FAULT_FLAG_TRIED for minor faults if the VMA
> lock is used.
>
> < discussions of tradeoff, where the waiting actually happens >
>
> Maybe?
>
> >
> > No current code sets VM_FAULT_MAY_USE_VMA_LOCK yet; this patch only
> > prepares the retry plumbing for later users.
> >
> > No functional change is intended.
> >
> > Signed-off-by: Hongru Zhang <zhanghongru@xxxxxxxxxx>
> > Suggested-by: Barry Song <baohua@xxxxxxxxxx>
> > Suggested-by: Suren Baghdasaryan <surenb@xxxxxxxxxx>
> > ---
> > arch/arm/mm/fault.c | 6 ++++--
> > arch/arm64/mm/fault.c | 7 +++++--
> > arch/loongarch/mm/fault.c | 6 ++++--
> > arch/powerpc/mm/fault.c | 6 ++++--
> > arch/riscv/mm/fault.c | 6 ++++--
> > arch/s390/mm/fault.c | 5 +++--
> > arch/x86/mm/fault.c | 6 ++++--
>
> Really badly need that code to separate out fault handling code. Matthew?
> :) Or perhaps he's waiting on this to land first...
>
> It does seem that these are the arches that use VMA locks though.
>
> Suren - is there any reason we shouldn't just enable VMA locks for every
> CONFIG_MMU arch now it's headed for being default-enabled?
>
>
> > include/linux/mm.h | 28 ++++++++++++++++++++++++++++
> > include/linux/mm_types.h | 4 ++++
> > 9 files changed, 60 insertions(+), 14 deletions(-)
> >
> > diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
> > index e62cc4be5adf..158923b70901 100644
> > --- a/arch/arm/mm/fault.c
> > +++ b/arch/arm/mm/fault.c
> > @@ -391,6 +391,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
> > if (!(flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> >
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, addr);
> > if (!vma)
> > goto lock_mmap;
> > @@ -411,8 +412,6 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
> > goto done;
> > }
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - flags |= FAULT_FLAG_TRIED;
> >
> > /* Quick path to respond to signals */
> > if (fault_signal_pending(fault, regs)) {
> > @@ -420,6 +419,9 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
> > goto no_context;
> > return 0;
> > }
> > +
> > + if (fault_should_retry_under_vma_lock(fault, &flags))
> > + goto retry_vma;
> > lock_mmap:
> >
> > retry:
> > diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> > index 0b52557652be..b17986b40ac3 100644
> > --- a/arch/arm64/mm/fault.c
> > +++ b/arch/arm64/mm/fault.c
> > @@ -678,6 +678,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
> > if (!(mm_flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> >
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, addr);
> > if (!vma)
> > goto lock_mmap;
> > @@ -715,8 +716,6 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
> > goto done;
> > }
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - mm_flags |= FAULT_FLAG_TRIED;
> >
> > /* Quick path to respond to signals */
> > if (fault_signal_pending(fault, regs)) {
> > @@ -724,6 +723,10 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
> > goto no_context;
> > return 0;
> > }
> > +
> > + if (fault_should_retry_under_vma_lock(fault, &mm_flags))
> > + goto retry_vma;
> > +
> > lock_mmap:
> >
> > retry:
> > diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c
> > index 2c93d33356e5..6a946838b54b 100644
> > --- a/arch/loongarch/mm/fault.c
> > +++ b/arch/loongarch/mm/fault.c
> > @@ -219,6 +219,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs,
> > if (!(flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> >
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, address);
> > if (!vma)
> > goto lock_mmap;
> > @@ -256,8 +257,6 @@ static void __kprobes __do_page_fault(struct pt_regs *regs,
> > }
> >
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - flags |= FAULT_FLAG_TRIED;
> >
> > /* Quick path to respond to signals */
> > if (fault_signal_pending(fault, regs)) {
> > @@ -265,6 +264,9 @@ static void __kprobes __do_page_fault(struct pt_regs *regs,
> > no_context(regs, write, address);
> > return;
> > }
> > +
> > + if (fault_should_retry_under_vma_lock(fault, &flags))
> > + goto retry_vma;
> > lock_mmap:
> >
> > retry:
> > diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> > index 806c74e0d5ab..e2a128fba408 100644
> > --- a/arch/powerpc/mm/fault.c
> > +++ b/arch/powerpc/mm/fault.c
> > @@ -487,6 +487,7 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
> > if (!(flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> >
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, address);
> > if (!vma)
> > goto lock_mmap;
> > @@ -511,12 +512,13 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
> > goto done;
> > }
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - flags |= FAULT_FLAG_TRIED;
> >
> > if (fault_signal_pending(fault, regs))
> > return user_mode(regs) ? 0 : SIGBUS;
> >
> > + if (fault_should_retry_under_vma_lock(fault, &flags))
> > + goto retry_vma;
> > +
> > lock_mmap:
> >
> > /* When running in the kernel we expect faults to occur only to
> > diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c
> > index 04ed6f8acae4..87b061feba51 100644
> > --- a/arch/riscv/mm/fault.c
> > +++ b/arch/riscv/mm/fault.c
> > @@ -347,6 +347,7 @@ void handle_page_fault(struct pt_regs *regs)
> > if (!(flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> >
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, addr);
> > if (!vma)
> > goto lock_mmap;
> > @@ -368,14 +369,15 @@ void handle_page_fault(struct pt_regs *regs)
> > goto done;
> > }
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - flags |= FAULT_FLAG_TRIED;
> >
> > if (fault_signal_pending(fault, regs)) {
> > if (!user_mode(regs))
> > no_context(regs, addr);
> > return;
> > }
> > +
> > + if (fault_should_retry_under_vma_lock(fault, &flags))
> > + goto retry_vma;
> > lock_mmap:
> >
> > retry:
> > diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
> > index 028aeb9c48d6..8e90e522436b 100644
> > --- a/arch/s390/mm/fault.c
> > +++ b/arch/s390/mm/fault.c
> > @@ -294,6 +294,7 @@ static void do_exception(struct pt_regs *regs, int access)
> > flags |= FAULT_FLAG_WRITE;
> > if (!(flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, address);
> > if (!vma)
> > goto lock_mmap;
> > @@ -310,14 +311,14 @@ static void do_exception(struct pt_regs *regs, int access)
> > goto done;
> > }
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - flags |= FAULT_FLAG_TRIED;
> > /* Quick path to respond to signals */
> > if (fault_signal_pending(fault, regs)) {
> > if (!user_mode(regs))
> > handle_fault_error_nolock(regs, 0);
> > return;
> > }
> > + if (fault_should_retry_under_vma_lock(fault, &flags))
> > + goto retry_vma;
> > lock_mmap:
> > retry:
> > vma = lock_mm_and_find_vma(mm, address, regs);
> > diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> > index 45b99c3b1442..53c8f003fe53 100644
> > --- a/arch/x86/mm/fault.c
> > +++ b/arch/x86/mm/fault.c
> > @@ -1331,6 +1331,7 @@ void do_user_addr_fault(struct pt_regs *regs,
> > if (!(flags & FAULT_FLAG_USER))
> > goto lock_mmap;
> >
> > +retry_vma:
> > vma = lock_vma_under_rcu(mm, address);
> > if (!vma)
> > goto lock_mmap;
> > @@ -1349,8 +1350,6 @@ void do_user_addr_fault(struct pt_regs *regs,
> > goto done;
> > }
> > count_vm_vma_lock_event(VMA_LOCK_RETRY);
> > - if (fault & VM_FAULT_MAJOR)
> > - flags |= FAULT_FLAG_TRIED;
> >
> > /* Quick path to respond to signals */
> > if (fault_signal_pending(fault, regs)) {
> > @@ -1360,6 +1359,9 @@ void do_user_addr_fault(struct pt_regs *regs,
> > ARCH_DEFAULT_PKEY);
> > return;
> > }
> > +
> > + if (fault_should_retry_under_vma_lock(fault, &flags))
> > + goto retry_vma;
> > lock_mmap:
> >
> > retry:
> > diff --git a/include/linux/mm.h b/include/linux/mm.h
> > index 7fabe6c66b4b..27ec6673acfe 100644
> > --- a/include/linux/mm.h
> > +++ b/include/linux/mm.h
> > @@ -727,6 +727,34 @@ static inline bool fault_flag_allow_retry_first(enum fault_flag flags)
> > (!(flags & FAULT_FLAG_TRIED));
> > }
> >
> > +/**
> > + * fault_should_retry_under_vma_lock - decide whether to retry with VMA lock
> > + * @fault: fault result from handle_mm_fault() under FAULT_FLAG_VMA_LOCK
> > + * @flags: fault flags for the current fault, updated on retry
> > + *
> > + * Architecture page fault handlers call this after a VMA-lock fault returns
> > + * VM_FAULT_RETRY. If the fault result also has VM_FAULT_MAY_USE_VMA_LOCK,
> > + * allow one bounded retry under the VMA lock and set FAULT_FLAG_TRIED.
> > + *
> > + * When the fault must fall back to the mmap_lock path, preserve the existing
> > + * VM_FAULT_MAJOR behavior by marking FAULT_FLAG_TRIED before the retry.
> > + *
> > + * Return: true if the caller should retry under the VMA lock, false if it
>
> Oh good half of the kdoc comments in mm.h have Return and the other half have
> Returns: :)) OK I guess Return is fine here.
>
> > + * should fall back to the mmap_lock fault path.
> > + */
> > +static inline bool fault_should_retry_under_vma_lock(vm_fault_t fault, unsigned int *flags)
> > +{
> > + if ((fault & VM_FAULT_MAY_USE_VMA_LOCK) && !(*flags & FAULT_FLAG_TRIED)) {
> > + *flags |= FAULT_FLAG_TRIED;
> > + return true;
> > + }
> > +
> > + if (fault & VM_FAULT_MAJOR)
> > + *flags |= FAULT_FLAG_TRIED;
> > +
> > + return false;
> > +}
>
> I really hate this function. It's not doing what it says it is (it's
> modifying fault behaviour too via the flags parameter AND setting state for
> major faults), it's combining VMA fault path handling AND mmap major fault
> handling it's doing ugly horrors with an output parameter.
>
> I think it's better just as a straight-up predicate. Yes there'll be
> duplication on setting FAULT_FLAG_TRIED. It sucks, but you're _already_
> duplicating every single invocation and goto anyway.
>
> The fix for that is finally de-duplicating the arch fault code properly.
>
> So it'd be like:
>
> < kdoc etc. >
> static inline bool should_retry_fault_under_vma_lock(vm_fault fault_type,
> unsigned int flags)
> {
> /* Don't wait holding the VMA lock. */
> if (!(fault_type & VM_FAULT_RETRY_WONT_BLOCK))
> return false;
> /* Already retried the fault under the VMA lock. */
> if (flags & FAULT_FLAG_TRIED)
> return false;
> return true;
> }
>
>
> - if (fault_should_retry_under_vma_lock(fault, &flags))
> - goto retry_vma;
> + if (should_retry_fault_under_vma_lock(fault, flags)) {
> + flags |= FAULT_FLAG_TRIED;
> + goto retry_vma;
> + }
> +
> + if (fault & VM_FAULT_MAJOR)
> + flags |= FAULT_FLAG_TRIED;
>
> This way also you explicitly see where FAULT_FLAG_TRIED is set.
>
> > +
> > #define FAULT_FLAG_TRACE \
> > { FAULT_FLAG_WRITE, "WRITE" }, \
> > { FAULT_FLAG_MKWRITE, "MKWRITE" }, \
> > diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> > index b5d4cd3b067b..46a832757109 100644
> > --- a/include/linux/mm_types.h
> > +++ b/include/linux/mm_types.h
> > @@ -1684,6 +1684,8 @@ typedef __bitwise unsigned int vm_fault_t;
> > * @VM_FAULT_NOPAGE: ->fault installed the pte, not return page
> > * @VM_FAULT_LOCKED: ->fault locked the returned page
> > * @VM_FAULT_RETRY: ->fault blocked, must retry
> > + * @VM_FAULT_MAY_USE_VMA_LOCK: ->fault blocked, retry may be handled under
> > + * the VMA lock
> > * @VM_FAULT_FALLBACK: huge page fault failed, fall back to small
> > * @VM_FAULT_DONE_COW: ->fault has fully handled COW
> > * @VM_FAULT_NEEDDSYNC: ->fault did not modify page tables and needs
> > @@ -1707,6 +1709,7 @@ enum vm_fault_reason {
> > VM_FAULT_DONE_COW = (__force vm_fault_t)0x001000,
> > VM_FAULT_NEEDDSYNC = (__force vm_fault_t)0x002000,
> > VM_FAULT_COMPLETED = (__force vm_fault_t)0x004000,
> > + VM_FAULT_MAY_USE_VMA_LOCK = (__force vm_fault_t)0x008000,
> > VM_FAULT_HINDEX_MASK = (__force vm_fault_t)0x0f0000,
> > };
> >
> > @@ -1731,6 +1734,7 @@ enum vm_fault_reason {
> > { VM_FAULT_FALLBACK, "FALLBACK" }, \
> > { VM_FAULT_DONE_COW, "DONE_COW" }, \
> > { VM_FAULT_NEEDDSYNC, "NEEDDSYNC" }, \
> > + { VM_FAULT_MAY_USE_VMA_LOCK, "MAY_USE_VMA_LOCK" }, \
>
> This makes the naming confusion even worse - because now we have
> FAULT_FLAG_VMA_LOCK and this which 'specifies' may use VMA lock whereas
> really it means 'may use once on retry'...
>
> So again, as per above, I think it's better to actually have this
> communicate the _reason_ why it seems OK to use the VMA lock on retry
> rather than actually specifying that the caller should.
>
Will address in the next revision if we keep this approach.
> > { VM_FAULT_COMPLETED, "COMPLETED" }
> >
> > struct vm_special_mapping {
> > --
> > 2.43.0
> >
>
> BTW in sanitize_fault_flags() there's:
>
> /*
> * Per-VMA locks can't be used with FAULT_FLAG_RETRY_NOWAIT because of
> * the assumption that lock is dropped on VM_FAULT_RETRY.
> */
> if (WARN_ON_ONCE((*flags &
> (FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)) ==
> (FAULT_FLAG_VMA_LOCK | FAULT_FLAG_RETRY_NOWAIT)))
> return VM_FAULT_SIGSEGV;
>
> Except now VM_FAULT_RETRY doesn't drop the lock :) I think the comment
> needs to be updated to say 'dropped on VM_FAULT_RETRY after one attempt'.
The patch doesn't change VM_FAULT_RETRY semantics: returning VM_FAULT_RETRY
still means the lock has been dropped, whether it's the mmap lock or the VMA
lock. The retry path re-acquires the VMA lock before the second attempt. So
I think the existing comment is still accurate?
> Also I think there's an issue with major/minor fault counting as a result
> of this, in mm_account_fault():
>
> /*
> * We define the fault as a major fault when the final successful fault
> * is VM_FAULT_MAJOR, or if it retried (which implies that we couldn't
> * handle it immediately previously).
> */
> major = (ret & VM_FAULT_MAJOR) || (flags & FAULT_FLAG_TRIED);
>
> if (major)
> current->maj_flt++;
> else
> current->min_flt++;
>
> Now you're getting minor faults being counted as major ones?
>
> If intended, that's a user-visible change that should be documented and
> defended in the commit msg.
>
> If not, you could do some REALLY gross checks making things _even more
> complicated_ here.
>
> But maybe something like:
>
> < kdoc comment etc. >
> static bool is_major_fault(vm_fault_t fault_type, unsigned int fault_flags)
> {
> /* Explicitly marked as major. */
> if (fault_type & VM_FAULT_MAJOR)
> return true;
> /* If no retry occurred, minor. */
> if (!(fault_flags & FAULT_FLAG_TRIED))
> return false;
> /* Quickly retrying fault under the VMA lock implies minor. */
> return !(fault_flags & FAULT_FLAG_VMA_LOCK);
> }
>
> Then the code above becomes:
>
> if (is_major_fault(ret, flags))
> current->maj_flt++;
> else
> current->min_flt++;
>
> --
> Cheers, Lorenzo
With FAULT_FLAG_TRIED set, the retry attempt behaves identically regardless
of whether it's under the mmap lock or the VMA lock. Is a change still
needed here?
Thanks,
Hongru