AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/08 15:12 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP",
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON"
  ],
  "FocusSymbols": [
    "vmemmap_split_pmd",
    "vmemmap_remap_pte",
    "vmemmap_restore_pte",
    "vmemmap_remap_free",
    "vmemmap_remap_alloc",
    "hugetlb_vmemmap_optimization_try_disable",
    "__hugetlb_vmemmap_restore_folio",
    "__hugetlb_vmemmap_optimize_folio",
    "vmemmap_should_optimize_folio",
    "vmemmap_should_optimize_bootmem_page",
    "late_cpu_enable_hvo",
    "hvo_compatible"
  ],
  "Reasoning": "The patch introduces ARM64 support for HugeTLB Vmemmap Optimization (HVO). It modifies core kernel logic in `mm/hugetlb_vmemmap.c` to use architecture-specific functions for populating and updating vmemmap PMDs and PTEs. It also adds CPU capability checks for HVO on ARM64. This is a functional change that can be reached when allocating and freeing HugeTLB pages with HVO enabled, making it worth fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/07/08 15:12 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit ddf2305462eb8fab9abcb5f12b97b1f970cba595\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Jul 8 15:12:22 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex f37a81950e250e..55bd4c09f49496 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -12013,6 +12013,7 @@ F:\tDocumentation/mm/hugetlbfs_reserv.rst\n F:\tDocumentation/mm/vmemmap_dedup.rst\n F:\tfs/hugetlbfs/\n F:\tinclude/linux/hugetlb.h\n+F:\tinclude/linux/hugetlb_vmemmap.h\n F:\tinclude/trace/events/hugetlbfs.h\n F:\tmm/hugetlb.c\n F:\tmm/hugetlb_cgroup.c\n@@ -12021,7 +12022,7 @@ F:\tmm/hugetlb_cma.h\n F:\tmm/hugetlb_sysctl.c\n F:\tmm/hugetlb_sysfs.c\n F:\tmm/hugetlb_vmemmap.c\n-F:\tmm/hugetlb_vmemmap.h\n+F:\tmm/hugetlb_vmemmap_internal.h\n F:\ttools/testing/selftests/cgroup/test_hugetlb_memcg.c\n \n HVA ST MEDIA DRIVER\ndiff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig\nindex b3afe0688919bd..2a59155f68dcd8 100644\n--- a/arch/arm64/Kconfig\n+++ b/arch/arm64/Kconfig\n@@ -259,6 +259,7 @@ config ARM64\n \tselect USER_STACKTRACE_SUPPORT\n \tselect VDSO_GETRANDOM\n \tselect VMAP_STACK\n+\tselect ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if ARM64_HW_AFDBM\n \thelp\n \t  ARM 64-bit (AArch64) Linux support.\n \ndiff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h\nindex 25c61cda901c5d..6db3ef827f86a9 100644\n--- a/arch/arm64/include/asm/cpucaps.h\n+++ b/arch/arm64/include/asm/cpucaps.h\n@@ -75,6 +75,8 @@ cpucap_is_possible(const unsigned int cap)\n \t\treturn IS_ENABLED(CONFIG_HW_PERF_EVENTS);\n \tcase ARM64_HAS_LSUI:\n \t\treturn IS_ENABLED(CONFIG_ARM64_LSUI);\n+\tcase ARM64_HVO_COMPATIBLE:\n+\t\treturn IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP);\n \t}\n \n \treturn true;\ndiff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h\nindex a57870fa96db5b..d1504804f2bea9 100644\n--- a/arch/arm64/include/asm/cpufeature.h\n+++ b/arch/arm64/include/asm/cpufeature.h\n@@ -367,6 +367,14 @@ struct arm64_cpu_capabilities {\n \t * routine must check it before taking any action.\n \t */\n \tvoid (*cpu_enable)(const struct arm64_cpu_capabilities *cap);\n+\t/*\n+\t * In rare cases, capabilities are *sometimes* optional for late CPUs.\n+\t * This callback allows a capability to prevent onlining of\n+\t * incompatible CPUs when the capability is in fact required.\n+\t *\n+\t * Returns true iff onlining the CPU is permitted.\n+\t */\n+\tbool (*late_cpu_enable)(const struct arm64_cpu_capabilities *cap);\n \tunion {\n \t\tstruct {\t/* To be used for erratum handling only */\n \t\t\tstruct midr_range midr_range;\n@@ -885,6 +893,11 @@ static inline bool system_supports_bbml2_noabort(void)\n \treturn alternative_has_cap_unlikely(ARM64_HAS_BBML2_NOABORT);\n }\n \n+static inline bool system_supports_hvo(void)\n+{\n+\treturn alternative_has_cap_unlikely(ARM64_HVO_COMPATIBLE);\n+}\n+\n int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);\n bool try_emulate_mrs(struct pt_regs *regs, u32 isn);\n \n@@ -910,22 +923,36 @@ static inline u32 id_aa64mmfr0_parange_to_phys_shift(int parange)\n }\n \n /* Check whether hardware update of the Access flag is supported */\n-static inline bool cpu_has_hw_af(void)\n+static inline bool supports_hw_af(int scope)\n {\n \tu64 mmfr1;\n \n \tif (!IS_ENABLED(CONFIG_ARM64_HW_AFDBM))\n \t\treturn false;\n \n-\t/*\n-\t * Use cached version to avoid emulated msr operation on KVM\n-\t * guests.\n-\t */\n-\tmmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);\n+\tif (scope == SCOPE_SYSTEM) {\n+\t\t/*\n+\t\t * Use cached version to avoid emulated msr operation on KVM\n+\t\t * guests.\n+\t\t */\n+\t\tmmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);\n+\t} else {\n+\t\tmmfr1 = read_cpuid(ID_AA64MMFR1_EL1);\n+\t}\n \treturn cpuid_feature_extract_unsigned_field(mmfr1,\n \t\t\t\t\t\tID_AA64MMFR1_EL1_HAFDBS_SHIFT);\n }\n \n+static inline bool system_has_hw_af(void)\n+{\n+\treturn supports_hw_af(SCOPE_SYSTEM);\n+}\n+\n+static inline bool cpu_has_hw_af(void)\n+{\n+\treturn supports_hw_af(SCOPE_LOCAL_CPU);\n+}\n+\n static inline bool cpu_has_pan(void)\n {\n \tu64 mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);\ndiff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h\nindex d038ff14d16cae..73af7911fd8313 100644\n--- a/arch/arm64/include/asm/hugetlb.h\n+++ b/arch/arm64/include/asm/hugetlb.h\n@@ -11,6 +11,7 @@\n #define __ASM_HUGETLB_H\n \n #include \u003casm/cacheflush.h\u003e\n+#include \u003casm/cpufeature.h\u003e\n #include \u003casm/mte.h\u003e\n #include \u003casm/page.h\u003e\n \n@@ -65,6 +66,12 @@ extern void huge_ptep_modify_prot_commit(struct vm_area_struct *vma,\n \t\t\t\t\t unsigned long addr, pte_t *ptep,\n \t\t\t\t\t pte_t old_pte, pte_t new_pte);\n \n+#define __HAVE_ARCH_HVO_SUPPORTED\n+static inline bool arch_hugetlb_vmemmap_optimization_supported(void)\n+{\n+\treturn system_supports_hvo();\n+}\n+\n #include \u003casm-generic/hugetlb.h\u003e\n \n static inline void __flush_hugetlb_tlb_range(struct vm_area_struct *vma,\ndiff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h\nindex 1b4509d3382c63..26b05f8b70cd2e 100644\n--- a/arch/arm64/include/asm/pgalloc.h\n+++ b/arch/arm64/include/asm/pgalloc.h\n@@ -121,4 +121,58 @@ pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)\n \t\t       PMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_PXN);\n }\n \n+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD\n+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,\n+\t\t\t\t\t   unsigned long addr)\n+{\n+\tconst int max_attempts = 16;\n+\tint attempts = 0;\n+\tpmd_t old_pmd, new_pmd;\n+\n+\tif (!system_supports_hvo())\n+\t\treturn -EOPNOTSUPP;\n+\n+\tif (system_supports_bbml2_noabort()) {\n+\t\t/*\n+\t\t * BBML2_NOABORT allows block-\u003etable transitions if the PTEs\n+\t\t * underneath do not conflict with existing, potentially cached\n+\t\t * translations.\n+\t\t */\n+\t\tpmd_populate_kernel(\u0026init_mm, pmdp, pgtable);\n+\t\treturn 0;\n+\t}\n+\n+\tnew_pmd = __pmd(__phys_to_pmd_val(__pa(pgtable)) |\n+\t\t\tPMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_UXN);\n+\n+\told_pmd = pmdp_get(pmdp);\n+\n+\tdo {\n+\t\tif (WARN_ON_ONCE(!pmd_valid(old_pmd)))\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (WARN_ON_ONCE(!pmd_leaf(old_pmd)))\n+\t\t\treturn -EINVAL;\n+\n+\t\t/* We should never get a contiguous PMD here. */\n+\t\tif (WARN_ON_ONCE(pmd_cont(old_pmd)))\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (pmd_young(old_pmd)) {\n+\t\t\t/* __ptep_clear_young() returns the overwritten PTE */\n+\t\t\told_pmd = pte_pmd(pte_mkold(__ptep_clear_young((pte_t *)pmdp)));\n+\n+\t\t\tflush_tlb_kernel_range(addr, addr + PMD_SIZE);\n+\t\t}\n+\t/*\n+\t * Translations without AF cannot be cached, so we can replace\n+\t * them without BBM.\n+\t */\n+\t} while (!try_cmpxchg_relaxed(\u0026pmd_val(*pmdp), \u0026pmd_val(old_pmd),\n+\t\t\t\t      pmd_val(new_pmd)) \u0026\u0026\n+\t\t ++attempts \u003c max_attempts);\n+\n+\treturn attempts == max_attempts ? -EAGAIN : 0;\n+}\n+\n #endif\ndiff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h\nindex 27689c62bd25dd..7b11aa41d0a074 100644\n--- a/arch/arm64/include/asm/pgtable.h\n+++ b/arch/arm64/include/asm/pgtable.h\n@@ -1302,8 +1302,7 @@ static inline void __pte_clear(struct mm_struct *mm,\n \t__set_pte(ptep, __pte(0));\n }\n \n-static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,\n-\t\tunsigned long address, pte_t *ptep)\n+static inline pte_t __ptep_clear_young(pte_t *ptep)\n {\n \tpte_t old_pte, pte;\n \n@@ -1315,7 +1314,13 @@ static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,\n \t\t\t\t\t       pte_val(old_pte), pte_val(pte));\n \t} while (pte_val(pte) != pte_val(old_pte));\n \n-\treturn pte_young(pte);\n+\treturn pte;\n+}\n+\n+static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,\n+\t\tunsigned long address, pte_t *ptep)\n+{\n+\treturn pte_young(__ptep_clear_young(ptep));\n }\n \n static inline bool __ptep_clear_flush_young(struct vm_area_struct *vma,\n@@ -1598,7 +1603,7 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf,\n  * page after fork() + CoW for pfn mappings. We don't always have a\n  * hardware-managed access flag on arm64.\n  */\n-#define arch_has_hw_pte_young\t\tcpu_has_hw_af\n+#define arch_has_hw_pte_young\t\tsystem_has_hw_af\n \n #ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG\n #define arch_has_hw_nonleaf_pmd_young\tsystem_supports_haft\n@@ -1608,7 +1613,7 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf,\n  * Experimentally, it's cheap to set the access flag in hardware and we\n  * benefit from prefaulting mappings as 'old' to start with.\n  */\n-#define arch_wants_old_prefaulted_pte\tcpu_has_hw_af\n+#define arch_wants_old_prefaulted_pte\tsystem_has_hw_af\n \n /*\n  * Request exec memory is read into pagecache in at least 64K folios. This size\n@@ -1793,6 +1798,48 @@ static inline void pte_clear(struct mm_struct *mm,\n \t__pte_clear(mm, addr, ptep);\n }\n \n+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE\n+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,\n+\t\t\t\t\t const pte_t pte)\n+{\n+\tconst int max_attempts = 16;\n+\tint attempts = 0;\n+\tpte_t old_pte;\n+\n+\tif (!system_supports_hvo())\n+\t\treturn -EOPNOTSUPP;\n+\n+\t/* This routine is only to be used for valid-to-valid transitions. */\n+\tif (WARN_ON_ONCE(!pte_valid(pte)))\n+\t\treturn -EINVAL;\n+\n+\told_pte = __ptep_get(ptep);\n+\n+\tdo {\n+\t\tif (WARN_ON_ONCE(!pte_valid(old_pte)))\n+\t\t\treturn -EINVAL;\n+\n+\t\t/* We should never get a contiguous PTE here. */\n+\t\tif (WARN_ON_ONCE(pte_valid_cont(old_pte)))\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (pte_young(old_pte)) {\n+\t\t\t/* __ptep_clear_young() returns the overwritten PTE */\n+\t\t\told_pte = pte_mkold(__ptep_clear_young(ptep));\n+\n+\t\t\tflush_tlb_kernel_range(addr, addr + PAGE_SIZE);\n+\t\t}\n+\t/*\n+\t * Translations without AF cannot be cached, so we can replace\n+\t * them without BBM.\n+\t */\n+\t} while (!try_cmpxchg_relaxed(\u0026pte_val(*ptep), \u0026pte_val(old_pte),\n+\t\t\t\t      pte_val(pte)) \u0026\u0026\n+\t\t ++attempts \u003c max_attempts);\n+\n+\treturn attempts == max_attempts ? -EAGAIN : 0;\n+}\n+\n #define clear_full_ptes clear_full_ptes\n static inline void clear_full_ptes(struct mm_struct *mm, unsigned long addr,\n \t\t\t\tpte_t *ptep, unsigned int nr, int full)\ndiff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c\nindex 9a22df0c5120f3..65bb3c7c95952b 100644\n--- a/arch/arm64/kernel/cpufeature.c\n+++ b/arch/arm64/kernel/cpufeature.c\n@@ -76,6 +76,7 @@\n #include \u003clinux/kasan.h\u003e\n #include \u003clinux/percpu.h\u003e\n #include \u003clinux/sched/isolation.h\u003e\n+#include \u003clinux/hugetlb_vmemmap.h\u003e\n \n #include \u003casm/arm_pmuv3.h\u003e\n #include \u003casm/cpu.h\u003e\n@@ -2172,6 +2173,31 @@ static bool has_bbml2_noabort(const struct arm64_cpu_capabilities *caps, int sco\n \treturn cpu_supports_bbml2_noabort();\n }\n \n+static bool hvo_compatible(const struct arm64_cpu_capabilities *caps, int scope)\n+{\n+\t/*\n+\t * We need HW AF support to support changing vmemmap mapping level and\n+\t * OA without taking faults.\n+\t */\n+\treturn supports_hw_af(scope);\n+}\n+\n+static bool late_cpu_enable_hvo(const struct arm64_cpu_capabilities *__unused)\n+{\n+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\n+\tif (cpu_has_hw_af())\n+\t\treturn true;\n+\n+\t/*\n+\t * If the CPU does not support HW AF, we cannot online it if HVO is\n+\t * currently in use.\n+\t */\n+\treturn hugetlb_vmemmap_optimization_try_disable();\n+#else\n+\treturn true;\n+#endif\n+}\n+\n static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused)\n {\n \t/*\n@@ -3067,6 +3093,15 @@ static const struct arm64_cpu_capabilities arm64_features[] = {\n \t\t.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,\n \t\t.matches = has_bbml2_noabort,\n \t},\n+\t{\n+\t\t.desc = \"HugeTLB Vmemmap Optimization Support\",\n+\t\t.capability = ARM64_HVO_COMPATIBLE,\n+\t\t.type = ARM64_CPUCAP_SCOPE_SYSTEM |\n+\t\t\tARM64_CPUCAP_OPTIONAL_FOR_LATE_CPU |\n+\t\t\tARM64_CPUCAP_PERMITTED_FOR_LATE_CPU,\n+\t\t.matches = hvo_compatible,\n+\t\t.late_cpu_enable = late_cpu_enable_hvo,\n+\t},\n \t{\n \t\t.desc = \"52-bit Virtual Addressing for KVM (LPA2)\",\n \t\t.capability = ARM64_HAS_LPA2,\n@@ -3669,6 +3704,14 @@ static void verify_local_cpu_caps(u16 scope_mask)\n \t\t\t */\n \t\t\tif (!cpu_has_cap \u0026\u0026 !cpucap_late_cpu_optional(caps))\n \t\t\t\tbreak;\n+\n+\t\t\t/*\n+\t\t\t * Some optional features may in fact be required due\n+\t\t\t * to particular runtime conditions.\n+\t\t\t */\n+\t\t\tif (caps-\u003elate_cpu_enable \u0026\u0026 !caps-\u003elate_cpu_enable(caps))\n+\t\t\t\tbreak;\n+\n \t\t\t/*\n \t\t\t * We have to issue cpu_enable() irrespective of\n \t\t\t * whether the CPU has it or not, as it is enabeld\ndiff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps\nindex 9b85a84f6fd494..630953d59c5276 100644\n--- a/arch/arm64/tools/cpucaps\n+++ b/arch/arm64/tools/cpucaps\n@@ -70,6 +70,7 @@ HAS_VIRT_HOST_EXTN\n HAS_WFXT\n HAS_XNX\n HAFT\n+HVO_COMPATIBLE\n HW_DBM\n KVM_HVHE\n KVM_PROTECTED_MODE\ndiff --git a/arch/loongarch/include/asm/pgalloc.h b/arch/loongarch/include/asm/pgalloc.h\nindex 248f62d0b590ec..9322d962c9cc80 100644\n--- a/arch/loongarch/include/asm/pgalloc.h\n+++ b/arch/loongarch/include/asm/pgalloc.h\n@@ -24,6 +24,14 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t pte)\n \tset_pmd(pmd, __pmd((unsigned long)page_address(pte)));\n }\n \n+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD\n+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,\n+\t\t\t\t\t   unsigned long addr)\n+{\n+\tpmd_populate_kernel(\u0026init_mm, pmdp, pgtable);\n+\treturn 0;\n+}\n+\n #ifndef __PAGETABLE_PMD_FOLDED\n \n static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)\ndiff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h\nindex 223528c04d7392..e7b65056ef7384 100644\n--- a/arch/loongarch/include/asm/pgtable.h\n+++ b/arch/loongarch/include/asm/pgtable.h\n@@ -638,6 +638,14 @@ static inline long pmd_protnone(pmd_t pmd)\n #define pmd_leaf(pmd)\t\t((pmd_val(pmd) \u0026 _PAGE_HUGE) != 0)\n #define pud_leaf(pud)\t\t((pud_val(pud) \u0026 _PAGE_HUGE) != 0)\n \n+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE\n+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,\n+\t\t\t\t\t pte_t pte)\n+{\n+\tset_pte(ptep, pte);\n+\treturn 0;\n+}\n+\n /*\n  * We provide our own get_unmapped area to cope with the virtual aliasing\n  * constraints placed on us by the cache architecture.\ndiff --git a/arch/riscv/include/asm/pgalloc.h b/arch/riscv/include/asm/pgalloc.h\nindex 770ce18a7328b8..8dbbe62b257425 100644\n--- a/arch/riscv/include/asm/pgalloc.h\n+++ b/arch/riscv/include/asm/pgalloc.h\n@@ -31,6 +31,14 @@ static inline void pmd_populate(struct mm_struct *mm,\n \tset_pmd(pmd, __pmd((pfn \u003c\u003c _PAGE_PFN_SHIFT) | _PAGE_TABLE));\n }\n \n+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD\n+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,\n+\t\t\t\t\t   unsigned long addr)\n+{\n+\tpmd_populate_kernel(\u0026init_mm, pmdp, pgtable);\n+\treturn 0;\n+}\n+\n #ifndef __PAGETABLE_PMD_FOLDED\n static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)\n {\ndiff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h\nindex 5d5756bda82e3d..a11c569ee2f06f 100644\n--- a/arch/riscv/include/asm/pgtable.h\n+++ b/arch/riscv/include/asm/pgtable.h\n@@ -1162,6 +1162,14 @@ static inline pud_t pud_modify(pud_t pud, pgprot_t newprot)\n \n #endif /* CONFIG_TRANSPARENT_HUGEPAGE */\n \n+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE\n+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,\n+\t\t\t\t\t pte_t pte)\n+{\n+\tset_pte(ptep, pte);\n+\treturn 0;\n+}\n+\n /*\n  * Encode/decode swap entries and swap PTEs. Swap PTEs are all PTEs that\n  * are !pte_none() \u0026\u0026 !pte_present().\ndiff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h\nindex c88691b15f3c67..47b56bbd623614 100644\n--- a/arch/x86/include/asm/pgalloc.h\n+++ b/arch/x86/include/asm/pgalloc.h\n@@ -82,6 +82,14 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,\n \tset_pmd(pmd, __pmd(((pteval_t)pfn \u003c\u003c PAGE_SHIFT) | _PAGE_TABLE));\n }\n \n+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD\n+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,\n+\t\t\t\t\t   unsigned long addr)\n+{\n+\tpmd_populate_kernel(\u0026init_mm, pmdp, pgtable);\n+\treturn 0;\n+}\n+\n #if CONFIG_PGTABLE_LEVELS \u003e 2\n extern void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd);\n \ndiff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h\nindex ac295ca6c92f99..974151564071cb 100644\n--- a/arch/x86/include/asm/pgtable.h\n+++ b/arch/x86/include/asm/pgtable.h\n@@ -1372,6 +1372,14 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,\n }\n #endif\n \n+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE\n+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,\n+\t\t\t\t\t pte_t pte)\n+{\n+\tset_pte(ptep, pte);\n+\treturn 0;\n+}\n+\n #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD\n static inline pud_t pudp_establish(struct vm_area_struct *vma,\n \t\tunsigned long address, pud_t *pudp, pud_t pud)\ndiff --git a/include/asm-generic/hugetlb.h b/include/asm-generic/hugetlb.h\nindex e1a2e1b7c8e7b8..c4796c54b702a9 100644\n--- a/include/asm-generic/hugetlb.h\n+++ b/include/asm-generic/hugetlb.h\n@@ -128,4 +128,11 @@ static inline bool gigantic_page_runtime_supported(void)\n }\n #endif /* __HAVE_ARCH_GIGANTIC_PAGE_RUNTIME_SUPPORTED */\n \n+#ifndef __HAVE_ARCH_HVO_SUPPORTED\n+static inline bool arch_hugetlb_vmemmap_optimization_supported(void)\n+{\n+\treturn IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP);\n+}\n+#endif /* __HAVE_ARCH_HVO_SUPPORTED */\n+\n #endif /* _ASM_GENERIC_HUGETLB_H */\ndiff --git a/include/linux/hugetlb_vmemmap.h b/include/linux/hugetlb_vmemmap.h\nnew file mode 100644\nindex 00000000000000..a671eb4a4ff466\n--- /dev/null\n+++ b/include/linux/hugetlb_vmemmap.h\n@@ -0,0 +1,20 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _LINUX_HUGETLB_VMEMMAP_H\n+#define _LINUX_HUGETLB_VMEMMAP_H\n+\n+#include \u003clinux/types.h\u003e\n+\n+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\n+\n+bool hugetlb_vmemmap_optimization_try_disable(void);\n+\n+#else /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */\n+\n+static inline bool hugetlb_vmemmap_optimization_try_disable(void)\n+{\n+\treturn true;\n+}\n+\n+#endif\n+\n+#endif /* _LINUX_HUGETLB_VMEMMAP_H */\ndiff --git a/include/linux/pgalloc.h b/include/linux/pgalloc.h\nindex 9174fa59bbc54d..ed446d95ca3730 100644\n--- a/include/linux/pgalloc.h\n+++ b/include/linux/pgalloc.h\n@@ -26,4 +26,24 @@\n \t\t\tarch_sync_kernel_mappings(addr, addr);\t\t\\\n \t} while (0)\n \n+#ifndef __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD\n+/*\n+ * try_populate_vmemmap_pmd - Populate a PMD that is in use by the vmemmap.\n+ * @addr: Base address of the remapped PMD.\n+ * @pmdp: Page table pointer to be overwritten.\n+ * @pgtable: Pointer to the page table that the new PMD will point to.\n+ *\n+ * This function is only to be used to update PMDs that map the vmemmap to\n+ * point to a page of already-populated PTEs that map the same pages.\n+ *\n+ * Implementations of this function must ensure that, while the update is taking\n+ * place, CPUs will not fault on the remapped virtual address range.\n+ */\n+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,\n+\t\t\t\t\t   unsigned long addr)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+#endif\n+\n #endif /* _LINUX_PGALLOC_H */\ndiff --git a/include/linux/pgtable.h b/include/linux/pgtable.h\nindex 2981e386da7b3c..e1abccf76bb463 100644\n--- a/include/linux/pgtable.h\n+++ b/include/linux/pgtable.h\n@@ -457,6 +457,27 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr,\n #endif\n #define set_pte_at(mm, addr, ptep, pte) set_ptes(mm, addr, ptep, pte, 1)\n \n+#ifndef __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE\n+/*\n+ * try_update_vmemmap_pte - Remap PTEs used by the vmemmap.\n+ * @addr: Base address of the remapped PTE.\n+ * @ptep: Page table pointer to be overwritten.\n+ * @pte: Page table entry to write.\n+ *\n+ * This function is only to be used to update PTEs that map the vmemmap. The\n+ * only valid transitions supported by this function are: leaf-level\n+ * (PAGE_SIZE), valid-to-valid. The pfn and prot bits may be changed.\n+ *\n+ * Implementations of this function must ensure that, while the update is taking\n+ * place, CPUs will not fault on the remapped virtual address.\n+ */\n+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,\n+\t\t\t\t\t pte_t pte)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+#endif\n+\n #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS\n extern int ptep_set_access_flags(struct vm_area_struct *vma,\n \t\t\t\t unsigned long address, pte_t *ptep,\ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex 571212b80835ec..298673a6c57c5c 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -47,7 +47,7 @@\n #include \u003clinux/node.h\u003e\n #include \u003clinux/page_owner.h\u003e\n #include \"internal.h\"\n-#include \"hugetlb_vmemmap.h\"\n+#include \"hugetlb_vmemmap_internal.h\"\n #include \"hugetlb_cma.h\"\n #include \"hugetlb_internal.h\"\n #include \u003clinux/page-isolation.h\u003e\ndiff --git a/mm/hugetlb_sysfs.c b/mm/hugetlb_sysfs.c\nindex 79ece91406bfa4..83a95f5142f5a4 100644\n--- a/mm/hugetlb_sysfs.c\n+++ b/mm/hugetlb_sysfs.c\n@@ -8,7 +8,7 @@\n #include \u003clinux/page_owner.h\u003e\n #include \u003clinux/page-isolation.h\u003e\n \n-#include \"hugetlb_vmemmap.h\"\n+#include \"hugetlb_vmemmap_internal.h\"\n #include \"hugetlb_internal.h\"\n \n #define HSTATE_ATTR_RO(_name) \\\ndiff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c\nindex 133b46dfb09f5c..7e7f8579cc2ae5 100644\n--- a/mm/hugetlb_vmemmap.c\n+++ b/mm/hugetlb_vmemmap.c\n@@ -16,9 +16,11 @@\n #include \u003clinux/mmdebug.h\u003e\n #include \u003clinux/pagewalk.h\u003e\n #include \u003clinux/pgalloc.h\u003e\n+#include \u003clinux/hugetlb.h\u003e\n+#include \u003clinux/hugetlb_vmemmap.h\u003e\n \n #include \u003casm/tlbflush.h\u003e\n-#include \"hugetlb_vmemmap.h\"\n+#include \"hugetlb_vmemmap_internal.h\"\n #include \"internal.h\"\n \n /**\n@@ -34,7 +36,7 @@\n  *\t\t\toperations.\n  */\n struct vmemmap_remap_walk {\n-\tvoid\t\t\t(*remap_pte)(pte_t *pte, unsigned long addr,\n+\tint\t\t\t(*remap_pte)(pte_t *pte, unsigned long addr,\n \t\t\t\t\t     struct vmemmap_remap_walk *walk);\n \n \tunsigned long\t\tnr_walked;\n@@ -54,6 +56,7 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,\n \t\t\t     struct vmemmap_remap_walk *walk)\n {\n \tpmd_t __pmd;\n+\tint ret;\n \tint i;\n \tunsigned long addr = start;\n \tpte_t *pgtable;\n@@ -73,8 +76,15 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,\n \t\tset_pte_at(\u0026init_mm, addr, pte, entry);\n \t}\n \n+\tret = 0;\n \tspin_lock(\u0026init_mm.page_table_lock);\n \tif (likely(pmd_leaf(*pmd))) {\n+\t\t/* Make pte visible before pmd. See comment in pmd_install(). */\n+\t\tsmp_wmb();\n+\t\tret = try_populate_vmemmap_pmd(pmd, pgtable, start);\n+\t\tif (ret)\n+\t\t\tgoto free;\n+\n \t\t/*\n \t\t * Higher order allocations from buddy allocator must be able to\n \t\t * be treated as independent small pages (as they can be freed\n@@ -83,17 +93,17 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,\n \t\tif (!PageReserved(head))\n \t\t\tsplit_page(head, get_order(PMD_SIZE));\n \n-\t\t/* Make pte visible before pmd. See comment in pmd_install(). */\n-\t\tsmp_wmb();\n-\t\tpmd_populate_kernel(\u0026init_mm, pmd, pgtable);\n \t\tif (!(walk-\u003eflags \u0026 VMEMMAP_SPLIT_NO_TLB_FLUSH))\n \t\t\tflush_tlb_kernel_range(start, start + PMD_SIZE);\n-\t} else {\n-\t\tpte_free_kernel(\u0026init_mm, pgtable);\n-\t}\n-\tspin_unlock(\u0026init_mm.page_table_lock);\n+\t} else\n+\t\tgoto free;\n \n-\treturn 0;\n+out:\n+\tspin_unlock(\u0026init_mm.page_table_lock);\n+\treturn ret;\n+free:\n+\tpte_free_kernel(\u0026init_mm, pgtable);\n+\tgoto out;\n }\n \n static int vmemmap_pmd_entry(pmd_t *pmd, unsigned long addr,\n@@ -141,11 +151,13 @@ static int vmemmap_pte_entry(pte_t *pte, unsigned long addr,\n \t\t\t     unsigned long next, struct mm_walk *walk)\n {\n \tstruct vmemmap_remap_walk *vmemmap_walk = walk-\u003eprivate;\n+\tint ret = 0;\n \n-\tvmemmap_walk-\u003eremap_pte(pte, addr, vmemmap_walk);\n-\tvmemmap_walk-\u003enr_walked++;\n+\tret = vmemmap_walk-\u003eremap_pte(pte, addr, vmemmap_walk);\n+\tif (!ret)\n+\t\tvmemmap_walk-\u003enr_walked++;\n \n-\treturn 0;\n+\treturn ret;\n }\n \n static const struct mm_walk_ops vmemmap_remap_ops = {\n@@ -164,13 +176,11 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end,\n \tret = walk_kernel_page_table_range(start, end, \u0026vmemmap_remap_ops,\n \t\t\t\t    NULL, walk);\n \tmmap_read_unlock(\u0026init_mm);\n-\tif (ret)\n-\t\treturn ret;\n \n \tif (walk-\u003eremap_pte \u0026\u0026 !(walk-\u003eflags \u0026 VMEMMAP_REMAP_NO_TLB_FLUSH))\n \t\tflush_tlb_kernel_range(start, end);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n /*\n@@ -199,18 +209,20 @@ static void free_vmemmap_page_list(struct list_head *list)\n \t\tfree_vmemmap_page(page);\n }\n \n-static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,\n-\t\t\t      struct vmemmap_remap_walk *walk)\n+static int vmemmap_remap_pte(pte_t *pte, unsigned long addr,\n+\t\t\t     struct vmemmap_remap_walk *walk)\n {\n \tstruct page *page = pte_page(ptep_get(pte));\n \tpte_t entry;\n+\tbool head;\n+\tint ret;\n+\n+\thead = walk-\u003enr_walked == 0 \u0026\u0026 walk-\u003evmemmap_head;\n \n \t/* Remapping the head page requires r/w */\n-\tif (unlikely(walk-\u003enr_walked == 0 \u0026\u0026 walk-\u003evmemmap_head)) {\n+\tif (unlikely(head)) {\n \t\tVM_WARN_ON_ONCE(!PageHead((const struct page *)addr));\n \n-\t\tlist_del(\u0026walk-\u003evmemmap_head-\u003elru);\n-\n \t\t/*\n \t\t * Makes sure that preceding stores to the page contents from\n \t\t * vmemmap_remap_free() become visible before the set_pte_at()\n@@ -229,35 +241,56 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,\n \t\tentry = mk_pte(walk-\u003evmemmap_tail, PAGE_KERNEL_RO);\n \t}\n \n+\tret = try_update_vmemmap_pte(addr, pte, entry);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* We successfully overwrote the vmemmap PTE, so we can free\n+\t * the vmemmap page that was just unmapped, and if we mapped\n+\t * the new head page, remove it from the list so that it\n+\t * doesn't get freed later.\n+\t */\n \tlist_add(\u0026page-\u003elru, walk-\u003evmemmap_pages);\n-\tset_pte_at(\u0026init_mm, addr, pte, entry);\n+\tif (head)\n+\t\tlist_del(\u0026walk-\u003evmemmap_head-\u003elru);\n+\n+\treturn 0;\n }\n \n-static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,\n-\t\t\t\tstruct vmemmap_remap_walk *walk)\n+static int vmemmap_restore_pte(pte_t *pte, unsigned long addr,\n+\t\t\t       struct vmemmap_remap_walk *walk)\n {\n \tstruct page *src = pte_page(ptep_get(pte)), *dst;\n+\tint ret;\n+\n+\tif (WARN_ON_ONCE(!walk-\u003evmemmap_tail))\n+\t\treturn -EINVAL;\n \n \t/*\n-\t * When rolling back vmemmap_remap_free(), keep the copied head page\n+\t * When restoring a partially-HVOed page, keep the copied head page\n \t * mapping and restore only PTEs currently pointing at the shared tail\n \t * page.\n \t */\n-\tif (walk-\u003evmemmap_tail \u0026\u0026 walk-\u003evmemmap_tail != src)\n-\t\treturn;\n+\tif (walk-\u003evmemmap_tail != src)\n+\t\treturn 0;\n \n \tVM_WARN_ON_ONCE(PageHead((const struct page *)addr));\n \n \tdst = list_first_entry(walk-\u003evmemmap_pages, struct page, lru);\n-\tlist_del(\u0026dst-\u003elru);\n \tcopy_page(page_to_virt(dst), page_to_virt(src));\n \n \t/*\n \t * Makes sure that preceding stores to the page contents become visible\n-\t * before the set_pte_at() write.\n+\t * before the try_update_vmemmap_pte() write.\n \t */\n \tsmp_wmb();\n-\tset_pte_at(\u0026init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));\n+\n+\tret = try_update_vmemmap_pte(addr, pte, mk_pte(dst, PAGE_KERNEL));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tlist_del(\u0026dst-\u003elru);\n+\treturn 0;\n }\n \n /**\n@@ -279,6 +312,7 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)\n \treturn vmemmap_remap_range(start, end, \u0026walk);\n }\n \n+static const int VMEMMAP_REMAP_INCOMPLETE = 1;\n /**\n  * vmemmap_remap_free - remap the vmemmap virtual address range [@start, @end)\n  *\t\t\tto use @vmemmap_head/tail, then free vmemmap which\n@@ -293,7 +327,8 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)\n  *\t\tresponsibility to free pages.\n  * @flags:\tmodifications to vmemmap_remap_walk flags\n  *\n- * Return: %0 on success, negative error code otherwise.\n+ * Return: %0 on success, VMEMMAP_REMAP_INCOMPLETE if the page is incompletely\n+ *         optimized, negative error code otherwise.\n  */\n static int vmemmap_remap_free(unsigned long start, unsigned long end,\n \t\t\t      struct page *vmemmap_head,\n@@ -328,7 +363,8 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,\n \t\t.flags\t\t= 0,\n \t};\n \n-\tvmemmap_remap_range(start, end, \u0026walk);\n+\tif (vmemmap_remap_range(start, end, \u0026walk))\n+\t\treturn VMEMMAP_REMAP_INCOMPLETE;\n \n \treturn ret;\n }\n@@ -357,10 +393,38 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,\n \treturn -ENOMEM;\n }\n \n+static struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)\n+{\n+\tconst unsigned int idx = order - VMEMMAP_TAIL_MIN_ORDER;\n+\tstruct page *tail, *p;\n+\tint node = zone_to_nid(zone);\n+\n+\ttail = READ_ONCE(zone-\u003evmemmap_tails[idx]);\n+\tif (likely(tail))\n+\t\treturn tail;\n+\n+\ttail = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);\n+\tif (!tail)\n+\t\treturn NULL;\n+\n+\tp = page_to_virt(tail);\n+\tfor (int i = 0; i \u003c PAGE_SIZE / sizeof(struct page); i++)\n+\t\tinit_compound_tail(p + i, NULL, order, zone);\n+\n+\tif (cmpxchg(\u0026zone-\u003evmemmap_tails[idx], NULL, tail)) {\n+\t\t__free_page(tail);\n+\t\ttail = READ_ONCE(zone-\u003evmemmap_tails[idx]);\n+\t}\n+\n+\treturn tail;\n+}\n+\n /**\n  * vmemmap_remap_alloc - remap the vmemmap virtual address range [@start, end)\n  *\t\t\t to the page which is from the @vmemmap_pages\n  *\t\t\t respectively.\n+ * @h:\t\tthe hstate for the folio whose vmemmap is getting remapped\n+ * @folio:\tthe folio whose vmemmap is getting remapped\n  * @start:\tstart address of the vmemmap virtual address range that we want\n  *\t\tto remap.\n  * @end:\tend address of the vmemmap virtual address range that we want to\n@@ -369,20 +433,68 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,\n  *\n  * Return: %0 on success, negative error code otherwise.\n  */\n-static int vmemmap_remap_alloc(unsigned long start, unsigned long end,\n+static int vmemmap_remap_alloc(const struct hstate *h, struct folio *folio,\n+\t\t\t       unsigned long start, unsigned long end,\n \t\t\t       unsigned long flags)\n {\n \tLIST_HEAD(vmemmap_pages);\n-\tstruct vmemmap_remap_walk walk = {\n+\tstruct vmemmap_remap_walk walk;\n+\tstruct page *vmemmap_tail;\n+\tint ret;\n+\n+\tvmemmap_tail = vmemmap_get_tail(h-\u003eorder, folio_zone(folio));\n+\tif (WARN_ON_ONCE(!vmemmap_tail))\n+\t\treturn -ENOMEM;\n+\n+\tif (alloc_vmemmap_page_list(start, end, \u0026vmemmap_pages))\n+\t\treturn -ENOMEM;\n+\n+\twalk = (struct vmemmap_remap_walk) {\n \t\t.remap_pte\t= vmemmap_restore_pte,\n+\t\t.vmemmap_tail\t= vmemmap_tail,\n \t\t.vmemmap_pages\t= \u0026vmemmap_pages,\n \t\t.flags\t\t= flags,\n \t};\n \n-\tif (alloc_vmemmap_page_list(start, end, \u0026vmemmap_pages))\n-\t\treturn -ENOMEM;\n+\tret = vmemmap_remap_range(start, end, \u0026walk);\n \n-\treturn vmemmap_remap_range(start, end, \u0026walk);\n+\t/* Not all pages may have been consumed */\n+\tfree_vmemmap_page_list(\u0026vmemmap_pages);\n+\n+\treturn ret;\n+}\n+\n+enum hugetlb_hvo_status {\n+\tHVO_INACTIVE = 0,\n+\tHVO_ACTIVE,\n+\tHVO_PERMANENTLY_INACTIVE,\n+};\n+static enum hugetlb_hvo_status hvo_status = HVO_INACTIVE;\n+\n+static bool hugetlb_hvo_status_try_set(enum hugetlb_hvo_status status)\n+{\n+\tenum hugetlb_hvo_status old;\n+\n+\told = READ_ONCE(hvo_status);\n+\n+retry:\n+\t/* The current setting is what we want. */\n+\tif (old == status)\n+\t\treturn true;\n+\n+\t/* The current setting cannot be changed. */\n+\tif (old != HVO_INACTIVE)\n+\t\treturn false;\n+\n+\tif (!try_cmpxchg_relaxed(\u0026hvo_status, \u0026old, status))\n+\t\tgoto retry;\n+\n+\treturn true;\n+}\n+\n+bool hugetlb_vmemmap_optimization_try_disable(void)\n+{\n+\treturn hugetlb_hvo_status_try_set(HVO_PERMANENTLY_INACTIVE);\n }\n \n static bool vmemmap_optimize_enabled = IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON);\n@@ -415,7 +527,7 @@ static int __hugetlb_vmemmap_restore_folio(const struct hstate *h,\n \t * When a HugeTLB page is freed to the buddy allocator, previously\n \t * discarded vmemmap pages must be allocated and remapping.\n \t */\n-\tret = vmemmap_remap_alloc(vmemmap_start, vmemmap_end, flags);\n+\tret = vmemmap_remap_alloc(h, folio, vmemmap_start, vmemmap_end, flags);\n \tif (!ret)\n \t\tfolio_clear_hugetlb_vmemmap_optimized(folio);\n \n@@ -487,36 +599,16 @@ static bool vmemmap_should_optimize_folio(const struct hstate *h, struct folio *\n \tif (!READ_ONCE(vmemmap_optimize_enabled))\n \t\treturn false;\n \n-\tif (!hugetlb_vmemmap_optimizable(h))\n+\tif (!arch_hugetlb_vmemmap_optimization_supported())\n \t\treturn false;\n \n-\treturn true;\n-}\n-\n-static struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)\n-{\n-\tconst unsigned int idx = order - VMEMMAP_TAIL_MIN_ORDER;\n-\tstruct page *tail, *p;\n-\tint node = zone_to_nid(zone);\n-\n-\ttail = READ_ONCE(zone-\u003evmemmap_tails[idx]);\n-\tif (likely(tail))\n-\t\treturn tail;\n-\n-\ttail = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);\n-\tif (!tail)\n-\t\treturn NULL;\n-\n-\tp = page_to_virt(tail);\n-\tfor (int i = 0; i \u003c PAGE_SIZE / sizeof(struct page); i++)\n-\t\tinit_compound_tail(p + i, NULL, order, zone);\n+\tif (!hugetlb_vmemmap_optimizable(h))\n+\t\treturn false;\n \n-\tif (cmpxchg(\u0026zone-\u003evmemmap_tails[idx], NULL, tail)) {\n-\t\t__free_page(tail);\n-\t\ttail = READ_ONCE(zone-\u003evmemmap_tails[idx]);\n-\t}\n+\tif (!hugetlb_hvo_status_try_set(HVO_ACTIVE))\n+\t\treturn false;\n \n-\treturn tail;\n+\treturn true;\n }\n \n static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,\n@@ -574,7 +666,11 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,\n \t\t\t\t vmemmap_head, vmemmap_tail,\n \t\t\t\t vmemmap_pages, flags);\n out:\n-\tif (ret)\n+\t/*\n+\t * If ret == VMEMMAP_REMAP_INCOMPLETE, the folio might be partially\n+\t * HVOed. Leave the HVO page folio flag in place.\n+\t */\n+\tif (ret \u003c 0)\n \t\tfolio_clear_hugetlb_vmemmap_optimized(folio);\n \n \treturn ret;\n@@ -717,6 +813,14 @@ static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)\n \tif (!READ_ONCE(vmemmap_optimize_enabled))\n \t\treturn false;\n \n+\t/*\n+\t * Architectures may return false here but true by the time\n+\t * hugetlb_init() is called. In this case, although the folios will\n+\t * not be pre-HVOed, they will be optimized in hugetlb_init().\n+\t */\n+\tif (!arch_hugetlb_vmemmap_optimization_supported())\n+\t\treturn false;\n+\n \tif (!hugetlb_vmemmap_optimizable(m-\u003ehstate))\n \t\treturn false;\n \n@@ -742,6 +846,9 @@ static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)\n \t    !IS_ALIGNED(psize, pmd_vmemmap_size))\n \t\treturn false;\n \n+\tif (!hugetlb_hvo_status_try_set(HVO_ACTIVE))\n+\t\treturn false;\n+\n \treturn true;\n }\n \ndiff --git a/mm/hugetlb_vmemmap.h b/mm/hugetlb_vmemmap_internal.h\nsimilarity index 95%\nrename from mm/hugetlb_vmemmap.h\nrename to mm/hugetlb_vmemmap_internal.h\nindex 18b4908252153d..6b28780014bc18 100644\n--- a/mm/hugetlb_vmemmap.h\n+++ b/mm/hugetlb_vmemmap_internal.h\n@@ -6,8 +6,8 @@\n  *\n  *     Author: Muchun Song \u003csongmuchun@bytedance.com\u003e\n  */\n-#ifndef _LINUX_HUGETLB_VMEMMAP_H\n-#define _LINUX_HUGETLB_VMEMMAP_H\n+#ifndef _MM_HUGETLB_VMEMMAP_INTERNAL_H\n+#define _MM_HUGETLB_VMEMMAP_INTERNAL_H\n #include \u003clinux/hugetlb.h\u003e\n #include \u003clinux/io.h\u003e\n #include \u003clinux/memblock.h\u003e\n@@ -95,4 +95,4 @@ static inline bool hugetlb_vmemmap_optimizable(const struct hstate *h)\n {\n \treturn hugetlb_vmemmap_optimizable_size(h) != 0;\n }\n-#endif /* _LINUX_HUGETLB_VMEMMAP_H */\n+#endif /* _MM_HUGETLB_VMEMMAP_INTERNAL_H */\ndiff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c\nindex 99e2be39671b63..94d5da9f8ab7c1 100644\n--- a/mm/sparse-vmemmap.c\n+++ b/mm/sparse-vmemmap.c\n@@ -32,7 +32,7 @@\n #include \u003casm/dma.h\u003e\n #include \u003casm/tlbflush.h\u003e\n \n-#include \"hugetlb_vmemmap.h\"\n+#include \"hugetlb_vmemmap_internal.h\"\n \n /*\n  * Flags for vmemmap_populate_range and friends.\n"
}

