]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
tools/testing/cxl: Test dax_hmem takeover of CXL regions
authorDan Williams <dan.j.williams@intel.com>
Fri, 27 Mar 2026 05:28:21 +0000 (22:28 -0700)
committerDave Jiang <dave.jiang@intel.com>
Wed, 1 Apr 2026 15:12:18 +0000 (08:12 -0700)
When platform firmware is committed to publishing EFI_CONVENTIONAL_MEMORY
in the memory map, but CXL fails to assemble the region, dax_hmem can
attempt to attach a dax device to the memory range.

Take advantage of the new ability to support multiple "hmem_platform"
devices, and to enable regression testing of several scenarios:

* CXL correctly assembles a region, check dax_hmem fails to attach dax
* CXL fails to assemble a region, check dax_hmem successfully attaches dax
* Check that loading the dax_cxl driver loads the dax_hmem driver
* Attempt to race cxl_mock_mem async probe vs dax_hmem probe flushing.
  Check that both positive and negative cases.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Tested-by: Alison Schofield <alison.schofield@intel.com>
Link: https://patch.msgid.link/20260327052821.440749-10-dan.j.williams@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
tools/testing/cxl/Kbuild
tools/testing/cxl/test/Kbuild
tools/testing/cxl/test/cxl.c
tools/testing/cxl/test/hmem_test.c [new file with mode: 0644]
tools/testing/cxl/test/mem.c
tools/testing/cxl/test/mock.c
tools/testing/cxl/test/mock.h

index 53d84a6874b76b0371d599069c2d2ab32f2553fd..540425c7cd411c849b933dc5ffed3b03d5044a8c 100644 (file)
@@ -11,8 +11,12 @@ ldflags-y += --wrap=devm_cxl_endpoint_decoders_setup
 ldflags-y += --wrap=hmat_get_extended_linear_cache_size
 ldflags-y += --wrap=devm_cxl_add_dport_by_dev
 ldflags-y += --wrap=devm_cxl_switch_port_decoders_setup
+ldflags-y += --wrap=walk_hmem_resources
+ldflags-y += --wrap=region_intersects
+ldflags-y += --wrap=region_intersects_soft_reserve
 
 DRIVERS := ../../../drivers
+DAX_HMEM_SRC := $(DRIVERS)/dax/hmem
 CXL_SRC := $(DRIVERS)/cxl
 CXL_CORE_SRC := $(DRIVERS)/cxl/core
 ccflags-y := -I$(srctree)/drivers/cxl/
@@ -70,6 +74,9 @@ cxl_core-y += config_check.o
 cxl_core-y += cxl_core_test.o
 cxl_core-y += cxl_core_exports.o
 
+obj-m += dax_hmem.o
+dax_hmem-y := $(DAX_HMEM_SRC)/hmem.o
+
 KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS))
 
 obj-m += test/
index af50972c8b6d30d878d541c8e597fc2a31f4a9ae..c168e3c998a7d081e114ba2b0883b2aed1e996d9 100644 (file)
@@ -7,6 +7,7 @@ obj-m += cxl_mock_mem.o
 obj-m += cxl_translate.o
 
 cxl_test-y := cxl.o
+cxl_test-y += hmem_test.o
 cxl_mock-y := mock.o
 cxl_mock_mem-y := mem.o
 
index 7deeb7ff7bdfd4eb42f68569ed1ad68032d792d5..9a9f52090c1d0385fb5c3a4df8c1d9a0db7d5c01 100644 (file)
@@ -1121,6 +1121,53 @@ static void mock_cxl_endpoint_parse_cdat(struct cxl_port *port)
        cxl_endpoint_get_perf_coordinates(port, ep_c);
 }
 
