Skip to contentSkip to conversation

mm: thp: always enable mTHP support

PATCH Superseded Luiz Capitulino 6 participants 36 messages May 1 – May 19

Subsystems (12)

v4 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

v4 cover mm: thp: always enable mTHP support

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. In addition, the kernel lacks a well defined API to check for PMD-sized page support. It currently relies on has_transparent_hugepage() and thp_disabled_by_hw(), but they are not well defined and are tied to THP support.

This series addresses both issues by introducing a new well defined API to query PMD-sized page support: pgtable_has_pmd_leaves(). Using this new helper, we ensure that mTHP remains enabled even when the architecture or CPU doesn't support PMD-sized pages.

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

This applies to Linus tree 08d0d3466664 ("Merge tag 'net-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")

NOTE: I used Claude Code Opus 4.6 to *review* the series before posting. It did find one issue where a pgtable_has_pmd_leaves() check was missing when assining huge_shmem_orders_inherit in shmem_init().

v4
--
- Use 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_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 (9):
docs: tmpfs: remove implementation detail reference
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: drop has_transparent_hugepage() usage
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               |  4 +--
 arch/mips/mm/tlb-r4k.c                        |  4 +--
 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 +--
 drivers/dax/dax-private.h                     |  2 +-
 drivers/nvdimm/pfn_devs.c                     |  6 +++--
 include/linux/huge_mm.h                       |  7 ------
 include/linux/pgtable.h                       | 19 ++++++++++++--
 init/main.c                                   |  1 +
 mm/debug_vm_pgtable.c                         | 20 +++++++--------
 mm/huge_memory.c                              | 25 +++++++++++++------
 mm/memory.c                                   | 11 +++++++-
 mm/shmem.c                                    | 21 +++++++++-------
 19 files changed, 93 insertions(+), 60 deletions(-)
Signature · 1 line
2.53.0
#1 lore raw

PATCH v4 1/9 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.53.0
#2 lore raw

PATCH v4 2/9 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(). This helper is used in fast paths

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

The new helper requires an initialization step which is performed by init_arch_has_pmd_leaves(). We call init_arch_has_pmd_leaves() early during boot in start_kernel() right after parse_early_param() but before parse_args(). This allows early_param() handlers to change CPU flags if needed (eg. parse_memopt() in x86-32) while also allowing users to use the API from __setup() handlers.

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

---
 include/linux/pgtable.h | 15 +++++++++++++++
 init/main.c             |  1 +
 mm/memory.c             |  9 +++++++++
 3 files changed, 25 insertions(+)
include/linux/pgtable.h +15 -0
index cdd68ed3ae1a..b365be3516bf 100644
@@ -2243,6 +2243,21 @@ static inline const char *pgtable_level_to_str(enum pgtable_level level)
 	}
 }
 
+#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 init_arch_has_pmd_leaves(void);
+#else
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return false;
+}
+static inline void __init init_arch_has_pmd_leaves(void) { }
+#endif
+
 #endif /* !__ASSEMBLY__ */
 
 #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
init/main.c +1 -0
index 96f93bb06c49..eea7c5bdddf7 100644
@@ -1053,6 +1053,7 @@ void start_kernel(void)
 	print_kernel_cmdline(saved_command_line);
 	/* parameters may set static keys */
 	parse_early_param();
+	init_arch_has_pmd_leaves();
 	after_dashes = parse_args("Booting kernel",
 				  static_command_line, __start___param,
 				  __stop___param - __start___param,
mm/memory.c +9 -0
index ea6568571131..90b2d9e84320 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 init_arch_has_pmd_leaves(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);
Signature · 1 line
2.53.0
#3 lore raw

(sashiko review) Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

Quoted 40 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(). This helper is used in fast paths

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

The new helper requires an initialization step which is performed by init_arch_has_pmd_leaves(). We call init_arch_has_pmd_leaves() early during boot in start_kernel() right after parse_early_param() but before parse_args(). This allows early_param() handlers to change CPU flags if needed (eg. parse_memopt() in x86-32) while also allowing users to use the API from __setup() handlers.

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

---
  include/linux/pgtable.h | 15 +++++++++++++++
  init/main.c             |  1 +
  mm/memory.c             |  9 +++++++++
  3 files changed, 25 insertions(+)
include/linux/pgtable.h +0 -0
index cdd68ed3ae1a..b365be3516bf 100644

include/linux/pgtable.h static inline const char *pgtable_level_to_str(enum pgtable_level level) · line 2247

Quoted 5 lines
include/linux/pgtable.h +2 -0
@@ -2243,6 +2243,21 @@ static inline const char *pgtable_level_to_str(enum pgtable_level level)
  	}
  }
 
+#ifdef CONFIG_MMU
+DECLARE_STATIC_KEY_TRUE(__arch_has_pmd_leaves_key);

"""
This adds DECLARE_STATIC_KEY_TRUE and static_branch_likely but doesn't explicitly include linux/jump_label.h. Can this cause build breakages in compilation units that include linux/pgtable.h but do not have linux/jump_label.h in their dependency chain?
"""

I've built this series for a few archs and I'm sure the kernel bot built it too, so I'd assume this is not an issue. But since adding the header doesn't hurt, I can do it.

Quoted 35 lines
include/linux/pgtable.h +13 -0
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return static_branch_likely(&__arch_has_pmd_leaves_key);
+}
+void __init init_arch_has_pmd_leaves(void);
+#else
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return false;
+}
+static inline void __init init_arch_has_pmd_leaves(void) { }
+#endif
+
  #endif /* !__ASSEMBLY__ */
 
  #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
init/main.c +1 -0
index 96f93bb06c49..eea7c5bdddf7 100644
@@ -1053,6 +1053,7 @@ void start_kernel(void)
  	print_kernel_cmdline(saved_command_line);
  	/* parameters may set static keys */
  	parse_early_param();
+	init_arch_has_pmd_leaves();
  	after_dashes = parse_args("Booting kernel",
  				  static_command_line, __start___param,
  				  __stop___param - __start___param,
mm/memory.c +0 -0
index ea6568571131..90b2d9e84320 100644
@@ -164,6 +164,15 @@ __setup("norandmaps", disable_randmaps);
 
  unsigned long highest_memmap_pfn __read_mostly;

mm/memory.c __setup("norandmaps", disable_randmaps); · line 175

Quoted 10 lines
mm/memory.c +9 -0
 
+DEFINE_STATIC_KEY_TRUE(__arch_has_pmd_leaves_key);
+EXPORT_SYMBOL(__arch_has_pmd_leaves_key);
+
+void __init init_arch_has_pmd_leaves(void)
+{
+	if (!has_transparent_hugepage())
+		static_branch_disable(&__arch_has_pmd_leaves_key);
+}
+

"""
Will this unconditionally disable the static key when CONFIG_TRANSPARENT_HUGEPAGE is not enabled, since has_transparent_hugepage() evaluates to 0 in that configuration?
This seems to contradict the commit message's goal of decoupling the hardware capability check from THP enablement.
I see this is fixed later in the patch series in commit 4782375d13da ("treewide: introduce arch_has_pmd_leaves()"), but could this cause regressions for non-THP PMD users if they test at this point in the series? """

There's no users of the new API in this patch and all converted users in the following patches are protected by #ifdef CONFIG_TRANSPARENT_HUGEPAGE so it's not an issue.

Quoted 3 lines
mm/memory.c +0 -0
  void mm_trace_rss_stat(struct mm_struct *mm, int member)
  {
  	trace_rss_stat(mm, member);
#4 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

In reply to Luiz Capitulino
Quoted 66 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(). This helper is used in fast paths

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

The new helper requires an initialization step which is performed by init_arch_has_pmd_leaves(). We call init_arch_has_pmd_leaves() early during boot in start_kernel() right after parse_early_param() but before parse_args(). This allows early_param() handlers to change CPU flags if needed (eg. parse_memopt() in x86-32) while also allowing users to use the API from __setup() handlers.

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

---
 include/linux/pgtable.h | 15 +++++++++++++++
 init/main.c             |  1 +
 mm/memory.c             |  9 +++++++++
 3 files changed, 25 insertions(+)
include/linux/pgtable.h +15 -0
index cdd68ed3ae1a..b365be3516bf 100644
@@ -2243,6 +2243,21 @@ static inline const char *pgtable_level_to_str(enum pgtable_level level)
 	}
 }
 
+#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 init_arch_has_pmd_leaves(void);
+#else
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return false;
+}
+static inline void __init init_arch_has_pmd_leaves(void) { }
+#endif
+
 #endif /* !__ASSEMBLY__ */
 
 #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
init/main.c +0 -0
index 96f93bb06c49..eea7c5bdddf7 100644

init/main.c void start_kernel(void) · line 1056

Quoted 4 lines
init/main.c +1 -0
@@ -1053,6 +1053,7 @@ void start_kernel(void)
 	print_kernel_cmdline(saved_command_line);
 	/* parameters may set static keys */
 	parse_early_param();
+	init_arch_has_pmd_leaves();

Can't we do this a bit later from some mm code?

This feels like something that can just go somewhere into mm_core_init()?

There, we should probably call this something like XXX_init(), and prepare it from detecting support for PUD leaves as well.

Maybe just

pgtable_init() ?

Signature · 3 lines
Cheers,

David
#5 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

Quoted 74 lines · David Hildenbrand (Arm)
Quoted 66 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(). This helper is used in fast paths

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

The new helper requires an initialization step which is performed by init_arch_has_pmd_leaves(). We call init_arch_has_pmd_leaves() early during boot in start_kernel() right after parse_early_param() but before parse_args(). This allows early_param() handlers to change CPU flags if needed (eg. parse_memopt() in x86-32) while also allowing users to use the API from __setup() handlers.

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

---
  include/linux/pgtable.h | 15 +++++++++++++++
  init/main.c             |  1 +
  mm/memory.c             |  9 +++++++++
  3 files changed, 25 insertions(+)
include/linux/pgtable.h +15 -0
index cdd68ed3ae1a..b365be3516bf 100644
@@ -2243,6 +2243,21 @@ static inline const char *pgtable_level_to_str(enum pgtable_level level)
  	}
  }
 
+#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 init_arch_has_pmd_leaves(void);
+#else
+static inline bool pgtable_has_pmd_leaves(void)
+{
+	return false;
+}
+static inline void __init init_arch_has_pmd_leaves(void) { }
+#endif
+
  #endif /* !__ASSEMBLY__ */
 
  #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
init/main.c +0 -0
index 96f93bb06c49..eea7c5bdddf7 100644

init/main.c void start_kernel(void) · line 1056

Quoted 4 lines
init/main.c +1 -0
@@ -1053,6 +1053,7 @@ void start_kernel(void)
  	print_kernel_cmdline(saved_command_line);
  	/* parameters may set static keys */
  	parse_early_param();
+	init_arch_has_pmd_leaves();

Can't we do this a bit later from some mm code?

Quoted 1 line

This feels like something that can just go somewhere into mm_core_init()?

Yes, this can be done. My intent in calling it as early as possible was to allow callers to use the API from __setup() handlers if needed, but since we don't have this case in the code today we can put it in mm_core_init() for now.

Quoted 6 lines

There, we should probably call this something like XXX_init(), and prepare it from detecting support for PUD leaves as well.

Maybe just

pgtable_init() ?

What about pgtable_api_init()? I'm afraid that pgtable_init() might be confused with code doing real page table initialization.

reviewer
#6 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

On Wed, May 13, 2026 at 09:52:05PM -0400, Luiz Capitulino wrote:
[...]

Quoted 14 lines
Quoted 14 lines

init/main.c void start_kernel(void) · line 1056

Quoted 11 lines
  
  #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
init/main.c +1 -0
index 96f93bb06c49..eea7c5bdddf7 100644
@@ -1053,6 +1053,7 @@ void start_kernel(void)
  	print_kernel_cmdline(saved_command_line);
  	/* parameters may set static keys */
  	parse_early_param();
+	init_arch_has_pmd_leaves();

Can't we do this a bit later from some mm code?

Quoted 1 line
Quoted 1 line

This feels like something that can just go somewhere into mm_core_init()?

Indeed. If nobody needs pgtable_has_pmd_leaves() that early, it is cleaner to put it in MM init code :)

Quoted 8 lines

Yes, this can be done. My intent in calling it as early as possible was to allow callers to use the API from __setup() handlers if needed, but since we don't have this case in the code today we can put it in mm_core_init() for now.

Quoted 3 lines

There, we should probably call this something like XXX_init(), and prepare it from detecting support for PUD leaves as well.

Quoted 6 lines
Quoted 3 lines

Maybe just

pgtable_init() ?

What about pgtable_api_init()? I'm afraid that pgtable_init() might be confused with code doing real page table initialization.

Or what about pgtable_leaf_support_init()? It says what this init is for, setting up the cached leaf-support check. Today that only means PMD leaves, but the name still works if we add PUD leaves later :)

Cheers, Lance

#7 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

Quoted 32 lines · Lance Yang

On Wed, May 13, 2026 at 09:52:05PM -0400, Luiz Capitulino wrote:
[...]

Quoted 14 lines
Quoted 14 lines

init/main.c void start_kernel(void) · line 1056

Quoted 11 lines
   
   #if !defined(MAX_POSSIBLE_PHYSMEM_BITS) && !defined(CONFIG_64BIT)
init/main.c +1 -0
index 96f93bb06c49..eea7c5bdddf7 100644
@@ -1053,6 +1053,7 @@ void start_kernel(void)
   	print_kernel_cmdline(saved_command_line);
   	/* parameters may set static keys */
   	parse_early_param();
+	init_arch_has_pmd_leaves();

Can't we do this a bit later from some mm code?

Quoted 1 line
Quoted 1 line

This feels like something that can just go somewhere into mm_core_init()?

Indeed. If nobody needs pgtable_has_pmd_leaves() that early, it is cleaner to put it in MM init code :)

Quoted 10 lines

Yes, this can be done. My intent in calling it as early as possible was to allow callers to use the API from __setup() handlers if needed, but since we don't have this case in the code today we can put it in mm_core_init() for now.

Quoted 5 lines

There, we should probably call this something like XXX_init(), and prepare it from detecting support for PUD leaves as well.

Maybe just

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

pgtable_init() ?

What about pgtable_api_init()? I'm afraid that pgtable_init() might be confused with code doing real page table initialization.

Or what about pgtable_leaf_support_init()? It says what this init is for, setting up the cached leaf-support check. Today that only means PMD leaves, but the name still works if we add PUD leaves later :)

Yes, I can use that one.

#8 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

In reply to Luiz Capitulino
Quoted 12 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(). This helper is used in fast paths

Quoted 5 lines

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

Oh, one more thing: what will be the semantics regarding CONFIG_TRANSPARENT_HUGEPAGE?

I assume it will only return true if CONFIG_TRANSPARENT_HUGEPAGE is enabled, correct?

That is, for example, relevant for patch #2.

We could later change these semantics, but for now we should be very clear about what it means.

Signature · 3 lines
Cheers,

David
#9 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

Quoted 22 lines · David Hildenbrand (Arm)
Quoted 12 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(). This helper is used in fast paths

Quoted 5 lines

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

Oh, one more thing: what will be the semantics regarding CONFIG_TRANSPARENT_HUGEPAGE?

Quoted 7 lines

I assume it will only return true if CONFIG_TRANSPARENT_HUGEPAGE is enabled, correct?

That is, for example, relevant for patch #2.

We could later change these semantics, but for now we should be very clear about what it means.

I intended to decouple the API from CONFIG_TRANSPARENT_HUGEPAGE,so my goal was to return true as long as PMD-sized pages are supported[*]. If arch_has_pmd_leaves() is not implemented by the arch, we default to CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE, not CONFIG_TRANSPARENT_HUGEPAGE.

Now, do you mean to say that the API should still be tied to CONFIG_TRANSPARENT_HUGEPAGE? If yes, why?

* Sashiko found out that the current arch_has_pmd_leaves() implementation for x86 (and possibly the other archs) is guarded by CONFIG_TRANSPARENT_HUGEPAGE, so we may return true without the hardware check. But that's a bug

reviewer
#10 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

Quoted 28 lines · Luiz Capitulino
Quoted 27 lines · David Hildenbrand (Arm)
Quoted 12 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(). This helper is used in fast paths

Quoted 5 lines

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

Oh, one more thing: what will be the semantics regarding CONFIG_TRANSPARENT_HUGEPAGE?

I assume it will only return true if CONFIG_TRANSPARENT_HUGEPAGE is enabled, correct?

That is, for example, relevant for patch #2.

Quoted 7 lines
Quoted 2 lines

We could later change these semantics, but for now we should be very clear about what it means.

I intended to decouple the API from CONFIG_TRANSPARENT_HUGEPAGE,so my goal was to return true as long as PMD-sized pages are supported[*]. If arch_has_pmd_leaves() is not implemented by the arch, we default to CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE, not CONFIG_TRANSPARENT_HUGEPAGE.

After patch #07, what still confuses me a bit is that the arch hoooks for arch_has_pmd_leaves() still seems to be guarded by CONFIG_TRANSPARENT_HUGEPAGE, not CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE ..

For example, x86 only define arch_has_pmd_leaves() when CONFIG_TRANSPARENT_HUGEPAGE=y, Same for s390 and mips, unless I missed something.

So is the intended meaning:

"this arch can support PMD leaves"

or

"this CPU actually supports PMD leaves"

?

If it's the second one, shouldn't the arch_has_pmd_leaves hooks move out of the CONFIG_TRANSPARENT_HUGEPAGE guards as well?

Quoted 7 lines

Now, do you mean to say that the API should still be tied to CONFIG_TRANSPARENT_HUGEPAGE? If yes, why?

* Sashiko found out that the current arch_has_pmd_leaves() implementation for x86 (and possibly the other archs) is guarded by CONFIG_TRANSPARENT_HUGEPAGE, so we may return true without the hardware check. But that's a bug

Not sure whether that bug can actually happen ...

But decoupling them looks cleaner to me :)

CONFIG_TRANSPARENT_HUGEPAGE controls whether THP code is built, while arch_has_pmd_leaves() is about PMD-leaf capability.

Cheers, Lance

#11 lore raw

Re: [PATCH v4 2/9] mm: introduce pgtable_has_pmd_leaves()

Quoted 50 lines · Lance Yang
Quoted 28 lines · Luiz Capitulino
Quoted 27 lines · David Hildenbrand (Arm)
Quoted 12 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(). This helper is used in fast paths

Quoted 5 lines

This commit introduces a new helper called pgtable_has_pmd_leaves() which is intended to replace both has_transparent_hugepage() and thp_disabled_by_hw(). pgtable_has_pmd_leaves() has very clear semantics:
it returns true if the CPU supports PMD-sized pages and false otherwise. It always returns a cached value, so it can be used in fast paths.

Oh, one more thing: what will be the semantics regarding CONFIG_TRANSPARENT_HUGEPAGE?

I assume it will only return true if CONFIG_TRANSPARENT_HUGEPAGE is enabled, correct?

That is, for example, relevant for patch #2.

Quoted 7 lines
Quoted 2 lines

We could later change these semantics, but for now we should be very clear about what it means.

I intended to decouple the API from CONFIG_TRANSPARENT_HUGEPAGE,so my goal was to return true as long as PMD-sized pages are supported[*]. If arch_has_pmd_leaves() is not implemented by the arch, we default to CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE, not CONFIG_TRANSPARENT_HUGEPAGE.

After patch #07, what still confuses me a bit is that the arch hoooks for arch_has_pmd_leaves() still seems to be guarded by CONFIG_TRANSPARENT_HUGEPAGE, not CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE ..

For example, x86 only define arch_has_pmd_leaves() when CONFIG_TRANSPARENT_HUGEPAGE=y, Same for s390 and mips, unless I missed something.

So is the intended meaning:

"this arch can support PMD leaves"

Quoted 8 lines

or

"this CPU actually supports PMD leaves"

?

If it's the second one, shouldn't the arch_has_pmd_leaves hooks move out of the CONFIG_TRANSPARENT_HUGEPAGE guards as well?

Yes, Sashiko caught this as well. I'll fix this for v5.

Quoted 8 lines
Quoted 7 lines

Now, do you mean to say that the API should still be tied to CONFIG_TRANSPARENT_HUGEPAGE? If yes, why?

* Sashiko found out that the current arch_has_pmd_leaves() implementation for x86 (and possibly the other archs) is guarded by CONFIG_TRANSPARENT_HUGEPAGE, so we may return true without the hardware check. But that's a bug

Quoted 6 lines

Not sure whether that bug can actually happen ...

But decoupling them looks cleaner to me :)

CONFIG_TRANSPARENT_HUGEPAGE controls whether THP code is built, while arch_has_pmd_leaves() is about PMD-leaf capability.

Agreed.

#12 lore raw

PATCH v4 3/9 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 81e4af49e39c..35744ff6592a 100644
@@ -123,7 +123,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.53.0
#13 lore raw

Re: [PATCH v4 3/9] drivers: dax: use pgtable_has_pmd_leaves()

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 81e4af49e39c..35744ff6592a 100644

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

Quoted 6 lines
drivers/dax/dax-private.h +1 -1
@@ -123,7 +123,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;

I think this code really depends on the implied CONFIG_TRANSPARENT_HUGEPAGE check.

For now, should we just keep saying that pgtable_has_pmd_leaves() implies CONFIG_TRANSPARENT_HUGEPAGE support?

That would also e.g., make patch #4 easier.

Signature · 3 lines
Cheers,

David
#14 lore raw

Re: [PATCH v4 3/9] drivers: dax: use pgtable_has_pmd_leaves()

Quoted 24 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 81e4af49e39c..35744ff6592a 100644

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

Quoted 6 lines
drivers/dax/dax-private.h +1 -1
@@ -123,7 +123,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;

I think this code really depends on the implied CONFIG_TRANSPARENT_HUGEPAGE check.

Quoted 2 lines

For now, should we just keep saying that pgtable_has_pmd_leaves() implies CONFIG_TRANSPARENT_HUGEPAGE support?

Yes, my thinking is that any caller that wants to use PMD-sized pages with THP, should check for CONFIG_TRANSPARENT_HUGEPAGE. The way we're doing this in this series is to add a call to IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) when calling pgtable_has_pmd_leaves() (the interesting thing is that some has_transparent_hugepage() callers were doing it already) or rely on already existing guards like this case.

Btw, in a previous conversation I suggested a wrapper for this case (say pgtable_has_pmd_thp()) but you didn't like it :)

Quoted 3 lines

That would also e.g., make patch #4 easier.

#15 lore raw

PATCH v4 4/9 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.53.0
#16 lore raw

PATCH v4 5/9 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 23dc3ee09561..bd53417dde2f 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_NUMA_BALANCING))
 		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_ENABLE_THP_MIGRATION))
 		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.53.0
#17 lore raw

PATCH v4 6/9 mm: shmem: drop has_transparent_hugepage() usage

In reply to Luiz Capitulino

Shmem uses has_transparent_hugepage() in the following ways:

- 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 mTHP size support at folio allocation time. Therefore, drop has_transparent_hugepage() usage while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

---
 mm/shmem.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
mm/shmem.c +3 -4
index 3b5dc21b323c..1948d73fb1e3 100644
@@ -689,7 +689,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;
 
@@ -4656,8 +4656,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;
@@ -5449,7 +5448,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.53.0
#18 lore raw

(sashiko review) Re: [PATCH v4 6/9] mm: shmem: drop has_transparent_hugepage() usage

Quoted 37 lines · Luiz Capitulino

Shmem uses has_transparent_hugepage() in the following ways:

- 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 mTHP size support at folio allocation time. Therefore, drop has_transparent_hugepage() usage while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

---
  mm/shmem.c | 7 +++----
  1 file changed, 3 insertions(+), 4 deletions(-)
mm/shmem.c +1 -1
index 3b5dc21b323c..1948d73fb1e3 100644
@@ -689,7 +689,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_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 4662

Quoted 9 lines
mm/shmem.c +1 -2
@@ -4656,8 +4656,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;

"""
By dropping the has_transparent_hugepage() check, will mount -t tmpfs -o huge=always now succeed on hardware lacking PMD support?

If so, since hugepage_init() still sets the TRANSPARENT_HUGEPAGE_UNSUPPORTED flag, thp_disabled_by_hw() will unconditionally block all large folio allocations in shmem_allowable_huge_orders().

Does this create an intermediate state where the mount silently succeeds but no huge pages of any size can actually be allocated?

I see this is resolved later in the series by commit cd27430097e8 ("mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()") and commit 641a20ae032f ("mm: thp: always enable mTHP support"). """

The mount -t tmpfs -o huge=always succeeding on hardware without PMD support can happen in this patch, yes. But this seems very minor, the impact seems to be someone doing bisection, landing on this patch and their reproducer is depedent on mounting tmpfs with -o huge=always on hardware without PMD size support? I can fix it if others feel strong about this.

mm/shmem.c void __init shmem_init(void) · line 5454

Quoted 9 lines
mm/shmem.c +1 -1
@@ -5449,7 +5448,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 */

"""
Also, by allowing shmem_huge to be set to SHMEM_HUGE_ALWAYS on systems without PMD support, does this incorrectly affect shmem_getattr()?

shmem_getattr() relies on shmem_huge_global_enabled(), which only checks the software configuration and not hardware PMD support. Consequently, shmem_getattr() will erroneously report stat->blksize = HPAGE_PMD_SIZE to userspace.

Since subsequent patches in the series do not appear to update shmem_getattr(), could this misleading block size cause userspace tools to over-allocate IO buffers on hardware where PMD-sized pages are structurally impossible?
"""

