Commit 71684406 authored by Alex Williamson's avatar Alex Williamson Committed by Joerg Roedel

iommu/vt-d: Detach domain *only* from attached iommus

Device domains never span IOMMU hardware units, which allows the
domain ID space for each IOMMU to be an independent address space.
Therefore we can have multiple, independent domains, each with the
same domain->id, but attached to different hardware units.  This is
also why we need to do a heavy-weight search for VM domains since
they can span multiple IOMMUs hardware units and we don't require a
single global ID to use for all hardware units.

Therefore, if we call iommu_detach_domain() across all active IOMMU
hardware units for a non-VM domain, the result is that we clear domain
IDs that are not associated with our domain, allowing them to be
re-allocated and causing apparent coherency issues when the device
cannot access IOVAs for the intended domain.

This bug was introduced in commit fb170fb4 ("iommu/vt-d: Introduce
helper functions to make code symmetric for readability"), but is
significantly exacerbated by the more recent commit 62c22167
("iommu/vt-d: Fix dmar_domain leak in iommu_attach_device") which calls
domain_exit() more frequently to resolve a domain leak.

Fixes: fb170fb4 ("iommu/vt-d: Introduce helper functions to make code symmetric for readability")
Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: stable@vger.kernel.org # v3.17+
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 83a60ed8
...@@ -1743,8 +1743,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width) ...@@ -1743,8 +1743,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
static void domain_exit(struct dmar_domain *domain) static void domain_exit(struct dmar_domain *domain)
{ {
struct dmar_drhd_unit *drhd; struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
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)
...@@ -1764,8 +1764,8 @@ static void domain_exit(struct dmar_domain *domain) ...@@ -1764,8 +1764,8 @@ static void domain_exit(struct dmar_domain *domain)
/* clear attached or cached domains */ /* clear attached or cached domains */
rcu_read_lock(); rcu_read_lock();
for_each_active_iommu(iommu, drhd) for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus)
iommu_detach_domain(domain, iommu); iommu_detach_domain(domain, g_iommus[i]);
rcu_read_unlock(); rcu_read_unlock();
dma_free_pagelist(freelist); dma_free_pagelist(freelist);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment