2022-06-07 06:00:53

by Yang Shi

[permalink] [raw]
Subject: [mm-unstable v3 PATCH 0/7] Cleanup transhuge_xxx helpers


v3: * Fixed the comment from Willy
v2: * Rebased to the latest mm-unstable
* Fixed potential regression for smaps's THPeligible

This series is the follow-up of the discussion about cleaning up transhuge_xxx
helpers at https://lore.kernel.org/linux-mm/[email protected]/.

THP has a bunch of helpers that do VMA sanity check for different paths, they
do the similar checks for the most callsites and have a lot duplicate codes.
And it is confusing what helpers should be used at what conditions.

This series reorganized and cleaned up the code so that we could consolidate
all the checks into hugepage_vma_check().

The transhuge_vma_enabled(), transparent_hugepage_active() and
__transparent_hugepage_enabled() are killed by this series.

Added transhuge_vma_size_ok() helper to remove some duplicate code.


Yang Shi (7):
mm: khugepaged: check THP flag in hugepage_vma_check()
mm: thp: introduce transhuge_vma_size_ok() helper
mm: khugepaged: remove the redundant anon vma check
mm: khugepaged: use transhuge_vma_suitable replace open-code
mm: thp: kill transparent_hugepage_active()
mm: thp: kill __transhuge_page_enabled()
mm: khugepaged: reorg some khugepaged helpers

fs/proc/task_mmu.c | 2 +-
include/linux/huge_mm.h | 84 ++++++++++++++++++++++++++++------------------------------------------
include/linux/khugepaged.h | 21 ++----------------
mm/huge_memory.c | 64 +++++++++++++++++++++++++++++++++++++++++++++--------
mm/khugepaged.c | 78 +++++++++++++++--------------------------------------------------
mm/memory.c | 7 ++++--
6 files changed, 114 insertions(+), 142 deletions(-)



2022-06-07 08:30:48

by Yang Shi

[permalink] [raw]
Subject: [v3 PATCH 1/7] mm: khugepaged: check THP flag in hugepage_vma_check()

Currently the THP flag check in hugepage_vma_check() will fallthrough if
the flag is NEVER and VM_HUGEPAGE is set. This is not a problem for now
since all the callers have the flag checked before or can't be invoked if
the flag is NEVER.

However, the following patch will call hugepage_vma_check() in more
places, for example, page fault, so this flag must be checked in
hugepge_vma_check().

Signed-off-by: Yang Shi <[email protected]>
---
mm/khugepaged.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 671ac7800e53..84b9cf4b9be9 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -458,6 +458,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
if (shmem_file(vma->vm_file))
return shmem_huge_enabled(vma);

+ if (!khugepaged_enabled())
+ return false;
+
/* THP settings require madvise. */
if (!(vm_flags & VM_HUGEPAGE) && !khugepaged_always())
return false;
--
2.26.3

2022-06-07 09:20:04

by Yang Shi

[permalink] [raw]
Subject: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

The page fault path checks THP eligibility with
__transhuge_page_enabled() which does the similar thing as
hugepage_vma_check(), so use hugepage_vma_check() instead.

However page fault allows DAX and !anon_vma cases, so added a new flag,
in_pf, to hugepage_vma_check() to make page fault work correctly.

The in_pf flag is also used to skip shmem and file THP for page fault
since shmem handles THP in its own shmem_fault() and file THP allocation
on fault is not supported yet.

Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
only caller now, it is not necessary to have a helper function.

Signed-off-by: Yang Shi <[email protected]>
---
fs/proc/task_mmu.c | 2 +-
include/linux/huge_mm.h | 57 ++------------------------------------
include/linux/khugepaged.h | 2 +-
mm/huge_memory.c | 25 ++++++++++++-----
mm/khugepaged.c | 8 +++---
mm/memory.c | 7 +++--
6 files changed, 31 insertions(+), 70 deletions(-)

diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index fd79566e204c..a0850303baec 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
__show_smap(m, &mss, false);

seq_printf(m, "THPeligible: %d\n",
- hugepage_vma_check(vma, vma->vm_flags, true));
+ hugepage_vma_check(vma, vma->vm_flags, true, false));

if (arch_pkeys_enabled())
seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index f561c3e16def..d478e8875023 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
return true;
}

-static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
- unsigned long vm_flags)
-{
- /* Explicitly disabled through madvise. */
- if ((vm_flags & VM_NOHUGEPAGE) ||
- test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
- return false;
- return true;
-}
-
-/*
- * to be used on vmas which are known to support THP.
- * Use transparent_hugepage_active otherwise
- */
-static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
-{
-
- /*
- * If the hardware/firmware marked hugepage support disabled.
- */
- if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
- return false;
-
- if (!transhuge_vma_enabled(vma, vma->vm_flags))
- return false;
-
- if (vma_is_temporary_stack(vma))
- return false;
-
- if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
- return true;
-
- if (vma_is_dax(vma))
- return true;
-
- if (transparent_hugepage_flags &
- (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
- return !!(vma->vm_flags & VM_HUGEPAGE);
-
- return false;
-}
-
static inline bool file_thp_enabled(struct vm_area_struct *vma)
{
struct inode *inode;
@@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)

bool hugepage_vma_check(struct vm_area_struct *vma,
unsigned long vm_flags,
- bool smaps);
+ bool smaps, bool in_pf);

#define transparent_hugepage_use_zero_page() \
(transparent_hugepage_flags & \
@@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
return false;
}

-static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
-{
- return false;
-}
-
static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
{
return false;
@@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
return false;
}

-static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
- unsigned long vm_flags)
-{
- return false;
-}
-
static inline bool hugepage_vma_check(struct vm_area_struct *vma,
unsigned long vm_flags,
- bool smaps)
+ bool smaps, bool in_pf)
{
return false;
}
diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
index 8a6452e089ca..e047be601268 100644
--- a/include/linux/khugepaged.h
+++ b/include/linux/khugepaged.h
@@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
{
if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
khugepaged_enabled()) {
- if (hugepage_vma_check(vma, vm_flags, false))
+ if (hugepage_vma_check(vma, vm_flags, false, false))
__khugepaged_enter(vma->vm_mm);
}
}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index bc8370856e85..b95786ada466 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;

bool hugepage_vma_check(struct vm_area_struct *vma,
unsigned long vm_flags,
- bool smaps)
+ bool smaps, bool in_pf)
{
- if (!transhuge_vma_enabled(vma, vm_flags))
+ /* Explicitly disabled through madvise or prctl. */
+ if ((vm_flags & VM_NOHUGEPAGE) ||
+ test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
+ return false;
+ /*
+ * If the hardware/firmware marked hugepage support disabled.
+ */
+ if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
return false;

+ /* Special VMA and hugetlb VMA */
if (vm_flags & VM_NO_KHUGEPAGED)
return false;

- /* Don't run khugepaged against DAX vma */
+ /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
if (vma_is_dax(vma))
- return false;
+ return in_pf;

if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
vma->vm_pgoff, HPAGE_PMD_NR))
@@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
return false;

/* Enabled via shmem mount options or sysfs settings. */
- if (shmem_file(vma->vm_file))
+ if (!in_pf && shmem_file(vma->vm_file))
return shmem_huge_enabled(vma);

if (!khugepaged_enabled())
@@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
return false;

/* Only regular file is valid */
- if (file_thp_enabled(vma))
+ if (!in_pf && file_thp_enabled(vma))
return true;

if (!vma_is_anonymous(vma))
@@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
/*
* THPeligible bit of smaps should show 1 for proper VMAs even
* though anon_vma is not initialized yet.
+ *
+ * Allow page fault since anon_vma may be not initialized until
+ * the first page fault.
*/
if (!vma->anon_vma)
- return smaps;
+ return (smaps || in_pf);

return true;
}
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index aa0769e3b0d9..ab6183c5489f 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
{
if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
khugepaged_enabled()) {
- if (hugepage_vma_check(vma, vm_flags, false))
+ if (hugepage_vma_check(vma, vm_flags, false, false))
__khugepaged_enter(vma->vm_mm);
}
}
@@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,

if (!transhuge_vma_suitable(vma, address))
return SCAN_ADDRESS_RANGE;
- if (!hugepage_vma_check(vma, vma->vm_flags, false))
+ if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
return SCAN_VMA_CHECK;
return 0;
}
@@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
* the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
* will not fail the vma for missing VM_HUGEPAGE
*/
- if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
+ if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
return;

/* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
@@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
progress++;
break;
}
- if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
+ if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
skip:
progress++;
continue;
diff --git a/mm/memory.c b/mm/memory.c
index bc5d40eec5d5..673f7561a30a 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
.gfp_mask = __get_fault_gfp_mask(vma),
};
struct mm_struct *mm = vma->vm_mm;
+ unsigned long vm_flags = vma->vm_flags;
pgd_t *pgd;
p4d_t *p4d;
vm_fault_t ret;
@@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
if (!vmf.pud)
return VM_FAULT_OOM;
retry_pud:
- if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
+ if (pud_none(*vmf.pud) &&
+ hugepage_vma_check(vma, vm_flags, false, true)) {
ret = create_huge_pud(&vmf);
if (!(ret & VM_FAULT_FALLBACK))
return ret;
@@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
if (pud_trans_unstable(vmf.pud))
goto retry_pud;

- if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
+ if (pmd_none(*vmf.pmd) &&
+ hugepage_vma_check(vma, vm_flags, false, true)) {
ret = create_huge_pmd(&vmf);
if (!(ret & VM_FAULT_FALLBACK))
return ret;
--
2.26.3

2022-06-07 18:34:37

by Yang Shi

[permalink] [raw]
Subject: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

There are couple of places that check whether the vma size is ok for
THP or not, they are open coded and duplicate, introduce
transhuge_vma_size_ok() helper to do the job.

Signed-off-by: Yang Shi <[email protected]>
---
include/linux/huge_mm.h | 17 +++++++++++++++++
mm/huge_memory.c | 5 +----
mm/khugepaged.c | 12 ++++++------
3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 648cb3ce7099..a8f61db47f2a 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;

extern unsigned long transparent_hugepage_flags;

+/*
+ * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
+ */
+static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
+{
+ if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
+ (vma->vm_end & HPAGE_PMD_MASK))
+ return true;
+
+ return false;
+}
+
static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
unsigned long addr)
{
@@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
return false;
}

+static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
+{
+ return false;
+}
+
static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
unsigned long addr)
{
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 48182c8fe151..36ada544e494 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;

bool transparent_hugepage_active(struct vm_area_struct *vma)
{
- /* The addr is used to check if the vma size fits */
- unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
-
- if (!transhuge_vma_suitable(vma, addr))
+ if (!transhuge_vma_size_ok(vma))
return false;
if (vma_is_anonymous(vma))
return __transparent_hugepage_enabled(vma);
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 84b9cf4b9be9..d0f8020164fc 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
vma->vm_pgoff, HPAGE_PMD_NR))
return false;

+ if (!transhuge_vma_size_ok(vma))
+ return false;
+
/* Enabled via shmem mount options or sysfs settings. */
if (shmem_file(vma->vm_file))
return shmem_huge_enabled(vma);
@@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
unsigned long vm_flags)
{
if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
- khugepaged_enabled() &&
- (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
- (vma->vm_end & HPAGE_PMD_MASK))) {
+ khugepaged_enabled()) {
if (hugepage_vma_check(vma, vm_flags))
__khugepaged_enter(vma->vm_mm);
}
@@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
progress++;
continue;
}
- hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
+
+ hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
hend = vma->vm_end & HPAGE_PMD_MASK;
- if (hstart >= hend)
- goto skip;
if (khugepaged_scan.address > hend)
goto skip;
if (khugepaged_scan.address < hstart)
--
2.26.3

2022-06-09 18:54:00

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [v3 PATCH 1/7] mm: khugepaged: check THP flag in hugepage_vma_check()

Reviewed-by: Zach O'Keefe <[email protected]>

On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
>
> Currently the THP flag check in hugepage_vma_check() will fallthrough if
> the flag is NEVER and VM_HUGEPAGE is set. This is not a problem for now
> since all the callers have the flag checked before or can't be invoked if
> the flag is NEVER.
>
> However, the following patch will call hugepage_vma_check() in more
> places, for example, page fault, so this flag must be checked in
> hugepge_vma_check().
>
> Signed-off-by: Yang Shi <[email protected]>
> ---
> mm/khugepaged.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 671ac7800e53..84b9cf4b9be9 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -458,6 +458,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> if (shmem_file(vma->vm_file))
> return shmem_huge_enabled(vma);
>
> + if (!khugepaged_enabled())
> + return false;
> +
> /* THP settings require madvise. */
> if (!(vm_flags & VM_HUGEPAGE) && !khugepaged_always())
> return false;
> --
> 2.26.3
>
>

2022-06-09 22:51:39

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
>
> There are couple of places that check whether the vma size is ok for
> THP or not, they are open coded and duplicate, introduce
> transhuge_vma_size_ok() helper to do the job.
>
> Signed-off-by: Yang Shi <[email protected]>
> ---
> include/linux/huge_mm.h | 17 +++++++++++++++++
> mm/huge_memory.c | 5 +----
> mm/khugepaged.c | 12 ++++++------
> 3 files changed, 24 insertions(+), 10 deletions(-)
>
> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> index 648cb3ce7099..a8f61db47f2a 100644
> --- a/include/linux/huge_mm.h
> +++ b/include/linux/huge_mm.h
> @@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;
>
> extern unsigned long transparent_hugepage_flags;
>
> +/*
> + * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
> + */
> +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> +{
> + if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
> + (vma->vm_end & HPAGE_PMD_MASK))
> + return true;
> +
> + return false;
> +}

First time coming across round_up() - thanks for that - but for
symmetry, maybe also use round_down() for the end? No strong opinion -
just a suggestion given I've just discovered it.

