All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] cxl/region: Translate DPA->HPA in unaligned MOD3 regions
@ 2025-09-30  4:08 Alison Schofield
  2025-10-01 14:44 ` Jonathan Cameron
  0 siblings, 1 reply; 3+ messages in thread
From: Alison Schofield @ 2025-09-30  4:08 UTC (permalink / raw)
  To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny, Dan Williams
  Cc: linux-cxl, Qing Huang

The CXL driver implementation of DPA->HPA address translation depends
on a region's starting address always being aligned to Host Bridge
Interleave Ways * 256MB. The driver follows the decode methods
defined in the CXL Spec[1] and expanded upon in the CXL Driver Writers
Guide[2], which describe bit manipulations based on power-of-2
alignment to translate a DPA to an HPA.

With the introduction of MOD3 interleave way support, platforms may
create regions at starting addresses that are not power-of-2 aligned.
This allows platforms to avoid gaps in the memory map, but addresses
within those regions cannot be translated using the existing bit
manipulation method.

Introduce an unaligned translation method for DPA->HPA that
reconstructs an HPA by restoring the address first at the port level
and then at the host bridge level.

[1] CXL Spec 3.2 8.2.4.20.13 Implementation Note Device Decoder Logic
[2] CXL Type 3 Memory Software Guide 1.1 2.13.25 DPA to HPA Translation

Suggested-by: Qing Huang <qing.huang@intel.com>
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---

Changes in v1 (was RFC):
- Replace "/" with do_div() to quiet i386 build warning (lkp)
- Replace 'cxld->interleave_ways' with 'hbiw' for clarity
- Use div64_u64_rem() for alignment alignment
- Fix up a printk format specifier (lkp)
- Update code comments and commit log
- Rebase on v717-rc7

 drivers/cxl/core/region.c | 125 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 71cc42d05248..4fc0d1450c2c 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2918,13 +2918,115 @@ static bool cxl_is_hpa_in_chunk(u64 hpa, struct cxl_region *cxlr, int pos)
 	return false;
 }
 
+static int decode_pos(int region_ways, int pos, int *pos_port, int *pos_hb)
+{
+	switch (region_ways) {
+	/*
+	 * Extract the port and hb positions from an endpoint position.
+	 * Knowing there is only one possible decode** for each of these
+	 * regions, decode logic is:
+	 *	 3-way: port always 0, hb is pos
+	 *	 6-way: port cycles thru 0,1, hb increments every 2
+	 *	12-way: port cycles thru 0,1,2,3, hb increments every 4
+	 *
+	 *	**CXL Spec 3.2 9.13.1.1 Legal Interleaving Configurations
+	 */
+	case 3:
+		*pos_port = 0;
+		*pos_hb = pos;
+		break;
+	case 6:
+		*pos_port = pos % 2;
+		*pos_hb = pos / 2;
+		break;
+	case 12:
+		*pos_port = pos % 4;
+		*pos_hb = pos / 4;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * restore_parent() reconstruct the address in parent
+ *
+ * [mask] isolate the offset with the granularity
+ * [addr & ~mask] remove the offset leaving the aligned portion
+ * [* ways] distribute across all interleave ways
+ * [+ (pos * gran)] add the positional offset
+ * [+ (addr & mask)] restore the masked offset
+ */
+static u64 restore_parent(u64 addr, u64 pos, u64 gran, u64 ways)
+{
+	u64 mask = gran - 1;
+
+	return ((addr & ~mask) * ways) + (pos * gran) + (addr & mask);
+}
+
+/*
+ * unaligned_dpa_to_hpa() translates a DPA to HPA when the region resource
+ * start address is not aligned at Host Bridge Interleave Ways * 256MB.
+ *
+ * Unaligned start addresses only occur with MOD3 interleaves. All power-
+ * of-two interleaves are guaranteed aligned.
+ */
+static u64 unaligned_dpa_to_hpa(struct cxl_decoder *cxld,
+				struct cxl_region_params *p, int pos, u64 dpa)
+{
+	int ways_port = p->interleave_ways / cxld->interleave_ways;
+	int gran_port = p->interleave_granularity;
+	int gran_hb = cxld->interleave_granularity;
+	int ways_hb = cxld->interleave_ways;
+	int pos_port, pos_hb, gran_shift;
+	u64 shifted, hpa, hpa_port = 0;
+
+	/* Decode an endpoint 'pos' into port and host-bridge components */
+	if (decode_pos(p->interleave_ways, pos, &pos_port, &pos_hb)) {
+		dev_dbg(&cxld->dev, "not supported for region ways:%d\n",
+			p->interleave_ways);
+		return ULLONG_MAX;
+	}
+	/* Restore the port parent address if needed */
+	if (gran_hb != gran_port)
+		hpa_port = restore_parent(dpa, pos_port, gran_port, ways_port);
+	else
+		hpa_port = dpa;
+
+	/*
+	 * Complete the HPA reconstruction by restoring the address as if
+	 * each HB position is a candidate. Test against expected pos_hb
+	 * to confirm match.
+	 */
+	gran_shift = ilog2(gran_hb);
+	for (int index = 0; index < ways_hb; index++) {
+		hpa = restore_parent(hpa_port, index, gran_hb, ways_hb);
+		hpa += p->res->start;
+
+		shifted = hpa >> gran_shift;
+		if (do_div(shifted, ways_hb) == pos_hb)
+			return hpa;
+	}
+
+	dev_dbg(&cxld->dev, "fail dpa:%#llx region:%pr pos:%d\n", dpa, p->res,
+		pos);
+	dev_dbg(&cxld->dev, "     port-w/g/p:%d/%d/%d hb-w/g/p:%d/%d/%d\n",
+		ways_port, gran_port, pos_port, ways_hb, gran_hb, pos_hb);
+
+	return ULLONG_MAX;
+}
+
 u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 		   u64 dpa)
 {
 	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
-	u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
+	u64 dpa_offset, rem, hpa_offset, bits_upper, mask_upper, hpa;
+	struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
 	struct cxl_region_params *p = &cxlr->params;
 	struct cxl_endpoint_decoder *cxled = NULL;
+	int hbiw = cxld->interleave_ways;
+	bool aligned;
 	u16 eig = 0;
 	u8 eiw = 0;
 	int pos;
@@ -2937,6 +3039,18 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 	if (!cxled || cxlmd != cxled_to_memdev(cxled))
 		return ULLONG_MAX;
 
+	dpa_offset = dpa - cxl_dpa_resource_start(cxled);
+	div64_u64_rem(p->res->start, (u64)hbiw * SZ_256M, &rem);
+	aligned = (rem == 0);
+	if (!aligned) {
+		hpa = unaligned_dpa_to_hpa(cxld, p, cxled->pos, dpa_offset);
+		if (hpa == ULLONG_MAX)
+			return ULLONG_MAX;
+
+		hpa += p->cache_size;
+		goto to_spa;
+	}
+
 	pos = cxled->pos;
 	ways_to_eiw(p->interleave_ways, &eiw);
 	granularity_to_eig(p->interleave_granularity, &eig);
@@ -2951,9 +3065,6 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 	 * 8.2.4.19.13 Implementation Note: Device Decode Logic
 	 */
 
-	/* Remove the dpa base */
-	dpa_offset = dpa - cxl_dpa_resource_start(cxled);
-
 	mask_upper = GENMASK_ULL(51, eig + 8);
 
 	if (eiw < 8) {
@@ -2971,6 +3082,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 	/* Apply the hpa_offset to the region base address */
 	hpa = hpa_offset + p->res->start + p->cache_size;
 
+to_spa:
 	/* Root decoder translation overrides typical modulo decode */
 	if (cxlrd->hpa_to_spa)
 		hpa = cxlrd->hpa_to_spa(cxlrd, hpa);
@@ -2981,8 +3093,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
 		return ULLONG_MAX;
 	}
 
-	/* Simple chunk check, by pos & gran, only applies to modulo decodes */
-	if (!cxlrd->hpa_to_spa && (!cxl_is_hpa_in_chunk(hpa, cxlr, pos)))
+	/* Chunk check applies to aligned modulo decodes only */
+	if (aligned && !cxlrd->hpa_to_spa &&
+	    !cxl_is_hpa_in_chunk(hpa, cxlr, pos))
 		return ULLONG_MAX;
 
 	return hpa;

base-commit: 07e27ad16399afcd693be20211b0dfae63e0615f
-- 
2.37.3


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] cxl/region: Translate DPA->HPA in unaligned MOD3 regions
  2025-09-30  4:08 [PATCH] cxl/region: Translate DPA->HPA in unaligned MOD3 regions Alison Schofield
@ 2025-10-01 14:44 ` Jonathan Cameron
  2025-10-02  2:01   ` Alison Schofield
  0 siblings, 1 reply; 3+ messages in thread
From: Jonathan Cameron @ 2025-10-01 14:44 UTC (permalink / raw)
  Cc: Alison Schofield, Davidlohr Bueso, Dave Jiang, Vishal Verma,
	Ira Weiny, Dan Williams, linux-cxl, Qing Huang

On Mon, 29 Sep 2025 21:08:36 -0700
Alison Schofield <alison.schofield@intel.com> wrote:

> The CXL driver implementation of DPA->HPA address translation depends
> on a region's starting address always being aligned to Host Bridge
> Interleave Ways * 256MB. The driver follows the decode methods
> defined in the CXL Spec[1] and expanded upon in the CXL Driver Writers
> Guide[2], which describe bit manipulations based on power-of-2
> alignment to translate a DPA to an HPA.
> 
> With the introduction of MOD3 interleave way support, platforms may
> create regions at starting addresses that are not power-of-2 aligned.
> This allows platforms to avoid gaps in the memory map, but addresses
> within those regions cannot be translated using the existing bit
> manipulation method.
> 
> Introduce an unaligned translation method for DPA->HPA that
> reconstructs an HPA by restoring the address first at the port level
> and then at the host bridge level.
> 
> [1] CXL Spec 3.2 8.2.4.20.13 Implementation Note Device Decoder Logic
> [2] CXL Type 3 Memory Software Guide 1.1 2.13.25 DPA to HPA Translation
> 
> Suggested-by: Qing Huang <qing.huang@intel.com>
> Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Hi Alison,

My 'favourite' headache inducer in the spec. Anyhow, questions on the comments
inline.

J

> ---
> 
> Changes in v1 (was RFC):
> - Replace "/" with do_div() to quiet i386 build warning (lkp)
> - Replace 'cxld->interleave_ways' with 'hbiw' for clarity
> - Use div64_u64_rem() for alignment alignment
> - Fix up a printk format specifier (lkp)
> - Update code comments and commit log
> - Rebase on v717-rc7
> 
>  drivers/cxl/core/region.c | 125 ++++++++++++++++++++++++++++++++++++--
>  1 file changed, 119 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 71cc42d05248..4fc0d1450c2c 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2918,13 +2918,115 @@ static bool cxl_is_hpa_in_chunk(u64 hpa, struct cxl_region *cxlr, int pos)
>  	return false;
>  }
>  
> +static int decode_pos(int region_ways, int pos, int *pos_port, int *pos_hb)
> +{
> +	switch (region_ways) {
> +	/*
> +	 * Extract the port and hb positions from an endpoint position.
> +	 * Knowing there is only one possible decode** for each of these
> +	 * regions, decode logic is:
> +	 *	 3-way: port always 0, hb is pos
> +	 *	 6-way: port cycles thru 0,1, hb increments every 2

Why is 6 host bridge option not relevant?

> +	 *	12-way: port cycles thru 0,1,2,3, hb increments every 4

Why is 6 host bridge option, cycle every 2 or indeed 12 host bridge option
not relevant?

> +	 *
> +	 *	**CXL Spec 3.2 9.13.1.1 Legal Interleaving Configurations
> +	 */
> +	case 3:
> +		*pos_port = 0;
> +		*pos_hb = pos;
> +		break;
> +	case 6:
> +		*pos_port = pos % 2;
> +		*pos_hb = pos / 2;
> +		break;
> +	case 12:
> +		*pos_port = pos % 4;
> +		*pos_hb = pos / 4;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	return 0;
> +}

...



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] cxl/region: Translate DPA->HPA in unaligned MOD3 regions
  2025-10-01 14:44 ` Jonathan Cameron
