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


Reply via email to