Re: [PATCH v3 3/9] cxl/region: Derive port granularity from selector bits

Robert Richter <[email protected]>
Newsgroups org.kernel.vger.linux-cxl
Message-ID <[email protected]>
On 30.07.26 15:20:23, Alison Schofield wrote:
> 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.

The only requirement I see here is that firmware programmed selector
bits must be within the endpoint's bit mask (which matches the
region's total ways and gran configuration). The selector and
granularity of a single decoder within the chain may vary, as long as
it is within the total selector and does not overlap with other
decoders. It is not possible to calculate a single correct granularity
value for auto-mode. And in user-mode there are muliple setups and
values possible too, unless there are strict assignment rules given
that only allow a single subset.

> 
> 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)

This function interface gets really out of control now. You need 5
args to calc the granularity and return 2 values? Something is wrong
here.

I guess we need to change the approach here. Maybe split target setup
for auto- and user-mode. We must simplify the code, I think that is
possible.

Will take a look.

-Robert

>  {
>  	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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.