iommu/vt-d: Update to use PCI DMA aliases
VT-d code currently makes use of pci_find_upstream_pcie_bridge() in order to find the topology based alias of a device. This function has a few problems. First, it doesn't check the entire alias path of the device to the root bus, therefore if a PCIe device is masked upstream, the wrong result is produced. Also, it's known to get confused and give up when it crosses a bridge from a conventional PCI bus to a PCIe bus that lacks a PCIe capability. The PCI-core provided DMA alias support solves both of these problems and additionally adds support for DMA function quirks allowing VT-d to work with devices like Marvell and Ricoh with known broken requester IDs. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Cc: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:

committed by
Joerg Roedel

parent
e17f9ff413
commit
579305f75d
@@ -369,29 +369,52 @@ static int set_hpet_sid(struct irte *irte, u8 id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct set_msi_sid_data {
|
||||
struct pci_dev *pdev;
|
||||
u16 alias;
|
||||
};
|
||||
|
||||
static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
|
||||
{
|
||||
struct set_msi_sid_data *data = opaque;
|
||||
|
||||
data->pdev = pdev;
|
||||
data->alias = alias;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *bridge;
|
||||
struct set_msi_sid_data data;
|
||||
|
||||
if (!irte || !dev)
|
||||
return -1;
|
||||
|
||||
/* PCIe device or Root Complex integrated PCI device */
|
||||
if (pci_is_pcie(dev) || !dev->bus->parent) {
|
||||
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
|
||||
(dev->bus->number << 8) | dev->devfn);
|
||||
return 0;
|
||||
}
|
||||
pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
|
||||
|
||||
bridge = pci_find_upstream_pcie_bridge(dev);
|
||||
if (bridge) {
|
||||
if (pci_is_pcie(bridge))/* this is a PCIe-to-PCI/PCIX bridge */
|
||||
set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
|
||||
(bridge->bus->number << 8) | dev->bus->number);
|
||||
else /* this is a legacy PCI bridge */
|
||||
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
|
||||
(bridge->bus->number << 8) | bridge->devfn);
|
||||
}
|
||||
/*
|
||||
* DMA alias provides us with a PCI device and alias. The only case
|
||||
* where the it will return an alias on a different bus than the
|
||||
* device is the case of a PCIe-to-PCI bridge, where the alias is for
|
||||
* the subordinate bus. In this case we can only verify the bus.
|
||||
*
|
||||
* If the alias device is on a different bus than our source device
|
||||
* then we have a topology based alias, use it.
|
||||
*
|
||||
* Otherwise, the alias is for a device DMA quirk and we cannot
|
||||
* assume that MSI uses the same requester ID. Therefore use the
|
||||
* original device.
|
||||
*/
|
||||
if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
|
||||
set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
|
||||
PCI_DEVID(PCI_BUS_NUM(data.alias),
|
||||
dev->bus->number));
|
||||
else if (data.pdev->bus->number != dev->bus->number)
|
||||
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
|
||||
else
|
||||
set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
|
||||
PCI_DEVID(dev->bus->number, dev->devfn));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user