> static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> unsigned long addr)
> {
> @@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
> return false;
> }
>
> +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> +{
> + return false;
> +}
> +
> static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> unsigned long addr)
> {
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 48182c8fe151..36ada544e494 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
>
> bool transparent_hugepage_active(struct vm_area_struct *vma)
> {
> - /* The addr is used to check if the vma size fits */
> - unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
> -
> - if (!transhuge_vma_suitable(vma, addr))
> + if (!transhuge_vma_size_ok(vma))
> return false;
> if (vma_is_anonymous(vma))
> return __transparent_hugepage_enabled(vma);

Do we need a check for vma->vm_pgoff alignment here, after
!vma_is_anonymous(), and now that we don't call
transhuge_vma_suitable()?

> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 84b9cf4b9be9..d0f8020164fc 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> vma->vm_pgoff, HPAGE_PMD_NR))
> return false;
>
> + if (!transhuge_vma_size_ok(vma))
> + return false;
> +
> /* Enabled via shmem mount options or sysfs settings. */
> if (shmem_file(vma->vm_file))
> return shmem_huge_enabled(vma);
> @@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> unsigned long vm_flags)
> {
> if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> - khugepaged_enabled() &&
> - (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
> - (vma->vm_end & HPAGE_PMD_MASK))) {
> + khugepaged_enabled()) {
> if (hugepage_vma_check(vma, vm_flags))
> __khugepaged_enter(vma->vm_mm);
> }
> @@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> progress++;
> continue;
> }
> - hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
> +
> + hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
> hend = vma->vm_end & HPAGE_PMD_MASK;
> - if (hstart >= hend)
> - goto skip;
> if (khugepaged_scan.address > hend)
> goto skip;
> if (khugepaged_scan.address < hstart)

Likewise, could do round_down() here (just a suggestion)

> --
> 2.26.3
>
>

2022-06-10 00:17:52

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [mm-unstable v3 PATCH 0/7] Cleanup transhuge_xxx helpers

On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
>
>
> v3: * Fixed the comment from Willy
> v2: * Rebased to the latest mm-unstable
> * Fixed potential regression for smaps's THPeligible
>
> This series is the follow-up of the discussion about cleaning up transhuge_xxx
> helpers at https://lore.kernel.org/linux-mm/[email protected]/.
>
> THP has a bunch of helpers that do VMA sanity check for different paths, they
> do the similar checks for the most callsites and have a lot duplicate codes.
> And it is confusing what helpers should be used at what conditions.
>
> This series reorganized and cleaned up the code so that we could consolidate
> all the checks into hugepage_vma_check().

By the way, thanks for doing this work. I know I personally was quite
confused about which vma checking function does what / which I should
be using. I briefly tried sketching out how to do something like this
as well - but the various corner cases where e.g. hugepage_vma_check()
and transparent_hugepage_active() differed got confusing. Thanks for
figuring this all out.

> The transhuge_vma_enabled(), transparent_hugepage_active() and
> __transparent_hugepage_enabled() are killed by this series.
>
> Added transhuge_vma_size_ok() helper to remove some duplicate code.
>
>
> Yang Shi (7):
> mm: khugepaged: check THP flag in hugepage_vma_check()
> mm: thp: introduce transhuge_vma_size_ok() helper
> mm: khugepaged: remove the redundant anon vma check
> mm: khugepaged: use transhuge_vma_suitable replace open-code
> mm: thp: kill transparent_hugepage_active()
> mm: thp: kill __transhuge_page_enabled()
> mm: khugepaged: reorg some khugepaged helpers
>
> fs/proc/task_mmu.c | 2 +-
> include/linux/huge_mm.h | 84 ++++++++++++++++++++++++++++------------------------------------------
> include/linux/khugepaged.h | 21 ++----------------
> mm/huge_memory.c | 64 +++++++++++++++++++++++++++++++++++++++++++++--------
> mm/khugepaged.c | 78 +++++++++++++++--------------------------------------------------
> mm/memory.c | 7 ++++--
> 6 files changed, 114 insertions(+), 142 deletions(-)
>
>
>

2022-06-10 00:47:12

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

On Thu, Jun 9, 2022 at 3:21 PM Zach O'Keefe <[email protected]> wrote:
>
> On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> >
> > There are couple of places that check whether the vma size is ok for
> > THP or not, they are open coded and duplicate, introduce
> > transhuge_vma_size_ok() helper to do the job.
> >
> > Signed-off-by: Yang Shi <[email protected]>
> > ---
> > include/linux/huge_mm.h | 17 +++++++++++++++++
> > mm/huge_memory.c | 5 +----
> > mm/khugepaged.c | 12 ++++++------
> > 3 files changed, 24 insertions(+), 10 deletions(-)
> >
> > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > index 648cb3ce7099..a8f61db47f2a 100644
> > --- a/include/linux/huge_mm.h
> > +++ b/include/linux/huge_mm.h
> > @@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;
> >
> > extern unsigned long transparent_hugepage_flags;
> >
> > +/*
> > + * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
> > + */
> > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > +{
> > + if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
> > + (vma->vm_end & HPAGE_PMD_MASK))
> > + return true;
> > +
> > + return false;
> > +}
>
> First time coming across round_up() - thanks for that - but for
> symmetry, maybe also use round_down() for the end? No strong opinion -
> just a suggestion given I've just discovered it.

Yeah, round_down is fine too.

>
> > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > unsigned long addr)
> > {
> > @@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
> > return false;
> > }
> >
> > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > +{
> > + return false;
> > +}
> > +
> > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > unsigned long addr)
> > {
> > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > index 48182c8fe151..36ada544e494 100644
> > --- a/mm/huge_memory.c
> > +++ b/mm/huge_memory.c
> > @@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> >
> > bool transparent_hugepage_active(struct vm_area_struct *vma)
> > {
> > - /* The addr is used to check if the vma size fits */
> > - unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
> > -
> > - if (!transhuge_vma_suitable(vma, addr))
> > + if (!transhuge_vma_size_ok(vma))
> > return false;
> > if (vma_is_anonymous(vma))
> > return __transparent_hugepage_enabled(vma);
>
> Do we need a check for vma->vm_pgoff alignment here, after
> !vma_is_anonymous(), and now that we don't call
> transhuge_vma_suitable()?

Actually I was thinking about this too. But the THPeligible bit shown
by smaps is a little bit ambiguous for file vma. The document says:
"THPeligible" indicates whether the mapping is eligible for allocating
THP pages - 1 if true, 0 otherwise.

Even though it doesn't fulfill the alignment, it is still possible to
get THP allocated, but just can't be PMD mapped. So the old behavior
of THPeligible for file vma seems problematic, or at least doesn't
match the document.

I should elaborate this in the commit log.

>
> > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > index 84b9cf4b9be9..d0f8020164fc 100644
> > --- a/mm/khugepaged.c
> > +++ b/mm/khugepaged.c
> > @@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > vma->vm_pgoff, HPAGE_PMD_NR))
> > return false;
> >
> > + if (!transhuge_vma_size_ok(vma))
> > + return false;
> > +
> > /* Enabled via shmem mount options or sysfs settings. */
> > if (shmem_file(vma->vm_file))
> > return shmem_huge_enabled(vma);
> > @@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > unsigned long vm_flags)
> > {
> > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > - khugepaged_enabled() &&
> > - (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
> > - (vma->vm_end & HPAGE_PMD_MASK))) {
> > + khugepaged_enabled()) {
> > if (hugepage_vma_check(vma, vm_flags))
> > __khugepaged_enter(vma->vm_mm);
> > }
> > @@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > progress++;
> > continue;
> > }
> > - hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
> > +
> > + hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
> > hend = vma->vm_end & HPAGE_PMD_MASK;
> > - if (hstart >= hend)
> > - goto skip;
> > if (khugepaged_scan.address > hend)
> > goto skip;
> > if (khugepaged_scan.address < hstart)
>
> Likewise, could do round_down() here (just a suggestion)

Fine to me.

>
> > --
> > 2.26.3
> >
> >

2022-06-10 02:29:10

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

On Thu, Jun 9, 2022 at 5:08 PM Yang Shi <[email protected]> wrote:
>
> On Thu, Jun 9, 2022 at 3:21 PM Zach O'Keefe <[email protected]> wrote:
> >
> > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > >
> > > There are couple of places that check whether the vma size is ok for
> > > THP or not, they are open coded and duplicate, introduce
> > > transhuge_vma_size_ok() helper to do the job.
> > >
> > > Signed-off-by: Yang Shi <[email protected]>
> > > ---
> > > include/linux/huge_mm.h | 17 +++++++++++++++++
> > > mm/huge_memory.c | 5 +----
> > > mm/khugepaged.c | 12 ++++++------
> > > 3 files changed, 24 insertions(+), 10 deletions(-)
> > >
> > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > index 648cb3ce7099..a8f61db47f2a 100644
> > > --- a/include/linux/huge_mm.h
> > > +++ b/include/linux/huge_mm.h
> > > @@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;
> > >
> > > extern unsigned long transparent_hugepage_flags;
> > >
> > > +/*
> > > + * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
> > > + */
> > > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > +{
> > > + if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
> > > + (vma->vm_end & HPAGE_PMD_MASK))
> > > + return true;
> > > +
> > > + return false;
> > > +}
> >
> > First time coming across round_up() - thanks for that - but for
> > symmetry, maybe also use round_down() for the end? No strong opinion -
> > just a suggestion given I've just discovered it.
>
> Yeah, round_down is fine too.
>
> >
> > > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > unsigned long addr)
> > > {
> > > @@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
> > > return false;
> > > }
> > >
> > > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > +{
> > > + return false;
> > > +}
> > > +
> > > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > unsigned long addr)
> > > {
> > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > index 48182c8fe151..36ada544e494 100644
> > > --- a/mm/huge_memory.c
> > > +++ b/mm/huge_memory.c
> > > @@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > >
> > > bool transparent_hugepage_active(struct vm_area_struct *vma)
> > > {
> > > - /* The addr is used to check if the vma size fits */
> > > - unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
> > > -
> > > - if (!transhuge_vma_suitable(vma, addr))
> > > + if (!transhuge_vma_size_ok(vma))
> > > return false;
> > > if (vma_is_anonymous(vma))
> > > return __transparent_hugepage_enabled(vma);
> >
> > Do we need a check for vma->vm_pgoff alignment here, after
> > !vma_is_anonymous(), and now that we don't call
> > transhuge_vma_suitable()?
>
> Actually I was thinking about this too. But the THPeligible bit shown
> by smaps is a little bit ambiguous for file vma. The document says:
> "THPeligible" indicates whether the mapping is eligible for allocating
> THP pages - 1 if true, 0 otherwise.
>
> Even though it doesn't fulfill the alignment, it is still possible to
> get THP allocated, but just can't be PMD mapped. So the old behavior
> of THPeligible for file vma seems problematic, or at least doesn't
> match the document.

I think the term "THP" is used ambiguously. Often, but not always, in
the code, folks will go out of their way to specify "hugepage-sized"
page vs "pmd-mapped hugepage" - but at least from my experience,
external documentation doesn't. Given that THP as a concept doesn't
make much sense without the possibility of pmd-mapping, I think
"THPeligible here means "pmd mappable". For example, AnonHugePages in
smaps means pmd-mapped anon hugepages.

That all said - the following patches will delete
transparent_hugepage_active() anyways.

> I should elaborate this in the commit log.
>
> >
> > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > index 84b9cf4b9be9..d0f8020164fc 100644
> > > --- a/mm/khugepaged.c
> > > +++ b/mm/khugepaged.c
> > > @@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > return false;
> > >
> > > + if (!transhuge_vma_size_ok(vma))
> > > + return false;
> > > +
> > > /* Enabled via shmem mount options or sysfs settings. */
> > > if (shmem_file(vma->vm_file))
> > > return shmem_huge_enabled(vma);
> > > @@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > unsigned long vm_flags)
> > > {
> > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > - khugepaged_enabled() &&
> > > - (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
> > > - (vma->vm_end & HPAGE_PMD_MASK))) {
> > > + khugepaged_enabled()) {
> > > if (hugepage_vma_check(vma, vm_flags))
> > > __khugepaged_enter(vma->vm_mm);
> > > }
> > > @@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > progress++;
> > > continue;
> > > }
> > > - hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
> > > +
> > > + hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
> > > hend = vma->vm_end & HPAGE_PMD_MASK;
> > > - if (hstart >= hend)
> > > - goto skip;
> > > if (khugepaged_scan.address > hend)
> > > goto skip;
> > > if (khugepaged_scan.address < hstart)
> >
> > Likewise, could do round_down() here (just a suggestion)
>
> Fine to me.
>
> >
> > > --
> > > 2.26.3
> > >
> > >

