Do not shatter hugezeropage on wp-fault
Subsystems (3)
Dev Jain
reviewercover Do not shatter hugezeropage on wp-fault
It was observed at [1] and [2] that the current kernel behaviour of shattering a hugezeropage is inconsistent and suboptimal. For a VMA with a THP allowable order, when we write-fault on it, the kernel installs a PMD-mapped THP. On the other hand, if we first get a read fault, we get a PMD pointing to the hugezeropage; subsequent write will trigger a write-protection fault, shattering the hugezeropage into one writable page, and all the other PTEs write-protected. The conclusion being, as compared to the case of a single write-fault, applications have to suffer 512 extra page faults if they were to use the VMA as such, plus we get the overhead of khugepaged trying to replace that area with a THP anyway.
Instead, replace the hugezeropage with a THP on wp-fault.
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/all/[email protected]/
Dev Jain (2):
mm: Abstract THP allocation
mm: Allocate THP on hugezeropage wp-fault
include/linux/huge_mm.h | 7 ++ mm/huge_memory.c | 171 +++++++++++++++++++++++++++++----------- mm/memory.c | 5 +- 3 files changed, 136 insertions(+), 47 deletions(-)
Signature · 1 line
2.30.2
Dev Jain
reviewerPATCH 1/2 mm: Abstract THP allocation
In preparation for the second patch, abstract away the THP allocation logic present in the create_huge_pmd() path, which corresponds to the faulting case when no page is present.
There should be no functional change as a result of applying this patch.
--- mm/huge_memory.c | 113 +++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 44 deletions(-)
mm/huge_memory.c +69 -44
@@ -943,47 +943,92 @@ unsigned long thp_get_unmapped_area(struct file *filp, unsigned long addr, }EXPORT_SYMBOL_GPL (thp_get_unmapped_area); -static vm_fault_t __do_huge_pmd_anonymous_page (struct vm_fault *vmf, -struct page *page,gfp_t gfp) +static vm_fault_t thp_fault_alloc (gfp_t gfp,int order,struct vm_area_struct *vma, +unsigned long haddr,struct folio **foliop, +unsigned long addr) { -struct vm_area_struct *vma = vmf->vma; -struct folio *folio =page_folio (page); -pgtable_t pgtable; -unsigned long haddr = vmf->address &HPAGE_PMD_MASK ; -vm_fault_t ret =0 ; +struct folio *folio =vma_alloc_folio (gfp, order, vma, haddr, true); -VM_BUG_ON_FOLIO (!folio_test_large (folio), folio); + *foliop = folio; +if (unlikely (!folio)) { +count_vm_event (THP_FAULT_FALLBACK ); +count_mthp_stat (order,MTHP_STAT_ANON_FAULT_FALLBACK ); +return VM_FAULT_FALLBACK ; + } +VM_BUG_ON_FOLIO (!folio_test_large (folio), folio);if (mem_cgroup_charge (folio, vma->vm_mm, gfp)) {folio_put (folio);count_vm_event (THP_FAULT_FALLBACK );count_vm_event (THP_FAULT_FALLBACK_CHARGE ); -count_mthp_stat (HPAGE_PMD_ORDER ,MTHP_STAT_ANON_FAULT_FALLBACK ); -count_mthp_stat (HPAGE_PMD_ORDER ,MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE ); +count_mthp_stat (order,MTHP_STAT_ANON_FAULT_FALLBACK ); +count_mthp_stat (order,MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE );return VM_FAULT_FALLBACK ; }folio_throttle_swaprate (folio, gfp); - pgtable =pte_alloc_one (vma->vm_mm); -if (unlikely (!pgtable)) { - ret =VM_FAULT_OOM ; - goto release; - } - -folio_zero_user (folio, vmf->address); +folio_zero_user (folio, addr);/* * The memory barrier inside __folio_mark_uptodate makes sure that * folio_zero_user writes become visible before the set_pmd_at() * write. */ __folio_mark_uptodate (folio); +return 0 ; +} + +static void __thp_fault_success_stats (struct vm_area_struct *vma,int order) +{ +count_vm_event (THP_FAULT_ALLOC ); +count_mthp_stat (order,MTHP_STAT_ANON_FAULT_ALLOC ); +count_memcg_event_mm (vma->vm_mm,THP_FAULT_ALLOC ); +} + +static void map_pmd_thp (struct folio *folio,struct vm_fault *vmf, +struct vm_area_struct *vma,unsigned long haddr, +pgtable_t pgtable) + __releases(vmf->ptl) +{ +pmd_t entry; + + entry =mk_huge_pmd (&folio->page, vma->vm_page_prot); + entry =maybe_pmd_mkwrite (pmd_mkdirty (entry), vma); +folio_add_new_anon_rmap (folio, vma, haddr,RMAP_EXCLUSIVE ); +folio_add_lru_vma (folio, vma); +pgtable_trans_huge_deposit (vma->vm_mm, vmf->pmd, pgtable); +set_pmd_at (vma->vm_mm, haddr, vmf->pmd, entry); +update_mmu_cache_pmd (vma, vmf->address, vmf->pmd); +add_mm_counter (vma->vm_mm,MM_ANONPAGES ,HPAGE_PMD_NR ); +mm_inc_nr_ptes (vma->vm_mm); +spin_unlock (vmf->ptl); +__thp_fault_success_stats (vma,HPAGE_PMD_ORDER ); +} + +static vm_fault_t __do_huge_pmd_anonymous_page (struct vm_fault *vmf) +{ +struct vm_area_struct *vma = vmf->vma; +struct folio *folio =NULL ; +pgtable_t pgtable; +unsigned long haddr = vmf->address &HPAGE_PMD_MASK ; +vm_fault_t ret =0 ; +gfp_t gfp =vma_thp_gfp_mask (vma); + + pgtable =pte_alloc_one (vma->vm_mm); +if (unlikely (!pgtable)) { + ret =VM_FAULT_OOM ; + goto release; + } + + ret =thp_fault_alloc (gfp,HPAGE_PMD_ORDER , vma, haddr, &folio, + vmf->address); +if (ret) + goto release; vmf->ptl =pmd_lock (vma->vm_mm, vmf->pmd); +if (unlikely (!pmd_none (*vmf->pmd))) { goto unlock_release; }else { -pmd_t entry; - ret =check_stable_address_space (vma->vm_mm);if (ret) goto unlock_release;
@@ -997,20 +1042,7 @@ static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf,VM_BUG_ON (ret &VM_FAULT_FALLBACK );return ret; } - - entry =mk_huge_pmd (page, vma->vm_page_prot); - entry =maybe_pmd_mkwrite (pmd_mkdirty (entry), vma); -folio_add_new_anon_rmap (folio, vma, haddr,RMAP_EXCLUSIVE ); -folio_add_lru_vma (folio, vma); -pgtable_trans_huge_deposit (vma->vm_mm, vmf->pmd, pgtable); -set_pmd_at (vma->vm_mm, haddr, vmf->pmd, entry); -update_mmu_cache_pmd (vma, vmf->address, vmf->pmd); -add_mm_counter (vma->vm_mm,MM_ANONPAGES ,HPAGE_PMD_NR ); -mm_inc_nr_ptes (vma->vm_mm); -spin_unlock (vmf->ptl); -count_vm_event (THP_FAULT_ALLOC ); -count_mthp_stat (HPAGE_PMD_ORDER ,MTHP_STAT_ANON_FAULT_ALLOC ); -count_memcg_event_mm (vma->vm_mm,THP_FAULT_ALLOC ); +map_pmd_thp (folio, vmf, vma, haddr, pgtable); }return 0 ;
@@ -1019,7 +1051,8 @@ static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf, release:if (pgtable)pte_free (vma->vm_mm, pgtable); -folio_put (folio); +if (folio) +folio_put (folio);return ret; }
@@ -1077,8 +1110,6 @@ static void set_huge_zero_folio(pgtable_t pgtable, struct mm_struct *mm,vm_fault_t do_huge_pmd_anonymous_page (struct vm_fault *vmf) {struct vm_area_struct *vma = vmf->vma; -gfp_t gfp; -struct folio *folio;unsigned long haddr = vmf->address &HPAGE_PMD_MASK ;vm_fault_t ret;
@@ -1129,14 +1160,8 @@ vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf) }return ret; } - gfp =vma_thp_gfp_mask (vma); - folio =vma_alloc_folio (gfp,HPAGE_PMD_ORDER , vma, haddr, true); -if (unlikely (!folio)) { -count_vm_event (THP_FAULT_FALLBACK ); -count_mthp_stat (HPAGE_PMD_ORDER ,MTHP_STAT_ANON_FAULT_FALLBACK ); -return VM_FAULT_FALLBACK ; - } -return __do_huge_pmd_anonymous_page (vmf, &folio->page, gfp); + +return __do_huge_pmd_anonymous_page (vmf); } static void insert_pfn_pmd(struct vm_area_struct *vma, unsigned long addr,
Signature · 1 line
2.30.2
Dev Jain
reviewerPATCH 2/2 mm: Allocate THP on hugezeropage wp-fault
In reply to Dev JainIntroduce do_huge_zero_wp_pmd() to handle wp-fault on a hugezeropage and replace it with a PMD-mapped THP. Change the helpers introduced in the previous patch to flush TLB entry corresponding to the hugezeropage, and preserve PMD uffd-wp marker. In case of failure, fallback to splitting the PMD.
--- include/linux/huge_mm.h | 7 ++++ mm/huge_memory.c | 76 +++++++++++++++++++++++++++++++++++------ mm/memory.c | 5 +-- 3 files changed, 76 insertions(+), 12 deletions(-)
include/linux/huge_mm.h +7 -0
@@ -9,6 +9,13 @@#include <linux/kobject.h> vm_fault_t do_huge_pmd_anonymous_page (struct vm_fault *vmf); +vm_fault_t thp_fault_alloc (gfp_t gfp,int order,struct vm_area_struct *vma, +unsigned long haddr,struct folio **foliop, +unsigned long addr); +void map_pmd_thp(struct folio *folio, struct vm_fault *vmf, + struct vm_area_struct *vma, unsigned long haddr, + pgtable_t pgtable) + __releases(vmf->ptl);int copy_huge_pmd (struct mm_struct *dst_mm,struct mm_struct *src_mm,pmd_t *dst_pmd,pmd_t *src_pmd,unsigned long addr,struct vm_area_struct *dst_vma,struct vm_area_struct *src_vma);
mm/huge_memory.c +66 -10
111 lines not on this page · raw message
mm/memory.c +3 -2
@@ -5409,9 +5409,10 @@ static inline vm_fault_t wp_huge_pmd(struct vm_fault *vmf)if (vma_is_anonymous (vma)) {if (likely (!unshare) &&userfaultfd_huge_pmd_wp (vma, vmf->orig_pmd)) { -if (userfaultfd_wp_async (vmf->vma)) +if (!userfaultfd_wp_async (vmf->vma)) +return handle_userfault (vmf,VM_UFFD_WP ); +if (!is_huge_zero_pmd (vmf->orig_pmd)) goto split; -return handle_userfault (vmf,VM_UFFD_WP ); }return do_huge_pmd_wp_page (vmf); }
Signature · 1 line
2.30.2
kernel test robot
build botRe: [PATCH 2/2] mm: Allocate THP on hugezeropage wp-fault
kernel test robot noticed "BUG:sleeping_function_called_from_invalid_context_at_mm/memory.c" on:
Hello,
kernel test robot noticed "BUG:sleeping_function_called_from_invalid_context_at_mm/memory.c" on:
commit: c636ba74f021bfe8d72845f9e53ee2b8ea16f5f8 ("[PATCH 2/2] mm: Allocate THP on hugezeropage wp-fault")
url: https://github.com/intel-lab-lkp/linux/commits/Dev-Jain/mm-Abstract-THP-allocation/20240830-164300
base: https://git.kernel.org/cgit/linux/kernel/git/akpm/mm.git mm-everything
patch link: https://lore.kernel.org/all/[email protected]/
patch subject: [PATCH 2/2] mm: Allocate THP on hugezeropage wp-fault
in testcase: trinity
version: trinity-i386-abe9de86-1_20230429
with following parameters:
runtime: 600s
compiler: gcc-12
test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G
(please refer to attached dmesg/kmsg for entire log/backtrace)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-lkp/[email protected]
[ 189.202955][T15284] BUG: sleeping function called from invalid context at mm/memory.c:6690
[ 189.203611][T15284] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 15284, name: trinity-c6
[ 189.204103][T15284] preempt_count: 1, expected: 0
[ 189.204364][T15284] RCU nest depth: 0, expected: 0
[ 189.204630][T15284] 2 locks held by trinity-c6/15284:
[189.204909][T15284] #0: ffff888164fc27b0 (&mm->mmap_lock){++++}-{3:3}, at: lock_mm_and_find_vma (arch/x86/include/asm/atomic.h:23 include/linux/atomic/atomic-arch-fallback.h:457 include/linux/jump_label.h:261 include/linux/jump_label.h:273 include/linux/mmap_lock.h:35 include/linux/mmap_lock.h:164 mm/memory.c:6067 mm/memory.c:6127)
[189.205536][T15284] #1: ffff888160945c48 (ptlock_ptr(ptdesc)){+.+.}-{2:2}, at: do_huge_pmd_wp_page (mm/huge_memory.c:1816 mm/huge_memory.c:1838)
[ 189.206099][T15284] CPU: 1 UID: 65534 PID: 15284 Comm: trinity-c6 Not tainted 6.11.0-rc4-00551-gc636ba74f021 #1
[ 189.206657][T15284] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
[ 189.207205][T15284] Call Trace:
[ 189.207389][T15284] <TASK>
[189.207552][T15284] dump_stack_lvl (lib/dump_stack.c:122)
[189.207803][T15284] __might_resched (kernel/sched/core.c:8462)
[189.208106][T15284] ? __pfx___might_resched (kernel/sched/core.c:8416)
[189.208397][T15284] folio_zero_user (include/linux/kernel.h:73 mm/memory.c:6690 mm/memory.c:6767)
[189.208657][T15284] thp_fault_alloc (include/linux/page-flags.h:785 (discriminator 2) mm/huge_memory.c:1156 (discriminator 2))
[189.208914][T15284] do_huge_pmd_wp_page (mm/huge_memory.c:1792 mm/huge_memory.c:1818 mm/huge_memory.c:1838)
[189.209197][T15284] ? __lock_release+0x3fe/0x860
[189.209494][T15284] ? __pfx_do_huge_pmd_wp_page (mm/huge_memory.c:1826)
[189.209802][T15284] __handle_mm_fault (mm/memory.c:5614 mm/memory.c:5852)
[189.210072][T15284] ? mt_find (lib/maple_tree.c:6961)
[189.210303][T15284] ? __pfx___handle_mm_fault (mm/memory.c:5771)
[189.210595][T15284] ? __pfx_mt_find (lib/maple_tree.c:6927)
[189.210875][T15284] handle_mm_fault (mm/memory.c:6042)
[189.211134][T15284] ? __pfx_handle_mm_fault (mm/memory.c:5997)
[189.211424][T15284] ? down_read_trylock (kernel/locking/rwsem.c:1568)
[189.211701][T15284] ? lock_mm_and_find_vma (mm/memory.c:6130)
[189.211997][T15284] do_user_addr_fault (arch/x86/mm/fault.c:1391)
[189.212283][T15284] exc_page_fault (arch/x86/include/asm/irqflags.h:26 arch/x86/include/asm/irqflags.h:87 arch/x86/include/asm/irqflags.h:147 arch/x86/mm/fault.c:1489 arch/x86/mm/fault.c:1539)
[189.212539][T15284] asm_exc_page_fault (arch/x86/include/asm/idtentry.h:623)
[189.212811][T15284] RIP: 0010:__put_user_4 (arch/x86/lib/putuser.S:88)
[ 189.213091][T15284] Code: 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 48 89 cb 48 c1 fb 3f 48 09 d9 0f 1f 00 <89> 01 31 c9 0f 1f 00 c3 cc cc cc cc 0f 1f 00 90 90 90 90 90 90 90All code
========
0: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
7: 00
8: 90 nop 9: 90 nop a: 90 nop b: 90 nop c: 90 nop d: 90 nop e: 90 nop f: 90 nop 10: 90 nop 11: 90 nop 12: 90 nop 13: 90 nop 14: 90 nop 15: 90 nop 16: 90 nop 17: 90 nop 18: 90 nop 19: f3 0f 1e fa endbr64 1d: 48 89 cb mov %rcx,%rbx 20: 48 c1 fb 3f sar $0x3f,%rbx 24: 48 09 d9 or %rbx,%rcx 27: 0f 1f 00 nopl (%rax) 2a:* 89 01 mov %eax,(%rcx) <-- trapping instruction 2c: 31 c9 xor %ecx,%ecx 2e: 0f 1f 00 nopl (%rax) 31: c3 ret 32: cc int3 33: cc int3 34: cc int3 35: cc int3 36: 0f 1f 00 nopl (%rax) 39: 90 nop 3a: 90 nop 3b: 90 nop 3c: 90 nop 3d: 90 nop 3e: 90 nop 3f: 90 nop
Code starting with the faulting instruction
===========================================
0: 89 01 mov %eax,(%rcx) 2: 31 c9 xor %ecx,%ecx 4: 0f 1f 00 nopl (%rax) 7: c3 ret 8: cc int3 9: cc int3 a: cc int3 b: cc int3 c: 0f 1f 00 nopl (%rax) f: 90 nop 10: 90 nop 11: 90 nop 12: 90 nop 13: 90 nop 14: 90 nop 15: 90 nop [ 189.214257][T15284] RSP: 0000:ffff88812fc0fd70 EFLAGS: 00010206 [ 189.214726][T15284] RAX: 0000000020080522 RBX: 0000000000000000 RCX: 00000000ff7fffff [ 189.215167][T15284] RDX: 1ffff1102bfd712e RSI: 1ffff11075e09456 RDI: ffff88815feb8970 [ 189.215605][T15284] RBP: 00000000ff7fffff R08: ffff88815feb8978 R09: fffffbfff50eecad [ 189.216059][T15284] R10: ffffffffa877656f R11: 0000000000000000 R12: 1ffff11025f81faf [ 189.216637][T15284] R13: ffff88812fc0fe30 R14: 00000000000000b9 R15: 0000000000000000 [189.217074][T15284] cap_validate_magic (kernel/capability.c:94) [189.217349][T15284] ? __pfx_cap_validate_magic (kernel/capability.c:76) [189.217654][T15284] __do_sys_capset (kernel/capability.c:230) [189.217925][T15284] ? __pfx___do_sys_capset (kernel/capability.c:221) [189.218226][T15284] do_int80_emulation (arch/x86/entry/common.c:165 arch/x86/entry/common.c:253) [189.218497][T15284] asm_int80_emulation (arch/x86/include/asm/idtentry.h:626) [ 189.218871][T15284] RIP: 0023:0xf7f30092 [ 189.219200][T15284] Code: 00 00 00 e9 90 ff ff ff ff a3 24 00 00 00 68 30 00 00 00 e9 80 ff ff ff ff a3 f8 ff ff ff 66 90 00 00 00 00 00 00 00 00 cd 80 <c3> 8d b4 26 00 00 00 00 8d b6 00 00 00 00 8b 1c 24 c3 8d b4 26 00 All code ======== 0: 00 00 add %al,(%rax) 2: 00 e9 add %ch,%cl 4: 90 nop 5: ff (bad) 6: ff (bad) 7: ff (bad) 8: ff a3 24 00 00 00 jmp *0x24(%rbx) e: 68 30 00 00 00 push $0x30 13: e9 80 ff ff ff jmp 0xffffffffffffff98 18: ff a3 f8 ff ff ff jmp *-0x8(%rbx) 1e: 66 90 xchg %ax,%ax ... 28: cd 80 int $0x80 2a:* c3 ret <-- trapping instruction 2b: 8d b4 26 00 00 00 00 lea 0x0(%rsi,%riz,1),%esi 32: 8d b6 00 00 00 00 lea 0x0(%rsi),%esi 38: 8b 1c 24 mov (%rsp),%ebx 3b: c3 ret 3c: 8d .byte 0x8d 3d: b4 26 mov $0x26,%ah ...
Code starting with the faulting instruction
===========================================
0: c3 ret
1: 8d b4 26 00 00 00 00 lea 0x0(%rsi,%riz,1),%esi 8: 8d b6 00 00 00 00 lea 0x0(%rsi),%esi e: 8b 1c 24 mov (%rsp),%ebx 11: c3 ret 12: 8d .byte 0x8d 13: b4 26 mov $0x26,%ah
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20240903/[email protected]
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Dev Jain
reviewerRe: [PATCH 2/2] mm: Allocate THP on hugezeropage wp-fault
Quoted 26 lines · kernel test robot
Hello,
kernel test robot noticed "BUG:sleeping_function_called_from_invalid_context_at_mm/memory.c" on:
commit:
c636ba74f021bfe8d72845f9e53ee2b8ea16f5f8("[PATCH 2/2] mm: Allocate THP on hugezeropage wp-fault")
url: https://github.com/intel-lab-lkp/linux/commits/Dev-Jain/mm-Abstract-THP-allocation/20240830-164300
base: https://git.kernel.org/cgit/linux/kernel/git/akpm/mm.git mm-everything
patch link: https://lore.kernel.org/all/[email protected]/
patch subject: [PATCH 2/2] mm: Allocate THP on hugezeropage wp-faultin testcase: trinity
version: trinity-i386-abe9de86-1_20230429
with following parameters:runtime: 600scompiler: gcc-12
test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G(please refer to attached dmesg/kmsg for entire log/backtrace)
Quoted 8 lines
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-lkp/[email protected][ 189.202955][T15284] BUG: sleeping function called from invalid context at mm/memory.c:6690 [ 189.203611][T15284] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 15284, name: trinity-c6
I guess, since thp_fault_alloc() may sleep due to folio_zero_user(), I need to call it before do_huge_zero_wp_pmd_locked(). I will send a v2 since I also need to clean up the lock/unlock sites.