This a real issue (albeit small one), the problem is this check in shmem_getattr():

	if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0))
		stat->blksize = HPAGE_PMD_SIZE;

So, we may report HPAGE_PMD_SIZE even when PMD size is not supported. Looks like we may over-report today as well for the SHMEM_HUGE_WITHIN_SIZE case? In any case, I'll fix this.

reviewer
#19 lore raw

Re: (sashiko review) Re: [PATCH v4 6/9] mm: shmem: drop has_transparent_hugepage() usage

Quoted 99 lines · Luiz Capitulino
Quoted 37 lines · Luiz Capitulino

Shmem uses has_transparent_hugepage() in the following ways:

- 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 mTHP size support at folio allocation time. Therefore, drop has_transparent_hugepage() usage while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

---
  mm/shmem.c | 7 +++----
  1 file changed, 3 insertions(+), 4 deletions(-)
mm/shmem.c +1 -1
index 3b5dc21b323c..1948d73fb1e3 100644
@@ -689,7 +689,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_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 4662

Quoted 9 lines
mm/shmem.c +1 -2
@@ -4656,8 +4656,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;

"""
By dropping the has_transparent_hugepage() check, will mount -t tmpfs -o huge=always now succeed on hardware lacking PMD support?

If so, since hugepage_init() still sets the TRANSPARENT_HUGEPAGE_UNSUPPORTED flag, thp_disabled_by_hw() will unconditionally block all large folio allocations in shmem_allowable_huge_orders().

Does this create an intermediate state where the mount silently succeeds but no huge pages of any size can actually be allocated?

I see this is resolved later in the series by commit cd27430097e8 ("mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()") and commit 641a20ae032f ("mm: thp: always enable mTHP support"). """

The mount -t tmpfs -o huge=always succeeding on hardware without PMD support can happen in this patch, yes. But this seems very minor, the impact seems to be someone doing bisection, landing on this patch and their reproducer is depedent on mounting tmpfs with -o huge=always on hardware without PMD size support? I can fix it if others feel strong about this.

mm/shmem.c void __init shmem_init(void) · line 5454

Quoted 9 lines
mm/shmem.c +1 -1
@@ -5449,7 +5448,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 */

"""
Also, by allowing shmem_huge to be set to SHMEM_HUGE_ALWAYS on systems without PMD support, does this incorrectly affect shmem_getattr()?

shmem_getattr() relies on shmem_huge_global_enabled(), which only checks the software configuration and not hardware PMD support. Consequently, shmem_getattr() will erroneously report stat->blksize = HPAGE_PMD_SIZE to userspace.

Since subsequent patches in the series do not appear to update shmem_getattr(), could this misleading block size cause userspace tools to over-allocate IO buffers on hardware where PMD-sized pages are structurally impossible?
"""

This a real issue (albeit small one), the problem is this check in shmem_getattr():

Quoted 6 lines
	if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0))
		stat->blksize = HPAGE_PMD_SIZE;

So, we may report HPAGE_PMD_SIZE even when PMD size is not supported. Looks like we may over-report today as well for the SHMEM_HUGE_WITHIN_SIZE case? In any case, I'll fix this.

Well spotted.

@Baolin looks like shmem_getattr() might already be buggy?

shmem_huge_global_enabled() returns an order mask. For huge=always and huge=within_size it can return THP_ORDERS_ALL_FILE_DEFAULT, which is not PMD-only and can include smaller file mTHP orders as well ...

So shmem_getattr() treating any non-zero mask as HPAGE_PMD_SIZE looks like an over-report?

Cheers, Lance

reviewer
#20 lore raw

Re: (sashiko review) Re: [PATCH v4 6/9] mm: shmem: drop has_transparent_hugepage() usage

Quoted 110 lines · Lance Yang
Quoted 99 lines · Luiz Capitulino
Quoted 37 lines · Luiz Capitulino

Shmem uses has_transparent_hugepage() in the following ways:

- 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 mTHP size support at folio allocation time. Therefore, drop has_transparent_hugepage() usage while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

---
   mm/shmem.c | 7 +++----
   1 file changed, 3 insertions(+), 4 deletions(-)
mm/shmem.c +1 -1
index 3b5dc21b323c..1948d73fb1e3 100644
@@ -689,7 +689,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_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 4662

Quoted 9 lines
mm/shmem.c +1 -2
@@ -4656,8 +4656,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;

"""
By dropping the has_transparent_hugepage() check, will mount -t tmpfs -o huge=always now succeed on hardware lacking PMD support?

If so, since hugepage_init() still sets the TRANSPARENT_HUGEPAGE_UNSUPPORTED flag, thp_disabled_by_hw() will unconditionally block all large folio allocations in shmem_allowable_huge_orders().

Does this create an intermediate state where the mount silently succeeds but no huge pages of any size can actually be allocated?

I see this is resolved later in the series by commit cd27430097e8 ("mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()") and commit 641a20ae032f ("mm: thp: always enable mTHP support"). """

The mount -t tmpfs -o huge=always succeeding on hardware without PMD support can happen in this patch, yes. But this seems very minor, the impact seems to be someone doing bisection, landing on this patch and their reproducer is depedent on mounting tmpfs with -o huge=always on hardware without PMD size support? I can fix it if others feel strong about this.

mm/shmem.c void __init shmem_init(void) · line 5454

Quoted 9 lines
mm/shmem.c +1 -1
@@ -5449,7 +5448,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 */

"""
Also, by allowing shmem_huge to be set to SHMEM_HUGE_ALWAYS on systems without PMD support, does this incorrectly affect shmem_getattr()?

shmem_getattr() relies on shmem_huge_global_enabled(), which only checks the software configuration and not hardware PMD support. Consequently, shmem_getattr() will erroneously report stat->blksize = HPAGE_PMD_SIZE to userspace.

Since subsequent patches in the series do not appear to update shmem_getattr(), could this misleading block size cause userspace tools to over-allocate IO buffers on hardware where PMD-sized pages are structurally impossible?
"""

This a real issue (albeit small one), the problem is this check in shmem_getattr():

Quoted 6 lines
	if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0))
		stat->blksize = HPAGE_PMD_SIZE;

So, we may report HPAGE_PMD_SIZE even when PMD size is not supported. Looks like we may over-report today as well for the SHMEM_HUGE_WITHIN_SIZE case? In any case, I'll fix this.

Well spotted.

Quoted 8 lines

@Baolin looks like shmem_getattr() might already be buggy?

shmem_huge_global_enabled() returns an order mask. For huge=always and huge=within_size it can return THP_ORDERS_ALL_FILE_DEFAULT, which is not PMD-only and can include smaller file mTHP orders as well ...

So shmem_getattr() treating any non-zero mask as HPAGE_PMD_SIZE looks like an over-report?

Normally, it looks fine because we always start trying from PMD-sized large order if tmpfs supports large order (see commit 69e0a3b49003 ("mm: shmem: fix the strategy for the tmpfs 'huge=' options")).

And the code here works for 'force' or 'always', but not so much for 'within_size' or 'advise', as Hugh mentioned before[1].

Especially in 'within_size' mode, after we allow fallback to smaller large orders, the logic becomes unreasonable. Although I'm not sure if there's any benefit after the modification, it would make the logic clearer, for example:

mm/shmem.c +8 -2
index 106e4de943fb..e9f43aefdc7d 100644
@@ -1297,8 +1297,14 @@ 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 (!pgtable_has_pmd_leaves())
+               orders &= ~BIT(PMD_ORDER);
+       if (orders) {
+               unsigned int hi_order = highest_order(orders);
+
+               stat->blksize = PAGE_SIZE << hi_order;
+       }
 
        if (request_mask & STATX_BTIME) {
                stat->result_mask |= STATX_BTIME;

[1] https://lore.kernel.org/all/[email protected]/T/#m21037b23be70fb9f7ab1965bb8b39242752594d1

reviewer
#21 lore raw

Re: (sashiko review) Re: [PATCH v4 6/9] mm: shmem: drop has_transparent_hugepage() usage

Quoted 118 lines · Baolin Wang
Quoted 115 lines · Lance Yang
Quoted 98 lines · Luiz Capitulino
Quoted 38 lines · Luiz Capitulino

Shmem uses has_transparent_hugepage() in the following ways:

- 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 mTHP size support at folio allocation time. Therefore, drop has_transparent_hugepage() usage while keeping the CONFIG_TRANSPARENT_HUGEPAGE checks.

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

   1 file changed, 3 insertions(+), 4 deletions(-)
mm/shmem.c +0 -0
index 3b5dc21b323c..1948d73fb1e3 100644
@@ -689,7 +689,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 +0 -0
@@ -4656,8 +4656,7 @@ static int shmem_parse_one(struct fs_context 
 *fc, struct fs_parameter *param)

       case Opt_huge:

Quoted 8 lines

           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;

"""
By dropping the has_transparent_hugepage() check, will mount -t tmpfs -o huge=always now succeed on hardware lacking PMD support?

If so, since hugepage_init() still sets the TRANSPARENT_HUGEPAGE_UNSUPPORTED flag, thp_disabled_by_hw() will unconditionally block all large folio allocations in shmem_allowable_huge_orders().

Does this create an intermediate state where the mount silently succeeds but no huge pages of any size can actually be allocated?

I see this is resolved later in the series by commit cd27430097e8 ("mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()") and commit 641a20ae032f ("mm: thp: always enable mTHP support"). """

The mount -t tmpfs -o huge=always succeeding on hardware without PMD support can happen in this patch, yes. But this seems very minor, the impact seems to be someone doing bisection, landing on this patch and their reproducer is depedent on mounting tmpfs with -o huge=always on hardware without PMD size support? I can fix it if others feel strong about this.

Quoted 8 lines
mm/shmem.c +1 -1
@@ -5449,7 +5448,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 */

"""
Also, by allowing shmem_huge to be set to SHMEM_HUGE_ALWAYS on systems without PMD support, does this incorrectly affect shmem_getattr()?

shmem_getattr() relies on shmem_huge_global_enabled(), which only checks the software configuration and not hardware PMD support. Consequently, shmem_getattr() will erroneously report stat->blksize = HPAGE_PMD_SIZE to userspace.

Since subsequent patches in the series do not appear to update shmem_getattr(), could this misleading block size cause userspace tools to over-allocate IO buffers on hardware where PMD-sized pages are structurally impossible?
"""

This a real issue (albeit small one), the problem is this check in shmem_getattr():

Quoted 6 lines

    if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0)) stat->blksize = HPAGE_PMD_SIZE;

So, we may report HPAGE_PMD_SIZE even when PMD size is not supported. Looks like we may over-report today as well for the SHMEM_HUGE_WITHIN_SIZE case? In any case, I'll fix this.

Well spotted.

@Baolin looks like shmem_getattr() might already be buggy?

shmem_huge_global_enabled() returns an order mask. For huge=always and huge=within_size it can return THP_ORDERS_ALL_FILE_DEFAULT, which is not PMD-only and can include smaller file mTHP orders as well ...

Quoted 8 lines
Quoted 2 lines

So shmem_getattr() treating any non-zero mask as HPAGE_PMD_SIZE looks like an over-report?

Normally, it looks fine because we always start trying from PMD-sized large order if tmpfs supports large order (see commit 69e0a3b49003 ("mm: shmem: fix the strategy for the tmpfs 'huge=' options")).

And the code here works for 'force' or 'always', but not so much for 'within_size' or 'advise', as Hugh mentioned before[1].

Especially in 'within_size' mode, after we allow fallback to smaller large orders, the logic becomes unreasonable. Although I'm not sure if there's any benefit after the modification, it would make the logic clearer, for example:

Ah, I see. Thanks for the explanation!

Quoted 8 lines
mm/shmem.c +0 -0
index 106e4de943fb..e9f43aefdc7d 100644
@@ -1297,8 +1297,14 @@ static int shmem_getattr(struct mnt_idmap *idmap,
                         STATX_ATTR_NODUMP);
         generic_fillattr(idmap, request_mask, inode, stat);
 

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

Quoted 10 lines
mm/shmem.c +8 -2
-       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 (!pgtable_has_pmd_leaves())
+               orders &= ~BIT(PMD_ORDER);
+       if (orders) {
+               unsigned int hi_order = highest_order(orders);
+
+               stat->blksize = PAGE_SIZE << hi_order;
+       }

Yep, using the highest remaining order after filtering out unsupported PMD/PUD orders looks better to me. That would keep the stat hint aligned with the allocation path :D

Quoted 5 lines
mm/shmem.c +0 -0
         if (request_mask & STATX_BTIME) {
                 stat->result_mask |= STATX_BTIME;
 

[1] https://lore.kernel.org/all/1524665633-83806-1-git-send-email- [email protected]/T/#m21037b23be70fb9f7ab1965bb8b39242752594d1

Thanks, Lance

#22 lore raw

PATCH v4 7/9 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 init_arch_has_pmd_leaves(). The remaining exception is hugepage_init() which will be converted in a future commit.

---
 arch/mips/include/asm/pgtable.h               |  4 ++--
 arch/mips/mm/tlb-r4k.c                        |  4 ++--
 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, 22 insertions(+), 22 deletions(-)
arch/mips/include/asm/pgtable.h +2 -2
index fa7b935f947c..a97b788315e2 100644
@@ -615,8 +615,8 @@ 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);
+#define arch_has_pmd_leaves arch_has_pmd_leaves
+extern int arch_has_pmd_leaves(void);
 
 static inline int pmd_trans_huge(pmd_t pmd)
 {
arch/mips/mm/tlb-r4k.c +2 -2
index 24fe85fa169d..c423b5784337 100644
@@ -434,7 +434,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 
-int has_transparent_hugepage(void)
+int arch_has_pmd_leaves(void)
 {
 	static unsigned int mask = -1;
 
@@ -450,7 +450,7 @@ int has_transparent_hugepage(void)
 	}
 	return mask == PM_HUGE_MASK;
 }
-EXPORT_SYMBOL(has_transparent_hugepage);
+EXPORT_SYMBOL(arch_has_pmd_leaves);
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE  */
 
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..6744c2287199 100644
@@ -165,7 +165,7 @@ 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);
+extern int hash__arch_has_pmd_leaves(void);
 #endif
 
 #endif /* !__ASSEMBLER__ */
arch/powerpc/include/asm/book3s/64/hash-64k.h +1 -1
index 7deb3a66890b..9392aba5e5dc 100644
@@ -278,7 +278,7 @@ 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);
+extern int hash__arch_has_pmd_leaves(void);
 #endif /*  CONFIG_TRANSPARENT_HUGEPAGE */
 
 #endif	/* __ASSEMBLER__ */
arch/powerpc/include/asm/book3s/64/pgtable.h +5 -5
index e67e64ac6e8c..b6629c041e75 100644
@@ -1121,14 +1121,14 @@ 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)
+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
 
 static inline int has_transparent_pud_hugepage(void)
 {
arch/powerpc/include/asm/book3s/64/radix.h +1 -1
index da954e779744..c884a119cbd9 100644
@@ -298,7 +298,7 @@ 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__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 d9b5b751d7b7..88a4a2eab513 100644
@@ -391,7 +391,7 @@ pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
 	return old_pmd;
 }
 
-int hash__has_transparent_hugepage(void)
+int hash__arch_has_pmd_leaves(void)
 {
 
 	if (!mmu_has_feature(MMU_FTR_16M_PAGE))
@@ -420,7 +420,7 @@ int hash__has_transparent_hugepage(void)
 
 	return 1;
 }
-EXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
+EXPORT_SYMBOL_GPL(hash__arch_has_pmd_leaves);
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
arch/s390/include/asm/pgtable.h +2 -2
index 2c6cee8241e0..33b165dbf3db 100644
@@ -1799,8 +1799,8 @@ static inline int pmd_trans_huge(pmd_t pmd)
 	return pmd_leaf(pmd);
 }
 
-#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 2187e9cfcefa..2edd6c9d789c 100644
@@ -306,8 +306,8 @@ static inline int pud_trans_huge(pud_t pud)
 }
 #endif
 
-#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 b365be3516bf..3d7eeb50c183 100644
@@ -2273,8 +2273,8 @@ static inline void __init init_arch_has_pmd_leaves(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 970e077019b7..4da10e94bbb6 100644
@@ -969,7 +969,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 90b2d9e84320..c62fce83b8d0 100644
@@ -169,7 +169,7 @@ EXPORT_SYMBOL(__arch_has_pmd_leaves_key);
 
 void __init init_arch_has_pmd_leaves(void)
 {
-	if (!has_transparent_hugepage())
+	if (!arch_has_pmd_leaves())
 		static_branch_disable(&__arch_has_pmd_leaves_key);
 }
 
Signature · 1 line
2.53.0
#23 lore raw

(sashiko review) Re: [PATCH v4 7/9] treewide: introduce arch_has_pmd_leaves()

Quoted 225 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 init_arch_has_pmd_leaves(). The remaining exception is hugepage_init() which will be converted in a future commit.

---
  arch/mips/include/asm/pgtable.h               |  4 ++--
  arch/mips/mm/tlb-r4k.c                        |  4 ++--
  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, 22 insertions(+), 22 deletions(-)
arch/mips/include/asm/pgtable.h +2 -2
index fa7b935f947c..a97b788315e2 100644
@@ -615,8 +615,8 @@ 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);
+#define arch_has_pmd_leaves arch_has_pmd_leaves
+extern int arch_has_pmd_leaves(void);
 
  static inline int pmd_trans_huge(pmd_t pmd)
  {
arch/mips/mm/tlb-r4k.c +2 -2
index 24fe85fa169d..c423b5784337 100644
@@ -434,7 +434,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
 
  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 
-int has_transparent_hugepage(void)
+int arch_has_pmd_leaves(void)
  {
  	static unsigned int mask = -1;
 
@@ -450,7 +450,7 @@ int has_transparent_hugepage(void)
  	}
  	return mask == PM_HUGE_MASK;
  }