2022-06-10 02:56:14

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
>
> The page fault path checks THP eligibility with
> __transhuge_page_enabled() which does the similar thing as
> hugepage_vma_check(), so use hugepage_vma_check() instead.
>
> However page fault allows DAX and !anon_vma cases, so added a new flag,
> in_pf, to hugepage_vma_check() to make page fault work correctly.
>
> The in_pf flag is also used to skip shmem and file THP for page fault
> since shmem handles THP in its own shmem_fault() and file THP allocation
> on fault is not supported yet.
>
> Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
> only caller now, it is not necessary to have a helper function.
>
> Signed-off-by: Yang Shi <[email protected]>
> ---
> fs/proc/task_mmu.c | 2 +-
> include/linux/huge_mm.h | 57 ++------------------------------------
> include/linux/khugepaged.h | 2 +-
> mm/huge_memory.c | 25 ++++++++++++-----
> mm/khugepaged.c | 8 +++---
> mm/memory.c | 7 +++--
> 6 files changed, 31 insertions(+), 70 deletions(-)
>
> diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> index fd79566e204c..a0850303baec 100644
> --- a/fs/proc/task_mmu.c
> +++ b/fs/proc/task_mmu.c
> @@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
> __show_smap(m, &mss, false);
>
> seq_printf(m, "THPeligible: %d\n",
> - hugepage_vma_check(vma, vma->vm_flags, true));
> + hugepage_vma_check(vma, vma->vm_flags, true, false));
>
> if (arch_pkeys_enabled())
> seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> index f561c3e16def..d478e8875023 100644
> --- a/include/linux/huge_mm.h
> +++ b/include/linux/huge_mm.h
> @@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> return true;
> }
>
> -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> - unsigned long vm_flags)
> -{
> - /* Explicitly disabled through madvise. */
> - if ((vm_flags & VM_NOHUGEPAGE) ||
> - test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> - return false;
> - return true;
> -}
> -
> -/*
> - * to be used on vmas which are known to support THP.
> - * Use transparent_hugepage_active otherwise
> - */
> -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> -{
> -
> - /*
> - * If the hardware/firmware marked hugepage support disabled.
> - */
> - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> - return false;
> -
> - if (!transhuge_vma_enabled(vma, vma->vm_flags))
> - return false;
> -
> - if (vma_is_temporary_stack(vma))
> - return false;
> -
> - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
> - return true;
> -
> - if (vma_is_dax(vma))
> - return true;
> -
> - if (transparent_hugepage_flags &
> - (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
> - return !!(vma->vm_flags & VM_HUGEPAGE);
> -
> - return false;
> -}
> -
> static inline bool file_thp_enabled(struct vm_area_struct *vma)
> {
> struct inode *inode;
> @@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
>
> bool hugepage_vma_check(struct vm_area_struct *vma,
> unsigned long vm_flags,
> - bool smaps);
> + bool smaps, bool in_pf);
>
> #define transparent_hugepage_use_zero_page() \
> (transparent_hugepage_flags & \
> @@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
> return false;
> }
>
> -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> -{
> - return false;
> -}
> -
> static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> {
> return false;
> @@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> return false;
> }
>
> -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> - unsigned long vm_flags)
> -{
> - return false;
> -}
> -
> static inline bool hugepage_vma_check(struct vm_area_struct *vma,
> unsigned long vm_flags,
> - bool smaps)
> + bool smaps, bool in_pf)
> {
> return false;
> }
> diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
> index 8a6452e089ca..e047be601268 100644
> --- a/include/linux/khugepaged.h
> +++ b/include/linux/khugepaged.h
> @@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
> {
> if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> khugepaged_enabled()) {
> - if (hugepage_vma_check(vma, vm_flags, false))
> + if (hugepage_vma_check(vma, vm_flags, false, false))
> __khugepaged_enter(vma->vm_mm);
> }
> }
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index bc8370856e85..b95786ada466 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
>
> bool hugepage_vma_check(struct vm_area_struct *vma,
> unsigned long vm_flags,
> - bool smaps)
> + bool smaps, bool in_pf)
> {
> - if (!transhuge_vma_enabled(vma, vm_flags))
> + /* Explicitly disabled through madvise or prctl. */

Or s390 kvm (not that this has to be exhaustively maintained).

> + if ((vm_flags & VM_NOHUGEPAGE) ||
> + test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> + return false;
> + /*
> + * If the hardware/firmware marked hugepage support disabled.
> + */
> + if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> return false;

This introduces an extra check for khugepaged path. I don't know
enough about TRANSPARENT_HUGEPAGE_NEVER_DAX, but I assume this is ok?
What would have happened previously if khugepaged tried to collapse
this memory?

> + /* Special VMA and hugetlb VMA */
> if (vm_flags & VM_NO_KHUGEPAGED)
> return false;

This adds an extra check along the fault path. Is it also safe to add?

> - /* Don't run khugepaged against DAX vma */
> + /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
> if (vma_is_dax(vma))
> - return false;
> + return in_pf;

I assume vma_is_temporary_stack() and vma_is_dax() is mutually exclusive.

> if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
> vma->vm_pgoff, HPAGE_PMD_NR))
> @@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> return false;
>
> /* Enabled via shmem mount options or sysfs settings. */
> - if (shmem_file(vma->vm_file))
> + if (!in_pf && shmem_file(vma->vm_file))
> return shmem_huge_enabled(vma);

Will shmem_file() ever be true in the fault path? Or is this just an
optimization?

> if (!khugepaged_enabled())
> @@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> return false;
>
> /* Only regular file is valid */
> - if (file_thp_enabled(vma))
> + if (!in_pf && file_thp_enabled(vma))
> return true;

Likewise for file_thp_enabled()

> if (!vma_is_anonymous(vma))
> @@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> /*
> * THPeligible bit of smaps should show 1 for proper VMAs even
> * though anon_vma is not initialized yet.
> + *
> + * Allow page fault since anon_vma may be not initialized until
> + * the first page fault.
> */
> if (!vma->anon_vma)
> - return smaps;
> + return (smaps || in_pf);
>
> return true;
> }
> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index aa0769e3b0d9..ab6183c5489f 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c
> @@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> {
> if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> khugepaged_enabled()) {
> - if (hugepage_vma_check(vma, vm_flags, false))
> + if (hugepage_vma_check(vma, vm_flags, false, false))
> __khugepaged_enter(vma->vm_mm);
> }
> }
> @@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
>
> if (!transhuge_vma_suitable(vma, address))
> return SCAN_ADDRESS_RANGE;
> - if (!hugepage_vma_check(vma, vma->vm_flags, false))
> + if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
> return SCAN_VMA_CHECK;
> return 0;
> }
> @@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
> * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
> * will not fail the vma for missing VM_HUGEPAGE
> */
> - if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
> + if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
> return;
>
> /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
> @@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> progress++;
> break;
> }
> - if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
> + if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
> skip:
> progress++;
> continue;
> diff --git a/mm/memory.c b/mm/memory.c
> index bc5d40eec5d5..673f7561a30a 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> .gfp_mask = __get_fault_gfp_mask(vma),
> };
> struct mm_struct *mm = vma->vm_mm;
> + unsigned long vm_flags = vma->vm_flags;
> pgd_t *pgd;
> p4d_t *p4d;
> vm_fault_t ret;
> @@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> if (!vmf.pud)
> return VM_FAULT_OOM;
> retry_pud:
> - if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
> + if (pud_none(*vmf.pud) &&
> + hugepage_vma_check(vma, vm_flags, false, true)) {
> ret = create_huge_pud(&vmf);
> if (!(ret & VM_FAULT_FALLBACK))
> return ret;
> @@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> if (pud_trans_unstable(vmf.pud))
> goto retry_pud;
>
> - if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
> + if (pmd_none(*vmf.pmd) &&
> + hugepage_vma_check(vma, vm_flags, false, true)) {
> ret = create_huge_pmd(&vmf);
> if (!(ret & VM_FAULT_FALLBACK))
> return ret;
> --
> 2.26.3
>
>

2022-06-10 16:49:00

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

On Thu, Jun 9, 2022 at 5:52 PM Zach O'Keefe <[email protected]> wrote:
>
> On Thu, Jun 9, 2022 at 5:08 PM Yang Shi <[email protected]> wrote:
> >
> > On Thu, Jun 9, 2022 at 3:21 PM Zach O'Keefe <[email protected]> wrote:
> > >
> > > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > > >
> > > > There are couple of places that check whether the vma size is ok for
> > > > THP or not, they are open coded and duplicate, introduce
> > > > transhuge_vma_size_ok() helper to do the job.
> > > >
> > > > Signed-off-by: Yang Shi <[email protected]>
> > > > ---
> > > > include/linux/huge_mm.h | 17 +++++++++++++++++
> > > > mm/huge_memory.c | 5 +----
> > > > mm/khugepaged.c | 12 ++++++------
> > > > 3 files changed, 24 insertions(+), 10 deletions(-)
> > > >
> > > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > > index 648cb3ce7099..a8f61db47f2a 100644
> > > > --- a/include/linux/huge_mm.h
> > > > +++ b/include/linux/huge_mm.h
> > > > @@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;
> > > >
> > > > extern unsigned long transparent_hugepage_flags;
> > > >
> > > > +/*
> > > > + * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
> > > > + */
> > > > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > +{
> > > > + if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
> > > > + (vma->vm_end & HPAGE_PMD_MASK))
> > > > + return true;
> > > > +
> > > > + return false;
> > > > +}
> > >
> > > First time coming across round_up() - thanks for that - but for
> > > symmetry, maybe also use round_down() for the end? No strong opinion -
> > > just a suggestion given I've just discovered it.
> >
> > Yeah, round_down is fine too.
> >
> > >
> > > > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > unsigned long addr)
> > > > {
> > > > @@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
> > > > return false;
> > > > }
> > > >
> > > > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > +{
> > > > + return false;
> > > > +}
> > > > +
> > > > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > unsigned long addr)
> > > > {
> > > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > > index 48182c8fe151..36ada544e494 100644
> > > > --- a/mm/huge_memory.c
> > > > +++ b/mm/huge_memory.c
> > > > @@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > > >
> > > > bool transparent_hugepage_active(struct vm_area_struct *vma)
> > > > {
> > > > - /* The addr is used to check if the vma size fits */
> > > > - unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
> > > > -
> > > > - if (!transhuge_vma_suitable(vma, addr))
> > > > + if (!transhuge_vma_size_ok(vma))
> > > > return false;
> > > > if (vma_is_anonymous(vma))
> > > > return __transparent_hugepage_enabled(vma);
> > >
> > > Do we need a check for vma->vm_pgoff alignment here, after
> > > !vma_is_anonymous(), and now that we don't call
> > > transhuge_vma_suitable()?
> >
> > Actually I was thinking about this too. But the THPeligible bit shown
> > by smaps is a little bit ambiguous for file vma. The document says:
> > "THPeligible" indicates whether the mapping is eligible for allocating
> > THP pages - 1 if true, 0 otherwise.
> >
> > Even though it doesn't fulfill the alignment, it is still possible to
> > get THP allocated, but just can't be PMD mapped. So the old behavior
> > of THPeligible for file vma seems problematic, or at least doesn't
> > match the document.
>
> I think the term "THP" is used ambiguously. Often, but not always, in
> the code, folks will go out of their way to specify "hugepage-sized"
> page vs "pmd-mapped hugepage" - but at least from my experience,
> external documentation doesn't. Given that THP as a concept doesn't
> make much sense without the possibility of pmd-mapping, I think
> "THPeligible here means "pmd mappable". For example, AnonHugePages in
> smaps means pmd-mapped anon hugepages.

Yeah, depends on the expectation.

>
> That all said - the following patches will delete
> transparent_hugepage_active() anyways.

Yes, how I could forget this :-( The following removal of
transparent_hugepage_active() will restore the old behavior.

>
> > I should elaborate this in the commit log.
> >
> > >
> > > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > > index 84b9cf4b9be9..d0f8020164fc 100644
> > > > --- a/mm/khugepaged.c
> > > > +++ b/mm/khugepaged.c
> > > > @@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > > return false;
> > > >
> > > > + if (!transhuge_vma_size_ok(vma))
> > > > + return false;
> > > > +
> > > > /* Enabled via shmem mount options or sysfs settings. */
> > > > if (shmem_file(vma->vm_file))
> > > > return shmem_huge_enabled(vma);
> > > > @@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > > unsigned long vm_flags)
> > > > {
> > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > - khugepaged_enabled() &&
> > > > - (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
> > > > - (vma->vm_end & HPAGE_PMD_MASK))) {
> > > > + khugepaged_enabled()) {
> > > > if (hugepage_vma_check(vma, vm_flags))
> > > > __khugepaged_enter(vma->vm_mm);
> > > > }
> > > > @@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > > progress++;
> > > > continue;
> > > > }
> > > > - hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
> > > > +
> > > > + hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
> > > > hend = vma->vm_end & HPAGE_PMD_MASK;
> > > > - if (hstart >= hend)
> > > > - goto skip;
> > > > if (khugepaged_scan.address > hend)
> > > > goto skip;
> > > > if (khugepaged_scan.address < hstart)
> > >
> > > Likewise, could do round_down() here (just a suggestion)
> >
> > Fine to me.
> >
> > >
> > > > --
> > > > 2.26.3
> > > >
> > > >

2022-06-10 17:05:06

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

On Fri, Jun 10, 2022 at 12:20 AM Miaohe Lin <[email protected]> wrote:
>
> On 2022/6/7 5:44, Yang Shi wrote:
> > There are couple of places that check whether the vma size is ok for
> > THP or not, they are open coded and duplicate, introduce
> > transhuge_vma_size_ok() helper to do the job.
> >
> > Signed-off-by: Yang Shi <[email protected]>
> > ---
> > include/linux/huge_mm.h | 17 +++++++++++++++++
> > mm/huge_memory.c | 5 +----
> > mm/khugepaged.c | 12 ++++++------
> > 3 files changed, 24 insertions(+), 10 deletions(-)
> >
> > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > index 648cb3ce7099..a8f61db47f2a 100644
> > --- a/include/linux/huge_mm.h
> > +++ b/include/linux/huge_mm.h
> > @@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;
> >
> > extern unsigned long transparent_hugepage_flags;
> >
> > +/*
> > + * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
> > + */
> > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > +{
> > + if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
> > + (vma->vm_end & HPAGE_PMD_MASK))
> > + return true;
> > +
> > + return false;
> > +}
> > +
> > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > unsigned long addr)
> > {
> > @@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
> > return false;
> > }
> >
> > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > +{
> > + return false;
> > +}
> > +
> > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > unsigned long addr)
> > {
> > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > index 48182c8fe151..36ada544e494 100644
> > --- a/mm/huge_memory.c
> > +++ b/mm/huge_memory.c
> > @@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> >
> > bool transparent_hugepage_active(struct vm_area_struct *vma)
> > {
> > - /* The addr is used to check if the vma size fits */
> > - unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
> > -
> > - if (!transhuge_vma_suitable(vma, addr))
>
> There is also pgoff check for file page in transhuge_vma_suitable. Is it ignored
> deliberately?

This has been discussed in the previous threads. The following removal
of transparent_hugepage_active() will restore the behavior.

>
> > + if (!transhuge_vma_size_ok(vma))
> > return false;
> > if (vma_is_anonymous(vma))
> > return __transparent_hugepage_enabled(vma);
> > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > index 84b9cf4b9be9..d0f8020164fc 100644
> > --- a/mm/khugepaged.c
> > +++ b/mm/khugepaged.c
> > @@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > vma->vm_pgoff, HPAGE_PMD_NR))
> > return false;
> >
> > + if (!transhuge_vma_size_ok(vma))
> > + return false;
> > +
> > /* Enabled via shmem mount options or sysfs settings. */
> > if (shmem_file(vma->vm_file))
> > return shmem_huge_enabled(vma);
> > @@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > unsigned long vm_flags)
> > {
> > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > - khugepaged_enabled() &&
> > - (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
> > - (vma->vm_end & HPAGE_PMD_MASK))) {
> > + khugepaged_enabled()) {
> > if (hugepage_vma_check(vma, vm_flags))
> > __khugepaged_enter(vma->vm_mm);
> > }
>
> After this change, khugepaged_enter_vma is identical to khugepaged_enter. Should one of
> them be removed?

