Skip to contentSkip to conversation

mm: thp: always enable mTHP support

PATCH In review Luiz Capitulino 7 participants 52 messages Sep 18 – Oct 8

Subsystems (13)
lore
Share & tools
lore
#0 lore raw

v8 cover mm: thp: always enable mTHP support

Introduction
============

Today, if an architecture implements has_transparent_hugepage() and the CPU lacks support for PMD-sized pages, the THP code disables all THP, including mTHP.

This happens because the has_transparent_hugepage() helper is overloaded:
its name implies it checks whether THP is enabled, but on some architectures it actually checks whether the CPU supports PMD-sized pages. In addition, the THP and shmem code have a big switch on has_transparent_hugepage().

This series solves this by decoupling THP availability checking from querying CPU support for PMD-sized pages. THP availability can be checked with IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE). For querying PMD-sized page support, this series introduces a new helper called pgtable_has_pmd_leaves(), which is independent of THP, has well defined semantics and can be used in fast paths.

Core THP code and shmem can use pgtable_has_pmd_leaves() to determine PMD-sized THP support at page fault time (or folio allocation time for shmem), leaving THP and shmem always enabled for other THP sizes. For architectures and CPUs that do support PMD-sized pages there's no intended change in behavior.

We also convert each user of has_transparent_hugepage(), and the related helper thp_disabled_by_hw(), to IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) and/or pgtable_has_pmd_leaves() according to the call site's check semantics. On s390, powerpc, mips and x86 the arch has_transparent_hugepage() implementation is moved out of the CONFIG_TRANSPARENT_HUGEPAGE guard and renamed to arch_has_pmd_leaves().

Thanks to David Hildenbrand for suggesting this improvement and for providing initial guidance (all bugs and misconceptions are mine).

This applies to mm-new 93f615a22169 ("mm/swap, PM: hibernate: atomically replace hibernation pin").

Reporting availability of PMD-sized pages to user-space
=======================================================

Before this series, not having PMD pages available would shut down THP support meaning that /sys/kernel/mm/transparent_hugepage would not be available. After this series, /sys/kernel/mm/transparent_hugepage and hpage_pmd_size are always available but hugepages-<PMD-size>kB is only available if PMD-sized pages are supported.

To me this seems logical, as hugepages-<size>kB is only available if <size> is supported. If this is correct, then MM kselftests that depend on PMD-sized pages will need to be updated to check for it as they fail with this series applied when PMD-sized pages are not available.

The alternative is changing hpage_pmd_size: either don't create the file when PMD-sized pages are not available or set hpage_pmd_size=0. My concern is that this could be considered an ABI breakage as this file was never reported to be optional or report a zero value.

Testing
=======

- Ran MM kselftests on x86_64 and s390
- Tested all mTHP sizes allocation on x86_64 (with and without PMD-sized pages available)
- Tested shmem with within_size w/ mTHP on x86_64 (with and without PMD-sized pages available)
- Performed defconfig build on x86_64, s390, arm64, powerpc, and mips

NOTES:
* I'm forcing arch_has_pmd_leaves() off on x86 to simulate not having PMD-sized pages available

* Running the MM kselftests when PMD pages are not available causes some tests that depend on PMD-sized THP pages to fail as noted earlier

Changelog
=========

v8
--
- Fixed build breakage on MIPS (Lance)
- Rebased on top of mm-new (fixed conflicts in include/linux/pgtable.h and mm/shmem.c - dropped a Reviewed-by as a result)

v7
--
- Applied on top of latest mm-new
- Improved various changelogs including cover-letter
- Changed arch_has_pmd_leaves() default implementation to use IS_ENABLED() instead of IS_BUILTIN()

v6
--
- Rebased on top of mm-unstable (required a minor conflict resolution)
- Added more Reviewed-by and Acked-by tags

v5
--
- Moved init_arch_has_pmd_leaves() to mm_core_init() (David)
- Renamed init_arch_has_pmd_leaves() to pgtable_leaf_support_init() (Lance)
- Added new patch changing shmem_getattr() to set blksize according to highest supported THP order (Baolin)
- Added new patches to move has_transparent_hugepage() out of CONFIG_TRANSPARENT_HUGEPAGE guards
- shmem_allowable_huge_orders(): rename variable and only disable PMD_ORDER (David)
- Added <linux/jmp_label.h> to include/linux/pgtable.h
- Added new tags and removed Reviewed-by from changed patches

v4
--
- Used static key for pgtable_has_pmd_leaves() API (Lance)
- Moved shmem pgtable_has_pmd_leaves() check to shmem_allowable_huge_orders() (Baolin)
- Default pgtable_has_pmd_leaves() implementation to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) (Zi)
- Dropped patch “mm: thp: x86: cleanup PSE feature bit usage” (Dave)

v3
--
- Rebased on top of latest Linus tree
- Removed i915 patch as driver dropped has_transparent_hugepage() usage
- Moved init_arch_has_pmd_leaves() call in start_kernel() to avoid conflict with early_param handlers clearing CPU feature flags
- Fixed build error with CONFIG_MMU=n (kernel test robot)
- Fixed huge_anon_orders_inherit default setting when !pgtable_has_pmd_leaves() (Baolin)
- Small commit changelog improvements

v2
--
- Added support for always enabling mTHPs for shmem (Baolin)
- Improved commits changelog & added reviewed-by

v1
--
- Call init_arch_has_pmd_leaves() from start_kernel()
- Keep pgtable_has_pmd_leaves() calls tied to CONFIG_TRANSPARENT_HUGEPAGE (David)
- Clear PUD_ORDER when clearing PMD_ORDER (David)
- Small changelog improvements (David)
- Rebased on top of latest mm-new

Luiz Capitulino (14):
docs: tmpfs: remove implementation detail reference
mm: shmem: shmem_getattr(): set blksize to highest supported THP order
mm: introduce pgtable_has_pmd_leaves()
drivers: dax: use pgtable_has_pmd_leaves()
drivers: nvdimm: use pgtable_has_pmd_leaves()
mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()
mm: shmem: allow THP support determination at folio allocation time
s390: move has_transparent_hugepage() out of THP guard
powerpc: move has_transparent_hugepage() out of THP guard
mips: move has_transparent_hugepage() out of THP guard
x86: move has_transparent_hugepage() out of THP guard
treewide: introduce arch_has_pmd_leaves()
mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()
mm: thp: always enable mTHP support

 Documentation/filesystems/tmpfs.rst           |  5 ++--
 arch/mips/include/asm/pgtable.h               |  6 ++--
 arch/mips/mm/pgtable.c                        | 25 ++++++++++++++++
 arch/mips/mm/tlb-r4k.c                        | 22 --------------
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
 arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
 arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 ++++++------
 arch/powerpc/include/asm/book3s/64/radix.h    | 14 ++++-----
 arch/powerpc/mm/book3s64/hash_pgtable.c       |  8 ++---
 arch/s390/include/asm/pgtable.h               |  6 ++--
 arch/x86/include/asm/pgtable.h                | 12 ++++----
 drivers/dax/dax-private.h                     |  2 +-
 drivers/nvdimm/pfn_devs.c                     |  6 ++--
 include/linux/huge_mm.h                       |  7 -----
 include/linux/pgtable.h                       | 21 ++++++++++++--
 mm/debug_vm_pgtable.c                         | 20 ++++++-------
 mm/huge_memory.c                              | 27 ++++++++++++-----
 mm/memory.c                                   | 11 ++++++-
 mm/mm_init.c                                  |  1 +
 mm/shmem.c                                    | 29 ++++++++++++-------
 20 files changed, 144 insertions(+), 100 deletions(-)
Signature · 1 line
2.55.0
#1 lore raw

PATCH v8 1/14 docs: tmpfs: remove implementation detail reference

The tmpfs.rst doc references the has_transparent_hugepage() helper, which is an implementation detail in the kernel and not relevant for users wishing to properly configure THP support for tmpfs. Remove it.

---
 Documentation/filesystems/tmpfs.rst | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
Documentation/filesystems/tmpfs.rst +2 -3
index d677e0428c3f..46fc986c3388 100644
@@ -109,9 +109,8 @@ noswap  Disables swap. Remounts must respect the original settings.
 ======  ===========================================================
 
 tmpfs also supports Transparent Huge Pages which requires a kernel
-configured with CONFIG_TRANSPARENT_HUGEPAGE and with huge supported for
-your system (has_transparent_hugepage(), which is architecture specific).
-The mount options for this are:
+configured with CONFIG_TRANSPARENT_HUGEPAGE and with huge pages
+supported for your system. The mount options for this are:
 
 ================ ==============================================================
 huge=never       Do not allocate huge pages.  This is the default.
Signature · 1 line
2.55.0
#2 lore raw

PATCH v8 2/14 mm: shmem: shmem_getattr(): set blksize to highest supported THP order

In reply to Luiz Capitulino

Today, shmem_getattr() sets stat->blksize to PMD size whenever shmem_huge_global_enabled() returns non-zero. While this works fine for the normal THP-enabled case as explained by Baolin in [1], this has two problems:

1. Theoretically, when shmem is configured for within_size, this could set blksize to PMD size even though the allocation may be a smaller mTHP order

2. A future commit will allow shmem THP support to be enabled even when the CPU doesn't support PMD-sized pages. We should not allow blksize to be set to PMD size in this case

In order to fix #1 and prepare for #2, this commit sets blksize to the size of the highest supported order returned by shmem_huge_global_enabled().

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

---
 mm/shmem.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
mm/shmem.c +4 -2
index b572c60f2af8..776dff8a848e 100644
@@ -1506,6 +1506,7 @@ static int shmem_getattr(struct mnt_idmap *idmap,
 {
 	struct inode *inode = path->dentry->d_inode;
 	struct shmem_inode_info *info = SHMEM_I(inode);
+	unsigned int orders;
 
 	/* Fast-path hint; recalc under info->lock corrects any stale read. */
 	if (data_race(info->alloced - info->swapped != inode->i_mapping->nrpages))
@@ -1522,8 +1523,9 @@ static int shmem_getattr(struct mnt_idmap *idmap,
 			STATX_ATTR_NODUMP);
 	generic_fillattr(idmap, request_mask, inode, stat);
 
-	if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0))
-		stat->blksize = HPAGE_PMD_SIZE;
+	orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);
+	if (orders)
+		stat->blksize = PAGE_SIZE << highest_order(orders);
 
 	if (request_mask & STATX_BTIME) {
 		stat->result_mask |= STATX_BTIME;
Signature · 1 line
2.55.0
maintainer Acked-by on v8 2/14
#3 lore raw

Re: [PATCH v8 02/14] mm: shmem: shmem_getattr(): set blksize to highest supported THP order

Quoted 40 lines · Luiz Capitulino

Today, shmem_getattr() sets stat->blksize to PMD size whenever shmem_huge_global_enabled() returns non-zero. While this works fine for the normal THP-enabled case as explained by Baolin in [1], this has two problems:

1. Theoretically, when shmem is configured for within_size, this could set blksize to PMD size even though the allocation may be a smaller mTHP order

2. A future commit will allow shmem THP support to be enabled even when the CPU doesn't support PMD-sized pages. We should not allow blksize to be set to PMD size in this case

In order to fix #1 and prepare for #2, this commit sets blksize to the size of the highest supported order returned by shmem_huge_global_enabled().

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

---
 mm/shmem.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
mm/shmem.c +1 -0
index b572c60f2af8..776dff8a848e 100644
@@ -1506,6 +1506,7 @@ static int shmem_getattr(struct mnt_idmap *idmap,
 {
 	struct inode *inode = path->dentry->d_inode;
 	struct shmem_inode_info *info = SHMEM_I(inode);
+	unsigned int orders;
 
 	/* Fast-path hint; recalc under info->lock corrects any stale read. */
 	if (data_race(info->alloced - info->swapped != inode->i_mapping->nrpages))
@@ -1522,8 +1523,9 @@ static int shmem_getattr(struct mnt_idmap *idmap,
 			STATX_ATTR_NODUMP);

mm/shmem.c static int shmem_getattr(struct mnt_idmap *idmap, · line 1531

Quoted 10 lines
mm/shmem.c +3 -2
 	generic_fillattr(idmap, request_mask, inode, stat);
 
-	if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0))
-		stat->blksize = HPAGE_PMD_SIZE;
+	orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);
+	if (orders)
+		stat->blksize = PAGE_SIZE << highest_order(orders);
 
 	if (request_mask & STATX_BTIME) {
 		stat->result_mask |= STATX_BTIME;

Heh, looking into the history of this I stumble over

commit 89fdcd262fd40712da65e922558872a12b203332

Author: Yang Shi <[email protected]>
Date:   Thu Jun 7 17:06:59 2018 -0700

mm: shmem: make stat.st_blksize return huge page size if THP is on

Since tmpfs THP was supported in 4.8, hugetlbfs is not the only filesystem with huge page support anymore. tmpfs can use huge page via THP when mounting by "huge=" mount option.

Which is a good read, especially regarding Hugh's comments:

"
: Sorry, I have no enthusiasm for this patch; but do I feel strongly : enough to override you and everyone else to NAK it? No, I don't feel : that strongly, maybe st_blksize isn't worth arguing over.
:
: We did look at struct stat when designing huge tmpfs, to see if there : were any fields that should be adjusted for it; but concluded none. : Yes, it would sometimes be nice to have a quickly accessible indicator : for when tmpfs has been mounted huge (scanning /proc/mounts for options : can be tiresome, agreed); but since tmpfs tries to supply huge (or not) : pages transparently, no difference seemed right.
"

(I agree with Hugh, also I find it odd to indicate for THP something > PAGE_SIZE)

Anyhow, that ship has sailed.

Effectively, I think "stat->blksize" is not that relevant. But indeed, today it gives us the highest *possible* size we could see on that system.

shmem_huge_global_enabled() does not seem to depend on any runtime toggles that could change, only on inode properties.

So your change looks good to me.

Signature · 3 lines
Cheers,

David
#4 lore raw

PATCH v8 3/14 mm: introduce pgtable_has_pmd_leaves()

In reply to Luiz Capitulino

Currently, we have two helpers that check for PMD-sized pages but have different names and slightly different semantics:

- has_transparent_hugepage(): the name suggests it checks if THP is enabled, but when CONFIG_TRANSPARENT_HUGEPAGE=y and the architecture implements this helper, it actually checks if the CPU supports PMD-sized pages

- thp_disabled_by_hw(): the name suggests it checks if THP is disabled by the hardware, but it just returns a cached value acquired with has_transparent_hugepage() during boot. This helper is used in fast paths

A better design would be to separate CONFIG_TRANSPARENT_HUGEPAGE checking, which can be done with the IS_ENABLED() macro, from checking if a CPU supports PMD-sized pages.

To this end, this commit introduces a new helper called pgtable_has_pmd_leaves() which offers the following advantages:

1. Well defined and clear semantics: it returns true if the CPU supports PMD-sized pages and false otherwise
2. It always returns a cached value, so it can be used in fast paths
3. It's implemented with a static key: it's a no-op for archs not implemeting it and for archs implementing it the static key is changed only during boot

The new helper requires an initialization step which is performed by pgtable_leaf_support_init(). We call pgtable_leaf_support_init() early during boot from mm_core_init().

The next commits will convert users of both has_transparent_hugepage() and thp_disabled_by_hw() to pgtable_has_pmd_leaves() and/or IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE).

---
 include/linux/pgtable.h | 17 +++++++++++++++++
 mm/memory.c             |  9 +++++++++
 mm/mm_init.c            |  1 +
 3 files changed, 27 insertions(+)
include/linux/pgtable.h +17 -0
index e3c8ab96941c..ad852a415114 100644
@@ -14,6 +14,7 @@
 #include <linux/mm_types.h>
 #include <linux/bug.h>
 #include <linux/errno.h>
+#include <linux/jump_label.h>
 #include <asm-generic/pgtable_uffd.h>
 #include <linux/page_table_check.h>
 
@@ -2327,6 +2328,22 @@ void ptval_bytes_to_hex_str(char *buf, size_t buf_size, const void *entry, size_
 #else
 #define PTVAL_STR_MAX	(16 + 1) /* Max 64-bit value in hex + NUL */
 #endif
+
+#ifdef CONFIG_MMU
+DECLARE_STATIC_KEY_TRUE(__arch_has_pmd_leaves_key);
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return static_branch_likely(&__arch_has_pmd_leaves_key);
+}
+void __init pgtable_leaf_support_init(void);
+#else
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return false;
+}
+static inline void __init pgtable_leaf_support_init(void) { }
+#endif
+
 #endif /* !__ASSEMBLER__ */
 
 #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
mm/memory.c +9 -0
index 9e4a70421a6b..900539acb7d9 100644
@@ -164,6 +164,15 @@ __setup("norandmaps", disable_randmaps);
 
 unsigned long highest_memmap_pfn __read_mostly;
 
