[PATCH v2 2/4] cxl: Update Soft Reserve resources upon region creation

Nathan Fontenot
2025-01-16T17:43:06Z

Update handling of SOFT RESERVE iomem resources that intersect with
CXL region resources to remove intersections from the SOFT RESERVE
resources. The current approach of leaving SOFT RESERVE resources as
is can cause failures during hotplug replace of CXL devices bercause
the resource is not avaialable for reuse after teardown of the CXL device.

The approach is to use the CONFIG_SOFT_RESERVED_MANAGED config option
to have SOFT RESERVE resources set aside during boot. After the CXL
drivers complete their probe and CXL regions are created any CXL
region intersections with SOFT RESERVE resources are removed from the
SOFT RESERVE resource. The remaining SOFT RESERVE resources, if any,
are then added to the iomem resource tree.

To accomplish this the cxl acpi driver creates a worker thread at the
end of cxl_acpi_probe(). This worker thread first waits for the CXL PCI
CXL mem drivers have loaded. The cxl core/suspend.c code is updated to
add a pci_loaded variable, in addition to the mem_active variable, that
is updated when the pci driver loads. A new cxl_wait_for_pci_mem() routine
uses a waitqueue for both these driver to be loaded. The need to add
this additional waitqueue is ensure the CXL PCI and CXL mem drivers
have loaded before we wait for their probe, without it the cxl acpi probe
worker thread calls wait_for_device_probe() before these drivers
are loaded.

After the CXL PCI and CXL mem drivers load the cxl acpi worker thread
uses wait_for_device_probe() to ensure device probe routines have
completed.

After probe completes, find all cxl regions that have been created and
remove any intersections with SOFT RESERVE resources and add remaining
SOFT RESERRVES to the iomem resource tree.

Signed-off-by: Nathan Fontenot <nathan.fontenot@amd.com>
---
drivers/cxl/Kconfig | 1 +
drivers/cxl/acpi.c | 26 ++++++++++++++++++++++++
drivers/cxl/core/Makefile | 2 +-
drivers/cxl/core/region.c | 25 ++++++++++++++++++++++-
drivers/cxl/core/suspend.c | 41 ++++++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 3 +++
drivers/cxl/cxlmem.h | 9 ---------
drivers/cxl/cxlpci.h | 2 ++
drivers/cxl/pci.c | 1 +
9 files changed, 99 insertions(+), 11 deletions(-)

diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 99b5c25be079..5d9dce2fb282 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -60,6 +60,7 @@ config CXL_ACPI
default CXL_BUS
select ACPI_TABLE_LIB
select ACPI_HMAT
+ select SOFT_RESERVED_MANAGED
help
Enable support for host managed device memory (HDM) resources
published by a platform's ACPI CXL memory layout description. See
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 82b78e331d8e..a95004230f85 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -7,6 +7,8 @@
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/node.h>
+#include <linux/pm.h>
+#include <linux/workqueue.h>
#include <asm/div64.h>
#include "cxlpci.h"
#include "cxl.h"
@@ -813,6 +815,27 @@ static int pair_cxl_resource(struct device *dev, void *data)
return 0;
}

+static void cxl_srmem_work_fn(struct work_struct *work)
+{
+ /* Wait for CXL PCI and mem drivers to load */
+ cxl_wait_for_pci_mem();
+
+ /*
+ * Once the CXL PCI and mem drivers have loaded wait
+ * for the driver probe routines to complete.
+ */
+ wait_for_device_probe();
+
+ cxl_region_srmem_update();
+}
+
+DECLARE_WORK(cxl_sr_work, cxl_srmem_work_fn);
+
+static void cxl_srmem_update(void)
+{
+ schedule_work(&cxl_sr_work);
+}
+
static int cxl_acpi_probe(struct platform_device *pdev)
{
int rc;
@@ -887,6 +910,9 @@ static int cxl_acpi_probe(struct platform_device *pdev)

/* In case PCI is scanned before ACPI re-trigger memdev attach */
cxl_bus_rescan();
+
+ /* Update SOFT RESERVED resources that intersect with CXL regions */
+ cxl_srmem_update();
return 0;
}

diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index 9259bcc6773c..01587ba1dcdb 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CXL_BUS) += cxl_core.o
-obj-$(CONFIG_CXL_SUSPEND) += suspend.o
+obj-y += suspend.o

ccflags-y += -I$(srctree)/drivers/cxl
CFLAGS_trace.o = -DTRACE_INCLUDE_PATH=. -I$(src)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 21ad5f242875..3f4a7cc4539b 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -10,6 +10,7 @@
#include <linux/sort.h>
#include <linux/idr.h>
#include <linux/memory-tiers.h>
+#include <linux/ioport.h>
#include <cxlmem.h>
#include <cxl.h>
#include "core.h"
@@ -2294,7 +2295,7 @@ const struct device_type cxl_region_type = {

bool is_cxl_region(struct device *dev)
{
- return dev->type == &cxl_region_type;
+ return dev && dev->type == &cxl_region_type;
}
EXPORT_SYMBOL_NS_GPL(is_cxl_region, CXL);

@@ -3377,6 +3378,28 @@ int cxl_add_to_region(struct cxl_port *root, struct cxl_endpoint_decoder *cxled)
}
EXPORT_SYMBOL_NS_GPL(cxl_add_to_region, CXL);

+int cxl_region_srmem_update(void)
+{
+ struct device *dev = NULL;
+ struct cxl_region *cxlr;
+ struct resource *res;
+
+ do {
+ dev = bus_find_next_device(&cxl_bus_type, dev);
+ if (is_cxl_region(dev)) {
+ cxlr = to_cxl_region(dev);
+ res = cxlr->params.res;
+ release_srmem_region_adjustable(res->start,
+ resource_size(res));
+ put_device(dev);
+ }
+ } while (dev);
+
+ merge_srmem_resources();
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_region_srmem_update, CXL);
+
static int is_system_ram(struct resource *res, void *arg)
{
struct cxl_region *cxlr = arg;
diff --git a/drivers/cxl/core/suspend.c b/drivers/cxl/core/suspend.c
index a5984d96ea1d..589f7fc931ee 100644
--- a/drivers/cxl/core/suspend.c
+++ b/drivers/cxl/core/suspend.c
@@ -2,18 +2,27 @@
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
#include <linux/atomic.h>
#include <linux/export.h>
+#include <linux/wait.h>
#include "cxlmem.h"
+#include "cxlpci.h"

static atomic_t mem_active;

+static DECLARE_WAIT_QUEUE_HEAD(cxl_wait_queue);
+
+static atomic_t pci_loaded;
+
+#ifdef CONFIG_CXL_SUSPEND
bool cxl_mem_active(void)
{
return atomic_read(&mem_active) != 0;
}
+#endif

void cxl_mem_active_inc(void)
{
atomic_inc(&mem_active);
+ wake_up(&cxl_wait_queue);
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_active_inc, CXL);

@@ -22,3 +31,35 @@ void cxl_mem_active_dec(void)
atomic_dec(&mem_active);
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_active_dec, CXL);
+
+void mark_cxl_pci_loaded(void)
+{
+ atomic_inc(&pci_loaded);
+ wake_up(&cxl_wait_queue);
+}
+EXPORT_SYMBOL_NS_GPL(mark_cxl_pci_loaded, CXL);
+
+static bool cxl_pci_loaded(void)
+{
+ if (IS_ENABLED(CONFIG_CXL_PCI))
+ return atomic_read(&pci_loaded) != 0;
+
+ return true;
+}
+
+static bool cxl_mem_probed(void)
+{
+ if (IS_ENABLED(CONFIG_CXL_MEM))
+ return atomic_read(&mem_active) != 0;
+
+ return true;
+}
+
+void cxl_wait_for_pci_mem(void)
+{
+ if (IS_ENABLED(CONFIG_CXL_PCI) || IS_ENABLED(CONFIG_CXL_MEM))
+ wait_event_timeout(cxl_wait_queue,
+ cxl_pci_loaded() && cxl_mem_probed(),
+ 30 * HZ);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_wait_for_pci_mem, CXL);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 9afb407d438f..65e425e72970 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -861,6 +861,7 @@ bool is_cxl_pmem_region(struct device *dev);
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
int cxl_add_to_region(struct cxl_port *root,
struct cxl_endpoint_decoder *cxled);
+int cxl_region_srmem_update(void);
struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
@@ -898,6 +899,8 @@ void cxl_coordinates_combine(struct access_coordinate *out,

bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);

+void cxl_wait_for_pci_mem(void);
+
/*
* Unit test builds overrides this to __weak, find the 'strong' version
* of these symbols in tools/testing/cxl/.
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index afb53d058d62..df6bf7778321 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -838,17 +838,8 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd);
int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa);
int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa);

-#ifdef CONFIG_CXL_SUSPEND
void cxl_mem_active_inc(void);
void cxl_mem_active_dec(void);
-#else
-static inline void cxl_mem_active_inc(void)
-{
-}
-static inline void cxl_mem_active_dec(void)
-{
-}
-#endif

int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);

diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h
index 4da07727ab9c..42d9423ba4c0 100644
--- a/drivers/cxl/cxlpci.h
+++ b/drivers/cxl/cxlpci.h
@@ -129,4 +129,6 @@ void read_cdat_data(struct cxl_port *port);
void cxl_cor_error_detected(struct pci_dev *pdev);
pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
pci_channel_state_t state);
+
+void mark_cxl_pci_loaded(void);
#endif /* __CXL_PCI_H__ */
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 4be35dc22202..d90505e3605c 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1054,6 +1054,7 @@ static int __init cxl_pci_driver_init(void)
if (rc)
pci_unregister_driver(&cxl_pci_driver);

+ mark_cxl_pci_loaded();
return rc;
}

--
2.43.0