2/1 2026/07/08 15:12 agent patch-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP",
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON"
  ],
  "FocusSymbols": [
    "vmemmap_split_pmd",
    "vmemmap_remap_pte",
    "vmemmap_restore_pte",
    "vmemmap_remap_free",
    "vmemmap_remap_alloc",
    "hugetlb_vmemmap_optimization_try_disable",
    "__hugetlb_vmemmap_restore_folio",
    "__hugetlb_vmemmap_optimize_folio",
    "vmemmap_should_optimize_folio",
    "vmemmap_should_optimize_bootmem_page",
    "late_cpu_enable_hvo",
    "hvo_compatible"
  ],
  "Reasoning": "The patch introduces ARM64 support for HugeTLB Vmemmap Optimization (HVO). It modifies core kernel logic in `mm/hugetlb_vmemmap.c` to use architecture-specific functions for populating and updating vmemmap PMDs and PTEs. It also adds CPU capability checks for HVO on ARM64. This is a functional change that can be reached when allocating and freeing HugeTLB pages with HVO enabled, making it worth fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit ddf2305462eb8fab9abcb5f12b97b1f970cba595
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Jul 8 15:12:22 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index f37a81950e250e..55bd4c09f49496 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12013,6 +12013,7 @@ F:	Documentation/mm/hugetlbfs_reserv.rst
 F:	Documentation/mm/vmemmap_dedup.rst
 F:	fs/hugetlbfs/
 F:	include/linux/hugetlb.h
+F:	include/linux/hugetlb_vmemmap.h
 F:	include/trace/events/hugetlbfs.h
 F:	mm/hugetlb.c
 F:	mm/hugetlb_cgroup.c
@@ -12021,7 +12022,7 @@ F:	mm/hugetlb_cma.h
 F:	mm/hugetlb_sysctl.c
 F:	mm/hugetlb_sysfs.c
 F:	mm/hugetlb_vmemmap.c
-F:	mm/hugetlb_vmemmap.h
+F:	mm/hugetlb_vmemmap_internal.h
 F:	tools/testing/selftests/cgroup/test_hugetlb_memcg.c
 
 HVA ST MEDIA DRIVER
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b3afe0688919bd..2a59155f68dcd8 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -259,6 +259,7 @@ config ARM64
 	select USER_STACKTRACE_SUPPORT
 	select VDSO_GETRANDOM
 	select VMAP_STACK
+	select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if ARM64_HW_AFDBM
 	help
 	  ARM 64-bit (AArch64) Linux support.
 
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 25c61cda901c5d..6db3ef827f86a9 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -75,6 +75,8 @@ cpucap_is_possible(const unsigned int cap)
 		return IS_ENABLED(CONFIG_HW_PERF_EVENTS);
 	case ARM64_HAS_LSUI:
 		return IS_ENABLED(CONFIG_ARM64_LSUI);
+	case ARM64_HVO_COMPATIBLE:
+		return IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP);
 	}
 
 	return true;
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index a57870fa96db5b..d1504804f2bea9 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -367,6 +367,14 @@ struct arm64_cpu_capabilities {
 	 * routine must check it before taking any action.
 	 */
 	void (*cpu_enable)(const struct arm64_cpu_capabilities *cap);
+	/*
+	 * In rare cases, capabilities are *sometimes* optional for late CPUs.
+	 * This callback allows a capability to prevent onlining of
+	 * incompatible CPUs when the capability is in fact required.
+	 *
+	 * Returns true iff onlining the CPU is permitted.
+	 */
+	bool (*late_cpu_enable)(const struct arm64_cpu_capabilities *cap);
 	union {
 		struct {	/* To be used for erratum handling only */
 			struct midr_range midr_range;
@@ -885,6 +893,11 @@ static inline bool system_supports_bbml2_noabort(void)
 	return alternative_has_cap_unlikely(ARM64_HAS_BBML2_NOABORT);
 }
 
+static inline bool system_supports_hvo(void)
+{
+	return alternative_has_cap_unlikely(ARM64_HVO_COMPATIBLE);
+}
+
 int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);
 bool try_emulate_mrs(struct pt_regs *regs, u32 isn);
 
@@ -910,22 +923,36 @@ static inline u32 id_aa64mmfr0_parange_to_phys_shift(int parange)
 }
 
 /* Check whether hardware update of the Access flag is supported */
-static inline bool cpu_has_hw_af(void)
+static inline bool supports_hw_af(int scope)
 {
 	u64 mmfr1;
 
 	if (!IS_ENABLED(CONFIG_ARM64_HW_AFDBM))
 		return false;
 
-	/*
-	 * Use cached version to avoid emulated msr operation on KVM
-	 * guests.
-	 */
-	mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	if (scope == SCOPE_SYSTEM) {
+		/*
+		 * Use cached version to avoid emulated msr operation on KVM
+		 * guests.
+		 */
+		mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+	} else {
+		mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
+	}
 	return cpuid_feature_extract_unsigned_field(mmfr1,
 						ID_AA64MMFR1_EL1_HAFDBS_SHIFT);
 }
 
+static inline bool system_has_hw_af(void)
+{
+	return supports_hw_af(SCOPE_SYSTEM);
+}
+
+static inline bool cpu_has_hw_af(void)
+{
+	return supports_hw_af(SCOPE_LOCAL_CPU);
+}
+
 static inline bool cpu_has_pan(void)
 {
 	u64 mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
diff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h
index d038ff14d16cae..73af7911fd8313 100644
--- a/arch/arm64/include/asm/hugetlb.h
+++ b/arch/arm64/include/asm/hugetlb.h
@@ -11,6 +11,7 @@
 #define __ASM_HUGETLB_H
 
 #include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
 #include <asm/mte.h>
 #include <asm/page.h>
 
@@ -65,6 +66,12 @@ extern void huge_ptep_modify_prot_commit(struct vm_area_struct *vma,
 					 unsigned long addr, pte_t *ptep,
 					 pte_t old_pte, pte_t new_pte);
 
+#define __HAVE_ARCH_HVO_SUPPORTED
+static inline bool arch_hugetlb_vmemmap_optimization_supported(void)
+{
+	return system_supports_hvo();
+}
+
 #include <asm-generic/hugetlb.h>
 
 static inline void __flush_hugetlb_tlb_range(struct vm_area_struct *vma,
diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h
index 1b4509d3382c63..26b05f8b70cd2e 100644
--- a/arch/arm64/include/asm/pgalloc.h
+++ b/arch/arm64/include/asm/pgalloc.h
@@ -121,4 +121,58 @@ pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)
 		       PMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_PXN);
 }
 
