Commit 0da81b65 authored by Aneesh Kumar K.V's avatar Aneesh Kumar K.V Committed by Michael Ellerman

powerpc/mce: Don't reload pte val in addr_to_pfn

A lockless page table walk should be safe against parallel THP collapse, THP
split and madvise(MADV_DONTNEED)/parallel fault. This patch makes sure kernel
won't reload the pteval when checking for different conditions. The patch also added
a check for pte_present to make sure the kernel is indeed operating
on a PTE and not a pointer to level 0 table page.

The pfn value we find here can be different from the actual pfn on which
machine check happened. This can happen if we raced with a parallel update
of the page table. In such a scenario we end up isolating a wrong pfn. But that
doesn't have any other side effect.
Signed-off-by: default avatarAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200505071729.54912-7-aneesh.kumar@linux.ibm.com
parent 2f92447f
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
*/ */
unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
{ {
pte_t *ptep; pte_t *ptep, pte;
unsigned int shift; unsigned int shift;
unsigned long pfn, flags; unsigned long pfn, flags;
struct mm_struct *mm; struct mm_struct *mm;
...@@ -39,19 +39,23 @@ unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) ...@@ -39,19 +39,23 @@ unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
local_irq_save(flags); local_irq_save(flags);
ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift); ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift);
if (!ptep) {
pfn = ULONG_MAX;
goto out;
}
pte = READ_ONCE(*ptep);
if (!ptep || pte_special(*ptep)) { if (!pte_present(pte) || pte_special(pte)) {
pfn = ULONG_MAX; pfn = ULONG_MAX;
goto out; goto out;
} }
if (shift <= PAGE_SHIFT) if (shift <= PAGE_SHIFT)
pfn = pte_pfn(*ptep); pfn = pte_pfn(pte);
else { else {
unsigned long rpnmask = (1ul << shift) - PAGE_SIZE; unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
pfn = pte_pfn(__pte(pte_val(*ptep) | (addr & rpnmask))); pfn = pte_pfn(__pte(pte_val(pte) | (addr & rpnmask)));
} }
out: out:
local_irq_restore(flags); local_irq_restore(flags);
return pfn; return pfn;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment