[PATCH] cxl/hdm: Avoid DVSEC fallback after region teardown

From: Smita Koralahalli

Date: Thu Feb 12 2026 - 17:39:10 EST


After destroy-region, cxl_region_decode_reset() clears the HDM decoder
registers (base/size/commit). If the memdev is subsequently bounced
(disable/enable), port probe re-evaluates decoder capability via
should_emulate_decoders().

The existing logic checks each decoder's COMMITTED bit. Since those bits
are cleared by region teardown, should_emulate_decoders() incorrectly
falls back to DVSEC range emulation, even though HDM capability is still
present.

DVSEC fallback marks the endpoint decoder as AUTO, which triggers
cxl_add_to_region() -> construct_region(). That path copies the default
interleave_granularity (4096) into the region parameters. The resulting
spurious autodiscovered region consumes the CFMWS HPA space and causes a
subsequent create-region to fail in hpa_alloc().

Use the global CXL_HDM_DECODER_ENABLE bit instead of per-decoder COMMITTED
bits to detect HDM capability. If the HDM decoder block is enabled, zeroed
registers indicate teardown, not absence of HDM support. This prevents the
unintended DVSEC fallback and subsequent region creation failure.

Based on cxl/fixes.
base-commit: 8441c7d3bd6c5a52ab2ecf77e43a5bf262004f5c

Fixes: 52cc48ad2a76 ("cxl/hdm: Limit emulation to the number of range registers")
Signed-off-by: Smita Koralahalli <Smita.KoralahalliChannabasappa@xxxxxxx>
---
drivers/cxl/core/hdm.c | 25 +++++++++----------------
1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index eb5a3a7640c6..a0718cbcc355 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -94,7 +94,6 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
struct cxl_hdm *cxlhdm;
void __iomem *hdm;
u32 ctrl;
- int i;

if (!info)
return false;
@@ -113,22 +112,16 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
return false;

/*
- * If any decoders are committed already, there should not be any
- * emulated DVSEC decoders.
+ * If HDM decoders are globally enabled, do not fall back to DVSEC
+ * range emulation. Zeroed decoder registers after region teardown
+ * do not imply absence of HDM capability.
+ *
+ * Falling back to DVSEC here would treat the decoder as AUTO and
+ * may incorrectly latch default interleave settings.
*/
- for (i = 0; i < cxlhdm->decoder_count; i++) {
- ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
- dev_dbg(&info->port->dev,
- "decoder%d.%d: committed: %ld base: %#x_%.8x size: %#x_%.8x\n",
- info->port->id, i,
- FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl),
- readl(hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i)),
- readl(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(i)),
- readl(hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i)),
- readl(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i)));
- if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl))
- return false;
- }
+ ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+ if (ctrl & CXL_HDM_DECODER_ENABLE)
+ return false;

return true;
}
--
2.17.1