+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD
+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,
+					   unsigned long addr)
+{
+	const int max_attempts = 16;
+	int attempts = 0;
+	pmd_t old_pmd, new_pmd;
+
+	if (!system_supports_hvo())
+		return -EOPNOTSUPP;
+
+	if (system_supports_bbml2_noabort()) {
+		/*
+		 * BBML2_NOABORT allows block->table transitions if the PTEs
+		 * underneath do not conflict with existing, potentially cached
+		 * translations.
+		 */
+		pmd_populate_kernel(&init_mm, pmdp, pgtable);
+		return 0;
+	}
+
+	new_pmd = __pmd(__phys_to_pmd_val(__pa(pgtable)) |
+			PMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_UXN);
+
+	old_pmd = pmdp_get(pmdp);
+
+	do {
+		if (WARN_ON_ONCE(!pmd_valid(old_pmd)))
+			return -EINVAL;
+
+		if (WARN_ON_ONCE(!pmd_leaf(old_pmd)))
+			return -EINVAL;
+
+		/* We should never get a contiguous PMD here. */
+		if (WARN_ON_ONCE(pmd_cont(old_pmd)))
+			return -EINVAL;
+
+		if (pmd_young(old_pmd)) {
+			/* __ptep_clear_young() returns the overwritten PTE */
+			old_pmd = pte_pmd(pte_mkold(__ptep_clear_young((pte_t *)pmdp)));
+
+			flush_tlb_kernel_range(addr, addr + PMD_SIZE);
+		}
+	/*
+	 * Translations without AF cannot be cached, so we can replace
+	 * them without BBM.
+	 */
+	} while (!try_cmpxchg_relaxed(&pmd_val(*pmdp), &pmd_val(old_pmd),
+				      pmd_val(new_pmd)) &&
+		 ++attempts < max_attempts);
+
+	return attempts == max_attempts ? -EAGAIN : 0;
+}
+
 #endif
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 27689c62bd25dd..7b11aa41d0a074 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1302,8 +1302,7 @@ static inline void __pte_clear(struct mm_struct *mm,
 	__set_pte(ptep, __pte(0));
 }
 
-static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
-		unsigned long address, pte_t *ptep)
+static inline pte_t __ptep_clear_young(pte_t *ptep)
 {
 	pte_t old_pte, pte;
 
@@ -1315,7 +1314,13 @@ static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
 					       pte_val(old_pte), pte_val(pte));
 	} while (pte_val(pte) != pte_val(old_pte));
 
-	return pte_young(pte);
+	return pte;
+}
+
+static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
+		unsigned long address, pte_t *ptep)
+{
+	return pte_young(__ptep_clear_young(ptep));
 }
 
 static inline bool __ptep_clear_flush_young(struct vm_area_struct *vma,
@@ -1598,7 +1603,7 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf,
  * page after fork() + CoW for pfn mappings. We don't always have a
  * hardware-managed access flag on arm64.
  */
-#define arch_has_hw_pte_young		cpu_has_hw_af
+#define arch_has_hw_pte_young		system_has_hw_af
 
 #ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG
 #define arch_has_hw_nonleaf_pmd_young	system_supports_haft
@@ -1608,7 +1613,7 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf,
  * Experimentally, it's cheap to set the access flag in hardware and we
  * benefit from prefaulting mappings as 'old' to start with.
  */
-#define arch_wants_old_prefaulted_pte	cpu_has_hw_af
+#define arch_wants_old_prefaulted_pte	system_has_hw_af
 
 /*
  * Request exec memory is read into pagecache in at least 64K folios. This size
@@ -1793,6 +1798,48 @@ static inline void pte_clear(struct mm_struct *mm,
 	__pte_clear(mm, addr, ptep);
 }
 
+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE
+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,
+					 const pte_t pte)
+{
+	const int max_attempts = 16;
+	int attempts = 0;
+	pte_t old_pte;
+
+	if (!system_supports_hvo())
+		return -EOPNOTSUPP;
+
+	/* This routine is only to be used for valid-to-valid transitions. */
+	if (WARN_ON_ONCE(!pte_valid(pte)))
+		return -EINVAL;
+
+	old_pte = __ptep_get(ptep);
+
+	do {
+		if (WARN_ON_ONCE(!pte_valid(old_pte)))
+			return -EINVAL;
+
+		/* We should never get a contiguous PTE here. */
+		if (WARN_ON_ONCE(pte_valid_cont(old_pte)))
+			return -EINVAL;
+
+		if (pte_young(old_pte)) {
+			/* __ptep_clear_young() returns the overwritten PTE */
+			old_pte = pte_mkold(__ptep_clear_young(ptep));
+
+			flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
+		}
+	/*
+	 * Translations without AF cannot be cached, so we can replace
+	 * them without BBM.
+	 */
+	} while (!try_cmpxchg_relaxed(&pte_val(*ptep), &pte_val(old_pte),
+				      pte_val(pte)) &&
+		 ++attempts < max_attempts);
+
+	return attempts == max_attempts ? -EAGAIN : 0;
+}
+
 #define clear_full_ptes clear_full_ptes
 static inline void clear_full_ptes(struct mm_struct *mm, unsigned long addr,
 				pte_t *ptep, unsigned int nr, int full)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9a22df0c5120f3..65bb3c7c95952b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -76,6 +76,7 @@
 #include <linux/kasan.h>
 #include <linux/percpu.h>
 #include <linux/sched/isolation.h>
+#include <linux/hugetlb_vmemmap.h>
 
 #include <asm/arm_pmuv3.h>
 #include <asm/cpu.h>
@@ -2172,6 +2173,31 @@ static bool has_bbml2_noabort(const struct arm64_cpu_capabilities *caps, int sco
 	return cpu_supports_bbml2_noabort();
 }
 
+static bool hvo_compatible(const struct arm64_cpu_capabilities *caps, int scope)
+{
+	/*
+	 * We need HW AF support to support changing vmemmap mapping level and
+	 * OA without taking faults.
+	 */
+	return supports_hw_af(scope);
+}
+
+static bool late_cpu_enable_hvo(const struct arm64_cpu_capabilities *__unused)
+{
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+	if (cpu_has_hw_af())
+		return true;
+
+	/*
+	 * If the CPU does not support HW AF, we cannot online it if HVO is
+	 * currently in use.
+	 */
+	return hugetlb_vmemmap_optimization_try_disable();
+#else
+	return true;
+#endif
+}
+
 static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused)
 {
 	/*
@@ -3067,6 +3093,15 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.type = ARM64_CPUCAP_EARLY_LOCAL_CPU_FEATURE,
 		.matches = has_bbml2_noabort,
 	},
+	{
+		.desc = "HugeTLB Vmemmap Optimization Support",
+		.capability = ARM64_HVO_COMPATIBLE,
+		.type = ARM64_CPUCAP_SCOPE_SYSTEM |
+			ARM64_CPUCAP_OPTIONAL_FOR_LATE_CPU |
+			ARM64_CPUCAP_PERMITTED_FOR_LATE_CPU,
+		.matches = hvo_compatible,
+		.late_cpu_enable = late_cpu_enable_hvo,
+	},
 	{
 		.desc = "52-bit Virtual Addressing for KVM (LPA2)",
 		.capability = ARM64_HAS_LPA2,
@@ -3669,6 +3704,14 @@ static void verify_local_cpu_caps(u16 scope_mask)
 			 */
 			if (!cpu_has_cap && !cpucap_late_cpu_optional(caps))
 				break;
+
+			/*
+			 * Some optional features may in fact be required due
+			 * to particular runtime conditions.
+			 */
+			if (caps->late_cpu_enable && !caps->late_cpu_enable(caps))
+				break;
+
 			/*
 			 * We have to issue cpu_enable() irrespective of
 			 * whether the CPU has it or not, as it is enabeld
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 9b85a84f6fd494..630953d59c5276 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -70,6 +70,7 @@ HAS_VIRT_HOST_EXTN
 HAS_WFXT
 HAS_XNX
 HAFT
+HVO_COMPATIBLE
 HW_DBM
 KVM_HVHE
 KVM_PROTECTED_MODE
diff --git a/arch/loongarch/include/asm/pgalloc.h b/arch/loongarch/include/asm/pgalloc.h
index 248f62d0b590ec..9322d962c9cc80 100644
--- a/arch/loongarch/include/asm/pgalloc.h
+++ b/arch/loongarch/include/asm/pgalloc.h
@@ -24,6 +24,14 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t pte)
 	set_pmd(pmd, __pmd((unsigned long)page_address(pte)));
 }
 
+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD
+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,
+					   unsigned long addr)
+{
+	pmd_populate_kernel(&init_mm, pmdp, pgtable);
+	return 0;
+}
+
 #ifndef __PAGETABLE_PMD_FOLDED
 
 static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h
index 223528c04d7392..e7b65056ef7384 100644
--- a/arch/loongarch/include/asm/pgtable.h
+++ b/arch/loongarch/include/asm/pgtable.h
@@ -638,6 +638,14 @@ static inline long pmd_protnone(pmd_t pmd)
 #define pmd_leaf(pmd)		((pmd_val(pmd) & _PAGE_HUGE) != 0)
 #define pud_leaf(pud)		((pud_val(pud) & _PAGE_HUGE) != 0)
 
+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE
+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,
+					 pte_t pte)
+{
+	set_pte(ptep, pte);
+	return 0;
+}
+
 /*
  * We provide our own get_unmapped area to cope with the virtual aliasing
  * constraints placed on us by the cache architecture.
diff --git a/arch/riscv/include/asm/pgalloc.h b/arch/riscv/include/asm/pgalloc.h
index 770ce18a7328b8..8dbbe62b257425 100644
--- a/arch/riscv/include/asm/pgalloc.h
+++ b/arch/riscv/include/asm/pgalloc.h
@@ -31,6 +31,14 @@ static inline void pmd_populate(struct mm_struct *mm,
 	set_pmd(pmd, __pmd((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE));
 }
 
+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD
+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,
+					   unsigned long addr)
+{
+	pmd_populate_kernel(&init_mm, pmdp, pgtable);
+	return 0;
+}
+
 #ifndef __PAGETABLE_PMD_FOLDED
 static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
 {
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 5d5756bda82e3d..a11c569ee2f06f 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -1162,6 +1162,14 @@ static inline pud_t pud_modify(pud_t pud, pgprot_t newprot)
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE
+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,
+					 pte_t pte)
+{
+	set_pte(ptep, pte);
+	return 0;
+}
+
 /*
  * Encode/decode swap entries and swap PTEs. Swap PTEs are all PTEs that
  * are !pte_none() && !pte_present().
diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h
index c88691b15f3c67..47b56bbd623614 100644
--- a/arch/x86/include/asm/pgalloc.h
+++ b/arch/x86/include/asm/pgalloc.h
@@ -82,6 +82,14 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
 	set_pmd(pmd, __pmd(((pteval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE));
 }
 
+#define __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD
+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,
+					   unsigned long addr)
+{
+	pmd_populate_kernel(&init_mm, pmdp, pgtable);
+	return 0;
+}
+
 #if CONFIG_PGTABLE_LEVELS > 2
 extern void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd);
 
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index ac295ca6c92f99..974151564071cb 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -1372,6 +1372,14 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
 }
 #endif
 
+#define __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE
+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,
+					 pte_t pte)
+{
+	set_pte(ptep, pte);
+	return 0;
+}
+
 #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
 static inline pud_t pudp_establish(struct vm_area_struct *vma,
 		unsigned long address, pud_t *pudp, pud_t pud)
diff --git a/include/asm-generic/hugetlb.h b/include/asm-generic/hugetlb.h
index e1a2e1b7c8e7b8..c4796c54b702a9 100644
--- a/include/asm-generic/hugetlb.h
+++ b/include/asm-generic/hugetlb.h
@@ -128,4 +128,11 @@ static inline bool gigantic_page_runtime_supported(void)
 }
 #endif /* __HAVE_ARCH_GIGANTIC_PAGE_RUNTIME_SUPPORTED */
 
+#ifndef __HAVE_ARCH_HVO_SUPPORTED
+static inline bool arch_hugetlb_vmemmap_optimization_supported(void)
+{
+	return IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP);
+}
+#endif /* __HAVE_ARCH_HVO_SUPPORTED */
+
 #endif /* _ASM_GENERIC_HUGETLB_H */
diff --git a/include/linux/hugetlb_vmemmap.h b/include/linux/hugetlb_vmemmap.h
new file mode 100644
index 00000000000000..a671eb4a4ff466
--- /dev/null
+++ b/include/linux/hugetlb_vmemmap.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_HUGETLB_VMEMMAP_H
+#define _LINUX_HUGETLB_VMEMMAP_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+
+bool hugetlb_vmemmap_optimization_try_disable(void);
+
+#else /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */
+
+static inline bool hugetlb_vmemmap_optimization_try_disable(void)
+{
+	return true;
+}
+
+#endif
+
+#endif /* _LINUX_HUGETLB_VMEMMAP_H */
diff --git a/include/linux/pgalloc.h b/include/linux/pgalloc.h
index 9174fa59bbc54d..ed446d95ca3730 100644
--- a/include/linux/pgalloc.h
+++ b/include/linux/pgalloc.h
@@ -26,4 +26,24 @@
 			arch_sync_kernel_mappings(addr, addr);		\
 	} while (0)
 
+#ifndef __HAVE_ARCH_TRY_POPULATE_VMEMMAP_PMD
+/*
+ * try_populate_vmemmap_pmd - Populate a PMD that is in use by the vmemmap.
+ * @addr: Base address of the remapped PMD.
+ * @pmdp: Page table pointer to be overwritten.
+ * @pgtable: Pointer to the page table that the new PMD will point to.
+ *
+ * This function is only to be used to update PMDs that map the vmemmap to
+ * point to a page of already-populated PTEs that map the same pages.
+ *
+ * Implementations of this function must ensure that, while the update is taking
+ * place, CPUs will not fault on the remapped virtual address range.
+ */
+static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable,
+					   unsigned long addr)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 #endif /* _LINUX_PGALLOC_H */
diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
index 2981e386da7b3c..e1abccf76bb463 100644
--- a/include/linux/pgtable.h
+++ b/include/linux/pgtable.h
@@ -457,6 +457,27 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr,
 #endif
 #define set_pte_at(mm, addr, ptep, pte) set_ptes(mm, addr, ptep, pte, 1)
 
+#ifndef __HAVE_ARCH_TRY_UPDATE_VMEMMAP_PTE
+/*
+ * try_update_vmemmap_pte - Remap PTEs used by the vmemmap.
+ * @addr: Base address of the remapped PTE.
+ * @ptep: Page table pointer to be overwritten.
+ * @pte: Page table entry to write.
+ *
+ * This function is only to be used to update PTEs that map the vmemmap. The
+ * only valid transitions supported by this function are: leaf-level
+ * (PAGE_SIZE), valid-to-valid. The pfn and prot bits may be changed.
+ *
+ * Implementations of this function must ensure that, while the update is taking
+ * place, CPUs will not fault on the remapped virtual address.
+ */
+static inline int try_update_vmemmap_pte(unsigned long addr, pte_t *ptep,
+					 pte_t pte)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
 extern int ptep_set_access_flags(struct vm_area_struct *vma,
 				 unsigned long address, pte_t *ptep,
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 571212b80835ec..298673a6c57c5c 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -47,7 +47,7 @@
 #include <linux/node.h>
 #include <linux/page_owner.h>
 #include "internal.h"
-#include "hugetlb_vmemmap.h"
+#include "hugetlb_vmemmap_internal.h"
 #include "hugetlb_cma.h"
 #include "hugetlb_internal.h"
 #include <linux/page-isolation.h>
diff --git a/mm/hugetlb_sysfs.c b/mm/hugetlb_sysfs.c
index 79ece91406bfa4..83a95f5142f5a4 100644
--- a/mm/hugetlb_sysfs.c
+++ b/mm/hugetlb_sysfs.c
@@ -8,7 +8,7 @@
 #include <linux/page_owner.h>
 #include <linux/page-isolation.h>
 
-#include "hugetlb_vmemmap.h"
+#include "hugetlb_vmemmap_internal.h"
 #include "hugetlb_internal.h"
 
 #define HSTATE_ATTR_RO(_name) \
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 133b46dfb09f5c..7e7f8579cc2ae5 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -16,9 +16,11 @@
 #include <linux/mmdebug.h>
 #include <linux/pagewalk.h>
 #include <linux/pgalloc.h>
+#include <linux/hugetlb.h>
+#include <linux/hugetlb_vmemmap.h>
 
 #include <asm/tlbflush.h>
-#include "hugetlb_vmemmap.h"
+#include "hugetlb_vmemmap_internal.h"
 #include "internal.h"
 
 /**
@@ -34,7 +36,7 @@
  *			operations.
  */
 struct vmemmap_remap_walk {
-	void			(*remap_pte)(pte_t *pte, unsigned long addr,
+	int			(*remap_pte)(pte_t *pte, unsigned long addr,
 					     struct vmemmap_remap_walk *walk);
 
 	unsigned long		nr_walked;
@@ -54,6 +56,7 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
 			     struct vmemmap_remap_walk *walk)
 {
 	pmd_t __pmd;
+	int ret;
 	int i;
 	unsigned long addr = start;
 	pte_t *pgtable;
@@ -73,8 +76,15 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
 		set_pte_at(&init_mm, addr, pte, entry);
 	}
 
+	ret = 0;
 	spin_lock(&init_mm.page_table_lock);
 	if (likely(pmd_leaf(*pmd))) {
+		/* Make pte visible before pmd. See comment in pmd_install(). */
+		smp_wmb();
+		ret = try_populate_vmemmap_pmd(pmd, pgtable, start);
+		if (ret)
+			goto free;
+
 		/*
 		 * Higher order allocations from buddy allocator must be able to
 		 * be treated as independent small pages (as they can be freed
@@ -83,17 +93,17 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
 		if (!PageReserved(head))
 			split_page(head, get_order(PMD_SIZE));
 
-		/* Make pte visible before pmd. See comment in pmd_install(). */
-		smp_wmb();
-		pmd_populate_kernel(&init_mm, pmd, pgtable);
 		if (!(walk->flags & VMEMMAP_SPLIT_NO_TLB_FLUSH))
 			flush_tlb_kernel_range(start, start + PMD_SIZE);
-	} else {
-		pte_free_kernel(&init_mm, pgtable);
-	}
-	spin_unlock(&init_mm.page_table_lock);
+	} else
+		goto free;
 
-	return 0;
+out:
+	spin_unlock(&init_mm.page_table_lock);
+	return ret;
+free:
+	pte_free_kernel(&init_mm, pgtable);
+	goto out;
 }
 
 static int vmemmap_pmd_entry(pmd_t *pmd, unsigned long addr,
@@ -141,11 +151,13 @@ static int vmemmap_pte_entry(pte_t *pte, unsigned long addr,
 			     unsigned long next, struct mm_walk *walk)
 {
 	struct vmemmap_remap_walk *vmemmap_walk = walk->private;
+	int ret = 0;
 
-	vmemmap_walk->remap_pte(pte, addr, vmemmap_walk);
-	vmemmap_walk->nr_walked++;
+	ret = vmemmap_walk->remap_pte(pte, addr, vmemmap_walk);
+	if (!ret)
+		vmemmap_walk->nr_walked++;
 
-	return 0;
+	return ret;
 }
 
 static const struct mm_walk_ops vmemmap_remap_ops = {
@@ -164,13 +176,11 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end,
 	ret = walk_kernel_page_table_range(start, end, &vmemmap_remap_ops,
 				    NULL, walk);
 	mmap_read_unlock(&init_mm);
-	if (ret)
-		return ret;
 
 	if (walk->remap_pte && !(walk->flags & VMEMMAP_REMAP_NO_TLB_FLUSH))
 		flush_tlb_kernel_range(start, end);
 
-	return 0;
+	return ret;
 }
 
 /*
@@ -199,18 +209,20 @@ static void free_vmemmap_page_list(struct list_head *list)
 		free_vmemmap_page(page);
 }
 
-static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
-			      struct vmemmap_remap_walk *walk)
+static int vmemmap_remap_pte(pte_t *pte, unsigned long addr,
+			     struct vmemmap_remap_walk *walk)
 {
 	struct page *page = pte_page(ptep_get(pte));
 	pte_t entry;
+	bool head;
+	int ret;
+
+	head = walk->nr_walked == 0 && walk->vmemmap_head;
 
 	/* Remapping the head page requires r/w */
-	if (unlikely(walk->nr_walked == 0 && walk->vmemmap_head)) {
+	if (unlikely(head)) {
 		VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
 
-		list_del(&walk->vmemmap_head->lru);
-
 		/*
 		 * Makes sure that preceding stores to the page contents from
 		 * vmemmap_remap_free() become visible before the set_pte_at()
@@ -229,35 +241,56 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
 		entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL_RO);
 	}
 
+	ret = try_update_vmemmap_pte(addr, pte, entry);
+	if (ret)
+		return ret;
+
+	/* We successfully overwrote the vmemmap PTE, so we can free
+	 * the vmemmap page that was just unmapped, and if we mapped
+	 * the new head page, remove it from the list so that it
+	 * doesn't get freed later.
+	 */
 	list_add(&page->lru, walk->vmemmap_pages);
-	set_pte_at(&init_mm, addr, pte, entry);
+	if (head)
+		list_del(&walk->vmemmap_head->lru);
+
+	return 0;
 }
 
-static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
-				struct vmemmap_remap_walk *walk)
+static int vmemmap_restore_pte(pte_t *pte, unsigned long addr,
+			       struct vmemmap_remap_walk *walk)
 {
 	struct page *src = pte_page(ptep_get(pte)), *dst;
+	int ret;
+
+	if (WARN_ON_ONCE(!walk->vmemmap_tail))
+		return -EINVAL;
 
 	/*
-	 * When rolling back vmemmap_remap_free(), keep the copied head page
+	 * When restoring a partially-HVOed page, keep the copied head page
 	 * mapping and restore only PTEs currently pointing at the shared tail
 	 * page.
 	 */
-	if (walk->vmemmap_tail && walk->vmemmap_tail != src)
-		return;
+	if (walk->vmemmap_tail != src)
+		return 0;
 
 	VM_WARN_ON_ONCE(PageHead((const struct page *)addr));
 
 	dst = list_first_entry(walk->vmemmap_pages, struct page, lru);
-	list_del(&dst->lru);
 	copy_page(page_to_virt(dst), page_to_virt(src));
 
 	/*
 	 * Makes sure that preceding stores to the page contents become visible
-	 * before the set_pte_at() write.
+	 * before the try_update_vmemmap_pte() write.
 	 */
 	smp_wmb();
-	set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
+
+	ret = try_update_vmemmap_pte(addr, pte, mk_pte(dst, PAGE_KERNEL));
+	if (ret)
+		return ret;
+
+	list_del(&dst->lru);
+	return 0;
 }
 
 /**
@@ -279,6 +312,7 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
 	return vmemmap_remap_range(start, end, &walk);
 }
 
+static const int VMEMMAP_REMAP_INCOMPLETE = 1;
 /**
  * vmemmap_remap_free - remap the vmemmap virtual address range [@start, @end)
  *			to use @vmemmap_head/tail, then free vmemmap which
@@ -293,7 +327,8 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
  *		responsibility to free pages.
  * @flags:	modifications to vmemmap_remap_walk flags
  *
- * Return: %0 on success, negative error code otherwise.
+ * Return: %0 on success, VMEMMAP_REMAP_INCOMPLETE if the page is incompletely
+ *         optimized, negative error code otherwise.
  */
 static int vmemmap_remap_free(unsigned long start, unsigned long end,
 			      struct page *vmemmap_head,
@@ -328,7 +363,8 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
 		.flags		= 0,
 	};
 
-	vmemmap_remap_range(start, end, &walk);
+	if (vmemmap_remap_range(start, end, &walk))
+		return VMEMMAP_REMAP_INCOMPLETE;
 
 	return ret;
 }
@@ -357,10 +393,38 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
 	return -ENOMEM;
 }
 
+static struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
+{
+	const unsigned int idx = order - VMEMMAP_TAIL_MIN_ORDER;
+	struct page *tail, *p;
+	int node = zone_to_nid(zone);
+
+	tail = READ_ONCE(zone->vmemmap_tails[idx]);
+	if (likely(tail))
+		return tail;
+
+	tail = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
+	if (!tail)
+		return NULL;
+
+	p = page_to_virt(tail);
+	for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++)
+		init_compound_tail(p + i, NULL, order, zone);
+
+	if (cmpxchg(&zone->vmemmap_tails[idx], NULL, tail)) {
+		__free_page(tail);
+		tail = READ_ONCE(zone->vmemmap_tails[idx]);
+	}
+
+	return tail;
+}
+
 /**
  * vmemmap_remap_alloc - remap the vmemmap virtual address range [@start, end)
  *			 to the page which is from the @vmemmap_pages
  *			 respectively.
+ * @h:		the hstate for the folio whose vmemmap is getting remapped
+ * @folio:	the folio whose vmemmap is getting remapped
  * @start:	start address of the vmemmap virtual address range that we want
  *		to remap.
  * @end:	end address of the vmemmap virtual address range that we want to
@@ -369,20 +433,68 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
  *
  * Return: %0 on success, negative error code otherwise.
  */
-static int vmemmap_remap_alloc(unsigned long start, unsigned long end,
+static int vmemmap_remap_alloc(const struct hstate *h, struct folio *folio,
+			       unsigned long start, unsigned long end,
 			       unsigned long flags)
 {
 	LIST_HEAD(vmemmap_pages);
-	struct vmemmap_remap_walk walk = {
+	struct vmemmap_remap_walk walk;
+	struct page *vmemmap_tail;
+	int ret;
+
+	vmemmap_tail = vmemmap_get_tail(h->order, folio_zone(folio));
+	if (WARN_ON_ONCE(!vmemmap_tail))
+		return -ENOMEM;
+
+	if (alloc_vmemmap_page_list(start, end, &vmemmap_pages))
+		return -ENOMEM;
+
+	walk = (struct vmemmap_remap_walk) {
 		.remap_pte	= vmemmap_restore_pte,
+		.vmemmap_tail	= vmemmap_tail,
 		.vmemmap_pages	= &vmemmap_pages,
 		.flags		= flags,
 	};
 
-	if (alloc_vmemmap_page_list(start, end, &vmemmap_pages))
-		return -ENOMEM;
+	ret = vmemmap_remap_range(start, end, &walk);
 
-	return vmemmap_remap_range(start, end, &walk);
+	/* Not all pages may have been consumed */
+	free_vmemmap_page_list(&vmemmap_pages);
+
+	return ret;
+}
+
+enum hugetlb_hvo_status {
+	HVO_INACTIVE = 0,
+	HVO_ACTIVE,
+	HVO_PERMANENTLY_INACTIVE,
+};
+static enum hugetlb_hvo_status hvo_status = HVO_INACTIVE;
+
+static bool hugetlb_hvo_status_try_set(enum hugetlb_hvo_status status)
+{
+	enum hugetlb_hvo_status old;
+
+	old = READ_ONCE(hvo_status);
+
+retry:
+	/* The current setting is what we want. */
+	if (old == status)
+		return true;
+
+	/* The current setting cannot be changed. */
+	if (old != HVO_INACTIVE)
+		return false;
+
+	if (!try_cmpxchg_relaxed(&hvo_status, &old, status))
+		goto retry;
+
+	return true;
+}
+
+bool hugetlb_vmemmap_optimization_try_disable(void)
+{
+	return hugetlb_hvo_status_try_set(HVO_PERMANENTLY_INACTIVE);
 }
 
 static bool vmemmap_optimize_enabled = IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON);
@@ -415,7 +527,7 @@ static int __hugetlb_vmemmap_restore_folio(const struct hstate *h,
 	 * When a HugeTLB page is freed to the buddy allocator, previously
 	 * discarded vmemmap pages must be allocated and remapping.
 	 */
-	ret = vmemmap_remap_alloc(vmemmap_start, vmemmap_end, flags);
+	ret = vmemmap_remap_alloc(h, folio, vmemmap_start, vmemmap_end, flags);
 	if (!ret)
 		folio_clear_hugetlb_vmemmap_optimized(folio);
 
@@ -487,36 +599,16 @@ static bool vmemmap_should_optimize_folio(const struct hstate *h, struct folio *
 	if (!READ_ONCE(vmemmap_optimize_enabled))
 		return false;
 
-	if (!hugetlb_vmemmap_optimizable(h))
+	if (!arch_hugetlb_vmemmap_optimization_supported())
 		return false;
 
-	return true;
-}
-
-static struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
-{
-	const unsigned int idx = order - VMEMMAP_TAIL_MIN_ORDER;
-	struct page *tail, *p;
-	int node = zone_to_nid(zone);
-
-	tail = READ_ONCE(zone->vmemmap_tails[idx]);
-	if (likely(tail))
-		return tail;
-
-	tail = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
-	if (!tail)
-		return NULL;
-
-	p = page_to_virt(tail);
-	for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++)
-		init_compound_tail(p + i, NULL, order, zone);
+	if (!hugetlb_vmemmap_optimizable(h))
+		return false;
 
-	if (cmpxchg(&zone->vmemmap_tails[idx], NULL, tail)) {
-		__free_page(tail);
-		tail = READ_ONCE(zone->vmemmap_tails[idx]);
-	}
+	if (!hugetlb_hvo_status_try_set(HVO_ACTIVE))
+		return false;
 
-	return tail;
+	return true;
 }
 
 static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
@@ -574,7 +666,11 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
 				 vmemmap_head, vmemmap_tail,
 				 vmemmap_pages, flags);
 out:
-	if (ret)
+	/*
+	 * If ret == VMEMMAP_REMAP_INCOMPLETE, the folio might be partially
+	 * HVOed. Leave the HVO page folio flag in place.
+	 */
+	if (ret < 0)
 		folio_clear_hugetlb_vmemmap_optimized(folio);
 
 	return ret;
@@ -717,6 +813,14 @@ static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)
 	if (!READ_ONCE(vmemmap_optimize_enabled))
 		return false;
 
+	/*
+	 * Architectures may return false here but true by the time
+	 * hugetlb_init() is called. In this case, although the folios will
+	 * not be pre-HVOed, they will be optimized in hugetlb_init().
+	 */
+	if (!arch_hugetlb_vmemmap_optimization_supported())
+		return false;
+
 	if (!hugetlb_vmemmap_optimizable(m->hstate))
 		return false;
 
@@ -742,6 +846,9 @@ static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)
 	    !IS_ALIGNED(psize, pmd_vmemmap_size))
 		return false;
 
+	if (!hugetlb_hvo_status_try_set(HVO_ACTIVE))
+		return false;
+
 	return true;
 }
 
diff --git a/mm/hugetlb_vmemmap.h b/mm/hugetlb_vmemmap_internal.h
similarity index 95%
rename from mm/hugetlb_vmemmap.h
rename to mm/hugetlb_vmemmap_internal.h
index 18b4908252153d..6b28780014bc18 100644
--- a/mm/hugetlb_vmemmap.h
+++ b/mm/hugetlb_vmemmap_internal.h
@@ -6,8 +6,8 @@
  *
  *     Author: Muchun Song <songmuchun@bytedance.com>
  */
-#ifndef _LINUX_HUGETLB_VMEMMAP_H
-#define _LINUX_HUGETLB_VMEMMAP_H
+#ifndef _MM_HUGETLB_VMEMMAP_INTERNAL_H
+#define _MM_HUGETLB_VMEMMAP_INTERNAL_H
 #include <linux/hugetlb.h>
 #include <linux/io.h>
 #include <linux/memblock.h>
@@ -95,4 +95,4 @@ static inline bool hugetlb_vmemmap_optimizable(const struct hstate *h)
 {
 	return hugetlb_vmemmap_optimizable_size(h) != 0;
 }
-#endif /* _LINUX_HUGETLB_VMEMMAP_H */
+#endif /* _MM_HUGETLB_VMEMMAP_INTERNAL_H */
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 99e2be39671b63..94d5da9f8ab7c1 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -32,7 +32,7 @@
 #include <asm/dma.h>
 #include <asm/tlbflush.h>
 
