Skip to content

Commit

Permalink
iommu/vt-d: Simplify function get_domain_for_dev()
Browse files Browse the repository at this point in the history
Function get_domain_for_dev() is a little complex, simplify it
by factoring out dmar_search_domain_by_dev_info() and
dmar_insert_dev_info().

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: Joerg Roedel <joro@8bytes.org>
  • Loading branch information
Jiang Liu authored and joergroedel committed Mar 4, 2014
1 parent b94e411 commit 745f258
Showing 1 changed file with 66 additions and 76 deletions.
142 changes: 66 additions & 76 deletions drivers/iommu/intel-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -1986,109 +2039,46 @@ 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;
}

/* 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);
Expand Down

0 comments on commit 745f258

Please sign in to comment.