pci/aer: fix error injection

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>
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
This commit is contained in:
Michael S. Tsirkin 2010-12-08 17:46:25 +09:00
parent 624c716cc5
commit c3f33667a6
1 changed files with 35 additions and 16 deletions

View File

@ -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; 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;
}
/* /*
* return value: * return value:
* true: error message is sent up * true: error message is sent up
@ -272,14 +288,14 @@ static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
uint16_t cmd; uint16_t cmd;
uint8_t *aer_cap; uint8_t *aer_cap;
uint32_t root_cmd; uint32_t root_cmd;
uint32_t root_status; uint32_t root_status, prev_status;
bool msi_trigger; bool msi_trigger;
msg_sent = false; msg_sent = false;
cmd = pci_get_word(dev->config + PCI_COMMAND); cmd = pci_get_word(dev->config + PCI_COMMAND);
aer_cap = dev->config + dev->exp.aer_cap; aer_cap = dev->config + dev->exp.aer_cap;
root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); 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);
msi_trigger = false; msi_trigger = false;
if (cmd & PCI_COMMAND_SERR) { if (cmd & PCI_COMMAND_SERR) {
@ -337,20 +353,23 @@ static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
} }
pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status);
if (root_cmd & msg->severity) { /* 6.2.4.1.2 Interrupt Generation */
/* 6.2.4.1.2 Interrupt Generation */ /* All the above did was set some bits in the status register.
if (msix_enabled(dev)) { * Specifically these that match message severity.
if (msi_trigger) { * The below code relies on this fact. */
msix_notify(dev, pcie_aer_root_get_vector(dev)); if (!(root_cmd & msg->severity) ||
} (pcie_aer_status_to_cmd(prev_status) & root_cmd)) {
} else if (msi_enabled(dev)) { /* Condition is not being set or was already true so nothing to do. */
if (msi_trigger) { return msg_sent;
msi_notify(dev, pcie_aer_root_get_vector(dev)); }
}
} else { msg_sent = true;
qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); if (msix_enabled(dev)) {
} msix_notify(dev, pcie_aer_root_get_vector(dev));
msg_sent = true; } 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);
} }
return msg_sent; return msg_sent;
} }