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

From: Barry Song

Date: Mon Aug 03 2026 - 18:48:57 EST


On Sun, Jul 12, 2026 at 9:28 PM Hongru Zhang <zhanghongru06@xxxxxxxxx> 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 excluded,
> > > 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@xxxxxxxxxxxxx/
>
> 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 perform
> file-backed I/O while holding either mmap_lock or a per-VMA lock. The traces
> 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 example
> 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 reliability

https://lore.kernel.org/linux-mm/20260724222934.1463812-1-riel@xxxxxxxxxxx/

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/20260802074018.73887-1-baohua@xxxxxxxxxx/

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=52Qd_POL18Kg@xxxxxxxxxxxxxx/

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