* [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves
@ 2026-08-20 23:31 Alison Schofield
2026-08-20 23:31 ` [PATCH v4 1/6] cxl/region: Warn on user region position mismatch Alison Schofield
` (5 more replies)
0 siblings, 6 replies; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
RobertR: I've dropped the Originally-by tags since the implementation no
longer carries the selector-bit approach from your original patch. If
you think Patch 3 still warrants an authorship tag based on the origin
of the auto-region work, let me know.
Changes in v4:
- Rework port decoder setup around parent granularity and target count,
dropping the selector-walk and selector-derived granularity machinery
from v3 (RobertR)
- Replace the mixed-granularity-specific endpoint position handling with
one weighted position calculation for existing and mixed-gran regions
- Derive mixed-gran port decoder settings directly from the parent interleave
geometry
- Promote the user-region position self-test from dev_dbg() to dev_warn()
- Update the documentation patch
- Update the cover letter summary and series structure
Link to v3:
https://lore.kernel.org/linux-cxl/cover.1785444498.git.alison.schofield@intel.com/
Changes in v3:
- Reduce scope to coarse-to-fine ordering (RichardC, Sashiko)
- Reject out-of-order layouts (RichardC, Sashiko)
- P1: Factor the selector walk and per-port checks into helpers (RobertR)
That pre-work is the new Patch 1/8 and the Series Structure section of
this cover letter describe same below.
- P3: Derive granularity from the highest available selector span (RobertR)
- P3: Validate auto-programmed granularity against the derived value
- P4: Rename root_pos_stride() to root_positions_per_target() (RobertR)
- P4: Drop the "stride" terminology throughout (RobertR)
- The complex Patch 4 of v2 is split into patches 4-6 in v3.
Also noted in the Series Structure section of cover letter below.
- P9: Move the peer-distance walk and per-function walkthroughs from the doc
into in-code comments and kernel-doc (RobertR)
- Drop Reviewed-by tags
Link to v2:
https://lore.kernel.org/linux-cxl/cover.1781199122.git.alison.schofield@intel.com/
Changes in v2:
- Patch 1,2: Defer the unused selector var store to keep P1 bisectable (Sashiko)
- Patch 1: Make divide by 3 in get_selctor() work on 32-bit builds) (lkp)
- Patch 4: Use local vars in cxl_region_attach() for readability (DaveJ)
- Patch 5: Add NULL checks on unused mock arrays (Sashiko)
- Resolved errant err_rch unwind with rc7 merge (DaveJ)
- Rebase onto 7.1-rc7
- Update commit logs in 1,2,5 to align w changes in v2
Link to v1:
https://lore.kernel.org/all/cover.1780095671.git.alison.schofield@intel.com/
Begin Cover Letter:
A CXL region may interleave across multiple decoder levels: root,
optional switches, and endpoint. The driver has historically required
equal region and root decoder granularities. That blocks legal
mixed-granularity arrangements permitted by CXL 4.0 Section 9.13.1 and
makes some 6-way and 12-way configurations defined in Section 9.13.1.1
(Tables 9-6, 9-7, and 9-8) impossible to create.
Two prior proposals addressed parts of this gap:
AlisonS introduced position arithmetic and sysfs gating for auto and
user-created regions to support the 6- and 12-way interleave
configurations without a same-granularity alternative:
https://lore.kernel.org/all/20250306232239.2609017-1-alison.schofield@intel.com/
RobertR introduced an HPA selector-bit model for auto regions that
allows multi-level power-of-two interleaves regardless of granularity
ordering:
https://lore.kernel.org/all/20251028094754.72816-1-rrichter@amd.com/
This series adds support for CXL regions where interleave granularity
differs between levels of the decoder hierarchy. The support applies to
both firmware-programmed auto regions and user-created regions.
Linux supports mixed-granularity configurations that are monotonic from
the interleaving root toward the endpoints. Granularity may remain the
same or become finer at each interleaving level. Configurations that
refine and then become coarser are permitted by the CXL Specification
but are not supported by Linux.
This support includes all power-of-two interleaves, as well as the
3-, 6-, and 12-way interleave configurations described by CXL 4.0
Section 9.13.1.1.
Series structure - reworked in v4
---------------------------------
Patch 1 promotes the existing user-region endpoint position self-test to
a visible diagnostic.
Patch 2 generalizes endpoint position calculation to account for
granularity changes between decoder levels.
Patch 3 enables mixed-granularity auto regions and validates their
coarse-to-fine decoder geometry.
Patch 4 enables mixed-granularity user regions by relaxing the existing
root/region granularity restriction.
Patch 5 adds cxl_test topology and coverage for mixed-granularity regions.
Patch 6 documents the mixed-granularity model and Linux support policy.
A companion NDCTL patchset that allows mixed-gran 'cxl create-region'
and adds the unit test is posted here:
https://lore.kernel.org/all/fa5c109f08824180f58341ebd9055545a2ff3142.1780099216.git.alison.schofield@intel.com/
Alison Schofield (6):
cxl/region: Warn on user region position mismatch
cxl/region: Generalize endpoint position mapping
cxl/region: Support mixed-granularity auto regions
cxl/region: Support mixed-granularity user created regions
cxl/test: Add a topology to test mixed-granularity regions
Documentation/cxl: Describe mixed-granularity regions
.../driver-api/cxl/linux/cxl-driver.rst | 135 +++++
drivers/cxl/core/region.c | 193 ++++---
tools/testing/cxl/test/cxl.c | 496 ++++++++++++++++--
3 files changed, 706 insertions(+), 118 deletions(-)
base-commit: db2ddb87143519e20a95aa36c60b36107b736a58
--
2.37.3
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 1/6] cxl/region: Warn on user region position mismatch
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
@ 2026-08-20 23:31 ` Alison Schofield
2026-08-21 19:01 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping Alison Schofield
` (4 subsequent siblings)
5 siblings, 1 reply; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
User region creation includes a self-test that checks the assigned
endpoint positions against the position calculation used by auto
region creation. A mismatch is reported with dev_dbg().
Promote that to a dev_warn() so bugs in the auto region position
calculation are easier to catch as that calculation is extended to
support more region configurations.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/region.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 1e211542b6b6..3b640c9ba5a0 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2218,10 +2218,10 @@ static int cxl_region_attach(struct cxl_region *cxlr,
int test_pos;
test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range);
- dev_dbg(&target->cxld.dev,
- "Test cxl_calc_interleave_pos(): %s test_pos:%d target->pos:%d\n",
- (test_pos == target->pos) ? "success" : "fail",
- test_pos, target->pos);
+ if (test_pos != target->pos)
+ dev_warn(&target->cxld.dev,
+ "position mismatch: calculated:%d programmed:%d\n",
+ test_pos, target->pos);
}
return 0;
--
2.37.3
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
2026-08-20 23:31 ` [PATCH v4 1/6] cxl/region: Warn on user region position mismatch Alison Schofield
@ 2026-08-20 23:31 ` Alison Schofield
2026-08-20 23:43 ` sashiko-bot
` (2 more replies)
2026-08-20 23:31 ` [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions Alison Schofield
` (3 subsequent siblings)
5 siblings, 3 replies; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
Endpoint position calculation currently relies on the requirement that
an interleaving root have the same granularity as the region. Auto
region creation builds the position by multiplying by each parent
decoder's ways, while user region creation selects the root target
with 'pos % ways'. Those calculations are sufficient under the current
granularity restriction.
In order to support mixed-granularity regions, that granularity
restriction will need to be removed so decoder granularity can change
between levels of the interleave hierarchy. The position calculation
needs to account for those changes to produce the correct endpoint
ordering.
Change the position calculation so each decoder's contribution is
weighted by its granularity relative to the region granularity:
position += target_pos *
(decoder_granularity / region_granularity)
Use the same relationship to select the root target during user region
creation.
For currently supported regions, the new weighted calculation reduces
to the existing position calculation and produces identical endpoint
positions.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/region.c | 60 ++++++++++++++++++++++-----------------
1 file changed, 34 insertions(+), 26 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 3b640c9ba5a0..4f367feaf6c8 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1805,9 +1805,14 @@ static int cxl_region_attach_position(struct cxl_region *cxlr,
struct cxl_decoder *cxld = &cxlsd->cxld;
int iw = cxld->interleave_ways;
struct cxl_port *iter;
- int rc;
+ int root_pos = pos, rc;
- if (dport != cxlrd->cxlsd.target[pos % iw]) {
+ /* Root target selection advances at root-granularity intervals */
+ if (iw > 1)
+ root_pos = pos * cxlr->params.interleave_granularity /
+ cxld->interleave_granularity;
+
+ if (dport != cxlrd->cxlsd.target[root_pos % iw]) {
dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n",
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
dev_name(&cxlrd->cxlsd.cxld.dev));
@@ -1908,13 +1913,13 @@ static int match_switch_decoder_by_range(struct device *dev,
return (r1->start == r2->start && r1->end == r2->end);
}
-static int find_pos_and_ways(struct cxl_port *port, struct range *range,
- int *pos, int *ways)
+static int find_pos_and_gran(struct cxl_port *port, struct range *range,
+ int *pos, int *gran)
{
struct cxl_switch_decoder *cxlsd;
struct cxl_port *parent;
struct device *dev;
- int rc = -ENXIO;
+ int ways, rc = -ENXIO;
parent = parent_port_of(port);
if (!parent)
@@ -1929,9 +1934,10 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
return rc;
}
cxlsd = to_cxl_switch_decoder(dev);
- *ways = cxlsd->cxld.interleave_ways;
+ ways = cxlsd->cxld.interleave_ways;
+ *gran = cxlsd->cxld.interleave_granularity;
- for (int i = 0; i < *ways; i++) {
+ for (int i = 0; i < ways; i++) {
if (cxlsd->target[i] == port->parent_dport) {
*pos = i;
rc = 0;
@@ -1955,13 +1961,16 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
* @cxled: endpoint decoder member of given region
* @hpa_range: translated HPA range of the endpoint
*
- * The endpoint position is calculated by traversing the topology from
- * the endpoint to the root decoder and iteratively applying this
- * calculation:
+ * The endpoint position is calculated by traversing the topology from the
+ * endpoint to the root decoder and accumulating the contribution of each
+ * decoder level:
*
- * position = position * parent_ways + parent_pos;
+ * position += parent_pos * (parent_granularity / region_granularity);
*
- * ...where @position is inferred from switch and root decoder target lists.
+ * ...where @parent_pos is inferred from switch and root decoder target
+ * lists, and the multiplier is the number of region positions that the
+ * level's granularity spans. A level that selects a single target
+ * contributes nothing.
*
* Return: position >= 0 on success
* -ENXIO on failure
@@ -1971,7 +1980,8 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
{
struct cxl_port *iter, *port = cxled_to_port(cxled);
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- int parent_ways = 0, parent_pos = 0, pos = 0;
+ int gran = cxled->cxld.interleave_granularity;
+ int parent_gran = 0, parent_pos = 0, pos = 0;
int rc;
/*
@@ -1984,20 +1994,18 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
* | | | |
* mem0 mem1 mem2 mem3
*
- * In the example the calculator will iterate twice. The first iteration
- * uses the mem position in the host-bridge and the ways of the host-
- * bridge to generate the first, or local, position. The second
- * iteration uses the host-bridge position in the root_port and the ways
- * of the root_port to refine the position.
+ * The region and the root decoder interleave at granularity g, so
+ * each host-bridge decoder interleaves at 2g and spans two region
+ * positions while the root decoder spans one.
*
* A trace of the calculation per endpoint looks like this:
- * mem0: pos = 0 * 2 + 0 mem2: pos = 0 * 2 + 0
- * pos = 0 * 2 + 0 pos = 0 * 2 + 1
+ * mem0: pos += 0 * 2 mem2: pos += 0 * 2
+ * pos += 0 * 1 pos += 1 * 1
* pos: 0 pos: 1
*
- * mem1: pos = 0 * 2 + 1 mem3: pos = 0 * 2 + 1
- * pos = 1 * 2 + 0 pos = 1 * 2 + 1
- * pos: 2 pos = 3
+ * mem1: pos += 1 * 2 mem3: pos += 1 * 2
+ * pos += 0 * 1 pos += 1 * 1
+ * pos: 2 pos: 3
*
* Note that while this example is simple, the method applies to more
* complex topologies, including those with switches.
@@ -2008,12 +2016,12 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
if (is_cxl_root(iter))
break;
- rc = find_pos_and_ways(iter, hpa_range, &parent_pos,
- &parent_ways);
+ rc = find_pos_and_gran(iter, hpa_range, &parent_pos,
+ &parent_gran);
if (rc)
return rc;
- pos = pos * parent_ways + parent_pos;
+ pos += parent_pos * (parent_gran / gran);
}
dev_dbg(&cxlmd->dev,
--
2.37.3
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
2026-08-20 23:31 ` [PATCH v4 1/6] cxl/region: Warn on user region position mismatch Alison Schofield
2026-08-20 23:31 ` [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping Alison Schofield
@ 2026-08-20 23:31 ` Alison Schofield
2026-08-20 23:43 ` sashiko-bot
2026-08-21 21:57 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 4/6] cxl/region: Support mixed-granularity user created regions Alison Schofield
` (2 subsequent siblings)
5 siblings, 2 replies; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
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 assumes the interleaving root has the same
granularity as the region, with each successive interleaving level
becoming coarser toward the endpoints. This prevents the driver from
assembling an auto region where the root granularity is coarser than
the region granularity.
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
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.
Require the root and region to describe the same interleave span:
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 | 114 ++++++++++++++++++++++++--------------
1 file changed, 73 insertions(+), 41 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 4f367feaf6c8..637d1b60a570 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1434,6 +1434,16 @@ 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)
@@ -1447,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;
@@ -1463,26 +1472,21 @@ static int cxl_port_setup_targets(struct cxl_port *port,
}
cxlsd = to_cxl_switch_decoder(&cxld->dev);
+ iw = cxl_rr->nr_targets;
+
if (cxl_rr->nr_targets_set) {
- int i, distance = 1;
- struct cxl_region_ref *cxl_rr_iter;
+ int i, distance;
/*
- * 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 those positions
+ * 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))
+ distance = 1;
+ else
+ distance = cxld->interleave_granularity * iw /
+ p->interleave_granularity;
for (i = 0; i < cxl_rr->nr_targets_set; i++)
if (ep->dport == cxlsd->target[i]) {
@@ -1496,15 +1500,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.
@@ -1537,7 +1541,6 @@ static int cxl_port_setup_targets(struct cxl_port *port,
return rc;
}
- iw = cxl_rr->nr_targets;
rc = ways_to_eiw(iw, &eiw);
if (rc) {
dev_dbg(&cxlr->dev, "%s:%s: invalid port interleave: %d\n",
@@ -1545,23 +1548,15 @@ 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;
- }
+ if (cxl_region_is_mixed_gran(cxlr))
+ ig = parent_ig / iw;
+ else
+ ig = parent_ig * parent_iw;
- rc = eig_to_granularity(eig, &ig);
+ rc = granularity_to_eig(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), ig);
return rc;
}
@@ -2058,6 +2053,39 @@ 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-gran power-of-two regions may span multiple root targets */
+ if (is_power_of_2(root_iw) && p->interleave_granularity == root_ig)
+ return 0;
+
+ /* Mixed-gran regions must span exactly one root interleave */
+ if (root_iw * root_ig != p->interleave_ways * p->interleave_granularity) {
+ dev_dbg(&cxlr->dev,
+ "%d ways at %d does not span root decoder %d ways at %d\n",
+ p->interleave_ways, p->interleave_granularity, root_iw,
+ root_ig);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
static int cxl_region_attach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos)
{
@@ -2100,6 +2128,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
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v4 4/6] cxl/region: Support mixed-granularity user created regions
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
` (2 preceding siblings ...)
2026-08-20 23:31 ` [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions Alison Schofield
@ 2026-08-20 23:31 ` Alison Schofield
2026-08-21 22:00 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 5/6] cxl/test: Add a topology to test mixed-granularity regions Alison Schofield
2026-08-20 23:31 ` [PATCH v4 6/6] Documentation/cxl: Describe " Alison Schofield
5 siblings, 1 reply; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
User region creation currently requires an interleaving root to have
the same granularity as the region.
That rejects valid mixed-granularity layouts where the region
granularity is finer than the root granularity. A region granularity
coarser than the root remains invalid because it produces incorrect
DPA translations.
Allow a region granularity finer than an interleaving root and
continue to reject a coarser granularity. Validate the complete
ways/granularity relationship when the region endpoints are attached.
For example, CXL 4.0 Section 9.13.1.1 Table 9-7 describes a 6-way
region at IGB across three host bridges as a 3-way root interleave at
2 * IGB with a 2-way interleave at IGB below it.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/core/region.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 637d1b60a570..4bbfb7cd7e01 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -571,15 +571,8 @@ static int set_interleave_granularity(struct cxl_region *cxlr, int val)
if (rc)
return rc;
- /*
- * When the host-bridge is interleaved, disallow region granularity !=
- * root granularity. Regions with a granularity less than the root
- * interleave result in needing multiple endpoints to support a single
- * slot in the interleave (possible to support in the future). Regions
- * with a granularity greater than the root interleave result in invalid
- * DPA translations (invalid to support).
- */
- if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
+ /* Region granularity must not be coarser than an interleaving root's */
+ if (cxld->interleave_ways > 1 && val > cxld->interleave_granularity)
return -EINVAL;
lockdep_assert_held_write(&cxl_rwsem.region);
--
2.37.3
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v4 5/6] cxl/test: Add a topology to test mixed-granularity regions
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
` (3 preceding siblings ...)
2026-08-20 23:31 ` [PATCH v4 4/6] cxl/region: Support mixed-granularity user created regions Alison Schofield
@ 2026-08-20 23:31 ` Alison Schofield
2026-08-21 22:07 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 6/6] Documentation/cxl: Describe " Alison Schofield
5 siblings, 1 reply; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
Add a cxl_test topology for mixed-granularity region testing.
modprobe cxl_test mixed_gran_regions=1 creates three host bridges,
each with a passthrough host-bridge port above two switch levels
and four endpoints, for twelve endpoints total.
Two CFMWS windows are added:
1) A 2-way window at 4K gran over two host bridges, that hosts 8-way
regions at 1K.
2) A 3-way window at 512 gran over three host bridges, that hosts
6-way regions at 256.
Both windows also host the same-granularity regions their ways allow.
Register every mock device through cxl_mock_platform_device_add() so
the mock array is populated before platform_device_add(), matching the
ordering the rest of cxl_test relies on to avoid the endpoint-probe
race in commit d90f236f8b9e ("cxl/test: Update mock dev array before
calling platform_device_add()").
NULL-check the legacy topology arrays, which are left empty when
mixed_gran_regions=1 selects the new topology.
The default and existing special module params are unaffected.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
tools/testing/cxl/test/cxl.c | 496 ++++++++++++++++++++++++++++++++---
1 file changed, 458 insertions(+), 38 deletions(-)
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index ef92dd35e030..0ec9b402e1af 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -17,6 +17,7 @@
static int interleave_arithmetic;
static bool extended_linear_cache;
static bool fail_autoassemble;
+static bool mixed_gran_regions;
#define FAKE_QTG_ID 42
@@ -26,7 +27,26 @@ static bool fail_autoassemble;
#define NR_CXL_ROOT_PORTS 2
#define NR_CXL_SWITCH_PORTS 2
#define NR_CXL_PORT_DECODERS 8
-#define NR_BRIDGES (NR_CXL_HOST_BRIDGES + NR_CXL_SINGLE_HOST + NR_CXL_RCH)
+
+/*
+ * mixed_gran_regions=1: three host bridges, two switch levels, and
+ * twelve endpoints for power-of-2 and 3-way-family region layouts.
+ */
+#define NR_CXL_MIX_GRAN_HB 3
+#define NR_CXL_MIX_GRAN_ROOT_PORTS NR_CXL_MIX_GRAN_HB /* 1 per HB */
+#define NR_CXL_MIX_GRAN_L1_DPORTS (NR_CXL_MIX_GRAN_HB * 2)
+#define NR_CXL_MIX_GRAN_L2_SWITCHES NR_CXL_MIX_GRAN_L1_DPORTS
+#define NR_CXL_MIX_GRAN_L2_DPORTS (NR_CXL_MIX_GRAN_L2_SWITCHES * 2)
+
+#define NR_DEFAULT_BRIDGES \
+ (NR_CXL_HOST_BRIDGES + NR_CXL_SINGLE_HOST + NR_CXL_RCH)
+#define NR_BRIDGES (NR_DEFAULT_BRIDGES + NR_CXL_MIX_GRAN_HB)
+
+/* CHBS index ranges within mock_cedt.chbs[] */
+#define CHBS_DEFAULT_START 0
+#define CHBS_DEFAULT_END (NR_DEFAULT_BRIDGES - 1)
+#define CHBS_MIX_GRAN_START NR_DEFAULT_BRIDGES
+#define CHBS_MIX_GRAN_END (NR_BRIDGES - 1)
#define MOCK_AUTO_REGION_SIZE_DEFAULT SZ_512M
static int mock_auto_region_size = MOCK_AUTO_REGION_SIZE_DEFAULT;
@@ -52,6 +72,14 @@ struct platform_device *cxl_mem_single[NR_MEM_SINGLE];
static struct platform_device *cxl_rch[NR_CXL_RCH];
static struct platform_device *cxl_rcd[NR_CXL_RCH];
+static struct platform_device *cxl_mix_gran_hb[NR_CXL_MIX_GRAN_HB];
+static struct platform_device *cxl_mix_gran_root_port[NR_CXL_MIX_GRAN_ROOT_PORTS];
+static struct platform_device *cxl_mix_gran_l1_uport[NR_CXL_MIX_GRAN_ROOT_PORTS];
+static struct platform_device *cxl_mix_gran_l1_dport[NR_CXL_MIX_GRAN_L1_DPORTS];
+static struct platform_device *cxl_mix_gran_l2_uport[NR_CXL_MIX_GRAN_L2_SWITCHES];
+static struct platform_device *cxl_mix_gran_l2_dport[NR_CXL_MIX_GRAN_L2_DPORTS];
+static struct platform_device *cxl_mem_mix_gran[NR_CXL_MIX_GRAN_L2_DPORTS];
+
/*
* Decoder registry
*
@@ -115,6 +143,19 @@ static struct acpi_device host_bridge[NR_BRIDGES] = {
.handle = &host_bridge[3],
.pnp.unique_id = "3",
},
+ /* Dedicated mixed-granularity topology */
+ [4] = {
+ .handle = &host_bridge[4],
+ .pnp.unique_id = "4",
+ },
+ [5] = {
+ .handle = &host_bridge[5],
+ .pnp.unique_id = "5",
+ },
+ [6] = {
+ .handle = &host_bridge[6],
+ .pnp.unique_id = "6",
+ },
};
static bool is_mock_dev(struct device *dev)
@@ -122,15 +163,18 @@ static bool is_mock_dev(struct device *dev)
int i;
for (i = 0; i < ARRAY_SIZE(cxl_mem); i++)
- if (dev == &cxl_mem[i]->dev)
+ if (cxl_mem[i] && dev == &cxl_mem[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++)
- if (dev == &cxl_mem_single[i]->dev)
+ if (cxl_mem_single[i] && dev == &cxl_mem_single[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++)
- if (dev == &cxl_rcd[i]->dev)
+ if (cxl_rcd[i] && dev == &cxl_rcd[i]->dev)
return true;
- if (dev == &cxl_acpi->dev)
+ for (i = 0; i < ARRAY_SIZE(cxl_mem_mix_gran); i++)
+ if (cxl_mem_mix_gran[i] && dev == &cxl_mem_mix_gran[i]->dev)
+ return true;
+ if (cxl_acpi && dev == &cxl_acpi->dev)
return true;
return false;
}
@@ -188,6 +232,14 @@ static struct {
struct acpi_cedt_cfmws cfmws;
u32 target[3];
} cfmws8;
+ struct {
+ struct acpi_cedt_cfmws cfmws;
+ u32 target[2];
+ } cfmws9;
+ struct {
+ struct acpi_cedt_cfmws cfmws;
+ u32 target[3];
+ } cfmws10;
struct {
struct acpi_cedt_cxims cxims;
u64 xormap_list[2];
@@ -232,6 +284,31 @@ static struct {
.uid = 3,
.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL11,
},
+ /* Dedicated mixed-granularity topology */
+ .chbs[4] = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CHBS,
+ .length = sizeof(mock_cedt.chbs[0]),
+ },
+ .uid = 4,
+ .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
+ },
+ .chbs[5] = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CHBS,
+ .length = sizeof(mock_cedt.chbs[0]),
+ },
+ .uid = 5,
+ .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
+ },
+ .chbs[6] = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CHBS,
+ .length = sizeof(mock_cedt.chbs[0]),
+ },
+ .uid = 6,
+ .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
+ },
.cfmws0 = {
.cfmws = {
.header = {
@@ -371,6 +448,36 @@ static struct {
},
.target = { 0, 1, 2, },
},
+ .cfmws9 = {
+ .cfmws = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CFMWS,
+ .length = sizeof(mock_cedt.cfmws9),
+ },
+ .interleave_ways = 1,
+ .granularity = 4,
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
+ ACPI_CEDT_CFMWS_RESTRICT_PMEM,
+ .qtg_id = FAKE_QTG_ID,
+ .window_size = SZ_512M * 4UL,
+ },
+ .target = { 4, 5, },
+ },
+ .cfmws10 = {
+ .cfmws = {
+ .header = {
+ .type = ACPI_CEDT_TYPE_CFMWS,
+ .length = sizeof(mock_cedt.cfmws10),
+ },
+ .interleave_ways = 8,
+ .granularity = 1,
+ .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM |
+ ACPI_CEDT_CFMWS_RESTRICT_PMEM,
+ .qtg_id = FAKE_QTG_ID,
+ .window_size = SZ_512M * 6UL,
+ },
+ .target = { 4, 5, 6, },
+ },
.cxims0 = {
.cxims = {
.header = {
@@ -395,6 +502,9 @@ struct acpi_cedt_cfmws *mock_cfmws[] = {
[6] = &mock_cedt.cfmws6.cfmws,
[7] = &mock_cedt.cfmws7.cfmws,
[8] = &mock_cedt.cfmws8.cfmws,
+ /* Dedicated mixed-granularity topology */
+ [9] = &mock_cedt.cfmws9.cfmws,
+ [10] = &mock_cedt.cfmws10.cfmws,
};
static int cfmws_start;
@@ -403,6 +513,8 @@ static int cfmws_end;
#define CFMWS_MOD_ARRAY_END 5
#define CFMWS_XOR_ARRAY_START 6
#define CFMWS_XOR_ARRAY_END 8
+#define CFMWS_MIX_GRAN_ARRAY_START 9
+#define CFMWS_MIX_GRAN_ARRAY_END 10
struct acpi_cedt_cxims *mock_cxims[1] = {
[0] = &mock_cedt.cxims0.cxims,
@@ -479,9 +591,18 @@ static void cfmws_elc_update(struct acpi_cedt_cfmws *window, int index)
static int populate_cedt(void)
{
struct cxl_mock_res *res;
+ int chbs_lo, chbs_hi;
int i;
- for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
+ if (mixed_gran_regions) {
+ chbs_lo = CHBS_MIX_GRAN_START;
+ chbs_hi = CHBS_MIX_GRAN_END;
+ } else {
+ chbs_lo = CHBS_DEFAULT_START;
+ chbs_hi = CHBS_DEFAULT_END;
+ }
+
+ for (i = chbs_lo; i <= chbs_hi; i++) {
struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i];
resource_size_t size;
@@ -538,12 +659,23 @@ static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id,
if (!is_mock_port(dev) && !is_mock_dev(dev))
return acpi_table_parse_cedt(id, handler_arg, arg);
- if (id == ACPI_CEDT_TYPE_CHBS)
- for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
+ if (id == ACPI_CEDT_TYPE_CHBS) {
+ int chbs_lo, chbs_hi;
+
+ if (mixed_gran_regions) {
+ chbs_lo = CHBS_MIX_GRAN_START;
+ chbs_hi = CHBS_MIX_GRAN_END;
+ } else {
+ chbs_lo = CHBS_DEFAULT_START;
+ chbs_hi = CHBS_DEFAULT_END;
+ }
+
+ for (i = chbs_lo; i <= chbs_hi; i++) {
h = (union acpi_subtable_headers *)&mock_cedt.chbs[i];
end = (unsigned long)&mock_cedt.chbs[i + 1];
handler_arg(h, arg, end);
}
+ }
if (id == ACPI_CEDT_TYPE_CFMWS)
for (i = cfmws_start; i <= cfmws_end; i++) {
@@ -567,13 +699,16 @@ static bool is_mock_bridge(struct device *dev)
int i;
for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++)
- if (dev == &cxl_host_bridge[i]->dev)
+ if (cxl_host_bridge[i] && dev == &cxl_host_bridge[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_hb_single); i++)
- if (dev == &cxl_hb_single[i]->dev)
+ if (cxl_hb_single[i] && dev == &cxl_hb_single[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_rch); i++)
- if (dev == &cxl_rch[i]->dev)
+ if (cxl_rch[i] && dev == &cxl_rch[i]->dev)
+ return true;
+ for (i = 0; i < ARRAY_SIZE(cxl_mix_gran_hb); i++)
+ if (cxl_mix_gran_hb[i] && dev == &cxl_mix_gran_hb[i]->dev)
return true;
return false;
@@ -587,27 +722,52 @@ static bool is_mock_port(struct device *dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++)
- if (dev == &cxl_root_port[i]->dev)
+ if (cxl_root_port[i] && dev == &cxl_root_port[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++)
- if (dev == &cxl_switch_uport[i]->dev)
+ if (cxl_switch_uport[i] && dev == &cxl_switch_uport[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++)
- if (dev == &cxl_switch_dport[i]->dev)
+ if (cxl_switch_dport[i] && dev == &cxl_switch_dport[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_root_single); i++)
- if (dev == &cxl_root_single[i]->dev)
+ if (cxl_root_single[i] && dev == &cxl_root_single[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_swu_single); i++)
- if (dev == &cxl_swu_single[i]->dev)
+ if (cxl_swu_single[i] && dev == &cxl_swu_single[i]->dev)
return true;
for (i = 0; i < ARRAY_SIZE(cxl_swd_single); i++)
- if (dev == &cxl_swd_single[i]->dev)
+ if (cxl_swd_single[i] && dev == &cxl_swd_single[i]->dev)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(cxl_mix_gran_root_port); i++)
+ if (cxl_mix_gran_root_port[i] &&
+ dev == &cxl_mix_gran_root_port[i]->dev)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(cxl_mix_gran_l1_uport); i++)
+ if (cxl_mix_gran_l1_uport[i] &&
+ dev == &cxl_mix_gran_l1_uport[i]->dev)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(cxl_mix_gran_l1_dport); i++)
+ if (cxl_mix_gran_l1_dport[i] &&
+ dev == &cxl_mix_gran_l1_dport[i]->dev)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(cxl_mix_gran_l2_uport); i++)
+ if (cxl_mix_gran_l2_uport[i] &&
+ dev == &cxl_mix_gran_l2_uport[i]->dev)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(cxl_mix_gran_l2_dport); i++)
+ if (cxl_mix_gran_l2_dport[i] &&
+ dev == &cxl_mix_gran_l2_dport[i]->dev)
return true;
if (is_cxl_memdev(dev))
@@ -678,7 +838,15 @@ static struct acpi_pci_root mock_pci_root[ARRAY_SIZE(mock_pci_bus)] = {
[3] = {
.bus = &mock_pci_bus[3],
},
-
+ [4] = {
+ .bus = &mock_pci_bus[4],
+ },
+ [5] = {
+ .bus = &mock_pci_bus[5],
+ },
+ [6] = {
+ .bus = &mock_pci_bus[6],
+ },
};
static bool is_mock_bus(struct pci_bus *bus)
@@ -1099,7 +1267,8 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
/* check is endpoint is attach to host-bridge0 */
port = cxled_to_port(cxled);
do {
- if (port->uport_dev == &cxl_host_bridge[0]->dev) {
+ if (cxl_host_bridge[0] &&
+ port->uport_dev == &cxl_host_bridge[0]->dev) {
hb0 = true;
break;
}
@@ -1242,6 +1411,8 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
if (is_cxl_endpoint(port))
target_count = 0;
+ else if (mixed_gran_regions && is_cxl_root(parent_port))
+ target_count = NR_CXL_MIX_GRAN_ROOT_PORTS / NR_CXL_MIX_GRAN_HB;
else if (is_cxl_root(parent_port))
target_count = NR_CXL_ROOT_PORTS;
else
@@ -1341,6 +1512,27 @@ static int get_port_array(struct cxl_port *port,
struct platform_device **array;
int array_size;
+ if (mixed_gran_regions) {
+ if (port->depth == 1) {
+ array_size = ARRAY_SIZE(cxl_mix_gran_root_port);
+ array = cxl_mix_gran_root_port;
+ } else if (port->depth == 2) {
+ array_size = ARRAY_SIZE(cxl_mix_gran_l1_dport);
+ array = cxl_mix_gran_l1_dport;
+ } else if (port->depth == 3) {
+ array_size = ARRAY_SIZE(cxl_mix_gran_l2_dport);
+ array = cxl_mix_gran_l2_dport;
+ } else {
+ dev_WARN_ONCE(&port->dev, 1, "unexpected depth %d\n",
+ port->depth);
+ return -ENXIO;
+ }
+
+ *port_array = array;
+ *port_array_size = array_size;
+ return 0;
+ }
+
if (port->depth == 1) {
if (is_multi_bridge(port->uport_dev)) {
array_size = ARRAY_SIZE(cxl_root_port);
@@ -1724,10 +1916,182 @@ static void cxl_single_topo_exit(void)
}
}
+#define MIX_GRAN_RP_ID_BASE (NR_MULTI_ROOT + NR_CXL_SINGLE_HOST)
+#define MIX_GRAN_L1U_ID_BASE MIX_GRAN_RP_ID_BASE
+#define MIX_GRAN_L1D_ID_BASE (NR_MEM_MULTI + NR_MEM_SINGLE)
+#define MIX_GRAN_L2U_ID_BASE MIX_GRAN_L1D_ID_BASE
+#define MIX_GRAN_L2D_ID_BASE \
+ (MIX_GRAN_L1D_ID_BASE + NR_CXL_MIX_GRAN_L1_DPORTS)
+#define MIX_GRAN_MEM_ID_BASE (NR_MEM_MULTI + NR_MEM_SINGLE + NR_CXL_RCH)
+
+static void cxl_mix_gran_topo_exit(void)
+{
+ int i;
+
+ for (i = NR_CXL_MIX_GRAN_L2_DPORTS - 1; i >= 0; i--) {
+ platform_device_unregister(cxl_mix_gran_l2_dport[i]);
+ cxl_mix_gran_l2_dport[i] = NULL;
+ }
+ for (i = NR_CXL_MIX_GRAN_L2_SWITCHES - 1; i >= 0; i--) {
+ platform_device_unregister(cxl_mix_gran_l2_uport[i]);
+ cxl_mix_gran_l2_uport[i] = NULL;
+ }
+ for (i = NR_CXL_MIX_GRAN_L1_DPORTS - 1; i >= 0; i--) {
+ platform_device_unregister(cxl_mix_gran_l1_dport[i]);
+ cxl_mix_gran_l1_dport[i] = NULL;
+ }
+ for (i = NR_CXL_MIX_GRAN_ROOT_PORTS - 1; i >= 0; i--) {
+ platform_device_unregister(cxl_mix_gran_l1_uport[i]);
+ cxl_mix_gran_l1_uport[i] = NULL;
+ }
+ for (i = NR_CXL_MIX_GRAN_ROOT_PORTS - 1; i >= 0; i--) {
+ platform_device_unregister(cxl_mix_gran_root_port[i]);
+ cxl_mix_gran_root_port[i] = NULL;
+ }
+ for (i = NR_CXL_MIX_GRAN_HB - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_mix_gran_hb[i];
+
+ if (!pdev)
+ continue;
+ sysfs_remove_link(&pdev->dev.kobj, "physical_node");
+ platform_device_unregister(pdev);
+ cxl_mix_gran_hb[i] = NULL;
+ }
+}
+
+static __init int cxl_mix_gran_topo_init(void)
+{
+ int i, rc;
+
+ for (i = 0; i < NR_CXL_MIX_GRAN_HB; i++) {
+ struct acpi_device *adev = &host_bridge[NR_DEFAULT_BRIDGES + i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_host_bridge",
+ NR_DEFAULT_BRIDGES + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ mock_companion(adev, &pdev->dev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mix_gran_hb[i]);
+ if (rc)
+ goto err;
+
+ mock_pci_bus[NR_DEFAULT_BRIDGES + i].bridge = &pdev->dev;
+ rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
+ "physical_node");
+ if (rc)
+ goto err;
+ }
+
+ /* 1 root port per host bridge */
+ for (i = 0; i < NR_CXL_MIX_GRAN_ROOT_PORTS; i++) {
+ struct platform_device *bridge = cxl_mix_gran_hb[i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_root_port",
+ MIX_GRAN_RP_ID_BASE + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &bridge->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mix_gran_root_port[i]);
+ if (rc)
+ goto err;
+ }
+
+ /* L1 switch uport under each root port */
+ for (i = 0; i < NR_CXL_MIX_GRAN_ROOT_PORTS; i++) {
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_switch_uport",
+ MIX_GRAN_L1U_ID_BASE + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &cxl_mix_gran_root_port[i]->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mix_gran_l1_uport[i]);
+ if (rc)
+ goto err;
+ }
+
+ /* 2 L1 dports per L1 switch */
+ for (i = 0; i < NR_CXL_MIX_GRAN_L1_DPORTS; i++) {
+ struct platform_device *uport =
+ cxl_mix_gran_l1_uport[i / NR_CXL_SWITCH_PORTS];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_switch_dport",
+ MIX_GRAN_L1D_ID_BASE + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &uport->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mix_gran_l1_dport[i]);
+ if (rc)
+ goto err;
+ }
+
+ /* L2 switch uport under each L1 dport */
+ for (i = 0; i < NR_CXL_MIX_GRAN_L2_SWITCHES; i++) {
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_switch_uport",
+ MIX_GRAN_L2U_ID_BASE + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &cxl_mix_gran_l1_dport[i]->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mix_gran_l2_uport[i]);
+ if (rc)
+ goto err;
+ }
+
+ /* 2 L2 dports per L2 switch */
+ for (i = 0; i < NR_CXL_MIX_GRAN_L2_DPORTS; i++) {
+ struct platform_device *uport =
+ cxl_mix_gran_l2_uport[i / NR_CXL_SWITCH_PORTS];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_switch_dport",
+ MIX_GRAN_L2D_ID_BASE + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &uport->dev;
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mix_gran_l2_dport[i]);
+ if (rc)
+ goto err;
+ }
+
+ return 0;
+err:
+ cxl_mix_gran_topo_exit();
+ return rc;
+}
+
static void cxl_mem_exit(void)
{
int i;
+ if (mixed_gran_regions) {
+ for (i = NR_CXL_MIX_GRAN_L2_DPORTS - 1; i >= 0; i--)
+ platform_device_unregister(cxl_mem_mix_gran[i]);
+ return;
+ }
+
for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--)
platform_device_unregister(cxl_rcd[i]);
for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--)
@@ -1736,10 +2100,42 @@ static void cxl_mem_exit(void)
platform_device_unregister(cxl_mem[i]);
}
+static int cxl_mem_mix_gran_init(void)
+{
+ int i, rc;
+
+ for (i = 0; i < NR_CXL_MIX_GRAN_L2_DPORTS; i++) {
+ struct platform_device *dport = cxl_mix_gran_l2_dport[i];
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cxl_mem",
+ MIX_GRAN_MEM_ID_BASE + i);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &dport->dev;
+ set_dev_node(&pdev->dev, i % 2);
+
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem_mix_gran[i]);
+ if (rc)
+ goto err;
+ }
+
+ return 0;
+err:
+ for (i = NR_CXL_MIX_GRAN_L2_DPORTS - 1; i >= 0; i--)
+ platform_device_unregister(cxl_mem_mix_gran[i]);
+ return rc;
+}
+
static int cxl_mem_init(void)
{
int i, rc;
+ if (mixed_gran_regions)
+ return cxl_mem_mix_gran_init();
+
for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) {
struct platform_device *dport = cxl_switch_dport[i];
struct platform_device *pdev;
@@ -1860,7 +2256,10 @@ static __init int cxl_test_init(void)
if (rc)
goto err_gen_pool_add;
- if (interleave_arithmetic == 1) {
+ if (mixed_gran_regions) {
+ cfmws_start = CFMWS_MIX_GRAN_ARRAY_START;
+ cfmws_end = CFMWS_MIX_GRAN_ARRAY_END;
+ } else if (interleave_arithmetic == 1) {
cfmws_start = CFMWS_XOR_ARRAY_START;
cfmws_end = CFMWS_XOR_ARRAY_END;
} else {
@@ -1872,6 +2271,13 @@ static __init int cxl_test_init(void)
if (rc)
goto err_populate;
+ if (mixed_gran_regions) {
+ rc = cxl_mix_gran_topo_init();
+ if (rc)
+ goto err_populate;
+ goto topo_done;
+ }
+
for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
struct acpi_device *adev = &host_bridge[i];
struct platform_device *pdev;
@@ -1945,9 +2351,12 @@ static __init int cxl_test_init(void)
if (rc)
goto err_single;
+topo_done:
cxl_acpi = platform_device_alloc("cxl_acpi", 0);
- if (!cxl_acpi)
- goto err_rch;
+ if (!cxl_acpi) {
+ rc = -ENOMEM;
+ goto err_topo;
+ }
mock_companion(&acpi0017_mock, &cxl_acpi->dev);
acpi0017_mock.dev.bus = &platform_bus_type;
@@ -1955,7 +2364,7 @@ static __init int cxl_test_init(void)
rc = cxl_mock_platform_device_add(cxl_acpi, NULL);
if (rc)
- goto err_rch;
+ goto err_topo;
rc = cxl_mem_init();
if (rc)
@@ -1971,7 +2380,11 @@ static __init int cxl_test_init(void)
cxl_mem_exit();
err_root:
platform_device_unregister(cxl_acpi);
-err_rch:
+err_topo:
+ if (mixed_gran_regions) {
+ cxl_mix_gran_topo_exit();
+ goto err_populate;
+ }
cxl_rch_topo_exit();
err_single:
cxl_single_topo_exit();
@@ -2020,21 +2433,25 @@ static __exit void cxl_test_exit(void)
hmem_test_exit();
cxl_mem_exit();
platform_device_unregister(cxl_acpi);
- cxl_rch_topo_exit();
- cxl_single_topo_exit();
- for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_dport[i]);
- for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
- platform_device_unregister(cxl_switch_uport[i]);
- for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
- platform_device_unregister(cxl_root_port[i]);
- for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
- struct platform_device *pdev = cxl_host_bridge[i];
+ if (mixed_gran_regions) {
+ cxl_mix_gran_topo_exit();
+ } else {
+ cxl_rch_topo_exit();
+ cxl_single_topo_exit();
+ for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
+ platform_device_unregister(cxl_switch_dport[i]);
+ for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
+ platform_device_unregister(cxl_switch_uport[i]);
+ for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
+ platform_device_unregister(cxl_root_port[i]);
+ for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
+ struct platform_device *pdev = cxl_host_bridge[i];
- if (!pdev)
- continue;
- sysfs_remove_link(&pdev->dev.kobj, "physical_node");
- platform_device_unregister(cxl_host_bridge[i]);
+ if (!pdev)
+ continue;
+ sysfs_remove_link(&pdev->dev.kobj, "physical_node");
+ platform_device_unregister(cxl_host_bridge[i]);
+ }
}
depopulate_all_mock_resources();
gen_pool_destroy(cxl_mock_pool);
@@ -2049,6 +2466,9 @@ module_param(extended_linear_cache, bool, 0444);
MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support");
module_param(fail_autoassemble, bool, 0444);
MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region");
+module_param(mixed_gran_regions, bool, 0444);
+MODULE_PARM_DESC(mixed_gran_regions,
+ "Topology supporting mixed granularity regions");
module_init(cxl_test_init);
module_exit(cxl_test_exit);
MODULE_LICENSE("GPL v2");
--
2.37.3
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v4 6/6] Documentation/cxl: Describe mixed-granularity regions
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
` (4 preceding siblings ...)
2026-08-20 23:31 ` [PATCH v4 5/6] cxl/test: Add a topology to test mixed-granularity regions Alison Schofield
@ 2026-08-20 23:31 ` Alison Schofield
2026-08-21 20:35 ` Jonathan Cameron
5 siblings, 1 reply; 18+ messages in thread
From: Alison Schofield @ 2026-08-20 23:31 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Robert Richter
Cc: linux-cxl
Mixed-granularity region support introduces interleave relationships
that are not obvious from the existing CXL documentation. This is
particularly true for the 3-, 6-, and 12-way configurations and for
understanding which configurations permitted by the CXL Specification
are supported by Linux.
Document the mixed-granularity model and Linux's coarse-to-fine
restriction. Include the relevant configurations from CXL 4.0 Section
9.13.1.1 and annotate their Linux support status.
This intentionally repeats information from the CXL Specification.
The specification remains authoritative, but showing the Linux support
policy alongside the legal configurations ensures readers do not
mistake an unsupported Linux setup for a configuration unsupported by
the CXL Specification.
Assisted-by: Claude:Opus-5
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
.../driver-api/cxl/linux/cxl-driver.rst | 135 ++++++++++++++++++
1 file changed, 135 insertions(+)
diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
index dd6dd17dc536..4e56c18294ef 100644
--- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
+++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
@@ -602,6 +602,11 @@ derived from their upstream port connections. In `Cross-Link First` interleave
configurations, the :code:`interleave_granularity` of a decoder is equal to
:code:`parent_interleave_granularity * parent_interleave_ways`.
+When the region granularity is finer than the granularity of an interleaving
+root decoder, the relation inverts: the :code:`interleave_granularity` of a
+decoder is equal to :code:`parent_interleave_granularity / interleave_ways`.
+See `Mixed Granularity`_.
+
At Endpoint
~~~~~~~~~~~
`Endpoint Decoders` are programmed similar to Host Bridge and Switch decoders,
@@ -619,6 +624,136 @@ from HPA to DPA. This is why they must be aware of the entire interleave set.
Linux does not support unbalanced interleave configurations. As a result, all
endpoints in an interleave set must have the same ways and granularity.
+Mixed Granularity
+~~~~~~~~~~~~~~~~~
+Every decoder advances one target every multiple of its own granularity, and
+the decoders below it subdivide the span their parent assigns to a single
+target. Linux supports two orderings of granularity down the hierarchy.
+
+The `Cross-Link First` example above shows the first ordering, where the region
+granularity equals the granularity of the root decoder and granularity coarsens
+toward the endpoints. In the second ordering the region granularity is finer
+than the root decoder's and granularity refines toward the endpoints, reaching
+the region granularity at the innermost interleaving decoder. A region using
+that ordering is a *mixed-granularity* region. A mixed-granularity region
+requires an interleaving root decoder.
+
+Linux supports only monotonic granularity hierarchies, either coarsening or
+refining from the root toward the endpoints. The CXL Specification does not
+require a monotonic ordering, see `Mod3 Interleave Configurations`_.
+
+For an 8-way mixed-granularity region below a 2-way interleaving root decoder
+at 4096, where each host bridge routes through two levels of switch, Linux
+programs::
+
+ Level Ways Granularity
+ ----- ---- -----------
+ Root 2 4096
+ Host bridge 1 4096
+ Upper switch 2 2048
+ Lower switch 2 1024
+ Endpoint 8 1024
+
+Each decoder contributes to an endpoint's region position in proportion to its
+granularity::
+
+ position += target_position *
+ decoder_granularity / region_granularity
+
+The root above selects a host bridge every 4096 bytes, so it advances one
+target every four region positions, while the lower switch advances one target
+every position. When the region granularity equals the root granularity, the
+root advances one target per region position and each level's weight is the
+number of ways below it.
+
+The ways and granularity of a mixed-granularity region must describe the same
+interleave span as the root decoder::
+
+ root_ways * root_granularity == region_ways * region_granularity
+
+A region that does not describe that span either leaves part of the range
+unclaimed or reaches beyond it, and Linux rejects it. A same-granularity
+region below a power-of-two root decoder spans a multiple of the root's range
+rather than one target's share of it, and is not subject to this relationship.
+
+Mod3 Interleave Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A 3-way, 6-way, or 12-way interleave, known as a Mod3 interleave, selects its
+target with a factor-of-three selection rather than from binary interleave
+selector bits alone. CXL 4.0 Section 9.13.1.1 defines the 3-way selection as
+the address above the decoder granularity taken modulo 3. A 6-way selection
+claims one binary HPA bit at the decoder granularity and takes the modulo 3 of
+the address above that bit, and a 12-way selection claims two.
+
+The factor-of-three selection is not an additional binary selector bit, so a
+Mod3 interleave distributes across the hierarchy as::
+
+ 3 = 3
+ 6 = 3 * 2
+ 12 = 3 * 4
+
+The cross-host bridge selection carries the factor of three, and the remaining
+x2 or x4 is binary interleave selection below it. A 6-way region at IGB across
+three host bridges is therefore::
+
+ Device-level region: 6-way @ IGB
+ Cross-host bridge: 3-way @ 2*IGB
+ Below the root: 2-way @ IGB
+
+where both levels describe the same interleave span::
+
+ 3 * (2 * IGB) == 6 * IGB
+
+The CXL Specification defines the legal Mod3 compositions and is normative.
+CXL 4.0 Section 9.13.1.1, "Legal Interleaving Configurations: 12-way, 6-way,
+and 3-way", Tables 9-6, 9-7, and 9-8 list them for a 12-way, 6-way, and 3-way
+device-level interleave at IGB. Those tables are summarized below, annotated
+with the subset Linux supports.
+
+CXL 4.0 Table 9-8, 3-way device-level interleave at IGB::
+
+ Row Cross-host bridge Host bridge Switch Linux
+ --- ----------------- ----------- ------ -----
+ 1 3-way @ IGB none none supported
+
+CXL 4.0 Table 9-7, 6-way device-level interleave at IGB::
+
+ Row Cross-host bridge Host bridge Switch Linux
+ --- ----------------- ----------- ------ -----
+ 1 6-way @ IGB none none supported
+ 2 3-way @ 2*IGB 2-way @ IGB none supported
+ 3 3-way @ 2*IGB none 2-way @ IGB supported
+
+CXL 4.0 Table 9-6, 12-way device-level interleave at IGB::
+
+ Row Cross-host bridge Host bridge Switch Linux
+ --- ----------------- ----------- ------ -----
+ 1 12-way @ IGB none none supported
+ 2 6-way @ 2*IGB 2-way @ IGB none supported
+ 3 6-way @ 2*IGB none 2-way @ IGB supported
+ 4 3-way @ 4*IGB 4-way @ IGB none supported
+ 5 3-way @ 4*IGB none 4-way @ IGB supported
+ 6 3-way @ 4*IGB 2-way @ IGB 2-way @ 2*IGB unsupported
+ 7 3-way @ 4*IGB 2-way @ 2*IGB 2-way @ IGB supported
+
+Table 9-6 row 6 is legal per the CXL Specification and unsupported by Linux.
+Walking it from the root toward the endpoints, granularity goes::
+
+ 4*IGB -> IGB -> 2*IGB
+
+which refines and then coarsens. Row 7 interleaves the same 12 endpoints at
+the same granularity with those two levels exchanged::
+
+ 4*IGB -> 2*IGB -> IGB
+
+which is monotonic. Linux programs row 7 for a user region and assembles an
+auto region whose decoders are programmed that way. An auto region matching
+row 6 is not assembled.
+
+Leaving row 6 unsupported does not prevent a 12-way device-level interleave.
+The specification defines six other legal compositions, all monotonic and
+supported by Linux.
+
Example Configurations
======================
.. toctree::
--
2.37.3
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping
2026-08-20 23:31 ` [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping Alison Schofield
@ 2026-08-20 23:43 ` sashiko-bot
2026-08-21 20:16 ` Alison Schofield
2026-08-21 20:54 ` Jonathan Cameron
2026-08-24 5:46 ` Richard Cheng
2 siblings, 1 reply; 18+ messages in thread
From: sashiko-bot @ 2026-08-20 23:43 UTC (permalink / raw)
To: Alison Schofield; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-After-Free read of device structure in find_pos_and_gran error path.
--
--- Patch [2]: [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping ---
commit 74f53df9f58a0339cb5f29038f2e514904e34cf5
Author: Alison Schofield <alison.schofield@intel.com>
cxl/region: Generalize endpoint position mapping
This commit alters the endpoint position calculation to support
mixed-granularity regions. It weights each decoder's contribution by its
relative granularity instead of assuming a uniform granularity across the
interleave hierarchy.
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 3b640c9ba5a08..4f367feaf6c84 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
[ ... ]
> @@ -1929,9 +1934,10 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> return rc;
> }
> cxlsd = to_cxl_switch_decoder(dev);
> - *ways = cxlsd->cxld.interleave_ways;
> + ways = cxlsd->cxld.interleave_ways;
> + *gran = cxlsd->cxld.interleave_granularity;
>
> - for (int i = 0; i < *ways; i++) {
> + for (int i = 0; i < ways; i++) {
> if (cxlsd->target[i] == port->parent_dport) {
> *pos = i;
> rc = 0;
[Severity: High]
This is a pre-existing issue, but could the error handling path just after
this loop trigger a use-after-free?
Looking at find_pos_and_gran(), if the loop completes without finding a
match, rc remains -ENXIO. Immediately after the loop, put_device(dev) is
called, and then dev_err() formats an error message using cxlsd:
put_device(dev);
if (rc)
dev_err(port->uport_dev,
"failed to find %s:%s in target list of %s\n",
dev_name(&port->dev),
dev_name(port->parent_dport->dport_dev),
dev_name(&cxlsd->cxld.dev));
Since cxlsd is derived from dev, does calling put_device() before dev_err()
release the structure prematurely and cause a read of freed memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787255388.git.alison.schofield@intel.com?part=2
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions
2026-08-20 23:31 ` [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions Alison Schofield
@ 2026-08-20 23:43 ` sashiko-bot
2026-08-21 22:23 ` Alison Schofield
2026-08-21 21:57 ` Jonathan Cameron
1 sibling, 1 reply; 18+ messages in thread
From: sashiko-bot @ 2026-08-20 23:43 UTC (permalink / raw)
To: Alison Schofield; +Cc: linux-cxl
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Incorrect distance calculation for non-mixed-granularity regions with non-power-of-2 root interleaves causes a NULL pointer dereference.
--
--- Patch [3]: [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions ---
commit 13bc48fe2efef1744b5bbf29c756bd07f400ebe3
Author: Alison Schofield <alison.schofield@intel.com>
cxl/region: Support mixed-granularity auto regions
The CXL driver currently assumes the interleaving root has the same
granularity as the region, with each successive interleaving level
becoming coarser toward the endpoints. This patch allows the CXL driver
to assemble an auto region where the root granularity is coarser than
the region granularity.
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4f367feaf6c84..637d1b60a570b 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
[ ... ]
> @@ -1463,26 +1472,21 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> }
>
> cxlsd = to_cxl_switch_decoder(&cxld->dev);
> + iw = cxl_rr->nr_targets;
> +
> if (cxl_rr->nr_targets_set) {
> - int i, distance = 1;
> - struct cxl_region_ref *cxl_rr_iter;
> + int i, distance;
>
> /*
> - * 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 those positions
> + * 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))
> + distance = 1;
> + else
> + distance = cxld->interleave_granularity * iw /
> + p->interleave_granularity;
[Severity: High]
Does this mathematical distance calculation correctly handle non-power-of-2
root interleaves?
When configuring a CXL region with a non-power-of-2 root interleave (like
3-way or 6-way) and multiple endpoints sharing a downstream port, this
formula seems to calculate a distance 3 times smaller than the true
topological stride. This appears to happen because parent_iw is divided
by 3 for address routing, which scales down cxld->interleave_granularity.
If the distance is undersized, check_last_peer() will check an endpoint on
a completely different host bridge:
drivers/cxl/core/region.c:check_last_peer() {
...
cxled_peer = p->targets[pos - distance];
cxlmd_peer = cxled_to_memdev(cxled_peer);
ep_peer = cxl_ep_load(port, cxlmd_peer);
if (ep->dport != ep_peer->dport) {
...
}
Since cxl_ep_load() returns NULL when selecting a peer on a different host
bridge, will ep_peer->dport trigger a NULL pointer dereference here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787255388.git.alison.schofield@intel.com?part=3
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 1/6] cxl/region: Warn on user region position mismatch
2026-08-20 23:31 ` [PATCH v4 1/6] cxl/region: Warn on user region position mismatch Alison Schofield
@ 2026-08-21 19:01 ` Jonathan Cameron
0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-08-21 19:01 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny, Li Ming,
Robert Richter, linux-cxl
On Thu, 20 Aug 2026 16:31:19 -0700
Alison Schofield <alison.schofield@intel.com> wrote:
> User region creation includes a self-test that checks the assigned
> endpoint positions against the position calculation used by auto
> region creation. A mismatch is reported with dev_dbg().
>
> Promote that to a dev_warn() so bugs in the auto region position
> calculation are easier to catch as that calculation is extended to
> support more region configurations.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Seems reasonable to me.
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> ---
>
> drivers/cxl/core/region.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 1e211542b6b6..3b640c9ba5a0 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2218,10 +2218,10 @@ static int cxl_region_attach(struct cxl_region *cxlr,
> int test_pos;
>
> test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range);
> - dev_dbg(&target->cxld.dev,
> - "Test cxl_calc_interleave_pos(): %s test_pos:%d target->pos:%d\n",
> - (test_pos == target->pos) ? "success" : "fail",
> - test_pos, target->pos);
> + if (test_pos != target->pos)
> + dev_warn(&target->cxld.dev,
> + "position mismatch: calculated:%d programmed:%d\n",
> + test_pos, target->pos);
> }
>
> return 0;
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping
2026-08-20 23:43 ` sashiko-bot
@ 2026-08-21 20:16 ` Alison Schofield
0 siblings, 0 replies; 18+ messages in thread
From: Alison Schofield @ 2026-08-21 20:16 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-cxl
On Thu, Aug 20, 2026 at 11:43:08PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] Use-After-Free read of device structure in find_pos_and_gran error path.
> --
The fix for this issue is in the 7.3 CXL Pull Request.
I expect the next version of this patchset will be based on 7.3-rc1 so
this complaint will go away.
https://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl.git/commit/?h=next&id=15da704b732332cc1e8f121f624e5e6c05124c5d
>
> --- Patch [2]: [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping ---
> commit 74f53df9f58a0339cb5f29038f2e514904e34cf5
> Author: Alison Schofield <alison.schofield@intel.com>
>
> cxl/region: Generalize endpoint position mapping
>
> This commit alters the endpoint position calculation to support
> mixed-granularity regions. It weights each decoder's contribution by its
> relative granularity instead of assuming a uniform granularity across the
> interleave hierarchy.
>
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 3b640c9ba5a08..4f367feaf6c84 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> [ ... ]
> > @@ -1929,9 +1934,10 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> > return rc;
> > }
> > cxlsd = to_cxl_switch_decoder(dev);
> > - *ways = cxlsd->cxld.interleave_ways;
> > + ways = cxlsd->cxld.interleave_ways;
> > + *gran = cxlsd->cxld.interleave_granularity;
> >
> > - for (int i = 0; i < *ways; i++) {
> > + for (int i = 0; i < ways; i++) {
> > if (cxlsd->target[i] == port->parent_dport) {
> > *pos = i;
> > rc = 0;
>
> [Severity: High]
> This is a pre-existing issue, but could the error handling path just after
> this loop trigger a use-after-free?
>
> Looking at find_pos_and_gran(), if the loop completes without finding a
> match, rc remains -ENXIO. Immediately after the loop, put_device(dev) is
> called, and then dev_err() formats an error message using cxlsd:
>
> put_device(dev);
>
> if (rc)
> dev_err(port->uport_dev,
> "failed to find %s:%s in target list of %s\n",
> dev_name(&port->dev),
> dev_name(port->parent_dport->dport_dev),
> dev_name(&cxlsd->cxld.dev));
>
> Since cxlsd is derived from dev, does calling put_device() before dev_err()
> release the structure prematurely and cause a read of freed memory?
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787255388.git.alison.schofield@intel.com?part=2
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 6/6] Documentation/cxl: Describe mixed-granularity regions
2026-08-20 23:31 ` [PATCH v4 6/6] Documentation/cxl: Describe " Alison Schofield
@ 2026-08-21 20:35 ` Jonathan Cameron
0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-08-21 20:35 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny, Li Ming,
Robert Richter, linux-cxl
On Thu, 20 Aug 2026 16:31:24 -0700
Alison Schofield <alison.schofield@intel.com> wrote:
Hi Alison,
Can we pull this up to be at top of patch set? People really need
to read this first, so make that easy!
> Mixed-granularity region support introduces interleave relationships
> that are not obvious from the existing CXL documentation. This is
> particularly true for the 3-, 6-, and 12-way configurations and for
> understanding which configurations permitted by the CXL Specification
> are supported by Linux.
>
> Document the mixed-granularity model and Linux's coarse-to-fine
> restriction. Include the relevant configurations from CXL 4.0 Section
> 9.13.1.1 and annotate their Linux support status.
>
> This intentionally repeats information from the CXL Specification.
> The specification remains authoritative, but showing the Linux support
> policy alongside the legal configurations ensures readers do not
> mistake an unsupported Linux setup for a configuration unsupported by
> the CXL Specification.
>
> Assisted-by: Claude:Opus-5
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
My biggest queries are:
- Naming. bike shed time :) I think this evolved to point where 'mixed'
no longer describes what is being built. I'm not sure what is mixed.
- Are we sure people are doing coarse to fine?
They might be - I remember a discussion with Dan way back where I was
arguing that was the natural way round, but he convinced me that fine
as fast as possible made more sense, as about spreading larger hotspots
and linear accesses onto as many paths as possible as quickly as
possible.
Given the reason to do this is either a hardware restriction, or non
power of 2, going fine as fast as possible may still make sense.
Honestly I don't (I think) have any skin in the game here so if this
works for you I am fine with restricting things - as long as we make
it even clearer what is going on!
> ---
> .../driver-api/cxl/linux/cxl-driver.rst | 135 ++++++++++++++++++
> 1 file changed, 135 insertions(+)
>
> diff --git a/Documentation/driver-api/cxl/linux/cxl-driver.rst b/Documentation/driver-api/cxl/linux/cxl-driver.rst
> index dd6dd17dc536..4e56c18294ef 100644
> --- a/Documentation/driver-api/cxl/linux/cxl-driver.rst
> +++ b/Documentation/driver-api/cxl/linux/cxl-driver.rst
> @@ -602,6 +602,11 @@ derived from their upstream port connections. In `Cross-Link First` interleave
> configurations, the :code:`interleave_granularity` of a decoder is equal to
> :code:`parent_interleave_granularity * parent_interleave_ways`.
>
> +When the region granularity is finer than the granularity of an interleaving
> +root decoder, the relation inverts: the :code:`interleave_granularity` of a
> +decoder is equal to :code:`parent_interleave_granularity / interleave_ways`.
> +See `Mixed Granularity`_.
> +
> At Endpoint
> ~~~~~~~~~~~
> `Endpoint Decoders` are programmed similar to Host Bridge and Switch decoders,
> @@ -619,6 +624,136 @@ from HPA to DPA. This is why they must be aware of the entire interleave set.
> Linux does not support unbalanced interleave configurations. As a result, all
> endpoints in an interleave set must have the same ways and granularity.
>
> +Mixed Granularity
> +~~~~~~~~~~~~~~~~~
My main question here is why are we calling them mixed?
From that name I was assuming we were doing
Root 4K
HB 1K
Sw 2K
Where the granularity isn't monotonic.
> +Every decoder advances one target every multiple of its own granularity, and
> +the decoders below it subdivide the span their parent assigns to a single
> +target. Linux supports two orderings of granularity down the hierarchy.
> +
> +The `Cross-Link First` example above shows the first ordering, where the region
> +granularity equals the granularity of the root decoder and granularity coarsens
> +toward the endpoints. In the second ordering the region granularity is finer
> +than the root decoder's and granularity refines toward the endpoints, reaching
> +the region granularity at the innermost interleaving decoder. A region using
> +that ordering is a *mixed-granularity* region. A mixed-granularity region
> +requires an interleaving root decoder.
> +
> +Linux supports only monotonic granularity hierarchies, either coarsening or
> +refining from the root toward the endpoints. The CXL Specification does not
> +require a monotonic ordering, see `Mod3 Interleave Configurations`_.
> +
> +For an 8-way mixed-granularity region below a 2-way interleaving root decoder
> +at 4096, where each host bridge routes through two levels of switch, Linux
> +programs::
> +
> + Level Ways Granularity
> + ----- ---- -----------
> + Root 2 4096
> + Host bridge 1 4096
> + Upper switch 2 2048
> + Lower switch 2 1024
> + Endpoint 8 1024
Given multi switch restrictions, why not just do one level and make the
host bridge do 2 way interleave (to two RPs each of which has a switch below)
I don't think that changes the logic, but it reflects more standard
CXL topology (if no PBR fun involved)
> +
> +Each decoder contributes to an endpoint's region position in proportion to its
> +granularity::
> +
> + position += target_position *
> + decoder_granularity / region_granularity
> +
> +The root above selects a host bridge every 4096 bytes, so it advances one
> +target every four region positions, while the lower switch advances one target
> +every position. When the region granularity equals the root granularity, the
> +root advances one target per region position and each level's weight is the
> +number of ways below it.
> +
> +The ways and granularity of a mixed-granularity region must describe the same
> +interleave span as the root decoder::
> +
> + root_ways * root_granularity == region_ways * region_granularity
> +
> +A region that does not describe that span either leaves part of the range
> +unclaimed or reaches beyond it, and Linux rejects it. A same-granularity
> +region below a power-of-two root decoder spans a multiple of the root's range
> +rather than one target's share of it, and is not subject to this relationship.
> +
> +Mod3 Interleave Configurations
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +A 3-way, 6-way, or 12-way interleave, known as a Mod3 interleave, selects its
> +target with a factor-of-three selection rather than from binary interleave
> +selector bits alone. CXL 4.0 Section 9.13.1.1 defines the 3-way selection as
> +the address above the decoder granularity taken modulo 3. A 6-way selection
> +claims one binary HPA bit at the decoder granularity and takes the modulo 3 of
> +the address above that bit, and a 12-way selection claims two.
> +
> +The factor-of-three selection is not an additional binary selector bit, so a
> +Mod3 interleave distributes across the hierarchy as::
> +
> + 3 = 3
> + 6 = 3 * 2
> + 12 = 3 * 4
> +
> +The cross-host bridge selection carries the factor of three, and the remaining
> +x2 or x4 is binary interleave selection below it. A 6-way region at IGB across
> +three host bridges is therefore::
> +
> + Device-level region: 6-way @ IGB
> + Cross-host bridge: 3-way @ 2*IGB
> + Below the root: 2-way @ IGB
> +
> +where both levels describe the same interleave span::
> +
> + 3 * (2 * IGB) == 6 * IGB
> +
> +The CXL Specification defines the legal Mod3 compositions and is normative.
> +CXL 4.0 Section 9.13.1.1, "Legal Interleaving Configurations: 12-way, 6-way,
> +and 3-way", Tables 9-6, 9-7, and 9-8 list them for a 12-way, 6-way, and 3-way
> +device-level interleave at IGB. Those tables are summarized below, annotated
> +with the subset Linux supports.
> +
> +CXL 4.0 Table 9-8, 3-way device-level interleave at IGB::
> +
> + Row Cross-host bridge Host bridge Switch Linux
> + --- ----------------- ----------- ------ -----
> + 1 3-way @ IGB none none supported
> +
> +CXL 4.0 Table 9-7, 6-way device-level interleave at IGB::
> +
> + Row Cross-host bridge Host bridge Switch Linux
> + --- ----------------- ----------- ------ -----
> + 1 6-way @ IGB none none supported
> + 2 3-way @ 2*IGB 2-way @ IGB none supported
> + 3 3-way @ 2*IGB none 2-way @ IGB supported
> +
> +CXL 4.0 Table 9-6, 12-way device-level interleave at IGB::
> +
> + Row Cross-host bridge Host bridge Switch Linux
> + --- ----------------- ----------- ------ -----
> + 1 12-way @ IGB none none supported
> + 2 6-way @ 2*IGB 2-way @ IGB none supported
> + 3 6-way @ 2*IGB none 2-way @ IGB supported
> + 4 3-way @ 4*IGB 4-way @ IGB none supported
> + 5 3-way @ 4*IGB none 4-way @ IGB supported
> + 6 3-way @ 4*IGB 2-way @ IGB 2-way @ 2*IGB unsupported
> + 7 3-way @ 4*IGB 2-way @ 2*IGB 2-way @ IGB supported
> +
> +Table 9-6 row 6 is legal per the CXL Specification and unsupported by Linux.
but is unsupported by Linux.
(perhaps clearer?)
> +Walking it from the root toward the endpoints, granularity goes::
> +
> + 4*IGB -> IGB -> 2*IGB
> +
> +which refines and then coarsens. Row 7 interleaves the same 12 endpoints at
> +the same granularity with those two levels exchanged::
> +
> + 4*IGB -> 2*IGB -> IGB
> +
> +which is monotonic. Linux programs row 7 for a user region and assembles an
> +auto region whose decoders are programmed that way. An auto region matching
> +row 6 is not assembled.
> +
> +Leaving row 6 unsupported does not prevent a 12-way device-level interleave.
> +The specification defines six other legal compositions, all monotonic and
> +supported by Linux.
Silly question - does anyone actually care about 12 way? :) This would all
be much easier without it.
> +
> Example Configurations
> ======================
> .. toctree::
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping
2026-08-20 23:31 ` [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping Alison Schofield
2026-08-20 23:43 ` sashiko-bot
@ 2026-08-21 20:54 ` Jonathan Cameron
2026-08-24 5:46 ` Richard Cheng
2 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-08-21 20:54 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny, Li Ming,
Robert Richter, linux-cxl
On Thu, 20 Aug 2026 16:31:20 -0700
Alison Schofield <alison.schofield@intel.com> wrote:
> Endpoint position calculation currently relies on the requirement that
> an interleaving root have the same granularity as the region. Auto
> region creation builds the position by multiplying by each parent
> decoder's ways, while user region creation selects the root target
decoders' (more than one and belongs to I think)
> with 'pos % ways'. Those calculations are sufficient under the current
> granularity restriction.
>
So far no definition of mixed-granularity - hence suggestion to drag
the docs patch ahead of this one.
> In order to support mixed-granularity regions, that granularity
> restriction will need to be removed so decoder granularity can change
> between levels of the interleave hierarchy. The position calculation
> needs to account for those changes to produce the correct endpoint
> ordering.
>
> Change the position calculation so each decoder's contribution is
> weighted by its granularity relative to the region granularity:
>
> position += target_pos *
> (decoder_granularity / region_granularity)
>
> Use the same relationship to select the root target during user region
> creation.
>
> For currently supported regions, the new weighted calculation reduces
> to the existing position calculation and produces identical endpoint
> positions.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> ---
> drivers/cxl/core/region.c | 60 ++++++++++++++++++++++-----------------
> 1 file changed, 34 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 3b640c9ba5a0..4f367feaf6c8 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -1805,9 +1805,14 @@ static int cxl_region_attach_position(struct cxl_region *cxlr,
> struct cxl_decoder *cxld = &cxlsd->cxld;
> int iw = cxld->interleave_ways;
> struct cxl_port *iter;
> - int rc;
> + int root_pos = pos, rc;
>
> - if (dport != cxlrd->cxlsd.target[pos % iw]) {
> + /* Root target selection advances at root-granularity intervals */
> + if (iw > 1)
> + root_pos = pos * cxlr->params.interleave_granularity /
> + cxld->interleave_granularity;
> +
> + if (dport != cxlrd->cxlsd.target[root_pos % iw]) {
> dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n",
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
> dev_name(&cxlrd->cxlsd.cxld.dev));
> @@ -1908,13 +1913,13 @@ static int match_switch_decoder_by_range(struct device *dev,
> return (r1->start == r2->start && r1->end == r2->end);
> }
>
> -static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> - int *pos, int *ways)
> +static int find_pos_and_gran(struct cxl_port *port, struct range *range,
> + int *pos, int *gran)
> {
> struct cxl_switch_decoder *cxlsd;
> struct cxl_port *parent;
> struct device *dev;
> - int rc = -ENXIO;
> + int ways, rc = -ENXIO;
Trivial and perhaps just me, but I really don't like combining declarations with
and without assignments on one line.
>
> parent = parent_port_of(port);
> if (!parent)
> @@ -1929,9 +1934,10 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> return rc;
> }
> cxlsd = to_cxl_switch_decoder(dev);
> - *ways = cxlsd->cxld.interleave_ways;
> + ways = cxlsd->cxld.interleave_ways;
> + *gran = cxlsd->cxld.interleave_granularity;
>
> - for (int i = 0; i < *ways; i++) {
> + for (int i = 0; i < ways; i++) {
> if (cxlsd->target[i] == port->parent_dport) {
> *pos = i;
> rc = 0;
> @@ -1955,13 +1961,16 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> * @cxled: endpoint decoder member of given region
> * @hpa_range: translated HPA range of the endpoint
> *
> - * The endpoint position is calculated by traversing the topology from
> - * the endpoint to the root decoder and iteratively applying this
> - * calculation:
> + * The endpoint position is calculated by traversing the topology from the
> + * endpoint to the root decoder and accumulating the contribution of each
> + * decoder level:
> *
> - * position = position * parent_ways + parent_pos;
> + * position += parent_pos * (parent_granularity / region_granularity);
> *
> - * ...where @position is inferred from switch and root decoder target lists.
> + * ...where @parent_pos is inferred from switch and root decoder target
> + * lists, and the multiplier is the number of region positions that the
> + * level's granularity spans. A level that selects a single target
> + * contributes nothing.
Wrap is a bit short. I don't care much but you made one line above longer
and one line here shorter. So if going to do that, target 80 chars max
for any line you have to touch.
> *
> * Return: position >= 0 on success
> * -ENXIO on failure
> @@ -1971,7 +1980,8 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
> {
> struct cxl_port *iter, *port = cxled_to_port(cxled);
> struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> - int parent_ways = 0, parent_pos = 0, pos = 0;
> + int gran = cxled->cxld.interleave_granularity;
> + int parent_gran = 0, parent_pos = 0, pos = 0;
> int rc;
>
> /*
> @@ -1984,20 +1994,18 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
> * | | | |
> * mem0 mem1 mem2 mem3
> *
> - * In the example the calculator will iterate twice. The first iteration
> - * uses the mem position in the host-bridge and the ways of the host-
> - * bridge to generate the first, or local, position. The second
> - * iteration uses the host-bridge position in the root_port and the ways
> - * of the root_port to refine the position.
> + * The region and the root decoder interleave at granularity g, so
> + * each host-bridge decoder interleaves at 2g and spans two region
> + * positions while the root decoder spans one.
I've read this a few times and don't follow it. What are region positions?
I was assuming positions in the region. In which case the root decoder
spans 4, the host bridges 2. So I guess not that?
> *
> * A trace of the calculation per endpoint looks like this:
> - * mem0: pos = 0 * 2 + 0 mem2: pos = 0 * 2 + 0
> - * pos = 0 * 2 + 0 pos = 0 * 2 + 1
> + * mem0: pos += 0 * 2 mem2: pos += 0 * 2
> + * pos += 0 * 1 pos += 1 * 1
> * pos: 0 pos: 1
> *
> - * mem1: pos = 0 * 2 + 1 mem3: pos = 0 * 2 + 1
> - * pos = 1 * 2 + 0 pos = 1 * 2 + 1
> - * pos: 2 pos = 3
> + * mem1: pos += 1 * 2 mem3: pos += 1 * 2
> + * pos += 0 * 1 pos += 1 * 1
> + * pos: 2 pos: 3
> *
> * Note that while this example is simple, the method applies to more
> * complex topologies, including those with switches.
> @@ -2008,12 +2016,12 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
> if (is_cxl_root(iter))
> break;
>
> - rc = find_pos_and_ways(iter, hpa_range, &parent_pos,
> - &parent_ways);
> + rc = find_pos_and_gran(iter, hpa_range, &parent_pos,
> + &parent_gran);
> if (rc)
> return rc;
>
> - pos = pos * parent_ways + parent_pos;
> + pos += parent_pos * (parent_gran / gran);
> }
>
> dev_dbg(&cxlmd->dev,
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions
2026-08-20 23:31 ` [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions Alison Schofield
2026-08-20 23:43 ` sashiko-bot
@ 2026-08-21 21:57 ` Jonathan Cameron
1 sibling, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-08-21 21:57 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny, Li Ming,
Robert Richter, linux-cxl
On Thu, 20 Aug 2026 16:31:21 -0700
Alison Schofield <alison.schofield@intel.com> 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.
With my fussy hat on (I blame Friday!), guidance for commit message wrap is
75 chars so bit longer than here.
>
> The CXL driver currently assumes the interleaving root has the same
> granularity as the region, with each successive interleaving level
> becoming coarser toward the endpoints. This prevents the driver from
> assembling an auto region where the root granularity is coarser than
> the region granularity.
>
> 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
>
> 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.
>
> Require the root and region to describe the same interleave span:
>
> 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>
This interleave stuff is a rapid path to a headache :( - I vaguely wondered if
we can use a few local variable names to make it clear which ig and which iw
each one is.
I'll go as far as I saying I ran some "paper tests" and it seems right
but I've been out of this stuff long enough I'm not feeling that confident.
Good thing you have tests in a later patch :)
> ---
> drivers/cxl/core/region.c | 114 ++++++++++++++++++++++++--------------
> 1 file changed, 73 insertions(+), 41 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4f367feaf6c8..637d1b60a570 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -1434,6 +1434,16 @@ 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;
return cxld->interleave_ways > 1 &&
cxld->interleave_granularity > cxlr->params.interleave_granularity;
I'd go a little long to improve readability.
> +}
...
> +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) {
This took me a while. It's specific case of more general one that region
granularity can't be greater than any decoder granularity in the path.
I was trying to figure out what was special about the root :(
> + dev_dbg(&cxlr->dev,
> + "granularity %d exceeds root decoder granularity %d\n",
> + p->interleave_granularity, root_ig);
> + return -ENXIO;
> + }
> +
> + /* Same-gran power-of-two regions may span multiple root targets */
> + if (is_power_of_2(root_iw) && p->interleave_granularity == root_ig)
So this is our original fine to coarse check dropping out nice and early?
What is this letting through that doesn't get through next check anyway?
If power of 2 and root_ig == region_ig aren't we guaranteed that
root_iw == region_iw and next test passes anyway.
Basically I'm lost on what this test is here for. What do you mean by
span multiple root targets that isn't true of the x12?
> + return 0;
> +
> + /* Mixed-gran regions must span exactly one root interleave */
Maths is fine, but I'm getting confused by terms. This is talking I think
about complete interleave cycles or maybe strides depending on definitions?
> + if (root_iw * root_ig != p->interleave_ways * p->interleave_granularity) {
> + dev_dbg(&cxlr->dev,
> + "%d ways at %d does not span root decoder %d ways at %d\n",
> + p->interleave_ways, p->interleave_granularity, root_iw,
> + root_ig);
> + return -ENXIO;
> + }
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 4/6] cxl/region: Support mixed-granularity user created regions
2026-08-20 23:31 ` [PATCH v4 4/6] cxl/region: Support mixed-granularity user created regions Alison Schofield
@ 2026-08-21 22:00 ` Jonathan Cameron
0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-08-21 22:00 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny, Li Ming,
Robert Richter, linux-cxl
On Thu, 20 Aug 2026 16:31:22 -0700
Alison Schofield <alison.schofield@intel.com> wrote:
> User region creation currently requires an interleaving root to have
> the same granularity as the region.
>
> That rejects valid mixed-granularity layouts where the region
> granularity is finer than the root granularity. A region granularity
> coarser than the root remains invalid because it produces incorrect
> DPA translations.
>
> Allow a region granularity finer than an interleaving root and
> continue to reject a coarser granularity. Validate the complete
> ways/granularity relationship when the region endpoints are attached.
>
> For example, CXL 4.0 Section 9.13.1.1 Table 9-7 describes a 6-way
> region at IGB across three host bridges as a 3-way root interleave at
> 2 * IGB with a 2-way interleave at IGB below it.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
> ---
> drivers/cxl/core/region.c | 11 ++---------
> 1 file changed, 2 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 637d1b60a570..4bbfb7cd7e01 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -571,15 +571,8 @@ static int set_interleave_granularity(struct cxl_region *cxlr, int val)
> if (rc)
> return rc;
>
> - /*
> - * When the host-bridge is interleaved, disallow region granularity !=
> - * root granularity. Regions with a granularity less than the root
> - * interleave result in needing multiple endpoints to support a single
> - * slot in the interleave (possible to support in the future). Regions
> - * with a granularity greater than the root interleave result in invalid
> - * DPA translations (invalid to support).
> - */
> - if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
> + /* Region granularity must not be coarser than an interleaving root's */
> + if (cxld->interleave_ways > 1 && val > cxld->interleave_granularity)
> return -EINVAL;
>
> lockdep_assert_held_write(&cxl_rwsem.region);
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 5/6] cxl/test: Add a topology to test mixed-granularity regions
2026-08-20 23:31 ` [PATCH v4 5/6] cxl/test: Add a topology to test mixed-granularity regions Alison Schofield
@ 2026-08-21 22:07 ` Jonathan Cameron
0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-08-21 22:07 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny, Li Ming,
Robert Richter, linux-cxl
On Thu, 20 Aug 2026 16:31:23 -0700
Alison Schofield <alison.schofield@intel.com> wrote:
> Add a cxl_test topology for mixed-granularity region testing.
>
> modprobe cxl_test mixed_gran_regions=1 creates three host bridges,
> each with a passthrough host-bridge port above two switch levels
> and four endpoints, for twelve endpoints total.
Why the pass through HB?
I queried the 2 levels of switches in the docs patch - maybe
you already answered that (I haven't checked just yet)
The spec examples that do multi level interleave are all cross-HB, HB and
then switch.
>
> Two CFMWS windows are added:
> 1) A 2-way window at 4K gran over two host bridges, that hosts 8-way
> regions at 1K.
> 2) A 3-way window at 512 gran over three host bridges, that hosts
> 6-way regions at 256.
>
> Both windows also host the same-granularity regions their ways allow.
>
> Register every mock device through cxl_mock_platform_device_add() so
> the mock array is populated before platform_device_add(), matching the
> ordering the rest of cxl_test relies on to avoid the endpoint-probe
> race in commit d90f236f8b9e ("cxl/test: Update mock dev array before
> calling platform_device_add()").
>
> NULL-check the legacy topology arrays, which are left empty when
> mixed_gran_regions=1 selects the new topology.
>
> The default and existing special module params are unaffected.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions
2026-08-20 23:43 ` sashiko-bot
@ 2026-08-21 22:23 ` Alison Schofield
0 siblings, 0 replies; 18+ messages in thread
From: Alison Schofield @ 2026-08-21 22:23 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-cxl
On Thu, Aug 20, 2026 at 11:43:19PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Incorrect distance calculation for non-mixed-granularity regions with non-power-of-2 root interleaves causes a NULL pointer dereference.
> --
The arithmetic observation is correct. For a modulo-3 root, the formula
does not account for the factor of three in the positional spacing.
However, that case cannot reach check_last_peer(). A same-granularity
region below a modulo-3 root must match the root interleave width, which
places one region position per host bridge. No dport is shared, so
check_last_peer() is not called. The mixed-granularity cases that can
share a dport take the distance = 1 path instead.
Rather than add arithmetic for an unreachable case, I will add a comment
to v5 documenting that invariant.
Separately, the report exposed that a missing peer mapping is
dereferenced without a NULL check. Posting a standalone patch to
guard against that so a future violation fails region creation rather
than causing an oops.
-- Alison
>
> --- Patch [3]: [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions ---
> commit 13bc48fe2efef1744b5bbf29c756bd07f400ebe3
> Author: Alison Schofield <alison.schofield@intel.com>
>
> cxl/region: Support mixed-granularity auto regions
>
> The CXL driver currently assumes the interleaving root has the same
> granularity as the region, with each successive interleaving level
> becoming coarser toward the endpoints. This patch allows the CXL driver
> to assemble an auto region where the root granularity is coarser than
> the region granularity.
>
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 4f367feaf6c84..637d1b60a570b 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> [ ... ]
> > @@ -1463,26 +1472,21 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> > }
> >
> > cxlsd = to_cxl_switch_decoder(&cxld->dev);
> > + iw = cxl_rr->nr_targets;
> > +
> > if (cxl_rr->nr_targets_set) {
> > - int i, distance = 1;
> > - struct cxl_region_ref *cxl_rr_iter;
> > + int i, distance;
> >
> > /*
> > - * 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 those positions
> > + * 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))
> > + distance = 1;
> > + else
> > + distance = cxld->interleave_granularity * iw /
> > + p->interleave_granularity;
>
> [Severity: High]
> Does this mathematical distance calculation correctly handle non-power-of-2
> root interleaves?
>
> When configuring a CXL region with a non-power-of-2 root interleave (like
> 3-way or 6-way) and multiple endpoints sharing a downstream port, this
> formula seems to calculate a distance 3 times smaller than the true
> topological stride. This appears to happen because parent_iw is divided
> by 3 for address routing, which scales down cxld->interleave_granularity.
>
> If the distance is undersized, check_last_peer() will check an endpoint on
> a completely different host bridge:
>
> drivers/cxl/core/region.c:check_last_peer() {
> ...
> cxled_peer = p->targets[pos - distance];
> cxlmd_peer = cxled_to_memdev(cxled_peer);
> ep_peer = cxl_ep_load(port, cxlmd_peer);
> if (ep->dport != ep_peer->dport) {
> ...
> }
>
> Since cxl_ep_load() returns NULL when selecting a peer on a different host
> bridge, will ep_peer->dport trigger a NULL pointer dereference here?
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787255388.git.alison.schofield@intel.com?part=3
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping
2026-08-20 23:31 ` [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping Alison Schofield
2026-08-20 23:43 ` sashiko-bot
2026-08-21 20:54 ` Jonathan Cameron
@ 2026-08-24 5:46 ` Richard Cheng
2 siblings, 0 replies; 18+ messages in thread
From: Richard Cheng @ 2026-08-24 5:46 UTC (permalink / raw)
To: Alison Schofield
Cc: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Vishal Verma,
Ira Weiny, Li Ming, Robert Richter, linux-cxl
On Thu, Aug 20, 2026 at 04:31:20PM +0800, Alison Schofield wrote:
Hi Alison,
> Endpoint position calculation currently relies on the requirement that
> an interleaving root have the same granularity as the region. Auto
> region creation builds the position by multiplying by each parent
> decoder's ways, while user region creation selects the root target
> with 'pos % ways'. Those calculations are sufficient under the current
> granularity restriction.
>
> In order to support mixed-granularity regions, that granularity
> restriction will need to be removed so decoder granularity can change
> between levels of the interleave hierarchy. The position calculation
> needs to account for those changes to produce the correct endpoint
> ordering.
>
> Change the position calculation so each decoder's contribution is
> weighted by its granularity relative to the region granularity:
>
> position += target_pos *
> (decoder_granularity / region_granularity)
>
This looks correct to me.
Please see the below comment, having a question there.
> Use the same relationship to select the root target during user region
> creation.
>
> For currently supported regions, the new weighted calculation reduces
> to the existing position calculation and produces identical endpoint
> positions.
>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> ---
> drivers/cxl/core/region.c | 60 ++++++++++++++++++++++-----------------
> 1 file changed, 34 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 3b640c9ba5a0..4f367feaf6c8 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -1805,9 +1805,14 @@ static int cxl_region_attach_position(struct cxl_region *cxlr,
> struct cxl_decoder *cxld = &cxlsd->cxld;
> int iw = cxld->interleave_ways;
> struct cxl_port *iter;
> - int rc;
> + int root_pos = pos, rc;
>
> - if (dport != cxlrd->cxlsd.target[pos % iw]) {
> + /* Root target selection advances at root-granularity intervals */
> + if (iw > 1)
> + root_pos = pos * cxlr->params.interleave_granularity /
> + cxld->interleave_granularity;
> +
> + if (dport != cxlrd->cxlsd.target[root_pos % iw]) {
> dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n",
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
> dev_name(&cxlrd->cxlsd.cxld.dev));
> @@ -1908,13 +1913,13 @@ static int match_switch_decoder_by_range(struct device *dev,
> return (r1->start == r2->start && r1->end == r2->end);
> }
>
> -static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> - int *pos, int *ways)
> +static int find_pos_and_gran(struct cxl_port *port, struct range *range,
> + int *pos, int *gran)
> {
> struct cxl_switch_decoder *cxlsd;
> struct cxl_port *parent;
> struct device *dev;
> - int rc = -ENXIO;
> + int ways, rc = -ENXIO;
>
> parent = parent_port_of(port);
> if (!parent)
> @@ -1929,9 +1934,10 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> return rc;
> }
> cxlsd = to_cxl_switch_decoder(dev);
> - *ways = cxlsd->cxld.interleave_ways;
> + ways = cxlsd->cxld.interleave_ways;
> + *gran = cxlsd->cxld.interleave_granularity;
>
> - for (int i = 0; i < *ways; i++) {
> + for (int i = 0; i < ways; i++) {
> if (cxlsd->target[i] == port->parent_dport) {
> *pos = i;
> rc = 0;
> @@ -1955,13 +1961,16 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range,
> * @cxled: endpoint decoder member of given region
> * @hpa_range: translated HPA range of the endpoint
> *
> - * The endpoint position is calculated by traversing the topology from
> - * the endpoint to the root decoder and iteratively applying this
> - * calculation:
> + * The endpoint position is calculated by traversing the topology from the
> + * endpoint to the root decoder and accumulating the contribution of each
> + * decoder level:
> *
> - * position = position * parent_ways + parent_pos;
> + * position += parent_pos * (parent_granularity / region_granularity);
> *
> - * ...where @position is inferred from switch and root decoder target lists.
> + * ...where @parent_pos is inferred from switch and root decoder target
> + * lists, and the multiplier is the number of region positions that the
> + * level's granularity spans. A level that selects a single target
> + * contributes nothing.
> *
> * Return: position >= 0 on success
> * -ENXIO on failure
> @@ -1971,7 +1980,8 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
> {
> struct cxl_port *iter, *port = cxled_to_port(cxled);
> struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> - int parent_ways = 0, parent_pos = 0, pos = 0;
> + int gran = cxled->cxld.interleave_granularity;
> + int parent_gran = 0, parent_pos = 0, pos = 0;
> int rc;
>
The "gran" here is using EP decoder's granularity, I think maybe th region
granularity is correct ?
They're normally equal, but in the scenario of normalized-addressing auto regions
they're not.
In that case the EP decoder stays in passthrough mode, for example 1W1/IG256,
while ccxl_prm_setup_root() discover a translated region at IW2/IG4K
For root target 1, this code calculates
1 * (4096/256) = 16
The correct region position is 1, a 2-way region only has pos 0 and 1.
I saw in v3 the regrion granularity was passed into this function explicitly,
maybe I know the reason to make this change if I am missing anything there.
Best regards,
Richard Cheng.
> /*
> @@ -1984,20 +1994,18 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
> * | | | |
> * mem0 mem1 mem2 mem3
> *
> - * In the example the calculator will iterate twice. The first iteration
> - * uses the mem position in the host-bridge and the ways of the host-
> - * bridge to generate the first, or local, position. The second
> - * iteration uses the host-bridge position in the root_port and the ways
> - * of the root_port to refine the position.
> + * The region and the root decoder interleave at granularity g, so
> + * each host-bridge decoder interleaves at 2g and spans two region
> + * positions while the root decoder spans one.
> *
> * A trace of the calculation per endpoint looks like this:
> - * mem0: pos = 0 * 2 + 0 mem2: pos = 0 * 2 + 0
> - * pos = 0 * 2 + 0 pos = 0 * 2 + 1
> + * mem0: pos += 0 * 2 mem2: pos += 0 * 2
> + * pos += 0 * 1 pos += 1 * 1
> * pos: 0 pos: 1
> *
> - * mem1: pos = 0 * 2 + 1 mem3: pos = 0 * 2 + 1
> - * pos = 1 * 2 + 0 pos = 1 * 2 + 1
> - * pos: 2 pos = 3
> + * mem1: pos += 1 * 2 mem3: pos += 1 * 2
> + * pos += 0 * 1 pos += 1 * 1
> + * pos: 2 pos: 3
> *
> * Note that while this example is simple, the method applies to more
> * complex topologies, including those with switches.
> @@ -2008,12 +2016,12 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
> if (is_cxl_root(iter))
> break;
>
> - rc = find_pos_and_ways(iter, hpa_range, &parent_pos,
> - &parent_ways);
> + rc = find_pos_and_gran(iter, hpa_range, &parent_pos,
> + &parent_gran);
> if (rc)
> return rc;
>
> - pos = pos * parent_ways + parent_pos;
> + pos += parent_pos * (parent_gran / gran);
> }
>
> dev_dbg(&cxlmd->dev,
> --
> 2.37.3
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-08-24 5:46 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-20 23:31 [PATCH v4 0/6] cxl: Support mixed-granularity region interleaves Alison Schofield
2026-08-20 23:31 ` [PATCH v4 1/6] cxl/region: Warn on user region position mismatch Alison Schofield
2026-08-21 19:01 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 2/6] cxl/region: Generalize endpoint position mapping Alison Schofield
2026-08-20 23:43 ` sashiko-bot
2026-08-21 20:16 ` Alison Schofield
2026-08-21 20:54 ` Jonathan Cameron
2026-08-24 5:46 ` Richard Cheng
2026-08-20 23:31 ` [PATCH v4 3/6] cxl/region: Support mixed-granularity auto regions Alison Schofield
2026-08-20 23:43 ` sashiko-bot
2026-08-21 22:23 ` Alison Schofield
2026-08-21 21:57 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 4/6] cxl/region: Support mixed-granularity user created regions Alison Schofield
2026-08-21 22:00 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 5/6] cxl/test: Add a topology to test mixed-granularity regions Alison Schofield
2026-08-21 22:07 ` Jonathan Cameron
2026-08-20 23:31 ` [PATCH v4 6/6] Documentation/cxl: Describe " Alison Schofield
2026-08-21 20:35 ` Jonathan Cameron
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.