Commit f144d149 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt

PCI/MSI: Add device flag indicating that 64-bit MSIs don't work

This can be set by quirks/drivers to be used by the architecture code
that assigns the MSI addresses.

We additionally add verification in the core MSI code that the values
assigned by the architecture do satisfy the limitation in order to fail
gracefully if they don't (ie. the arch hasn't been updated to deal with
that quirk yet).
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
CC: <stable@vger.kernel.org>
Acked-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 413cbf46
...@@ -590,6 +590,20 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev) ...@@ -590,6 +590,20 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev)
return entry; return entry;
} }
static int msi_verify_entries(struct pci_dev *dev)
{
struct msi_desc *entry;
list_for_each_entry(entry, &dev->msi_list, list) {
if (!dev->no_64bit_msi || !entry->msg.address_hi)
continue;
dev_err(&dev->dev, "Device has broken 64-bit MSI but arch"
" tried to assign one above 4G\n");
return -EIO;
}
return 0;
}
/** /**
* msi_capability_init - configure device's MSI capability structure * msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function * @dev: pointer to the pci_dev data structure of MSI device function
...@@ -627,6 +641,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) ...@@ -627,6 +641,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
return ret; return ret;
} }
ret = msi_verify_entries(dev);
if (ret) {
msi_mask_irq(entry, mask, ~mask);
free_msi_irqs(dev);
return ret;
}
ret = populate_msi_sysfs(dev); ret = populate_msi_sysfs(dev);
if (ret) { if (ret) {
msi_mask_irq(entry, mask, ~mask); msi_mask_irq(entry, mask, ~mask);
...@@ -739,6 +760,11 @@ static int msix_capability_init(struct pci_dev *dev, ...@@ -739,6 +760,11 @@ static int msix_capability_init(struct pci_dev *dev,
if (ret) if (ret)
goto out_avail; goto out_avail;
/* Check if all MSI entries honor device restrictions */
ret = msi_verify_entries(dev);
if (ret)
goto out_free;
/* /*
* Some devices require MSI-X to be enabled before we can touch the * Some devices require MSI-X to be enabled before we can touch the
* MSI-X registers. We need to mask all the vectors to prevent * MSI-X registers. We need to mask all the vectors to prevent
......
...@@ -331,6 +331,7 @@ struct pci_dev { ...@@ -331,6 +331,7 @@ struct pci_dev {
unsigned int is_added:1; unsigned int is_added:1;
unsigned int is_busmaster:1; /* device is busmaster */ unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */ unsigned int no_msi:1; /* device may not use msi */
unsigned int no_64bit_msi:1; /* device may only use 32-bit MSIs */
unsigned int block_cfg_access:1; /* config space access is blocked */ unsigned int block_cfg_access:1; /* config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */ unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */ unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment