[PATCH v3 4/9] cxl/region: Account for mixed-granularity in position calculations
Alison Schofield <[email protected]> Thu, 30 Jul 2026 15:20:24 -0700
| Newsgroups | org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <05d8d1f002757c53bdb61175e937e42991c3f465.1785444498.git.alison.schofield@intel.com> |
Endpoint positions identify the order in which a region routes addresses to its endpoint decoders. The existing calculation assumes the root target changes at every region position. In a mixed-granularity region, one root target instead covers multiple positions before the next root target is selected. Add root_positions_per_target() to express that ratio and use it when validating root targets, checking peer distance, and calculating endpoint positions. Keep the existing position recurrence for same-granularity regions and use each decoder level's granularity when calculating positions for mixed-granularity regions. Remove the temporary mixed-granularity reject now that endpoint positions match the selector layout. Signed-off-by: Alison Schofield <[email protected]> --- drivers/cxl/core/region.c | 109 +++++++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 871dedd37cc8..938781c9bc5c 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1569,6 +1569,49 @@ static inline bool cxl_region_is_mixed_gran(struct cxl_region *cxlr) cxld->interleave_granularity > cxlr->params.interleave_granularity; } +/** + * root_positions_per_target() - Count positions covered by a root target + * @cxlr: region + * + * In a mixed-granularity region, a root target covers + * ``root_granularity / region_granularity`` consecutive region positions. + * A same-granularity root covers one position. + * + * Return: the number of region positions covered by one root target. + */ +static inline int root_positions_per_target(struct cxl_region *cxlr) +{ + struct cxl_root_decoder *cxlrd = cxlr->cxlrd; + + if (!cxl_region_is_mixed_gran(cxlr)) + return 1; + + return cxlrd->cxlsd.cxld.interleave_granularity / + cxlr->params.interleave_granularity; +} + +/** + * peer_pos_distance() - Calculate the distance between same-dport endpoints + * @cxlr: region under construction + * @fanout: product of the ancestor switch ways + * @iw: port decoder interleave ways + * + * Mixed-granularity routing groups the endpoints below a dport into + * consecutive region positions. Same-granularity routing spaces them by + * the root ways and the switch fan-out above the port. + * + * Return: the distance in region positions. + */ +static int peer_pos_distance(struct cxl_region *cxlr, int fanout, int iw) +{ + int root_iw = cxlr->cxlrd->cxlsd.cxld.interleave_ways; + + if (cxl_region_is_mixed_gran(cxlr)) + return 1; + + return root_iw * fanout * iw; +} + /** * derive_port_granularity() - Calculate the granularity for a port decoder * @port: port being configured @@ -1645,7 +1688,6 @@ static int cxl_port_setup_targets(struct cxl_port *port, struct cxl_endpoint_decoder *cxled) { struct cxl_root_decoder *cxlrd = cxlr->cxlrd; - int root_iw = cxlrd->cxlsd.cxld.interleave_ways; struct cxl_port *parent_port = to_cxl_port(port->dev.parent); struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); @@ -1689,7 +1731,7 @@ static int cxl_port_setup_targets(struct cxl_port *port, for (int i = 0; i < cxl_rr->nr_targets_set; i++) if (ep->dport == cxlsd->target[i]) { rc = check_last_peer(cxled, ep, cxl_rr, - root_iw * fanout * iw); + peer_pos_distance(cxlr, fanout, iw)); if (rc) return rc; goto out_target_set; @@ -1947,14 +1989,16 @@ static int cxl_region_attach_position(struct cxl_region *cxlr, struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd; struct cxl_decoder *cxld = &cxlsd->cxld; + int root_pos_per_target = root_positions_per_target(cxlr); int iw = cxld->interleave_ways; struct cxl_port *iter; int rc; - if (dport != cxlrd->cxlsd.target[pos % iw]) { - dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n", + if (dport != cxlrd->cxlsd.target[pos / root_pos_per_target % iw]) { + dev_dbg(&cxlr->dev, + "%s:%s invalid target position for %s (positions per target %d)\n", dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), - dev_name(&cxlrd->cxlsd.cxld.dev)); + dev_name(&cxlrd->cxlsd.cxld.dev), root_pos_per_target); return -ENXIO; } @@ -2053,7 +2097,7 @@ static int match_switch_decoder_by_range(struct device *dev, } static int find_pos_and_ways(struct cxl_port *port, struct range *range, - int *pos, int *ways) + int *pos, int *ways, int *gran) { struct cxl_switch_decoder *cxlsd; struct cxl_port *parent; @@ -2074,6 +2118,7 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range, } cxlsd = to_cxl_switch_decoder(dev); *ways = cxlsd->cxld.interleave_ways; + *gran = cxlsd->cxld.interleave_granularity; for (int i = 0; i < *ways; i++) { if (cxlsd->target[i] == port->parent_dport) { @@ -2098,6 +2143,8 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range, * cxl_calc_interleave_pos() - calculate an endpoint position in a region * @cxled: endpoint decoder member of given region * @hpa_range: translated HPA range of the endpoint + * @root_pos_per_target: region positions under one root target + * @region_gran: region interleave granularity * * The endpoint position is calculated by traversing the topology from * the endpoint to the root decoder and iteratively applying this @@ -2107,15 +2154,25 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range, * * ...where @position is inferred from switch and root decoder target lists. * + * For a mixed-granularity region, each interleaving level contributes its + * target index at the number of region positions covered by one of its + * targets: + * + * position += positions_per_target * target_index + * + * Switch positions-per-target is ``parent_gran / region_gran``. The root + * uses @root_pos_per_target. + * * Return: position >= 0 on success * -ENXIO on failure */ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled, - struct range *hpa_range) + struct range *hpa_range, + int root_pos_per_target, int region_gran) { 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 parent_ways = 0, parent_pos = 0, parent_gran = 0, pos = 0; int rc; /* @@ -2153,11 +2210,24 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled, break; rc = find_pos_and_ways(iter, hpa_range, &parent_pos, - &parent_ways); + &parent_ways, &parent_gran); if (rc) return rc; - pos = pos * parent_ways + parent_pos; + if (root_pos_per_target > 1 && + is_cxl_root(parent_port_of(iter))) { + pos = pos + root_pos_per_target * parent_pos; + break; + } + + /* + * Passthrough levels do not select a target and therefore do + * not contribute to the region position. + */ + if (root_pos_per_target > 1) + pos = pos + (parent_gran / region_gran) * parent_pos; + else + pos = pos * parent_ways + parent_pos; } dev_dbg(&cxlmd->dev, @@ -2171,12 +2241,15 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled, static int cxl_region_sort_targets(struct cxl_region *cxlr) { struct cxl_region_params *p = &cxlr->params; + int root_pos_per_target = root_positions_per_target(cxlr); int i, rc = 0; for (i = 0; i < p->nr_targets; i++) { struct cxl_endpoint_decoder *cxled = p->targets[i]; - cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); + cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range, + root_pos_per_target, + p->interleave_granularity); /* * Record that sorting failed, but still continue to calc * cxled->pos so that cxl_calc_interleave_pos() emits its @@ -2204,6 +2277,7 @@ static int cxl_region_attach(struct cxl_region *cxlr, struct cxl_port *ep_port, *root_port; struct cxl_dport *dport; int rc = -ENXIO; + int root_pos_per_target; rc = check_interleave_cap(&cxled->cxld, p->interleave_ways, p->interleave_granularity); @@ -2236,14 +2310,6 @@ static int cxl_region_attach(struct cxl_region *cxlr, return -ENXIO; } - /* - * Mixed-granularity position calculation is added by the next patch. - * Reject it until then so this intermediate state remains bisectable. - */ - if (cxlrd->cxlsd.cxld.interleave_granularity > - p->interleave_granularity) - return -ENXIO; - if (p->nr_targets >= p->interleave_ways) { dev_dbg(&cxlr->dev, "region already has %d endpoints\n", p->nr_targets); @@ -2365,11 +2431,14 @@ static int cxl_region_attach(struct cxl_region *cxlr, * A fail message here means that this interleave config * will fail when presented as CXL_REGION_F_AUTO. */ + root_pos_per_target = root_positions_per_target(cxlr); for (int i = 0; i < p->nr_targets; i++) { struct cxl_endpoint_decoder *target = p->targets[i]; int test_pos; - test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range); + test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range, + root_pos_per_target, + p->interleave_granularity); dev_dbg(&target->cxld.dev, "Test cxl_calc_interleave_pos(): %s test_pos:%d target->pos:%d\n", (test_pos == target->pos) ? "success" : "fail", -- 2.37.3