提交 aa6ca5a9 编写于 作者: A Alex Williamson 提交者: Bjorn Helgaas

PCI/DPC: Fix shared interrupt handling

DPC supports shared interrupts, but it plays very loosely with testing
whether the interrupt is generated by DPC before generating spurious log
messages, such as:

  dpc 0000:10:01.2:pcie010: DPC containment event, status:0x1f00 source:0x0000

Testing the status register for zero or -1 is not sufficient when the
device supports the RP PIO First Error Pointer register.  Change this to
test whether the interrupt is enabled in the control register, retaining
the device present test, and that the status reports the interrupt as
signaled and DPC is triggered, clearing as a spurious interrupt otherwise.

Additionally, since the interrupt is actually serviced by a workqueue,
disable the interrupt in the control register until that completes or else
we may never see it execute due to further incoming interrupts.  A software
generated DPC floods the system otherwise.
Signed-off-by: NAlex Williamson <alex.williamson@redhat.com>
Signed-off-by: NBjorn Helgaas <helgaas@kernel.org>
Reviewed-by: NKeith Busch <keith.busch@intel.com>
上级 1291a0d5
...@@ -109,6 +109,7 @@ static void interrupt_event_handler(struct work_struct *work) ...@@ -109,6 +109,7 @@ static void interrupt_event_handler(struct work_struct *work)
struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
struct pci_dev *dev, *temp, *pdev = dpc->dev->port; struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
struct pci_bus *parent = pdev->subordinate; struct pci_bus *parent = pdev->subordinate;
u16 ctl;
pci_lock_rescan_remove(); pci_lock_rescan_remove();
list_for_each_entry_safe_reverse(dev, temp, &parent->devices, list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
...@@ -135,6 +136,10 @@ static void interrupt_event_handler(struct work_struct *work) ...@@ -135,6 +136,10 @@ static void interrupt_event_handler(struct work_struct *work)
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT); PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL,
ctl | PCI_EXP_DPC_CTL_INT_EN);
} }
static void dpc_rp_pio_print_tlp_header(struct device *dev, static void dpc_rp_pio_print_tlp_header(struct device *dev,
...@@ -249,34 +254,49 @@ static irqreturn_t dpc_irq(int irq, void *context) ...@@ -249,34 +254,49 @@ static irqreturn_t dpc_irq(int irq, void *context)
struct dpc_dev *dpc = (struct dpc_dev *)context; struct dpc_dev *dpc = (struct dpc_dev *)context;
struct pci_dev *pdev = dpc->dev->port; struct pci_dev *pdev = dpc->dev->port;
struct device *dev = &dpc->dev->device; struct device *dev = &dpc->dev->device;
u16 status, source; u16 ctl, status, source, reason, ext_reason;
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
return IRQ_NONE;
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status); pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
return IRQ_NONE;
if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
PCI_EXP_DPC_STATUS_INTERRUPT);
return IRQ_HANDLED;
}
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL,
ctl & ~PCI_EXP_DPC_CTL_INT_EN);
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_SOURCE_ID, pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_SOURCE_ID,
&source); &source);
if (!status || status == (u16)(~0))
return IRQ_NONE;
dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n", dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
status, source); status, source);
if (status & PCI_EXP_DPC_STATUS_TRIGGER) { reason = (status >> 1) & 0x3;
u16 reason = (status >> 1) & 0x3; ext_reason = (status >> 5) & 0x3;
u16 ext_reason = (status >> 5) & 0x3;
dev_warn(dev, "DPC %s detected, remove downstream devices\n",
dev_warn(dev, "DPC %s detected, remove downstream devices\n", (reason == 0) ? "unmasked uncorrectable error" :
(reason == 0) ? "unmasked uncorrectable error" : (reason == 1) ? "ERR_NONFATAL" :
(reason == 1) ? "ERR_NONFATAL" : (reason == 2) ? "ERR_FATAL" :
(reason == 2) ? "ERR_FATAL" : (ext_reason == 0) ? "RP PIO error" :
(ext_reason == 0) ? "RP PIO error" : (ext_reason == 1) ? "software trigger" :
(ext_reason == 1) ? "software trigger" : "reserved error");
"reserved error"); /* show RP PIO error detail information */
/* show RP PIO error detail information */ if (reason == 3 && ext_reason == 0)
if (reason == 3 && ext_reason == 0) dpc_process_rp_pio_error(dpc);
dpc_process_rp_pio_error(dpc);
schedule_work(&dpc->work);
schedule_work(&dpc->work);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册