Merge branch 'for-7.3/cxl-port-err-prep' into cxl-for-next

cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
cxl: Tighten CPER kfifo registration API and symbol visibility
acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register
cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read
This commit is contained in:
Dave Jiang 2026-08-05 13:55:07 -07:00
commit 0673986e12
7 changed files with 87 additions and 68 deletions

View File

@ -749,7 +749,7 @@ 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);
static DEFINE_RAW_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,
@ -761,7 +761,7 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
if (cxl_cper_sec_prot_err_valid(prot_err))
return;
guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
if (!cxl_cper_prot_err_work)
return;
@ -778,40 +778,48 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
#endif
}
int cxl_cper_register_prot_err_work(struct work_struct *work)
void cxl_cper_register_prot_err_work(struct work_struct *work)
{
if (cxl_cper_prot_err_work)
return -EINVAL;
guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
guard(spinlock)(&cxl_cper_prot_err_work_lock);
if (WARN_ONCE(cxl_cper_prot_err_work,
"CPER-CXL kfifo consumer already registered\n"))
return;
cxl_cper_prot_err_work = work;
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL");
EXPORT_SYMBOL_FOR_MODULES(cxl_cper_register_prot_err_work, "cxl_core");
int cxl_cper_unregister_prot_err_work(struct work_struct *work)
void cxl_cper_unregister_prot_err_work(void)
{
if (cxl_cper_prot_err_work != work)
return -EINVAL;
struct work_struct *old;
guard(spinlock)(&cxl_cper_prot_err_work_lock);
cxl_cper_prot_err_work = NULL;
return 0;
scoped_guard(raw_spinlock_irqsave, &cxl_cper_prot_err_work_lock) {
WARN_ONCE(!cxl_cper_prot_err_work,
"CPER-CXL kfifo consumer not registered on unregister\n");
old = cxl_cper_prot_err_work;
cxl_cper_prot_err_work = NULL;
}
if (old)
cancel_work_sync(old);
/* Discard stale entries so they are not replayed on next module load */
kfifo_reset(&cxl_cper_prot_err_fifo);
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
EXPORT_SYMBOL_FOR_MODULES(cxl_cper_unregister_prot_err_work, "cxl_core");
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");
EXPORT_SYMBOL_FOR_MODULES(cxl_cper_prot_err_kfifo_get, "cxl_core");
/* 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);
static DEFINE_RAW_SPINLOCK(cxl_cper_work_lock);
struct work_struct *cxl_cper_work;
static void cxl_cper_post_event(enum cxl_event_type event_type,
@ -831,7 +839,7 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
return;
}
guard(spinlock_irqsave)(&cxl_cper_work_lock);
guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
if (!cxl_cper_work)
return;
@ -849,23 +857,29 @@ static void cxl_cper_post_event(enum cxl_event_type event_type,
int cxl_cper_register_work(struct work_struct *work)
{
if (cxl_cper_work)
guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
if (WARN_ONCE(cxl_cper_work,
"CXL CPER kfifo consumer already registered\n"))
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)
void cxl_cper_unregister_work(struct work_struct *work)
{
if (cxl_cper_work != work)
return -EINVAL;
scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) {
if (WARN_ONCE(cxl_cper_work != work,
"CXL CPER kfifo consumer mismatch on unregister\n"))
return;
cxl_cper_work = NULL;
}
guard(spinlock)(&cxl_cper_work_lock);
cxl_cper_work = NULL;
return 0;
cancel_work_sync(work);
/* Discard stale entries so they are not replayed on next module load */
kfifo_reset(&cxl_cper_fifo);
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");

View File

@ -185,7 +185,7 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
return &port->dev;
}
#ifdef CONFIG_CXL_RAS
int cxl_ras_init(void);
void cxl_ras_init(void);
void cxl_ras_exit(void);
bool cxl_handle_ras(struct device *dev, void __iomem *ras_base);
void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base);
@ -194,10 +194,7 @@ void cxl_disable_rch_root_ints(struct cxl_dport *dport);
void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds);
void devm_cxl_dport_ras_setup(struct cxl_dport *dport);
#else
static inline int cxl_ras_init(void)
{
return 0;
}
static inline void cxl_ras_init(void) { }
static inline void cxl_ras_exit(void) { }
static inline bool cxl_handle_ras(struct device *dev, void __iomem *ras_base)
{

View File

@ -1379,7 +1379,7 @@ static int match_port_by_dport(struct device *dev, const void *data)
return dport != NULL;
}
static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
static struct cxl_port *__find_cxl_port_by_dport(struct cxl_find_port_ctx *ctx)
{
struct device *dev;
@ -1392,8 +1392,16 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
return NULL;
}
static struct cxl_port *find_cxl_port(struct device *dport_dev,
struct cxl_dport **dport)
/**
* find_cxl_port_by_dport - find a cxl_port by one of its targets
* @dport_dev: device representing the dport target
* @dport: optional output of the 'struct cxl_dport' companion of the @dport_dev
*
* Return a 'struct cxl_port' with an elevated reference if found. Use
* __free(put_cxl_port) to release.
*/
static struct cxl_port *find_cxl_port_by_dport(struct device *dport_dev,
struct cxl_dport **dport)
{
struct cxl_find_port_ctx ctx = {
.dport_dev = dport_dev,
@ -1401,7 +1409,7 @@ static struct cxl_port *find_cxl_port(struct device *dport_dev,
};
struct cxl_port *port;
port = __find_cxl_port(&ctx);
port = __find_cxl_port_by_dport(&ctx);
return port;
}
@ -1895,14 +1903,14 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, "CXL");
struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
struct cxl_dport **dport)
{
return find_cxl_port(pdev->dev.parent, dport);
return find_cxl_port_by_dport(pdev->dev.parent, dport);
}
EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, "CXL");
struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
struct cxl_dport **dport)
{
return find_cxl_port(grandparent(&cxlmd->dev), dport);
return find_cxl_port_by_dport(grandparent(&cxlmd->dev), dport);
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, "CXL");
@ -2531,14 +2539,10 @@ static __init int cxl_core_init(void)
if (rc)
goto err_region;
rc = cxl_ras_init();
if (rc)
goto err_ras;
cxl_ras_init();
return 0;
err_ras:
cxl_region_exit();
err_region:
bus_unregister(&cxl_bus_type);
err_bus:

