mirror of https://gitee.com/openkylin/linux.git
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: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20200505071729.54912-7-aneesh.kumar@linux.ibm.com
This commit is contained in:
parent
2f92447f9f
commit
0da81b658b
|
@ -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)
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue