* [PATCH] cxl/core: Create einj_types when EINJ becomes available
@ 2026-10-28 5:54 Yongxin Yang
2026-10-28 6:08 ` sashiko-bot
2026-10-28 8:49 ` [PATCH v2] " Yongxin Yang
0 siblings, 2 replies; 12+ messages in thread
From: Yongxin Yang @ 2026-10-28 5:54 UTC (permalink / raw)
To: dave.jiang
Cc: dave, jic23, alison.schofield, vishal.l.verma, djbw, iweiny,
ming.li, linux-cxl, linux-kernel, Yang Yongxin, stable
From: Yang Yongxin <yangyongxin@kylinos.cn>
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 module_init level. When cxl_core_init() evaluates
einj_cxl_is_initialized() the 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 lazily, from the first place where EINJ is known to be up.
Note that this is a partial fix: if no injectable dport is ever added, the
file still does not appear. Fixing the initcall ordering itself would
require moving cxl_core_init() after EINJ is probed, which risks the CXL
bus registration order, so it is left out of scope here.
Fixes: 8039804cfa73 ("cxl/core: Add CXL EINJ debugfs files")
Cc: <stable@vger.kernel.org>
Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
---
drivers/cxl/core/port.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 625e4aa427db..7de08063469f 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -802,6 +802,27 @@ 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 cannot be created from cxl_core_init(): that runs at
+ * subsys_initcall level, before einj_init() has set einj_initialized.
+ * Create it lazily, from the first place where EINJ is known to be up.
+ */
+static void cxl_einj_debugfs_add_types(void)
+{
+ if (einj_types_dentry)
+ return;
+
+ if (!einj_cxl_is_initialized())
+ return;
+
+ einj_types_dentry = debugfs_create_file("einj_types", 0400, cxl_debugfs,
+ NULL,
+ &einj_cxl_available_error_type_fops);
+}
+
static int cxl_einj_inject(void *data, u64 type)
{
struct cxl_dport *dport = data;
@@ -819,6 +840,8 @@ static void cxl_debugfs_create_dport_dir(struct cxl_dport *dport)
struct cxl_port *parent = parent_port_of(dport->port);
struct dentry *dir;
+ cxl_einj_debugfs_add_types();
+
if (!einj_cxl_is_initialized())
return;
@@ -2501,8 +2524,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 +2536,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();
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH] cxl/core: Create einj_types when EINJ becomes available
2026-10-28 5:54 [PATCH] cxl/core: Create einj_types when EINJ becomes available Yongxin Yang
@ 2026-10-28 6:08 ` sashiko-bot
2026-10-28 8:49 ` [PATCH v2] " Yongxin Yang
1 sibling, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-10-28 6:08 UTC (permalink / raw)
To: Yongxin Yang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] cxl/core: data race on einj_types_dentry in cxl_einj_debugfs_add_types()
- [Medium] cxl/core: incomplete einj_types init ordering and modular regression
--
commit 8c31c56682570f7a66710a4b59dab14f80b09a59
Author: Yang Yongxin <yangyongxin@kylinos.cn>
cxl/core: Create einj_types when EINJ becomes available
This commit moves the creation of the einj_types debugfs file out of
cxl_core_init() into a helper called from cxl_debugfs_create_dport_dir() so
that the file can be created lazily after the einj driver initializes.
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index f3705ea44db77..f80dd8f4221a7 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -811,6 +811,27 @@ 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 cannot be created from cxl_core_init(): that runs at
> + * subsys_initcall level, before einj_init() has set einj_initialized.
> + * Create it lazily, from the first place where EINJ is known to be up.
> + */
> +static void cxl_einj_debugfs_add_types(void)
> +{
> + if (einj_types_dentry)
> + return;
> +
> + if (!einj_cxl_is_initialized())
> + return;
> +
> + einj_types_dentry = debugfs_create_file("einj_types", 0400, cxl_debugfs,
> + NULL,
> + &einj_cxl_available_error_type_fops);
> +}
[Severity: Medium]
Can concurrent dport enumeration race on einj_types_dentry in
cxl_einj_debugfs_add_types()?
Previously, debugfs_create_file("einj_types", ...) was only called once from
cxl_core_init() in single-threaded initcall context. With this change, it is
called via:
cxl_pci_probe()
-> devm_cxl_add_memdev()
-> cxl_mem_probe()
-> devm_cxl_enumerate_ports()
-> add_port_attach_ep()
-> probe_dport()
-> cxl_port_add_dport()
-> devm_cxl_add_dport_by_dev()
-> __devm_cxl_add_dport()
-> cxl_debugfs_create_dport_dir()
-> cxl_einj_debugfs_add_types()
Because cxl_pci_driver enables asynchronous probing in drivers/cxl/pci.c:
.driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
and add_port_attach_ep() in drivers/cxl/core/port.c only serializes on the
per-port device lock:
scoped_guard(device, &parent_port->dev) {
parent_dport = cxl_find_dport_by_dev(parent_port, dparent);
if (!parent_dport) {
parent_dport = probe_dport(parent_port, dparent);
two worker threads probing endpoints under different parent ports can enter
cxl_einj_debugfs_add_types() simultaneously, both observe einj_types_dentry
as null, and both call debugfs_create_file("einj_types", ...).
In fs/debugfs/inode.c:debugfs_start_creating(), the losing caller will fail
with:
dentry = simple_start_creating(parent, name);
if (IS_ERR(dentry)) {
if (dentry == ERR_PTR(-EEXIST))
pr_err("'%s' already exists in '%pd'\n", name, parent);
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
}
logging a spurious error and racing to overwrite einj_types_dentry with the
error pointer. Should einj_types_dentry creation be synchronized with a lock
or once-only helper?
> +
> static int cxl_einj_inject(void *data, u64 type)
> {
> struct cxl_dport *dport = data;
> @@ -833,6 +854,8 @@ static void cxl_debugfs_create_dport_dir(struct cxl_dport *dport)
> struct cxl_port *parent = parent_port_of(dport->port);
> struct dentry *dir;
>
> + cxl_einj_debugfs_add_types();
> +
> if (!einj_cxl_is_initialized())
> return;
>
> @@ -2589,8 +2612,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);
> @@ -2603,10 +2624,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);
> -
[Severity: Medium]
Does removing this check from cxl_core_init() introduce a regression for
modular builds, while still missing built-in restricted host topologies?
First, when cxl_core is built as a module, subsys_initcall(cxl_core_init)
compiles to module_init(cxl_core_init). Because cxl_core directly references
einj symbols (einj_cxl_is_initialized and
einj_cxl_available_error_type_show), the einj module is already initialized
before cxl_core_init() runs. Removing the creation from cxl_core_init()
rather than calling cxl_einj_debugfs_add_types() here means einj_types is no
longer created when cxl_core.ko is loaded before or without dport
enumeration.
Second, when both cxl and einj are built-in, cxl_acpi_init() runs at
subsys_initcall_sync in drivers/cxl/acpi.c:
subsys_initcall_sync(cxl_acpi_init);
which still executes before einj_init() at device_initcall in
drivers/acpi/apei/einj-core.c:
module_init(einj_init);
During cxl_acpi_probe(), add_host_bridge_dport() in drivers/cxl/acpi.c adds
the host bridge and restricted host dports:
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
dev_dbg(match, "RCRB found for UID %lld: %pa\n", ctx.uid,
&ctx.base);
dport = devm_cxl_add_rch_dport(root_port, bridge, ctx.uid,
ctx.base);
} else {
dport = devm_cxl_add_dport(root_port, bridge, ctx.uid,
CXL_RESOURCE_NONE);
}
At that point einj_cxl_is_initialized() is still false in
cxl_debugfs_create_dport_dir(). Later, when restricted host endpoints probe
at device_initcall, devm_cxl_enumerate_ports() in drivers/cxl/core/port.c
returns early without adding any further dports:
if (cxlmd->cxlds->rcd)
return 0;
As a result, on built-in restricted host platforms,
cxl_debugfs_create_dport_dir() is never called after einj_init(), so neither
einj_types nor the dport's einj_inject file is created despite an injectable
dport being present.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028055401.1626640-1-yangyongxin@kylinos.cn?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2] cxl/core: Create einj_types when EINJ becomes available
2026-10-28 5:54 [PATCH] cxl/core: Create einj_types when EINJ becomes available Yongxin Yang
2026-10-28 6:08 ` sashiko-bot
@ 2026-10-28 8:49 ` Yongxin Yang
2026-10-28 20:46 ` Cheatham, Benjamin
2026-10-29 6:56 ` [PATCH v3] " Yongxin Yang
1 sibling, 2 replies; 12+ messages in thread
From: Yongxin Yang @ 2026-10-28 8:49 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma
Cc: Dan Williams, Ira Weiny, Li Ming, linux-cxl, linux-kernel, stable,
Yang Yongxin
From: Yang Yongxin <yangyongxin@kylinos.cn>
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 module_init 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 both places that can know EINJ is up: cxl_core_init(),
which covers the case where cxl_core is a module and einj has already been
initialized by the time the module is loaded, and the dport probe path,
which covers the built-in case. 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.
Note that this is still a partial fix: an RCH platform returns early from
devm_cxl_enumerate_ports() before any dport is added, so einj_types does
not appear there. Fixing that requires moving cxl_core_init() after EINJ
is probed, or creating the file from cxl_acpi_probe(), both of which are
out of scope here.
Fixes: 8039804cfa73 ("cxl/core: Add CXL EINJ debugfs files")
Cc: <stable@vger.kernel.org>
Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
---
Changes in v2:
- Create einj_types from cxl_core_init() as well, so that the module case,
where einj is already initialized when cxl_core is loaded, keeps working
and is not regressed by the lazy path alone.
- Serialize the check and the create with a mutex, as the dport probe path
can run concurrently for different parent ports.
- Spell out that RCH platforms are still not covered, and why.
- Rebase onto mainline, so the diff no longer carries the CXL fixes that
are queued in linux-next.
drivers/cxl/core/port.c | 34 ++++++++++++++++++++++++++++------
1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 625e4aa427db..c1736a00f22f 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -802,6 +802,30 @@ 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 is created from two places: cxl_core_init() catches the case
+ * where cxl_core is a module and einj is already up, while the dport probe
+ * path catches the built-in case, where cxl_core_init() runs at
+ * subsys_initcall level before einj_init() has set einj_initialized.
+ *
+ * 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);
+static void cxl_einj_debugfs_add_types(void)
+{
+ mutex_lock(&einj_types_lock);
+ if (!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);
+ mutex_unlock(&einj_types_lock);
+}
+
static int cxl_einj_inject(void *data, u64 type)
{
struct cxl_dport *dport = data;
@@ -819,6 +843,8 @@ static void cxl_debugfs_create_dport_dir(struct cxl_dport *dport)
struct cxl_port *parent = parent_port_of(dport->port);
struct dentry *dir;
+ cxl_einj_debugfs_add_types();
+
if (!einj_cxl_is_initialized())
return;
@@ -2501,8 +2527,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 +2539,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 +2561,8 @@ static __init int cxl_core_init(void)
cxl_ras_init();
+ cxl_einj_debugfs_add_types();
+
return 0;
err_region:
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2] cxl/core: Create einj_types when EINJ becomes available
2026-10-28 8:49 ` [PATCH v2] " Yongxin Yang
@ 2026-10-28 20:46 ` Cheatham, Benjamin
2026-10-29 6:56 ` [PATCH v3] " Yongxin Yang
1 sibling, 0 replies; 12+ messages in thread
From: Cheatham, Benjamin @ 2026-10-28 20:46 UTC (permalink / raw)
To: Yongxin Yang, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
Alison Schofield, Vishal Verma
Cc: Dan Williams, Ira Weiny, Li Ming, linux-cxl, linux-kernel, stable
On 10/28/2026 3:49 AM, Yongxin Yang wrote:
> From: Yang Yongxin <yangyongxin@kylinos.cn>
>
> 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 module_init 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 both places that can know EINJ is up: cxl_core_init(),
> which covers the case where cxl_core is a module and einj has already been
> initialized by the time the module is loaded, and the dport probe path,
> which covers the built-in case. 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.
>
> Note that this is still a partial fix: an RCH platform returns early from
> devm_cxl_enumerate_ports() before any dport is added, so einj_types does
> not appear there. Fixing that requires moving cxl_core_init() after EINJ
> is probed, or creating the file from cxl_acpi_probe(), both of which are
> out of scope here.
I'm not a fan of having to write a value to einj_inject to see what values are
possible to write to einj_inject. Maybe if it was changed to reading einj_inject
creating einj_types it would be better, but I think I'd rather see the file created
in cxl_acpi_probe() as a fix.
I was able to make a quick mock up using your fix here and it wasn't that bad:
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 0c21dc03a6..4017500ef4 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -974,6 +974,8 @@
if (rc < 0)
return rc;
+ 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 f3705ea44d..0b75792a9a 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -811,6 +811,20 @@
DEFINE_SHOW_ATTRIBUTE(einj_cxl_available_error_type);
+static struct dentry *cxl_debugfs;
+static struct dentry *einj_types_dentry;
+
+static DEFINE_MUTEX(einj_types_lock);
+void cxl_debugfs_einj_add_types(void)
+{
+ guard(mutex)(&einj_types_lock);
+ if (!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;
@@ -2589,8 +2603,6 @@
};
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);
@@ -2603,10 +2615,6 @@
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_debugfs_einj_add_types();
cxl_mbox_init();
rc = cxl_memdev_init();
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index baa6df2d36..40be8ea938 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -956,4 +956,5 @@
u16 cxl_gpf_get_dvsec(struct device *dev);
+void cxl_debugfs_einj_add_types(void);
#endif /* __CXL_H__ */
---
I only did some quick compile testing with a few common configs and didn't see any errors,
so take it with a grain of salt. The only issue I see with it at the moment is the debugfs
init is now partially decoupled from the cxl_core module, but I think that's just a
maintainability issue instead of a technical one.
You may also be able to get away with removing the locking in cxl_debugfs_einj_add_types()
since cxl_acpi doesn't force async probe, but I left it there just in case.
Thanks,
Ben
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3] cxl/core: Create einj_types when EINJ becomes available
2026-10-28 8:49 ` [PATCH v2] " Yongxin Yang
2026-10-28 20:46 ` Cheatham, Benjamin
@ 2026-10-29 6:56 ` Yongxin Yang
2026-10-29 17:06 ` Cheatham, Benjamin
2026-10-30 3:20 ` [PATCH v4] " Yongxin Yang
1 sibling, 2 replies; 12+ messages in thread
From: Yongxin Yang @ 2026-10-29 6:56 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl, linux-kernel, Ben Cheatham, Yang Yongxin, stable
From: Yang Yongxin <yangyongxin@kylinos.cn>
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@vger.kernel.org>
Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@amd.com/
Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
---
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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3] cxl/core: Create einj_types when EINJ becomes available
2026-10-29 6:56 ` [PATCH v3] " Yongxin Yang
@ 2026-10-29 17:06 ` Cheatham, Benjamin
2026-10-30 3:20 ` [PATCH v4] " Yongxin Yang
1 sibling, 0 replies; 12+ messages in thread
From: Cheatham, Benjamin @ 2026-10-29 17:06 UTC (permalink / raw)
To: Yongxin Yang, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl, linux-kernel, stable
Hi Yang, thanks for walking through your reasoning in the last revision. It made looking through
the changes here much easier!
Nit-picky comments in line :)
On 10/29/2026 1:56 AM, Yongxin Yang wrote:
> [You don't often get email from yangyongxin@kylinos.cn. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> From: Yang Yongxin <yangyongxin@kylinos.cn>
>
> The einj_types file under the CXL debugfs directory is never created.
>
I think this should be "The einj_types file under the CXL debugfs directory
is never created when cxl_core is built-in", but I haven't looked in a while.
I remember you saying something about subsys_initcall() being downgraded to a
module_init() when cxl_core is built-in, and it looks like that's the case
after a quick look at the macro definition. So module dependencies should
cause the file creation to happen post EINJ init when both cxl_core and einj_cxl
are modules, in which case the file will be there.
That's probably why I never noticed this either, I (like most people) almost always
set everything CXL related to 'm' when compiling a kernel...
> 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
I would rather this not mention the case where no dport is ever added and
instead talk about the RCH/RCD case. If there's no dport ever added then
error injection isn't possible and this file is useless, so there's not
really a point in bringing it up.
> 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@vger.kernel.org>
> Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@amd.com/
> Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
> ---
The contents of the patch look good to me, so with the changes to the description
and comment below:
Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
> 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
Same thing as in the commit description, just mention the RCH/RCD case here instead.
Thanks,
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v4] cxl/core: Create einj_types when EINJ becomes available
2026-10-29 6:56 ` [PATCH v3] " Yongxin Yang
2026-10-29 17:06 ` Cheatham, Benjamin
@ 2026-10-30 3:20 ` Yongxin Yang
2026-10-30 3:34 ` sashiko-bot
` (2 more replies)
1 sibling, 3 replies; 12+ messages in thread
From: Yongxin Yang @ 2026-10-30 3:20 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl, linux-kernel, Ben Cheatham, Yang Yongxin, stable
From: Yang Yongxin <yangyongxin@kylinos.cn>
The einj_types file under the CXL debugfs directory is never created when
cxl_core is built in.
cxl_core_init() runs at subsys_initcall level, while EINJ registers itself
from einj_init() at device_initcall level. With cxl_core built in, the
einj_cxl_is_initialized() check in cxl_core_init() therefore always sees
false and the debugfs_create_file("einj_types") call below it is never
reached, so users have no way to discover which CXL error types the
platform supports from debugfs. When cxl_core is a module, module
dependencies make sure it is loaded after EINJ is up, and the file is
created.
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. The
cxl_acpi_probe() hook covers cxl_core being built in while cxl_acpi is a
module, and is the only hook that can work on RCH/RCD hierarchies, where
the single dport is added by the ACPI probe itself, too early to help. The
dport probe path covers the built-in virtual host 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@vger.kernel.org>
Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@amd.com/
Link: https://lore.kernel.org/all/7a22cc6a-deba-40e6-8033-97d1234420ba@amd.com/
Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
---
Changes in v4:
- Scope the problem statement to the built-in case: the file is only never
created when cxl_core is built in, the module dependencies take care of the
ordering otherwise.
- Replace the "no dport is ever added" rationale with the RCH/RCD case, in
the changelog and in the cxl_acpi_probe() comment: a system without any
dport cannot do error injection at all, so the file is useless there. On
RCH/RCD hierarchies the only dport comes from the ACPI probe, so this hook
is the one that matters.
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..ca7741203740 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: with cxl_core built in,
+ * cxl_core_init() runs before EINJ is up, so when cxl_acpi is a module
+ * loaded afterwards this is the only hook that can create the file,
+ * including on RCH/RCD hierarchies, where the single dport is added by
+ * this probe itself and cannot help.
+ */
+ 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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v4] cxl/core: Create einj_types when EINJ becomes available
2026-10-30 3:20 ` [PATCH v4] " Yongxin Yang
@ 2026-10-30 3:34 ` sashiko-bot
2026-10-30 18:33 ` Dave Jiang
2026-10-30 18:39 ` Dave Jiang
2 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-10-30 3:34 UTC (permalink / raw)
To: Yongxin Yang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] cxl/core: einj_types still missing on built-in RCH/RCD and redundant ACPI hook
--
commit 85011aad4fb602610b914a77c4e46997107304a4
Author: Yang Yongxin <yangyongxin@kylinos.cn>
cxl/core: Create einj_types when EINJ becomes available
This commit defers the creation of the CXL debugfs einj_types file to a
new helper, cxl_debugfs_einj_add_types(), called from cxl_core_init(),
cxl_acpi_probe(), and cxl_debugfs_create_dport_dir() so the file can be
created once EINJ is initialized.
Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@amd.com/
Link: https://lore.kernel.org/all/7a22cc6a-deba-40e6-8033-97d1234420ba@amd.com/
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index b7ce6b59c680b..62dcb2b308430 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -968,6 +968,15 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> if (rc < 0)
> return rc;
>
> + /*
> + * Create einj_types from here as well: with cxl_core built in,
> + * cxl_core_init() runs before EINJ is up, so when cxl_acpi is a module
> + * loaded afterwards this is the only hook that can create the file,
> + * including on RCH/RCD hierarchies, where the single dport is added by
> + * this probe itself and cannot help.
> + */
> + cxl_debugfs_einj_add_types();
[Severity: Medium]
Does this hook in cxl_acpi_probe() ever actually create the einj_types
file, and does it still leave RCH/RCD hierarchies without einj_types (and
without the dport einj_inject file) when cxl_acpi is built in?
Earlier in cxl_acpi_probe(), host bridge dports are registered via
bus_for_each_dev(..., add_host_bridge_dport):
drivers/cxl/acpi.c:add_host_bridge_dport() {
...
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
dev_dbg(match, "RCRB found for UID %lld: %pa\n", ctx.uid,
&ctx.base);
dport = devm_cxl_add_rch_dport(root_port, bridge, ctx.uid,
ctx.base);
} else {
dport = devm_cxl_add_dport(root_port, bridge, ctx.uid,
CXL_RESOURCE_NONE);
}
...
}
Both devm_cxl_add_rch_dport() and devm_cxl_add_dport() call
__devm_cxl_add_dport() -> cxl_debugfs_create_dport_dir() ->
cxl_debugfs_einj_add_types().
When cxl_acpi is loaded as a module after einj_init() has completed,
add_host_bridge_dport() has already called cxl_debugfs_einj_add_types()
before execution reaches the direct cxl_debugfs_einj_add_types() call at
the end of cxl_acpi_probe().
Conversely, when both cxl_core and cxl_acpi are built in, cxl_acpi_init()
runs at subsys_initcall_sync level, before einj_init() runs at
device_initcall level. At that point, einj_cxl_is_initialized() returns
false during both add_host_bridge_dport() and the direct call in
cxl_acpi_probe().
Later, when an RCD endpoint probes after einj_init(),
devm_cxl_enumerate_ports() returns early without adding any dports:
drivers/cxl/core/port.c:devm_cxl_enumerate_ports() {
...
/*
* Skip intermediate port enumeration in the RCH case, there
* are no ports in between a host bridge and an endpoint.
*/
if (cxlmd->cxlds->rcd)
return 0;
...
}
And cxl_endpoint_port_probe() sets up RCH RAS without setting up EINJ
debugfs:
drivers/cxl/port.c:cxl_endpoint_port_probe() {
...
if (dport->rch)
devm_cxl_dport_rch_ras_setup(dport);
...
}
As a result, on built-in RCH/RCD configurations, wouldn't
cxl_debugfs_create_dport_dir() and cxl_debugfs_einj_add_types() never be
called after einj_init(), leaving both einj_types and the RCH dport's
einj_inject debugfs files missing?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030032050.787682-1-yangyongxin@kylinos.cn?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4] cxl/core: Create einj_types when EINJ becomes available
2026-10-30 3:20 ` [PATCH v4] " Yongxin Yang
2026-10-30 3:34 ` sashiko-bot
@ 2026-10-30 18:33 ` Dave Jiang
2026-10-30 19:24 ` Cheatham, Benjamin
2026-10-30 18:39 ` Dave Jiang
2 siblings, 1 reply; 12+ messages in thread
From: Dave Jiang @ 2026-10-30 18:33 UTC (permalink / raw)
To: Yongxin Yang, Davidlohr Bueso, Jonathan Cameron, Alison Schofield,
Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl, linux-kernel, Ben Cheatham, stable
On 10/29/26 8:20 PM, Yongxin Yang wrote:
> From: Yang Yongxin <yangyongxin@kylinos.cn>
>
> The einj_types file under the CXL debugfs directory is never created when
> cxl_core is built in.
>
> cxl_core_init() runs at subsys_initcall level, while EINJ registers itself
> from einj_init() at device_initcall level. With cxl_core built in, the
> einj_cxl_is_initialized() check in cxl_core_init() therefore always sees
> false and the debugfs_create_file("einj_types") call below it is never
> reached, so users have no way to discover which CXL error types the
> platform supports from debugfs. When cxl_core is a module, module
> dependencies make sure it is loaded after EINJ is up, and the file is
> created.
>
> 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. The
> cxl_acpi_probe() hook covers cxl_core being built in while cxl_acpi is a
> module, and is the only hook that can work on RCH/RCD hierarchies, where
> the single dport is added by the ACPI probe itself, too early to help. The
> dport probe path covers the built-in virtual host 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.
That is a REALLY wordy commit log. Maybe AI generated? Can you please condense and simplify it?
>
> Fixes: 8039804cfa73 ("cxl/core: Add CXL EINJ debugfs files")
> Cc: <stable@vger.kernel.org>
> Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@amd.com/
> Link: https://lore.kernel.org/all/7a22cc6a-deba-40e6-8033-97d1234420ba@amd.com/
> Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
> Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
> ---
> Changes in v4:
> - Scope the problem statement to the built-in case: the file is only never
> created when cxl_core is built in, the module dependencies take care of the
> ordering otherwise.
> - Replace the "no dport is ever added" rationale with the RCH/RCD case, in
> the changelog and in the cxl_acpi_probe() comment: a system without any
> dport cannot do error injection at all, so the file is useless there. On
> RCH/RCD hierarchies the only dport comes from the ACPI probe, so this hook
> is the one that matters.
>
> 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..ca7741203740 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: with cxl_core built in,
> + * cxl_core_init() runs before EINJ is up, so when cxl_acpi is a module
> + * loaded afterwards this is the only hook that can create the file,
> + * including on RCH/RCD hierarchies, where the single dport is added by
> + * this probe itself and cannot help.
> + */
> + 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__ */
Maybe drop the cxl_acpi_probe() hook? Host bridge dports, RCH included, already go through __devm_cxl_add_dport() -> cxl_debugfs_create_dport_dir() -> cxl_debugfs_einj_add_types() inside the same probe, so the hook runs at the same time and adds nothing. That also removes the export.
Also, the RCH all built-in case still never gets einj_types since the only RCH dport is added at subsys_initcall_sync. Should the commit log say so?
I do wonder if it's simpler to always create einj_types in cxl_core_init() and have einj_cxl_available_error_type_show() return -ENODEV when EINJ isn't initialized. That covers every config without the lock and the extra hooks. Any thoughts Ben?
The NULL resets in cxl_core_exit() aren't needed. Module reload gets fresh .bss.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4] cxl/core: Create einj_types when EINJ becomes available
2026-10-30 3:20 ` [PATCH v4] " Yongxin Yang
2026-10-30 3:34 ` sashiko-bot
2026-10-30 18:33 ` Dave Jiang
@ 2026-10-30 18:39 ` Dave Jiang
2 siblings, 0 replies; 12+ messages in thread
From: Dave Jiang @ 2026-10-30 18:39 UTC (permalink / raw)
To: Yongxin Yang, Davidlohr Bueso, Alison Schofield, Vishal Verma,
Jonathan Cameron, Ira Weiny
Cc: linux-cxl, linux-kernel, Ben Cheatham, stable
On 10/29/26 8:20 PM, Yongxin Yang wrote:
> From: Yang Yongxin <yangyongxin@kylinos.cn>
>
> The einj_types file under the CXL debugfs directory is never created when
> cxl_core is built in.
>
> cxl_core_init() runs at subsys_initcall level, while EINJ registers itself
> from einj_init() at device_initcall level. With cxl_core built in, the
> einj_cxl_is_initialized() check in cxl_core_init() therefore always sees
> false and the debugfs_create_file("einj_types") call below it is never
> reached, so users have no way to discover which CXL error types the
> platform supports from debugfs. When cxl_core is a module, module
> dependencies make sure it is loaded after EINJ is up, and the file is
> created.
>
> 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. The
> cxl_acpi_probe() hook covers cxl_core being built in while cxl_acpi is a
> module, and is the only hook that can work on RCH/RCD hierarchies, where
> the single dport is added by the ACPI probe itself, too early to help. The
> dport probe path covers the built-in virtual host 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@vger.kernel.org>
> Link: https://lore.kernel.org/all/de14c4ef-e3d8-432f-9ce8-87c468d6d2ad@amd.com/
> Link: https://lore.kernel.org/all/7a22cc6a-deba-40e6-8033-97d1234420ba@amd.com/
> Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
> Signed-off-by: Yang Yongxin <yangyongxin@kylinos.cn>
Couple more things. Can you send a new email per revision instead of reply-to the previous post? Otherwise it's a mess of email threads.
Also, please update email addresses in your submission. Many of them have changed and are now bouncing. Check the latest MAINTAINERS please.
DJ
> ---
> Changes in v4:
> - Scope the problem statement to the built-in case: the file is only never
> created when cxl_core is built in, the module dependencies take care of the
> ordering otherwise.
> - Replace the "no dport is ever added" rationale with the RCH/RCD case, in
> the changelog and in the cxl_acpi_probe() comment: a system without any
> dport cannot do error injection at all, so the file is useless there. On
> RCH/RCD hierarchies the only dport comes from the ACPI probe, so this hook
> is the one that matters.
>
> 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..ca7741203740 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: with cxl_core built in,
> + * cxl_core_init() runs before EINJ is up, so when cxl_acpi is a module
> + * loaded afterwards this is the only hook that can create the file,
> + * including on RCH/RCD hierarchies, where the single dport is added by
> + * this probe itself and cannot help.
> + */
> + 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__ */
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4] cxl/core: Create einj_types when EINJ becomes available
2026-10-30 18:33 ` Dave Jiang
@ 2026-10-30 19:24 ` Cheatham, Benjamin
2026-10-30 20:36 ` Cheatham, Benjamin
0 siblings, 1 reply; 12+ messages in thread
From: Cheatham, Benjamin @ 2026-10-30 19:24 UTC (permalink / raw)
To: Dave Jiang, Yongxin Yang, Davidlohr Bueso, Jonathan Cameron,
Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl, linux-kernel, stable
>> 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__ */
>
> Maybe drop the cxl_acpi_probe() hook? Host bridge dports, RCH included, already go through __devm_cxl_add_dport() -> cxl_debugfs_create_dport_dir() -> cxl_debugfs_einj_add_types() inside the same probe, so the hook runs at the same time and adds nothing. That also removes the export.
>
> Also, the RCH all built-in case still never gets einj_types since the only RCH dport is added at subsys_initcall_sync. Should the commit log say so?
>
> I do wonder if it's simpler to always create einj_types in cxl_core_init() and have einj_cxl_available_error_type_show() return -ENODEV when EINJ isn't initialized. That covers every config without the lock and the extra hooks. Any thoughts Ben?
Yeah, that sounds good to me. Wish I had thought of it :)
Thanks,
Ben
>
> The NULL resets in cxl_core_exit() aren't needed. Module reload gets fresh .bss.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4] cxl/core: Create einj_types when EINJ becomes available
2026-10-30 19:24 ` Cheatham, Benjamin
@ 2026-10-30 20:36 ` Cheatham, Benjamin
0 siblings, 0 replies; 12+ messages in thread
From: Cheatham, Benjamin @ 2026-10-30 20:36 UTC (permalink / raw)
To: Dave Jiang, Yongxin Yang, Davidlohr Bueso, Jonathan Cameron,
Alison Schofield, Vishal Verma, Ira Weiny, Dan Williams
Cc: linux-cxl, linux-kernel, stable
On 10/30/2026 2:24 PM, Cheatham, Benjamin wrote:
>
>>> 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__ */
>>
>> Maybe drop the cxl_acpi_probe() hook? Host bridge dports, RCH included, already go through __devm_cxl_add_dport() -> cxl_debugfs_create_dport_dir() -> cxl_debugfs_einj_add_types() inside the same probe, so the hook runs at the same time and adds nothing. That also removes the export.
>>
>> Also, the RCH all built-in case still never gets einj_types since the only RCH dport is added at subsys_initcall_sync. Should the commit log say so?
>>
>> I do wonder if it's simpler to always create einj_types in cxl_core_init() and have einj_cxl_available_error_type_show() return -ENODEV when EINJ isn't initialized. That covers every config without the lock and the extra hooks. Any thoughts Ben?
>
> Yeah, that sounds good to me. Wish I had thought of it :)
I thought about it a bit more and there should also be a check for CONFIG_ACPI_APEI_EINJ_CXL as well. It doesn't make sense to
add the file if the support won't ever be turned on. So change the check for creating the file from "if (einj_cxl_is_initialized())"
to "if (IS_ENABLED(CONFIG_ACPI_APEI_EINJ_CXL))".
Thanks,
Ben
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-30 20:36 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 5:54 [PATCH] cxl/core: Create einj_types when EINJ becomes available Yongxin Yang
2026-10-28 6:08 ` sashiko-bot
2026-10-28 8:49 ` [PATCH v2] " Yongxin Yang
2026-10-28 20:46 ` Cheatham, Benjamin
2026-10-29 6:56 ` [PATCH v3] " Yongxin Yang
2026-10-29 17:06 ` Cheatham, Benjamin
2026-10-30 3:20 ` [PATCH v4] " Yongxin Yang
2026-10-30 3:34 ` sashiko-bot
2026-10-30 18:33 ` Dave Jiang
2026-10-30 19:24 ` Cheatham, Benjamin
2026-10-30 20:36 ` Cheatham, Benjamin
2026-10-30 18:39 ` Dave Jiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox