aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGanesh Goudar <ganeshgr@linux.ibm.com>2019-04-15 06:05:44 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2019-04-20 08:02:35 -0400
commit7f177f9810ada8ec2e8b378eddbe2d91fda79c9b (patch)
treee0c9192e60fb0fdc3f864563e0485c42dfb3a1df
parent4df2cb633b5b22ba152511f1a55e718efca6c0d9 (diff)
powerpc/pseries: hwpoison the pages upon hitting UE
Add support to hwpoison the pages upon hitting machine check exception. This patch queues the address where UE is hit to percpu array and schedules work to plumb it into memory poison infrastructure. Reviewed-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Signed-off-by: Ganesh Goudar <ganeshgr@linux.ibm.com> [mpe: Combine #ifdefs, drop PPC_BIT8(), and empty inline stub] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/include/asm/mce.h1
-rw-r--r--arch/powerpc/kernel/mce_power.c2
-rw-r--r--arch/powerpc/platforms/pseries/ras.c83
3 files changed, 85 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h
index 17996bc9382b..ad47fa865324 100644
--- a/arch/powerpc/include/asm/mce.h
+++ b/arch/powerpc/include/asm/mce.h
@@ -210,6 +210,7 @@ extern void release_mce_event(void);
210extern void machine_check_queue_event(void); 210extern void machine_check_queue_event(void);
211extern void machine_check_print_event_info(struct machine_check_event *evt, 211extern void machine_check_print_event_info(struct machine_check_event *evt,
212 bool user_mode, bool in_guest); 212 bool user_mode, bool in_guest);
213unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr);
213#ifdef CONFIG_PPC_BOOK3S_64 214#ifdef CONFIG_PPC_BOOK3S_64
214void flush_and_reload_slb(void); 215void flush_and_reload_slb(void);
215#endif /* CONFIG_PPC_BOOK3S_64 */ 216#endif /* CONFIG_PPC_BOOK3S_64 */
diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c
index 6b800eec31f2..367fbfa2e835 100644
--- a/arch/powerpc/kernel/mce_power.c
+++ b/arch/powerpc/kernel/mce_power.c
@@ -36,7 +36,7 @@
36 * Convert an address related to an mm to a PFN. NOTE: we are in real 36 * Convert an address related to an mm to a PFN. NOTE: we are in real
37 * mode, we could potentially race with page table updates. 37 * mode, we could potentially race with page table updates.
38 */ 38 */
39static unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) 39unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
40{ 40{
41 pte_t *ptep; 41 pte_t *ptep;
42 unsigned long flags; 42 unsigned long flags;
diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
index a25c2ac0c9c0..c97d15352f9f 100644
--- a/arch/powerpc/platforms/pseries/ras.c
+++ b/arch/powerpc/platforms/pseries/ras.c
@@ -707,6 +707,87 @@ out:
707 return disposition; 707 return disposition;
708} 708}
709 709
710#ifdef CONFIG_MEMORY_FAILURE
711
712static DEFINE_PER_CPU(int, rtas_ue_count);
713static DEFINE_PER_CPU(unsigned long, rtas_ue_paddr[MAX_MC_EVT]);
714
715#define UE_EFFECTIVE_ADDR_PROVIDED 0x40
716#define UE_LOGICAL_ADDR_PROVIDED 0x20
717
718
719static void pseries_hwpoison_work_fn(struct work_struct *work)
720{
721 unsigned long paddr;
722 int index;
723
724 while (__this_cpu_read(rtas_ue_count) > 0) {
725 index = __this_cpu_read(rtas_ue_count) - 1;
726 paddr = __this_cpu_read(rtas_ue_paddr[index]);
727 memory_failure(paddr >> PAGE_SHIFT, 0);
728 __this_cpu_dec(rtas_ue_count);
729 }
730}
731
732static DECLARE_WORK(hwpoison_work, pseries_hwpoison_work_fn);
733
734static void queue_ue_paddr(unsigned long paddr)
735{
736 int index;
737
738 index = __this_cpu_inc_return(rtas_ue_count) - 1;
739 if (index >= MAX_MC_EVT) {
740 __this_cpu_dec(rtas_ue_count);
741 return;
742 }
743 this_cpu_write(rtas_ue_paddr[index], paddr);
744 schedule_work(&hwpoison_work);
745}
746
747static void pseries_do_memory_failure(struct pt_regs *regs,
748 struct pseries_mc_errorlog *mce_log)
749{
750 unsigned long paddr;
751
752 if (mce_log->sub_err_type & UE_LOGICAL_ADDR_PROVIDED) {
753 paddr = be64_to_cpu(mce_log->logical_address);
754 } else if (mce_log->sub_err_type & UE_EFFECTIVE_ADDR_PROVIDED) {
755 unsigned long pfn;
756
757 pfn = addr_to_pfn(regs,
758 be64_to_cpu(mce_log->effective_address));
759 if (pfn == ULONG_MAX)
760 return;
761 paddr = pfn << PAGE_SHIFT;
762 } else {
763 return;
764 }
765 queue_ue_paddr(paddr);
766}
767
768static void pseries_process_ue(struct pt_regs *regs,
769 struct rtas_error_log *errp)
770{
771 struct pseries_errorlog *pseries_log;
772 struct pseries_mc_errorlog *mce_log;
773
774 if (!rtas_error_extended(errp))
775 return;
776
777 pseries_log = get_pseries_errorlog(errp, PSERIES_ELOG_SECT_ID_MCE);
778 if (!pseries_log)
779 return;
780
781 mce_log = (struct pseries_mc_errorlog *)pseries_log->data;
782
783 if (mce_log->error_type == MC_ERROR_TYPE_UE)
784 pseries_do_memory_failure(regs, mce_log);
785}
786#else
787static inline void pseries_process_ue(struct pt_regs *regs,
788 struct rtas_error_log *errp) { }
789#endif /*CONFIG_MEMORY_FAILURE */
790
710/* 791/*
711 * Process MCE rtas errlog event. 792 * Process MCE rtas errlog event.
712 */ 793 */
@@ -765,6 +846,8 @@ static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err)
765 recovered = 1; 846 recovered = 1;
766 } 847 }
767 848
849 pseries_process_ue(regs, err);
850
768 /* Queue irq work to log this rtas event later. */ 851 /* Queue irq work to log this rtas event later. */
769 irq_work_queue(&mce_errlog_process_work); 852 irq_work_queue(&mce_errlog_process_work);
770 853