[PATCH 5.3 022/148] powerpc/mce: Fix MCE handling for huge pages

From: Greg Kroah-Hartman
Date: Thu Oct 10 2019 - 04:38:28 EST


From: Balbir Singh <bsingharora@xxxxxxxxx>

commit 99ead78afd1128bfcebe7f88f3b102fb2da09aee upstream.

The current code would fail on huge pages addresses, since the shift would
be incorrect. Use the correct page shift value returned by
__find_linux_pte() to get the correct physical address. The code is more
generic and can handle both regular and compound pages.

Fixes: ba41e1e1ccb9 ("powerpc/mce: Hookup derror (load/store) UE errors")
Signed-off-by: Balbir Singh <bsingharora@xxxxxxxxx>
[arbab@xxxxxxxxxxxxx: Fixup pseries_do_memory_failure()]
Signed-off-by: Reza Arbab <arbab@xxxxxxxxxxxxx>
Tested-by: Mahesh Salgaonkar <mahesh@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Santosh Sivaraj <santosh@xxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx # v4.15+
Signed-off-by: Michael Ellerman <mpe@xxxxxxxxxxxxxx>
Link: https://lore.kernel.org/r/20190820081352.8641-3-santosh@xxxxxxxxxx
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
arch/powerpc/kernel/mce_power.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)

--- a/arch/powerpc/kernel/mce_power.c
+++ b/arch/powerpc/kernel/mce_power.c
@@ -26,6 +26,7 @@
unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
{
pte_t *ptep;
+ unsigned int shift;
unsigned long flags;
struct mm_struct *mm;

@@ -35,13 +36,18 @@ unsigned long addr_to_pfn(struct pt_regs
mm = &init_mm;

local_irq_save(flags);
- if (mm == current->mm)
- ptep = find_current_mm_pte(mm->pgd, addr, NULL, NULL);
- else
- ptep = find_init_mm_pte(addr, NULL);
+ ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift);
local_irq_restore(flags);
+
if (!ptep || pte_special(*ptep))
return ULONG_MAX;
+
+ if (shift > PAGE_SHIFT) {
+ unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
+
+ return pte_pfn(__pte(pte_val(*ptep) | (addr & rpnmask)));
+ }
+
return pte_pfn(*ptep);
}

@@ -344,7 +350,7 @@ static const struct mce_derror_table mce
MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
{ 0, false, 0, 0, 0, 0, 0 } };

-static int mce_find_instr_ea_and_pfn(struct pt_regs *regs, uint64_t *addr,
+static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr,
uint64_t *phys_addr)
{
/*
@@ -541,7 +547,8 @@ static int mce_handle_derror(struct pt_r
* kernel/exception-64s.h
*/
if (get_paca()->in_mce < MAX_MCE_DEPTH)
- mce_find_instr_ea_and_pfn(regs, addr, phys_addr);
+ mce_find_instr_ea_and_phys(regs, addr,
+ phys_addr);
}
found = 1;
}