-EXPORT_SYMBOL(has_transparent_hugepage);
+EXPORT_SYMBOL(arch_has_pmd_leaves);
 
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE  */
 
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..6744c2287199 100644
@@ -165,7 +165,7 @@ 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);
+extern int hash__arch_has_pmd_leaves(void);
  #endif
 
  #endif /* !__ASSEMBLER__ */
arch/powerpc/include/asm/book3s/64/hash-64k.h +1 -1
index 7deb3a66890b..9392aba5e5dc 100644
@@ -278,7 +278,7 @@ 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);
+extern int hash__arch_has_pmd_leaves(void);
  #endif /*  CONFIG_TRANSPARENT_HUGEPAGE */
 
  #endif	/* __ASSEMBLER__ */
arch/powerpc/include/asm/book3s/64/pgtable.h +5 -5
index e67e64ac6e8c..b6629c041e75 100644
@@ -1121,14 +1121,14 @@ 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)
+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
 
  static inline int has_transparent_pud_hugepage(void)
  {
arch/powerpc/include/asm/book3s/64/radix.h +1 -1
index da954e779744..c884a119cbd9 100644
@@ -298,7 +298,7 @@ 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__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 d9b5b751d7b7..88a4a2eab513 100644
@@ -391,7 +391,7 @@ pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
  	return old_pmd;
  }
 
-int hash__has_transparent_hugepage(void)
+int hash__arch_has_pmd_leaves(void)
  {
 
  	if (!mmu_has_feature(MMU_FTR_16M_PAGE))
@@ -420,7 +420,7 @@ int hash__has_transparent_hugepage(void)
 
  	return 1;
  }
-EXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
+EXPORT_SYMBOL_GPL(hash__arch_has_pmd_leaves);
 
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
arch/s390/include/asm/pgtable.h +2 -2
index 2c6cee8241e0..33b165dbf3db 100644
@@ -1799,8 +1799,8 @@ static inline int pmd_trans_huge(pmd_t pmd)
  	return pmd_leaf(pmd);
  }
 
-#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 2187e9cfcefa..2edd6c9d789c 100644
@@ -306,8 +306,8 @@ static inline int pud_trans_huge(pud_t pud)
  }
  #endif
 
-#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 b365be3516bf..3d7eeb50c183 100644
@@ -2273,8 +2273,8 @@ static inline void __init init_arch_has_pmd_leaves(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 970e077019b7..4da10e94bbb6 100644
@@ -969,7 +969,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 90b2d9e84320..c62fce83b8d0 100644
@@ -169,7 +169,7 @@ EXPORT_SYMBOL(__arch_has_pmd_leaves_key);
 
  void __init init_arch_has_pmd_leaves(void)
  {
-	if (!has_transparent_hugepage())
+	if (!arch_has_pmd_leaves())
  		static_branch_disable(&__arch_has_pmd_leaves_key);
  }
 
#24 lore raw

Re: (sashiko review) Re: [PATCH v4 7/9] treewide: introduce arch_has_pmd_leaves()

Quoted 170 lines · Luiz Capitulino
Quoted 169 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 init_arch_has_pmd_leaves(). The remaining exception is hugepage_init() which will be converted in a future commit.

---
  arch/mips/include/asm/pgtable.h               |  4 ++--
  arch/mips/mm/tlb-r4k.c                        |  4 ++--
  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, 22 insertions(+), 22 deletions(-)
arch/mips/include/asm/pgtable.h +2 -2
index fa7b935f947c..a97b788315e2 100644
@@ -615,8 +615,8 @@ 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);
+#define arch_has_pmd_leaves arch_has_pmd_leaves
+extern int arch_has_pmd_leaves(void);
  static inline int pmd_trans_huge(pmd_t pmd)
  {
arch/mips/mm/tlb-r4k.c +2 -2
index 24fe85fa169d..c423b5784337 100644
@@ -434,7 +434,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-int has_transparent_hugepage(void)
+int arch_has_pmd_leaves(void)
  {
      static unsigned int mask = -1;
@@ -450,7 +450,7 @@ int has_transparent_hugepage(void)
      }
      return mask == PM_HUGE_MASK;
  }
-EXPORT_SYMBOL(has_transparent_hugepage);
+EXPORT_SYMBOL(arch_has_pmd_leaves);
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE  */
arch/powerpc/include/asm/book3s/64/hash-4k.h +1 -1
index 8e5bd9902bed..6744c2287199 100644
@@ -165,7 +165,7 @@ 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);
+extern int hash__arch_has_pmd_leaves(void);
  #endif
  #endif /* !__ASSEMBLER__ */
arch/powerpc/include/asm/book3s/64/hash-64k.h +1 -1
index 7deb3a66890b..9392aba5e5dc 100644
@@ -278,7 +278,7 @@ 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);
+extern int hash__arch_has_pmd_leaves(void);
  #endif /*  CONFIG_TRANSPARENT_HUGEPAGE */
  #endif    /* __ASSEMBLER__ */
arch/powerpc/include/asm/book3s/64/pgtable.h +5 -5
index e67e64ac6e8c..b6629c041e75 100644
@@ -1121,14 +1121,14 @@ 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)
+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
  static inline int has_transparent_pud_hugepage(void)
  {
arch/powerpc/include/asm/book3s/64/radix.h +1 -1
index da954e779744..c884a119cbd9 100644
@@ -298,7 +298,7 @@ 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__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 d9b5b751d7b7..88a4a2eab513 100644
@@ -391,7 +391,7 @@ pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
      return old_pmd;
  }
-int hash__has_transparent_hugepage(void)
+int hash__arch_has_pmd_leaves(void)
  {
      if (!mmu_has_feature(MMU_FTR_16M_PAGE))
@@ -420,7 +420,7 @@ int hash__has_transparent_hugepage(void)
      return 1;
  }
-EXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
+EXPORT_SYMBOL_GPL(hash__arch_has_pmd_leaves);
  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
arch/s390/include/asm/pgtable.h +2 -2
index 2c6cee8241e0..33b165dbf3db 100644
@@ -1799,8 +1799,8 @@ static inline int pmd_trans_huge(pmd_t pmd)
      return pmd_leaf(pmd);
  }
-#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 2187e9cfcefa..2edd6c9d789c 100644
@@ -306,8 +306,8 @@ static inline int pud_trans_huge(pud_t pud)
  }
  #endif
-#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 +0 -0
index b365be3516bf..3d7eeb50c183 100644
Quoted 8 lines

include/linux/pgtable.h static inline void __init init_arch_has_pmd_leaves(void) { } · line 2277