@ 2025-10-02  2:01   ` Alison Schofield
  0 siblings, 0 replies; 3+ messages in thread
From: Alison Schofield @ 2025-10-02  2:01 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Davidlohr Bueso, Dave Jiang, Vishal Verma, Ira Weiny,
	Dan Williams, linux-cxl, Qing Huang

On Wed, Oct 01, 2025 at 03:44:02PM +0100, Jonathan Cameron wrote:
> On Mon, 29 Sep 2025 21:08:36 -0700
> Alison Schofield <alison.schofield@intel.com> wrote:
> 
> > The CXL driver implementation of DPA->HPA address translation depends
> > on a region's starting address always being aligned to Host Bridge
> > Interleave Ways * 256MB. The driver follows the decode methods
> > defined in the CXL Spec[1] and expanded upon in the CXL Driver Writers
> > Guide[2], which describe bit manipulations based on power-of-2
> > alignment to translate a DPA to an HPA.
> > 
> > With the introduction of MOD3 interleave way support, platforms may
> > create regions at starting addresses that are not power-of-2 aligned.
> > This allows platforms to avoid gaps in the memory map, but addresses
> > within those regions cannot be translated using the existing bit
> > manipulation method.
> > 
> > Introduce an unaligned translation method for DPA->HPA that
> > reconstructs an HPA by restoring the address first at the port level
> > and then at the host bridge level.
> > 
> > [1] CXL Spec 3.2 8.2.4.20.13 Implementation Note Device Decoder Logic
> > [2] CXL Type 3 Memory Software Guide 1.1 2.13.25 DPA to HPA Translation
> > 
> > Suggested-by: Qing Huang <qing.huang@intel.com>
> > Signed-off-by: Alison Schofield <alison.schofield@intel.com>
> Hi Alison,
> 
> My 'favourite' headache inducer in the spec. Anyhow, questions on the comments
> inline.

I always count on you for the bits and bytes ;)

> 
> J
> 
> > ---
> > 
> > Changes in v1 (was RFC):
> > - Replace "/" with do_div() to quiet i386 build warning (lkp)
> > - Replace 'cxld->interleave_ways' with 'hbiw' for clarity
> > - Use div64_u64_rem() for alignment alignment
> > - Fix up a printk format specifier (lkp)
> > - Update code comments and commit log
> > - Rebase on v717-rc7
> > 
> >  drivers/cxl/core/region.c | 125 ++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 119 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 71cc42d05248..4fc0d1450c2c 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> > @@ -2918,13 +2918,115 @@ static bool cxl_is_hpa_in_chunk(u64 hpa, struct cxl_region *cxlr, int pos)
> >  	return false;
> >  }
> >  
> > +static int decode_pos(int region_ways, int pos, int *pos_port, int *pos_hb)
> > +{
> > +	switch (region_ways) {
> > +	/*
> > +	 * Extract the port and hb positions from an endpoint position.
> > +	 * Knowing there is only one possible decode** for each of these
> > +	 * regions, decode logic is:
> > +	 *	 3-way: port always 0, hb is pos
> > +	 *	 6-way: port cycles thru 0,1, hb increments every 2
> 
> Why is 6 host bridge option not relevant?
> 
> > +	 *	12-way: port cycles thru 0,1,2,3, hb increments every 4
> 
> Why is 6 host bridge option, cycle every 2 or indeed 12 host bridge option
> not relevant?

It is relevant. I'll add 6 & 12 way HBIW.

HBIW	Region_Ways	Dev-per-HB	pos_port_calc	pos_hb_calc
12	12      	1		pos % 1		pos / 1 = pos
06	06		1		pos % 1		pos / 1 = pos
06	12		2		pos % 2		pos / 2

I'll add that in v3 by adding HBIW to decode_pos() params and calcs, rather
than assuming only 3.

--Alison


> 
> > +	 *
> > +	 *	**CXL Spec 3.2 9.13.1.1 Legal Interleaving Configurations
> > +	 */
> > +	case 3:
> > +		*pos_port = 0;
> > +		*pos_hb = pos;
> > +		break;
> > +	case 6:
> > +		*pos_port = pos % 2;
> > +		*pos_hb = pos / 2;
> > +		break;
> > +	case 12:
> > +		*pos_port = pos % 4;
> > +		*pos_hb = pos / 4;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	return 0;
> > +}
> 
> ...
> 
> 

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-10-02  2:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-30  4:08 [PATCH] cxl/region: Translate DPA->HPA in unaligned MOD3 regions Alison Schofield
2025-10-01 14:44 ` Jonathan Cameron
2025-10-02  2:01   ` Alison Schofield

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.