[PATCH v2 07/13] cxl/core: Add region disable and enable for a DPort SBR
"Fabio M. De Francesco" <[email protected]>
| Newsgroups | gmane.linux.ports.ppc.embedded |
|---|---|
| Message-ID | <20260825022628.3651434-8-fabio.m.de.francesco__28605.146326457$1787624881$gmane$org@linux.intel.com> |
A Secondary Bus Reset of a CXL Downstream Port removes the downstream component from the bus, so a CXL region reached through that Port has to be disabled while the reset runs. Add cxl_region_disable() to offline memory, invalidate the CPU caches for its range and release its driver, and cxl_region_enable() to re-attach the driver afterwards. Neither function has a caller yet. Both are driven by the reset once the set of regions routed through the Port is known. Signed-off-by: Fabio M. De Francesco <[email protected]> --- drivers/cxl/core/core.h | 3 ++ drivers/cxl/core/dport_sbr.c | 79 ++++++++++++++++++++++++++++++++++++ drivers/cxl/core/region.c | 3 +- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index a8b9d7990d32..ac55f9e8160e 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -56,6 +56,9 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, int devm_cxl_add_dax_region(struct cxl_region *cxlr); int devm_cxl_add_pmem_region(struct cxl_region *cxlr); void kill_regions(struct cxl_root_decoder *cxlrd); +int cxl_region_invalidate_memregion(struct cxl_region *cxlr); +int cxl_region_disable(struct cxl_region *cxlr); +void cxl_region_enable(struct cxl_region *cxlr); struct pci_dev; void cxl_sbr_recommit_decoders(struct pci_dev *dport_pci, struct xarray *hdm_state); diff --git a/drivers/cxl/core/dport_sbr.c b/drivers/cxl/core/dport_sbr.c index 55c35386d0cb..0f398ab0d3a4 100644 --- a/drivers/cxl/core/dport_sbr.c +++ b/drivers/cxl/core/dport_sbr.c @@ -1,11 +1,90 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2026 Intel Corporation. */ +#include <linux/memregion.h> +#include <linux/memory_hotplug.h> +#include <linux/memory.h> #include <linux/device.h> #include <linux/pci.h> #include <cxl.h> #include "core.h" +/* + * cxl_region_disable - make a region inactive ahead of a Secondary Bus Reset + * @cxlr: region routed through the CXL Downstream Port being reset + * + * Offline the memory blocks the region owns and unbind its driver. An SBR + * zeroes the downstream bus number, so a region left live as System RAM would + * be accessed while the device is in reset. On offline failure return the error + * so the caller aborts the reset; the memory is never force-removed. + * + * Context: process context. Offlining and driver unbind sleep and take the + * memory hotplug lock, so this cannot run in atomic context. + */ +int cxl_region_disable(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + unsigned long block_size; + u64 start, end; + int rc; + + /* + * Per CXL r4.0 sec 9.13.1 an Interleave Set has a Base HPA and a Size + * that are multiples of 256 MB, while a memory block spans up to 2 GB. + * A block overlapping either end of the range therefore also covers + * memory outside this region, so round the range inward to block + * granularity as dax_kmem did when it onlined the range. Offlining a + * straddling block would migrate pages that the reset does not affect. + */ + block_size = memory_block_size_bytes(); + start = ALIGN(p->res->start, block_size); + end = ALIGN_DOWN(p->res->end + 1, block_size); + if (start >= end) { + dev_dbg(&cxlr->dev, "%s: HPA %pr spans no whole memory block, no System RAM to offline\n", + __func__, p->res); + } else { + rc = cxl_offline_memory(start, end - start); + if (rc) { + dev_warn(&cxlr->dev, "offline System RAM failed before reset: %d\n", + rc); + return rc; + } + } + + rc = cxl_region_invalidate_memregion(cxlr); + if (rc) { + dev_warn(&cxlr->dev, "CPU cache invalidate failed before reset: %d\n", + rc); + return rc; + } + + device_release_driver(&cxlr->dev); + dev_dbg(&cxlr->dev, "%s: System RAM offline, region disabled before reset, HPA %pr\n", + __func__, p->res); + + return 0; +} + +/* + * cxl_region_enable - restore a region after a Secondary Bus Reset + * @cxlr: region disabled by cxl_region_disable() before the reset + * + * Rebind the region driver. The System RAM is left offline; bringing it back + * online is a separate administrative step. + */ +void cxl_region_enable(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + + if (device_attach(&cxlr->dev) < 0) { + dev_dbg(&cxlr->dev, "driver re-attach failed after reset\n"); + return; + } + + dev_dbg(&cxlr->dev, "%s: region re-enabled after reset, HPA %pr, IW %d, IG %d\n", + __func__, p->res, p->interleave_ways, p->interleave_granularity); +} + /* * The reset cleared the HDM Decoder registers of every CXL component below * @dport_pci, so restore them from the settings the driver holds and from diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 1e211542b6b6..fc0bec991a69 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -222,7 +222,7 @@ static struct cxl_region_ref *cxl_rr_load(struct cxl_port *port, return xa_load(&port->regions, (unsigned long)cxlr); } -static int cxl_region_invalidate_memregion(struct cxl_region *cxlr) +int cxl_region_invalidate_memregion(struct cxl_region *cxlr) { if (!cpu_cache_has_invalidate_memregion()) { if (IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST)) { @@ -4273,4 +4273,5 @@ void cxl_region_exit(void) MODULE_IMPORT_NS("CXL"); MODULE_IMPORT_NS("DEVMEM"); +MODULE_IMPORT_NS("CXL_MHP"); MODULE_ALIAS_CXL(CXL_DEVICE_REGION); -- 2.55.0