* [PATCH v10 1/3] cxl/region: Simplify poison_by_decoder() error handling
2026-09-14 9:08 [PATCH v10 0/3] Support zero-sized HDM decoders Richard Cheng
@ 2026-09-14 9:08 ` Richard Cheng
2026-09-14 9:08 ` [PATCH v10 2/3] cxl/hdm: Allow zero sized HDM decoders Richard Cheng
2026-09-14 9:08 ` [PATCH v10 3/3] tools/testing/cxl: Enable zero sized decoders under hb0 Richard Cheng
2 siblings, 0 replies; 4+ messages in thread
From: Richard Cheng @ 2026-09-14 9:08 UTC (permalink / raw)
To: jic23, dave, dave.jiang, vishal.l.verma, alison.schofield
Cc: iweiny, ming.li, kaihengf, kobak, vaslot, newtonl, mochs,
kristinc, linux-cxl, linux-kernel, Richard Cheng,
Jonathan Cameron
"rc" carries both an error code and the loop control
signal for device_for_each_child(), so returning it bare is misleading,
the early guards mean "keep walking", not "no error". Zeroing "rc" to
forgive an -EFAULT on a RAM partition adds to that by discarding what
the device actually returned.
Return a literal 0 where the walk should continue, and test the
forgiven case directly instead of rewriting "rc". Give that test a
name, poison_efault_forgiven(), so cxl_get_poison_unmapped() and
poison_by_decoder() spell the same rule the same way. No functional
change.
Suggested-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Richard Cheng <icheng@nvidia.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/region.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 4f6069451fed..677ebec8f48d 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2903,6 +2903,16 @@ struct cxl_poison_context {
u64 offset;
};
+/*
+ * A device may answer a Get Poison List request with "physical address
+ * specified is invalid" (-EFAULT). That answer is tolerated for a RAM
+ * partition and the poison walk continues.
+ */
+static inline bool poison_efault_forgiven(int rc, enum cxl_partition_mode mode)
+{
+ return rc == -EFAULT && mode == CXL_PARTMODE_RAM;
+}
+
static int cxl_get_poison_unmapped(struct cxl_memdev *cxlmd,
struct cxl_poison_context *ctx)
{
@@ -2931,7 +2941,7 @@ static int cxl_get_poison_unmapped(struct cxl_memdev *cxlmd,
if (!length)
break;
rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
- if (rc == -EFAULT && cxlds->part[i].mode == CXL_PARTMODE_RAM)
+ if (poison_efault_forgiven(rc, cxlds->part[i].mode))
continue;
if (rc)
break;
@@ -2948,14 +2958,14 @@ static int poison_by_decoder(struct device *dev, void *arg)
struct cxl_dev_state *cxlds;
struct cxl_memdev *cxlmd;
u64 offset, length;
- int rc = 0;
+ int rc;
if (!is_endpoint_decoder(dev))
- return rc;
+ return 0;
cxled = to_cxl_endpoint_decoder(dev);
if (!cxled->dpa_res)
- return rc;
+ return 0;
cxlmd = cxled_to_memdev(cxled);
cxlds = cxlmd->cxlds;
@@ -2965,18 +2975,14 @@ static int poison_by_decoder(struct device *dev, void *arg)
offset = cxled->dpa_res->start - cxled->skip;
length = cxled->skip;
rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
- if (rc == -EFAULT && mode == CXL_PARTMODE_RAM)
- rc = 0;
- if (rc)
+ if (rc && !poison_efault_forgiven(rc, mode))
return rc;
}
offset = cxled->dpa_res->start;
length = cxled->dpa_res->end - offset + 1;
rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
- if (rc == -EFAULT && mode == CXL_PARTMODE_RAM)
- rc = 0;
- if (rc)
+ if (rc && !poison_efault_forgiven(rc, mode))
return rc;
/* Iterate until commit_end is reached */
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v10 2/3] cxl/hdm: Allow zero sized HDM decoders
2026-09-14 9:08 [PATCH v10 0/3] Support zero-sized HDM decoders Richard Cheng
2026-09-14 9:08 ` [PATCH v10 1/3] cxl/region: Simplify poison_by_decoder() error handling Richard Cheng
@ 2026-09-14 9:08 ` Richard Cheng
2026-09-14 9:08 ` [PATCH v10 3/3] tools/testing/cxl: Enable zero sized decoders under hb0 Richard Cheng
2 siblings, 0 replies; 4+ messages in thread
From: Richard Cheng @ 2026-09-14 9:08 UTC (permalink / raw)
To: jic23, dave, dave.jiang, vishal.l.verma, alison.schofield
Cc: iweiny, ming.li, kaihengf, kobak, vaslot, newtonl, mochs,
kristinc, linux-cxl, linux-kernel, Richard Cheng, Dan Williams,
Jonathan Cameron
CXL r4.0 §8.2.4.20.12 ("Committing Decoder Programming") and §14.13.10
("CXL HDM Decoder Zero Size Commit") permit committing an HDM decoder
with size 0. BIOS may commit and lock such decoders so the OS cannot
program regions through them, this is a design choice rather than a spec
requirement.
The kernel rejected these with -ENXIO during port enumeration and
aborted the whole port, so affected systems showed nothing under "cxl
list".
Treat empty decoders as first class reservations. Back them with a
separately allocated resource, since the resource tree cannot represent
an empty range, and keep the skip and hdm_end accounting intact. Exclude
empty decoders from region assembly and avoid zero-length poison queries.
Suggested-by: Dan Williams <djbw@kernel.org>
Signed-off-by: Vishal Aslot <vaslot@nvidia.com>
Signed-off-by: Richard Cheng <icheng@nvidia.com>
Reviewed-by: Dan Williams <djbw@kernel.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
Changelog:
v9 -> v10:
- Preserve -ENOMEM when allocation of the standalone zero-sized resource
fails.
- Retain the !cxled->dpa_res guard in cxl_dpa_free().
- Reconstruct the commit message
Best regards,
Richard Cheng
---
drivers/cxl/core/hdm.c | 58 +++++++++++++++++++++++++++------------
drivers/cxl/core/mbox.c | 3 ++
drivers/cxl/core/region.c | 45 ++++++++++++++++++++----------
drivers/cxl/cxl.h | 10 +++++++
drivers/cxl/port.c | 3 ++
5 files changed, 86 insertions(+), 33 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0c80b76a5f9b..39fe283cbc74 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -240,6 +240,18 @@ static resource_size_t __adjust_skip(struct cxl_dev_state *cxlds,
}
#define release_skip(c, b, l) __adjust_skip((c), (b), (l), NULL)
+static void cxl_dpa_release_region(struct resource *parent,
+ struct resource *res)
+{
+ /* zero sized decoders are not tracked in the resource tree */
+ if (resource_size(res) == 0) {
+ kfree(res);
+ return;
+ }
+
+ __release_region(parent, res->start, resource_size(res));
+}
+
/*
* Must be called in a context that synchronizes against this decoder's
* port ->remove() callback (like an endpoint decoder sysfs attribute)
@@ -256,7 +268,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
/* save @skip_start, before @res is released */
skip_start = res->start - cxled->skip;
- __release_region(&cxlds->dpa_res, res->start, resource_size(res));
+ cxl_dpa_release_region(&cxlds->dpa_res, res);
if (cxled->skip)
release_skip(cxlds, skip_start, cxled->skip);
cxled->skip = 0;
@@ -336,6 +348,27 @@ static int request_skip(struct cxl_dev_state *cxlds,
return -EBUSY;
}
+static struct resource *cxl_dpa_request_region(struct resource *parent,
+ resource_size_t start,
+ resource_size_t n,
+ const char *name)
+{
+ struct resource *res;
+
+ if (!n) {
+ res = kmalloc_obj(*res);
+ if (!res)
+ return ERR_PTR(-ENOMEM);
+
+ *res = DEFINE_RES_NAMED(start, 0, name, IORESOURCE_MEM);
+
+ return res;
+ }
+
+ res = __request_region(parent, start, n, name, 0);
+ return res ?: ERR_PTR(-EBUSY);
+}
+
static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
resource_size_t base, resource_size_t len,
resource_size_t skipped)
@@ -349,12 +382,6 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
lockdep_assert_held_write(&cxl_rwsem.dpa);
- if (!len) {
- dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
- port->id, cxled->cxld.id);
- return -EINVAL;
- }
-
if (cxled->dpa_res) {
dev_dbg(dev, "decoder%d.%d: existing allocation %pr assigned\n",
port->id, cxled->cxld.id, cxled->dpa_res);
@@ -378,14 +405,14 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
if (rc)
return rc;
}
- res = __request_region(&cxlds->dpa_res, base, len,
- dev_name(&cxled->cxld.dev), 0);
- if (!res) {
+ res = cxl_dpa_request_region(&cxlds->dpa_res, base, len,
+ dev_name(&cxled->cxld.dev));
+ if (IS_ERR(res)) {
dev_dbg(dev, "decoder%d.%d: failed to reserve allocation\n",
port->id, cxled->cxld.id);
if (skipped)
release_skip(cxlds, base - skipped, skipped);
- return -EBUSY;
+ return PTR_ERR(res);
}
cxled->dpa_res = res;
cxled->skip = skipped;
@@ -402,7 +429,8 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
break;
}
- if (cxled->part < 0)
+ /* Empty decoders may not be contained by a partition boundary */
+ if (cxled->part < 0 && resource_size(res))
dev_warn(dev, "decoder%d.%d: %pr does not map any partition\n",
port->id, cxled->cxld.id, res);
@@ -1031,12 +1059,6 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
return -ENXIO;
}
- if (size == 0) {
- dev_warn(&port->dev,
- "decoder%d.%d: Committed with zero size\n",
- port->id, cxld->id);
- return -ENXIO;
- }
port->commit_end = cxld->id;
} else {
if (cxled) {
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 55828a836c01..1a2553332801 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1386,6 +1386,9 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
int nr_records = 0;
int rc;
+ if (!len)
+ return 0;
+
ACQUIRE(mutex_intr, lock)(&mds->poison.mutex);
if ((rc = ACQUIRE_ERR(mutex_intr, &lock)))
return rc;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 677ebec8f48d..f54acbf68e84 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2113,7 +2113,7 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return -ENXIO;
}
- if (!cxled->dpa_res) {
+ if (cxled_empty(cxled)) {
dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n",
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
return -ENXIO;
@@ -2967,24 +2967,31 @@ static int poison_by_decoder(struct device *dev, void *arg)
if (!cxled->dpa_res)
return 0;
- cxlmd = cxled_to_memdev(cxled);
- cxlds = cxlmd->cxlds;
- mode = cxlds->part[cxled->part].mode;
+ /*
+ * Handle the degenerate case of a device with only empty decoders. An
+ * empty decoder can still map a non-zero skip range, so advance the
+ * walk to commit_end either way.
+ */
+ if (cxled->part >= 0) {
+ cxlmd = cxled_to_memdev(cxled);
+ cxlds = cxlmd->cxlds;
+ mode = cxlds->part[cxled->part].mode;
- if (cxled->skip) {
- offset = cxled->dpa_res->start - cxled->skip;
- length = cxled->skip;
- rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
+ if (cxled->skip) {
+ offset = cxled->dpa_res->start - cxled->skip;
+ length = cxled->skip;
+ rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
+ if (rc && !poison_efault_forgiven(rc, mode))
+ return rc;
+ }
+
+ offset = cxled->dpa_res->start;
+ length = cxled->dpa_res->end - offset + 1;
+ rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
if (rc && !poison_efault_forgiven(rc, mode))
return rc;
}
- offset = cxled->dpa_res->start;
- length = cxled->dpa_res->end - offset + 1;
- rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
- if (rc && !poison_efault_forgiven(rc, mode))
- return rc;
-
/* Iterate until commit_end is reached */
if (cxled->cxld.id == ctx->port->commit_end) {
ctx->offset = cxled->dpa_res->end + 1;
@@ -3006,9 +3013,17 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
};
rc = device_for_each_child(&port->dev, &ctx, poison_by_decoder);
- if (rc == 1)
+ if (rc == 1) {
+ /*
+ * No decoder with a sized DPA reservation was walked
+ * (every committed decoder is zero-size): scan all
+ * partitions in full.
+ */
+ if (ctx.part < 0)
+ ctx.part = 0;
rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport_dev),
&ctx);
+ }
return rc;
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index cab8ce39f465..3ef0810ab86b 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -336,6 +336,16 @@ struct cxl_endpoint_decoder {
int pos;
};
+/*
+ * The common case is decoders with no reservation, but also handle
+ * decoders with a zero-sized reservation that firmware may install for
+ * security lockdown purposes.
+ */
+static inline bool cxled_empty(struct cxl_endpoint_decoder *cxled)
+{
+ return !cxled->dpa_res || !resource_size(cxled->dpa_res);
+}
+
/**
* struct cxl_switch_decoder - Switch specific CXL HDM Decoder
* @cxld: base cxl_decoder object
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index 99cf77b6b699..c12fd0b89883 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -46,6 +46,9 @@ static int discover_region(struct device *dev, void *unused)
if (cxled->state != CXL_DECODER_STATE_AUTO)
return 0;
+ if (cxled_empty(cxled))
+ return 0;
+
/*
* Region enumeration is opportunistic, if this add-event fails,
* continue to the next endpoint decoder.
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v10 3/3] tools/testing/cxl: Enable zero sized decoders under hb0
2026-09-14 9:08 [PATCH v10 0/3] Support zero-sized HDM decoders Richard Cheng
2026-09-14 9:08 ` [PATCH v10 1/3] cxl/region: Simplify poison_by_decoder() error handling Richard Cheng
2026-09-14 9:08 ` [PATCH v10 2/3] cxl/hdm: Allow zero sized HDM decoders Richard Cheng
@ 2026-09-14 9:08 ` Richard Cheng
2 siblings, 0 replies; 4+ messages in thread
From: Richard Cheng @ 2026-09-14 9:08 UTC (permalink / raw)
To: jic23, dave, dave.jiang, vishal.l.verma, alison.schofield
Cc: iweiny, ming.li, kaihengf, kobak, vaslot, newtonl, mochs,
kristinc, linux-cxl, linux-kernel, Richard Cheng,
Jonathan Cameron
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>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
---
tools/testing/cxl/test/cxl.c | 109 ++++++++++++++++++++++++++++++-----
1 file changed, 94 insertions(+), 15 deletions(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 221be4addb0e..3eb1051b1ee8 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -18,6 +18,7 @@ static int interleave_arithmetic;
static bool extended_linear_cache;
static bool fail_autoassemble;
static bool type2_test;
+static bool mock_zero_size_decoders;
#define FAKE_QTG_ID 42
@@ -872,14 +873,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;
}
@@ -1072,16 +1072,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;
}
enum cxld_init_type {
@@ -1114,7 +1147,7 @@ static enum cxld_init_type get_decoder_init_type(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))
return MOCK_DECODER_INIT_DEFAULT;
return type2_test ? MOCK_DECODER_INIT_TYPE2_AUTO :
@@ -1147,6 +1180,7 @@ static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled,
struct cxl_port *root_port;
struct device *dev;
u64 base;
+ int id = 0;
base = window->base_hpa;
cxld->hpa_range = (struct range) {
@@ -1172,7 +1206,7 @@ static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled,
*/
dport = port->parent_dport;
root_port = dport->port;
- dev = device_find_child(&root_port->dev, NULL, first_decoder);
+ dev = device_find_child(&root_port->dev, &id, match_decoder_by_index);
/*
* Ancestor ports are guaranteed to be enumerated before
* @port, and all ports have at least one decoder.
@@ -1232,6 +1266,20 @@ static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled,
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;
+ }
cxld->hpa_range = (struct range) {
.start = base,
.end = base + mock_auto_region_size - 1,
@@ -1255,9 +1303,11 @@ static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled,
*/
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.
@@ -1307,6 +1357,26 @@ static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled,
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);
+ }
}
}
@@ -2076,6 +2146,12 @@ static bool __init have_multiple_modparms(void)
count++;
if (type2_test)
count++;
+ if (mock_zero_size_decoders)
+ count++;
+
+ /* Zero-size decoders require decoder[0] to preserve commit order. */
+ if (fail_autoassemble && mock_zero_size_decoders)
+ return true;
return count > 1;
}
@@ -2484,6 +2560,9 @@ module_param(fail_autoassemble, bool, 0444);
MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region");
module_param(type2_test, bool, 0444);
MODULE_PARM_DESC(type2_test, "Enable type 2 support testing");
+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
^ permalink raw reply [flat|nested] 4+ messages in thread