-#include "hugetlb_vmemmap.h"
+#include "hugetlb_vmemmap_internal.h"
 
 /*
  * Flags for vmemmap_populate_range and friends.

3/2 2026/07/08 15:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16218
							output: 26
							thoughts: 474
						

Thoughts:
**My Assessment of the HugeTLB Vmemmap Optimization Patch**

Okay, so I'm looking at this patch series that brings ARM64 support to HugeTLB Vmemmap Optimization, or HVO. It's a fundamental change, adding architecture-specific functions like `arch_hugetlb_vmemmap_optimization_supported()`, `try_populate_vmemmap_pmd()`, and `try_update_vmemmap_pte()` along with the necessary core logic modifications within `mm/hugetlb_vmemmap.c`. This is definitely something that warrants a thorough fuzzing campaign given its impact on core kernel functionality related to HugeTLB management.

Now, let's zero in on the key areas within `mm/hugetlb_vmemmap.c` that the patch modifies. Specifically, I need to focus on these functions as potential entry points for testing: `vmemmap_split_pmd`, `vmemmap_pmd_entry`, `vmemmap_pte_entry`, `vmemmap_remap_range`, `vmemmap_remap_pte`, `vmemmap_restore_pte`, `vmemmap_remap_free`, `vmemmap_remap_alloc`, `hugetlb_vmemmap_optimization_try_disable`, `__hugetlb_vmemmap_restore_folio`, `vmemmap_should_optimize_folio`, `__hugetlb_vmemmap_optimize_folio`, and `vmemmap_should_optimize_bootmem_page`. These are the functions where the ARM64 integration will most likely introduce potential vulnerabilities or regressions.

Next, I need to understand the configuration requirements. The patch modifies `arch/arm64/Kconfig` which indicates that `select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if ARM64_HW_AFDBM` is relevant. This leads me to believe that `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` is a central configuration option for this functionality. `ARM64_HW_AFDBM` also seems important as a dependent config. The changes in `arch/arm64/include/asm/cpucaps.h` which references `CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP` further confirms that `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` is the key. I need to make sure I verify these configurations during my testing to ensure the relevant code paths are exercised.



4/2 2026/07/08 15:12 tool grepper
0m Args:
{
  "Expression": "HUGETLB_PAGE_OPTIMIZE_VMEMMAP"
}

Results:
{
  "Output": "Documentation/admin-guide/kernel-parameters.txt=95=Kernel parameters\n--\nDocumentation/admin-guide/kernel-parameters.txt-2085-\thugetlb_free_vmemmap=\nDocumentation/admin-guide/kernel-parameters.txt:2086:\t\t\t[KNL] Requires CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nDocumentation/admin-guide/kernel-parameters.txt-2087-\t\t\tenabled.\n--\nDocumentation/admin-guide/kernel-parameters.txt-2095-\nDocumentation/admin-guide/kernel-parameters.txt:2096:\t\t\tBuilt with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON=y,\nDocumentation/admin-guide/kernel-parameters.txt-2097-\t\t\tthe default is on.\n--\nDocumentation/admin-guide/mm/hugetlbpage.rst=174=hugetlb_free_vmemmap\nDocumentation/admin-guide/mm/hugetlbpage.rst:175:\tWhen CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP is set, this enables HugeTLB\nDocumentation/admin-guide/mm/hugetlbpage.rst-176-\tVmemmap Optimization (HVO).\n--\narch/arm64/include/asm/cpucaps.h=14=cpucap_is_possible(const unsigned int cap)\n--\narch/arm64/include/asm/cpucaps.h-78-\tcase ARM64_HVO_COMPATIBLE:\narch/arm64/include/asm/cpucaps.h:79:\t\treturn IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP);\narch/arm64/include/asm/cpucaps.h-80-\t}\n--\narch/arm64/kernel/cpufeature.c=2185=static bool late_cpu_enable_hvo(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2186-{\narch/arm64/kernel/cpufeature.c:2187:#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\narch/arm64/kernel/cpufeature.c-2188-\tif (cpu_has_hw_af())\n--\narch/x86/entry/vdso/vdso32/fake_32bit_build.h-13-#undef CONFIG_SPARSEMEM_VMEMMAP\narch/x86/entry/vdso/vdso32/fake_32bit_build.h:14:#undef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\narch/x86/entry/vdso/vdso32/fake_32bit_build.h-15-#undef CONFIG_NR_CPUS\n--\narch/x86/mm/init_64.c=1316=static void __init register_page_bootmem_info(void)\narch/x86/mm/init_64.c-1317-{\narch/x86/mm/init_64.c:1318:#if defined(CONFIG_NUMA) || defined(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP)\narch/x86/mm/init_64.c-1319-\tint i;\n--\nfs/Kconfig=262=if HUGETLBFS\nfs/Kconfig:263:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON\nfs/Kconfig-264-\tbool \"HugeTLB Vmemmap Optimization (HVO) defaults to on\"\nfs/Kconfig-265-\tdefault n\nfs/Kconfig:266:\tdepends on HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nfs/Kconfig-267-\thelp\n--\nfs/Kconfig=273=config HUGETLB_PAGE\n--\nfs/Kconfig-276-\nfs/Kconfig:277:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nfs/Kconfig-278-\tdef_bool HUGETLB_PAGE\n--\ninclude/asm-generic/hugetlb.h=132=static inline bool arch_hugetlb_vmemmap_optimization_supported(void)\ninclude/asm-generic/hugetlb.h-133-{\ninclude/asm-generic/hugetlb.h:134:\treturn IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP);\ninclude/asm-generic/hugetlb.h-135-}\n--\ninclude/linux/hugetlb_vmemmap.h-6-\ninclude/linux/hugetlb_vmemmap.h:7:#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\ninclude/linux/hugetlb_vmemmap.h-8-\ninclude/linux/hugetlb_vmemmap.h=9=bool hugetlb_vmemmap_optimization_try_disable(void);\ninclude/linux/hugetlb_vmemmap.h-10-\ninclude/linux/hugetlb_vmemmap.h:11:#else /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */\ninclude/linux/hugetlb_vmemmap.h-12-\n--\ninclude/linux/mmzone.h-105-#define MAX_FOLIO_VMEMMAP_ALIGN \\\ninclude/linux/mmzone.h:106:\t(IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP) \u0026\u0026 \\\ninclude/linux/mmzone.h-107-\t is_power_of_2(sizeof(struct page)) ? \\\n--\ninclude/linux/mmzone.h=968=struct zone {\n--\ninclude/linux/mmzone.h-1148-\tatomic_long_t\t\tvm_numa_event[NR_VM_NUMA_EVENT_ITEMS];\ninclude/linux/mmzone.h:1149:#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\ninclude/linux/mmzone.h-1150-\tstruct page *vmemmap_tails[NR_VMEMMAP_TAILS];\n--\ninclude/linux/page-flags.h=208=static __always_inline bool compound_info_has_mask(void)\n--\ninclude/linux/page-flags.h-217-\t */\ninclude/linux/page-flags.h:218:\tif (!IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP))\ninclude/linux/page-flags.h-219-\t\treturn false;\n--\nmm/Makefile=84=endif\nmm/Makefile:85:obj-$(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP)\t+= hugetlb_vmemmap.o\nmm/Makefile-86-obj-$(CONFIG_NUMA) \t+= mempolicy.o\n--\nmm/hugetlb_vmemmap.c=495=bool hugetlb_vmemmap_optimization_try_disable(void)\n--\nmm/hugetlb_vmemmap.c-499-\nmm/hugetlb_vmemmap.c:500:static bool vmemmap_optimize_enabled = IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON);\nmm/hugetlb_vmemmap.c-501-static int __init hugetlb_vmemmap_optimize_param(char *buf)\n--\nmm/hugetlb_vmemmap_internal.h-21-\nmm/hugetlb_vmemmap_internal.h:22:#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nmm/hugetlb_vmemmap_internal.h-23-int hugetlb_vmemmap_restore_folio(const struct hstate *h, struct folio *folio);\n--\nmm/hugetlb_vmemmap_internal.h=88=static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate *h)\n--\nmm/hugetlb_vmemmap_internal.h-91-}\nmm/hugetlb_vmemmap_internal.h:92:#endif /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */\nmm/hugetlb_vmemmap_internal.h-93-\n--\nmm/sparse-vmemmap.c=311=void vmemmap_wrprotect_hvo(unsigned long addr, unsigned long end,\n--\nmm/sparse-vmemmap.c-322-\nmm/sparse-vmemmap.c:323:#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nmm/sparse-vmemmap.c-324-static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)\n"
}

5/2 2026/07/08 15:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19443
							output: 29
							thoughts: 87
						

Thoughts:
Here's my thought process, as if I were considering this myself:

**Investigating HugeTLB VMEMMAP Optimization**

Alright, so I need to configure this kernel for optimized HugeTLB vmemmap usage. The key config option seems to be `HUGETLB_PAGE_OPTIMIZE_VMEMMAP`. I need to ensure that it is enabled.

I should also check `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON` to guarantee it's enabled by default, this will save any potential headaches down the line. I always prefer sensible defaults!

To confirm if this is being handled correctly for this architecture, I should also look in `arch/arm64/Kconfig` for `ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP`. That will give me the architecture-specific context and any platform considerations I need to be aware of. Let's see what's in there.



6/2 2026/07/08 15:12 tool grepper
0m Args:
{
  "Expression": "ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP"
}

Results:
{
  "Output": "arch/arm64/Kconfig=2=config ARM64\n--\narch/arm64/Kconfig-261-\tselect VMAP_STACK\narch/arm64/Kconfig:262:\tselect ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if ARM64_HW_AFDBM\narch/arm64/Kconfig-263-\thelp\n--\narch/loongarch/Kconfig=2=config LOONGARCH\n--\narch/loongarch/Kconfig-82-\tselect ARCH_WANT_LD_ORPHAN_WARN\narch/loongarch/Kconfig:83:\tselect ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if 64BIT\narch/loongarch/Kconfig-84-\tselect ARCH_WANTS_NO_INSTR\n--\narch/riscv/Kconfig=13=config RISCV\n--\narch/riscv/Kconfig-88-\tselect ARCH_WANT_OPTIMIZE_DAX_VMEMMAP\narch/riscv/Kconfig:89:\tselect ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP\narch/riscv/Kconfig-90-\tselect ARCH_WANTS_NO_INSTR\n--\narch/x86/Kconfig=58=config X86\n--\narch/x86/Kconfig-148-\tselect ARCH_WANT_OPTIMIZE_DAX_VMEMMAP\tif X86_64\narch/x86/Kconfig:149:\tselect ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP\tif X86_64\narch/x86/Kconfig-150-\tselect ARCH_WANT_HUGETLB_VMEMMAP_PREINIT if X86_64\n--\nfs/Kconfig=277=config HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nfs/Kconfig-278-\tdef_bool HUGETLB_PAGE\nfs/Kconfig:279:\tdepends on ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP\nfs/Kconfig-280-\tdepends on SPARSEMEM_VMEMMAP\n--\nmm/Kconfig=472=config ARCH_WANT_OPTIMIZE_DAX_VMEMMAP\n--\nmm/Kconfig-474-\nmm/Kconfig:475:config ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP\nmm/Kconfig-476-\tbool\n"
}

7/2 2026/07/08 15:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20376
							output: 53
							thoughts: 591
						

Thoughts:
**Analyzing the hugetlb_vmemmap Optimization for ARM64**

Alright, let's break this down. It looks like we're enabling `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` specifically for ARM64, contingent on a few other configurations.  `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` hinges on `ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP` and `SPARSEMEM_VMEMMAP`. On ARM64, `ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP` is selected *if* `ARM64_HW_AFDBM` is active.  So, the logical first step here is to enable `ARM64_HW_AFDBM` alongside `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` and its default-on setting, `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON`. Good, got the dependencies lined up.

Now, where's the meat of this optimization happening? The patch touches `mm/hugetlb_vmemmap.c`, so that's where I need to focus my attention. Let's see... a quick scan reveals a bunch of relevant functions.  I need to prioritize and really dig into these:

*   `vmemmap_split_pmd`
*   `vmemmap_pmd_entry`
*   `vmemmap_pte_entry`
*   `vmemmap_remap_range`
*   `vmemmap_remap_pte`
*   `vmemmap_restore_pte`
*   `vmemmap_remap_free`
*   `vmemmap_remap_alloc`
*   `hugetlb_vmemmap_optimization_try_disable`
*   `__hugetlb_vmemmap_restore_folio`
*   `vmemmap_should_optimize_folio`
*   `__hugetlb_vmemmap_optimize_folio`
*   `vmemmap_should_optimize_bootmem_page`

Wait, there are a couple of inline functions as well: `try_populate_vmemmap_pmd` and `try_update_vmemmap_pte`, located in `arch/arm64/include/asm/pgalloc.h` and `arch/arm64/include/asm/pgtable.h`. Since they're inline, they're probably getting compiled directly into the functions that use them, like the `vmemmap_split_pmd` and `vmemmap_remap_pte`. I'll keep that in mind when I analyze the code flow.

So, let's refine that list of functions I need to dissect:

*   `vmemmap_split_pmd`
*   `vmemmap_remap_pte`
*   `vmemmap_restore_pte`
*   `vmemmap_remap_free`
*   `vmemmap_remap_alloc`
*   `hugetlb_vmemmap_optimization_try_disable`
*   `__hugetlb_vmemmap_restore_folio`
*   `__hugetlb_vmemmap_optimize_folio`

Right, got it. Now I just need to verify that all these functions actually exist as expected in `mm/hugetlb_vmemmap.c`. A quick check will confirm this before I dive into the actual code.



8/2 2026/07/08 15:12 tool grepper
0m Args:
{
  "Expression": "^(static )?(int|void|bool) [a-zA-Z0-9_]+\\(",
  "PathPrefix": "mm/hugetlb_vmemmap.c"
}

Results:
{
  "Output": "mm/hugetlb_vmemmap.c=38=struct vmemmap_remap_walk {\n--\nmm/hugetlb_vmemmap.c-54-\nmm/hugetlb_vmemmap.c:55:static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,\nmm/hugetlb_vmemmap.c-56-\t\t\t     struct vmemmap_remap_walk *walk)\n--\nmm/hugetlb_vmemmap.c-108-\nmm/hugetlb_vmemmap.c:109:static int vmemmap_pmd_entry(pmd_t *pmd, unsigned long addr,\nmm/hugetlb_vmemmap.c-110-\t\t\t     unsigned long next, struct mm_walk *walk)\n--\nmm/hugetlb_vmemmap.c-149-\nmm/hugetlb_vmemmap.c:150:static int vmemmap_pte_entry(pte_t *pte, unsigned long addr,\nmm/hugetlb_vmemmap.c-151-\t\t\t     unsigned long next, struct mm_walk *walk)\n--\nmm/hugetlb_vmemmap.c=163=static const struct mm_walk_ops vmemmap_remap_ops = {\n--\nmm/hugetlb_vmemmap.c-167-\nmm/hugetlb_vmemmap.c:168:static int vmemmap_remap_range(unsigned long start, unsigned long end,\nmm/hugetlb_vmemmap.c-169-\t\t\t       struct vmemmap_remap_walk *walk)\n--\nmm/hugetlb_vmemmap.c=192=static inline void free_vmemmap_page(struct page *page)\n--\nmm/hugetlb_vmemmap.c-203-/* Free a list of the vmemmap pages */\nmm/hugetlb_vmemmap.c:204:static void free_vmemmap_page_list(struct list_head *list)\nmm/hugetlb_vmemmap.c-205-{\n--\nmm/hugetlb_vmemmap.c-211-\nmm/hugetlb_vmemmap.c:212:static int vmemmap_remap_pte(pte_t *pte, unsigned long addr,\nmm/hugetlb_vmemmap.c-213-\t\t\t     struct vmemmap_remap_walk *walk)\n--\nmm/hugetlb_vmemmap.c-259-\nmm/hugetlb_vmemmap.c:260:static int vmemmap_restore_pte(pte_t *pte, unsigned long addr,\nmm/hugetlb_vmemmap.c-261-\t\t\t       struct vmemmap_remap_walk *walk)\n--\nmm/hugetlb_vmemmap.c-304- */\nmm/hugetlb_vmemmap.c:305:static int vmemmap_remap_split(unsigned long start, unsigned long end)\nmm/hugetlb_vmemmap.c-306-{\n--\nmm/hugetlb_vmemmap.c=315=static const int VMEMMAP_REMAP_INCOMPLETE = 1;\n--\nmm/hugetlb_vmemmap.c-332- */\nmm/hugetlb_vmemmap.c:333:static int vmemmap_remap_free(unsigned long start, unsigned long end,\nmm/hugetlb_vmemmap.c-334-\t\t\t      struct page *vmemmap_head,\n--\nmm/hugetlb_vmemmap.c-371-\nmm/hugetlb_vmemmap.c:372:static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,\nmm/hugetlb_vmemmap.c-373-\t\t\t\t   struct list_head *list)\n--\nmm/hugetlb_vmemmap.c=396=static struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)\n--\nmm/hugetlb_vmemmap.c-435- */\nmm/hugetlb_vmemmap.c:436:static int vmemmap_remap_alloc(const struct hstate *h, struct folio *folio,\nmm/hugetlb_vmemmap.c-437-\t\t\t       unsigned long start, unsigned long end,\n--\nmm/hugetlb_vmemmap.c=472=static enum hugetlb_hvo_status hvo_status = HVO_INACTIVE;\nmm/hugetlb_vmemmap.c-473-\nmm/hugetlb_vmemmap.c:474:static bool hugetlb_hvo_status_try_set(enum hugetlb_hvo_status status)\nmm/hugetlb_vmemmap.c-475-{\n--\nmm/hugetlb_vmemmap.c-494-\nmm/hugetlb_vmemmap.c:495:bool hugetlb_vmemmap_optimization_try_disable(void)\nmm/hugetlb_vmemmap.c-496-{\n--\nmm/hugetlb_vmemmap.c=505=early_param(\"hugetlb_free_vmemmap\", hugetlb_vmemmap_optimize_param);\nmm/hugetlb_vmemmap.c-506-\nmm/hugetlb_vmemmap.c:507:static int __hugetlb_vmemmap_restore_folio(const struct hstate *h,\nmm/hugetlb_vmemmap.c-508-\t\t\t\t\t   struct folio *folio, unsigned long flags)\n--\nmm/hugetlb_vmemmap.c-546- */\nmm/hugetlb_vmemmap.c:547:int hugetlb_vmemmap_restore_folio(const struct hstate *h, struct folio *folio)\nmm/hugetlb_vmemmap.c-548-{\n--\nmm/hugetlb_vmemmap.c=565=long hugetlb_vmemmap_restore_folios(const struct hstate *h,\n--\nmm/hugetlb_vmemmap.c-593-/* Return true iff a HugeTLB whose vmemmap should and can be optimized. */\nmm/hugetlb_vmemmap.c:594:static bool vmemmap_should_optimize_folio(const struct hstate *h, struct folio *folio)\nmm/hugetlb_vmemmap.c-595-{\n--\nmm/hugetlb_vmemmap.c-613-\nmm/hugetlb_vmemmap.c:614:static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,\nmm/hugetlb_vmemmap.c-615-\t\t\t\t\t    struct folio *folio,\n--\nmm/hugetlb_vmemmap.c-688- */\nmm/hugetlb_vmemmap.c:689:void hugetlb_vmemmap_optimize_folio(const struct hstate *h, struct folio *folio)\nmm/hugetlb_vmemmap.c-690-{\n--\nmm/hugetlb_vmemmap.c-696-\nmm/hugetlb_vmemmap.c:697:static int hugetlb_vmemmap_split_folio(const struct hstate *h, struct folio *folio)\nmm/hugetlb_vmemmap.c-698-{\n--\nmm/hugetlb_vmemmap.c-713-\nmm/hugetlb_vmemmap.c:714:static void __hugetlb_vmemmap_optimize_folios(struct hstate *h,\nmm/hugetlb_vmemmap.c-715-\t\t\t\t\t      struct list_head *folio_list,\n--\nmm/hugetlb_vmemmap.c-794-\nmm/hugetlb_vmemmap.c:795:void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_list)\nmm/hugetlb_vmemmap.c-796-{\n--\nmm/hugetlb_vmemmap.c-799-\nmm/hugetlb_vmemmap.c:800:void hugetlb_vmemmap_optimize_bootmem_folios(struct hstate *h, struct list_head *folio_list)\nmm/hugetlb_vmemmap.c-801-{\n--\nmm/hugetlb_vmemmap.c-807-/* Return true of a bootmem allocated HugeTLB page should be pre-HVO-ed */\nmm/hugetlb_vmemmap.c:808:static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)\nmm/hugetlb_vmemmap.c-809-{\n"
}

