Re: [PATCH v3 1/9] cxl/region: Factor port target calculations

Alison Schofield <[email protected]>
Newsgroups org.kernel.vger.linux-cxl
Message-ID <[email protected]>
On Tue, Aug 18, 2026 at 10:20:34AM +0200, Robert Richter wrote:
> On 30.07.26 15:20:21, Alison Schofield wrote:
> > cxl_port_setup_targets() calculates the interleave fan-out above a
> > port, the port decoder granularity, and the distance between
> > endpoints routed through the same downstream port.
> > 
> > Factor those calculations into helpers so the target setup path can
> > be extended. Validation of parent decoder values is dropped since
> > those values are validated where they are set, before this port's
> > setup runs. A configuration that is invalid in more than one way may
> > report a different error first.
> > 
> > Suggested-by: Originally-by: Robert Richter <[email protected]>
> > Signed-off-by: Alison Schofield <[email protected]>
> > ---
> >  drivers/cxl/core/region.c | 197 +++++++++++++++++++-------------------
> 
> Please split patch and move out changes in error handling, see also
> below.

Thanks for the reviews Robert!

I took another pass at this, including the broader complexity concern
you raised here and in the collab mtg.

Rather than further splitting the refactoring here, I reworked the
the series around the existing region setup flow. The selector walk
and the helper machinery introduced here are gone in v4, along with
the unrelated error-handling movement.

more below

> 
> >  1 file changed, 96 insertions(+), 101 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 1e211542b6b6..1082db7b2cca 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> > @@ -1350,6 +1350,19 @@ static void cxl_port_detach_region(struct cxl_port *port,
> >  		free_region_ref(cxl_rr);
> >  }
> >  
> > +/**
> > + * check_last_peer() - Verify the previous endpoint routed to this dport
> > + * @cxled: endpoint decoder being placed
> > + * @ep: this endpoint's entry for the port
> > + * @cxl_rr: region reference for the port
> > + * @distance: distance to the previous endpoint routed to this dport
> > + *
> > + * Endpoints routed through the same dport recur at @distance intervals in
> > + * region-position order. Verify that the endpoint at ``pos - distance`` used
> > + * the same dport.
> > + *
> > + * Return: 0 on success, -ENXIO on a routing mismatch.
> > + */
> >  static int check_last_peer(struct cxl_endpoint_decoder *cxled,
> >  			   struct cxl_ep *ep, struct cxl_region_ref *cxl_rr,
> >  			   int distance)
> > @@ -1434,60 +1447,106 @@ static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig)
> >  	return 0;
> >  }
> >  
> > +/**
> > + * get_parent_fanout() - Calculate the switch fan-out above a port
> > + * @parent_port: first ancestor port
> > + * @cxlr: region under construction
> > + * @fanout: filled with the product of the ancestor switch ways
> > + *
> > + * Walk from @parent_port to the root and multiply the interleave ways of
> > + * each switch decoder. Root decoder ways are not included.
> > + *
> > + * Return: 0 on success.
> > + */
> 
> I don't think that static functions should be documented. They do not
> describe an interface, are short and also called only once. Instead,
> the function's purpose should be understandable from reading the code
> with small comments only where really helpful.
> 

These helpers are gone in v4. I also took your larger point here and
tried to make the resulting code readable without needing comments
to explain the implementation.


> > +static int get_parent_fanout(struct cxl_port *parent_port,
> > +			     struct cxl_region *cxlr, int *fanout)
> > +{
> > +	int distance = 1;
> > +	struct cxl_port *iter;
> > +
> > +	for (iter = parent_port; !is_cxl_root(iter);
> > +	     iter = to_cxl_port(iter->dev.parent)) {
> > +		struct cxl_region_ref *cxl_rr_iter = cxl_rr_load(iter, cxlr);
> > +
> > +		distance *= cxl_rr_iter->nr_targets;
> > +	}
> > +
> > +	*fanout = distance;
> > +	return 0;
> > +}
> > +
> > +/**
> > + * derive_port_granularity() - Calculate the granularity for a port decoder
> > + * @cxlr: region under construction
> > + * @fanout: product of the ancestor switch ways
> > + * @ig: filled with the port decoder granularity
> > + *
> > + * Preserve the existing parent-granularity times parent-ways recurrence in
> > + * terms of the region granularity and the fan-out above this port.
> > + *
> > + * Return: 0 on success.
> > + */
> > +static int derive_port_granularity(struct cxl_region *cxlr, int fanout,
> > +				   int *ig)
> > +{
> > +	struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> > +	int root_iw = cxlrd->cxlsd.cxld.interleave_ways;
> > +	struct cxl_region_params *p = &cxlr->params;
> > +	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;
> > +
> > +	return 0;
> 
> Always succeeds. Should directly return ig.

Agree. This helper is gone in v4.

> 
> > +}
> 
> I will review both functions again after reading the rest of the
> series.
> 
> > +
> >  static int cxl_port_setup_targets(struct cxl_port *port,
> >  				  struct cxl_region *cxlr,
> >  				  struct cxl_endpoint_decoder *cxled)
> >  {
> >  	struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
> > -	int parent_iw, parent_ig, ig, iw, rc, pos = cxled->pos;
> > +	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);
> >  	struct cxl_ep *ep = cxl_ep_load(port, cxlmd);
> >  	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;
> > +	struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(&cxld->dev);
> > +	int ig, iw = cxl_rr->nr_targets;
> 
> The changes around here should be a separate patch only containing
> error handling changes and other related reworks.
> 

