aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/pseries/ras.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/pseries/ras.c')
-rw-r--r--arch/powerpc/platforms/pseries/ras.c83
1 files changed, 83 insertions, 0 deletions
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