hugetlb: Disable huge pmd unshare for uffd-wp
Subsystems (13)
Peter Xu
reviewerv4 cover hugetlb: Disable huge pmd unshare for uffd-wp
v4:
- fix build for sparc by removing extra line in patch 1 [Mike]
- pick Mike's r-b for patch 4
v3:
- patch 4:
- fix build failure for !CMA and/or !HUGETLBFS [Axel]
- Fix mmu notifier range to use start/end [Mike]
- add more r-bs
v2:
- patch 4: move hugetlb_unshare_all_pmds() into mm/hugetlb.c, so it can be used even outside userfaultfd.c
This series tries to disable huge pmd unshare of hugetlbfs backed memory for uffd-wp. Although uffd-wp of hugetlbfs is still during rfc stage, the idea of this series may be needed for multiple tasks (Axel's uffd minor fault series, and Mike's soft dirty series), so I picked it out from the larger series.
References works:
Uffd shmem+hugetlbfs rfc:
https://lore.kernel.org/lkml/[email protected]/
Uffd minor mode for hugetlbfs:
https://lore.kernel.org/lkml/[email protected]/
Soft dirty for hugetlbfs:
https://lore.kernel.org/lkml/[email protected]/
Please review, thanks.
Peter Xu (4):
hugetlb: Pass vma into huge_pte_alloc() and huge_pmd_share() hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled mm/hugetlb: Move flush_hugetlb_tlb_range() into hugetlb.h hugetlb/userfaultfd: Unshare all pmds for hugetlbfs when register wp
arch/arm64/mm/hugetlbpage.c | 7 ++- arch/ia64/mm/hugetlbpage.c | 3 +- arch/mips/mm/hugetlbpage.c | 4 +- arch/parisc/mm/hugetlbpage.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 3 +- arch/s390/mm/hugetlbpage.c | 2 +- arch/sh/mm/hugetlbpage.c | 2 +- arch/sparc/mm/hugetlbpage.c | 2 +- fs/userfaultfd.c | 4 ++ include/linux/hugetlb.h | 18 ++++++- include/linux/userfaultfd_k.h | 9 ++++ mm/hugetlb.c | 94 +++++++++++++++++++++++++++-------- mm/userfaultfd.c | 2 +- 13 files changed, 116 insertions(+), 36 deletions(-)
Signature · 1 line
2.26.2
Peter Xu
reviewerPATCH v4 1/4 hugetlb: Pass vma into huge_pte_alloc() and huge_pmd_share()
It is a preparation work to be able to behave differently in the per architecture huge_pte_alloc() according to different VMA attributes.
Pass it deeper into huge_pmd_share() so that we can avoid the find_vma() call.
--- arch/arm64/mm/hugetlbpage.c | 4 ++-- arch/ia64/mm/hugetlbpage.c | 3 ++- arch/mips/mm/hugetlbpage.c | 4 ++-- arch/parisc/mm/hugetlbpage.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 3 ++- arch/s390/mm/hugetlbpage.c | 2 +- arch/sh/mm/hugetlbpage.c | 2 +- arch/sparc/mm/hugetlbpage.c | 2 +- include/linux/hugetlb.h | 5 +++-- mm/hugetlb.c | 15 ++++++++------- mm/userfaultfd.c | 2 +- 11 files changed, 24 insertions(+), 20 deletions(-)
arch/arm64/mm/hugetlbpage.c +2 -2
@@ -252,7 +252,7 @@ void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,set_pte (ptep, pte); } -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz) {pgd_t *pgdp;
@@ -286,7 +286,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, }else if (sz ==PMD_SIZE ) {if (IS_ENABLED (CONFIG_ARCH_WANT_HUGE_PMD_SHARE ) &&pud_none (READ_ONCE (*pudp))) - ptep =huge_pmd_share (mm, addr, pudp); + ptep =huge_pmd_share (mm, vma, addr, pudp);else ptep = (pte_t *)pmd_alloc (mm, pudp, addr); }else if (sz == (CONT_PMD_SIZE )) {
arch/ia64/mm/hugetlbpage.c +2 -1
@@ -25,7 +25,8 @@ unsigned int hpage_shift = HPAGE_SHIFT_DEFAULT;EXPORT_SYMBOL (hpage_shift);pte_t * -huge_pte_alloc (struct mm_struct *mm,unsigned long addr,unsigned long sz) +huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma, +unsigned long addr,unsigned long sz) {unsigned long taddr =htlbpage_to_page (addr);pgd_t *pgd;
arch/mips/mm/hugetlbpage.c +2 -2
@@ -21,8 +21,8 @@#include <asm/tlb.h> #include <asm/tlbflush.h> -pte_t *huge_pte_alloc (struct mm_struct *mm,unsigned long addr, -unsigned long sz) +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma, +unsigned long addr,unsigned long sz) {pgd_t *pgd;p4d_t *p4d;
arch/parisc/mm/hugetlbpage.c +1 -1
@@ -44,7 +44,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, } -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz) {pgd_t *pgd;
arch/powerpc/mm/hugetlbpage.c +2 -1
@@ -106,7 +106,8 @@ static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp,* At this point we do the placement change only for BOOK3S 64. This would * possibly work on other subarchs. */ -pte_t *huge_pte_alloc (struct mm_struct *mm,unsigned long addr,unsigned long sz) +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma, +unsigned long addr,unsigned long sz) {pgd_t *pg;p4d_t *p4;
arch/s390/mm/hugetlbpage.c +1 -1
@@ -189,7 +189,7 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm,return pte; } -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz) {pgd_t *pgdp;
arch/sh/mm/hugetlbpage.c +1 -1
@@ -21,7 +21,7 @@#include <asm/tlbflush.h> #include <asm/cacheflush.h> -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz) {pgd_t *pgd;
arch/sparc/mm/hugetlbpage.c +1 -1
@@ -279,7 +279,7 @@ unsigned long pud_leaf_size(pud_t pud) { return 1UL << tte_to_shift(*(pte_t *)&punsigned long pmd_leaf_size (pmd_t pmd) {return 1UL <<tte_to_shift (*(pte_t *)&pmd); }unsigned long pte_leaf_size (pte_t pte) {return 1UL <<tte_to_shift (pte); } -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz) {pgd_t *pgd;
include/linux/hugetlb.h +3 -2
@@ -152,7 +152,8 @@ void hugetlb_fix_reserve_counts(struct inode *inode);extern struct mutex *hugetlb_fault_mutex_table;u32 hugetlb_fault_mutex_hash (struct address_space *mapping,pgoff_t idx); -pte_t *huge_pmd_share (struct mm_struct *mm,unsigned long addr,pud_t *pud); +pte_t *huge_pmd_share (struct mm_struct *mm,struct vm_area_struct *vma, +unsigned long addr,pud_t *pud);struct address_space *hugetlb_page_mapping_lock_write (struct page *hpage);
@@ -161,7 +162,7 @@ extern struct list_head huge_boot_pages;/* arch callbacks */ -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz);pte_t *huge_pte_offset (struct mm_struct *mm,unsigned long addr,unsigned long sz);
mm/hugetlb.c +8 -7
@@ -3807,7 +3807,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, src_pte =huge_pte_offset (src, addr, sz);if (!src_pte)continue ; - dst_pte =huge_pte_alloc (dst, addr, sz); + dst_pte =huge_pte_alloc (dst, vma, addr, sz);if (!dst_pte) { ret = -ENOMEM ;break ;
@@ -4544,7 +4544,7 @@ vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,*/ mapping = vma->vm_file->f_mapping;i_mmap_lock_read (mapping); - ptep =huge_pte_alloc (mm, haddr,huge_page_size (h)); + ptep =huge_pte_alloc (mm, vma, haddr,huge_page_size (h));if (!ptep) {i_mmap_unlock_read (mapping);return VM_FAULT_OOM ;
@@ -5334,9 +5334,9 @@ void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,* if !vma_shareable check at the beginning of the routine. i_mmap_rwsem is * only required for subsequent processing. */ -pte_t *huge_pmd_share (struct mm_struct *mm,unsigned long addr,pud_t *pud) +pte_t *huge_pmd_share (struct mm_struct *mm,struct vm_area_struct *vma, +unsigned long addr,pud_t *pud) { -struct vm_area_struct *vma =find_vma (mm, addr);struct address_space *mapping = vma->vm_file->f_mapping;pgoff_t idx = ((addr - vma->vm_start) >>PAGE_SHIFT ) + vma->vm_pgoff;
@@ -5414,7 +5414,8 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, }#define want_pmd_share () (1)#else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */ -pte_t *huge_pmd_share (struct mm_struct *mm,unsigned long addr,pud_t *pud) +pte_t *huge_pmd_share (struct mm_struct *mm,struct vm_area_struct vma, +unsigned long addr,pud_t *pud) {return NULL ; }
@@ -5433,7 +5434,7 @@ void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,#endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */ #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB -pte_t *huge_pte_alloc (struct mm_struct *mm, +pte_t *huge_pte_alloc (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long sz) {pgd_t *pgd;
@@ -5452,7 +5453,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, }else {BUG_ON (sz !=PMD_SIZE );if (want_pmd_share () &&pud_none (*pud)) - pte =huge_pmd_share (mm, addr, pud); + pte =huge_pmd_share (mm, vma, addr, pud);else pte = (pte_t *)pmd_alloc (mm, pud, addr); }
mm/userfaultfd.c +1 -1
@@ -290,7 +290,7 @@ static __always_inline ssize_t __mcopy_atomic_hugetlb(struct mm_struct *dst_mm,mutex_lock (&hugetlb_fault_mutex_table[hash]); err = -ENOMEM ; - dst_pte =huge_pte_alloc (dst_mm, dst_addr, vma_hpagesize); + dst_pte =huge_pte_alloc (dst_mm, dst_vma, dst_addr, vma_hpagesize);if (!dst_pte) {mutex_unlock (&hugetlb_fault_mutex_table[hash]);i_mmap_unlock_read (mapping);
Signature · 1 line
2.26.2
kernel test robot
build botRe: [PATCH v4 1/4] hugetlb: Pass vma into huge_pte_alloc() and huge_pmd_share()
Thank you for the patch! Yet something to improve:
Hi Peter,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on arm64/for-next/core]
[also build test ERROR on linux/master linus/master v5.12-rc1 next-20210304] [cannot apply to hnaz-linux-mm/master]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Peter-Xu/hugetlb-Disable-huge-pmd-unshare-for-uffd-wp/20210219-071334 base: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core config: mips-randconfig-m031-20210304 (attached as .config) compiler: mipsel-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/7ede06d6f63e59db4b9dee54f78eeac0c9ca17e4
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Peter-Xu/hugetlb-Disable-huge-pmd-unshare-for-uffd-wp/20210219-071334 git checkout 7ede06d6f63e59db4b9dee54f78eeac0c9ca17e4
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=mips
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>):
>> mm/hugetlb.c:5376:8: error: conflicting types for 'huge_pmd_share'
5376 | pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct vma,
| ^~~~~~~~~~~~~~
In file included from mm/hugetlb.c:39:
include/linux/hugetlb.h:155:8: note: previous declaration of 'huge_pmd_share' was here
155 | pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
| ^~~~~~~~~~~~~~
vim +/huge_pmd_share +5376 mm/hugetlb.c
5343
5344 /*
5345 * unmap huge page backed by shared pte.
5346 *
5347 * Hugetlb pte page is ref counted at the time of mapping. If pte is shared
5348 * indicated by page_count > 1, unmap is achieved by clearing pud and
5349 * decrementing the ref count. If count == 1, the pte page is not shared.
5350 *
5351 * Called with page table lock held and i_mmap_rwsem held in write mode.
5352 *
5353 * returns: 1 successfully unmapped a shared pte page
5354 * 0 the underlying pte page is not shared, or it is the last user
5355 */
5356 int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
5357 unsigned long *addr, pte_t *ptep)
5358 {
5359 pgd_t *pgd = pgd_offset(mm, *addr);
5360 p4d_t *p4d = p4d_offset(pgd, *addr);
5361 pud_t *pud = pud_offset(p4d, *addr);
5362
5363 i_mmap_assert_write_locked(vma->vm_file->f_mapping);
5364 BUG_ON(page_count(virt_to_page(ptep)) == 0);
5365 if (page_count(virt_to_page(ptep)) == 1)
5366 return 0;
5367
5368 pud_clear(pud);
5369 put_page(virt_to_page(ptep));
5370 mm_dec_nr_pmds(mm);
5371 *addr = ALIGN(*addr, HPAGE_SIZE * PTRS_PER_PTE) - HPAGE_SIZE;
5372 return 1;
5373 }
5374 #define want_pmd_share() (1)
5375 #else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
> 5376 pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct vma,
5377 unsigned long addr, pud_t *pud)
5378 {
5379 return NULL;
5380 }
5381
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Peter Xu
reviewerRe: [PATCH v4 1/4] hugetlb: Pass vma into huge_pte_alloc() and huge_pmd_share()
Quoted 74 lines · kernel test robot
Hi Peter,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on arm64/for-next/core]
[also build test ERROR on linux/master linus/master v5.12-rc1 next-20210304] [cannot apply to hnaz-linux-mm/master]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]url: https://github.com/0day-ci/linux/commits/Peter-Xu/hugetlb-Disable-huge-pmd-unshare-for-uffd-wp/20210219-071334 base: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core config: mips-randconfig-m031-20210304 (attached as .config) compiler: mipsel-linux-gcc (GCC) 9.3.0reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/7ede06d6f63e59db4b9dee54f78eeac0c9ca17e4
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Peter-Xu/hugetlb-Disable-huge-pmd-unshare-for-uffd-wp/20210219-071334 git checkout7ede06d6f63e59db4b9dee54f78eeac0c9ca17e4
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=mipsIf you fix the issue, kindly add following tag as appropriate
Reported-by kernel test robot <[email protected]>All errors (new ones prefixed by >>):
Quoted 1 line
Quoted 1 line
mm/hugetlb.c:5376:8: error: conflicting types for 'huge_pmd_share'5376 |
pte_t*huge_pmd_share(structmm_struct*mm, structvm_area_structvma,
| ^~~~~~~~~~~~~~
In file included frommm/hugetlb.c:39:
include/linux/hugetlb.h:155:8: note: previous declaration of 'huge_pmd_share' was here
155 |pte_t*huge_pmd_share(structmm_struct*mm, structvm_area_struct*vma,
| ^~~~~~~~~~~~~~vim +/huge_pmd_share +5376
mm/hugetlb.c5343
5344 /*
5345 * unmap huge page backed by shared pte.
5346 *
5347 * Hugetlb pte page is ref counted at the time of mapping. If pte is shared
5348 * indicated bypage_count> 1, unmap is achieved by clearing pud and
5349 * decrementing the ref count. If count == 1, the pte page is not shared.
5350 *
5351 * Called with page table lock held andi_mmap_rwsemheld in write mode.
5352 *
5353 * returns: 1 successfully unmapped a shared pte page
5354 * 0 the underlying pte page is not shared, or it is the last user
5355 */
5356 int huge_pmd_unshare(structmm_struct*mm, structvm_area_struct*vma,
5357 unsigned long *addr,pte_t*ptep)
5358 {
5359pgd_t*pgd =pgd_offset(mm, *addr);
5360p4d_t*p4d =p4d_offset(pgd, *addr);
5361pud_t*pud =pud_offset(p4d, *addr);
5362
5363i_mmap_assert_write_locked(vma->vm_file->f_mapping);
5364BUG_ON(page_count(virt_to_page(ptep)) == 0);
5365 if (page_count(virt_to_page(ptep)) == 1)
5366 return 0;
5367
5368pud_clear(pud);
5369 put_page(virt_to_page(ptep));
5370mm_dec_nr_pmds(mm);
5371 *addr =ALIGN(*addr, HPAGE_SIZE * PTRS_PER_PTE)-HPAGE_SIZE;
5372 return 1;
5373 }
5374 #definewant_pmd_share()(1)
5375 #else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
Quoted 6 lines
Quoted 1 line
5376
pte_t*huge_pmd_share(structmm_struct*mm, structvm_area_structvma,5377 unsigned long addr,
pud_t*pud)
5378 {
5379 return NULL;
5380 }
5381
Sorry for this! I think we need to squash below into this patch for !CONFIG_ARCH_WANT_HUGE_PMD_SHARE:
-----8<-----
mm/hugetlb.c +1 -1
@@ -5415,7 +5415,7 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, }#else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */ -pte_t *huge_pmd_share (struct mm_struct *mm,struct vm_area_struct vma, +pte_t *huge_pmd_share (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,pud_t *pud) {return NULL ;
-----8<-----
Andrew, please kindly let me know when a repost is needed.
Thanks,
Signature · 1 line
Peter Xu
Peter Xu
reviewerPATCH v4 2/4 hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled
In reply to Peter XuHuge pmd sharing could bring problem to userfaultfd. The thing is that userfaultfd is running its logic based on the special bits on page table entries, however the huge pmd sharing could potentially share page table entries for different address ranges. That could cause issues on either:
- When sharing huge pmd page tables for an uffd write protected range, the newly mapped huge pmd range will also be write protected unexpectedly, or,
- When we try to write protect a range of huge pmd shared range, we'll first do huge_pmd_unshare() in hugetlb_change_protection(), however that also means the UFFDIO_WRITEPROTECT could be silently skipped for the shared region, which could lead to data loss.
Since at it, a few other things are done altogether:
- Move want_pmd_share() from mm/hugetlb.c into linux/hugetlb.h, because that's definitely something that arch code would like to use too
- ARM64 currently directly check against CONFIG_ARCH_WANT_HUGE_PMD_SHARE when trying to share huge pmd. Switch to the want_pmd_share() helper.
Since at it, move vma_shareable() from huge_pmd_share() into want_pmd_share().
--- arch/arm64/mm/hugetlbpage.c | 3 +-- include/linux/hugetlb.h | 2 ++ include/linux/userfaultfd_k.h | 9 +++++++++ mm/hugetlb.c | 20 ++++++++++++++------ 4 files changed, 26 insertions(+), 8 deletions(-)
arch/arm64/mm/hugetlbpage.c +1 -2
@@ -284,8 +284,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,*/ ptep =pte_alloc_map (mm, pmdp, addr); }else if (sz ==PMD_SIZE ) { -if (IS_ENABLED (CONFIG_ARCH_WANT_HUGE_PMD_SHARE ) && -pud_none (READ_ONCE (*pudp))) +if (want_pmd_share (vma, addr) &&pud_none (READ_ONCE (*pudp))) ptep =huge_pmd_share (mm, vma, addr, pudp);else ptep = (pte_t *)pmd_alloc (mm, pudp, addr);
include/linux/hugetlb.h +2 -0
@@ -950,4 +950,6 @@ static inline __init void hugetlb_cma_check(void) }#endif +bool want_pmd_share (struct vm_area_struct *vma,unsigned long addr); +#endif /* _LINUX_HUGETLB_H */
include/linux/userfaultfd_k.h +9 -0
@@ -52,6 +52,15 @@ static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma,return vma->vm_userfaultfd_ctx.ctx == vm_ctx.ctx; } +/* +* Never enable huge pmd sharing on uffd-wp registered vmas, because uffd-wp +* protect information is per pgtable entry. +*/ +static inline bool uffd_disable_huge_pmd_share (struct vm_area_struct *vma) +{ +return vma->vm_flags &VM_UFFD_WP ; +} +static inline bool userfaultfd_missing (struct vm_area_struct *vma) {return vma->vm_flags &VM_UFFD_MISSING ;
mm/hugetlb.c +14 -6
@@ -5292,6 +5292,18 @@ static bool vma_shareable(struct vm_area_struct *vma, unsigned long addr)return false; } +bool want_pmd_share (struct vm_area_struct *vma,unsigned long addr) +{ +#ifndef CONFIG_ARCH_WANT_HUGE_PMD_SHARE +return false; +#endif +#ifdef CONFIG_USERFAULTFD +if (uffd_disable_huge_pmd_share (vma)) +return false; +#endif +return vma_shareable (vma, addr); +} +/* * Determine if start,end range within vma could be mapped by shared pmd. * If yes, adjust start and end to cover range associated with possible
@@ -5346,9 +5358,6 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,pte_t *pte;spinlock_t *ptl; -if (!vma_shareable (vma, addr)) -return (pte_t *)pmd_alloc (mm, pud, addr); -i_mmap_assert_locked (mapping); vma_interval_tree_foreach(svma, &mapping->i_mmap, idx, idx) {if (svma == vma)
@@ -5412,7 +5421,7 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, *addr =ALIGN (*addr,HPAGE_SIZE *PTRS_PER_PTE ) -HPAGE_SIZE ; return 1; } -#define want_pmd_share () (1) +#else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */ pte_t *huge_pmd_share (struct mm_struct *mm,struct vm_area_struct vma,unsigned long addr,pud_t *pud)
@@ -5430,7 +5439,6 @@ void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma, unsigned long *start, unsigned long *end) { } -#define want_pmd_share () (0)#endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */ #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
@@ -5452,7 +5460,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, pte = (pte_t *)pud; }else {BUG_ON (sz !=PMD_SIZE ); -if (want_pmd_share () &&pud_none (*pud)) +if (want_pmd_share (vma, addr) &&pud_none (*pud)) pte =huge_pmd_share (mm, vma, addr, pud);else pte = (pte_t *)pmd_alloc (mm, pud, addr);
Signature · 1 line
2.26.2
Re: [PATCH v4 2/4] hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled
Hi Peter,
Quoted 39 lines · Peter Xu
Huge pmd sharing could bring problem to userfaultfd. The thing is that userfaultfd is running its logic based on the special bits on page table entries, however the huge pmd sharing could potentially share page table entries for different address ranges. That could cause issues on either:
- When sharing huge pmd page tables for an uffd write protected range, the newly mapped huge pmd range will also be write protected unexpectedly, or,
- When we try to write protect a range of huge pmd shared range, we'll first do
huge_pmd_unshare()inhugetlb_change_protection(), however that also means theUFFDIO_WRITEPROTECTcould be silently skipped for the shared region, which could lead to data loss.Since at it, a few other things are done altogether:
- Move
want_pmd_share()frommm/hugetlb.cintolinux/hugetlb.h, because that's definitely something that arch code would like to use too- ARM64 currently directly check against
CONFIG_ARCH_WANT_HUGE_PMD_SHAREwhen trying to share huge pmd. Switch to thewant_pmd_share()helper.Since at it, move
vma_shareable()fromhuge_pmd_share()intowant_pmd_share().Reviewed-by Mike Kravetz <[email protected]>Reviewed-by Axel Rasmussen <[email protected]>Signed-off-by Peter Xu <[email protected]>--- arch/arm64/mm/hugetlbpage.c | 3 +-- include/linux/hugetlb.h | 2 ++ include/linux/userfaultfd_k.h | 9 +++++++++ mm/hugetlb.c | 20 ++++++++++++++------ 4 files changed, 26 insertions(+), 8 deletions(-)arch/arm64/mm/hugetlbpage.c +0
-0
arch/arm64/mm/hugetlbpage.c pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, · line 287
Quoted 6 lines
arch/arm64/mm/hugetlbpage.c +1
-2@@ -284,8 +284,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,*/ ptep =pte_alloc_map (mm, pmdp, addr); }else if (sz ==PMD_SIZE ) { -if (IS_ENABLED (CONFIG_ARCH_WANT_HUGE_PMD_SHARE ) && -pud_none (READ_ONCE (*pudp))) +if (want_pmd_share (vma, addr) &&pud_none (READ_ONCE (*pudp)))
While building Linux next 20210310 tag for arm64 architecture with
- CONFIG_ARM64_64K_PAGES=y
enabled the build failed due to below errors / warnings
make --silent --keep-going --jobs=8 O=/home/tuxbuild/.cache/tuxmake/builds/1/tmp ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 'CC=sccache aarch64-linux-gnu-gcc' 'HOSTCC=sccache gcc'
aarch64-linux-gnu-ld: Unexpected GOT/PLT entries detected!
aarch64-linux-gnu-ld: Unexpected run-time procedure linkages detected!
aarch64-linux-gnu-ld: arch/arm64/mm/hugetlbpage.o: in function `huge_pte_alloc':
hugetlbpage.c:(.text+0x7d8): undefined reference to `want_pmd_share'
Steps to reproduce:
----------------------------
# TuxMake is a command line tool and Python library that provides
# portable and repeatable Linux kernel builds across a variety of # architectures, toolchains, kernel configurations, and make targets. # # TuxMake supports the concept of runtimes. # See https://docs.tuxmake.org/runtimes/, for that to work it requires # that you install podman or docker on your system. # # To install tuxmake on your system globally: # sudo pip3 install -U tuxmake # # See https://docs.tuxmake.org/ for complete documentation.
tuxmake --runtime podman --target-arch arm64 --toolchain gcc-9 --kconfig defconfig --kconfig-add
https://builds.tuxbuild.com/1pYCSoc1oGtPWlPgLAJxbHx07kL/config
Build link,
https://builds.tuxbuild.com/1pYCSoc1oGtPWlPgLAJxbHx07kL/
Signature · 2 lines
Linaro LKFT https://lkft.linaro.org
Peter Xu
reviewerRe: [PATCH v4 2/4] hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled
Quoted 1 line · Naresh Kamboju
Hi Peter,
Hi, Naresh,
Quoted 63 lines · Peter Xu
Quoted 39 lines
Huge pmd sharing could bring problem to userfaultfd. The thing is that userfaultfd is running its logic based on the special bits on page table entries, however the huge pmd sharing could potentially share page table entries for different address ranges. That could cause issues on either:
- When sharing huge pmd page tables for an uffd write protected range, the newly mapped huge pmd range will also be write protected unexpectedly, or,
- When we try to write protect a range of huge pmd shared range, we'll first do
huge_pmd_unshare()inhugetlb_change_protection(), however that also means theUFFDIO_WRITEPROTECTcould be silently skipped for the shared region, which could lead to data loss.Since at it, a few other things are done altogether:
- Move
want_pmd_share()frommm/hugetlb.cintolinux/hugetlb.h, because that's definitely something that arch code would like to use too- ARM64 currently directly check against
CONFIG_ARCH_WANT_HUGE_PMD_SHAREwhen trying to share huge pmd. Switch to thewant_pmd_share()helper.Since at it, move
vma_shareable()fromhuge_pmd_share()intowant_pmd_share().Reviewed-by Mike Kravetz <[email protected]>Reviewed-by Axel Rasmussen <[email protected]>Signed-off-by Peter Xu <[email protected]>--- arch/arm64/mm/hugetlbpage.c | 3 +-- include/linux/hugetlb.h | 2 ++ include/linux/userfaultfd_k.h | 9 +++++++++ mm/hugetlb.c | 20 ++++++++++++++------ 4 files changed, 26 insertions(+), 8 deletions(-)arch/arm64/mm/hugetlbpage.c +0
-0arch/arm64/mm/hugetlbpage.c pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, · line 287
Quoted 6 lines
arch/arm64/mm/hugetlbpage.c +1
-2@@ -284,8 +284,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,*/ ptep =pte_alloc_map (mm, pmdp, addr); }else if (sz ==PMD_SIZE ) { -if (IS_ENABLED (CONFIG_ARCH_WANT_HUGE_PMD_SHARE ) && -pud_none (READ_ONCE (*pudp))) +if (want_pmd_share (vma, addr) &&pud_none (READ_ONCE (*pudp)))While building Linux next 20210310 tag for arm64 architecture with
-
CONFIG_ARM64_64K_PAGES=yenabled the build failed due to below errors / warnings
make --silent --keep-going --jobs=8 O=/home/tuxbuild/.cache/tuxmake/builds/1/tmp ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 'CC=sccache aarch64-linux-gnu-gcc' 'HOSTCC=sccache gcc'
aarch64-linux-gnu-ld: Unexpected GOT/PLT entries detected!
aarch64-linux-gnu-ld: Unexpected run-time procedure linkages detected!
aarch64-linux-gnu-ld:arch/arm64/mm/hugetlbpage.o: in function `huge_pte_alloc':
hugetlbpage.c:(.text+0x7d8): undefined reference to `want_pmd_share'
Quoted 1 line
Reported-by Naresh Kamboju <[email protected]>
Sorry for the issue & thanks for the report. Would you please check whether the patch attached could fix the issue?
Signature · 1 line
Peter Xu
From 4072042b415d1f78ac1ab017671e345b414ca6ab Mon Sep 17 00:00:00 2001
From: Peter Xu <[email protected]>
Date: Wed, 10 Mar 2021 11:48:56 -0500
Subject: [PATCH] mm/hugetlb: Fix build with !ARCH_WANT_HUGE_PMD_SHARE
want_pmd_share() is undefined with !ARCH_WANT_HUGE_PMD_SHARE since it's put by accident into a "#ifdef ARCH_WANT_HUGE_PMD_SHARE" block. Moving it out won't work either since vma_shareable() is only defined within the block. Define it for !ARCH_WANT_HUGE_PMD_SHARE instead.
--- mm/hugetlb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
mm/hugetlb.c +5 -3
@@ -5469,9 +5469,6 @@ static bool vma_shareable(struct vm_area_struct *vma, unsigned long addr)bool want_pmd_share (struct vm_area_struct *vma,unsigned long addr) { -#ifndef CONFIG_ARCH_WANT_HUGE_PMD_SHARE -return false; -#endif #ifdef CONFIG_USERFAULTFD if (uffd_disable_huge_pmd_share (vma))return false;
@@ -5616,6 +5613,11 @@ void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma, unsigned long *start, unsigned long *end) { } + +bool want_pmd_share (struct vm_area_struct *vma,unsigned long addr) +{ +return false; +}#endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */ #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
Signature · 1 line
2.26.2
Re: [PATCH v4 2/4] hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled
Quoted 69 lines · Peter Xu
Quoted 1 line · Naresh Kamboju
Hi Peter,
Hi, Naresh,
Quoted 63 lines · Peter Xu
Quoted 39 lines
Huge pmd sharing could bring problem to userfaultfd. The thing is that userfaultfd is running its logic based on the special bits on page table entries, however the huge pmd sharing could potentially share page table entries for different address ranges. That could cause issues on either:
- When sharing huge pmd page tables for an uffd write protected range, the newly mapped huge pmd range will also be write protected unexpectedly, or,
- When we try to write protect a range of huge pmd shared range, we'll first do
huge_pmd_unshare()inhugetlb_change_protection(), however that also means theUFFDIO_WRITEPROTECTcould be silently skipped for the shared region, which could lead to data loss.Since at it, a few other things are done altogether:
- Move
want_pmd_share()frommm/hugetlb.cintolinux/hugetlb.h, because that's definitely something that arch code would like to use too- ARM64 currently directly check against
CONFIG_ARCH_WANT_HUGE_PMD_SHAREwhen trying to share huge pmd. Switch to thewant_pmd_share()helper.Since at it, move
vma_shareable()fromhuge_pmd_share()intowant_pmd_share().Reviewed-by Mike Kravetz <[email protected]>Reviewed-by Axel Rasmussen <[email protected]>Signed-off-by Peter Xu <[email protected]>--- arch/arm64/mm/hugetlbpage.c | 3 +-- include/linux/hugetlb.h | 2 ++ include/linux/userfaultfd_k.h | 9 +++++++++ mm/hugetlb.c | 20 ++++++++++++++------ 4 files changed, 26 insertions(+), 8 deletions(-)arch/arm64/mm/hugetlbpage.c +0
-0arch/arm64/mm/hugetlbpage.c pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, · line 287
Quoted 6 lines
arch/arm64/mm/hugetlbpage.c +1
-2@@ -284,8 +284,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,*/ ptep =pte_alloc_map (mm, pmdp, addr); }else if (sz ==PMD_SIZE ) { -if (IS_ENABLED (CONFIG_ARCH_WANT_HUGE_PMD_SHARE ) && -pud_none (READ_ONCE (*pudp))) +if (want_pmd_share (vma, addr) &&pud_none (READ_ONCE (*pudp)))While building Linux next 20210310 tag for arm64 architecture with
-
CONFIG_ARM64_64K_PAGES=yenabled the build failed due to below errors / warnings
make --silent --keep-going --jobs=8 O=/home/tuxbuild/.cache/tuxmake/builds/1/tmp ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- 'CC=sccache aarch64-linux-gnu-gcc' 'HOSTCC=sccache gcc'
aarch64-linux-gnu-ld: Unexpected GOT/PLT entries detected!
aarch64-linux-gnu-ld: Unexpected run-time procedure linkages detected!
aarch64-linux-gnu-ld:arch/arm64/mm/hugetlbpage.o: in function `huge_pte_alloc':
hugetlbpage.c:(.text+0x7d8): undefined reference to `want_pmd_share'
Quoted 4 lines
Quoted 1 line
Reported-by Naresh Kamboju <[email protected]>Sorry for the issue & thanks for the report. Would you please check whether the patch attached could fix the issue?
The attached patch build tested and build pass for arm64 including 64k pages config.
CONFIG_ARM64_64K_PAGES=y
- Naresh
Peter Xu
reviewerPATCH v4 3/4 mm/hugetlb: Move flush_hugetlb_tlb_range() into hugetlb.h
In reply to Peter XuPrepare for it to be called outside of mm/hugetlb.c.
--- include/linux/hugetlb.h | 8 ++++++++ mm/hugetlb.c | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-)
include/linux/hugetlb.h +8 -0
@@ -952,4 +952,12 @@ static inline __init void hugetlb_cma_check(void)bool want_pmd_share (struct vm_area_struct *vma,unsigned long addr); +#ifndef __HAVE_ARCH_FLUSH_HUGETLB_TLB_RANGE +/* +* ARCHes with special requirements for evicting HUGETLB backing TLB entries can +* implement this. +*/ +#define flush_hugetlb_tlb_range (vma, addr, end) flush_tlb_range(vma, addr, end) +#endif +#endif /* _LINUX_HUGETLB_H */
mm/hugetlb.c +0 -8
@@ -4965,14 +4965,6 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,return i ? i : err; } -#ifndef __HAVE_ARCH_FLUSH_HUGETLB_TLB_RANGE -/* -* ARCHes with special requirements for evicting HUGETLB backing TLB entries can -* implement this. -*/ -#define flush_hugetlb_tlb_range (vma, addr, end) flush_tlb_range(vma, addr, end) -#endif -unsigned long hugetlb_change_protection (struct vm_area_struct *vma,unsigned long address,unsigned long end,pgprot_t newprot) {
Signature · 1 line
2.26.2
Peter Xu
reviewerPATCH v4 4/4 hugetlb/userfaultfd: Unshare all pmds for hugetlbfs when register wp
In reply to Peter XuHuge pmd sharing for hugetlbfs is racy with userfaultfd-wp because userfaultfd-wp is always based on pgtable entries, so they cannot be shared.
Walk the hugetlb range and unshare all such mappings if there is, right before UFFDIO_REGISTER will succeed and return to userspace.
This will pair with want_pmd_share() in hugetlb code so that huge pmd sharing is completely disabled for userfaultfd-wp registered range.
--- fs/userfaultfd.c | 4 ++++ include/linux/hugetlb.h | 3 +++ mm/hugetlb.c | 51 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+)
fs/userfaultfd.c +4 -0
@@ -15,6 +15,7 @@#include <linux/sched/signal.h> #include <linux/sched/mm.h> #include <linux/mm.h> +#include <linux/mmu_notifier.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/seq_file.h>
@@ -1448,6 +1449,9 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, vma->vm_flags = new_flags; vma->vm_userfaultfd_ctx.ctx = ctx; +if (is_vm_hugetlb_page (vma) &&uffd_disable_huge_pmd_share (vma)) +hugetlb_unshare_all_pmds (vma); + skip: prev = vma; start = vma->vm_end;
include/linux/hugetlb.h +3 -0
@@ -188,6 +188,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma, unsigned long address, unsigned long end, pgprot_t newprot);bool is_hugetlb_entry_migration (pte_t pte); +void hugetlb_unshare_all_pmds (struct vm_area_struct *vma);#else /* !CONFIG_HUGETLB_PAGE */
@@ -369,6 +370,8 @@ static inline vm_fault_t hugetlb_fault(struct mm_struct *mm, return 0; } +static inline void hugetlb_unshare_all_pmds (struct vm_area_struct *vma) { } +#endif /* !CONFIG_HUGETLB_PAGE */ /* * hugepages at page global directory. If arch support
mm/hugetlb.c +51 -0
57 lines not on this page · raw message
Signature · 1 line
2.26.2
Peter Xu
reviewerQuoted 21 lines · Peter Xu
v4:
- fix build for sparc by removing extra line in patch 1 [Mike]
- pick Mike's r-b for patch 4v3:
- patch 4:
- fix build failure for !CMA and/or !HUGETLBFS [Axel]
- Fix mmu notifier range to use start/end [Mike]
- add more r-bsv2:
- patch 4: movehugetlb_unshare_all_pmds()intomm/hugetlb.c, so it can be used even outsideuserfaultfd.cThis series tries to disable huge pmd unshare of hugetlbfs backed memory for uffd-wp. Although uffd-wp of hugetlbfs is still during rfc stage, the idea of this series may be needed for multiple tasks (Axel's uffd minor fault series, and Mike's soft dirty series), so I picked it out from the larger series.
References works:
Quoted 8 lines
Uffd shmem+hugetlbfs rfc:
https://lore.kernel.org/lkml/[email protected]/Uffd minor mode for hugetlbfs:
https://lore.kernel.org/lkml/[email protected]/Soft dirty for hugetlbfs:
https://lore.kernel.org/lkml/[email protected]/
Andrew/Mike,
Do you have any further comment on this series?
Thanks,
Signature · 1 line
Peter Xu