Skip to content

Commit 8625147

Browse files
48caakpm00
authored andcommitted
hugetlbfs: don't delete error page from pagecache
This change is very similar to the change that was made for shmem [1], and it solves the same problem but for HugeTLBFS instead. Currently, when poison is found in a HugeTLB page, the page is removed from the page cache. That means that attempting to map or read that hugepage in the future will result in a new hugepage being allocated instead of notifying the user that the page was poisoned. As [1] states, this is effectively memory corruption. The fix is to leave the page in the page cache. If the user attempts to use a poisoned HugeTLB page with a syscall, the syscall will fail with EIO, the same error code that shmem uses. For attempts to map the page, the thread will get a BUS_MCEERR_AR SIGBUS. [1]: commit a760542 ("mm: shmem: don't truncate page if memory failure happens") Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: James Houghton <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Reviewed-by: Naoya Horiguchi <[email protected]> Tested-by: Naoya Horiguchi <[email protected]> Reviewed-by: Yang Shi <[email protected]> Cc: Axel Rasmussen <[email protected]> Cc: James Houghton <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Muchun Song <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 120b116 commit 8625147

File tree

3 files changed

+14
-8
lines changed

3 files changed

+14
-8
lines changed

fs/hugetlbfs/inode.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,12 @@ static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
328328
} else {
329329
unlock_page(page);
330330

331+
if (PageHWPoison(page)) {
332+
put_page(page);
333+
retval = -EIO;
334+
break;
335+
}
336+
331337
/*
332338
* We have the page, copy it to user space buffer.
333339
*/
@@ -1111,13 +1117,6 @@ static int hugetlbfs_migrate_folio(struct address_space *mapping,
11111117
static int hugetlbfs_error_remove_page(struct address_space *mapping,
11121118
struct page *page)
11131119
{
1114-
struct inode *inode = mapping->host;
1115-
pgoff_t index = page->index;
1116-
1117-
hugetlb_delete_from_page_cache(page);
1118-
if (unlikely(hugetlb_unreserve_pages(inode, index, index + 1, 1)))
1119-
hugetlb_fix_reserve_counts(inode);
1120-
11211120
return 0;
11221121
}
11231122

mm/hugetlb.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6111,6 +6111,10 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
61116111

61126112
ptl = huge_pte_lock(h, dst_mm, dst_pte);
61136113

6114+
ret = -EIO;
6115+
if (PageHWPoison(page))
6116+
goto out_release_unlock;
6117+
61146118
/*
61156119
* We allow to overwrite a pte marker: consider when both MISSING|WP
61166120
* registered, we firstly wr-protect a none pte which has no page cache

mm/memory-failure.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,13 +1080,16 @@ static int me_huge_page(struct page_state *ps, struct page *p)
10801080
int res;
10811081
struct page *hpage = compound_head(p);
10821082
struct address_space *mapping;
1083+
bool extra_pins = false;
10831084

10841085
if (!PageHuge(hpage))
10851086
return MF_DELAYED;
10861087

10871088
mapping = page_mapping(hpage);
10881089
if (mapping) {
10891090
res = truncate_error_page(hpage, page_to_pfn(p), mapping);
1091+
/* The page is kept in page cache. */
1092+
extra_pins = true;
10901093
unlock_page(hpage);
10911094
} else {
10921095
unlock_page(hpage);
@@ -1104,7 +1107,7 @@ static int me_huge_page(struct page_state *ps, struct page *p)
11041107
}
11051108
}
11061109

1107-
if (has_extra_refcount(ps, p, false))
1110+
if (has_extra_refcount(ps, p, extra_pins))
11081111
res = MF_FAILED;
11091112

11101113
return res;

0 commit comments

Comments
 (0)