[PATCH v3 5/9] cxl/region: Allow mixed-granularity regions
Alison Schofield <[email protected]> Thu, 30 Jul 2026 15:20:25 -0700
| Newsgroups | org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <93bd16e6a327fcd037b5f1206c7bc15f5aa9b305.1785444498.git.alison.schofield@intel.com> |
The sysfs granularity rule required region_gran == root_gran whenever the root decoder interleaves. That blocked the legal mixed-granularity layouts permitted by CXL Spec 4.0 Section 9.13.1 and made every 6-way and 12-way configuration in Tables 9-6, 9-7, and 9-8 impossible to create. Auto regions skipped the sysfs path entirely and so had no equivalent gate. Replace the equality rule with is_ig_allowed(), which permits any region granularity not greater than the root's, and apply it at attach time so auto regions get the same rule. Add a ways*gran span identity check for 3-way-family roots: a 3-way interleave consumes no HPA selector bits, so selector containment cannot prove that the root and region cover the same address span, and the identity restores that constraint. Power-of-2 roots get the equivalent constraint from selector containment for free. Signed-off-by: Alison Schofield <[email protected]> --- drivers/cxl/core/region.c | 53 +++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 938781c9bc5c..3919f64e2467 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -559,10 +559,27 @@ static ssize_t interleave_granularity_show(struct device *dev, return sysfs_emit(buf, "%d\n", p->interleave_granularity); } +/** + * is_ig_allowed() - Check region granularity against the root decoder + * @cxlrd: root decoder + * @ig: proposed region granularity + * + * Return: true when the root does not interleave or @ig is no greater than + * the root decoder granularity. + */ +static inline bool is_ig_allowed(struct cxl_root_decoder *cxlrd, int ig) +{ + struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; + + if (cxld->interleave_ways <= 1) + return true; + + return ig <= cxld->interleave_granularity; +} + static int set_interleave_granularity(struct cxl_region *cxlr, int val) { struct cxl_root_decoder *cxlrd = cxlr->cxlrd; - struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; struct cxl_region_params *p = &cxlr->params; int rc; u16 ig; @@ -571,15 +588,7 @@ 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) + if (!is_ig_allowed(cxlrd, val)) return -EINVAL; lockdep_assert_held_write(&cxl_rwsem.region); @@ -2278,6 +2287,8 @@ static int cxl_region_attach(struct cxl_region *cxlr, struct cxl_dport *dport; int rc = -ENXIO; int root_pos_per_target; + int root_ways = cxlrd->cxlsd.cxld.interleave_ways; + int root_gran = cxlrd->cxlsd.cxld.interleave_granularity; rc = check_interleave_cap(&cxled->cxld, p->interleave_ways, p->interleave_granularity); @@ -2310,6 +2321,28 @@ static int cxl_region_attach(struct cxl_region *cxlr, return -ENXIO; } + if (!is_ig_allowed(cxlrd, p->interleave_granularity)) { + dev_dbg(&cxlr->dev, + "ig %d incompatible with root ways %d ig %d\n", + p->interleave_granularity, root_ways, root_gran); + return -ENXIO; + } + + /* + * A 3-way-family root contributes no selector bits, so selector + * validation cannot confirm that the root and region cover the same + * interleave span. Check the span explicitly. + */ + if (!is_power_of_2(root_ways) && + (u64)p->interleave_ways * p->interleave_granularity != + (u64)root_ways * root_gran) { + dev_dbg(&cxlr->dev, + "region ways*gran (%d*%d) != root ways*gran (%d*%d)\n", + p->interleave_ways, p->interleave_granularity, + root_ways, root_gran); + return -ENXIO; + } + if (p->nr_targets >= p->interleave_ways) { dev_dbg(&cxlr->dev, "region already has %d endpoints\n", p->nr_targets); -- 2.37.3