Move the CXL CPER handling paths out of ghes.c and into ghes_cper.c so the
helpers can be reused. The code is moved as-is, with the public
prototypes updated so GHES keeps calling into the new translation unit.

While moving this code, also add CXL CPER section length checks and use
spinlock_irqsave() in CXL register/unregister paths for locking
consistency.

Reviewed-by: Jonathan Cameron <[email protected]>
Signed-off-by: Ahmed Tiba <[email protected]>
---
 drivers/acpi/apei/ghes.c      | 180 +++++++++++-------------------------------
 drivers/acpi/apei/ghes_cper.c | 135 +++++++++++++++++++++++++++++++
 include/acpi/ghes_cper.h      |  11 +++
 3 files changed, 194 insertions(+), 132 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 07e4001ea8d7..2a83d326e692 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -383,138 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_data 
*gdata)
 #endif
 }
 
-/* Room for 8 entries */
-#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8
-static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
-                   CXL_CPER_PROT_ERR_FIFO_DEPTH);
-
-/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
-static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
-struct work_struct *cxl_cper_prot_err_work;
-
-static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
-                                  int severity)
-{
-#ifdef CONFIG_ACPI_APEI_PCIEAER
-       struct cxl_cper_prot_err_work_data wd;
-
-       if (cxl_cper_sec_prot_err_valid(prot_err))
-               return;
-
-       guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
-
-       if (!cxl_cper_prot_err_work)
-               return;
-
-       if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity))
-               return;
-
-       if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) {
-               pr_err_ratelimited("CXL CPER kfifo overflow\n");
-               return;
-       }
-
-       schedule_work(cxl_cper_prot_err_work);
-#endif
-}
-
-int cxl_cper_register_prot_err_work(struct work_struct *work)
-{
-       if (cxl_cper_prot_err_work)
-               return -EINVAL;
-
-       guard(spinlock)(&cxl_cper_prot_err_work_lock);
-       cxl_cper_prot_err_work = work;
-       return 0;
-}
-EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
-
-int cxl_cper_unregister_prot_err_work(struct work_struct *work)
-{
-       if (cxl_cper_prot_err_work != work)
-               return -EINVAL;
-
-       guard(spinlock)(&cxl_cper_prot_err_work_lock);
-       cxl_cper_prot_err_work = NULL;
-       return 0;
-}
-EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
-
-int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
-{
-       return kfifo_get(&cxl_cper_prot_err_fifo, wd);
-}
-EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
-
-/* Room for 8 entries for each of the 4 event log queues */
-#define CXL_CPER_FIFO_DEPTH 32
-DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
-
-/* Synchronize schedule_work() with cxl_cper_work changes */
-static DEFINE_SPINLOCK(cxl_cper_work_lock);
-struct work_struct *cxl_cper_work;
-
-static void cxl_cper_post_event(enum cxl_event_type event_type,
-                               struct cxl_cper_event_rec *rec)
-{
-       struct cxl_cper_work_data wd;
-
-       if (rec->hdr.length <= sizeof(rec->hdr) ||
-           rec->hdr.length > sizeof(*rec)) {
-               pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
-                      rec->hdr.length);
-               return;
-       }
-
-       if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
-               pr_err(FW_WARN "CXL CPER invalid event\n");
-               return;
-       }
-
-       guard(spinlock_irqsave)(&cxl_cper_work_lock);
-
-       if (!cxl_cper_work)
-               return;
-
-       wd.event_type = event_type;
-       memcpy(&wd.rec, rec, sizeof(wd.rec));
-
-       if (!kfifo_put(&cxl_cper_fifo, wd)) {
-               pr_err_ratelimited("CXL CPER kfifo overflow\n");
-               return;
-       }
-
-       schedule_work(cxl_cper_work);
-}
-
-int cxl_cper_register_work(struct work_struct *work)
-{
-       if (cxl_cper_work)
-               return -EINVAL;
-
-       guard(spinlock)(&cxl_cper_work_lock);
-       cxl_cper_work = work;
-       return 0;
-}
-EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
-
-int cxl_cper_unregister_work(struct work_struct *work)
-{
-       if (cxl_cper_work != work)
-               return -EINVAL;
-
-       guard(spinlock)(&cxl_cper_work_lock);
-       cxl_cper_work = NULL;
-       return 0;
-}
-EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
-
-int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
-{
-       return kfifo_get(&cxl_cper_fifo, wd);
-}
-EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL");
-
 static void ghes_log_hwerr(int sev, guid_t *sec_type)
 {
        if (sev != CPER_SEV_RECOVERABLE)
@@ -549,6 +417,42 @@ static void ghes_log_hwerr(int sev, guid_t *sec_type)
        hwerr_log_error_type(HWERR_RECOV_OTHERS);
 }
 
