summaryrefslogtreecommitdiff
path: root/tools/testing/cxl/test/cxl.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/cxl/test/cxl.c')
-rw-r--r--tools/testing/cxl/test/cxl.c755
1 files changed, 629 insertions, 126 deletions
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index cc8948f49117..296516eecfd6 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -2,6 +2,7 @@
// Copyright(c) 2021 Intel Corporation. All rights reserved.
#include <linux/platform_device.h>
+#include <linux/memory_hotplug.h>
#include <linux/genalloc.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -14,6 +15,8 @@
#include "mock.h"
static int interleave_arithmetic;
+static bool extended_linear_cache;
+static bool fail_autoassemble;
#define FAKE_QTG_ID 42
@@ -25,6 +28,9 @@ static int interleave_arithmetic;
#define NR_CXL_PORT_DECODERS 8
#define NR_BRIDGES (NR_CXL_HOST_BRIDGES + NR_CXL_SINGLE_HOST + NR_CXL_RCH)
+#define MOCK_AUTO_REGION_SIZE_DEFAULT SZ_512M
+static int mock_auto_region_size = MOCK_AUTO_REGION_SIZE_DEFAULT;
+
static struct platform_device *cxl_acpi;
static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES];
#define NR_MULTI_ROOT (NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS)
@@ -46,6 +52,31 @@ struct platform_device *cxl_mem_single[NR_MEM_SINGLE];
static struct platform_device *cxl_rch[NR_CXL_RCH];
static struct platform_device *cxl_rcd[NR_CXL_RCH];
+/*
+ * Decoder registry
+ *
+ * Record decoder programming so that the topology can be reconstructed
+ * after cxl_acpi unbind/bind. This allows a user-created region config
+ * to be replayed as if firmware had provided the region at enumeration
+ * time.
+ *
+ * Entries are keyed by a stable port identity (port->uport_dev) combined
+ * with the decoder id. Decoder state is saved at initialization and
+ * updated on commit and reset.
+ *
+ * On re-enumeration mock_init_hdm_decoder() consults this registry to
+ * restore enabled decoders. Disabled decoders are reinitialized to a
+ * clean default state rather than replaying stale programming.
+ */
+static DEFINE_XARRAY(decoder_registry);
+
+/*
+ * When set, decoder reset will not update the registry. This allows
+ * region destroy operations to reset live decoders without erasing
+ * the saved programming needed for replay after re-enumeration.
+ */
+static bool decoder_reset_preserve_registry;
+
static inline bool is_multi_bridge(struct device *dev)
{
int i;
@@ -155,7 +186,7 @@ static struct {
} cfmws7;
struct {
struct acpi_cedt_cfmws cfmws;
- u32 target[4];
+ u32 target[3];
} cfmws8;
struct {
struct acpi_cedt_cxims cxims;
@@ -209,7 +240,7 @@ static struct {
},
.interleave_ways = 0,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 4UL,
@@ -224,7 +255,7 @@ static struct {
},
.interleave_ways = 1,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 8UL,
@@ -239,7 +270,7 @@ static struct {
},
.interleave_ways = 0,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_PMEM,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 4UL,
@@ -254,7 +285,7 @@ static struct {
},
.interleave_ways = 1,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_PMEM,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 8UL,
@@ -269,7 +300,7 @@ static struct {
},
.interleave_ways = 0,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_PMEM,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 4UL,
@@ -284,7 +315,7 @@ static struct {
},
.interleave_ways = 0,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M,
@@ -301,7 +332,7 @@ static struct {
.interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR,
.interleave_ways = 0,
.granularity = 4,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_PMEM,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 8UL,
@@ -317,7 +348,7 @@ static struct {
.interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR,
.interleave_ways = 1,
.granularity = 0,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_PMEM,
.qtg_id = FAKE_QTG_ID,
.window_size = SZ_256M * 8UL,
@@ -331,14 +362,14 @@ static struct {
.length = sizeof(mock_cedt.cfmws8),
},
.interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR,
- .interleave_ways = 2,
- .granularity = 0,
- .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
+ .interleave_ways = 8,
+ .granularity = 1,
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
ACPI_CEDT_CFMWS_RESTRICT_PMEM,
.qtg_id = FAKE_QTG_ID,
- .window_size = SZ_256M * 16UL,
+ .window_size = SZ_512M * 6UL,
},
- .target = { 0, 1, 0, 1, },
+ .target = { 0, 1, 2, },
},
.cxims0 = {
.cxims = {
@@ -425,6 +456,22 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align)
return res;
}
+/* Only update CFMWS0 as this is used by the auto region. */
+static void cfmws_elc_update(struct acpi_cedt_cfmws *window, int index)
+{
+ if (!extended_linear_cache)
+ return;
+
+ if (index != 0)
+ return;
+
+ /*
+ * The window size should be 2x of the CXL region size where half is
+ * DRAM and half is CXL
+ */
+ window->window_size = mock_auto_region_size * 2;
+}
+
static int populate_cedt(void)
{
struct cxl_mock_res *res;
@@ -449,6 +496,7 @@ static int populate_cedt(void)
for (i = cfmws_start; i <= cfmws_end; i++) {
struct acpi_cedt_cfmws *window = mock_cfmws[i];
+ cfmws_elc_update(window, i);
res = alloc_mock_res(window->window_size, SZ_256M);
if (!res)
return -ENOMEM;
@@ -590,6 +638,25 @@ mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname,
return AE_OK;
}
+static int
+mock_hmat_get_extended_linear_cache_size(struct resource *backing_res,
+ int nid, resource_size_t *cache_size)
+{
+ struct acpi_cedt_cfmws *window = mock_cfmws[0];
+ struct resource cfmws0_res =
+ DEFINE_RES_MEM(window->base_hpa, window->window_size);
+
+ if (!extended_linear_cache ||
+ !resource_contains(&cfmws0_res, backing_res)) {
+ return hmat_get_extended_linear_cache_size(backing_res,
+ nid, cache_size);
+ }
+
+ *cache_size = mock_auto_region_size;
+
+ return 0;
+}
+
static struct pci_bus mock_pci_bus[NR_BRIDGES];
static struct acpi_pci_root mock_pci_root[ARRAY_SIZE(mock_pci_bus)] = {
[0] = {
@@ -642,15 +709,8 @@ static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port,
return cxlhdm;
}
-static int mock_cxl_add_passthrough_decoder(struct cxl_port *port)
-{
- dev_err(&port->dev, "unexpected passthrough decoder for cxl_test\n");
- return -EOPNOTSUPP;
-}
-
-
struct target_map_ctx {
- int *target_map;
+ u32 *target_map;
int index;
int target_count;
};
@@ -670,6 +730,194 @@ static int map_targets(struct device *dev, void *data)
return 0;
}
+/*
+ * Build a stable registry key from the decoder's upstream port identity
+ * and decoder id.
+ *
+ * Decoder objects and cxl_port objects are reallocated on each enumeration,
+ * so their addresses cannot be used directly as replay keys. However,
+ * port->uport_dev is stable for a given topology across cxl_acpi unbind/bind
+ * in cxl_test, so use that as the port identity and pack the local decoder
+ * id into the low bits.
+ *
+ * The key is formed as:
+ * ((unsigned long)port->uport_dev << 4) | cxld->id
+ *
+ * The low bits hold the decoder id (which must fit in 4 bits) while
+ * the remaining bits identify the upstream port. This key is only used
+ * within cxl_test to locate saved decoder state during replay.
+ */
+static unsigned long cxld_registry_index(struct cxl_decoder *cxld)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+
+ dev_WARN_ONCE(&port->dev, cxld->id >= 16,
+ "decoder id:%d out of range\n", cxld->id);
+ return (((unsigned long)port->uport_dev) << 4) | cxld->id;
+}
+
+struct cxl_test_decoder {
+ union {
+ struct cxl_switch_decoder cxlsd;
+ struct cxl_endpoint_decoder cxled;
+ };
+ struct range dpa_range;
+};
+
+static struct cxl_test_decoder *cxld_registry_find(struct cxl_decoder *cxld)
+{
+ return xa_load(&decoder_registry, cxld_registry_index(cxld));
+}
+
+#define dbg_cxld(port, msg, cxld) \
+ do { \
+ struct cxl_decoder *___d = (cxld); \
+ dev_dbg((port)->uport_dev, \
+ "decoder%d: %s range: %#llx-%#llx iw: %d ig: %d flags: %#lx\n", \
+ ___d->id, msg, ___d->hpa_range.start, \
+ ___d->hpa_range.end + 1, ___d->interleave_ways, \
+ ___d->interleave_granularity, ___d->flags); \
+ } while (0)
+
+static int mock_decoder_commit(struct cxl_decoder *cxld);
+static void mock_decoder_reset(struct cxl_decoder *cxld);
+static void init_disabled_mock_decoder(struct cxl_decoder *cxld);
+
+static void cxld_copy(struct cxl_decoder *a, struct cxl_decoder *b)
+{
+ a->id = b->id;
+ a->hpa_range = b->hpa_range;
+ a->interleave_ways = b->interleave_ways;
+ a->interleave_granularity = b->interleave_granularity;
+ a->target_type = b->target_type;
+ a->flags = b->flags;
+ a->commit = mock_decoder_commit;
+ a->reset = mock_decoder_reset;
+}
+
+/*
+ * Restore decoder programming saved in the registry.
+ *
+ * Only decoders that were saved enabled are restored. Disabled decoders
+ * are left in their default inactive state so that stale programming is
+ * not resurrected after topology replay.
+ *
+ * For endpoint decoders this also restores the DPA reservation needed
+ * to reconstruct committed mappings.
+ */
+static int cxld_registry_restore(struct cxl_decoder *cxld,
+ struct cxl_test_decoder *td)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+ int rc;
+
+ if (is_switch_decoder(&cxld->dev)) {
+ struct cxl_switch_decoder *cxlsd =
+ to_cxl_switch_decoder(&cxld->dev);
+
+ if (!(td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE))
+ return 0;
+
+ dbg_cxld(port, "restore", &td->cxlsd.cxld);
+ cxld_copy(cxld, &td->cxlsd.cxld);
+ WARN_ON(cxlsd->nr_targets != td->cxlsd.nr_targets);
+
+ /* Restore saved target intent; live dport binding happens later */
+ for (int i = 0; i < cxlsd->nr_targets; i++) {
+ cxlsd->target[i] = NULL;
+ cxld->target_map[i] = td->cxlsd.cxld.target_map[i];
+ }
+
+ port->commit_end = cxld->id;
+
+ } else {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ if (!(td->cxled.cxld.flags & CXL_DECODER_F_ENABLE))
+ return 0;
+
+ dbg_cxld(port, "restore", &td->cxled.cxld);
+ cxld_copy(cxld, &td->cxled.cxld);
+ cxled->state = td->cxled.state;
+ cxled->skip = td->cxled.skip;
+ if (range_len(&td->dpa_range)) {
+ rc = devm_cxl_dpa_reserve(cxled, td->dpa_range.start,
+ range_len(&td->dpa_range),
+ td->cxled.skip);
+ if (rc) {
+ init_disabled_mock_decoder(cxld);
+ return rc;
+ }
+ }
+ port->commit_end = cxld->id;
+ }
+
+ return 0;
+}
+
+static void __cxld_registry_save(struct cxl_test_decoder *td,
+ struct cxl_decoder *cxld)
+{
+ if (is_switch_decoder(&cxld->dev)) {
+ struct cxl_switch_decoder *cxlsd =
+ to_cxl_switch_decoder(&cxld->dev);
+
+ cxld_copy(&td->cxlsd.cxld, cxld);
+ td->cxlsd.nr_targets = cxlsd->nr_targets;
+
+ /* Save target port_id as a stable identify for the dport */
+ for (int i = 0; i < cxlsd->nr_targets; i++) {
+ struct cxl_dport *dport;
+
+ if (!cxlsd->target[i])
+ continue;
+
+ dport = cxlsd->target[i];
+ td->cxlsd.cxld.target_map[i] = dport->port_id;
+ }
+ } else {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxld_copy(&td->cxled.cxld, cxld);
+ td->cxled.state = cxled->state;
+ td->cxled.skip = cxled->skip;
+
+ if (!(cxld->flags & CXL_DECODER_F_ENABLE)) {
+ td->dpa_range.start = 0;
+ td->dpa_range.end = -1;
+ } else if (cxled->dpa_res) {
+ td->dpa_range.start = cxled->dpa_res->start;
+ td->dpa_range.end = cxled->dpa_res->end;
+ } else {
+ td->dpa_range.start = 0;
+ td->dpa_range.end = -1;
+ }
+ }
+}
+
+static void cxld_registry_save(struct cxl_test_decoder *td,
+ struct cxl_decoder *cxld)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+
+ dbg_cxld(port, "save", cxld);
+ __cxld_registry_save(td, cxld);
+}
+
+static void cxld_registry_update(struct cxl_decoder *cxld)
+{
+ struct cxl_test_decoder *td = cxld_registry_find(cxld);
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+
+ if (WARN_ON_ONCE(!td))
+ return;
+
+ dbg_cxld(port, "update", cxld);
+ __cxld_registry_save(td, cxld);
+}
+
static int mock_decoder_commit(struct cxl_decoder *cxld)
{
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
@@ -689,6 +937,13 @@ static int mock_decoder_commit(struct cxl_decoder *cxld)
port->commit_end++;
cxld->flags |= CXL_DECODER_F_ENABLE;
+ if (is_endpoint_decoder(&cxld->dev)) {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxled->state = CXL_DECODER_STATE_AUTO;
+ }
+ cxld_registry_update(cxld);
return 0;
}
@@ -709,6 +964,65 @@ static void mock_decoder_reset(struct cxl_decoder *cxld)
"%s: out of order reset, expected decoder%d.%d\n",
dev_name(&cxld->dev), port->id, port->commit_end);
cxld->flags &= ~CXL_DECODER_F_ENABLE;
+
+ if (is_endpoint_decoder(&cxld->dev)) {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxled->state = CXL_DECODER_STATE_MANUAL;
+ cxled->skip = 0;
+ }
+ if (decoder_reset_preserve_registry)
+ dev_dbg(port->uport_dev, "decoder%d: skip registry update\n",
+ cxld->id);
+ else
+ cxld_registry_update(cxld);
+}
+
+static struct cxl_test_decoder *cxld_registry_new(struct cxl_decoder *cxld)
+{
+ struct cxl_test_decoder *td __free(kfree) =
+ kzalloc(sizeof(*td), GFP_KERNEL);
+ unsigned long key = cxld_registry_index(cxld);
+
+ if (!td)
+ return NULL;
+
+ if (xa_insert(&decoder_registry, key, td, GFP_KERNEL)) {
+ WARN_ON(1);
+ return NULL;
+ }
+
+ cxld_registry_save(td, cxld);
+ return no_free_ptr(td);
+}
+
+static void init_disabled_mock_decoder(struct cxl_decoder *cxld)
+{
+ cxld->hpa_range.start = 0;
+ cxld->hpa_range.end = -1;
+ cxld->interleave_ways = 1;
+ cxld->interleave_granularity = 0;
+ cxld->target_type = CXL_DECODER_HOSTONLYMEM;
+ cxld->flags = 0;
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ if (is_switch_decoder(&cxld->dev)) {
+ struct cxl_switch_decoder *cxlsd =
+ to_cxl_switch_decoder(&cxld->dev);
+
+ for (int i = 0; i < cxlsd->nr_targets; i++) {
+ cxlsd->target[i] = NULL;
+ cxld->target_map[i] = 0;
+ }
+ } else {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxled->state = CXL_DECODER_STATE_MANUAL;
+ cxled->skip = 0;
+ }
}
static void default_mock_decoder(struct cxl_decoder *cxld)
@@ -723,6 +1037,8 @@ static void default_mock_decoder(struct cxl_decoder *cxld)
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
cxld->commit = mock_decoder_commit;
cxld->reset = mock_decoder_reset;
+
+ WARN_ON_ONCE(!cxld_registry_new(cxld));
}
static int first_decoder(struct device *dev, const void *data)
@@ -737,14 +1053,29 @@ static int first_decoder(struct device *dev, const void *data)
return 0;
}
-static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
+/*
+ * 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 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;
- const int size = SZ_512M;
+ struct cxl_test_decoder *td;
struct cxl_memdev *cxlmd;
struct cxl_dport *dport;
struct device *dev;
@@ -771,6 +1102,24 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
port = NULL;
} while (port);
port = cxled_to_port(cxled);
+ } else {
+ port = to_cxl_port(cxld->dev.parent);
+ }
+
+ 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;
}
/*
@@ -781,15 +1130,24 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
*
* See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4'
*/
- if (!hb0 || pdev->id % 4 || pdev->id > 4 || cxld->id > 0) {
+ if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 ||
+ pdev->id > 4 || cxld->id > 0) {
default_mock_decoder(cxld);
- return;
+ return false;
+ }
+
+ /* Simulate missing cxl_mem.4 configuration */
+ if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) {
+ default_mock_decoder(cxld);
+ return false;
}
base = window->base_hpa;
+ if (extended_linear_cache)
+ base += mock_auto_region_size;
cxld->hpa_range = (struct range) {
.start = base,
- .end = base + size - 1,
+ .end = base + mock_auto_region_size - 1,
};
cxld->interleave_ways = 2;
@@ -798,10 +1156,12 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxld->flags = CXL_DECODER_F_ENABLE;
cxled->state = CXL_DECODER_STATE_AUTO;
port->commit_end = cxld->id;
- devm_cxl_dpa_reserve(cxled, 0, size / cxld->interleave_ways, 0);
+ 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 switch and root port decoders targeting @cxlmd.
@@ -817,15 +1177,21 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
*/
if (WARN_ON(!dev))
continue;
+
cxlsd = to_cxl_switch_decoder(dev);
if (i == 0) {
/* put cxl_mem.4 second in the decode order */
- if (pdev->id == 4)
+ if (pdev->id == 4) {
cxlsd->target[1] = dport;
- else
+ cxlsd->cxld.target_map[1] = dport->port_id;
+ } else {
cxlsd->target[0] = dport;
- } else
+ cxlsd->cxld.target_map[0] = dport->port_id;
+ }
+ } else {
cxlsd->target[0] = dport;
+ cxlsd->cxld.target_map[0] = dport->port_id;
+ }
cxld = &cxlsd->cxld;
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
cxld->flags = CXL_DECODER_F_ENABLE;
@@ -841,10 +1207,16 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxld->interleave_granularity = 4096;
cxld->hpa_range = (struct range) {
.start = base,
- .end = base + size - 1,
+ .end = base + mock_auto_region_size - 1,
};
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ cxld_registry_update(cxld);
put_device(dev);
}
+
+ return false;
}
static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
@@ -853,6 +1225,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
struct cxl_port *port = cxlhdm->port;
struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
int target_count, i;
+ bool restored;
if (is_cxl_endpoint(port))
target_count = 0;
@@ -862,9 +1235,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
target_count = NR_CXL_SWITCH_PORTS;
for (i = 0; i < NR_CXL_PORT_DECODERS; i++) {
- int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
struct target_map_ctx ctx = {
- .target_map = target_map,
.target_count = target_count,
};
struct cxl_decoder *cxld;
@@ -893,9 +1264,9 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
cxld = &cxled->cxld;
}
- mock_init_hdm_decoder(cxld);
-
- if (target_count) {
+ ctx.target_map = cxld->target_map;
+ restored = mock_init_hdm_decoder(cxld);
+ if (target_count && !restored) {
rc = device_for_each_child(port->uport_dev, &ctx,
map_targets);
if (rc) {
@@ -904,7 +1275,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
}
}
- rc = cxl_decoder_add_locked(cxld, target_map);
+ rc = cxl_decoder_add_locked(cxld);
if (rc) {
put_device(&cxld->dev);
dev_err(&port->dev, "Failed to add decoder\n");
@@ -920,10 +1291,42 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
return 0;
}
-static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
+static int __mock_cxl_decoders_setup(struct cxl_port *port)
+{
+ struct cxl_hdm *cxlhdm;
+
+ cxlhdm = mock_cxl_setup_hdm(port, NULL);
+ if (IS_ERR(cxlhdm)) {
+ if (PTR_ERR(cxlhdm) != -ENODEV)
+ dev_err(&port->dev, "Failed to map HDM decoder capability\n");
+ return PTR_ERR(cxlhdm);
+ }
+
+ return mock_cxl_enumerate_decoders(cxlhdm, NULL);
+}
+
+static int mock_cxl_switch_port_decoders_setup(struct cxl_port *port)
+{
+ if (is_cxl_root(port) || is_cxl_endpoint(port))
+ return -EOPNOTSUPP;
+
+ return __mock_cxl_decoders_setup(port);
+}
+
+static int mock_cxl_endpoint_decoders_setup(struct cxl_port *port)
+{
+ if (!is_cxl_endpoint(port))
+ return -EOPNOTSUPP;
+
+ return __mock_cxl_decoders_setup(port);
+}
+
+static int get_port_array(struct cxl_port *port,
+ struct platform_device ***port_array,
+ int *port_array_size)
{
struct platform_device **array;
- int i, array_size;
+ int array_size;
if (port->depth == 1) {
if (is_multi_bridge(port->uport_dev)) {
@@ -957,9 +1360,24 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
return -ENXIO;
}
+ *port_array = array;
+ *port_array_size = array_size;
+
+ return 0;
+}
+
+static struct cxl_dport *mock_cxl_add_dport_by_dev(struct cxl_port *port,
+ struct device *dport_dev)
+{
+ struct platform_device **array;
+ int rc, i, array_size;
+
+ rc = get_port_array(port, &array, &array_size);
+ if (rc)
+ return ERR_PTR(rc);
+
for (i = 0; i < array_size; i++) {
struct platform_device *pdev = array[i];
- struct cxl_dport *dport;
if (pdev->dev.parent != port->uport_dev) {
dev_dbg(&port->dev, "%s: mismatch parent %s\n",
@@ -968,14 +1386,14 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
continue;
}
- dport = devm_cxl_add_dport(port, &pdev->dev, pdev->id,
- CXL_RESOURCE_NONE);
+ if (&pdev->dev != dport_dev)
+ continue;
- if (IS_ERR(dport))
- return PTR_ERR(dport);
+ return devm_cxl_add_dport(port, &pdev->dev, pdev->id,
+ CXL_RESOURCE_NONE);
}
- return 0;
+ return ERR_PTR(-ENODEV);
}
/*
@@ -1000,25 +1418,21 @@ static void mock_cxl_endpoint_parse_cdat(struct cxl_port *port)
find_cxl_root(port);
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
- struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
struct access_coordinate ep_c[ACCESS_COORDINATE_MAX];
- struct range pmem_range = {
- .start = cxlds->pmem_res.start,
- .end = cxlds->pmem_res.end,
- };
- struct range ram_range = {
- .start = cxlds->ram_res.start,
- .end = cxlds->ram_res.end,
- };
if (!cxl_root)
return;
- if (range_len(&ram_range))
- dpa_perf_setup(port, &ram_range, &mds->ram_perf);
+ for (int i = 0; i < cxlds->nr_partitions; i++) {
+ struct resource *res = &cxlds->part[i].res;
+ struct cxl_dpa_perf *perf = &cxlds->part[i].perf;
+ struct range range = {
+ .start = res->start,
+ .end = res->end,
+ };
- if (range_len(&pmem_range))
- dpa_perf_setup(port, &pmem_range, &mds->pmem_perf);
+ dpa_perf_setup(port, &range, perf);
+ }
cxl_memdev_update_perf(cxlmd);
@@ -1029,6 +1443,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,
@@ -1038,11 +1499,15 @@ static struct cxl_mock_ops cxl_mock_ops = {
.acpi_table_parse_cedt = mock_acpi_table_parse_cedt,
.acpi_evaluate_integer = mock_acpi_evaluate_integer,
.acpi_pci_find_root = mock_acpi_pci_find_root,
- .devm_cxl_port_enumerate_dports = mock_cxl_port_enumerate_dports,
- .devm_cxl_setup_hdm = mock_cxl_setup_hdm,
- .devm_cxl_add_passthrough_decoder = mock_cxl_add_passthrough_decoder,
- .devm_cxl_enumerate_decoders = mock_cxl_enumerate_decoders,
+ .devm_cxl_switch_port_decoders_setup = mock_cxl_switch_port_decoders_setup,
+ .devm_cxl_endpoint_decoders_setup = mock_cxl_endpoint_decoders_setup,
.cxl_endpoint_parse_cdat = mock_cxl_endpoint_parse_cdat,
+ .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),
};
@@ -1058,6 +1523,23 @@ static void mock_companion(struct acpi_device *adev, struct device *dev)
#define SZ_64G (SZ_32G * 2)
#endif
+static int cxl_mock_platform_device_add(struct platform_device *pdev,
+ struct platform_device **ppdev)
+{
+ int rc;
+
+ if (ppdev)
+ *ppdev = pdev;
+ rc = platform_device_add(pdev);
+ if (rc) {
+ platform_device_put(pdev);
+ if (ppdev)
+ *ppdev = NULL;
+ }
+
+ return rc;
+}
+
static __init int cxl_rch_topo_init(void)
{
int rc, i;
@@ -1072,13 +1554,10 @@ static __init int cxl_rch_topo_init(void)
goto err_bridge;
mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_rch[i]);
+ if (rc)
goto err_bridge;
- }
- cxl_rch[i] = pdev;
mock_pci_bus[idx].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"firmware_node");
@@ -1130,13 +1609,10 @@ static __init int cxl_single_topo_init(void)
goto err_bridge;
mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_hb_single[i]);
+ if (rc)
goto err_bridge;
- }
- cxl_hb_single[i] = pdev;
mock_pci_bus[i + NR_CXL_HOST_BRIDGES].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"physical_node");
@@ -1155,12 +1631,9 @@ static __init int cxl_single_topo_init(void)
goto err_port;
pdev->dev.parent = &bridge->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_single[i]);
+ if (rc)
goto err_port;
- }
- cxl_root_single[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_swu_single); i++) {
@@ -1173,12 +1646,9 @@ static __init int cxl_single_topo_init(void)
goto err_uport;
pdev->dev.parent = &root_port->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_swu_single[i]);
+ if (rc)
goto err_uport;
- }
- cxl_swu_single[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_swd_single); i++) {
@@ -1192,12 +1662,9 @@ static __init int cxl_single_topo_init(void)
goto err_dport;
pdev->dev.parent = &uport->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_swd_single[i]);
+ if (rc)
goto err_dport;
- }
- cxl_swd_single[i] = pdev;
}
return 0;
@@ -1270,12 +1737,9 @@ static int cxl_mem_init(void)
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem[i]);
+ if (rc)
goto err_mem;
- }
- cxl_mem[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++) {
@@ -1288,12 +1752,9 @@ static int cxl_mem_init(void)
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem_single[i]);
+ if (rc)
goto err_single;
- }
- cxl_mem_single[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++) {
@@ -1307,12 +1768,9 @@ static int cxl_mem_init(void)
pdev->dev.parent = &rch->dev;
set_dev_node(&pdev->dev, i % 2);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_rcd[i]);
+ if (rc)
goto err_rcd;
- }
- cxl_rcd[i] = pdev;
}
return 0;
@@ -1329,9 +1787,37 @@ err_mem:
return rc;
}
+static ssize_t
+decoder_reset_preserve_registry_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", decoder_reset_preserve_registry);
+}
+
+static ssize_t
+decoder_reset_preserve_registry_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+
+ rc = kstrtobool(buf, &decoder_reset_preserve_registry);
+ if (rc)
+ return rc;
+ return count;
+}
+
+static DEVICE_ATTR_RW(decoder_reset_preserve_registry);
+
+static struct attribute *cxl_acpi_attrs[] = {
+ &dev_attr_decoder_reset_preserve_registry.attr, NULL
+};
+ATTRIBUTE_GROUPS(cxl_acpi);
+
static __init int cxl_test_init(void)
{
int rc, i;
+ struct range mappable;
cxl_acpi_test();
cxl_core_test();
@@ -1346,8 +1832,11 @@ static __init int cxl_test_init(void)
rc = -ENOMEM;
goto err_gen_pool_create;
}
+ mappable = mhp_get_pluggable_range(true);
- rc = gen_pool_add(cxl_mock_pool, iomem_resource.end + 1 - SZ_64G,
+ rc = gen_pool_add(cxl_mock_pool,
+ min(iomem_resource.end + 1 - SZ_64G,
+ mappable.end + 1 - SZ_64G),
SZ_64G, NUMA_NO_NODE);
if (rc)
goto err_gen_pool_add;
@@ -1373,13 +1862,10 @@ static __init int cxl_test_init(void)
goto err_bridge;
mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]);
+ if (rc)
goto err_bridge;
- }
- cxl_host_bridge[i] = pdev;
mock_pci_bus[i].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"physical_node");
@@ -1397,12 +1883,9 @@ static __init int cxl_test_init(void)
goto err_port;
pdev->dev.parent = &bridge->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]);
+ if (rc)
goto err_port;
- }
- cxl_root_port[i] = pdev;
}
BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
@@ -1415,12 +1898,9 @@ static __init int cxl_test_init(void)
goto err_uport;
pdev->dev.parent = &root_port->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]);
+ if (rc)
goto err_uport;
- }
- cxl_switch_uport[i] = pdev;
}
for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
@@ -1433,12 +1913,9 @@ static __init int cxl_test_init(void)
goto err_dport;
pdev->dev.parent = &uport->dev;
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]);
+ if (rc)
goto err_dport;
- }
- cxl_switch_dport[i] = pdev;
}
rc = cxl_single_topo_init();
@@ -1455,17 +1932,24 @@ static __init int cxl_test_init(void)
mock_companion(&acpi0017_mock, &cxl_acpi->dev);
acpi0017_mock.dev.bus = &platform_bus_type;
+ cxl_acpi->dev.groups = cxl_acpi_groups;
- rc = platform_device_add(cxl_acpi);
+ rc = cxl_mock_platform_device_add(cxl_acpi, NULL);
if (rc)
- goto err_root;
+ goto err_rch;
rc = cxl_mem_init();
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:
@@ -1499,10 +1983,22 @@ err_gen_pool_create:
return rc;
}
+static void free_decoder_registry(void)
+{
+ unsigned long index;
+ void *entry;
+
+ xa_for_each(&decoder_registry, index, entry) {
+ xa_erase(&decoder_registry, index);
+ kfree(entry);
+ }
+}
+
static __exit void cxl_test_exit(void)
{
int i;
+ hmem_test_exit();
cxl_mem_exit();
platform_device_unregister(cxl_acpi);
cxl_rch_topo_exit();
@@ -1524,12 +2020,19 @@ static __exit void cxl_test_exit(void)
depopulate_all_mock_resources();
gen_pool_destroy(cxl_mock_pool);
unregister_cxl_mock_ops(&cxl_mock_ops);
+ free_decoder_registry();
+ xa_destroy(&decoder_registry);
}
module_param(interleave_arithmetic, int, 0444);
MODULE_PARM_DESC(interleave_arithmetic, "Modulo:0, XOR:1");
+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_init(cxl_test_init);
module_exit(cxl_test_exit);
MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("cxl_test: setup module");
MODULE_IMPORT_NS("ACPI");
MODULE_IMPORT_NS("CXL");