Skip to content

Commit 23584c1

Browse files
l1kbjorn-helgaas
authored andcommitted
PCI: pciehp: Fix infinite loop in IRQ handler upon power fault
The Power Fault Detected bit in the Slot Status register differs from all other hotplug events in that it is sticky: It can only be cleared after turning off slot power. Per PCIe r5.0, sec. 6.7.1.8: If a power controller detects a main power fault on the hot-plug slot, it must automatically set its internal main power fault latch [...]. The main power fault latch is cleared when software turns off power to the hot-plug slot. The stickiness used to cause interrupt storms and infinite loops which were fixed in 2009 by commits 5651c48 ("PCI pciehp: fix power fault interrupt storm problem") and 99f0169 ("PCI: pciehp: enable software notification on empty slots"). Unfortunately in 2020 the infinite loop issue was inadvertently reintroduced by commit 8edf533 ("PCI: pciehp: Fix MSI interrupt race"): The hardirq handler pciehp_isr() clears the PFD bit until pciehp's power_fault_detected flag is set. That happens in the IRQ thread pciehp_ist(), which never learns of the event because the hardirq handler is stuck in an infinite loop. Fix by setting the power_fault_detected flag already in the hardirq handler. Link: https://bugzilla.kernel.org/show_bug.cgi?id=214989 Link: https://lore.kernel.org/linux-pci/DM8PR11MB5702255A6A92F735D90A4446868B9@DM8PR11MB5702.namprd11.prod.outlook.com Fixes: 8edf533 ("PCI: pciehp: Fix MSI interrupt race") Link: https://lore.kernel.org/r/66eaeef31d4997ceea357ad93259f290ededecfd.1637187226.git.lukas@wunner.de Reported-by: Joseph Bao <joseph.bao@intel.com> Tested-by: Joseph Bao <joseph.bao@intel.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Cc: stable@vger.kernel.org # v4.19+ Cc: Stuart Hayes <stuart.w.hayes@gmail.com>
1 parent fa55b7d commit 23584c1

1 file changed

Lines changed: 4 additions & 3 deletions

File tree

drivers/pci/hotplug/pciehp_hpc.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,8 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
642642
*/
643643
if (ctrl->power_fault_detected)
644644
status &= ~PCI_EXP_SLTSTA_PFD;
645+
else if (status & PCI_EXP_SLTSTA_PFD)
646+
ctrl->power_fault_detected = true;
645647

646648
events |= status;
647649
if (!events) {
@@ -651,7 +653,7 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
651653
}
652654

653655
if (status) {
654-
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
656+
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, status);
655657

656658
/*
657659
* In MSI mode, all event bits must be zero before the port
@@ -725,8 +727,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
725727
}
726728

727729
/* Check Power Fault Detected */
728-
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
729-
ctrl->power_fault_detected = 1;
730+
if (events & PCI_EXP_SLTSTA_PFD) {
730731
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
731732
pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
732733
PCI_EXP_SLTCTL_ATTN_IND_ON);

0 commit comments

Comments
 (0)