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:
parent
c6c2cebd66
commit
d160aca527
|
@ -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))
|
||||||
|
|
Loading…
Reference in a new issue