Quoted 8 lines
include/linux/pgtable.h +2 -2
@@ -2273,8 +2273,8 @@ static inline void __init init_arch_has_pmd_leaves(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

[Forgot to include the review, doing it now]

"""
When CONFIG_TRANSPARENT_HUGEPAGE is disabled, do we bypass crucial runtime hardware feature checks on multiple architectures?

Because the architecture-specific declarations are often placed inside CONFIG_TRANSPARENT_HUGEPAGE blocks (as seen above, and similarly in arch/x86/include/asm/pgtable.h and arch/s390/include/asm/pgtable.h), they will not be exposed to the generic headers when THP is disabled.

The preprocessor will then fall back to the default implementation here. Since architectures like x86, s390, and powerpc select HAVE_ARCH_TRANSPARENT_HUGEPAGE in their Kconfig independent of the THP setting, this fallback evaluates to true.

Will this cause init_arch_has_pmd_leaves() to keep the static key enabled, making pgtable_has_pmd_leaves() unconditionally return true at runtime even when THP is disabled?

If this happens on older hardware without these features, could subsystems like DAX that rely on pgtable_has_pmd_leaves() attempt to map PMD-sized leaves and cause page table corruption or hardware exceptions? """

Although a bit of a corner case, this is a real issue.

Checking on x86, the problem is that the arch's implementation of arch_has_pmd_leaves() lives inside a CONFIG_TRANSPARENT_HUGEPAGE block so it won't be built when CONFIG_TRANSPARENT_HUGEPAGE=n which will cause arch_has_pmd_leaves() to default to true without the proper hardware check. I'll check the other archs and get this fixed.

Quoted 25 lines
Quoted 24 lines
include/linux/pgtable.h +0 -0
  #ifndef has_transparent_pud_hugepage
mm/huge_memory.c +1 -1
index 970e077019b7..4da10e94bbb6 100644
@@ -969,7 +969,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 90b2d9e84320..c62fce83b8d0 100644
@@ -169,7 +169,7 @@ EXPORT_SYMBOL(__arch_has_pmd_leaves_key);
  void __init init_arch_has_pmd_leaves(void)
  {
-    if (!has_transparent_hugepage())
+    if (!arch_has_pmd_leaves())
          static_branch_disable(&__arch_has_pmd_leaves_key);
  }
#25 lore raw

PATCH v4 8/9 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 2949e5acff35..da048aa06761 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,
@@ -352,12 +351,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_vmflags(struct file *filp, unsigned long addr,
mm/huge_memory.c +2 -4
index 4da10e94bbb6..32254febe097 100644
@@ -133,7 +133,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. */
@@ -969,10 +969,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 c62fce83b8d0..483af476d9b2 100644
@@ -5522,7 +5522,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 1948d73fb1e3..a48f034830cd 100644
@@ -1842,7 +1842,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.53.0
maintainer Acked-by on v4 8/9
#26 lore raw

Re: [PATCH v4 8/9] mm: replace thp_disabled_by_hw() with pgtable_has_pmd_leaves()

Quoted 6 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.

Quoted 8 lines

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

---

Makes sense, and the next patch will actually limit it only to PMD orders.

Signature · 3 lines
Cheers,

David
#27 lore raw

PATCH v4 9/9 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 | 23 ++++++++++++++++++-----
 mm/shmem.c       | 14 +++++++++-----
 2 files changed, 27 insertions(+), 10 deletions(-)
mm/huge_memory.c +18 -5
index 32254febe097..c1765c8e3dc6 100644
@@ -126,6 +126,14 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	else
 		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * The CPU doesn't support PMD-sized pages, assume it
+		 * doesn't support PUD-sized pages either.
+		 */
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	orders &= supported_orders;
 	if (!orders)
 		return 0;
@@ -133,7 +141,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. */
@@ -848,7 +856,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);
@@ -870,6 +878,14 @@ 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()) {
+		/*
+		 * The CPU doesn't support PMD-sized pages, assume it
+		 * doesn't support PUD-sized pages either.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	order = highest_order(orders);
 	while (orders) {
 		thpsize = thpsize_create(order, *hugepage_kobj);
@@ -969,9 +985,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 a48f034830cd..23893c2bc2dd 100644
@@ -1840,16 +1840,19 @@ 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, filter_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())
+		filter_orders = BIT(PMD_ORDER) | BIT(PUD_ORDER);
+
 	global_orders = shmem_huge_global_enabled(inode, index, write_end,
 						  shmem_huge_force, vma, vm_flags);
 	/* Tmpfs huge pages allocation */
 	if (!vma || !vma_is_anon_shmem(vma))
-		return global_orders;
+		return global_orders & ~filter_orders;
 
 	/*
 	 * Following the 'deny' semantics of the top level, force the huge
@@ -1863,7 +1866,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) & ~filter_orders;
 
 	/* Allow mTHP that will be fully within i_size. */
 	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -1874,6 +1877,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	if (global_orders > 0)
 		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~filter_orders;
 	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
 }
 
@@ -5457,7 +5461,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.53.0
reviewer Reviewed-by on v4 9/9
#28 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

---

Nothing else caught my eye. Thanks. Feel free to add:

#29 lore raw

(sashiko review) Re: [PATCH v4 9/9] mm: thp: always enable mTHP support

In reply to Luiz Capitulino
Quoted 98 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 | 23 ++++++++++++++++++-----
  mm/shmem.c       | 14 +++++++++-----
  2 files changed, 27 insertions(+), 10 deletions(-)
mm/huge_memory.c +18 -5
index 32254febe097..c1765c8e3dc6 100644
@@ -126,6 +126,14 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
  	else
  		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * The CPU doesn't support PMD-sized pages, assume it
+		 * doesn't support PUD-sized pages either.
+		 */
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
  	orders &= supported_orders;
  	if (!orders)
  		return 0;
@@ -133,7 +141,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. */
@@ -848,7 +856,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);
@@ -870,6 +878,14 @@ 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()) {
+		/*
+		 * The CPU doesn't support PMD-sized pages, assume it
+		 * doesn't support PUD-sized pages either.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
  	order = highest_order(orders);
  	while (orders) {
  		thpsize = thpsize_create(order, *hugepage_kobj);
@@ -969,9 +985,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 +2 -2
index a48f034830cd..23893c2bc2dd 100644
@@ -1840,16 +1840,19 @@ 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, filter_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;

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

Quoted 10 lines
mm/shmem.c +4 -1
 
+	if (!pgtable_has_pmd_leaves())
+		filter_orders = BIT(PMD_ORDER) | BIT(PUD_ORDER);
+
  	global_orders = shmem_huge_global_enabled(inode, index, write_end,
  						  shmem_huge_force, vma, vm_flags);
  	/* Tmpfs huge pages allocation */
  	if (!vma || !vma_is_anon_shmem(vma))
-		return global_orders;
+		return global_orders & ~filter_orders;

"""
Could this lead to unintended truncation of the order masks?

Because filter_orders is declared as an unsigned int, assigning the result of BIT() causes an implicit downcast from unsigned long.
"""

If I'm checking this right, the maximum bit number we can get to is 22 on arm64. So, we're safe. That being said we should be consistent with the return type for both filter_order and global_orders. I'll change it.

NOTE: I'm skipping the other comment from Sashiko which is about the same issue.

Quoted 29 lines
mm/shmem.c +3 -2
  	/*
  	 * Following the 'deny' semantics of the top level, force the huge
@@ -1863,7 +1866,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) & ~filter_orders;
 
  	/* Allow mTHP that will be fully within i_size. */
  	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -1874,6 +1877,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	if (global_orders > 0)
  		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~filter_orders;
  	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
  }
 
@@ -5457,7 +5461,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;
#30 lore raw
In reply to Luiz Capitulino
Quoted 27 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 | 23 ++++++++++++++++++-----
 mm/shmem.c       | 14 +++++++++-----
 2 files changed, 27 insertions(+), 10 deletions(-)
mm/huge_memory.c +0 -0
index 32254febe097..c1765c8e3dc6 100644

mm/huge_memory.c unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma, · line 133

Quoted 8 lines
mm/huge_memory.c +5 -0
@@ -126,6 +126,14 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
 	else
 		supported_orders = THP_ORDERS_ALL_FILE_DEFAULT;
 
+	if (!pgtable_has_pmd_leaves()) {
+		/*
+		 * The CPU doesn't support PMD-sized pages, assume it
+		 * doesn't support PUD-sized pages either.
+		 */

I'd say here "If the CPU does not support PMD leaves, assume for now that it does not support PUD leaves and disable both folio orders."

Quoted 56 lines
mm/huge_memory.c +13 -5
+		supported_orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	orders &= supported_orders;
 	if (!orders)
 		return 0;
@@ -133,7 +141,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. */
@@ -848,7 +856,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);
@@ -870,6 +878,14 @@ 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()) {
+		/*
+		 * The CPU doesn't support PMD-sized pages, assume it
+		 * doesn't support PUD-sized pages either.
+		 */
+		orders &= ~(BIT(PMD_ORDER) | BIT(PUD_ORDER));
+	}
+
 	order = highest_order(orders);
 	while (orders) {
 		thpsize = thpsize_create(order, *hugepage_kobj);
@@ -969,9 +985,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 +0 -0
index a48f034830cd..23893c2bc2dd 100644
@@ -1840,16 +1840,19 @@ 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);

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

Quoted 10 lines
mm/shmem.c +4 -2
 	vm_flags_t vm_flags = vma ? vma->vm_flags : 0;
-	unsigned int global_orders;
+	unsigned int global_orders, filter_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())
+		filter_orders = BIT(PMD_ORDER) | BIT(PUD_ORDER);

Would "disabled_orders" or "unavailable_orders" be more appropriate?

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

Quoted 28 lines
mm/shmem.c +4 -2
+
 	global_orders = shmem_huge_global_enabled(inode, index, write_end,
 						  shmem_huge_force, vma, vm_flags);
 	/* Tmpfs huge pages allocation */
 	if (!vma || !vma_is_anon_shmem(vma))
-		return global_orders;
+		return global_orders & ~filter_orders;
 
 	/*
 	 * Following the 'deny' semantics of the top level, force the huge
@@ -1863,7 +1866,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) & ~filter_orders;
 
 	/* Allow mTHP that will be fully within i_size. */
 	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -1874,6 +1877,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
 	if (global_orders > 0)
 		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~filter_orders;
 	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
 }
 

mm/shmem.c void __init shmem_init(void) · line 5465

Quoted 6 lines
mm/shmem.c +1 -1
@@ -5457,7 +5461,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);

Do we really want to change that? We can just leave the defaults as is, no?

Signature · 3 lines
Cheers,

David
reviewer
#31 lore raw
Quoted 25 lines · David Hildenbrand (Arm)
Quoted 24 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 | 23 ++++++++++++++++++-----
  mm/shmem.c       | 14 +++++++++-----
  2 files changed, 27 insertions(+), 10 deletions(-)
mm/huge_memory.c +0 -0
index 32254febe097..c1765c8e3dc6 100644

[snip]

Quoted 11 lines
Quoted 11 lines
mm/shmem.c +1 -1
index a48f034830cd..23893c2bc2dd 100644
@@ -1840,16 +1840,19 @@ 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, filter_orders = 0;
 
Quoted 8 lines

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

Quoted 6 lines
mm/shmem.c +3 -1
-	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())
+		filter_orders = BIT(PMD_ORDER) | BIT(PUD_ORDER);

Would "disabled_orders" or "unavailable_orders" be more appropriate?

Thanks. Better than my naming:)

Quoted 2 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.

Agree.

Quoted 26 lines
Quoted 26 lines
mm/shmem.c +3 -2
  	global_orders = shmem_huge_global_enabled(inode, index, write_end,
  						  shmem_huge_force, vma, vm_flags);
  	/* Tmpfs huge pages allocation */
  	if (!vma || !vma_is_anon_shmem(vma))
-		return global_orders;
+		return global_orders & ~filter_orders;
 
  	/*
  	 * Following the 'deny' semantics of the top level, force the huge
@@ -1863,7 +1866,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) & ~filter_orders;
 
  	/* Allow mTHP that will be fully within i_size. */
  	mask |= shmem_get_orders_within_size(inode, within_size_orders, index, 0);
@@ -1874,6 +1877,7 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
  	if (global_orders > 0)
  		mask |= READ_ONCE(huge_shmem_orders_inherit);
 
+	mask &= ~filter_orders;
  	return THP_ORDERS_ALL_FILE_DEFAULT & mask;
  }
 
Quoted 9 lines

mm/shmem.c void __init shmem_init(void) · line 5465

Quoted 7 lines
mm/shmem.c +1 -1
@@ -5457,7 +5461,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);

Do we really want to change that? We can just leave the defaults as is, no?

Yes, this needs to be handled the same way as anonymous memory. If pgtable_has_pmd_leaves() returns false, but huge_shmem_orders_inherit still has the PMD order set by default, then shmem would still be able to allocate PMD-sized large folios if the global shmem interface is enabled.

maintainer
#32 lore raw
In reply to Luiz Capitulino
Quoted 15 lines · On Fri, 1 May 2026 15:18:42 -0400 Luiz Capitulino <[email protected]> wrote:

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. In addition, the kernel lacks a well defined API to check for PMD-sized page support. It currently relies on has_transparent_hugepage() and thp_disabled_by_hw(), but they are not well defined and are tied to THP support.

This series addresses both issues by introducing a new well defined API to query PMD-sized page support: pgtable_has_pmd_leaves(). Using this new helper, we ensure that mTHP remains enabled even when the architecture or CPU doesn't support PMD-sized pages.

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

Quoted 7 lines

This applies to Linus tree 08d0d3466664 ("Merge tag 'net-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")

NOTE: I used Claude Code Opus 4.6 to *review* the series before posting. It did find one issue where a pgtable_has_pmd_leaves() check was missing when assining huge_shmem_orders_inherit in shmem_init().

Thanks.

Sashiko found a few other things to ask about:

	https://sashiko.dev/#/patchset/[email protected]
#33 lore raw
Quoted 25 lines · Andrew Morton
Quoted 15 lines · On Fri, 1 May 2026 15:18:42 -0400 Luiz Capitulino <[email protected]> wrote:

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. In addition, the kernel lacks a well defined API to check for PMD-sized page support. It currently relies on has_transparent_hugepage() and thp_disabled_by_hw(), but they are not well defined and are tied to THP support.

This series addresses both issues by introducing a new well defined API to query PMD-sized page support: pgtable_has_pmd_leaves(). Using this new helper, we ensure that mTHP remains enabled even when the architecture or CPU doesn't support PMD-sized pages.

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

Quoted 7 lines

This applies to Linus tree 08d0d3466664 ("Merge tag 'net-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")

NOTE: I used Claude Code Opus 4.6 to *review* the series before posting. It did find one issue where a pgtable_has_pmd_leaves() check was missing when assining huge_shmem_orders_inherit in shmem_init().

Quoted 4 lines

Thanks.

Sashiko found a few other things to ask about:

	https://sashiko.dev/#/patchset/[email protected]

Thanks, I'll go over those soon.

#34 lore raw
In reply to Luiz Capitulino

-cc my previous kernel mail

(Minor annoying reminder to update my email to [email protected] :P)

Quoted 90 lines · Luiz Capitulino

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. In addition, the kernel lacks a well defined API to check for PMD-sized page support. It currently relies on has_transparent_hugepage() and thp_disabled_by_hw(), but they are not well defined and are tied to THP support.

This series addresses both issues by introducing a new well defined API to query PMD-sized page support: pgtable_has_pmd_leaves(). Using this new helper, we ensure that mTHP remains enabled even when the architecture or CPU doesn't support PMD-sized pages.

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

This applies to Linus tree 08d0d3466664 ("Merge tag 'net-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")

NOTE: I used Claude Code Opus 4.6 to *review* the series before posting. It did find one issue where a pgtable_has_pmd_leaves() check was missing when assining huge_shmem_orders_inherit in shmem_init().

v4
--
- Use 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_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 (9):
docs: tmpfs: remove implementation detail reference
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: drop has_transparent_hugepage() usage
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               |  4 +--
 arch/mips/mm/tlb-r4k.c                        |  4 +--
 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 +--
 drivers/dax/dax-private.h                     |  2 +-
 drivers/nvdimm/pfn_devs.c                     |  6 +++--
 include/linux/huge_mm.h                       |  7 ------
 include/linux/pgtable.h                       | 19 ++++++++++++--
 init/main.c                                   |  1 +
 mm/debug_vm_pgtable.c                         | 20 +++++++--------
 mm/huge_memory.c                              | 25 +++++++++++++------
 mm/memory.c                                   | 11 +++++++-
 mm/shmem.c                                    | 21 +++++++++-------
 19 files changed, 93 insertions(+), 60 deletions(-)

--
2.53.0

#35 lore raw
Quoted 3 lines · Lorenzo Stoakes

-cc my previous kernel mail

(Minor annoying reminder to update my email to [email protected] :P)

Nah, my fault. I promise I updated your email now.

Quoted 93 lines · Luiz Capitulino
Quoted 90 lines

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. In addition, the kernel lacks a well defined API to check for PMD-sized page support. It currently relies on has_transparent_hugepage() and thp_disabled_by_hw(), but they are not well defined and are tied to THP support.

This series addresses both issues by introducing a new well defined API to query PMD-sized page support: pgtable_has_pmd_leaves(). Using this new helper, we ensure that mTHP remains enabled even when the architecture or CPU doesn't support PMD-sized pages.

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

This applies to Linus tree 08d0d3466664 ("Merge tag 'net-7.1-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")

NOTE: I used Claude Code Opus 4.6 to *review* the series before posting. It did find one issue where a pgtable_has_pmd_leaves() check was missing when assining huge_shmem_orders_inherit in shmem_init().

v4
--
- Use 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_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 (9):
docs: tmpfs: remove implementation detail reference
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: drop has_transparent_hugepage() usage
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               |  4 +--
  arch/mips/mm/tlb-r4k.c                        |  4 +--
  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 +--
  drivers/dax/dax-private.h                     |  2 +-
  drivers/nvdimm/pfn_devs.c                     |  6 +++--
  include/linux/huge_mm.h                       |  7 ------
  include/linux/pgtable.h                       | 19 ++++++++++++--
  init/main.c                                   |  1 +
  mm/debug_vm_pgtable.c                         | 20 +++++++--------
  mm/huge_memory.c                              | 25 +++++++++++++------
  mm/memory.c                                   | 11 +++++++-
  mm/shmem.c                                    | 21 +++++++++-------
  19 files changed, 93 insertions(+), 60 deletions(-)

--
2.53.0