Re: [RFC PATCH v3] mm: retry page faults under per-VMA lock when mmap_lock is not required

Barry Song <[email protected]> Tue, 4 Aug 2026 06:48:27 +0800
Newsgroups org.kvack.linux-mm,org.kernel.vger.linux-kernel
Message-ID <CAGsJ_4x3HiRP7QU=N-oPiWuxuTG1VvzCCdHPqr3Trdk_8WMUGw@mail.gmail.com>
On Sun, Jul 12, 2026 at 9:28=E2=80=AFPM Hongru Zhang <[email protected]=
om> wrote:
>
> > On Tue, Jul 07, 2026 at 05:52:31PM +0100, Lorenzo Stoakes wrote:
> > > +cc Vlata, the only one from the MEMORY MAPPING - LOCKING people excl=
uded,
> > > and Willy as he's been heavily involved. But see below you need to cc=
- way
> > > more.
> >
> > Ugh, I hadn't seen this.
> >
> > No.
> >
> > I've been looking into this problem, and as usual it has turned into a
> > yak-shaving exercise.  I think I'm five yaks deep at this point:
> >
> >  - Need to start by simplifying filemap_fault
> >    https://lore.kernel.org/linux-mm/20260625195040.2508362-1-willy@infr=
adead.org/
>
> Hi Matthew,
>
> Thanks for looking at this.
>
> I understand your concern about adding more complexity to an already
> complicated fault path.
>
> However, I tested the RFC patch [1] on Android workloads and observed
> lock-wait regressions. The common pattern is that filemap_fault() can per=
form
> file-backed I/O while holding either mmap_lock or a per-VMA lock. The tra=
ces
> show three representative classes of lock-wait regressions: GUP faults,
> kernel uaccess faults, and per-VMA-lock faults blocking fork / copy_mm().
>
> 1) GUP faults under mmap_lock
>
>   APP: com.xs.fm.lite
>   lock type: mmap_lock
>   holder: Thread-77
>   faulted file: libbytehook.so
>   lock-held filemap fault time: 95.606ms
>   blocked writer: NetDownload#15 (blocked 218.971ms)
>
>   Holder stack:
>     filemap_fault+0x0
>     __do_fault+0xdc
>     do_pte_missing+0x2c8
>     handle_mm_fault+0x4c8
>     __get_user_pages+0x618
>     __gup_longterm_locked+0xac
>     pin_user_pages_remote+0x88
>     process_vm_rw+0x2cc
>     __arm64_sys_process_vm_readv+0x28
>     invoke_syscall+0x58
>     el0_svc_common+0x80
>     do_el0_svc+0x1c
>     el0_svc+0x48
>     el0t_64_sync_handler+0x70
>     el0t_64_sync+0x1bc
>
>   Blocked writer stack:
>     vm_mmap_pgoff+0x168
>     ksys_mmap_pgoff+0xa4
>     __arm64_sys_mmap+0x34
>     invoke_syscall+0x58
>     el0_svc_common+0x80
>     do_el0_svc+0x1c
>     el0_svc+0x48
>     el0t_64_sync_handler+0x70
>     el0t_64_sync+0x1bc
>
> 2) kernel uaccess faults under mmap_lock
>
>   APP: com.xingin.xhs
>   lock type: mmap_lock
>   holder: Thread-2531
>   faulted file: libtiny.so
>   lock-held filemap fault time: 172.375ms
>   blocked writer count: 5
>   writer wait range: 152.766ms - 172.006ms
>   representative blocked writer: com.xingin.xhs (main thread, blocked 172=
.006ms)
>
>   Holder stack:
>     filemap_fault+0x0
>     __do_fault+0xdc
>     do_pte_missing+0x2c8
>     handle_mm_fault+0x4c8
>     do_page_fault+0x39c
>     do_translation_fault+0x4c
>     do_mem_abort+0x54
>     el1_abort+0x3c
>     el1h_64_sync_handler+0x80
>     el1h_64_sync+0x7c
>     __arch_copy_from_user+0x1b4
>     copy_page_from_iter+0xd0
>     pipe_write+0x1ec
>     vfs_write+0x368
>     ksys_write+0x78
>     __arm64_sys_write+0x1c
>     invoke_syscall+0x58
>     el0_svc_common+0x80
>     do_el0_svc+0x1c
>     el0_svc+0x48
>     el0t_64_sync_handler+0x70
>     el0t_64_sync+0x1bc
>
>   Blocked writer stack:
>     vm_mmap_pgoff+0x168
>     ksys_mmap_pgoff+0xa4
>     __arm64_sys_mmap+0x34
>     invoke_syscall+0x58
>     el0_svc_common+0x80
>     do_el0_svc+0x1c
>     el0_svc+0x48
>     el0t_64_sync_handler+0x70
>     el0t_64_sync+0x1bc
>
> 3) filemap faults under per-VMA lock blocking fork / copy_mm()
>
> Other testing also reported similar per-VMA-lock blocking data [2], where
> filemap faults under a VMA read lock can block fork / copy_mm(). One exam=
ple
> from my trace is:
>
>   APP: com.xingin.xhs
>   lock type: per-VMA lock
>   holder: xylog_thread_po
>   faulted file: 2026-07-07-11-51-46.203.log
>   lock-held filemap fault time: 10.518ms
>   blocked writer: com.xingin.xhs (blocked 1.583ms)
>
>   Holder stack:
>     filemap_fault+0x0
>     __do_fault+0xdc
>     do_pte_missing+0x2c8
>     handle_mm_fault+0x4c8
>     do_page_fault+0x30c
>     do_translation_fault+0x4c
>     do_mem_abort+0x54
>     el0_da+0x54
>     el0t_64_sync_handler+0x50
>     el0t_64_sync+0x1bc
>
>   Blocked writer stack:
>     copy_mm+0x2a4
>     copy_process+0x4ec
>     kernel_clone+0xb0
>     __arm64_sys_clone+0x60
>     invoke_syscall+0x58
>     el0_svc_common+0x80
>     do_el0_svc+0x1c
>     el0_svc+0x48
>     el0t_64_sync_handler+0x70
>     el0t_64_sync+0x1bc
>

Thanks, Hongru. It seems these stacks clearly expose paths such as
GUP and copy_from/to_user() where I/O may be performed after
Matthew's approach is adopted.

I am putting together a list of paths that would perform I/O under
mmap_lock, along with possible ways to address them.

Performing I/O while holding mmap_lock may result in unpredictable
and potentially long mmap_lock hold times, so we need to address
these issues at least.

1. GUP doesn't support vma lock

Maybe this can be addressed by Rik van Riel's patchset? I haven't
looked into it in detail yet, so I'm not entirely sure.

mm: use per-VMA lock in __access_remote_vm for improved monitoring reliabil=
ity

https://lore.kernel.org/linux-mm/[email protected]/

2. copy_from/to_user doesn't support vma lock

Maybe this can be addressed by the topic currently under
discussion?

mm: use VMA lock for kernel faults on user addresses

https://lore.kernel.org/linux-mm/[email protected]/

3. first PF before retry might have been mmap_lock:

Maybe this can be addressed by `vma_start_read_unlocked()`, as we
discussed here:

mm: Unconditional per-VMA locks and cleanups

https://lore.kernel.org/linux-mm/CAJuCfpEF6SjKhRW33AuboAuUbQz5zv+QKr2eD=3D5=
[email protected]/

4. The last one is the fork regression, which is unrelated to
I/O under mmap_lock.

- No approach yet. Perhaps Android could enforce some rules to
  prevent apps from calling fork() in the future?

Thanks
Barry