* [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs
@ 2026-07-09 18:47 Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page() Matthew Wilcox (Oracle)
` (11 more replies)
0 siblings, 12 replies; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
This set of patches are separated out from Jane's earlier series.
https://lore.kernel.org/linux-mm/20260617172534.1740152-1-jane.chu@oracle.com/
There's enough potentially controversial things in here that I think
it's worth considering them by themselves. First, handling hwpoison
in filemap_read(). I want this for shmem as well as for hugetlbfs (a
later patchset, maybe). Second, handling misses in the pagecache as
holes rather than opportunities to populate the pagecache.
v1 here: https://lore.kernel.org/linux-mm/20260707181713.208282-1-willy@infradead.org/
Sashiko had a lot of valuable feedback on that, and that's all addressed
here except for the things that I don't think are relevant.
Jane Chu (2):
filemap: Add hwpoison handling to filemap_read()
hugetlb: replace hugetlbfs_read_iter() with generic_file_read_iter()
Matthew Wilcox (Oracle) (8):
mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page()
hugetlb: Mark some function arguments as const
filemap: Remove checks in mapping_set_folio_order_range()
hugetlb: Set mapping folio order
memory-failure: Remove raw_hwp_list_head()
memory-failure: Prevent UAF in raw_hwp_page list
mm: Handle hugetlb correctly in is_page_hwpoison()
filemap: Add support for authoritative mappings
fs/hugetlbfs/inode.c | 122 ++++---------------------------------
include/linux/hugetlb.h | 24 +++-----
include/linux/mm_types.h | 4 +-
include/linux/page-flags.h | 28 ++++++---
include/linux/pagemap.h | 32 +++++-----
mm/filemap.c | 46 +++++++++++++-
mm/huge_memory.c | 2 +-
mm/hugetlb.c | 2 +-
mm/memory-failure.c | 39 ++++++------
mm/memory_hotplug.c | 2 +-
mm/shmem.c | 2 +-
mm/vmscan.c | 2 +-
12 files changed, 128 insertions(+), 177 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page()
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-10 14:27 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 02/10] hugetlb: Mark some function arguments as const Matthew Wilcox (Oracle)
` (10 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
This is a better name for this predicate. Also make the argument const
to allow other callers to be made const.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/page-flags.h | 4 ++--
mm/huge_memory.c | 2 +-
mm/memory_hotplug.c | 2 +-
mm/shmem.c | 2 +-
mm/vmscan.c | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7223f6f4e2b4..4185a03a45cf 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -1083,10 +1083,10 @@ static inline bool is_page_hwpoison(const struct page *page)
return folio_test_hugetlb(folio) && PageHWPoison(&folio->page);
}
-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));
+ (folio_test_large(folio) && folio_test_has_hwpoisoned(folio));
}
bool is_free_buddy_page(const struct page *page);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2bccb0a53a0a..353340309107 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/memory_hotplug.c b/mm/memory_hotplug.c
index 7ac19fab2263..33007e8e546a 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 b51f83c970bb..492c9bef3620 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 35c3bb15ae96..70e54ace0e74 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
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 02/10] hugetlb: Mark some function arguments as const
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page() Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-10 14:28 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 03/10] filemap: Remove checks in mapping_set_folio_order_range() Matthew Wilcox (Oracle)
` (9 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
These functions do not modify their folio argument, so we can mark them
as being const which will allow for some minor optimisations and let us
mark other function arguments as being const in the future.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/hugetlb.h | 19 +++++++++----------
mm/hugetlb.c | 2 +-
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 2abaf99321e9..18661a7cdb37 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));
@@ -1088,7 +1087,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 +1156,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/mm/hugetlb.c b/mm/hugetlb.c
index 571212b80835..cc8d98399913 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -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);
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 03/10] filemap: Remove checks in mapping_set_folio_order_range()
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page() Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 02/10] hugetlb: Mark some function arguments as const Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-10 14:42 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 04/10] hugetlb: Set mapping folio order Matthew Wilcox (Oracle)
` (8 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
These checks make it impossible to use mapping_set_folio_order_range()
in hugetlb. They add very little value and can simply be removed.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/pagemap.h | 21 +++++----------------
1 file changed, 5 insertions(+), 16 deletions(-)
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 2c3718d592d6..d0c9aecaa4ac 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -405,29 +405,18 @@ 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.
*
* 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))
- return;
-
- if (min > MAX_PAGECACHE_ORDER)
- min = MAX_PAGECACHE_ORDER;
-
- if (max > MAX_PAGECACHE_ORDER)
- max = MAX_PAGECACHE_ORDER;
-
if (max < min)
max = min;
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 04/10] hugetlb: Set mapping folio order
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (2 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 03/10] filemap: Remove checks in mapping_set_folio_order_range() Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-10 14:49 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 05/10] memory-failure: Remove raw_hwp_list_head() Matthew Wilcox (Oracle)
` (7 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
hugetlbfs currently uses an fs-specific way to determine the size of
the folios in its page cache. Thanks to the support of block sizes
larger than page sizes, we now support this as part of the page cache.
It's somewhat more efficient as we have this information directly in the
mapping rather than going from inode->i_sb->s_fs_info. We can convert
the rest of the hugetlb code to use this at our leisure; this is needed
now so that we can use filemap_get_pages() unmodified.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
fs/hugetlbfs/inode.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 216e1a0dd0b2..1760df6f2709 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -883,6 +883,16 @@ 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);
+}
+
/*
* Hugetlbfs is not reclaimable; therefore its i_mmap_rwsem will never
* be taken from reclaim -- unlike regular filesystems. This needs an
@@ -926,8 +936,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;
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 05/10] memory-failure: Remove raw_hwp_list_head()
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (3 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 04/10] hugetlb: Set mapping folio order Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-10 14:55 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list Matthew Wilcox (Oracle)
` (6 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
There's no need to hide the llist_head; we can just declare it
directly in struct folio and do away with raw_hwp_list_head().
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/mm_types.h | 4 ++--
mm/memory-failure.c | 19 +++++--------------
2 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index b18c2b2e7d2c..d46f81f94848 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: Raw error pages, see struct raw_hwp_page.
* @_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/mm/memory-failure.c b/mm/memory-failure.c
index 51508a55c405..53063e2a1f97 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1810,21 +1810,15 @@ 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)
-{
- 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;
@@ -1844,8 +1838,7 @@ bool is_raw_hwpoison_page_in_hugepage(struct page *page)
mutex_lock(&mf_mutex);
- raw_hwp_head = raw_hwp_list_head(folio);
- llist_for_each_entry(p, raw_hwp_head->first, node) {
+ llist_for_each_entry(p, folio->hugetlb_hwpoison.first, node) {
if (page == p->page) {
ret = true;
break;
@@ -1863,7 +1856,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);
@@ -1887,7 +1880,6 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
*/
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;
@@ -1899,8 +1891,7 @@ static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)
*/
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 +1899,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
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (4 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 05/10] memory-failure: Remove raw_hwp_list_head() Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-10 15:02 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 07/10] mm: Handle hugetlb correctly in is_page_hwpoison() Matthew Wilcox (Oracle)
` (5 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
Most accesses to the raw_hwp_page list are protected by mf_mutex.
However, the call to folio_clear_hugetlb_hwpoison() in mm/hugetlb.c
does not. This is probably OK today as this is in the freeing path and
I think all current calls to is_raw_hwpoison_page_in_hugepage() hold a
reference to the folio. However, the next patch will call this function
without holding a reference on the folio, which will expose this race.
Rename the current folio_clear_hugetlb_hwpoison() to
__folio_clear_hugetlb_hwpoison() and wrap it with a new
folio_clear_hugetlb_hwpoison() for the benefit of external callers.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
mm/memory-failure.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 53063e2a1f97..353288cce1b2 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1935,7 +1935,7 @@ static unsigned long folio_free_raw_hwp(struct folio *folio, bool move_flag)
return __folio_free_raw_hwp(folio, move_flag);
}
-void folio_clear_hugetlb_hwpoison(struct folio *folio)
+static void __folio_clear_hugetlb_hwpoison(struct folio *folio)
{
if (folio_test_hugetlb_raw_hwp_unreliable(folio))
return;
@@ -1945,6 +1945,13 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
folio_free_raw_hwp(folio, true);
}
+void folio_clear_hugetlb_hwpoison(struct folio *folio)
+{
+ mutex_lock(&mf_mutex);
+ __folio_clear_hugetlb_hwpoison(folio);
+ mutex_unlock(&mf_mutex);
+}
+
static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared)
{
@@ -2051,7 +2058,7 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)
folio_lock(folio);
if (hwpoison_filter(p)) {
- folio_clear_hugetlb_hwpoison(folio);
+ __folio_clear_hugetlb_hwpoison(folio);
if (migratable_cleared)
folio_set_hugetlb_migratable(folio);
folio_unlock(folio);
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 07/10] mm: Handle hugetlb correctly in is_page_hwpoison()
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (5 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 08/10] filemap: Add hwpoison handling to filemap_read() Matthew Wilcox (Oracle)
` (4 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
Checking to see whether a page inside a hugetlb folio is hardware
poisoned is more complicated than I realised. The correct logic was
in is_raw_hwpoison_page_in_hugepage(). Make is_page_hwpoison() the
official entry point for "is this page hwpoison", no matter what kind
of folio it belongs to. Rename is_raw_hwpoison_page_in_hugepage() to
hugetlb_page_hwpoison(), and remove the attempt to handle non-hugetlb
folios from it.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
fs/hugetlbfs/inode.c | 4 ++--
include/linux/hugetlb.h | 5 -----
include/linux/page-flags.h | 24 +++++++++++++++++++-----
mm/memory-failure.c | 9 ++++-----
4 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 1760df6f2709..725bb945a440 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -198,7 +198,7 @@ static size_t adjust_range_hwpoison(struct folio *folio, size_t offset,
struct page *page = folio_page(folio, offset / PAGE_SIZE);
size_t safe_bytes;
- if (is_raw_hwpoison_page_in_hugepage(page))
+ if (hugetlb_page_hwpoison(folio, page))
return 0;
/* Safe to read the remaining bytes in this page. */
safe_bytes = PAGE_SIZE - (offset % PAGE_SIZE);
@@ -206,7 +206,7 @@ static size_t adjust_range_hwpoison(struct folio *folio, size_t offset,
/* 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))
+ if (hugetlb_page_hwpoison(folio, page))
break;
return min(safe_bytes, bytes);
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 18661a7cdb37..1878642e75ec 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -1069,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));
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 4185a03a45cf..8e57c12a7301 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -1068,6 +1068,9 @@ static inline bool PageHuge(const struct page *page)
return folio_test_hugetlb(page_folio(page));
}
+#ifdef CONFIG_MEMORY_FAILURE
+bool hugetlb_page_hwpoison(const struct folio *folio, 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
@@ -1075,13 +1078,24 @@ static inline bool PageHuge(const struct page *page)
*/
static inline bool is_page_hwpoison(const struct page *page)
{
- const struct folio *folio;
+ const struct folio *folio = page_folio(page);
+
+ if (folio_test_hugetlb(folio))
+ return hugetlb_page_hwpoison(folio, page);
+ return PageHWPoison(page);
+}
+#else
+static inline bool hugetlb_page_hwpoison(const struct folio *folio,
+ const struct page *page)
+{
+ return false;
+}
- if (PageHWPoison(page))
- return true;
- folio = page_folio(page);
- return folio_test_hugetlb(folio) && PageHWPoison(&folio->page);
+static inline bool is_page_hwpoison(const struct page *page)
+{
+ return false;
}
+#endif
static inline bool folio_has_hwpoisoned_page(const struct folio *folio)
{
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 353288cce1b2..58ab8166b7ec 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1817,18 +1817,17 @@ struct raw_hwp_page {
struct page *page;
};
-bool is_raw_hwpoison_page_in_hugepage(struct page *page)
+/*
+ * Check if a given @page in a hugetlb folio is HWPOISON.
+ */
+bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page)
{
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);
-
/*
* When RawHwpUnreliable is set, kernel lost track of which subpages
* are HWPOISON. So return as if ALL subpages are HWPOISONed.
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 08/10] filemap: Add hwpoison handling to filemap_read()
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (6 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 07/10] mm: Handle hugetlb correctly in is_page_hwpoison() Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 09/10] filemap: Add support for authoritative mappings Matthew Wilcox (Oracle)
` (3 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Muchun Song, Oscar Salvador, David Hildenbrand, Miaohe Lin,
Naoya Horiguchi, Jan Kara, linux-fsdevel, Christian Brauner,
Matthew Wilcox
From: Jane Chu <jane.chu@oracle.com>
Add hwpoison handling to filemap_read() such that .read_iter() could
make best effort copying data out of clean pages without risking
MCE in case page cache contains HWpoison.
Signed-off-by: Jane Chu <jane.chu@oracle.com>
Co-developed-by: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Matthew Wilcox <willy@infradead.org>
---
mm/filemap.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/mm/filemap.c b/mm/filemap.c
index 5af62e6abca5..c568d267c803 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))
@@ -2749,6 +2751,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_page_hwpoison(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_page_hwpoison(page))
+ break;
+
+ return min(safe_bytes, bytes);
+}
+
static void filemap_end_dropbehind_read(struct folio *folio)
{
if (!folio_test_dropbehind(folio))
@@ -2862,7 +2887,8 @@ 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);
+ copied = copy_folio_to_iter(folio, offset, copied, iter);
already_read += copied;
iocb->ki_pos += copied;
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 09/10] filemap: Add support for authoritative mappings
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (7 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 08/10] filemap: Add hwpoison handling to filemap_read() Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 10/10] hugetlb: replace hugetlbfs_read_iter() with generic_file_read_iter() Matthew Wilcox (Oracle)
` (2 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Matthew Wilcox (Oracle), Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
An authoritative mapping knows about all the folios in the mapping.
If read() finds a missing folio, there's no reason to allocate one and
try to read it because we know it's a zero region of the file. We can
just call iov_iter_zero() instead.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
include/linux/pagemap.h | 11 +++++++++++
mm/filemap.c | 18 ++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index d0c9aecaa4ac..64e67bf79b20 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;
diff --git a/mm/filemap.c b/mm/filemap.c
index c568d267c803..c113bc69887a 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2697,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)
@@ -2853,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:
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 10/10] hugetlb: replace hugetlbfs_read_iter() with generic_file_read_iter()
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (8 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 09/10] filemap: Add support for authoritative mappings Matthew Wilcox (Oracle)
@ 2026-07-09 18:47 ` Matthew Wilcox (Oracle)
2026-07-09 19:19 ` [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox
2026-07-10 14:25 ` Usama Arif
11 siblings, 0 replies; 20+ messages in thread
From: Matthew Wilcox (Oracle) @ 2026-07-09 18:47 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Muchun Song, Oscar Salvador, David Hildenbrand, Miaohe Lin,
Naoya Horiguchi, Jan Kara, linux-fsdevel, Christian Brauner,
Matthew Wilcox (Oracle)
From: Jane Chu <jane.chu@oracle.com>
generic_file_read_iter() now handles hugetlb poison, hugetlb page
size and missing folios correctly, so we no longer need a separate
hugetlbfs_read_iter().
Signed-off-by: Jane Chu <jane.chu@oracle.com>
Co-developed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
fs/hugetlbfs/inode.c | 109 +------------------------------------------
1 file changed, 1 insertion(+), 108 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 725bb945a440..a6f0459d79de 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 (hugetlb_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 (hugetlb_page_hwpoison(folio, 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,
@@ -1215,7 +1108,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,
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (9 preceding siblings ...)
2026-07-09 18:47 ` [PATCH v2 10/10] hugetlb: replace hugetlbfs_read_iter() with generic_file_read_iter() Matthew Wilcox (Oracle)
@ 2026-07-09 19:19 ` Matthew Wilcox
2026-07-10 14:25 ` Usama Arif
11 siblings, 0 replies; 20+ messages in thread
From: Matthew Wilcox @ 2026-07-09 19:19 UTC (permalink / raw)
To: Andrew Morton, Jane Chu, linux-mm
Cc: Muchun Song, Oscar Salvador, David Hildenbrand, Miaohe Lin,
Naoya Horiguchi, Jan Kara, linux-fsdevel, Christian Brauner
On Thu, Jul 09, 2026 at 07:47:28PM +0100, Matthew Wilcox (Oracle) wrote:
> This set of patches are separated out from Jane's earlier series.
> https://lore.kernel.org/linux-mm/20260617172534.1740152-1-jane.chu@oracle.com/
>
> There's enough potentially controversial things in here that I think
> it's worth considering them by themselves. First, handling hwpoison
> in filemap_read(). I want this for shmem as well as for hugetlbfs (a
> later patchset, maybe). Second, handling misses in the pagecache as
> holes rather than opportunities to populate the pagecache.
>
> v1 here: https://lore.kernel.org/linux-mm/20260707181713.208282-1-willy@infradead.org/
> Sashiko had a lot of valuable feedback on that, and that's all addressed
> here except for the things that I don't think are relevant.
Well, Sashiko pointed out a few different things this time. Will noodle
on them and produce a v3:
https://sashiko.dev/#/patchset/20260709184740.1286561-1-willy%40infradead.org
The only one I think is invalid is:
: Does returning 0 here with an empty batch cause an infinite loop in
: filemap_splice_read()?
No, it doesn't, because we don't call splice_read for hugetlbfs.
The rest all seem like legitimate concerns. If anyone spots anything
else, let me know. And really what I want from humans is higher level
feedback like "This is the wrong way to handle hwpoison, it should be
done inside copy_page_to_iter()".
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
` (10 preceding siblings ...)
2026-07-09 19:19 ` [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox
@ 2026-07-10 14:25 ` Usama Arif
11 siblings, 0 replies; 20+ messages in thread
From: Usama Arif @ 2026-07-10 14:25 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:28 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> This set of patches are separated out from Jane's earlier series.
> https://lore.kernel.org/linux-mm/20260617172534.1740152-1-jane.chu@oracle.com/
>
> There's enough potentially controversial things in here that I think
> it's worth considering them by themselves. First, handling hwpoison
> in filemap_read(). I want this for shmem as well as for hugetlbfs (a
> later patchset, maybe). Second, handling misses in the pagecache as
> holes rather than opportunities to populate the pagecache.
>
> v1 here: https://lore.kernel.org/linux-mm/20260707181713.208282-1-willy@infradead.org/
> Sashiko had a lot of valuable feedback on that, and that's all addressed
> here except for the things that I don't think are relevant.
>
> Jane Chu (2):
> filemap: Add hwpoison handling to filemap_read()
> hugetlb: replace hugetlbfs_read_iter() with generic_file_read_iter()
>
> Matthew Wilcox (Oracle) (8):
> mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page()
> hugetlb: Mark some function arguments as const
> filemap: Remove checks in mapping_set_folio_order_range()
> hugetlb: Set mapping folio order
> memory-failure: Remove raw_hwp_list_head()
> memory-failure: Prevent UAF in raw_hwp_page list
> mm: Handle hugetlb correctly in is_page_hwpoison()
> filemap: Add support for authoritative mappings
>
> fs/hugetlbfs/inode.c | 122 ++++---------------------------------
I really like the deletions here in hugetlbfs :)
> include/linux/hugetlb.h | 24 +++-----
> include/linux/mm_types.h | 4 +-
> include/linux/page-flags.h | 28 ++++++---
> include/linux/pagemap.h | 32 +++++-----
> mm/filemap.c | 46 +++++++++++++-
> mm/huge_memory.c | 2 +-
> mm/hugetlb.c | 2 +-
> mm/memory-failure.c | 39 ++++++------
> mm/memory_hotplug.c | 2 +-
> mm/shmem.c | 2 +-
> mm/vmscan.c | 2 +-
> 12 files changed, 128 insertions(+), 177 deletions(-)
>
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page()
2026-07-09 18:47 ` [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page() Matthew Wilcox (Oracle)
@ 2026-07-10 14:27 ` Usama Arif
0 siblings, 0 replies; 20+ messages in thread
From: Usama Arif @ 2026-07-10 14:27 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:29 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> This is a better name for this predicate. Also make the argument const
> to allow other callers to be made const.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
I find it nice in review if it says No functional change intended.
Acked-by: Usama Arif <usama.arif@linux.dev>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 02/10] hugetlb: Mark some function arguments as const
2026-07-09 18:47 ` [PATCH v2 02/10] hugetlb: Mark some function arguments as const Matthew Wilcox (Oracle)
@ 2026-07-10 14:28 ` Usama Arif
0 siblings, 0 replies; 20+ messages in thread
From: Usama Arif @ 2026-07-10 14:28 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:30 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> These functions do not modify their folio argument, so we can mark them
> as being const which will allow for some minor optimisations and let us
> mark other function arguments as being const in the future.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> include/linux/hugetlb.h | 19 +++++++++----------
> mm/hugetlb.c | 2 +-
> 2 files changed, 10 insertions(+), 11 deletions(-)
>
They all only seem to inspect state.
Acked-by: Usama Arif <usama.arif@linux.dev>
> diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
> index 2abaf99321e9..18661a7cdb37 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));
> @@ -1088,7 +1087,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 +1156,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/mm/hugetlb.c b/mm/hugetlb.c
> index 571212b80835..cc8d98399913 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -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);
>
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 03/10] filemap: Remove checks in mapping_set_folio_order_range()
2026-07-09 18:47 ` [PATCH v2 03/10] filemap: Remove checks in mapping_set_folio_order_range() Matthew Wilcox (Oracle)
@ 2026-07-10 14:42 ` Usama Arif
0 siblings, 0 replies; 20+ messages in thread
From: Usama Arif @ 2026-07-10 14:42 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:31 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> These checks make it impossible to use mapping_set_folio_order_range()
> in hugetlb. They add very little value and can simply be removed.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> include/linux/pagemap.h | 21 +++++----------------
> 1 file changed, 5 insertions(+), 16 deletions(-)
>
> diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
> index 2c3718d592d6..d0c9aecaa4ac 100644
> --- a/include/linux/pagemap.h
> +++ b/include/linux/pagemap.h
> @@ -405,29 +405,18 @@ 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.
> *
> * 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))
> - return;
> -
> - if (min > MAX_PAGECACHE_ORDER)
> - min = MAX_PAGECACHE_ORDER;
> -
> - if (max > MAX_PAGECACHE_ORDER)
> - max = MAX_PAGECACHE_ORDER;
> -
This one is a bit interesting, This has been useful to me in the past when I
was trying to [1] use mapping_max_folio_order() for force_thp_readahead order.
On arm64 with 64K base PAGE_SIZE HPAGE_PMD_ORDER is 13 (512MB), while
existing MAX_PAGECACHE_ORDER was smaller and the cap is what prevents generic
page-cache code from advertising that unsupported 512MB order due to
MAX_XAS_ORDER.
[1] https://lore.kernel.org/all/20260601102205.3985788-3-usama.arif@linux.dev/
> if (max < min)
> max = min;
>
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 04/10] hugetlb: Set mapping folio order
2026-07-09 18:47 ` [PATCH v2 04/10] hugetlb: Set mapping folio order Matthew Wilcox (Oracle)
@ 2026-07-10 14:49 ` Usama Arif
0 siblings, 0 replies; 20+ messages in thread
From: Usama Arif @ 2026-07-10 14:49 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:32 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> hugetlbfs currently uses an fs-specific way to determine the size of
> the folios in its page cache. Thanks to the support of block sizes
> larger than page sizes, we now support this as part of the page cache.
> It's somewhat more efficient as we have this information directly in the
> mapping rather than going from inode->i_sb->s_fs_info. We can convert
> the rest of the hugetlb code to use this at our leisure; this is needed
> now so that we can use filemap_get_pages() unmodified.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> fs/hugetlbfs/inode.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
> index 216e1a0dd0b2..1760df6f2709 100644
> --- a/fs/hugetlbfs/inode.c
> +++ b/fs/hugetlbfs/inode.c
> @@ -883,6 +883,16 @@ 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);
We are setting mapping_set_folio_order_range(), but mapping_min_folio_order()
and mapping_max_folio_order() will still return 0 if CONFIG_TRANSPARENT_HUGEPAGE
is disabled but hugetlb is enabled. I am guessing this will be tackled in
one of the following patches in the series.
> +}
> +
> /*
> * Hugetlbfs is not reclaimable; therefore its i_mmap_rwsem will never
> * be taken from reclaim -- unlike regular filesystems. This needs an
> @@ -926,8 +936,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;
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 05/10] memory-failure: Remove raw_hwp_list_head()
2026-07-09 18:47 ` [PATCH v2 05/10] memory-failure: Remove raw_hwp_list_head() Matthew Wilcox (Oracle)
@ 2026-07-10 14:55 ` Usama Arif
0 siblings, 0 replies; 20+ messages in thread
From: Usama Arif @ 2026-07-10 14:55 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:33 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> There's no need to hide the llist_head; we can just declare it
> directly in struct folio and do away with raw_hwp_list_head().
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> include/linux/mm_types.h | 4 ++--
> mm/memory-failure.c | 19 +++++--------------
> 2 files changed, 7 insertions(+), 16 deletions(-)
>
> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> index b18c2b2e7d2c..d46f81f94848 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: Raw error pages, see struct raw_hwp_page.
s/_hugetlb_hwpoison/hugetlb_hwpoison/ in the doc above, apart
from that, LGTM.
Also good to add No functional change intended in the commit message.
Acked-by: Usama Arif <usama.arif@linux.dev>
> * @_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/mm/memory-failure.c b/mm/memory-failure.c
> index 51508a55c405..53063e2a1f97 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1810,21 +1810,15 @@ 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)
> -{
> - 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;
> @@ -1844,8 +1838,7 @@ bool is_raw_hwpoison_page_in_hugepage(struct page *page)
>
> mutex_lock(&mf_mutex);
>
> - raw_hwp_head = raw_hwp_list_head(folio);
> - llist_for_each_entry(p, raw_hwp_head->first, node) {
> + llist_for_each_entry(p, folio->hugetlb_hwpoison.first, node) {
> if (page == p->page) {
> ret = true;
> break;
> @@ -1863,7 +1856,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);
> @@ -1887,7 +1880,6 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
> */
> 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;
> @@ -1899,8 +1891,7 @@ static int hugetlb_update_hwpoison(struct folio *folio, struct page *page)
> */
> 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 +1899,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
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list
2026-07-09 18:47 ` [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list Matthew Wilcox (Oracle)
@ 2026-07-10 15:02 ` Usama Arif
2026-07-10 17:16 ` Matthew Wilcox
0 siblings, 1 reply; 20+ messages in thread
From: Usama Arif @ 2026-07-10 15:02 UTC (permalink / raw)
To: Matthew Wilcox (Oracle)
Cc: Usama Arif, Andrew Morton, Jane Chu, linux-mm, Muchun Song,
Oscar Salvador, David Hildenbrand, Miaohe Lin, Naoya Horiguchi,
Jan Kara, linux-fsdevel, Christian Brauner
On Thu, 9 Jul 2026 19:47:34 +0100 "Matthew Wilcox (Oracle)" <willy@infradead.org> wrote:
> Most accesses to the raw_hwp_page list are protected by mf_mutex.
> However, the call to folio_clear_hugetlb_hwpoison() in mm/hugetlb.c
> does not. This is probably OK today as this is in the freeing path and
> I think all current calls to is_raw_hwpoison_page_in_hugepage() hold a
> reference to the folio. However, the next patch will call this function
> without holding a reference on the folio, which will expose this race.
>
> Rename the current folio_clear_hugetlb_hwpoison() to
> __folio_clear_hugetlb_hwpoison() and wrap it with a new
> folio_clear_hugetlb_hwpoison() for the benefit of external callers.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
> mm/memory-failure.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 53063e2a1f97..353288cce1b2 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1935,7 +1935,7 @@ static unsigned long folio_free_raw_hwp(struct folio *folio, bool move_flag)
> return __folio_free_raw_hwp(folio, move_flag);
> }
>
> -void folio_clear_hugetlb_hwpoison(struct folio *folio)
> +static void __folio_clear_hugetlb_hwpoison(struct folio *folio)
> {
Good to add lockdep_assert_held(&mf_mutex) here, but there is a issue below.
> if (folio_test_hugetlb_raw_hwp_unreliable(folio))
> return;
> @@ -1945,6 +1945,13 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
> folio_free_raw_hwp(folio, true);
> }
>
> +void folio_clear_hugetlb_hwpoison(struct folio *folio)
> +{
> + mutex_lock(&mf_mutex);
This wrapper can now sleep, but its external caller is the hugetlb free
path. __update_and_free_hugetlb_folio() is reached from
update_and_free_hugetlb_folio(), which has a comment saying
"Defer freeing to avoid using GFP_ATOMIC"
> + __folio_clear_hugetlb_hwpoison(folio);
> + mutex_unlock(&mf_mutex);
> +}
> +
> static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
> bool *migratable_cleared)
> {
> @@ -2051,7 +2058,7 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)
> folio_lock(folio);
>
> if (hwpoison_filter(p)) {
> - folio_clear_hugetlb_hwpoison(folio);
> + __folio_clear_hugetlb_hwpoison(folio);
> if (migratable_cleared)
> folio_set_hugetlb_migratable(folio);
> folio_unlock(folio);
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list
2026-07-10 15:02 ` Usama Arif
@ 2026-07-10 17:16 ` Matthew Wilcox
0 siblings, 0 replies; 20+ messages in thread
From: Matthew Wilcox @ 2026-07-10 17:16 UTC (permalink / raw)
To: Usama Arif
Cc: Andrew Morton, Jane Chu, linux-mm, Muchun Song, Oscar Salvador,
David Hildenbrand, Miaohe Lin, Naoya Horiguchi, Jan Kara,
linux-fsdevel, Christian Brauner
On Fri, Jul 10, 2026 at 08:02:03AM -0700, Usama Arif wrote:
> > +void folio_clear_hugetlb_hwpoison(struct folio *folio)
> > +{
> > + mutex_lock(&mf_mutex);
>
> This wrapper can now sleep, but its external caller is the hugetlb free
> path. __update_and_free_hugetlb_folio() is reached from
> update_and_free_hugetlb_folio(), which has a comment saying
> "Defer freeing to avoid using GFP_ATOMIC"
Yeah, Sashiko also pointed this out.
I think I'm going to have to change how we track hwpoison in hugetlb
in order to make this work. Let me think out loud here:
is_page_hwpoison() can be called without a reference on the folio
containing the page. The answer can be wrong in this case, but any
UAF must be benign.
Grabbing the mf_mutex inside hugetlb_page_hwpoison() is insufficient
because we can't protect against folio_clear_hugetlb_hwpoison() (the
approach attempted in this patch).
We could add a spinlock for protecting freeing & checking of the list.
That's probably simplest.
It'd be better to protect the list with RCU. But then we'd need to
put an RCU head inside raw_hwp_page, doubling its size from 2 pointers
to 4.
We could completely change how we do this; instead of having an llist,
we could have an array of 32-bit page indices. Reallocate it when we
run out, doubling its size each time, and put an rcu head in that.
That'd give us 32-bytes the first time, space for 4 indices, then double
to 64 (space for 12 indices), double again to 128 (28 indices), etc.
We could also RCU-free the folio if it's hwpoisoned. We'd need to
clear the hugetlb flag before asking RCU to cover up this problem
to ensure that any reader that saw the hugetlb flag set started its
grace period before.
Probably all too much trouble. I think I'll add a global spinlock.
> > + __folio_clear_hugetlb_hwpoison(folio);
> > + mutex_unlock(&mf_mutex);
> > +}
> > +
> > static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
> > bool *migratable_cleared)
> > {
> > @@ -2051,7 +2058,7 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)
> > folio_lock(folio);
> >
> > if (hwpoison_filter(p)) {
> > - folio_clear_hugetlb_hwpoison(folio);
> > + __folio_clear_hugetlb_hwpoison(folio);
> > if (migratable_cleared)
> > folio_set_hugetlb_migratable(folio);
> > folio_unlock(folio);
> > --
> > 2.47.3
> >
> >
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-07-10 17:17 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 18:47 [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 01/10] mm: Rename folio_contain_hwpoison_page() to folio_has_hwpoison_page() Matthew Wilcox (Oracle)
2026-07-10 14:27 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 02/10] hugetlb: Mark some function arguments as const Matthew Wilcox (Oracle)
2026-07-10 14:28 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 03/10] filemap: Remove checks in mapping_set_folio_order_range() Matthew Wilcox (Oracle)
2026-07-10 14:42 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 04/10] hugetlb: Set mapping folio order Matthew Wilcox (Oracle)
2026-07-10 14:49 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 05/10] memory-failure: Remove raw_hwp_list_head() Matthew Wilcox (Oracle)
2026-07-10 14:55 ` Usama Arif
2026-07-09 18:47 ` [PATCH v2 06/10] memory-failure: Prevent UAF in raw_hwp_page list Matthew Wilcox (Oracle)
2026-07-10 15:02 ` Usama Arif
2026-07-10 17:16 ` Matthew Wilcox
2026-07-09 18:47 ` [PATCH v2 07/10] mm: Handle hugetlb correctly in is_page_hwpoison() Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 08/10] filemap: Add hwpoison handling to filemap_read() Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 09/10] filemap: Add support for authoritative mappings Matthew Wilcox (Oracle)
2026-07-09 18:47 ` [PATCH v2 10/10] hugetlb: replace hugetlbfs_read_iter() with generic_file_read_iter() Matthew Wilcox (Oracle)
2026-07-09 19:19 ` [PATCH v2 00/10] Use generic_file_read_iter() in hugetlbfs Matthew Wilcox
2026-07-10 14:25 ` Usama Arif
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox