[PATCH v3 3/9] cxl/region: Derive port granularity from selector bits
Alison Schofield <[email protected]> Thu, 30 Jul 2026 15:20:23 -0700
| Newsgroups | org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <efef416440a89a44dfddb13ef390285a98c69cd4.1785444498.git.alison.schofield@intel.com> |
A user-created region currently derives each port decoder granularity from the parent granularity and ways. That recurrence assumes the selector bits follow the existing same-granularity ordering and cannot derive the decoder settings for a mixed-granularity region. Add cxl_region_is_mixed_gran() as the common predicate for regions whose granularity is finer than an interleaving root decoder. Update derive_port_granularity() to choose each port decoder's granularity from the selector bits not already used by its ancestors. For auto regions, compare the firmware-programmed granularity with the derived value and reject a mismatch. Originally-by: Robert Richter <[email protected]> Signed-off-by: Alison Schofield <[email protected]> --- drivers/cxl/core/region.c | 90 ++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index b0061f03892a..871dedd37cc8 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1552,32 +1552,94 @@ static bool region_selectors_fit(struct cxl_port *port, return true; } +/** + * cxl_region_is_mixed_gran() - Test for a mixed-granularity region + * @cxlr: region + * + * A region is mixed-granularity when an interleaving root decoder uses a + * larger granularity than the region. + * + * Return: true for a mixed-granularity region. + */ +static inline 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; +} + /** * derive_port_granularity() - Calculate the granularity for a port decoder + * @port: port being configured * @cxlr: region under construction + * @accum: selectors used by the ancestor decoders * @fanout: product of the ancestor switch ways - * @ig: filled with the port decoder granularity + * @iw: port decoder interleave ways + * @ig: filled with the derived granularity * - * Preserve the existing parent-granularity times parent-ways recurrence in - * terms of the region granularity and the fan-out above this port. + * Select the decoder granularity from the region selector bits not already + * used by its ancestors. Same-granularity regions allocate the lowest + * available selector bits first. Mixed-granularity regions allocate the + * highest available selector bits first so decoder granularities decrease + * from the root toward the endpoints. * - * Return: 0 on success. + * A passthrough decoder uses no selector bits and retains the granularity + * implied by the ancestor fan-out. + * + * Return: 0 on success, -ENXIO when no valid selector remains. */ -static int derive_port_granularity(struct cxl_region *cxlr, int fanout, - int *ig) +static int derive_port_granularity(struct cxl_port *port, + struct cxl_region *cxlr, u64 accum, + int fanout, int iw, int *ig) { struct cxl_root_decoder *cxlrd = cxlr->cxlrd; int root_iw = cxlrd->cxlsd.cxld.interleave_ways; struct cxl_region_params *p = &cxlr->params; + u64 selector; int sel_distance; - sel_distance = is_power_of_2(root_iw) ? root_iw : root_iw / 3; - sel_distance *= fanout; - *ig = p->interleave_granularity * sel_distance; + selector = get_selector(p->interleave_ways, + p->interleave_granularity) & ~accum; + + if (iw == 1) { + sel_distance = is_power_of_2(root_iw) ? root_iw : root_iw / 3; + sel_distance *= fanout; + *ig = p->interleave_granularity * sel_distance; + } else if (selector && cxl_region_is_mixed_gran(cxlr)) { + *ig = (1ULL << fls64(selector)) / iw; + } else if (selector) { + *ig = 1ULL << __ffs64(selector); + } else { + dev_dbg(&cxlr->dev, + "%s:%s: no selector bits available for iw %d\n", + dev_name(port->uport_dev), dev_name(&port->dev), iw); + return -ENXIO; + } + + if (iw > 1 && (~selector & get_selector(iw, *ig))) { + dev_dbg(&cxlr->dev, + "%s:%s: derived selector %#llx exceeds remaining %#llx (iw %d ig %d)\n", + dev_name(port->uport_dev), dev_name(&port->dev), + get_selector(iw, *ig), selector, iw, *ig); + return -ENXIO; + } return 0; } +/** + * cxl_port_setup_targets() - Validate and program a port decoder + * @port: port being configured + * @cxlr: region under construction + * @cxled: endpoint decoder being attached + * + * Validate the decoder's selector placement and derive its interleave + * geometry. User-created regions program the derived values; auto regions + * validate the firmware-programmed values. + * + * Return: 0 on success, negative errno on invalid interleave geometry. + */ static int cxl_port_setup_targets(struct cxl_port *port, struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled) @@ -1635,7 +1697,7 @@ static int cxl_port_setup_targets(struct cxl_port *port, goto add_target; } - rc = derive_port_granularity(cxlr, fanout, &ig); + rc = derive_port_granularity(port, cxlr, accum, fanout, iw, &ig); if (rc) return rc; @@ -1650,8 +1712,14 @@ static int cxl_port_setup_targets(struct cxl_port *port, } if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) { + if (iw > 1 && cxld->interleave_granularity != ig) { + dev_dbg(&cxlr->dev, + "%s:%s: firmware ig %d != derived ig %d (iw %d)\n", + dev_name(port->uport_dev), dev_name(&port->dev), + cxld->interleave_granularity, ig, iw); + return -ENXIO; + } if (cxld->interleave_ways != iw || - (iw > 1 && cxld->interleave_granularity != ig) || !spa_maps_hpa(p, &cxld->hpa_range) || ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) { dev_err(&cxlr->dev, -- 2.37.3