+/*
+ * Simulate that the first half of mock CXL Window 0 is "Soft Reserve" capacity
+ */
+static int mock_walk_hmem_resources(struct device *host, walk_hmem_fn fn)
+{
+       struct acpi_cedt_cfmws *cfmws = mock_cfmws[0];
+       struct resource window =
+               DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2);
+
+       dev_dbg(host, "walk cxl_test resource: %pr\n", &window);
+       return fn(host, 0, &window);
+}
+
+/*
+ * This should only be called by the dax_hmem case, treat mismatches (negative
+ * result) as "fallback to base region_intersects()". Simulate that the first
+ * half of mock CXL Window 0 is IORES_DESC_CXL capacity.
+ */
+static int mock_region_intersects(resource_size_t start, size_t size,
+                                 unsigned long flags, unsigned long desc)
+{
+       struct resource res = DEFINE_RES_MEM(start, size);
+       struct acpi_cedt_cfmws *cfmws = mock_cfmws[0];
+       struct resource window =
+               DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2);
+
+       if (resource_overlaps(&res, &window))
+               return REGION_INTERSECTS;
+       pr_debug("warning: no cxl_test CXL intersection for %pr\n", &res);
+       return -1;
+}
+
+
+static int
+mock_region_intersects_soft_reserve(resource_size_t start, size_t size)
+{
+       struct resource res = DEFINE_RES_MEM(start, size);
+       struct acpi_cedt_cfmws *cfmws = mock_cfmws[0];
+       struct resource window =
+               DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2);
+
+       if (resource_overlaps(&res, &window))
+               return REGION_INTERSECTS;
+       pr_debug("warning: no cxl_test soft reserve intersection for %pr\n", &res);
+       return -1;
+}
+
 static struct cxl_mock_ops cxl_mock_ops = {
        .is_mock_adev = is_mock_adev,
        .is_mock_bridge = is_mock_bridge,
@@ -1136,6 +1183,9 @@ static struct cxl_mock_ops cxl_mock_ops = {
        .devm_cxl_add_dport_by_dev = mock_cxl_add_dport_by_dev,
        .hmat_get_extended_linear_cache_size =
                mock_hmat_get_extended_linear_cache_size,
+       .walk_hmem_resources = mock_walk_hmem_resources,
+       .region_intersects = mock_region_intersects,
+       .region_intersects_soft_reserve = mock_region_intersects_soft_reserve,
        .list = LIST_HEAD_INIT(cxl_mock_ops.list),
 };
 
@@ -1561,8 +1611,14 @@ static __init int cxl_test_init(void)
        if (rc)
                goto err_root;
 
+       rc = hmem_test_init();
+       if (rc)
+               goto err_mem;
+
        return 0;
 
+err_mem:
+       cxl_mem_exit();
 err_root:
        platform_device_put(cxl_acpi);
 err_rch:
@@ -1600,6 +1656,7 @@ static __exit void cxl_test_exit(void)
 {
        int i;
 
+       hmem_test_exit();
        cxl_mem_exit();
        platform_device_unregister(cxl_acpi);
        cxl_rch_topo_exit();
diff --git a/tools/testing/cxl/test/hmem_test.c b/tools/testing/cxl/test/hmem_test.c
new file mode 100644 (file)
index 0000000..3a1a089
--- /dev/null
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Intel Corporation */
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+#include "../../../drivers/dax/bus.h"
+
+static bool hmem_test;
+
+static void hmem_test_work(struct work_struct *work)
+{
+}
+
+static void hmem_test_release(struct device *dev)
+{
+       struct hmem_platform_device *hpdev =
+               container_of(dev, typeof(*hpdev), pdev.dev);
+
+       memset(hpdev, 0, sizeof(*hpdev));
+}
+
+static struct hmem_platform_device hmem_test_device = {
+       .pdev = {
+               .name = "hmem_platform",
+               .id = 1,
+               .dev = {
+                       .release = hmem_test_release,
+               },
+       },
+       .work = __WORK_INITIALIZER(hmem_test_device.work, hmem_test_work),
+};
+
+int hmem_test_init(void)
+{
+       if (!hmem_test)
+               return 0;
+
+       return platform_device_register(&hmem_test_device.pdev);
+}
+
+void hmem_test_exit(void)
+{
+       if (hmem_test)
+               platform_device_unregister(&hmem_test_device.pdev);
+}
+
+module_param(hmem_test, bool, 0444);
+MODULE_PARM_DESC(hmem_test, "Enable/disable the dax_hmem test platform device");
index cb87e8c0e63c02b1d342d4a3e29fd0185981066b..cc847e9aeceb9308d6b5a89699f137c92342dd41 100644 (file)
@@ -1695,6 +1695,9 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
        struct cxl_dpa_info range_info = { 0 };
        int rc;
 
+       /* Increase async probe race window */
+       usleep_range(500*1000, 1000*1000);
+
        mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);
        if (!mdata)
                return -ENOMEM;
index b8fcb50c10278ad8133bf01bb1feba023e5617cd..6454b868b122c435194289a66a569aadefc17e65 100644 (file)
@@ -251,6 +251,56 @@ struct cxl_dport *__wrap_devm_cxl_add_dport_by_dev(struct cxl_port *port,
 }
 EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_dport_by_dev, "CXL");
 
+int __wrap_region_intersects(resource_size_t start, size_t size,
+                            unsigned long flags, unsigned long desc)
+{
+       int rc = -1;
+       int index;
+       struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+       if (ops)
+               rc = ops->region_intersects(start, size, flags, desc);
+       if (rc < 0)
+               rc = region_intersects(start, size, flags, desc);
+       put_cxl_mock_ops(index);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(__wrap_region_intersects);
+
+int __wrap_region_intersects_soft_reserve(resource_size_t start, size_t size)
+{
+       int rc = -1;
+       int index;
+       struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+       if (ops)
+               rc = ops->region_intersects_soft_reserve(start, size);
+       if (rc < 0)
+               rc = region_intersects_soft_reserve(start, size);
+       put_cxl_mock_ops(index);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(__wrap_region_intersects_soft_reserve);
+
+int __wrap_walk_hmem_resources(struct device *host, walk_hmem_fn fn)
+{
+       int index, rc = 0;
+       bool is_mock = strcmp(dev_name(host), "hmem_platform.1") == 0;
+       struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+       if (is_mock) {
+               if (ops)
+                       rc = ops->walk_hmem_resources(host, fn);
+       } else {
+               rc = walk_hmem_resources(host, fn);
+       }
+       put_cxl_mock_ops(index);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(__wrap_walk_hmem_resources);
+
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("cxl_test: emulation module");
 MODULE_IMPORT_NS("ACPI");
index 2684b89c8aa2dd2accc5448f75010ae9da6c000e..4f57dc80ae7d5f72c89a9d1a3c91d5ab66651dc2 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <linux/list.h>
 #include <linux/acpi.h>
+#include <linux/dax.h>
 #include <cxl.h>
 
 struct cxl_mock_ops {
@@ -27,8 +28,15 @@ struct cxl_mock_ops {
        int (*hmat_get_extended_linear_cache_size)(struct resource *backing_res,
                                                   int nid,
                                                   resource_size_t *cache_size);
+       int (*walk_hmem_resources)(struct device *host, walk_hmem_fn fn);
+       int (*region_intersects)(resource_size_t start, size_t size,
+                                unsigned long flags, unsigned long desc);
+       int (*region_intersects_soft_reserve)(resource_size_t start,
+                                             size_t size);
 };
 
+int hmem_test_init(void);
+void hmem_test_exit(void);
 void register_cxl_mock_ops(struct cxl_mock_ops *ops);
 void unregister_cxl_mock_ops(struct cxl_mock_ops *ops);
 struct cxl_mock_ops *get_cxl_mock_ops(int *index);