mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-29 23:53:32 +00:00
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 commits5651c48cfa
("PCI pciehp: fix power fault interrupt storm problem") and99f0169c17
("PCI: pciehp: enable software notification on empty slots"). Unfortunately in 2020 the infinite loop issue was inadvertently reintroduced by commit8edf5332c3
("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:8edf5332c3
("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>
This commit is contained in:
parent
fa55b7dcdc
commit
23584c1ed3
1 changed files with 4 additions and 3 deletions
|
@ -642,6 +642,8 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
|||
*/
|
||||
if (ctrl->power_fault_detected)
|
||||
status &= ~PCI_EXP_SLTSTA_PFD;
|
||||
else if (status & PCI_EXP_SLTSTA_PFD)
|
||||
ctrl->power_fault_detected = true;
|
||||
|
||||
events |= status;
|
||||
if (!events) {
|
||||
|
@ -651,7 +653,7 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
if (status) {
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, status);
|
||||
|
||||
/*
|
||||
* 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)
|
|||
}
|
||||
|
||||
/* Check Power Fault Detected */
|
||||
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
|
||||
ctrl->power_fault_detected = 1;
|
||||
if (events & PCI_EXP_SLTSTA_PFD) {
|
||||
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
|
||||
pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
|
||||
PCI_EXP_SLTCTL_ATTN_IND_ON);
|
||||
|
|
Loading…
Reference in a new issue