diff options
author | Joerg Roedel <jroedel@suse.de> | 2015-07-22 05:52:53 -0400 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2015-08-12 10:23:36 -0400 |
commit | d160aca5276d093fc68d6ff48888586c90309d03 (patch) | |
tree | 027e1adb88553ebcfe1b308cf687564c17d90055 /drivers/iommu | |
parent | c6c2cebd665933216785246a1d15b4112fa74bbf (diff) |
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>
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/intel-iommu.c | 125 |
1 files changed, 49 insertions, 76 deletions
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 46359bd9da63..1cb7a3eb29d3 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
@@ -1678,90 +1678,64 @@ static struct dmar_domain *alloc_domain(int flags) | |||
1678 | return domain; | 1678 | return domain; |
1679 | } | 1679 | } |
1680 | 1680 | ||
1681 | static int __iommu_attach_domain(struct dmar_domain *domain, | 1681 | /* Must be called with iommu->lock */ |
1682 | struct intel_iommu *iommu) | 1682 | static int domain_attach_iommu(struct dmar_domain *domain, |
1683 | { | ||
1684 | int num; | ||
1685 | unsigned long ndomains; | ||
1686 | |||
1687 | num = domain->iommu_did[iommu->seq_id]; | ||
1688 | if (num) | ||
1689 | return num; | ||
1690 | |||
1691 | ndomains = cap_ndoms(iommu->cap); | ||
1692 | num = find_first_zero_bit(iommu->domain_ids, ndomains); | ||
1693 | |||
1694 | if (num < ndomains) { | ||
1695 | set_bit(num, iommu->domain_ids); | ||
1696 | set_iommu_domain(iommu, num, domain); | ||
1697 | domain->iommu_did[iommu->seq_id] = num; | ||
1698 | } else { | ||
1699 | num = -ENOSPC; | ||
1700 | } | ||
1701 | |||
1702 | if (num < 0) | ||
1703 | pr_err("%s: No free domain ids\n", iommu->name); | ||
1704 | |||
1705 | return num; | ||
1706 | } | ||
1707 | |||
1708 | static int iommu_attach_domain(struct dmar_domain *domain, | ||
1709 | struct intel_iommu *iommu) | 1683 | struct intel_iommu *iommu) |
1710 | { | 1684 | { |
1711 | int num; | 1685 | unsigned long ndomains; |
1712 | unsigned long flags; | ||
1713 | |||
1714 | spin_lock_irqsave(&iommu->lock, flags); | ||
1715 | num = __iommu_attach_domain(domain, iommu); | ||
1716 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
1717 | |||
1718 | return num; | ||
1719 | } | ||
1720 | |||
1721 | static void iommu_detach_domain(struct dmar_domain *domain, | ||
1722 | struct intel_iommu *iommu) | ||
1723 | { | ||
1724 | unsigned long flags; | 1686 | unsigned long flags; |
1725 | int num; | 1687 | int ret, num; |
1726 | |||
1727 | spin_lock_irqsave(&iommu->lock, flags); | ||
1728 | 1688 | ||
1729 | num = domain->iommu_did[iommu->seq_id]; | 1689 | assert_spin_locked(&iommu->lock); |
1730 | |||
1731 | if (num == 0) | ||
1732 | return; | ||
1733 | |||
1734 | clear_bit(num, iommu->domain_ids); | ||
1735 | set_iommu_domain(iommu, num, NULL); | ||
1736 | |||
1737 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
1738 | } | ||
1739 | |||
1740 | static void domain_attach_iommu(struct dmar_domain *domain, | ||
1741 | struct intel_iommu *iommu) | ||
1742 | { | ||
1743 | unsigned long flags; | ||
1744 | 1690 | ||
1745 | spin_lock_irqsave(&domain->iommu_lock, flags); | 1691 | spin_lock_irqsave(&domain->iommu_lock, flags); |
1692 | |||
1746 | domain->iommu_refcnt[iommu->seq_id] += 1; | 1693 | domain->iommu_refcnt[iommu->seq_id] += 1; |
1747 | domain->iommu_count += 1; | 1694 | domain->iommu_count += 1; |
1748 | if (domain->iommu_refcnt[iommu->seq_id] == 1) { | 1695 | if (domain->iommu_refcnt[iommu->seq_id] == 1) { |
1749 | domain->nid = iommu->node; | 1696 | ndomains = cap_ndoms(iommu->cap); |
1697 | num = find_first_zero_bit(iommu->domain_ids, ndomains); | ||
1698 | |||
1699 | if (num >= ndomains) { | ||
1700 | pr_err("%s: No free domain ids\n", iommu->name); | ||
1701 | domain->iommu_refcnt[iommu->seq_id] -= 1; | ||
1702 | domain->iommu_count -= 1; | ||
1703 | ret = -ENOSPC; | ||
1704 | goto out_unlock; | ||
1705 | } | ||
1706 | |||
1707 | set_bit(num, iommu->domain_ids); | ||
1708 | set_iommu_domain(iommu, num, domain); | ||
1709 | |||
1710 | domain->iommu_did[iommu->seq_id] = num; | ||
1711 | domain->nid = iommu->node; | ||
1712 | |||
1750 | domain_update_iommu_cap(domain); | 1713 | domain_update_iommu_cap(domain); |
1751 | } | 1714 | } |
1715 | |||
1716 | ret = 0; | ||
1717 | out_unlock: | ||
1752 | spin_unlock_irqrestore(&domain->iommu_lock, flags); | 1718 | spin_unlock_irqrestore(&domain->iommu_lock, flags); |
1719 | |||
1720 | return ret; | ||
1753 | } | 1721 | } |
1754 | 1722 | ||
1755 | static int domain_detach_iommu(struct dmar_domain *domain, | 1723 | static int domain_detach_iommu(struct dmar_domain *domain, |
1756 | struct intel_iommu *iommu) | 1724 | struct intel_iommu *iommu) |
1757 | { | 1725 | { |
1726 | int num, count = INT_MAX; | ||
1758 | unsigned long flags; | 1727 | unsigned long flags; |
1759 | int count = INT_MAX; | 1728 | |
1729 | assert_spin_locked(&iommu->lock); | ||
1760 | 1730 | ||
1761 | spin_lock_irqsave(&domain->iommu_lock, flags); | 1731 | spin_lock_irqsave(&domain->iommu_lock, flags); |
1762 | domain->iommu_refcnt[iommu->seq_id] -= 1; | 1732 | domain->iommu_refcnt[iommu->seq_id] -= 1; |
1763 | count = --domain->iommu_count; | 1733 | count = --domain->iommu_count; |
1764 | if (domain->iommu_refcnt[iommu->seq_id] == 0) { | 1734 | if (domain->iommu_refcnt[iommu->seq_id] == 0) { |
1735 | num = domain->iommu_did[iommu->seq_id]; | ||
1736 | clear_bit(num, iommu->domain_ids); | ||
1737 | set_iommu_domain(iommu, num, NULL); | ||
1738 | |||
1765 | domain_update_iommu_cap(domain); | 1739 | domain_update_iommu_cap(domain); |
1766 | domain->iommu_did[iommu->seq_id] = 0; | 1740 | domain->iommu_did[iommu->seq_id] = 0; |
1767 | } | 1741 | } |
@@ -1886,7 +1860,6 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu, | |||
1886 | static void domain_exit(struct dmar_domain *domain) | 1860 | static void domain_exit(struct dmar_domain *domain) |
1887 | { | 1861 | { |
1888 | struct page *freelist = NULL; | 1862 | struct page *freelist = NULL; |
1889 | int i; | ||
1890 | 1863 | ||
1891 | /* Domain 0 is reserved, so dont process it */ | 1864 | /* Domain 0 is reserved, so dont process it */ |
1892 | if (!domain) | 1865 | if (!domain) |
@@ -1896,20 +1869,16 @@ static void domain_exit(struct dmar_domain *domain) | |||
1896 | if (!intel_iommu_strict) | 1869 | if (!intel_iommu_strict) |
1897 | flush_unmaps_timeout(0); | 1870 | flush_unmaps_timeout(0); |
1898 | 1871 | ||
1899 | /* remove associated devices */ | 1872 | /* Remove associated devices and clear attached or cached domains */ |
1873 | rcu_read_lock(); | ||
1900 | domain_remove_dev_info(domain); | 1874 | domain_remove_dev_info(domain); |
1875 | rcu_read_unlock(); | ||
1901 | 1876 | ||
1902 | /* destroy iovas */ | 1877 | /* destroy iovas */ |
1903 | put_iova_domain(&domain->iovad); | 1878 | put_iova_domain(&domain->iovad); |
1904 | 1879 | ||
1905 | freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw)); | 1880 | freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw)); |
1906 | 1881 | ||
1907 | /* clear attached or cached domains */ | ||
1908 | rcu_read_lock(); | ||
1909 | for_each_domain_iommu(i, domain) | ||
1910 | iommu_detach_domain(domain, g_iommus[i]); | ||
1911 | rcu_read_unlock(); | ||
1912 | |||
1913 | dma_free_pagelist(freelist); | 1882 | dma_free_pagelist(freelist); |
1914 | 1883 | ||
1915 | free_domain_mem(domain); | 1884 | free_domain_mem(domain); |
@@ -2286,6 +2255,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, | |||
2286 | struct dmar_domain *found = NULL; | 2255 | struct dmar_domain *found = NULL; |
2287 | struct device_domain_info *info; | 2256 | struct device_domain_info *info; |
2288 | unsigned long flags; | 2257 | unsigned long flags; |
2258 | int ret; | ||
2289 | 2259 | ||
2290 | info = alloc_devinfo_mem(); | 2260 | info = alloc_devinfo_mem(); |
2291 | if (!info) | 2261 | if (!info) |
@@ -2313,11 +2283,14 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, | |||
2313 | return found; | 2283 | return found; |
2314 | } | 2284 | } |
2315 | 2285 | ||
2316 | if (iommu_attach_domain(domain, iommu) < 0) { | 2286 | spin_lock(&iommu->lock); |
2287 | ret = domain_attach_iommu(domain, iommu); | ||
2288 | spin_unlock(&iommu->lock); | ||
2289 | |||
2290 | if (ret) { | ||
2317 | spin_unlock_irqrestore(&device_domain_lock, flags); | 2291 | spin_unlock_irqrestore(&device_domain_lock, flags); |
2318 | return NULL; | 2292 | return NULL; |
2319 | } | 2293 | } |
2320 | domain_attach_iommu(domain, iommu); | ||
2321 | 2294 | ||
2322 | list_add(&info->link, &domain->devices); | 2295 | list_add(&info->link, &domain->devices); |
2323 | list_add(&info->global, &device_domain_list); | 2296 | list_add(&info->global, &device_domain_list); |
@@ -4590,12 +4563,10 @@ static void dmar_remove_one_dev_info(struct dmar_domain *domain, | |||
4590 | iommu_disable_dev_iotlb(info); | 4563 | iommu_disable_dev_iotlb(info); |
4591 | domain_context_clear(iommu, dev); | 4564 | domain_context_clear(iommu, dev); |
4592 | free_devinfo_mem(info); | 4565 | free_devinfo_mem(info); |
4593 | domain_detach_iommu(domain, iommu); | ||
4594 | 4566 | ||
4595 | spin_lock_irqsave(&domain->iommu_lock, flags); | 4567 | spin_lock_irqsave(&iommu->lock, flags); |
4596 | if (!domain->iommu_refcnt[iommu->seq_id]) | 4568 | domain_detach_iommu(domain, iommu); |
4597 | iommu_detach_domain(domain, iommu); | 4569 | spin_unlock_irqrestore(&iommu->lock, flags); |
4598 | spin_unlock_irqrestore(&domain->iommu_lock, flags); | ||
4599 | } | 4570 | } |
4600 | 4571 | ||
4601 | static int md_domain_init(struct dmar_domain *domain, int guest_width) | 4572 | 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, | |||
4676 | 4647 | ||
4677 | old_domain = find_domain(dev); | 4648 | old_domain = find_domain(dev); |
4678 | if (old_domain) { | 4649 | if (old_domain) { |
4650 | rcu_read_lock(); | ||
4679 | if (domain_type_is_vm_or_si(dmar_domain)) | 4651 | if (domain_type_is_vm_or_si(dmar_domain)) |
4680 | dmar_remove_one_dev_info(old_domain, dev); | 4652 | dmar_remove_one_dev_info(old_domain, dev); |
4681 | else | 4653 | else |
4682 | domain_remove_dev_info(old_domain); | 4654 | domain_remove_dev_info(old_domain); |
4655 | rcu_read_unlock(); | ||
4683 | 4656 | ||
4684 | if (!domain_type_is_vm_or_si(old_domain) && | 4657 | if (!domain_type_is_vm_or_si(old_domain) && |
4685 | list_empty(&old_domain->devices)) | 4658 | list_empty(&old_domain->devices)) |