Skip to content

Commit 4d07c23

Browse files
l1kopsiff
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 Signed-off-by: LeoLiu-oc <leoliu-oc@zhaoxin.com>
1 parent 7290086 commit 4d07c23

3 files changed

Lines changed: 43 additions & 36 deletions

File tree

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
@@ -283,35 +283,6 @@ static int pciehp_suspend(struct pcie_device *dev)
283283
return 0;
284284
}
285285

286-
static bool pciehp_device_replaced(struct controller *ctrl)
287-
{
288-
struct pci_dev *pdev __free(pci_dev_put) = NULL;
289-
u32 reg;
290-
291-
if (pci_dev_is_disconnected(ctrl->pcie->port))
292-
return false;
293-
294-
pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0));
295-
if (!pdev)
296-
return true;
297-
298-
if (pci_read_config_dword(pdev, PCI_VENDOR_ID, &reg) ||
299-
reg != (pdev->vendor | (pdev->device << 16)) ||
300-
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &reg) ||
301-
reg != (pdev->revision | (pdev->class << 8)))
302-
return true;
303-
304-
if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
305-
(pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, &reg) ||
306-
reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16))))
307-
return true;
308-
309-
if (pci_get_dsn(pdev) != ctrl->dsn)
310-
return true;
311-
312-
return false;
313-
}
314-
315286
static int pciehp_resume_noirq(struct pcie_device *dev)
316287
{
317288
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
@@ -559,18 +559,48 @@ void pciehp_power_off_slot(struct controller *ctrl)
559559
PCI_EXP_SLTCTL_PWR_OFF);
560560
}
561561

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

@@ -580,8 +610,8 @@ static void pciehp_ignore_dpc_link_change(struct controller *ctrl,
580610
* Synthesize it to ensure that it is acted on.
581611
*/
582612
down_read_nested(&ctrl->reset_lock, ctrl->depth);
583-
if (!pciehp_check_link_active(ctrl))
584-
pciehp_request(ctrl, PCI_EXP_SLTSTA_DLLSC);
613+
if (!pciehp_check_link_active(ctrl) || pciehp_device_replaced(ctrl))
614+
pciehp_request(ctrl, ignored_events);
585615
up_read(&ctrl->reset_lock);
586616
}
587617

@@ -732,8 +762,13 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
732762
*/
733763
if ((events & PCI_EXP_SLTSTA_DLLSC) && pci_dpc_recovered(pdev) &&
734764
ctrl->state == ON_STATE) {
735-
events &= ~PCI_EXP_SLTSTA_DLLSC;
736-
pciehp_ignore_dpc_link_change(ctrl, pdev, irq);
765+
u16 ignored_events = PCI_EXP_SLTSTA_DLLSC;
766+
767+
if (!ctrl->inband_presence_disabled)
768+
ignored_events |= events & PCI_EXP_SLTSTA_PDC;
769+
770+
events &= ~ignored_events;
771+
pciehp_ignore_dpc_link_change(ctrl, pdev, irq, ignored_events);
737772
}
738773

739774
/*

0 commit comments

Comments
 (0)