[PATCH v3 6/9] cxl/region: Enforce coarse-to-fine ordering for mixed-granularity regions

Alison Schofield <[email protected]> Thu, 30 Jul 2026 15:20:26 -0700
Newsgroups org.kernel.vger.linux-cxl
Message-ID <c0c02a889a3e53d0daa5e69ad92cc6a3e88f1732.1785444498.git.alison.schofield@intel.com>
With mixed granularity allowed, selector disjointness alone would
accept an arrangement where an interleaving level is coarser than one
above it. The position arithmetic cannot represent such a layout: it
assumes each level below the root refines the one above.

In a mixed-granularity region require each interleaving level to be
strictly finer than its nearest interleaving ancestor, and reject a
violation with -ENXIO in check_gran_ordering(), called from
cxl_port_setup_targets(). The value validated is the derived
granularity on the user path and the firmware-programmed granularity on
the auto path. Same-granularity regions are exempt; their levels
legitimately coarsen away from the root.

Signed-off-by: Alison Schofield <[email protected]>
---
 drivers/cxl/core/region.c | 48 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 3919f64e2467..b4e8ebe158f7 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1680,6 +1680,50 @@ static int derive_port_granularity(struct cxl_port *port,
 	return 0;
 }
 
+/**
+ * check_gran_ordering() - Check mixed-granularity decoder ordering
+ * @port: port being configured
+ * @cxlr: region under construction
+ * @cxld: port decoder
+ * @accum: selectors used by the ancestor decoders
+ * @iw: port decoder interleave ways
+ * @ig: derived port decoder granularity
+ *
+ * The mixed-granularity position calculation requires each interleaving
+ * decoder below the root to use a smaller granularity than its nearest
+ * interleaving ancestor. Passthrough decoders do not participate.
+ *
+ * Auto regions validate the firmware-programmed granularity. User-created
+ * regions validate @ig.
+ *
+ * Return: 0 on success, -ENXIO on invalid ordering.
+ */
+static int check_gran_ordering(struct cxl_port *port, struct cxl_region *cxlr,
+			       struct cxl_decoder *cxld, u64 accum, int iw,
+			       int ig)
+{
+	struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
+	u64 val, ancestor_gran;
+
+	if (!cxl_region_is_mixed_gran(cxlr) || iw <= 1)
+		return 0;
+
+	val = test_bit(CXL_REGION_F_AUTO, &cxlr->flags) ?
+		cxld->interleave_granularity : ig;
+	ancestor_gran = accum ? (1ULL << __ffs64(accum)) :
+		cxlrd->cxlsd.cxld.interleave_granularity;
+
+	if (val >= ancestor_gran) {
+		dev_dbg(&cxlr->dev,
+			"%s:%s: iw %d ig %llu not finer than nearest interleaving ancestor (ig %llu); coarse-to-fine ordering required\n",
+			dev_name(port->uport_dev), dev_name(&port->dev),
+			iw, val, ancestor_gran);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
 /**
  * cxl_port_setup_targets() - Validate and program a port decoder
  * @port: port being configured
@@ -1762,6 +1806,10 @@ static int cxl_port_setup_targets(struct cxl_port *port,
 		return rc;
 	}
 
+	rc = check_gran_ordering(port, cxlr, cxld, accum, iw, ig);
+	if (rc)
+		return rc;
+
 	if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
 		if (iw > 1 && cxld->interleave_granularity != ig) {
 			dev_dbg(&cxlr->dev,
-- 
2.37.3