Disabling the CXL regions ahead of a Secondary Bus Reset needs the set of regions with a member endpoint below the Port being reset. Add cxl_sbr_collect_regions() to walk the CXL port that owns the Downstream Port and return every region with an endpoint below it, and cxl_sbr_put_regions() to release the set. Signed-off-by: Fabio M. De Francesco --- drivers/cxl/core/core.h | 2 ++ drivers/cxl/core/dport_sbr.c | 66 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index ac55f9e8160e..077a2af9cf0c 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -60,6 +60,8 @@ 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; +int cxl_sbr_collect_regions(struct pci_dev *dport_pci, struct xarray *regions); +void cxl_sbr_put_regions(struct xarray *regions); 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 0f398ab0d3a4..233499bc1fad 100644 --- a/drivers/cxl/core/dport_sbr.c +++ b/drivers/cxl/core/dport_sbr.c @@ -85,6 +85,72 @@ void cxl_region_enable(struct cxl_region *cxlr) __func__, p->res, p->interleave_ways, p->interleave_granularity); } +/* + * Collect the regions with a member endpoint routed through @dport_pci, the + * CXL Downstream Port about to be reset. cxl_rwsem.region keeps the topology + * stable for the duration of the walk only. Each collected region is pinned + * with get_device() so the object survives after the lock is dropped, since + * cxl_region_disable()/cxl_region_enable() run with the rwsem released (they + * unbind and rebind the region driver). Hence snapshot the set first. + */ +int cxl_sbr_collect_regions(struct pci_dev *dport_pci, + struct xarray *regions) +{ + struct cxl_region_ref *cxl_rr; + struct cxl_dport *dport; + unsigned long index; + int count = 0; + int rc; + + struct cxl_port *port __free(put_cxl_port) = + find_cxl_port(&dport_pci->dev, &dport); + if (!port) { + pci_dbg(dport_pci, "no CXL port found for reset dport\n"); + return 0; + } + + guard(rwsem_read)(&cxl_rwsem.region); + xa_for_each(&port->regions, index, cxl_rr) { + struct cxl_region *cxlr = cxl_rr->region; + struct cxl_ep *ep; + unsigned long ep_index; + + /* Skip unless a region endpoint sits below the reset dport. */ + xa_for_each(&cxl_rr->endpoints, ep_index, ep) + if (ep->dport == dport) + break; + if (!ep) { + dev_dbg(&cxlr->dev, "%s: no endpoint below %s, region excluded\n", + __func__, dev_name(dport->dport_dev)); + continue; + } + + get_device(&cxlr->dev); + rc = xa_insert(regions, (unsigned long)cxlr, cxlr, GFP_KERNEL); + if (rc) { + put_device(&cxlr->dev); + return rc; + } + dev_dbg(&cxlr->dev, "%s: endpoint below %s, region collected\n", + __func__, dev_name(dport->dport_dev)); + count++; + } + + dev_dbg(&port->dev, "%d region(s) routed through %s\n", count, + dev_name(dport->dport_dev)); + return 0; +} + +void cxl_sbr_put_regions(struct xarray *regions) +{ + struct cxl_region *cxlr; + unsigned long index; + + xa_for_each(regions, index, cxlr) + put_device(&cxlr->dev); + xa_destroy(regions); +} + /* * 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 -- 2.55.0