From: Davidlohr Bueso <dave@stgolabs.net>
To: dave.jiang@intel.com
Cc: jic23@kernel.org, alison.schofield@intel.com, icheng@nvidia.com,
ming.li@zohomail.com, benjamin.cheatham@amd.com,
alucerop@amd.com, dave@stgolabs.net, linux-cxl@vger.kernel.org
Subject: [PATCH v8 09/10] cxl/test: Add mock BI topology support
Date: Wed, 9 Sep 2026 10:03:01 -0700 [thread overview]
Message-ID: <20260909170302.1550680-10-dave@stgolabs.net> (raw)
In-Reply-To: <20260909170302.1550680-1-dave@stgolabs.net>
Extend the mock topology with an all-software Back-Invalidate path
covering both the type3 memdevs and the type2 accelerator.
Following the framework's convention of substituting software state
for register programming, cxl_bi_probe_capable() gains a --wrap shim
dispatching through cxl_mock_ops, and the mock decoder setup enables
BI in software in place of cxl_bi_setup(). The mock setup mirrors
cxl_bi_enable_path()'s walk - dport nr_bi accounting up to the root,
unwound by a devm action - and the capability check keeps the
VH-only rule.
A single HDM-DB window (BI | DEVMEM | VOLATILE, targeting host
bridge 0) is emitted in every topology mode and parses into a
CXL_DECODER_F_BI root decoder; DEVMEM satisfies can_create_ram().
In type2 mode it coexists with the accelerator's HDM-D window.
cxled_committed_bi() reads the BI bit from the HDM decoder
registers, so each mock port's cxl_hdm carries a page of plain
memory as that register block, maintained on decoder commit/reset
and restored on saved-decoder replay - letting a committed HDM-DB
region survive a cxl_acpi rebind. The accelerator grows to 1G of
capacity so the boot-time auto region leaves DPA for a BI region.
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
tools/testing/cxl/Kbuild | 1 +
tools/testing/cxl/test/accel.c | 2 +-
tools/testing/cxl/test/cxl.c | 159 ++++++++++++++++++++++++++++++++-
tools/testing/cxl/test/mock.c | 13 +++
tools/testing/cxl/test/mock.h | 1 +
5 files changed, 172 insertions(+), 4 deletions(-)
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 2be1df80fcc9..811fb43225b8 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -14,6 +14,7 @@ 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
+ldflags-y += --wrap=cxl_bi_probe_capable
DRIVERS := ../../../drivers
DAX_HMEM_SRC := $(DRIVERS)/dax/hmem
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 62bd92b3be45..8833124b4ec8 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -190,6 +190,10 @@ static struct {
struct acpi_cedt_cfmws cfmws;
u32 target[3];
} cfmws8;
+ struct {
+ struct acpi_cedt_cfmws cfmws;
+ u32 target[1];
+ } cfmws_bi;
struct {
struct acpi_cedt_cxims cxims;
u64 xormap_list[2];
@@ -373,6 +377,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 = {
@@ -534,6 +554,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;
}
@@ -569,12 +595,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++) {
@@ -736,10 +767,53 @@ 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 of plain memory stands in for the HDM decoder register
+ * block: cxled_committed_bi() reads the per-decoder BI bit from
+ * it, which mock_decoder_commit()/reset() maintain below. All
+ * other consumers of these registers are bypassed by the mocked
+ * decoder setup and commit paths.
+ */
+ cxlhdm->regs.hdm_decoder =
+ (void __iomem *)devm_get_free_pages(dev,
+ GFP_KERNEL | __GFP_ZERO, 0);
+ if (!cxlhdm->regs.hdm_decoder)
+ return ERR_PTR(-ENOMEM);
+
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;
+}
+
+static void mock_decoder_set_bi(struct cxl_decoder *cxld, bool bi)
+{
+ struct cxl_port *port = to_cxl_port(cxld->dev.parent);
+ struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
+ void __iomem *ctrl;
+ u32 val;
+
+ if (!is_endpoint_decoder(&cxld->dev) || !cxlhdm ||
+ !cxlhdm->regs.hdm_decoder)
+ return;
+
+ ctrl = cxlhdm->regs.hdm_decoder +
+ CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id);
+ val = readl(ctrl);
+ if (bi)
+ val |= CXL_HDM_DECODER0_CTRL_BI;
+ else
+ val &= ~CXL_HDM_DECODER0_CTRL_BI;
+ writel(val, ctrl);
+}
+
struct target_map_ctx {
u32 *target_map;
int index;
@@ -974,6 +1048,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;
@@ -1003,6 +1078,7 @@ static void mock_decoder_reset(struct cxl_decoder *cxld)
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);
@@ -1130,8 +1206,13 @@ 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;
@@ -1453,9 +1534,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)) {
@@ -1464,6 +1548,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);
}
@@ -1652,6 +1743,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 Decoder/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,
@@ -1670,6 +1822,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);
--
2.39.5
next prev parent reply other threads:[~2026-09-09 17:03 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 17:02 [PATCH v8 0/10] cxl: Support Back-Invalidate Davidlohr Bueso
2026-09-09 17:02 ` [PATCH v8 01/10] cxl: Add BI register probing and port initialization Davidlohr Bueso
2026-09-09 19:38 ` Jonathan Cameron
2026-09-09 17:02 ` [PATCH v8 02/10] cxl/pci: Add BI topology enable/disable Davidlohr Bueso
2026-09-09 21:21 ` Jonathan Cameron
2026-09-10 1:33 ` Davidlohr Bueso
2026-09-10 2:40 ` Li Ming
2026-09-15 20:17 ` Davidlohr Bueso
2026-09-16 1:28 ` Li Ming
2026-09-09 17:02 ` [PATCH v8 03/10] cxl/hdm: Add BI coherency support for endpoint decoders Davidlohr Bueso
2026-09-09 21:27 ` Jonathan Cameron
2026-09-09 17:02 ` [PATCH v8 04/10] cxl: Add HDM-DB region creation Davidlohr Bueso
2026-09-09 17:48 ` sashiko-bot
2026-09-16 23:27 ` Davidlohr Bueso
2026-09-09 21:31 ` Jonathan Cameron
2026-09-09 17:02 ` [PATCH v8 05/10] cxl/hdm: Rename decoder coherency flags Davidlohr Bueso
2026-09-09 21:32 ` Jonathan Cameron
2026-09-09 17:02 ` [PATCH v8 06/10] cxl/region: Log the coherency model at region creation Davidlohr Bueso
2026-09-09 21:32 ` Jonathan Cameron
2026-09-09 17:02 ` [PATCH v8 07/10] cxl/pci: Split BI capability probe from setup Davidlohr Bueso
2026-09-09 21:40 ` Jonathan Cameron
2026-09-09 21:45 ` Davidlohr Bueso
2026-09-09 17:03 ` [PATCH v8 08/10] cxl: Allow auto-committed BI hdm decoders Davidlohr Bueso
2026-09-09 21:45 ` Jonathan Cameron
2026-09-10 11:38 ` Li Ming
2026-09-16 18:10 ` Davidlohr Bueso
2026-09-09 17:03 ` Davidlohr Bueso [this message]
2026-09-09 21:49 ` [PATCH v8 09/10] cxl/test: Add mock BI topology support Jonathan Cameron
2026-09-09 22:08 ` Davidlohr Bueso
2026-09-09 17:03 ` [PATCH v8 10/10] cxl/doc: Update maturity map with BI support Davidlohr Bueso
2026-09-09 21:50 ` Jonathan Cameron
2026-09-09 18:47 ` [PATCH v8 0/10] cxl: Support Back-Invalidate Davidlohr Bueso
2026-09-18 16:39 ` Alison Schofield
2026-09-18 19:59 ` Davidlohr Bueso
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260909170302.1550680-10-dave@stgolabs.net \
--to=dave@stgolabs.net \
--cc=alison.schofield@intel.com \
--cc=alucerop@amd.com \
--cc=benjamin.cheatham@amd.com \
--cc=dave.jiang@intel.com \
--cc=icheng@nvidia.com \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=ming.li@zohomail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.