Agree. I dropped this unrelated rework rather than carrying as
part of this series.


> > +	int fanout, rc;
> > +	int pos = cxled->pos;
> > +	u16 eig;
> > +	u8 eiw;
> >  
> >  	/*
> >  	 * While root level decoders support x3, x6, x12, switch level
> >  	 * decoders only support powers of 2 up to x16.
> >  	 */
> > -	if (!is_power_of_2(cxl_rr->nr_targets)) {
> > +	if (!is_power_of_2(iw)) {
> >  		dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n",
> > -			dev_name(port->uport_dev), dev_name(&port->dev),
> > -			cxl_rr->nr_targets);
> > +			dev_name(port->uport_dev), dev_name(&port->dev), iw);
> >  		return -EINVAL;
> >  	}
> >  
> > -	cxlsd = to_cxl_switch_decoder(&cxld->dev);
> > +	if (iw > 8 || iw > cxlsd->nr_targets) {
> > +		dev_dbg(&cxlr->dev,
> > +			"%s:%s:%s: ways: %d overflows targets: %d\n",
> > +			dev_name(port->uport_dev), dev_name(&port->dev),
> > +			dev_name(&cxld->dev), iw, cxlsd->nr_targets);
> > +		return -ENXIO;
> > +	}
> 
> Split patch: Moving of this check and other changes above should be in
> a separate patch.

Agree. This movement is gone in v4.

> 
> > +
> > +	rc = get_parent_fanout(parent_port, cxlr, &fanout);
> 
> I am not a "fan" of that term. :-) IMO, target_count or target_total
> would fit better here.

:) No more fanout in v4.

> 
> > +	if (rc)
> > +		return rc;
> > +
> >  	if (cxl_rr->nr_targets_set) {
> 
> The check can be dropped now as it is done with the for loop already.

Agree. This rework is gone in v4 as well.

snip

> > @@ -1495,84 +1554,20 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> >  		goto add_target;
> >  	}
> >  
> > -	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;
> > -		/*
> > -		 * For purposes of address bit routing, use power-of-2 math for
> > -		 * switch ports.
> > -		 */
> > -		if (!is_power_of_2(parent_iw))
> > -			parent_iw /= 3;
> > -	} else {
> > -		struct cxl_region_ref *parent_rr;
> > -		struct cxl_decoder *parent_cxld;
> > -
> > -		parent_rr = cxl_rr_load(parent_port, cxlr);
> > -		parent_cxld = parent_rr->decoder;
> > -		parent_ig = parent_cxld->interleave_granularity;
> > -		parent_iw = parent_cxld->interleave_ways;
> > -	}
> > -
> > -	rc = granularity_to_eig(parent_ig, &peig);
> > -	if (rc) {
> > -		dev_dbg(&cxlr->dev, "%s:%s: invalid parent granularity: %d\n",
> > -			dev_name(parent_port->uport_dev),
> > -			dev_name(&parent_port->dev), parent_ig);
> > -		return rc;
> > -	}
> > -
> > -	rc = ways_to_eiw(parent_iw, &peiw);
> > -	if (rc) {
> > -		dev_dbg(&cxlr->dev, "%s:%s: invalid parent interleave: %d\n",
> > -			dev_name(parent_port->uport_dev),
> > -			dev_name(&parent_port->dev), parent_iw);
> > +	rc = derive_port_granularity(cxlr, fanout, &ig);
> 
> I still think, the granularity should be determined just by
> calculating the bit position of the ways bit within the HPA. But let's
> see next patches.


Agree with the direction here. I ended up dropping the selector walk as
well, though, and deriving the decoder granularity directly from the parent
interleave geometry. More on that in the following patches.


> 
> > +	if (rc)
> >  		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",
> > -			dev_name(port->uport_dev), dev_name(&port->dev), iw);
> > -		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)
> > +		rc = granularity_to_eig(ig, &eig);
> 
> Same here, separate the combination of those two checks in a separate
> patch.
> 
> With a patch split the actual change will be much better readable.


Agree. Rather than splitting this version further, I dropped this rework
and substantially simplified the target setup in v4.


> 
> Thanks,
> 
> -Robert
> 

snip
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.