+DEFINE_STATIC_KEY_TRUE(__arch_has_pmd_leaves_key);
+EXPORT_SYMBOL(__arch_has_pmd_leaves_key);
+
+void __init pgtable_leaf_support_init(void)
+{
+	if (!has_transparent_hugepage())
+		static_branch_disable(&__arch_has_pmd_leaves_key);
+}
+
 void mm_trace_rss_stat(struct mm_struct *mm, int member)
 {
 	trace_rss_stat(mm, member);
mm/mm_init.c +1 -0
index 56bb4567a494..28685f766234 100644
@@ -2693,6 +2693,7 @@ void __init mm_core_init(void)
 {
 	arch_mm_preinit();
 	init_zero_page_pfn();
+	pgtable_leaf_support_init();
 
 	/* Initializations relying on SMP setup */
 	BUILD_BUG_ON(MAX_ZONELISTS > 2);
Signature · 1 line
2.55.0
reviewer Acked-by on v8 3/14
#5 lore raw

Re: [PATCH v8 03/14] mm: introduce pgtable_has_pmd_leaves()

Quoted 35 lines · Luiz Capitulino

Currently, we have two helpers that check for PMD-sized pages but have different names and slightly different semantics:

- has_transparent_hugepage(): the name suggests it checks if THP is enabled, but when CONFIG_TRANSPARENT_HUGEPAGE=y and the architecture implements this helper, it actually checks if the CPU supports PMD-sized pages

- thp_disabled_by_hw(): the name suggests it checks if THP is disabled by the hardware, but it just returns a cached value acquired with has_transparent_hugepage() during boot. This helper is used in fast paths

A better design would be to separate CONFIG_TRANSPARENT_HUGEPAGE checking, which can be done with the IS_ENABLED() macro, from checking if a CPU supports PMD-sized pages.

To this end, this commit introduces a new helper called pgtable_has_pmd_leaves() which offers the following advantages:

1. Well defined and clear semantics: it returns true if the CPU supports PMD-sized pages and false otherwise
2. It always returns a cached value, so it can be used in fast paths
3. It's implemented with a static key: it's a no-op for archs not implemeting it and for archs implementing it the static key is changed only during boot

The new helper requires an initialization step which is performed by pgtable_leaf_support_init(). We call pgtable_leaf_support_init() early during boot from mm_core_init().

The next commits will convert users of both has_transparent_hugepage() and thp_disabled_by_hw() to pgtable_has_pmd_leaves() and/or IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE).

Quoted 7 lines
---
 include/linux/pgtable.h | 17 +++++++++++++++++
 mm/memory.c             |  9 +++++++++
 mm/mm_init.c            |  1 +
 3 files changed, 27 insertions(+)

LGTM.

Signature · 2 lines
Best Regards,
Yan, Zi
maintainer Acked-by on v8 3/14
#6 lore raw

Re: [PATCH v8 03/14] mm: introduce pgtable_has_pmd_leaves()

In reply to Luiz Capitulino
Quoted 31 lines · Luiz Capitulino

Currently, we have two helpers that check for PMD-sized pages but have different names and slightly different semantics:

- has_transparent_hugepage(): the name suggests it checks if THP is enabled, but when CONFIG_TRANSPARENT_HUGEPAGE=y and the architecture implements this helper, it actually checks if the CPU supports PMD-sized pages

- thp_disabled_by_hw(): the name suggests it checks if THP is disabled by the hardware, but it just returns a cached value acquired with has_transparent_hugepage() during boot. This helper is used in fast paths

A better design would be to separate CONFIG_TRANSPARENT_HUGEPAGE checking, which can be done with the IS_ENABLED() macro, from checking if a CPU supports PMD-sized pages.

To this end, this commit introduces a new helper called pgtable_has_pmd_leaves() which offers the following advantages:

1. Well defined and clear semantics: it returns true if the CPU supports PMD-sized pages and false otherwise
2. It always returns a cached value, so it can be used in fast paths
3. It's implemented with a static key: it's a no-op for archs not implemeting it and for archs implementing it the static key is changed only during boot

The new helper requires an initialization step which is performed by pgtable_leaf_support_init(). We call pgtable_leaf_support_init() early during boot from mm_core_init().

Quoted 6 lines

The next commits will convert users of both has_transparent_hugepage() and thp_disabled_by_hw() to pgtable_has_pmd_leaves() and/or IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE).

---

Clean, thanks

Signature · 3 lines
Cheers,

David
#7 lore raw

PATCH v8 4/14 drivers: dax: use pgtable_has_pmd_leaves()

In reply to Luiz Capitulino

dax_align_valid() uses has_transparent_hugepage() to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

---
 drivers/dax/dax-private.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dax/dax-private.h +1 -1
index 607a53a91f58..18a6295aa766 100644
@@ -121,7 +121,7 @@ static inline bool dax_align_valid(unsigned long align)
 {
 	if (align == PUD_SIZE && IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
 		return true;
-	if (align == PMD_SIZE && has_transparent_hugepage())
+	if (align == PMD_SIZE && pgtable_has_pmd_leaves())
 		return true;
 	if (align == PAGE_SIZE)
 		return true;
Signature · 1 line
2.55.0
reviewer Acked-by on v8 4/14
#8 lore raw

Re: [PATCH v8 04/14] drivers: dax: use pgtable_has_pmd_leaves()

Quoted 9 lines · Luiz Capitulino

dax_align_valid() uses has_transparent_hugepage() to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

---
 drivers/dax/dax-private.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

LGTM.

Signature · 2 lines
Best Regards,
Yan, Zi
#9 lore raw

Re: [PATCH v8 04/14] drivers: dax: use pgtable_has_pmd_leaves()

In reply to Luiz Capitulino
Quoted 14 lines · Luiz Capitulino

dax_align_valid() uses has_transparent_hugepage() to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

---
 drivers/dax/dax-private.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dax/dax-private.h +0 -0
index 607a53a91f58..18a6295aa766 100644

drivers/dax/dax-private.h static inline bool dax_align_valid(unsigned long align) · line 127

Quoted 8 lines
drivers/dax/dax-private.h +1 -1
@@ -121,7 +121,7 @@ static inline bool dax_align_valid(unsigned long align)
 {
 	if (align == PUD_SIZE && IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
 		return true;
-	if (align == PMD_SIZE && has_transparent_hugepage())
+	if (align == PMD_SIZE && pgtable_has_pmd_leaves())
 		return true;
 	if (align == PAGE_SIZE)
 		return true;

The main part why we don't see any change is that dax_align_valid() is wrapped in the #ifdef CONFIG_TRANSPARENT_HUGEPAGE today.

Might we worth spelling that out in the patch description.

Signature · 3 lines
Cheers,

David
#10 lore raw

Re: [PATCH v8 04/14] drivers: dax: use pgtable_has_pmd_leaves()

Quoted 27 lines · David Hildenbrand (Arm)
Quoted 14 lines · Luiz Capitulino

dax_align_valid() uses has_transparent_hugepage() to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

---
  drivers/dax/dax-private.h | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dax/dax-private.h +0 -0
index 607a53a91f58..18a6295aa766 100644

drivers/dax/dax-private.h static inline bool dax_align_valid(unsigned long align) · line 127

Quoted 8 lines
drivers/dax/dax-private.h +1 -1
@@ -121,7 +121,7 @@ static inline bool dax_align_valid(unsigned long align)
  {
  	if (align == PUD_SIZE && IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD))
  		return true;
-	if (align == PMD_SIZE && has_transparent_hugepage())
+	if (align == PMD_SIZE && pgtable_has_pmd_leaves())
  		return true;
  	if (align == PAGE_SIZE)
  		return true;

The main part why we don't see any change is that dax_align_valid() is wrapped in the #ifdef CONFIG_TRANSPARENT_HUGEPAGE today.

Quoted 1 line

Might we worth spelling that out in the patch description.

OK, the new changelog reads:

"""
dax_align_valid() uses has_transparent_hugepage() to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

No functional change intended since the call happens under the CONFIG_TRANSPARENT_HUGEPAGE guard and when CONFIG_TRANSPARENT_HUGEPAGE=y both functions return the same value.
"""

#11 lore raw

PATCH v8 5/14 drivers: nvdimm: use pgtable_has_pmd_leaves()

In reply to Luiz Capitulino

nd_pfn_supported_alignments() and nd_pfn_default_alignment() use has_transparent_hugepage() to check if THP is supported with PMD-sized pages. Use pgtable_has_pmd_leaves() instead. Also, check for IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) to preserve the current implementation semantics.

---
 drivers/nvdimm/pfn_devs.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/nvdimm/pfn_devs.c +4 -2
index 8fa9c16aba7e..457eb54e7ab6 100644
@@ -94,7 +94,8 @@ static unsigned long *nd_pfn_supported_alignments(unsigned long *alignments)
 
 	alignments[0] = PAGE_SIZE;
 
-	if (has_transparent_hugepage()) {
+	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+	    pgtable_has_pmd_leaves()) {
 		alignments[1] = HPAGE_PMD_SIZE;
 		if (has_transparent_pud_hugepage())
 			alignments[2] = HPAGE_PUD_SIZE;
@@ -109,7 +110,8 @@ static unsigned long *nd_pfn_supported_alignments(unsigned long *alignments)
 static unsigned long nd_pfn_default_alignment(void)
 {
 
-	if (has_transparent_hugepage())
+	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+	    pgtable_has_pmd_leaves())
 		return HPAGE_PMD_SIZE;
 	return PAGE_SIZE;
 }
Signature · 1 line
2.55.0
maintainer Acked-by on v8 5/14
#12 lore raw

Re: [PATCH v8 05/14] drivers: nvdimm: use pgtable_has_pmd_leaves()

Quoted 17 lines · Luiz Capitulino

nd_pfn_supported_alignments() and nd_pfn_default_alignment() use has_transparent_hugepage() to check if THP is supported with PMD-sized pages. Use pgtable_has_pmd_leaves() instead. Also, check for IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) to preserve the current implementation semantics.

---
 drivers/nvdimm/pfn_devs.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/nvdimm/pfn_devs.c +0 -0
index 8fa9c16aba7e..457eb54e7ab6 100644

drivers/nvdimm/pfn_devs.c static unsigned long *nd_pfn_supported_alignments(unsigned long *alignments) · line 98

Quoted 6 lines
drivers/nvdimm/pfn_devs.c +2 -1
@@ -94,7 +94,8 @@ static unsigned long *nd_pfn_supported_alignments(unsigned long *alignments)
 
 	alignments[0] = PAGE_SIZE;
 
-	if (has_transparent_hugepage()) {
+	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+	    pgtable_has_pmd_leaves()) {

Not sure if I mentioned it but we *could* provide a helper that checks for THP PMD order support. But I would only add that if really used frequently.

Scanning your other patches, that doesn't seem to be the case :)

Signature · 3 lines
Cheers,

David
#13 lore raw

PATCH v8 6/14 mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()

In reply to Luiz Capitulino

debug_vm_pgtable calls has_transparent_hugepage() in multiple places to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

---
 mm/debug_vm_pgtable.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
mm/debug_vm_pgtable.c +10 -10
index 2875fd22d7bb..25038b74c7bc 100644
@@ -177,7 +177,7 @@ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx)
 	unsigned long val = idx, *ptr = &val;
 	pmd_t pmd;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD basic (%pGv)\n", ptr);
@@ -222,7 +222,7 @@ static void __init pmd_advanced_tests(struct pgtable_debug_args *args)
 	pmd_t pmd;
 	unsigned long vaddr = args->vaddr;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	page = (args->pmd_pfn != ULONG_MAX) ? pfn_to_page(args->pmd_pfn) : NULL;
@@ -283,7 +283,7 @@ static void __init pmd_leaf_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD leaf\n");
@@ -688,7 +688,7 @@ static void __init pmd_protnone_tests(struct pgtable_debug_args *args)
 	if (!IS_ENABLED(CONFIG_ARCH_HAS_PTE_PROTNONE))
 		return;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD protnone\n");
@@ -737,7 +737,7 @@ static void __init pmd_soft_dirty_tests(struct pgtable_debug_args *args)
 	if (!pgtable_supports_soft_dirty())
 		return;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD soft dirty\n");
@@ -754,7 +754,7 @@ static void __init pmd_leaf_soft_dirty_tests(struct pgtable_debug_args *args)
 	    !IS_ENABLED(CONFIG_ARCH_HAS_PMD_SOFTLEAVES))
 		return;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD swap soft dirty\n");
@@ -825,7 +825,7 @@ static void __init pmd_softleaf_tests(struct pgtable_debug_args *args)
 	swp_entry_t arch_entry;
 	pmd_t pmd1, pmd2;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD swap\n");
@@ -906,7 +906,7 @@ static void __init pmd_thp_tests(struct pgtable_debug_args *args)
 {
 	pmd_t pmd;
 
-	if (!has_transparent_hugepage())
+	if (!pgtable_has_pmd_leaves())
 		return;
 
 	pr_debug("Validating PMD based THP\n");
@@ -997,7 +997,7 @@ static void __init destroy_args(struct pgtable_debug_args *args)
 	}
 
 	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
-	    has_transparent_hugepage() &&
+	    pgtable_has_pmd_leaves() &&
 	    args->pmd_pfn != ULONG_MAX) {
 		debug_vm_pgtable_free_huge_page(args, args->pmd_pfn, HPAGE_PMD_ORDER);
 		args->pmd_pfn = ULONG_MAX;
@@ -1249,7 +1249,7 @@ static int __init init_args(struct pgtable_debug_args *args)
 	}
 
 	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
