@@ -14,6 +14,17 @@ $(bounds-file): kernel/bounds.s FORCE
$(call filechk,offsets,__LINUX_BOUNDS_H__)
#####
+# Generate struct_page_size.h. Must follows bounds.h.
+
+struct_page_size-file := include/generated/struct_page_size.h
+
+always-y := $(struct_page_size-file)
+targets := mm/struct_page_size.s
+
+$(struct_page_size-file): mm/struct_page_size.s FORCE
+ $(call filechk,offsets,__LINUX_STRUCT_PAGE_SIZE_H__)
+
+#####
# Generate timeconst.h
timeconst-file := include/generated/timeconst.h
@@ -245,10 +245,13 @@ config HUGETLBFS
config HUGETLB_PAGE
def_bool HUGETLBFS
+config HAS_STRUCT_PAGE_SIZE_POWER_OF_2
+ def_bool $(success,test "$(shell, $(srctree)/scripts/check_struct_page_po2.sh)" = y)
+
config HUGETLB_PAGE_FREE_VMEMMAP
def_bool HUGETLB_PAGE
depends on X86_64
- depends on SPARSEMEM_VMEMMAP
+ depends on SPARSEMEM_VMEMMAP && HAS_STRUCT_PAGE_SIZE_POWER_OF_2
config HUGETLB_PAGE_FREE_VMEMMAP_DEFAULT_ON
bool "Default freeing vmemmap pages of HugeTLB to on"
@@ -224,6 +224,7 @@ struct page {
#endif
} _struct_page_alignment;
+#ifndef __GENERATING_STRUCT_PAGE_SIZE_H
/**
* struct folio - Represents a contiguous set of bytes.
* @flags: Identical to the page flags.
@@ -849,5 +850,6 @@ enum fault_flag {
FAULT_FLAG_INSTRUCTION = 1 << 8,
FAULT_FLAG_INTERRUPTIBLE = 1 << 9,
};
+#endif /* !__GENERATING_STRUCT_PAGE_SIZE_H */
#endif /* _LINUX_MM_TYPES_H */
@@ -28,12 +28,6 @@ EXPORT_SYMBOL(hugetlb_free_vmemmap_enabled_key);
static int __init early_hugetlb_free_vmemmap_param(char *buf)
{
- /* We cannot optimize if a "struct page" crosses page boundaries. */
- if (!is_power_of_2(sizeof(struct page))) {
- pr_warn("cannot free vmemmap pages because \"struct page\" crosses page boundaries\n");
- return 0;
- }
-
if (!buf)
return -EINVAL;
new file mode 100644
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generate definitions needed by the preprocessor.
+ * This code generates raw asm output which is post-processed
+ * to extract and format the required data.
+ */
+
+#define __GENERATING_STRUCT_PAGE_SIZE_H
+/* Include headers that define the enum constants of interest */
+#include <linux/kbuild.h>
+#include <linux/log2.h>
+#include <linux/mm_types.h>
+
+int main(void)
+{
+ DEFINE(STRUCT_PAGE_SIZE_POWER_OF_2, is_power_of_2(sizeof(struct page)));
+
+ return 0;
+}
new file mode 100755
@@ -0,0 +1,16 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Check if the size of "struct page" is power of 2
+
+file="include/generated/struct_page_size.h"
+if [ ! -f "$file" ]; then
+ exit 1
+fi
+
+struct_page_po2=$(grep STRUCT_PAGE_SIZE_POWER_OF_2 "$file" | cut -d' ' -f3)
+if [ "$struct_page_po2" = "1" ]; then
+ echo y
+else
+ echo n
+fi
If the size of "struct page" is not the power of two and this feature is enabled, then the vmemmap pages of HugeTLB will be corrupted after remapping (panic is about to happen in theory). But this only exists when !CONFIG_MEMCG && !CONFIG_SLUB on x86_64. However, it is not a conventional configuration nowadays. So it is not a real word issue, just the result of a code review. But we have to prevent anyone from configuring that combined configuration. In order to avoid many checks like "is_power_of_2 (sizeof(struct page))" through mm/hugetlb_vmemmap.c. Introduce HAS_STRUCT_PAGE_SIZE_POWER_OF_2 to detect if the size of struct page is power of 2 and make this feature depends on this new config. Then we could prevent anyone do any unexpected configuration. Signed-off-by: Muchun Song <songmuchun@bytedance.com> --- Thanks Luis for proposing this idea. The initial implementation is as follows. I found it could work properly at the first time after creating .config. Then, if we use "make menuconfig" to disable CONFIG_MEMCG and CONFIG_SLOB meaning the size of "struct page" becomes not power of 2, whereas HAS_STRUCT_PAGE_SIZE_POWER_OF_2 will not be changed to "n" accordingly. I don't know how to make HAS_STRUCT_PAGE_SIZE_POWER_OF_2 realize the potential change of the size of "struct page" since I am not familiar with the Kconfig and how to detect this dependency. If you have any suggestions, comments are really welcome. Kbuild | 11 +++++++++++ fs/Kconfig | 5 ++++- include/linux/mm_types.h | 2 ++ mm/hugetlb_vmemmap.c | 6 ------ mm/struct_page_size.c | 19 +++++++++++++++++++ scripts/check_struct_page_po2.sh | 16 ++++++++++++++++ 6 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 mm/struct_page_size.c create mode 100755 scripts/check_struct_page_po2.sh