KVM: Allocate userspace memory for older userspace
authorAnthony Liguori <aliguori@us.ibm.com>
Thu, 18 Oct 2007 14:59:34 +0000 (09:59 -0500)
committerAvi Kivity <avi@qumranet.com>
Wed, 30 Jan 2008 15:52:54 +0000 (17:52 +0200)
Allocate a userspace buffer for older userspaces.  Also eliminate phys_mem
buffer.  The memset() in kvmctl really kills initial memory usage but swapping
works even with old userspaces.

A side effect is that maximum guest side is reduced for older userspace on
i386.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
drivers/kvm/kvm.h
drivers/kvm/kvm_main.c

index e8a21e8..eb006ed 100644 (file)
@@ -406,10 +406,8 @@ struct kvm_memory_slot {
        gfn_t base_gfn;
        unsigned long npages;
        unsigned long flags;
-       struct page **phys_mem;
        unsigned long *rmap;
        unsigned long *dirty_bitmap;
-       int user_alloc; /* user allocated memory */
        unsigned long userspace_addr;
 };
 
index f86a47c..8f6c21d 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/profile.h>
 #include <linux/kvm_para.h>
 #include <linux/pagemap.h>
+#include <linux/mman.h>
 
 #include <asm/processor.h>
 #include <asm/msr.h>
@@ -300,36 +301,21 @@ static struct kvm *kvm_create_vm(void)
        return kvm;
 }
 
-static void kvm_free_kernel_physmem(struct kvm_memory_slot *free)
-{
-       int i;
-
-       for (i = 0; i < free->npages; ++i)
-               if (free->phys_mem[i])
-                       __free_page(free->phys_mem[i]);
-}
-
 /*
  * Free any memory in @free but not in @dont.
  */
 static void kvm_free_physmem_slot(struct kvm_memory_slot *free,
                                  struct kvm_memory_slot *dont)
 {
-       if (!dont || free->phys_mem != dont->phys_mem)
-               if (free->phys_mem) {
-                       if (!free->user_alloc)
-                               kvm_free_kernel_physmem(free);
-                       vfree(free->phys_mem);
-               }
        if (!dont || free->rmap != dont->rmap)
                vfree(free->rmap);
 
        if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
                vfree(free->dirty_bitmap);
 
-       free->phys_mem = NULL;
        free->npages = 0;
        free->dirty_bitmap = NULL;
+       free->rmap = NULL;
 }
 
 static void kvm_free_physmem(struct kvm *kvm)
@@ -712,10 +698,6 @@ static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
                        goto out_unlock;
        }
 
-       /* Deallocate if slot is being removed */
-       if (!npages)
-               new.phys_mem = NULL;
-
        /* Free page dirty bitmap if unneeded */
        if (!(new.flags & KVM_MEM_LOG_DIRTY_PAGES))
                new.dirty_bitmap = NULL;
@@ -723,29 +705,27 @@ static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
        r = -ENOMEM;
 
        /* Allocate if a slot is being created */
-       if (npages && !new.phys_mem) {
-               new.phys_mem = vmalloc(npages * sizeof(struct page *));
-
-               if (!new.phys_mem)
-                       goto out_unlock;
-
+       if (npages && !new.rmap) {
                new.rmap = vmalloc(npages * sizeof(struct page *));
 
                if (!new.rmap)
                        goto out_unlock;
 
-               memset(new.phys_mem, 0, npages * sizeof(struct page *));
                memset(new.rmap, 0, npages * sizeof(*new.rmap));
-               if (user_alloc) {
-                       new.user_alloc = 1;
+
+               if (user_alloc)
                        new.userspace_addr = mem->userspace_addr;
-               } else {
-                       for (i = 0; i < npages; ++i) {
-                               new.phys_mem[i] = alloc_page(GFP_HIGHUSER
-                                                            | __GFP_ZERO);
-                               if (!new.phys_mem[i])
-                                       goto out_unlock;
-                       }
+               else {
+                       down_write(&current->mm->mmap_sem);
+                       new.userspace_addr = do_mmap(NULL, 0,
+                                                    npages * PAGE_SIZE,
+                                                    PROT_READ | PROT_WRITE,
+                                                    MAP_SHARED | MAP_ANONYMOUS,
+                                                    0);
+                       up_write(&current->mm->mmap_sem);
+
+                       if (IS_ERR((void *)new.userspace_addr))
+                               goto out_unlock;
                }
        }
 
@@ -1010,6 +990,8 @@ 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[1];
+       int npages;
 
        gfn = unalias_gfn(kvm, gfn);
        slot = __gfn_to_memslot(kvm, gfn);
@@ -1017,24 +999,19 @@ struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
                get_page(bad_page);
                return bad_page;
        }
-       if (slot->user_alloc) {
-               struct page *page[1];
-               int npages;
-
-               down_read(&current->mm->mmap_sem);
-               npages = get_user_pages(current, current->mm,
-                                       slot->userspace_addr
-                                       + (gfn - slot->base_gfn) * PAGE_SIZE, 1,
-                                       1, 1, page, NULL);
-               up_read(&current->mm->mmap_sem);
-               if (npages != 1) {
-                       get_page(bad_page);
-                       return bad_page;
-               }
-               return page[0];
+
+       down_read(&current->mm->mmap_sem);
+       npages = get_user_pages(current, current->mm,
+                               slot->userspace_addr
+                               + (gfn - slot->base_gfn) * PAGE_SIZE, 1,
+                               1, 1, page, NULL);
+       up_read(&current->mm->mmap_sem);
+       if (npages != 1) {
+               get_page(bad_page);
+               return bad_page;
        }
-       get_page(slot->phys_mem[gfn - slot->base_gfn]);
-       return slot->phys_mem[gfn - slot->base_gfn];
+
+       return page[0];
 }
 EXPORT_SYMBOL_GPL(gfn_to_page);