[PATCH v10 12/12] PCI/CXL: Restore HDM state after CXL bus reset

Srirangan Madhavan <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,org.kernel.vger.linux-cxl,org.kernel.vger.linux-pci,org.kernel.vger.linux-tegra
Message-ID <[email protected]>
CXL bus reset can clear HDM global control and decoder programming.
Restore cached HDM state after a successful cxl_bus reset while the
IOMMU reset block remains active.

Restore PCI config state first so BARs are valid before HDM MMIO access.
If HDM restore fails, save a disabled PCI command state so later PCI
restore does not reopen bus mastering over invalid decoder state.

Reject cxl_bus reset for multifunction devices, matching cxl_reset.

This restores only HDM component decoder state. It does not restore CXL
Device DVSEC range/control registers.

Reported-by: Alex Williamson <[email protected]>
Tested-by: Richard Cheng <[email protected]>
Signed-off-by: Srirangan Madhavan <[email protected]>
---
 drivers/cxl/core/resource.c | 17 +++++++++++++++++
 drivers/pci/pci.c           |  6 ++++++
 include/cxl/cxl.h           |  6 ++++++
 3 files changed, 29 insertions(+)

diff --git a/drivers/cxl/core/resource.c b/drivers/cxl/core/resource.c
index ba0a26276477..daa5e7fa2509 100644
--- a/drivers/cxl/core/resource.c
+++ b/drivers/cxl/core/resource.c
@@ -834,6 +834,23 @@ static int cxl_pci_target_reset_prepare(struct pci_dev *pdev,
 	return 0;
 }
 
+int cxl_restore_hdm_after_pci_reset(struct pci_dev *pdev)
+{
+	u16 command;
+	int rc;
+
+	device_lock_assert(&pdev->dev);
+
+	cxl_restore_pci_state_for_hdm_restore(pdev, &command);
+	rc = cxl_restore_hdm(pdev);
+	if (rc) {
+		cxl_reset_save_disabled_state(pdev);
+		return rc;
+	}
+
+	return cxl_reset_save_restored_state(pdev, command);
+}
+
 static void cxl_hdm_range_context_init(struct cxl_hdm_range_context *ctx)
 {
 	INIT_LIST_HEAD(&ctx->ranges);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ef25ba5f7fe9..7d1e968a2aa2 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4977,6 +4977,9 @@ static int cxl_reset_bus_function(struct pci_dev *dev, bool probe)
 	if (!dvsec)
 		return -ENOTTY;
 
+	if (dev->multifunction)
+		return -ENOTTY;
+
 	if (probe)
 		return 0;
 
@@ -5004,6 +5007,9 @@ static int cxl_reset_bus_function(struct pci_dev *dev, bool probe)
 		pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
 				      reg);
 
+	if (!rc)
+		rc = cxl_restore_hdm_after_pci_reset(dev);
+
 	pci_dev_reset_iommu_done(dev);
 	return rc;
 }
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 8645d0fbd273..8e6e68751d88 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -162,6 +162,7 @@ struct cxl_hdm_info {
 #ifdef CONFIG_CXL_HDM
 void pci_cxl_hdm_init(struct pci_dev *pdev);
 void pci_cxl_hdm_release(struct pci_dev *pdev);
+int cxl_restore_hdm_after_pci_reset(struct pci_dev *pdev);
 int cxl_reset_function(struct pci_dev *pdev, bool probe);
 #else
 static inline void pci_cxl_hdm_init(struct pci_dev *pdev)
@@ -172,6 +173,11 @@ static inline void pci_cxl_hdm_release(struct pci_dev *pdev)
 {
 }
 
+static inline int cxl_restore_hdm_after_pci_reset(struct pci_dev *pdev)
+{
+	return 0;
+}
+
 static inline int cxl_reset_function(struct pci_dev *pdev, bool probe)
 {
 	return -ENOTTY;
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.