Skip to content

Commit 442b1ec

Browse files
jchu314atgithubakpm00
authored andcommitted
mm: make page_mapped_in_vma() hugetlb walk aware
When a process consumes a UE in a page, the memory failure handler attempts to collect information for a potential SIGBUS. If the page is an anonymous page, page_mapped_in_vma(page, vma) is invoked in order to 1. retrieve the vaddr from the process' address space, 2. verify that the vaddr is indeed mapped to the poisoned page, where 'page' is the precise small page with UE. It's been observed that when injecting poison to a non-head subpage of an anonymous hugetlb page, no SIGBUS shows up, while injecting to the head page produces a SIGBUS. The cause is that, though hugetlb_walk() returns a valid pmd entry (on x86), but check_pte() detects mismatch between the head page per the pmd and the input subpage. Thus the vaddr is considered not mapped to the subpage and the process is not collected for SIGBUS purpose. This is the calling stack: collect_procs_anon page_mapped_in_vma page_vma_mapped_walk hugetlb_walk huge_pte_lock check_pte check_pte() header says that it "check if [pvmw->pfn, @pvmw->pfn + @pvmw->nr_pages) is mapped at the @pvmw->pte" but practically works only if pvmw->pfn is the head page pfn at pvmw->pte. Hindsight acknowledging that some pvmw->pte could point to a hugepage of some sort such that it makes sense to make check_pte() work for hugepage. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Jane Chu <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Kirill A. Shuemov <[email protected]> Cc: linmiaohe <[email protected]> Cc: Matthew Wilcow (Oracle) <[email protected]> Cc: Peter Xu <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent a4138a2 commit 442b1ec

File tree

1 file changed

+9
-4
lines changed

1 file changed

+9
-4
lines changed

mm/page_vma_mapped.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, pmd_t *pmdvalp,
8484
* mapped at the @pvmw->pte
8585
* @pvmw: page_vma_mapped_walk struct, includes a pair pte and pfn range
8686
* for checking
87+
* @pte_nr: the number of small pages described by @pvmw->pte.
8788
*
8889
* page_vma_mapped_walk() found a place where pfn range is *potentially*
8990
* mapped. check_pte() has to validate this.
@@ -100,7 +101,7 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, pmd_t *pmdvalp,
100101
* Otherwise, return false.
101102
*
102103
*/
103-
static bool check_pte(struct page_vma_mapped_walk *pvmw)
104+
static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr)
104105
{
105106
unsigned long pfn;
106107
pte_t ptent = ptep_get(pvmw->pte);
@@ -132,7 +133,11 @@ static bool check_pte(struct page_vma_mapped_walk *pvmw)
132133
pfn = pte_pfn(ptent);
133134
}
134135

135-
return (pfn - pvmw->pfn) < pvmw->nr_pages;
136+
if ((pfn + pte_nr - 1) < pvmw->pfn)
137+
return false;
138+
if (pfn > (pvmw->pfn + pvmw->nr_pages - 1))
139+
return false;
140+
return true;
136141
}
137142

138143
/* Returns true if the two ranges overlap. Careful to not overflow. */
@@ -207,7 +212,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
207212
return false;
208213

209214
pvmw->ptl = huge_pte_lock(hstate, mm, pvmw->pte);
210-
if (!check_pte(pvmw))
215+
if (!check_pte(pvmw, pages_per_huge_page(hstate)))
211216
return not_found(pvmw);
212217
return true;
213218
}
@@ -290,7 +295,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
290295
goto next_pte;
291296
}
292297
this_pte:
293-
if (check_pte(pvmw))
298+
if (check_pte(pvmw, 1))
294299
return true;
295300
next_pte:
296301
do {

0 commit comments

Comments
 (0)