+static bool ghes_cxl_event_len_valid(struct acpi_hest_generic_data *gdata,
+                                    struct cxl_cper_event_rec *rec)
+{
+       if (gdata->error_data_length < sizeof(*rec) ||
+           rec->hdr.length <= sizeof(rec->hdr) ||
+           rec->hdr.length > gdata->error_data_length ||
+           rec->hdr.length > sizeof(*rec)) {
+               pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
+                      rec->hdr.length);
+               return false;
+       }
+
+       return true;
+}
+
+static bool ghes_cxl_prot_err_len_valid(struct acpi_hest_generic_data *gdata,
+                                       struct cxl_cper_sec_prot_err *prot_err)
+{
+       if (gdata->error_data_length < sizeof(*prot_err) +
+           sizeof(struct cxl_ras_capability_regs)) {
+               pr_err(FW_WARN "CXL CPER Invalid protocol error length (%u)\n",
+                      gdata->error_data_length);
+               return false;
+       }
+
+       if (prot_err->dvsec_len >
+           gdata->error_data_length - sizeof(*prot_err) -
+           sizeof(struct cxl_ras_capability_regs)) {
+               pr_err(FW_WARN "CXL CPER invalid DVSEC length (%u)\n",
+                      prot_err->dvsec_len);
+               return false;
+       }
+
+       return true;
+}
+
 static void ghes_do_proc(struct ghes *ghes,
                         const struct acpi_hest_generic_status *estatus)
 {
@@ -585,18 +489,30 @@ static void ghes_do_proc(struct ghes *ghes,
                } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
                        struct cxl_cper_sec_prot_err *prot_err = 
acpi_hest_get_payload(gdata);
 
+                       if (!ghes_cxl_prot_err_len_valid(gdata, prot_err))
+                               continue;
+
                        cxl_cper_post_prot_err(prot_err, gdata->error_severity);
                } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) {
                        struct cxl_cper_event_rec *rec = 
acpi_hest_get_payload(gdata);
 
+                       if (!ghes_cxl_event_len_valid(gdata, rec))
+                               continue;
+
                        cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec);
                } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) {
                        struct cxl_cper_event_rec *rec = 
acpi_hest_get_payload(gdata);
 
+                       if (!ghes_cxl_event_len_valid(gdata, rec))
+                               continue;
+
                        cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec);
                } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) 
{
                        struct cxl_cper_event_rec *rec = 
acpi_hest_get_payload(gdata);
 
+                       if (!ghes_cxl_event_len_valid(gdata, rec))
+                               continue;
+
                        cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec);
                } else {
                        void *err = acpi_hest_get_payload(gdata);
diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c
index 7e4a66b788b8..b59e3ed3eab3 100644
--- a/drivers/acpi/apei/ghes_cper.c
+++ b/drivers/acpi/apei/ghes_cper.c
@@ -12,9 +12,12 @@
  *   Author: Huang Ying <[email protected]>
  */
 
+#include <linux/aer.h>
+#include <linux/cleanup.h>
 #include <linux/err.h>
 #include <linux/genalloc.h>
 #include <linux/io.h>
+#include <linux/kfifo.h>
 #include <linux/kernel.h>
 #include <linux/math64.h>
 #include <linux/mm.h>
@@ -350,6 +353,138 @@ void ghes_defer_non_standard_event(struct 
acpi_hest_generic_data *gdata,
        schedule_work(&entry->work);
 }
 
+/* Room for 8 entries */
+#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8
+static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data,
+                   CXL_CPER_PROT_ERR_FIFO_DEPTH);
+
+/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
+static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
+struct work_struct *cxl_cper_prot_err_work;
+
+void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
+                           int severity)
+{
+#ifdef CONFIG_ACPI_APEI_PCIEAER
+       struct cxl_cper_prot_err_work_data wd;
+
+       if (cxl_cper_sec_prot_err_valid(prot_err))
+               return;
+
+       guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
+
+       if (!cxl_cper_prot_err_work)
+               return;
+
+       if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity))
+               return;
+
+       if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) {
+               pr_err_ratelimited("CXL CPER kfifo overflow\n");
+               return;
+       }
+
+       schedule_work(cxl_cper_prot_err_work);
+#endif
+}
+
+int cxl_cper_register_prot_err_work(struct work_struct *work)
+{
+       if (cxl_cper_prot_err_work)
+               return -EINVAL;
+
+       guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
+       cxl_cper_prot_err_work = work;
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
+
+int cxl_cper_unregister_prot_err_work(struct work_struct *work)
+{
+       if (cxl_cper_prot_err_work != work)
+               return -EINVAL;
+
+       guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
+       cxl_cper_prot_err_work = NULL;
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
+
+int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
+{
+       return kfifo_get(&cxl_cper_prot_err_fifo, wd);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL");
+
+/* Room for 8 entries for each of the 4 event log queues */
+#define CXL_CPER_FIFO_DEPTH 32
+static DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, 
CXL_CPER_FIFO_DEPTH);
+
+/* Synchronize schedule_work() with cxl_cper_work changes */
+static DEFINE_SPINLOCK(cxl_cper_work_lock);
+struct work_struct *cxl_cper_work;
+
+void cxl_cper_post_event(enum cxl_event_type event_type,
+                        struct cxl_cper_event_rec *rec)
+{
+       struct cxl_cper_work_data wd;
+
+       if (rec->hdr.length <= sizeof(rec->hdr) ||
+           rec->hdr.length > sizeof(*rec)) {
+               pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
+                      rec->hdr.length);
+               return;
+       }
+
+       if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
+               pr_err(FW_WARN "CXL CPER invalid event\n");
+               return;
+       }
+
+       guard(spinlock_irqsave)(&cxl_cper_work_lock);
+
+       if (!cxl_cper_work)
+               return;
+
+       wd.event_type = event_type;
+       memcpy(&wd.rec, rec, sizeof(wd.rec));
+
+       if (!kfifo_put(&cxl_cper_fifo, wd)) {
+               pr_err_ratelimited("CXL CPER kfifo overflow\n");
+               return;
+       }
+
+       schedule_work(cxl_cper_work);
+}
+
+int cxl_cper_register_work(struct work_struct *work)
+{
+       if (cxl_cper_work)
+               return -EINVAL;
+
+       guard(spinlock_irqsave)(&cxl_cper_work_lock);
+       cxl_cper_work = work;
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL");
+
+int cxl_cper_unregister_work(struct work_struct *work)
+{
+       if (cxl_cper_work != work)
+               return -EINVAL;
+
+       guard(spinlock_irqsave)(&cxl_cper_work_lock);
+       cxl_cper_work = NULL;
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
+
+int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
+{
+       return kfifo_get(&cxl_cper_fifo, wd);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL");
+
 /*
  * GHES error status reporting throttle, to report more kinds of
  * errors, instead of just most frequently occurred errors.
diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h
index d9f9253d8de9..a853a5996cdf 100644
--- a/include/acpi/ghes_cper.h
+++ b/include/acpi/ghes_cper.h
@@ -20,6 +20,7 @@
 
 #include <acpi/ghes.h>
 #include <asm/fixmap.h>
+#include <cxl/event.h>
 
 #define GHES_PFX       "GHES: "
 
@@ -106,5 +107,15 @@ void ghes_estatus_cache_add(struct acpi_hest_generic 
*generic,
                            struct acpi_hest_generic_status *estatus);
 void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
                                   int sev);
+void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
+                           int severity);
+int cxl_cper_register_prot_err_work(struct work_struct *work);
+int cxl_cper_unregister_prot_err_work(struct work_struct *work);
+int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd);
+void cxl_cper_post_event(enum cxl_event_type event_type,
+                        struct cxl_cper_event_rec *rec);
+int cxl_cper_register_work(struct work_struct *work);
+int cxl_cper_unregister_work(struct work_struct *work);
+int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd);
 
 #endif /* ACPI_APEI_GHES_CPER_H */

-- 
2.43.0


Reply via email to