From: Alison Schofield <alison.schofield@intel.com>
To: Davidlohr Bueso <dave@stgolabs.net>,
Jonathan Cameron <jic23@kernel.org>,
Dave Jiang <dave.jiang@intel.com>,
Alison Schofield <alison.schofield@intel.com>,
Vishal Verma <vishal.l.verma@intel.com>,
Ira Weiny <iweiny@kernel.org>, Li Ming <ming.li@zohomail.com>,
Robert Richter <rrichter@amd.com>
Cc: linux-cxl@vger.kernel.org
Subject: [PATCH v5 5/7] cxl/region: Support mixed-granularity auto regions
Date: Thu, 3 Sep 2026 16:23:47 -0700 [thread overview]
Message-ID: <4ff59676f6db40e86749ebc810ead8ddfd3d3366.1788475206.git.alison.schofield@intel.com> (raw)
In-Reply-To: <cover.1788475206.git.alison.schofield@intel.com>
The CXL Specification permits a region's interleave granularity to differ
between levels of the decoder hierarchy. For an auto region, the CXL driver
reconstructs that hierarchy from the decoder configuration programmed by
platform firmware.
The CXL driver currently requires the interleaving root to have the same
granularity as the region, with each successive interleaving level becoming
coarser toward the endpoints. A mixed-granularity region lifts that
requirement: its interleave granularity is finer than the granularity of
its interleaving root decoder. The driver cannot assemble such a region
today.
Support mixed-granularity auto regions in the CXL driver, restricted to
coarse-to-fine layouts. Derive each interleaving decoder's granularity from
its parent:
child_ig = parent_ig / child_iw
A same-granularity region keeps the existing derivation, which coarsens
toward the endpoints:
child_ig = parent_ig * parent_iw
The topology determines each decoder's interleave ways, so the parent
granularity and child ways determine the child granularity. Use that
relationship to validate the decoder geometry while assembling an auto
region. Only the interleaving levels are compared; a passthrough level,
child_iw == 1, keeps its granularity exemption.
Refuse a derived granularity finer than the region granularity. Dividing at
every level lands the deepest interleaving level exactly on the region
granularity, so a finer value means the topology below one port is deeper
than the region interleave has positions for, which happens only when
another branch is too shallow to cover its share. That is what rejects an
unbalanced tree below the interleaving root.
Require the root and region to interleave over the same span, the address
range in which the interleave pattern completes one full pass:
root_iw * root_ig == region_iw * region_ig
The same span relationship covers the CXL Specification's Mod3
configurations. For example, a 6-way region at IGB across three host
bridges uses a 3-way root interleave at 2 * IGB and a 2-way interleave at
IGB below it, as described in CXL 4.0 Section 9.13.1.1.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/region.c | 128 ++++++++++++++++++++++++++------------
1 file changed, 89 insertions(+), 39 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 116f84bb0745..5b7104a55fdd 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1435,6 +1435,15 @@ static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig)
return 0;
}
+/* Mixed granularity has a region IG finer than the interleaving root IG */
+static bool cxl_region_is_mixed_gran(struct cxl_region *cxlr)
+{
+ struct cxl_decoder *cxld = &cxlr->cxlrd->cxlsd.cxld;
+
+ return cxld->interleave_ways > 1 &&
+ cxld->interleave_granularity > cxlr->params.interleave_granularity;
+}
+
static int cxl_port_setup_targets(struct cxl_port *port,
struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled)
@@ -1448,7 +1457,6 @@ static int cxl_port_setup_targets(struct cxl_port *port,
struct cxl_region_params *p = &cxlr->params;
struct cxl_decoder *cxld = cxl_rr->decoder;
struct cxl_switch_decoder *cxlsd;
- struct cxl_port *iter = port;
u16 eig, peig;
u8 eiw, peiw;
@@ -1464,26 +1472,26 @@ static int cxl_port_setup_targets(struct cxl_port *port,
}
cxlsd = to_cxl_switch_decoder(&cxld->dev);
+ child_iw = cxl_rr->nr_targets;
+
if (cxl_rr->nr_targets_set) {
int i, distance = 1;
- struct cxl_region_ref *cxl_rr_iter;
/*
- * The "distance" between peer downstream ports represents which
- * endpoint positions in the region interleave a given port can
- * host.
- *
- * For example, at the root of a hierarchy the distance is
- * always 1 as every index targets a different host-bridge. At
- * each subsequent switch level those ports map every Nth region
- * position where N is the width of the switch == distance.
+ * @distance is the spacing between region positions sharing this
+ * dport. Mixed-granularity regions place them contiguously.
*/
- do {
- cxl_rr_iter = cxl_rr_load(iter, cxlr);
- distance *= cxl_rr_iter->nr_targets;
- iter = to_cxl_port(iter->dev.parent);
- } while (!is_cxl_root(iter));
- distance *= cxlrd->cxlsd.cxld.interleave_ways;
+ if (!cxl_region_is_mixed_gran(cxlr)) {
+ struct cxl_region_ref *cxl_rr_iter;
+ struct cxl_port *iter = port;
+
+ do {
+ cxl_rr_iter = cxl_rr_load(iter, cxlr);
+ distance *= cxl_rr_iter->nr_targets;
+ iter = to_cxl_port(iter->dev.parent);
+ } while (!is_cxl_root(iter));
+ distance *= cxlrd->cxlsd.cxld.interleave_ways;
+ }
for (i = 0; i < cxl_rr->nr_targets_set; i++)
if (ep->dport == cxlsd->target[i]) {
@@ -1497,15 +1505,15 @@ static int cxl_port_setup_targets(struct cxl_port *port,
}
if (is_cxl_root(parent_port)) {
- /*
- * Root decoder IG is always set to value in CFMWS which
- * may be different than this region's IG. We can use the
- * region's IG here since interleave_granularity_store()
- * does not allow interleaved host-bridges with
- * root IG != region IG.
- */
- parent_ig = p->interleave_granularity;
parent_iw = cxlrd->cxlsd.cxld.interleave_ways;
+ /*
+ * A non-interleaving root does not contribute to the region
+ * interleave.
+ */
+ if (parent_iw > 1)
+ parent_ig = cxlrd->cxlsd.cxld.interleave_granularity;
+ else
+ parent_ig = p->interleave_granularity;
/*
* For purposes of address bit routing, use power-of-2 math for
* switch ports.
@@ -1538,7 +1546,6 @@ static int cxl_port_setup_targets(struct cxl_port *port,
return rc;
}
- child_iw = cxl_rr->nr_targets;
rc = ways_to_eiw(child_iw, &eiw);
if (rc) {
dev_dbg(&cxlr->dev, "%s:%s: invalid port interleave: %d\n",
@@ -1546,23 +1553,25 @@ static int cxl_port_setup_targets(struct cxl_port *port,
return rc;
}
- /*
- * Interleave granularity is a multiple of @parent_port granularity.
- * Multiplier is the parent port interleave ways.
- */
- rc = granularity_to_eig(parent_ig * parent_iw, &eig);
- if (rc) {
- dev_dbg(&cxlr->dev,
- "%s: invalid granularity calculation (%d * %d)\n",
- dev_name(&parent_port->dev), parent_ig, parent_iw);
- return rc;
+ /* Only mixed-gran regions refine granularity toward the endpoints */
+ if (cxl_region_is_mixed_gran(cxlr)) {
+ child_ig = parent_ig / child_iw;
+
+ if (child_ig < p->interleave_granularity) {
+ dev_dbg(&cxlr->dev,
+ "%s:%s: granularity %d is finer than region granularity %d\n",
+ dev_name(port->uport_dev), dev_name(&port->dev),
+ child_ig, p->interleave_granularity);
+ return -ENXIO;
+ }
+ } else {
+ child_ig = parent_ig * parent_iw;
}
- rc = eig_to_granularity(eig, &child_ig);
+ rc = granularity_to_eig(child_ig, &eig);
if (rc) {
- dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n",
- dev_name(port->uport_dev), dev_name(&port->dev),
- 256 << eig);
+ dev_dbg(&cxlr->dev, "%s:%s: invalid granularity: %d\n",
+ dev_name(port->uport_dev), dev_name(&port->dev), child_ig);
return rc;
}
@@ -2061,6 +2070,43 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr)
return rc;
}
+static int cxl_region_validate_interleave(struct cxl_region *cxlr)
+{
+ struct cxl_decoder *cxld = &cxlr->cxlrd->cxlsd.cxld;
+ struct cxl_region_params *p = &cxlr->params;
+ int root_iw = cxld->interleave_ways;
+ int root_ig = cxld->interleave_granularity;
+
+ if (root_iw == 1)
+ return 0;
+
+ if (p->interleave_granularity > root_ig) {
+ dev_dbg(&cxlr->dev,
+ "granularity %d exceeds root decoder granularity %d\n",
+ p->interleave_granularity, root_ig);
+ return -ENXIO;
+ }
+
+ /*
+ * Same-granularity regions below a power-of-two root may span multiple root
+ * interleaves. Mod3 roots are width-matched instead.
+ */
+ if (is_power_of_2(root_iw) && p->interleave_granularity == root_ig)
+ return 0;
+
+ /* Span is one complete pass through the interleave pattern. */
+ if (root_iw * root_ig != p->interleave_ways * p->interleave_granularity) {
+ dev_dbg(&cxlr->dev,
+ "region span %d (%d ways at %d) must equal root span %d (%d ways at %d)\n",
+ p->interleave_ways * p->interleave_granularity,
+ p->interleave_ways, p->interleave_granularity,
+ root_iw * root_ig, root_iw, root_ig);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
static int cxl_region_attach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos)
{
@@ -2103,6 +2149,10 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return -ENXIO;
}
+ rc = cxl_region_validate_interleave(cxlr);
+ if (rc)
+ return rc;
+
if (p->nr_targets >= p->interleave_ways) {
dev_dbg(&cxlr->dev, "region already has %d endpoints\n",
p->nr_targets);
--
2.37.3
next prev parent reply other threads:[~2026-09-03 23:24 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-03 23:23 [PATCH v5 0/7] cxl: Support mixed-granularity region interleaves Alison Schofield
2026-09-03 23:23 ` [PATCH v5 1/7] Documentation/cxl: Describe mixed-granularity regions Alison Schofield
2026-09-03 23:23 ` [PATCH v5 2/7] cxl/region: Warn on user region position mismatch Alison Schofield
2026-09-03 23:23 ` [PATCH v5 3/7] cxl/region: Generalize endpoint position mapping Alison Schofield
2026-09-03 23:23 ` [PATCH v5 4/7] cxl/region: Name the interleave locals in cxl_port_setup_targets() Alison Schofield
2026-09-03 23:23 ` Alison Schofield [this message]
2026-09-03 23:23 ` [PATCH v5 6/7] cxl/region: Support mixed-granularity user created regions Alison Schofield
2026-09-03 23:23 ` [PATCH v5 7/7] cxl/test: Add a topology to test mixed-granularity regions Alison Schofield
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4ff59676f6db40e86749ebc810ead8ddfd3d3366.1788475206.git.alison.schofield@intel.com \
--to=alison.schofield@intel.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=iweiny@kernel.org \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=ming.li@zohomail.com \
--cc=rrichter@amd.com \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox