[PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86

Fabio M. De Francesco posted 4 patches 2 months ago
There is a newer version of this series
[PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Fabio M. De Francesco 2 months ago
On a x86 platform with a low memory hole (LHM), the BIOS may publish
CFMWS that describes a system physical address (SPA) range that
typically is only a subset of the corresponding CXL intermediate switch
and endpoint decoder's host physical address (HPA) ranges. The CFMWS
range never intersects the LHM and so the driver instantiates a root
decoder whose HPA range size doesn't fully contain the matching switch
and endpoint decoders' HPA ranges.[1]

To construct regions and attach decoders, the driver needs to match root
decoders and regions with endpoint decoders. The process fails and
returns errors because the driver is not designed to deal with SPA
ranges which are smaller than the corresponding hardware decoders HPA
ranges.

Introduce two functions that indirectly detect the presence of x86 LMH
and allow the matching between a root decoder or an already constructed
region with a corresponding intermediate switch or endpoint decoder to
enable the construction of a region and the subsequent attachment of the
same decoders to that region.

These functions return true when SPA/HPA misalignments due to LMH's are
detected under specific conditions:

- Both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (i.e.,
  0x0 on x86 with LMH's).
- The SPA range's size is less than HPA's.
- The SPA range's size is less than 4G.
- The HPA range's size is aligned to the NIW * 256M rule.

Also introduce a function that adjusts the range end of a region to be
constructed and the DPA range's end of the endpoint decoders that will
be later attached to that region.

[1] commit 7a81173f3 ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")

Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>
---
 drivers/cxl/Kconfig                |  4 ++
 drivers/cxl/core/Makefile          |  1 +
 drivers/cxl/core/platform_quirks.c | 99 ++++++++++++++++++++++++++++++
 drivers/cxl/core/platform_quirks.h | 33 ++++++++++
 4 files changed, 137 insertions(+)
 create mode 100644 drivers/cxl/core/platform_quirks.c
 create mode 100644 drivers/cxl/core/platform_quirks.h

diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 48b7314afdb8..03c0583bc9a3 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -211,6 +211,10 @@ config CXL_REGION
 
 	  If unsure say 'y'
 
+config CXL_PLATFORM_QUIRKS
+	def_bool y
+	depends on CXL_REGION
+
 config CXL_REGION_INVALIDATION_TEST
 	bool "CXL: Region Cache Management Bypass (TEST)"
 	depends on CXL_REGION
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 5ad8fef210b5..1684e46b8709 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -17,6 +17,7 @@ cxl_core-y += cdat.o
 cxl_core-y += ras.o
 cxl_core-$(CONFIG_TRACING) += trace.o
 cxl_core-$(CONFIG_CXL_REGION) += region.o
+cxl_core-$(CONFIG_CXL_PLATFORM_QUIRKS) += platform_quirks.o
 cxl_core-$(CONFIG_CXL_MCE) += mce.o
 cxl_core-$(CONFIG_CXL_FEATURES) += features.o
 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
diff --git a/drivers/cxl/core/platform_quirks.c b/drivers/cxl/core/platform_quirks.c
new file mode 100644
index 000000000000..7e76e392b1ae
--- /dev/null
+++ b/drivers/cxl/core/platform_quirks.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/range.h>
+#include "platform_quirks.h"
+#include "cxlmem.h"
+#include "core.h"
+
+/* Start of CFMWS range that end before x86 Low Memory Holes */
+#define LMH_CFMWS_RANGE_START 0x0ULL
+
+/**
+ * platform_cxlrd_matches_cxled() - Platform quirk to match CXL Root and
+ * Endpoint Decoders. It allows matching on platforms with LMH's.
+ * @cxlrd: The Root Decoder against which @cxled is tested for matching.
+ * @cxled: The Endpoint Decoder to be tested for matching @cxlrd.
+ *
+ * platform_cxlrd_matches_cxled() is typically called from the
+ * match_*_by_range() functions in region.c. It checks if an endpoint decoder
+ * matches a given root decoder and returns true to allow the driver to succeed
+ * in the construction of regions where it would otherwise fail for the presence
+ * of a Low Memory Hole (see Documentation/driver-api/cxl/conventions.rst).
+ *
+ * In x86 platforms with LMH's, the CFMWS ranges never intersect the LMH, the
+ * endpoint decoder's HPA range size is always guaranteed aligned to NIW*256MB
+ * and also typically larger than the matching root decoder's, and the root
+ * decoder's range end is at an address that is necessarily less than SZ_4G
+ * (i.e., the Hole is in Low Memory - this function doesn't deal with other
+ * kinds of holes).
+ *
+ * Return: true if an endpoint matches a root decoder, else false.
+ */
+bool platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
+				  const struct cxl_endpoint_decoder *cxled)
+{
+	const struct range *rd_r, *sd_r;
+	int align;
+
+	rd_r = &cxlrd->cxlsd.cxld.hpa_range;
+	sd_r = &cxled->cxld.hpa_range;
+	align = cxled->cxld.interleave_ways * SZ_256M;
+
+	if (rd_r->start == LMH_CFMWS_RANGE_START &&
+	    rd_r->start == sd_r->start && rd_r->end < sd_r->end &&
+	    rd_r->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
+	    IS_ALIGNED(range_len(sd_r), align))
+		return true;
+
+	return false;
+}
+
+/**
+ * platform_region_matches_cxld() - Platform quirk to match a CXL Region and a
+ * Switch or Endpoint Decoder. It allows matching on platforms with LMH's.
+ * @p: Region Params against which @cxled is matched.
+ * @cxld: Switch or Endpoint Decoder to be tested for matching @p.
+ *
+ * Similar to platform_cxlrd_matches_cxled(), it matches regions and
+ * decoders on platforms with LMH's.
+ *
+ * Return: true if a Decoder matches a Region, else false.
+ */
+bool platform_region_matches_cxld(const struct cxl_region_params *p,
+				  const struct cxl_decoder *cxld)
+{
+	const struct range *r = &cxld->hpa_range;
+	const struct resource *res = p->res;
+	int align = cxld->interleave_ways * SZ_256M;
+
+	if (res->start == LMH_CFMWS_RANGE_START && res->start == r->start &&
+	    res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
+	    IS_ALIGNED(range_len(r), align))
+		return true;
+
+	return false;
+}
+
+void platform_res_adjust(struct resource *res,
+			 struct cxl_endpoint_decoder *cxled,
+			 const struct cxl_root_decoder *cxlrd)
+{
+	if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
+		return;
+
+	guard(rwsem_write)(&cxl_rwsem.dpa);
+	dev_dbg(cxled_to_memdev(cxled)->dev.parent,
+		"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
+		dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
+	if (res) {
+		/* Trim region resource overlap with LMH */
+		res->end = cxlrd->res->end;
+	}
+	/* Match endpoint decoder's DPA resource to root decoder's */
+	cxled->dpa_res->end =
+		cxled->dpa_res->start +
+		resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;
+	dev_info(cxled_to_memdev(cxled)->dev.parent,
+		 "Resources have been adjusted for LMH (%s: %pr, %pr)\n",
+		 dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
+}
diff --git a/drivers/cxl/core/platform_quirks.h b/drivers/cxl/core/platform_quirks.h
new file mode 100644
index 000000000000..a15592b4e90e
--- /dev/null
+++ b/drivers/cxl/core/platform_quirks.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "cxl.h"
+
+#ifdef CONFIG_CXL_PLATFORM_QUIRKS
+bool platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
+				  const struct cxl_endpoint_decoder *cxled);
+bool platform_region_matches_cxld(const struct cxl_region_params *p,
+				  const struct cxl_decoder *cxld);
+void platform_res_adjust(struct resource *res,
+			 struct cxl_endpoint_decoder *cxled,
+			 const struct cxl_root_decoder *cxlrd);
+#else
+static inline bool
+platform_root_decoder_contains(const struct cxl_root_decoder *cxlrd,
+			       const struct cxl_endpoint_decoder *cxled)
+{
+	return false;
+}
+
+static inline bool
+platform_region_matches_cxld(const struct cxl_region_params *p,
+			     const struct cxl_decoder *cxld)
+{
+	return false;
+}
+
+inline void platform_res_adjust(struct resource *res,
+				struct cxl_endpoint_decoder *cxled,
+				const struct cxl_root_decoder *cxlrd)
+{
+}
+#endif /* CONFIG_CXL_PLATFORM_QUIRKS */
-- 
2.50.1
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Jonathan Cameron 1 month, 1 week ago
On Mon,  6 Oct 2025 17:58:05 +0200
"Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com> wrote:

> On a x86 platform with a low memory hole (LHM), the BIOS may publish
> CFMWS that describes a system physical address (SPA) range that
> typically is only a subset of the corresponding CXL intermediate switch
> and endpoint decoder's host physical address (HPA) ranges. The CFMWS
> range never intersects the LHM and so the driver instantiates a root
> decoder whose HPA range size doesn't fully contain the matching switch
> and endpoint decoders' HPA ranges.[1]
> 
> To construct regions and attach decoders, the driver needs to match root
> decoders and regions with endpoint decoders. The process fails and
> returns errors because the driver is not designed to deal with SPA
> ranges which are smaller than the corresponding hardware decoders HPA
> ranges.
> 
> Introduce two functions that indirectly detect the presence of x86 LMH
> and allow the matching between a root decoder or an already constructed
> region with a corresponding intermediate switch or endpoint decoder to
> enable the construction of a region and the subsequent attachment of the
> same decoders to that region.
> 
> These functions return true when SPA/HPA misalignments due to LMH's are
> detected under specific conditions:
> 
> - Both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (i.e.,
>   0x0 on x86 with LMH's).
> - The SPA range's size is less than HPA's.
> - The SPA range's size is less than 4G.
> - The HPA range's size is aligned to the NIW * 256M rule.
> 
> Also introduce a function that adjusts the range end of a region to be
> constructed and the DPA range's end of the endpoint decoders that will
> be later attached to that region.
> 
> [1] commit 7a81173f3 ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")
> 
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>
A few minor comments from me.

> diff --git a/drivers/cxl/core/platform_quirks.c b/drivers/cxl/core/platform_quirks.c
> new file mode 100644
> index 000000000000..7e76e392b1ae
> --- /dev/null
> +++ b/drivers/cxl/core/platform_quirks.c
> @@ -0,0 +1,99 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/range.h>
> +#include "platform_quirks.h"
> +#include "cxlmem.h"
> +#include "core.h"
> +
> +/* Start of CFMWS range that end before x86 Low Memory Holes */
> +#define LMH_CFMWS_RANGE_START 0x0ULL
> +
> +/**
> + * platform_cxlrd_matches_cxled() - Platform quirk to match CXL Root and
> + * Endpoint Decoders. It allows matching on platforms with LMH's.
> + * @cxlrd: The Root Decoder against which @cxled is tested for matching.
> + * @cxled: The Endpoint Decoder to be tested for matching @cxlrd.
> + *
> + * platform_cxlrd_matches_cxled() is typically called from the
> + * match_*_by_range() functions in region.c. It checks if an endpoint decoder
> + * matches a given root decoder and returns true to allow the driver to succeed
> + * in the construction of regions where it would otherwise fail for the presence
> + * of a Low Memory Hole (see Documentation/driver-api/cxl/conventions.rst).
> + *
> + * In x86 platforms with LMH's, the CFMWS ranges never intersect the LMH, the
> + * endpoint decoder's HPA range size is always guaranteed aligned to NIW*256MB
> + * and also typically larger than the matching root decoder's, and the root
> + * decoder's range end is at an address that is necessarily less than SZ_4G
> + * (i.e., the Hole is in Low Memory - this function doesn't deal with other
> + * kinds of holes).
> + *
> + * Return: true if an endpoint matches a root decoder, else false.
> + */
> +bool platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
> +				  const struct cxl_endpoint_decoder *cxled)
> +{
> +	const struct range *rd_r, *sd_r;
> +	int align;
> +
> +	rd_r = &cxlrd->cxlsd.cxld.hpa_range;
> +	sd_r = &cxled->cxld.hpa_range;
> +	align = cxled->cxld.interleave_ways * SZ_256M;
> +
> +	if (rd_r->start == LMH_CFMWS_RANGE_START &&
> +	    rd_r->start == sd_r->start && rd_r->end < sd_r->end &&
> +	    rd_r->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
> +	    IS_ALIGNED(range_len(sd_r), align))
> +		return true;
> +
> +	return false;
Similar to below.  Simply returning the boolean statement can be simpler

	return rd_r->start == LMH_CFMWS_RANGE_START &&
	       rd_r->start == sd_r->start &&
	       rd_r->end < sd_r->end &&
	       rd_r->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
	       IS_ALIGNED(range_len(sd_r), align);


> +}
> +
> +/**
> + * platform_region_matches_cxld() - Platform quirk to match a CXL Region and a
> + * Switch or Endpoint Decoder. It allows matching on platforms with LMH's.
> + * @p: Region Params against which @cxled is matched.
> + * @cxld: Switch or Endpoint Decoder to be tested for matching @p.
> + *
> + * Similar to platform_cxlrd_matches_cxled(), it matches regions and
> + * decoders on platforms with LMH's.
> + *
> + * Return: true if a Decoder matches a Region, else false.
> + */
> +bool platform_region_matches_cxld(const struct cxl_region_params *p,
> +				  const struct cxl_decoder *cxld)
> +{
> +	const struct range *r = &cxld->hpa_range;
> +	const struct resource *res = p->res;
> +	int align = cxld->interleave_ways * SZ_256M;
> +
> +	if (res->start == LMH_CFMWS_RANGE_START && res->start == r->start &&
> +	    res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
> +	    IS_ALIGNED(range_len(r), align))
> +		return true;

Dave commented on line break up here, but I'd go further.

	return res->start == LMH_CFMWS_RANGE_START &&
	       res->start == r->start &&
	       res->end < r->end &&
	       res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
	       IS_ALIGNED(range_len(r), align);

> +
> +	return false;
> +}

> diff --git a/drivers/cxl/core/platform_quirks.h b/drivers/cxl/core/platform_quirks.h
> new file mode 100644
> index 000000000000..a15592b4e90e
> --- /dev/null
> +++ b/drivers/cxl/core/platform_quirks.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#include "cxl.h"
> +
> +#ifdef CONFIG_CXL_PLATFORM_QUIRKS
> +bool platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
> +				  const struct cxl_endpoint_decoder *cxled);
> +bool platform_region_matches_cxld(const struct cxl_region_params *p,
> +				  const struct cxl_decoder *cxld);
> +void platform_res_adjust(struct resource *res,
> +			 struct cxl_endpoint_decoder *cxled,
> +			 const struct cxl_root_decoder *cxlrd);
> +#else
> +static inline bool
> +platform_root_decoder_contains(const struct cxl_root_decoder *cxlrd,
> +			       const struct cxl_endpoint_decoder *cxled)
> +{
> +	return false;
> +}
> +
> +static inline bool
> +platform_region_matches_cxld(const struct cxl_region_params *p,
> +			     const struct cxl_decoder *cxld)
> +{
> +	return false;
> +}
> +
> +inline void platform_res_adjust(struct resource *res,
static 
> +				struct cxl_endpoint_decoder *cxled,
> +				const struct cxl_root_decoder *cxlrd)
> +{
> +}
> +#endif /* CONFIG_CXL_PLATFORM_QUIRKS */
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by kernel test robot 1 month, 4 weeks ago
Hi Fabio,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 46037455cbb748c5e85071c95f2244e81986eb58]

url:    https://github.com/intel-lab-lkp/linux/commits/Fabio-M-De-Francesco/cxl-core-Change-match_-_by_range-signatures/20251010-111627
base:   46037455cbb748c5e85071c95f2244e81986eb58
patch link:    https://lore.kernel.org/r/20251006155836.791418-3-fabio.m.de.francesco%40linux.intel.com
patch subject: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
config: i386-randconfig-011-20251010 (https://download.01.org/0day-ci/archive/20251010/202510102229.iFcGqbMH-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251010/202510102229.iFcGqbMH-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510102229.iFcGqbMH-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/cxl/core/platform_quirks.c:70:36: warning: result of comparison of constant 4294967296 with expression of type 'const resource_size_t' (aka 'const unsigned int') is always true [-Wtautological-constant-out-of-range-compare]
      70 |             res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
         |                                  ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1 warning generated.


vim +70 drivers/cxl/core/platform_quirks.c

    50	
    51	/**
    52	 * platform_region_matches_cxld() - Platform quirk to match a CXL Region and a
    53	 * Switch or Endpoint Decoder. It allows matching on platforms with LMH's.
    54	 * @p: Region Params against which @cxled is matched.
    55	 * @cxld: Switch or Endpoint Decoder to be tested for matching @p.
    56	 *
    57	 * Similar to platform_cxlrd_matches_cxled(), it matches regions and
    58	 * decoders on platforms with LMH's.
    59	 *
    60	 * Return: true if a Decoder matches a Region, else false.
    61	 */
    62	bool platform_region_matches_cxld(const struct cxl_region_params *p,
    63					  const struct cxl_decoder *cxld)
    64	{
    65		const struct range *r = &cxld->hpa_range;
    66		const struct resource *res = p->res;
    67		int align = cxld->interleave_ways * SZ_256M;
    68	
    69		if (res->start == LMH_CFMWS_RANGE_START && res->start == r->start &&
  > 70		    res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
    71		    IS_ALIGNED(range_len(r), align))
    72			return true;
    73	
    74		return false;
    75	}
    76	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Fabio M. De Francesco 1 month ago
On Friday, October 10, 2025 4:49:01 PM Central European Standard Time kernel test robot wrote:
> Hi Fabio,
> 
> kernel test robot noticed the following build warnings:
> 
> [auto build test WARNING on 46037455cbb748c5e85071c95f2244e81986eb58]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Fabio-M-De-Francesco/cxl-core-Change-match_-_by_range-signatures/20251010-111627
> base:   46037455cbb748c5e85071c95f2244e81986eb58
> patch link:    https://lore.kernel.org/r/20251006155836.791418-3-fabio.m.de.francesco%40linux.intel.com
> patch subject: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
> config: i386-randconfig-011-20251010 (https://download.01.org/0day-ci/archive/20251010/202510102229.iFcGqbMH-lkp@intel.com/config)
> compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251010/202510102229.iFcGqbMH-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202510102229.iFcGqbMH-lkp@intel.com/
> 
> All warnings (new ones prefixed by >>):
> 
> >> drivers/cxl/core/platform_quirks.c:70:36: warning: result of comparison of constant 4294967296 with expression of type 'const resource_size_t' (aka 'const unsigned int') is always true [-Wtautological-constant-out-of-range-compare]
>       70 |             res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
>          |                                  ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    1 warning generated.
> 
Unavoidable warning wherever res->end is an u32 variable:

/* include/linux/types.h */

#ifdef CONFIG_PHYS_ADDR_T_64BIT
typedef u64 phys_addr_t;
#else
typedef u32 phys_addr_t;
#endif

typedef phys_addr_t resource_size_t;

/* include/linux/ioport.h */

struct resource {
	resource_size_t start;
	resource_size_t end;
};

I think we should ignore this report.

Fabio
> 
> vim +70 drivers/cxl/core/platform_quirks.c
> 
>     50	
>     51	/**
>     52	 * platform_region_matches_cxld() - Platform quirk to match a CXL Region and a
>     53	 * Switch or Endpoint Decoder. It allows matching on platforms with LMH's.
>     54	 * @p: Region Params against which @cxled is matched.
>     55	 * @cxld: Switch or Endpoint Decoder to be tested for matching @p.
>     56	 *
>     57	 * Similar to platform_cxlrd_matches_cxled(), it matches regions and
>     58	 * decoders on platforms with LMH's.
>     59	 *
>     60	 * Return: true if a Decoder matches a Region, else false.
>     61	 */
>     62	bool platform_region_matches_cxld(const struct cxl_region_params *p,
>     63					  const struct cxl_decoder *cxld)
>     64	{
>     65		const struct range *r = &cxld->hpa_range;
>     66		const struct resource *res = p->res;
>     67		int align = cxld->interleave_ways * SZ_256M;
>     68	
>     69		if (res->start == LMH_CFMWS_RANGE_START && res->start == r->start &&
>   > 70		    res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
>     71		    IS_ALIGNED(range_len(r), align))
>     72			return true;
>     73	
>     74		return false;
>     75	}
>     76	
> 
> -- 
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
> 
> 
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Dave Jiang 1 month ago

On 11/5/25 11:20 AM, Fabio M. De Francesco wrote:
> On Friday, October 10, 2025 4:49:01 PM Central European Standard Time kernel test robot wrote:
>> Hi Fabio,
>>
>> kernel test robot noticed the following build warnings:
>>
>> [auto build test WARNING on 46037455cbb748c5e85071c95f2244e81986eb58]
>>
>> url:    https://github.com/intel-lab-lkp/linux/commits/Fabio-M-De-Francesco/cxl-core-Change-match_-_by_range-signatures/20251010-111627
>> base:   46037455cbb748c5e85071c95f2244e81986eb58
>> patch link:    https://lore.kernel.org/r/20251006155836.791418-3-fabio.m.de.francesco%40linux.intel.com
>> patch subject: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
>> config: i386-randconfig-011-20251010 (https://download.01.org/0day-ci/archive/20251010/202510102229.iFcGqbMH-lkp@intel.com/config)
>> compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
>> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251010/202510102229.iFcGqbMH-lkp@intel.com/reproduce)
>>
>> If you fix the issue in a separate patch/commit (i.e. not just a new version of
>> the same patch/commit), kindly add following tags
>> | Reported-by: kernel test robot <lkp@intel.com>
>> | Closes: https://lore.kernel.org/oe-kbuild-all/202510102229.iFcGqbMH-lkp@intel.com/
>>
>> All warnings (new ones prefixed by >>):
>>
>>>> drivers/cxl/core/platform_quirks.c:70:36: warning: result of comparison of constant 4294967296 with expression of type 'const resource_size_t' (aka 'const unsigned int') is always true [-Wtautological-constant-out-of-range-compare]
>>       70 |             res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
>>          |                                  ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>    1 warning generated.
>>
> Unavoidable warning wherever res->end is an u32 variable:
> 
> /* include/linux/types.h */
> 
> #ifdef CONFIG_PHYS_ADDR_T_64BIT
> typedef u64 phys_addr_t;
> #else
> typedef u32 phys_addr_t;
> #endif
> 
> typedef phys_addr_t resource_size_t;
> 
> /* include/linux/ioport.h */
> 
> struct resource {
> 	resource_size_t start;
> 	resource_size_t end;
> };
> 
> I think we should ignore this report.

Maybe try casting res->end to (u64) and see if it shuts the warning up?

DJ

> 
> Fabio
>>
>> vim +70 drivers/cxl/core/platform_quirks.c
>>
>>     50	
>>     51	/**
>>     52	 * platform_region_matches_cxld() - Platform quirk to match a CXL Region and a
>>     53	 * Switch or Endpoint Decoder. It allows matching on platforms with LMH's.
>>     54	 * @p: Region Params against which @cxled is matched.
>>     55	 * @cxld: Switch or Endpoint Decoder to be tested for matching @p.
>>     56	 *
>>     57	 * Similar to platform_cxlrd_matches_cxled(), it matches regions and
>>     58	 * decoders on platforms with LMH's.
>>     59	 *
>>     60	 * Return: true if a Decoder matches a Region, else false.
>>     61	 */
>>     62	bool platform_region_matches_cxld(const struct cxl_region_params *p,
>>     63					  const struct cxl_decoder *cxld)
>>     64	{
>>     65		const struct range *r = &cxld->hpa_range;
>>     66		const struct resource *res = p->res;
>>     67		int align = cxld->interleave_ways * SZ_256M;
>>     68	
>>     69		if (res->start == LMH_CFMWS_RANGE_START && res->start == r->start &&
>>   > 70		    res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
>>     71		    IS_ALIGNED(range_len(r), align))
>>     72			return true;
>>     73	
>>     74		return false;
>>     75	}
>>     76	
>>
>> -- 
>> 0-DAY CI Kernel Test Service
>> https://github.com/intel/lkp-tests/wiki
>>
>>
> 
> 
> 
> 

Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by kernel test robot 1 month, 4 weeks ago
Hi Fabio,

kernel test robot noticed the following build errors:

[auto build test ERROR on 46037455cbb748c5e85071c95f2244e81986eb58]

url:    https://github.com/intel-lab-lkp/linux/commits/Fabio-M-De-Francesco/cxl-core-Change-match_-_by_range-signatures/20251010-111627
base:   46037455cbb748c5e85071c95f2244e81986eb58
patch link:    https://lore.kernel.org/r/20251006155836.791418-3-fabio.m.de.francesco%40linux.intel.com
patch subject: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
config: i386-buildonly-randconfig-005-20251010 (https://download.01.org/0day-ci/archive/20251010/202510101555.zofjGvZF-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251010/202510101555.zofjGvZF-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510101555.zofjGvZF-lkp@intel.com/

All errors (new ones prefixed by >>):

   ld: drivers/cxl/core/platform_quirks.o: in function `platform_res_adjust':
>> drivers/cxl/core/platform_quirks.c:95:(.text+0x1f1): undefined reference to `__udivdi3'


vim +95 drivers/cxl/core/platform_quirks.c

    76	
    77	void platform_res_adjust(struct resource *res,
    78				 struct cxl_endpoint_decoder *cxled,
    79				 const struct cxl_root_decoder *cxlrd)
    80	{
    81		if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
    82			return;
    83	
    84		guard(rwsem_write)(&cxl_rwsem.dpa);
    85		dev_dbg(cxled_to_memdev(cxled)->dev.parent,
    86			"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
    87			dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
    88		if (res) {
    89			/* Trim region resource overlap with LMH */
    90			res->end = cxlrd->res->end;
    91		}
    92		/* Match endpoint decoder's DPA resource to root decoder's */
    93		cxled->dpa_res->end =
    94			cxled->dpa_res->start +
  > 95			resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Fabio M. De Francesco 1 month ago
On Friday, October 10, 2025 9:38:02 AM Central European Standard Time kernel test robot wrote:
> Hi Fabio,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on 46037455cbb748c5e85071c95f2244e81986eb58]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Fabio-M-De-Francesco/cxl-core-Change-match_-_by_range-signatures/20251010-111627
> base:   46037455cbb748c5e85071c95f2244e81986eb58
> patch link:    https://lore.kernel.org/r/20251006155836.791418-3-fabio.m.de.francesco%40linux.intel.com
> patch subject: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
> config: i386-buildonly-randconfig-005-20251010 (https://download.01.org/0day-ci/archive/20251010/202510101555.zofjGvZF-lkp@intel.com/config)
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251010/202510101555.zofjGvZF-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202510101555.zofjGvZF-lkp@intel.com/
> 
> All errors (new ones prefixed by >>):
> 
>    ld: drivers/cxl/core/platform_quirks.o: in function `platform_res_adjust':
> >> drivers/cxl/core/platform_quirks.c:95:(.text+0x1f1): undefined reference to `__udivdi3'
> 
> 
> vim +95 drivers/cxl/core/platform_quirks.c
> 
>     76	
>     77	void platform_res_adjust(struct resource *res,
>     78				 struct cxl_endpoint_decoder *cxled,
>     79				 const struct cxl_root_decoder *cxlrd)
>     80	{
>     81		if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
>     82			return;
>     83	
>     84		guard(rwsem_write)(&cxl_rwsem.dpa);
>     85		dev_dbg(cxled_to_memdev(cxled)->dev.parent,
>     86			"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
>     87			dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
>     88		if (res) {
>     89			/* Trim region resource overlap with LMH */
>     90			res->end = cxlrd->res->end;
>     91		}
>     92		/* Match endpoint decoder's DPA resource to root decoder's */
>     93		cxled->dpa_res->end =
>     94			cxled->dpa_res->start +
>   > 95			resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;
> 
I think that the use of udiv_64() (per Alison suggestion) will fix this 
error.

Fabio
>
> -- 
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
> 
> 
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Alison Schofield 1 month, 4 weeks ago
On Mon, Oct 06, 2025 at 05:58:05PM +0200, Fabio M. De Francesco wrote:
> On a x86 platform with a low memory hole (LHM), the BIOS may publish
> CFMWS that describes a system physical address (SPA) range that
> typically is only a subset of the corresponding CXL intermediate switch
> and endpoint decoder's host physical address (HPA) ranges. The CFMWS
> range never intersects the LHM and so the driver instantiates a root
> decoder whose HPA range size doesn't fully contain the matching switch
> and endpoint decoders' HPA ranges.[1]
> 
> To construct regions and attach decoders, the driver needs to match root
> decoders and regions with endpoint decoders. The process fails and
> returns errors because the driver is not designed to deal with SPA
> ranges which are smaller than the corresponding hardware decoders HPA
> ranges.
> 
> Introduce two functions that indirectly detect the presence of x86 LMH
> and allow the matching between a root decoder or an already constructed
> region with a corresponding intermediate switch or endpoint decoder to
> enable the construction of a region and the subsequent attachment of the
> same decoders to that region.
> 
> These functions return true when SPA/HPA misalignments due to LMH's are
> detected under specific conditions:
> 
> - Both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (i.e.,
>   0x0 on x86 with LMH's).
> - The SPA range's size is less than HPA's.
> - The SPA range's size is less than 4G.
> - The HPA range's size is aligned to the NIW * 256M rule.
> 
> Also introduce a function that adjusts the range end of a region to be
> constructed and the DPA range's end of the endpoint decoders that will
> be later attached to that region.

Hi Fabio,

Your getting some fresh eyes on some of this with my review.
The adjustment of resources is what caught my eye, and I looked at
platform_res_adjust() in this patch and it's usage in the next patch.


> 
> [1] commit 7a81173f3 ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")
> 
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>
> ---
>  drivers/cxl/Kconfig                |  4 ++
>  drivers/cxl/core/Makefile          |  1 +
>  drivers/cxl/core/platform_quirks.c | 99 ++++++++++++++++++++++++++++++
>  drivers/cxl/core/platform_quirks.h | 33 ++++++++++
>  4 files changed, 137 insertions(+)
>  create mode 100644 drivers/cxl/core/platform_quirks.c
>  create mode 100644 drivers/cxl/core/platform_quirks.h
> 
> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> index 48b7314afdb8..03c0583bc9a3 100644
> --- a/drivers/cxl/Kconfig
> +++ b/drivers/cxl/Kconfig
> @@ -211,6 +211,10 @@ config CXL_REGION
>  
>  	  If unsure say 'y'
>  
> +config CXL_PLATFORM_QUIRKS
> +	def_bool y
> +	depends on CXL_REGION
> +

Why no help text for the new CONFIG option?
That text will probably answer my next question: why do we have the
option?

snip


I have comments for the callsites of platform_res_adjust() in the next
patch, but I'll pull some of that back into this patch to keep it all
in one, more logical, place.

There are 2 callsites, and one passes in NULL for 'res' because
at that site we know that the regions struct res has been adjusted.
I felt that was subtle, and that it may be better to just pass in
the 'res' all the time and let the function adjust if needed,
ignore if not needed.

The name platform_res_adjust() suggested that the 'res' as in the
region 'res' was getting adjusted. This is adjusting multiple resources
- the region resource and the endpoint decoder dpa resource. If it's
meant to be kind of opaque, that's ok, but by using _res_ it sure sounds
like it's adjusting the the region resource (when viewed from the call site).

I might have done this in 2 helpers for crispness:
res = platform_adjust_region_resource()
cxled = platform_adjust_endpoint_decoder()

Then you could adjust the region resource once when the region
is constructed, and the endpoint regions every time in 
cxl_add_to_region().

If you are settled with one adjust routine, perhaps just a 
rename to platform_adjust_resources() will make it sound as
broad as it is.


> +void platform_res_adjust(struct resource *res,
> +			 struct cxl_endpoint_decoder *cxled,
> +			 const struct cxl_root_decoder *cxlrd)
> +{
> +	if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
> +		return;
> +
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
> +	dev_dbg(cxled_to_memdev(cxled)->dev.parent,
> +		"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
> +		dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> +	if (res) {
> +		/* Trim region resource overlap with LMH */
> +		res->end = cxlrd->res->end;
> +	}

Prefer dev_info so always appears.
Prefer to see the region name.
I'm guessing the dev_dbg() above and the dev_info() below are written
with the idea that we want the before view only in dev_dbg() and the
after view only in dev_info().

Looks like this now:
[] cxl_core:platform_res_adjust:90: cxl_mock_mem cxl_mem.0: Low Memory Hole detected. Resources were (decoder12.0: [mem 0x3ff010000000-0x3ff04fffffff flags 0x200], [mem 0x00000000-0x1fffffff flags 0x80000200])
[] cxl_mock_mem cxl_mem.0: Resources have been adjusted for LMH (decoder12.0: [mem 0x3ff010000000-0x3ff03fffffff flags 0x200], [mem 0x00000000-0x17ffffff flags 0x80000200])
[] cxl_core:platform_res_adjust:90: cxl_mock_mem cxl_mem.4: Low Memory Hole detected. Resources were (decoder13.0: (null), [mem 0x00000000-0x1fffffff flags 0x80000200])
[] cxl_mock_mem cxl_mem.4: Resources have been adjusted for LMH (decoder13.0: (null), [mem 0x00000000-0x17ffffff flags 0x80000200])

I'll suggest this to emit explicitly what is changing:
[] cxl region0: LMH Low memory hole trims region resource [mem 0x3ff010000000-0x3ff04fffffff flags 0x200] to [mem 0x3ff010000000-0x3ff03fffffff flags 0x200])
[] cxl decoder13.0: LMH Low memory hole trims DPA resource [mem 0x00000000-0x1fffffff flags 0x80000200] to [mem 0x00000000-0x17ffffff flags 0x80000200])
[] cxl decoder17.0: LMH Low memory hole trims DPA resource [mem 0x00000000-0x1fffffff flags 0x80000200] to [mem 0x00000000-0x17ffffff flags 0x80000200])


> +	/* Match endpoint decoder's DPA resource to root decoder's */
A 'Match' would be if the the endpoint and root decoder resource were
same. This is more of adjustment or recalculation of the DPA length.

> +	cxled->dpa_res->end =
> +		cxled->dpa_res->start +
> +		resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;

I'm cautious about the use of division and suggest this as the more
bullet-proof kernel style:

	slice = div_u64(resource_size(cxlrd->res), cxled->cxld.interleave_ways);
	cxled->dpa_res->end = cxled->dpa_res->start + slice - 1;



> +	dev_info(cxled_to_memdev(cxled)->dev.parent,
> +		 "Resources have been adjusted for LMH (%s: %pr, %pr)\n",
> +		 dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> +}

Here's the diff showing how I emmited the that messaging above. I really
wanted to have that region name to emit. This was done keeping the
adjust in one function, but maybe you'll choose to split :)


---
 drivers/cxl/core/platform_quirks.c | 32 ++++++++++++++++++------------
 drivers/cxl/core/platform_quirks.h |  6 ++++--
 drivers/cxl/core/region.c          | 15 ++++++++------
 3 files changed, 32 insertions(+), 21 deletions(-)

diff --git a/drivers/cxl/core/platform_quirks.c b/drivers/cxl/core/platform_quirks.c
index aecd376f2766..aa25770c088a 100644
--- a/drivers/cxl/core/platform_quirks.c
+++ b/drivers/cxl/core/platform_quirks.c
@@ -81,24 +81,30 @@ EXPORT_SYMBOL_NS_GPL(__platform_region_matches_cxld, "CXL");
 
 void platform_res_adjust(struct resource *res,
 			 struct cxl_endpoint_decoder *cxled,
-			 const struct cxl_root_decoder *cxlrd)
+			 const struct cxl_root_decoder *cxlrd,
+			 const struct device *region_dev)
 {
+	struct resource dpa_res_orig = *cxled->dpa_res;
+	u64 slice;
+
 	if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
 		return;
 
 	guard(rwsem_write)(&cxl_rwsem.dpa);
-	dev_dbg(cxled_to_memdev(cxled)->dev.parent,
-		"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
-		dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
-	if (res) {
-		/* Trim region resource overlap with LMH */
+
+	/* Region resource will need a trim at first endpoint attach only */
+	if ((res) && (res->end != cxlrd->res->end)) {
+		dev_info(region_dev,
+			 "LMH Low memory hole trims region resource %pr to %pr)\n",
+			 res, cxlrd->res);
 		res->end = cxlrd->res->end;
 	}
-	/* Match endpoint decoder's DPA resource to root decoder's */
-	cxled->dpa_res->end =
-		cxled->dpa_res->start +
-		resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;
-	dev_info(cxled_to_memdev(cxled)->dev.parent,
-		 "Resources have been adjusted for LMH (%s: %pr, %pr)\n",
-		 dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
+
+	/* Adjust the endpoint decoder DPA resource end */
+	slice = div_u64(resource_size(cxlrd->res), cxled->cxld.interleave_ways);
+	cxled->dpa_res->end = cxled->dpa_res->start + slice - 1;
+
+	dev_info(&cxled->cxld.dev,
+		 "LMH Low memory hole trims DPA resource %pr to %pr)\n",
+		 &dpa_res_orig, cxled->dpa_res);
 }
diff --git a/drivers/cxl/core/platform_quirks.h b/drivers/cxl/core/platform_quirks.h
index bdea00365dad..55647711cdb4 100644
--- a/drivers/cxl/core/platform_quirks.h
+++ b/drivers/cxl/core/platform_quirks.h
@@ -17,7 +17,8 @@ bool __platform_region_matches_cxld(const struct cxl_region_params *p,
 				    const struct cxl_decoder *cxld);
 void platform_res_adjust(struct resource *res,
 			 struct cxl_endpoint_decoder *cxled,
-			 const struct cxl_root_decoder *cxlrd);
+			 const struct cxl_root_decoder *cxlrd,
+			 const struct device *region_dev);
 #else
 static inline bool
 platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
@@ -35,7 +36,8 @@ platform_region_matches_cxld(const struct cxl_region_params *p,
 
 inline void platform_res_adjust(struct resource *res,
 				struct cxl_endpoint_decoder *cxled,
-				const struct cxl_root_decoder *cxlrd)
+				const struct cxl_root_decoder *cxlrd,
+				const struct device *region_dev);
 {
 }
 #endif /* CONFIG_CXL_PLATFORM_QUIRKS */
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 9a499bfca23d..d4298a61b912 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3502,7 +3502,7 @@ static int __construct_region(struct cxl_region *cxlr,
 	 * Trim the HPA retrieved from hardware to fit the SPA mapped by the
 	 * platform
 	 */
-	platform_res_adjust(res, cxled, cxlrd);
+	platform_res_adjust(res, cxled, cxlrd, &cxlr->dev);
 
 	rc = cxl_extended_linear_cache_resize(cxlr, res);
 	if (rc && rc != -EOPNOTSUPP) {
@@ -3611,14 +3611,17 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
 	mutex_lock(&cxlrd->range_lock);
 	struct cxl_region *cxlr __free(put_cxl_region) =
 		cxl_find_region_by_range(cxlrd, cxled);
-	if (!cxlr)
+	if (!cxlr) {
 		cxlr = construct_region(cxlrd, cxled);
-	else
+	} else {
 		/*
-		 * Adjust the Endpoint Decoder's dpa_res to fit the Region which
-		 * it has to be attached to
+		 * Platform adjustments are done in construct_region()
+		 * for first target, and here for additional targets.
 		 */
-		platform_res_adjust(NULL, cxled, cxlrd);
+		p = &cxlr->params;
+		platform_res_adjust(p->res, cxled, cxlrd, &cxlr->dev);
+	}
+
 	mutex_unlock(&cxlrd->range_lock);
 
 	rc = PTR_ERR_OR_ZERO(cxlr);
-- 
2.37.3

>
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Fabio M. De Francesco 1 month ago
On Thursday, October 9, 2025 5:16:05 AM Central European Standard Time Alison Schofield wrote:
> On Mon, Oct 06, 2025 at 05:58:05PM +0200, Fabio M. De Francesco wrote:
> > On a x86 platform with a low memory hole (LHM), the BIOS may publish
> > CFMWS that describes a system physical address (SPA) range that
> > typically is only a subset of the corresponding CXL intermediate switch
> > and endpoint decoder's host physical address (HPA) ranges. The CFMWS
> > range never intersects the LHM and so the driver instantiates a root
> > decoder whose HPA range size doesn't fully contain the matching switch
> > and endpoint decoders' HPA ranges.[1]
> > 
> > To construct regions and attach decoders, the driver needs to match root
> > decoders and regions with endpoint decoders. The process fails and
> > returns errors because the driver is not designed to deal with SPA
> > ranges which are smaller than the corresponding hardware decoders HPA
> > ranges.
> > 
> > Introduce two functions that indirectly detect the presence of x86 LMH
> > and allow the matching between a root decoder or an already constructed
> > region with a corresponding intermediate switch or endpoint decoder to
> > enable the construction of a region and the subsequent attachment of the
> > same decoders to that region.
> > 
> > These functions return true when SPA/HPA misalignments due to LMH's are
> > detected under specific conditions:
> > 
> > - Both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (i.e.,
> >   0x0 on x86 with LMH's).
> > - The SPA range's size is less than HPA's.
> > - The SPA range's size is less than 4G.
> > - The HPA range's size is aligned to the NIW * 256M rule.
> > 
> > Also introduce a function that adjusts the range end of a region to be
> > constructed and the DPA range's end of the endpoint decoders that will
> > be later attached to that region.
> 
> Hi Fabio,
> 
> Your getting some fresh eyes on some of this with my review.
> The adjustment of resources is what caught my eye, and I looked at
> platform_res_adjust() in this patch and it's usage in the next patch.
> 
Hi Alison,

Thanks for looking at this version with fresh eyes. 

As always, your reviews catch details that others miss and go well beyond 
what's typical - providing working code and demonstrating the results is 
very helpful.
> > 
> > [1] commit 7a81173f3 ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")
> > 
> > Cc: Alison Schofield <alison.schofield@intel.com>
> > Cc: Dan Williams <dan.j.williams@intel.com>
> > Cc: Dave Jiang <dave.jiang@intel.com>
> > Cc: Ira Weiny <ira.weiny@intel.com>
> > Signed-off-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>
> > ---
> >  drivers/cxl/Kconfig                |  4 ++
> >  drivers/cxl/core/Makefile          |  1 +
> >  drivers/cxl/core/platform_quirks.c | 99 ++++++++++++++++++++++++++++++
> >  drivers/cxl/core/platform_quirks.h | 33 ++++++++++
> >  4 files changed, 137 insertions(+)
> >  create mode 100644 drivers/cxl/core/platform_quirks.c
> >  create mode 100644 drivers/cxl/core/platform_quirks.h
> > 
> > diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> > index 48b7314afdb8..03c0583bc9a3 100644
> > --- a/drivers/cxl/Kconfig
> > +++ b/drivers/cxl/Kconfig
> > @@ -211,6 +211,10 @@ config CXL_REGION
> >  
> >  	  If unsure say 'y'
> >  
> > +config CXL_PLATFORM_QUIRKS
> > +	def_bool y
> > +	depends on CXL_REGION
> > +
> 
> Why no help text for the new CONFIG option?
>
Good catch. Below I'll show you the help text I'm thinking to add.  
>
> That text will probably answer my next question: why do we have the
> option?
>
For now, PLATFORM_QUIRKS enables only region creation and endpoint
attachment support on x86 with LMH. In the future, it's intended to be
selected by other platforms that need quirks.[1]

I think we should allow users to choose whether to enable it - they can
leave it disabled, or select it if they know it's needed or are unsure
about potential platform issues. The overhead from running quirks should
be minimal.

All that said, how about this?

diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 03c0583bc9a3..5ab8d5c23187 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -212,8 +212,15 @@ config CXL_REGION
          If unsure say 'y'
 
 config CXL_PLATFORM_QUIRKS
-       def_bool y
+       bool "CXL: Region Platform Quirks"
        depends on CXL_REGION
+       help
+         Enable support for the following platform quirks:
+
+               - Region creation / Endpoint Decoders attach in x86 with Low
+                 Memory Holes (Documentation/driver-api/cxl/conventions.rst).
+
+         If unsure say 'y'
 
 config CXL_REGION_INVALIDATION_TEST
        bool "CXL: Region Cache Management Bypass (TEST)"

> 
> I have comments for the callsites of platform_res_adjust() in the next
> patch, but I'll pull some of that back into this patch to keep it all
> in one, more logical, place.
> 
> There are 2 callsites, and one passes in NULL for 'res' because
> at that site we know that the regions struct res has been adjusted.
> I felt that was subtle, and that it may be better to just pass in
> the 'res' all the time and let the function adjust if needed,
> ignore if not needed.
>
I'll implement your suggestion in v6.
>  
> The name platform_res_adjust() suggested that the 'res' as in the
> region 'res' was getting adjusted. This is adjusting multiple resources
> - the region resource and the endpoint decoder dpa resource. If it's
> meant to be kind of opaque, that's ok, but by using _res_ it sure sounds
> like it's adjusting the the region resource (when viewed from the call site).
> 
> I might have done this in 2 helpers for crispness:
> res = platform_adjust_region_resource()
> cxled = platform_adjust_endpoint_decoder()
> 
> Then you could adjust the region resource once when the region
> is constructed, and the endpoint regions every time in 
> cxl_add_to_region().
> 
> If you are settled with one adjust routine, perhaps just a 
> rename to platform_adjust_resources() will make it sound as
> broad as it is.
>
I prefer not to introduce more than one platform_adjust_*(), so I'll rename 
platform_res_adjust() to platform_adjust_resources().
> 
> > +void platform_res_adjust(struct resource *res,
> > +			 struct cxl_endpoint_decoder *cxled,
> > +			 const struct cxl_root_decoder *cxlrd)
> > +{
> > +	if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
> > +		return;
> > +
> > +	guard(rwsem_write)(&cxl_rwsem.dpa);
> > +	dev_dbg(cxled_to_memdev(cxled)->dev.parent,
> > +		"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
> > +		dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> > +	if (res) {
> > +		/* Trim region resource overlap with LMH */
> > +		res->end = cxlrd->res->end;
> > +	}
> 
> Prefer dev_info so always appears.
> Prefer to see the region name.
>
Okay to both suggestions.
>
> I'm guessing the dev_dbg() above and the dev_info() below are written
> with the idea that we want the before view only in dev_dbg() and the
> after view only in dev_info().
> 
> Looks like this now:
> [] cxl_core:platform_res_adjust:90: cxl_mock_mem cxl_mem.0: Low Memory Hole detected. Resources were (decoder12.0: [mem 0x3ff010000000-0x3ff04fffffff flags 0x200], [mem 0x00000000-0x1fffffff flags 0x80000200])
> [] cxl_mock_mem cxl_mem.0: Resources have been adjusted for LMH (decoder12.0: [mem 0x3ff010000000-0x3ff03fffffff flags 0x200], [mem 0x00000000-0x17ffffff flags 0x80000200])
> [] cxl_core:platform_res_adjust:90: cxl_mock_mem cxl_mem.4: Low Memory Hole detected. Resources were (decoder13.0: (null), [mem 0x00000000-0x1fffffff flags 0x80000200])
> [] cxl_mock_mem cxl_mem.4: Resources have been adjusted for LMH (decoder13.0: (null), [mem 0x00000000-0x17ffffff flags 0x80000200])
> 
> I'll suggest this to emit explicitly what is changing:
> [] cxl region0: LMH Low memory hole trims region resource [mem 0x3ff010000000-0x3ff04fffffff flags 0x200] to [mem 0x3ff010000000-0x3ff03fffffff flags 0x200])
> [] cxl decoder13.0: LMH Low memory hole trims DPA resource [mem 0x00000000-0x1fffffff flags 0x80000200] to [mem 0x00000000-0x17ffffff flags 0x80000200])
> [] cxl decoder17.0: LMH Low memory hole trims DPA resource [mem 0x00000000-0x1fffffff flags 0x80000200] to [mem 0x00000000-0x17ffffff flags 0x80000200])
>
I'll make platform_adjust_resources() output the messages you just showed. 
> 
> > +	/* Match endpoint decoder's DPA resource to root decoder's */
> A 'Match' would be if the the endpoint and root decoder resource were
> same. This is more of adjustment or recalculation of the DPA length.
> 
> > +	cxled->dpa_res->end =
> > +		cxled->dpa_res->start +
> > +		resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;
> 
> I'm cautious about the use of division and suggest this as the more
> bullet-proof kernel style:
> 
> 	slice = div_u64(resource_size(cxlrd->res), cxled->cxld.interleave_ways);
> 	cxled->dpa_res->end = cxled->dpa_res->start + slice - 1;
> 
div_u64() is the safer choice.  
> 
> > +	dev_info(cxled_to_memdev(cxled)->dev.parent,
> > +		 "Resources have been adjusted for LMH (%s: %pr, %pr)\n",
> > +		 dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> > +}
> 
> Here's the diff showing how I emmited the that messaging above. I really
> wanted to have that region name to emit. This was done keeping the
> adjust in one function, but maybe you'll choose to split :)
>
Thank you,

Fabio

[1] https://lore.kernel.org/linux-cxl/67ee07cd4f8ec_1c2c6294d5@dwillia2-xfh.jf.intel.com.notmuch/
> 
> ---
>  drivers/cxl/core/platform_quirks.c | 32 ++++++++++++++++++------------
>  drivers/cxl/core/platform_quirks.h |  6 ++++--
>  drivers/cxl/core/region.c          | 15 ++++++++------
>  3 files changed, 32 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/cxl/core/platform_quirks.c b/drivers/cxl/core/platform_quirks.c
> index aecd376f2766..aa25770c088a 100644
> --- a/drivers/cxl/core/platform_quirks.c
> +++ b/drivers/cxl/core/platform_quirks.c
> @@ -81,24 +81,30 @@ EXPORT_SYMBOL_NS_GPL(__platform_region_matches_cxld, "CXL");
>  
>  void platform_res_adjust(struct resource *res,
>  			 struct cxl_endpoint_decoder *cxled,
> -			 const struct cxl_root_decoder *cxlrd)
> +			 const struct cxl_root_decoder *cxlrd,
> +			 const struct device *region_dev)
>  {
> +	struct resource dpa_res_orig = *cxled->dpa_res;
> +	u64 slice;
> +
>  	if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
>  		return;
>  
>  	guard(rwsem_write)(&cxl_rwsem.dpa);
> -	dev_dbg(cxled_to_memdev(cxled)->dev.parent,
> -		"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
> -		dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> -	if (res) {
> -		/* Trim region resource overlap with LMH */
> +
> +	/* Region resource will need a trim at first endpoint attach only */
> +	if ((res) && (res->end != cxlrd->res->end)) {
> +		dev_info(region_dev,
> +			 "LMH Low memory hole trims region resource %pr to %pr)\n",
> +			 res, cxlrd->res);
>  		res->end = cxlrd->res->end;
>  	}
> -	/* Match endpoint decoder's DPA resource to root decoder's */
> -	cxled->dpa_res->end =
> -		cxled->dpa_res->start +
> -		resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;
> -	dev_info(cxled_to_memdev(cxled)->dev.parent,
> -		 "Resources have been adjusted for LMH (%s: %pr, %pr)\n",
> -		 dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> +
> +	/* Adjust the endpoint decoder DPA resource end */
> +	slice = div_u64(resource_size(cxlrd->res), cxled->cxld.interleave_ways);
> +	cxled->dpa_res->end = cxled->dpa_res->start + slice - 1;
> +
> +	dev_info(&cxled->cxld.dev,
> +		 "LMH Low memory hole trims DPA resource %pr to %pr)\n",
> +		 &dpa_res_orig, cxled->dpa_res);
>  }
> diff --git a/drivers/cxl/core/platform_quirks.h b/drivers/cxl/core/platform_quirks.h
> index bdea00365dad..55647711cdb4 100644
> --- a/drivers/cxl/core/platform_quirks.h
> +++ b/drivers/cxl/core/platform_quirks.h
> @@ -17,7 +17,8 @@ bool __platform_region_matches_cxld(const struct cxl_region_params *p,
>  				    const struct cxl_decoder *cxld);
>  void platform_res_adjust(struct resource *res,
>  			 struct cxl_endpoint_decoder *cxled,
> -			 const struct cxl_root_decoder *cxlrd);
> +			 const struct cxl_root_decoder *cxlrd,
> +			 const struct device *region_dev);
>  #else
>  static inline bool
>  platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
> @@ -35,7 +36,8 @@ platform_region_matches_cxld(const struct cxl_region_params *p,
>  
>  inline void platform_res_adjust(struct resource *res,
>  				struct cxl_endpoint_decoder *cxled,
> -				const struct cxl_root_decoder *cxlrd)
> +				const struct cxl_root_decoder *cxlrd,
> +				const struct device *region_dev);
>  {
>  }
>  #endif /* CONFIG_CXL_PLATFORM_QUIRKS */
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 9a499bfca23d..d4298a61b912 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3502,7 +3502,7 @@ static int __construct_region(struct cxl_region *cxlr,
>  	 * Trim the HPA retrieved from hardware to fit the SPA mapped by the
>  	 * platform
>  	 */
> -	platform_res_adjust(res, cxled, cxlrd);
> +	platform_res_adjust(res, cxled, cxlrd, &cxlr->dev);
>  
>  	rc = cxl_extended_linear_cache_resize(cxlr, res);
>  	if (rc && rc != -EOPNOTSUPP) {
> @@ -3611,14 +3611,17 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
>  	mutex_lock(&cxlrd->range_lock);
>  	struct cxl_region *cxlr __free(put_cxl_region) =
>  		cxl_find_region_by_range(cxlrd, cxled);
> -	if (!cxlr)
> +	if (!cxlr) {
>  		cxlr = construct_region(cxlrd, cxled);
> -	else
> +	} else {
>  		/*
> -		 * Adjust the Endpoint Decoder's dpa_res to fit the Region which
> -		 * it has to be attached to
> +		 * Platform adjustments are done in construct_region()
> +		 * for first target, and here for additional targets.
>  		 */
> -		platform_res_adjust(NULL, cxled, cxlrd);
> +		p = &cxlr->params;
> +		platform_res_adjust(p->res, cxled, cxlrd, &cxlr->dev);
> +	}
> +
>  	mutex_unlock(&cxlrd->range_lock);
>  
>  	rc = PTR_ERR_OR_ZERO(cxlr);
> -- 
> 2.37.3
> 
> > 
> 
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Dave Jiang 2 months ago

On 10/6/25 8:58 AM, Fabio M. De Francesco wrote:
> On a x86 platform with a low memory hole (LHM), the BIOS may publish
> CFMWS that describes a system physical address (SPA) range that
> typically is only a subset of the corresponding CXL intermediate switch
> and endpoint decoder's host physical address (HPA) ranges. The CFMWS
> range never intersects the LHM and so the driver instantiates a root
> decoder whose HPA range size doesn't fully contain the matching switch
> and endpoint decoders' HPA ranges.[1]
> 
> To construct regions and attach decoders, the driver needs to match root
> decoders and regions with endpoint decoders. The process fails and
> returns errors because the driver is not designed to deal with SPA
> ranges which are smaller than the corresponding hardware decoders HPA
> ranges.
> 
> Introduce two functions that indirectly detect the presence of x86 LMH
> and allow the matching between a root decoder or an already constructed
> region with a corresponding intermediate switch or endpoint decoder to
> enable the construction of a region and the subsequent attachment of the
> same decoders to that region.
> 
> These functions return true when SPA/HPA misalignments due to LMH's are
> detected under specific conditions:
> 
> - Both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (i.e.,
>   0x0 on x86 with LMH's).
> - The SPA range's size is less than HPA's.
> - The SPA range's size is less than 4G.
> - The HPA range's size is aligned to the NIW * 256M rule.
> 
> Also introduce a function that adjusts the range end of a region to be
> constructed and the DPA range's end of the endpoint decoders that will
> be later attached to that region.
> 
> [1] commit 7a81173f3 ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")

I don't think this is the right hash.
c5dca38633da ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")

> 
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>
> ---
>  drivers/cxl/Kconfig                |  4 ++
>  drivers/cxl/core/Makefile          |  1 +
>  drivers/cxl/core/platform_quirks.c | 99 ++++++++++++++++++++++++++++++
>  drivers/cxl/core/platform_quirks.h | 33 ++++++++++
>  4 files changed, 137 insertions(+)
>  create mode 100644 drivers/cxl/core/platform_quirks.c
>  create mode 100644 drivers/cxl/core/platform_quirks.h
> 
> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> index 48b7314afdb8..03c0583bc9a3 100644
> --- a/drivers/cxl/Kconfig
> +++ b/drivers/cxl/Kconfig
> @@ -211,6 +211,10 @@ config CXL_REGION
>  
>  	  If unsure say 'y'
>  
> +config CXL_PLATFORM_QUIRKS
> +	def_bool y
> +	depends on CXL_REGION
> +
>  config CXL_REGION_INVALIDATION_TEST
>  	bool "CXL: Region Cache Management Bypass (TEST)"
>  	depends on CXL_REGION
> diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
> index 5ad8fef210b5..1684e46b8709 100644
> --- a/drivers/cxl/core/Makefile
> +++ b/drivers/cxl/core/Makefile
> @@ -17,6 +17,7 @@ cxl_core-y += cdat.o
>  cxl_core-y += ras.o
>  cxl_core-$(CONFIG_TRACING) += trace.o
>  cxl_core-$(CONFIG_CXL_REGION) += region.o
> +cxl_core-$(CONFIG_CXL_PLATFORM_QUIRKS) += platform_quirks.o
>  cxl_core-$(CONFIG_CXL_MCE) += mce.o
>  cxl_core-$(CONFIG_CXL_FEATURES) += features.o
>  cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
> diff --git a/drivers/cxl/core/platform_quirks.c b/drivers/cxl/core/platform_quirks.c
> new file mode 100644
> index 000000000000..7e76e392b1ae
> --- /dev/null
> +++ b/drivers/cxl/core/platform_quirks.c
> @@ -0,0 +1,99 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/range.h>
> +#include "platform_quirks.h"
> +#include "cxlmem.h"
> +#include "core.h"
> +
> +/* Start of CFMWS range that end before x86 Low Memory Holes */
> +#define LMH_CFMWS_RANGE_START 0x0ULL
> +
> +/**
> + * platform_cxlrd_matches_cxled() - Platform quirk to match CXL Root and
> + * Endpoint Decoders. It allows matching on platforms with LMH's.
> + * @cxlrd: The Root Decoder against which @cxled is tested for matching.
> + * @cxled: The Endpoint Decoder to be tested for matching @cxlrd.
> + *
> + * platform_cxlrd_matches_cxled() is typically called from the
> + * match_*_by_range() functions in region.c. It checks if an endpoint decoder
> + * matches a given root decoder and returns true to allow the driver to succeed
> + * in the construction of regions where it would otherwise fail for the presence
> + * of a Low Memory Hole (see Documentation/driver-api/cxl/conventions.rst).
> + *
> + * In x86 platforms with LMH's, the CFMWS ranges never intersect the LMH, the
> + * endpoint decoder's HPA range size is always guaranteed aligned to NIW*256MB
> + * and also typically larger than the matching root decoder's, and the root
> + * decoder's range end is at an address that is necessarily less than SZ_4G
> + * (i.e., the Hole is in Low Memory - this function doesn't deal with other
> + * kinds of holes).
> + *
> + * Return: true if an endpoint matches a root decoder, else false.
> + */
> +bool platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
> +				  const struct cxl_endpoint_decoder *cxled)
> +{
> +	const struct range *rd_r, *sd_r;
> +	int align;
> +
> +	rd_r = &cxlrd->cxlsd.cxld.hpa_range;
> +	sd_r = &cxled->cxld.hpa_range;
> +	align = cxled->cxld.interleave_ways * SZ_256M;
> +
> +	if (rd_r->start == LMH_CFMWS_RANGE_START &&
> +	    rd_r->start == sd_r->start && rd_r->end < sd_r->end &&

Break this into 2 lines to make it more readable

> +	    rd_r->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&
> +	    IS_ALIGNED(range_len(sd_r), align))
> +		return true;
> +
> +	return false;
> +}
> +
> +/**
> + * platform_region_matches_cxld() - Platform quirk to match a CXL Region and a
> + * Switch or Endpoint Decoder. It allows matching on platforms with LMH's.
> + * @p: Region Params against which @cxled is matched.
> + * @cxld: Switch or Endpoint Decoder to be tested for matching @p.
> + *
> + * Similar to platform_cxlrd_matches_cxled(), it matches regions and
> + * decoders on platforms with LMH's.
> + *
> + * Return: true if a Decoder matches a Region, else false.
> + */
> +bool platform_region_matches_cxld(const struct cxl_region_params *p,
> +				  const struct cxl_decoder *cxld)
> +{
> +	const struct range *r = &cxld->hpa_range;
> +	const struct resource *res = p->res;
> +	int align = cxld->interleave_ways * SZ_256M;
> +
> +	if (res->start == LMH_CFMWS_RANGE_START && res->start == r->start &&
> +	    res->end < r->end && res->end < (LMH_CFMWS_RANGE_START + SZ_4G) &&

Break the first and this line into 2 lines each with a single compare to make it more readable

> +	    IS_ALIGNED(range_len(r), align))
> +		return true;
> +
> +	return false;
> +}
> +
> +void platform_res_adjust(struct resource *res,
> +			 struct cxl_endpoint_decoder *cxled,
> +			 const struct cxl_root_decoder *cxlrd)
> +{
> +	if (!platform_cxlrd_matches_cxled(cxlrd, cxled))
> +		return;
> +
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
> +	dev_dbg(cxled_to_memdev(cxled)->dev.parent,
> +		"Low Memory Hole detected. Resources were (%s: %pr, %pr)\n",
> +		dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> +	if (res) {
> +		/* Trim region resource overlap with LMH */
> +		res->end = cxlrd->res->end;
> +	}
> +	/* Match endpoint decoder's DPA resource to root decoder's */
> +	cxled->dpa_res->end =
> +		cxled->dpa_res->start +

this can be on the same line as the first

DJ

> +		resource_size(cxlrd->res) / cxled->cxld.interleave_ways - 1;
> +	dev_info(cxled_to_memdev(cxled)->dev.parent,
> +		 "Resources have been adjusted for LMH (%s: %pr, %pr)\n",
> +		 dev_name(&cxled->cxld.dev), res, cxled->dpa_res);
> +}
> diff --git a/drivers/cxl/core/platform_quirks.h b/drivers/cxl/core/platform_quirks.h
> new file mode 100644
> index 000000000000..a15592b4e90e
> --- /dev/null
> +++ b/drivers/cxl/core/platform_quirks.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#include "cxl.h"
> +
> +#ifdef CONFIG_CXL_PLATFORM_QUIRKS
> +bool platform_cxlrd_matches_cxled(const struct cxl_root_decoder *cxlrd,
> +				  const struct cxl_endpoint_decoder *cxled);
> +bool platform_region_matches_cxld(const struct cxl_region_params *p,
> +				  const struct cxl_decoder *cxld);
> +void platform_res_adjust(struct resource *res,
> +			 struct cxl_endpoint_decoder *cxled,
> +			 const struct cxl_root_decoder *cxlrd);
> +#else
> +static inline bool
> +platform_root_decoder_contains(const struct cxl_root_decoder *cxlrd,
> +			       const struct cxl_endpoint_decoder *cxled)
> +{
> +	return false;
> +}
> +
> +static inline bool
> +platform_region_matches_cxld(const struct cxl_region_params *p,
> +			     const struct cxl_decoder *cxld)
> +{
> +	return false;
> +}
> +
> +inline void platform_res_adjust(struct resource *res,
> +				struct cxl_endpoint_decoder *cxled,
> +				const struct cxl_root_decoder *cxlrd)
> +{
> +}
> +#endif /* CONFIG_CXL_PLATFORM_QUIRKS */
Re: [PATCH 2/4 v5] cxl/core: Add helpers to detect Low Memory Holes on x86
Posted by Gregory Price 2 months ago
On Mon, Oct 06, 2025 at 05:58:05PM +0200, Fabio M. De Francesco wrote:
> On a x86 platform with a low memory hole (LHM), the BIOS may publish
                                        ^^^ LMH ^^^

> CFMWS that describes a system physical address (SPA) range that
> typically is only a subset of the corresponding CXL intermediate switch
> and endpoint decoder's host physical address (HPA) ranges. The CFMWS
> range never intersects the LHM and so the driver instantiates a root
> decoder whose HPA range size doesn't fully contain the matching switch
> and endpoint decoders' HPA ranges.[1]
> 
> To construct regions and attach decoders, the driver needs to match root
> decoders and regions with endpoint decoders. The process fails and
> returns errors because the driver is not designed to deal with SPA
> ranges which are smaller than the corresponding hardware decoders HPA
> ranges.
> 
> Introduce two functions that indirectly detect the presence of x86 LMH
> and allow the matching between a root decoder or an already constructed
> region with a corresponding intermediate switch or endpoint decoder to
> enable the construction of a region and the subsequent attachment of the
> same decoders to that region.
> 
> These functions return true when SPA/HPA misalignments due to LMH's are
> detected under specific conditions:
> 
> - Both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (i.e.,
>   0x0 on x86 with LMH's).
> - The SPA range's size is less than HPA's.
> - The SPA range's size is less than 4G.
> - The HPA range's size is aligned to the NIW * 256M rule.
> 
> Also introduce a function that adjusts the range end of a region to be
> constructed and the DPA range's end of the endpoint decoders that will
> be later attached to that region.
> 
> [1] commit 7a81173f3 ("cxl: Documentation/driver-api/cxl: Describe the x86 Low Memory Hole solution")
> 
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>

lgmt

Reviewed-by: Gregory Price <gourry@gourry.net>