-	    has_transparent_hugepage()) {
+	    pgtable_has_pmd_leaves()) {
 		page = debug_vm_pgtable_alloc_huge_page(args, HPAGE_PMD_ORDER);
 		if (page) {
 			args->pmd_pfn = page_to_pfn(page);
Signature · 1 line
2.55.0
maintainer Acked-by on v8 6/14
#14 lore raw

Re: [PATCH v8 06/14] mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()

Quoted 8 lines · Luiz Capitulino

debug_vm_pgtable calls has_transparent_hugepage() in multiple places to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

---

Best to mention in the description that there is no effective change as all calls to has_transparent_hugepage() are predicated by CONFIG_TRANSPARENT_HUGEPAGE.

Signature · 3 lines
Cheers,

David
#15 lore raw

Re: [PATCH v8 06/14] mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()

Quoted 5 lines · David Hildenbrand (Arm)
Quoted 4 lines · Luiz Capitulino

debug_vm_pgtable calls has_transparent_hugepage() in multiple places to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

Quoted 7 lines
Quoted 4 lines

---

Best to mention in the description that there is no effective change as all calls to has_transparent_hugepage() are predicated by CONFIG_TRANSPARENT_HUGEPAGE.

OK, the new changelog reads:

"""
debug_vm_pgtable calls has_transparent_hugepage() in multiple places to check if PMD-sized pages are supported, use pgtable_has_pmd_leaves() instead.

No functional change intended since the call happens under the CONFIG_TRANSPARENT_HUGEPAGE guard and when CONFIG_TRANSPARENT_HUGEPAGE=y both functions return the same value.
"""

Quoted 2 lines
#16 lore raw

PATCH v8 7/14 mm: shmem: allow THP support determination at folio allocation time

In reply to Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
 mm/shmem.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +5 -4
index 776dff8a848e..930657d05375 100644
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)
 	else
 		return -EINVAL;
 
-	if (!has_transparent_hugepage() &&
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
 	    huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
 		return -EINVAL;
 
@@ -1524,6 +1524,8 @@ static int shmem_getattr(struct mnt_idmap *idmap,
 	generic_fillattr(idmap, request_mask, inode, stat);
 
 	orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);
+	if (!pgtable_has_pmd_leaves())
+		orders &= ~BIT(PMD_ORDER);
 	if (orders)
 		stat->blksize = PAGE_SIZE << highest_order(orders);
 
@@ -4814,8 +4816,7 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_huge:
 		ctx->huge = result.uint_32;
 		if (ctx->huge != SHMEM_HUGE_NEVER &&
-		    !(IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
-		      has_transparent_hugepage()))
+		    !IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
 			goto unsupported_parameter;
 		ctx->seen |= SHMEM_SEEN_HUGE;
 		break;
@@ -5620,7 +5621,7 @@ void __init shmem_init(void)
 #endif
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-	if (has_transparent_hugepage() && shmem_huge > SHMEM_HUGE_DENY)
+	if (shmem_huge > SHMEM_HUGE_DENY)
 		SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge;
 	else
 		shmem_huge = SHMEM_HUGE_NEVER; /* just in case it was patched */
Signature · 1 line
2.55.0
reviewer Reviewed-by on v8 7/14
#17 lore raw

Re: [PATCH v8 07/14] mm: shmem: allow THP support determination at folio allocation time

Quoted 32 lines · Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
 mm/shmem.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +0 -0
index 776dff8a848e..930657d05375 100644

mm/shmem.c static int shmem_parse_huge(const char *str) · line 695

Quoted 7 lines
mm/shmem.c +1 -1
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)
 	else
 		return -EINVAL;
 
-	if (!has_transparent_hugepage() &&
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
 	    huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
 		return -EINVAL;

This can be deleted, since shmem_parse_huge() is guarded by CONFIG_TRANSPARENT_HUGEPAGE and after the change the if is always false under that guard.

Otherwise, LGTM. With the above code deleted, feel free to add

Signature · 2 lines
Best Regards,
Yan, Zi
#18 lore raw

Re: [PATCH v8 07/14] mm: shmem: allow THP support determination at folio allocation time

Quoted 45 lines · Zi Yan
Quoted 32 lines · Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
  mm/shmem.c | 9 +++++----
  1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +0 -0
index 776dff8a848e..930657d05375 100644

mm/shmem.c static int shmem_parse_huge(const char *str) · line 695

Quoted 7 lines
mm/shmem.c +1 -1
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)
  	else
  		return -EINVAL;
 
-	if (!has_transparent_hugepage() &&
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
  	    huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
  		return -EINVAL;

This can be deleted, since shmem_parse_huge() is guarded by CONFIG_TRANSPARENT_HUGEPAGE and after the change the if is always false under that guard.

Quoted 3 lines

Otherwise, LGTM. With the above code deleted, feel free to add

OK, here's the diff:

mm/shmem.c +0 -4
index c00bc1829fc9..d12995d41846 100644
@@ -690,10 +690,6 @@ static int shmem_parse_huge(const char *str)
 	else
 		return -EINVAL;
 
-	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
-	    huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
-		return -EINVAL;
-
 	/* Do not override huge allocation policy with non-PMD sized mTHP */
 	if (huge == SHMEM_HUGE_FORCE &&
 	    huge_shmem_orders_inherit != BIT(HPAGE_PMD_ORDER))
#19 lore raw

Re: [PATCH v8 07/14] mm: shmem: allow THP support determination at folio allocation time

In reply to Luiz Capitulino
Quoted 41 lines · Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
 mm/shmem.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +1 -1
index 776dff8a848e..930657d05375 100644
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)
 	else
 		return -EINVAL;
 
-	if (!has_transparent_hugepage() &&
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
 	    huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
 		return -EINVAL;
 

mm/shmem.c static int shmem_getattr(struct mnt_idmap *idmap, · line 1526

Quoted 3 lines
mm/shmem.c +0 -0
@@ -1524,6 +1524,8 @@ static int shmem_getattr(struct mnt_idmap *idmap,
 	generic_fillattr(idmap, request_mask, inode, stat);
 
 	orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);

I'm curious: why is that not handled inside shmem_huge_global_enabled() ? If PMD order is impossible (well, okay, it is possible, but we simply cannot map these things through PMDs), I would expect that we never list them as "enabled".

(while shmem could allocate PMD folios, it would have to map them always through PTEs. I think this would be possible through some changes, but we can defer that to some future work).

Signature · 3 lines
Cheers,

David
#20 lore raw

Re: [PATCH v8 07/14] mm: shmem: allow THP support determination at folio allocation time

Quoted 41 lines · David Hildenbrand (Arm)
Quoted 40 lines · Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
  mm/shmem.c | 9 +++++----
  1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +1 -1
index 776dff8a848e..930657d05375 100644
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)
  	else
  		return -EINVAL;
 
-	if (!has_transparent_hugepage() &&
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
  	    huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
  		return -EINVAL;
 
Quoted 8 lines

mm/shmem.c static int shmem_getattr(struct mnt_idmap *idmap, · line 1526

Quoted 4 lines
mm/shmem.c +0 -0
@@ -1524,6 +1524,8 @@ static int shmem_getattr(struct mnt_idmap *idmap,
  	generic_fillattr(idmap, request_mask, inode, stat);
 
  	orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);

I'm curious: why is that not handled inside shmem_huge_global_enabled() ? If PMD order is impossible (well, okay, it is possible, but we simply cannot map these things through PMDs), I would expect that we never list them as "enabled".

Yes, you're right. What about renaming current shmem_huge_global_enabled() to __shmem_huge_global_enabled() and then having:

static unsigned int shmem_huge_global_enabled(struct inode *inode, pgoff_t index,
loff_t write_end, bool shmem_huge_force, struct vm_area_struct *vma, vm_flags_t vm_flags)

{
	unsigned int orders;

	orders = __shmem_huge_global_enabled(inode, index, write_end,
					     shmem_huge_force, vma, vm_flags);
	if (!pgtable_has_pmd_leaves())
		orders &= ~BIT(PMD_ORDER);

	return orders;
}

Would this be acceptable?

Quoted 5 lines

(while shmem could allocate PMD folios, it would have to map them always through PTEs. I think this would be possible through some changes, but we can defer that to some future work).

reviewer
#21 lore raw

Re: [PATCH v8 07/14] mm: shmem: allow THP support determination at folio allocation time

Quoted 65 lines · Luiz Capitulino
Quoted 38 lines · David Hildenbrand (Arm)
Quoted 37 lines · Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
  mm/shmem.c | 9 +++++----

  1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +0 -0
index 776dff8a848e..930657d05375 100644
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)

      else
          return -EINVAL;
-    if (!has_transparent_hugepage() &&
+    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)

Quoted 8 lines

mm/shmem.c static int shmem_getattr(struct mnt_idmap *idmap, · line 1525

Quoted 4 lines

          return -EINVAL;

mm/shmem.c +0 -0
@@ -1524,6 +1524,8 @@ static int shmem_getattr(struct mnt_idmap *idmap,
       generic_fillattr(idmap, request_mask, inode, stat);
       orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);

I'm curious: why is that not handled inside shmem_huge_global_enabled() ? If PMD order is impossible (well, okay, it is possible, but we simply cannot map these things through PMDs), I would expect that we never list them as "enabled".

Yes, you're right. What about renaming current shmem_huge_global_enabled() to __shmem_huge_global_enabled() and then having:

static unsigned int shmem_huge_global_enabled(struct inode *inode, pgoff_t index,
                          loff_t write_end, bool shmem_huge_force, struct vm_area_struct *vma,
                          vm_flags_t vm_flags)
{
    unsigned int orders;

    orders = __shmem_huge_global_enabled(inode, index, write_end, shmem_huge_force, vma, vm_flags); if (!pgtable_has_pmd_leaves())
        orders &= ~BIT(PMD_ORDER);

Quoted 4 lines

    return orders;
}

Would this be acceptable?

Not a fan of re-adding the wrapper. I think you could refer to the changes to shmem_allowable_huge_orders() in patch 14 and filter out the 'disabled_orders' in shmem_huge_global_enabled() directly.

#22 lore raw

Re: [PATCH v8 07/14] mm: shmem: allow THP support determination at folio allocation time

Quoted 68 lines · Baolin Wang
Quoted 65 lines · Luiz Capitulino
Quoted 38 lines · David Hildenbrand (Arm)
Quoted 37 lines · Luiz Capitulino

In order to enable THP support in shmem today, besides the user configuration required, the CPU must support PMD-sized pages. This is the case because of the following has_transparent_hugepage() usage:

- shmem_parse_one() and shmem_parse_huge(): Check if THP is built-in and if the CPU supports PMD-sized pages

- shmem_init(): Since the CONFIG_TRANSPARENT_HUGEPAGE guard is outside the code block calling has_transparent_hugepage(), the has_transparent_hugepage() call is exclusively checking if the CPU supports PMD-sized pages

While it's necessary to check if CONFIG_TRANSPARENT_HUGEPAGE is enabled in all cases, shmem can determine THP size support at folio allocation time. Therefore, drop the has_transparent_hugepage() usage listed above while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

Additionally, we need to check if PMD size order is supported in shmem_getattr(). Use pgtable_has_pmd_leaves() for that.

---
  mm/shmem.c | 9 +++++----
  1 file changed, 5 insertions(+), 4 deletions(-)
mm/shmem.c +1 -1
index 776dff8a848e..930657d05375 100644
@@ -690,7 +690,7 @@ static int shmem_parse_huge(const char *str)
      else
          return -EINVAL;
-    if (!has_transparent_hugepage() &&
+    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
          huge != SHMEM_HUGE_NEVER && huge != SHMEM_HUGE_DENY)
Quoted 8 lines

mm/shmem.c static int shmem_getattr(struct mnt_idmap *idmap, · line 1525

Quoted 4 lines
mm/shmem.c +0 -0
          return -EINVAL;
@@ -1524,6 +1524,8 @@ static int shmem_getattr(struct mnt_idmap *idmap,
      generic_fillattr(idmap, request_mask, inode, stat);
      orders = shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0);

I'm curious: why is that not handled inside shmem_huge_global_enabled() ? If PMD order is impossible (well, okay, it is possible, but we simply cannot map these things through PMDs), I would expect that we never list them as "enabled".

Yes, you're right. What about renaming current shmem_huge_global_enabled() to __shmem_huge_global_enabled() and then having:

static unsigned int shmem_huge_global_enabled(struct inode *inode, pgoff_t index,
loff_t write_end, bool shmem_huge_force, struct vm_area_struct *vma,
vm_flags_t vm_flags)

{
     unsigned int orders;

     orders = __shmem_huge_global_enabled(inode, index, write_end,
                          shmem_huge_force, vma, vm_flags);
     if (!pgtable_has_pmd_leaves())
         orders &= ~BIT(PMD_ORDER);
Quoted 6 lines
Quoted 4 lines
     return orders;
}

Would this be acceptable?

Not a fan of re-adding the wrapper. I think you could refer to the changes to shmem_allowable_huge_orders() in patch 14 and filter out the 'disabled_orders' in shmem_huge_global_enabled() directly.

OK, I'll look into doing this.

#23 lore raw

PATCH v8 8/14 s390: move has_transparent_hugepage() out of THP guard

In reply to Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
 arch/s390/include/asm/pgtable.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/include/asm/pgtable.h +1 -1
index 2d5c2ab06de9..e7a2bf9636b7 100644
@@ -1814,13 +1814,13 @@ static inline int pmd_trans_huge(pmd_t pmd)
 {
 	return pmd_leaf(pmd);
 }
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #define has_transparent_hugepage has_transparent_hugepage
 static inline int has_transparent_hugepage(void)
 {
 	return cpu_has_edat1() ? 1 : 0;
 }
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #ifdef CONFIG_PAGE_TABLE_CHECK
 static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte)
Signature · 1 line
2.55.0
reviewer Acked-by on v8 8/14
#24 lore raw

Re: [PATCH v8 08/14] s390: move has_transparent_hugepage() out of THP guard

Quoted 5 lines · Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Quoted 7 lines

Move its definition out of the THP guard.

---
 arch/s390/include/asm/pgtable.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Signature · 2 lines
Best Regards,
Yan, Zi
maintainer Acked-by on v8 8/14
#25 lore raw

Re: [PATCH v8 08/14] s390: move has_transparent_hugepage() out of THP guard

In reply to Luiz Capitulino
Quoted 8 lines · Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

Signature · 3 lines
Cheers,

David
#26 lore raw

PATCH v8 9/14 powerpc: move has_transparent_hugepage() out of THP guard

In reply to Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
 arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
 arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 +++++++++---------
 arch/powerpc/include/asm/book3s/64/radix.h    | 14 +++++++-------
 arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
 5 files changed, 20 insertions(+), 20 deletions(-)
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..79511e6abfca 100644
@@ -165,9 +165,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
 extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
 extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
 #endif
 
+extern int hash__has_transparent_hugepage(void);
 #endif /* !__ASSEMBLER__ */
 
 #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +1 -1
index 7deb3a66890b..a4a44a112ff9 100644
@@ -278,9 +278,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
 extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
 extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
 #endif /*  CONFIG_TRANSPARENT_HUGEPAGE */
 
+extern int hash__has_transparent_hugepage(void);
 #endif	/* __ASSEMBLER__ */
 
 #endif /* _ASM_POWERPC_BOOK3S_64_HASH_64K_H */
arch/powerpc/include/asm/book3s/64/pgtable.h +9 -9
index dff8790a047d..522bb58b6241 100644
@@ -1114,15 +1114,6 @@ static inline void update_mmu_cache_pud(struct vm_area_struct *vma,
 {
 }
 
-extern int hash__has_transparent_hugepage(void);
-static inline int has_transparent_hugepage(void)
-{
-	if (radix_enabled())
-		return radix__has_transparent_hugepage();
-	return hash__has_transparent_hugepage();
-}
-#define has_transparent_hugepage has_transparent_hugepage
-
 static inline int has_transparent_pud_hugepage(void)
 {
 	if (radix_enabled())
@@ -1434,6 +1425,15 @@ static inline bool arch_needs_pgtable_deposit(void)
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+extern int hash__has_transparent_hugepage(void);
+static inline int has_transparent_hugepage(void)
+{
+	if (radix_enabled())
+		return radix__has_transparent_hugepage();
+	return hash__has_transparent_hugepage();
+}
+#define has_transparent_hugepage has_transparent_hugepage
+
 #define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION
 pte_t ptep_modify_prot_start(struct vm_area_struct *, unsigned long, pte_t *);
 void ptep_modify_prot_commit(struct vm_area_struct *, unsigned long,
arch/powerpc/include/asm/book3s/64/radix.h +7 -7
index da954e779744..50545cf519bd 100644
@@ -298,22 +298,22 @@ extern pmd_t radix__pmdp_huge_get_and_clear(struct mm_struct *mm,
 pud_t radix__pudp_huge_get_and_clear(struct mm_struct *mm,
 				     unsigned long addr, pud_t *pudp);
 
-static inline int radix__has_transparent_hugepage(void)
+static inline int radix__has_transparent_pud_hugepage(void)
 {
-	/* For radix 2M at PMD level means thp */
-	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
+	/* For radix 1G at PUD level means pud hugepage support */
+	if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
 		return 1;
 	return 0;
 }
+#endif
 
-static inline int radix__has_transparent_pud_hugepage(void)
+static inline int radix__has_transparent_hugepage(void)
 {
-	/* For radix 1G at PUD level means pud hugepage support */
-	if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
+	/* For radix 2M at PMD level means thp */
+	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
 		return 1;
 	return 0;
 }
-#endif
 
 struct vmem_altmap;
 struct dev_pagemap;
arch/powerpc/mm/book3s64/hash_pgtable.c +2 -2
index d9b5b751d7b7..50316f3fc5d3 100644
@@ -391,6 +391,8 @@ pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 	return old_pmd;
 }
 
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+
 int hash__has_transparent_hugepage(void)
 {
 
@@ -422,8 +424,6 @@ int hash__has_transparent_hugepage(void)
 }
 EXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
 
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
-
 #ifdef CONFIG_STRICT_KERNEL_RWX
 
 struct change_memory_parms {
Signature · 1 line
2.55.0
#27 lore raw

Re: [PATCH v8 09/14] powerpc: move has_transparent_hugepage() out of THP guard

Quoted 36 lines · Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
 arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
 arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 +++++++++---------
 arch/powerpc/include/asm/book3s/64/radix.h    | 14 +++++++-------
 arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
 5 files changed, 20 insertions(+), 20 deletions(-)
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..79511e6abfca 100644
@@ -165,9 +165,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
 extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
 extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
 #endif
 
+extern int hash__has_transparent_hugepage(void);
 #endif /* !__ASSEMBLER__ */
 
 #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -0
index 7deb3a66890b..a4a44a112ff9 100644

arch/powerpc/include/asm/book3s/64/hash-64k.h extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, · line 281

Quoted 4 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -1
@@ -278,9 +278,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
 extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
 extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);

Without some of these helpers in place, I assume actually using PMD leafs without THP would require some more work. (which is not the goal of this series, just asking).

I do wonder whether the architecture should instead simply say "not supported" if !CONFIG_TRANSPARENT_HUGEPAGE?

That should still enable your series: using mTHP without PMD support.

[...]

Quoted 13 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +4 -3
-static inline int radix__has_transparent_hugepage(void)
+static inline int radix__has_transparent_pud_hugepage(void)
 {
-	/* For radix 2M at PMD level means thp */
-	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
+	/* For radix 1G at PUD level means pud hugepage support */
+	if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
 		return 1;
 	return 0;
 }
+#endif
 

arch/powerpc/include/asm/book3s/64/hash-64k.h extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,

Quoted 10 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +3 -3
-static inline int radix__has_transparent_pud_hugepage(void)
+static inline int radix__has_transparent_hugepage(void)
 {
-	/* For radix 1G at PUD level means pud hugepage support */
-	if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
+	/* For radix 2M at PMD level means thp */
+	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
 		return 1;
 	return 0;
 }

You are swapping both implementations, which might create some unnecessary churn I think.

Signature · 3 lines
Cheers,

David
#28 lore raw

Re: [PATCH v8 09/14] powerpc: move has_transparent_hugepage() out of THP guard

Quoted 31 lines · David Hildenbrand (Arm)
Quoted 30 lines · Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
  arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
  arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
  arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 +++++++++---------
  arch/powerpc/include/asm/book3s/64/radix.h    | 14 +++++++-------
  arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
  5 files changed, 20 insertions(+), 20 deletions(-)
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..79511e6abfca 100644
@@ -165,9 +165,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
  extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
  extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
  				       unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
  #endif
 
+extern int hash__has_transparent_hugepage(void);
  #endif /* !__ASSEMBLER__ */
 
Quoted 14 lines

arch/powerpc/include/asm/book3s/64/hash-64k.h extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, · line 281

Quoted 10 lines
arch/powerpc/include/asm/book3s/64/hash-4k.h +0 -0
  #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -1
index 7deb3a66890b..a4a44a112ff9 100644
@@ -278,9 +278,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
  extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
  extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
  				       unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);

Without some of these helpers in place, I assume actually using PMD leafs without THP would require some more work. (which is not the goal of this series, just asking).

Yes, you're right.

Quoted 4 lines

I do wonder whether the architecture should instead simply say "not supported" if !CONFIG_TRANSPARENT_HUGEPAGE?

That should still enable your series: using mTHP without PMD support.

Yes, it would. However, I think this introduces an inconsistency.

In in its current form, arch_has_pmd_leaves() should always report a hardware capability. This is true even for the default case where arch_has_pmd_leaves() defaults to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) in the assumption that archs supporting PMD-sized pages by default will have this config enabled.

Your suggestion will change this and for some archs arch_has_pmd_leaves() may have different behavior depending on the user configuration. Additionally, I intended to decouple the base API implementation from THP.

If you feel strongly about this I can implement your suggestion, but I'd still vote for keeping the API about consistently reporting the hardware capability. Even if the only user is THP code today, new use cases can be added incrementally.

Quoted 16 lines

[...]

Quoted 13 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +4 -3
-static inline int radix__has_transparent_hugepage(void)
+static inline int radix__has_transparent_pud_hugepage(void)
  {
-	/* For radix 2M at PMD level means thp */
-	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
+	/* For radix 1G at PUD level means pud hugepage support */
+	if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
  		return 1;
  	return 0;
  }
+#endif
Quoted 13 lines

arch/powerpc/include/asm/book3s/64/hash-64k.h

Quoted 10 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +3 -3
-static inline int radix__has_transparent_pud_hugepage(void)
+static inline int radix__has_transparent_hugepage(void)
  {
-	/* For radix 1G at PUD level means pud hugepage support */
-	if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
+	/* For radix 2M at PMD level means thp */
+	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
  		return 1;
  	return 0;
  }

You are swapping both implementations, which might create some unnecessary churn I think.

I suspect this was done by git diff as I just moved the functions, but I'll take a better look.

#29 lore raw

Re: [PATCH v8 09/14] powerpc: move has_transparent_hugepage() out of THP guard

Hi,

Don't forget <[email protected]> when you address powerpc architecture.

Le 03/10/2026 à 17:44, Luiz Capitulino a écrit :

Quoted 49 lines · David Hildenbrand (Arm)
Quoted 37 lines · Luiz Capitulino
Quoted 36 lines

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
  arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +- arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +- arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 +++++++++--------- arch/powerpc/include/asm/book3s/64/radix.h    | 14 +++++++------- arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--

  5 files changed, 20 insertions(+), 20 deletions(-)
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..79511e6abfca 100644
@@ -165,9 +165,9 @@ extern void 
 hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
   extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
   extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
                          unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
   #endif
+extern int hash__has_transparent_hugepage(void);
   #endif /* !__ASSEMBLER__ */
   #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -0
index 7deb3a66890b..a4a44a112ff9 100644
@@ -278,9 +278,9 @@ extern void 
 hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
Quoted 8 lines

arch/powerpc/include/asm/book3s/64/hash-64k.h extern void · line 282

Quoted 4 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -1
   extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
   extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
                          unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);

Without some of these helpers in place, I assume actually using PMD leafs without THP would require some more work. (which is not the goal of this series, just asking).

Quoted 8 lines

Yes, you're right.

Quoted 4 lines

I do wonder whether the architecture should instead simply say "not supported" if !CONFIG_TRANSPARENT_HUGEPAGE?

That should still enable your series: using mTHP without PMD support.

Yes, it would. However, I think this introduces an inconsistency.

powerpc has two types of MMU (HASH and RADIX) with different page layout. MMU type is selected at boottime based on the capabilities of the CPU. For transparent page you have:

static inline int has_transparent_hugepage(void)

{
	if (radix_enabled())
		return radix__has_transparent_hugepage();
	return hash__has_transparent_hugepage();
}
Quoted 49 lines

In in its current form, arch_has_pmd_leaves() should always report a hardware capability. This is true even for the default case where arch_has_pmd_leaves() defaults to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) in the assumption that archs supporting PMD-sized pages by default will have this config enabled.

Your suggestion will change this and for some archs arch_has_pmd_leaves() may have different behavior depending on the user configuration. Additionally, I intended to decouple the base API implementation from THP.

If you feel strongly about this I can implement your suggestion, but I'd still vote for keeping the API about consistently reporting the hardware capability. Even if the only user is THP code today, new use cases can be added incrementally.

Quoted 19 lines

[...]

Quoted 16 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +1 -1
-static inline int radix__has_transparent_hugepage(void)
+static inline int radix__has_transparent_pud_hugepage(void)

  {
-    /* For radix 2M at PMD level means thp */
-    if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT) +    /* For radix 1G at PUD level means pud hugepage support */ +    if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT) return 1;
      return 0;
  }

arch/powerpc/include/asm/book3s/64/hash-64k.h +2 -1
+#endif
-static inline int radix__has_transparent_pud_hugepage(void)
+static inline int radix__has_transparent_hugepage(void)

  {
-    /* For radix 1G at PUD level means pud hugepage support */ -    if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)

Quoted 8 lines
Quoted 5 lines

+    /* For radix 2M at PMD level means thp */ +    if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT) return 1;
      return 0;
  }

You are swapping both implementations, which might create some unnecessary churn I think.

I suspect this was done by git diff as I just moved the functions, but I'll take a better look.

#30 lore raw

Re: [PATCH v8 09/14] powerpc: move has_transparent_hugepage() out of THP guard

Quoted 3 lines · Christophe Leroy (CS GROUP)

Hi,

Don't forget <[email protected]> when you address powerpc architecture.

Will do.

Quoted 58 lines · Luiz Capitulino
Quoted 47 lines · David Hildenbrand (Arm)
Quoted 35 lines · Luiz Capitulino
Quoted 34 lines

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
  arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
  arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
  arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 +++++++++---------
  arch/powerpc/include/asm/book3s/64/radix.h    | 14 +++++++-------
  arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
  5 files changed, 20 insertions(+), 20 deletions(-)
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..79511e6abfca 100644
@@ -165,9 +165,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
  extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
  extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
                         unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
  #endif
+extern int hash__has_transparent_hugepage(void);
  #endif /* !__ASSEMBLER__ */
  #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -0
index 7deb3a66890b..a4a44a112ff9 100644
@@ -278,9 +278,9 @@ extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
Quoted 8 lines

arch/powerpc/include/asm/book3s/64/hash-64k.h extern void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, · line 281

Quoted 4 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -1
  extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
  extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
                         unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);

Without some of these helpers in place, I assume actually using PMD leafs without THP would require some more work. (which is not the goal of this series, just asking).

Quoted 8 lines

Yes, you're right.

Quoted 4 lines

I do wonder whether the architecture should instead simply say "not supported" if !CONFIG_TRANSPARENT_HUGEPAGE?

That should still enable your series: using mTHP without PMD support.

Yes, it would. However, I think this introduces an inconsistency.

Quoted 8 lines

powerpc has two types of MMU (HASH and RADIX) with different page layout. MMU type is selected at boottime based on the capabilities of the CPU. For transparent page you have:

static inline int has_transparent_hugepage(void)

{
     if (radix_enabled())
         return radix__has_transparent_hugepage();
     return hash__has_transparent_hugepage();
}

Right and, unless I'm misunderstanding your comment, this supports my position that the new arch_has_pmd_leaves() API is all about reporting a hardware capability and not tied to THP support.

Quoted 51 lines
Quoted 50 lines

In in its current form, arch_has_pmd_leaves() should always report a hardware capability. This is true even for the default case where arch_has_pmd_leaves() defaults to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) in the assumption that archs supporting PMD-sized pages by default will have this config enabled.

Your suggestion will change this and for some archs arch_has_pmd_leaves() may have different behavior depending on the user configuration. Additionally, I intended to decouple the base API implementation from THP.

If you feel strongly about this I can implement your suggestion, but I'd still vote for keeping the API about consistently reporting the hardware capability. Even if the only user is THP code today, new use cases can be added incrementally.

Quoted 19 lines

[...]

Quoted 16 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +5 -6
-static inline int radix__has_transparent_hugepage(void)
+static inline int radix__has_transparent_pud_hugepage(void)
  {
-    /* For radix 2M at PMD level means thp */
-    if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
+    /* For radix 1G at PUD level means pud hugepage support */
+    if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
          return 1;
      return 0;
  }
+#endif
-static inline int radix__has_transparent_pud_hugepage(void)
+static inline int radix__has_transparent_hugepage(void)
  {
-    /* For radix 1G at PUD level means pud hugepage support */
-    if (mmu_psize_defs[MMU_PAGE_1G].shift == PUD_SHIFT)
Quoted 8 lines

arch/powerpc/include/asm/book3s/64/hash-64k.h

Quoted 5 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +2 -0
+    /* For radix 2M at PMD level means thp */
+    if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
          return 1;
      return 0;
  }

You are swapping both implementations, which might create some unnecessary churn I think.

I suspect this was done by git diff as I just moved the functions, but I'll take a better look.

#31 lore raw

Re: [PATCH v8 09/14] powerpc: move has_transparent_hugepage() out of THP guard

Le 05/10/2026 à 22:40, Luiz Capitulino a écrit :

Quoted 79 lines · Christophe Leroy (CS GROUP)
Quoted 3 lines

Hi,

Don't forget <[email protected]> when you address powerpc architecture.

Will do.

Quoted 61 lines · Luiz Capitulino
Quoted 50 lines · David Hildenbrand (Arm)
Quoted 38 lines · Luiz Capitulino
Quoted 37 lines

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
  arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +- arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +- arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 ++++++++

+---------
  arch/powerpc/include/asm/book3s/64/radix.h    | 14 +++++++-------
  arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
  5 files changed, 20 insertions(+), 20 deletions(-)
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..79511e6abfca 100644
@@ -165,9 +165,9 @@ extern void 
 hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
   extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
   extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
                          unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);
   #endif
+extern int hash__has_transparent_hugepage(void);
   #endif /* !__ASSEMBLER__ */
   #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -0
index 7deb3a66890b..a4a44a112ff9 100644
@@ -278,9 +278,9 @@ extern void 
 hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
Quoted 8 lines

arch/powerpc/include/asm/book3s/64/hash-64k.h extern void · line 282

Quoted 4 lines
arch/powerpc/include/asm/book3s/64/hash-64k.h +0 -1
   extern pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
   extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
                          unsigned long addr, pmd_t *pmdp);
-extern int hash__has_transparent_hugepage(void);

Without some of these helpers in place, I assume actually using PMD leafs without THP would require some more work. (which is not the goal of this series, just asking).

Quoted 8 lines

Yes, you're right.

Quoted 4 lines

I do wonder whether the architecture should instead simply say "not supported" if !CONFIG_TRANSPARENT_HUGEPAGE?

That should still enable your series: using mTHP without PMD support.

Yes, it would. However, I think this introduces an inconsistency.

Quoted 8 lines

powerpc has two types of MMU (HASH and RADIX) with different page layout. MMU type is selected at boottime based on the capabilities of the CPU. For transparent page you have:

static inline int has_transparent_hugepage(void) {
     if (radix_enabled())
         return radix__has_transparent_hugepage(); return hash__has_transparent_hugepage(); }

Quoted 3 lines

Right and, unless I'm misunderstanding your comment, this supports my position that the new arch_has_pmd_leaves() API is all about reporting a hardware capability and not tied to THP support.

Yes my comment was in reaction of comment below, I wanted to say that CONFIG_TRANSPARENT_HUGEPAGE is not always enough to tell if a powerpc actually has transparent hugepages or not.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

I do wonder whether the architecture should instead simply say "not supported"
if !CONFIG_TRANSPARENT_HUGEPAGE?

That should still enable your series: using mTHP without PMD support.

#32 lore raw

PATCH v8 10/14 mips: move has_transparent_hugepage() out of THP guard

In reply to Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

This commit moves has_transparent_hugepage() definition to arch/mips/mm/pgtable.c under CONFIG_CPU_SUPPORTS_HUGEPAGES guard in order to ensure that it's only used in CPUs that support the register operations performed.

---
 arch/mips/include/asm/pgtable.h |  6 +++---
 arch/mips/mm/pgtable.c          | 25 +++++++++++++++++++++++++
 arch/mips/mm/tlb-r4k.c          | 22 ----------------------
 3 files changed, 28 insertions(+), 25 deletions(-)
arch/mips/include/asm/pgtable.h +3 -3
index fa7b935f947c..b038da872ec6 100644
@@ -615,9 +615,6 @@ unsigned long io_remap_pfn_range_pfn(unsigned long pfn, unsigned long size);
 /* We don't have hardware dirty/accessed bits, generic_pmdp_establish is fine.*/
 #define pmdp_establish generic_pmdp_establish
 
-#define has_transparent_hugepage has_transparent_hugepage
-extern int has_transparent_hugepage(void);
-
 static inline int pmd_trans_huge(pmd_t pmd)
 {
 	return !!(pmd_val(pmd) & _PAGE_HUGE);
@@ -743,6 +740,9 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+#define has_transparent_hugepage has_transparent_hugepage
+extern int has_transparent_hugepage(void);
+
 #ifdef _PAGE_HUGE
 #define pmd_leaf(pmd)	((pmd_val(pmd) & _PAGE_HUGE) != 0)
 #define pud_leaf(pud)	((pud_val(pud) & _PAGE_HUGE) != 0)
arch/mips/mm/pgtable.c +25 -0
index 10835414819f..4515fa42f0b3 100644
@@ -23,3 +23,28 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 	return ret;
 }
 EXPORT_SYMBOL_GPL(pgd_alloc);
+
+#ifdef CONFIG_CPU_SUPPORTS_HUGEPAGES
+int has_transparent_hugepage(void)
+{
+	static unsigned int mask = -1;
+
+	if (mask == -1) {	/* first call comes during __init */
+		unsigned long flags;
+
+		local_irq_save(flags);
+		write_c0_pagemask(PM_HUGE_MASK);
+		back_to_back_c0_hazard();
+		mask = read_c0_pagemask();
+		write_c0_pagemask(PM_DEFAULT_MASK);
+		local_irq_restore(flags);
+	}
+	return mask == PM_HUGE_MASK;
+}
+#else
+int has_transparent_hugepage(void)
+{
+	return 0;
+}
+#endif
+EXPORT_SYMBOL(has_transparent_hugepage);
arch/mips/mm/tlb-r4k.c +0 -22
index 24fe85fa169d..625f1c0dd71e 100644
@@ -432,28 +432,6 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
 #endif
 }
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-
-int has_transparent_hugepage(void)
-{
-	static unsigned int mask = -1;
-
-	if (mask == -1) {	/* first call comes during __init */
-		unsigned long flags;
-
-		local_irq_save(flags);
-		write_c0_pagemask(PM_HUGE_MASK);
-		back_to_back_c0_hazard();
-		mask = read_c0_pagemask();
-		write_c0_pagemask(PM_DEFAULT_MASK);
-		local_irq_restore(flags);
-	}
-	return mask == PM_HUGE_MASK;
-}
-EXPORT_SYMBOL(has_transparent_hugepage);
-
-#endif /* CONFIG_TRANSPARENT_HUGEPAGE  */
-
 /*
  * Used for loading TLB entries before trap_init() has started, when we
  * don't actually want to add a wired entry which remains throughout the
Signature · 1 line
2.55.0
#33 lore raw

Re: [PATCH v8 10/14] mips: move has_transparent_hugepage() out of THP guard

Quoted 4 lines · Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Same thought as for the previous commit: it would be fine for now for the architecture to simply say "not supported" without CONFIG_TRANSPARENT_HUGEPAGE, right?

Signature · 3 lines
Cheers,

David
#34 lore raw

PATCH v8 11/14 x86: move has_transparent_hugepage() out of THP guard

In reply to Luiz Capitulino

A future commit will introduce a kernel API to allow for checking if the CPU supports PMD-sized pages. This API will be based on the has_transparent_hugepage() implementation but will be orthogonal to THP and therefore must work when CONFIG_TRANSPARENT_HUGEPAGE=n.

Move its definition out of the THP guard.

---
 arch/x86/include/asm/pgtable.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
arch/x86/include/asm/pgtable.h +6 -6
index d551120a7c88..f46c1b502eb1 100644
@@ -306,12 +306,6 @@ static inline int pud_trans_huge(pud_t pud)
 }
 #endif
 
-#define has_transparent_hugepage has_transparent_hugepage
-static inline int has_transparent_hugepage(void)
-{
-	return boot_cpu_has(X86_FEATURE_PSE);
-}
-
 #ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP
 static inline bool pmd_special(pmd_t pmd)
 {
@@ -337,6 +331,12 @@ static inline pud_t pud_mkspecial(pud_t pud)
 #endif	/* CONFIG_ARCH_SUPPORTS_PUD_PFNMAP */
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+#define has_transparent_hugepage has_transparent_hugepage
+static inline int has_transparent_hugepage(void)
+{
+	return boot_cpu_has(X86_FEATURE_PSE);
+}
+
 static inline pte_t pte_set_flags(pte_t pte, pteval_t set)
 {
 	pteval_t v = native_pte_val(pte);
Signature · 1 line
2.55.0
#35 lore raw

PATCH v8 12/14 treewide: introduce arch_has_pmd_leaves()

In reply to Luiz Capitulino

Now that all the has_transparent_hugepage() callers have been converted to pgtable_has_pmd_leaves(), this commit does two things:

1. Rename has_transparent_hugepage() arch implementations to arch_has_pmd_leaves(), since that's what the helper checks for

2. Introduce the default implementation of arch_has_pmd_leaves() as IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE). This means that if the arch doesn't implement arch_has_pmd_leaves() we default to checking CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE as a way to determine if PMD-sized pages are supported

Note that arch_has_pmd_leaves() is supposed to be called only by pgtable_leaf_support_init(). The remaining exception is hugepage_init() which will be converted in a future commit.

---
 arch/mips/include/asm/pgtable.h               |  4 ++--
 arch/mips/mm/pgtable.c                        |  6 +++---
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
 arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
 arch/powerpc/include/asm/book3s/64/pgtable.h  | 10 +++++-----
 arch/powerpc/include/asm/book3s/64/radix.h    |  2 +-
 arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
 arch/s390/include/asm/pgtable.h               |  4 ++--
 arch/x86/include/asm/pgtable.h                |  4 ++--
 include/linux/pgtable.h                       |  4 ++--
 mm/huge_memory.c                              |  2 +-
 mm/memory.c                                   |  2 +-
 12 files changed, 23 insertions(+), 23 deletions(-)
arch/mips/include/asm/pgtable.h +2 -2
index b038da872ec6..2a0a7ddf8bee 100644
@@ -740,8 +740,8 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-#define has_transparent_hugepage has_transparent_hugepage
-extern int has_transparent_hugepage(void);
+#define arch_has_pmd_leaves arch_has_pmd_leaves
+extern int arch_has_pmd_leaves(void);
 
 #ifdef _PAGE_HUGE
 #define pmd_leaf(pmd)	((pmd_val(pmd) & _PAGE_HUGE) != 0)
arch/mips/mm/pgtable.c +3 -3
index 4515fa42f0b3..f498fc58bd7a 100644
@@ -25,7 +25,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 EXPORT_SYMBOL_GPL(pgd_alloc);
 
 #ifdef CONFIG_CPU_SUPPORTS_HUGEPAGES
