From: Robert Richter <rrichter@amd.com>
To: Alison Schofield <alison.schofield@intel.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>,
Jonathan Cameron <jic23@kernel.org>,
Dave Jiang <dave.jiang@intel.com>,
Vishal Verma <vishal.l.verma@intel.com>,
Ira Weiny <iweiny@kernel.org>, Li Ming <ming.li@zohomail.com>,
linux-cxl@vger.kernel.org
Subject: Re: [PATCH v5 5/7] cxl/region: Support mixed-granularity auto regions
Date: Wed, 16 Sep 2026 20:03:43 +0200 [thread overview]
Message-ID: <aqrZ_3deL8WWWiNF@rric.localdomain> (raw)
In-Reply-To: <4ff59676f6db40e86749ebc810ead8ddfd3d3366.1788475206.git.alison.schofield@intel.com>
On 03.09.26 16:23:47, Alison Schofield wrote:
> 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
This is the implementation specific limitation. Either buttom-up or
top-down.
>
> 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.
Tracking selector bits is order independent.
>
> Refuse a derived granularity finer than the region granularity. Dividing at
Yes, that is a general requirement as otherwise the combined
interleaving configs would never match the region.
> 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.
In general, unbalanced trees could work if all endpoints of the same
region end at the same level. Else, Endpoints would have different
granularities depending on their levels.
>
> 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.
Looks correct for Mod3 configs. Then, all the hpa bits above are used
to determine the host bridge interleave position. And else, the same
bits would be used again for subsequent interleaving levels causing an
invalid position (in the CXL spec, CFMWS Structure, Interleave Target
List).
But for factor-of-2 only configs that requirement is not valid. Bits
above HBIG and ENIW could be used at lower levels. Let's see the
checks added...
>
> 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;
> + }
Worth moving that to a helper.
distance == 1 in the other case? Add to comment...
>
> 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;
Finaly this calculates iw and ig. Can that code (including the next
part below) moved out to a function. Possibly the various code paths
could be simplified for the different cases and checks be centralized
for all of them.
> /*
> * 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);
This is a valid case for the non-Mod3 case.
> + return -ENXIO;
> + }
> +
> + /*
> + * Same-granularity regions below a power-of-two root may span multiple root
> + * interleaves. Mod3 roots are width-matched instead.
What about the other power-of-two root configs? Comment suggests the
check is just for Mod3, but those other configs "fall through" too.
> + */
> + 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);
maybe name this check_root_interleave and pair it with
check_interleave_cap? Function is "check" only. Will be easier to get
the function's purpose. Maybe, move it up.
I rather like to have a check close to the code it is actually needed
for. That makes it more reasonable.
-Robert
> + 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-16 18:04 UTC|newest]
Thread overview: 32+ 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-07 23:23 ` Jonathan Cameron
2026-09-16 11:51 ` Robert Richter
2026-09-18 20:48 ` Alison Schofield
2026-09-03 23:23 ` [PATCH v5 2/7] cxl/region: Warn on user region position mismatch Alison Schofield
2026-09-16 12:05 ` Robert Richter
2026-09-18 21:18 ` Alison Schofield
2026-09-25 0:30 ` Alison Schofield
2026-09-03 23:23 ` [PATCH v5 3/7] cxl/region: Generalize endpoint position mapping Alison Schofield
2026-09-07 23:42 ` Jonathan Cameron
2026-09-18 23:01 ` Alison Schofield
2026-09-16 14:15 ` Robert Richter
2026-09-18 23:20 ` 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-07 23:50 ` [PATCH v5 4/7] cxo_ol/region: " Jonathan Cameron
2026-09-19 0:42 ` Alison Schofield
2026-09-16 14:34 ` [PATCH v5 4/7] cxl/region: " Robert Richter
2026-09-16 17:45 ` Robert Richter
2026-09-19 0:58 ` Alison Schofield
2026-09-19 0:55 ` Alison Schofield
2026-09-03 23:23 ` [PATCH v5 5/7] cxl/region: Support mixed-granularity auto regions Alison Schofield
2026-09-08 0:02 ` Jonathan Cameron
2026-09-16 18:03 ` Robert Richter [this message]
2026-09-03 23:23 ` [PATCH v5 6/7] cxl/region: Support mixed-granularity user created regions Alison Schofield
2026-09-08 0:03 ` Jonathan Cameron
2026-09-16 18:13 ` Robert Richter
2026-09-03 23:23 ` [PATCH v5 7/7] cxl/test: Add a topology to test mixed-granularity regions Alison Schofield
2026-09-08 0:10 ` Jonathan Cameron
2026-09-16 18:19 ` [PATCH v5 0/7] cxl: Support mixed-granularity region interleaves Robert Richter
2026-09-18 19:49 ` Alison Schofield
2026-09-29 22:48 ` Dave Jiang
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=aqrZ_3deL8WWWiNF@rric.localdomain \
--to=rrichter@amd.com \
--cc=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=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