AI Trajectory


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

Results:
{
  "EnableConfigs": [
    "MEMORY_FAILURE",
    "HWPOISON_INJECT",
    "HUGETLBFS",
    "HUGETLB_PAGE",
    "TRANSPARENT_HUGEPAGE"
  ],
  "FocusSymbols": [
    "hugetlb_page_hwpoison",
    "hugetlb_unref_page_hwpoison",
    "unpoison_memory",
    "folio_clear_hugetlb_hwpoison"
  ],
  "Reasoning": "The patch modifies how hardware poisoned (HWPOISON) pages are tracked and handled within large folios, specifically affecting hugetlbfs and filemap read operations. It introduces new logic to track poisoned pages using `folio-\u003ehugetlb_hwpoison` and replaces the custom `hugetlbfs_read_iter` with `generic_file_read_iter`, handling hwpoison directly in `filemap_read`. These are reachable, functional changes in core memory management and hugetlbfs. Fuzzing should focus on the new hwpoison tracking functions and unpoisoning logic.",
  "WorthFuzzing": true
}

1/1 2026/07/24 18:46 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 0fe6de6c2f057664217d96a3da772a48667e5567\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Jul 24 18:46:16 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/fs/Kconfig b/fs/Kconfig\nindex cf6ae64776e62c..eddac4ed214b12 100644\n--- a/fs/Kconfig\n+++ b/fs/Kconfig\n@@ -272,7 +272,7 @@ endif # HUGETLBFS\n \n config HUGETLB_PAGE\n \tdef_bool HUGETLBFS\n-\tselect XARRAY_MULTI\n+\tselect LARGE_FOLIO\n \n config HUGETLB_PAGE_OPTIMIZE_VMEMMAP\n \tdef_bool HUGETLB_PAGE\ndiff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c\nindex 216e1a0dd0b237..cef7e61b827d71 100644\n--- a/fs/hugetlbfs/inode.c\n+++ b/fs/hugetlbfs/inode.c\n@@ -187,113 +187,6 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,\n \treturn mm_get_unmapped_area_vmflags(file, addr0, len, pgoff, flags, 0);\n }\n \n-/*\n- * Someone wants to read @bytes from a HWPOISON hugetlb @folio from @offset.\n- * Returns the maximum number of bytes one can read without touching the 1st raw\n- * HWPOISON page.\n- */\n-static size_t adjust_range_hwpoison(struct folio *folio, size_t offset,\n-\t\tsize_t bytes)\n-{\n-\tstruct page *page = folio_page(folio, offset / PAGE_SIZE);\n-\tsize_t safe_bytes;\n-\n-\tif (is_raw_hwpoison_page_in_hugepage(page))\n-\t\treturn 0;\n-\t/* Safe to read the remaining bytes in this page. */\n-\tsafe_bytes = PAGE_SIZE - (offset % PAGE_SIZE);\n-\tpage++;\n-\n-\t/* Check each remaining page as long as we are not done yet. */\n-\tfor (; safe_bytes \u003c bytes; safe_bytes += PAGE_SIZE, page++)\n-\t\tif (is_raw_hwpoison_page_in_hugepage(page))\n-\t\t\tbreak;\n-\n-\treturn min(safe_bytes, bytes);\n-}\n-\n-/*\n- * Support for read() - Find the page attached to f_mapping and copy out the\n- * data. This provides functionality similar to filemap_read().\n- */\n-static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)\n-{\n-\tstruct file *file = iocb-\u003eki_filp;\n-\tstruct hstate *h = hstate_file(file);\n-\tstruct address_space *mapping = file-\u003ef_mapping;\n-\tstruct inode *inode = mapping-\u003ehost;\n-\tunsigned long index = iocb-\u003eki_pos \u003e\u003e huge_page_shift(h);\n-\tunsigned long offset = iocb-\u003eki_pos \u0026 ~huge_page_mask(h);\n-\tunsigned long end_index;\n-\tloff_t isize;\n-\tssize_t retval = 0;\n-\n-\twhile (iov_iter_count(to)) {\n-\t\tstruct folio *folio;\n-\t\tsize_t nr, copied, want;\n-\n-\t\t/* nr is the maximum number of bytes to copy from this page */\n-\t\tnr = huge_page_size(h);\n-\t\tisize = i_size_read(inode);\n-\t\tif (!isize)\n-\t\t\tbreak;\n-\t\tend_index = (isize - 1) \u003e\u003e huge_page_shift(h);\n-\t\tif (index \u003e end_index)\n-\t\t\tbreak;\n-\t\tif (index == end_index) {\n-\t\t\tnr = ((isize - 1) \u0026 ~huge_page_mask(h)) + 1;\n-\t\t\tif (nr \u003c= offset)\n-\t\t\t\tbreak;\n-\t\t}\n-\t\tnr = nr - offset;\n-\n-\t\t/* Find the folio */\n-\t\tfolio = filemap_lock_hugetlb_folio(h, mapping, index);\n-\t\tif (IS_ERR(folio)) {\n-\t\t\t/*\n-\t\t\t * We have a HOLE, zero out the user-buffer for the\n-\t\t\t * length of the hole or request.\n-\t\t\t */\n-\t\t\tcopied = iov_iter_zero(nr, to);\n-\t\t} else {\n-\t\t\tfolio_unlock(folio);\n-\n-\t\t\tif (!folio_test_hwpoison(folio))\n-\t\t\t\twant = nr;\n-\t\t\telse {\n-\t\t\t\t/*\n-\t\t\t\t * Adjust how many bytes safe to read without\n-\t\t\t\t * touching the 1st raw HWPOISON page after\n-\t\t\t\t * offset.\n-\t\t\t\t */\n-\t\t\t\twant = adjust_range_hwpoison(folio, offset, nr);\n-\t\t\t\tif (want == 0) {\n-\t\t\t\t\tfolio_put(folio);\n-\t\t\t\t\tretval = -EIO;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\t/*\n-\t\t\t * We have the folio, copy it to user space buffer.\n-\t\t\t */\n-\t\t\tcopied = copy_folio_to_iter(folio, offset, want, to);\n-\t\t\tfolio_put(folio);\n-\t\t}\n-\t\toffset += copied;\n-\t\tretval += copied;\n-\t\tif (copied != nr \u0026\u0026 iov_iter_count(to)) {\n-\t\t\tif (!retval)\n-\t\t\t\tretval = -EFAULT;\n-\t\t\tbreak;\n-\t\t}\n-\t\tindex += offset \u003e\u003e huge_page_shift(h);\n-\t\toffset \u0026= ~huge_page_mask(h);\n-\t}\n-\tiocb-\u003eki_pos = ((loff_t)index \u003c\u003c huge_page_shift(h)) + offset;\n-\treturn retval;\n-}\n-\n static int hugetlbfs_write_begin(const struct kiocb *iocb,\n \t\t\tstruct address_space *mapping,\n \t\t\tloff_t pos, unsigned len,\n@@ -883,6 +776,17 @@ static struct inode *hugetlbfs_get_root(struct super_block *sb,\n \treturn inode;\n }\n \n+static void hugetlbfs_init_regular_inode(struct inode *inode)\n+{\n+\tstruct hstate *hstate = hstate_inode(inode);\n+\tunsigned int order = huge_page_order(hstate);\n+\n+\tinode-\u003ei_op = \u0026hugetlbfs_inode_operations;\n+\tinode-\u003ei_fop = \u0026hugetlbfs_file_operations;\n+\tmapping_set_folio_order_range(inode-\u003ei_mapping, order, order);\n+\tmapping_set_authoritative(inode-\u003ei_mapping);\n+}\n+\n /*\n  * Hugetlbfs is not reclaimable; therefore its i_mmap_rwsem will never\n  * be taken from reclaim -- unlike regular filesystems. This needs an\n@@ -926,8 +830,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,\n \t\t\tinit_special_inode(inode, mode, dev);\n \t\t\tbreak;\n \t\tcase S_IFREG:\n-\t\t\tinode-\u003ei_op = \u0026hugetlbfs_inode_operations;\n-\t\t\tinode-\u003ei_fop = \u0026hugetlbfs_file_operations;\n+\t\t\thugetlbfs_init_regular_inode(inode);\n \t\t\tbreak;\n \t\tcase S_IFDIR:\n \t\t\tinode-\u003ei_op = \u0026hugetlbfs_dir_inode_operations;\n@@ -1206,7 +1109,7 @@ static void init_once(void *foo)\n }\n \n static const struct file_operations hugetlbfs_file_operations = {\n-\t.read_iter\t\t= hugetlbfs_read_iter,\n+\t.read_iter\t\t= generic_file_read_iter,\n \t.mmap\t\t\t= hugetlbfs_file_mmap,\n \t.fsync\t\t\t= noop_fsync,\n \t.get_unmapped_area\t= hugetlb_get_unmapped_area,\ndiff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h\nindex 2abaf99321e90d..1878642e75ec51 100644\n--- a/include/linux/hugetlb.h\n+++ b/include/linux/hugetlb.h\n@@ -165,7 +165,7 @@ bool hugetlbfs_pagecache_present(struct hstate *h,\n \t\t\t\t struct vm_area_struct *vma,\n \t\t\t\t unsigned long address);\n \n-struct address_space *hugetlb_folio_mapping_lock_write(struct folio *folio);\n+struct address_space *hugetlb_folio_mapping_lock_write(const struct folio *folio);\n \n extern int movable_gigantic_pages __read_mostly;\n extern int sysctl_hugetlb_shm_group __read_mostly;\n@@ -292,8 +292,7 @@ static inline unsigned long hugetlb_total_pages(void)\n \treturn 0;\n }\n \n-static inline struct address_space *hugetlb_folio_mapping_lock_write(\n-\t\t\t\t\t\t\tstruct folio *folio)\n+static inline struct address_space *hugetlb_folio_mapping_lock_write(const struct folio *folio)\n {\n \treturn NULL;\n }\n@@ -595,8 +594,8 @@ enum hugetlb_page_flags {\n #ifdef CONFIG_HUGETLB_PAGE\n #define TESTHPAGEFLAG(uname, flname)\t\t\t\t\\\n static __always_inline\t\t\t\t\t\t\\\n-bool folio_test_hugetlb_##flname(struct folio *folio)\t\t\\\n-\t{\tvoid *private = \u0026folio-\u003eprivate;\t\t\\\n+bool folio_test_hugetlb_##flname(const struct folio *folio)\t\\\n+\t{\tconst void *private = \u0026folio-\u003eprivate;\t\t\\\n \t\treturn test_bit(HPG_##flname, private);\t\t\\\n \t}\n \n@@ -616,7 +615,7 @@ void folio_clear_hugetlb_##flname(struct folio *folio)\t\t\\\n #else\n #define TESTHPAGEFLAG(uname, flname)\t\t\t\t\\\n static inline bool\t\t\t\t\t\t\\\n-folio_test_hugetlb_##flname(struct folio *folio)\t\t\\\n+folio_test_hugetlb_##flname(const struct folio *folio)\t\t\\\n \t{ return 0; }\n \n #define SETHPAGEFLAG(uname, flname)\t\t\t\t\\\n@@ -727,7 +726,7 @@ static inline struct hugepage_subpool *subpool_inode(struct inode *inode)\n \treturn HUGETLBFS_SB(inode-\u003ei_sb)-\u003espool;\n }\n \n-static inline struct hugepage_subpool *hugetlb_folio_subpool(struct folio *folio)\n+static inline struct hugepage_subpool *hugetlb_folio_subpool(const struct folio *folio)\n {\n \treturn folio-\u003e_hugetlb_subpool;\n }\n@@ -857,7 +856,7 @@ static inline bool arch_has_huge_bootmem_alloc(void)\n }\n #endif\n \n-static inline struct hstate *folio_hstate(struct folio *folio)\n+static inline struct hstate *folio_hstate(const struct folio *folio)\n {\n \tVM_BUG_ON_FOLIO(!folio_test_hugetlb(folio), folio);\n \treturn size_to_hstate(folio_size(folio));\n@@ -1070,11 +1069,6 @@ void hugetlb_register_node(struct node *node);\n void hugetlb_unregister_node(struct node *node);\n #endif\n \n-/*\n- * Check if a given raw @page in a hugepage is HWPOISON.\n- */\n-bool is_raw_hwpoison_page_in_hugepage(struct page *page);\n-\n static inline unsigned long huge_page_mask_align(struct file *file)\n {\n \treturn PAGE_MASK \u0026 ~huge_page_mask(hstate_file(file));\n@@ -1088,7 +1082,7 @@ static inline unsigned long huge_page_mask_align(struct file *file)\n \treturn 0;\n }\n \n-static inline struct hugepage_subpool *hugetlb_folio_subpool(struct folio *folio)\n+static inline struct hugepage_subpool *hugetlb_folio_subpool(const struct folio *folio)\n {\n \treturn NULL;\n }\n@@ -1157,7 +1151,7 @@ static inline struct hstate *hstate_vma(struct vm_area_struct *vma)\n \treturn NULL;\n }\n \n-static inline struct hstate *folio_hstate(struct folio *folio)\n+static inline struct hstate *folio_hstate(const struct folio *folio)\n {\n \treturn NULL;\n }\ndiff --git a/include/linux/mm_types.h b/include/linux/mm_types.h\nindex b18c2b2e7d2c65..d7a9b0d8bbdfb1 100644\n--- a/include/linux/mm_types.h\n+++ b/include/linux/mm_types.h\n@@ -387,7 +387,7 @@ typedef unsigned short mm_id_t;\n  * @_hugetlb_subpool: Do not use directly, use accessor in hugetlb.h.\n  * @_hugetlb_cgroup: Do not use directly, use accessor in hugetlb_cgroup.h.\n  * @_hugetlb_cgroup_rsvd: Do not use directly, use accessor in hugetlb_cgroup.h.\n- * @_hugetlb_hwpoison: Do not use directly, call raw_hwp_list_head().\n+ * @hugetlb_hwpoison: List of pages with hwpoison.\n  * @_deferred_list: Folios to be split under memory pressure.\n  * @_unused_slab_obj_exts: Placeholder to match obj_exts in struct slab.\n  *\n@@ -499,7 +499,7 @@ struct folio {\n \t\t\tvoid *_hugetlb_subpool;\n \t\t\tvoid *_hugetlb_cgroup;\n \t\t\tvoid *_hugetlb_cgroup_rsvd;\n-\t\t\tvoid *_hugetlb_hwpoison;\n+\t\t\tstruct llist_head hugetlb_hwpoison;\n \t/* private: the union with struct page is transitional */\n \t\t};\n \t\tstruct page __page_3;\ndiff --git a/include/linux/page-flags.h b/include/linux/page-flags.h\nindex 7223f6f4e2b403..b146c5228741c0 100644\n--- a/include/linux/page-flags.h\n+++ b/include/linux/page-flags.h\n@@ -893,14 +893,19 @@ static inline int PageTransCompound(const struct page *page)\n TESTPAGEFLAG_FALSE(TransCompound, transcompound)\n #endif\n \n-#if defined(CONFIG_MEMORY_FAILURE) \u0026\u0026 defined(CONFIG_TRANSPARENT_HUGEPAGE)\n+#if defined(CONFIG_MEMORY_FAILURE) \u0026\u0026 defined(CONFIG_LARGE_FOLIO)\n /*\n- * PageHasHWPoisoned indicates that at least one subpage is hwpoisoned in the\n- * compound page.\n+ * folio_has_hwpoisoned indicates that at least one page is hwpoisoned in the\n+ * folio.  That page will usually also have the HWPoison flag set, but this\n+ * is not possible for folios which have HVO (see memory-failure for the\n+ * scheme used in that case).  You probably don't want to call this directly;\n+ * use folio_has_hwpoisoned_page() instead.\n  *\n  * This flag is set by hwpoison handler.  Cleared by THP split or free page.\n  */\n FOLIO_FLAG(has_hwpoisoned, FOLIO_SECOND_PAGE)\n+FOLIO_TEST_SET_FLAG(has_hwpoisoned, FOLIO_SECOND_PAGE)\n+FOLIO_TEST_CLEAR_FLAG(has_hwpoisoned, FOLIO_SECOND_PAGE)\n #else\n FOLIO_FLAG_FALSE(has_hwpoisoned)\n #endif\n@@ -1041,8 +1046,25 @@ PAGE_TYPE_OPS(Slab, slab, slab)\n \n #ifdef CONFIG_HUGETLB_PAGE\n FOLIO_TYPE_OPS(hugetlb, hugetlb)\n+\n+static inline bool folio_test_huge_poison(const struct folio *folio)\n+{\n+\treturn (READ_ONCE(folio-\u003epage.page_type) \u003e\u003e 23) ==\n+\t\t((PGTY_hugetlb \u003c\u003c 1) | 1);\n+}\n+\n+static inline void folio_set_huge_poison(struct folio *folio)\n+{\n+\tfolio-\u003epage.page_type |= (1 \u003c\u003c 23);\n+}\n+\n+static inline void folio_clear_huge_poison(struct folio *folio)\n+{\n+\tfolio-\u003epage.page_type \u0026= ~(1 \u003c\u003c 23);\n+}\n #else\n FOLIO_TEST_FLAG_FALSE(hugetlb)\n+FOLIO_TEST_FLAG_FALSE(huge_poison)\n #endif\n \n PAGE_TYPE_OPS(Zsmalloc, zsmalloc, zsmalloc)\n@@ -1068,10 +1090,14 @@ static inline bool PageHuge(const struct page *page)\n \treturn folio_test_hugetlb(page_folio(page));\n }\n \n+bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page);\n+bool hugetlb_unref_page_hwpoison(const struct page *page);\n+\n /*\n- * Check if a page is currently marked HWPoisoned. Note that this check is\n- * best effort only and inherently racy: there is no way to synchronize with\n- * failing hardware.\n+ * Check if a page is currently marked HWPoisoned.  This check is best\n+ * effort only and inherently racy: there is no way to synchronize with\n+ * failing hardware.  The caller may not have a refcount on the folio\n+ * containing the page, so we must be careful to not trip any assertions.\n  */\n static inline bool is_page_hwpoison(const struct page *page)\n {\n@@ -1080,13 +1106,26 @@ static inline bool is_page_hwpoison(const struct page *page)\n \tif (PageHWPoison(page))\n \t\treturn true;\n \tfolio = page_folio(page);\n-\treturn folio_test_hugetlb(folio) \u0026\u0026 PageHWPoison(\u0026folio-\u003epage);\n+\tif (folio_test_huge_poison(folio))\n+\t\treturn hugetlb_unref_page_hwpoison(page);\n+\t/* In case we raced with hugetlb transferring flags */\n+\treturn PageHWPoison(page);\n+}\n+\n+static inline bool is_ref_page_hwpoison(const struct folio *folio,\n+\t\tconst struct page *page)\n+{\n+\tif (PageHWPoison(page))\n+\t\treturn true;\n+\tif (folio_test_hugetlb(folio))\n+\t\treturn hugetlb_page_hwpoison(folio, page);\n+\treturn false;\n }\n \n-static inline bool folio_contain_hwpoisoned_page(struct folio *folio)\n+static inline bool folio_has_hwpoisoned_page(const struct folio *folio)\n {\n-\treturn folio_test_hwpoison(folio) ||\n-\t    (folio_test_large(folio) \u0026\u0026 folio_test_has_hwpoisoned(folio));\n+\treturn PageHWPoison(\u0026folio-\u003epage) ||\n+\t       (folio_test_large(folio) \u0026\u0026 folio_test_has_hwpoisoned(folio));\n }\n \n bool is_free_buddy_page(const struct page *page);\ndiff --git a/include/linux/pagemap.h b/include/linux/pagemap.h\nindex 2c3718d592d6dd..6fdee5b03621b9 100644\n--- a/include/linux/pagemap.h\n+++ b/include/linux/pagemap.h\n@@ -210,6 +210,7 @@ enum mapping_flags {\n \tAS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM = 9,\n \tAS_KERNEL_FILE = 10,\t/* mapping for a fake kernel file that shouldn't\n \t\t\t\t   account usage to user cgroups */\n+\tAS_AUTHORITATIVE = 11,\t/* If we miss in the page cache, it's a hole */\n \t/* Bits 16-25 are used for FOLIO_ORDER */\n \tAS_FOLIO_ORDER_BITS = 5,\n \tAS_FOLIO_ORDER_MIN = 16,\n@@ -345,6 +346,16 @@ static inline bool mapping_writeback_may_deadlock_on_reclaim(const struct addres\n \treturn test_bit(AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM, \u0026mapping-\u003eflags);\n }\n \n+static inline void mapping_set_authoritative(struct address_space *mapping)\n+{\n+\tset_bit(AS_AUTHORITATIVE, \u0026mapping-\u003eflags);\n+}\n+\n+static inline bool mapping_is_authoritative(const struct address_space *mapping)\n+{\n+\treturn test_bit(AS_AUTHORITATIVE, \u0026mapping-\u003eflags);\n+}\n+\n static inline gfp_t mapping_gfp_mask(const struct address_space *mapping)\n {\n \treturn mapping-\u003egfp_mask;\n@@ -373,7 +384,7 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)\n  * assumptions about maximum order if THP are disabled, but 8 seems like\n  * a good order (that's 1MB if you're using 4kB pages)\n  */\n-#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+#ifdef CONFIG_FILEMAP_LARGE_FOLIO\n #define PREFERRED_MAX_PAGECACHE_ORDER\tHPAGE_PMD_ORDER\n #else\n #define PREFERRED_MAX_PAGECACHE_ORDER\t8\n@@ -394,7 +405,7 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)\n  */\n static inline size_t mapping_max_folio_size_supported(void)\n {\n-\tif (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))\n+\tif (IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\n \t\treturn 1U \u003c\u003c (PAGE_SHIFT + MAX_PAGECACHE_ORDER);\n \treturn PAGE_SIZE;\n }\n@@ -405,29 +416,23 @@ static inline size_t mapping_max_folio_size_supported(void)\n  * @min: Minimum folio order (between 0-MAX_PAGECACHE_ORDER inclusive).\n  * @max: Maximum folio order (between @min-MAX_PAGECACHE_ORDER inclusive).\n  *\n- * The filesystem should call this function in its inode constructor to\n- * indicate which base size (min) and maximum size (max) of folio the VFS\n- * can use to cache the contents of the file.  This should only be used\n- * if the filesystem needs special handling of folio sizes (ie there is\n- * something the core cannot know).\n+ * The filesystem should call this function in its inode constructor\n+ * to indicate which size folios can be used to cache the contents of\n+ * the inode.  This should only be used if the filesystem needs special\n+ * handling of folio sizes (ie there is something the core cannot know).\n  * Do not tune it based on, eg, i_size.\n  *\n+ * hugetlb calls this with orders larger than MAX_PAGECACHE_ORDER.\n+ * Normal filesystems should not do this.\n+ *\n  * Context: This should not be called while the inode is active as it\n  * is non-atomic.\n  */\n static inline void mapping_set_folio_order_range(struct address_space *mapping,\n-\t\t\t\t\t\t unsigned int min,\n-\t\t\t\t\t\t unsigned int max)\n+\t\tunsigned int min, unsigned int max)\n {\n-\tif (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))\n+\tif (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\n \t\treturn;\n-\n-\tif (min \u003e MAX_PAGECACHE_ORDER)\n-\t\tmin = MAX_PAGECACHE_ORDER;\n-\n-\tif (max \u003e MAX_PAGECACHE_ORDER)\n-\t\tmax = MAX_PAGECACHE_ORDER;\n-\n \tif (max \u003c min)\n \t\tmax = min;\n \n@@ -460,7 +465,7 @@ static inline void mapping_set_large_folios(struct address_space *mapping)\n static inline unsigned int\n mapping_max_folio_order(const struct address_space *mapping)\n {\n-\tif (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))\n+\tif (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\n \t\treturn 0;\n \treturn (mapping-\u003eflags \u0026 AS_FOLIO_ORDER_MAX_MASK) \u003e\u003e AS_FOLIO_ORDER_MAX;\n }\n@@ -468,7 +473,7 @@ mapping_max_folio_order(const struct address_space *mapping)\n static inline unsigned int\n mapping_min_folio_order(const struct address_space *mapping)\n {\n-\tif (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))\n+\tif (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\n \t\treturn 0;\n \treturn (mapping-\u003eflags \u0026 AS_FOLIO_ORDER_MIN_MASK) \u003e\u003e AS_FOLIO_ORDER_MIN;\n }\n@@ -524,7 +529,7 @@ static inline bool mapping_large_folio_support(const struct address_space *mappi\n  *\n  * Return: True if PMD-sized folios are supported, otherwise false.\n  */\n-#ifdef CONFIG_TRANSPARENT_HUGEPAGE\n+#ifdef CONFIG_FILEMAP_LARGE_FOLIO\n static inline bool mapping_pmd_folio_support(const struct address_space *mapping)\n {\n \t/* AS_FOLIO_ORDER is only reasonable for pagecache folios */\ndiff --git a/mm/Kconfig b/mm/Kconfig\nindex 9e0ca48249054f..e666dd14ca0c3d 100644\n--- a/mm/Kconfig\n+++ b/mm/Kconfig\n@@ -843,11 +843,15 @@ config PERSISTENT_HUGE_ZERO_FOLIO\n config MM_ID\n \tdef_bool n\n \n+config LARGE_FOLIO\n+\tdef_bool n\n+\tselect XARRAY_MULTI\n+\n menuconfig TRANSPARENT_HUGEPAGE\n \tbool \"Transparent Hugepage Support\"\n \tdepends on HAVE_ARCH_TRANSPARENT_HUGEPAGE \u0026\u0026 !PREEMPT_RT\n \tselect COMPACTION\n-\tselect XARRAY_MULTI\n+\tselect LARGE_FOLIO\n \tselect MM_ID\n \thelp\n \t  Transparent Hugepages allows the kernel to use huge pages and\ndiff --git a/mm/filemap.c b/mm/filemap.c\nindex 58eb9d24064348..5a8cc20e624e4d 100644\n--- a/mm/filemap.c\n+++ b/mm/filemap.c\n@@ -2483,6 +2483,8 @@ static void filemap_get_read_batch(struct address_space *mapping,\n \n \t\tif (!folio_batch_add(fbatch, folio))\n \t\t\tbreak;\n+\t\tif (folio_has_hwpoisoned_page(folio))\n+\t\t\tbreak;\n \t\tif (!folio_test_uptodate(folio))\n \t\t\tbreak;\n \t\tif (folio_test_readahead(folio))\n@@ -2695,6 +2697,8 @@ static int filemap_get_pages(struct kiocb *iocb, size_t count,\n \tif (!folio_batch_count(fbatch)) {\n \t\tDEFINE_READAHEAD(ractl, filp, \u0026filp-\u003ef_ra, mapping, index);\n \n+\t\tif (mapping_is_authoritative(mapping))\n+\t\t\treturn 0;\n \t\tif (iocb-\u003eki_flags \u0026 IOCB_NOIO)\n \t\t\treturn -EAGAIN;\n \t\tif (iocb-\u003eki_flags \u0026 IOCB_NOWAIT)\n@@ -2749,6 +2753,29 @@ static inline bool pos_same_folio(loff_t pos1, loff_t pos2, struct folio *folio)\n \treturn (pos1 \u003e\u003e shift == pos2 \u003e\u003e shift);\n }\n \n+static size_t adjust_range_hwpoison(const struct folio *folio, size_t offset,\n+\t\tsize_t bytes)\n+{\n+\tconst struct page *page = folio_page(folio, offset / PAGE_SIZE);\n+\tsize_t safe_bytes;\n+\n+\tif (!folio_has_hwpoisoned_page(folio))\n+\t\treturn bytes;\n+\tif (is_ref_page_hwpoison(folio, page))\n+\t\treturn 0;\n+\n+\t/* Safe to read the remaining bytes in this page. */\n+\tsafe_bytes = PAGE_SIZE - (offset % PAGE_SIZE);\n+\tpage++;\n+\n+\t/* Check each remaining page as long as we are not done yet. */\n+\tfor (; safe_bytes \u003c bytes; safe_bytes += PAGE_SIZE, page++)\n+\t\tif (is_ref_page_hwpoison(folio, page))\n+\t\t\tbreak;\n+\n+\treturn min(safe_bytes, bytes);\n+}\n+\n static void filemap_end_dropbehind_read(struct folio *folio)\n {\n \tif (!folio_test_dropbehind(folio))\n@@ -2828,6 +2855,22 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,\n \t\t\tgoto put_folios;\n \t\tend_offset = min_t(loff_t, isize, iocb-\u003eki_pos + iter-\u003ecount);\n \n+\t\tif (!folio_batch_count(\u0026fbatch)) {\n+\t\t\tsize_t fsize = mapping_min_folio_nrbytes(mapping);\n+\t\t\tsize_t offset = iocb-\u003eki_pos \u0026 (fsize - 1);\n+\t\t\tsize_t bytes = min_t(loff_t, end_offset - iocb-\u003eki_pos,\n+\t\t\t\t\t     fsize - offset);\n+\t\t\tsize_t copied = iov_iter_zero(bytes, iter);\n+\n+\t\t\talready_read += copied;\n+\t\t\tiocb-\u003eki_pos += copied;\n+\t\t\tlast_pos = iocb-\u003eki_pos;\n+\n+\t\t\tif (copied \u003c bytes)\n+\t\t\t\terror = -EFAULT;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\t/*\n \t\t * Once we start copying data, we don't want to be touching any\n \t\t * cachelines that might be contended:\n@@ -2862,14 +2905,18 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,\n \t\t\tif (writably_mapped)\n \t\t\t\tflush_dcache_folio(folio);\n \n-\t\t\tcopied = copy_folio_to_iter(folio, offset, bytes, iter);\n+\t\t\tcopied = adjust_range_hwpoison(folio, offset, bytes);\n+\t\t\tif (copied \u003c bytes)\n+\t\t\t\terror = -EIO;\n+\t\t\tcopied = copy_folio_to_iter(folio, offset, copied, iter);\n \n \t\t\talready_read += copied;\n \t\t\tiocb-\u003eki_pos += copied;\n \t\t\tlast_pos = iocb-\u003eki_pos;\n \n \t\t\tif (copied \u003c bytes) {\n-\t\t\t\terror = -EFAULT;\n+\t\t\t\tif (!error)\n+\t\t\t\t\terror = -EFAULT;\n \t\t\t\tbreak;\n \t\t\t}\n \t\t}\ndiff --git a/mm/huge_memory.c b/mm/huge_memory.c\nindex 2bccb0a53a0a60..3533403091074c 100644\n--- a/mm/huge_memory.c\n+++ b/mm/huge_memory.c\n@@ -4413,7 +4413,7 @@ static bool thp_underused(struct folio *folio)\n \tif (khugepaged_max_ptes_none == HPAGE_PMD_NR - 1)\n \t\treturn false;\n \n-\tif (folio_contain_hwpoisoned_page(folio))\n+\tif (folio_has_hwpoisoned_page(folio))\n \t\treturn false;\n \n \tfor (i = 0; i \u003c folio_nr_pages(folio); i++) {\ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex 571212b80835ec..aaac6e2af4c24a 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -1255,7 +1255,7 @@ static struct folio *dequeue_hugetlb_folio_node_exact(struct hstate *h,\n \t\tif (pin \u0026\u0026 !folio_is_longterm_pinnable(folio))\n \t\t\tcontinue;\n \n-\t\tif (folio_test_hwpoison(folio))\n+\t\tif (folio_test_has_hwpoisoned(folio))\n \t\t\tcontinue;\n \n \t\tif (is_migrate_isolate_page(\u0026folio-\u003epage))\n@@ -1498,10 +1498,10 @@ static void __update_and_free_hugetlb_folio(struct hstate *h,\n \t}\n \n \t/*\n-\t * Move PageHWPoison flag from head page to the raw error pages,\n-\t * which makes any healthy subpages reusable.\n+\t * Move HWPoison flag to each error page\n+\t * which makes any healthy pages reusable.\n \t */\n-\tif (unlikely(folio_test_hwpoison(folio)))\n+\tif (unlikely(folio_test_has_hwpoisoned(folio)))\n \t\tfolio_clear_hugetlb_hwpoison(folio);\n \n \tVM_BUG_ON_FOLIO(folio_ref_count(folio), folio);\n@@ -1775,7 +1775,7 @@ void init_new_hugetlb_folio(struct folio *folio)\n  * stable.  Due to locking order, we can only trylock_write.  If we can\n  * not get the lock, simply return NULL to caller.\n  */\n-struct address_space *hugetlb_folio_mapping_lock_write(struct folio *folio)\n+struct address_space *hugetlb_folio_mapping_lock_write(const struct folio *folio)\n {\n \tstruct address_space *mapping = folio_mapping(folio);\n \n@@ -3998,7 +3998,7 @@ long demote_pool_huge_page(struct hstate *src, nodemask_t *nodes_allowed,\n \t\tstruct folio *folio, *next;\n \n \t\tlist_for_each_entry_safe(folio, next, \u0026src-\u003ehugepage_freelists[node], lru) {\n-\t\t\tif (folio_test_hwpoison(folio))\n+\t\t\tif (folio_test_has_hwpoisoned(folio))\n \t\t\t\tcontinue;\n \n \t\t\tremove_hugetlb_folio(src, folio, false);\n@@ -5809,7 +5809,7 @@ static vm_fault_t hugetlb_no_page(struct address_space *mapping,\n \t\t * don't have hwpoisoned swap entry for errored virtual address.\n \t\t * So we need to block hugepage fault by PG_hwpoison bit check.\n \t\t */\n-\t\tif (unlikely(folio_test_hwpoison(folio))) {\n+\t\tif (unlikely(folio_test_has_hwpoisoned(folio))) {\n \t\t\tret = VM_FAULT_HWPOISON_LARGE |\n \t\t\t\tVM_FAULT_SET_HINDEX(hstate_index(h));\n \t\t\tgoto backout_unlocked;\n@@ -6318,7 +6318,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte,\n \tptl = huge_pte_lock(h, dst_mm, dst_pte);\n \n \tret = -EIO;\n-\tif (folio_test_hwpoison(folio))\n+\tif (folio_test_has_hwpoisoned(folio))\n \t\tgoto out_release_unlock;\n \n \tret = -EEXIST;\ndiff --git a/mm/memory-failure.c b/mm/memory-failure.c\nindex 51508a55c4055e..73ebbc93566182 100644\n--- a/mm/memory-failure.c\n+++ b/mm/memory-failure.c\n@@ -1810,50 +1810,65 @@ EXPORT_SYMBOL_GPL(mf_dax_kill_procs);\n \n /*\n  * Struct raw_hwp_page represents information about \"raw error page\",\n- * constructing singly linked list from -\u003e_hugetlb_hwpoison field of folio.\n+ * constructing singly linked list from folio-\u003ehugetlb_hwpoison field.\n  */\n struct raw_hwp_page {\n \tstruct llist_node node;\n \tstruct page *page;\n };\n \n-static inline struct llist_head *raw_hwp_list_head(struct folio *folio)\n+static bool precise_page_poisoned(const struct folio *folio,\n+\t\tconst struct page *page)\n {\n-\treturn (struct llist_head *)\u0026folio-\u003e_hugetlb_hwpoison;\n-}\n-\n-bool is_raw_hwpoison_page_in_hugepage(struct page *page)\n-{\n-\tstruct llist_head *raw_hwp_head;\n-\tstruct raw_hwp_page *p;\n-\tstruct folio *folio = page_folio(page);\n-\tbool ret = false;\n-\n-\tif (!folio_test_hwpoison(folio))\n-\t\treturn false;\n-\n-\tif (!folio_test_hugetlb(folio))\n-\t\treturn PageHWPoison(page);\n+\tconst struct llist_head *list = \u0026folio-\u003ehugetlb_hwpoison;\n+\tconst struct raw_hwp_page *p;\n \n \t/*\n-\t * When RawHwpUnreliable is set, kernel lost track of which subpages\n-\t * are HWPOISON. So return as if ALL subpages are HWPOISONed.\n+\t * When RawHwpUnreliable is set, kernel lost track of which pages\n+\t * are HWPOISON. So return as if ALL pages are HWPOISONed.\n \t */\n \tif (folio_test_hugetlb_raw_hwp_unreliable(folio))\n \t\treturn true;\n \n-\tmutex_lock(\u0026mf_mutex);\n-\n-\traw_hwp_head = raw_hwp_list_head(folio);\n-\tllist_for_each_entry(p, raw_hwp_head-\u003efirst, node) {\n-\t\tif (page == p-\u003epage) {\n-\t\t\tret = true;\n-\t\t\tbreak;\n-\t\t}\n+\tllist_for_each_entry(p, READ_ONCE(list-\u003efirst), node) {\n+\t\tif (page == p-\u003epage)\n+\t\t\treturn true;\n \t}\n \n-\tmutex_unlock(\u0026mf_mutex);\n+\treturn false;\n+}\n \n+/*\n+ * Check if a given @page in a hugetlb folio is HWPOISON.\n+ */\n+bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page)\n+{\n+\tif (!folio_test_has_hwpoisoned(folio))\n+\t\treturn false;\n+\n+\treturn precise_page_poisoned(folio, page);\n+}\n+\n+/*\n+ * We have no reference on the folio containing this page.\n+ * The hugetlb_lock keeps hugetlb folios from being freed.\n+ */\n+bool hugetlb_unref_page_hwpoison(const struct page *page)\n+{\n+\tconst struct folio *folio;\n+\tunsigned long flags;\n+\tbool ret;\n+\n+\tspin_lock_irqsave(\u0026hugetlb_lock, flags);\n+\tfolio = page_folio(page);\n+\tif (!folio_test_huge_poison(folio)) {\n+\t\tret = PageHWPoison(page);\n+\t\tgoto unlock;\n+\t}\n+\n+\tret = precise_page_poisoned(folio, page);\n+unlock:\n+\tspin_unlock_irqrestore(\u0026hugetlb_lock, flags);\n \treturn ret;\n }\n \n@@ -1863,7 +1878,7 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)\n \tstruct raw_hwp_page *p, *next;\n \tunsigned long count = 0;\n \n-\thead = llist_del_all(raw_hwp_list_head(folio));\n+\thead = llist_del_all(\u0026folio-\u003ehugetlb_hwpoison);\n \tllist_for_each_entry_safe(p, next, head, node) {\n \t\tif (move_flag)\n \t\t\tSetPageHWPoison(p-\u003epage);\n@@ -1881,26 +1896,41 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)\n #define\tMF_HUGETLB_FOLIO_PRE_POISONED\t3\t/* folio already poisoned */\n #define\tMF_HUGETLB_PAGE_PRE_POISONED\t4\t/* exact page already poisoned */\n #define\tMF_HUGETLB_RETRY\t\t5\t/* hugepage is busy, retry */\n+\n+static inline int hugetlb_set_poison(struct folio *folio)\n+{\n+\tif (folio_test_set_has_hwpoisoned(folio))\n+\t\treturn MF_HUGETLB_FOLIO_PRE_POISONED;\n+\tfolio_set_huge_poison(folio);\n+\treturn 0;\n+}\n+\n+static inline int hugetlb_clear_poison(struct folio *folio)\n+{\n+\tif (!folio_test_clear_has_hwpoisoned(folio))\n+\t\treturn -EBUSY;\n+\tfolio_clear_huge_poison(folio);\n+\treturn 0;\n+}\n+\n /*\n  * Set hugetlb folio as hwpoisoned, update folio private raw hwpoison list\n  * to keep track of the poisoned pages.\n  */\n static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)\n {\n-\tstruct llist_head *head;\n \tstruct raw_hwp_page *raw_hwp;\n \tstruct raw_hwp_page *p;\n-\tint ret = folio_test_set_hwpoison(folio) ? MF_HUGETLB_FOLIO_PRE_POISONED : 0;\n+\tint ret = hugetlb_set_poison(folio);\n \n \t/*\n \t * Once the hwpoison hugepage has lost reliable raw error info,\n-\t * there is little meaning to keep additional error info precisely,\n-\t * so skip to add additional raw error info.\n+\t * there is no point in keeping additional error info precisely,\n+\t * so skip adding additional raw error info.\n \t */\n \tif (folio_test_hugetlb_raw_hwp_unreliable(folio))\n \t\treturn MF_HUGETLB_FOLIO_PRE_POISONED;\n-\thead = raw_hwp_list_head(folio);\n-\tllist_for_each_entry(p, head-\u003efirst, node) {\n+\tllist_for_each_entry(p, folio-\u003ehugetlb_hwpoison.first, node) {\n \t\tif (p-\u003epage == page)\n \t\t\treturn MF_HUGETLB_PAGE_PRE_POISONED;\n \t}\n@@ -1908,7 +1938,7 @@ static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)\n \traw_hwp = kmalloc_obj(struct raw_hwp_page, GFP_ATOMIC);\n \tif (raw_hwp) {\n \t\traw_hwp-\u003epage = page;\n-\t\tllist_add(\u0026raw_hwp-\u003enode, head);\n+\t\tllist_add(\u0026raw_hwp-\u003enode, \u0026folio-\u003ehugetlb_hwpoison);\n \t} else {\n \t\t/*\n \t\t * Failed to save raw error info.  We no longer trace all\n@@ -1950,8 +1980,8 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)\n \t\treturn;\n \tif (folio_test_hugetlb_vmemmap_optimized(folio))\n \t\treturn;\n-\tfolio_clear_hwpoison(folio);\n \tfolio_free_raw_hwp(folio, true);\n+\tfolio_clear_has_hwpoisoned(folio);\n }\n \n static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,\n@@ -2104,6 +2134,11 @@ static inline unsigned long folio_free_raw_hwp(struct folio *folio, bool flag)\n {\n \treturn 0;\n }\n+\n+static inline int hugetlb_clear_poison(struct folio *folio)\n+{\n+\treturn 0;\n+}\n #endif\t/* CONFIG_HUGETLB_PAGE */\n \n /* Drop the extra refcount in case we come from madvise() */\n@@ -2677,7 +2712,12 @@ int unpoison_memory(unsigned long pfn)\n \tp = pfn_to_online_page(pfn);\n \tif (!p)\n \t\treturn -EIO;\n-\tfolio = page_folio(p);\n+\n+\tif (!is_page_hwpoison(p)) {\n+\t\tunpoison_pr_info(\"%#lx: page is not poisoned\\n\",\n+\t\t\t\t pfn, \u0026unpoison_rs);\n+\t\treturn -EBUSY;\n+\t}\n \n \tmutex_lock(\u0026mf_mutex);\n \n@@ -2688,6 +2728,8 @@ int unpoison_memory(unsigned long pfn)\n \t\tgoto unlock_mutex;\n \t}\n \n+\tfolio = page_folio(p);\n+\n \tif (is_huge_zero_folio(folio)) {\n \t\tunpoison_pr_info(\"%#lx: huge zero page is not supported\\n\",\n \t\t\t\t pfn, \u0026unpoison_rs);\n@@ -2695,12 +2737,6 @@ int unpoison_memory(unsigned long pfn)\n \t\tgoto unlock_mutex;\n \t}\n \n-\tif (!PageHWPoison(p)) {\n-\t\tunpoison_pr_info(\"%#lx: page was already unpoisoned\\n\",\n-\t\t\t\t pfn, \u0026unpoison_rs);\n-\t\tgoto unlock_mutex;\n-\t}\n-\n \tif (folio_ref_count(folio) \u003e 1) {\n \t\tunpoison_pr_info(\"%#lx: someone grabs the hwpoison page\\n\",\n \t\t\t\t pfn, \u0026unpoison_rs);\n@@ -2730,8 +2766,8 @@ int unpoison_memory(unsigned long pfn)\n \t\t\tcount = folio_free_raw_hwp(folio, false);\n \t\t\tif (count == 0)\n \t\t\t\tgoto unlock_mutex;\n+\t\t\tret = hugetlb_clear_poison(folio);\n \t\t}\n-\t\tret = folio_test_clear_hwpoison(folio) ? 0 : -EBUSY;\n \t} else if (ghp \u003c 0) {\n \t\tif (ghp == -EHWPOISON) {\n \t\t\tret = put_page_back_buddy(p) ? 0 : -EBUSY;\n@@ -2740,16 +2776,18 @@ int unpoison_memory(unsigned long pfn)\n \t\t\tunpoison_pr_info(\"%#lx: failed to grab page\\n\",\n \t\t\t\t\t pfn, \u0026unpoison_rs);\n \t\t}\n-\t} else {\n-\t\tif (folio_test_hugetlb(folio)) {\n-\t\t\thuge = true;\n-\t\t\tcount = folio_free_raw_hwp(folio, false);\n-\t\t\tif (count == 0) {\n-\t\t\t\tfolio_put(folio);\n-\t\t\t\tgoto unlock_mutex;\n-\t\t\t}\n+\t} else if (folio_test_hugetlb(folio)) {\n+\t\thuge = true;\n+\t\tcount = folio_free_raw_hwp(folio, false);\n+\t\tif (count == 0) {\n+\t\t\tfolio_put(folio);\n+\t\t\tgoto unlock_mutex;\n \t\t}\n-\n+\t\tfolio_put(folio);\n+\t\tret = hugetlb_clear_poison(folio);\n+\t\tif (!ret)\n+\t\t\tfolio_put(folio);\n+\t} else {\n \t\tfolio_put(folio);\n \t\tif (TestClearPageHWPoison(p)) {\n \t\t\tfolio_put(folio);\ndiff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c\nindex 7ac19fab226324..33007e8e546a09 100644\n--- a/mm/memory_hotplug.c\n+++ b/mm/memory_hotplug.c\n@@ -1813,7 +1813,7 @@ static void do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)\n \t\tif (folio_test_large(folio))\n \t\t\tpfn = folio_pfn(folio) + folio_nr_pages(folio) - 1;\n \n-\t\tif (folio_contain_hwpoisoned_page(folio)) {\n+\t\tif (folio_has_hwpoisoned_page(folio)) {\n \t\t\t/*\n \t\t\t * unmap_poisoned_folio() cannot handle large folios\n \t\t\t * in all cases yet.\ndiff --git a/mm/shmem.c b/mm/shmem.c\nindex b51f83c970bb37..492c9bef362074 100644\n--- a/mm/shmem.c\n+++ b/mm/shmem.c\n@@ -3240,7 +3240,7 @@ shmem_write_begin(const struct kiocb *iocb, struct address_space *mapping,\n \tif (ret)\n \t\treturn ret;\n \n-\tif (folio_contain_hwpoisoned_page(folio)) {\n+\tif (folio_has_hwpoisoned_page(folio)) {\n \t\tfolio_unlock(folio);\n \t\tfolio_put(folio);\n \t\treturn -EIO;\ndiff --git a/mm/vmscan.c b/mm/vmscan.c\nindex 35c3bb15ae96a3..70e54ace0e741c 100644\n--- a/mm/vmscan.c\n+++ b/mm/vmscan.c\n@@ -1086,7 +1086,7 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,\n \t\tif (!folio_trylock(folio))\n \t\t\tgoto keep;\n \n-\t\tif (folio_contain_hwpoisoned_page(folio)) {\n+\t\tif (folio_has_hwpoisoned_page(folio)) {\n \t\t\t/*\n \t\t\t * unmap_poisoned_folio() can't handle large\n \t\t\t * folio, just skip it. memory_failure() will\ndiff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c\nindex db57c5766ab604..94eedcc7f1a9e0 100644\n--- a/virt/kvm/guest_memfd.c\n+++ b/virt/kvm/guest_memfd.c\n@@ -774,7 +774,7 @@ static struct folio *__kvm_gmem_get_pfn(struct file *file,\n \tif (IS_ERR(folio))\n \t\treturn folio;\n \n-\tif (folio_test_hwpoison(folio)) {\n+\tif (folio_has_hwpoisoned_page(folio)) {\n \t\tfolio_unlock(folio);\n \t\tfolio_put(folio);\n \t\treturn ERR_PTR(-EHWPOISON);\n"
}

2/1 2026/07/24 18:46 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "MEMORY_FAILURE",
    "HWPOISON_INJECT",
    "HUGETLBFS",
    "HUGETLB_PAGE",
    "TRANSPARENT_HUGEPAGE"
  ],
  "FocusSymbols": [
    "hugetlb_page_hwpoison",
    "hugetlb_unref_page_hwpoison",
    "unpoison_memory",
    "folio_clear_hugetlb_hwpoison"
  ],
  "Reasoning": "The patch modifies how hardware poisoned (HWPOISON) pages are tracked and handled within large folios, specifically affecting hugetlbfs and filemap read operations. It introduces new logic to track poisoned pages using `folio-\u003ehugetlb_hwpoison` and replaces the custom `hugetlbfs_read_iter` with `generic_file_read_iter`, handling hwpoison directly in `filemap_read`. These are reachable, functional changes in core memory management and hugetlbfs. Fuzzing should focus on the new hwpoison tracking functions and unpoisoning logic.",
  "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 0fe6de6c2f057664217d96a3da772a48667e5567
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Jul 24 18:46:16 2026 +0000

    syz-cluster: applied patch under review

diff --git a/fs/Kconfig b/fs/Kconfig
index cf6ae64776e62c..eddac4ed214b12 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -272,7 +272,7 @@ endif # HUGETLBFS
 
 config HUGETLB_PAGE
 	def_bool HUGETLBFS
