Skip to content

Commit c3be50f

Browse files
l1kbjorn-helgaas
authored andcommitted
PCI: pciehp: Ignore Presence Detect Changed caused by DPC
Commit a97396c ("PCI: pciehp: Ignore Link Down/Up caused by DPC") amended PCIe hotplug to not bring down the slot upon Data Link Layer State Changed events caused by Downstream Port Containment. However Keith reports off-list that if the slot uses in-band presence detect (i.e. Presence Detect State is derived from Data Link Layer Link Active), DPC also causes a spurious Presence Detect Changed event. This needs to be ignored as well. Unfortunately there's no register indicating that in-band presence detect is used. PCIe r5.0 sec 7.5.3.10 introduced the In-Band PD Disable bit in the Slot Control Register. The PCIe hotplug driver sets this bit on ports supporting it. But older ports may still use in-band presence detect. If in-band presence detect can be disabled, Presence Detect Changed events occurring during DPC must not be ignored because they signal device replacement. On all other ports, device replacement cannot be detected reliably because the Presence Detect Changed event could be a side effect of DPC. On those (older) ports, perform a best-effort device replacement check by comparing the Vendor ID, Device ID and other data in Config Space with the values cached in struct pci_dev. Use the existing helper pciehp_device_replaced() to accomplish this. It is currently #ifdef'ed to CONFIG_PM_SLEEP in pciehp_core.c, so move it to pciehp_hpc.c where most other functions accessing config space reside. Reported-by: Keith Busch <kbusch@kernel.org> Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Link: https://patch.msgid.link/fa264ff71952915c4e35a53c89eb0cde8455a5c5.1744298239.git.lukas@wunner.de
1 parent 0af2f6b commit c3be50f

File tree

3 files changed

+43
-36
lines changed

3 files changed

+43
-36
lines changed

drivers/pci/hotplug/pciehp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ int pciehp_card_present(struct controller *ctrl);
187187
int pciehp_card_present_or_link_active(struct controller *ctrl);
188188
int pciehp_check_link_status(struct controller *ctrl);
189189
int pciehp_check_link_active(struct controller *ctrl);
190+
bool pciehp_device_replaced(struct controller *ctrl);
190191
void pciehp_release_ctrl(struct controller *ctrl);
191192

192193
int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot);

drivers/pci/hotplug/pciehp_core.c

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -284,35 +284,6 @@ 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) = NULL;
290-
u32 reg;
291-
292-
if (pci_dev_is_disconnected(ctrl->pcie->port))
293-
return false;
294-
295-
pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0));
296-
if (!pdev)
297-
return true;
298-
299-
if (pci_read_config_dword(pdev, PCI_VENDOR_ID, &reg) ||
300-
reg != (pdev->vendor | (pdev->device << 16)) ||
301-
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &reg) ||
302-
reg != (pdev->revision | (pdev->class << 8)))
303-
return true;
304-
305-
if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
306-
(pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, &reg) ||
307-
reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16))))
308-
return true;
309-
310-
if (pci_get_dsn(pdev) != ctrl->dsn)
311-
return true;
312-
313-
return false;
314-
}
315-
316287
static int pciehp_resume_noirq(struct pcie_device *dev)
317288
{
318289
struct controller *ctrl = get_service_data(dev);

drivers/pci/hotplug/pciehp_hpc.c

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -563,18 +563,48 @@ void pciehp_power_off_slot(struct controller *ctrl)
563563
PCI_EXP_SLTCTL_PWR_OFF);
564564
}
565565

566+
bool pciehp_device_replaced(struct controller *ctrl)
567+
{
568+
struct pci_dev *pdev __free(pci_dev_put) = NULL;
569+
u32 reg;
570+
571+
if (pci_dev_is_disconnected(ctrl->pcie->port))
572+
return false;
573+
574+
pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0));
575+
if (!pdev)
576+
return true;
577+
578+
if (pci_read_config_dword(pdev, PCI_VENDOR_ID, &reg) ||
579+
reg != (pdev->vendor | (pdev->device << 16)) ||
580+
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &reg) ||
581+
reg != (pdev->revision | (pdev->class << 8)))
582+
return true;
583+
584+
if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
585+
(pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, &reg) ||
586+
reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16))))
587+
return true;
588+
589+
if (pci_get_dsn(pdev) != ctrl->dsn)
590+
return true;
591+
592+
return false;
593+
}
594+
566595
static void pciehp_ignore_dpc_link_change(struct controller *ctrl,
567-
struct pci_dev *pdev, int irq)
596+
struct pci_dev *pdev, int irq,
597+
u16 ignored_events)
568598
{
569599
/*
570600
* Ignore link changes which occurred while waiting for DPC recovery.
571601
* Could be several if DPC triggered multiple times consecutively.
572602
*/
573603
synchronize_hardirq(irq);
574-
atomic_and(~PCI_EXP_SLTSTA_DLLSC, &ctrl->pending_events);
604+
atomic_and(~ignored_events, &ctrl->pending_events);
575605
if (pciehp_poll_mode)
576606
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
577-
PCI_EXP_SLTSTA_DLLSC);
607+
ignored_events);
578608
ctrl_info(ctrl, "Slot(%s): Link Down/Up ignored (recovered by DPC)\n",
579609
slot_name(ctrl));
580610

@@ -584,8 +614,8 @@ static void pciehp_ignore_dpc_link_change(struct controller *ctrl,
584614
* Synthesize it to ensure that it is acted on.
585615
*/
586616
down_read_nested(&ctrl->reset_lock, ctrl->depth);
587-
if (!pciehp_check_link_active(ctrl))
588-
pciehp_request(ctrl, PCI_EXP_SLTSTA_DLLSC);
617+
if (!pciehp_check_link_active(ctrl) || pciehp_device_replaced(ctrl))
618+
pciehp_request(ctrl, ignored_events);
589619
up_read(&ctrl->reset_lock);
590620
}
591621

@@ -736,8 +766,13 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
736766
*/
737767
if ((events & PCI_EXP_SLTSTA_DLLSC) && pci_dpc_recovered(pdev) &&
738768
ctrl->state == ON_STATE) {
739-
events &= ~PCI_EXP_SLTSTA_DLLSC;
740-
pciehp_ignore_dpc_link_change(ctrl, pdev, irq);
769+
u16 ignored_events = PCI_EXP_SLTSTA_DLLSC;
770+
771+
if (!ctrl->inband_presence_disabled)
772+
ignored_events |= events & PCI_EXP_SLTSTA_PDC;
773+
774+
events &= ~ignored_events;
775+
pciehp_ignore_dpc_link_change(ctrl, pdev, irq, ignored_events);
741776
}
742777

743778
/*

0 commit comments

Comments
 (0)