From: Balbir Singh bsingharora@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 bsingharora@gmail.com [arbab@linux.ibm.com: Fixup pseries_do_memory_failure()] Signed-off-by: Reza Arbab arbab@linux.ibm.com Co-developed-by: Santosh Sivaraj santosh@fossix.org Signed-off-by: Santosh Sivaraj santosh@fossix.org Tested-by: Mahesh Salgaonkar mahesh@linux.vnet.ibm.com Cc: stable@vger.kernel.org # v4.15+ --- arch/powerpc/include/asm/mce.h | 2 +- arch/powerpc/kernel/mce_power.c | 55 ++++++++++++++-------------- arch/powerpc/platforms/pseries/ras.c | 9 ++--- 3 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index a4c6a74ad2fb..f3a6036b6bc0 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -209,7 +209,7 @@ extern void release_mce_event(void); extern void machine_check_queue_event(void); extern void machine_check_print_event_info(struct machine_check_event *evt, bool user_mode, bool in_guest); -unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr); +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr); #ifdef CONFIG_PPC_BOOK3S_64 void flush_and_reload_slb(void); #endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index a814d2dfb5b0..e74816f045f8 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -20,13 +20,14 @@ #include <asm/exception-64s.h>
/* - * Convert an address related to an mm to a PFN. NOTE: we are in real - * mode, we could potentially race with page table updates. + * Convert an address related to an mm to a physical address. + * NOTE: we are in real mode, we could potentially race with page table updates. */ -unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr) { - pte_t *ptep; - unsigned long flags; + pte_t *ptep, pte; + unsigned int shift; + unsigned long flags, phys_addr; struct mm_struct *mm;
if (user_mode(regs)) @@ -35,14 +36,21 @@ 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; - return pte_pfn(*ptep); + + pte = *ptep; + if (shift > PAGE_SHIFT) { + unsigned long rpnmask = (1ul << shift) - PAGE_SIZE; + + pte = __pte(pte_val(pte) | (addr & rpnmask)); + } + phys_addr = pte_pfn(pte) << PAGE_SHIFT; + + return phys_addr; }
/* flush SLBs and reload */ @@ -344,7 +352,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) { /* @@ -354,18 +362,16 @@ static int mce_find_instr_ea_and_pfn(struct pt_regs *regs, uint64_t *addr, * faults */ int instr; - unsigned long pfn, instr_addr; + unsigned long instr_addr; struct instruction_op op; struct pt_regs tmp = *regs;
- pfn = addr_to_pfn(regs, regs->nip); - if (pfn != ULONG_MAX) { - instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK); + instr_addr = addr_to_phys(regs, regs->nip) + (regs->nip & ~PAGE_MASK); + if (instr_addr != ULONG_MAX) { instr = *(unsigned int *)(instr_addr); if (!analyse_instr(&op, &tmp, instr)) { - pfn = addr_to_pfn(regs, op.ea); *addr = op.ea; - *phys_addr = (pfn << PAGE_SHIFT); + *phys_addr = addr_to_phys(regs, op.ea); return 0; } /* @@ -440,15 +446,9 @@ static int mce_handle_ierror(struct pt_regs *regs, *addr = regs->nip; if (mce_err->sync_error && table[i].error_type == MCE_ERROR_TYPE_UE) { - unsigned long pfn; - - if (get_paca()->in_mce < MAX_MCE_DEPTH) { - pfn = addr_to_pfn(regs, regs->nip); - if (pfn != ULONG_MAX) { - *phys_addr = - (pfn << PAGE_SHIFT); - } - } + if (get_paca()->in_mce < MAX_MCE_DEPTH) + *phys_addr = addr_to_phys(regs, + regs->nip); } } return handled; @@ -541,7 +541,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; } diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index f16fdd0f71f7..5743f6353638 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -739,13 +739,10 @@ static void pseries_do_memory_failure(struct pt_regs *regs, if (mce_log->sub_err_type & UE_LOGICAL_ADDR_PROVIDED) { paddr = be64_to_cpu(mce_log->logical_address); } else if (mce_log->sub_err_type & UE_EFFECTIVE_ADDR_PROVIDED) { - unsigned long pfn; - - pfn = addr_to_pfn(regs, - be64_to_cpu(mce_log->effective_address)); - if (pfn == ULONG_MAX) + paddr = addr_to_phys(regs, + be64_to_cpu(mce_log->effective_address)); + if (paddr == ULONG_MAX) return; - paddr = pfn << PAGE_SHIFT; } else { return; }
Santosh Sivaraj's on August 15, 2019 10:39 am:
From: Balbir Singh bsingharora@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 bsingharora@gmail.com [arbab@linux.ibm.com: Fixup pseries_do_memory_failure()] Signed-off-by: Reza Arbab arbab@linux.ibm.com Co-developed-by: Santosh Sivaraj santosh@fossix.org Signed-off-by: Santosh Sivaraj santosh@fossix.org Tested-by: Mahesh Salgaonkar mahesh@linux.vnet.ibm.com Cc: stable@vger.kernel.org # v4.15+
arch/powerpc/include/asm/mce.h | 2 +- arch/powerpc/kernel/mce_power.c | 55 ++++++++++++++-------------- arch/powerpc/platforms/pseries/ras.c | 9 ++--- 3 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index a4c6a74ad2fb..f3a6036b6bc0 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -209,7 +209,7 @@ extern void release_mce_event(void); extern void machine_check_queue_event(void); extern void machine_check_print_event_info(struct machine_check_event *evt, bool user_mode, bool in_guest); -unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr); +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr); #ifdef CONFIG_PPC_BOOK3S_64 void flush_and_reload_slb(void); #endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index a814d2dfb5b0..e74816f045f8 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -20,13 +20,14 @@ #include <asm/exception-64s.h> /*
- Convert an address related to an mm to a PFN. NOTE: we are in real
- mode, we could potentially race with page table updates.
- Convert an address related to an mm to a physical address.
*/
- NOTE: we are in real mode, we could potentially race with page table updates.
-unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr) {
- pte_t *ptep;
- unsigned long flags;
- pte_t *ptep, pte;
- unsigned int shift;
- unsigned long flags, phys_addr; struct mm_struct *mm;
if (user_mode(regs)) @@ -35,14 +36,21 @@ 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;
- return pte_pfn(*ptep);
- pte = *ptep;
- if (shift > PAGE_SHIFT) {
unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
pte = __pte(pte_val(pte) | (addr & rpnmask));
- }
- phys_addr = pte_pfn(pte) << PAGE_SHIFT;
- return phys_addr;
}
This should remain addr_to_pfn I think. None of the callers care what size page the EA was mapped with. 'pfn' is referring to the Linux pfn, which is the small page number.
if (shift > PAGE_SHIFT) return (pte_pfn(*ptep) | ((addr & ((1UL << shift) - 1)) >> PAGE_SHIFT); else return pte_pfn(*ptep);
Something roughly like that, then you don't have to change any callers or am I missing something?
Thanks, Nick
Hi Nick,
Nicholas Piggin npiggin@gmail.com writes:
Santosh Sivaraj's on August 15, 2019 10:39 am:
From: Balbir Singh bsingharora@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 bsingharora@gmail.com [arbab@linux.ibm.com: Fixup pseries_do_memory_failure()] Signed-off-by: Reza Arbab arbab@linux.ibm.com Co-developed-by: Santosh Sivaraj santosh@fossix.org Signed-off-by: Santosh Sivaraj santosh@fossix.org Tested-by: Mahesh Salgaonkar mahesh@linux.vnet.ibm.com Cc: stable@vger.kernel.org # v4.15+
arch/powerpc/include/asm/mce.h | 2 +- arch/powerpc/kernel/mce_power.c | 55 ++++++++++++++-------------- arch/powerpc/platforms/pseries/ras.c | 9 ++--- 3 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index a4c6a74ad2fb..f3a6036b6bc0 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -209,7 +209,7 @@ extern void release_mce_event(void); extern void machine_check_queue_event(void); extern void machine_check_print_event_info(struct machine_check_event *evt, bool user_mode, bool in_guest); -unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr); +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr); #ifdef CONFIG_PPC_BOOK3S_64 void flush_and_reload_slb(void); #endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index a814d2dfb5b0..e74816f045f8 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -20,13 +20,14 @@ #include <asm/exception-64s.h> /*
- Convert an address related to an mm to a PFN. NOTE: we are in real
- mode, we could potentially race with page table updates.
- Convert an address related to an mm to a physical address.
*/
- NOTE: we are in real mode, we could potentially race with page table updates.
-unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr) {
- pte_t *ptep;
- unsigned long flags;
- pte_t *ptep, pte;
- unsigned int shift;
- unsigned long flags, phys_addr; struct mm_struct *mm;
if (user_mode(regs)) @@ -35,14 +36,21 @@ 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;
- return pte_pfn(*ptep);
- pte = *ptep;
- if (shift > PAGE_SHIFT) {
unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
pte = __pte(pte_val(pte) | (addr & rpnmask));
- }
- phys_addr = pte_pfn(pte) << PAGE_SHIFT;
- return phys_addr;
}
This should remain addr_to_pfn I think. None of the callers care what size page the EA was mapped with. 'pfn' is referring to the Linux pfn, which is the small page number.
if (shift > PAGE_SHIFT) return (pte_pfn(*ptep) | ((addr & ((1UL << shift) - 1)) >> PAGE_SHIFT); else return pte_pfn(*ptep);
Something roughly like that, then you don't have to change any callers or am I missing something?
Here[1] you asked to return the real address rather than pfn, which all callers care about. So made the changes accordingly.
[1] https://www.spinics.net/lists/kernel/msg3187658.html
Thanks, Santosh
Thanks, Nick
Santosh Sivaraj's on August 20, 2019 11:47 am:
Hi Nick,
Nicholas Piggin npiggin@gmail.com writes:
Santosh Sivaraj's on August 15, 2019 10:39 am:
From: Balbir Singh bsingharora@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 bsingharora@gmail.com [arbab@linux.ibm.com: Fixup pseries_do_memory_failure()] Signed-off-by: Reza Arbab arbab@linux.ibm.com Co-developed-by: Santosh Sivaraj santosh@fossix.org Signed-off-by: Santosh Sivaraj santosh@fossix.org Tested-by: Mahesh Salgaonkar mahesh@linux.vnet.ibm.com Cc: stable@vger.kernel.org # v4.15+
arch/powerpc/include/asm/mce.h | 2 +- arch/powerpc/kernel/mce_power.c | 55 ++++++++++++++-------------- arch/powerpc/platforms/pseries/ras.c | 9 ++--- 3 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index a4c6a74ad2fb..f3a6036b6bc0 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -209,7 +209,7 @@ extern void release_mce_event(void); extern void machine_check_queue_event(void); extern void machine_check_print_event_info(struct machine_check_event *evt, bool user_mode, bool in_guest); -unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr); +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr); #ifdef CONFIG_PPC_BOOK3S_64 void flush_and_reload_slb(void); #endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index a814d2dfb5b0..e74816f045f8 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -20,13 +20,14 @@ #include <asm/exception-64s.h> /*
- Convert an address related to an mm to a PFN. NOTE: we are in real
- mode, we could potentially race with page table updates.
- Convert an address related to an mm to a physical address.
*/
- NOTE: we are in real mode, we could potentially race with page table updates.
-unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) +unsigned long addr_to_phys(struct pt_regs *regs, unsigned long addr) {
- pte_t *ptep;
- unsigned long flags;
- pte_t *ptep, pte;
- unsigned int shift;
- unsigned long flags, phys_addr; struct mm_struct *mm;
if (user_mode(regs)) @@ -35,14 +36,21 @@ 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;
- return pte_pfn(*ptep);
- pte = *ptep;
- if (shift > PAGE_SHIFT) {
unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
pte = __pte(pte_val(pte) | (addr & rpnmask));
- }
- phys_addr = pte_pfn(pte) << PAGE_SHIFT;
- return phys_addr;
}
This should remain addr_to_pfn I think. None of the callers care what size page the EA was mapped with. 'pfn' is referring to the Linux pfn, which is the small page number.
if (shift > PAGE_SHIFT) return (pte_pfn(*ptep) | ((addr & ((1UL << shift) - 1)) >> PAGE_SHIFT); else return pte_pfn(*ptep);
Something roughly like that, then you don't have to change any callers or am I missing something?
Here[1] you asked to return the real address rather than pfn, which all callers care about. So made the changes accordingly.
Ah I did suggest it, but I meant _exact_ physical address :) The one matching the effective address you gave it.
As it is now, the physical address is truncated at the small page size, so if you do that you might as well just keep it as a pfn and no change to callers.
I would also prefer getting the pfn as above rather than constructing a new pte, which is a neat hack but is not a normal pattern.
Thanks, Nick
linux-stable-mirror@lists.linaro.org