iommu/vt-d: Unify domain->iommu attach/detachment

Move the code to attach/detach domains to iommus and vice
verce into a single function to make sure there are no
dangling references.

Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
Joerg Roedel 2015-07-22 11:52:53 +02:00
parent c6c2cebd66
commit d160aca527

View File

@ -1678,90 +1678,64 @@ static struct dmar_domain *alloc_domain(int flags)
return domain; return domain;
} }
static int __iommu_attach_domain(struct dmar_domain *domain, /* Must be called with iommu->lock */
struct intel_iommu *iommu) static int domain_attach_iommu(struct dmar_domain *domain,
struct intel_iommu *iommu)
{ {
int num;
unsigned long ndomains; unsigned long ndomains;
num = domain->iommu_did[iommu->seq_id];
if (num)
return num;
ndomains = cap_ndoms(iommu->cap);
num = find_first_zero_bit(iommu->domain_ids, ndomains);
if (num < ndomains) {
set_bit(num, iommu->domain_ids);
set_iommu_domain(iommu, num, domain);
domain->iommu_did[iommu->seq_id] = num;
} else {
num = -ENOSPC;
}
if (num < 0)
pr_err("%s: No free domain ids\n", iommu->name);
return num;
}
static int iommu_attach_domain(struct dmar_domain *domain,
struct intel_iommu *iommu)
{
int num;
unsigned long flags; unsigned long flags;
int ret, num;
spin_lock_irqsave(&iommu->lock, flags); assert_spin_locked(&iommu->lock);
num = __iommu_attach_domain(domain, iommu);
spin_unlock_irqrestore(&iommu->lock, flags);
return num;
}
static void iommu_detach_domain(struct dmar_domain *domain,
struct intel_iommu *iommu)
{
unsigned long flags;
int num;
spin_lock_irqsave(&iommu->lock, flags);
num = domain->iommu_did[iommu->seq_id];
if (num == 0)
return;
clear_bit(num, iommu->domain_ids);
set_iommu_domain(iommu, num, NULL);
spin_unlock_irqrestore(&iommu->lock, flags);
}
static void domain_attach_iommu(struct dmar_domain *domain,
struct intel_iommu *iommu)
{
unsigned long flags;
spin_lock_irqsave(&domain->iommu_lock, flags); spin_lock_irqsave(&domain->iommu_lock, flags);
domain->iommu_refcnt[iommu->seq_id] += 1; domain->iommu_refcnt[iommu->seq_id] += 1;
domain->iommu_count += 1; domain->iommu_count += 1;
if (domain->iommu_refcnt[iommu->seq_id] == 1) { if (domain->iommu_refcnt[iommu->seq_id] == 1) {
domain->nid = iommu->node; ndomains = cap_ndoms(iommu->cap);
num = find_first_zero_bit(iommu->domain_ids, ndomains);
if (num >= ndomains) {
pr_err("%s: No free domain ids\n", iommu->name);
domain->iommu_refcnt[iommu->seq_id] -= 1;
domain->iommu_count -= 1;
ret = -ENOSPC;
goto out_unlock;
}
set_bit(num, iommu->domain_ids);
set_iommu_domain(iommu, num, domain);
domain->iommu_did[iommu->seq_id] = num;
domain->nid = iommu->node;
domain_update_iommu_cap(domain); domain_update_iommu_cap(domain);
} }
ret = 0;
out_unlock:
spin_unlock_irqrestore(&domain->iommu_lock, flags); spin_unlock_irqrestore(&domain->iommu_lock, flags);
return ret;
} }
static int domain_detach_iommu(struct dmar_domain *domain, static int domain_detach_iommu(struct dmar_domain *domain,
struct intel_iommu *iommu) struct intel_iommu *iommu)
{ {
int num, count = INT_MAX;
unsigned long flags; unsigned long flags;
int count = INT_MAX;
assert_spin_locked(&iommu->lock);
spin_lock_irqsave(&domain->iommu_lock, flags); spin_lock_irqsave(&domain->iommu_lock, flags);
domain->iommu_refcnt[iommu->seq_id] -= 1; domain->iommu_refcnt[iommu->seq_id] -= 1;
count = --domain->iommu_count; count = --domain->iommu_count;
if (domain->iommu_refcnt[iommu->seq_id] == 0) { if (domain->iommu_refcnt[iommu->seq_id] == 0) {
num = domain->iommu_did[iommu->seq_id];
clear_bit(num, iommu->domain_ids);
set_iommu_domain(iommu, num, NULL);
domain_update_iommu_cap(domain); domain_update_iommu_cap(domain);
domain->iommu_did[iommu->seq_id] = 0; domain->iommu_did[iommu->seq_id] = 0;
} }
@ -1886,7 +1860,6 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu,
static void domain_exit(struct dmar_domain *domain) static void domain_exit(struct dmar_domain *domain)
{ {
struct page *freelist = NULL; struct page *freelist = NULL;
int i;
/* Domain 0 is reserved, so dont process it */ /* Domain 0 is reserved, so dont process it */
if (!domain) if (!domain)
@ -1896,20 +1869,16 @@ static void domain_exit(struct dmar_domain *domain)
if (!intel_iommu_strict) if (!intel_iommu_strict)
flush_unmaps_timeout(0); flush_unmaps_timeout(0);
/* remove associated devices */ /* Remove associated devices and clear attached or cached domains */
rcu_read_lock();
domain_remove_dev_info(domain); domain_remove_dev_info(domain);
rcu_read_unlock();
/* destroy iovas */ /* destroy iovas */
put_iova_domain(&domain->iovad); put_iova_domain(&domain->iovad);
freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw)); freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
/* clear attached or cached domains */
rcu_read_lock();
for_each_domain_iommu(i, domain)
iommu_detach_domain(domain, g_iommus[i]);
rcu_read_unlock();
dma_free_pagelist(freelist); dma_free_pagelist(freelist);
free_domain_mem(domain); free_domain_mem(domain);
@ -2286,6 +2255,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
struct dmar_domain *found = NULL; struct dmar_domain *found = NULL;
struct device_domain_info *info; struct device_domain_info *info;
unsigned long flags; unsigned long flags;
int ret;
info = alloc_devinfo_mem(); info = alloc_devinfo_mem();
if (!info) if (!info)
@ -2313,11 +2283,14 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
return found; return found;
} }
if (iommu_attach_domain(domain, iommu) < 0) { spin_lock(&iommu->lock);
ret = domain_attach_iommu(domain, iommu);
spin_unlock(&iommu->lock);
if (ret) {
spin_unlock_irqrestore(&device_domain_lock, flags); spin_unlock_irqrestore(&device_domain_lock, flags);
return NULL; return NULL;
} }
domain_attach_iommu(domain, iommu);
list_add(&info->link, &domain->devices); list_add(&info->link, &domain->devices);
list_add(&info->global, &device_domain_list); list_add(&info->global, &device_domain_list);
@ -4590,12 +4563,10 @@ static void dmar_remove_one_dev_info(struct dmar_domain *domain,
iommu_disable_dev_iotlb(info); iommu_disable_dev_iotlb(info);
domain_context_clear(iommu, dev); domain_context_clear(iommu, dev);
free_devinfo_mem(info); free_devinfo_mem(info);
domain_detach_iommu(domain, iommu);
spin_lock_irqsave(&domain->iommu_lock, flags); spin_lock_irqsave(&iommu->lock, flags);
if (!domain->iommu_refcnt[iommu->seq_id]) domain_detach_iommu(domain, iommu);
iommu_detach_domain(domain, iommu); spin_unlock_irqrestore(&iommu->lock, flags);
spin_unlock_irqrestore(&domain->iommu_lock, flags);
} }
static int md_domain_init(struct dmar_domain *domain, int guest_width) static int md_domain_init(struct dmar_domain *domain, int guest_width)
@ -4676,10 +4647,12 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
old_domain = find_domain(dev); old_domain = find_domain(dev);
if (old_domain) { if (old_domain) {
rcu_read_lock();
if (domain_type_is_vm_or_si(dmar_domain)) if (domain_type_is_vm_or_si(dmar_domain))
dmar_remove_one_dev_info(old_domain, dev); dmar_remove_one_dev_info(old_domain, dev);
else else
domain_remove_dev_info(old_domain); domain_remove_dev_info(old_domain);
rcu_read_unlock();
if (!domain_type_is_vm_or_si(old_domain) && if (!domain_type_is_vm_or_si(old_domain) &&
list_empty(&old_domain->devices)) list_empty(&old_domain->devices))