Patchwork [4/6] pci/aer: fix error injection

login
register
mail settings
Submitter Michael S. Tsirkin
Date Dec. 2, 2010, 10:54 p.m.
Message ID <7aedc0ee96b2adabd3230cc1ceacea3c78454338.1291330353.git.mst@redhat.com>
Download mbox | patch
Permalink /patch/74041/
State New
Headers show

Comments

Michael S. Tsirkin - Dec. 2, 2010, 10:54 p.m.
Fix the injection logic upon aer message to follow 6.2.4.1.2 more
closely: specifically only send an msi interrupt when the logical or of
the enabled bits changed, not when a bit which was previously clear
becomes set.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 hw/pcie_aer.c |   45 ++++++++++++++++++++++++++++++++++-----------
 1 files changed, 34 insertions(+), 11 deletions(-)

Patch

diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
index 204155b..0fc191f 100644
--- a/hw/pcie_aer.c
+++ b/hw/pcie_aer.c
@@ -257,6 +257,22 @@  static unsigned int pcie_aer_root_get_vector(PCIDevice *dev)
     return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT;
 }
 
+/* Given a status register, get corresponding bits in the command register */
+static uint32_t pcie_aer_status_to_cmd(uint32_t status)
+{
+    uint32_t cmd = 0;
+    if (status & PCI_ERR_ROOT_COR_RCV) {
+        cmd |= PCI_ERR_ROOT_CMD_COR_EN;
+    }
+    if (status & PCI_ERR_ROOT_NONFATAL_RCV) {
+        cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN;
+    }
+    if (status & PCI_ERR_ROOT_FATAL_RCV) {
+        cmd |= PCI_ERR_ROOT_CMD_FATAL_EN;
+    }
+    return cmd;
+}
+
 /*
  * 6.2.6 Error Message Control
  * Figure 6-3
@@ -267,12 +283,12 @@  static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
     uint16_t cmd;
     uint8_t *aer_cap;
     uint32_t root_cmd;
-    uint32_t root_status;
+    uint32_t root_status, prev_status;
 
     cmd = pci_get_word(dev->config + PCI_COMMAND);
     aer_cap = dev->config + dev->exp.aer_cap;
     root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
-    root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+    prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
 
     if (cmd & PCI_COMMAND_SERR) {
         /* System Error.
@@ -326,15 +342,22 @@  static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
     }
     pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status);
 
-    if (root_cmd & msg->severity) {
-        /* 6.2.4.1.2 Interrupt Generation */
-        if (msix_enabled(dev)) {
-            msix_notify(dev, pcie_aer_root_get_vector(dev));
-        } else if (msi_enabled(dev)) {
-            msi_notify(dev, pcie_aer_root_get_vector(dev));
-        } else {
-            qemu_set_irq(dev->irq[dev->exp.aer_intx], 1);
-        }
+    /* 6.2.4.1.2 Interrupt Generation */
+    /* All the above did was set some bits in the status register.
+     * Specifically these that match message severity.
+     * The below code relies on this fact. */
+    if (!(root_cmd & msg->severity) ||
+        (pcie_aer_status_to_cmd(prev_status) & root_cmd)) {
+        /* Condition is not being set or was already true so nothing to do. */
+        return;
+    }
+
+    if (msix_enabled(dev)) {
+        msix_notify(dev, pcie_aer_root_get_vector(dev));
+    } else if (msi_enabled(dev)) {
+        msi_notify(dev, pcie_aer_root_get_vector(dev));
+    } else {
+        qemu_set_irq(dev->irq[dev->exp.aer_intx], 1);
     }
 }