iommu/vt-d: Stop dmar_insert_dev_info() freeing domains on losing race

By moving this into get_domain_for_dev() we can make dmar_insert_dev_info()
suitable for use with "special" domains such as the si_domain, which
currently use domain_add_dev_info().

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
David Woodhouse 2014-03-09 13:11:33 -07:00
parent 64ae892bfe
commit b718cd3d84

View file

@ -2147,16 +2147,17 @@ dmar_search_domain_by_dev_info(int segment, int bus, int devfn)
return NULL; return NULL;
} }
static int dmar_insert_dev_info(int segment, int bus, int devfn, static struct dmar_domain *dmar_insert_dev_info(int segment, int bus, int devfn,
struct device *dev, struct dmar_domain **domp) struct device *dev,
struct dmar_domain *domain)
{ {
struct dmar_domain *found, *domain = *domp; struct dmar_domain *found;
struct device_domain_info *info; struct device_domain_info *info;
unsigned long flags; unsigned long flags;
info = alloc_devinfo_mem(); info = alloc_devinfo_mem();
if (!info) if (!info)
return -ENOMEM; return NULL;
info->segment = segment; info->segment = segment;
info->bus = bus; info->bus = bus;
@ -2174,19 +2175,17 @@ static int dmar_insert_dev_info(int segment, int bus, int devfn,
if (found) { if (found) {
spin_unlock_irqrestore(&device_domain_lock, flags); spin_unlock_irqrestore(&device_domain_lock, flags);
free_devinfo_mem(info); free_devinfo_mem(info);
if (found != domain) { /* Caller must free the original domain */
domain_exit(domain); return found;
*domp = found;
}
} else {
list_add(&info->link, &domain->devices);
list_add(&info->global, &device_domain_list);
if (dev)
dev->archdata.iommu = info;
spin_unlock_irqrestore(&device_domain_lock, flags);
} }
return 0; list_add(&info->link, &domain->devices);
list_add(&info->global, &device_domain_list);
if (dev)
dev->archdata.iommu = info;
spin_unlock_irqrestore(&device_domain_lock, flags);
return domain;
} }
/* domain is initialized */ /* domain is initialized */
@ -2245,21 +2244,19 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
/* register pcie-to-pci device */ /* register pcie-to-pci device */
if (dev_tmp) { if (dev_tmp) {
if (dmar_insert_dev_info(segment, bus, devfn, NULL, &domain)) domain = dmar_insert_dev_info(segment, bus, devfn, NULL, domain);
if (!domain)
goto error; goto error;
else
free = NULL;
} }
found_domain: found_domain:
if (dmar_insert_dev_info(segment, pdev->bus->number, pdev->devfn, domain = dmar_insert_dev_info(segment, pdev->bus->number, pdev->devfn,
&pdev->dev, &domain) == 0) &pdev->dev, domain);
return domain;
error: error:
if (free) if (free != domain)
domain_exit(free); domain_exit(free);
/* recheck it here, maybe others set it */
return find_domain(&pdev->dev); return domain;
} }
static int iommu_identity_mapping; static int iommu_identity_mapping;