Destroy-region will partially tear down a region for an out-of-order decoder reset request, resetting decode state and removing targets before later failing in DPA release. The user sees:
cxl region: destroy_region: decoder11.0: set_dpa_size failed: Device or resource busy After that failure, follow-on operations (list, enable, destroy) can fail even after correcting the order. Validate endpoint decoder teardown order before starting cleanup. If a later endpoint decoder on the same port is still active, fail early with -EBUSY and a targeted message. Signed-off-by: Alison Schofield <[email protected]> --- cxl/region.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/cxl/region.c b/cxl/region.c index 95a7aa14268c..85d4d9bb54f2 100644 --- a/cxl/region.c +++ b/cxl/region.c @@ -831,6 +831,64 @@ out: return cxl_region_disable(region); } +static int region_destroy_validate_order(struct cxl_region *region) +{ + const char *devname = cxl_region_get_devname(region); + unsigned int ways, i; + + ways = cxl_region_get_interleave_ways(region); + if (ways == 0 || ways == UINT_MAX) { + log_err(&rl, "%s: error getting interleave ways\n", devname); + return -ENXIO; + } + + for (i = 0; i < ways; i++) { + struct cxl_decoder *ep_decoder, *decoder; + struct cxl_port *port; + struct cxl_memdev *memdev; + int id; + + ep_decoder = cxl_region_get_target_decoder(region, i); + if (!ep_decoder) + return -ENXIO; + + port = cxl_decoder_get_port(ep_decoder); + if (!port) + return -ENXIO; + + memdev = cxl_decoder_get_memdev(ep_decoder); + if (!memdev) + return -ENXIO; + + id = cxl_decoder_get_id(ep_decoder); + + cxl_decoder_foreach(port, decoder) { + struct cxl_memdev *other_memdev; + + if (decoder == ep_decoder) + continue; + + other_memdev = cxl_decoder_get_memdev(decoder); + if (other_memdev != memdev) + continue; + + if (cxl_decoder_get_id(decoder) <= id) + continue; + + if (cxl_decoder_get_dpa_size(decoder) == 0) + continue; + + log_err(&rl, + "%s: destroy blocked, %s still has later active decoder %s\n", + devname, cxl_memdev_get_devname(memdev), + cxl_decoder_get_devname(decoder)); + return -EBUSY; + } + } + + return 0; +} + static int destroy_region(struct cxl_region *region) { const char *devname = cxl_region_get_devname(region); @@ -843,6 +901,10 @@ static int destroy_region(struct cxl_region *region) return -EPERM; } + rc = region_destroy_validate_order(region); + if (rc) + return rc; + /* First, unbind/disable the region if needed */ if (cxl_region_is_enabled(region)) { if (param.force) { -- 2.37.3
