Skip to content

Commit 675ba77

Browse files
committed
Merge branch 'pci/hotplug'
- Detect if a device was removed or replaced during system sleep so we don't assume a new device is the one that used to be there. This uses Vendor/Device/Subsystem/Class/Revision and Device Serial Number (if implemented), so it's not fool-proof and drivers may know how to detect more cases (Lukas Wunner) - Add missing MODULE_DESCRIPTION() macro (Jeff Johnson) * pci/hotplug: PCI: acpiphp: Add missing MODULE_DESCRIPTION() macro PCI: pciehp: Detect device replacement during system sleep
2 parents 5249048 + 618b29a commit 675ba77

File tree

5 files changed

+55
-1
lines changed

5 files changed

+55
-1
lines changed

drivers/pci/hotplug/acpiphp_ampere_altra.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,5 @@ static struct platform_driver altra_led_driver = {
124124
module_platform_driver(altra_led_driver);
125125

126126
MODULE_AUTHOR("D Scott Phillips <[email protected]>");
127+
MODULE_DESCRIPTION("ACPI PCI Hot Plug Extension for Ampere Altra");
127128
MODULE_LICENSE("GPL");

drivers/pci/hotplug/pciehp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ extern int pciehp_poll_time;
4646
/**
4747
* struct controller - PCIe hotplug controller
4848
* @pcie: pointer to the controller's PCIe port service device
49+
* @dsn: cached copy of Device Serial Number of Function 0 in the hotplug slot
50+
* (PCIe r6.2 sec 7.9.3); used to determine whether a hotplugged device
51+
* was replaced with a different one during system sleep
4952
* @slot_cap: cached copy of the Slot Capabilities register
5053
* @inband_presence_disabled: In-Band Presence Detect Disable supported by
5154
* controller and disabled per spec recommendation (PCIe r5.0, appendix I
@@ -87,6 +90,7 @@ extern int pciehp_poll_time;
8790
*/
8891
struct controller {
8992
struct pcie_device *pcie;
93+
u64 dsn;
9094

9195
u32 slot_cap; /* capabilities and quirks */
9296
unsigned int inband_presence_disabled:1;

drivers/pci/hotplug/pciehp_core.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,32 @@ static int pciehp_suspend(struct pcie_device *dev)
284284
return 0;
285285
}
286286

287+
static bool pciehp_device_replaced(struct controller *ctrl)
288+
{
289+
struct pci_dev *pdev __free(pci_dev_put);
290+
u32 reg;
291+
292+
pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0));
293+
if (!pdev)
294+
return true;
295+
296+
if (pci_read_config_dword(pdev, PCI_VENDOR_ID, &reg) ||
297+
reg != (pdev->vendor | (pdev->device << 16)) ||
298+
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &reg) ||
299+
reg != (pdev->revision | (pdev->class << 8)))
300+
return true;
301+
302+
if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
303+
(pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, &reg) ||
304+
reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16))))
305+
return true;
306+
307+
if (pci_get_dsn(pdev) != ctrl->dsn)
308+
return true;
309+
310+
return false;
311+
}
312+
287313
static int pciehp_resume_noirq(struct pcie_device *dev)
288314
{
289315
struct controller *ctrl = get_service_data(dev);
@@ -293,9 +319,23 @@ static int pciehp_resume_noirq(struct pcie_device *dev)
293319
ctrl->cmd_busy = true;
294320

295321
/* clear spurious events from rediscovery of inserted card */
296-
if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE)
322+
if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) {
297323
pcie_clear_hotplug_events(ctrl);
298324

325+
/*
326+
* If hotplugged device was replaced with a different one
327+
* during system sleep, mark the old device disconnected
328+
* (to prevent its driver from accessing the new device)
329+
* and synthesize a Presence Detect Changed event.
330+
*/
331+
if (pciehp_device_replaced(ctrl)) {
332+
ctrl_dbg(ctrl, "device replaced during system sleep\n");
333+
pci_walk_bus(ctrl->pcie->port->subordinate,
334+
pci_dev_set_disconnected, NULL);
335+
pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
336+
}
337+
}
338+
299339
return 0;
300340
}
301341
#endif

drivers/pci/hotplug/pciehp_hpc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,11 @@ struct controller *pcie_init(struct pcie_device *dev)
10551055
}
10561056
}
10571057

1058+
pdev = pci_get_slot(subordinate, PCI_DEVFN(0, 0));
1059+
if (pdev)
1060+
ctrl->dsn = pci_get_dsn(pdev);
1061+
pci_dev_put(pdev);
1062+
10581063
return ctrl;
10591064
}
10601065

drivers/pci/hotplug/pciehp_pci.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ int pciehp_configure_device(struct controller *ctrl)
7272
pci_bus_add_devices(parent);
7373
down_read_nested(&ctrl->reset_lock, ctrl->depth);
7474

75+
dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
76+
ctrl->dsn = pci_get_dsn(dev);
77+
pci_dev_put(dev);
78+
7579
out:
7680
pci_unlock_rescan_remove();
7781
return ret;

0 commit comments

Comments
 (0)