Commit 99ead78a authored by Balbir Singh's avatar Balbir Singh Committed by Michael Ellerman
Browse files

powerpc/mce: Fix MCE handling for huge pages



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: ba41e1e1 ("powerpc/mce: Hookup derror (load/store) UE errors")
Signed-off-by: default avatarBalbir Singh <bsingharora@gmail.com>
[arbab@linux.ibm.com: Fixup pseries_do_memory_failure()]
Signed-off-by: default avatarReza Arbab <arbab@linux.ibm.com>
Tested-by: default avatarMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: default avatarSantosh Sivaraj <santosh@fossix.org>
Cc: stable@vger.kernel.org # v4.15+
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190820081352.8641-3-santosh@fossix.org
parent b5bda626
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -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;
	}