summaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2012-04-17 21:46:44 -0600
committerMarcelo Tosatti <mtosatti@redhat.com>2012-04-19 00:04:18 -0300
commit21a1416a1c945c5aeaeaf791b63c64926018eb77 (patch)
tree4a6ac6aae66648bba9de57902ec98dfa5ae9b98d /virt
parent2225fd56049643c1a7d645c0ce9d499d43c7974e (diff)
downloadlinux-stable-21a1416a1c945c5aeaeaf791b63c64926018eb77.tar.gz
linux-stable-21a1416a1c945c5aeaeaf791b63c64926018eb77.tar.bz2
linux-stable-21a1416a1c945c5aeaeaf791b63c64926018eb77.zip
KVM: lock slots_lock around device assignment
As pointed out by Jason Baron, when assigning a device to a guest we first set the iommu domain pointer, which enables mapping and unmapping of memory slots to the iommu. This leaves a window where this path is enabled, but we haven't synchronized the iommu mappings to the existing memory slots. Thus a slot being removed at that point could send us down unexpected code paths removing non-existent pinnings and iommu mappings. Take the slots_lock around creating the iommu domain and initial mappings as well as around iommu teardown to avoid this race. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/iommu.c23
1 files changed, 15 insertions, 8 deletions
diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c
index fec1723de9b4..e9fff9830bf0 100644
--- a/virt/kvm/iommu.c
+++ b/virt/kvm/iommu.c
@@ -240,9 +240,13 @@ int kvm_iommu_map_guest(struct kvm *kvm)
return -ENODEV;
}
+ mutex_lock(&kvm->slots_lock);
+
kvm->arch.iommu_domain = iommu_domain_alloc(&pci_bus_type);
- if (!kvm->arch.iommu_domain)
- return -ENOMEM;
+ if (!kvm->arch.iommu_domain) {
+ r = -ENOMEM;
+ goto out_unlock;
+ }
if (!allow_unsafe_assigned_interrupts &&
!iommu_domain_has_cap(kvm->arch.iommu_domain,
@@ -253,17 +257,16 @@ int kvm_iommu_map_guest(struct kvm *kvm)
" module option.\n", __func__);
iommu_domain_free(kvm->arch.iommu_domain);
kvm->arch.iommu_domain = NULL;
- return -EPERM;
+ r = -EPERM;
+ goto out_unlock;
}
r = kvm_iommu_map_memslots(kvm);
if (r)
- goto out_unmap;
-
- return 0;
+ kvm_iommu_unmap_memslots(kvm);
-out_unmap:
- kvm_iommu_unmap_memslots(kvm);
+out_unlock:
+ mutex_unlock(&kvm->slots_lock);
return r;
}
@@ -340,7 +343,11 @@ int kvm_iommu_unmap_guest(struct kvm *kvm)
if (!domain)
return 0;
+ mutex_lock(&kvm->slots_lock);
kvm_iommu_unmap_memslots(kvm);
+ kvm->arch.iommu_domain = NULL;
+ mutex_unlock(&kvm->slots_lock);
+
iommu_domain_free(domain);
return 0;
}