| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/05 18:21 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"MEMORY_FAILURE",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE"
],
"FocusSymbols": [
"__hugepage_handle_poison",
"free_has_hwpoisoned"
],
"Reasoning": "The patch modifies memory failure handling for HugeTLB pages, ensuring that healthy subpages of a compound page with hardware poisoned subpages are correctly freed to the buddy allocator, while isolating the poisoned ones. This involves functional changes to core MM and page allocator logic, specifically in mm/memory-failure.c and mm/page_alloc.c.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/05 18:21 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 977545c98956913e026d34f525299ab97906ccfb\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Jul 5 18:21:23 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/page-flags.h b/include/linux/page-flags.h\nindex 7a863572adce79..3cd524e04a30bb 100644\n--- a/include/linux/page-flags.h\n+++ b/include/linux/page-flags.h\n@@ -879,7 +879,7 @@ FOLIO_FLAG_FALSE(partially_mapped)\n \n #define PG_head_mask ((1UL \u003c\u003c PG_head))\n \n-#if defined(CONFIG_MEMORY_FAILURE) \u0026\u0026 defined(CONFIG_TRANSPARENT_HUGEPAGE)\n+#if defined(CONFIG_MEMORY_FAILURE) \u0026\u0026 (defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLB_PAGE))\n /*\n * PageHasHWPoisoned indicates that at least one subpage is hwpoisoned in the\n * compound page.\ndiff --git a/mm/memory-failure.c b/mm/memory-failure.c\nindex 4916ab14532573..a37b6755071827 100644\n--- a/mm/memory-failure.c\n+++ b/mm/memory-failure.c\n@@ -174,6 +174,30 @@ static struct rb_root_cached pfn_space_itree = RB_ROOT_CACHED;\n static DEFINE_MUTEX(pfn_space_lock);\n \n /*\n+ * Only for a HugeTLB page being handled by memory_failure(). The key\n+ * difference to soft_offline() is that, no HWPoison subpage will make\n+ * into buddy allocator after a successful dissolve_free_hugetlb_folio(),\n+ * so take_page_off_buddy() is unnecessary.\n+ */\n+static int __hugepage_handle_poison(struct page *page)\n+{\n+\tstruct folio *folio = page_folio(page);\n+\n+\t/*\n+\t * Can't use dissolve_free_hugetlb_folio() without a reliable\n+\t * raw_hwp_list telling which subpage is HWPoison. So do not free\n+\t * them to the buddy allocator. dequeue_hugetlb_folio_node_exact()\n+\t * will ensure to never re-allocate this hugepage.\n+\t */\n+\tif (folio_test_hugetlb_raw_hwp_unreliable(folio))\n+\t\t/* raw_hwp_list becomes unreliable when kmalloc() fails. */\n+\t\treturn -ENOMEM;\n+\n+\treturn dissolve_free_hugetlb_folio(folio);\n+}\n+\n+/*\n+ * Only for a free or HugeTLB page being handled by soft_offline().\n * Return values:\n * 1: the page is dissolved (if needed) and taken off from buddy,\n * 0: the page is dissolved (if needed) and not taken off from buddy,\n@@ -1166,11 +1190,11 @@ static int me_huge_page(struct page_state *ps, struct page *p)\n \t\t * subpages.\n \t\t */\n \t\tfolio_put(folio);\n-\t\tif (__page_handle_poison(p) \u003e 0) {\n+\t\tif (__hugepage_handle_poison(p)) {\n+\t\t\tres = MF_FAILED;\n+\t\t} else {\n \t\t\tpage_ref_inc(p);\n \t\t\tres = MF_RECOVERED;\n-\t\t} else {\n-\t\t\tres = MF_FAILED;\n \t\t}\n \t}\n \n@@ -2008,6 +2032,7 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)\n \tif (folio_test_hugetlb_vmemmap_optimized(folio))\n \t\treturn;\n \tfolio_clear_hwpoison(folio);\n+\tfolio_set_has_hwpoisoned(folio);\n \tfolio_free_raw_hwp(folio, true);\n }\n \n@@ -2132,11 +2157,11 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)\n \t */\n \tif (res == MF_HUGETLB_FREED) {\n \t\tfolio_unlock(folio);\n-\t\tif (__page_handle_poison(p) \u003e 0) {\n+\t\tif (__hugepage_handle_poison(p)) {\n+\t\t\tres = MF_FAILED;\n+\t\t} else {\n \t\t\tpage_ref_inc(p);\n \t\t\tres = MF_RECOVERED;\n-\t\t} else {\n-\t\t\tres = MF_FAILED;\n \t\t}\n \t\treturn action_result(pfn, MF_MSG_FREE_HUGE, res);\n \t}\ndiff --git a/mm/page_alloc.c b/mm/page_alloc.c\nindex 762d9b6bc792f1..6418896c9df660 100644\n--- a/mm/page_alloc.c\n+++ b/mm/page_alloc.c\n@@ -93,6 +93,13 @@ typedef int __bitwise fpi_t;\n /* free_pages_prepare() has already been called for page(s) being freed. */\n #define FPI_PREPARED\t\t((__force fpi_t)BIT(3))\n \n+/*\n+ * Page(s) needs to go through free_pages_sanitize(), for example, because\n+ * free_pages_prepare() cannot sanitize a high-order page block due to\n+ * hardware error in some page(s).\n+ */\n+#define FPI_SANITIZE\t\t((__force fpi_t)BIT(4))\n+\n /* prevent \u003e1 _updater_ of zone percpu pageset -\u003ehigh and -\u003ebatch fields */\n static DEFINE_MUTEX(pcp_batch_high_lock);\n #define MIN_PERCPU_PAGELIST_HIGH_FRACTION (8)\n@@ -210,6 +217,8 @@ gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;\n unsigned int pageblock_order __read_mostly;\n #endif\n \n+static void free_has_hwpoisoned(struct page *page, unsigned int order,\n+\t\t\t\tfpi_t fpi_flags);\n static void __free_pages_ok(struct page *page, unsigned int order,\n \t\t\t fpi_t fpi_flags);\n static void reserve_highatomic_pageblock(struct page *page, int order,\n@@ -1311,14 +1320,73 @@ static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr)\n \n #endif /* CONFIG_MEM_ALLOC_PROFILING */\n \n+/*\n+ * Sanitize, which requires writing, a block of pages at the last moment of\n+ * preparing to freeing them, i.e. __free_pages_prepare().\n+ */\n+static void free_pages_sanitize(struct page *page, unsigned int order)\n+{\n+\tbool init = want_init_on_free();\n+\t/*\n+\t * __kasan_unpoison_pages() sets kasan tag on every tail page, so\n+\t * it is fine to use should_skip_kasan_poison() when pages here\n+\t * were a set of tail pages from a compound folio.\n+\t */\n+\tbool skip_kasan_poison = should_skip_kasan_poison(page);\n+\n+\tkernel_poison_pages(page, 1 \u003c\u003c order);\n+\n+\t/*\n+\t * As memory initialization might be integrated into KASAN,\n+\t * KASAN poisoning and memory initialization code must be\n+\t * kept together to avoid discrepancies in behavior.\n+\t *\n+\t * With hardware tag-based KASAN, memory tags must be set before the\n+\t * page becomes unavailable via debug_pagealloc or arch_free_page.\n+\t */\n+\tif (!skip_kasan_poison) {\n+\t\tkasan_poison_pages(page, order, init);\n+\n+\t\t/* Memory is already initialized if KASAN did it internally. */\n+\t\tif (kasan_has_integrated_init())\n+\t\t\tinit = false;\n+\t}\n+\tif (init)\n+\t\tclear_highpages_kasan_tagged(page, 1 \u003c\u003c order);\n+\n+\t/*\n+\t * arch_free_page() can make the page's contents inaccessible. s390\n+\t * does this. So nothing which can access the page's contents should\n+\t * happen after this.\n+\t */\n+\tarch_free_page(page, order);\n+\n+\tdebug_pagealloc_unmap_pages(page, 1 \u003c\u003c order);\n+}\n+\n+/*\n+ * Returns\n+ * - true: checks and preparations all good, caller can proceed freeing.\n+ * - false: do not proceed freeing for one of the following reasons:\n+ * 1. Some check failed so it is not safe to proceed freeing.\n+ * 2. A compound page has some HWPoison pages. The healthy pages\n+ * are already safely freed, and the HWPoison ones isolated.\n+ */\n static __always_inline bool __free_pages_prepare(struct page *page,\n \t\tunsigned int order, fpi_t fpi_flags)\n {\n \tint bad = 0;\n-\tbool skip_kasan_poison = should_skip_kasan_poison(page);\n-\tbool init = want_init_on_free();\n \tbool compound = PageCompound(page);\n \tstruct folio *folio = page_folio(page);\n+\t/*\n+\t * When dealing with compound page, PG_has_hwpoisoned is cleared\n+\t * with PAGE_FLAGS_SECOND. So the check must be done first.\n+\t *\n+\t * Note we can't exclude PG_has_hwpoisoned from PAGE_FLAGS_SECOND.\n+\t * Because PG_has_hwpoisoned == PG_active, free_page_is_bad() will\n+\t * confuse and complaint that the first tail page is still active.\n+\t */\n+\tbool should_fhh = compound \u0026\u0026 folio_test_has_hwpoisoned(folio);\n \n \tif (fpi_flags \u0026 FPI_PREPARED)\n \t\treturn true;\n@@ -1416,34 +1484,19 @@ static __always_inline bool __free_pages_prepare(struct page *page,\n \t\t\t\t\t PAGE_SIZE \u003c\u003c order);\n \t}\n \n-\tkernel_poison_pages(page, 1 \u003c\u003c order);\n-\n \t/*\n-\t * As memory initialization might be integrated into KASAN,\n-\t * KASAN poisoning and memory initialization code must be\n-\t * kept together to avoid discrepancies in behavior.\n+\t * After breaking down compound page and dealing with page metadata\n+\t * (e.g. page owner and page alloc tags), take a shortcut if this\n+\t * was a compound page containing certain HWPoison subpages.\n \t *\n-\t * With hardware tag-based KASAN, memory tags must be set before the\n-\t * page becomes unavailable via debug_pagealloc or arch_free_page.\n+\t * FPI_SANITIZE to remember free_pages_sanitize() healthy pages.\n \t */\n-\tif (!skip_kasan_poison) {\n-\t\tkasan_poison_pages(page, order, init);\n-\n-\t\t/* Memory is already initialized if KASAN did it internally. */\n-\t\tif (kasan_has_integrated_init())\n-\t\t\tinit = false;\n+\tif (should_fhh) {\n+\t\tfree_has_hwpoisoned(page, order, fpi_flags | FPI_SANITIZE);\n+\t\treturn false;\n \t}\n-\tif (init)\n-\t\tclear_highpages_kasan_tagged(page, 1 \u003c\u003c order);\n-\n-\t/*\n-\t * arch_free_page() can make the page's contents inaccessible. s390\n-\t * does this. So nothing which can access the page's contents should\n-\t * happen after this.\n-\t */\n-\tarch_free_page(page, order);\n \n-\tdebug_pagealloc_unmap_pages(page, 1 \u003c\u003c order);\n+\tfree_pages_sanitize(page, order);\n \n \treturn true;\n }\n@@ -6852,8 +6905,8 @@ void __init page_alloc_sysctl_init(void)\n \tregister_sysctl_init(\"vm\", page_alloc_sysctl_table);\n }\n \n-static void free_prepared_contig_range(struct page *page,\n-\t\tunsigned long nr_pages)\n+static void __free_prepared_contig_range(struct page *page,\n+\t\tunsigned long nr_pages, fpi_t fpi_flags)\n {\n \tunsigned long pfn = page_to_pfn(page);\n \n@@ -6869,8 +6922,15 @@ static void free_prepared_contig_range(struct page *page,\n \t\t/*\n \t\t * Free the chunk as a single block. Our caller has already\n \t\t * called free_pages_prepare() for each order-0 page.\n+\t\t *\n+\t\t * If the original compound page has HWPoison page,\n+\t\t * free_pages_prepare() has to skip sanitize at that time,\n+\t\t * but now it is good time to do that.\n \t\t */\n-\t\t__free_frozen_pages(page, order, FPI_PREPARED);\n+\t\tif (fpi_flags \u0026 FPI_SANITIZE)\n+\t\t\tfree_pages_sanitize(page, order);\n+\n+\t\t__free_frozen_pages(page, order, fpi_flags | FPI_PREPARED);\n \n \t\tpfn += 1UL \u003c\u003c order;\n \t\tpage += 1UL \u003c\u003c order;\n@@ -6878,6 +6938,11 @@ static void free_prepared_contig_range(struct page *page,\n \t}\n }\n \n+static void free_prepared_contig_range(struct page *page, unsigned long nr_pages)\n+{\n+\t__free_prepared_contig_range(page, nr_pages, FPI_NONE);\n+}\n+\n static void __free_contig_range_common(unsigned long pfn, unsigned long nr_pages,\n \t\tbool is_frozen)\n {\n@@ -6951,6 +7016,61 @@ void __free_contig_range(unsigned long pfn, unsigned long nr_pages)\n \t__free_contig_range_common(pfn, nr_pages, /* is_frozen= */ false);\n }\n \n+/*\n+ * Given some contiguous pages that have certain number of HWPoison page(s),\n+ * free only the healthy ones.\n+ *\n+ * Used at the end of __free_pages_prepare(). Even if having HWPoison pages,\n+ * breaking down compound page and clearing metadata (e.g. page owner, alloc\n+ * tag) can be done together during __free_pages_prepare(), which simplifies\n+ * the splitting here: unlike __split_unmapped_folio(), there is no need to\n+ * turn split pages into a compound page or to carry metadata.\n+ *\n+ * It scans every raw page of the compound page and causes nontrivial overhead.\n+ * So only use this when the compound page contains HWPoison page(s).\n+ *\n+ * It also works when order == 0, regardless of PageHWPoison() or not.\n+ *\n+ * This implementation needs rework in memdesc world.\n+ */\n+static void free_has_hwpoisoned(struct page *page, unsigned int order,\n+\t\t\t\tfpi_t fpi_flags)\n+{\n+\tunsigned long curr = page_to_pfn(page);\n+\tunsigned long end_pfn = curr + (1 \u003c\u003c order);\n+\tunsigned long next;\n+\tunsigned long total_freed = 0;\n+\tunsigned long total_hwp = 0;\n+\n+\twhile (curr \u003c end_pfn) {\n+\t\tnext = curr;\n+\n+\t\twhile (next \u003c end_pfn \u0026\u0026 !PageHWPoison(pfn_to_page(next)))\n+\t\t\t++next;\n+\n+\t\tif (next != end_pfn) {\n+\t\t\t/*\n+\t\t\t * Avoid accounting error when the page is freed\n+\t\t\t * by unpoison_memory().\n+\t\t\t */\n+\t\t\tclear_page_tag_ref(pfn_to_page(next));\n+\t\t\t++total_hwp;\n+\t\t}\n+\n+\t\t__free_prepared_contig_range(pfn_to_page(curr), next - curr,\n+\t\t\t\t\t fpi_flags);\n+\t\ttotal_freed += next - curr;\n+\n+\t\tif (next == end_pfn)\n+\t\t\tbreak;\n+\n+\t\tcurr = next + 1;\n+\t}\n+\n+\tpr_info(\"Freed %#lx pages, excluded %#lx HWPoison pages\\n\",\n+\t\ttotal_freed, total_hwp);\n+}\n+\n #ifdef CONFIG_CONTIG_ALLOC\n /* Usage: See admin-guide/dynamic-debug-howto.rst */\n static void alloc_contig_dump_pages(struct list_head *page_list)\ndiff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/selftests/mm/memory-failure.c\nindex 032ed952057c62..78c105ce2af185 100644\n--- a/tools/testing/selftests/mm/memory-failure.c\n+++ b/tools/testing/selftests/mm/memory-failure.c\n@@ -18,6 +18,7 @@\n #include \u003clinux/magic.h\u003e\n #include \u003cerrno.h\u003e\n \n+#include \"hugepage_settings.h\"\n #include \"vm_util.h\"\n \n enum inject_type {\n@@ -27,6 +28,7 @@ enum inject_type {\n \n enum result_type {\n \tMADV_HARD_ANON,\n+\tMADV_HARD_ANON_HUGETLB,\n \tMADV_HARD_CLEAN_PAGECACHE,\n \tMADV_HARD_DIRTY_PAGECACHE,\n \tMADV_SOFT_ANON,\n@@ -47,6 +49,8 @@ FIXTURE(memory_failure)\n \tint pagemap_fd;\n \tint kpageflags_fd;\n \tbool triggered;\n+\t/* Number of initial HugeTLB pages with default page size. */\n+\tunsigned long nr_hugetlb_pages;\n };\n \n FIXTURE_VARIANT(memory_failure)\n@@ -157,6 +161,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure\n \t\t void *vaddr, enum result_type type, int setjmp)\n {\n \tunsigned long size;\n+\tunsigned long nr_hugetlb_pages;\n \tuint64_t pfn_flags;\n \n \tswitch (type) {\n@@ -174,6 +179,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure\n \t\tASSERT_NE(pagemap_get_pfn(self-\u003epagemap_fd, vaddr), self-\u003epfn);\n \t\tbreak;\n \tcase MADV_HARD_ANON:\n+\tcase MADV_HARD_ANON_HUGETLB:\n \tcase MADV_HARD_DIRTY_PAGECACHE:\n \t\t/* The SIGBUS signal should have been received. */\n \t\tASSERT_EQ(setjmp, 1);\n@@ -184,8 +190,15 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure\n \t\tASSERT_EQ(1UL \u003c\u003c siginfo.si_addr_lsb, self-\u003epage_size);\n \t\tASSERT_EQ(siginfo.si_addr, vaddr);\n \n-\t\t/* XXX Check backing pte is hwpoison entry when supported. */\n-\t\tASSERT_TRUE(pagemap_is_swapped(self-\u003epagemap_fd, vaddr));\n+\t\tif (type != MADV_HARD_ANON_HUGETLB)\n+\t\t\t/*\n+\t\t\t * Check backing pte is hwpoison entry when supported.\n+\t\t\t * Although try_to_unmap_one() also installs hwpoison\n+\t\t\t * entry for HugeTLB, pagemap_hugetlb_range() doesn't\n+\t\t\t * parse swap entries at all.\n+\t\t\t */\n+\t\t\tASSERT_TRUE(pagemap_is_swapped(self-\u003epagemap_fd, vaddr));\n+\n \t\tbreak;\n \tdefault:\n \t\tSKIP(return, \"unexpected inject type %d.\\n\", type);\n@@ -193,7 +206,19 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure\n \n \t/* Check if the value of HardwareCorrupted has increased. */\n \tASSERT_EQ(get_hardware_corrupted_size(\u0026size), 0);\n-\tASSERT_EQ(size, self-\u003ecorrupted_size + self-\u003epage_size / 1024);\n+\n+\tif (type == MADV_HARD_ANON_HUGETLB) {\n+\t\t/*\n+\t\t * Only one page is hardware corrupted; the rest should all be\n+\t\t * released to buddy allocator.\n+\t\t */\n+\t\tASSERT_EQ(size, self-\u003ecorrupted_size + getpagesize() / 1024);\n+\t\t/* HugeTLB should have lost the HWPoison HugeTLB page. */\n+\t\tnr_hugetlb_pages = hugetlb_nr_default_pages();\n+\t\tASSERT_EQ(nr_hugetlb_pages + 1, self-\u003enr_hugetlb_pages);\n+\t} else {\n+\t\tASSERT_EQ(size, self-\u003ecorrupted_size + self-\u003epage_size / 1024);\n+\t}\n \n \t/* Check if HWPoison flag is set. */\n \tASSERT_EQ(pageflags_get(self-\u003epfn, self-\u003ekpageflags_fd, \u0026pfn_flags), 0);\n@@ -247,6 +272,45 @@ TEST_F(memory_failure, anon)\n \tASSERT_EQ(munmap(addr, self-\u003epage_size), 0);\n }\n \n+TEST_F(memory_failure, anon_hugetlb)\n+{\n+\tchar *addr;\n+\tint ret;\n+\tconst unsigned long nr_alloc_hugetlb_pages = 4;\n+\tunsigned long alloc_size;\n+\n+\tif (variant-\u003etype == MADV_SOFT)\n+\t\tSKIP(return, \"Soft offline test is not implemented\");\n+\n+\t/* HugeTLB settings will be automatically restored when test exits. */\n+\thugetlb_setup_default(nr_alloc_hugetlb_pages);\n+\n+\talloc_size = default_huge_page_size() * nr_alloc_hugetlb_pages;\n+\tself-\u003epage_size = default_huge_page_size();\n+\tself-\u003enr_hugetlb_pages = hugetlb_nr_default_pages();\n+\n+\taddr = mmap(0, alloc_size, PROT_READ | PROT_WRITE,\n+\t\t MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0);\n+\tif (addr == MAP_FAILED)\n+\t\tSKIP(return, \"mmap failed, not enough memory or hugetlb not supported.\\n\");\n+\tmemset(addr, 0xce, alloc_size);\n+\n+\tprepare(_metadata, self, addr);\n+\n+\tret = sigsetjmp(signal_jmp_buf, 1);\n+\tif (!self-\u003etriggered) {\n+\t\tself-\u003etriggered = true;\n+\t\tASSERT_EQ(variant-\u003einject(self, addr), 0);\n+\t\tFORCE_READ(*addr);\n+\t}\n+\n+\tcheck(_metadata, self, addr, MADV_HARD_ANON_HUGETLB, ret);\n+\n+\tcleanup(_metadata, self, addr);\n+\n+\tASSERT_EQ(munmap(addr, alloc_size), 0);\n+}\n+\n static int prepare_file(const char *fname, unsigned long size)\n {\n \tint fd;\n"
} |
| 2/1 | 2026/07/05 18:21 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"MEMORY_FAILURE",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE"
],
"FocusSymbols": [
"__hugepage_handle_poison",
"free_has_hwpoisoned"
],
"Reasoning": "The patch modifies memory failure handling for HugeTLB pages, ensuring that healthy subpages of a compound page with hardware poisoned subpages are correctly freed to the buddy allocator, while isolating the poisoned ones. This involves functional changes to core MM and page allocator logic, specifically in mm/memory-failure.c and mm/page_alloc.c.",
"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 977545c98956913e026d34f525299ab97906ccfb
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Jul 5 18:21:23 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7a863572adce79..3cd524e04a30bb 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -879,7 +879,7 @@ FOLIO_FLAG_FALSE(partially_mapped)
#define PG_head_mask ((1UL << PG_head))
-#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_TRANSPARENT_HUGEPAGE)
+#if defined(CONFIG_MEMORY_FAILURE) && (defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLB_PAGE))
/*
* PageHasHWPoisoned indicates that at least one subpage is hwpoisoned in the
* compound page.
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 4916ab14532573..a37b6755071827 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -174,6 +174,30 @@ static struct rb_root_cached pfn_space_itree = RB_ROOT_CACHED;
static DEFINE_MUTEX(pfn_space_lock);
/*
+ * Only for a HugeTLB page being handled by memory_failure(). The key
+ * difference to soft_offline() is that, no HWPoison subpage will make
+ * into buddy allocator after a successful dissolve_free_hugetlb_folio(),
+ * so take_page_off_buddy() is unnecessary.
+ */
+static int __hugepage_handle_poison(struct page *page)
+{
+ struct folio *folio = page_folio(page);
+
+ /*
+ * Can't use dissolve_free_hugetlb_folio() without a reliable
+ * raw_hwp_list telling which subpage is HWPoison. So do not free
+ * them to the buddy allocator. dequeue_hugetlb_folio_node_exact()
+ * will ensure to never re-allocate this hugepage.
+ */
+ if (folio_test_hugetlb_raw_hwp_unreliable(folio))
+ /* raw_hwp_list becomes unreliable when kmalloc() fails. */
+ return -ENOMEM;
+
+ return dissolve_free_hugetlb_folio(folio);
+}
+
+/*
+ * Only for a free or HugeTLB page being handled by soft_offline().
* Return values:
* 1: the page is dissolved (if needed) and taken off from buddy,
* 0: the page is dissolved (if needed) and not taken off from buddy,
@@ -1166,11 +1190,11 @@ static int me_huge_page(struct page_state *ps, struct page *p)
* subpages.
*/
folio_put(folio);
- if (__page_handle_poison(p) > 0) {
+ if (__hugepage_handle_poison(p)) {
+ res = MF_FAILED;
+ } else {
page_ref_inc(p);
res = MF_RECOVERED;
- } else {
- res = MF_FAILED;
}
}
@@ -2008,6 +2032,7 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
if (folio_test_hugetlb_vmemmap_optimized(folio))
return;
folio_clear_hwpoison(folio);
+ folio_set_has_hwpoisoned(folio);
folio_free_raw_hwp(folio, true);
}
@@ -2132,11 +2157,11 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)
*/
if (res == MF_HUGETLB_FREED) {
folio_unlock(folio);
- if (__page_handle_poison(p) > 0) {
+ if (__hugepage_handle_poison(p)) {
+ res = MF_FAILED;
+ } else {
page_ref_inc(p);
res = MF_RECOVERED;
- } else {
- res = MF_FAILED;
}
return action_result(pfn, MF_MSG_FREE_HUGE, res);
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 762d9b6bc792f1..6418896c9df660 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -93,6 +93,13 @@ typedef int __bitwise fpi_t;
/* free_pages_prepare() has already been called for page(s) being freed. */
#define FPI_PREPARED ((__force fpi_t)BIT(3))
+/*
+ * Page(s) needs to go through free_pages_sanitize(), for example, because
+ * free_pages_prepare() cannot sanitize a high-order page block due to
+ * hardware error in some page(s).
+ */
+#define FPI_SANITIZE ((__force fpi_t)BIT(4))
+
/* prevent >1 _updater_ of zone percpu pageset ->high and ->batch fields */
static DEFINE_MUTEX(pcp_batch_high_lock);
#define MIN_PERCPU_PAGELIST_HIGH_FRACTION (8)
@@ -210,6 +217,8 @@ gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;
unsigned int pageblock_order __read_mostly;
#endif
+static void free_has_hwpoisoned(struct page *page, unsigned int order,
+ fpi_t fpi_flags);
static void __free_pages_ok(struct page *page, unsigned int order,
fpi_t fpi_flags);
static void reserve_highatomic_pageblock(struct page *page, int order,
@@ -1311,14 +1320,73 @@ static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr)
#endif /* CONFIG_MEM_ALLOC_PROFILING */
+/*
+ * Sanitize, which requires writing, a block of pages at the last moment of
+ * preparing to freeing them, i.e. __free_pages_prepare().
+ */
+static void free_pages_sanitize(struct page *page, unsigned int order)
+{
+ bool init = want_init_on_free();
+ /*
+ * __kasan_unpoison_pages() sets kasan tag on every tail page, so
+ * it is fine to use should_skip_kasan_poison() when pages here
+ * were a set of tail pages from a compound folio.
+ */
+ bool skip_kasan_poison = should_skip_kasan_poison(page);
+
+ kernel_poison_pages(page, 1 << order);
+
+ /*
+ * As memory initialization might be integrated into KASAN,
+ * KASAN poisoning and memory initialization code must be
+ * kept together to avoid discrepancies in behavior.
+ *
+ * With hardware tag-based KASAN, memory tags must be set before the
+ * page becomes unavailable via debug_pagealloc or arch_free_page.
+ */
+ if (!skip_kasan_poison) {
+ kasan_poison_pages(page, order, init);
+
+ /* Memory is already initialized if KASAN did it internally. */
+ if (kasan_has_integrated_init())
+ init = false;
+ }
+ if (init)
+ clear_highpages_kasan_tagged(page, 1 << order);
+
+ /*
+ * arch_free_page() can make the page's contents inaccessible. s390
+ * does this. So nothing which can access the page's contents should
+ * happen after this.
+ */
+ arch_free_page(page, order);
+
+ debug_pagealloc_unmap_pages(page, 1 << order);
+}
+
+/*
+ * Returns
+ * - true: checks and preparations all good, caller can proceed freeing.
+ * - false: do not proceed freeing for one of the following reasons:
+ * 1. Some check failed so it is not safe to proceed freeing.
+ * 2. A compound page has some HWPoison pages. The healthy pages
+ * are already safely freed, and the HWPoison ones isolated.
+ */
static __always_inline bool __free_pages_prepare(struct page *page,
unsigned int order, fpi_t fpi_flags)
{
int bad = 0;
- bool skip_kasan_poison = should_skip_kasan_poison(page);
- bool init = want_init_on_free();
bool compound = PageCompound(page);
struct folio *folio = page_folio(page);
+ /*
+ * When dealing with compound page, PG_has_hwpoisoned is cleared
+ * with PAGE_FLAGS_SECOND. So the check must be done first.
+ *
+ * Note we can't exclude PG_has_hwpoisoned from PAGE_FLAGS_SECOND.
+ * Because PG_has_hwpoisoned == PG_active, free_page_is_bad() will
+ * confuse and complaint that the first tail page is still active.
+ */
+ bool should_fhh = compound && folio_test_has_hwpoisoned(folio);
if (fpi_flags & FPI_PREPARED)
return true;
@@ -1416,34 +1484,19 @@ static __always_inline bool __free_pages_prepare(struct page *page,
PAGE_SIZE << order);
}
- kernel_poison_pages(page, 1 << order);
-
/*
- * As memory initialization might be integrated into KASAN,
- * KASAN poisoning and memory initialization code must be
- * kept together to avoid discrepancies in behavior.
+ * After breaking down compound page and dealing with page metadata
+ * (e.g. page owner and page alloc tags), take a shortcut if this
+ * was a compound page containing certain HWPoison subpages.
*
- * With hardware tag-based KASAN, memory tags must be set before the
- * page becomes unavailable via debug_pagealloc or arch_free_page.
+ * FPI_SANITIZE to remember free_pages_sanitize() healthy pages.
*/
- if (!skip_kasan_poison) {
- kasan_poison_pages(page, order, init);
-
- /* Memory is already initialized if KASAN did it internally. */
- if (kasan_has_integrated_init())
- init = false;
+ if (should_fhh) {
+ free_has_hwpoisoned(page, order, fpi_flags | FPI_SANITIZE);
+ return false;
}
- if (init)
- clear_highpages_kasan_tagged(page, 1 << order);
-
- /*
- * arch_free_page() can make the page's contents inaccessible. s390
- * does this. So nothing which can access the page's contents should
- * happen after this.
- */
- arch_free_page(page, order);
- debug_pagealloc_unmap_pages(page, 1 << order);
+ free_pages_sanitize(page, order);
return true;
}
@@ -6852,8 +6905,8 @@ void __init page_alloc_sysctl_init(void)
register_sysctl_init("vm", page_alloc_sysctl_table);
}
-static void free_prepared_contig_range(struct page *page,
- unsigned long nr_pages)
+static void __free_prepared_contig_range(struct page *page,
+ unsigned long nr_pages, fpi_t fpi_flags)
{
unsigned long pfn = page_to_pfn(page);
@@ -6869,8 +6922,15 @@ static void free_prepared_contig_range(struct page *page,
/*
* Free the chunk as a single block. Our caller has already
* called free_pages_prepare() for each order-0 page.
+ *
+ * If the original compound page has HWPoison page,
+ * free_pages_prepare() has to skip sanitize at that time,
+ * but now it is good time to do that.
*/
- __free_frozen_pages(page, order, FPI_PREPARED);
+ if (fpi_flags & FPI_SANITIZE)
+ free_pages_sanitize(page, order);
+
+ __free_frozen_pages(page, order, fpi_flags | FPI_PREPARED);
pfn += 1UL << order;
page += 1UL << order;
@@ -6878,6 +6938,11 @@ static void free_prepared_contig_range(struct page *page,
}
}
+static void free_prepared_contig_range(struct page *page, unsigned long nr_pages)
+{
+ __free_prepared_contig_range(page, nr_pages, FPI_NONE);
+}
+
static void __free_contig_range_common(unsigned long pfn, unsigned long nr_pages,
bool is_frozen)
{
@@ -6951,6 +7016,61 @@ void __free_contig_range(unsigned long pfn, unsigned long nr_pages)
__free_contig_range_common(pfn, nr_pages, /* is_frozen= */ false);
}
+/*
+ * Given some contiguous pages that have certain number of HWPoison page(s),
+ * free only the healthy ones.
+ *
+ * Used at the end of __free_pages_prepare(). Even if having HWPoison pages,
+ * breaking down compound page and clearing metadata (e.g. page owner, alloc
+ * tag) can be done together during __free_pages_prepare(), which simplifies
+ * the splitting here: unlike __split_unmapped_folio(), there is no need to
+ * turn split pages into a compound page or to carry metadata.
+ *
+ * It scans every raw page of the compound page and causes nontrivial overhead.
+ * So only use this when the compound page contains HWPoison page(s).
+ *
+ * It also works when order == 0, regardless of PageHWPoison() or not.
+ *
+ * This implementation needs rework in memdesc world.
+ */
+static void free_has_hwpoisoned(struct page *page, unsigned int order,
+ fpi_t fpi_flags)
+{
+ unsigned long curr = page_to_pfn(page);
+ unsigned long end_pfn = curr + (1 << order);
+ unsigned long next;
+ unsigned long total_freed = 0;
+ unsigned long total_hwp = 0;
+
+ while (curr < end_pfn) {
+ next = curr;
+
+ while (next < end_pfn && !PageHWPoison(pfn_to_page(next)))
+ ++next;
+
+ if (next != end_pfn) {
+ /*
+ * Avoid accounting error when the page is freed
+ * by unpoison_memory().
+ */
+ clear_page_tag_ref(pfn_to_page(next));
+ ++total_hwp;
+ }
+
+ __free_prepared_contig_range(pfn_to_page(curr), next - curr,
+ fpi_flags);
+ total_freed += next - curr;
+
+ if (next == end_pfn)
+ break;
+
+ curr = next + 1;
+ }
+
+ pr_info("Freed %#lx pages, excluded %#lx HWPoison pages\n",
+ total_freed, total_hwp);
+}
+
#ifdef CONFIG_CONTIG_ALLOC
/* Usage: See admin-guide/dynamic-debug-howto.rst */
static void alloc_contig_dump_pages(struct list_head *page_list)
diff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/selftests/mm/memory-failure.c
index 032ed952057c62..78c105ce2af185 100644
--- a/tools/testing/selftests/mm/memory-failure.c
+++ b/tools/testing/selftests/mm/memory-failure.c
@@ -18,6 +18,7 @@
#include <linux/magic.h>
#include <errno.h>
+#include "hugepage_settings.h"
#include "vm_util.h"
enum inject_type {
@@ -27,6 +28,7 @@ enum inject_type {
enum result_type {
MADV_HARD_ANON,
+ MADV_HARD_ANON_HUGETLB,
MADV_HARD_CLEAN_PAGECACHE,
MADV_HARD_DIRTY_PAGECACHE,
MADV_SOFT_ANON,
@@ -47,6 +49,8 @@ FIXTURE(memory_failure)
int pagemap_fd;
int kpageflags_fd;
bool triggered;
+ /* Number of initial HugeTLB pages with default page size. */
+ unsigned long nr_hugetlb_pages;
};
FIXTURE_VARIANT(memory_failure)
@@ -157,6 +161,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
void *vaddr, enum result_type type, int setjmp)
{
unsigned long size;
+ unsigned long nr_hugetlb_pages;
uint64_t pfn_flags;
switch (type) {
@@ -174,6 +179,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
ASSERT_NE(pagemap_get_pfn(self->pagemap_fd, vaddr), self->pfn);
break;
case MADV_HARD_ANON:
+ case MADV_HARD_ANON_HUGETLB:
case MADV_HARD_DIRTY_PAGECACHE:
/* The SIGBUS signal should have been received. */
ASSERT_EQ(setjmp, 1);
@@ -184,8 +190,15 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
ASSERT_EQ(1UL << siginfo.si_addr_lsb, self->page_size);
ASSERT_EQ(siginfo.si_addr, vaddr);
- /* XXX Check backing pte is hwpoison entry when supported. */
- ASSERT_TRUE(pagemap_is_swapped(self->pagemap_fd, vaddr));
+ if (type != MADV_HARD_ANON_HUGETLB)
+ /*
+ * Check backing pte is hwpoison entry when supported.
+ * Although try_to_unmap_one() also installs hwpoison
+ * entry for HugeTLB, pagemap_hugetlb_range() doesn't
+ * parse swap entries at all.
+ */
+ ASSERT_TRUE(pagemap_is_swapped(self->pagemap_fd, vaddr));
+
break;
default:
SKIP(return, "unexpected inject type %d.\n", type);
@@ -193,7 +206,19 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
/* Check if the value of HardwareCorrupted has increased. */
ASSERT_EQ(get_hardware_corrupted_size(&size), 0);
- ASSERT_EQ(size, self->corrupted_size + self->page_size / 1024);
+
+ if (type == MADV_HARD_ANON_HUGETLB) {
+ /*
+ * Only one page is hardware corrupted; the rest should all be
+ * released to buddy allocator.
+ */
+ ASSERT_EQ(size, self->corrupted_size + getpagesize() / 1024);
+ /* HugeTLB should have lost the HWPoison HugeTLB page. */
+ nr_hugetlb_pages = hugetlb_nr_default_pages();
+ ASSERT_EQ(nr_hugetlb_pages + 1, self->nr_hugetlb_pages);
+ } else {
+ ASSERT_EQ(size, self->corrupted_size + self->page_size / 1024);
+ }
/* Check if HWPoison flag is set. */
ASSERT_EQ(pageflags_get(self->pfn, self->kpageflags_fd, &pfn_flags), 0);
@@ -247,6 +272,45 @@ TEST_F(memory_failure, anon)
ASSERT_EQ(munmap(addr, self->page_size), 0);
}
+TEST_F(memory_failure, anon_hugetlb)
+{
+ char *addr;
+ int ret;
+ const unsigned long nr_alloc_hugetlb_pages = 4;
+ unsigned long alloc_size;
+
+ if (variant->type == MADV_SOFT)
+ SKIP(return, "Soft offline test is not implemented");
+
+ /* HugeTLB settings will be automatically restored when test exits. */
+ hugetlb_setup_default(nr_alloc_hugetlb_pages);
+
+ alloc_size = default_huge_page_size() * nr_alloc_hugetlb_pages;
+ self->page_size = default_huge_page_size();
+ self->nr_hugetlb_pages = hugetlb_nr_default_pages();
+
+ addr = mmap(0, alloc_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0);
+ if (addr == MAP_FAILED)
+ SKIP(return, "mmap failed, not enough memory or hugetlb not supported.\n");
+ memset(addr, 0xce, alloc_size);
+
+ prepare(_metadata, self, addr);
+
+ ret = sigsetjmp(signal_jmp_buf, 1);
+ if (!self->triggered) {
+ self->triggered = true;
+ ASSERT_EQ(variant->inject(self, addr), 0);
+ FORCE_READ(*addr);
+ }
+
+ check(_metadata, self, addr, MADV_HARD_ANON_HUGETLB, ret);
+
+ cleanup(_metadata, self, addr);
+
+ ASSERT_EQ(munmap(addr, alloc_size), 0);
+}
+
static int prepare_file(const char *fname, unsigned long size)
{
int fd;
|
| 3/2 | 2026/07/05 18:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7538 output: 21 thoughts: 630 Thoughts: **Analyzing a Kernel Patch for Fuzzing Relevance** Okay, so I've got this patch in front of me, and my immediate concern is whether it introduces functional changes significant enough to warrant fuzzing. The files touched are: `include/linux/page-flags.h`, `mm/memory-failure.c`, `mm/page_alloc.c`, and `tools/testing/selftests/mm/memory-failure.c`. That's already telling - the core memory management subsystem is being tweaked. Looking closer: 1. In `include/linux/page-flags.h`, it looks like a conditional preprocessor macro, `PageHasHWPoisoned`, has been updated to account for both `CONFIG_HUGETLB_PAGE` and `CONFIG_TRANSPARENT_HUGEPAGE`, not just the latter. 2. `mm/memory-failure.c` is seeing some significant additions and modifications. There's a new function, `__hugepage_handle_poison`, which appears to be handling poisoned huge pages. It calls `dissolve_free_hugetlb_folio()` and replaces calls to `__page_handle_poison()`. Plus, `folio_clear_hugetlb_hwpoison()` now calls `folio_set_has_hwpoisoned()`. All of this screams HugeTLB and memory failure interaction. 3. `mm/page_alloc.c` is getting even more interesting. It's adding an `FPI_SANITIZE` flag, and a new `free_pages_sanitize()` function. The existing `__free_pages_prepare()` now checks for `folio_test_has_hwpoisoned()` and calls `free_has_hwpoisoned()`, which looks like it's specifically dealing with freeing pages that are marked as having HWPoisoned pages, skipping the poisoned ones and only freeing healthy ones. Additionally, `free_prepared_contig_range()` now conditionally calls `free_pages_sanitize()`. 4. Finally, `tools/testing/selftests/mm/memory-failure.c` adds a new test called `anon_hugetlb`. This patch is undeniably a functional change, directly impacting how the kernel handles memory failures, specifically related to HWPoison and HugeTLB pages. Given the scope of changes, it warrants a closer look, especially focusing on `CONFIG_MEMORY_FAILURE`, `CONFIG_HUGETLB_PAGE`, and `CONFIG_TRANSPARENT_HUGEPAGE`. I'm particularly interested in `__hugepage_handle_poison` (in `mm/memory-failure.c`) and `free_has_hwpoisoned` (in `mm/page_alloc.c`). I need to check how these functions are reachable, so I'll be using `default_api:grepper` to see where they are called and if they are relevant to fuzzing. |
| 4/2 | 2026/07/05 18:21 | tool | grepper |
0mArgs:{
"Expression": "__hugepage_handle_poison"
}Results: {
"Output": "mm/memory-failure.c=174=static DEFINE_MUTEX(pfn_space_lock);\n--\nmm/memory-failure.c-181- */\nmm/memory-failure.c:182:static int __hugepage_handle_poison(struct page *page)\nmm/memory-failure.c-183-{\n--\nmm/memory-failure.c=1172=static int me_huge_page(struct page_state *ps, struct page *p)\n--\nmm/memory-failure.c-1192-\t\tfolio_put(folio);\nmm/memory-failure.c:1193:\t\tif (__hugepage_handle_poison(p)) {\nmm/memory-failure.c-1194-\t\t\tres = MF_FAILED;\n--\nmm/memory-failure.c=2107=static int try_memory_failure_hugetlb(unsigned long pfn, int flags)\n--\nmm/memory-failure.c-2159-\t\tfolio_unlock(folio);\nmm/memory-failure.c:2160:\t\tif (__hugepage_handle_poison(p)) {\nmm/memory-failure.c-2161-\t\t\tres = MF_FAILED;\n"
} |
| 5/2 | 2026/07/05 18:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9128 output: 21 thoughts: 146 Thoughts: **Analyzing Memory Failure Handling** Okay, so I'm diving into this memory failure handling code. It looks like `me_huge_page` and `try_memory_failure_hugetlb` are key players here. I'm focusing on how the system responds when memory errors are detected. Now, `free_has_hwpoisoned` is intriguing. It's called from `__free_pages_prepare`, which is a *very* hot path – that's something I need to pay attention to because performance matters. However, `free_has_hwpoisoned` itself is only executed when `should_fhh` is true. `should_fhh` seems to be conditional on a `compound` page and the `folio_test_has_hwpoisoned(folio)` result. That means it only gets called when dealing with compounds with hardware poisoning. Given its context, `free_has_hwpoisoned` seems like a prime target for deeper investigation, a good focus symbol. Also, I should definitely check `__hugepage_handle_poison` alongside it. Right now, I want to start by examining `free_has_hwpoisoned` closely. Let's see what's actually going on there. |
| 6/2 | 2026/07/05 18:21 | tool | grepper |
0mArgs:{
"Expression": "free_has_hwpoisoned"
}Results: {
"Output": "mm/page_alloc.c=217=unsigned int pageblock_order __read_mostly;\n--\nmm/page_alloc.c-219-\nmm/page_alloc.c:220:static void free_has_hwpoisoned(struct page *page, unsigned int order,\nmm/page_alloc.c-221-\t\t\t\tfpi_t fpi_flags);\n--\nmm/page_alloc.c=1375=static __always_inline bool __free_pages_prepare(struct page *page,\n--\nmm/page_alloc.c-1494-\tif (should_fhh) {\nmm/page_alloc.c:1495:\t\tfree_has_hwpoisoned(page, order, fpi_flags | FPI_SANITIZE);\nmm/page_alloc.c-1496-\t\treturn false;\n--\nmm/page_alloc.c=7014=void __free_contig_range(unsigned long pfn, unsigned long nr_pages)\n--\nmm/page_alloc.c-7035- */\nmm/page_alloc.c:7036:static void free_has_hwpoisoned(struct page *page, unsigned int order,\nmm/page_alloc.c-7037-\t\t\t\tfpi_t fpi_flags)\n"
} |
| 7/2 | 2026/07/05 18:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9890 output: 24 thoughts: 54 Thoughts: Here's my analysis of those function calls: **Dissecting Hugepage Management** Okay, so I'm digging into how hugepages are handled. My initial focus is on the `__hugepage_handle_poison` function. I need to understand its role, and it looks like it's crucial, considering it's called from two key locations. Specifically, `me_huge_page` and `try_memory_failure_hugetlb`. That tells me it's likely involved in error handling or validation related to hugepages, maybe dealing with potentially corrupted memory regions. I need to look closer at what those callers do to get the full picture. Next, I'll turn my attention to `dissolve_free_hugetlb_folio`. Based on the name, it seems related to releasing or deallocating hugepage folios, which is the higher-level representation of a hugepage. The "dissolve" part suggests it might be handling fragmentation or combining multiple smaller regions. I expect this function has interesting interaction with the page reclaim subsystem. I'll need to see how it fits into the broader allocation and deallocation lifecycle of hugepages. |
| 8/2 | 2026/07/05 18:21 | tool | grepper |
0mArgs:{
"Expression": "dissolve_free_hugetlb_folio"
}Results: {
"Output": "include/linux/hugetlb.h=880=static inline int hstate_index(struct hstate *h)\n--\ninclude/linux/hugetlb.h-884-\ninclude/linux/hugetlb.h:885:int dissolve_free_hugetlb_folio(struct folio *folio);\ninclude/linux/hugetlb.h:886:int dissolve_free_hugetlb_folios(unsigned long start_pfn,\ninclude/linux/hugetlb.h-887-\t\t\t\t unsigned long end_pfn);\n--\ninclude/linux/hugetlb.h=1214=static inline int hstate_index(struct hstate *h)\n--\ninclude/linux/hugetlb.h-1218-\ninclude/linux/hugetlb.h:1219:static inline int dissolve_free_hugetlb_folio(struct folio *folio)\ninclude/linux/hugetlb.h-1220-{\n--\ninclude/linux/hugetlb.h-1223-\ninclude/linux/hugetlb.h:1224:static inline int dissolve_free_hugetlb_folios(unsigned long start_pfn,\ninclude/linux/hugetlb.h-1225-\t\t\t\t\t unsigned long end_pfn)\n--\nmm/hugetlb.c=1906=static struct folio *remove_pool_hugetlb_folio(struct hstate *h,\n--\nmm/hugetlb.c-1943- */\nmm/hugetlb.c:1944:int dissolve_free_hugetlb_folio(struct folio *folio)\nmm/hugetlb.c-1945-{\n--\nmm/hugetlb.c-2027- * part of it lies within the given range.\nmm/hugetlb.c:2028: * Also note that if dissolve_free_hugetlb_folio() returns with an error, all\nmm/hugetlb.c-2029- * free hugetlb folios that were dissolved before that error are lost.\nmm/hugetlb.c-2030- */\nmm/hugetlb.c:2031:int dissolve_free_hugetlb_folios(unsigned long start_pfn, unsigned long end_pfn)\nmm/hugetlb.c-2032-{\n--\nmm/hugetlb.c-2047-\t\tfolio = pfn_folio(pfn);\nmm/hugetlb.c:2048:\t\trc = dissolve_free_hugetlb_folio(folio);\nmm/hugetlb.c-2049-\t\tif (rc)\n--\nmm/memory-failure.c=174=static DEFINE_MUTEX(pfn_space_lock);\n--\nmm/memory-failure.c-178- * difference to soft_offline() is that, no HWPoison subpage will make\nmm/memory-failure.c:179: * into buddy allocator after a successful dissolve_free_hugetlb_folio(),\nmm/memory-failure.c-180- * so take_page_off_buddy() is unnecessary.\n--\nmm/memory-failure.c=182=static int __hugepage_handle_poison(struct page *page)\n--\nmm/memory-failure.c-186-\t/*\nmm/memory-failure.c:187:\t * Can't use dissolve_free_hugetlb_folio() without a reliable\nmm/memory-failure.c-188-\t * raw_hwp_list telling which subpage is HWPoison. So do not free\n--\nmm/memory-failure.c-195-\nmm/memory-failure.c:196:\treturn dissolve_free_hugetlb_folio(folio);\nmm/memory-failure.c-197-}\n--\nmm/memory-failure.c=206=static int __page_handle_poison(struct page *page)\n--\nmm/memory-failure.c-210-\tzone_pcp_disable(page_zone(page));\nmm/memory-failure.c:211:\tret = dissolve_free_hugetlb_folio(page_folio(page));\nmm/memory-failure.c-212-\tif (!ret)\n--\nmm/memory-failure.c=219=static bool page_handle_poison(struct page *page, bool hugepage_or_freepage, bool release)\n--\nmm/memory-failure.c-223-\t\t * Doing this check for free pages is also fine since\nmm/memory-failure.c:224:\t\t * dissolve_free_hugetlb_folio() returns 0 for non-hugetlb folios as well.\nmm/memory-failure.c-225-\t\t */\n--\nmm/memory_hotplug.c=1903=int offline_pages(unsigned long start_pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-2034-\t\t */\nmm/memory_hotplug.c:2035:\t\tret = dissolve_free_hugetlb_folios(start_pfn, end_pfn);\nmm/memory_hotplug.c-2036-\t\tif (ret) {\n"
} |
| 9/2 | 2026/07/05 18:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11498 output: 19 thoughts: 116 Thoughts: **Analyzing the Impact of the Memory Management Patch** Okay, so this patch is definitely functional – it's not just a refactor. It's touching some core memory management bits. It seems to be changing the behavior of the memory failure handling, particularly around how we deal with huge pages. It's also making adjustments to the buddy allocator and how it preps pages for freeing, specifically when hardware-poisoned subpages are involved. That's a critical area, we need to be very careful that this doesn't introduce any regressions. The build dependencies look pretty clear. It's expecting `MEMORY_FAILURE` enabled, which is standard when we're dealing with this level of hardware-related issues. The presence of `HUGETLB_PAGE` is also expected, since we're clearly dealing with huge pages, and that will need to be well-defined. Then, there is the potentially conditional dependency on `TRANSPARENT_HUGEPAGE`, which seems likely, given the `#if` statement. Of course, the logic is handling the OR case of either of those. I'm going to jump into `mm/memory-failure.c` and take a look at the Kconfig file. I need to understand the exact dependencies and any potential configuration conflicts before I start looking at the code itself. The Kconfig will tell the story of the architecture's configuration needs, and give clues about the overall impact of the patch. |
| 10/2 | 2026/07/05 18:21 | tool | grepper |
0mArgs:{
"Expression": "config MEMORY_FAILURE"
}Results: {
"Output": "mm/Kconfig=774=config ARCH_SUPPORTS_MEMORY_FAILURE\n--\nmm/Kconfig-776-\nmm/Kconfig:777:config MEMORY_FAILURE\nmm/Kconfig-778-\tdepends on MMU\n"
} |
| 11/2 | 2026/07/05 18:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12009 output: 20 thoughts: 14 Thoughts: Here's a summary of my thought process: **Diving into `HUGETLB_PAGE`** Okay, time to dig into the `HUGETLB_PAGE` Kconfig option. I need to refresh my understanding of how this is configured and how it interacts with the rest of the system. I should probably start by quickly verifying its dependencies and what architectures are supported. A quick glance at the relevant documentation would be a good idea to confirm I'm on the right track before going any further. This is a critical building block for large memory pages, so understanding the specifics is important for memory management and performance tuning. I also should consider the implications of enabling or disabling this feature in relation to other settings. |
| 12/2 | 2026/07/05 18:21 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLB_PAGE"
}Results: {
"Output": "arch/sh/mm/Kconfig=158=choice\n--\narch/sh/mm/Kconfig-163-\narch/sh/mm/Kconfig:164:config HUGETLB_PAGE_SIZE_64K\narch/sh/mm/Kconfig-165-\tbool \"64kB\"\n--\narch/sh/mm/Kconfig-167-\narch/sh/mm/Kconfig:168:config HUGETLB_PAGE_SIZE_256K\narch/sh/mm/Kconfig-169-\tbool \"256kB\"\n--\narch/sh/mm/Kconfig-171-\narch/sh/mm/Kconfig:172:config HUGETLB_PAGE_SIZE_1MB\narch/sh/mm/Kconfig-173-\tbool \"1MB\"\narch/sh/mm/Kconfig-174-\narch/sh/mm/Kconfig:175:config HUGETLB_PAGE_SIZE_4MB\narch/sh/mm/Kconfig-176-\tbool \"4MB\"\n--\narch/sh/mm/Kconfig-178-\narch/sh/mm/Kconfig:179:config HUGETLB_PAGE_SIZE_64MB\narch/sh/mm/Kconfig-180-\tbool \"64MB\"\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\"\n--\nfs/Kconfig=271=endif # HUGETLBFS\nfs/Kconfig-272-\nfs/Kconfig:273:config HUGETLB_PAGE\nfs/Kconfig-274-\tdef_bool HUGETLBFS\n--\nfs/Kconfig-276-\nfs/Kconfig:277:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nfs/Kconfig-278-\tdef_bool HUGETLB_PAGE\n--\nmm/Kconfig=706=config ARCH_SUPPORTS_PMD_SOFTLEAF\n--\nmm/Kconfig-708-\nmm/Kconfig:709:config HUGETLB_PAGE_SIZE_VARIABLE\nmm/Kconfig-710-\tdef_bool n\n"
} |
| 13/2 | 2026/07/05 18:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12788 output: 19 thoughts: 26 Thoughts: Here's my summary, as I see it: **Digging into Huge Pages** Okay, so `HUGETLB_PAGE` is the feature we're interested in, and I know it relies on `HUGETLBFS`. Right. Makes sense. To really understand the whole picture, I need to verify that dependency and then, naturally, dive into `HUGETLBFS` itself. Time to start there. Let's see what's what with the underlying file system. Need to understand how this is all tied together. |
| 14/2 | 2026/07/05 18:22 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLBFS"
}Results: {
"Output": "fs/Kconfig=247=config ARCH_SUPPORTS_HUGETLBFS\n--\nfs/Kconfig-249-\nfs/Kconfig:250:menuconfig HUGETLBFS\nfs/Kconfig-251-\tbool \"HugeTLB file system support\"\n"
} |
| 15/2 | 2026/07/05 18:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13027 output: 22 thoughts: 35 Thoughts: Okay, here's my take on those config options, thinking about them as I would if I were considering them: **Navigating the NUMA Landscape: Configuring Huge Pages and Memory Management** Alright, so I'm looking at how memory is managed, and specifically how we can best leverage huge pages. It looks like the key starting points are going to be `HUGETLBFS` to actually enable the huge page file system, and then also `MEMORY_FAILURE` - likely to handle cases where allocation fails, which is important for any robust system. But before diving too deep, I need to check something critical to modern performance: `TRANSPARENT_HUGEPAGE`. I need to understand whether we can rely on the system automatically managing the creation and use of huge pages, or if we need to be more hands-on with the configuration to achieve optimal results. It's a trade-off between convenience and control, and figuring out what the ideal balance is in *this* specific context is the next step. |
| 16/2 | 2026/07/05 18:22 | tool | grepper |
0mArgs:{
"Expression": "config TRANSPARENT_HUGEPAGE"
}Results: {
"Output": "mm/Kconfig=843=config MM_ID\n--\nmm/Kconfig-845-\nmm/Kconfig:846:menuconfig TRANSPARENT_HUGEPAGE\nmm/Kconfig-847-\tbool \"Transparent Hugepage Support\"\n--\nmm/Kconfig=864=choice\n--\nmm/Kconfig-869-\nmm/Kconfig:870:\tconfig TRANSPARENT_HUGEPAGE_ALWAYS\nmm/Kconfig-871-\t\tbool \"always\"\n--\nmm/Kconfig-876-\nmm/Kconfig:877:\tconfig TRANSPARENT_HUGEPAGE_MADVISE\nmm/Kconfig-878-\t\tbool \"madvise\"\n--\nmm/Kconfig-885-\nmm/Kconfig:886:\tconfig TRANSPARENT_HUGEPAGE_NEVER\nmm/Kconfig-887-\t\tbool \"never\"\n--\nmm/Kconfig=893=choice\n--\nmm/Kconfig-902-\nmm/Kconfig:903:\tconfig TRANSPARENT_HUGEPAGE_SHMEM_HUGE_NEVER\nmm/Kconfig-904-\t\tbool \"never\"\n--\nmm/Kconfig-912-\nmm/Kconfig:913:\tconfig TRANSPARENT_HUGEPAGE_SHMEM_HUGE_ALWAYS\nmm/Kconfig-914-\t\tbool \"always\"\n--\nmm/Kconfig-920-\nmm/Kconfig:921:\tconfig TRANSPARENT_HUGEPAGE_SHMEM_HUGE_WITHIN_SIZE\nmm/Kconfig-922-\t\tbool \"within_size\"\n--\nmm/Kconfig-928-\nmm/Kconfig:929:\tconfig TRANSPARENT_HUGEPAGE_SHMEM_HUGE_ADVISE\nmm/Kconfig-930-\t\tbool \"advise\"\n--\nmm/Kconfig=938=choice\n--\nmm/Kconfig-947-\nmm/Kconfig:948:\tconfig TRANSPARENT_HUGEPAGE_TMPFS_HUGE_NEVER\nmm/Kconfig-949-\t\tbool \"never\"\n--\nmm/Kconfig-956-\nmm/Kconfig:957:\tconfig TRANSPARENT_HUGEPAGE_TMPFS_HUGE_ALWAYS\nmm/Kconfig-958-\t\tbool \"always\"\n--\nmm/Kconfig-964-\nmm/Kconfig:965:\tconfig TRANSPARENT_HUGEPAGE_TMPFS_HUGE_WITHIN_SIZE\nmm/Kconfig-966-\t\tbool \"within_size\"\n--\nmm/Kconfig-972-\nmm/Kconfig:973:\tconfig TRANSPARENT_HUGEPAGE_TMPFS_HUGE_ADVISE\nmm/Kconfig-974-\t\tbool \"advise\"\n"
} |
| 17/2 | 2026/07/05 18:22 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14010 output: 143 thoughts: 376 Thoughts: **My Analysis of the Memory Failure Handling Patch** Okay, so I'm looking at this patch related to memory failure handling, specifically for HugeTLB pages. The configurations involved are `MEMORY_FAILURE`, `HUGETLBFS`, and `TRANSPARENT_HUGEPAGE`. I need to understand the core changes here. My focus should be on how the system is dealing with hardware-poisoned subpages within compound pages. I see two key symbols jumping out at me: `__hugepage_handle_poison` and `free_has_hwpoisoned`. These seem to be tied directly to the error paths, which is exactly where my attention should be. I also see that `free_pages_sanitize` has been added. However, since it is called from `__free_pages_prepare` and `free_prepared_contig_range`, it’s likely in a hot path. While important, the changes here are perhaps not the core of *this* patch, though I'll keep that in mind. `free_has_hwpoisoned` seems more targeted to the specific error handling scenarios, as does `__hugepage_handle_poison`. Now, I should examine `me_huge_page`, `try_memory_failure_hugetlb`, and `folio_clear_hugetlb_hwpoison`. They're existing functions, but they've been modified. The fact that they already exist suggests this isn't entirely new functionality, but an enhancement or refinement. In essence, the patch is addressing how to properly manage healthy versus poisoned subpages within compound HugeTLB pages when memory failures occur. The goal is to ensure that non-poisoned subpages are correctly freed back to the buddy allocator. This also means we need to isolate the poisoned ones. This is a core MM and page allocator issue, and the focus seems to be in `mm/memory-failure.c` and `mm/page_alloc.c`, as I'd expect. So, in summary, I'm concentrating on `__hugepage_handle_poison` and `free_has_hwpoisoned` as the best points to analyze the patch’s core logic, as well as the changes to `me_huge_page`, `try_memory_failure_hugetlb`, and `folio_clear_hugetlb_hwpoison`. I need to see exactly how these functions interact to achieve this more granular and robust handling of hardware-poisoned pages. Let's call `set-results`. |
| 18/2 | 2026/07/05 18:22 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"MEMORY_FAILURE",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE"
],
"FocusSymbols": [
"__hugepage_handle_poison",
"free_has_hwpoisoned"
],
"Reasoning": "The patch modifies memory failure handling for HugeTLB pages, ensuring that healthy subpages of a compound page with hardware poisoned subpages are correctly freed to the buddy allocator, while isolating the poisoned ones. This involves functional changes to core MM and page allocator logic, specifically in mm/memory-failure.c and mm/page_alloc.c.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"MEMORY_FAILURE",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE"
],
"FocusSymbols": [
"__hugepage_handle_poison",
"free_has_hwpoisoned"
],
"Reasoning": "The patch modifies memory failure handling for HugeTLB pages, ensuring that healthy subpages of a compound page with hardware poisoned subpages are correctly freed to the buddy allocator, while isolating the poisoned ones. This involves functional changes to core MM and page allocator logic, specifically in mm/memory-failure.c and mm/page_alloc.c.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|