Now that we have userspace memory allocation, I wanted to play with ballooning.
The idea is that when a guest "balloons" down, we simply unpin the underlying
physical memory and the host kernel may or may not swap it.  To reclaim
ballooned memory, the guest can just start using it and we'll pin it on demand.

The following patch is a stab at providing the right infrastructure for pinning
and automatic repinning.  I don't have a lot of comfort in the MMU code so I
thought I'd get some feedback before going much further.

gpa_to_hpa is a little awkward to hook, but it seems like the right place in the
code.  I'm most uncertain about the SMP safety of the unpinning.  Presumably,
I have to hold the kvm lock around the mmu_unshadow and page_cache release to
ensure that another VCPU doesn't fault the page back in after mmu_unshadow?

Feedback would be greatly appreciated!

diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
index 4a52d6e..8abe770 100644
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -409,6 +409,7 @@ struct kvm_memory_slot {
        unsigned long *rmap;
        unsigned long *dirty_bitmap;
        int user_alloc; /* user allocated memory */
+       unsigned long userspace_addr;
 };
 
 struct kvm {
@@ -652,6 +653,7 @@ int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, 
gva_t gva);
 void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
 int kvm_mmu_load(struct kvm_vcpu *vcpu);
 void kvm_mmu_unload(struct kvm_vcpu *vcpu);
+int kvm_mmu_unpin(struct kvm *kvm, gfn_t gfn);
 
 int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
 
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index a0f8366..74105d1 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -774,6 +774,7 @@ static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
                        unsigned long pages_num;
 
                        new.user_alloc = 1;
+                       new.userspace_addr = mem->userspace_addr;
                        down_read(&current->mm->mmap_sem);
 
                        pages_num = get_user_pages(current, current->mm,
@@ -1049,12 +1050,36 @@ struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, 
gfn_t gfn)
 struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
 {
        struct kvm_memory_slot *slot;
+       struct page *page;
+       uint64_t slot_index;
 
        gfn = unalias_gfn(kvm, gfn);
        slot = __gfn_to_memslot(kvm, gfn);
        if (!slot)
                return NULL;
-       return slot->phys_mem[gfn - slot->base_gfn];
+
+       slot_index = gfn - slot->base_gfn;
+       page = slot->phys_mem[slot_index];
+       if (unlikely(page == NULL)) {
+               unsigned long pages_num;
+
+               down_read(&current->mm->mmap_sem);
+
+               pages_num = get_user_pages(current, current->mm,
+                                          slot->userspace_addr +
+                                          (slot_index << PAGE_SHIFT),
+                                          1, 1, 0, &slot->phys_mem[slot_index],
+                                          NULL);
+
+               up_read(&current->mm->mmap_sem);
+
+               if (pages_num != 1)
+                       page = NULL;
+               else
+                       page = slot->phys_mem[slot_index];
+       }
+
+       return page;
 }
 EXPORT_SYMBOL_GPL(gfn_to_page);
 
diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c
index f52604a..1820816 100644
--- a/drivers/kvm/mmu.c
+++ b/drivers/kvm/mmu.c
@@ -25,6 +25,7 @@
 #include <linux/mm.h>
 #include <linux/highmem.h>
 #include <linux/module.h>
+#include <linux/pagemap.h>
 
 #include <asm/page.h>
 #include <asm/cmpxchg.h>
@@ -820,6 +821,33 @@ static void mmu_unshadow(struct kvm *kvm, gfn_t gfn)
        }
 }
 
+int kvm_mmu_unpin(struct kvm *kvm, gfn_t gfn)
+{
+       struct kvm_memory_slot *slot;
+       struct page *page;
+
+       /* FIXME for each active vcpu */
+
+       gfn = unalias_gfn(kvm, gfn);
+       slot = gfn_to_memslot(kvm, gfn);
+       if (!gfn)
+               return -EINVAL;
+
+       /* FIXME: do we need to hold a lock here? */
+
+       /* Remove page from shadow MMU and unpin page */
+       mmu_unshadow(kvm, gfn);
+       page = slot->phys_mem[gfn - slot->base_gfn];
+       if (page) {
+               if (!PageReserved(page))
+                       SetPageDirty(page);
+               page_cache_release(page);
+               slot->phys_mem[gfn - slot->base_gfn] = NULL;
+       }
+
+       return 0;
+}
+
 static void page_header_update_slot(struct kvm *kvm, void *pte, gpa_t gpa)
 {
        int slot = memslot_id(kvm, gfn_to_memslot(kvm, gpa >> PAGE_SHIFT));

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to