mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-06 14:05:39 +00:00
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.
This commit is contained in:
commit
df2a8f4b44
@ -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>
|
||||
@ -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>
|
||||
@ -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)
|
||||
{
|
||||
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/workqueue_types.h>
|
||||
|
||||
/*
|
||||
* Common Event Record Format
|
||||
@ -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…
Reference in New Issue
Block a user