-	select XARRAY_MULTI
+	select LARGE_FOLIO
 
 config HUGETLB_PAGE_OPTIMIZE_VMEMMAP
 	def_bool HUGETLB_PAGE
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 216e1a0dd0b237..cef7e61b827d71 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -187,113 +187,6 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
 	return mm_get_unmapped_area_vmflags(file, addr0, len, pgoff, flags, 0);
 }
 
-/*
- * Someone wants to read @bytes from a HWPOISON hugetlb @folio from @offset.
- * Returns the maximum number of bytes one can read without touching the 1st raw
- * HWPOISON page.
- */
-static size_t adjust_range_hwpoison(struct folio *folio, size_t offset,
-		size_t bytes)
-{
-	struct page *page = folio_page(folio, offset / PAGE_SIZE);
-	size_t safe_bytes;
-
-	if (is_raw_hwpoison_page_in_hugepage(page))
-		return 0;
-	/* Safe to read the remaining bytes in this page. */
-	safe_bytes = PAGE_SIZE - (offset % PAGE_SIZE);
-	page++;
-
-	/* Check each remaining page as long as we are not done yet. */
-	for (; safe_bytes < bytes; safe_bytes += PAGE_SIZE, page++)
-		if (is_raw_hwpoison_page_in_hugepage(page))
-			break;
-
-	return min(safe_bytes, bytes);
-}
-
-/*
- * Support for read() - Find the page attached to f_mapping and copy out the
- * data. This provides functionality similar to filemap_read().
- */
-static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
-{
-	struct file *file = iocb->ki_filp;
-	struct hstate *h = hstate_file(file);
-	struct address_space *mapping = file->f_mapping;
-	struct inode *inode = mapping->host;
-	unsigned long index = iocb->ki_pos >> huge_page_shift(h);
-	unsigned long offset = iocb->ki_pos & ~huge_page_mask(h);
-	unsigned long end_index;
-	loff_t isize;
-	ssize_t retval = 0;
-
-	while (iov_iter_count(to)) {
-		struct folio *folio;
-		size_t nr, copied, want;
-
-		/* nr is the maximum number of bytes to copy from this page */
-		nr = huge_page_size(h);
-		isize = i_size_read(inode);
-		if (!isize)
-			break;
-		end_index = (isize - 1) >> huge_page_shift(h);
-		if (index > end_index)
-			break;
-		if (index == end_index) {
-			nr = ((isize - 1) & ~huge_page_mask(h)) + 1;
-			if (nr <= offset)
-				break;
-		}
-		nr = nr - offset;
-
-		/* Find the folio */
-		folio = filemap_lock_hugetlb_folio(h, mapping, index);
-		if (IS_ERR(folio)) {
-			/*
-			 * We have a HOLE, zero out the user-buffer for the
-			 * length of the hole or request.
-			 */
-			copied = iov_iter_zero(nr, to);
-		} else {
-			folio_unlock(folio);
-
-			if (!folio_test_hwpoison(folio))
-				want = nr;
-			else {
-				/*
-				 * Adjust how many bytes safe to read without
-				 * touching the 1st raw HWPOISON page after
-				 * offset.
-				 */
-				want = adjust_range_hwpoison(folio, offset, nr);
-				if (want == 0) {
-					folio_put(folio);
-					retval = -EIO;
-					break;
-				}
-			}
-
-			/*
-			 * We have the folio, copy it to user space buffer.
-			 */
-			copied = copy_folio_to_iter(folio, offset, want, to);
-			folio_put(folio);
-		}
-		offset += copied;
-		retval += copied;
-		if (copied != nr && iov_iter_count(to)) {
-			if (!retval)
-				retval = -EFAULT;
-			break;
-		}
-		index += offset >> huge_page_shift(h);
-		offset &= ~huge_page_mask(h);
-	}
-	iocb->ki_pos = ((loff_t)index << huge_page_shift(h)) + offset;
-	return retval;
-}
-
 static int hugetlbfs_write_begin(const struct kiocb *iocb,
 			struct address_space *mapping,
 			loff_t pos, unsigned len,
@@ -883,6 +776,17 @@ static struct inode *hugetlbfs_get_root(struct super_block *sb,
 	return inode;
 }
 
+static void hugetlbfs_init_regular_inode(struct inode *inode)
+{
+	struct hstate *hstate = hstate_inode(inode);
+	unsigned int order = huge_page_order(hstate);
+
+	inode->i_op = &hugetlbfs_inode_operations;
+	inode->i_fop = &hugetlbfs_file_operations;
+	mapping_set_folio_order_range(inode->i_mapping, order, order);
+	mapping_set_authoritative(inode->i_mapping);
+}
+
 /*
  * Hugetlbfs is not reclaimable; therefore its i_mmap_rwsem will never
  * be taken from reclaim -- unlike regular filesystems. This needs an
@@ -926,8 +830,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
 			init_special_inode(inode, mode, dev);
 			break;
 		case S_IFREG:
-			inode->i_op = &hugetlbfs_inode_operations;
-			inode->i_fop = &hugetlbfs_file_operations;
+			hugetlbfs_init_regular_inode(inode);
 			break;
 		case S_IFDIR:
 			inode->i_op = &hugetlbfs_dir_inode_operations;
@@ -1206,7 +1109,7 @@ static void init_once(void *foo)
 }
 
 static const struct file_operations hugetlbfs_file_operations = {
-	.read_iter		= hugetlbfs_read_iter,
+	.read_iter		= generic_file_read_iter,
 	.mmap			= hugetlbfs_file_mmap,
 	.fsync			= noop_fsync,
 	.get_unmapped_area	= hugetlb_get_unmapped_area,
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 2abaf99321e90d..1878642e75ec51 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -165,7 +165,7 @@ bool hugetlbfs_pagecache_present(struct hstate *h,
 				 struct vm_area_struct *vma,
 				 unsigned long address);
 
-struct address_space *hugetlb_folio_mapping_lock_write(struct folio *folio);
+struct address_space *hugetlb_folio_mapping_lock_write(const struct folio *folio);
 
 extern int movable_gigantic_pages __read_mostly;
 extern int sysctl_hugetlb_shm_group __read_mostly;
@@ -292,8 +292,7 @@ static inline unsigned long hugetlb_total_pages(void)
 	return 0;
 }
 
-static inline struct address_space *hugetlb_folio_mapping_lock_write(
-							struct folio *folio)
+static inline struct address_space *hugetlb_folio_mapping_lock_write(const struct folio *folio)
 {
 	return NULL;
 }
@@ -595,8 +594,8 @@ enum hugetlb_page_flags {
 #ifdef CONFIG_HUGETLB_PAGE
 #define TESTHPAGEFLAG(uname, flname)				\
 static __always_inline						\
-bool folio_test_hugetlb_##flname(struct folio *folio)		\
-	{	void *private = &folio->private;		\
+bool folio_test_hugetlb_##flname(const struct folio *folio)	\
+	{	const void *private = &folio->private;		\
 		return test_bit(HPG_##flname, private);		\
 	}
 
@@ -616,7 +615,7 @@ void folio_clear_hugetlb_##flname(struct folio *folio)		\
 #else
 #define TESTHPAGEFLAG(uname, flname)				\
 static inline bool						\
-folio_test_hugetlb_##flname(struct folio *folio)		\
+folio_test_hugetlb_##flname(const struct folio *folio)		\
 	{ return 0; }
 
 #define SETHPAGEFLAG(uname, flname)				\
@@ -727,7 +726,7 @@ static inline struct hugepage_subpool *subpool_inode(struct inode *inode)
 	return HUGETLBFS_SB(inode->i_sb)->spool;
 }
 
-static inline struct hugepage_subpool *hugetlb_folio_subpool(struct folio *folio)
+static inline struct hugepage_subpool *hugetlb_folio_subpool(const struct folio *folio)
 {
 	return folio->_hugetlb_subpool;
 }
@@ -857,7 +856,7 @@ static inline bool arch_has_huge_bootmem_alloc(void)
 }
 #endif
 
-static inline struct hstate *folio_hstate(struct folio *folio)
+static inline struct hstate *folio_hstate(const struct folio *folio)
 {
 	VM_BUG_ON_FOLIO(!folio_test_hugetlb(folio), folio);
 	return size_to_hstate(folio_size(folio));
@@ -1070,11 +1069,6 @@ void hugetlb_register_node(struct node *node);
 void hugetlb_unregister_node(struct node *node);
 #endif
 
-/*
- * Check if a given raw @page in a hugepage is HWPOISON.
- */
-bool is_raw_hwpoison_page_in_hugepage(struct page *page);
-
 static inline unsigned long huge_page_mask_align(struct file *file)
 {
 	return PAGE_MASK & ~huge_page_mask(hstate_file(file));
@@ -1088,7 +1082,7 @@ static inline unsigned long huge_page_mask_align(struct file *file)
 	return 0;
 }
 
-static inline struct hugepage_subpool *hugetlb_folio_subpool(struct folio *folio)
+static inline struct hugepage_subpool *hugetlb_folio_subpool(const struct folio *folio)
 {
 	return NULL;
 }
@@ -1157,7 +1151,7 @@ static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
 	return NULL;
 }
 
-static inline struct hstate *folio_hstate(struct folio *folio)
+static inline struct hstate *folio_hstate(const struct folio *folio)
 {
 	return NULL;
 }
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index b18c2b2e7d2c65..d7a9b0d8bbdfb1 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -387,7 +387,7 @@ typedef unsigned short mm_id_t;
  * @_hugetlb_subpool: Do not use directly, use accessor in hugetlb.h.
  * @_hugetlb_cgroup: Do not use directly, use accessor in hugetlb_cgroup.h.
  * @_hugetlb_cgroup_rsvd: Do not use directly, use accessor in hugetlb_cgroup.h.
- * @_hugetlb_hwpoison: Do not use directly, call raw_hwp_list_head().
+ * @hugetlb_hwpoison: List of pages with hwpoison.
  * @_deferred_list: Folios to be split under memory pressure.
  * @_unused_slab_obj_exts: Placeholder to match obj_exts in struct slab.
  *
@@ -499,7 +499,7 @@ struct folio {
 			void *_hugetlb_subpool;
 			void *_hugetlb_cgroup;
 			void *_hugetlb_cgroup_rsvd;
-			void *_hugetlb_hwpoison;
+			struct llist_head hugetlb_hwpoison;
 	/* private: the union with struct page is transitional */
 		};
 		struct page __page_3;
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7223f6f4e2b403..b146c5228741c0 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -893,14 +893,19 @@ static inline int PageTransCompound(const struct page *page)
 TESTPAGEFLAG_FALSE(TransCompound, transcompound)
 #endif
 
-#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_TRANSPARENT_HUGEPAGE)
+#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_LARGE_FOLIO)
 /*
- * PageHasHWPoisoned indicates that at least one subpage is hwpoisoned in the
- * compound page.
+ * folio_has_hwpoisoned indicates that at least one page is hwpoisoned in the
+ * folio.  That page will usually also have the HWPoison flag set, but this
+ * is not possible for folios which have HVO (see memory-failure for the
+ * scheme used in that case).  You probably don't want to call this directly;
+ * use folio_has_hwpoisoned_page() instead.
  *
  * This flag is set by hwpoison handler.  Cleared by THP split or free page.
  */
 FOLIO_FLAG(has_hwpoisoned, FOLIO_SECOND_PAGE)
+FOLIO_TEST_SET_FLAG(has_hwpoisoned, FOLIO_SECOND_PAGE)
+FOLIO_TEST_CLEAR_FLAG(has_hwpoisoned, FOLIO_SECOND_PAGE)
 #else
 FOLIO_FLAG_FALSE(has_hwpoisoned)
 #endif
@@ -1041,8 +1046,25 @@ PAGE_TYPE_OPS(Slab, slab, slab)
 
 #ifdef CONFIG_HUGETLB_PAGE
 FOLIO_TYPE_OPS(hugetlb, hugetlb)
+
+static inline bool folio_test_huge_poison(const struct folio *folio)
+{
+	return (READ_ONCE(folio->page.page_type) >> 23) ==
+		((PGTY_hugetlb << 1) | 1);
+}
+
+static inline void folio_set_huge_poison(struct folio *folio)
+{
+	folio->page.page_type |= (1 << 23);
+}
+
+static inline void folio_clear_huge_poison(struct folio *folio)
+{
+	folio->page.page_type &= ~(1 << 23);
+}
 #else
 FOLIO_TEST_FLAG_FALSE(hugetlb)
+FOLIO_TEST_FLAG_FALSE(huge_poison)
 #endif
 
 PAGE_TYPE_OPS(Zsmalloc, zsmalloc, zsmalloc)
@@ -1068,10 +1090,14 @@ static inline bool PageHuge(const struct page *page)
 	return folio_test_hugetlb(page_folio(page));
 }
 
+bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page);
+bool hugetlb_unref_page_hwpoison(const struct page *page);
+
 /*
- * Check if a page is currently marked HWPoisoned. Note that this check is
- * best effort only and inherently racy: there is no way to synchronize with
- * failing hardware.
+ * Check if a page is currently marked HWPoisoned.  This check is best
+ * effort only and inherently racy: there is no way to synchronize with
+ * failing hardware.  The caller may not have a refcount on the folio
+ * containing the page, so we must be careful to not trip any assertions.
  */
 static inline bool is_page_hwpoison(const struct page *page)
 {
@@ -1080,13 +1106,26 @@ static inline bool is_page_hwpoison(const struct page *page)
 	if (PageHWPoison(page))
 		return true;
 	folio = page_folio(page);
-	return folio_test_hugetlb(folio) && PageHWPoison(&folio->page);
+	if (folio_test_huge_poison(folio))
+		return hugetlb_unref_page_hwpoison(page);
+	/* In case we raced with hugetlb transferring flags */
+	return PageHWPoison(page);
+}
+
+static inline bool is_ref_page_hwpoison(const struct folio *folio,
+		const struct page *page)
+{
+	if (PageHWPoison(page))
+		return true;
+	if (folio_test_hugetlb(folio))
+		return hugetlb_page_hwpoison(folio, page);
+	return false;
 }
 
-static inline bool folio_contain_hwpoisoned_page(struct folio *folio)
+static inline bool folio_has_hwpoisoned_page(const struct folio *folio)
 {
-	return folio_test_hwpoison(folio) ||
-	    (folio_test_large(folio) && folio_test_has_hwpoisoned(folio));
+	return PageHWPoison(&folio->page) ||
+	       (folio_test_large(folio) && folio_test_has_hwpoisoned(folio));
 }
 
 bool is_free_buddy_page(const struct page *page);
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 2c3718d592d6dd..6fdee5b03621b9 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -210,6 +210,7 @@ enum mapping_flags {
 	AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM = 9,
 	AS_KERNEL_FILE = 10,	/* mapping for a fake kernel file that shouldn't
 				   account usage to user cgroups */
+	AS_AUTHORITATIVE = 11,	/* If we miss in the page cache, it's a hole */
 	/* Bits 16-25 are used for FOLIO_ORDER */
 	AS_FOLIO_ORDER_BITS = 5,
 	AS_FOLIO_ORDER_MIN = 16,
@@ -345,6 +346,16 @@ static inline bool mapping_writeback_may_deadlock_on_reclaim(const struct addres
 	return test_bit(AS_WRITEBACK_MAY_DEADLOCK_ON_RECLAIM, &mapping->flags);
 }
 
+static inline void mapping_set_authoritative(struct address_space *mapping)
+{
+	set_bit(AS_AUTHORITATIVE, &mapping->flags);
+}
+
+static inline bool mapping_is_authoritative(const struct address_space *mapping)
+{
+	return test_bit(AS_AUTHORITATIVE, &mapping->flags);
+}
+
 static inline gfp_t mapping_gfp_mask(const struct address_space *mapping)
 {
 	return mapping->gfp_mask;
@@ -373,7 +384,7 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
  * assumptions about maximum order if THP are disabled, but 8 seems like
  * a good order (that's 1MB if you're using 4kB pages)
  */
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#ifdef CONFIG_FILEMAP_LARGE_FOLIO
 #define PREFERRED_MAX_PAGECACHE_ORDER	HPAGE_PMD_ORDER
 #else
 #define PREFERRED_MAX_PAGECACHE_ORDER	8
@@ -394,7 +405,7 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
  */
 static inline size_t mapping_max_folio_size_supported(void)
 {
-	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+	if (IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))
 		return 1U << (PAGE_SHIFT + MAX_PAGECACHE_ORDER);
 	return PAGE_SIZE;
 }
@@ -405,29 +416,23 @@ static inline size_t mapping_max_folio_size_supported(void)
  * @min: Minimum folio order (between 0-MAX_PAGECACHE_ORDER inclusive).
  * @max: Maximum folio order (between @min-MAX_PAGECACHE_ORDER inclusive).
  *
- * The filesystem should call this function in its inode constructor to
- * indicate which base size (min) and maximum size (max) of folio the VFS
- * can use to cache the contents of the file.  This should only be used
- * if the filesystem needs special handling of folio sizes (ie there is
- * something the core cannot know).
+ * The filesystem should call this function in its inode constructor
+ * to indicate which size folios can be used to cache the contents of
+ * the inode.  This should only be used if the filesystem needs special
+ * handling of folio sizes (ie there is something the core cannot know).
  * Do not tune it based on, eg, i_size.
  *
+ * hugetlb calls this with orders larger than MAX_PAGECACHE_ORDER.
+ * Normal filesystems should not do this.
+ *
  * Context: This should not be called while the inode is active as it
  * is non-atomic.
  */
 static inline void mapping_set_folio_order_range(struct address_space *mapping,
-						 unsigned int min,
-						 unsigned int max)
+		unsigned int min, unsigned int max)
 {
-	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+	if (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))
 		return;
-
-	if (min > MAX_PAGECACHE_ORDER)
-		min = MAX_PAGECACHE_ORDER;
-
-	if (max > MAX_PAGECACHE_ORDER)
-		max = MAX_PAGECACHE_ORDER;
-
 	if (max < min)
 		max = min;
 
@@ -460,7 +465,7 @@ static inline void mapping_set_large_folios(struct address_space *mapping)
 static inline unsigned int
 mapping_max_folio_order(const struct address_space *mapping)
 {
-	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+	if (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))
 		return 0;
 	return (mapping->flags & AS_FOLIO_ORDER_MAX_MASK) >> AS_FOLIO_ORDER_MAX;
 }
@@ -468,7 +473,7 @@ mapping_max_folio_order(const struct address_space *mapping)
 static inline unsigned int
 mapping_min_folio_order(const struct address_space *mapping)
 {
-	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+	if (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))
 		return 0;
 	return (mapping->flags & AS_FOLIO_ORDER_MIN_MASK) >> AS_FOLIO_ORDER_MIN;
 }