-int has_transparent_hugepage(void)
+int arch_has_pmd_leaves(void)
 {
 	static unsigned int mask = -1;
 
@@ -42,9 +42,9 @@ int has_transparent_hugepage(void)
 	return mask == PM_HUGE_MASK;
 }
 #else
-int has_transparent_hugepage(void)
+int arch_has_pmd_leaves(void)
 {
 	return 0;
 }
 #endif
-EXPORT_SYMBOL(has_transparent_hugepage);
+EXPORT_SYMBOL(arch_has_pmd_leaves);
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 79511e6abfca..d532e3f0dfc3 100644
@@ -167,7 +167,7 @@ extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pmd_t *pmdp);
 #endif
 
-extern int hash__has_transparent_hugepage(void);
+extern int hash__arch_has_pmd_leaves(void);
 #endif /* !__ASSEMBLER__ */
 
 #endif /* _ASM_POWERPC_BOOK3S_64_HASH_4K_H */
arch/powerpc/include/asm/book3s/64/hash-64k.h +1 -1
index a4a44a112ff9..d523c80f44f7 100644
@@ -280,7 +280,7 @@ extern pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 				       unsigned long addr, pmd_t *pmdp);
 #endif /*  CONFIG_TRANSPARENT_HUGEPAGE */
 
-extern int hash__has_transparent_hugepage(void);
+extern int hash__arch_has_pmd_leaves(void);
 #endif	/* __ASSEMBLER__ */
 
 #endif /* _ASM_POWERPC_BOOK3S_64_HASH_64K_H */
arch/powerpc/include/asm/book3s/64/pgtable.h +5 -5
index 522bb58b6241..88c417e3f016 100644
@@ -1425,14 +1425,14 @@ static inline bool arch_needs_pgtable_deposit(void)
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-extern int hash__has_transparent_hugepage(void);
-static inline int has_transparent_hugepage(void)
+extern int hash__arch_has_pmd_leaves(void);
+static inline int arch_has_pmd_leaves(void)
 {
 	if (radix_enabled())
-		return radix__has_transparent_hugepage();
-	return hash__has_transparent_hugepage();
+		return radix__arch_has_pmd_leaves();
+	return hash__arch_has_pmd_leaves();
 }
-#define has_transparent_hugepage has_transparent_hugepage
+#define arch_has_pmd_leaves arch_has_pmd_leaves
 
 #define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION
 pte_t ptep_modify_prot_start(struct vm_area_struct *, unsigned long, pte_t *);
arch/powerpc/include/asm/book3s/64/radix.h +1 -1
index 50545cf519bd..e8b74528953f 100644
@@ -307,7 +307,7 @@ static inline int radix__has_transparent_pud_hugepage(void)
 }
 #endif
 
-static inline int radix__has_transparent_hugepage(void)
+static inline int radix__arch_has_pmd_leaves(void)
 {
 	/* For radix 2M at PMD level means thp */
 	if (mmu_psize_defs[MMU_PAGE_2M].shift == PMD_SHIFT)
arch/powerpc/mm/book3s64/hash_pgtable.c +2 -2
index 50316f3fc5d3..a67bc1723404 100644
@@ -393,7 +393,7 @@ pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-int hash__has_transparent_hugepage(void)
+int hash__arch_has_pmd_leaves(void)
 {
 
 	if (!mmu_has_feature(MMU_FTR_16M_PAGE))
@@ -422,7 +422,7 @@ int hash__has_transparent_hugepage(void)
 
 	return 1;
 }
-EXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
+EXPORT_SYMBOL_GPL(hash__arch_has_pmd_leaves);
 
 #ifdef CONFIG_STRICT_KERNEL_RWX
 
arch/s390/include/asm/pgtable.h +2 -2
index e7a2bf9636b7..d4a7c938e2e4 100644
@@ -1816,8 +1816,8 @@ static inline int pmd_trans_huge(pmd_t pmd)
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-#define has_transparent_hugepage has_transparent_hugepage
-static inline int has_transparent_hugepage(void)
+#define arch_has_pmd_leaves arch_has_pmd_leaves
+static inline int arch_has_pmd_leaves(void)
 {
 	return cpu_has_edat1() ? 1 : 0;
 }
arch/x86/include/asm/pgtable.h +2 -2
index f46c1b502eb1..b4de9068d461 100644
@@ -331,8 +331,8 @@ static inline pud_t pud_mkspecial(pud_t pud)
 #endif	/* CONFIG_ARCH_SUPPORTS_PUD_PFNMAP */
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-#define has_transparent_hugepage has_transparent_hugepage
-static inline int has_transparent_hugepage(void)
+#define arch_has_pmd_leaves arch_has_pmd_leaves
+static inline int arch_has_pmd_leaves(void)
 {
 	return boot_cpu_has(X86_FEATURE_PSE);
 }
include/linux/pgtable.h +2 -2
index ad852a415114..fd2c73335604 100644
@@ -2359,8 +2359,8 @@ static inline void __init pgtable_leaf_support_init(void) { }
 #endif
 #endif
 
-#ifndef has_transparent_hugepage
-#define has_transparent_hugepage() IS_BUILTIN(CONFIG_TRANSPARENT_HUGEPAGE)
+#ifndef arch_has_pmd_leaves
+#define arch_has_pmd_leaves() IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE)
 #endif
 
 #ifndef has_transparent_pud_hugepage
mm/huge_memory.c +1 -1
index b49bffe36d22..c9ee387a59f9 100644
@@ -1091,7 +1091,7 @@ static int __init hugepage_init(void)
 	int err;
 	struct kobject *hugepage_kobj;
 
-	if (!has_transparent_hugepage()) {
+	if (!arch_has_pmd_leaves()) {
 		transparent_hugepage_flags = 1 << TRANSPARENT_HUGEPAGE_UNSUPPORTED;
 		return -EINVAL;
 	}
mm/memory.c +1 -1
index 900539acb7d9..cbd9082d33a0 100644
@@ -169,7 +169,7 @@ EXPORT_SYMBOL(__arch_has_pmd_leaves_key);
 
 void __init pgtable_leaf_support_init(void)
 {
-	if (!has_transparent_hugepage())
+	if (!arch_has_pmd_leaves())
 		static_branch_disable(&__arch_has_pmd_leaves_key);
 }
 
Signature · 1 line
2.55.0
reviewer Acked-by on v8 12/14
#36 lore raw

Re: [PATCH v8 12/14] treewide: introduce arch_has_pmd_leaves()

Quoted 17 lines · Luiz Capitulino

Now that all the has_transparent_hugepage() callers have been converted to pgtable_has_pmd_leaves(), this commit does two things:

1. Rename has_transparent_hugepage() arch implementations to arch_has_pmd_leaves(), since that's what the helper checks for

2. Introduce the default implementation of arch_has_pmd_leaves() as IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE). This means that if the arch doesn't implement arch_has_pmd_leaves() we default to checking CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE as a way to determine if PMD-sized pages are supported

Note that arch_has_pmd_leaves() is supposed to be called only by pgtable_leaf_support_init(). The remaining exception is hugepage_init() which will be converted in a future commit.

Quoted 15 lines
---
 arch/mips/include/asm/pgtable.h               |  4 ++--
 arch/mips/mm/pgtable.c                        |  6 +++---
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
 arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
 arch/powerpc/include/asm/book3s/64/pgtable.h  | 10 +++++-----
 arch/powerpc/include/asm/book3s/64/radix.h    |  2 +-
 arch/powerpc/mm/book3s64/hash_pgtable.c       |  4 ++--
 arch/s390/include/asm/pgtable.h               |  4 ++--
 arch/x86/include/asm/pgtable.h                |  4 ++--
 include/linux/pgtable.h                       |  4 ++--
 mm/huge_memory.c                              |  2 +-
 mm/memory.c                                   |  2 +-
 12 files changed, 23 insertions(+), 23 deletions(-)
Signature · 2 lines
Best Regards,
Yan, Zi
#37 lore raw

PATCH v8 13/14 mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()

In reply to Luiz Capitulino

Despite its name, thp_disabled_by_hw() just checks whether the architecture supports PMD-sized pages. It returns true when TRANSPARENT_HUGEPAGE_UNSUPPORTED is set in transparent_hugepage_flags, this only occurs if the architecture implements arch_has_pmd_leaves() and that function returns false.

Since pgtable_has_pmd_leaves() provides the same semantics, use it instead.

---
 include/linux/huge_mm.h | 7 -------
 mm/huge_memory.c        | 6 ++----
 mm/memory.c             | 2 +-
 mm/shmem.c              | 2 +-
 4 files changed, 4 insertions(+), 13 deletions(-)
include/linux/huge_mm.h +0 -7
index 8ca0fa3be2ac..196f17afcb5d 100644
@@ -47,7 +47,6 @@ vm_fault_t vmf_insert_folio_pud(struct vm_fault *vmf, struct folio *folio,
 				bool write);
 
 enum transparent_hugepage_flag {
-	TRANSPARENT_HUGEPAGE_UNSUPPORTED,
 	TRANSPARENT_HUGEPAGE_FLAG,
 	TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG,
 	TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG,
@@ -383,12 +382,6 @@ static inline bool vma_thp_disabled(struct vm_area_struct *vma,
 	return mm_flags_test(MMF_DISABLE_THP_EXCEPT_ADVISED, vma->vm_mm);
 }
 
-static inline bool thp_disabled_by_hw(void)
-{
-	/* If the hardware/firmware marked hugepage support disabled. */
-	return transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_UNSUPPORTED);
-}
-
 unsigned long thp_get_unmapped_area(struct file *filp, unsigned long addr,
 		unsigned long len, unsigned long pgoff, unsigned long flags);
 unsigned long thp_get_unmapped_area_vmaflags(struct file *filp, unsigned long addr,
mm/huge_memory.c +2 -4
index c9ee387a59f9..a06025b87e7c 100644
@@ -196,7 +196,7 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	if (!vma->vm_mm)		/* vdso */
 		return 0;
 
-	if (thp_disabled_by_hw() || vma_thp_disabled(vma, vm_flags, forced_collapse))
+	if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vm_flags, forced_collapse))
 		return 0;
 
 	/* khugepaged doesn't collapse DAX vma, but page fault is fine. */
@@ -1091,10 +1091,8 @@ static int __init hugepage_init(void)
 	int err;
 	struct kobject *hugepage_kobj;
 
-	if (!arch_has_pmd_leaves()) {
-		transparent_hugepage_flags = 1 << TRANSPARENT_HUGEPAGE_UNSUPPORTED;
+	if (!pgtable_has_pmd_leaves())
 		return -EINVAL;
-	}
 
 	/*
 	 * hugepages can't be allocated by the buddy allocator
mm/memory.c +1 -1
index cbd9082d33a0..9e7d2d73fa5b 100644
@@ -5694,7 +5694,7 @@ vm_fault_t do_set_pmd(struct vm_fault *vmf, struct folio *folio, struct page *pa
 	 * PMD mappings if THPs are disabled. As we already have a THP,
 	 * behave as if we are forcing a collapse.
 	 */
-	if (thp_disabled_by_hw() || vma_thp_disabled(vma, vma->vm_flags,
+	if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vma->vm_flags,
 						     /* forced_collapse=*/ true))
 		return ret;
 
mm/shmem.c +1 -1
index 930657d05375..bc2de3a7c1ea 100644
@@ -2048,7 +2048,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
 	unsigned int global_orders;
 
-	if (thp_disabled_by_hw() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
+	if (!pgtable_has_pmd_leaves() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
 		return 0;
 
 	global_orders = shmem_huge_global_enabled(inode, index, write_end,
Signature · 1 line
2.55.0
#38 lore raw

Re: [PATCH v8 13/14] mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()

Quoted 9 lines · Luiz Capitulino

Despite its name, thp_disabled_by_hw() just checks whether the architecture supports PMD-sized pages. It returns true when TRANSPARENT_HUGEPAGE_UNSUPPORTED is set in transparent_hugepage_flags, this only occurs if the architecture implements arch_has_pmd_leaves() and that function returns false.

Since pgtable_has_pmd_leaves() provides the same semantics, use it instead.

Quoted 6 lines

---

Yes, that's a nice cleanup.

Signature · 3 lines
Cheers,

David
#39 lore raw

PATCH v8 14/14 mm: thp: always enable mTHP support

In reply to Luiz Capitulino

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

---
 mm/huge_memory.c | 25 ++++++++++++++++++++-----
 mm/shmem.c       | 14 +++++++++-----
 2 files changed, 29 insertions(+), 10 deletions(-)
mm/huge_memory.c +20 -5
index a06025b87e7c..a2d6de3ea988 100644
@@ -189,6 +189,15 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	else
 		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	orders &= supported_orders;
 	if (!orders)
 		return 0;
@@ -196,7 +205,7 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	if (!vma->vm_mm)		/* vdso */
 		return 0;
 
-	if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vm_flags, forced_collapse))
+	if (vma_thp_disabled(vma, vm_flags, forced_collapse))
 		return 0;
 
 	/* khugepaged doesn't collapse DAX vma, but page fault is fine. */
@@ -979,7 +988,7 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
 	 * disable all other sizes. powerpc's PMD_ORDER isn't a compile-time
 	 * constant so we have to do this here.
 	 */
-	if (!anon_orders_configured)
+	if (!anon_orders_configured && pgtable_has_pmd_leaves())
 		huge_anon_orders_inherit = BIT(PMD_ORDER);
 
 	*hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
@@ -1001,6 +1010,15 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
 	}
 
 	orders = THP_ORDERS_ALL_ANON | THP_ORDERS_ALL_FILE_DEFAULT;
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	order = highest_order(orders);
 	while (orders) {
 		thpsize = thpsize_create(order, *hugepage_kobj);
@@ -1091,9 +1109,6 @@ static int __init hugepage_init(void)
 	int err;
 	struct kobject *hugepage_kobj;
 
-	if (!pgtable_has_pmd_leaves())
-		return -EINVAL;
-
 	/*
 	 * hugepages can't be allocated by the buddy allocator
 	 */
mm/shmem.c +9 -5
index bc2de3a7c1ea..8c0f7e3efeeb 100644
@@ -2046,11 +2046,14 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	unsigned long mask = READ_ONCE(huge_shmem_orders_always);
 	unsigned long within_size_orders = READ_ONCE(huge_shmem_orders_within_size);
 	vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
-	unsigned int global_orders;
+	unsigned int global_orders, disabled_orders = 0;
 
-	if (!pgtable_has_pmd_leaves() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
+	if (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force))
 		return 0;
 
+	if (!pgtable_has_pmd_leaves())
+		disabled_orders = BIT(PMD_ORDER);
+
 	global_orders = shmem_huge_global_enabled(inode, index, write_end,
 						  shmem_huge_force, vma, vm_flags);
 	/*
@@ -2058,7 +2061,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	 * sysfs configs.
 	 */
 	if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force)
-		return global_orders;
+		return global_orders & ~disabled_orders;
 
 	/*
 	 * Following the 'deny' semantics of the top level, force the huge
@@ -2072,7 +2075,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	 * means non-PMD sized THP can not override 'huge' mount option now.
 	 */
 	if (shmem_huge == SHMEM_HUGE_FORCE)
-		return READ_ONCE(huge_shmem_orders_inherit);
+		return READ_ONCE(huge_shmem_orders_inherit) & ~disabled_orders;
 
 	/* Allow mTHP that will be fully within i_size. */
 	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -2083,6 +2086,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	if (global_orders > 0)
 		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~disabled_orders;
 	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
 }
 
@@ -5630,7 +5634,7 @@ void __init shmem_init(void)
 	 * Default to setting PMD-sized THP to inherit the global setting and
 	 * disable all other multi-size THPs.
 	 */
-	if (!shmem_orders_configured)
+	if (!shmem_orders_configured && pgtable_has_pmd_leaves())
 		huge_shmem_orders_inherit = BIT(HPAGE_PMD_ORDER);
 #endif
 	return;
Signature · 1 line
2.55.0
reviewer Reviewed-by on v8 14/14
#40 lore raw
Quoted 11 lines · Luiz Capitulino

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

Quoted 7 lines

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

---

LGTM.

reviewer
#41 lore raw
In reply to Luiz Capitulino
Quoted 77 lines · On Thu, 17 Sep 2026 21:45:35 -0400 Luiz Capitulino <[email protected]> wrote:

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

---
 mm/huge_memory.c | 25 ++++++++++++++++++++-----
 mm/shmem.c       | 14 +++++++++-----
 2 files changed, 29 insertions(+), 10 deletions(-)
mm/huge_memory.c +20 -2
index a06025b87e7c..a2d6de3ea988 100644
@@ -189,6 +189,15 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	else
 		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	orders &= supported_orders;
 	if (!orders)
 		return 0;
@@ -196,7 +205,7 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	if (!vma->vm_mm)		/* vdso */
 		return 0;
 
-	if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vm_flags, forced_collapse))
+	if (vma_thp_disabled(vma, vm_flags, forced_collapse))
 		return 0;
 
 	/* khugepaged doesn't collapse DAX vma, but page fault is fine. */
@@ -979,7 +988,7 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
 	 * disable all other sizes. powerpc's PMD_ORDER isn't a compile-time
 	 * constant so we have to do this here.
 	 */
-	if (!anon_orders_configured)
+	if (!anon_orders_configured && pgtable_has_pmd_leaves())
 		huge_anon_orders_inherit = BIT(PMD_ORDER);
 
 	*hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