9/2 2026/07/08 15:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24039
							output: 24
							thoughts: 354
						

Thoughts:
**Analyzing the Impact of Hugetlb Vmemmap Modifications**

Okay, let's break down these changes in `mm/hugetlb_vmemmap.c`. I need to zero in on the core modifications to understand their implications. From the provided list, it looks like there's been some targeted work on the interaction between hugepages and the vmemmap.

I'm seeing a lot of functions here, but I need to focus on the ones that are likely to be the most critical, and not simple generic hot paths. Based on the naming and likely function, I'd say the ones that stand out as worthy of immediate attention are:

*   `vmemmap_split_pmd`
*   `vmemmap_remap_pte`
*   `vmemmap_restore_pte`
*   `vmemmap_remap_free`
*   `vmemmap_remap_alloc`
*   `hugetlb_vmemmap_optimization_try_disable`
*   `__hugetlb_vmemmap_restore_folio`
*   `__hugetlb_vmemmap_optimize_folio`

Those seem to be the ones that directly affect how pages are remapped, freed, and optimized in the context of hugetlbfs. The `try_disable` and folio functions suggest an optimization strategy is involved.

Before I go further, I should check for any related configurations. It looks like `ARM64_HW_AFDBM` is selected on ARM64 by default. Is that a Kconfig option that impacts the behavior of these functions? I should verify that before diving into the details of the code.



10/2 2026/07/08 15:12 tool grepper
0m Args:
{
  "Expression": "config ARM64_HW_AFDBM"
}

Results:
{
  "Output": "arch/arm64/Kconfig=1900=menu \"ARMv8.1 architectural features\"\narch/arm64/Kconfig-1901-\narch/arm64/Kconfig:1902:config ARM64_HW_AFDBM\narch/arm64/Kconfig-1903-\tbool \"Support for hardware updates of the Access and Dirty page flags\"\n"
}

11/2 2026/07/08 15:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24887
							output: 31
							thoughts: 300
						

Thoughts:
**Analyzing ARM64 and Huge Pages Configuration**

Okay, so `ARM64_HW_AFDBM` is definitely a key configuration option here. My initial thought was to simply enable `HUGETLB_PAGE_OPTIMIZE_VMEMMAP`, `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON`, and `ARM64_HW_AFDBM`.

But then I remembered that `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` is a `def_bool` that's contingent on `HUGETLB_PAGE` being enabled. So, the kernel's configuration logic will handle its enabling automatically, *if* the dependencies are satisfied. Let's see... the dependencies are `ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP` and `SPARSEMEM_VMEMMAP`. And ah-ha! `ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP` *is* selected by `ARM64` if `ARM64_HW_AFDBM` is set. So, the real enabler here on ARM64 is indeed `ARM64_HW_AFDBM`.

That leaves us with `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON`. This is a boolean option that defaults to "n", so we definitely want to make sure *that* is enabled. I should double-check that this is being set correctly within the `fs/Kconfig` file. Also, let's assume `HUGETLB_PAGE` is already enabled but I need to make sure about that.



12/2 2026/07/08 15:13 tool grepper
0m Args:
{
  "Expression": "config HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON"
}

Results:
{
  "Output": "fs/Kconfig=262=if HUGETLBFS\nfs/Kconfig:263:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON\nfs/Kconfig-264-\tbool \"HugeTLB Vmemmap Optimization (HVO) defaults to on\"\n"
}

13/2 2026/07/08 15:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25649
							output: 54
							thoughts: 394
						

Thoughts:
**Refining the Hugetlb Vmemmap Optimization Checklist**

Okay, so I've been thinking about the `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` feature. Yes, `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON` is definitely the key here – that's the default state we're shooting for.

Alright, let's nail down the core elements. The relevant configuration options, as far as I can tell, are: `HUGETLB_PAGE_OPTIMIZE_VMEMMAP`, `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON`, and the architecture-specific `ARM64_HW_AFDBM` (which, knowing how these things work, likely interacts with the optimization in some way).

Now for the functions themselves. My initial list was: `vmemmap_split_pmd`, `vmemmap_remap_pte`, `vmemmap_restore_pte`, `vmemmap_remap_free`, `vmemmap_remap_alloc`, `hugetlb_vmemmap_optimization_try_disable`, `__hugetlb_vmemmap_restore_folio`, and `__hugetlb_vmemmap_optimize_folio`.

But, wait, let's refine that list for completeness. I need to make sure I haven't missed anything obvious. I'll add `vmemmap_should_optimize_folio` and `vmemmap_should_optimize_bootmem_page` to the list. That feels more complete.

Okay, this revised list looks pretty solid. It covers the core operations and checks related to the vmemmap optimization.

Let's do a quick sweep for any architecture-specific functions. I should check `arch/arm64/kernel/cpufeature.c` now. Right, I found `late_cpu_enable_hvo` and `hvo_compatible`. These are also strong candidates and could provide important clues, so they're in. That's good. Now, I feel a lot more confident. I have a solid understanding of this aspect.



14/2 2026/07/08 15:13 tool grepper
0m Args:
{
  "Expression": "^(static )?(int|void|bool) [a-zA-Z0-9_]+\\(",
  "PathPrefix": "arch/arm64/kernel/cpufeature.c"
}

Results:
{
  "Output": "arch/arm64/kernel/cpufeature.c=150=static cpumask_var_t cpu_32bit_el0_mask __cpumask_var_read_mostly;\narch/arm64/kernel/cpufeature.c-151-\narch/arm64/kernel/cpufeature.c:152:void dump_cpu_features(void)\narch/arm64/kernel/cpufeature.c-153-{\n--\narch/arm64/kernel/cpufeature.c-218-\narch/arm64/kernel/cpufeature.c:219:static void cpu_enable_cnp(struct arm64_cpu_capabilities const *cap);\narch/arm64/kernel/cpufeature.c-220-\narch/arm64/kernel/cpufeature.c:221:static bool __system_matches_cap(unsigned int n);\narch/arm64/kernel/cpufeature.c-222-\n--\narch/arm64/kernel/cpufeature.c=798=static const struct __ftr_reg_entry {\n--\narch/arm64/kernel/cpufeature.c-877-\narch/arm64/kernel/cpufeature.c:878:static int search_cmp_ftr_reg(const void *id, const void *regp)\narch/arm64/kernel/cpufeature.c-879-{\n--\narch/arm64/kernel/cpufeature.c=964=static void __init sort_ftr_regs(void)\n--\narch/arm64/kernel/cpufeature.c-1019- */\narch/arm64/kernel/cpufeature.c:1020:static void init_cpu_ftr_reg(u32 sys_reg, u64 new)\narch/arm64/kernel/cpufeature.c-1021-{\n--\narch/arm64/kernel/cpufeature.c=1114=static void __init setup_boot_cpu_capabilities(void);\narch/arm64/kernel/cpufeature.c-1115-\narch/arm64/kernel/cpufeature.c:1116:static void init_32bit_cpu_features(struct cpuinfo_32bit *info)\narch/arm64/kernel/cpufeature.c-1117-{\n--\narch/arm64/kernel/cpufeature.c=1169=static inline void detect_system_supports_pseudo_nmi(void) { }\n--\narch/arm64/kernel/cpufeature.c-1171-\narch/arm64/kernel/cpufeature.c:1172:static bool detect_ftr_has_mpam(void)\narch/arm64/kernel/cpufeature.c-1173-{\n--\narch/arm64/kernel/cpufeature.c=1180=void __init init_cpu_features(struct cpuinfo_arm64 *info)\n--\narch/arm64/kernel/cpufeature.c-1235-\narch/arm64/kernel/cpufeature.c:1236:static void update_cpu_ftr_reg(struct arm64_ftr_reg *reg, u64 new)\narch/arm64/kernel/cpufeature.c-1237-{\n--\narch/arm64/kernel/cpufeature.c-1252-\narch/arm64/kernel/cpufeature.c:1253:static int check_update_ftr_reg(u32 sys_id, int cpu, u64 val, u64 boot)\narch/arm64/kernel/cpufeature.c-1254-{\n--\narch/arm64/kernel/cpufeature.c-1267-\narch/arm64/kernel/cpufeature.c:1268:static void relax_cpu_ftr_reg(u32 sys_id, int field)\narch/arm64/kernel/cpufeature.c-1269-{\n--\narch/arm64/kernel/cpufeature.c-1286-\narch/arm64/kernel/cpufeature.c:1287:static void lazy_init_32bit_cpu_features(struct cpuinfo_arm64 *info,\narch/arm64/kernel/cpufeature.c-1288-\t\t\t\t\t struct cpuinfo_arm64 *boot)\n--\narch/arm64/kernel/cpufeature.c-1302-\narch/arm64/kernel/cpufeature.c:1303:static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,\narch/arm64/kernel/cpufeature.c-1304-\t\t\t\t     struct cpuinfo_32bit *boot)\n--\narch/arm64/kernel/cpufeature.c-1378- */\narch/arm64/kernel/cpufeature.c:1379:void update_cpu_features(int cpu,\narch/arm64/kernel/cpufeature.c-1380-\t\t\t struct cpuinfo_arm64 *info,\n--\narch/arm64/kernel/cpufeature.c=1724=device_initcall(aarch32_el0_sysfs_init);\narch/arm64/kernel/cpufeature.c-1725-\narch/arm64/kernel/cpufeature.c:1726:static bool has_32bit_el0(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-1727-{\n--\narch/arm64/kernel/cpufeature.c-1736-\narch/arm64/kernel/cpufeature.c:1737:static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-1738-{\n--\narch/arm64/kernel/cpufeature.c-1751-\narch/arm64/kernel/cpufeature.c:1752:static bool has_cache_idc(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-1753-\t\t\t  int scope)\n--\narch/arm64/kernel/cpufeature.c-1764-\narch/arm64/kernel/cpufeature.c:1765:static void cpu_emulate_effective_ctr(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-1766-{\n--\narch/arm64/kernel/cpufeature.c-1776-\narch/arm64/kernel/cpufeature.c:1777:static bool has_cache_dic(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-1778-\t\t\t  int scope)\n--\narch/arm64/kernel/cpufeature.c=1808=static int __kpti_forced; /* 0: not forced, \u003e0: forced on, \u003c0: forced off */\narch/arm64/kernel/cpufeature.c-1809-\narch/arm64/kernel/cpufeature.c:1810:static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-1811-\t\t\t\tint scope)\n--\narch/arm64/kernel/cpufeature.c-1885-\narch/arm64/kernel/cpufeature.c:1886:static bool has_nv1(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-1887-{\n--\narch/arm64/kernel/cpufeature.c-1908-#if defined(ID_AA64MMFR0_EL1_TGRAN_LPA2) \u0026\u0026 defined(ID_AA64MMFR0_EL1_TGRAN_2_SUPPORTED_LPA2)\narch/arm64/kernel/cpufeature.c:1909:static bool has_lpa2_at_stage1(u64 mmfr0)\narch/arm64/kernel/cpufeature.c-1910-{\n--\narch/arm64/kernel/cpufeature.c-1917-\narch/arm64/kernel/cpufeature.c:1918:static bool has_lpa2_at_stage2(u64 mmfr0)\narch/arm64/kernel/cpufeature.c-1919-{\n--\narch/arm64/kernel/cpufeature.c-1926-\narch/arm64/kernel/cpufeature.c:1927:static bool has_lpa2(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-1928-{\n--\narch/arm64/kernel/cpufeature.c-1934-#else\narch/arm64/kernel/cpufeature.c:1935:static bool has_lpa2(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-1936-{\n--\narch/arm64/kernel/cpufeature.c-1941-#ifdef CONFIG_HW_PERF_EVENTS\narch/arm64/kernel/cpufeature.c:1942:static bool has_pmuv3(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-1943-{\n--\narch/arm64/kernel/cpufeature.c-1953-\narch/arm64/kernel/cpufeature.c:1954:static void cpu_enable_kpti(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-1955-{\n--\narch/arm64/kernel/cpufeature.c=1980=static inline void __cpu_enable_hw_dbm(void)\n--\narch/arm64/kernel/cpufeature.c-1988-\narch/arm64/kernel/cpufeature.c:1989:static bool cpu_has_broken_dbm(void)\narch/arm64/kernel/cpufeature.c-1990-{\n--\narch/arm64/kernel/cpufeature.c-2006-\narch/arm64/kernel/cpufeature.c:2007:static bool cpu_can_use_dbm(const struct arm64_cpu_capabilities *cap)\narch/arm64/kernel/cpufeature.c-2008-{\n--\narch/arm64/kernel/cpufeature.c-2012-\narch/arm64/kernel/cpufeature.c:2013:static void cpu_enable_hw_dbm(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-2014-{\n--\narch/arm64/kernel/cpufeature.c-2020-\narch/arm64/kernel/cpufeature.c:2021:static bool has_hw_dbm(const struct arm64_cpu_capabilities *cap,\narch/arm64/kernel/cpufeature.c-2022-\t\t       int __unused)\n--\narch/arm64/kernel/cpufeature.c=2048=static struct cpumask amu_cpus __read_mostly;\narch/arm64/kernel/cpufeature.c-2049-\narch/arm64/kernel/cpufeature.c:2050:bool cpu_has_amu_feat(int cpu)\narch/arm64/kernel/cpufeature.c-2051-{\n--\narch/arm64/kernel/cpufeature.c-2054-\narch/arm64/kernel/cpufeature.c:2055:int get_cpu_with_amu_feat(void)\narch/arm64/kernel/cpufeature.c-2056-{\n--\narch/arm64/kernel/cpufeature.c-2059-\narch/arm64/kernel/cpufeature.c:2060:static void cpu_amu_enable(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-2061-{\n--\narch/arm64/kernel/cpufeature.c-2070-\narch/arm64/kernel/cpufeature.c:2071:static bool has_amu(const struct arm64_cpu_capabilities *cap,\narch/arm64/kernel/cpufeature.c-2072-\t\t    int __unused)\n--\narch/arm64/kernel/cpufeature.c-2089-#else\narch/arm64/kernel/cpufeature.c:2090:int get_cpu_with_amu_feat(void)\narch/arm64/kernel/cpufeature.c-2091-{\n--\narch/arm64/kernel/cpufeature.c-2095-\narch/arm64/kernel/cpufeature.c:2096:static bool runs_at_el2(const struct arm64_cpu_capabilities *entry, int __unused)\narch/arm64/kernel/cpufeature.c-2097-{\n--\narch/arm64/kernel/cpufeature.c-2100-\narch/arm64/kernel/cpufeature.c:2101:static void cpu_copy_el2regs(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2102-{\n--\narch/arm64/kernel/cpufeature.c-2114-\narch/arm64/kernel/cpufeature.c:2115:static bool has_nested_virt_support(const struct arm64_cpu_capabilities *cap,\narch/arm64/kernel/cpufeature.c-2116-\t\t\t\t    int scope)\n--\narch/arm64/kernel/cpufeature.c-2128-\narch/arm64/kernel/cpufeature.c:2129:static bool hvhe_possible(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-2130-\t\t\t  int __unused)\n--\narch/arm64/kernel/cpufeature.c-2134-\narch/arm64/kernel/cpufeature.c:2135:bool cpu_supports_bbml2_noabort(void)\narch/arm64/kernel/cpufeature.c-2136-{\n--\narch/arm64/kernel/cpufeature.c-2170-\narch/arm64/kernel/cpufeature.c:2171:static bool has_bbml2_noabort(const struct arm64_cpu_capabilities *caps, int scope)\narch/arm64/kernel/cpufeature.c-2172-{\n--\narch/arm64/kernel/cpufeature.c-2175-\narch/arm64/kernel/cpufeature.c:2176:static bool hvo_compatible(const struct arm64_cpu_capabilities *caps, int scope)\narch/arm64/kernel/cpufeature.c-2177-{\n--\narch/arm64/kernel/cpufeature.c-2184-\narch/arm64/kernel/cpufeature.c:2185:static bool late_cpu_enable_hvo(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2186-{\n--\narch/arm64/kernel/cpufeature.c-2200-\narch/arm64/kernel/cpufeature.c:2201:static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2202-{\n--\narch/arm64/kernel/cpufeature.c-2213-#ifdef CONFIG_ARM64_RAS_EXTN\narch/arm64/kernel/cpufeature.c:2214:static void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2215-{\n--\narch/arm64/kernel/cpufeature.c-2218-}\narch/arm64/kernel/cpufeature.c:2219:static bool has_rasv1p1(const struct arm64_cpu_capabilities *__unused, int scope)\narch/arm64/kernel/cpufeature.c-2220-{\n--\narch/arm64/kernel/cpufeature.c-2239-#ifdef CONFIG_ARM64_PTR_AUTH\narch/arm64/kernel/cpufeature.c:2240:static bool has_address_auth_cpucap(const struct arm64_cpu_capabilities *entry, int scope)\narch/arm64/kernel/cpufeature.c-2241-{\n--\narch/arm64/kernel/cpufeature.c-2265-\narch/arm64/kernel/cpufeature.c:2266:static bool has_address_auth_metacap(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-2267-\t\t\t\t     int scope)\n--\narch/arm64/kernel/cpufeature.c-2275-\narch/arm64/kernel/cpufeature.c:2276:static bool has_generic_auth(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-2277-\t\t\t     int __unused)\n--\narch/arm64/kernel/cpufeature.c-2287-#ifdef CONFIG_ARM64_E0PD\narch/arm64/kernel/cpufeature.c:2288:static void cpu_enable_e0pd(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-2289-{\n--\narch/arm64/kernel/cpufeature.c-2294-\narch/arm64/kernel/cpufeature.c:2295:static void cpu_enable_ls64(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-2296-{\n--\narch/arm64/kernel/cpufeature.c-2299-\narch/arm64/kernel/cpufeature.c:2300:static void cpu_enable_ls64_v(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-2301-{\n--\narch/arm64/kernel/cpufeature.c-2305-#ifdef CONFIG_ARM64_PSEUDO_NMI\narch/arm64/kernel/cpufeature.c:2306:static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-2307-\t\t\t\t   int scope)\n--\narch/arm64/kernel/cpufeature.c-2319-\narch/arm64/kernel/cpufeature.c:2320:static bool has_gic_prio_relaxed_sync(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-2321-\t\t\t\t      int scope)\n--\narch/arm64/kernel/cpufeature.c-2348-\narch/arm64/kernel/cpufeature.c:2349:static bool can_trap_icv_dir_el1(const struct arm64_cpu_capabilities *entry,\narch/arm64/kernel/cpufeature.c-2350-\t\t\t\t int scope)\n--\narch/arm64/kernel/cpufeature.c-2401-#ifdef CONFIG_ARM64_BTI\narch/arm64/kernel/cpufeature.c:2402:static void bti_enable(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2403-{\n--\narch/arm64/kernel/cpufeature.c-2416-#ifdef CONFIG_ARM64_MTE\narch/arm64/kernel/cpufeature.c:2417:static void cpu_enable_mte(struct arm64_cpu_capabilities const *cap)\narch/arm64/kernel/cpufeature.c-2418-{\n--\narch/arm64/kernel/cpufeature.c-2439-\narch/arm64/kernel/cpufeature.c:2440:static void user_feature_fixup(void)\narch/arm64/kernel/cpufeature.c-2441-{\n--\narch/arm64/kernel/cpufeature.c-2458-\narch/arm64/kernel/cpufeature.c:2459:static void elf_hwcap_fixup(void)\narch/arm64/kernel/cpufeature.c-2460-{\n--\narch/arm64/kernel/cpufeature.c-2467-#ifdef CONFIG_KVM\narch/arm64/kernel/cpufeature.c:2468:static bool is_kvm_protected_mode(const struct arm64_cpu_capabilities *entry, int __unused)\narch/arm64/kernel/cpufeature.c-2469-{\n--\narch/arm64/kernel/cpufeature.c-2473-\narch/arm64/kernel/cpufeature.c:2474:static void cpu_trap_el0_impdef(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2475-{\n--\narch/arm64/kernel/cpufeature.c-2478-\narch/arm64/kernel/cpufeature.c:2479:static void cpu_enable_dit(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2480-{\n--\narch/arm64/kernel/cpufeature.c-2483-\narch/arm64/kernel/cpufeature.c:2484:static void cpu_enable_mops(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2485-{\n--\narch/arm64/kernel/cpufeature.c-2489-#ifdef CONFIG_ARM64_POE\narch/arm64/kernel/cpufeature.c:2490:static void cpu_enable_poe(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2491-{\n--\narch/arm64/kernel/cpufeature.c-2497-#ifdef CONFIG_ARM64_GCS\narch/arm64/kernel/cpufeature.c:2498:static void cpu_enable_gcs(const struct arm64_cpu_capabilities *__unused)\narch/arm64/kernel/cpufeature.c-2499-{\n--\narch/arm64/kernel/cpufeature.c=3290=static const struct arm64_cpu_capabilities ptr_auth_hwcap_gen_matches[] = {\n--\narch/arm64/kernel/cpufeature.c-3304-#ifdef CONFIG_ARM64_SVE\narch/arm64/kernel/cpufeature.c:3305:static bool has_sve_feature(const struct arm64_cpu_capabilities *cap, int scope)\narch/arm64/kernel/cpufeature.c-3306-{\n--\narch/arm64/kernel/cpufeature.c-3311-#ifdef CONFIG_ARM64_SME\narch/arm64/kernel/cpufeature.c:3312:static bool has_sme_feature(const struct arm64_cpu_capabilities *cap, int scope)\narch/arm64/kernel/cpufeature.c-3313-{\n--\narch/arm64/kernel/cpufeature.c=3318=static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {\n--\narch/arm64/kernel/cpufeature.c-3458-#ifdef CONFIG_COMPAT\narch/arm64/kernel/cpufeature.c:3459:static bool compat_has_neon(const struct arm64_cpu_capabilities *cap, int scope)\narch/arm64/kernel/cpufeature.c-3460-{\n--\narch/arm64/kernel/cpufeature.c=3480=static const struct arm64_cpu_capabilities compat_elf_hwcaps[] = {\n--\narch/arm64/kernel/cpufeature.c-3503-\narch/arm64/kernel/cpufeature.c:3504:static void cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap)\narch/arm64/kernel/cpufeature.c-3505-{\n--\narch/arm64/kernel/cpufeature.c-3524-/* Check if we have a particular HWCAP enabled */\narch/arm64/kernel/cpufeature.c:3525:static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap)\narch/arm64/kernel/cpufeature.c-3526-{\n--\narch/arm64/kernel/cpufeature.c-3548-\narch/arm64/kernel/cpufeature.c:3549:static void setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)\narch/arm64/kernel/cpufeature.c-3550-{\n--\narch/arm64/kernel/cpufeature.c-3557-\narch/arm64/kernel/cpufeature.c:3558:static void update_cpu_capabilities(u16 scope_mask)\narch/arm64/kernel/cpufeature.c-3559-{\n--\narch/arm64/kernel/cpufeature.c-3615- */\narch/arm64/kernel/cpufeature.c:3616:static int cpu_enable_non_boot_scope_capabilities(void *__unused)\narch/arm64/kernel/cpufeature.c-3617-{\n--\narch/arm64/kernel/cpufeature.c=3640=static void __init enable_cpu_capabilities(u16 scope_mask)\n--\narch/arm64/kernel/cpufeature.c-3683- */\narch/arm64/kernel/cpufeature.c:3684:static void verify_local_cpu_caps(u16 scope_mask)\narch/arm64/kernel/cpufeature.c-3685-{\n--\narch/arm64/kernel/cpufeature.c-3748- */\narch/arm64/kernel/cpufeature.c:3749:static void check_early_cpu_features(void)\narch/arm64/kernel/cpufeature.c-3750-{\n--\narch/arm64/kernel/cpufeature.c=3757=__verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)\n--\narch/arm64/kernel/cpufeature.c-3767-\narch/arm64/kernel/cpufeature.c:3768:static void verify_local_elf_hwcaps(void)\narch/arm64/kernel/cpufeature.c-3769-{\n--\narch/arm64/kernel/cpufeature.c-3775-\narch/arm64/kernel/cpufeature.c:3776:static void verify_sve_features(void)\narch/arm64/kernel/cpufeature.c-3777-{\n--\narch/arm64/kernel/cpufeature.c-3788-\narch/arm64/kernel/cpufeature.c:3789:static void verify_sme_features(void)\narch/arm64/kernel/cpufeature.c-3790-{\n--\narch/arm64/kernel/cpufeature.c-3801-\narch/arm64/kernel/cpufeature.c:3802:static void verify_hyp_capabilities(void)\narch/arm64/kernel/cpufeature.c-3803-{\n--\narch/arm64/kernel/cpufeature.c-3832-\narch/arm64/kernel/cpufeature.c:3833:static void verify_mpam_capabilities(void)\narch/arm64/kernel/cpufeature.c-3834-{\n--\narch/arm64/kernel/cpufeature.c-3870- */\narch/arm64/kernel/cpufeature.c:3871:static void verify_local_cpu_capabilities(void)\narch/arm64/kernel/cpufeature.c-3872-{\n--\narch/arm64/kernel/cpufeature.c-3893-\narch/arm64/kernel/cpufeature.c:3894:void check_local_cpu_capabilities(void)\narch/arm64/kernel/cpufeature.c-3895-{\n--\narch/arm64/kernel/cpufeature.c-3913-\narch/arm64/kernel/cpufeature.c:3914:bool this_cpu_has_cap(unsigned int n)\narch/arm64/kernel/cpufeature.c-3915-{\n--\narch/arm64/kernel/cpufeature.c=3932=static bool __maybe_unused __system_matches_cap(unsigned int n)\n--\narch/arm64/kernel/cpufeature.c-3942-\narch/arm64/kernel/cpufeature.c:3943:void cpu_set_feature(unsigned int num)\narch/arm64/kernel/cpufeature.c-3944-{\n--\narch/arm64/kernel/cpufeature.c-3947-\narch/arm64/kernel/cpufeature.c:3948:bool cpu_have_feature(unsigned int num)\narch/arm64/kernel/cpufeature.c-3949-{\n--\narch/arm64/kernel/cpufeature.c=4066=void __init setup_user_features(void)\n--\narch/arm64/kernel/cpufeature.c-4079-\narch/arm64/kernel/cpufeature.c:4080:static int enable_mismatched_32bit_el0(unsigned int cpu)\narch/arm64/kernel/cpufeature.c-4081-{\n--\narch/arm64/kernel/cpufeature.c=4163=static inline int emulate_id_reg(u32 id, u64 *valp)\n--\narch/arm64/kernel/cpufeature.c-4182-\narch/arm64/kernel/cpufeature.c:4183:static int emulate_sys_reg(u32 id, u64 *valp)\narch/arm64/kernel/cpufeature.c-4184-{\n--\narch/arm64/kernel/cpufeature.c-4204-\narch/arm64/kernel/cpufeature.c:4205:int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt)\narch/arm64/kernel/cpufeature.c-4206-{\n--\narch/arm64/kernel/cpufeature.c-4217-\narch/arm64/kernel/cpufeature.c:4218:bool try_emulate_mrs(struct pt_regs *regs, u32 insn)\narch/arm64/kernel/cpufeature.c-4219-{\n"
}

15/2 2026/07/08 15:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34545
							output: 320
							thoughts: 452
						

Thoughts:
**My Analysis of the HugeTLB Vmemmap Optimization for ARM64**

Okay, so I've been reviewing this patch and I'm feeling good about where it's headed. The inclusion of `late_cpu_enable_hvo` and `hvo_compatible` is a solid addition, and they definitely need to be included in the focus symbols.

So, the definitive list of functions for analysis looks like this, and it seems like a very good set to focus on:
*   `vmemmap_split_pmd`
*   `vmemmap_remap_pte`
*   `vmemmap_restore_pte`
*   `vmemmap_remap_free`
*   `vmemmap_remap_alloc`
*   `hugetlb_vmemmap_optimization_try_disable`
*   `__hugetlb_vmemmap_restore_folio`
*   `__hugetlb_vmemmap_optimize_folio`
*   `vmemmap_should_optimize_folio`
*   `vmemmap_should_optimize_bootmem_page`
*   `late_cpu_enable_hvo`
*   `hvo_compatible`

Now, let's nail down the configurations. We're clearly dealing with:
*   `HUGETLB_PAGE_OPTIMIZE_VMEMMAP`
*   `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON`
*   `ARM64_HW_AFDBM`

Just double-checking...yep, `config ARM64_HW_AFDBM` is the right name. And while we need `HUGETLB_PAGE` and `SPARSEMEM_VMEMMAP`, they're generic and not specific to this ARM64 optimization.

Okay, let's recap the rationale here. The patch is about bringing ARM64 support to HugeTLB Vmemmap Optimization (HVO). It's been touching core kernel logic in `mm/hugetlb_vmemmap.c` and leveraging architecture-specific functions for managing vmemmap PMDs and PTEs. It's also adding those necessary CPU capability checks. This is a functional change, especially around allocation and freeing of HugeTLB pages when HVO is enabled, making it prime for fuzzing.

Alright, let's call `set-results`.



16/2 2026/07/08 15:13 tool set-results
0m Error:
the following configs do not exist in the kernel tree: ARM64_HW_AFDBM

Args:
{
  "EnableConfigs": [
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP",
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON",
    "ARM64_HW_AFDBM"
  ],
  "FocusSymbols": [
    "vmemmap_split_pmd",
    "vmemmap_remap_pte",
    "vmemmap_restore_pte",
    "vmemmap_remap_free",
    "vmemmap_remap_alloc",
    "hugetlb_vmemmap_optimization_try_disable",
    "__hugetlb_vmemmap_restore_folio",
    "__hugetlb_vmemmap_optimize_folio",
    "vmemmap_should_optimize_folio",
    "vmemmap_should_optimize_bootmem_page",
    "late_cpu_enable_hvo",
    "hvo_compatible"
  ],
  "Reasoning": "The patch introduces ARM64 support for HugeTLB Vmemmap Optimization (HVO). It modifies core kernel logic in `mm/hugetlb_vmemmap.c` to use architecture-specific functions for populating and updating vmemmap PMDs and PTEs. It also adds CPU capability checks for HVO on ARM64. This is a functional change that can be reached when allocating and freeing HugeTLB pages with HVO enabled, making it worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "error": "the following configs do not exist in the kernel tree: ARM64_HW_AFDBM"
}

17/2 2026/07/08 15:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 35879
							output: 24
							thoughts: 77
						

Thoughts:
**Investigating ARM64_HW_AFDBM Configuration**

Okay, so I see `ARM64_HW_AFDBM` is actually defined, which is a good starting point. But the way it's used suggests I shouldn't be enabling it directly, maybe it's dependent on some other configuration options. It could be selected by another Kconfig option, or it might just be the way `ARM64_HW_AFDBM` is defined in `arch/arm64/Kconfig`. Time to double-check that Kconfig file and see how this is configured and where the dependencies are. I need to understand the relationship of this option to other configurations before I proceed.



18/2 2026/07/08 15:13 tool grepper
0m Args:
{
  "Expression": "config ARM64_HW_AFDBM"
}

Results:
{
  "Output": "arch/arm64/Kconfig=1900=menu \"ARMv8.1 architectural features\"\narch/arm64/Kconfig-1901-\narch/arm64/Kconfig:1902:config ARM64_HW_AFDBM\narch/arm64/Kconfig-1903-\tbool \"Support for hardware updates of the Access and Dirty page flags\"\n"
}

19/2 2026/07/08 15:13 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 36236
							output: 308
							thoughts: 170
						

Thoughts:
**Debugging a Configuration Discrepancy**

Okay, hold on a second. Why did the build system flag `ARM64_HW_AFDBM` as non-existent? That's a valid configuration option, at least I thought it was. Could it be that the build tools, or whatever's checking the configuration, are running against `x86_64` configurations?

Yes, that makes sense. Syzkaller, the fuzzer that's likely driving this build, is typically used with `x86_64` targets. And this specific patch I'm working on is specifically targeting `arm64`. So, if the configuration check is assuming an `x86_64` context, it wouldn't know about `ARM64_HW_AFDBM` at all.

Alright, so the solution is probably to either simply omit `ARM64_HW_AFDBM` from my config or, if this option is strictly an `arm64` feature, ensure I maintain it for the `arm64` configuration. I'll need to double-check the context of this configuration option to be sure.

But, for now, let's keep it simple. Let's just leverage `HUGETLB_PAGE_OPTIMIZE_VMEMMAP` and `HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON` instead. That's a safer bet and will get things moving.



20/2 2026/07/08 15:13 tool set-results
0m Args:
{
  "EnableConfigs": [
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP",
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON"
  ],
  "FocusSymbols": [
    "vmemmap_split_pmd",
    "vmemmap_remap_pte",
    "vmemmap_restore_pte",
    "vmemmap_remap_free",
    "vmemmap_remap_alloc",
    "hugetlb_vmemmap_optimization_try_disable",
    "__hugetlb_vmemmap_restore_folio",
    "__hugetlb_vmemmap_optimize_folio",
    "vmemmap_should_optimize_folio",
    "vmemmap_should_optimize_bootmem_page",
    "late_cpu_enable_hvo",
    "hvo_compatible"
  ],
  "Reasoning": "The patch introduces ARM64 support for HugeTLB Vmemmap Optimization (HVO). It modifies core kernel logic in `mm/hugetlb_vmemmap.c` to use architecture-specific functions for populating and updating vmemmap PMDs and PTEs. It also adds CPU capability checks for HVO on ARM64. This is a functional change that can be reached when allocating and freeing HugeTLB pages with HVO enabled, making it worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP",
    "HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON"
  ],
  "FocusSymbols": [
    "vmemmap_split_pmd",
    "vmemmap_remap_pte",
    "vmemmap_restore_pte",
    "vmemmap_remap_free",
    "vmemmap_remap_alloc",
    "hugetlb_vmemmap_optimization_try_disable",
    "__hugetlb_vmemmap_restore_folio",
    "__hugetlb_vmemmap_optimize_folio",
    "vmemmap_should_optimize_folio",
    "vmemmap_should_optimize_bootmem_page",
    "late_cpu_enable_hvo",
    "hvo_compatible"
  ],
  "Reasoning": "The patch introduces ARM64 support for HugeTLB Vmemmap Optimization (HVO). It modifies core kernel logic in `mm/hugetlb_vmemmap.c` to use architecture-specific functions for populating and updating vmemmap PMDs and PTEs. It also adds CPU capability checks for HVO on ARM64. This is a functional change that can be reached when allocating and freeing HugeTLB pages with HVO enabled, making it worth fuzzing.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)