Thanks for catching this. Although the later patch will make them
slightly different (khugepaged_enter() won't check hugepage flag
anymore), but the only user of khugepaged_enter() is page fault, and
it seems not worth keeping both. Will remove khugepaged_enter() in the
next version.

>
> Thanks!
>
> > @@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > progress++;
> > continue;
> > }
> > - hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
> > +
> > + hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
> > hend = vma->vm_end & HPAGE_PMD_MASK;
> > - if (hstart >= hend)
> > - goto skip;
> > if (khugepaged_scan.address > hend)
> > goto skip;
> > if (khugepaged_scan.address < hstart)
> >
>

2022-06-10 18:27:36

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

On Thu, Jun 9, 2022 at 7:22 PM Zach O'Keefe <[email protected]> wrote:
>
> On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> >
> > The page fault path checks THP eligibility with
> > __transhuge_page_enabled() which does the similar thing as
> > hugepage_vma_check(), so use hugepage_vma_check() instead.
> >
> > However page fault allows DAX and !anon_vma cases, so added a new flag,
> > in_pf, to hugepage_vma_check() to make page fault work correctly.
> >
> > The in_pf flag is also used to skip shmem and file THP for page fault
> > since shmem handles THP in its own shmem_fault() and file THP allocation
> > on fault is not supported yet.
> >
> > Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
> > only caller now, it is not necessary to have a helper function.
> >
> > Signed-off-by: Yang Shi <[email protected]>
> > ---
> > fs/proc/task_mmu.c | 2 +-
> > include/linux/huge_mm.h | 57 ++------------------------------------
> > include/linux/khugepaged.h | 2 +-
> > mm/huge_memory.c | 25 ++++++++++++-----
> > mm/khugepaged.c | 8 +++---
> > mm/memory.c | 7 +++--
> > 6 files changed, 31 insertions(+), 70 deletions(-)
> >
> > diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> > index fd79566e204c..a0850303baec 100644
> > --- a/fs/proc/task_mmu.c
> > +++ b/fs/proc/task_mmu.c
> > @@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
> > __show_smap(m, &mss, false);
> >
> > seq_printf(m, "THPeligible: %d\n",
> > - hugepage_vma_check(vma, vma->vm_flags, true));
> > + hugepage_vma_check(vma, vma->vm_flags, true, false));
> >
> > if (arch_pkeys_enabled())
> > seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
> > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > index f561c3e16def..d478e8875023 100644
> > --- a/include/linux/huge_mm.h
> > +++ b/include/linux/huge_mm.h
> > @@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > return true;
> > }
> >
> > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > - unsigned long vm_flags)
> > -{
> > - /* Explicitly disabled through madvise. */
> > - if ((vm_flags & VM_NOHUGEPAGE) ||
> > - test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > - return false;
> > - return true;
> > -}
> > -
> > -/*
> > - * to be used on vmas which are known to support THP.
> > - * Use transparent_hugepage_active otherwise
> > - */
> > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > -{
> > -
> > - /*
> > - * If the hardware/firmware marked hugepage support disabled.
> > - */
> > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > - return false;
> > -
> > - if (!transhuge_vma_enabled(vma, vma->vm_flags))
> > - return false;
> > -
> > - if (vma_is_temporary_stack(vma))
> > - return false;
> > -
> > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
> > - return true;
> > -
> > - if (vma_is_dax(vma))
> > - return true;
> > -
> > - if (transparent_hugepage_flags &
> > - (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
> > - return !!(vma->vm_flags & VM_HUGEPAGE);
> > -
> > - return false;
> > -}
> > -
> > static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > {
> > struct inode *inode;
> > @@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
> >
> > bool hugepage_vma_check(struct vm_area_struct *vma,
> > unsigned long vm_flags,
> > - bool smaps);
> > + bool smaps, bool in_pf);
> >
> > #define transparent_hugepage_use_zero_page() \
> > (transparent_hugepage_flags & \
> > @@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
> > return false;
> > }
> >
> > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > -{
> > - return false;
> > -}
> > -
> > static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > {
> > return false;
> > @@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > return false;
> > }
> >
> > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > - unsigned long vm_flags)
> > -{
> > - return false;
> > -}
> > -
> > static inline bool hugepage_vma_check(struct vm_area_struct *vma,
> > unsigned long vm_flags,
> > - bool smaps)
> > + bool smaps, bool in_pf)
> > {
> > return false;
> > }
> > diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
> > index 8a6452e089ca..e047be601268 100644
> > --- a/include/linux/khugepaged.h
> > +++ b/include/linux/khugepaged.h
> > @@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
> > {
> > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > khugepaged_enabled()) {
> > - if (hugepage_vma_check(vma, vm_flags, false))
> > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > __khugepaged_enter(vma->vm_mm);
> > }
> > }
> > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > index bc8370856e85..b95786ada466 100644
> > --- a/mm/huge_memory.c
> > +++ b/mm/huge_memory.c
> > @@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> >
> > bool hugepage_vma_check(struct vm_area_struct *vma,
> > unsigned long vm_flags,
> > - bool smaps)
> > + bool smaps, bool in_pf)
> > {
> > - if (!transhuge_vma_enabled(vma, vm_flags))
> > + /* Explicitly disabled through madvise or prctl. */
>
> Or s390 kvm (not that this has to be exhaustively maintained).
>
> > + if ((vm_flags & VM_NOHUGEPAGE) ||
> > + test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > + return false;
> > + /*
> > + * If the hardware/firmware marked hugepage support disabled.
> > + */
> > + if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > return false;
>
> This introduces an extra check for khugepaged path. I don't know
> enough about TRANSPARENT_HUGEPAGE_NEVER_DAX, but I assume this is ok?
> What would have happened previously if khugepaged tried to collapse
> this memory?

Please refer to commit bae849538157 ("mm/pmem: avoid inserting
hugepage PTE entry with fsdax if hugepage support is disabled") for
why this flag was introduced.

It is set if hardware doesn't support hugepages, and khugepaged
doesn't collapse since khugepaged won't be started at all.

But this flag needs to be checked in the page fault path.

>
> > + /* Special VMA and hugetlb VMA */
> > if (vm_flags & VM_NO_KHUGEPAGED)
> > return false;
>
> This adds an extra check along the fault path. Is it also safe to add?

I think it is safe since hugepage_vma_check() is just used by THP.
Hugetlb has its own page fault handler.

>
> > - /* Don't run khugepaged against DAX vma */
> > + /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
> > if (vma_is_dax(vma))
> > - return false;
> > + return in_pf;
>
> I assume vma_is_temporary_stack() and vma_is_dax() is mutually exclusive.

I think so.

>
> > if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
> > vma->vm_pgoff, HPAGE_PMD_NR))
> > @@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > return false;
> >
> > /* Enabled via shmem mount options or sysfs settings. */
> > - if (shmem_file(vma->vm_file))
> > + if (!in_pf && shmem_file(vma->vm_file))
> > return shmem_huge_enabled(vma);
>
> Will shmem_file() ever be true in the fault path? Or is this just an
> optimization?

It could be true. But shmem has its own implementation for huge page
fault and doesn't implement huge_fault() in its vm_operations, so it
will fallback even though "in_pf" is not checked.

But xfs does have huge_fault() implemented, so it may try to allocate
THP for non-DAX xfs files. So the "in_pf" flag is introduced to handle
this. Since we need this flag anyway, why not use it to return earlier
for shmem instead of relying on fallback.

Anyway this is all because __transparent_huge_enabled() is replaced by
hugepage_vma_check().

> > if (!khugepaged_enabled())
> > @@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > return false;
> >
> > /* Only regular file is valid */
> > - if (file_thp_enabled(vma))
> > + if (!in_pf && file_thp_enabled(vma))
> > return true;
>
> Likewise for file_thp_enabled()

Yes, same as the above.

>
> > if (!vma_is_anonymous(vma))
> > @@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > /*
> > * THPeligible bit of smaps should show 1 for proper VMAs even
> > * though anon_vma is not initialized yet.
> > + *
> > + * Allow page fault since anon_vma may be not initialized until
> > + * the first page fault.
> > */
> > if (!vma->anon_vma)
> > - return smaps;
> > + return (smaps || in_pf);
> >
> > return true;
> > }
> > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > index aa0769e3b0d9..ab6183c5489f 100644
> > --- a/mm/khugepaged.c
> > +++ b/mm/khugepaged.c
> > @@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > {
> > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > khugepaged_enabled()) {
> > - if (hugepage_vma_check(vma, vm_flags, false))
> > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > __khugepaged_enter(vma->vm_mm);
> > }
> > }
> > @@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
> >
> > if (!transhuge_vma_suitable(vma, address))
> > return SCAN_ADDRESS_RANGE;
> > - if (!hugepage_vma_check(vma, vma->vm_flags, false))
> > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
> > return SCAN_VMA_CHECK;
> > return 0;
> > }
> > @@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
> > * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
> > * will not fail the vma for missing VM_HUGEPAGE
> > */
> > - if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
> > + if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
> > return;
> >
> > /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
> > @@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > progress++;
> > break;
> > }
> > - if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
> > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
> > skip:
> > progress++;
> > continue;
> > diff --git a/mm/memory.c b/mm/memory.c
> > index bc5d40eec5d5..673f7561a30a 100644
> > --- a/mm/memory.c
> > +++ b/mm/memory.c
> > @@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > .gfp_mask = __get_fault_gfp_mask(vma),
> > };
> > struct mm_struct *mm = vma->vm_mm;
> > + unsigned long vm_flags = vma->vm_flags;
> > pgd_t *pgd;
> > p4d_t *p4d;
> > vm_fault_t ret;
> > @@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > if (!vmf.pud)
> > return VM_FAULT_OOM;
> > retry_pud:
> > - if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
> > + if (pud_none(*vmf.pud) &&
> > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > ret = create_huge_pud(&vmf);
> > if (!(ret & VM_FAULT_FALLBACK))
> > return ret;
> > @@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > if (pud_trans_unstable(vmf.pud))
> > goto retry_pud;
> >
> > - if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
> > + if (pmd_none(*vmf.pmd) &&
> > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > ret = create_huge_pmd(&vmf);
> > if (!(ret & VM_FAULT_FALLBACK))
> > return ret;
> > --
> > 2.26.3
> >
> >

2022-06-10 21:58:27

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

On Fri, Jun 10, 2022 at 10:24 AM Yang Shi <[email protected]> wrote:
>
> On Thu, Jun 9, 2022 at 7:22 PM Zach O'Keefe <[email protected]> wrote:
> >
> > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > >
> > > The page fault path checks THP eligibility with
> > > __transhuge_page_enabled() which does the similar thing as
> > > hugepage_vma_check(), so use hugepage_vma_check() instead.
> > >
> > > However page fault allows DAX and !anon_vma cases, so added a new flag,
> > > in_pf, to hugepage_vma_check() to make page fault work correctly.
> > >
> > > The in_pf flag is also used to skip shmem and file THP for page fault
> > > since shmem handles THP in its own shmem_fault() and file THP allocation
> > > on fault is not supported yet.
> > >
> > > Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
> > > only caller now, it is not necessary to have a helper function.
> > >
> > > Signed-off-by: Yang Shi <[email protected]>
> > > ---
> > > fs/proc/task_mmu.c | 2 +-
> > > include/linux/huge_mm.h | 57 ++------------------------------------
> > > include/linux/khugepaged.h | 2 +-
> > > mm/huge_memory.c | 25 ++++++++++++-----
> > > mm/khugepaged.c | 8 +++---
> > > mm/memory.c | 7 +++--
> > > 6 files changed, 31 insertions(+), 70 deletions(-)
> > >
> > > diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> > > index fd79566e204c..a0850303baec 100644
> > > --- a/fs/proc/task_mmu.c
> > > +++ b/fs/proc/task_mmu.c
> > > @@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
> > > __show_smap(m, &mss, false);
> > >
> > > seq_printf(m, "THPeligible: %d\n",
> > > - hugepage_vma_check(vma, vma->vm_flags, true));
> > > + hugepage_vma_check(vma, vma->vm_flags, true, false));
> > >
> > > if (arch_pkeys_enabled())
> > > seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
> > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > index f561c3e16def..d478e8875023 100644
> > > --- a/include/linux/huge_mm.h
> > > +++ b/include/linux/huge_mm.h
> > > @@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > return true;
> > > }
> > >
> > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > - unsigned long vm_flags)
> > > -{
> > > - /* Explicitly disabled through madvise. */
> > > - if ((vm_flags & VM_NOHUGEPAGE) ||
> > > - test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > - return false;
> > > - return true;
> > > -}
> > > -
> > > -/*
> > > - * to be used on vmas which are known to support THP.
> > > - * Use transparent_hugepage_active otherwise
> > > - */
> > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > -{
> > > -
> > > - /*
> > > - * If the hardware/firmware marked hugepage support disabled.
> > > - */
> > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > - return false;
> > > -
> > > - if (!transhuge_vma_enabled(vma, vma->vm_flags))
> > > - return false;
> > > -
> > > - if (vma_is_temporary_stack(vma))
> > > - return false;
> > > -
> > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
> > > - return true;
> > > -
> > > - if (vma_is_dax(vma))
> > > - return true;
> > > -
> > > - if (transparent_hugepage_flags &
> > > - (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
> > > - return !!(vma->vm_flags & VM_HUGEPAGE);
> > > -
> > > - return false;
> > > -}
> > > -
> > > static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > {
> > > struct inode *inode;
> > > @@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > >
> > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > unsigned long vm_flags,
> > > - bool smaps);
> > > + bool smaps, bool in_pf);
> > >
> > > #define transparent_hugepage_use_zero_page() \
> > > (transparent_hugepage_flags & \
> > > @@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
> > > return false;
> > > }
> > >
> > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > -{
> > > - return false;
> > > -}
> > > -
> > > static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > {
> > > return false;
> > > @@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > return false;
> > > }
> > >
> > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > - unsigned long vm_flags)
> > > -{
> > > - return false;
> > > -}
> > > -
> > > static inline bool hugepage_vma_check(struct vm_area_struct *vma,
> > > unsigned long vm_flags,
> > > - bool smaps)
> > > + bool smaps, bool in_pf)
> > > {
> > > return false;
> > > }
> > > diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
> > > index 8a6452e089ca..e047be601268 100644
> > > --- a/include/linux/khugepaged.h
> > > +++ b/include/linux/khugepaged.h
> > > @@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
> > > {
> > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > khugepaged_enabled()) {
> > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > __khugepaged_enter(vma->vm_mm);
> > > }
> > > }
> > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > index bc8370856e85..b95786ada466 100644
> > > --- a/mm/huge_memory.c
> > > +++ b/mm/huge_memory.c
> > > @@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > >
> > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > unsigned long vm_flags,
> > > - bool smaps)
> > > + bool smaps, bool in_pf)
> > > {
> > > - if (!transhuge_vma_enabled(vma, vm_flags))
> > > + /* Explicitly disabled through madvise or prctl. */
> >
> > Or s390 kvm (not that this has to be exhaustively maintained).
> >
> > > + if ((vm_flags & VM_NOHUGEPAGE) ||
> > > + test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > + return false;
> > > + /*
> > > + * If the hardware/firmware marked hugepage support disabled.
> > > + */
> > > + if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > return false;
> >
> > This introduces an extra check for khugepaged path. I don't know
> > enough about TRANSPARENT_HUGEPAGE_NEVER_DAX, but I assume this is ok?
> > What would have happened previously if khugepaged tried to collapse
> > this memory?
>
> Please refer to commit bae849538157 ("mm/pmem: avoid inserting
> hugepage PTE entry with fsdax if hugepage support is disabled") for
> why this flag was introduced.
>
> It is set if hardware doesn't support hugepages, and khugepaged
> doesn't collapse since khugepaged won't be started at all.
>
> But this flag needs to be checked in the page fault path.
>
> >
> > > + /* Special VMA and hugetlb VMA */
> > > if (vm_flags & VM_NO_KHUGEPAGED)
> > > return false;
> >
> > This adds an extra check along the fault path. Is it also safe to add?
>
> I think it is safe since hugepage_vma_check() is just used by THP.
> Hugetlb has its own page fault handler.

I just found one exception. The fuse dax has VM_MIXEDMAP set for its
vmas, so this check should be moved after vma_is_dax() check.

AFAICT, only dax supports huge_fault() and dax vmas don't have any
VM_SPECIAL flags set other than fuse.

>
> >
> > > - /* Don't run khugepaged against DAX vma */
> > > + /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
> > > if (vma_is_dax(vma))
> > > - return false;
> > > + return in_pf;
> >
> > I assume vma_is_temporary_stack() and vma_is_dax() is mutually exclusive.
>
> I think so.
>
> >
> > > if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
> > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > @@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > return false;
> > >
> > > /* Enabled via shmem mount options or sysfs settings. */
> > > - if (shmem_file(vma->vm_file))
> > > + if (!in_pf && shmem_file(vma->vm_file))
> > > return shmem_huge_enabled(vma);
> >
> > Will shmem_file() ever be true in the fault path? Or is this just an
> > optimization?
>
> It could be true. But shmem has its own implementation for huge page
> fault and doesn't implement huge_fault() in its vm_operations, so it
> will fallback even though "in_pf" is not checked.
>
> But xfs does have huge_fault() implemented, so it may try to allocate
> THP for non-DAX xfs files. So the "in_pf" flag is introduced to handle
> this. Since we need this flag anyway, why not use it to return earlier
> for shmem instead of relying on fallback.
>
> Anyway this is all because __transparent_huge_enabled() is replaced by
> hugepage_vma_check().
>
> > > if (!khugepaged_enabled())
> > > @@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > return false;
> > >
> > > /* Only regular file is valid */
> > > - if (file_thp_enabled(vma))
> > > + if (!in_pf && file_thp_enabled(vma))
> > > return true;
> >
> > Likewise for file_thp_enabled()
>
> Yes, same as the above.
>
> >
> > > if (!vma_is_anonymous(vma))
> > > @@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > /*
> > > * THPeligible bit of smaps should show 1 for proper VMAs even
> > > * though anon_vma is not initialized yet.
> > > + *
> > > + * Allow page fault since anon_vma may be not initialized until
> > > + * the first page fault.
> > > */
> > > if (!vma->anon_vma)
> > > - return smaps;
> > > + return (smaps || in_pf);
> > >
> > > return true;
> > > }
> > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > index aa0769e3b0d9..ab6183c5489f 100644
> > > --- a/mm/khugepaged.c
> > > +++ b/mm/khugepaged.c
> > > @@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > {
> > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > khugepaged_enabled()) {
> > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > __khugepaged_enter(vma->vm_mm);
> > > }
> > > }
> > > @@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
> > >
> > > if (!transhuge_vma_suitable(vma, address))
> > > return SCAN_ADDRESS_RANGE;
> > > - if (!hugepage_vma_check(vma, vma->vm_flags, false))
> > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
> > > return SCAN_VMA_CHECK;
> > > return 0;
> > > }
> > > @@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
> > > * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
> > > * will not fail the vma for missing VM_HUGEPAGE
> > > */
> > > - if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
> > > + if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
> > > return;
> > >
> > > /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
> > > @@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > progress++;
> > > break;
> > > }
> > > - if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
> > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
> > > skip:
> > > progress++;
> > > continue;
> > > diff --git a/mm/memory.c b/mm/memory.c
> > > index bc5d40eec5d5..673f7561a30a 100644
> > > --- a/mm/memory.c
> > > +++ b/mm/memory.c
> > > @@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > .gfp_mask = __get_fault_gfp_mask(vma),
> > > };
> > > struct mm_struct *mm = vma->vm_mm;
> > > + unsigned long vm_flags = vma->vm_flags;
> > > pgd_t *pgd;
> > > p4d_t *p4d;
> > > vm_fault_t ret;
> > > @@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > if (!vmf.pud)
> > > return VM_FAULT_OOM;
> > > retry_pud:
> > > - if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
> > > + if (pud_none(*vmf.pud) &&
> > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > ret = create_huge_pud(&vmf);
> > > if (!(ret & VM_FAULT_FALLBACK))
> > > return ret;
> > > @@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > if (pud_trans_unstable(vmf.pud))
> > > goto retry_pud;
> > >
> > > - if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
> > > + if (pmd_none(*vmf.pmd) &&
> > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > ret = create_huge_pmd(&vmf);
> > > if (!(ret & VM_FAULT_FALLBACK))
> > > return ret;
> > > --
> > > 2.26.3
> > >
> > >

2022-06-10 22:12:03

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 2/7] mm: thp: introduce transhuge_vma_size_ok() helper

On Fri, Jun 10, 2022 at 9:38 AM Yang Shi <[email protected]> wrote:
>
> On Thu, Jun 9, 2022 at 5:52 PM Zach O'Keefe <[email protected]> wrote:
> >
> > On Thu, Jun 9, 2022 at 5:08 PM Yang Shi <[email protected]> wrote:
> > >
> > > On Thu, Jun 9, 2022 at 3:21 PM Zach O'Keefe <[email protected]> wrote:
> > > >
> > > > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > > > >
> > > > > There are couple of places that check whether the vma size is ok for
> > > > > THP or not, they are open coded and duplicate, introduce
> > > > > transhuge_vma_size_ok() helper to do the job.
> > > > >
> > > > > Signed-off-by: Yang Shi <[email protected]>
> > > > > ---
> > > > > include/linux/huge_mm.h | 17 +++++++++++++++++
> > > > > mm/huge_memory.c | 5 +----
> > > > > mm/khugepaged.c | 12 ++++++------
> > > > > 3 files changed, 24 insertions(+), 10 deletions(-)
> > > > >
> > > > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > > > index 648cb3ce7099..a8f61db47f2a 100644
> > > > > --- a/include/linux/huge_mm.h
> > > > > +++ b/include/linux/huge_mm.h
> > > > > @@ -116,6 +116,18 @@ extern struct kobj_attribute shmem_enabled_attr;
> > > > >
> > > > > extern unsigned long transparent_hugepage_flags;
> > > > >
> > > > > +/*
> > > > > + * The vma size has to be large enough to hold an aligned HPAGE_PMD_SIZE area.
> > > > > + */
> > > > > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > > +{
> > > > > + if (round_up(vma->vm_start, HPAGE_PMD_SIZE) <
> > > > > + (vma->vm_end & HPAGE_PMD_MASK))
> > > > > + return true;
> > > > > +
> > > > > + return false;
> > > > > +}
> > > >
> > > > First time coming across round_up() - thanks for that - but for
> > > > symmetry, maybe also use round_down() for the end? No strong opinion -
> > > > just a suggestion given I've just discovered it.
> > >
> > > Yeah, round_down is fine too.
> > >
> > > >
> > > > > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > > unsigned long addr)
> > > > > {
> > > > > @@ -345,6 +357,11 @@ static inline bool transparent_hugepage_active(struct vm_area_struct *vma)
> > > > > return false;
> > > > > }
> > > > >
> > > > > +static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > > +{
> > > > > + return false;
> > > > > +}
> > > > > +
> > > > > static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > > unsigned long addr)
> > > > > {
> > > > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > > > index 48182c8fe151..36ada544e494 100644
> > > > > --- a/mm/huge_memory.c
> > > > > +++ b/mm/huge_memory.c
> > > > > @@ -71,10 +71,7 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > > > >
> > > > > bool transparent_hugepage_active(struct vm_area_struct *vma)
> > > > > {
> > > > > - /* The addr is used to check if the vma size fits */
> > > > > - unsigned long addr = (vma->vm_end & HPAGE_PMD_MASK) - HPAGE_PMD_SIZE;
> > > > > -
> > > > > - if (!transhuge_vma_suitable(vma, addr))
> > > > > + if (!transhuge_vma_size_ok(vma))
> > > > > return false;
> > > > > if (vma_is_anonymous(vma))
> > > > > return __transparent_hugepage_enabled(vma);
> > > >
> > > > Do we need a check for vma->vm_pgoff alignment here, after
> > > > !vma_is_anonymous(), and now that we don't call
> > > > transhuge_vma_suitable()?
> > >
> > > Actually I was thinking about this too. But the THPeligible bit shown
> > > by smaps is a little bit ambiguous for file vma. The document says:
> > > "THPeligible" indicates whether the mapping is eligible for allocating
> > > THP pages - 1 if true, 0 otherwise.
> > >
> > > Even though it doesn't fulfill the alignment, it is still possible to
> > > get THP allocated, but just can't be PMD mapped. So the old behavior
> > > of THPeligible for file vma seems problematic, or at least doesn't
> > > match the document.
> >
> > I think the term "THP" is used ambiguously. Often, but not always, in
> > the code, folks will go out of their way to specify "hugepage-sized"
> > page vs "pmd-mapped hugepage" - but at least from my experience,
> > external documentation doesn't. Given that THP as a concept doesn't
> > make much sense without the possibility of pmd-mapping, I think
> > "THPeligible here means "pmd mappable". For example, AnonHugePages in
> > smaps means pmd-mapped anon hugepages.
>
> Yeah, depends on the expectation.

The funny thing is I was the last one who touched the THPeligible. It
seems the document needs to be updated too to make "pmd mappable" more
explicitly.

>
> >
> > That all said - the following patches will delete
> > transparent_hugepage_active() anyways.
>
> Yes, how I could forget this :-( The following removal of
> transparent_hugepage_active() will restore the old behavior.
>
> >
> > > I should elaborate this in the commit log.
> > >
> > > >
> > > > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > > > index 84b9cf4b9be9..d0f8020164fc 100644
> > > > > --- a/mm/khugepaged.c
> > > > > +++ b/mm/khugepaged.c
> > > > > @@ -454,6 +454,9 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > > > return false;
> > > > >
> > > > > + if (!transhuge_vma_size_ok(vma))
> > > > > + return false;
> > > > > +
> > > > > /* Enabled via shmem mount options or sysfs settings. */
> > > > > if (shmem_file(vma->vm_file))
> > > > > return shmem_huge_enabled(vma);
> > > > > @@ -512,9 +515,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > > > unsigned long vm_flags)
> > > > > {
> > > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > > - khugepaged_enabled() &&
> > > > > - (((vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK) <
> > > > > - (vma->vm_end & HPAGE_PMD_MASK))) {
> > > > > + khugepaged_enabled()) {
> > > > > if (hugepage_vma_check(vma, vm_flags))
> > > > > __khugepaged_enter(vma->vm_mm);
> > > > > }
> > > > > @@ -2142,10 +2143,9 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > > > progress++;
> > > > > continue;
> > > > > }
> > > > > - hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
> > > > > +
> > > > > + hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE);
> > > > > hend = vma->vm_end & HPAGE_PMD_MASK;
> > > > > - if (hstart >= hend)
> > > > > - goto skip;
> > > > > if (khugepaged_scan.address > hend)
> > > > > goto skip;
> > > > > if (khugepaged_scan.address < hstart)
> > > >
> > > > Likewise, could do round_down() here (just a suggestion)
> > >
> > > Fine to me.
> > >
> > > >
> > > > > --
> > > > > 2.26.3
> > > > >
> > > > >

2022-06-13 18:40:41

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

On 10 Jun 14:07, Yang Shi wrote:
> On Fri, Jun 10, 2022 at 10:24 AM Yang Shi <[email protected]> wrote:
> >
> > On Thu, Jun 9, 2022 at 7:22 PM Zach O'Keefe <[email protected]> wrote:
> > >
> > > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > > >
> > > > The page fault path checks THP eligibility with
> > > > __transhuge_page_enabled() which does the similar thing as
> > > > hugepage_vma_check(), so use hugepage_vma_check() instead.
> > > >
> > > > However page fault allows DAX and !anon_vma cases, so added a new flag,
> > > > in_pf, to hugepage_vma_check() to make page fault work correctly.
> > > >
> > > > The in_pf flag is also used to skip shmem and file THP for page fault
> > > > since shmem handles THP in its own shmem_fault() and file THP allocation
> > > > on fault is not supported yet.
> > > >
> > > > Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
> > > > only caller now, it is not necessary to have a helper function.
> > > >
> > > > Signed-off-by: Yang Shi <[email protected]>
> > > > ---
> > > > fs/proc/task_mmu.c | 2 +-
> > > > include/linux/huge_mm.h | 57 ++------------------------------------
> > > > include/linux/khugepaged.h | 2 +-
> > > > mm/huge_memory.c | 25 ++++++++++++-----
> > > > mm/khugepaged.c | 8 +++---
> > > > mm/memory.c | 7 +++--
> > > > 6 files changed, 31 insertions(+), 70 deletions(-)
> > > >
> > > > diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> > > > index fd79566e204c..a0850303baec 100644
> > > > --- a/fs/proc/task_mmu.c
> > > > +++ b/fs/proc/task_mmu.c
> > > > @@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
> > > > __show_smap(m, &mss, false);
> > > >
> > > > seq_printf(m, "THPeligible: %d\n",
> > > > - hugepage_vma_check(vma, vma->vm_flags, true));
> > > > + hugepage_vma_check(vma, vma->vm_flags, true, false));
> > > >
> > > > if (arch_pkeys_enabled())
> > > > seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
> > > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > > index f561c3e16def..d478e8875023 100644
> > > > --- a/include/linux/huge_mm.h
> > > > +++ b/include/linux/huge_mm.h
> > > > @@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > return true;
> > > > }
> > > >
> > > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > > - unsigned long vm_flags)
> > > > -{
> > > > - /* Explicitly disabled through madvise. */
> > > > - if ((vm_flags & VM_NOHUGEPAGE) ||
> > > > - test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > > - return false;
> > > > - return true;
> > > > -}
> > > > -
> > > > -/*
> > > > - * to be used on vmas which are known to support THP.
> > > > - * Use transparent_hugepage_active otherwise
> > > > - */
> > > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > > -{
> > > > -
> > > > - /*
> > > > - * If the hardware/firmware marked hugepage support disabled.
> > > > - */
> > > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > > - return false;
> > > > -
> > > > - if (!transhuge_vma_enabled(vma, vma->vm_flags))
> > > > - return false;
> > > > -
> > > > - if (vma_is_temporary_stack(vma))
> > > > - return false;
> > > > -
> > > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
> > > > - return true;
> > > > -
> > > > - if (vma_is_dax(vma))
> > > > - return true;
> > > > -
> > > > - if (transparent_hugepage_flags &
> > > > - (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
> > > > - return !!(vma->vm_flags & VM_HUGEPAGE);
> > > > -
> > > > - return false;
> > > > -}
> > > > -
> > > > static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > > {
> > > > struct inode *inode;
> > > > @@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > >
> > > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > unsigned long vm_flags,
> > > > - bool smaps);
> > > > + bool smaps, bool in_pf);
> > > >
> > > > #define transparent_hugepage_use_zero_page() \
> > > > (transparent_hugepage_flags & \
> > > > @@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
> > > > return false;
> > > > }
> > > >
> > > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > > -{
> > > > - return false;
> > > > -}
> > > > -
> > > > static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > {
> > > > return false;
> > > > @@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > return false;
> > > > }
> > > >
> > > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > > - unsigned long vm_flags)
> > > > -{
> > > > - return false;
> > > > -}
> > > > -
> > > > static inline bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > unsigned long vm_flags,
> > > > - bool smaps)
> > > > + bool smaps, bool in_pf)
> > > > {
> > > > return false;
> > > > }
> > > > diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
> > > > index 8a6452e089ca..e047be601268 100644
> > > > --- a/include/linux/khugepaged.h
> > > > +++ b/include/linux/khugepaged.h
> > > > @@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
> > > > {
> > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > khugepaged_enabled()) {
> > > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > > __khugepaged_enter(vma->vm_mm);
> > > > }
> > > > }
> > > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > > index bc8370856e85..b95786ada466 100644
> > > > --- a/mm/huge_memory.c
> > > > +++ b/mm/huge_memory.c
> > > > @@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > > >
> > > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > unsigned long vm_flags,
> > > > - bool smaps)
> > > > + bool smaps, bool in_pf)
> > > > {
> > > > - if (!transhuge_vma_enabled(vma, vm_flags))
> > > > + /* Explicitly disabled through madvise or prctl. */
> > >
> > > Or s390 kvm (not that this has to be exhaustively maintained).
> > >
> > > > + if ((vm_flags & VM_NOHUGEPAGE) ||
> > > > + test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > > + return false;
> > > > + /*
> > > > + * If the hardware/firmware marked hugepage support disabled.
> > > > + */
> > > > + if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > > return false;
> > >
> > > This introduces an extra check for khugepaged path. I don't know
> > > enough about TRANSPARENT_HUGEPAGE_NEVER_DAX, but I assume this is ok?
> > > What would have happened previously if khugepaged tried to collapse
> > > this memory?
> >
> > Please refer to commit bae849538157 ("mm/pmem: avoid inserting
> > hugepage PTE entry with fsdax if hugepage support is disabled") for
> > why this flag was introduced.
> >
> > It is set if hardware doesn't support hugepages, and khugepaged
> > doesn't collapse since khugepaged won't be started at all.
> >
> > But this flag needs to be checked in the page fault path.
> >

Thanks for the ref to the commit. I'm not sure I understand it in its entirety,
but at least I can tell khugepaged won't be started :)

> > >
> > > > + /* Special VMA and hugetlb VMA */
> > > > if (vm_flags & VM_NO_KHUGEPAGED)
> > > > return false;
> > >
> > > This adds an extra check along the fault path. Is it also safe to add?
> >
> > I think it is safe since hugepage_vma_check() is just used by THP.
> > Hugetlb has its own page fault handler.
>
> I just found one exception. The fuse dax has VM_MIXEDMAP set for its
> vmas, so this check should be moved after vma_is_dax() check.
>
> AFAICT, only dax supports huge_fault() and dax vmas don't have any
> VM_SPECIAL flags set other than fuse.
>

Ordering wrt VM_NO_KHUGEPAGED check seems fine. We could always use in_pf to opt
out of this check, but I think itemizing where collapse and fault paths are
different would be good.

> >
> > >
> > > > - /* Don't run khugepaged against DAX vma */
> > > > + /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
> > > > if (vma_is_dax(vma))
> > > > - return false;
> > > > + return in_pf;
> > >
> > > I assume vma_is_temporary_stack() and vma_is_dax() is mutually exclusive.
> >
> > I think so.
> >
> > >
> > > > if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
> > > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > > @@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > return false;
> > > >
> > > > /* Enabled via shmem mount options or sysfs settings. */
> > > > - if (shmem_file(vma->vm_file))
> > > > + if (!in_pf && shmem_file(vma->vm_file))
> > > > return shmem_huge_enabled(vma);
> > >
> > > Will shmem_file() ever be true in the fault path? Or is this just an
> > > optimization?
> >
> > It could be true. But shmem has its own implementation for huge page
> > fault and doesn't implement huge_fault() in its vm_operations, so it
> > will fallback even though "in_pf" is not checked.
> >
> > But xfs does have huge_fault() implemented, so it may try to allocate
> > THP for non-DAX xfs files. So the "in_pf" flag is introduced to handle
> > this. Since we need this flag anyway, why not use it to return earlier
> > for shmem instead of relying on fallback.
> >
> > Anyway this is all because __transparent_huge_enabled() is replaced by
> > hugepage_vma_check().
> >

Thanks for the explanation. Admittedly I don't fully understand the involvement
of xfs in the shmem case, but general "fail early" logic seems fine to me.

> > > > if (!khugepaged_enabled())
> > > > @@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > return false;
> > > >
> > > > /* Only regular file is valid */
> > > > - if (file_thp_enabled(vma))
> > > > + if (!in_pf && file_thp_enabled(vma))
> > > > return true;
> > >
> > > Likewise for file_thp_enabled()
> >
> > Yes, same as the above.

Ditto.

> > >
> > > > if (!vma_is_anonymous(vma))
> > > > @@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > /*
> > > > * THPeligible bit of smaps should show 1 for proper VMAs even
> > > > * though anon_vma is not initialized yet.
> > > > + *
> > > > + * Allow page fault since anon_vma may be not initialized until
> > > > + * the first page fault.
> > > > */
> > > > if (!vma->anon_vma)
> > > > - return smaps;
> > > > + return (smaps || in_pf);
> > > >
> > > > return true;
> > > > }
> > > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > > index aa0769e3b0d9..ab6183c5489f 100644
> > > > --- a/mm/khugepaged.c
> > > > +++ b/mm/khugepaged.c
> > > > @@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > > {
> > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > khugepaged_enabled()) {
> > > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > > __khugepaged_enter(vma->vm_mm);
> > > > }
> > > > }
> > > > @@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
> > > >
> > > > if (!transhuge_vma_suitable(vma, address))
> > > > return SCAN_ADDRESS_RANGE;
> > > > - if (!hugepage_vma_check(vma, vma->vm_flags, false))
> > > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
> > > > return SCAN_VMA_CHECK;
> > > > return 0;
> > > > }
> > > > @@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
> > > > * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
> > > > * will not fail the vma for missing VM_HUGEPAGE
> > > > */
> > > > - if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
> > > > + if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
> > > > return;
> > > >
> > > > /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
> > > > @@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > > progress++;
> > > > break;
> > > > }
> > > > - if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
> > > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
> > > > skip:
> > > > progress++;
> > > > continue;
> > > > diff --git a/mm/memory.c b/mm/memory.c
> > > > index bc5d40eec5d5..673f7561a30a 100644
> > > > --- a/mm/memory.c
> > > > +++ b/mm/memory.c
> > > > @@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > .gfp_mask = __get_fault_gfp_mask(vma),
> > > > };
> > > > struct mm_struct *mm = vma->vm_mm;
> > > > + unsigned long vm_flags = vma->vm_flags;
> > > > pgd_t *pgd;
> > > > p4d_t *p4d;
> > > > vm_fault_t ret;
> > > > @@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > if (!vmf.pud)
> > > > return VM_FAULT_OOM;
> > > > retry_pud:
> > > > - if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
> > > > + if (pud_none(*vmf.pud) &&
> > > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > > ret = create_huge_pud(&vmf);
> > > > if (!(ret & VM_FAULT_FALLBACK))
> > > > return ret;
> > > > @@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > if (pud_trans_unstable(vmf.pud))
> > > > goto retry_pud;
> > > >
> > > > - if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
> > > > + if (pmd_none(*vmf.pmd) &&
> > > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > > ret = create_huge_pmd(&vmf);
> > > > if (!(ret & VM_FAULT_FALLBACK))
> > > > return ret;
> > > > --
> > > > 2.26.3
> > > >
> > > >

2022-06-14 19:15:47

by Yang Shi

[permalink] [raw]
Subject: Re: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

On Mon, Jun 13, 2022 at 7:54 AM Zach O'Keefe <[email protected]> wrote:
>
> On 10 Jun 14:07, Yang Shi wrote:
> > On Fri, Jun 10, 2022 at 10:24 AM Yang Shi <[email protected]> wrote:
> > >
> > > On Thu, Jun 9, 2022 at 7:22 PM Zach O'Keefe <[email protected]> wrote:
> > > >
> > > > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > > > >
> > > > > The page fault path checks THP eligibility with
> > > > > __transhuge_page_enabled() which does the similar thing as
> > > > > hugepage_vma_check(), so use hugepage_vma_check() instead.
> > > > >
> > > > > However page fault allows DAX and !anon_vma cases, so added a new flag,
> > > > > in_pf, to hugepage_vma_check() to make page fault work correctly.
> > > > >
> > > > > The in_pf flag is also used to skip shmem and file THP for page fault
> > > > > since shmem handles THP in its own shmem_fault() and file THP allocation
> > > > > on fault is not supported yet.
> > > > >
> > > > > Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
> > > > > only caller now, it is not necessary to have a helper function.
> > > > >
> > > > > Signed-off-by: Yang Shi <[email protected]>
> > > > > ---
> > > > > fs/proc/task_mmu.c | 2 +-
> > > > > include/linux/huge_mm.h | 57 ++------------------------------------
> > > > > include/linux/khugepaged.h | 2 +-
> > > > > mm/huge_memory.c | 25 ++++++++++++-----
> > > > > mm/khugepaged.c | 8 +++---
> > > > > mm/memory.c | 7 +++--
> > > > > 6 files changed, 31 insertions(+), 70 deletions(-)
> > > > >
> > > > > diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> > > > > index fd79566e204c..a0850303baec 100644
> > > > > --- a/fs/proc/task_mmu.c
> > > > > +++ b/fs/proc/task_mmu.c
> > > > > @@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
> > > > > __show_smap(m, &mss, false);
> > > > >
> > > > > seq_printf(m, "THPeligible: %d\n",
> > > > > - hugepage_vma_check(vma, vma->vm_flags, true));
> > > > > + hugepage_vma_check(vma, vma->vm_flags, true, false));
> > > > >
> > > > > if (arch_pkeys_enabled())
> > > > > seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
> > > > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > > > index f561c3e16def..d478e8875023 100644
> > > > > --- a/include/linux/huge_mm.h
> > > > > +++ b/include/linux/huge_mm.h
> > > > > @@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > > return true;
> > > > > }
> > > > >
> > > > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > > > - unsigned long vm_flags)
> > > > > -{
> > > > > - /* Explicitly disabled through madvise. */
> > > > > - if ((vm_flags & VM_NOHUGEPAGE) ||
> > > > > - test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > > > - return false;
> > > > > - return true;
> > > > > -}
> > > > > -
> > > > > -/*
> > > > > - * to be used on vmas which are known to support THP.
> > > > > - * Use transparent_hugepage_active otherwise
> > > > > - */
> > > > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > > > -{
> > > > > -
> > > > > - /*
> > > > > - * If the hardware/firmware marked hugepage support disabled.
> > > > > - */
> > > > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > > > - return false;
> > > > > -
> > > > > - if (!transhuge_vma_enabled(vma, vma->vm_flags))
> > > > > - return false;
> > > > > -
> > > > > - if (vma_is_temporary_stack(vma))
> > > > > - return false;
> > > > > -
> > > > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
> > > > > - return true;
> > > > > -
> > > > > - if (vma_is_dax(vma))
> > > > > - return true;
> > > > > -
> > > > > - if (transparent_hugepage_flags &
> > > > > - (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
> > > > > - return !!(vma->vm_flags & VM_HUGEPAGE);
> > > > > -
> > > > > - return false;
> > > > > -}
> > > > > -
> > > > > static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > > > {
> > > > > struct inode *inode;
> > > > > @@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > > >
> > > > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > unsigned long vm_flags,
> > > > > - bool smaps);
> > > > > + bool smaps, bool in_pf);
> > > > >
> > > > > #define transparent_hugepage_use_zero_page() \
> > > > > (transparent_hugepage_flags & \
> > > > > @@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
> > > > > return false;
> > > > > }
> > > > >
> > > > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > > > -{
> > > > > - return false;
> > > > > -}
> > > > > -
> > > > > static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > > {
> > > > > return false;
> > > > > @@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > > return false;
> > > > > }
> > > > >
> > > > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > > > - unsigned long vm_flags)
> > > > > -{
> > > > > - return false;
> > > > > -}
> > > > > -
> > > > > static inline bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > unsigned long vm_flags,
> > > > > - bool smaps)
> > > > > + bool smaps, bool in_pf)
> > > > > {
> > > > > return false;
> > > > > }
> > > > > diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
> > > > > index 8a6452e089ca..e047be601268 100644
> > > > > --- a/include/linux/khugepaged.h
> > > > > +++ b/include/linux/khugepaged.h
> > > > > @@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
> > > > > {
> > > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > > khugepaged_enabled()) {
> > > > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > > > __khugepaged_enter(vma->vm_mm);
> > > > > }
> > > > > }
> > > > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > > > index bc8370856e85..b95786ada466 100644
> > > > > --- a/mm/huge_memory.c
> > > > > +++ b/mm/huge_memory.c
> > > > > @@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > > > >
> > > > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > unsigned long vm_flags,
> > > > > - bool smaps)
> > > > > + bool smaps, bool in_pf)
> > > > > {
> > > > > - if (!transhuge_vma_enabled(vma, vm_flags))
> > > > > + /* Explicitly disabled through madvise or prctl. */
> > > >
> > > > Or s390 kvm (not that this has to be exhaustively maintained).
> > > >
> > > > > + if ((vm_flags & VM_NOHUGEPAGE) ||
> > > > > + test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > > > + return false;
> > > > > + /*
> > > > > + * If the hardware/firmware marked hugepage support disabled.
> > > > > + */
> > > > > + if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > > > return false;
> > > >
> > > > This introduces an extra check for khugepaged path. I don't know
> > > > enough about TRANSPARENT_HUGEPAGE_NEVER_DAX, but I assume this is ok?
> > > > What would have happened previously if khugepaged tried to collapse
> > > > this memory?
> > >
> > > Please refer to commit bae849538157 ("mm/pmem: avoid inserting
> > > hugepage PTE entry with fsdax if hugepage support is disabled") for
> > > why this flag was introduced.
> > >
> > > It is set if hardware doesn't support hugepages, and khugepaged
> > > doesn't collapse since khugepaged won't be started at all.
> > >
> > > But this flag needs to be checked in the page fault path.
> > >
>
> Thanks for the ref to the commit. I'm not sure I understand it in its entirety,
> but at least I can tell khugepaged won't be started :)
>
> > > >
> > > > > + /* Special VMA and hugetlb VMA */
> > > > > if (vm_flags & VM_NO_KHUGEPAGED)
> > > > > return false;
> > > >
> > > > This adds an extra check along the fault path. Is it also safe to add?
> > >
> > > I think it is safe since hugepage_vma_check() is just used by THP.
> > > Hugetlb has its own page fault handler.
> >
> > I just found one exception. The fuse dax has VM_MIXEDMAP set for its
> > vmas, so this check should be moved after vma_is_dax() check.
> >
> > AFAICT, only dax supports huge_fault() and dax vmas don't have any
> > VM_SPECIAL flags set other than fuse.
> >
>
> Ordering wrt VM_NO_KHUGEPAGED check seems fine. We could always use in_pf to opt
> out of this check, but I think itemizing where collapse and fault paths are
> different would be good.

Maybe using "in_pf" is easier to follow? Depending on the order of
check seems subtle although we already did so for shmem (shmem check
must be done before hugepage flags check).

>
> > >
> > > >
> > > > > - /* Don't run khugepaged against DAX vma */
> > > > > + /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
> > > > > if (vma_is_dax(vma))
> > > > > - return false;
> > > > > + return in_pf;
> > > >
> > > > I assume vma_is_temporary_stack() and vma_is_dax() is mutually exclusive.
> > >
> > > I think so.
> > >
> > > >
> > > > > if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
> > > > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > > > @@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > return false;
> > > > >
> > > > > /* Enabled via shmem mount options or sysfs settings. */
> > > > > - if (shmem_file(vma->vm_file))
> > > > > + if (!in_pf && shmem_file(vma->vm_file))
> > > > > return shmem_huge_enabled(vma);
> > > >
> > > > Will shmem_file() ever be true in the fault path? Or is this just an
> > > > optimization?
> > >
> > > It could be true. But shmem has its own implementation for huge page
> > > fault and doesn't implement huge_fault() in its vm_operations, so it
> > > will fallback even though "in_pf" is not checked.
> > >
> > > But xfs does have huge_fault() implemented, so it may try to allocate
> > > THP for non-DAX xfs files. So the "in_pf" flag is introduced to handle
> > > this. Since we need this flag anyway, why not use it to return earlier
> > > for shmem instead of relying on fallback.
> > >
> > > Anyway this is all because __transparent_huge_enabled() is replaced by
> > > hugepage_vma_check().
> > >
>
> Thanks for the explanation. Admittedly I don't fully understand the involvement
> of xfs in the shmem case, but general "fail early" logic seems fine to me.
> > > > > if (!khugepaged_enabled())
> > > > > @@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > return false;
> > > > >
> > > > > /* Only regular file is valid */
> > > > > - if (file_thp_enabled(vma))
> > > > > + if (!in_pf && file_thp_enabled(vma))
> > > > > return true;
> > > >
> > > > Likewise for file_thp_enabled()
> > >
> > > Yes, same as the above.
>
> Ditto.
>
> > > >
> > > > > if (!vma_is_anonymous(vma))
> > > > > @@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > /*
> > > > > * THPeligible bit of smaps should show 1 for proper VMAs even
> > > > > * though anon_vma is not initialized yet.
> > > > > + *
> > > > > + * Allow page fault since anon_vma may be not initialized until
> > > > > + * the first page fault.
> > > > > */
> > > > > if (!vma->anon_vma)
> > > > > - return smaps;
> > > > > + return (smaps || in_pf);
> > > > >
> > > > > return true;
> > > > > }
> > > > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > > > index aa0769e3b0d9..ab6183c5489f 100644
> > > > > --- a/mm/khugepaged.c
> > > > > +++ b/mm/khugepaged.c
> > > > > @@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > > > {
> > > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > > khugepaged_enabled()) {
> > > > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > > > __khugepaged_enter(vma->vm_mm);
> > > > > }
> > > > > }
> > > > > @@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
> > > > >
> > > > > if (!transhuge_vma_suitable(vma, address))
> > > > > return SCAN_ADDRESS_RANGE;
> > > > > - if (!hugepage_vma_check(vma, vma->vm_flags, false))
> > > > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
> > > > > return SCAN_VMA_CHECK;
> > > > > return 0;
> > > > > }
> > > > > @@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
> > > > > * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
> > > > > * will not fail the vma for missing VM_HUGEPAGE
> > > > > */
> > > > > - if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
> > > > > + if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
> > > > > return;
> > > > >
> > > > > /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
> > > > > @@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > > > progress++;
> > > > > break;
> > > > > }
> > > > > - if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
> > > > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
> > > > > skip:
> > > > > progress++;
> > > > > continue;
> > > > > diff --git a/mm/memory.c b/mm/memory.c
> > > > > index bc5d40eec5d5..673f7561a30a 100644
> > > > > --- a/mm/memory.c
> > > > > +++ b/mm/memory.c
> > > > > @@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > > .gfp_mask = __get_fault_gfp_mask(vma),
> > > > > };
> > > > > struct mm_struct *mm = vma->vm_mm;
> > > > > + unsigned long vm_flags = vma->vm_flags;
> > > > > pgd_t *pgd;
> > > > > p4d_t *p4d;
> > > > > vm_fault_t ret;
> > > > > @@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > > if (!vmf.pud)
> > > > > return VM_FAULT_OOM;
> > > > > retry_pud:
> > > > > - if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
> > > > > + if (pud_none(*vmf.pud) &&
> > > > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > > > ret = create_huge_pud(&vmf);
> > > > > if (!(ret & VM_FAULT_FALLBACK))
> > > > > return ret;
> > > > > @@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > > if (pud_trans_unstable(vmf.pud))
> > > > > goto retry_pud;
> > > > >
> > > > > - if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
> > > > > + if (pmd_none(*vmf.pmd) &&
> > > > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > > > ret = create_huge_pmd(&vmf);
> > > > > if (!(ret & VM_FAULT_FALLBACK))
> > > > > return ret;
> > > > > --
> > > > > 2.26.3
> > > > >
> > > > >

2022-06-15 00:14:42

by Zach O'Keefe

[permalink] [raw]
Subject: Re: [v3 PATCH 6/7] mm: thp: kill __transhuge_page_enabled()

On 14 Jun 11:51, Yang Shi wrote:
> On Mon, Jun 13, 2022 at 7:54 AM Zach O'Keefe <[email protected]> wrote:
> >
> > On 10 Jun 14:07, Yang Shi wrote:
> > > On Fri, Jun 10, 2022 at 10:24 AM Yang Shi <[email protected]> wrote:
> > > >
> > > > On Thu, Jun 9, 2022 at 7:22 PM Zach O'Keefe <[email protected]> wrote:
> > > > >
> > > > > On Mon, Jun 6, 2022 at 2:44 PM Yang Shi <[email protected]> wrote:
> > > > > >
> > > > > > The page fault path checks THP eligibility with
> > > > > > __transhuge_page_enabled() which does the similar thing as
> > > > > > hugepage_vma_check(), so use hugepage_vma_check() instead.
> > > > > >
> > > > > > However page fault allows DAX and !anon_vma cases, so added a new flag,
> > > > > > in_pf, to hugepage_vma_check() to make page fault work correctly.
> > > > > >
> > > > > > The in_pf flag is also used to skip shmem and file THP for page fault
> > > > > > since shmem handles THP in its own shmem_fault() and file THP allocation
> > > > > > on fault is not supported yet.
> > > > > >
> > > > > > Also remove hugepage_vma_enabled() since hugepage_vma_check() is the
> > > > > > only caller now, it is not necessary to have a helper function.
> > > > > >
> > > > > > Signed-off-by: Yang Shi <[email protected]>
> > > > > > ---
> > > > > > fs/proc/task_mmu.c | 2 +-
> > > > > > include/linux/huge_mm.h | 57 ++------------------------------------
> > > > > > include/linux/khugepaged.h | 2 +-
> > > > > > mm/huge_memory.c | 25 ++++++++++++-----
> > > > > > mm/khugepaged.c | 8 +++---
> > > > > > mm/memory.c | 7 +++--
> > > > > > 6 files changed, 31 insertions(+), 70 deletions(-)
> > > > > >
> > > > > > diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> > > > > > index fd79566e204c..a0850303baec 100644
> > > > > > --- a/fs/proc/task_mmu.c
> > > > > > +++ b/fs/proc/task_mmu.c
> > > > > > @@ -860,7 +860,7 @@ static int show_smap(struct seq_file *m, void *v)
> > > > > > __show_smap(m, &mss, false);
> > > > > >
> > > > > > seq_printf(m, "THPeligible: %d\n",
> > > > > > - hugepage_vma_check(vma, vma->vm_flags, true));
> > > > > > + hugepage_vma_check(vma, vma->vm_flags, true, false));
> > > > > >
> > > > > > if (arch_pkeys_enabled())
> > > > > > seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
> > > > > > diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> > > > > > index f561c3e16def..d478e8875023 100644
> > > > > > --- a/include/linux/huge_mm.h
> > > > > > +++ b/include/linux/huge_mm.h
> > > > > > @@ -153,48 +153,6 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > > > return true;
> > > > > > }
> > > > > >
> > > > > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > > > > - unsigned long vm_flags)
> > > > > > -{
> > > > > > - /* Explicitly disabled through madvise. */
> > > > > > - if ((vm_flags & VM_NOHUGEPAGE) ||
> > > > > > - test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > > > > - return false;
> > > > > > - return true;
> > > > > > -}
> > > > > > -
> > > > > > -/*
> > > > > > - * to be used on vmas which are known to support THP.
> > > > > > - * Use transparent_hugepage_active otherwise
> > > > > > - */
> > > > > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > > > > -{
> > > > > > -
> > > > > > - /*
> > > > > > - * If the hardware/firmware marked hugepage support disabled.
> > > > > > - */
> > > > > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > > > > - return false;
> > > > > > -
> > > > > > - if (!transhuge_vma_enabled(vma, vma->vm_flags))
> > > > > > - return false;
> > > > > > -
> > > > > > - if (vma_is_temporary_stack(vma))
> > > > > > - return false;
> > > > > > -
> > > > > > - if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_FLAG))
> > > > > > - return true;
> > > > > > -
> > > > > > - if (vma_is_dax(vma))
> > > > > > - return true;
> > > > > > -
> > > > > > - if (transparent_hugepage_flags &
> > > > > > - (1 << TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG))
> > > > > > - return !!(vma->vm_flags & VM_HUGEPAGE);
> > > > > > -
> > > > > > - return false;
> > > > > > -}
> > > > > > -
> > > > > > static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > > > > {
> > > > > > struct inode *inode;
> > > > > > @@ -211,7 +169,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma)
> > > > > >
> > > > > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > > unsigned long vm_flags,
> > > > > > - bool smaps);
> > > > > > + bool smaps, bool in_pf);
> > > > > >
> > > > > > #define transparent_hugepage_use_zero_page() \
> > > > > > (transparent_hugepage_flags & \
> > > > > > @@ -355,11 +313,6 @@ static inline bool folio_test_pmd_mappable(struct folio *folio)
> > > > > > return false;
> > > > > > }
> > > > > >
> > > > > > -static inline bool __transparent_hugepage_enabled(struct vm_area_struct *vma)
> > > > > > -{
> > > > > > - return false;
> > > > > > -}
> > > > > > -
> > > > > > static inline bool transhuge_vma_size_ok(struct vm_area_struct *vma)
> > > > > > {
> > > > > > return false;
> > > > > > @@ -371,15 +324,9 @@ static inline bool transhuge_vma_suitable(struct vm_area_struct *vma,
> > > > > > return false;
> > > > > > }
> > > > > >
> > > > > > -static inline bool transhuge_vma_enabled(struct vm_area_struct *vma,
> > > > > > - unsigned long vm_flags)
> > > > > > -{
> > > > > > - return false;
> > > > > > -}
> > > > > > -
> > > > > > static inline bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > > unsigned long vm_flags,
> > > > > > - bool smaps)
> > > > > > + bool smaps, bool in_pf)
> > > > > > {
> > > > > > return false;
> > > > > > }
> > > > > > diff --git a/include/linux/khugepaged.h b/include/linux/khugepaged.h
> > > > > > index 8a6452e089ca..e047be601268 100644
> > > > > > --- a/include/linux/khugepaged.h
> > > > > > +++ b/include/linux/khugepaged.h
> > > > > > @@ -55,7 +55,7 @@ static inline void khugepaged_enter(struct vm_area_struct *vma,
> > > > > > {
> > > > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > > > khugepaged_enabled()) {
> > > > > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > > > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > > > > __khugepaged_enter(vma->vm_mm);
> > > > > > }
> > > > > > }
> > > > > > diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> > > > > > index bc8370856e85..b95786ada466 100644
> > > > > > --- a/mm/huge_memory.c
> > > > > > +++ b/mm/huge_memory.c
> > > > > > @@ -71,17 +71,25 @@ unsigned long huge_zero_pfn __read_mostly = ~0UL;
> > > > > >
> > > > > > bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > > unsigned long vm_flags,
> > > > > > - bool smaps)
> > > > > > + bool smaps, bool in_pf)
> > > > > > {
> > > > > > - if (!transhuge_vma_enabled(vma, vm_flags))
> > > > > > + /* Explicitly disabled through madvise or prctl. */
> > > > >
> > > > > Or s390 kvm (not that this has to be exhaustively maintained).
> > > > >
> > > > > > + if ((vm_flags & VM_NOHUGEPAGE) ||
> > > > > > + test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags))
> > > > > > + return false;
> > > > > > + /*
> > > > > > + * If the hardware/firmware marked hugepage support disabled.
> > > > > > + */
> > > > > > + if (transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_NEVER_DAX))
> > > > > > return false;
> > > > >
> > > > > This introduces an extra check for khugepaged path. I don't know
> > > > > enough about TRANSPARENT_HUGEPAGE_NEVER_DAX, but I assume this is ok?
> > > > > What would have happened previously if khugepaged tried to collapse
> > > > > this memory?
> > > >
> > > > Please refer to commit bae849538157 ("mm/pmem: avoid inserting
> > > > hugepage PTE entry with fsdax if hugepage support is disabled") for
> > > > why this flag was introduced.
> > > >
> > > > It is set if hardware doesn't support hugepages, and khugepaged
> > > > doesn't collapse since khugepaged won't be started at all.
> > > >
> > > > But this flag needs to be checked in the page fault path.
> > > >
> >
> > Thanks for the ref to the commit. I'm not sure I understand it in its entirety,
> > but at least I can tell khugepaged won't be started :)
> >
> > > > >
> > > > > > + /* Special VMA and hugetlb VMA */
> > > > > > if (vm_flags & VM_NO_KHUGEPAGED)
> > > > > > return false;
> > > > >
> > > > > This adds an extra check along the fault path. Is it also safe to add?
> > > >
> > > > I think it is safe since hugepage_vma_check() is just used by THP.
> > > > Hugetlb has its own page fault handler.
> > >
> > > I just found one exception. The fuse dax has VM_MIXEDMAP set for its
> > > vmas, so this check should be moved after vma_is_dax() check.
> > >
> > > AFAICT, only dax supports huge_fault() and dax vmas don't have any
> > > VM_SPECIAL flags set other than fuse.
> > >
> >
> > Ordering wrt VM_NO_KHUGEPAGED check seems fine. We could always use in_pf to opt
> > out of this check, but I think itemizing where collapse and fault paths are
> > different would be good.
>
> Maybe using "in_pf" is easier to follow? Depending on the order of
> check seems subtle although we already did so for shmem (shmem check
> must be done before hugepage flags check).

Ya, I think the subtleties wrt ordering is getting tricky. I'm OK either way -
but we should document some of these ordering subtleties less someone
inadvertently changes something in the future and has to rediscover e.g. that
particular VM_MIXEDMAP case.

> >
> > > >
> > > > >
> > > > > > - /* Don't run khugepaged against DAX vma */
> > > > > > + /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
> > > > > > if (vma_is_dax(vma))
> > > > > > - return false;
> > > > > > + return in_pf;
> > > > >
> > > > > I assume vma_is_temporary_stack() and vma_is_dax() is mutually exclusive.
> > > >
> > > > I think so.
> > > >
> > > > >
> > > > > > if (vma->vm_file && !IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) -
> > > > > > vma->vm_pgoff, HPAGE_PMD_NR))
> > > > > > @@ -91,7 +99,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > > return false;
> > > > > >
> > > > > > /* Enabled via shmem mount options or sysfs settings. */
> > > > > > - if (shmem_file(vma->vm_file))
> > > > > > + if (!in_pf && shmem_file(vma->vm_file))
> > > > > > return shmem_huge_enabled(vma);
> > > > >
> > > > > Will shmem_file() ever be true in the fault path? Or is this just an
> > > > > optimization?
> > > >
> > > > It could be true. But shmem has its own implementation for huge page
> > > > fault and doesn't implement huge_fault() in its vm_operations, so it
> > > > will fallback even though "in_pf" is not checked.
> > > >
> > > > But xfs does have huge_fault() implemented, so it may try to allocate
> > > > THP for non-DAX xfs files. So the "in_pf" flag is introduced to handle
> > > > this. Since we need this flag anyway, why not use it to return earlier
> > > > for shmem instead of relying on fallback.
> > > >
> > > > Anyway this is all because __transparent_huge_enabled() is replaced by
> > > > hugepage_vma_check().
> > > >
> >
> > Thanks for the explanation. Admittedly I don't fully understand the involvement
> > of xfs in the shmem case, but general "fail early" logic seems fine to me.
> > > > > > if (!khugepaged_enabled())
> > > > > > @@ -102,7 +110,7 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > > return false;
> > > > > >
> > > > > > /* Only regular file is valid */
> > > > > > - if (file_thp_enabled(vma))
> > > > > > + if (!in_pf && file_thp_enabled(vma))
> > > > > > return true;
> > > > >
> > > > > Likewise for file_thp_enabled()
> > > >
> > > > Yes, same as the above.
> >
> > Ditto.
> >
> > > > >
> > > > > > if (!vma_is_anonymous(vma))
> > > > > > @@ -114,9 +122,12 @@ bool hugepage_vma_check(struct vm_area_struct *vma,
> > > > > > /*
> > > > > > * THPeligible bit of smaps should show 1 for proper VMAs even
> > > > > > * though anon_vma is not initialized yet.
> > > > > > + *
> > > > > > + * Allow page fault since anon_vma may be not initialized until
> > > > > > + * the first page fault.
> > > > > > */
> > > > > > if (!vma->anon_vma)
> > > > > > - return smaps;
> > > > > > + return (smaps || in_pf);
> > > > > >
> > > > > > return true;
> > > > > > }
> > > > > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > > > > > index aa0769e3b0d9..ab6183c5489f 100644
> > > > > > --- a/mm/khugepaged.c
> > > > > > +++ b/mm/khugepaged.c
> > > > > > @@ -473,7 +473,7 @@ void khugepaged_enter_vma(struct vm_area_struct *vma,
> > > > > > {
> > > > > > if (!test_bit(MMF_VM_HUGEPAGE, &vma->vm_mm->flags) &&
> > > > > > khugepaged_enabled()) {
> > > > > > - if (hugepage_vma_check(vma, vm_flags, false))
> > > > > > + if (hugepage_vma_check(vma, vm_flags, false, false))
> > > > > > __khugepaged_enter(vma->vm_mm);
> > > > > > }
> > > > > > }
> > > > > > @@ -918,7 +918,7 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
> > > > > >
> > > > > > if (!transhuge_vma_suitable(vma, address))
> > > > > > return SCAN_ADDRESS_RANGE;
> > > > > > - if (!hugepage_vma_check(vma, vma->vm_flags, false))
> > > > > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false))
> > > > > > return SCAN_VMA_CHECK;
> > > > > > return 0;
> > > > > > }
> > > > > > @@ -1399,7 +1399,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr)
> > > > > > * the valid THP. Add extra VM_HUGEPAGE so hugepage_vma_check()
> > > > > > * will not fail the vma for missing VM_HUGEPAGE
> > > > > > */
> > > > > > - if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false))
> > > > > > + if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE, false, false))
> > > > > > return;
> > > > > >
> > > > > > /* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
> > > > > > @@ -2089,7 +2089,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages,
> > > > > > progress++;
> > > > > > break;
> > > > > > }
> > > > > > - if (!hugepage_vma_check(vma, vma->vm_flags, false)) {
> > > > > > + if (!hugepage_vma_check(vma, vma->vm_flags, false, false)) {
> > > > > > skip:
> > > > > > progress++;
> > > > > > continue;
> > > > > > diff --git a/mm/memory.c b/mm/memory.c
> > > > > > index bc5d40eec5d5..673f7561a30a 100644
> > > > > > --- a/mm/memory.c
> > > > > > +++ b/mm/memory.c
> > > > > > @@ -4962,6 +4962,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > > > .gfp_mask = __get_fault_gfp_mask(vma),
> > > > > > };
> > > > > > struct mm_struct *mm = vma->vm_mm;
> > > > > > + unsigned long vm_flags = vma->vm_flags;
> > > > > > pgd_t *pgd;
> > > > > > p4d_t *p4d;
> > > > > > vm_fault_t ret;
> > > > > > @@ -4975,7 +4976,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > > > if (!vmf.pud)
> > > > > > return VM_FAULT_OOM;
> > > > > > retry_pud:
> > > > > > - if (pud_none(*vmf.pud) && __transparent_hugepage_enabled(vma)) {
> > > > > > + if (pud_none(*vmf.pud) &&
> > > > > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > > > > ret = create_huge_pud(&vmf);
> > > > > > if (!(ret & VM_FAULT_FALLBACK))
> > > > > > return ret;
> > > > > > @@ -5008,7 +5010,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
> > > > > > if (pud_trans_unstable(vmf.pud))
> > > > > > goto retry_pud;
> > > > > >
> > > > > > - if (pmd_none(*vmf.pmd) && __transparent_hugepage_enabled(vma)) {
> > > > > > + if (pmd_none(*vmf.pmd) &&
> > > > > > + hugepage_vma_check(vma, vm_flags, false, true)) {
> > > > > > ret = create_huge_pmd(&vmf);
> > > > > > if (!(ret & VM_FAULT_FALLBACK))
> > > > > > return ret;
> > > > > > --
> > > > > > 2.26.3
> > > > > >
> > > > > >