Skip to contentSkip to conversation

replace the slots lock from the mmap_sem to private kvm lock

PATCH In review Izik Eidus 2 participants 3 messages Feb 11, 2008

Subsystems (3)
lore
Share & tools
lore

Izik Eidus

author
#0 lore raw

PATCH 1/1 replace the slots lock from the mmap_sem to private kvm lock

right now kvm take the down_read(mmap_sem) lock to make sure that no slots will be removed while trying to get them / make the dirty log safe,
but in some cases when the down_read(mmap_sem) is called and after that kvm_read_guest() is called copy_from_user can result in a page fault that will lead to recursivly taking the mmap_sem,

this patch remove the mmap_sem, with new kvm private lock.

Signature · 1 line
woof.
Quoted 1 line

From 17ab59f76968731a1dc1067fc07d637b5c52e3da Mon Sep 17 00:00:00 2001

From: Izik Eidus <[email protected]>
Date: Sun, 10 Feb 2008 18:04:15 +0200
Subject: [PATCH] KVM: remove the usage of the mmap_sem for the protection of the memory slots.

this patch replace the mmap_sem lock for the memory slots with a new kvm private lock,
it is needed beacuse untill now there were cases where the down_read for the mmap_sem can be called recursivly.
(for example when doing copy_to_user() unpresent memory that will result in pagefault)

---
 arch/x86/kvm/mmu.c         |   24 +++++++++++++---
 arch/x86/kvm/paging_tmpl.h |   13 ++++++---
 arch/x86/kvm/vmx.c         |    7 +++-
 arch/x86/kvm/x86.c         |   65 +++++++++++++++++++++++++-------------------
 include/linux/kvm_host.h   |    1 +
 virt/kvm/kvm_main.c        |    7 +++-
 6 files changed, 76 insertions(+), 41 deletions(-)
arch/x86/kvm/mmu.c +19 -5
index 635e70c..461a8de 100644
@@ -868,11 +868,18 @@ static void page_header_update_slot(struct kvm *kvm, void *pte, gfn_t gfn)
 
 struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva)
 {
+	struct page *page;
+
 	gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gva);
 
 	if (gpa == UNMAPPED_GVA)
 		return NULL;
-	return gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
+
+	down_read(&current->mm->mmap_sem);
+	page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
+	up_read(&current->mm->mmap_sem);
+
+	return page;
 }
 
 static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
@@ -1005,13 +1012,16 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
 
 	struct page *page;
 
+	down_read(&vcpu->kvm->slots_lock);
+
 	down_read(&current->mm->mmap_sem);
 	page = gfn_to_page(vcpu->kvm, gfn);
+	up_read(&current->mm->mmap_sem);
 
 	/* mmio */
 	if (is_error_page(page)) {
 		kvm_release_page_clean(page);
-		up_read(&current->mm->mmap_sem);
+		up_read(&vcpu->kvm->slots_lock);
 		return 1;
 	}
 
@@ -1020,7 +1030,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
 	r = __nonpaging_map(vcpu, v, write, gfn, page);
 	spin_unlock(&vcpu->kvm->mmu_lock);
 
-	up_read(&current->mm->mmap_sem);
+	up_read(&vcpu->kvm->slots_lock);
 
 	return r;
 }
@@ -1381,7 +1391,11 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
 	if (!is_present_pte(gpte))
 		return;
 	gfn = (gpte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
+
+	down_read(&current->mm->mmap_sem);
 	page = gfn_to_page(vcpu->kvm, gfn);
+	up_read(&current->mm->mmap_sem);
+
 	if (is_error_page(page)) {
 		kvm_release_page_clean(page);
 		return;
@@ -1503,9 +1517,9 @@ int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva)
 	gpa_t gpa;
 	int r;
 
-	down_read(&current->mm->mmap_sem);
+	down_read(&vcpu->kvm->slots_lock);
 	gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gva);
-	up_read(&current->mm->mmap_sem);
+	up_read(&vcpu->kvm->slots_lock);
 
 	spin_lock(&vcpu->kvm->mmu_lock);
 	r = kvm_mmu_unprotect_page(vcpu->kvm, gpa >> PAGE_SHIFT);
arch/x86/kvm/paging_tmpl.h +9 -4
index b13e823..f58c143 100644
@@ -91,7 +91,10 @@ static bool FNAME(cmpxchg_gpte)(struct kvm *kvm,
 	pt_element_t *table;
 	struct page *page;
 
+	down_read(&current->mm->mmap_sem);
 	page = gfn_to_page(kvm, table_gfn);
+	up_read(&current->mm->mmap_sem);
+
 	table = kmap_atomic(page, KM_USER0);
 
 	ret = CMPXCHG(&table[index], orig_pte, new_pte);
@@ -375,7 +378,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
 	if (r)
 		return r;
 
-	down_read(&current->mm->mmap_sem);
+	down_read(&vcpu->kvm->slots_lock);
 	/*
 	 * Look up the shadow pte for the faulting address.
 	 */
@@ -389,17 +392,19 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
 		pgprintk("%s: guest page fault\n", __FUNCTION__);
 		inject_page_fault(vcpu, addr, walker.error_code);
 		vcpu->arch.last_pt_write_count = 0; /* reset fork detector */
-		up_read(&current->mm->mmap_sem);
+		up_read(&vcpu->kvm->slots_lock);
 		return 0;
 	}
 
+	down_read(&current->mm->mmap_sem);
 	page = gfn_to_page(vcpu->kvm, walker.gfn);
+	up_read(&current->mm->mmap_sem);
 
 	/* mmio */
 	if (is_error_page(page)) {
 		pgprintk("gfn %x is mmio\n", walker.gfn);
 		kvm_release_page_clean(page);
-		up_read(&current->mm->mmap_sem);
+		up_read(&vcpu->kvm->slots_lock);
 		return 1;
 	}
 
@@ -416,7 +421,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
 	++vcpu->stat.pf_fixed;
 	kvm_mmu_audit(vcpu, "post page fault (fixed)");
 	spin_unlock(&vcpu->kvm->mmu_lock);
-	up_read(&current->mm->mmap_sem);
+	up_read(&vcpu->kvm->slots_lock);
 
 	return write_pt;
 }
arch/x86/kvm/vmx.c +5 -2
index 8496dbe..e83c415 100644
@@ -1519,7 +1519,7 @@ static int alloc_apic_access_page(struct kvm *kvm)
 	struct kvm_userspace_memory_region kvm_userspace_mem;
 	int r = 0;
 
-	down_write(&current->mm->mmap_sem);
+	down_write(&kvm->slots_lock);
 	if (kvm->arch.apic_access_page)
 		goto out;
 	kvm_userspace_mem.slot = APIC_ACCESS_PAGE_PRIVATE_MEMSLOT;
@@ -1529,9 +1529,12 @@ static int alloc_apic_access_page(struct kvm *kvm)
 	r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, 0);
 	if (r)
 		goto out;
+
+	down_read(&current->mm->mmap_sem);
 	kvm->arch.apic_access_page = gfn_to_page(kvm, 0xfee00);
+	up_read(&current->mm->mmap_sem);
 out:
-	up_write(&current->mm->mmap_sem);
+	up_write(&kvm->slots_lock);
 	return r;
 }
 
arch/x86/kvm/x86.c +37 -28

230 lines not on this page · raw message

include/linux/kvm_host.h +1 -0
index a84c912..b90ca36 100644
@@ -109,6 +109,7 @@ struct kvm_memory_slot {
 struct kvm {
 	struct mutex lock; /* protects the vcpus array and APIC accesses */
 	spinlock_t mmu_lock;
+	struct rw_semaphore slots_lock;
 	struct mm_struct *mm; /* userspace tied to this vm */
 	int nmemslots;
 	struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS +
virt/kvm/kvm_main.c +5 -2
index 4da308e..8ea19d5 100644
@@ -169,6 +169,7 @@ static struct kvm *kvm_create_vm(void)
 	kvm_io_bus_init(&kvm->pio_bus);
 	mutex_init(&kvm->lock);
 	kvm_io_bus_init(&kvm->mmio_bus);
+	init_rwsem(&kvm->slots_lock);
 	spin_lock(&kvm_lock);
 	list_add(&kvm->vm_list, &vm_list);
 	spin_unlock(&kvm_lock);
@@ -339,9 +340,9 @@ int kvm_set_memory_region(struct kvm *kvm,
 {
 	int r;
 
-	down_write(&current->mm->mmap_sem);
+	down_write(&kvm->slots_lock);
 	r = __kvm_set_memory_region(kvm, mem, user_alloc);
-	up_write(&current->mm->mmap_sem);
+	up_write(&kvm->slots_lock);
 	return r;
 }
 EXPORT_SYMBOL_GPL(kvm_set_memory_region);
@@ -989,7 +990,9 @@ static int kvm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
 	if (!kvm_is_visible_gfn(kvm, vmf->pgoff))
 		return VM_FAULT_SIGBUS;
+
 	page = gfn_to_page(kvm, vmf->pgoff);
+
 	if (is_error_page(page)) {
 		kvm_release_page_clean(page);
 		return VM_FAULT_SIGBUS;
Signature · 1 line
1.5.3.6

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel

#1 lore raw
Quoted 9 lines · Izik Eidus

right now kvm take the down_read(mmap_sem) lock to make sure that no slots will be
removed while trying to get them / make the dirty log safe, but in some cases when the down_read(mmap_sem) is called and after that kvm_read_guest() is called
copy_from_user can result in a page fault that will lead to recursivly taking the mmap_sem,

this patch remove the mmap_sem, with new kvm private lock.

Hi Izik,

do_mmap/do_munmap requires the mmap_sem in write-mode.

I couldnt find any sequence which grabs mmap_sem and then slots_lock, but it would be nice to verify again that can't happen.

Other than that looks good.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

author
#2 lore raw
Quoted 10 lines · Marcelo Tosatti
Quoted 8 lines · Izik Eidus

right now kvm take the down_read(mmap_sem) lock to make sure that no slots will be
removed while trying to get them / make the dirty log safe, but in some cases when the down_read(mmap_sem) is called and after that kvm_read_guest() is called
copy_from_user can result in a page fault that will lead to recursivly taking the mmap_sem,

Quoted 7 lines
Quoted 2 lines

this patch remove the mmap_sem, with new kvm private lock.

Hi Izik,

do_mmap/do_munmap requires the mmap_sem in write-mode.

right, i missed it

Quoted 3 lines

I couldnt find any sequence which grabs mmap_sem and then slots_lock, but it would be nice to verify again that can't happen.

sure

Quoted 3 lines

Other than that looks good.

thanks

Signature · 7 lines
woof.


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/