diff options
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/intel-iommu.c | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 84f0d4284125..8303f256fe84 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1274,8 +1274,6 @@ iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu, if (!dmar_find_matched_atsr_unit(pdev)) return NULL; - info->iommu = iommu; - return info; } @@ -2134,7 +2132,7 @@ static struct dmar_domain *find_domain(struct device *dev) return NULL; } -static inline struct dmar_domain * +static inline struct device_domain_info * dmar_search_domain_by_dev_info(int segment, int bus, int devfn) { struct device_domain_info *info; @@ -2142,16 +2140,17 @@ dmar_search_domain_by_dev_info(int segment, int bus, int devfn) list_for_each_entry(info, &device_domain_list, global) if (info->segment == segment && info->bus == bus && info->devfn == devfn) - return info->domain; + return info; return NULL; } -static struct dmar_domain *dmar_insert_dev_info(int segment, int bus, int devfn, +static struct dmar_domain *dmar_insert_dev_info(struct intel_iommu *iommu, + int segment, int bus, int devfn, struct device *dev, struct dmar_domain *domain) { - struct dmar_domain *found; + struct dmar_domain *found = NULL; struct device_domain_info *info; unsigned long flags; @@ -2164,14 +2163,19 @@ static struct dmar_domain *dmar_insert_dev_info(int segment, int bus, int devfn, info->devfn = devfn; info->dev = dev; info->domain = domain; + info->iommu = iommu; if (!dev) domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES; spin_lock_irqsave(&device_domain_lock, flags); if (dev) found = find_domain(dev); - else - found = dmar_search_domain_by_dev_info(segment, bus, devfn); + else { + struct device_domain_info *info2; + info2 = dmar_search_domain_by_dev_info(segment, bus, devfn); + if (info2) + found = info2->domain; + } if (found) { spin_unlock_irqrestore(&device_domain_lock, flags); free_devinfo_mem(info); @@ -2192,7 +2196,8 @@ static struct dmar_domain *dmar_insert_dev_info(int segment, int bus, int devfn, static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) { struct dmar_domain *domain, *free = NULL; - struct intel_iommu *iommu; + struct intel_iommu *iommu = NULL; + struct device_domain_info *info; struct dmar_drhd_unit *drhd; struct pci_dev *dev_tmp; unsigned long flags; @@ -2215,10 +2220,13 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) devfn = dev_tmp->devfn; } spin_lock_irqsave(&device_domain_lock, flags); - domain = dmar_search_domain_by_dev_info(segment, bus, devfn); + info = dmar_search_domain_by_dev_info(segment, bus, devfn); + if (info) { + iommu = info->iommu; + domain = info->domain; + } spin_unlock_irqrestore(&device_domain_lock, flags); - /* pcie-pci bridge already has a domain, uses it */ - if (domain) + if (info) goto found_domain; } @@ -2244,14 +2252,15 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) /* register pcie-to-pci device */ if (dev_tmp) { - domain = dmar_insert_dev_info(segment, bus, devfn, NULL, domain); + domain = dmar_insert_dev_info(iommu, segment, bus, devfn, NULL, + domain); if (!domain) goto error; } found_domain: - domain = dmar_insert_dev_info(segment, pdev->bus->number, pdev->devfn, - &pdev->dev, domain); + domain = dmar_insert_dev_info(iommu, segment, pdev->bus->number, + pdev->devfn, &pdev->dev, domain); error: if (free != domain) domain_exit(free); @@ -2453,9 +2462,15 @@ static int domain_add_dev_info(struct dmar_domain *domain, int translation) { struct dmar_domain *ndomain; + struct intel_iommu *iommu; int ret; - ndomain = dmar_insert_dev_info(pci_domain_nr(pdev->bus), + iommu = device_to_iommu(pci_domain_nr(pdev->bus), + pdev->bus->number, pdev->devfn); + if (!iommu) + return -ENODEV; + + ndomain = dmar_insert_dev_info(iommu, pci_domain_nr(pdev->bus), pdev->bus->number, pdev->devfn, &pdev->dev, domain); if (ndomain != domain) |