diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index fffe3d166662..67b114e784bc 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1957,18 +1957,71 @@ find_domain(struct pci_dev *pdev) return NULL; } +static inline struct dmar_domain * +dmar_search_domain_by_dev_info(int segment, int bus, int devfn) +{ + struct device_domain_info *info; + + list_for_each_entry(info, &device_domain_list, global) + if (info->segment == segment && info->bus == bus && + info->devfn == devfn) + return info->domain; + + return NULL; +} + +static int dmar_insert_dev_info(int segment, int bus, int devfn, + struct pci_dev *dev, struct dmar_domain **domp) +{ + struct dmar_domain *found, *domain = *domp; + struct device_domain_info *info; + unsigned long flags; + + info = alloc_devinfo_mem(); + if (!info) + return -ENOMEM; + + info->segment = segment; + info->bus = bus; + info->devfn = devfn; + info->dev = dev; + info->domain = domain; + 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); + if (found) { + spin_unlock_irqrestore(&device_domain_lock, flags); + free_devinfo_mem(info); + if (found != domain) { + domain_exit(domain); + *domp = found; + } + } else { + list_add(&info->link, &domain->devices); + list_add(&info->global, &device_domain_list); + if (dev) + dev->dev.archdata.iommu = info; + spin_unlock_irqrestore(&device_domain_lock, flags); + } + + return 0; +} + /* domain is initialized */ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) { - struct dmar_domain *domain, *found = NULL; + struct dmar_domain *domain; struct intel_iommu *iommu; struct dmar_drhd_unit *drhd; - struct device_domain_info *info, *tmp; struct pci_dev *dev_tmp; unsigned long flags; int bus = 0, devfn = 0; int segment; - int ret; domain = find_domain(pdev); if (domain) @@ -1986,41 +2039,29 @@ 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); - list_for_each_entry(info, &device_domain_list, global) { - if (info->segment == segment && - info->bus == bus && info->devfn == devfn) { - found = info->domain; - break; - } - } + domain = dmar_search_domain_by_dev_info(segment, bus, devfn); spin_unlock_irqrestore(&device_domain_lock, flags); /* pcie-pci bridge already has a domain, uses it */ - if (found) { - domain = found; + if (domain) goto found_domain; - } } - domain = alloc_domain(); - if (!domain) - goto error; - - /* Allocate new domain for the device */ drhd = dmar_find_matched_drhd_unit(pdev); if (!drhd) { printk(KERN_ERR "IOMMU: can't find DMAR for device %s\n", pci_name(pdev)); - free_domain_mem(domain); return NULL; } iommu = drhd->iommu; - ret = iommu_attach_domain(domain, iommu); - if (ret) { + /* Allocate and intialize new domain for the device */ + domain = alloc_domain(); + if (!domain) + goto error; + if (iommu_attach_domain(domain, iommu)) { free_domain_mem(domain); goto error; } - if (domain_init(domain, gaw)) { domain_exit(domain); goto error; @@ -2028,67 +2069,16 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) /* register pcie-to-pci device */ if (dev_tmp) { - info = alloc_devinfo_mem(); - if (!info) { + if (dmar_insert_dev_info(segment, bus, devfn, NULL, &domain)) { domain_exit(domain); goto error; } - info->segment = segment; - info->bus = bus; - info->devfn = devfn; - info->dev = NULL; - info->domain = domain; - /* This domain is shared by devices under p2p bridge */ - domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES; - - /* pcie-to-pci bridge already has a domain, uses it */ - found = NULL; - spin_lock_irqsave(&device_domain_lock, flags); - list_for_each_entry(tmp, &device_domain_list, global) { - if (tmp->segment == segment && - tmp->bus == bus && tmp->devfn == devfn) { - found = tmp->domain; - break; - } - } - if (found) { - spin_unlock_irqrestore(&device_domain_lock, flags); - free_devinfo_mem(info); - domain_exit(domain); - domain = found; - } else { - list_add(&info->link, &domain->devices); - list_add(&info->global, &device_domain_list); - spin_unlock_irqrestore(&device_domain_lock, flags); - } } found_domain: - info = alloc_devinfo_mem(); - if (!info) - goto error; - info->segment = segment; - info->bus = pdev->bus->number; - info->devfn = pdev->devfn; - info->dev = pdev; - info->domain = domain; - spin_lock_irqsave(&device_domain_lock, flags); - /* somebody is fast */ - found = find_domain(pdev); - if (found != NULL) { - spin_unlock_irqrestore(&device_domain_lock, flags); - if (found != domain) { - domain_exit(domain); - domain = found; - } - free_devinfo_mem(info); + if (dmar_insert_dev_info(segment, pdev->bus->number, pdev->devfn, + pdev, &domain) == 0) return domain; - } - list_add(&info->link, &domain->devices); - list_add(&info->global, &device_domain_list); - pdev->dev.archdata.iommu = info; - spin_unlock_irqrestore(&device_domain_lock, flags); - return domain; error: /* recheck it here, maybe others set it */ return find_domain(pdev);