* [PATCH v4 1/6] cxl/test: Add test for module parameters
2026-06-11 15:21 [PATCH v4 0/6] cxl: Add CXL type2 accelerator support for cxl_test Dave Jiang
@ 2026-06-11 15:21 ` Dave Jiang
2026-06-11 15:34 ` sashiko-bot
2026-06-11 15:21 ` [PATCH v4 2/6] cxl/test: Add type2 support for mock CFMWS0 Dave Jiang
` (4 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-cxl; +Cc: djbw, dave, jic23, alison.schofield, vishal.l.verma
Add a test for module paraters during module init to make sure that
only one is activated.
Suggested-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
v4:
- Make have_multiple_modparms static (sashiko)
- Check hmem_test mod param (sashiko)
---
tools/testing/cxl/test/cxl.c | 20 ++++++++++++++++++++
tools/testing/cxl/test/hmem_test.c | 3 ++-
tools/testing/cxl/test/mock.h | 2 ++
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 296516eecfd6..1c8ac8bdd8f7 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -1814,11 +1814,31 @@ static struct attribute *cxl_acpi_attrs[] = {
};
ATTRIBUTE_GROUPS(cxl_acpi);
+static bool __init have_multiple_modparms(void)
+{
+ int count = 0;
+
+ if (interleave_arithmetic)
+ count++;
+ if (extended_linear_cache)
+ count++;
+ if (fail_autoassemble)
+ count++;
+ if (hmem_test)
+ count++;
+
+ return count > 1;
+}
+
static __init int cxl_test_init(void)
{
int rc, i;
struct range mappable;
+ /* Enforce a single module param active at a time */
+ if (have_multiple_modparms())
+ return -EINVAL;
+
cxl_acpi_test();
cxl_core_test();
cxl_mem_test();
diff --git a/tools/testing/cxl/test/hmem_test.c b/tools/testing/cxl/test/hmem_test.c
index 3a1a089e1721..0fa00f7e16db 100644
--- a/tools/testing/cxl/test/hmem_test.c
+++ b/tools/testing/cxl/test/hmem_test.c
@@ -3,8 +3,9 @@
#include <linux/moduleparam.h>
#include <linux/workqueue.h>
#include "../../../drivers/dax/bus.h"
+#include "mock.h"
-static bool hmem_test;
+bool hmem_test;
static void hmem_test_work(struct work_struct *work)
{
diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h
index 4f57dc80ae7d..846d7c5d6eaa 100644
--- a/tools/testing/cxl/test/mock.h
+++ b/tools/testing/cxl/test/mock.h
@@ -5,6 +5,8 @@
#include <linux/dax.h>
#include <cxl.h>
+extern bool hmem_test;
+
struct cxl_mock_ops {
struct list_head list;
bool (*is_mock_adev)(struct acpi_device *dev);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v4 1/6] cxl/test: Add test for module parameters
2026-06-11 15:21 ` [PATCH v4 1/6] cxl/test: Add test for module parameters Dave Jiang
@ 2026-06-11 15:34 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-11 15:34 UTC (permalink / raw)
To: Dave Jiang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The module unregisters `hmem_test_device` without flushing or canceling its pending work, leading to a use-after-free and execute-after-free when the `cxl_test` module unloads.
--
commit 25e58b40eb66000f930689ce58f1f38f11a8a5bd
Author: Dave Jiang <dave.jiang@intel.com>
cxl/test: Add test for module parameters
Add a test for module parameters during module init to make sure that
only one is activated.
[ ... ]
> diff --git a/tools/testing/cxl/test/hmem_test.c b/tools/testing/cxl/test/hmem_test.c
> index 3a1a089e1721b..0fa00f7e16db5 100644
> --- a/tools/testing/cxl/test/hmem_test.c
> +++ b/tools/testing/cxl/test/hmem_test.c
> @@ -3,8 +3,9 @@
> #include <linux/moduleparam.h>
> #include <linux/workqueue.h>
> #include "../../../drivers/dax/bus.h"
> +#include "mock.h"
>
> -static bool hmem_test;
> +bool hmem_test;
>
> static void hmem_test_work(struct work_struct *work)
> {
[Severity: High]
This is a pre-existing issue, but does the hmem_test module leak
outstanding workqueue references upon unload?
The module unregisters hmem_test_device in hmem_test_exit() without flushing
or canceling its pending work:
tools/testing/cxl/test/hmem_test.c:hmem_test_exit() {
if (hmem_test)
platform_device_unregister(&hmem_test_device.pdev);
}
Because hmem_test_device is statically allocated in the module:
tools/testing/cxl/test/hmem_test.c:
static struct hmem_platform_device hmem_test_device = { ... };
and its release function is in the module's text section:
tools/testing/cxl/test/hmem_test.c:hmem_test_release() {
struct hmem_platform_device *hpdev =
container_of(dev, typeof(*hpdev), pdev.dev);
memset(hpdev, 0, sizeof(*hpdev));
}
could the module unload, allowing its memory to be freed before the
workqueue executes?
If the pending work executes after the module is unloaded, it would access the
freed hmem_test_device memory and trigger hmem_test_release() from the unloaded
text section, causing a kernel crash. Should there be a call to cancel the
work before unregistering the device?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611152124.3656434-1-dave.jiang@intel.com?part=1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4 2/6] cxl/test: Add type2 support for mock CFMWS0
2026-06-11 15:21 [PATCH v4 0/6] cxl: Add CXL type2 accelerator support for cxl_test Dave Jiang
2026-06-11 15:21 ` [PATCH v4 1/6] cxl/test: Add test for module parameters Dave Jiang
@ 2026-06-11 15:21 ` Dave Jiang
2026-06-11 15:38 ` sashiko-bot
2026-06-11 15:21 ` [PATCH v4 3/6] cxl/test: Refactor platform device enumerations Dave Jiang
` (3 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-cxl; +Cc: djbw, dave, jic23, alison.schofield, vishal.l.verma
Add a module parameter 'type2_test' for triggering type2 test support
in cxl_test. Setup the CFMWS0 configuration to be type2 when 'type2_test'
is set.
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
tools/testing/cxl/test/cxl.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 1c8ac8bdd8f7..c5529cee482d 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -17,6 +17,7 @@
static int interleave_arithmetic;
static bool extended_linear_cache;
static bool fail_autoassemble;
+static bool type2_test;
#define FAKE_QTG_ID 42
@@ -384,6 +385,19 @@ static struct {
},
};
+static struct acpi_cedt_cfmws type2_cfmws0 = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CFMWS,
+ .length = sizeof(mock_cedt.cfmws0),
+ },
+ .interleave_ways = 0,
+ .granularity = 4,
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_DEVMEM |
+ ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
+ .qtg_id = FAKE_QTG_ID,
+ .window_size = SZ_256M * 4,
+};
+
struct acpi_cedt_cfmws *mock_cfmws[] = {
[0] = &mock_cedt.cfmws0.cfmws,
[1] = &mock_cedt.cfmws1.cfmws,
@@ -472,6 +486,11 @@ static void cfmws_elc_update(struct acpi_cedt_cfmws *window, int index)
window->window_size = mock_auto_region_size * 2;
}
+static void update_type2_cfmws(void)
+{
+ memcpy(&mock_cedt.cfmws0.cfmws, &type2_cfmws0, sizeof(type2_cfmws0));
+}
+
static int populate_cedt(void)
{
struct cxl_mock_res *res;
@@ -493,10 +512,14 @@ static int populate_cedt(void)
chbs->length = size;
}
+ if (type2_test)
+ update_type2_cfmws();
+
for (i = cfmws_start; i <= cfmws_end; i++) {
struct acpi_cedt_cfmws *window = mock_cfmws[i];
- cfmws_elc_update(window, i);
+ if (i == 0 && !type2_test)
+ cfmws_elc_update(window, i);
res = alloc_mock_res(window->window_size, SZ_256M);
if (!res)
return -ENOMEM;
@@ -1826,6 +1849,8 @@ static bool __init have_multiple_modparms(void)
count++;
if (hmem_test)
count++;
+ if (type2_test)
+ count++;
return count > 1;
}
@@ -2050,6 +2075,8 @@ module_param(extended_linear_cache, bool, 0444);
MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support");
module_param(fail_autoassemble, bool, 0444);
MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region");
+module_param(type2_test, bool, 0444);
+MODULE_PARM_DESC(type2_test, "Enable type 2 support testing");
module_init(cxl_test_init);
module_exit(cxl_test_exit);
MODULE_LICENSE("GPL v2");
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v4 2/6] cxl/test: Add type2 support for mock CFMWS0
2026-06-11 15:21 ` [PATCH v4 2/6] cxl/test: Add type2 support for mock CFMWS0 Dave Jiang
@ 2026-06-11 15:38 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-11 15:38 UTC (permalink / raw)
To: Dave Jiang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Improper teardown of `cxl_acpi` platform device in `cxl_test_init()` error path leads to double-put or stranded device.
- [High] Missing NULL check after `kzalloc` in `alloc_mock_res` causes a NULL pointer dereference on allocation failure.
--
commit 4122a7a4346775c8cc5370b036f6cfe7a92557ff
Author: Dave Jiang <dave.jiang@intel.com>
cxl/test: Add type2 support for mock CFMWS0
Add a module parameter 'type2_test' for triggering type2 test support
in cxl_test. Setup the CFMWS0 configuration to be type2 when 'type2_test'
is set.
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> index 1c8ac8bdd8f7a..c5529cee482d4 100644
> --- a/tools/testing/cxl/test/cxl.c
> +++ b/tools/testing/cxl/test/cxl.c
[ ... ]
> @@ -493,10 +512,14 @@ static int populate_cedt(void)
> chbs->length = size;
> }
>
> + if (type2_test)
> + update_type2_cfmws();
> +
> for (i = cfmws_start; i <= cfmws_end; i++) {
> struct acpi_cedt_cfmws *window = mock_cfmws[i];
>
> - cfmws_elc_update(window, i);
> + if (i == 0 && !type2_test)
> + cfmws_elc_update(window, i);
> res = alloc_mock_res(window->window_size, SZ_256M);
[Severity: High]
This is a pre-existing issue, but does alloc_mock_res() have a potential null
pointer dereference on allocation failure?
tools/testing/cxl/test/cxl.c:alloc_mock_res() {
struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
INIT_LIST_HEAD(&res->list);
...
}
If kzalloc() fails, INIT_LIST_HEAD() will unconditionally dereference the null
pointer. Could a check be added to handle the allocation failure before
initializing the list head?
> if (!res)
> return -ENOMEM;
[ ... ]
> @@ -2050,6 +2075,8 @@ module_param(extended_linear_cache, bool, 0444);
> MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support");
> module_param(fail_autoassemble, bool, 0444);
> MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region");
> +module_param(type2_test, bool, 0444);
> +MODULE_PARM_DESC(type2_test, "Enable type 2 support testing");
> module_init(cxl_test_init);
[Severity: High]
This isn't a bug introduced by this patch, but does the error path in
cxl_test_init() improperly tear down the cxl_acpi platform device?
If cxl_mock_platform_device_add() fails, it already drops the reference, so
calling platform_device_put(cxl_acpi) again in the err_root path would cause
a double-put.
Alternatively, if subsequent initialization fails, calling
platform_device_put() instead of platform_device_unregister() leaves the
device registered but drops the initial module reference, leading to a
stranded device when the module unloads.
Could this error path be updated to safely unregister the device?
> module_exit(cxl_test_exit);
> MODULE_LICENSE("GPL v2");
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611152124.3656434-1-dave.jiang@intel.com?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4 3/6] cxl/test: Refactor platform device enumerations
2026-06-11 15:21 [PATCH v4 0/6] cxl: Add CXL type2 accelerator support for cxl_test Dave Jiang
2026-06-11 15:21 ` [PATCH v4 1/6] cxl/test: Add test for module parameters Dave Jiang
2026-06-11 15:21 ` [PATCH v4 2/6] cxl/test: Add type2 support for mock CFMWS0 Dave Jiang
@ 2026-06-11 15:21 ` Dave Jiang
2026-06-11 15:21 ` [PATCH v4 4/6] cxl/test: Add hierarchy enumeration support for type2 device Dave Jiang
` (2 subsequent siblings)
5 siblings, 0 replies; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-cxl; +Cc: djbw, dave, jic23, alison.schofield, vishal.l.verma
Split all the host bridges, rootports, upstream and downstream ports
enumerations to separate helper functions. This should make adding
type2 hierarchy easier later on.
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
v4:
- Skip over null slots when removing devices. (sashiko)
- Set rc with -NOMEM when platform device alloc fails. (sashiko)
---
tools/testing/cxl/test/cxl.c | 312 ++++++++++++++++++++++++-----------
1 file changed, 214 insertions(+), 98 deletions(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index c5529cee482d..71b6203d2fcf 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -1855,10 +1855,204 @@ static bool __init have_multiple_modparms(void)
return count > 1;
}
+static void host_bridges_remove(void)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_host_bridge[i];
+
+ if (!pdev)
+ continue;
+
+ sysfs_remove_link(&pdev->dev.kobj, "physical_node");
+ platform_device_unregister(cxl_host_bridge[i]);
+ }
+}
+
+static int host_bridges_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
+ struct acpi_device *adev = &host_bridge[i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_host_bridge", i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_bridge;
+ }
+
+ mock_companion(adev, &pdev->dev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]);
+ if (rc)
+ goto err_bridge;
+
+ mock_pci_bus[i].bridge = &pdev->dev;
+ rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
+ "physical_node");
+ if (rc)
+ goto err_bridge;
+ }
+
+ return 0;
+
+err_bridge:
+ host_bridges_remove();
+ return rc;
+}
+
+static void cxl_rootports_remove(void)
+{
+ for (int i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_root_port[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(pdev);
+ }
+}
+
+static int cxl_rootports_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
+ struct platform_device *bridge =
+ cxl_host_bridge[i % ARRAY_SIZE(cxl_host_bridge)];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_root_port", i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_port;
+ }
+
+ pdev->dev.parent = &bridge->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]);
+ if (rc)
+ goto err_port;
+ }
+
+ return 0;
+
+err_port:
+ cxl_rootports_remove();
+ return rc;
+}
+
+static void cxl_usps_remove(void)
+{
+ for (int i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_switch_uport[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(cxl_switch_uport[i]);
+ }
+}
+
+static int cxl_usps_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) {
+ struct platform_device *root_port = cxl_root_port[i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_switch_uport", i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_uport;
+ }
+
+ pdev->dev.parent = &root_port->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]);
+ if (rc)
+ goto err_uport;
+ }
+
+ return 0;
+
+err_uport:
+ cxl_usps_remove();
+ return rc;
+}
+
+static void cxl_dsps_remove(void)
+{
+ for (int i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_switch_dport[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(cxl_switch_dport[i]);
+ }
+}
+
+
+static int cxl_dsps_populate(void)
+{
+ int rc = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
+ struct platform_device *uport =
+ cxl_switch_uport[i % ARRAY_SIZE(cxl_switch_uport)];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_switch_dport", i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_dport;
+ }
+ pdev->dev.parent = &uport->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]);
+ if (rc)
+ goto err_dport;
+ }
+
+ return 0;
+
+err_dport:
+ cxl_dsps_remove();
+ return rc;
+}
+
+static void cxl_switches_remove(void)
+{
+ cxl_dsps_remove();
+ cxl_usps_remove();
+}
+
+static int cxl_switches_populate(void)
+{
+ int rc;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
+ rc = cxl_usps_populate();
+ if (rc)
+ return rc;
+
+ rc = cxl_dsps_populate();
+ if (rc) {
+ cxl_usps_remove();
+ return rc;
+ }
+
+ return 0;
+}
+
static __init int cxl_test_init(void)
{
- int rc, i;
struct range mappable;
+ int rc;
/* Enforce a single module param active at a time */
if (have_multiple_modparms())
@@ -1898,74 +2092,21 @@ static __init int cxl_test_init(void)
if (rc)
goto err_populate;
- for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
- struct acpi_device *adev = &host_bridge[i];
- struct platform_device *pdev;
+ rc = host_bridges_populate();
+ if (rc)
+ goto err_populate;
- pdev = platform_device_alloc("cxl_host_bridge", i);
- if (!pdev)
- goto err_bridge;
+ rc = cxl_rootports_populate();
+ if (rc)
+ goto err_host_bridges;
- mock_companion(adev, &pdev->dev);
- rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]);
- if (rc)
- goto err_bridge;
-
- mock_pci_bus[i].bridge = &pdev->dev;
- rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
- "physical_node");
- if (rc)
- goto err_bridge;
- }
-
- for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
- struct platform_device *bridge =
- cxl_host_bridge[i % ARRAY_SIZE(cxl_host_bridge)];
- struct platform_device *pdev;
-
- pdev = platform_device_alloc("cxl_root_port", i);
- if (!pdev)
- goto err_port;
- pdev->dev.parent = &bridge->dev;
-
- rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]);
- if (rc)
- goto err_port;
- }
-
- BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
- for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) {
- struct platform_device *root_port = cxl_root_port[i];
- struct platform_device *pdev;
-
- pdev = platform_device_alloc("cxl_switch_uport", i);
- if (!pdev)
- goto err_uport;
- pdev->dev.parent = &root_port->dev;
-
- rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]);
- if (rc)
- goto err_uport;
- }
-
- for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
- struct platform_device *uport =
- cxl_switch_uport[i % ARRAY_SIZE(cxl_switch_uport)];
- struct platform_device *pdev;
-
- pdev = platform_device_alloc("cxl_switch_dport", i);
- if (!pdev)
- goto err_dport;
- pdev->dev.parent = &uport->dev;
-
- rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]);
- if (rc)
- goto err_dport;
- }
+ rc = cxl_switches_populate();
+ if (rc)
+ goto err_root_ports;
rc = cxl_single_topo_init();
if (rc)
- goto err_dport;
+ goto err_switches;
rc = cxl_rch_topo_init();
if (rc)
@@ -2001,24 +2142,12 @@ static __init int cxl_test_init(void)
cxl_rch_topo_exit();
err_single:
cxl_single_topo_exit();
-err_dport:
- for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_dport[i]);
-err_uport:
- for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_uport[i]);
-err_port:
- for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
- platform_device_unregister(cxl_root_port[i]);
-err_bridge:
- for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
- struct platform_device *pdev = cxl_host_bridge[i];
-
- if (!pdev)
- continue;
- sysfs_remove_link(&pdev->dev.kobj, "physical_node");
- platform_device_unregister(cxl_host_bridge[i]);
- }
+err_switches:
+ cxl_switches_remove();
+err_root_ports:
+ cxl_rootports_remove();
+err_host_bridges:
+ host_bridges_remove();
err_populate:
depopulate_all_mock_resources();
err_gen_pool_add:
@@ -2041,27 +2170,14 @@ static void free_decoder_registry(void)
static __exit void cxl_test_exit(void)
{
- int i;
-
hmem_test_exit();
cxl_mem_exit();
platform_device_unregister(cxl_acpi);
cxl_rch_topo_exit();
cxl_single_topo_exit();
- for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_dport[i]);
- for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_uport[i]);
- for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
- platform_device_unregister(cxl_root_port[i]);
- for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
- struct platform_device *pdev = cxl_host_bridge[i];
-
- if (!pdev)
- continue;
- sysfs_remove_link(&pdev->dev.kobj, "physical_node");
- platform_device_unregister(cxl_host_bridge[i]);
- }
+ cxl_switches_remove();
+ cxl_rootports_remove();
+ host_bridges_remove();
depopulate_all_mock_resources();
gen_pool_destroy(cxl_mock_pool);
unregister_cxl_mock_ops(&cxl_mock_ops);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v4 4/6] cxl/test: Add hierarchy enumeration support for type2 device
2026-06-11 15:21 [PATCH v4 0/6] cxl: Add CXL type2 accelerator support for cxl_test Dave Jiang
` (2 preceding siblings ...)
2026-06-11 15:21 ` [PATCH v4 3/6] cxl/test: Refactor platform device enumerations Dave Jiang
@ 2026-06-11 15:21 ` Dave Jiang
2026-06-11 15:31 ` sashiko-bot
2026-06-11 15:21 ` [PATCH v4 5/6] cxl/test: Fixup hdm init for auto region to support type2 Dave Jiang
2026-06-11 15:21 ` [PATCH v4 6/6] cxl/test: Add cxl_test accelerator driver Dave Jiang
5 siblings, 1 reply; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-cxl; +Cc: djbw, dave, jic23, alison.schofield, vishal.l.verma
Add enumeration of type2 device hierarchy in cxl-test. The type2 device
is setup to be directly attached to a root port instead of rp -> switch
-> device that type3 hierarchy is setup..
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
v4:
- Add setting of rc when platform device allocate fails (sashiko)
- On exit functions, use continue instead of break. (sashiko)
---
tools/testing/cxl/test/cxl.c | 238 ++++++++++++++++++++++++++++-------
1 file changed, 192 insertions(+), 46 deletions(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 71b6203d2fcf..de4124afa132 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -28,6 +28,7 @@ static bool type2_test;
#define NR_CXL_SWITCH_PORTS 2
#define NR_CXL_PORT_DECODERS 8
#define NR_BRIDGES (NR_CXL_HOST_BRIDGES + NR_CXL_SINGLE_HOST + NR_CXL_RCH)
+#define NR_CXL_TYPE2_ACCEL 1
#define MOCK_AUTO_REGION_SIZE_DEFAULT SZ_512M
static int mock_auto_region_size = MOCK_AUTO_REGION_SIZE_DEFAULT;
@@ -1734,19 +1735,93 @@ static void cxl_single_topo_exit(void)
}
}
+static void cxl_type3_mem_exit(void)
+{
+ struct platform_device *pdev;
+ int i;
+
+ for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) {
+ pdev = cxl_rcd[i];
+ if (!pdev)
+ continue;
+ platform_device_unregister(cxl_rcd[i]);
+ }
+
+ for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) {
+ pdev = cxl_mem_single[i];
+ if (!pdev)
+ continue;
+ platform_device_unregister(cxl_mem_single[i]);
+ }
+
+ for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) {
+ pdev = cxl_mem[i];
+ if (!pdev)
+ continue;
+ platform_device_unregister(pdev);
+ }
+}
+
+static void cxl_type2_mem_exit(void)
+{
+ for (int i = NR_CXL_TYPE2_ACCEL - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_mem[i];
+
+ if (!pdev)
+ continue;
+ platform_device_unregister(pdev);
+ }
+}
+
static void cxl_mem_exit(void)
{
- int i;
-
- for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--)
- platform_device_unregister(cxl_rcd[i]);
- for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--)
- platform_device_unregister(cxl_mem_single[i]);
- for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
- platform_device_unregister(cxl_mem[i]);
+ if (type2_test) {
+ cxl_type2_mem_exit();
+ return;
+ }
+
+ cxl_type3_mem_exit();
+}
+
+static int cxl_type2_mem_init(void)
+{
+ int i, rc;
+
+ for (i = 0; i < NR_CXL_TYPE2_ACCEL; i++) {
+ struct platform_device *dport = cxl_root_port[i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_type2_accel", i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_mem;
+ }
+ pdev->dev.parent = &dport->dev;
+ set_dev_node(&pdev->dev, i % 2);
+
+ rc = platform_device_add(pdev);
+ if (rc) {
+ rc = -ENOMEM;
+ platform_device_put(pdev);
+ goto err_mem;
+ }
+ cxl_mem[i] = pdev;
+ }
+
+ return 0;
+
+err_mem:
+ for (i = NR_CXL_TYPE2_ACCEL - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_mem[i];
+
+ if (!pdev)
+ continue;
+ platform_device_unregister(pdev);
+ }
+ return rc;
}
-static int cxl_mem_init(void)
+static int cxl_type3_mem_init(void)
{
int i, rc;
@@ -1755,8 +1830,10 @@ static int cxl_mem_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_mem", i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_mem;
+ }
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);
@@ -1770,8 +1847,10 @@ static int cxl_mem_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_mem", NR_MEM_MULTI + i);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_single;
+ }
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);
@@ -1786,8 +1865,10 @@ static int cxl_mem_init(void)
struct platform_device *pdev;
pdev = platform_device_alloc("cxl_rcd", idx);
- if (!pdev)
+ if (!pdev) {
+ rc = -ENOMEM;
goto err_rcd;
+ }
pdev->dev.parent = &rch->dev;
set_dev_node(&pdev->dev, i % 2);
@@ -1810,6 +1891,13 @@ static int cxl_mem_init(void)
return rc;
}
+static int cxl_mem_init(void)
+{
+ if (type2_test)
+ return cxl_type2_mem_init();
+ return cxl_type3_mem_init();
+}
+
static ssize_t
decoder_reset_preserve_registry_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -2049,6 +2137,92 @@ static int cxl_switches_populate(void)
return 0;
}
+static void cxl_type2_topo_exit(void)
+{
+ host_bridges_remove();
+ cxl_rootports_remove();
+}
+
+static int cxl_type2_topo_init(void)
+{
+ int rc;
+
+ rc = host_bridges_populate();
+ if (rc)
+ return rc;
+
+ rc = cxl_rootports_populate();
+ if (rc) {
+ host_bridges_remove();
+ return rc;
+ }
+
+ return 0;
+}
+
+static void cxl_type3_topo_exit(void)
+{
+ host_bridges_remove();
+ cxl_rootports_remove();
+ cxl_switches_remove();
+ cxl_single_topo_exit();
+ cxl_rch_topo_exit();
+}
+
+static int cxl_type3_topo_init(void)
+{
+ int rc;
+
+ rc = host_bridges_populate();
+ if (rc)
+ return rc;
+
+ rc = cxl_rootports_populate();
+ if (rc)
+ goto err_host_bridges;
+
+ rc = cxl_switches_populate();
+ if (rc)
+ goto err_root_ports;
+
+ rc = cxl_single_topo_init();
+ if (rc)
+ goto err_switches;
+
+ rc = cxl_rch_topo_init();
+ if (rc)
+ goto err_single;
+
+ return 0;
+
+err_single:
+ cxl_single_topo_exit();
+err_switches:
+ cxl_switches_remove();
+err_root_ports:
+ cxl_rootports_remove();
+err_host_bridges:
+ host_bridges_remove();
+ return rc;
+}
+
+static void cxl_topo_exit(void)
+{
+ if (type2_test) {
+ cxl_type2_topo_exit();
+ return;
+ }
+
+ cxl_type3_topo_exit();
+}
+
+static int cxl_topo_init(void)
+{
+ if (type2_test)
+ return cxl_type2_topo_init();
+ return cxl_type3_topo_init();
+}
+
static __init int cxl_test_init(void)
{
struct range mappable;
@@ -2092,29 +2266,13 @@ static __init int cxl_test_init(void)
if (rc)
goto err_populate;
- rc = host_bridges_populate();
+ rc = cxl_topo_init();
if (rc)
goto err_populate;
- rc = cxl_rootports_populate();
- if (rc)
- goto err_host_bridges;
-
- rc = cxl_switches_populate();
- if (rc)
- goto err_root_ports;
-
- rc = cxl_single_topo_init();
- if (rc)
- goto err_switches;
-
- rc = cxl_rch_topo_init();
- if (rc)
- goto err_single;
-
cxl_acpi = platform_device_alloc("cxl_acpi", 0);
if (!cxl_acpi)
- goto err_rch;
+ goto err_topo;
mock_companion(&acpi0017_mock, &cxl_acpi->dev);
acpi0017_mock.dev.bus = &platform_bus_type;
@@ -2122,7 +2280,7 @@ static __init int cxl_test_init(void)
rc = cxl_mock_platform_device_add(cxl_acpi, NULL);
if (rc)
- goto err_rch;
+ goto err_root;
rc = cxl_mem_init();
if (rc)
@@ -2138,16 +2296,8 @@ static __init int cxl_test_init(void)
cxl_mem_exit();
err_root:
platform_device_put(cxl_acpi);
-err_rch:
- cxl_rch_topo_exit();
-err_single:
- cxl_single_topo_exit();
-err_switches:
- cxl_switches_remove();
-err_root_ports:
- cxl_rootports_remove();
-err_host_bridges:
- host_bridges_remove();
+err_topo:
+ cxl_topo_exit();
err_populate:
depopulate_all_mock_resources();
err_gen_pool_add:
@@ -2173,11 +2323,7 @@ static __exit void cxl_test_exit(void)
hmem_test_exit();
cxl_mem_exit();
platform_device_unregister(cxl_acpi);
- cxl_rch_topo_exit();
- cxl_single_topo_exit();
- cxl_switches_remove();
- cxl_rootports_remove();
- host_bridges_remove();
+ cxl_topo_exit();
depopulate_all_mock_resources();
gen_pool_destroy(cxl_mock_pool);
unregister_cxl_mock_ops(&cxl_mock_ops);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v4 4/6] cxl/test: Add hierarchy enumeration support for type2 device
2026-06-11 15:21 ` [PATCH v4 4/6] cxl/test: Add hierarchy enumeration support for type2 device Dave Jiang
@ 2026-06-11 15:31 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-11 15:31 UTC (permalink / raw)
To: Dave Jiang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The topology teardown functions remove devices in forward (parent-to-child) order, causing use-after-free and double-free vulnerabilities.
- [High] Double `platform_device_put()` on `cxl_acpi` when `cxl_mock_platform_device_add()` fails.
--
commit 7f72f92a89170bc076d1c5ae46854f2ff2fcf32d
Author: Dave Jiang <dave.jiang@intel.com>
cxl/test: Add hierarchy enumeration support for type2 device
Add enumeration of type2 device hierarchy in cxl-test. The type2 device
is setup to be directly attached to a root port instead of rp -> switch
-> device that type3 hierarchy is setup..
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> index 71b6203d2fcf0..de4124afa1328 100644
> --- a/tools/testing/cxl/test/cxl.c
> +++ b/tools/testing/cxl/test/cxl.c
[ ... ]
> @@ -2049,6 +2137,92 @@ static int cxl_switches_populate(void)
> return 0;
> }
>
> +static void cxl_type2_topo_exit(void)
> +{
> + host_bridges_remove();
> + cxl_rootports_remove();
> +}
[Severity: High]
Does this topology teardown order cause a use-after-free? When the parent
device (host bridge) is removed, the driver core recursively unregisters and
frees its child devices. It seems that calling cxl_rootports_remove() after
host_bridges_remove() will operate on already-freed platform_device
pointers. Should the teardown be done in bottom-up (reverse) order?
> +
> +static int cxl_type2_topo_init(void)
> +{
[ ... ]
> +static void cxl_type3_topo_exit(void)
> +{
> + host_bridges_remove();
> + cxl_rootports_remove();
> + cxl_switches_remove();
> + cxl_single_topo_exit();
> + cxl_rch_topo_exit();
> +}
[Severity: High]
Similarly here in cxl_type3_topo_exit(), does removing parent devices before
their children cause the subsequent removal functions to access freed memory?
[ ... ]
> @@ -2122,7 +2280,7 @@ static __init int cxl_test_init(void)
>
> rc = cxl_mock_platform_device_add(cxl_acpi, NULL);
> if (rc)
> - goto err_rch;
> + goto err_root;
>
> rc = cxl_mem_init();
> if (rc)
> @@ -2138,16 +2296,8 @@ static __init int cxl_test_init(void)
> cxl_mem_exit();
> err_root:
> platform_device_put(cxl_acpi);
[Severity: High]
If cxl_mock_platform_device_add() fails, it drops the reference to cxl_acpi
before returning. Jumping to err_root will call platform_device_put()
a second time on the same pointer. Does this result in a double-free?
In the pre-patch code, the error path jumped past this to err_rch, which
bypassed the extraneous put.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611152124.3656434-1-dave.jiang@intel.com?part=4
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4 5/6] cxl/test: Fixup hdm init for auto region to support type2
2026-06-11 15:21 [PATCH v4 0/6] cxl: Add CXL type2 accelerator support for cxl_test Dave Jiang
` (3 preceding siblings ...)
2026-06-11 15:21 ` [PATCH v4 4/6] cxl/test: Add hierarchy enumeration support for type2 device Dave Jiang
@ 2026-06-11 15:21 ` Dave Jiang
2026-06-11 15:44 ` sashiko-bot
2026-06-11 15:21 ` [PATCH v4 6/6] cxl/test: Add cxl_test accelerator driver Dave Jiang
5 siblings, 1 reply; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-cxl; +Cc: djbw, dave, jic23, alison.schofield, vishal.l.verma
Add support to setup initialization of decoders in order to support
type2 auto region.
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
- rebased against v7.1-rc7
---
tools/testing/cxl/test/cxl.c | 255 +++++++++++++++++++++++++----------
1 file changed, 186 insertions(+), 69 deletions(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index de4124afa132..0310f2efd331 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -1077,74 +1077,26 @@ static int first_decoder(struct device *dev, const void *data)
return 0;
}
-/*
- * Initialize a decoder during HDM enumeration.
- *
- * If a saved registry entry exists:
- * - enabled decoders are restored from the saved programming
- * - disabled decoders are initialized in a clean disabled state
- *
- * If no registry entry exists the decoder follows the normal mock
- * initialization path, including the special auto-region setup for
- * the first endpoints under host-bridge0.
- *
- * Returns true if decoder state was restored from the registry. In
- * that case the saved decode configuration (including target mapping)
- * has already been applied and the map_targets() is skipped.
- */
-static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
+enum cxld_init_type {
+ MOCK_DECODER_INIT_DEFAULT,
+ MOCK_DECODER_INIT_SAVED,
+ MOCK_DECODER_INIT_TYPE3_AUTO,
+ MOCK_DECODER_INIT_TYPE2_AUTO,
+};
+
+static enum cxld_init_type get_decoder_init_type(struct cxl_decoder *cxld,
+ struct platform_device *pdev,
+ bool hb0,
+ struct cxl_test_decoder **td)
{
- struct acpi_cedt_cfmws *window = mock_cfmws[0];
- struct platform_device *pdev = NULL;
- struct cxl_endpoint_decoder *cxled;
- struct cxl_switch_decoder *cxlsd;
- struct cxl_port *port, *iter;
- struct cxl_test_decoder *td;
- struct cxl_memdev *cxlmd;
- struct cxl_dport *dport;
- struct device *dev;
- bool hb0 = false;
- u64 base;
- int i;
+ struct cxl_test_decoder *found_td = cxld_registry_find(cxld);
- if (is_endpoint_decoder(&cxld->dev)) {
- cxled = to_cxl_endpoint_decoder(&cxld->dev);
- cxlmd = cxled_to_memdev(cxled);
- WARN_ON(!dev_is_platform(cxlmd->dev.parent));
- pdev = to_platform_device(cxlmd->dev.parent);
-
- /* check is endpoint is attach to host-bridge0 */
- port = cxled_to_port(cxled);
- do {
- if (port->uport_dev == &cxl_host_bridge[0]->dev) {
- hb0 = true;
- break;
- }
- if (is_cxl_port(port->dev.parent))
- port = to_cxl_port(port->dev.parent);
- else
- port = NULL;
- } while (port);
- port = cxled_to_port(cxled);
- } else {
- port = to_cxl_port(cxld->dev.parent);
+ if (found_td) {
+ *td = found_td;
+ return MOCK_DECODER_INIT_SAVED;
}
- td = cxld_registry_find(cxld);
- if (td) {
- bool enabled;
-
- if (is_switch_decoder(&cxld->dev))
- enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE;
- else
- enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE;
-
- if (enabled)
- return !cxld_registry_restore(cxld, td);
-
- init_disabled_mock_decoder(cxld);
- return false;
- }
+ *td = NULL;
/*
* The first decoder on the first 2 devices on the first switch
@@ -1155,15 +1107,111 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
* See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4'
*/
if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 ||
- pdev->id > 4 || cxld->id > 0) {
- default_mock_decoder(cxld);
- return false;
- }
+ pdev->id > 4 || cxld->id > 0)
+ return MOCK_DECODER_INIT_DEFAULT;
+
+ return type2_test ? MOCK_DECODER_INIT_TYPE2_AUTO :
+ MOCK_DECODER_INIT_TYPE3_AUTO;
+}
+
+static bool mock_decoder_handle_saved(struct cxl_decoder *cxld, struct cxl_test_decoder *td)
+{
+ bool enabled;
+
+ if (is_switch_decoder(&cxld->dev))
+ enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE;
+ else
+ enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE;
+
+ if (enabled)
+ return !cxld_registry_restore(cxld, td);
+
+ init_disabled_mock_decoder(cxld);
+ return false;
+}
+
+static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled,
+ struct cxl_port *port,
+ struct platform_device *pdev)
+{
+ struct acpi_cedt_cfmws *window = mock_cfmws[0];
+ struct cxl_decoder *cxld = &cxled->cxld;
+ struct cxl_switch_decoder *cxlsd;
+ struct cxl_dport *dport;
+ struct cxl_port *root_port;
+ struct device *dev;
+ u64 base;
+
+ base = window->base_hpa;
+ cxld->hpa_range = (struct range) {
+ .start = base,
+ .end = base + mock_auto_region_size - 1,
+ };
+
+ cxld->interleave_ways = 1;
+ eig_to_granularity(window->granularity, &cxld->interleave_granularity);
+ cxld->target_type = CXL_DECODER_DEVMEM;
+ cxld->flags = CXL_DECODER_F_ENABLE;
+ cxled->state = CXL_DECODER_STATE_AUTO;
+ port->commit_end = cxld->id;
+ devm_cxl_dpa_reserve(cxled, 0,
+ mock_auto_region_size / cxld->interleave_ways, 0);
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ WARN_ON_ONCE(!cxld_registry_new(cxld));
+ /*
+ * Now that endpoint decoder is set up, walk up the hierarchy
+ * and setup the root port decoder targeting @cxlmd.
+ */
+ dport = port->parent_dport;
+ root_port = dport->port;
+ dev = device_find_child(&root_port->dev, NULL, first_decoder);
+ /*
+ * Ancestor ports are guaranteed to be enumerated before
+ * @port, and all ports have at least one decoder.
+ */
+ if (WARN_ON(!dev))
+ return;
+
+ cxlsd = to_cxl_switch_decoder(dev);
+ cxlsd->target[0] = dport;
+ cxlsd->cxld.target_map[0] = dport->port_id;
+ cxld = &cxlsd->cxld;
+ cxld->target_type = CXL_DECODER_DEVMEM;
+ cxld->flags = CXL_DECODER_F_ENABLE;
+ root_port->commit_end = 0;
+ cxld->interleave_ways = 1;
+ cxld->interleave_granularity = 4096;
+ cxld->hpa_range = (struct range) {
+ .start = base,
+ .end = base + mock_auto_region_size - 1,
+ };
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ cxld_registry_update(cxld);
+ put_device(dev);
+}
+
+static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled,
+ struct cxl_port *port,
+ struct platform_device *pdev,
+ bool hb0)
+{
+ struct acpi_cedt_cfmws *window = mock_cfmws[0];
+ struct cxl_decoder *cxld = &cxled->cxld;
+ struct cxl_switch_decoder *cxlsd;
+ struct cxl_dport *dport;
+ struct cxl_port *iter;
+ struct device *dev;
+ u64 base;
+ int i;
/* Simulate missing cxl_mem.4 configuration */
if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) {
default_mock_decoder(cxld);
- return false;
+ return;
}
base = window->base_hpa;
@@ -1239,7 +1287,76 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxld_registry_update(cxld);
put_device(dev);
}
+}
+/*
+ * Initialize a decoder during HDM enumeration.
+ *
+ * If a saved registry entry exists:
+ * - enabled decoders are restored from the saved programming
+ * - disabled decoders are initialized in a clean disabled state
+ *
+ * If no registry entry exists the decoder follows the normal mock
+ * initialization path, including the special auto-region setup for
+ * the first endpoints under host-bridge0.
+ *
+ * Returns true if decoder state was restored from the registry. In
+ * that case the saved decode configuration (including target mapping)
+ * has already been applied and the map_targets() is skipped.
+ */
+static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
+{
+ struct cxl_endpoint_decoder *cxled = NULL;
+ struct platform_device *pdev = NULL;
+ struct cxl_test_decoder *td;
+ struct cxl_memdev *cxlmd;
+ struct cxl_port *port;
+ bool hb0 = false;
+
+ if (is_endpoint_decoder(&cxld->dev)) {
+ cxled = to_cxl_endpoint_decoder(&cxld->dev);
+ cxlmd = cxled_to_memdev(cxled);
+ WARN_ON(!dev_is_platform(cxlmd->dev.parent));
+ pdev = to_platform_device(cxlmd->dev.parent);
+
+ /* check is endpoint is attach to host-bridge0 */
+ port = cxled_to_port(cxled);
+ do {
+ if (port->uport_dev == &cxl_host_bridge[0]->dev) {
+ hb0 = true;
+ break;
+ }
+ if (is_cxl_port(port->dev.parent))
+ port = to_cxl_port(port->dev.parent);
+ else
+ port = NULL;
+ } while (port);
+ port = cxled_to_port(cxled);
+ } else {
+ port = to_cxl_port(cxld->dev.parent);
+ }
+
+ switch (get_decoder_init_type(cxld, pdev, hb0, &td)) {
+ case MOCK_DECODER_INIT_SAVED:
+ if (WARN_ON(!td))
+ return false;
+ return mock_decoder_handle_saved(cxld, td);
+ case MOCK_DECODER_INIT_DEFAULT:
+ /*
+ * The default path picks up all the decoders that are not
+ * endpoint.
+ */
+ default_mock_decoder(cxld);
+ return false;
+ case MOCK_DECODER_INIT_TYPE3_AUTO:
+ mock_init_hdm_type3_cxled(cxled, port, pdev, hb0);
+ return false;
+ case MOCK_DECODER_INIT_TYPE2_AUTO:
+ mock_init_hdm_type2_cxled(cxled, port, pdev);
+ return false;
+ default:
+ return false;
+ }
return false;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v4 5/6] cxl/test: Fixup hdm init for auto region to support type2
2026-06-11 15:21 ` [PATCH v4 5/6] cxl/test: Fixup hdm init for auto region to support type2 Dave Jiang
@ 2026-06-11 15:44 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-11 15:44 UTC (permalink / raw)
To: Dave Jiang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Medium] Concurrent modification of shared switch/root decoders and their registry entries without synchronization.
--
commit 7bae93279b06918f3ff41cc5d5704995d896c3cb
Author: Dave Jiang <dave.jiang@intel.com>
cxl/test: Fixup hdm init for auto region to support type2
Add support to setup initialization of decoders in order to support type2
auto region.
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> index de4124afa1328..0310f2efd3319 100644
> --- a/tools/testing/cxl/test/cxl.c
> +++ b/tools/testing/cxl/test/cxl.c
[ ... ]
> @@ -1239,7 +1287,76 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
> cxld_registry_update(cxld);
[Severity: Medium]
This is a pre-existing issue, but is there a potential data race here?
Because cxl_mem.0 and cxl_mem.4 are probed asynchronously, both threads
can reach mock_init_hdm_type3_cxled() concurrently. They both look up the
shared upstream switch and root port decoders via device_find_child(),
and then simultaneously modify shared fields like cxlsd->target[] and
call cxld_registry_update(cxld).
Could this lack of mutual exclusion corrupt the mock cxld state via
non-atomic writes or concurrent registry updates?
> put_device(dev);
> }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611152124.3656434-1-dave.jiang@intel.com?part=5
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4 6/6] cxl/test: Add cxl_test accelerator driver
2026-06-11 15:21 [PATCH v4 0/6] cxl: Add CXL type2 accelerator support for cxl_test Dave Jiang
` (4 preceding siblings ...)
2026-06-11 15:21 ` [PATCH v4 5/6] cxl/test: Fixup hdm init for auto region to support type2 Dave Jiang
@ 2026-06-11 15:21 ` Dave Jiang
2026-06-11 15:32 ` sashiko-bot
5 siblings, 1 reply; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:21 UTC (permalink / raw)
To: linux-cxl; +Cc: djbw, dave, jic23, alison.schofield, vishal.l.verma
Add a type2 accelerator mock driver for the platform device that
simulates a CXL type2 device. The driver exercises the same
minimal API calls that a real CXL type2 driver would utilize.
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
v4:
- Use pdev->id instead of pdev->dev->id. (sashiko)
---
tools/testing/cxl/test/Kbuild | 2 ++
tools/testing/cxl/test/accel.c | 66 ++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
create mode 100644 tools/testing/cxl/test/accel.c
diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
index c168e3c998a7..9a24ddc28488 100644
--- a/tools/testing/cxl/test/Kbuild
+++ b/tools/testing/cxl/test/Kbuild
@@ -5,10 +5,12 @@ obj-m += cxl_test.o
obj-m += cxl_mock.o
obj-m += cxl_mock_mem.o
obj-m += cxl_translate.o
+obj-m += cxl_mock_accel.o
cxl_test-y := cxl.o
cxl_test-y += hmem_test.o
cxl_mock-y := mock.o
cxl_mock_mem-y := mem.o
+cxl_mock_accel-y := accel.o
KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS))
diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c
new file mode 100644
index 000000000000..9c8235f9d97b
--- /dev/null
+++ b/tools/testing/cxl/test/accel.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2026 Intel Corporation. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include <cxl/mailbox.h>
+#include <cxlmem.h>
+
+struct mock_cxl_accel {
+ struct cxl_dev_state cxlds;
+ struct cxl_memdev *cxlmd;
+};
+
+static int cxl_mock_accel_probe(struct platform_device *pdev)
+{
+ struct mock_cxl_accel *cxl_accel;
+ struct device *dev = &pdev->dev;
+ struct cxl_dev_state *cxlds;
+ struct cxl_memdev *cxlmd;
+ struct range mock_range;
+ int rc;
+
+ cxl_accel = devm_cxl_dev_state_create(&pdev->dev, CXL_DEVTYPE_DEVMEM,
+ pdev->id + 1, 0,
+ struct mock_cxl_accel, cxlds,
+ false);
+ if (!cxl_accel)
+ return -ENOMEM;
+
+ cxlds = &cxl_accel->cxlds;
+ cxlds->media_ready = true;
+ rc = cxl_set_capacity(cxlds, SZ_512M);
+ if (rc)
+ return rc;
+
+ cxlmd = devm_cxl_probe_mem(cxlds, &mock_range);
+ if (IS_ERR(cxlmd))
+ return PTR_ERR(cxlmd);
+ cxl_accel->cxlmd = cxlmd;
+
+ dev_dbg(dev, "Probed mock accelerator with range %pra\n", &mock_range);
+
+ return 0;
+}
+
+static const struct platform_device_id cxl_mock_accel_ids[] = {
+ { .name = "cxl_type2_accel", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, cxl_mock_accel_ids);
+
+static struct platform_driver cxl_mock_accel_driver = {
+ .probe = cxl_mock_accel_probe,
+ .id_table = cxl_mock_accel_ids,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+
+module_platform_driver(cxl_mock_accel_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("cxl_test: accelerator device mock module");
+MODULE_IMPORT_NS("CXL");
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v4 6/6] cxl/test: Add cxl_test accelerator driver
2026-06-11 15:21 ` [PATCH v4 6/6] cxl/test: Add cxl_test accelerator driver Dave Jiang
@ 2026-06-11 15:32 ` sashiko-bot
2026-06-11 15:41 ` Dave Jiang
0 siblings, 1 reply; 13+ messages in thread
From: sashiko-bot @ 2026-06-11 15:32 UTC (permalink / raw)
To: Dave Jiang; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Undeclared API Usage / Build Failure due to missing functions `cxl_set_capacity` and `devm_cxl_probe_mem`.
--
commit fd0aec5a2701e0475907c9138fa0ea33cfb68569
Author: Dave Jiang <dave.jiang@intel.com>
cxl/test: Add cxl_test accelerator driver
This commit adds a mock platform driver simulating a CXL type 2 accelerator
device. It is designed to test minimal API calls required by real CXL type 2
drivers.
> diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c
> new file mode 100644
> index 0000000000000..9c8235f9d97b7
> --- /dev/null
> +++ b/tools/testing/cxl/test/accel.c
[ ... ]
> +static int cxl_mock_accel_probe(struct platform_device *pdev)
> +{
[ ... ]
> + cxlds = &cxl_accel->cxlds;
> + cxlds->media_ready = true;
> + rc = cxl_set_capacity(cxlds, SZ_512M);
> + if (rc)
> + return rc;
> +
> + cxlmd = devm_cxl_probe_mem(cxlds, &mock_range);
[Severity: Low]
Are the cxl_set_capacity and devm_cxl_probe_mem functions defined anywhere in
the kernel source tree?
It appears these functions do not exist, which causes an implicit declaration
error and undefined references during compilation. Will this lead to an
immediate build failure for the test module?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611152124.3656434-1-dave.jiang@intel.com?part=6
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v4 6/6] cxl/test: Add cxl_test accelerator driver
2026-06-11 15:32 ` sashiko-bot
@ 2026-06-11 15:41 ` Dave Jiang
0 siblings, 0 replies; 13+ messages in thread
From: Dave Jiang @ 2026-06-11 15:41 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-cxl
On 6/11/26 8:32 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Low] Undeclared API Usage / Build Failure due to missing functions `cxl_set_capacity` and `devm_cxl_probe_mem`.
False positive
> --
>
> commit fd0aec5a2701e0475907c9138fa0ea33cfb68569
> Author: Dave Jiang <dave.jiang@intel.com>
>
> cxl/test: Add cxl_test accelerator driver
>
> This commit adds a mock platform driver simulating a CXL type 2 accelerator
> device. It is designed to test minimal API calls required by real CXL type 2
> drivers.
>
>> diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c
>> new file mode 100644
>> index 0000000000000..9c8235f9d97b7
>> --- /dev/null
>> +++ b/tools/testing/cxl/test/accel.c
> [ ... ]
>> +static int cxl_mock_accel_probe(struct platform_device *pdev)
>> +{
> [ ... ]
>> + cxlds = &cxl_accel->cxlds;
>> + cxlds->media_ready = true;
>> + rc = cxl_set_capacity(cxlds, SZ_512M);
>> + if (rc)
>> + return rc;
>> +
>> + cxlmd = devm_cxl_probe_mem(cxlds, &mock_range);
>
> [Severity: Low]
> Are the cxl_set_capacity and devm_cxl_probe_mem functions defined anywhere in
> the kernel source tree?
>
> It appears these functions do not exist, which causes an implicit declaration
> error and undefined references during compilation. Will this lead to an
> immediate build failure for the test module?
>
^ permalink raw reply [flat|nested] 13+ messages in thread