Skip to content

Commit df2a8f4

Browse files
committed
Merge remote-tracking branch 'cxl/for-6.10/cper' into cxl-for-next
Add support to send CPER records to CXL for more detailed parsing.
2 parents d357dd8 + c19ac30 commit df2a8f4

File tree

3 files changed

+206
-1
lines changed

3 files changed

+206
-1
lines changed

drivers/acpi/apei/ghes.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
#include <linux/interrupt.h>
2727
#include <linux/timer.h>
2828
#include <linux/cper.h>
29+
#include <linux/cleanup.h>
30+
#include <linux/cxl-event.h>
2931
#include <linux/platform_device.h>
3032
#include <linux/mutex.h>
3133
#include <linux/ratelimit.h>
3234
#include <linux/vmalloc.h>
3335
#include <linux/irq_work.h>
3436
#include <linux/llist.h>
3537
#include <linux/genalloc.h>
38+
#include <linux/kfifo.h>
3639
#include <linux/pci.h>
3740
#include <linux/pfn.h>
3841
#include <linux/aer.h>
@@ -673,6 +676,101 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
673676
schedule_work(&entry->work);
674677
}
675678

679+
/* CXL Event record UUIDs are formated as GUIDs and reported in section type */
680+
681+
/*
682+
* General Media Event Record
683+
* CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43
684+
*/
685+
#define CPER_SEC_CXL_GEN_MEDIA_GUID \
686+
GUID_INIT(0xfbcd0a77, 0xc260, 0x417f, \
687+
0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6)
688+
689+
/*
690+
* DRAM Event Record
691+
* CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44
692+
*/
693+
#define CPER_SEC_CXL_DRAM_GUID \
694+
GUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, \
695+
0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24)
696+
697+
/*
698+
* Memory Module Event Record
699+
* CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45
700+
*/
701+
#define CPER_SEC_CXL_MEM_MODULE_GUID \
702+
GUID_INIT(0xfe927475, 0xdd59, 0x4339, \
703+
0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74)
704+
705+
/* Room for 8 entries for each of the 4 event log queues */
706+
#define CXL_CPER_FIFO_DEPTH 32
707+
DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
708+
709+
/* Synchronize schedule_work() with cxl_cper_work changes */
710+
static DEFINE_SPINLOCK(cxl_cper_work_lock);
711+
struct work_struct *cxl_cper_work;
712+
713+
static void cxl_cper_post_event(enum cxl_event_type event_type,
714+
struct cxl_cper_event_rec *rec)
715+
{
716+
struct cxl_cper_work_data wd;
717+
718+
if (rec->hdr.length <= sizeof(rec->hdr) ||
719+
rec->hdr.length > sizeof(*rec)) {
720+
pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
721+
rec->hdr.length);
722+
return;
723+
}
724+
725+
if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
726+
pr_err(FW_WARN "CXL CPER invalid event\n");
727+
return;
728+
}
729+
730+
guard(spinlock_irqsave)(&cxl_cper_work_lock);
731+
732+
if (!cxl_cper_work)
733+
return;
734+
735+
wd.event_type = event_type;
736+
memcpy(&wd.rec, rec, sizeof(wd.rec));
737+
738+
if (!kfifo_put(&cxl_cper_fifo, wd)) {
739+
pr_err_ratelimited("CXL CPER kfifo overflow\n");
740+
return;
741+
}
742+
743+
schedule_work(cxl_cper_work);
744+
}
745+
746+
int cxl_cper_register_work(struct work_struct *work)
747+
{
748+
if (cxl_cper_work)
749+
return -EINVAL;
750+
751+
guard(spinlock)(&cxl_cper_work_lock);
752+
cxl_cper_work = work;
753+
return 0;
754+
}
755+
EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, CXL);
756+
757+
int cxl_cper_unregister_work(struct work_struct *work)
758+
{
759+
if (cxl_cper_work != work)
760+
return -EINVAL;
761+
762+
guard(spinlock)(&cxl_cper_work_lock);
763+
cxl_cper_work = NULL;
764+
return 0;
765+
}
766+
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, CXL);
767+
768+
int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
769+
{
770+
return kfifo_get(&cxl_cper_fifo, wd);
771+
}
772+
EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, CXL);
773+
676774
static bool ghes_do_proc(struct ghes *ghes,
677775
const struct acpi_hest_generic_status *estatus)
678776
{
@@ -707,6 +805,18 @@ static bool ghes_do_proc(struct ghes *ghes,
707805
}
708806
else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
709807
queued = ghes_handle_arm_hw_error(gdata, sev, sync);
808+
} else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) {
809+
struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
810+
811+
cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec);
812+
} else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) {
813+
struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
814+
815+
cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec);
816+
} else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) {
817+
struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);
818+
819+
cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec);
710820
} else {
711821
void *err = acpi_hest_get_payload(gdata);
712822

drivers/cxl/pci.c

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,75 @@ static struct pci_driver cxl_pci_driver = {
974974
},
975975
};
976976

977-
module_pci_driver(cxl_pci_driver);
977+
#define CXL_EVENT_HDR_FLAGS_REC_SEVERITY GENMASK(1, 0)
978+
static void cxl_handle_cper_event(enum cxl_event_type ev_type,
979+
struct cxl_cper_event_rec *rec)
980+
{
981+
struct cper_cxl_event_devid *device_id = &rec->hdr.device_id;
982+
struct pci_dev *pdev __free(pci_dev_put) = NULL;
983+
enum cxl_event_log_type log_type;
984+
struct cxl_dev_state *cxlds;
985+
unsigned int devfn;
986+
u32 hdr_flags;
987+
988+
pr_debug("CPER event %d for device %u:%u:%u.%u\n", ev_type,
989+
device_id->segment_num, device_id->bus_num,
990+
device_id->device_num, device_id->func_num);
991+
992+
devfn = PCI_DEVFN(device_id->device_num, device_id->func_num);
993+
pdev = pci_get_domain_bus_and_slot(device_id->segment_num,
994+
device_id->bus_num, devfn);
995+
if (!pdev)
996+
return;
997+
998+
guard(device)(&pdev->dev);
999+
if (pdev->driver != &cxl_pci_driver)
1000+
return;
1001+
1002+
cxlds = pci_get_drvdata(pdev);
1003+
if (!cxlds)
1004+
return;
1005+
1006+
/* Fabricate a log type */
1007+
hdr_flags = get_unaligned_le24(rec->event.generic.hdr.flags);
1008+
log_type = FIELD_GET(CXL_EVENT_HDR_FLAGS_REC_SEVERITY, hdr_flags);
1009+
1010+
cxl_event_trace_record(cxlds->cxlmd, log_type, ev_type,
1011+
&uuid_null, &rec->event);
1012+
}
1013+
1014+
static void cxl_cper_work_fn(struct work_struct *work)
1015+
{
1016+
struct cxl_cper_work_data wd;
1017+
1018+
while (cxl_cper_kfifo_get(&wd))
1019+
cxl_handle_cper_event(wd.event_type, &wd.rec);
1020+
}
1021+
static DECLARE_WORK(cxl_cper_work, cxl_cper_work_fn);
1022+
1023+
static int __init cxl_pci_driver_init(void)
1024+
{
1025+
int rc;
1026+
1027+
rc = pci_register_driver(&cxl_pci_driver);
1028+
if (rc)
1029+
return rc;
1030+
1031+
rc = cxl_cper_register_work(&cxl_cper_work);
1032+
if (rc)
1033+
pci_unregister_driver(&cxl_pci_driver);
1034+
1035+
return rc;
1036+
}
1037+
1038+
static void __exit cxl_pci_driver_exit(void)
1039+
{
1040+
cxl_cper_unregister_work(&cxl_cper_work);
1041+
cancel_work_sync(&cxl_cper_work);
1042+
pci_unregister_driver(&cxl_pci_driver);
1043+
}
1044+
1045+
module_init(cxl_pci_driver_init);
1046+
module_exit(cxl_pci_driver_exit);
9781047
MODULE_LICENSE("GPL v2");
9791048
MODULE_IMPORT_NS(CXL);

include/linux/cxl-event.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <linux/types.h>
77
#include <linux/uuid.h>
8+
#include <linux/workqueue_types.h>
89

910
/*
1011
* Common Event Record Format
@@ -153,4 +154,29 @@ struct cxl_cper_event_rec {
153154
union cxl_event event;
154155
} __packed;
155156

157+
struct cxl_cper_work_data {
158+
enum cxl_event_type event_type;
159+
struct cxl_cper_event_rec rec;
160+
};
161+
162+
#ifdef CONFIG_ACPI_APEI_GHES
163+
int cxl_cper_register_work(struct work_struct *work);
164+
int cxl_cper_unregister_work(struct work_struct *work);
165+
int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd);
166+
#else
167+
static inline int cxl_cper_register_work(struct work_struct *work);
168+
{
169+
return 0;
170+
}
171+
172+
static inline int cxl_cper_unregister_work(struct work_struct *work);
173+
{
174+
return 0;
175+
}
176+
static inline int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
177+
{
178+
return 0;
179+
}
180+
#endif
181+
156182
#endif /* _LINUX_CXL_EVENT_H */

0 commit comments

Comments
 (0)