From: Jane Chu 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. [1] https://lore.kernel.org/linux-mm/aeZwAz6PcdlqSnJ2@casper.infradead.org/ Suggested-by: Matthew Wilcox Signed-off-by: Jane Chu Signed-off-by: Matthew Wilcox --- mm/filemap.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/mm/filemap.c b/mm/filemap.c index 5af62e6abca5..2b65367574be 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_contain_hwpoisoned_page(folio)) + break; if (!folio_test_uptodate(folio)) break; if (folio_test_readahead(folio)) @@ -2749,6 +2751,27 @@ 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 (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,6 +2885,14 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter, if (writably_mapped) flush_dcache_folio(folio); + if (folio_contain_hwpoisoned_page(folio)) { + bytes = adjust_range_hwpoison(folio, offset, bytes); + if (bytes == 0) { + error = -EIO; + break; + } + } + copied = copy_folio_to_iter(folio, offset, bytes, iter); already_read += copied; -- 2.47.3