CXL r4.0 sec 8.1.5.2 Table 8-32 describes a bit that makes the SBR bit in Bridge Control take effect at all, so a Port left at the firmware default is one the reset must not silently unmask.
Add cxl_sbr_allowed() to answer if SBR is allowed: check if a Port whose Unmask SBR is already set needs no consent, as it happens with cxl_bus reset. Signed-off-by: Fabio M. De Francesco <[email protected]> --- drivers/pci/pci.c | 26 ++++++++++++++++++++++++++ drivers/pci/pci.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index eedd516f8484..08873ea3957b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4881,6 +4881,11 @@ static int cxl_sbr_prepare(struct pci_dev *bridge, u16 dvsec, { int rc; + if (action == CXL_SBR_OFFLINE_AND_UNBIND && !cxl_sbr_allowed(bridge)) { + pci_info(bridge, "SBR masked, write 1 to cxl_unmask_sbr to allow a bus reset\n"); + return -ENOTTY; + } + /* * CXL_SBR_UNBIND: the link is already down, so offlining the regions' * memory would take the reads that page migration performs as a machine @@ -5138,6 +5143,27 @@ static bool cxl_sbr_masked(struct pci_dev *dev) return true; } +/* + * cxl_sbr_allowed - whether an SBR of a CXL Downstream Port may go ahead + * @dev: Downstream Port to test + * + * Per CXL r4.0 sec 8.1.5.2 Table 8-32 the SBR bit in a CXL Port's Bridge + * Control register has no effect while the Port's Unmask SBR bit is clear, and + * sec 9.12.3 says System Firmware may leave it clear "to prevent CXL-unaware + * PCIe software from resetting the device and the link". A Port that already + * has it set needs no further permission; otherwise unmasking it takes the + * administrator's consent, given by writing 1 to the Port's cxl_unmask_sbr. + * + * Return: true if the reset paths may unmask and generate an SBR of @dev. + */ +bool cxl_sbr_allowed(struct pci_dev *dev) +{ + if (!cxl_port_dvsec(dev)) + return false; + + return dev->cxl_unmask_sbr || !cxl_sbr_masked(dev); +} + static int pci_reset_bus_function(struct pci_dev *dev, bool probe) { struct pci_dev *bridge = pci_upstream_bridge(dev); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b6d873b077ed..bea05acc58f0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -248,6 +248,7 @@ int __pci_bridge_secondary_bus_reset(struct pci_dev *dev, enum cxl_sbr_region_action action); bool is_cxl_dport(struct pci_dev *dev); u16 cxl_port_dvsec(struct pci_dev *dev); +bool cxl_sbr_allowed(struct pci_dev *dev); int pci_bus_error_reset(struct pci_dev *dev); int pci_try_reset_bridge(struct pci_dev *bridge); -- 2.55.0
