Skip to content

Commit 6b97059

Browse files
Kefeng Wangakpm00
authored andcommitted
mm: hwpoison: support recovery from ksm_might_need_to_copy()
When the kernel copies a page from ksm_might_need_to_copy(), but runs into an uncorrectable error, it will crash since poisoned page is consumed by kernel, this is similar to the issue recently fixed by Copy-on-write poison recovery. When an error is detected during the page copy, return VM_FAULT_HWPOISON in do_swap_page(), and install a hwpoison entry in unuse_pte() when swapoff, which help us to avoid system crash. Note, memory failure on a KSM page will be skipped, but still call memory_failure_queue() to be consistent with general memory failure process, and we could support KSM page recovery in the feature. [[email protected]: enhance unuse_pte(), fix issue found by lkp] Link: https://lkml.kernel.org/r/[email protected] [[email protected]: update changelog, alter ksm_might_need_to_copy(), restore unlikely() in unuse_pte()] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Kefeng Wang <[email protected]> Reviewed-by: Naoya Horiguchi <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Tony Luck <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 55d77ba commit 6b97059

File tree

3 files changed

+22
-8
lines changed

3 files changed

+22
-8
lines changed

mm/ksm.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2629,8 +2629,11 @@ struct page *ksm_might_need_to_copy(struct page *page,
26292629
new_page = NULL;
26302630
}
26312631
if (new_page) {
2632-
copy_user_highpage(new_page, page, address, vma);
2633-
2632+
if (copy_mc_user_highpage(new_page, page, address, vma)) {
2633+
put_page(new_page);
2634+
memory_failure_queue(page_to_pfn(page), 0);
2635+
return ERR_PTR(-EHWPOISON);
2636+
}
26342637
SetPageDirty(new_page);
26352638
__SetPageUptodate(new_page);
26362639
__SetPageLocked(new_page);

mm/memory.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3840,6 +3840,9 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
38403840
if (unlikely(!page)) {
38413841
ret = VM_FAULT_OOM;
38423842
goto out_page;
3843+
} else if (unlikely(PTR_ERR(page) == -EHWPOISON)) {
3844+
ret = VM_FAULT_HWPOISON;
3845+
goto out_page;
38433846
}
38443847
folio = page_folio(page);
38453848

mm/swapfile.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,28 +1764,35 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
17641764
struct page *swapcache;
17651765
spinlock_t *ptl;
17661766
pte_t *pte, new_pte;
1767+
bool hwposioned = false;
17671768
int ret = 1;
17681769

17691770
swapcache = page;
17701771
page = ksm_might_need_to_copy(page, vma, addr);
17711772
if (unlikely(!page))
17721773
return -ENOMEM;
1774+
else if (unlikely(PTR_ERR(page) == -EHWPOISON))
1775+
hwposioned = true;
17731776

17741777
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
17751778
if (unlikely(!pte_same_as_swp(*pte, swp_entry_to_pte(entry)))) {
17761779
ret = 0;
17771780
goto out;
17781781
}
17791782

1780-
if (unlikely(!PageUptodate(page))) {
1781-
pte_t pteval;
1783+
if (unlikely(hwposioned || !PageUptodate(page))) {
1784+
swp_entry_t swp_entry;
17821785

17831786
dec_mm_counter(vma->vm_mm, MM_SWAPENTS);
1784-
pteval = swp_entry_to_pte(make_swapin_error_entry());
1785-
set_pte_at(vma->vm_mm, addr, pte, pteval);
1786-
swap_free(entry);
1787+
if (hwposioned) {
1788+
swp_entry = make_hwpoison_entry(swapcache);
1789+
page = swapcache;
1790+
} else {
1791+
swp_entry = make_swapin_error_entry();
1792+
}
1793+
new_pte = swp_entry_to_pte(swp_entry);
17871794
ret = 0;
1788-
goto out;
1795+
goto setpte;
17891796
}
17901797

17911798
/* See do_swap_page() */
@@ -1817,6 +1824,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
18171824
new_pte = pte_mksoft_dirty(new_pte);
18181825
if (pte_swp_uffd_wp(*pte))
18191826
new_pte = pte_mkuffd_wp(new_pte);
1827+
setpte:
18201828
set_pte_at(vma->vm_mm, addr, pte, new_pte);
18211829
swap_free(entry);
18221830
out:

0 commit comments

Comments
 (0)