@@ -1001,6 +1010,15 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
 	}
 
 	orders = THP_ORDERS_ALL_ANON | THP_ORDERS_ALL_FILE_DEFAULT;
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	order = highest_order(orders);
 	while (orders) {
 		thpsize = thpsize_create(order, *hugepage_kobj);

mm/huge_memory.c static int __init hugepage_init(void) · line 1096

Quoted 6 lines
mm/huge_memory.c +0 -3
@@ -1091,9 +1109,6 @@ static int __init hugepage_init(void)
 	int err;
 	struct kobject *hugepage_kobj;
 
-	if (!pgtable_has_pmd_leaves())
-		return -EINVAL;
-

Removing this guard lets start_stop_khugepaged() run on a system without PMD leaves. All mTHP orders default to never, but the global always or madvise flag still makes hugepage_enabled() return true.

That starts an idle khugepaged thread and can unnecessarily raise min_free_kbytes. Could hugepage_enabled() instead test whether an enabled order remains after masking PMD_ORDER when pgtable_has_pmd_leaves() is false?

Quoted 63 lines
mm/huge_memory.c +0 -0
 	/*
 	 * hugepages can't be allocated by the buddy allocator
 	 */
mm/shmem.c +9 -5
index bc2de3a7c1ea..8c0f7e3efeeb 100644
@@ -2046,11 +2046,14 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	unsigned long mask = READ_ONCE(huge_shmem_orders_always);
 	unsigned long within_size_orders = READ_ONCE(huge_shmem_orders_within_size);
 	vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
-	unsigned int global_orders;
+	unsigned int global_orders, disabled_orders = 0;
 
-	if (!pgtable_has_pmd_leaves() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
+	if (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force))
 		return 0;
 
+	if (!pgtable_has_pmd_leaves())
+		disabled_orders = BIT(PMD_ORDER);
+
 	global_orders = shmem_huge_global_enabled(inode, index, write_end,
 						  shmem_huge_force, vma, vm_flags);
 	/*
@@ -2058,7 +2061,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	 * sysfs configs.
 	 */
 	if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force)
-		return global_orders;
+		return global_orders & ~disabled_orders;
 
 	/*
 	 * Following the 'deny' semantics of the top level, force the huge
@@ -2072,7 +2075,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	 * means non-PMD sized THP can not override 'huge' mount option now.
 	 */
 	if (shmem_huge == SHMEM_HUGE_FORCE)
-		return READ_ONCE(huge_shmem_orders_inherit);
+		return READ_ONCE(huge_shmem_orders_inherit) & ~disabled_orders;
 
 	/* Allow mTHP that will be fully within i_size. */
 	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -2083,6 +2086,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	if (global_orders > 0)
 		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~disabled_orders;
 	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
 }
 
@@ -5630,7 +5634,7 @@ void __init shmem_init(void)
 	 * Default to setting PMD-sized THP to inherit the global setting and
 	 * disable all other multi-size THPs.
 	 */
-	if (!shmem_orders_configured)
+	if (!shmem_orders_configured && pgtable_has_pmd_leaves())
 		huge_shmem_orders_inherit = BIT(HPAGE_PMD_ORDER);
 #endif
 	return;
Signature · 1 line
2.55.0
#42 lore raw
Quoted 90 lines · Usama Arif
Quoted 77 lines · On Thu, 17 Sep 2026 21:45:35 -0400 Luiz Capitulino <[email protected]> wrote:

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

---
  mm/huge_memory.c | 25 ++++++++++++++++++++-----
  mm/shmem.c       | 14 +++++++++-----
  2 files changed, 29 insertions(+), 10 deletions(-)
mm/huge_memory.c +20 -2
index a06025b87e7c..a2d6de3ea988 100644
@@ -189,6 +189,15 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
  	else
  		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
  	orders &= supported_orders;
  	if (!orders)
  		return 0;
@@ -196,7 +205,7 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
  	if (!vma->vm_mm)		/* vdso */
  		return 0;
 
-	if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vm_flags, forced_collapse))
+	if (vma_thp_disabled(vma, vm_flags, forced_collapse))
  		return 0;
 
  	/* khugepaged doesn't collapse DAX vma, but page fault is fine. */
@@ -979,7 +988,7 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
  	 * disable all other sizes. powerpc's PMD_ORDER isn't a compile-time
  	 * constant so we have to do this here.
  	 */
-	if (!anon_orders_configured)
+	if (!anon_orders_configured && pgtable_has_pmd_leaves())
  		huge_anon_orders_inherit = BIT(PMD_ORDER);
 
  	*hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
@@ -1001,6 +1010,15 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
  	}
 
  	orders = THP_ORDERS_ALL_ANON | THP_ORDERS_ALL_FILE_DEFAULT;
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
  	order = highest_order(orders);
  	while (orders) {
  		thpsize = thpsize_create(order, *hugepage_kobj);

mm/huge_memory.c static int __init hugepage_init(void) · line 1096

Quoted 6 lines
mm/huge_memory.c +0 -3
@@ -1091,9 +1109,6 @@ static int __init hugepage_init(void)
  	int err;
  	struct kobject *hugepage_kobj;
 
-	if (!pgtable_has_pmd_leaves())
-		return -EINVAL;
-

Removing this guard lets start_stop_khugepaged() run on a system without PMD leaves. All mTHP orders default to never, but the global always or madvise flag still makes hugepage_enabled() return true.

Quoted 4 lines

That starts an idle khugepaged thread and can unnecessarily raise min_free_kbytes. Could hugepage_enabled() instead test whether an enabled order remains after masking PMD_ORDER when pgtable_has_pmd_leaves() is false?

You're right about the issue. Before this series, checking the global configuration was sufficient to determine if THP was "enabled" as PMD leaves would always be available (otherwise THP would be shut down). With this series, we also need to check pgtable_has_pmd_leaves().

I prefer the simple fix below over masking orders in hugepage_enabled():
it's simple and adds the check only in the call site that requires it.

mm/khugepaged.c +1 -1
index a3a9e4d93b46..175252a1d729 100644
@@ -453,7 +453,7 @@ static bool hugepage_enabled(void)
 	 * Shmem pmd-sized hugepages are also determined by its pmd-size control,
 	 * except when the global shmem_huge is set to SHMEM_HUGE_DENY.
 	 */
-	if (hugepage_global_enabled())
+	if (hugepage_global_enabled() && pgtable_has_pmd_leaves())
 		return true;
 	if (anon_hpage_enabled())
 		return true;
Quoted 65 lines
Quoted 64 lines
mm/huge_memory.c +0 -0
  	/*
  	 * hugepages can't be allocated by the buddy allocator
  	 */
mm/shmem.c +9 -5
index bc2de3a7c1ea..8c0f7e3efeeb 100644
@@ -2046,11 +2046,14 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	unsigned long mask = READ_ONCE(huge_shmem_orders_always);
  	unsigned long within_size_orders = READ_ONCE(huge_shmem_orders_within_size);
  	vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
-	unsigned int global_orders;
+	unsigned int global_orders, disabled_orders = 0;
 
-	if (!pgtable_has_pmd_leaves() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
+	if (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force))
  		return 0;
 
+	if (!pgtable_has_pmd_leaves())
+		disabled_orders = BIT(PMD_ORDER);
+
  	global_orders = shmem_huge_global_enabled(inode, index, write_end,
  						  shmem_huge_force, vma, vm_flags);
  	/*
@@ -2058,7 +2061,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	 * sysfs configs.
  	 */
  	if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force)
-		return global_orders;
+		return global_orders & ~disabled_orders;
 
  	/*
  	 * Following the 'deny' semantics of the top level, force the huge
@@ -2072,7 +2075,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	 * means non-PMD sized THP can not override 'huge' mount option now.
  	 */
  	if (shmem_huge == SHMEM_HUGE_FORCE)
-		return READ_ONCE(huge_shmem_orders_inherit);
+		return READ_ONCE(huge_shmem_orders_inherit) & ~disabled_orders;
 
  	/* Allow mTHP that will be fully within i_size. */
  	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -2083,6 +2086,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	if (global_orders > 0)
  		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~disabled_orders;
  	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
  }
 
@@ -5630,7 +5634,7 @@ void __init shmem_init(void)
  	 * Default to setting PMD-sized THP to inherit the global setting and
  	 * disable all other multi-size THPs.
  	 */
-	if (!shmem_orders_configured)
+	if (!shmem_orders_configured && pgtable_has_pmd_leaves())
  		huge_shmem_orders_inherit = BIT(HPAGE_PMD_ORDER);
  #endif
  	return;
Signature · 1 line
2.55.0
reviewer
#43 lore raw
In reply to Luiz Capitulino
Quoted 8 lines · Luiz Capitulino

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Quoted 7 lines

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

Nit: "and PUD" can go.

David mentioned

"
There is no need to disable PUD-orders, as shmem does not support PUD-orders (unlike DAX). So you can keep it simpler here.
"

in v4

https://lore.kernel.org/all/[email protected]/

Cheers, Lance

#44 lore raw
Quoted 22 lines · Lance Yang
Quoted 8 lines · Luiz Capitulino

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Quoted 7 lines

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

Nit: "and PUD" can go.

David mentioned

Quoted 8 lines

"
There is no need to disable PUD-orders, as shmem does not support PUD-orders (unlike DAX). So you can keep it simpler here.
"

in v4

https://lore.kernel.org/all/[email protected]/

Yes, I changed the code and didn't update the changelog. Fixed now, thanks Lance.

Quoted 2 lines

Cheers, Lance

reviewer
#45 lore raw
In reply to Luiz Capitulino
Quoted 108 lines · Luiz Capitulino

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

---
  mm/huge_memory.c | 25 ++++++++++++++++++++-----
  mm/shmem.c       | 14 +++++++++-----
  2 files changed, 29 insertions(+), 10 deletions(-)
mm/huge_memory.c +20 -5
index a06025b87e7c..a2d6de3ea988 100644
@@ -189,6 +189,15 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
  	else
  		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
  	orders &= supported_orders;
  	if (!orders)
  		return 0;
@@ -196,7 +205,7 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
  	if (!vma->vm_mm)		/* vdso */
  		return 0;
 
-	if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vm_flags, forced_collapse))
+	if (vma_thp_disabled(vma, vm_flags, forced_collapse))
  		return 0;
 
  	/* khugepaged doesn't collapse DAX vma, but page fault is fine. */
@@ -979,7 +988,7 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
  	 * disable all other sizes. powerpc's PMD_ORDER isn't a compile-time
  	 * constant so we have to do this here.
  	 */
-	if (!anon_orders_configured)
+	if (!anon_orders_configured && pgtable_has_pmd_leaves())
  		huge_anon_orders_inherit = BIT(PMD_ORDER);
 
  	*hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
@@ -1001,6 +1010,15 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
  	}
 
  	orders = THP_ORDERS_ALL_ANON | THP_ORDERS_ALL_FILE_DEFAULT;
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * If the CPU does not support PMD leaves, assume for
+		 * now that it does not support PUD leaves and disable
+		 * both folio orders.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
  	order = highest_order(orders);
  	while (orders) {
  		thpsize = thpsize_create(order, *hugepage_kobj);
@@ -1091,9 +1109,6 @@ static int __init hugepage_init(void)
  	int err;
  	struct kobject *hugepage_kobj;
 
-	if (!pgtable_has_pmd_leaves())
-		return -EINVAL;
-
  	/*
  	 * hugepages can't be allocated by the buddy allocator
  	 */
mm/shmem.c +5 -2
index bc2de3a7c1ea..8c0f7e3efeeb 100644
@@ -2046,11 +2046,14 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	unsigned long mask = READ_ONCE(huge_shmem_orders_always);
  	unsigned long within_size_orders = READ_ONCE(huge_shmem_orders_within_size);
  	vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
-	unsigned int global_orders;
+	unsigned int global_orders, disabled_orders = 0;
 
-	if (!pgtable_has_pmd_leaves() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
+	if (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force))
  		return 0;
 
+	if (!pgtable_has_pmd_leaves())
+		disabled_orders = BIT(PMD_ORDER);
+
  	global_orders = shmem_huge_global_enabled(inode, index, write_end,
  						  shmem_huge_force, vma, vm_flags);
  	/*

mm/shmem.c unsigned long shmem_allowable_huge_orders(struct inode *inode, · line 2064

Quoted 5 lines
mm/shmem.c +1 -1
@@ -2058,7 +2061,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	 * sysfs configs.
  	 */
  	if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force)
-		return global_orders;
+		return global_orders & ~disabled_orders;

If you move the 'disabled_orders' filtering logic into shmem_huge_global_enabled(), as I mentioned in patch 8, then this change can be removed.

mm/shmem.c unsigned long shmem_allowable_huge_orders(struct inode *inode, · line 2078

Quoted 8 lines
mm/shmem.c +1 -1
  	/*
  	 * Following the 'deny' semantics of the top level, force the huge
@@ -2072,7 +2075,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	 * means non-PMD sized THP can not override 'huge' mount option now.
  	 */
  	if (shmem_huge == SHMEM_HUGE_FORCE)
-		return READ_ONCE(huge_shmem_orders_inherit);
+		return READ_ONCE(huge_shmem_orders_inherit) & ~disabled_orders;

Since you've already disabled PMD-sized and PUD-sized orders for the 'shmem_enabled' interface in hugepage_init_sysfs(), this change can also be dropped.

mm/shmem.c unsigned long shmem_allowable_huge_orders(struct inode *inode, · line 2089

Quoted 7 lines
mm/shmem.c +1 -0
  	/* Allow mTHP that will be fully within i_size. */
  	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -2083,6 +2086,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	if (global_orders > 0)
  		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~disabled_orders;

Ditto.

Quoted 2 lines
mm/shmem.c +0 -0
  	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
  }
#46 lore raw
Quoted 122 lines · Baolin Wang
Quoted 100 lines · Luiz Capitulino

If PMD-sized pages are not supported on an architecture (ie. the arch implements arch_has_pmd_leaves() and it returns false) then the current code disables all THP, including mTHP.

This commit fixes this by allowing mTHP to be always enabled for all archs. When PMD-sized pages are not supported, its sysfs entry won't be created and their mapping will be disallowed at page-fault time.

Similarly, this commit implements the following changes for shmem in shmem_allowable_huge_orders():

- Drop the pgtable_has_pmd_leaves() check so that mTHP sizes are considered
- Filter out PMD and PUD orders from allowable orders when PMD-sized pages are not supported by the CPU

---
  mm/huge_memory.c | 25 ++++++++++++++++++++-----
  mm/shmem.c       | 14 +++++++++-----
  2 files changed, 29 insertions(+), 10 deletions(-)
mm/huge_memory.c +20 -5
index a06025b87e7c..a2d6de3ea988 100644
@@ -189,6 +189,15 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
      else
          supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
+    if (!pgtable_has_pmd_leaves()) {
+        /*
+         * If the CPU does not support PMD leaves, assume for
+         * now that it does not support PUD leaves and disable
+         * both folio orders.
+         */
+        supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+    }
+
      orders &= supported_orders;
      if (!orders)
          return 0;
@@ -196,7 +205,7 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
      if (!vma->vm_mm)        /* vdso */
          return 0;
-    if (!pgtable_has_pmd_leaves() || vma_thp_disabled(vma, vm_flags, forced_collapse))
+    if (vma_thp_disabled(vma, vm_flags, forced_collapse))
          return 0;
      /* khugepaged doesn't collapse DAX vma, but page fault is fine. */
@@ -979,7 +988,7 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
       * disable all other sizes. powerpc's PMD_ORDER isn't a compile-time
       * constant so we have to do this here.
       */
-    if (!anon_orders_configured)
+    if (!anon_orders_configured && pgtable_has_pmd_leaves())
          huge_anon_orders_inherit = BIT(PMD_ORDER);
      *hugepage_kobj = kobject_create_and_add("transparent_hugepage", mm_kobj);
@@ -1001,6 +1010,15 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
      }
      orders = THP_ORDERS_ALL_ANON | THP_ORDERS_ALL_FILE_DEFAULT;
+    if (!pgtable_has_pmd_leaves()) {
+        /*
+         * If the CPU does not support PMD leaves, assume for
+         * now that it does not support PUD leaves and disable
+         * both folio orders.
+         */
+        orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+    }
+
      order = highest_order(orders);
      while (orders) {
          thpsize = thpsize_create(order, *hugepage_kobj);
@@ -1091,9 +1109,6 @@ static int __init hugepage_init(void)
      int err;
      struct kobject *hugepage_kobj;
-    if (!pgtable_has_pmd_leaves())
-        return -EINVAL;
-
      /*
       * hugepages can't be allocated by the buddy allocator
       */
mm/shmem.c +5 -2
index bc2de3a7c1ea..8c0f7e3efeeb 100644
@@ -2046,11 +2046,14 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
      unsigned long mask = READ_ONCE(huge_shmem_orders_always);
      unsigned long within_size_orders = READ_ONCE(huge_shmem_orders_within_size);
      vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
-    unsigned int global_orders;
+    unsigned int global_orders, disabled_orders = 0;
-    if (!pgtable_has_pmd_leaves() || (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force)))
+    if (vma && vma_thp_disabled(vma, vm_flags, shmem_huge_force))
          return 0;
+    if (!pgtable_has_pmd_leaves())
+        disabled_orders = BIT(PMD_ORDER);
+
      global_orders = shmem_huge_global_enabled(inode, index, write_end,
                            shmem_huge_force, vma, vm_flags);
      /*

mm/shmem.c unsigned long shmem_allowable_huge_orders(struct inode *inode, · line 2064

Quoted 5 lines
mm/shmem.c +1 -1
@@ -2058,7 +2061,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
       * sysfs configs.
       */
      if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force)
-        return global_orders;
+        return global_orders & ~disabled_orders;

If you move the 'disabled_orders' filtering logic into shmem_huge_global_enabled(), as I mentioned in patch 8, then this change can be removed.

mm/shmem.c unsigned long shmem_allowable_huge_orders(struct inode *inode, · line 2078

Quoted 8 lines
mm/shmem.c +1 -1
      /*
       * Following the 'deny' semantics of the top level, force the huge
@@ -2072,7 +2075,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
       * means non-PMD sized THP can not override 'huge' mount option now.
       */
      if (shmem_huge == SHMEM_HUGE_FORCE)
-        return READ_ONCE(huge_shmem_orders_inherit);
+        return READ_ONCE(huge_shmem_orders_inherit) & ~disabled_orders;

Since you've already disabled PMD-sized and PUD-sized orders for the 'shmem_enabled' interface in hugepage_init_sysfs(), this change can also be dropped.

Quoted 8 lines

mm/shmem.c unsigned long shmem_allowable_huge_orders(struct inode *inode, · line 2088

Quoted 6 lines
mm/shmem.c +1 -0
      /* Allow mTHP that will be fully within i_size. */
      mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -2083,6 +2086,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
      if (global_orders > 0)
          mask |= READ_ONCE(huge_shmem_orders_inherit);
+    mask &= ~disabled_orders;

Ditto.

Yes, I'll look into implementing your suggestions for the next version.

Quoted 4 lines
Quoted 3 lines
mm/shmem.c +0 -0
      return THP_ORDERS_ALL_FILE_DEFAULT & mask;
  }
reviewer
#47 lore raw
In reply to Luiz Capitulino
Quoted 6 lines · Luiz Capitulino

Introduction
============

Today, if an architecture implements has_transparent_hugepage() and the CPU lacks support for PMD-sized pages, the THP code disables all THP, including mTHP.

Hi Luiz,

Sorry for asking this so late in the series, but which CPUs lack support for PMD sized pages?

Is this series mainly for PowerPC?

Thanks,
Usama

Quoted 172 lines

This happens because the has_transparent_hugepage() helper is overloaded:
its name implies it checks whether THP is enabled, but on some architectures it actually checks whether the CPU supports PMD-sized pages. In addition, the THP and shmem code have a big switch on has_transparent_hugepage().

This series solves this by decoupling THP availability checking from querying CPU support for PMD-sized pages. THP availability can be checked with IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE). For querying PMD-sized page support, this series introduces a new helper called pgtable_has_pmd_leaves(), which is independent of THP, has well defined semantics and can be used in fast paths.

Core THP code and shmem can use pgtable_has_pmd_leaves() to determine PMD-sized THP support at page fault time (or folio allocation time for shmem), leaving THP and shmem always enabled for other THP sizes. For architectures and CPUs that do support PMD-sized pages there's no intended change in behavior.

We also convert each user of has_transparent_hugepage(), and the related helper thp_disabled_by_hw(), to IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) and/or pgtable_has_pmd_leaves() according to the call site's check semantics. On s390, powerpc, mips and x86 the arch has_transparent_hugepage() implementation is moved out of the CONFIG_TRANSPARENT_HUGEPAGE guard and renamed to arch_has_pmd_leaves().

Thanks to David Hildenbrand for suggesting this improvement and for providing initial guidance (all bugs and misconceptions are mine).

This applies to mm-new 93f615a22169 ("mm/swap, PM: hibernate: atomically replace hibernation pin").

Reporting availability of PMD-sized pages to user-space
=======================================================

Before this series, not having PMD pages available would shut down THP support meaning that /sys/kernel/mm/transparent_hugepage would not be available. After this series, /sys/kernel/mm/transparent_hugepage and hpage_pmd_size are always available but hugepages-<PMD-size>kB is only available if PMD-sized pages are supported.

To me this seems logical, as hugepages-<size>kB is only available if <size> is supported. If this is correct, then MM kselftests that depend on PMD-sized pages will need to be updated to check for it as they fail with this series applied when PMD-sized pages are not available.

The alternative is changing hpage_pmd_size: either don't create the file when PMD-sized pages are not available or set hpage_pmd_size=0. My concern is that this could be considered an ABI breakage as this file was never reported to be optional or report a zero value.

Testing
=======

- Ran MM kselftests on x86_64 and s390
- Tested all mTHP sizes allocation on x86_64 (with and without PMD-sized pages available)
- Tested shmem with within_size w/ mTHP on x86_64 (with and without PMD-sized pages available)
- Performed defconfig build on x86_64, s390, arm64, powerpc, and mips

NOTES:
* I'm forcing arch_has_pmd_leaves() off on x86 to simulate not having PMD-sized pages available

* Running the MM kselftests when PMD pages are not available causes some tests that depend on PMD-sized THP pages to fail as noted earlier

Changelog
=========

v8
--
- Fixed build breakage on MIPS (Lance)
- Rebased on top of mm-new (fixed conflicts in include/linux/pgtable.h and mm/shmem.c - dropped a Reviewed-by as a result)

v7
--
- Applied on top of latest mm-new
- Improved various changelogs including cover-letter
- Changed arch_has_pmd_leaves() default implementation to use IS_ENABLED() instead of IS_BUILTIN()

v6
--
- Rebased on top of mm-unstable (required a minor conflict resolution)
- Added more Reviewed-by and Acked-by tags

v5
--
- Moved init_arch_has_pmd_leaves() to mm_core_init() (David)
- Renamed init_arch_has_pmd_leaves() to pgtable_leaf_support_init() (Lance)
- Added new patch changing shmem_getattr() to set blksize according to highest supported THP order (Baolin)
- Added new patches to move has_transparent_hugepage() out of CONFIG_TRANSPARENT_HUGEPAGE guards
- shmem_allowable_huge_orders(): rename variable and only disable PMD_ORDER (David)
- Added <linux/jmp_label.h> to include/linux/pgtable.h
- Added new tags and removed Reviewed-by from changed patches

v4
--
- Used static key for pgtable_has_pmd_leaves() API (Lance)
- Moved shmem pgtable_has_pmd_leaves() check to shmem_allowable_huge_orders() (Baolin)
- Default pgtable_has_pmd_leaves() implementation to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) (Zi)
- Dropped patch “mm: thp: x86: cleanup PSE feature bit usage” (Dave)

v3
--
- Rebased on top of latest Linus tree
- Removed i915 patch as driver dropped has_transparent_hugepage() usage
- Moved init_arch_has_pmd_leaves() call in start_kernel() to avoid conflict with early_param handlers clearing CPU feature flags
- Fixed build error with CONFIG_MMU=n (kernel test robot)
- Fixed huge_anon_orders_inherit default setting when !pgtable_has_pmd_leaves() (Baolin)
- Small commit changelog improvements

v2
--
- Added support for always enabling mTHPs for shmem (Baolin)
- Improved commits changelog & added reviewed-by

v1
--
- Call init_arch_has_pmd_leaves() from start_kernel()
- Keep pgtable_has_pmd_leaves() calls tied to CONFIG_TRANSPARENT_HUGEPAGE (David)
- Clear PUD_ORDER when clearing PMD_ORDER (David)
- Small changelog improvements (David)
- Rebased on top of latest mm-new

Luiz Capitulino (14):
docs: tmpfs: remove implementation detail reference
mm: shmem: shmem_getattr(): set blksize to highest supported THP order
mm: introduce pgtable_has_pmd_leaves()
drivers: dax: use pgtable_has_pmd_leaves()
drivers: nvdimm: use pgtable_has_pmd_leaves()
mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()
mm: shmem: allow THP support determination at folio allocation time
s390: move has_transparent_hugepage() out of THP guard
powerpc: move has_transparent_hugepage() out of THP guard
mips: move has_transparent_hugepage() out of THP guard
x86: move has_transparent_hugepage() out of THP guard
treewide: introduce arch_has_pmd_leaves()
mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()
mm: thp: always enable mTHP support

 Documentation/filesystems/tmpfs.rst           |  5 ++--
 arch/mips/include/asm/pgtable.h               |  6 ++--
 arch/mips/mm/pgtable.c                        | 25 ++++++++++++++++
 arch/mips/mm/tlb-r4k.c                        | 22 --------------
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
 arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
 arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 ++++++------
 arch/powerpc/include/asm/book3s/64/radix.h    | 14 ++++-----
 arch/powerpc/mm/book3s64/hash_pgtable.c       |  8 ++---
 arch/s390/include/asm/pgtable.h               |  6 ++--
 arch/x86/include/asm/pgtable.h                | 12 ++++----
 drivers/dax/dax-private.h                     |  2 +-
 drivers/nvdimm/pfn_devs.c                     |  6 ++--
 include/linux/huge_mm.h                       |  7 -----
 include/linux/pgtable.h                       | 21 ++++++++++++--
 mm/debug_vm_pgtable.c                         | 20 ++++++-------
 mm/huge_memory.c                              | 27 ++++++++++++-----
 mm/memory.c                                   | 11 ++++++-
 mm/mm_init.c                                  |  1 +
 mm/shmem.c                                    | 29 ++++++++++++-------
 20 files changed, 144 insertions(+), 100 deletions(-)
#48 lore raw
Quoted 11 lines · Usama Arif
Quoted 6 lines · Luiz Capitulino

Introduction
============

Today, if an architecture implements has_transparent_hugepage() and the CPU lacks support for PMD-sized pages, the THP code disables all THP, including mTHP.

Quoted 6 lines

Hi Luiz,

Sorry for asking this so late in the series, but which CPUs lack support for PMD sized pages?

Is this series mainly for PowerPC?

No :)

The architectures that have conditional PMD page support (ie. discovered at run-time via has_transparent_hugepage()) are x86, s390, powerpc and mips.

For x86 for example, even on 64-bit there are cases where the PSE bit may not be present (eg. some errata and by hypervisor CPUID masking).

That being said, the main goal of the series is that has_transparent_hugepage() is overloaded: it has different semantics on those architectures and is used to gate all THP support (which is not the right thing to do for mTHP).

Quoted 177 lines

Thanks,
Usama

Quoted 172 lines

This happens because the has_transparent_hugepage() helper is overloaded:
its name implies it checks whether THP is enabled, but on some architectures it actually checks whether the CPU supports PMD-sized pages. In addition, the THP and shmem code have a big switch on has_transparent_hugepage().

This series solves this by decoupling THP availability checking from querying CPU support for PMD-sized pages. THP availability can be checked with IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE). For querying PMD-sized page support, this series introduces a new helper called pgtable_has_pmd_leaves(), which is independent of THP, has well defined semantics and can be used in fast paths.

Core THP code and shmem can use pgtable_has_pmd_leaves() to determine PMD-sized THP support at page fault time (or folio allocation time for shmem), leaving THP and shmem always enabled for other THP sizes. For architectures and CPUs that do support PMD-sized pages there's no intended change in behavior.

We also convert each user of has_transparent_hugepage(), and the related helper thp_disabled_by_hw(), to IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) and/or pgtable_has_pmd_leaves() according to the call site's check semantics. On s390, powerpc, mips and x86 the arch has_transparent_hugepage() implementation is moved out of the CONFIG_TRANSPARENT_HUGEPAGE guard and renamed to arch_has_pmd_leaves().

Thanks to David Hildenbrand for suggesting this improvement and for providing initial guidance (all bugs and misconceptions are mine).

This applies to mm-new 93f615a22169 ("mm/swap, PM: hibernate: atomically replace hibernation pin").

Reporting availability of PMD-sized pages to user-space
=======================================================

Before this series, not having PMD pages available would shut down THP support meaning that /sys/kernel/mm/transparent_hugepage would not be available. After this series, /sys/kernel/mm/transparent_hugepage and hpage_pmd_size are always available but hugepages-<PMD-size>kB is only available if PMD-sized pages are supported.

To me this seems logical, as hugepages-<size>kB is only available if <size> is supported. If this is correct, then MM kselftests that depend on PMD-sized pages will need to be updated to check for it as they fail with this series applied when PMD-sized pages are not available.

The alternative is changing hpage_pmd_size: either don't create the file when PMD-sized pages are not available or set hpage_pmd_size=0. My concern is that this could be considered an ABI breakage as this file was never reported to be optional or report a zero value.

Testing
=======

- Ran MM kselftests on x86_64 and s390
- Tested all mTHP sizes allocation on x86_64 (with and without PMD-sized pages available)
- Tested shmem with within_size w/ mTHP on x86_64 (with and without PMD-sized pages available)
- Performed defconfig build on x86_64, s390, arm64, powerpc, and mips

NOTES:
* I'm forcing arch_has_pmd_leaves() off on x86 to simulate not having PMD-sized pages available

* Running the MM kselftests when PMD pages are not available causes some tests that depend on PMD-sized THP pages to fail as noted earlier

Changelog
=========

v8
--
- Fixed build breakage on MIPS (Lance)
- Rebased on top of mm-new (fixed conflicts in include/linux/pgtable.h and mm/shmem.c - dropped a Reviewed-by as a result)

v7
--
- Applied on top of latest mm-new
- Improved various changelogs including cover-letter
- Changed arch_has_pmd_leaves() default implementation to use IS_ENABLED() instead of IS_BUILTIN()

v6
--
- Rebased on top of mm-unstable (required a minor conflict resolution)
- Added more Reviewed-by and Acked-by tags

v5
--
- Moved init_arch_has_pmd_leaves() to mm_core_init() (David)
- Renamed init_arch_has_pmd_leaves() to pgtable_leaf_support_init() (Lance)
- Added new patch changing shmem_getattr() to set blksize according to highest supported THP order (Baolin)
- Added new patches to move has_transparent_hugepage() out of CONFIG_TRANSPARENT_HUGEPAGE guards
- shmem_allowable_huge_orders(): rename variable and only disable PMD_ORDER (David)
- Added <linux/jmp_label.h> to include/linux/pgtable.h
- Added new tags and removed Reviewed-by from changed patches

v4
--
- Used static key for pgtable_has_pmd_leaves() API (Lance)
- Moved shmem pgtable_has_pmd_leaves() check to shmem_allowable_huge_orders() (Baolin)
- Default pgtable_has_pmd_leaves() implementation to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) (Zi)
- Dropped patch “mm: thp: x86: cleanup PSE feature bit usage” (Dave)

v3
--
- Rebased on top of latest Linus tree
- Removed i915 patch as driver dropped has_transparent_hugepage() usage
- Moved init_arch_has_pmd_leaves() call in start_kernel() to avoid conflict with early_param handlers clearing CPU feature flags
- Fixed build error with CONFIG_MMU=n (kernel test robot)
- Fixed huge_anon_orders_inherit default setting when !pgtable_has_pmd_leaves() (Baolin)
- Small commit changelog improvements

v2
--
- Added support for always enabling mTHPs for shmem (Baolin)
- Improved commits changelog & added reviewed-by

v1
--
- Call init_arch_has_pmd_leaves() from start_kernel()
- Keep pgtable_has_pmd_leaves() calls tied to CONFIG_TRANSPARENT_HUGEPAGE (David)
- Clear PUD_ORDER when clearing PMD_ORDER (David)
- Small changelog improvements (David)
- Rebased on top of latest mm-new

Luiz Capitulino (14):
docs: tmpfs: remove implementation detail reference
mm: shmem: shmem_getattr(): set blksize to highest supported THP order
mm: introduce pgtable_has_pmd_leaves()
drivers: dax: use pgtable_has_pmd_leaves()
drivers: nvdimm: use pgtable_has_pmd_leaves()
mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()
mm: shmem: allow THP support determination at folio allocation time
s390: move has_transparent_hugepage() out of THP guard
powerpc: move has_transparent_hugepage() out of THP guard
mips: move has_transparent_hugepage() out of THP guard
x86: move has_transparent_hugepage() out of THP guard
treewide: introduce arch_has_pmd_leaves()
mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()
mm: thp: always enable mTHP support

  Documentation/filesystems/tmpfs.rst           |  5 ++--
  arch/mips/include/asm/pgtable.h               |  6 ++--
  arch/mips/mm/pgtable.c                        | 25 ++++++++++++++++
  arch/mips/mm/tlb-r4k.c                        | 22 --------------
  arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
  arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
  arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 ++++++------
  arch/powerpc/include/asm/book3s/64/radix.h    | 14 ++++-----
  arch/powerpc/mm/book3s64/hash_pgtable.c       |  8 ++---
  arch/s390/include/asm/pgtable.h               |  6 ++--
  arch/x86/include/asm/pgtable.h                | 12 ++++----
  drivers/dax/dax-private.h                     |  2 +-
  drivers/nvdimm/pfn_devs.c                     |  6 ++--
  include/linux/huge_mm.h                       |  7 -----
  include/linux/pgtable.h                       | 21 ++++++++++++--
  mm/debug_vm_pgtable.c                         | 20 ++++++-------
  mm/huge_memory.c                              | 27 ++++++++++++-----
  mm/memory.c                                   | 11 ++++++-
  mm/mm_init.c                                  |  1 +
  mm/shmem.c                                    | 29 ++++++++++++-------
  20 files changed, 144 insertions(+), 100 deletions(-)
#49 lore raw
Quoted 27 lines · Luiz Capitulino
Quoted 11 lines · Usama Arif
Quoted 6 lines · Luiz Capitulino

Introduction
============

Today, if an architecture implements has_transparent_hugepage() and the CPU lacks support for PMD-sized pages, the THP code disables all THP, including mTHP.

Quoted 6 lines

Hi Luiz,

Sorry for asking this so late in the series, but which CPUs lack support for PMD sized pages?

Is this series mainly for PowerPC?

No :)

The architectures that have conditional PMD page support (ie. discovered at run-time via has_transparent_hugepage()) are x86, s390, powerpc and mips.

Quoted 7 lines

For x86 for example, even on 64-bit there are cases where the PSE bit may not be present (eg. some errata and by hypervisor CPUID masking).

