]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
iommu/vt-d: Unify domain->iommu attach/detachment
authorJoerg Roedel <jroedel@suse.de>
Wed, 22 Jul 2015 09:52:53 +0000 (11:52 +0200)
committerJoerg Roedel <jroedel@suse.de>
Wed, 12 Aug 2015 14:23:36 +0000 (16:23 +0200)
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>
drivers/iommu/intel-iommu.c

index 46359bd9da63ccd74710ee335ff15227ba643db6..1cb7a3eb29d3f1db61aac1371974d86f05952e7a 100644 (file)
@@ -1678,90 +1678,64 @@ static struct dmar_domain *alloc_domain(int flags)
        return domain;
 }
 
-static int __iommu_attach_domain(struct dmar_domain *domain,
-                                struct intel_iommu *iommu)
-{
-       int num;
-       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,
+/* Must be called with iommu->lock */
+static int domain_attach_iommu(struct dmar_domain *domain,
                               struct intel_iommu *iommu)
 {
-       int num;
-       unsigned long flags;
-
-       spin_lock_irqsave(&iommu->lock, flags);
-       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 ndomains;
        unsigned long flags;
-       int num;
-
-       spin_lock_irqsave(&iommu->lock, flags);
+       int ret, num;
 
-       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;
+       assert_spin_locked(&iommu->lock);
 
        spin_lock_irqsave(&domain->iommu_lock, flags);
+
        domain->iommu_refcnt[iommu->seq_id] += 1;
        domain->iommu_count += 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);
        }
+
+       ret = 0;
+out_unlock:
        spin_unlock_irqrestore(&domain->iommu_lock, flags);
+
+       return ret;
 }
 
 static int domain_detach_iommu(struct dmar_domain *domain,
                               struct intel_iommu *iommu)
 {
+       int num, count = INT_MAX;
        unsigned long flags;
-       int count = INT_MAX;
+
+       assert_spin_locked(&iommu->lock);
 
        spin_lock_irqsave(&domain->iommu_lock, flags);
        domain->iommu_refcnt[iommu->seq_id] -= 1;
        count = --domain->iommu_count;
        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->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)
 {
        struct page *freelist = NULL;
-       int i;
 
        /* Domain 0 is reserved, so dont process it */
        if (!domain)
@@ -1896,20 +1869,16 @@ static void domain_exit(struct dmar_domain *domain)
        if (!intel_iommu_strict)
                flush_unmaps_timeout(0);
 
-       /* remove associated devices */
+       /* Remove associated devices and clear attached or cached domains */
+       rcu_read_lock();
        domain_remove_dev_info(domain);
+       rcu_read_unlock();
 
        /* destroy iovas */
        put_iova_domain(&domain->iovad);
 
        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);
 
        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 device_domain_info *info;
        unsigned long flags;
+       int ret;
 
        info = alloc_devinfo_mem();
        if (!info)
@@ -2313,11 +2283,14 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
                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);
                return NULL;
        }
-       domain_attach_iommu(domain, iommu);
 
        list_add(&info->link, &domain->devices);
        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);
        domain_context_clear(iommu, dev);
        free_devinfo_mem(info);
-       domain_detach_iommu(domain, iommu);
 
-       spin_lock_irqsave(&domain->iommu_lock, flags);
-       if (!domain->iommu_refcnt[iommu->seq_id])
-               iommu_detach_domain(domain, iommu);
-       spin_unlock_irqrestore(&domain->iommu_lock, flags);
+       spin_lock_irqsave(&iommu->lock, flags);
+       domain_detach_iommu(domain, iommu);
+       spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
 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);
                if (old_domain) {
+                       rcu_read_lock();
                        if (domain_type_is_vm_or_si(dmar_domain))
                                dmar_remove_one_dev_info(old_domain, dev);
                        else
                                domain_remove_dev_info(old_domain);
+                       rcu_read_unlock();
 
                        if (!domain_type_is_vm_or_si(old_domain) &&
                             list_empty(&old_domain->devices))