Re: [PATCH v9 11/15] filemap: Add hwpoison handling to filemap_read()

Pedro Falcato <[email protected]>
Newsgroups org.kernel.vger.linux-fsdevel,org.kvack.linux-mm
Message-ID <[email protected]>
On Wed, Aug 05, 2026 at 10:05:51PM +0100, Matthew Wilcox (Oracle) wrote:
> From: Jane Chu <[email protected]>
> 
> 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 <[email protected]>
> Co-developed-by: Matthew Wilcox <[email protected]>
> Signed-off-by: Matthew Wilcox <[email protected]>
> ---
>  include/linux/hugetlb.h    |  2 --
>  include/linux/page-flags.h | 11 +++++++++++
>  mm/filemap.c               | 33 +++++++++++++++++++++++++++++++--
>  3 files changed, 42 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
> index ec604cee8d22..639c0a772856 100644
> --- a/include/linux/hugetlb.h
> +++ b/include/linux/hugetlb.h
> @@ -1088,8 +1088,6 @@ void hugetlb_register_node(struct node *node);
>  void hugetlb_unregister_node(struct node *node);
>  #endif
>  
> -bool hugetlb_page_hwpoison(const struct folio *folio, const 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 5d01e5b28d0f..f75d66c42509 100644
> --- a/include/linux/page-flags.h
> +++ b/include/linux/page-flags.h
> @@ -1095,6 +1095,7 @@ 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);
>  
>  /*
> @@ -1116,6 +1117,16 @@ static inline bool is_page_hwpoison(const struct page *page)
>  	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_has_hwpoisoned_page(const struct folio *folio)
>  {
>  	return PageHWPoison(&folio->page) ||
> diff --git a/mm/filemap.c b/mm/filemap.c
> index 58eb9d240643..26a5f18121f9 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;
						    offset >> PAGE_SHIFT would probably be more idiomatic
> +
> +	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);
			similarly, offset_in_page(offset)

(yes, no codegen difference in both cases, but there could be a difference
if the ARM dynamic page size stuff ever moves forward)

> +	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);
> +}

Wouldn't this whole logic fit better in copy_folio_to_iter? shmem for
instance also handrolls its own hwpoison read_iter logic with
per-page-copies, etc; not amazing.

> +
>  static void filemap_end_dropbehind_read(struct folio *folio)
>  {
>  	if (!folio_test_dropbehind(folio))
> @@ -2862,14 +2887,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;

I don't love reusing copied here, but I guess there isn't much better
(in order to detect hwpoison-derived truncation)

> +			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;

Logic looks good though.

-- 
Pedro
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.