list_del(&dev_data->dev_data_list);
spin_unlock_irqrestore(&dev_data_list_lock, flags);
+ if (dev_data->group)
+ iommu_group_put(dev_data->group);
+
kfree(dev_data);
}
return ret;
}
+static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data,
+ struct device *dev)
+{
+ if (!dev_data->group) {
+ struct iommu_group *group = iommu_group_alloc();
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+
+ dev_data->group = group;
+ }
+
+ return iommu_group_add_device(dev_data->group, dev);
+}
+
static int init_iommu_group(struct device *dev)
{
struct iommu_dev_data *dev_data;
struct iommu_group *group;
- struct pci_dev *dma_pdev = NULL;
+ struct pci_dev *dma_pdev;
int ret;
group = iommu_group_get(dev);
if (dev_data->alias_data) {
u16 alias;
+ struct pci_bus *bus;
+
+ if (dev_data->alias_data->group)
+ goto use_group;
+ /*
+ * If the alias device exists, it's effectively just a first
+ * level quirk for finding the DMA source.
+ */
alias = amd_iommu_alias_table[dev_data->devid];
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
- }
+ if (dma_pdev) {
+ dma_pdev = get_isolation_root(dma_pdev);
+ goto use_pdev;
+ }
+
+ /*
+ * If the alias is virtual, try to find a parent device
+ * and test whether the IOMMU group is actualy rooted above
+ * the alias. Be careful to also test the parent device if
+ * we think the alias is the root of the group.
+ */
+ bus = pci_find_bus(0, alias >> 8);
+ if (!bus)
+ goto use_group;
+
+ bus = find_hosted_bus(bus);
+ if (IS_ERR(bus) || !bus->self)
+ goto use_group;
- if (!dma_pdev)
- dma_pdev = pci_dev_get(to_pci_dev(dev));
+ dma_pdev = get_isolation_root(pci_dev_get(bus->self));
+ if (dma_pdev != bus->self || (dma_pdev->multifunction &&
+ !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)))
+ goto use_pdev;
+
+ pci_dev_put(dma_pdev);
+ goto use_group;
+ }
- dma_pdev = get_isolation_root(dma_pdev);
+ dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev)));
+use_pdev:
ret = use_pdev_iommu_group(dma_pdev, dev);
pci_dev_put(dma_pdev);
return ret;
+use_group:
+ return use_dev_data_iommu_group(dev_data->alias_data, dev);
}
static int iommu_init_device(struct device *dev)