From: Richard Cheng <icheng@nvidia.com>
To: dave@stgolabs.net, jic23@kernel.org, dave.jiang@intel.com,
alison.schofield@intel.com, vishal.l.verma@intel.com,
djbw@kernel.org, iweiny@kernel.org, danwilliams@nvidia.com
Cc: ming.li@zohomail.com, terry.bowman@amd.com, alucerop@amd.com,
linux-cxl@vger.kernel.org, linux-kernel@vger.kernel.org,
newtonl@nvidia.com, kristinc@nvidia.com, kaihengf@nvidia.com,
kobak@nvidia.com, mochs@nvidia.com,
Richard Cheng <icheng@nvidia.com>,
Vishal Aslot <vaslot@nvidia.com>
Subject: [PATCH v5 2/2] tools/testing/cxl: Enable zero sized decoders under hb0
Date: Tue, 23 Jun 2026 17:10:19 +0800 [thread overview]
Message-ID: <20260623091019.33417-3-icheng@nvidia.com> (raw)
In-Reply-To: <20260623091019.33417-1-icheng@nvidia.com>
The kernel now allows committed zero-size HDM decoders so BIOS can lock
empty decoders; cxl_test needs to exercise that path.
Add a mock_zero_size_decoders module parameter (default off). When set,
the special endpoints under host-bridge0 (cxl_mem.0 and cxl_mem.4)
commit decoders 1 and 2 as zero-size + locked above the decoder[0]
auto-region, mirrored on the parent switch and host bridge. The mocks
take a real zero-size DPA reservation, like enumeration of real
hardware, so commit_end lands on a zero-size decoder and the
reservation, poison-by-endpoint, and teardown paths all run.
Signed-off-by: Vishal Aslot <vaslot@nvidia.com>
Signed-off-by: Richard Cheng <icheng@nvidia.com>
---
v4->v5:
- Mirror the v5 core semantics: mock zero-size decoders take a real
zero-size DPA reservation via devm_cxl_dpa_reserve() so
dpa_res/hdm_end/cxled->part match real hardware enumeration and
the poison-by-endpoint walk reaches commit_end.
- Set cxled->state = CXL_DECODER_STATE_AUTO to match
init_hdm_decoder()'s fall-through (undoes the v2 switch to
MANUAL).
- cxld_registry_restore(): reserve unconditionally for enabled
endpoint decoders; enabled now implies reserved, sized or not.
v3->v4:
- No change.
v2->v3:
- Gate the zero-size + locked decoder injection behind a new
mock_zero_size_decoders module parameter (default off). v2
applied it unconditionally on the host-bridge0 auto-region
endpoints, which the region test suite reuses, regressing 7 of
17 cxl unit tests; defaulting off leaves the shared topology
untouched.
v1->v2:
- Replace second_decoder(), third_decoder() with a single
match_decoder_by_index() helper, so all lookups share one
matcher.
- Use DEFINE_RANGE() for the empty range instead of an open-coded
struct.
- Set cxled->state = CXL_DECODER_STATE_MANUAL rather than
STATE_AUTO.
- Set CXL_DECODER_F_LOCK on the mock zero-size decoders to model
the BIOS-burns-slots case.
---
tools/testing/cxl/test/cxl.c | 100 ++++++++++++++++++++++++++++++-----
1 file changed, 86 insertions(+), 14 deletions(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 418669927fb0..33cea0116a71 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 mock_zero_size_decoders;
#define FAKE_QTG_ID 42
@@ -841,14 +842,13 @@ static int cxld_registry_restore(struct cxl_decoder *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;
- }
+ /* enabled endpoint decoders hold a reservation, sized or not */
+ 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;
}
@@ -1041,16 +1041,49 @@ static void default_mock_decoder(struct cxl_decoder *cxld)
WARN_ON_ONCE(!cxld_registry_new(cxld));
}
-static int first_decoder(struct device *dev, const void *data)
+static int match_decoder_by_index(struct device *dev, const void *data)
{
+ int target_id = *(const int *)data;
struct cxl_decoder *cxld;
if (!is_switch_decoder(dev))
return 0;
cxld = to_cxl_decoder(dev);
- if (cxld->id == 0)
- return 1;
- return 0;
+ return cxld->id == target_id;
+}
+
+/*
+ * Mock a committed, locked, empty decoder
+ * (CXL r4.0 8.2.4.20.12). Gated by the mock_zero_size_decoders module
+ * param so the default cxl_test topology, shared by the region test
+ * suite, is left undisturbed.
+ */
+static void size_zero_mock_decoder_ep(struct cxl_decoder *cxld, u64 base)
+{
+ struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(&cxld->dev);
+
+ cxld->hpa_range = DEFINE_RANGE(base, base - 1);
+ cxld->interleave_ways = 2;
+ cxld->interleave_granularity = 4096;
+ cxld->target_type = CXL_DECODER_HOSTONLYMEM;
+ cxld->flags = CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK;
+ cxled->state = CXL_DECODER_STATE_AUTO;
+ /* decoder[0] reserved [0, size/2); empty decoders sit at that watermark */
+ devm_cxl_dpa_reserve(cxled, mock_auto_region_size / 2, 0, 0);
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+}
+
+static void size_zero_mock_decoder_sw(struct cxl_decoder *cxld, u64 base,
+ int level)
+{
+ cxld->flags = CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK;
+ cxld->target_type = CXL_DECODER_HOSTONLYMEM;
+ cxld->interleave_ways = level == 0 ? 2 : 1;
+ cxld->interleave_granularity = 4096;
+ cxld->hpa_range = DEFINE_RANGE(base, base - 1);
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
}
/*
@@ -1131,7 +1164,7 @@ 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) {
+ pdev->id > 4 || cxld->id > (mock_zero_size_decoders ? 2 : 0)) {
default_mock_decoder(cxld);
return false;
}
@@ -1145,6 +1178,20 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
base = window->base_hpa;
if (extended_linear_cache)
base += mock_auto_region_size;
+
+ /*
+ * With mock_zero_size_decoders, decoders 1 and 2 of the special
+ * endpoints mock committed, locked, empty decoders above the
+ * decoder[0] auto-region (CXL r4.0 8.2.4.20.12). commit_end then
+ * points at a zero-size decoder, exercising the zero-size
+ * reservation and poison-by-endpoint code paths.
+ */
+ if (cxld->id == 1 || cxld->id == 2) {
+ size_zero_mock_decoder_ep(cxld, base);
+ port->commit_end = cxld->id;
+ WARN_ON_ONCE(!cxld_registry_new(cxld));
+ return false;
+ }
cxld->hpa_range = (struct range) {
.start = base,
.end = base + mock_auto_region_size - 1,
@@ -1168,9 +1215,11 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
*/
iter = port;
for (i = 0; i < 2; i++) {
+ int id = 0;
+
dport = iter->parent_dport;
iter = dport->port;
- dev = device_find_child(&iter->dev, NULL, first_decoder);
+ dev = device_find_child(&iter->dev, &id, match_decoder_by_index);
/*
* Ancestor ports are guaranteed to be enumerated before
* @port, and all ports have at least one decoder.
@@ -1214,6 +1263,26 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxld_registry_update(cxld);
put_device(dev);
+
+ if (!mock_zero_size_decoders)
+ continue;
+
+ /*
+ * Mirror the endpoint: commit the next two switch decoders
+ * as zero-size + locked so the empty-decoder layout extends
+ * end-to-end through the switch and host bridge.
+ */
+ for (id = 1; id <= 2; id++) {
+ dev = device_find_child(&iter->dev, &id,
+ match_decoder_by_index);
+ if (WARN_ON(!dev))
+ continue;
+ cxld = to_cxl_decoder(dev);
+ size_zero_mock_decoder_sw(cxld, base, i);
+ iter->commit_end = id;
+ cxld_registry_update(cxld);
+ put_device(dev);
+ }
}
return false;
@@ -2049,6 +2118,9 @@ 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(mock_zero_size_decoders, bool, 0444);
+MODULE_PARM_DESC(mock_zero_size_decoders,
+ "Mock committed, locked, empty decoders under host-bridge0");
module_init(cxl_test_init);
module_exit(cxl_test_exit);
MODULE_LICENSE("GPL v2");
--
2.43.0
prev parent reply other threads:[~2026-06-23 9:10 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-23 9:10 [PATCH v5 0/2] Support zero-sized HDM decoders Richard Cheng
2026-06-23 9:10 ` [PATCH v5 1/2] cxl/hdm: Allow zero sized " Richard Cheng
2026-06-23 9:37 ` sashiko-bot
2026-06-23 19:55 ` Dan Williams (nvidia)
2026-06-23 20:13 ` Dan Williams (nvidia)
2026-06-23 9:10 ` Richard Cheng [this message]
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=20260623091019.33417-3-icheng@nvidia.com \
--to=icheng@nvidia.com \
--cc=alison.schofield@intel.com \
--cc=alucerop@amd.com \
--cc=danwilliams@nvidia.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=djbw@kernel.org \
--cc=iweiny@kernel.org \
--cc=jic23@kernel.org \
--cc=kaihengf@nvidia.com \
--cc=kobak@nvidia.com \
--cc=kristinc@nvidia.com \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ming.li@zohomail.com \
--cc=mochs@nvidia.com \
--cc=newtonl@nvidia.com \
--cc=terry.bowman@amd.com \
--cc=vaslot@nvidia.com \
--cc=vishal.l.verma@intel.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.