[kernel] Fix KVM device assignment page leak

Justin M. Forbes jforbes at fedoraproject.org
Thu Apr 19 14:45:58 UTC 2012


commit 7d3a78564ab43271375c063fe37bd9bdd0296d37
Author: Justin M. Forbes <jforbes at redhat.com>
Date:   Thu Apr 19 09:47:12 2012 -0500

    Fix KVM device assignment page leak

 ...ges-from-the-iommu-when-slots-are-removed.patch |   92 ++++++++++++++++++++
 kernel.spec                                        |    9 ++
 2 files changed, 101 insertions(+), 0 deletions(-)
---
diff --git a/KVM-unmap-pages-from-the-iommu-when-slots-are-removed.patch b/KVM-unmap-pages-from-the-iommu-when-slots-are-removed.patch
new file mode 100644
index 0000000..b98e392
--- /dev/null
+++ b/KVM-unmap-pages-from-the-iommu-when-slots-are-removed.patch
@@ -0,0 +1,92 @@
+commit 32f6daad4651a748a58a3ab6da0611862175722f
+Author: Alex Williamson <alex.williamson at redhat.com>
+Date:   Wed Apr 11 09:51:49 2012 -0600
+
+    KVM: unmap pages from the iommu when slots are removed
+    
+    We've been adding new mappings, but not destroying old mappings.
+    This can lead to a page leak as pages are pinned using
+    get_user_pages, but only unpinned with put_page if they still
+    exist in the memslots list on vm shutdown.  A memslot that is
+    destroyed while an iommu domain is enabled for the guest will
+    therefore result in an elevated page reference count that is
+    never cleared.
+    
+    Additionally, without this fix, the iommu is only programmed
+    with the first translation for a gpa.  This can result in
+    peer-to-peer errors if a mapping is destroyed and replaced by a
+    new mapping at the same gpa as the iommu will still be pointing
+    to the original, pinned memory address.
+    
+    Signed-off-by: Alex Williamson <alex.williamson at redhat.com>
+    Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>
+
+diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
+index 665a260..72cbf08 100644
+--- a/include/linux/kvm_host.h
++++ b/include/linux/kvm_host.h
+@@ -596,6 +596,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id);
+
+ #ifdef CONFIG_IOMMU_API
+ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot);
++void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot);
+ int kvm_iommu_map_guest(struct kvm *kvm);
+ int kvm_iommu_unmap_guest(struct kvm *kvm);
+ int kvm_assign_device(struct kvm *kvm,
+@@ -609,6 +610,11 @@ static inline int kvm_iommu_map_pages(struct kvm *kvm,
+ 	return 0;
+ }
+
++static inline void kvm_iommu_unmap_pages(struct kvm *kvm,
++					 struct kvm_memory_slot *slot)
++{
++}
++
+ static inline int kvm_iommu_map_guest(struct kvm *kvm)
+ {
+ 	return -ENODEV;
+diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c
+index a457d21..fec1723 100644
+--- a/virt/kvm/iommu.c
++++ b/virt/kvm/iommu.c
+@@ -310,6 +310,11 @@ static void kvm_iommu_put_pages(struct kvm *kvm,
+ 	}
+ }
+
++void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot)
++{
++	kvm_iommu_put_pages(kvm, slot->base_gfn, slot->npages);
++}
++
+ static int kvm_iommu_unmap_memslots(struct kvm *kvm)
+ {
+ 	int idx;
+@@ -320,7 +325,7 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm)
+ 	slots = kvm_memslots(kvm);
+
+ 	kvm_for_each_memslot(memslot, slots)
+-		kvm_iommu_put_pages(kvm, memslot->base_gfn, memslot->npages);
++		kvm_iommu_unmap_pages(kvm, memslot);
+
+ 	srcu_read_unlock(&kvm->srcu, idx);
+
+diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
+index 42b7393..9739b53 100644
+--- a/virt/kvm/kvm_main.c
++++ b/virt/kvm/kvm_main.c
+@@ -808,12 +808,13 @@ int __kvm_set_memory_region(struct kvm *kvm,
+ 	if (r)
+ 		goto out_free;
+
+-	/* map the pages in iommu page table */
++	/* map/unmap the pages in iommu page table */
+ 	if (npages) {
+ 		r = kvm_iommu_map_pages(kvm, &new);
+ 		if (r)
+ 			goto out_free;
+-	}
++	} else
++		kvm_iommu_unmap_pages(kvm, &old);
+
+ 	r = -ENOMEM;
+ 	slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots),
diff --git a/kernel.spec b/kernel.spec
index 382a397..9d50c7c 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -752,6 +752,9 @@ Patch22000: weird-root-dentry-name-debug.patch
 #selinux ptrace child permissions
 Patch22001: selinux-apply-different-permission-to-ptrace-child.patch
 
+#rhbz 814149 814155
+Patch22006: KVM-unmap-pages-from-the-iommu-when-slots-are-removed.patch
+
 # END OF PATCH DEFINITIONS
 
 %endif
@@ -1453,6 +1456,9 @@ ApplyPatch vgaarb-vga_default_device.patch
 ApplyPatch x86-microcode-Fix-sysfs-warning-during-module-unload-on-unsupported-CPUs.patch
 ApplyPatch x86-microcode-Ensure-that-module-is-only-loaded-for-supported-AMD-CPUs.patch
 
+#rhbz 814149 814155
+ApplyPatch KVM-unmap-pages-from-the-iommu-when-slots-are-removed.patch
+
 # END OF PATCH APPLICATIONS
 
 %endif
@@ -2313,6 +2319,9 @@ fi
 #                 ||----w |
 #                 ||     ||
 %changelog
+* Thu Apr 19 2012 Justin M. Forbes <jforbes at redhat.com>
+- Fix KVM device assignment page leak (rhbz 814149 814155)
+
 * Wed Apr 18 2012 Justin M. Forbes <jforbes at redhat.com> - 3.4.0-0.rc3.git2.1
 - Linux v3.4-rc3-36-g592fe89
 


More information about the scm-commits mailing list