[PATCH 07/13] iommu/amd: Update to use PCI DMA aliases
From: Alex Williamson
Date: Thu May 01 2014 - 12:28:00 EST
AMD-Vi already has a concept of an alias provided via the IVRS table.
This alias only handles topology based aliases, such as PCIe-to-PCI
bridges. When such an alias is present, we continue to use it. When
a platform alias is not present, we can now add a check of the device
dma_func_alias to create our own. Note that the current code can
only handle a single alias of a device, and we don't attempt to change
that here. It would only become a factor for the requester ID seen by
the IOMMU if PCI-X were involved anway.
Since the alias is now potentially device specific rather than the
topology based alias provided by the platform, we need to clear it
when the device goes away.
With the DMA alias and isolation infrastructure now in PCI-core, we
could opt to ignore IVRS provided aliases. We now do this for IOMMU
groups. However, for this more common use case, the "don't fix what
isn't broken" mantra seems like the safer route.
This should allow AMD-Vi to work with devices like Marvell and Ricoh
with DMA function alias quirks.
Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
Cc: Joerg Roedel <joro@xxxxxxxxxx>
---
drivers/iommu/amd_iommu.c | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 3d58de4..4621692 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -294,6 +294,7 @@ static int iommu_init_device(struct device *dev)
struct pci_dev *pdev = to_pci_dev(dev);
struct iommu_dev_data *dev_data;
u16 alias;
+ u8 func_alias;
int ret;
if (dev->archdata.iommu)
@@ -304,6 +305,19 @@ static int iommu_init_device(struct device *dev)
return -ENOMEM;
alias = amd_iommu_alias_table[dev_data->devid];
+
+ /*
+ * If there is no platform provided alias (topology-based) check
+ * if we have a device quirk based alias.
+ */
+ func_alias = pdev->dma_func_alias & ~(1 << PCI_SLOT(pdev->devfn));
+ if (func_alias && alias == dev_data->devid) {
+ WARN_ON(hweight8(func_alias) > 1);
+ alias = PCI_DEVID(pdev->bus->number,
+ PCI_DEVFN(PCI_SLOT(pdev->devfn),
+ ffs(func_alias) - 1));
+ }
+
if (alias != dev_data->devid) {
struct iommu_dev_data *alias_data;
@@ -351,12 +365,19 @@ static void iommu_ignore_device(struct device *dev)
static void iommu_uninit_device(struct device *dev)
{
+ struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev));
+
+ if (!dev_data)
+ return;
+
iommu_group_remove_device(dev);
+ /* Unlink from alias, it may change if another device is re-plugged */
+ dev_data->alias_data = NULL;
+
/*
- * Nothing to do here - we keep dev_data around for unplugged devices
- * and reuse it when the device is re-plugged - not doing so would
- * introduce a ton of races.
+ * We keep dev_data around for unplugged devices and reuse it when the
+ * device is re-plugged - not doing so would introduce a ton of races.
*/
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/