@@ -524,7 +529,7 @@ static inline bool mapping_large_folio_support(const struct address_space *mappi
  *
  * Return: True if PMD-sized folios are supported, otherwise false.
  */
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#ifdef CONFIG_FILEMAP_LARGE_FOLIO
 static inline bool mapping_pmd_folio_support(const struct address_space *mapping)
 {
 	/* AS_FOLIO_ORDER is only reasonable for pagecache folios */
diff --git a/mm/Kconfig b/mm/Kconfig
index 9e0ca48249054f..e666dd14ca0c3d 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -843,11 +843,15 @@ config PERSISTENT_HUGE_ZERO_FOLIO
 config MM_ID
 	def_bool n
 
+config LARGE_FOLIO
+	def_bool n
+	select XARRAY_MULTI
+
 menuconfig TRANSPARENT_HUGEPAGE
 	bool "Transparent Hugepage Support"
 	depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE && !PREEMPT_RT
 	select COMPACTION
-	select XARRAY_MULTI
+	select LARGE_FOLIO
 	select MM_ID
 	help
 	  Transparent Hugepages allows the kernel to use huge pages and
diff --git a/mm/filemap.c b/mm/filemap.c
index 58eb9d24064348..5a8cc20e624e4d 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2483,6 +2483,8 @@ static void filemap_get_read_batch(struct address_space *mapping,
 
 		if (!folio_batch_add(fbatch, folio))
 			break;
+		if (folio_has_hwpoisoned_page(folio))
+			break;
 		if (!folio_test_uptodate(folio))
 			break;
 		if (folio_test_readahead(folio))
@@ -2695,6 +2697,8 @@ static int filemap_get_pages(struct kiocb *iocb, size_t count,
 	if (!folio_batch_count(fbatch)) {
 		DEFINE_READAHEAD(ractl, filp, &filp->f_ra, mapping, index);
 
+		if (mapping_is_authoritative(mapping))
+			return 0;
 		if (iocb->ki_flags & IOCB_NOIO)
 			return -EAGAIN;
 		if (iocb->ki_flags & IOCB_NOWAIT)
@@ -2749,6 +2753,29 @@ static inline bool pos_same_folio(loff_t pos1, loff_t pos2, struct folio *folio)
 	return (pos1 >> shift == pos2 >> shift);
 }
 
+static size_t adjust_range_hwpoison(const struct folio *folio, size_t offset,
+		size_t bytes)
+{
+	const struct page *page = folio_page(folio, offset / PAGE_SIZE);
+	size_t safe_bytes;
+
+	if (!folio_has_hwpoisoned_page(folio))
+		return bytes;
+	if (is_ref_page_hwpoison(folio, page))
+		return 0;
+
+	/* Safe to read the remaining bytes in this page. */
+	safe_bytes = PAGE_SIZE - (offset % PAGE_SIZE);
+	page++;
+
+	/* Check each remaining page as long as we are not done yet. */
+	for (; safe_bytes < bytes; safe_bytes += PAGE_SIZE, page++)
+		if (is_ref_page_hwpoison(folio, page))
+			break;
+
+	return min(safe_bytes, bytes);
+}
+
 static void filemap_end_dropbehind_read(struct folio *folio)
 {
 	if (!folio_test_dropbehind(folio))
@@ -2828,6 +2855,22 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,
 			goto put_folios;
 		end_offset = min_t(loff_t, isize, iocb->ki_pos + iter->count);
 
+		if (!folio_batch_count(&fbatch)) {
+			size_t fsize = mapping_min_folio_nrbytes(mapping);
+			size_t offset = iocb->ki_pos & (fsize - 1);
+			size_t bytes = min_t(loff_t, end_offset - iocb->ki_pos,
+					     fsize - offset);
+			size_t copied = iov_iter_zero(bytes, iter);
+
+			already_read += copied;
+			iocb->ki_pos += copied;
+			last_pos = iocb->ki_pos;
+
+			if (copied < bytes)
+				error = -EFAULT;
+			continue;
+		}
+
 		/*
 		 * Once we start copying data, we don't want to be touching any
 		 * cachelines that might be contended:
@@ -2862,14 +2905,18 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,
 			if (writably_mapped)
 				flush_dcache_folio(folio);
 
-			copied = copy_folio_to_iter(folio, offset, bytes, iter);
+			copied = adjust_range_hwpoison(folio, offset, bytes);
+			if (copied < bytes)
+				error = -EIO;
+			copied = copy_folio_to_iter(folio, offset, copied, iter);
 
 			already_read += copied;
 			iocb->ki_pos += copied;
 			last_pos = iocb->ki_pos;
 
 			if (copied < bytes) {
-				error = -EFAULT;
+				if (!error)
+					error = -EFAULT;
 				break;
 			}
 		}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2bccb0a53a0a60..3533403091074c 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4413,7 +4413,7 @@ static bool thp_underused(struct folio *folio)
 	if (khugepaged_max_ptes_none == HPAGE_PMD_NR - 1)
 		return false;
 
-	if (folio_contain_hwpoisoned_page(folio))
+	if (folio_has_hwpoisoned_page(folio))
 		return false;
 
 	for (i = 0; i < folio_nr_pages(folio); i++) {
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 571212b80835ec..aaac6e2af4c24a 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1255,7 +1255,7 @@ static struct folio *dequeue_hugetlb_folio_node_exact(struct hstate *h,
 		if (pin && !folio_is_longterm_pinnable(folio))
 			continue;
 
-		if (folio_test_hwpoison(folio))
+		if (folio_test_has_hwpoisoned(folio))
 			continue;
 
 		if (is_migrate_isolate_page(&folio->page))
@@ -1498,10 +1498,10 @@ static void __update_and_free_hugetlb_folio(struct hstate *h,
 	}
 
 	/*
-	 * Move PageHWPoison flag from head page to the raw error pages,
-	 * which makes any healthy subpages reusable.
+	 * Move HWPoison flag to each error page
+	 * which makes any healthy pages reusable.
 	 */
-	if (unlikely(folio_test_hwpoison(folio)))
+	if (unlikely(folio_test_has_hwpoisoned(folio)))
 		folio_clear_hugetlb_hwpoison(folio);
 
 	VM_BUG_ON_FOLIO(folio_ref_count(folio), folio);
@@ -1775,7 +1775,7 @@ void init_new_hugetlb_folio(struct folio *folio)
  * stable.  Due to locking order, we can only trylock_write.  If we can
  * not get the lock, simply return NULL to caller.
  */
-struct address_space *hugetlb_folio_mapping_lock_write(struct folio *folio)
+struct address_space *hugetlb_folio_mapping_lock_write(const struct folio *folio)
 {
 	struct address_space *mapping = folio_mapping(folio);
 
@@ -3998,7 +3998,7 @@ long demote_pool_huge_page(struct hstate *src, nodemask_t *nodes_allowed,
 		struct folio *folio, *next;
 
 		list_for_each_entry_safe(folio, next, &src->hugepage_freelists[node], lru) {
-			if (folio_test_hwpoison(folio))
+			if (folio_test_has_hwpoisoned(folio))
 				continue;
 
 			remove_hugetlb_folio(src, folio, false);
@@ -5809,7 +5809,7 @@ static vm_fault_t hugetlb_no_page(struct address_space *mapping,
 		 * don't have hwpoisoned swap entry for errored virtual address.
 		 * So we need to block hugepage fault by PG_hwpoison bit check.
 		 */
-		if (unlikely(folio_test_hwpoison(folio))) {
+		if (unlikely(folio_test_has_hwpoisoned(folio))) {
 			ret = VM_FAULT_HWPOISON_LARGE |
 				VM_FAULT_SET_HINDEX(hstate_index(h));
 			goto backout_unlocked;
@@ -6318,7 +6318,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte,
 	ptl = huge_pte_lock(h, dst_mm, dst_pte);
 
 	ret = -EIO;
-	if (folio_test_hwpoison(folio))
+	if (folio_test_has_hwpoisoned(folio))
 		goto out_release_unlock;
 
 	ret = -EEXIST;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 51508a55c4055e..73ebbc93566182 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1810,50 +1810,65 @@ EXPORT_SYMBOL_GPL(mf_dax_kill_procs);
 
 /*
  * Struct raw_hwp_page represents information about "raw error page",
- * constructing singly linked list from ->_hugetlb_hwpoison field of folio.
+ * constructing singly linked list from folio->hugetlb_hwpoison field.
  */
 struct raw_hwp_page {
 	struct llist_node node;
 	struct page *page;
 };
 
-static inline struct llist_head *raw_hwp_list_head(struct folio *folio)
+static bool precise_page_poisoned(const struct folio *folio,
+		const struct page *page)
 {
-	return (struct llist_head *)&folio->_hugetlb_hwpoison;
-}
-
-bool is_raw_hwpoison_page_in_hugepage(struct page *page)
-{
-	struct llist_head *raw_hwp_head;
-	struct raw_hwp_page *p;
-	struct folio *folio = page_folio(page);
-	bool ret = false;
-
-	if (!folio_test_hwpoison(folio))
-		return false;
-
-	if (!folio_test_hugetlb(folio))
-		return PageHWPoison(page);
+	const struct llist_head *list = &folio->hugetlb_hwpoison;
+	const struct raw_hwp_page *p;
 
 	/*
-	 * When RawHwpUnreliable is set, kernel lost track of which subpages
-	 * are HWPOISON. So return as if ALL subpages are HWPOISONed.
+	 * When RawHwpUnreliable is set, kernel lost track of which pages
+	 * are HWPOISON. So return as if ALL pages are HWPOISONed.
 	 */
 	if (folio_test_hugetlb_raw_hwp_unreliable(folio))
 		return true;
 
-	mutex_lock(&mf_mutex);
-
-	raw_hwp_head = raw_hwp_list_head(folio);
-	llist_for_each_entry(p, raw_hwp_head->first, node) {
-		if (page == p->page) {
-			ret = true;
-			break;
-		}
+	llist_for_each_entry(p, READ_ONCE(list->first), node) {
+		if (page == p->page)
+			return true;
 	}
 
-	mutex_unlock(&mf_mutex);
+	return false;
+}
 
+/*
+ * Check if a given @page in a hugetlb folio is HWPOISON.
+ */
+bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page)
+{
+	if (!folio_test_has_hwpoisoned(folio))
+		return false;
+
+	return precise_page_poisoned(folio, page);
+}
+
+/*
+ * We have no reference on the folio containing this page.
+ * The hugetlb_lock keeps hugetlb folios from being freed.
+ */
+bool hugetlb_unref_page_hwpoison(const struct page *page)
+{
+	const struct folio *folio;
+	unsigned long flags;
+	bool ret;
+
+	spin_lock_irqsave(&hugetlb_lock, flags);
+	folio = page_folio(page);
+	if (!folio_test_huge_poison(folio)) {
+		ret = PageHWPoison(page);
+		goto unlock;
+	}
+
+	ret = precise_page_poisoned(folio, page);
+unlock:
+	spin_unlock_irqrestore(&hugetlb_lock, flags);
 	return ret;
 }
 
@@ -1863,7 +1878,7 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
 	struct raw_hwp_page *p, *next;
 	unsigned long count = 0;
 
-	head = llist_del_all(raw_hwp_list_head(folio));
+	head = llist_del_all(&folio->hugetlb_hwpoison);
 	llist_for_each_entry_safe(p, next, head, node) {
 		if (move_flag)
 			SetPageHWPoison(p->page);
@@ -1881,26 +1896,41 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
 #define	MF_HUGETLB_FOLIO_PRE_POISONED	3	/* folio already poisoned */
 #define	MF_HUGETLB_PAGE_PRE_POISONED	4	/* exact page already poisoned */
 #define	MF_HUGETLB_RETRY		5	/* hugepage is busy, retry */
+
+static inline int hugetlb_set_poison(struct folio *folio)
+{
+	if (folio_test_set_has_hwpoisoned(folio))
+		return MF_HUGETLB_FOLIO_PRE_POISONED;
+	folio_set_huge_poison(folio);
+	return 0;
+}
+
+static inline int hugetlb_clear_poison(struct folio *folio)
+{
+	if (!folio_test_clear_has_hwpoisoned(folio))
+		return -EBUSY;
+	folio_clear_huge_poison(folio);
+	return 0;
+}
+
 /*
  * Set hugetlb folio as hwpoisoned, update folio private raw hwpoison list
  * to keep track of the poisoned pages.
  */
 static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)
 {
-	struct llist_head *head;
 	struct raw_hwp_page *raw_hwp;
 	struct raw_hwp_page *p;
-	int ret = folio_test_set_hwpoison(folio) ? MF_HUGETLB_FOLIO_PRE_POISONED : 0;
+	int ret = hugetlb_set_poison(folio);
 
 	/*
 	 * Once the hwpoison hugepage has lost reliable raw error info,
-	 * there is little meaning to keep additional error info precisely,
-	 * so skip to add additional raw error info.
+	 * there is no point in keeping additional error info precisely,
+	 * so skip adding additional raw error info.
 	 */
 	if (folio_test_hugetlb_raw_hwp_unreliable(folio))
 		return MF_HUGETLB_FOLIO_PRE_POISONED;
-	head = raw_hwp_list_head(folio);
-	llist_for_each_entry(p, head->first, node) {
+	llist_for_each_entry(p, folio->hugetlb_hwpoison.first, node) {
 		if (p->page == page)
 			return MF_HUGETLB_PAGE_PRE_POISONED;
 	}
@@ -1908,7 +1938,7 @@ static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)
 	raw_hwp = kmalloc_obj(struct raw_hwp_page, GFP_ATOMIC);
 	if (raw_hwp) {
 		raw_hwp->page = page;
-		llist_add(&raw_hwp->node, head);
+		llist_add(&raw_hwp->node, &folio->hugetlb_hwpoison);
 	} else {
 		/*
 		 * Failed to save raw error info.  We no longer trace all
@@ -1950,8 +1980,8 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
 		return;
 	if (folio_test_hugetlb_vmemmap_optimized(folio))
 		return;
-	folio_clear_hwpoison(folio);
 	folio_free_raw_hwp(folio, true);
+	folio_clear_has_hwpoisoned(folio);
 }
 
 static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
@@ -2104,6 +2134,11 @@ static inline unsigned long folio_free_raw_hwp(struct folio *folio, bool flag)
 {
 	return 0;
 }
+
+static inline int hugetlb_clear_poison(struct folio *folio)
+{
+	return 0;
+}
 #endif	/* CONFIG_HUGETLB_PAGE */
 
 /* Drop the extra refcount in case we come from madvise() */
@@ -2677,7 +2712,12 @@ int unpoison_memory(unsigned long pfn)
 	p = pfn_to_online_page(pfn);
 	if (!p)
 		return -EIO;
-	folio = page_folio(p);
+
+	if (!is_page_hwpoison(p)) {
+		unpoison_pr_info("%#lx: page is not poisoned\n",
+				 pfn, &unpoison_rs);
+		return -EBUSY;
+	}
 
 	mutex_lock(&mf_mutex);
 
@@ -2688,6 +2728,8 @@ int unpoison_memory(unsigned long pfn)
 		goto unlock_mutex;
 	}
 
+	folio = page_folio(p);
+
 	if (is_huge_zero_folio(folio)) {
 		unpoison_pr_info("%#lx: huge zero page is not supported\n",
 				 pfn, &unpoison_rs);
@@ -2695,12 +2737,6 @@ int unpoison_memory(unsigned long pfn)
 		goto unlock_mutex;
 	}
 
-	if (!PageHWPoison(p)) {
-		unpoison_pr_info("%#lx: page was already unpoisoned\n",
-				 pfn, &unpoison_rs);
-		goto unlock_mutex;
-	}
-
 	if (folio_ref_count(folio) > 1) {
 		unpoison_pr_info("%#lx: someone grabs the hwpoison page\n",
 				 pfn, &unpoison_rs);
@@ -2730,8 +2766,8 @@ int unpoison_memory(unsigned long pfn)
 			count = folio_free_raw_hwp(folio, false);
 			if (count == 0)
 				goto unlock_mutex;
+			ret = hugetlb_clear_poison(folio);
 		}
-		ret = folio_test_clear_hwpoison(folio) ? 0 : -EBUSY;
 	} else if (ghp < 0) {
 		if (ghp == -EHWPOISON) {
 			ret = put_page_back_buddy(p) ? 0 : -EBUSY;
@@ -2740,16 +2776,18 @@ int unpoison_memory(unsigned long pfn)
 			unpoison_pr_info("%#lx: failed to grab page\n",
 					 pfn, &unpoison_rs);
 		}
-	} else {
-		if (folio_test_hugetlb(folio)) {
-			huge = true;
-			count = folio_free_raw_hwp(folio, false);
-			if (count == 0) {
-				folio_put(folio);
-				goto unlock_mutex;
-			}
+	} else if (folio_test_hugetlb(folio)) {
+		huge = true;
+		count = folio_free_raw_hwp(folio, false);
+		if (count == 0) {
+			folio_put(folio);
+			goto unlock_mutex;
 		}
-
+		folio_put(folio);
+		ret = hugetlb_clear_poison(folio);
+		if (!ret)
+			folio_put(folio);
+	} else {
 		folio_put(folio);
 		if (TestClearPageHWPoison(p)) {
 			folio_put(folio);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 7ac19fab226324..33007e8e546a09 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1813,7 +1813,7 @@ static void do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
 		if (folio_test_large(folio))
 			pfn = folio_pfn(folio) + folio_nr_pages(folio) - 1;
 
-		if (folio_contain_hwpoisoned_page(folio)) {
+		if (folio_has_hwpoisoned_page(folio)) {
 			/*
 			 * unmap_poisoned_folio() cannot handle large folios
 			 * in all cases yet.
diff --git a/mm/shmem.c b/mm/shmem.c
index b51f83c970bb37..492c9bef362074 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3240,7 +3240,7 @@ shmem_write_begin(const struct kiocb *iocb, struct address_space *mapping,
 	if (ret)
 		return ret;
 
-	if (folio_contain_hwpoisoned_page(folio)) {
+	if (folio_has_hwpoisoned_page(folio)) {
 		folio_unlock(folio);
 		folio_put(folio);
 		return -EIO;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 35c3bb15ae96a3..70e54ace0e741c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1086,7 +1086,7 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
 		if (!folio_trylock(folio))
 			goto keep;
 
-		if (folio_contain_hwpoisoned_page(folio)) {
+		if (folio_has_hwpoisoned_page(folio)) {
 			/*
 			 * unmap_poisoned_folio() can't handle large
 			 * folio, just skip it. memory_failure() will
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index db57c5766ab604..94eedcc7f1a9e0 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -774,7 +774,7 @@ static struct folio *__kvm_gmem_get_pfn(struct file *file,
 	if (IS_ERR(folio))
 		return folio;
 
-	if (folio_test_hwpoison(folio)) {
+	if (folio_has_hwpoisoned_page(folio)) {
 		folio_unlock(folio);
 		folio_put(folio);
 		return ERR_PTR(-EHWPOISON);

3/2 2026/07/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14734
							output: 21
							thoughts: 800
						

Thoughts:
**Fuzzing Strategy for This Kernel Patch**

Okay, so I've got this patch series in front of me, and my immediate concern is whether it introduces new functionality or modifies existing code in a way that warrants targeted fuzzing. It's clearly touching a significant chunk of the kernel: `fs/hugetlbfs/inode.c`, various include files related to memory management, and several core memory management files (`mm/Kconfig`, `mm/filemap.c`, etc.). Looks like the main focus is around handling HWPOISON (hardware-injected poison) within large folios, specifically in hugetlbfs and filemap.

The major changes are replacing `hugetlbfs_read_iter` with `generic_file_read_iter`, modifying `filemap_read` to account for hwpoisoned pages by adding `adjust_range_hwpoison`, and changing how HWPOISON is tracked within folios using `folio->hugetlb_hwpoison`. There are also some new functions introduced: `folio_has_hwpoisoned_page` and `is_ref_page_hwpoison`.

Now, the crucial question is, is this code reachable and functional? Yes, it's modifying fundamental memory management and how hugetlbfs read operations work, so we need to test this. And what about virtualization? We can trigger HWPOISON via `madvise(MADV_HWPOISON)` or through debugfs, so we can test it in a virtualized environment.

Now, let's nail down what functions to focus on for fuzzing. `filemap_read` is a hot-path function, so it's a good candidate, but `adjust_range_hwpoison` is new and specific to this patch. Wait a second, it's a static function in `mm/filemap.c`. That limits its direct call sites. So, we're better off focusing on the entry points: `hugetlb_update_hwpoison`, `hugetlb_page_hwpoison`, `hugetlb_unref_page_hwpoison`, `unpoison_memory`, and `folio_clear_hugetlb_hwpoison`. Although `filemap_get_read_batch` and `filemap_get_pages` are also modified, these are hot paths, so not high priority.

Since `adjust_range_hwpoison` is static, let's confirm this by checking the code. Yes, it is static. Okay, let's move back to the previously mentioned functions: `hugetlb_update_hwpoison`, `hugetlb_page_hwpoison`, `hugetlb_unref_page_hwpoison`, and `unpoison_memory`. We should also include the `folio_clear_hugetlb_hwpoison` function.

Finally, we need to think about configurations. `CONFIG_MEMORY_FAILURE` is clearly required for HWPOISON. `CONFIG_HUGETLBFS` and `CONFIG_HUGETLB_PAGE` are necessary for the hugetlbfs bits. `CONFIG_LARGE_FOLIO` is a given, as it's selected by `HUGETLB_PAGE` and `TRANSPARENT_HUGEPAGE`. Ah, but I need to be sure about `CONFIG_FILEMAP_LARGE_FOLIO`. It's used instead of `CONFIG_TRANSPARENT_HUGEPAGE`, and that's critical. Let me check the kernel config for `CONFIG_FILEMAP_LARGE_FOLIO`. I'll grep for that quickly to make sure it exists and that I understand where it's defined.




4/2 2026/07/24 18:46 tool grepper
0m Args:
{
  "Expression": "FILEMAP_LARGE_FOLIO"
}

Results:
{
  "Output": "include/linux/pagemap.h=375=static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)\n--\ninclude/linux/pagemap.h-386- */\ninclude/linux/pagemap.h:387:#ifdef CONFIG_FILEMAP_LARGE_FOLIO\ninclude/linux/pagemap.h-388-#define PREFERRED_MAX_PAGECACHE_ORDER\tHPAGE_PMD_ORDER\n--\ninclude/linux/pagemap.h=406=static inline size_t mapping_max_folio_size_supported(void)\ninclude/linux/pagemap.h-407-{\ninclude/linux/pagemap.h:408:\tif (IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\ninclude/linux/pagemap.h-409-\t\treturn 1U \u003c\u003c (PAGE_SHIFT + MAX_PAGECACHE_ORDER);\n--\ninclude/linux/pagemap.h=431=static inline void mapping_set_folio_order_range(struct address_space *mapping,\n--\ninclude/linux/pagemap.h-433-{\ninclude/linux/pagemap.h:434:\tif (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\ninclude/linux/pagemap.h-435-\t\treturn;\n--\ninclude/linux/pagemap.h=466=mapping_max_folio_order(const struct address_space *mapping)\ninclude/linux/pagemap.h-467-{\ninclude/linux/pagemap.h:468:\tif (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\ninclude/linux/pagemap.h-469-\t\treturn 0;\n--\ninclude/linux/pagemap.h=474=mapping_min_folio_order(const struct address_space *mapping)\ninclude/linux/pagemap.h-475-{\ninclude/linux/pagemap.h:476:\tif (!IS_ENABLED(CONFIG_FILEMAP_LARGE_FOLIO))\ninclude/linux/pagemap.h-477-\t\treturn 0;\n--\ninclude/linux/pagemap.h=512=static inline bool mapping_large_folio_support(const struct address_space *mapping)\n--\ninclude/linux/pagemap.h-531- */\ninclude/linux/pagemap.h:532:#ifdef CONFIG_FILEMAP_LARGE_FOLIO\ninclude/linux/pagemap.h-533-static inline bool mapping_pmd_folio_support(const struct address_space *mapping)\n"
}

5/2 2026/07/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16989
							output: 22
							thoughts: 55
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Investigating the Missing Definition**

Hmm, `CONFIG_FILEMAP_LARGE_FOLIO`... It's looking like this symbol isn't defined where I expected it to be, or perhaps it's simply not present in this particular patch. Before I jump to conclusions, I need to verify its availability. Right, the standard places to check for these sorts of configurations are the kernel's Kconfig files. Specifically, I should look in `mm/Kconfig`, as it seems memory management related, and also consider `fs/Kconfig` since it's about file systems. Let's start there and see if I can find where `FILEMAP_LARGE_FOLIO` is controlled. I need to know *why* it is or isn't available before I can proceed.



6/2 2026/07/24 18:46 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config FILEMAP_LARGE_FOLIO"
}

Results:
{
  "error": "no matches"
}

7/2 2026/07/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17266
							output: 19
							thoughts: 17
						

Thoughts:
Okay, let's see...

**Checking the Kconfig for `LARGE_FOLIO`**

Alright, my first thought is to dig into the `mm/Kconfig` file. I'm going to quickly check if `LARGE_FOLIO` is defined there. That's the logical starting point for understanding how this feature is configured within the memory management subsystem. I need to see the dependencies, help text, and default settings - all the usual Kconfig goodies. This initial check will give me a good lay of the land and point me in the right direction. No point in wandering aimlessly; let's get a baseline understanding of `LARGE_FOLIO` from the source.



8/2 2026/07/24 18:46 tool grepper
0m Args:
{
  "Expression": "config LARGE_FOLIO"
}

Results:
{
  "Output": "mm/Kconfig=843=config MM_ID\n--\nmm/Kconfig-845-\nmm/Kconfig:846:config LARGE_FOLIO\nmm/Kconfig-847-\tdef_bool n\n"
}

9/2 2026/07/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17515
							output: 19
							thoughts: 28
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, and delivered as a first-person summary:

**Kernel Memory Management Considerations**

Okay, so I see that the `LARGE_FOLIO` option has been introduced within the `mm/Kconfig` file. That's good, that's what I was expecting to find. But wait, a little flag just went off in my head. Whenever I see these memory management additions, my immediate thought is *always* about how it interacts with error handling. Specifically, how does this interplay with `MEMORY_FAILURE`? It's a critical component in ensuring system stability when things go sideways with memory allocation, and I need to consider how this new feature integrates with those existing safeguards. Is there explicit handling of `LARGE_FOLIO` allocation failures, or does it fall back to some generic strategy? I need to check the Kconfig's surrounding code and the usage patterns to make sure this doesn't introduce any subtle regressions or blind spots in the memory failure handling path.



10/2 2026/07/24 18:46 tool grepper
0m Args:
{
  "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/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17854
							output: 19
							thoughts: 32
						

Thoughts:
Okay, here's my interpretation of those thoughts, presented as my own mental process, given my expertise:

**Checking Hardware Poison Configuration**

Alright, let me quickly parse this. `MEMORY_FAILURE` - ah yes, that's the configuration option we use to enable hardware poison handling. I need to make sure the system is behaving as expected with respect to that. My next immediate thought is to check related memory features. My expertise is that the system's reaction to hardware errors is impacted by several configuration and runtime options. Thus I'll now quickly drill down on `HUGETLBFS` and `HUGETLB_PAGE`. I want to immediately understand the interaction between those settings and how hardware errors are handled in this particular configuration. Essentially, I'm trying to quickly establish a baseline or pinpoint any potential conflicts or areas that require special attention related to how large pages, and the large page file system, are involved in memory error handling. The first step will be checking their states.



12/2 2026/07/24 18:46 tool grepper
0m Args:
{
  "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"
}

13/2 2026/07/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18190
							output: 46
							thoughts: 218
						

Thoughts:
**Analyzing Huge Page Poisoning in the Kernel**

Okay, so I'm looking at memory failure scenarios related to huge pages. My initial focus is on the configurations: `MEMORY_FAILURE`, `HUGETLBFS`, and `HUGETLB_PAGE`. Right, and I see `LARGE_FOLIO` is being selected by both `HUGETLB_PAGE` and `TRANSPARENT_HUGEPAGE`, which seems correct.

Now, I need to pinpoint the relevant symbols. Let's see... I have `hugetlb_update_hwpoison`, `hugetlb_page_hwpoison`, `hugetlb_unref_page_hwpoison`, `unpoison_memory`, and `folio_clear_hugetlb_hwpoison`. Those seem like they're directly related to the hardware poisoning aspect of huge pages. I'm also noting `adjust_range_hwpoison`, which is static, so that's probably not a good starting point, although it's related. I considered `filemap_read`, since that's a hot path, but it's probably too general. Maybe `hugetlbfs_init_regular_inode`? That's also static, so probably not the best spot to start either. Let me consider `hugetlb_folio_mapping_lock_write`. That sounds like it could be relevant, since we are talking about memory failures.

Alright, the next thing is to dive into `mm/memory-failure.c` to see if there are any useful exported or non-static functions within that file. That should give me a better view of where I can make an impact.



14/2 2026/07/24 18:46 tool grepper
0m Args:
{
  "Expression": "^([a-z_]+ )+hugetlb_[a-z_]+\\(",
  "PathPrefix": "mm/memory-failure.c"
}

Results:
{
  "Output": "mm/memory-failure.c=1820=static bool precise_page_poisoned(const struct folio *folio,\n--\nmm/memory-failure.c-1843- */\nmm/memory-failure.c:1844:bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page)\nmm/memory-failure.c-1845-{\n--\nmm/memory-failure.c-1855- */\nmm/memory-failure.c:1856:bool hugetlb_unref_page_hwpoison(const struct page *page)\nmm/memory-failure.c-1857-{\n--\nmm/memory-failure.c=1875=static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)\n--\nmm/memory-failure.c-1899-\nmm/memory-failure.c:1900:static inline int hugetlb_set_poison(struct folio *folio)\nmm/memory-failure.c-1901-{\n--\nmm/memory-failure.c-1907-\nmm/memory-failure.c:1908:static inline int hugetlb_clear_poison(struct folio *folio)\nmm/memory-failure.c-1909-{\n--\nmm/memory-failure.c-1919- */\nmm/memory-failure.c:1920:static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)\nmm/memory-failure.c-1921-{\n--\nmm/memory-failure.c=2133=static inline unsigned long folio_free_raw_hwp(struct folio *folio, bool flag)\n--\nmm/memory-failure.c-2137-\nmm/memory-failure.c:2138:static inline int hugetlb_clear_poison(struct folio *folio)\nmm/memory-failure.c-2139-{\n"
}

15/2 2026/07/24 18:46 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19299
							output: 34
							thoughts: 40
						

Thoughts:
Here's my take on the changes I'm seeing:

**Analyzing Recent Code Modifications in the HugeTLB Subsystem**

Alright, let's break down these code changes. First, it looks like `hugetlb_page_hwpoison` and `hugetlb_unref_page_hwpoison` are now exposed beyond their previous scope – they're not static anymore. That's a significant shift. My immediate thought is, what are the implications for how other modules or functions will interact with them? Are we seeing a move towards a more general hardware poison handling strategy, or are these functions being repurposed for something specific within the NUMA architecture of the HugeTLB system? I need to dig into the call sites to understand the new dependencies that are being introduced.

Also, `unpoison_memory` has also been touched. This is interesting. What's the nature of the modification? Is it a change to the unpoisoning algorithm itself, an expansion of its capabilities, or perhaps a modification of the error reporting or logging? I'll need to carefully review the diff to understand what the intent of the change is. I need to keep in mind, specifically, that unpoisoning is a performance-critical function and any changes to it could have significant ramifications on the overall system's stability, especially if it is changed in a non-obvious way.



16/2 2026/07/24 18:47 tool grepper
0m Args:
{
  "Expression": "^int unpoison_memory\\(",
  "PathPrefix": "mm/memory-failure.c"
}

Results:
{
  "Output": "mm/memory-failure.c=2680=core_initcall(memory_failure_init);\n--\nmm/memory-failure.c-2701- */\nmm/memory-failure.c:2702:int unpoison_memory(unsigned long pfn)\nmm/memory-failure.c-2703-{\n"
}

17/2 2026/07/24 18:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19754
							output: 46
							thoughts: 17
						

Thoughts:
Here's a summary of my thought process:

**Diving into mm/hugetlb.c for Clues**

Okay, so I need to understand what's been changed in the huge pages code. My first instinct is to dive directly into `mm/hugetlb.c`. It's the central hub for handling huge page management, so any modifications related to performance or new features are likely to be reflected there. I'm going to look for the key functions that have been touched since whatever the baseline is – probably a previous kernel version or a specific commit. This will let me quickly pinpoint the code responsible for the behavior I'm investigating. I expect to be looking at things like page allocation, deallocation, mapping, unmapping, and any related locking or NUMA-awareness mechanisms. The diffs will be the crucial part, of course. Let's see what's changed and what it tells us.



18/2 2026/07/24 18:47 tool grepper
0m Args:
{
  "Expression": "^([a-z_]+ )+hugetlb_[a-z_]+\\(",
  "PathPrefix": "mm/hugetlb.c"
}

Results:
{
  "Output": "mm/hugetlb.c=92=static int hugetlb_param_index __initdata;\nmm/hugetlb.c:93:static __init int hugetlb_add_param(char *s, int (*setup)(char *val));\nmm/hugetlb.c:94:static __init void hugetlb_parse_params(void);\nmm/hugetlb.c-95-\n--\nmm/hugetlb.c=114=struct mutex *hugetlb_fault_mutex_table __ro_after_init;\n--\nmm/hugetlb.c-116-/* Forward declaration */\nmm/hugetlb.c:117:static int hugetlb_acct_memory(struct hstate *h, long delta);\nmm/hugetlb.c:118:static void hugetlb_vma_lock_free(struct vm_area_struct *vma);\nmm/hugetlb.c:119:static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma);\nmm/hugetlb.c-120-static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma);\nmm/hugetlb.c=121=static int __huge_pmd_unshare(struct mmu_gather *tlb,\n--\nmm/hugetlb.c-123-\t\tbool check_locks);\nmm/hugetlb.c:124:static void hugetlb_unshare_pmds(struct vm_area_struct *vma,\nmm/hugetlb.c-125-\t\tunsigned long start, unsigned long end, bool take_locks);\n--\nmm/hugetlb.c=278=static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma)\n--\nmm/hugetlb.c-285- */\nmm/hugetlb.c:286:void hugetlb_vma_lock_read(struct vm_area_struct *vma)\nmm/hugetlb.c-287-{\n--\nmm/hugetlb.c-298-\nmm/hugetlb.c:299:void hugetlb_vma_unlock_read(struct vm_area_struct *vma)\nmm/hugetlb.c-300-{\n--\nmm/hugetlb.c-311-\nmm/hugetlb.c:312:void hugetlb_vma_lock_write(struct vm_area_struct *vma)\nmm/hugetlb.c-313-{\n--\nmm/hugetlb.c-324-\nmm/hugetlb.c:325:void hugetlb_vma_unlock_write(struct vm_area_struct *vma)\nmm/hugetlb.c-326-{\n--\nmm/hugetlb.c-337-\nmm/hugetlb.c:338:int hugetlb_vma_trylock_write(struct vm_area_struct *vma)\nmm/hugetlb.c-339-{\n--\nmm/hugetlb.c-353-\nmm/hugetlb.c:354:void hugetlb_vma_assert_locked(struct vm_area_struct *vma)\nmm/hugetlb.c-355-{\n--\nmm/hugetlb.c-366-\nmm/hugetlb.c:367:void hugetlb_vma_lock_release(struct kref *kref)\nmm/hugetlb.c-368-{\n--\nmm/hugetlb.c=390=static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma)\n--\nmm/hugetlb.c-403-\nmm/hugetlb.c:404:static void hugetlb_vma_lock_free(struct vm_area_struct *vma)\nmm/hugetlb.c-405-{\n--\nmm/hugetlb.c-419-\nmm/hugetlb.c:420:static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma)\nmm/hugetlb.c-421-{\n--\nmm/hugetlb.c=853=static long region_del(struct resv_map *resv, long f, long t)\n--\nmm/hugetlb.c-955- */\nmm/hugetlb.c:956:void hugetlb_fix_reserve_counts(struct inode *inode)\nmm/hugetlb.c-957-{\n--\nmm/hugetlb.c=1171=bool __vma_private_lock(struct vm_area_struct *vma)\n--\nmm/hugetlb.c-1177-\nmm/hugetlb.c:1178:void hugetlb_dup_vma_private(struct vm_area_struct *vma)\nmm/hugetlb.c-1179-{\n--\nmm/hugetlb.c=3080=int __alloc_bootmem_huge_page(struct hstate *h, int nid)\n--\nmm/hugetlb.c-3117-/* Initialize [start_page:end_page_number] tail struct pages of a hugepage */\nmm/hugetlb.c:3118:static void __init hugetlb_folio_init_tail_vmemmap(struct folio *folio,\nmm/hugetlb.c-3119-\t\t\t\t\tstruct hstate *h,\n--\nmm/hugetlb.c-3140-\nmm/hugetlb.c:3141:static void __init hugetlb_folio_init_vmemmap(struct folio *folio,\nmm/hugetlb.c-3142-\t\t\t\t\t      struct hstate *h,\n--\nmm/hugetlb.c-3159-\nmm/hugetlb.c:3160:static bool __init hugetlb_bootmem_page_prehvo(struct huge_bootmem_page *m)\nmm/hugetlb.c-3161-{\n--\nmm/hugetlb.c-3164-\nmm/hugetlb.c:3165:static bool __init hugetlb_bootmem_page_earlycma(struct huge_bootmem_page *m)\nmm/hugetlb.c-3166-{\n--\nmm/hugetlb.c-3179- */\nmm/hugetlb.c:3180:static void __init hugetlb_bootmem_init_migratetype(struct folio *folio,\nmm/hugetlb.c-3181-\t\t\t\t\t\t\t  struct hstate *h)\n--\nmm/hugetlb.c=3196=static void __init prep_and_add_bootmem_folios(struct hstate *h,\n--\nmm/hugetlb.c-3225-\nmm/hugetlb.c:3226:bool __init hugetlb_bootmem_page_zones_valid(int nid,\nmm/hugetlb.c-3227-\t\t\t\t\t     struct huge_bootmem_page *m)\n--\nmm/hugetlb.c-3262- */\nmm/hugetlb.c:3263:static void __init hugetlb_bootmem_free_invalid_page(int nid, struct page *page,\nmm/hugetlb.c-3264-\t\t\t\t\t     struct hstate *h)\n--\nmm/hugetlb.c=3356=static void __init gather_bootmem_prealloc(void)\n--\nmm/hugetlb.c-3371-\nmm/hugetlb.c:3372:static void __init hugetlb_hstate_alloc_pages_onenode(struct hstate *h, int nid)\nmm/hugetlb.c-3373-{\n--\nmm/hugetlb.c-3414-\nmm/hugetlb.c:3415:static bool __init hugetlb_hstate_alloc_pages_specific_nodes(struct hstate *h)\nmm/hugetlb.c-3416-{\n--\nmm/hugetlb.c-3429-\nmm/hugetlb.c:3430:static void __init hugetlb_hstate_alloc_pages_errcheck(unsigned long allocated, struct hstate *h)\nmm/hugetlb.c-3431-{\n--\nmm/hugetlb.c-3441-\nmm/hugetlb.c:3442:static void __init hugetlb_pages_alloc_boot_node(unsigned long start, unsigned long end, void *arg)\nmm/hugetlb.c-3443-{\n--\nmm/hugetlb.c-3472-\nmm/hugetlb.c:3473:static unsigned long __init hugetlb_gigantic_pages_alloc_boot(struct hstate *h)\nmm/hugetlb.c-3474-{\n--\nmm/hugetlb.c-3485-\nmm/hugetlb.c:3486:static unsigned long __init hugetlb_pages_alloc_boot(struct hstate *h)\nmm/hugetlb.c-3487-{\n--\nmm/hugetlb.c-3564- */\nmm/hugetlb.c:3565:static void __init hugetlb_hstate_alloc_pages(struct hstate *h)\nmm/hugetlb.c-3566-{\n--\nmm/hugetlb.c-3594-\nmm/hugetlb.c:3595:static void __init hugetlb_init_hstates(void)\nmm/hugetlb.c-3596-{\n--\nmm/hugetlb.c=4047=ssize_t __nr_hugepages_store_common(bool obey_mempolicy,\n--\nmm/hugetlb.c-4079-\nmm/hugetlb.c:4080:static int __init hugetlb_init(void)\nmm/hugetlb.c-4081-{\n--\nmm/hugetlb.c=4153=bool __init __attribute((weak)) arch_hugetlb_valid_size(unsigned long size)\n--\nmm/hugetlb.c-4157-\nmm/hugetlb.c:4158:void __init hugetlb_add_hstate(unsigned int order)\nmm/hugetlb.c-4159-{\n--\nmm/hugetlb.c-4181-\nmm/hugetlb.c:4182:bool __init __weak hugetlb_node_alloc_supported(void)\nmm/hugetlb.c-4183-{\n--\nmm/hugetlb.c=4187=static void __init hugepages_clear_pages_in_node(void)\n--\nmm/hugetlb.c-4199-\nmm/hugetlb.c:4200:static __init int hugetlb_add_param(char *s, int (*setup)(char *))\nmm/hugetlb.c-4201-{\n--\nmm/hugetlb.c-4226-\nmm/hugetlb.c:4227:static __init void hugetlb_parse_params(void)\nmm/hugetlb.c-4228-{\n--\nmm/hugetlb.c=4442=hugetlb_early_param(\"default_hugepagesz\", default_hugepagesz_setup);\nmm/hugetlb.c-4443-\nmm/hugetlb.c:4444:void __init hugetlb_bootmem_set_nodes(void)\nmm/hugetlb.c-4445-{\n--\nmm/hugetlb.c-4457-\nmm/hugetlb.c:4458:void __init hugetlb_bootmem_alloc(void)\nmm/hugetlb.c-4459-{\n--\nmm/hugetlb.c=4500=static unsigned int allowed_mems_nr(struct hstate *h)\n--\nmm/hugetlb.c-4516-\nmm/hugetlb.c:4517:void hugetlb_report_meminfo(struct seq_file *m)\nmm/hugetlb.c-4518-{\n--\nmm/hugetlb.c-4546-\nmm/hugetlb.c:4547:int hugetlb_report_node_meminfo(char *buf, int len, int nid)\nmm/hugetlb.c-4548-{\n--\nmm/hugetlb.c-4562-\nmm/hugetlb.c:4563:void hugetlb_show_meminfo_node(int nid)\nmm/hugetlb.c-4564-{\n--\nmm/hugetlb.c-4578-\nmm/hugetlb.c:4579:void hugetlb_report_usage(struct seq_file *m, struct mm_struct *mm)\nmm/hugetlb.c-4580-{\n--\nmm/hugetlb.c-4585-/* Return the number pages of memory we physically have, in PAGE_SIZE units. */\nmm/hugetlb.c:4586:unsigned long hugetlb_total_pages(void)\nmm/hugetlb.c-4587-{\n--\nmm/hugetlb.c-4595-\nmm/hugetlb.c:4596:static int hugetlb_acct_memory(struct hstate *h, long delta)\nmm/hugetlb.c-4597-{\n--\nmm/hugetlb.c-4645-\nmm/hugetlb.c:4646:static void hugetlb_vm_op_open(struct vm_area_struct *vma)\nmm/hugetlb.c-4647-{\n--\nmm/hugetlb.c-4685-\nmm/hugetlb.c:4686:static void hugetlb_vm_op_close(struct vm_area_struct *vma)\nmm/hugetlb.c-4687-{\n--\nmm/hugetlb.c-4716-\nmm/hugetlb.c:4717:static int hugetlb_vm_op_split(struct vm_area_struct *vma, unsigned long addr)\nmm/hugetlb.c-4718-{\n--\nmm/hugetlb.c-4723-\nmm/hugetlb.c:4724:void hugetlb_split(struct vm_area_struct *vma, unsigned long addr)\nmm/hugetlb.c-4725-{\n--\nmm/hugetlb.c-4756-\nmm/hugetlb.c:4757:static unsigned long hugetlb_vm_op_pagesize(struct vm_area_struct *vma)\nmm/hugetlb.c-4758-{\n--\nmm/hugetlb.c-4767- */\nmm/hugetlb.c:4768:static vm_fault_t hugetlb_vm_op_fault(struct vm_fault *vmf)\nmm/hugetlb.c-4769-{\n--\nmm/hugetlb.c-4774-#ifdef CONFIG_USERFAULTFD\nmm/hugetlb.c:4775:static bool hugetlb_can_userfault(struct vm_area_struct *vma,\nmm/hugetlb.c-4776-\t\t\t\t  vm_flags_t vm_flags)\n--\nmm/hugetlb.c=5362=static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,\n--\nmm/hugetlb.c-5418- */\nmm/hugetlb.c:5419:static vm_fault_t hugetlb_wp(struct vm_fault *vmf)\nmm/hugetlb.c-5420-{\n--\nmm/hugetlb.c=5614=bool hugetlbfs_pagecache_present(struct hstate *h,\n--\nmm/hugetlb.c-5627-\nmm/hugetlb.c:5628:int hugetlb_add_to_page_cache(struct folio *folio, struct address_space *mapping,\nmm/hugetlb.c-5629-\t\t\t   pgoff_t idx)\n--\nmm/hugetlb.c-5656-\nmm/hugetlb.c:5657:static inline vm_fault_t hugetlb_handle_userfault(struct vm_fault *vmf,\nmm/hugetlb.c-5658-\t\t\t\t\t\t  struct address_space *mapping,\n--\nmm/hugetlb.c-5677- */\nmm/hugetlb.c:5678:static bool hugetlb_pte_stable(struct hstate *h, struct mm_struct *mm, unsigned long addr,\nmm/hugetlb.c-5679-\t\t\t       pte_t *ptep, pte_t old_pte)\n--\nmm/hugetlb.c-5690-\nmm/hugetlb.c:5691:static vm_fault_t hugetlb_no_page(struct address_space *mapping,\nmm/hugetlb.c-5692-\t\t\tstruct vm_fault *vmf)\n--\nmm/hugetlb.c=5935=u32 hugetlb_fault_mutex_hash(struct address_space *mapping, pgoff_t idx)\n--\nmm/hugetlb.c-5940-\nmm/hugetlb.c:5941:vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,\nmm/hugetlb.c-5942-\t\t\tunsigned long address, unsigned int flags)\n--\nmm/hugetlb.c=6136=static struct folio *alloc_hugetlb_folio_vma(struct hstate *h,\n--\nmm/hugetlb.c-6162- */\nmm/hugetlb.c:6163:int hugetlb_mfill_atomic_pte(pte_t *dst_pte,\nmm/hugetlb.c-6164-\t\t\t     struct vm_area_struct *dst_vma,\n--\nmm/hugetlb.c-6383-\nmm/hugetlb.c:6384:long hugetlb_change_protection(struct vm_area_struct *vma,\nmm/hugetlb.c-6385-\t\tunsigned long address, unsigned long end,\n--\nmm/hugetlb.c-6539-\nmm/hugetlb.c:6540:long hugetlb_reserve_pages(struct inode *inode,\nmm/hugetlb.c-6541-\t\tlong from, long to,\n--\nmm/hugetlb.c-6727-\nmm/hugetlb.c:6728:long hugetlb_unreserve_pages(struct inode *inode, long start, long end,\nmm/hugetlb.c-6729-\t\t\t\t\t\t\t\tlong freed)\n--\nmm/hugetlb.c=7050=pte_t *huge_pte_offset(struct mm_struct *mm,\n--\nmm/hugetlb.c-7084- */\nmm/hugetlb.c:7085:unsigned long hugetlb_mask_last_page(struct hstate *h)\nmm/hugetlb.c-7086-{\n--\nmm/hugetlb.c-7099-/* See description above.  Architectures can provide their own version. */\nmm/hugetlb.c:7100:__weak unsigned long hugetlb_mask_last_page(struct hstate *h)\nmm/hugetlb.c-7101-{\n--\nmm/hugetlb.c=7185=void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason)\n--\nmm/hugetlb.c-7240- */\nmm/hugetlb.c:7241:static void hugetlb_unshare_pmds(struct vm_area_struct *vma,\nmm/hugetlb.c-7242-\t\t\t\t   unsigned long start,\n--\nmm/hugetlb.c-7301- */\nmm/hugetlb.c:7302:void hugetlb_unshare_all_pmds(struct vm_area_struct *vma)\nmm/hugetlb.c-7303-{\n"
}

19/2 2026/07/24 18:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25196
							output: 44
							thoughts: 291
						

Thoughts:
**Analyzing Memory Poisoning in the HugeTLB and File Systems**

Okay, so I'm digging into how memory poisoning is handled, specifically within the context of HugeTLB and file systems. The functions of interest – `hugetlb_update_hwpoison`, `hugetlb_page_hwpoison`, `hugetlb_unref_page_hwpoison`, and `unpoison_memory` – reside in `mm/memory-failure.c`. Interesting… I see that `hugetlb_update_hwpoison` is static, which limits its direct accessibility from elsewhere. The others, `hugetlb_page_hwpoison`, `hugetlb_unref_page_hwpoison`, and `unpoison_memory`, are, fortunately, non-static (exported), making them available for external use.

Now, I need to understand how these are *used*. I'd normally check `mm/filemap.c` for calls related to file I/O, perhaps `filemap_read`, `filemap_get_read_batch`, or `filemap_get_pages`. Hold on, `filemap_read` is a known hot path; we want to avoid potentially disrupting that if possible. Let's step back... hmm, wasn't there something about `hugetlbfs_read_iter`? Ah yes, that was removed – so no joy there. What about `adjust_range_hwpoison`? Nope, it's static again, so also not the direct answer I'm seeking.

Okay, let's cast a wider net. What about `hugetlb_folio_mapping_lock_write` in `mm/hugetlb.c`? That might be relevant as we are dealing with HugeTLB specifically. And also, perhaps, `hugetlbfs_init_regular_inode` in `fs/hugetlbfs/inode.c`... It looks like a good place to start, so I'll dive into `fs/hugetlbfs/inode.c` now to investigate that.



20/2 2026/07/24 18:47 tool grepper
0m Args:
{
  "Expression": "^static void hugetlbfs_init_regular_inode\\(",
  "PathPrefix": "fs/hugetlbfs/inode.c"
}

Results:
{
  "Output": "fs/hugetlbfs/inode.c=757=static struct inode *hugetlbfs_get_root(struct super_block *sb,\n--\nfs/hugetlbfs/inode.c-778-\nfs/hugetlbfs/inode.c:779:static void hugetlbfs_init_regular_inode(struct inode *inode)\nfs/hugetlbfs/inode.c-780-{\n"
}

21/2 2026/07/24 18:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26089
							output: 40
							thoughts: 111
						

Thoughts:
**Focusing on Hardware Poisoning in HugeTLB Pages**

Okay, so I'm looking at `hugetlbfs_init_regular_inode` and immediately notice it's static, which, while potentially relevant, isn't where I need to focus right now. Let's drill down on the core hardware poisoning functions. I need to understand how the poison handling works in the context of HugeTLB pages. Specifically, I'm thinking about `hugetlb_page_hwpoison`, `hugetlb_unref_page_hwpoison`, and `unpoison_memory`. Those seem to be the key players in detecting, reporting, and hopefully, recovering from hardware errors.

I'm also seeing `hugetlb_set_poison` and `hugetlb_clear_poison`, which are static inline functions. That's good, they're probably optimized for their specific use within the HugeTLB context.

Wait a minute, what's this? `folio_clear_hugetlb_hwpoison` in `mm/memory-failure.c` is non-static. That's interesting, it opens up the possibility of external use, which warrants further investigation. I'll need to remember that.



22/2 2026/07/24 18:47 tool grepper
0m Args:
{
  "Expression": "^void folio_clear_hugetlb_hwpoison\\(",
  "PathPrefix": "mm/memory-failure.c"
}

Results:
{
  "Output": "mm/memory-failure.c=1958=static unsigned long folio_free_raw_hwp(struct folio *folio, bool move_flag)\n--\nmm/memory-failure.c-1976-\nmm/memory-failure.c:1977:void folio_clear_hugetlb_hwpoison(struct folio *folio)\nmm/memory-failure.c-1978-{\n"
}

23/2 2026/07/24 18:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26612
							output: 21
							thoughts: 750
						

Thoughts:
**My Analysis of the Patch for HWPoison Tracking in Large Folios**

Okay, first things first, `folio_clear_hugetlb_hwpoison` is confirmed to be non-static. Good. Now, let's see what else this patch touches. Scanning the code, I see modifications in `mm/filemap.c` (specifically `filemap_read`), `mm/huge_memory.c` (specifically `thp_underused`), `mm/shmem.c` (specifically `shmem_write_begin`), `mm/vmscan.c` (specifically `shrink_folio_list`), and `virt/kvm/guest_memfd.c` (specifically `__kvm_gmem_get_pfn`). This means it's pretty widespread.

Essentially, this patch is a functional shift in how hwpoison is handled for large folios. We're moving from a custom list (`folio->_hugetlb_hwpoison`) to a single flag (`folio->hugetlb_hwpoison`) and using `folio_has_hwpoisoned_page` instead of the old `folio_contain_hwpoisoned_page`. It also removes the custom `hugetlbfs_read_iter` and uses `generic_file_read_iter`, integrating hwpoison handling within `filemap_read`. This is significant; the change in the read path is a good indicator of where we should focus.

Given the functional nature of this change, fuzzing is definitely warranted. My primary focus symbols will be:

*   `hugetlb_page_hwpoison`
*   `hugetlb_unref_page_hwpoison`
*   `unpoison_memory`
*   `folio_clear_hugetlb_hwpoison`

Now, let's consider the relevant configurations. The critical ones for this area are:

*   `MEMORY_FAILURE` - This is non-negotiable for hwpoison.
*   `HUGETLBFS` - Pretty obvious, given the names.
*   `HUGETLB_PAGE` - Also expected.
*   `TRANSPARENT_HUGEPAGE` - Also, added.
*   `KVM` - This one is important for `virt/kvm/guest_memfd.c`.

AHA! The `LARGE_FOLIO` config *is* new, but it's selected by `HUGETLB_PAGE` and `TRANSPARENT_HUGEPAGE`. So, including it is important to include.

Double-checking my config assumptions, `MEMORY_FAILURE` seems to be the only *required* one for the core hwpoison logic.

Quick side-note. Even though `shmem_write_begin` and `shrink_folio_list` are modified, they are hot paths. We should de-prioritize them for now.

Wait... `unpoison_memory` is exported. That's interesting. I know it can be called through debugfs or madvise. But, ah-ha! It's called from `hwpoison_unpoison` in `mm/hwpoison-inject.c`. I need to ensure that `HWPOISON_INJECT` is enabled for this fuzzing effort to cover that code path.



24/2 2026/07/24 18:47 tool grepper
0m Args:
{
  "Expression": "config HWPOISON_INJECT"
}

Results:
{
  "Output": "mm/Kconfig=777=config MEMORY_FAILURE\n--\nmm/Kconfig-787-\nmm/Kconfig:788:config HWPOISON_INJECT\nmm/Kconfig-789-\ttristate \"HWPoison pages injector\"\n"
}

25/2 2026/07/24 18:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28163
							output: 236
							thoughts: 288
						

Thoughts:
**My Analysis of HWPOISON Fuzzing Potential**

Okay, so I'm thinking about how to best fuzz the `hwpoison` functionality. Yes, using `HWPOISON_INJECT` is definitely a good starting point for that. 

The first thing I need to confirm is that `MADV_HWPOISON` is even available in the current configuration. Ah, yes, it's tied to `CONFIG_MEMORY_FAILURE`, so that's the key.  Now, what other config options are likely relevant here?  Let me see:

*   `MEMORY_FAILURE` - obviously.
*   `HWPOISON_INJECT` - for injection, of course.
*   `HUGETLBFS` - because we are thinking about hugetlb pages.
*   `HUGETLB_PAGE` - again, for the pages themselves.
*   `TRANSPARENT_HUGEPAGE` - might open up some interesting fuzzing vectors.

Now, let's refresh my memory on the critical symbols I should be focusing on. We have:

*   `hugetlb_page_hwpoison`
*   `hugetlb_unref_page_hwpoison`
*   `unpoison_memory`
*   `folio_clear_hugetlb_hwpoison`

Are there any other functions that might be useful targets? Let's dig deeper. Ok, looking into `mm/hugetlb.c`, I see `hugetlb_folio_mapping_lock_write` has been modified in the commit. And in `mm/memory-failure.c`, there are quite a few static functions that will not be useful for us:

*   `precise_page_poisoned` (static)
*   `hugetlb_set_poison` (static)
*   `hugetlb_clear_poison` (static)
*   `hugetlb_update_hwpoison` (static)

Since we are looking for functions we can directly target for fuzzing, I should only concern myself with functions that are exported or are non-static. Those 4 functions I mentioned initially are the ones.

Alright, that's a good overview. Let's call the `set-results` function to summarize.



26/2 2026/07/24 18:47 tool set-results
0m Args:
{
  "EnableConfigs": [
    "MEMORY_FAILURE",
    "HWPOISON_INJECT",
    "HUGETLBFS",
    "HUGETLB_PAGE",
    "TRANSPARENT_HUGEPAGE"
  ],
  "FocusSymbols": [
    "hugetlb_page_hwpoison",
    "hugetlb_unref_page_hwpoison",
    "unpoison_memory",
    "folio_clear_hugetlb_hwpoison"
  ],
  "Reasoning": "The patch modifies how hardware poisoned (HWPOISON) pages are tracked and handled within large folios, specifically affecting hugetlbfs and filemap read operations. It introduces new logic to track poisoned pages using `folio-\u003ehugetlb_hwpoison` and replaces the custom `hugetlbfs_read_iter` with `generic_file_read_iter`, handling hwpoison directly in `filemap_read`. These are reachable, functional changes in core memory management and hugetlbfs. Fuzzing should focus on the new hwpoison tracking functions and unpoisoning logic.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "MEMORY_FAILURE",
    "HWPOISON_INJECT",
    "HUGETLBFS",
    "HUGETLB_PAGE",
    "TRANSPARENT_HUGEPAGE"
  ],
  "FocusSymbols": [
    "hugetlb_page_hwpoison",
    "hugetlb_unref_page_hwpoison",
    "unpoison_memory",
    "folio_clear_hugetlb_hwpoison"
  ],
  "Reasoning": "The patch modifies how hardware poisoned (HWPOISON) pages are tracked and handled within large folios, specifically affecting hugetlbfs and filemap read operations. It introduces new logic to track poisoned pages using `folio-\u003ehugetlb_hwpoison` and replaces the custom `hugetlbfs_read_iter` with `generic_file_read_iter`, handling hwpoison directly in `filemap_read`. These are reachable, functional changes in core memory management and hugetlbfs. Fuzzing should focus on the new hwpoison tracking functions and unpoisoning logic.",
  "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)