EDAC/ghes: Add a notifier for reporting memory errors
authorJia He <justin.he@arm.com>
Mon, 10 Oct 2022 02:35:54 +0000 (02:35 +0000)
committerBorislav Petkov <bp@suse.de>
Thu, 20 Oct 2022 11:25:53 +0000 (13:25 +0200)
In order to make it a proper module and disentangle it from facilities,
add a notifier for reporting memory errors. Use an atomic notifier
because calls sites like ghes_proc_in_irq() run in interrupt context.

  [ bp: Massage commit message. ]

Suggested-by: Borislav Petkov <bp@alien8.de>
Signed-off-by: Jia He <justin.he@arm.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lore.kernel.org/r/20221010023559.69655-3-justin.he@arm.com
drivers/acpi/apei/ghes.c
drivers/edac/ghes_edac.c
include/acpi/ghes.h

index 80ad530583c9c9c602eb1d17fca4eca7fd74efab..55013e024ba39b098cc7bf654a858f888b0d7296 100644 (file)
@@ -94,6 +94,8 @@
 #define FIX_APEI_GHES_SDEI_CRITICAL    __end_of_fixed_addresses
 #endif
 
+static ATOMIC_NOTIFIER_HEAD(ghes_report_chain);
+
 static inline bool is_hest_type_generic_v2(struct ghes *ghes)
 {
        return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
@@ -645,7 +647,7 @@ static bool ghes_do_proc(struct ghes *ghes,
                if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
                        struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
 
-                       ghes_edac_report_mem_error(sev, mem_err);
+                       atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err);
 
                        arch_apei_report_mem_error(sev, mem_err);
                        queued = ghes_handle_memory_failure(gdata, sev);
@@ -1497,3 +1499,15 @@ void __init acpi_ghes_init(void)
        else
                pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
 }
+
+void ghes_register_report_chain(struct notifier_block *nb)
+{
+       atomic_notifier_chain_register(&ghes_report_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_register_report_chain);
+
+void ghes_unregister_report_chain(struct notifier_block *nb)
+{
+       atomic_notifier_chain_unregister(&ghes_report_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ghes_unregister_report_chain);
index c8fa7dcfdbd0817b8ed2861b06ffd4854bd2a6a8..7b8d56a769f6b3487ecc453c8682467d5189e314 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/dmi.h>
 #include "edac_module.h"
 #include <ras/ras_event.h>
+#include <linux/notifier.h>
 
 #define OTHER_DETAIL_LEN       400
 
@@ -267,11 +268,14 @@ out:
        return n;
 }
 
-void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
+static int ghes_edac_report_mem_error(struct notifier_block *nb,
+                                     unsigned long val, void *data)
 {
+       struct cper_sec_mem_err *mem_err = (struct cper_sec_mem_err *)data;
        struct cper_mem_err_compact cmem;
        struct edac_raw_error_desc *e;
        struct mem_ctl_info *mci;
+       unsigned long sev = val;
        struct ghes_pvt *pvt;
        unsigned long flags;
        char *p;
@@ -282,7 +286,7 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
         * know.
         */
        if (WARN_ON_ONCE(in_nmi()))
-               return;
+               return NOTIFY_OK;
 
        spin_lock_irqsave(&ghes_lock, flags);
 
@@ -374,8 +378,15 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
 
 unlock:
        spin_unlock_irqrestore(&ghes_lock, flags);
+
+       return NOTIFY_OK;
 }
 
+static struct notifier_block ghes_edac_mem_err_nb = {
+       .notifier_call  = ghes_edac_report_mem_error,
+       .priority       = 0,
+};
+
 /*
  * Known systems that are safe to enable this module.
  */
@@ -503,6 +514,8 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
        ghes_pvt = pvt;
        spin_unlock_irqrestore(&ghes_lock, flags);
 
+       ghes_register_report_chain(&ghes_edac_mem_err_nb);
+
        /* only set on success */
        refcount_set(&ghes_refcount, 1);
 
@@ -548,6 +561,8 @@ void ghes_edac_unregister(struct ghes *ghes)
        if (mci)
                edac_mc_free(mci);
 
+       ghes_unregister_report_chain(&ghes_edac_mem_err_nb);
+
 unlock:
        mutex_unlock(&ghes_reg_mutex);
 }
index 34fb3431a8f3646c8e45c34183bb43dbfc45540d..5cbd38b6e4e13beefb001315819a77d56c0495f0 100644 (file)
@@ -76,18 +76,11 @@ int ghes_estatus_pool_init(int num_ghes);
 /* From drivers/edac/ghes_edac.c */
 
 #ifdef CONFIG_EDAC_GHES
-void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err);
-
 int ghes_edac_register(struct ghes *ghes, struct device *dev);
 
 void ghes_edac_unregister(struct ghes *ghes);
 
 #else
-static inline void ghes_edac_report_mem_error(int sev,
-                                      struct cper_sec_mem_err *mem_err)
-{
-}
-
 static inline int ghes_edac_register(struct ghes *ghes, struct device *dev)
 {
        return -ENODEV;
@@ -145,4 +138,7 @@ int ghes_notify_sea(void);
 static inline int ghes_notify_sea(void) { return -ENOENT; }
 #endif
 
+struct notifier_block;
+extern void ghes_register_report_chain(struct notifier_block *nb);
+extern void ghes_unregister_report_chain(struct notifier_block *nb);
 #endif /* GHES_H */