From: Balbir Singh <bsinghar...@gmail.com>

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 <bsinghar...@gmail.com>
[ar...@linux.ibm.com: Fixup pseries_do_memory_failure()]
Signed-off-by: Reza Arbab <ar...@linux.ibm.com>
Tested-by: Mahesh Salgaonkar <mah...@linux.vnet.ibm.com>
Signed-off-by: Santosh Sivaraj <sant...@fossix.org>
Cc: sta...@vger.kernel.org # v4.15+
---
 arch/powerpc/kernel/mce_power.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c
index a814d2dfb5b0..714a98e0927f 100644
--- 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 *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;
        }
-- 
2.21.0

Reply via email to