Loading drivers/acpi/apei/ghes.c +110 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <linux/interrupt.h> #include <linux/timer.h> #include <linux/cper.h> #include <linux/cleanup.h> #include <linux/cxl-event.h> #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/ratelimit.h> Loading @@ -33,6 +35,7 @@ #include <linux/irq_work.h> #include <linux/llist.h> #include <linux/genalloc.h> #include <linux/kfifo.h> #include <linux/pci.h> #include <linux/pfn.h> #include <linux/aer.h> Loading Loading @@ -673,6 +676,101 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, schedule_work(&entry->work); } /* CXL Event record UUIDs are formated as GUIDs and reported in section type */ /* * General Media Event Record * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 */ #define CPER_SEC_CXL_GEN_MEDIA_GUID \ GUID_INIT(0xfbcd0a77, 0xc260, 0x417f, \ 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6) /* * DRAM Event Record * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 */ #define CPER_SEC_CXL_DRAM_GUID \ GUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, \ 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24) /* * Memory Module Event Record * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 */ #define CPER_SEC_CXL_MEM_MODULE_GUID \ GUID_INIT(0xfe927475, 0xdd59, 0x4339, \ 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74) /* 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 bool ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { Loading Loading @@ -707,6 +805,18 @@ static bool ghes_do_proc(struct ghes *ghes, } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { queued = ghes_handle_arm_hw_error(gdata, sev, sync); } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata); 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); 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); cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); } else { void *err = acpi_hest_get_payload(gdata); Loading drivers/cxl/pci.c +70 −1 Original line number Diff line number Diff line Loading @@ -974,6 +974,75 @@ static struct pci_driver cxl_pci_driver = { }, }; module_pci_driver(cxl_pci_driver); #define CXL_EVENT_HDR_FLAGS_REC_SEVERITY GENMASK(1, 0) static void cxl_handle_cper_event(enum cxl_event_type ev_type, struct cxl_cper_event_rec *rec) { struct cper_cxl_event_devid *device_id = &rec->hdr.device_id; struct pci_dev *pdev __free(pci_dev_put) = NULL; enum cxl_event_log_type log_type; struct cxl_dev_state *cxlds; unsigned int devfn; u32 hdr_flags; pr_debug("CPER event %d for device %u:%u:%u.%u\n", ev_type, device_id->segment_num, device_id->bus_num, device_id->device_num, device_id->func_num); devfn = PCI_DEVFN(device_id->device_num, device_id->func_num); pdev = pci_get_domain_bus_and_slot(device_id->segment_num, device_id->bus_num, devfn); if (!pdev) return; guard(device)(&pdev->dev); if (pdev->driver != &cxl_pci_driver) return; cxlds = pci_get_drvdata(pdev); if (!cxlds) return; /* Fabricate a log type */ hdr_flags = get_unaligned_le24(rec->event.generic.hdr.flags); log_type = FIELD_GET(CXL_EVENT_HDR_FLAGS_REC_SEVERITY, hdr_flags); cxl_event_trace_record(cxlds->cxlmd, log_type, ev_type, &uuid_null, &rec->event); } static void cxl_cper_work_fn(struct work_struct *work) { struct cxl_cper_work_data wd; while (cxl_cper_kfifo_get(&wd)) cxl_handle_cper_event(wd.event_type, &wd.rec); } static DECLARE_WORK(cxl_cper_work, cxl_cper_work_fn); static int __init cxl_pci_driver_init(void) { int rc; rc = pci_register_driver(&cxl_pci_driver); if (rc) return rc; rc = cxl_cper_register_work(&cxl_cper_work); if (rc) pci_unregister_driver(&cxl_pci_driver); return rc; } static void __exit cxl_pci_driver_exit(void) { cxl_cper_unregister_work(&cxl_cper_work); cancel_work_sync(&cxl_cper_work); pci_unregister_driver(&cxl_pci_driver); } module_init(cxl_pci_driver_init); module_exit(cxl_pci_driver_exit); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(CXL); include/linux/cxl-event.h +26 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ #include <linux/types.h> #include <linux/uuid.h> #include <linux/workqueue_types.h> /* * Common Event Record Format Loading Loading @@ -153,4 +154,29 @@ struct cxl_cper_event_rec { union cxl_event event; } __packed; struct cxl_cper_work_data { enum cxl_event_type event_type; struct cxl_cper_event_rec rec; }; #ifdef CONFIG_ACPI_APEI_GHES 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); #else static inline int cxl_cper_register_work(struct work_struct *work); { return 0; } static inline int cxl_cper_unregister_work(struct work_struct *work); { return 0; } static inline int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) { return 0; } #endif #endif /* _LINUX_CXL_EVENT_H */ Loading
drivers/acpi/apei/ghes.c +110 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <linux/interrupt.h> #include <linux/timer.h> #include <linux/cper.h> #include <linux/cleanup.h> #include <linux/cxl-event.h> #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/ratelimit.h> Loading @@ -33,6 +35,7 @@ #include <linux/irq_work.h> #include <linux/llist.h> #include <linux/genalloc.h> #include <linux/kfifo.h> #include <linux/pci.h> #include <linux/pfn.h> #include <linux/aer.h> Loading Loading @@ -673,6 +676,101 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, schedule_work(&entry->work); } /* CXL Event record UUIDs are formated as GUIDs and reported in section type */ /* * General Media Event Record * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 */ #define CPER_SEC_CXL_GEN_MEDIA_GUID \ GUID_INIT(0xfbcd0a77, 0xc260, 0x417f, \ 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6) /* * DRAM Event Record * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 */ #define CPER_SEC_CXL_DRAM_GUID \ GUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, \ 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24) /* * Memory Module Event Record * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 */ #define CPER_SEC_CXL_MEM_MODULE_GUID \ GUID_INIT(0xfe927475, 0xdd59, 0x4339, \ 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74) /* 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 bool ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { Loading Loading @@ -707,6 +805,18 @@ static bool ghes_do_proc(struct ghes *ghes, } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { queued = ghes_handle_arm_hw_error(gdata, sev, sync); } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata); 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); 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); cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); } else { void *err = acpi_hest_get_payload(gdata); Loading
drivers/cxl/pci.c +70 −1 Original line number Diff line number Diff line Loading @@ -974,6 +974,75 @@ static struct pci_driver cxl_pci_driver = { }, }; module_pci_driver(cxl_pci_driver); #define CXL_EVENT_HDR_FLAGS_REC_SEVERITY GENMASK(1, 0) static void cxl_handle_cper_event(enum cxl_event_type ev_type, struct cxl_cper_event_rec *rec) { struct cper_cxl_event_devid *device_id = &rec->hdr.device_id; struct pci_dev *pdev __free(pci_dev_put) = NULL; enum cxl_event_log_type log_type; struct cxl_dev_state *cxlds; unsigned int devfn; u32 hdr_flags; pr_debug("CPER event %d for device %u:%u:%u.%u\n", ev_type, device_id->segment_num, device_id->bus_num, device_id->device_num, device_id->func_num); devfn = PCI_DEVFN(device_id->device_num, device_id->func_num); pdev = pci_get_domain_bus_and_slot(device_id->segment_num, device_id->bus_num, devfn); if (!pdev) return; guard(device)(&pdev->dev); if (pdev->driver != &cxl_pci_driver) return; cxlds = pci_get_drvdata(pdev); if (!cxlds) return; /* Fabricate a log type */ hdr_flags = get_unaligned_le24(rec->event.generic.hdr.flags); log_type = FIELD_GET(CXL_EVENT_HDR_FLAGS_REC_SEVERITY, hdr_flags); cxl_event_trace_record(cxlds->cxlmd, log_type, ev_type, &uuid_null, &rec->event); } static void cxl_cper_work_fn(struct work_struct *work) { struct cxl_cper_work_data wd; while (cxl_cper_kfifo_get(&wd)) cxl_handle_cper_event(wd.event_type, &wd.rec); } static DECLARE_WORK(cxl_cper_work, cxl_cper_work_fn); static int __init cxl_pci_driver_init(void) { int rc; rc = pci_register_driver(&cxl_pci_driver); if (rc) return rc; rc = cxl_cper_register_work(&cxl_cper_work); if (rc) pci_unregister_driver(&cxl_pci_driver); return rc; } static void __exit cxl_pci_driver_exit(void) { cxl_cper_unregister_work(&cxl_cper_work); cancel_work_sync(&cxl_cper_work); pci_unregister_driver(&cxl_pci_driver); } module_init(cxl_pci_driver_init); module_exit(cxl_pci_driver_exit); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(CXL);
include/linux/cxl-event.h +26 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ #include <linux/types.h> #include <linux/uuid.h> #include <linux/workqueue_types.h> /* * Common Event Record Format Loading Loading @@ -153,4 +154,29 @@ struct cxl_cper_event_rec { union cxl_event event; } __packed; struct cxl_cper_work_data { enum cxl_event_type event_type; struct cxl_cper_event_rec rec; }; #ifdef CONFIG_ACPI_APEI_GHES 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); #else static inline int cxl_cper_register_work(struct work_struct *work); { return 0; } static inline int cxl_cper_unregister_work(struct work_struct *work); { return 0; } static inline int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) { return 0; } #endif #endif /* _LINUX_CXL_EVENT_H */