[PATCH v3] cxl/core: Create einj_types when EINJ becomes available
From: Yongxin Yang
Date: Thu Oct 29 2026 - 02:58:26 EST
From: Yang Yongxin <yangyongxin@xxxxxxxxxx>
The einj_types file under the CXL debugfs directory is never created.
cxl_core_init() runs at subsys_initcall level, while EINJ registers itself
from einj_init() at device_initcall level. When cxl_core_init() evaluates
einj_cxl_is_initialized() the built-in EINJ driver has not been initialized
yet, so the value is always false and the debugfs_create_file("einj_types")
call below it is never reached. Users have no way to discover which CXL
error types the platform supports from debugfs.
Create the file from every point that can tell EINJ is up: cxl_core_init()
covers cxl_core being a module loaded after EINJ has been initialized;
cxl_acpi_probe() covers the same case for cxl_acpi and also systems where
the CEDT is parsed but no dport is ever added; and the dport probe path
covers the built-in case, where the dport added from the memdev probe is
the only hook reached after einj_init().
The dport probe path can run concurrently for different parent ports, each
holding only its own device lock, so serialize the check and the create to
avoid a duplicate debugfs_create_file() attempt that would only fail with
-EEXIST. Reset the cached dentry in cxl_core_exit() so that reloading
cxl_core starts from a clean state.
Fixes: 8039804cfa73 ("cxl/core: Add CXL EINJ debugfs files")
Cc: <stable@xxxxxxxxxxxxxxx>
Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@xxxxxxx/
Signed-off-by: Yang Yongxin <yangyongxin@xxxxxxxxxx>
---
Changes in v3:
- Take the review from Ben: the helper is now cxl_debugfs_einj_add_types(),
non-static, declared in cxl.h and exported to cxl_acpi only, so that the
cxl_acpi hook can call it.
- Add the cxl_acpi_probe() hook, before cxl_bus_rescan(), so the file is
created once the CXL topology exists instead of waiting for a dport.
- Keep the dport path as a retry point. With EINJ built in, cxl_core_init()
and cxl_acpi_probe() both run before einj_init(), so dropping it would
still leave the file uncreated there.
- Drop the "RCH is out of scope" paragraph. RCH does add a dport, from
add_host_bridge_dport(); the file is still not created there when EINJ is
built in, so do not claim the acpi hook fixes RCH.
- Reset einj_types_dentry and cxl_debugfs in cxl_core_exit(), so that a
reloaded cxl_core does not keep a stale pointer and lose the file.
- Use guard(mutex) instead of mutex_lock()/mutex_unlock().
drivers/cxl/acpi.c | 9 +++++++++
drivers/cxl/core/port.c | 41 +++++++++++++++++++++++++++++++++++------
drivers/cxl/cxl.h | 2 ++
3 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 3b818adbd38b..0584245b30f2 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -958,6 +958,15 @@ static int cxl_acpi_probe(struct platform_device *pdev)
if (rc < 0)
return rc;
+ /*
+ * Create einj_types from here as well, so the file also shows up when
+ * cxl_acpi is a module loaded after EINJ, and on systems where the
+ * CEDT is parsed but no dport is ever added (no CHBS for the host
+ * bridge, or a CHBS without a valid component register base), which
+ * leaves the dport probe path untouched.
+ */
+ cxl_debugfs_einj_add_types();
+
/* In case PCI is scanned before ACPI re-trigger memdev attach */
cxl_bus_rescan();
return 0;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 625e4aa427db..76ac612b514c 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -802,6 +802,35 @@ static int cxl_dport_setup_regs(struct device *host, struct cxl_dport *dport,
DEFINE_SHOW_ATTRIBUTE(einj_cxl_available_error_type);
+static struct dentry *cxl_debugfs;
+static struct dentry *einj_types_dentry;
+
+/*
+ * einj_types can only be created once EINJ is up, and no single hook is
+ * reached after that point in every configuration:
+ *
+ * - cxl_core_init() runs at subsys_initcall level and cxl_acpi_probe() at
+ * subsys_initcall_sync level, so both create the file when cxl_core and
+ * cxl_acpi are modules loaded after EINJ has been initialized;
+ * - when EINJ is built in, einj_init() runs at device_initcall level, so
+ * both of them are too early and the only hook left is the dport added
+ * from the memdev probe.
+ *
+ * The dport probe path can run concurrently for different parent ports, each
+ * holding only its own device lock, so the check and the create need to be
+ * serialized to avoid a duplicate debugfs_create_file() attempt.
+ */
+static DEFINE_MUTEX(einj_types_lock);
+void cxl_debugfs_einj_add_types(void)
+{
+ guard(mutex)(&einj_types_lock);
+ if (cxl_debugfs && !einj_types_dentry && einj_cxl_is_initialized())
+ einj_types_dentry = debugfs_create_file("einj_types", 0400,
+ cxl_debugfs, NULL,
+ &einj_cxl_available_error_type_fops);
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_debugfs_einj_add_types, "cxl_acpi");
+
static int cxl_einj_inject(void *data, u64 type)
{
struct cxl_dport *dport = data;
@@ -822,6 +851,8 @@ static void cxl_debugfs_create_dport_dir(struct cxl_dport *dport)
if (!einj_cxl_is_initialized())
return;
+ cxl_debugfs_einj_add_types();
+
/*
* Protocol error injection is only available for CXL 2.0+ root ports
* and CXL 1.1 downstream ports
@@ -2501,8 +2532,6 @@ const struct bus_type cxl_bus_type = {
};
EXPORT_SYMBOL_NS_GPL(cxl_bus_type, "CXL");
-static struct dentry *cxl_debugfs;
-
struct dentry *cxl_debugfs_create_dir(const char *dir)
{
return debugfs_create_dir(dir, cxl_debugfs);
@@ -2515,10 +2544,6 @@ static __init int cxl_core_init(void)
cxl_debugfs = debugfs_create_dir("cxl", NULL);
- if (einj_cxl_is_initialized())
- debugfs_create_file("einj_types", 0400, cxl_debugfs, NULL,
- &einj_cxl_available_error_type_fops);
-
cxl_mbox_init();
rc = cxl_memdev_init();
@@ -2541,6 +2566,8 @@ static __init int cxl_core_init(void)
cxl_ras_init();
+ cxl_debugfs_einj_add_types();
+
return 0;
err_region:
@@ -2560,6 +2587,8 @@ static void cxl_core_exit(void)
destroy_workqueue(cxl_bus_wq);
cxl_memdev_exit();
debugfs_remove_recursive(cxl_debugfs);
+ cxl_debugfs = NULL;
+ einj_types_dentry = NULL;
}
subsys_initcall(cxl_core_init);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index cab8ce39f465..b9245261ee39 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -926,4 +926,6 @@ struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port,
u16 cxl_gpf_get_dvsec(struct device *dev);
+void cxl_debugfs_einj_add_types(void);
+
#endif /* __CXL_H__ */
--
2.25.1