diff options
| author | Mark Brown <broonie@kernel.org> | 2026-11-13 17:23:45 +0000 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-11-13 17:23:45 +0000 |
| commit | 91e174e86ee7b4c636fea03752d43aa0ded8df4e (patch) | |
| tree | ac7af2d6e5ab445c4100a968ea9a4a4e4a42a4b3 /tools | |
| parent | 4507970404e9dde2a3cb6b799d36d44f03131879 (diff) | |
| parent | d4780f61df995bef6b7b97958b9f1856593740d7 (diff) | |
| download | linux-next-91e174e86ee7b4c636fea03752d43aa0ded8df4e.tar.gz linux-next-91e174e86ee7b4c636fea03752d43aa0ded8df4e.zip | |
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl.git
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/testing/cxl/Kbuild | 1 | ||||
| -rw-r--r-- | tools/testing/cxl/test/accel.c | 2 | ||||
| -rw-r--r-- | tools/testing/cxl/test/cxl.c | 278 | ||||
| -rw-r--r-- | tools/testing/cxl/test/mock.c | 13 | ||||
| -rw-r--r-- | tools/testing/cxl/test/mock.h | 1 |
5 files changed, 261 insertions, 34 deletions
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index e49ce644b4b1..39b4bb2e0404 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -14,6 +14,7 @@ wrap_cxl_core += --wrap=devm_cxl_add_dport_by_dev wrap_cxl_core += --wrap=devm_cxl_switch_port_decoders_setup wrap_cxl_core += --wrap=devm_cxl_endpoint_decoders_setup wrap_cxl_core += --wrap=cxl_endpoint_parse_cdat +wrap_cxl_core += --wrap=cxl_bi_probe_capable LDFLAGS_cxl_acpi.o += $(wrap_cxl_core) LDFLAGS_cxl_pmem.o += $(wrap_cxl_core) LDFLAGS_cxl_port.o += $(wrap_cxl_core) diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c index 8e6f4687ca02..f3800083ea27 100644 --- a/tools/testing/cxl/test/accel.c +++ b/tools/testing/cxl/test/accel.c @@ -31,7 +31,7 @@ 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); + rc = cxl_set_capacity(cxlds, SZ_1G); if (rc) return rc; diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 3eb1051b1ee8..4724297446de 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -185,13 +185,25 @@ static struct { } cfmws6; struct { struct acpi_cedt_cfmws cfmws; - u32 target[2]; + u32 target[1]; } cfmws7; struct { struct acpi_cedt_cfmws cfmws; - u32 target[3]; + u32 target[1]; } cfmws8; struct { + struct acpi_cedt_cfmws cfmws; + u32 target[2]; + } cfmws9; + struct { + struct acpi_cedt_cfmws cfmws; + u32 target[3]; + } cfmws10; + struct { + struct acpi_cedt_cfmws cfmws; + u32 target[1]; + } cfmws_bi; + struct { struct acpi_cedt_cxims cxims; u64 xormap_list[2]; } cxims0; @@ -325,13 +337,44 @@ static struct { }, .target = { 3 }, }, - /* .cfmws6,7,8 use ACPI_CEDT_CFMWS_ARITHMETIC_XOR */ .cfmws6 = { .cfmws = { .header = { .type = ACPI_CEDT_TYPE_CFMWS, .length = sizeof(mock_cedt.cfmws6), }, + .interleave_ways = 0, + .granularity = 4, + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | + ACPI_CEDT_CFMWS_RESTRICT_PMEM, + .qtg_id = FAKE_QTG_ID, + .window_size = SZ_256M * 4UL, + }, + .target = { 2 }, + }, + .cfmws7 = { + .cfmws = { + .header = { + .type = ACPI_CEDT_TYPE_CFMWS, + .length = sizeof(mock_cedt.cfmws7), + }, + .interleave_ways = 0, + .granularity = 4, + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | + ACPI_CEDT_CFMWS_RESTRICT_VOLATILE | + ACPI_CEDT_CFMWS_RESTRICT_PMEM, + .qtg_id = FAKE_QTG_ID, + .window_size = SZ_256M * 4UL, + }, + .target = { 1 }, + }, + /* .cfmws8,9,10 use ACPI_CEDT_CFMWS_ARITHMETIC_XOR */ + .cfmws8 = { + .cfmws = { + .header = { + .type = ACPI_CEDT_TYPE_CFMWS, + .length = sizeof(mock_cedt.cfmws8), + }, .interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR, .interleave_ways = 0, .granularity = 4, @@ -342,11 +385,11 @@ static struct { }, .target = { 0, }, }, - .cfmws7 = { + .cfmws9 = { .cfmws = { .header = { .type = ACPI_CEDT_TYPE_CFMWS, - .length = sizeof(mock_cedt.cfmws7), + .length = sizeof(mock_cedt.cfmws9), }, .interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR, .interleave_ways = 1, @@ -358,11 +401,11 @@ static struct { }, .target = { 0, 1, }, }, - .cfmws8 = { + .cfmws10 = { .cfmws = { .header = { .type = ACPI_CEDT_TYPE_CFMWS, - .length = sizeof(mock_cedt.cfmws8), + .length = sizeof(mock_cedt.cfmws10), }, .interleave_arithmetic = ACPI_CEDT_CFMWS_ARITHMETIC_XOR, .interleave_ways = 8, @@ -374,6 +417,22 @@ static struct { }, .target = { 0, 1, 2, }, }, + .cfmws_bi = { + .cfmws = { + .header = { + .type = ACPI_CEDT_TYPE_CFMWS, + .length = sizeof(mock_cedt.cfmws_bi), + }, + .interleave_ways = 0, + .granularity = 4, + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_BI | + ACPI_CEDT_CFMWS_RESTRICT_DEVMEM | + ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, + .qtg_id = FAKE_QTG_ID, + .window_size = SZ_256M * 4UL, + }, + .target = { 0 }, + }, .cxims0 = { .cxims = { .header = { @@ -407,23 +466,37 @@ struct acpi_cedt_cfmws *mock_cfmws[] = { [3] = &mock_cedt.cfmws3.cfmws, [4] = &mock_cedt.cfmws4.cfmws, [5] = &mock_cedt.cfmws5.cfmws, - /* Modulo Math above, XOR Math below */ [6] = &mock_cedt.cfmws6.cfmws, [7] = &mock_cedt.cfmws7.cfmws, + /* Modulo Math above, XOR Math below */ [8] = &mock_cedt.cfmws8.cfmws, + [9] = &mock_cedt.cfmws9.cfmws, + [10] = &mock_cedt.cfmws10.cfmws, }; static int cfmws_start; static int cfmws_end; #define CFMWS_MOD_ARRAY_START 0 -#define CFMWS_MOD_ARRAY_END 5 -#define CFMWS_XOR_ARRAY_START 6 -#define CFMWS_XOR_ARRAY_END 8 +#define CFMWS_MOD_ARRAY_END 7 +#define CFMWS_XOR_ARRAY_START 8 +#define CFMWS_XOR_ARRAY_END 10 struct acpi_cedt_cxims *mock_cxims[1] = { [0] = &mock_cedt.cxims0.cxims, }; +static struct acpi_cedt_cfmws *mock_auto_region_cfmws(void) +{ + for (int i = cfmws_start; i <= cfmws_end; i++) { + struct acpi_cedt_cfmws *window = mock_cfmws[i]; + + if (window->restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) + return window; + } + + return NULL; +} + struct cxl_mock_res { struct list_head list; struct range range; @@ -535,6 +608,12 @@ static int populate_cedt(void) window->base_hpa = res->range.start; } + res = alloc_mock_res(mock_cedt.cfmws_bi.cfmws.window_size, + max_t(int, SZ_256M, PMD_SIZE)); + if (!res) + return -ENOMEM; + mock_cedt.cfmws_bi.cfmws.base_hpa = res->range.start; + return 0; } @@ -570,12 +649,17 @@ static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id, handler_arg(h, arg, end); } - if (id == ACPI_CEDT_TYPE_CFMWS) + if (id == ACPI_CEDT_TYPE_CFMWS) { for (i = cfmws_start; i <= cfmws_end; i++) { h = (union acpi_subtable_headers *) mock_cfmws[i]; end = (unsigned long) h + mock_cfmws[i]->header.length; handler_arg(h, arg, end); } + /* one HDM-DB window in every topology */ + h = (union acpi_subtable_headers *)&mock_cedt.cfmws_bi.cfmws; + end = (unsigned long)h + mock_cedt.cfmws_bi.cfmws.header.length; + handler_arg(h, arg, end); + } if (id == ACPI_CEDT_TYPE_CXIMS) for (i = 0; i < ARRAY_SIZE(mock_cxims); i++) { @@ -737,10 +821,43 @@ static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port, cxlhdm->port = port; cxlhdm->interleave_mask = ~0U; cxlhdm->iw_cap_mask = ~0UL; + + /* a page stands in for the endpoint's BI Decoder block, Enable set */ + if (is_cxl_endpoint(port)) { + void __iomem *bi = (void __iomem *) + devm_get_free_pages(dev, GFP_KERNEL | __GFP_ZERO, 0); + + if (!bi) + return ERR_PTR(-ENOMEM); + writel(CXL_BI_DECODER_CTRL_BI_ENABLE, + bi + CXL_BI_DECODER_CTRL_OFFSET); + port->regs.bi_decoder = bi; + } + dev_set_drvdata(dev, cxlhdm); return cxlhdm; } +/* HPA-based, so replay after cxl_acpi rebind can re-derive it */ +static bool mock_hpa_is_bi(u64 hpa) +{ + struct acpi_cedt_cfmws *bi = &mock_cedt.cfmws_bi.cfmws; + + return hpa >= bi->base_hpa && hpa < bi->base_hpa + bi->window_size; +} + +/* committed BI state is a decoder flag, so no register to emulate */ +static void mock_decoder_set_bi(struct cxl_decoder *cxld, bool bi) +{ + if (!is_endpoint_decoder(&cxld->dev)) + return; + + if (bi) + cxld->flags |= CXL_DECODER_F_BI; + else + cxld->flags &= ~CXL_DECODER_F_BI; +} + struct target_map_ctx { u32 *target_map; int index; @@ -913,15 +1030,13 @@ static void __cxld_registry_save(struct cxl_test_decoder *td, 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) { + if ((cxld->flags & CXL_DECODER_F_ENABLE) && cxled->dpa_res) { + td->cxled.skip = cxled->skip; td->dpa_range.start = cxled->dpa_res->start; td->dpa_range.end = cxled->dpa_res->end; } else { + td->cxled.skip = 0; td->dpa_range.start = 0; td->dpa_range.end = -1; } @@ -974,6 +1089,7 @@ static int mock_decoder_commit(struct cxl_decoder *cxld) cxled->state = CXL_DECODER_STATE_AUTO; } + mock_decoder_set_bi(cxld, mock_hpa_is_bi(cxld->hpa_range.start)); cxld_registry_update(cxld); return 0; @@ -1001,8 +1117,8 @@ static void mock_decoder_reset(struct cxl_decoder *cxld) to_cxl_endpoint_decoder(&cxld->dev); cxled->state = CXL_DECODER_STATE_MANUAL; - cxled->skip = 0; } + mock_decoder_set_bi(cxld, false); if (decoder_reset_preserve_registry) dev_dbg(port->uport_dev, "decoder%d: skip registry update\n", cxld->id); @@ -1127,7 +1243,8 @@ enum cxld_init_type { 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 cxl_test_decoder **td, + struct acpi_cedt_cfmws **window) { struct cxl_test_decoder *found_td = cxld_registry_find(cxld); @@ -1137,6 +1254,7 @@ static enum cxld_init_type get_decoder_init_type(struct cxl_decoder *cxld, } *td = NULL; + *window = NULL; /* * The first decoder on the first 2 devices on the first switch @@ -1150,6 +1268,11 @@ static enum cxld_init_type get_decoder_init_type(struct cxl_decoder *cxld, pdev->id > 4 || cxld->id > (mock_zero_size_decoders ? 2 : 0)) return MOCK_DECODER_INIT_DEFAULT; + /* XOR windows are all pmem, no window to host the RAM region */ + *window = mock_auto_region_cfmws(); + if (!*window) + return MOCK_DECODER_INIT_DEFAULT; + return type2_test ? MOCK_DECODER_INIT_TYPE2_AUTO : MOCK_DECODER_INIT_TYPE3_AUTO; } @@ -1163,17 +1286,22 @@ static bool mock_decoder_handle_saved(struct cxl_decoder *cxld, struct cxl_test_ else enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE; - if (enabled) - return !cxld_registry_restore(cxld, td); + if (enabled) { + if (cxld_registry_restore(cxld, td)) + return false; + mock_decoder_set_bi(cxld, + mock_hpa_is_bi(cxld->hpa_range.start)); + return true; + } init_disabled_mock_decoder(cxld); return false; } static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled, - struct cxl_port *port) + struct cxl_port *port, + struct acpi_cedt_cfmws *window) { - struct acpi_cedt_cfmws *window = mock_cfmws[0]; struct cxl_decoder *cxld = &cxled->cxld; struct cxl_switch_decoder *cxlsd; struct cxl_dport *dport; @@ -1246,9 +1374,9 @@ static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled, static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled, struct cxl_port *port, struct platform_device *pdev, - bool hb0) + bool hb0, + struct acpi_cedt_cfmws *window) { - struct acpi_cedt_cfmws *window = mock_cfmws[0]; struct cxl_decoder *cxld = &cxled->cxld; struct cxl_switch_decoder *cxlsd; struct cxl_dport *dport; @@ -1399,6 +1527,7 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) { struct cxl_endpoint_decoder *cxled = NULL; struct platform_device *pdev = NULL; + struct acpi_cedt_cfmws *window; struct cxl_test_decoder *td; struct cxl_memdev *cxlmd; struct cxl_port *port; @@ -1427,7 +1556,7 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) port = to_cxl_port(cxld->dev.parent); } - switch (get_decoder_init_type(cxld, pdev, hb0, &td)) { + switch (get_decoder_init_type(cxld, pdev, hb0, &td, &window)) { case MOCK_DECODER_INIT_SAVED: if (WARN_ON(!td)) return false; @@ -1440,10 +1569,10 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) default_mock_decoder(cxld); return false; case MOCK_DECODER_INIT_TYPE3_AUTO: - mock_init_hdm_type3_cxled(cxled, port, pdev, hb0); + mock_init_hdm_type3_cxled(cxled, port, pdev, hb0, window); return false; case MOCK_DECODER_INIT_TYPE2_AUTO: - mock_init_hdm_type2_cxled(cxled, port); + mock_init_hdm_type2_cxled(cxled, port, window); return false; default: return false; @@ -1523,9 +1652,12 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, return 0; } +static int mock_cxl_bi_setup(struct cxl_port *endpoint); + static int __mock_cxl_decoders_setup(struct cxl_port *port) { struct cxl_hdm *cxlhdm; + int rc; cxlhdm = mock_cxl_setup_hdm(port, NULL); if (IS_ERR(cxlhdm)) { @@ -1534,6 +1666,13 @@ static int __mock_cxl_decoders_setup(struct cxl_port *port) return PTR_ERR(cxlhdm); } + /* as the real setup: BI between the HDM state and the decoders */ + if (is_cxl_endpoint(port)) { + rc = mock_cxl_bi_setup(port); + if (rc) + dev_dbg(&port->dev, "BI setup failed rc=%d\n", rc); + } + return mock_cxl_enumerate_decoders(cxlhdm, NULL); } @@ -1629,18 +1768,29 @@ static struct cxl_dport *mock_cxl_add_dport_by_dev(struct cxl_port *port, } /* - * Faking the cxl_dpa_perf for the memdev when appropriate. + * Derive mock performance data from the memdev id so tests can identify + * each memdev's contribution to region performance. + * + * latency = 100 + 10 * id (nanoseconds) + * bandwidth = 1000 + 100 * id (MB/s) + * + * Keep the values nonzero so the region access attributes are visible. + * The ndctl cxl-perf-data.sh test relies on these formulas. */ static void dpa_perf_setup(struct cxl_port *endpoint, struct range *range, struct cxl_dpa_perf *dpa_perf) { + struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev); + unsigned int bandwidth = 1000 + 100 * cxlmd->id; + unsigned int latency = 100 + 10 * cxlmd->id; + dpa_perf->qos_class = FAKE_QTG_ID; dpa_perf->dpa_range = *range; for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) { - dpa_perf->coord[i].read_latency = 500; - dpa_perf->coord[i].write_latency = 500; - dpa_perf->coord[i].read_bandwidth = 1000; - dpa_perf->coord[i].write_bandwidth = 1000; + dpa_perf->coord[i].read_latency = latency; + dpa_perf->coord[i].write_latency = latency; + dpa_perf->coord[i].read_bandwidth = bandwidth; + dpa_perf->coord[i].write_bandwidth = bandwidth; } } @@ -1722,6 +1872,67 @@ mock_region_intersects_soft_reserve(resource_size_t start, size_t size) return -1; } +/* + * All-software mirror of the BI enable path: no BI RT registers exist + * on mock devices, so capability and enablement are asserted directly + * while the dport nr_bi accounting - the part with driver-visible + * semantics (shared transit dports, teardown order) - follows the same + * walk the real cxl_bi_enable_path() takes. + */ +static void mock_cxl_bi_probe_capable(struct cxl_port *endpoint) +{ + struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + + /* BI is VH-only, mirroring cxl_bi_probe_capable() */ + cxlds->bi_capable = !cxlds->rcd; +} + +static void mock_cxl_bi_dealloc(void *data) +{ + struct cxl_port *endpoint = data; + struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_dport *dport_iter; + struct cxl_port *port_iter; + + cxlds->bi = false; + dport_iter = endpoint->parent_dport; + port_iter = dport_iter->port; + while (port_iter->parent_dport) { + scoped_guard(mutex, &port_iter->bi_lock) { + if (!WARN_ON_ONCE(dport_iter->nr_bi == 0)) + dport_iter->nr_bi--; + } + dport_iter = port_iter->parent_dport; + port_iter = dport_iter->port; + } +} + +static int mock_cxl_bi_setup(struct cxl_port *endpoint) +{ + struct cxl_memdev *cxlmd = to_cxl_memdev(endpoint->uport_dev); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_dport *dport_iter; + struct cxl_port *port_iter; + + if (!cxlds->bi_capable) + return 0; + + dport_iter = endpoint->parent_dport; + port_iter = dport_iter->port; + while (port_iter->parent_dport) { + scoped_guard(mutex, &port_iter->bi_lock) + dport_iter->nr_bi++; + dport_iter = port_iter->parent_dport; + port_iter = dport_iter->port; + } + cxlds->bi = true; + + return devm_add_action_or_reset(&endpoint->dev, mock_cxl_bi_dealloc, + endpoint); +} + static struct cxl_mock_ops cxl_mock_ops = { .is_mock_adev = is_mock_adev, .is_mock_bridge = is_mock_bridge, @@ -1740,6 +1951,7 @@ static struct cxl_mock_ops cxl_mock_ops = { .walk_hmem_resources = mock_walk_hmem_resources, .region_intersects = mock_region_intersects, .region_intersects_soft_reserve = mock_region_intersects_soft_reserve, + .cxl_bi_probe_capable = mock_cxl_bi_probe_capable, .list = LIST_HEAD_INIT(cxl_mock_ops.list), }; diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c index 6454b868b122..eabc9bba0fac 100644 --- a/tools/testing/cxl/test/mock.c +++ b/tools/testing/cxl/test/mock.c @@ -181,6 +181,19 @@ int __wrap_devm_cxl_endpoint_decoders_setup(struct cxl_port *port) } EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_endpoint_decoders_setup, "CXL"); +void __wrap_cxl_bi_probe_capable(struct cxl_port *endpoint) +{ + int index; + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); + + if (ops && ops->is_mock_port(endpoint->uport_dev)) + ops->cxl_bi_probe_capable(endpoint); + else + cxl_bi_probe_capable(endpoint); + put_cxl_mock_ops(index); +} +EXPORT_SYMBOL_NS_GPL(__wrap_cxl_bi_probe_capable, "CXL"); + int __wrap_cxl_await_media_ready(struct cxl_dev_state *cxlds) { int rc, index; diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h index 846d7c5d6eaa..c7919c3eb875 100644 --- a/tools/testing/cxl/test/mock.h +++ b/tools/testing/cxl/test/mock.h @@ -35,6 +35,7 @@ struct cxl_mock_ops { unsigned long flags, unsigned long desc); int (*region_intersects_soft_reserve)(resource_size_t start, size_t size); + void (*cxl_bi_probe_capable)(struct cxl_port *endpoint); }; int hmem_test_init(void); |
