Skip to content

Commit b1956e2

Browse files
davejiangbjorn-helgaas
authored andcommitted
PCI/CXL: Fail bus reset if upstream CXL Port has SBR masked
Per CXL spec r3.1, sec 8.1.5.2, the Secondary Bus Reset (SBR) bit in the Bridge Control register of a CXL port has no effect unless the "Unmask SBR" bit is set. Return -ENOTTY if we attempt a bus reset on a device below a CXL Port where "Unmask SBR" is 0. Otherwise, the bus reset would appear to have succeeded even though setting the bridge SBR bit had no effect. Link: https://lore.kernel.org/linux-cxl/20240220203956.GA1502351@bhelgaas/ Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Dave Jiang <[email protected]> [bhelgaas: simplify commit log and comments] Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> Reviewed-by: Kuppuswamy Sathyanarayanan <[email protected]> Reviewed-by: Dan Williams <[email protected]>
1 parent 7e89efc commit b1956e2

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

drivers/pci/pci.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4928,10 +4928,52 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe)
49284928
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
49294929
}
49304930

4931+
static u16 cxl_port_dvsec(struct pci_dev *dev)
4932+
{
4933+
return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
4934+
PCI_DVSEC_CXL_PORT);
4935+
}
4936+
4937+
static bool cxl_sbr_masked(struct pci_dev *dev)
4938+
{
4939+
u16 dvsec, reg;
4940+
int rc;
4941+
4942+
dvsec = cxl_port_dvsec(dev);
4943+
if (!dvsec)
4944+
return false;
4945+
4946+
rc = pci_read_config_word(dev, dvsec + PCI_DVSEC_CXL_PORT_CTL, &reg);
4947+
if (rc || PCI_POSSIBLE_ERROR(reg))
4948+
return false;
4949+
4950+
/*
4951+
* Per CXL spec r3.1, sec 8.1.5.2, when "Unmask SBR" is 0, the SBR
4952+
* bit in Bridge Control has no effect. When 1, the Port generates
4953+
* hot reset when the SBR bit is set to 1.
4954+
*/
4955+
if (reg & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR)
4956+
return false;
4957+
4958+
return true;
4959+
}
4960+
49314961
static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
49324962
{
4963+
struct pci_dev *bridge = pci_upstream_bridge(dev);
49334964
int rc;
49344965

4966+
/*
4967+
* If "dev" is below a CXL port that has SBR control masked, SBR
4968+
* won't do anything, so return error.
4969+
*/
4970+
if (bridge && cxl_sbr_masked(bridge)) {
4971+
if (probe)
4972+
return 0;
4973+
4974+
return -ENOTTY;
4975+
}
4976+
49354977
rc = pci_dev_reset_slot_function(dev, probe);
49364978
if (rc != -ENOTTY)
49374979
return rc;

include/uapi/linux/pci_regs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,4 +1148,9 @@
11481148
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000
11491149
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000
11501150

1151+
/* Compute Express Link (CXL r3.1, sec 8.1.5) */
1152+
#define PCI_DVSEC_CXL_PORT 3
1153+
#define PCI_DVSEC_CXL_PORT_CTL 0x0c
1154+
#define PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR 0x00000001
1155+
11511156
#endif /* LINUX_PCI_REGS_H */

0 commit comments

Comments
 (0)