powerpc/mce: Fix MCE handling for huge pages
authorBalbir Singh <bsingharora@gmail.com>
Tue, 20 Aug 2019 08:13:47 +0000 (13:43 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Wed, 21 Aug 2019 12:23:47 +0000 (22:23 +1000)
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@gmail.com>
[arbab@linux.ibm.com: Fixup pseries_do_memory_failure()]
Signed-off-by: Reza Arbab <arbab@linux.ibm.com>
Tested-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Santosh Sivaraj <santosh@fossix.org>
Cc: stable@vger.kernel.org # v4.15+
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190820081352.8641-3-santosh@fossix.org
arch/powerpc/kernel/mce_power.c

index a814d2dfb5b05cf75558a7a4b73bd54ba50cc2ab..714a98e0927f031df12328c2a8e0398c0a8d1dab 100644 (file)
@@ -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 *regs, unsigned long addr)
                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_p9_derror_table[] = {
   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_regs *regs,
                         * 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;
        }