Re: [PATCH v2 2/2] tools/testing/cxl: Enable zero sized decoders under hb0
From: Alison Schofield
Date: Wed Jun 03 2026 - 22:44:57 EST
On Tue, Jun 02, 2026 at 01:40:53PM +0800, Richard Cheng wrote:
> The kernel now allows committed HDM decoders of zero size so BIOS can
> burn slots with LOCK. Without cxl_test coverage the new path goes
> unexercised.
>
> For the special endpoints under host-bridge 0, e.g. cxl_mem.0 and
> cxl_mem.4, commit decoders 1 and 2 as zero-size + locked alongside the
> existing autoregion at decoder[0]. Mirro the same shape on the parent
> switch and host bridge via the walk-up that already builds the
> autoregion path.
>
With these patches I've got 7 of 17 cxl unit tests failing.
cxl-region-sysfs, cxl-create-region, cxl-xor-region,
cxl-destroy-region, cxl-qos-class, cxl-region-replay,
cxl-poison
I think all of them are failing for the at least one reason, the 'burned'
slots sit on the auto region endpoints that the suite reuses. So how we test
this needs to be reconsidered.
cxl-poison is actually failing for a second reason, and that
is an issue in patch 1. It gets to a point where 4 poison records
are expected and only 2 are found. That is because commit_end
points at a decoder wihtout a dpa_resource so cxl_get_poison_unmapped()
gets skipped, meaning we don't get the tail end of the possible poison.
I'll jump back to patch 1 and point that out next.
> Signed-off-by: Vishal Aslot <vaslot@xxxxxxxxxx>
> Signed-off-by: Richard Cheng <icheng@xxxxxxxxxx>
> ---
> Changelog:
>
> 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 instread 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 | 79 +++++++++++++++++++++++++++++++-----
> 1 file changed, 69 insertions(+), 10 deletions(-)
>
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> index 418669927fb0..1ae0290a9221 100644
> --- a/tools/testing/cxl/test/cxl.c
> +++ b/tools/testing/cxl/test/cxl.c
> @@ -1041,16 +1041,41 @@ 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;
> +}
> +
> +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_MANUAL;
> + 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;
> }
>
> /*
> @@ -1123,15 +1148,17 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
> }
>
> /*
> - * The first decoder on the first 2 devices on the first switch
> - * attached to host-bridge0 mock a fake / static RAM region. All
> - * other decoders are default disabled. Given the round robin
> - * assignment those devices are named cxl_mem.0, and cxl_mem.4.
> + * On the first 2 devices on the first switch attached to
> + * host-bridge0, decoder[0] mocks a fake / static RAM region and
> + * decoders 1 and 2 mock BIOS-burnt zero-size + locked slots
> + * (CXL r3.2 §8.2.4.20.12, §14.13.10). All other decoders are
> + * default disabled. Given the round robin assignment those
> + * devices are named cxl_mem.0 and cxl_mem.4.
> *
> * 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 > 2) {
> default_mock_decoder(cxld);
> return false;
> }
> @@ -1145,6 +1172,18 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
> base = window->base_hpa;
> if (extended_linear_cache)
> base += mock_auto_region_size;
> +
> + /*
> + * Decoders 1 and 2 of the special endpoints under host-bridge0
> + * are committed as zero-size + locked to mock BIOS burning
> + * decoder slots (CXL r3.2 §8.2.4.20.12, §14.13.10).
> + */
> + 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 +1207,12 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
> */
> iter = port;
> for (i = 0; i < 2; i++) {
> + int id;
> +
> dport = iter->parent_dport;
> iter = dport->port;
> - dev = device_find_child(&iter->dev, NULL, first_decoder);
> + id = 0;
> + 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 +1256,23 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
>
> cxld_registry_update(cxld);
> put_device(dev);
> +
> + /*
> + * Mirror the endpoint: also commit the next two switch
> + * decoders as zero-size + locked so the hierarchy looks
> + * like a BIOS post-lock layout end-to-end.
> + */
> + 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;
> --
> 2.43.0
>