That being said, the main goal of the series is that has_transparent_hugepage() is overloaded: it has different semantics on those architectures and is used to gate all THP support (which is not the right thing to do for mTHP).

Yes, and I hope that we can then follow-up with similar PUD support cleanup to rework some of that CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD magic.

Signature · 3 lines
Cheers,

David
reviewer Acked-by
#50 lore raw
In reply to Luiz Capitulino
Quoted 27 lines · On Fri, 18 Sep 2026 10:01:35 -0400 Luiz Capitulino <[email protected]> wrote:
Quoted 11 lines · Usama Arif
Quoted 6 lines · Luiz Capitulino

Introduction
============

Today, if an architecture implements has_transparent_hugepage() and the CPU lacks support for PMD-sized pages, the THP code disables all THP, including mTHP.

Quoted 6 lines

Hi Luiz,

Sorry for asking this so late in the series, but which CPUs lack support for PMD sized pages?

Is this series mainly for PowerPC?

No :)

The architectures that have conditional PMD page support (ie. discovered at run-time via has_transparent_hugepage()) are x86, s390, powerpc and mips.

Quoted 7 lines

For x86 for example, even on 64-bit there are cases where the PSE bit may not be present (eg. some errata and by hypervisor CPUID masking).

That being said, the main goal of the series is that has_transparent_hugepage() is overloaded: it has different semantics on those architectures and is used to gate all THP support (which is not the right thing to do for mTHP).

Thanks for clearing that and the patches.

They all look good to me, apart from the the comment in the last patch.

Feel free to add

to all of them apart from last.

One nit: I would put the above goal of clean up at the start of the coverletter, instead of starting with adding mTHP support for CPUs that lack PMD.

Thanks,
Usama

Quoted 180 lines
Quoted 178 lines

Thanks,
Usama

Quoted 172 lines

This happens because the has_transparent_hugepage() helper is overloaded:
its name implies it checks whether THP is enabled, but on some architectures it actually checks whether the CPU supports PMD-sized pages. In addition, the THP and shmem code have a big switch on has_transparent_hugepage().

This series solves this by decoupling THP availability checking from querying CPU support for PMD-sized pages. THP availability can be checked with IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE). For querying PMD-sized page support, this series introduces a new helper called pgtable_has_pmd_leaves(), which is independent of THP, has well defined semantics and can be used in fast paths.

Core THP code and shmem can use pgtable_has_pmd_leaves() to determine PMD-sized THP support at page fault time (or folio allocation time for shmem), leaving THP and shmem always enabled for other THP sizes. For architectures and CPUs that do support PMD-sized pages there's no intended change in behavior.

We also convert each user of has_transparent_hugepage(), and the related helper thp_disabled_by_hw(), to IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) and/or pgtable_has_pmd_leaves() according to the call site's check semantics. On s390, powerpc, mips and x86 the arch has_transparent_hugepage() implementation is moved out of the CONFIG_TRANSPARENT_HUGEPAGE guard and renamed to arch_has_pmd_leaves().

Thanks to David Hildenbrand for suggesting this improvement and for providing initial guidance (all bugs and misconceptions are mine).

This applies to mm-new 93f615a22169 ("mm/swap, PM: hibernate: atomically replace hibernation pin").

Reporting availability of PMD-sized pages to user-space
=======================================================

Before this series, not having PMD pages available would shut down THP support meaning that /sys/kernel/mm/transparent_hugepage would not be available. After this series, /sys/kernel/mm/transparent_hugepage and hpage_pmd_size are always available but hugepages-<PMD-size>kB is only available if PMD-sized pages are supported.

To me this seems logical, as hugepages-<size>kB is only available if <size> is supported. If this is correct, then MM kselftests that depend on PMD-sized pages will need to be updated to check for it as they fail with this series applied when PMD-sized pages are not available.

The alternative is changing hpage_pmd_size: either don't create the file when PMD-sized pages are not available or set hpage_pmd_size=0. My concern is that this could be considered an ABI breakage as this file was never reported to be optional or report a zero value.

Testing
=======

- Ran MM kselftests on x86_64 and s390
- Tested all mTHP sizes allocation on x86_64 (with and without PMD-sized pages available)
- Tested shmem with within_size w/ mTHP on x86_64 (with and without PMD-sized pages available)
- Performed defconfig build on x86_64, s390, arm64, powerpc, and mips

NOTES:
* I'm forcing arch_has_pmd_leaves() off on x86 to simulate not having PMD-sized pages available

* Running the MM kselftests when PMD pages are not available causes some tests that depend on PMD-sized THP pages to fail as noted earlier

Changelog
=========

v8
--
- Fixed build breakage on MIPS (Lance)
- Rebased on top of mm-new (fixed conflicts in include/linux/pgtable.h and mm/shmem.c - dropped a Reviewed-by as a result)

v7
--
- Applied on top of latest mm-new
- Improved various changelogs including cover-letter
- Changed arch_has_pmd_leaves() default implementation to use IS_ENABLED() instead of IS_BUILTIN()

v6
--
- Rebased on top of mm-unstable (required a minor conflict resolution)
- Added more Reviewed-by and Acked-by tags

v5
--
- Moved init_arch_has_pmd_leaves() to mm_core_init() (David)
- Renamed init_arch_has_pmd_leaves() to pgtable_leaf_support_init() (Lance)
- Added new patch changing shmem_getattr() to set blksize according to highest supported THP order (Baolin)
- Added new patches to move has_transparent_hugepage() out of CONFIG_TRANSPARENT_HUGEPAGE guards
- shmem_allowable_huge_orders(): rename variable and only disable PMD_ORDER (David)
- Added <linux/jmp_label.h> to include/linux/pgtable.h
- Added new tags and removed Reviewed-by from changed patches

v4
--
- Used static key for pgtable_has_pmd_leaves() API (Lance)
- Moved shmem pgtable_has_pmd_leaves() check to shmem_allowable_huge_orders() (Baolin)
- Default pgtable_has_pmd_leaves() implementation to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) (Zi)
- Dropped patch “mm: thp: x86: cleanup PSE feature bit usage” (Dave)

v3
--
- Rebased on top of latest Linus tree
- Removed i915 patch as driver dropped has_transparent_hugepage() usage
- Moved init_arch_has_pmd_leaves() call in start_kernel() to avoid conflict with early_param handlers clearing CPU feature flags
- Fixed build error with CONFIG_MMU=n (kernel test robot)
- Fixed huge_anon_orders_inherit default setting when !pgtable_has_pmd_leaves() (Baolin)
- Small commit changelog improvements

v2
--
- Added support for always enabling mTHPs for shmem (Baolin)
- Improved commits changelog & added reviewed-by

v1
--
- Call init_arch_has_pmd_leaves() from start_kernel()
- Keep pgtable_has_pmd_leaves() calls tied to CONFIG_TRANSPARENT_HUGEPAGE (David)
- Clear PUD_ORDER when clearing PMD_ORDER (David)
- Small changelog improvements (David)
- Rebased on top of latest mm-new

Luiz Capitulino (14):
docs: tmpfs: remove implementation detail reference
mm: shmem: shmem_getattr(): set blksize to highest supported THP order
mm: introduce pgtable_has_pmd_leaves()
drivers: dax: use pgtable_has_pmd_leaves()
drivers: nvdimm: use pgtable_has_pmd_leaves()
mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()
mm: shmem: allow THP support determination at folio allocation time
s390: move has_transparent_hugepage() out of THP guard
powerpc: move has_transparent_hugepage() out of THP guard
mips: move has_transparent_hugepage() out of THP guard
x86: move has_transparent_hugepage() out of THP guard
treewide: introduce arch_has_pmd_leaves()
mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()
mm: thp: always enable mTHP support

  Documentation/filesystems/tmpfs.rst           |  5 ++--
  arch/mips/include/asm/pgtable.h               |  6 ++--
  arch/mips/mm/pgtable.c                        | 25 ++++++++++++++++
  arch/mips/mm/tlb-r4k.c                        | 22 --------------
  arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
  arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
  arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 ++++++------
  arch/powerpc/include/asm/book3s/64/radix.h    | 14 ++++-----
  arch/powerpc/mm/book3s64/hash_pgtable.c       |  8 ++---
  arch/s390/include/asm/pgtable.h               |  6 ++--
  arch/x86/include/asm/pgtable.h                | 12 ++++----
  drivers/dax/dax-private.h                     |  2 +-
  drivers/nvdimm/pfn_devs.c                     |  6 ++--
  include/linux/huge_mm.h                       |  7 -----
  include/linux/pgtable.h                       | 21 ++++++++++++--
  mm/debug_vm_pgtable.c                         | 20 ++++++-------
  mm/huge_memory.c                              | 27 ++++++++++++-----
  mm/memory.c                                   | 11 ++++++-
  mm/mm_init.c                                  |  1 +
  mm/shmem.c                                    | 29 ++++++++++++-------
  20 files changed, 144 insertions(+), 100 deletions(-)
#51 lore raw
Quoted 43 lines · Usama Arif
Quoted 27 lines · On Fri, 18 Sep 2026 10:01:35 -0400 Luiz Capitulino <[email protected]> wrote:
Quoted 11 lines · Usama Arif
Quoted 6 lines · Luiz Capitulino

Introduction
============

Today, if an architecture implements has_transparent_hugepage() and the CPU lacks support for PMD-sized pages, the THP code disables all THP, including mTHP.

Quoted 6 lines

Hi Luiz,

Sorry for asking this so late in the series, but which CPUs lack support for PMD sized pages?

Is this series mainly for PowerPC?

No :)

The architectures that have conditional PMD page support (ie. discovered at run-time via has_transparent_hugepage()) are x86, s390, powerpc and mips.

Quoted 7 lines

For x86 for example, even on 64-bit there are cases where the PSE bit may not be present (eg. some errata and by hypervisor CPUID masking).

That being said, the main goal of the series is that has_transparent_hugepage() is overloaded: it has different semantics on those architectures and is used to gate all THP support (which is not the right thing to do for mTHP).

Thanks for clearing that and the patches.

They all look good to me, apart from the the comment in the last patch.

Feel free to add

Quoted 7 lines

to all of them apart from last.

One nit: I would put the above goal of clean up at the start of the coverletter, instead of starting with adding mTHP support for CPUs that lack PMD.

Thanks Usama, I appreciate the review. I'll do this if I need to send a new version.

Quoted 185 lines

Thanks,
Usama

Quoted 180 lines
Quoted 178 lines

Thanks,
Usama

Quoted 172 lines

This happens because the has_transparent_hugepage() helper is overloaded:
its name implies it checks whether THP is enabled, but on some architectures it actually checks whether the CPU supports PMD-sized pages. In addition, the THP and shmem code have a big switch on has_transparent_hugepage().

This series solves this by decoupling THP availability checking from querying CPU support for PMD-sized pages. THP availability can be checked with IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE). For querying PMD-sized page support, this series introduces a new helper called pgtable_has_pmd_leaves(), which is independent of THP, has well defined semantics and can be used in fast paths.

Core THP code and shmem can use pgtable_has_pmd_leaves() to determine PMD-sized THP support at page fault time (or folio allocation time for shmem), leaving THP and shmem always enabled for other THP sizes. For architectures and CPUs that do support PMD-sized pages there's no intended change in behavior.

We also convert each user of has_transparent_hugepage(), and the related helper thp_disabled_by_hw(), to IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) and/or pgtable_has_pmd_leaves() according to the call site's check semantics. On s390, powerpc, mips and x86 the arch has_transparent_hugepage() implementation is moved out of the CONFIG_TRANSPARENT_HUGEPAGE guard and renamed to arch_has_pmd_leaves().

Thanks to David Hildenbrand for suggesting this improvement and for providing initial guidance (all bugs and misconceptions are mine).

This applies to mm-new 93f615a22169 ("mm/swap, PM: hibernate: atomically replace hibernation pin").

Reporting availability of PMD-sized pages to user-space
=======================================================

Before this series, not having PMD pages available would shut down THP support meaning that /sys/kernel/mm/transparent_hugepage would not be available. After this series, /sys/kernel/mm/transparent_hugepage and hpage_pmd_size are always available but hugepages-<PMD-size>kB is only available if PMD-sized pages are supported.

To me this seems logical, as hugepages-<size>kB is only available if <size> is supported. If this is correct, then MM kselftests that depend on PMD-sized pages will need to be updated to check for it as they fail with this series applied when PMD-sized pages are not available.

The alternative is changing hpage_pmd_size: either don't create the file when PMD-sized pages are not available or set hpage_pmd_size=0. My concern is that this could be considered an ABI breakage as this file was never reported to be optional or report a zero value.

Testing
=======

- Ran MM kselftests on x86_64 and s390
- Tested all mTHP sizes allocation on x86_64 (with and without PMD-sized pages available)
- Tested shmem with within_size w/ mTHP on x86_64 (with and without PMD-sized pages available)
- Performed defconfig build on x86_64, s390, arm64, powerpc, and mips

NOTES:
* I'm forcing arch_has_pmd_leaves() off on x86 to simulate not having PMD-sized pages available

* Running the MM kselftests when PMD pages are not available causes some tests that depend on PMD-sized THP pages to fail as noted earlier

Changelog
=========

v8
--
- Fixed build breakage on MIPS (Lance)
- Rebased on top of mm-new (fixed conflicts in include/linux/pgtable.h and mm/shmem.c - dropped a Reviewed-by as a result)

v7
--
- Applied on top of latest mm-new
- Improved various changelogs including cover-letter
- Changed arch_has_pmd_leaves() default implementation to use IS_ENABLED() instead of IS_BUILTIN()

v6
--
- Rebased on top of mm-unstable (required a minor conflict resolution)
- Added more Reviewed-by and Acked-by tags

v5
--
- Moved init_arch_has_pmd_leaves() to mm_core_init() (David)
- Renamed init_arch_has_pmd_leaves() to pgtable_leaf_support_init() (Lance)
- Added new patch changing shmem_getattr() to set blksize according to highest supported THP order (Baolin)
- Added new patches to move has_transparent_hugepage() out of CONFIG_TRANSPARENT_HUGEPAGE guards
- shmem_allowable_huge_orders(): rename variable and only disable PMD_ORDER (David)
- Added <linux/jmp_label.h> to include/linux/pgtable.h
- Added new tags and removed Reviewed-by from changed patches

v4
--
- Used static key for pgtable_has_pmd_leaves() API (Lance)
- Moved shmem pgtable_has_pmd_leaves() check to shmem_allowable_huge_orders() (Baolin)
- Default pgtable_has_pmd_leaves() implementation to IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE) (Zi)
- Dropped patch “mm: thp: x86: cleanup PSE feature bit usage” (Dave)

v3
--
- Rebased on top of latest Linus tree
- Removed i915 patch as driver dropped has_transparent_hugepage() usage
- Moved init_arch_has_pmd_leaves() call in start_kernel() to avoid conflict with early_param handlers clearing CPU feature flags
- Fixed build error with CONFIG_MMU=n (kernel test robot)
- Fixed huge_anon_orders_inherit default setting when !pgtable_has_pmd_leaves() (Baolin)
- Small commit changelog improvements

v2
--
- Added support for always enabling mTHPs for shmem (Baolin)
- Improved commits changelog & added reviewed-by

v1
--
- Call init_arch_has_pmd_leaves() from start_kernel()
- Keep pgtable_has_pmd_leaves() calls tied to CONFIG_TRANSPARENT_HUGEPAGE (David)
- Clear PUD_ORDER when clearing PMD_ORDER (David)
- Small changelog improvements (David)
- Rebased on top of latest mm-new

Luiz Capitulino (14):
docs: tmpfs: remove implementation detail reference
mm: shmem: shmem_getattr(): set blksize to highest supported THP order
mm: introduce pgtable_has_pmd_leaves()
drivers: dax: use pgtable_has_pmd_leaves()
drivers: nvdimm: use pgtable_has_pmd_leaves()
mm: debug_vm_pgtable: use pgtable_has_pmd_leaves()
mm: shmem: allow THP support determination at folio allocation time
s390: move has_transparent_hugepage() out of THP guard
powerpc: move has_transparent_hugepage() out of THP guard
mips: move has_transparent_hugepage() out of THP guard
x86: move has_transparent_hugepage() out of THP guard
treewide: introduce arch_has_pmd_leaves()
mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()
mm: thp: always enable mTHP support

   Documentation/filesystems/tmpfs.rst           |  5 ++--
   arch/mips/include/asm/pgtable.h               |  6 ++--
   arch/mips/mm/pgtable.c                        | 25 ++++++++++++++++
   arch/mips/mm/tlb-r4k.c                        | 22 --------------
   arch/powerpc/include/asm/book3s/64/hash-4k.h  |  2 +-
   arch/powerpc/include/asm/book3s/64/hash-64k.h |  2 +-
   arch/powerpc/include/asm/book3s/64/pgtable.h  | 18 ++++++------
   arch/powerpc/include/asm/book3s/64/radix.h    | 14 ++++-----
   arch/powerpc/mm/book3s64/hash_pgtable.c       |  8 ++---
   arch/s390/include/asm/pgtable.h               |  6 ++--
   arch/x86/include/asm/pgtable.h                | 12 ++++----
   drivers/dax/dax-private.h                     |  2 +-
   drivers/nvdimm/pfn_devs.c                     |  6 ++--
   include/linux/huge_mm.h                       |  7 -----
   include/linux/pgtable.h                       | 21 ++++++++++++--
   mm/debug_vm_pgtable.c                         | 20 ++++++-------
   mm/huge_memory.c                              | 27 ++++++++++++-----
   mm/memory.c                                   | 11 ++++++-
   mm/mm_init.c                                  |  1 +
   mm/shmem.c                                    | 29 ++++++++++++-------
   20 files changed, 144 insertions(+), 100 deletions(-)