View File

@ -129,15 +129,14 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
}
static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
int cxl_ras_init(void)
void cxl_ras_init(void)
{
return cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
}
void cxl_ras_exit(void)
{
cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work);
cancel_work_sync(&cxl_cper_prot_err_work);
cxl_cper_unregister_prot_err_work();
}
static void cxl_dport_map_ras(struct cxl_dport *dport)

View File

@ -58,13 +58,28 @@ void cxl_disable_rch_root_ints(struct cxl_dport *dport)
static bool cxl_rch_get_aer_info(void __iomem *aer_base,
struct aer_capability_regs *aer_regs)
{
int read_cnt = sizeof(struct aer_capability_regs) / sizeof(u32);
/*
* Bound the copy to the physically-defined AER registers (header
* through the 16-byte Header Log). struct aer_capability_regs is a
* software layout whose embedded struct pcie_tlp_log is larger than
* the on-wire AER capability; copying sizeof(*aer_regs) would
* over-read the RCRB-mapped MMIO block.
*/
int read_cnt = (PCI_ERR_HEADER_LOG + 16) / sizeof(u32);
u32 *aer_regs_buf = (u32 *)aer_regs;
int n;
if (!aer_base)
return false;
/*
* Zero the destination so the software-only tail fields
* (e.g. header_log.header_len) are deterministic rather than
* left as uninitialized stack, which could drive a bogus loop
* length in pcie_print_tlp_log().
*/
memset(aer_regs, 0, sizeof(*aer_regs));
/* Use readl() to guarantee 32-bit accesses */
for (n = 0; n < read_cnt; n++)
aer_regs_buf[n] = readl(aer_base + n * sizeof(u32));
@ -79,11 +94,11 @@ static bool cxl_rch_get_aer_info(void __iomem *aer_base,
static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
int *severity)
{
if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
*severity = AER_FATAL;
else
*severity = AER_NONFATAL;
u32 uncor_status = aer_regs->uncor_status & ~aer_regs->uncor_mask;
if (uncor_status) {
*severity = (uncor_status & aer_regs->uncor_severity) ?
AER_FATAL : AER_NONFATAL;
return true;
}

View File

@ -1081,7 +1081,6 @@ static int __init cxl_pci_driver_init(void)
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);
}

View File

@ -287,10 +287,10 @@ struct cxl_cper_prot_err_work_data {
#ifdef CONFIG_ACPI_APEI_GHES
int cxl_cper_register_work(struct work_struct *work);
int cxl_cper_unregister_work(struct work_struct *work);
void cxl_cper_unregister_work(struct work_struct *work);
int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd);
int cxl_cper_register_prot_err_work(struct work_struct *work);
int cxl_cper_unregister_prot_err_work(struct work_struct *work);
void cxl_cper_register_prot_err_work(struct work_struct *work);
void cxl_cper_unregister_prot_err_work(void);
int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd);
#else
static inline int cxl_cper_register_work(struct work_struct *work)
@ -298,22 +298,13 @@ 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 void cxl_cper_unregister_work(struct work_struct *work) { }
static inline int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
{
return 0;
}
static inline int cxl_cper_register_prot_err_work(struct work_struct *work)
{
return 0;
}
static inline int cxl_cper_unregister_prot_err_work(struct work_struct *work)
{
return 0;
}
static inline void cxl_cper_register_prot_err_work(struct work_struct *work) { }
static inline void cxl_cper_unregister_prot_err_work(void) { }
static inline int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd)
{
return 0;