From: Srirangan Madhavan <smadhavan@nvidia.com>
To: Alison Schofield <alison.schofield@intel.com>,
Bjorn Helgaas <bhelgaas@google.com>,
Dave Jiang <dave.jiang@intel.com>,
Davidlohr Bueso <dave@stgolabs.net>,
Ira Weiny <ira.weiny@intel.com>,
Jonathan Cameron <jic23@kernel.org>,
Vishal Verma <vishal.l.verma@intel.com>,
linux-cxl@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: Alex Williamson <alex.williamson@redhat.com>,
vsethi@nvidia.com, alwilliamson@nvidia.com,
Sai Yashwanth Reddy Kancherla <skancherla@nvidia.com>,
Vishal Aslot <vaslot@nvidia.com>,
Manish Honap <mhonap@nvidia.com>, Jiandi An <jan@nvidia.com>,
Richard Cheng <icheng@nvidia.com>,
linux-tegra@vger.kernel.org,
Srirangan Madhavan <smadhavan@nvidia.com>
Subject: [PATCH v15 08/16] cxl: Refresh cached PCI HDM decoder settings
Date: Sun, 11 Oct 2026 02:14:14 +0000 [thread overview]
Message-ID: <20261011021422.3428136-9-smadhavan@nvidia.com> (raw)
In-Reply-To: <20261011021422.3428136-1-smadhavan@nvidia.com>
Early PCI discovery creates the HDM cache, while later CXL enumeration and
decoder operations provide updated programming state.
Refresh the PCI snapshot when decoders are enumerated, committed, or reset
so reset recovery need not walk the CXL topology. Ignore updates when no
cache exists and reject decoder-count mismatches.
Use CXL_RESET for the PCI HDM cache. Shared HDM register helpers do not
require it.
Skip cache checks and updates for DVSEC-emulated decoders, which do not
use HDM registers. Keep cached global control when MMIO reads return all
ones.
Skip non-PCI mock ports to avoid test wrappers and a circular dependency
between cxl_core and cxl_mock.
Link: https://git.kernel.org/pub/scm/linux/kernel/git/djiang/linux.git/commit/?id=50a88f5194e5df38590d413f925d8d5afa8a8488
Suggested-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Srirangan Madhavan <smadhavan@nvidia.com>
Assisted-by: LLM
---
drivers/cxl/Kconfig | 5 +++
drivers/cxl/core/core.h | 3 ++
drivers/cxl/core/hdm.c | 55 +++++++++++++++++++++++++++++++-
drivers/cxl/core/pci.c | 70 +++++++++++++++++++++++++++++++++++++++++
include/cxl/cxl.h | 1 +
include/cxl/hdm.h | 25 +++++++++++++++
include/linux/pci.h | 6 ++++
7 files changed, 164 insertions(+), 1 deletion(-)
diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 80aeb0d556bd..8b08bfdc4982 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -6,6 +6,7 @@ menuconfig CXL_BUS
select FW_UPLOAD
select PCI_DOE
select FIRMWARE_TABLE
+ select CXL_RESET
select NUMA_KEEP_MEMINFO if NUMA_MEMBLKS
select FWCTL if CXL_FEATURES
help
@@ -244,3 +245,7 @@ config CXL_ATL
depends on ACPI_PRMT && AMD_NB
endif
+
+# CXL_BUS requires the PCI HDM cache and built-in state helpers, even when modular.
+config CXL_RESET
+ bool
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index d69864b25656..77bc4f7d2e99 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -222,6 +222,9 @@ static inline void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { }
int cxl_gpf_port_setup(struct cxl_dport *dport);
struct cxl_hdm;
+/* Hold cxl_rwsem.dpa for write while using the returned cache. */
+struct cxl_hdm_info *cxl_hdm_get_cached_info(struct cxl_hdm *cxlhdm, int id);
+int cxl_hdm_check_decoder_count(struct cxl_hdm *cxlhdm);
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
struct cxl_endpoint_dvsec_info *info);
int cxl_port_get_possible_dports(struct cxl_port *port);
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 1a19078e6c6c..b57257efbf4e 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -730,6 +730,47 @@ static void cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
*tgt |= FIELD_PREP(GENMASK_ULL(63, 56), t[7]->port_id);
}
+static void cxl_hdm_refresh_decoder(struct cxl_hdm *cxlhdm,
+ struct cxl_decoder *cxld)
+{
+ void __iomem *hdm = cxlhdm->regs.hdm_decoder;
+ struct cxl_endpoint_decoder_settings *settings;
+ struct cxl_hdm_info *info;
+ u32 global_ctrl;
+
+ if (!hdm)
+ return;
+ if (!is_endpoint_decoder(&cxld->dev))
+ return;
+
+ guard(rwsem_write)(&cxl_rwsem.dpa);
+ info = cxl_hdm_get_cached_info(cxlhdm, cxld->config.id);
+ if (!info)
+ return;
+
+ /* Preserve the last good value when the device does not respond. */
+ global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+ if (global_ctrl != U32_MAX)
+ info->global_ctrl = global_ctrl;
+ settings = &info->settings[cxld->config.id];
+
+ /*
+ * A disabled decoder's software object may retain its old range and
+ * skip state. Leave only the decoder id in the cached settings so stale
+ * state is not restored as an enabled decode.
+ */
+ *settings = (struct cxl_endpoint_decoder_settings) {
+ .config.id = cxld->config.id,
+ };
+ if (cxld->config.flags & CXL_DECODER_F_ENABLE) {
+ struct cxl_endpoint_decoder *cxled =
+ to_cxl_endpoint_decoder(&cxld->dev);
+
+ settings->config = cxld->config;
+ settings->skip = cxled->skip;
+ }
+}
+
static void
cxl_endpoint_decoder_snapshot(struct cxl_endpoint_decoder *cxled,
struct cxl_endpoint_decoder_settings *settings)
@@ -811,6 +852,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
}
port->commit_end++;
cxld->config.flags |= CXL_DECODER_F_ENABLE;
+ cxl_hdm_refresh_decoder(cxlhdm, cxld);
return 0;
}
@@ -883,6 +925,7 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
cxld->config.flags &= ~CXL_DECODER_F_ENABLE;
+ cxl_hdm_refresh_decoder(cxlhdm, cxld);
/* Userspace is now responsible for reconfiguring this decoder */
if (is_endpoint_decoder(&cxld->dev)) {
@@ -1090,9 +1133,17 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
{
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
struct cxl_port *port = cxlhdm->port;
- int i;
+ /* DVSEC-emulated decoders do not describe the cached HDM registers. */
+ bool emulated = should_emulate_decoders(info);
+ int check_rc, i;
u64 dpa_base = 0;
+ if (is_cxl_endpoint(port) && hdm && !emulated) {
+ check_rc = cxl_hdm_check_decoder_count(cxlhdm);
+ if (check_rc)
+ return check_rc;
+ }
+
cxl_settle_decoders(cxlhdm);
for (i = 0; i < cxlhdm->decoder_count; i++) {
@@ -1131,6 +1182,8 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
put_device(&cxld->dev);
return rc;
}
+ if (!emulated)
+ cxl_hdm_refresh_decoder(cxlhdm, cxld);
rc = add_hdm_decoder(port, cxld);
if (rc) {
dev_warn(&port->dev,
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 33b21c8dcb21..2acddb472103 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
+#include <cxl/hdm.h>
#include <cxl/pci.h>
#include <linux/aer.h>
#include <cxlpci.h>
@@ -180,6 +181,75 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds)
}
EXPORT_SYMBOL_NS_GPL(cxl_await_media_ready, "CXL");
+/**
+ * cxl_hdm_get_cached_info() - get the PCI-owned HDM cache for a decoder
+ * @cxlhdm: HDM capability whose port identifies the upstream PCI device
+ * @id: decoder ID to validate against the cached decoder count
+ *
+ * The caller must hold cxl_rwsem.dpa for write throughout this call and
+ * all access through the returned pointer. The pointer refers to the live
+ * cache; ownership remains with the PCI device.
+ *
+ * Return: the cache containing @id, or NULL if no upstream PCI device or
+ * cache exists, or @id is outside the cached decoder count.
+ */
+struct cxl_hdm_info *cxl_hdm_get_cached_info(struct cxl_hdm *cxlhdm, int id)
+{
+ struct cxl_hdm_info *info;
+
+ struct pci_dev *pdev __free(pci_dev_put) =
+ cxl_port_get_uport_pci_dev(cxlhdm->port);
+
+ if (!pdev)
+ return NULL;
+
+ lockdep_assert_held_write(&cxl_rwsem.dpa);
+ info = pdev->hdm;
+ if (!info)
+ return NULL;
+
+ if (id < 0 || id >= info->decoder_count) {
+ pci_warn(pdev, "CXL HDM decoder %d exceeds cached count %d\n",
+ id, info->decoder_count);
+ return NULL;
+ }
+
+ return info;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_hdm_get_cached_info, "CXL");
+
+/**
+ * cxl_hdm_check_decoder_count() - check enumeration against the PCI HDM cache
+ * @cxlhdm: HDM capability containing the decoder count to check
+ *
+ * Take cxl_rwsem.dpa for read while comparing the cached count. Call only
+ * for hardware HDM decoders, since DVSEC-emulated ranges do not describe
+ * the HDM registers represented by the cache.
+ *
+ * Return: 0 if the counts match or no upstream PCI device or cache exists,
+ * or -ENXIO if the cached and enumerated decoder counts differ.
+ */
+int cxl_hdm_check_decoder_count(struct cxl_hdm *cxlhdm)
+{
+ struct pci_dev *pdev __free(pci_dev_put) =
+ cxl_port_get_uport_pci_dev(cxlhdm->port);
+
+ if (!pdev)
+ return 0;
+
+ guard(rwsem_read)(&cxl_rwsem.dpa);
+ if (pdev->hdm &&
+ pdev->hdm->decoder_count != cxlhdm->decoder_count) {
+ pci_warn(pdev,
+ "CXL HDM cache decoder count mismatch: cached=%d hdm=%d\n",
+ pdev->hdm->decoder_count, cxlhdm->decoder_count);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_hdm_check_decoder_count, "CXL");
+
static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
{
struct pci_dev *pdev = to_pci_dev(cxlds->dev);
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 9af01e1f45ba..b29374b34cfa 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -26,6 +26,7 @@ enum cxl_devtype {
};
struct cxl_region;
+struct cxl_hdm_info;
enum cxl_decoder_type {
CXL_DECODER_DEVMEM = 2,
diff --git a/include/cxl/hdm.h b/include/cxl/hdm.h
index 219b9bb67440..d5e7efd2ffc0 100644
--- a/include/cxl/hdm.h
+++ b/include/cxl/hdm.h
@@ -16,4 +16,29 @@ struct cxl_endpoint_decoder_settings {
u64 skip;
};
+/**
+ * struct cxl_hdm_info - cached CXL HDM state for a PCI device
+ * @decoder_count: number of entries in @settings
+ * @hdm_bar: PCI BAR containing the HDM decoder capability
+ * @hdm_offset: offset of the HDM decoder capability in @hdm_bar
+ * @hdm_size: size of the HDM decoder register block
+ * @global_ctrl: HDM decoder global control register
+ * @dvsec_ctrl: CXL DVSEC control register
+ * @dvsec_ctrl_valid: whether @dvsec_ctrl contains valid state
+ * @settings: per-endpoint-decoder programming state
+ *
+ * Publication through pci_dev.hdm and all mutable fields are protected by
+ * cxl_rwsem.dpa.
+ */
+struct cxl_hdm_info {
+ int decoder_count;
+ int hdm_bar;
+ resource_size_t hdm_offset;
+ resource_size_t hdm_size;
+ u32 global_ctrl;
+ u16 dvsec_ctrl;
+ bool dvsec_ctrl_valid;
+ struct cxl_endpoint_decoder_settings settings[] __counted_by(decoder_count);
+};
+
#endif /* __CXL_HDM_H__ */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d31a8d107b1e..7bb37fcb556d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -339,6 +339,9 @@ struct pcie_link_state;
struct pci_sriov;
struct pci_p2pdma;
struct rcec_ea;
+#ifdef CONFIG_CXL_RESET
+struct cxl_hdm_info;
+#endif
/* struct pci_dev - describes a PCI device
*
@@ -566,6 +569,9 @@ struct pci_dev {
#ifdef CONFIG_PCI_DOE
struct xarray doe_mbs; /* Data Object Exchange mailboxes */
#endif
+#ifdef CONFIG_CXL_RESET
+ struct cxl_hdm_info *hdm; /* CXL HDM decoder state */
+#endif
#ifdef CONFIG_PCI_NPEM
struct npem *npem; /* Native PCIe Enclosure Management */
#endif
--
2.43.0
next prev parent reply other threads:[~2026-10-11 2:15 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-11 2:14 [PATCH v15 00/16] PCI/CXL: Add CXL reset support for Type 2 devices Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 01/16] cxl: Drop stale decoder interleave limit comment Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 02/16] cxl: Share CXL port upstream PCI device lookup Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 03/16] cxl: Move decoder declarations to shared header Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 04/16] cxl: Embed decoder configuration in a standalone structure Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 05/16] cxl: Introduce endpoint HDM decoder settings Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 06/16] cxl: Move HDM decoder helpers to built-in code Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 07/16] cxl: Share HDM decoder register unpacking Srirangan Madhavan
2026-10-11 2:14 ` Srirangan Madhavan [this message]
2026-10-11 2:14 ` [PATCH v15 09/16] PCI/CXL: Cache endpoint HDM state during PCI enumeration Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 10/16] PCI/CXL: Add CXL Device Reset sequencing Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 11/16] cxl: Validate and synchronize HDM ranges around reset Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 12/16] PCI/CXL: Reject reset with unsafe function scope Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 13/16] PCI/CXL: Restore CXL state after PCI reset Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 14/16] PCI/CXL: Expose CXL Reset as a PCI reset method Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 15/16] PCI/CXL: Restore CXL state after CXL bus reset Srirangan Madhavan
2026-10-11 2:14 ` [PATCH v15 16/16] tools/testing/cxl: Add HDM decoder range checks Srirangan Madhavan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261011021422.3428136-9-smadhavan@nvidia.com \
--to=smadhavan@nvidia.com \
--cc=alex.williamson@redhat.com \
--cc=alison.schofield@intel.com \
--cc=alwilliamson@nvidia.com \
--cc=bhelgaas@google.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=icheng@nvidia.com \
--cc=ira.weiny@intel.com \
--cc=jan@nvidia.com \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=mhonap@nvidia.com \
--cc=skancherla@nvidia.com \
--cc=vaslot@nvidia.com \
--cc=vishal.l.verma@intel.com \
--cc=vsethi@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®