Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Add swapping and pinning capability to Palacios
Daniel Zuo, Nikhat Karimi, Ahalya Srinivasan, Peter Dinda [Sat, 21 Jun 2014 02:08:54 +0000 (21:08 -0500)]
This commit adds allows swapping of VMs at the granularity of
base regions.  Since base regions down to the page granularity are
possible, this swapper can swap down to page granularity as well.

The functionality can be selected at compile time, and the use of
swapping is determined at run time with a new "swapping" block, like
this:

  <mem ... >N_MB</mem>             Size of memory in the GPA

  <swapping enable="y">
     <allocated>M_MB</allocated>   Allocated space (M_MB <= N_MB)
     <file>FILENAME</file>         Where to swap to
     <strategy>STRATEGY</strategy> Victim picker to use NEXT_FIT, RANDOM (default), LRU, DEFAULT
  </swapping>

Pinning is also supported

12 files changed:
Kconfig
palacios/include/interfaces/vmm_file.h
palacios/include/palacios/vm_guest.h
palacios/include/palacios/vmm_mem.h
palacios/include/palacios/vmm_swapping.h [new file with mode: 0644]
palacios/src/palacios/Makefile
palacios/src/palacios/vm_guest.c
palacios/src/palacios/vmm.c
palacios/src/palacios/vmm_config.c
palacios/src/palacios/vmm_mem.c
palacios/src/palacios/vmm_swapping.c [new file with mode: 0644]
v3_config_guest.pl

diff --git a/Kconfig b/Kconfig
index 58a998a..1813178 100644 (file)
--- a/Kconfig
+++ b/Kconfig
@@ -324,6 +324,20 @@ config DEBUG_SHADOW_PAGING_CACHE
 #        help
 #           Enables debugging messages for the KVM-style shadow pager
 
+
+config SWAPPING
+        bool "Enable swapping"
+        default n
+        depends on (SHADOW_PAGING || NESTED_PAGING) && FILE
+        help
+           Enables swapping of regions of guest physical memory to a file 
+
+config DEBUG_SWAPPING
+        bool "Enable swapping debugging"
+       default n
+        depends on SWAPPING
+        help
+           Provides debugging output from the swapping system
 endmenu
 
 menu "Symbiotic Functions"
index 421c73e..83d0f7b 100644 (file)
 #ifndef __VMM_FILE_H__
 #define __VMM_FILE_H__
 
-#include <palacios/vmm.h>
+
 
 
 #ifdef __V3VEE__
+
+#include <palacios/vmm_types.h>
+
+struct v3_vm_info;
+
 typedef void * v3_file_t;
 
 int v3_mkdir(char * path, uint16_t permissions, uint8_t recursive);
index 1819fa8..bc12edc 100644 (file)
@@ -47,6 +47,7 @@
 #include <palacios/vmm_fp.h>
 #include <palacios/vmm_perftune.h>
 
+
 #ifdef V3_CONFIG_TELEMETRY
 #include <palacios/vmm_telemetry.h>
 #endif
@@ -92,6 +93,10 @@ struct guest_info {
     addr_t direct_map_pt;
     // arch-independent state of the nested pager (currently none)
     // struct v3_nested_pg_state nested_pg_state;
+    // per-core state of the swapper (currently none)
+    //#ifdef V3_CONFIG_SWAPPING
+    //   struct v3_swap_impl_state swap_impl;
+    //#endif
     
 
     union {
@@ -189,6 +194,11 @@ struct v3_vm_info {
     struct v3_passthrough_impl_state passthrough_impl;
     // arch-independent state of the nested pager
     struct v3_nested_impl_state nested_impl;
+#ifdef V3_CONFIG_SWAPPING
+    // swapping state, if enabled
+    struct v3_swap_impl_state swap_state;
+#endif
+
     void * sched_priv_data;
 
     struct v3_io_map io_map;
@@ -230,7 +240,6 @@ struct v3_vm_info {
     struct v3_telemetry_state telemetry;
 #endif
 
-
     uint64_t yield_cycle_period;  
 
 
index 7bec3e6..775d9ed 100644 (file)
 struct guest_info;
 struct v3_vm_info;
 
+#ifdef V3_CONFIG_SWAPPING
+#include <palacios/vmm_swapping.h>
+#endif
+
+
 
 
 #define V3_MEM_CORE_ANY ((uint16_t)-1)
@@ -53,6 +58,10 @@ typedef struct {
            uint8_t base   : 1;
            uint8_t alloced : 1;
            uint8_t limit32 : 1; // must be < 4GB in host
+#ifdef V3_CONFIG_SWAPPING 
+           uint8_t swapped : 1;
+           uint8_t pinned : 1;
+#endif 
        } __attribute__((packed));
     } __attribute__((packed));
 } __attribute__((packed)) v3_mem_flags_t;
@@ -75,6 +84,10 @@ struct v3_mem_region {
     int core_id;     // The virtual core this region is assigned to (-1 means all cores)
     int numa_id;     // The NUMA node this region is allocated from
 
+#ifdef V3_CONFIG_SWAPPING 
+    struct v3_swap_region_state swap_state;
+#endif
+
     struct rb_node tree_node; // This for memory regions mapped to the global map
 };
 
@@ -85,7 +98,6 @@ struct v3_mem_map {
     
     uint32_t num_base_regions;
     struct v3_mem_region * base_regions;
-
 };
 
 
diff --git a/palacios/include/palacios/vmm_swapping.h b/palacios/include/palacios/vmm_swapping.h
new file mode 100644 (file)
index 0000000..ea5deb6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National 
+ * Science Foundation and the Department of Energy.  
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico.  You can find out more at 
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2014, The V3VEE Project <http://www.v3vee.org> 
+ * All rights reserved.
+ *
+ * Author: Daniel Zuo <pengzuo2014@u.northwestern.edu>
+ *         Nikhat Karimi <nikhatkarimi@gmail.com>
+ *         Ahalya Srinivasan <AhalyaSrinivasan2015@u.northwestern.edu>
+ *         Peter Dinda <pdinda@northwestern.edu>
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+
+#ifndef __VMM_SWAPPING_H
+#define __VMM_SWAPPING_H
+
+
+#ifdef __V3VEE__ 
+
+#include <palacios/vmm_types.h>
+#include <palacios/vmm_lock.h>
+#include <interfaces/vmm_file.h>
+
+typedef enum {
+    V3_SWAP_NEXT_FIT, 
+    V3_SWAP_RANDOM,
+    V3_SWAP_LRU,     // this is not the droid you're looking for
+} v3_swapping_strategy_t;
+
+// for inclusion in the vm struct
+struct v3_swap_impl_state {
+    // per-VM lock should be held when changing
+    // swap state or the swapping elements of base region state
+    v3_lock_t lock;
+    uint32_t enable_swapping:1;
+    v3_swapping_strategy_t strategy;
+    uint64_t host_mem_size; // allocated space in bytes
+    uint64_t swap_count; 
+    uint64_t last_region_used; // for use by V3_SWAP_NEXT_FIT
+    // This is the swap file on disk 
+    v3_file_t swapfd; 
+};
+
+
+// for inclusion in the region 
+struct v3_swap_region_state {
+    uint64_t last_accessed;  // timestamp
+};
+
+
+struct v3_mem_region;
+
+typedef struct v3_xml v3_cfg_tree_t;
+
+int v3_init_swapping();
+int v3_deinit_swapping();
+
+int v3_init_swapping_vm(struct v3_vm_info *vm, v3_cfg_tree_t *config);
+int v3_deinit_swapping_vm(struct v3_vm_info *vm);
+
+// not needed yet
+//int v3_init_swapping_core(struct guest_info *core);
+//int v3_deinit_swapping_core(struct guest_info *core);
+
+int v3_pin_region(struct v3_vm_info *vm, struct v3_mem_region *region);
+int v3_unpin_region(struct v3_vm_info *vm, struct v3_mem_region *region);
+
+// This will automatically swap out a victim if needed
+int v3_swap_in_region(struct v3_vm_info *vm, struct v3_mem_region *region);
+// Force a region out
+int v3_swap_out_region(struct v3_vm_info *vm, struct v3_mem_region *region);
+
+// drive LRU
+void v3_touch_region(struct v3_vm_info *vm, struct v3_mem_region *region);
+
+#endif /* ! __V3VEE__ */
+
+
+#endif
index 70d5475..2e2571a 100644 (file)
@@ -48,6 +48,8 @@ obj-y := \
        vmm_fw_cfg.o 
 
 
+obj-$(V3_CONFIG_SWAPPING) += vmm_swapping.o
+
 obj-$(V3_CONFIG_XED) +=        vmm_xed.o
 obj-$(V3_CONFIG_V3_DECODER) += vmm_v3dec.o
 obj-$(V3_CONFIG_QUIX86) += vmm_quix86.o
index a12c764..9f7afe8 100644 (file)
@@ -342,6 +342,11 @@ int v3_free_vm_internal(struct v3_vm_info * vm) {
     v3_deinit_time_vm(vm);
 
     v3_deinit_mem_hooks(vm);
+
+#ifdef V3_CONFIG_SWAPPING
+    v3_deinit_swapping_vm(vm);
+#endif
+
     v3_delete_mem_map(vm);
     v3_deinit_shdw_impl(vm);
     v3_deinit_passthrough_paging(vm);
index ac91873..2f1a7bb 100644 (file)
@@ -31,6 +31,7 @@
 #include <palacios/vmm_cpu_mapper.h>
 #include <palacios/vmm_direct_paging.h>
 #include <interfaces/vmm_numa.h>
+#include <interfaces/vmm_file.h>
 
 #ifdef V3_CONFIG_SVM
 #include <palacios/svm.h>
@@ -162,6 +163,10 @@ void Init_V3(struct v3_os_hooks * hooks, char * cpu_mask, int num_cpus, char *op
     // Register all shadow paging handlers
     V3_init_shdw_paging();
 
+#ifdef V3_CONFIG_SWAPPING
+    v3_init_swapping();
+#endif
+
     // Initialize the cpu_mapper framework (must be before extensions)
     V3_init_cpu_mapper();
 
@@ -239,6 +244,10 @@ void Shutdown_V3() {
     V3_deinit_scheduling();
     
     V3_deinit_cpu_mapper();
+
+#ifdef V3_CONFIG_SWAPPING
+    v3_deinit_swapping();
+#endif
     
     V3_deinit_shdw_paging();
     
index e463e32..781754a 100644 (file)
@@ -33,8 +33,9 @@
 #include <palacios/vmm_sprintf.h>
 
 
-
-
+#ifdef V3_CONFIG_SWAPPING
+#include <palacios/vmm_swapping.h>
+#endif
 
 #include <palacios/vmm_host_events.h>
 #include <palacios/vmm_perftune.h>
@@ -52,9 +53,6 @@
 #define COOKIE_V0 "v3vee\0\0\0"
 #define COOKIE_V1 "v3vee\0\0\1"
 
-
-
-
 // This is used to access the configuration file index table
 struct file_hdr_v0 {
     uint32_t index;
@@ -294,13 +292,14 @@ static inline uint32_t get_alignment(char * align_str) {
 }
 
 
+
 static int pre_config_vm(struct v3_vm_info * vm, v3_cfg_tree_t * vm_cfg) {
     char * memory_str = v3_cfg_val(vm_cfg, "memory");
     char * schedule_hz_str = v3_cfg_val(vm_cfg, "schedule_hz");
     char * vm_class = v3_cfg_val(vm_cfg, "class");
     char * align_str = v3_cfg_val(v3_cfg_subtree(vm_cfg, "memory"), "alignment");
     uint32_t sched_hz = 100;   // set the schedule frequency to 100 HZ
-
+   
 
     if (!memory_str) {
        PrintError(VM_NONE, VCORE_NONE, "Memory is a required configuration parameter\n");
@@ -317,8 +316,19 @@ static int pre_config_vm(struct v3_vm_info * vm, v3_cfg_tree_t * vm_cfg) {
     // Amount of ram the Guest will have, always in MB
     vm->mem_size = (addr_t)atoi(memory_str) * 1024 * 1024;
     vm->mem_align = get_alignment(align_str);
-
-
+    
+#ifdef V3_CONFIG_SWAPPING
+    if (v3_init_swapping_vm(vm,vm_cfg)) {
+       PrintError(vm,VCORE_NONE,"Unable to initialize swapping correctly\n");
+       return -1;
+    }
+    if (vm->swap_state.enable_swapping) { 
+       PrintDebug(vm,VCORE_NONE,"Swapping enabled\n");
+    } else {
+       PrintDebug(vm,VCORE_NONE,"Swapping disabled\n");
+    }
+#endif
+        
     PrintDebug(VM_NONE, VCORE_NONE, "Alignment for %lu bytes of memory computed as 0x%x\n", vm->mem_size, vm->mem_align);
 
     if (strcasecmp(vm_class, "PC") == 0) {
index 7bf0019..c975b84 100644 (file)
 
 #include <interfaces/vmm_numa.h>
 
+#ifdef V3_CONFIG_SWAPPING
+#include <palacios/vmm_swapping.h>
+#endif
+
 uint64_t v3_mem_block_size = V3_CONFIG_MEM_BLOCK_SIZE;
 
 
+
+
 struct v3_mem_region * v3_get_base_region(struct v3_vm_info * vm, addr_t gpa) {
+   
+    //PrintDebug(VM_NONE, VCORE_NONE, "get_base_region called"); 
     struct v3_mem_map * map = &(vm->mem_map);
     uint32_t block_index = gpa / v3_mem_block_size;
-
+    struct v3_mem_region *reg;
     if ((gpa >= (map->num_base_regions * v3_mem_block_size)) ||
         (block_index >= map->num_base_regions)) {
         PrintError(vm, VCORE_NONE, "Guest Address Exceeds Base Memory Size (ga=0x%p), (limit=0x%p)\n", 
@@ -45,11 +53,26 @@ struct v3_mem_region * v3_get_base_region(struct v3_vm_info * vm, addr_t gpa) {
         return NULL;
     }
 
+    reg = &(map->base_regions[block_index]);
 
-    return &(map->base_regions[block_index]);
+#ifdef V3_CONFIG_SWAPPING
+    if(vm->swap_state.enable_swapping) {
+       if (reg->flags.swapped) {
+           if (v3_swap_in_region(vm,reg)) { 
+               PrintError(vm, VCORE_NONE, "Unable to swap in region GPA=%p..%p!!!\n",(void*)reg->guest_start,(void*)reg->guest_end);
+               v3_print_mem_map(vm);
+               return NULL;
+           }
+       }
+    }
+    v3_touch_region(vm,reg);
+#endif
+
+    return reg;
 }
 
 
+
 static int mem_offset_hypercall(struct guest_info * info, uint_t hcall_id, void * private_data) {
     /*
     PrintDebug(info->vm_info, info,"V3Vee: Memory offset hypercall (offset=%p)\n", 
@@ -128,7 +151,7 @@ static int will_use_shadow_paging(struct v3_vm_info *vm)
            } else { 
                return 1; // ask for nested, get shadow
            }
-       } else if (strcasecmp(pg_mode, "shadow") != 0) { 
+        } else if (strcasecmp(pg_mode, "shadow") != 0) { 
            return 1;     // ask for shadow, get shadow
        } else {
            return 1;     // ask for something else, get shadow
@@ -136,18 +159,30 @@ static int will_use_shadow_paging(struct v3_vm_info *vm)
     }
 }
 
+#define CEIL_DIV(x,y) (((x)/(y)) + !!((x)%(y)))
 
 int v3_init_mem_map(struct v3_vm_info * vm) {
     struct v3_mem_map * map = &(vm->mem_map);
     addr_t block_pages = v3_mem_block_size >> 12;
     int i = 0;
+    uint64_t num_base_regions_host_mem;
+    map->num_base_regions = CEIL_DIV(vm->mem_size, v3_mem_block_size); 
 
-    map->num_base_regions = (vm->mem_size / v3_mem_block_size) + \
-        ((vm->mem_size % v3_mem_block_size) > 0);
+    num_base_regions_host_mem=map->num_base_regions;  // without swapping
 
+    PrintDebug(VM_NONE, VCORE_NONE, "v3_init_mem_map: num_base_regions:%d",map->num_base_regions);
 
     map->mem_regions.rb_node = NULL;
 
+#ifdef V3_CONFIG_SWAPPING
+    if (vm->swap_state.enable_swapping) {
+        num_base_regions_host_mem = CEIL_DIV(vm->swap_state.host_mem_size, v3_mem_block_size);
+    } 
+#endif
+
+    PrintDebug(VM_NONE, VCORE_NONE, "v3_init_mem_map: %llu base regions will be allocated of %llu base regions in guest\n",
+              (uint64_t)num_base_regions_host_mem, (uint64_t)map->num_base_regions);
+       
     map->base_regions = V3_Malloc(sizeof(struct v3_mem_region) * map->num_base_regions);
 
     if (map->base_regions == NULL) {
@@ -156,13 +191,15 @@ int v3_init_mem_map(struct v3_vm_info * vm) {
     }
 
     memset(map->base_regions, 0, sizeof(struct v3_mem_region) * map->num_base_regions);
-  
 
     for (i = 0; i < map->num_base_regions; i++) {
+  
+
        struct v3_mem_region * region = &(map->base_regions[i]);
        int node_id = -1;
 
        // 2MB page alignment needed for 2MB hardware nested paging
+       // If swapping is enabled, the host memory will be allocated to low address regions at initialization
         region->guest_start = v3_mem_block_size * i;
         region->guest_end = region->guest_start + v3_mem_block_size;
 
@@ -171,22 +208,46 @@ int v3_init_mem_map(struct v3_vm_info * vm) {
         //     and use whatever node the first byte of the block is assigned to
         node_id = gpa_to_node_from_cfg(vm, region->guest_start);
         
-        V3_Print(vm, VCORE_NONE, "Allocating block %d on node %d\n", i, node_id);
-
-       region->host_addr = (addr_t)V3_AllocPagesExtended(block_pages,
-                                                         PAGE_SIZE_4KB,
-                                                         node_id,
-                                                         0); // no constraints 
-                                                            
-        if ((void *)region->host_addr == NULL) { 
-            PrintError(vm, VCORE_NONE, "Could not allocate guest memory\n");
-            return -1;
-        }
 
-       // Clear the memory...
-       memset(V3_VAddr((void *)region->host_addr), 0, v3_mem_block_size);
+       if (i < num_base_regions_host_mem) {
+           //The regions within num_base_regions_in_mem are allocated in host memory
+           V3_Print(vm, VCORE_NONE, "Allocating block %d on node %d\n", i, node_id);
+
+#ifdef V3_CONFIG_SWAPPING
+           // nothing to do - memset will have done it.
+#endif
+    
+           region->host_addr = (addr_t)V3_AllocPagesExtended(block_pages,
+                                                             PAGE_SIZE_4KB,
+                                                             node_id,
+                                                             0); // no constraints 
+           
+           if ((void *)region->host_addr == NULL) { 
+               PrintError(vm, VCORE_NONE, "Could not allocate guest memory\n");
+               return -1;
+           }
+           
+           // Clear the memory...
+           memset(V3_VAddr((void *)region->host_addr), 0, v3_mem_block_size);
+
+       } else {
+
+#ifdef V3_CONFIG_SWAPPING
+            if(vm->swap_state.enable_swapping) {       
+               // The regions beyond num_base_regions_in_mem are allocated on disk to start
+               region->flags.swapped = 1;
+                region->host_addr=(addr_t) 0;
+               // other flags / state correctly set up by zeroing the region earlier
+            }
+#endif
+
+       }
+
        
        // Note assigned numa ID could be different than our request... 
+       // Also note that when swapping is used, the numa info will
+       // reflect the numa id of address 0x0 for unallocated regions
+       //
        region->numa_id = v3_numa_hpa_to_node(region->host_addr);
 
        region->flags.read = 1;
@@ -196,7 +257,6 @@ int v3_init_mem_map(struct v3_vm_info * vm) {
        region->flags.alloced = 1;
        region->flags.limit32 = will_use_shadow_paging(vm);
        
-
        region->unhandled = unhandled_err;
     }
 
@@ -224,7 +284,15 @@ void v3_delete_mem_map(struct v3_vm_info * vm) {
 
     for (i = 0; i < map->num_base_regions; i++) {
        struct v3_mem_region * region = &(map->base_regions[i]);
+#ifdef V3_CONFIG_SWAPPING
+       if (vm->swap_state.enable_swapping) { 
+           if (!region->flags.swapped) { 
+               V3_FreePages((void *)(region->host_addr), block_pages);
+           } // otherwise this is not allocated space
+       }
+#else
        V3_FreePages((void *)(region->host_addr), block_pages);
+#endif
     }
 
     V3_Free(map->base_regions);
diff --git a/palacios/src/palacios/vmm_swapping.c b/palacios/src/palacios/vmm_swapping.c
new file mode 100644 (file)
index 0000000..a24ef42
--- /dev/null
@@ -0,0 +1,622 @@
+/* 
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National 
+ * Science Foundation and the Department of Energy.  
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico.  You can find out more at 
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2014, The V3VEE Project <http://www.v3vee.org> 
+ * All rights reserved.
+ *
+ * Author: Daniel Zuo <pengzuo2014@u.northwestern.edu>
+ *         Nikhat Karimi <nikhatkarimi@gmail.com>
+ *         Ahalya Srinivasan <AhalyaSrinivasan2015@u.northwestern.edu>
+ *         Peter Dinda <pdinda@northwestern.edu> (pinning, cleanup, integration, locking etc)
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+#include <palacios/vmm_mem.h>
+#include <palacios/vmm.h>
+#include <palacios/vmm_util.h>
+#include <palacios/vmm_emulator.h>
+#include <palacios/vm_guest.h>
+#include <palacios/vmm_debug.h>
+
+#include <palacios/vmm_shadow_paging.h>
+#include <palacios/vmm_direct_paging.h>
+
+#include <palacios/vmm_xml.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+
+  <mem ... >N_MB</mem>             Size of memory in the GPA
+
+  <swapping enable="y">
+     <allocated>M_MB</allocated>   Allocated space (M_MB <= N_MB)
+     <file>FILENAME</file>         Where to swap to
+     <strategy>STRATEGY</strategy> Victim picker to use NEXT_FIT, RANDOM (default), LRU, DEFAULT 
+  </swapping>
+
+*/
+
+
+#ifndef V3_CONFIG_DEBUG_SWAPPING
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
+
+int v3_init_swapping()
+{
+    PrintDebug(VM_NONE,VCORE_NONE, "swapper: init\n");
+    return 0;
+
+}
+
+int v3_deinit_swapping()
+{
+    PrintDebug(VM_NONE,VCORE_NONE, "swapper: deinit\n");
+    return 0;
+}
+
+
+static int write_all(v3_file_t fd, void *buf, uint64_t len, uint64_t offset)
+{
+    sint64_t thisop;
+
+    while (len>0) { 
+       thisop = v3_file_write(fd, buf, len, offset);
+       if (thisop <= 0) { 
+           return -1;
+       }
+       buf+=thisop;
+       offset+=thisop;
+       len-=thisop;
+    }
+    return 0; 
+}
+
+static int read_all(v3_file_t fd, void *buf, uint64_t len, uint64_t offset)
+{
+    sint64_t thisop;
+
+    while (len>0) { 
+       thisop = v3_file_read(fd, buf, len, offset);
+       if (thisop <= 0) { 
+           return -1;
+       }
+       buf+=thisop;
+       offset+=thisop;
+       len-=thisop;
+    }
+    return 0; 
+}
+
+
+#define REGION_WARN_THRESH 16
+
+#define CEIL_DIV(x,y) (((x)/(y)) + !!((x)%(y)))
+
+int v3_init_swapping_vm(struct v3_vm_info *vm, v3_cfg_tree_t *config)
+{
+    v3_cfg_tree_t *swap_config;
+    char *enable;
+    char *allocated;
+    char *strategy;
+    char *file;
+    uint64_t alloc;
+    extern uint64_t v3_mem_block_size;
+
+
+    PrintDebug(vm, VCORE_NONE, "swapper: vm init\n");
+
+    memset(&vm->swap_state,0,sizeof(struct v3_swap_impl_state));
+
+    v3_lock_init(&(vm->swap_state.lock));
+
+    vm->swap_state.enable_swapping=0;
+    vm->swap_state.host_mem_size=vm->mem_size;
+    
+    if (!config || !(swap_config=v3_cfg_subtree(config,"swapping"))) {
+       PrintDebug(vm,VCORE_NONE,"swapper: no swapping configuration found\n");
+       return 0;
+    }
+    
+    if (!(enable=v3_cfg_val(swap_config,"enable")) || strcasecmp(enable,"y")) {
+       PrintDebug(vm,VCORE_NONE,"swapper: swapping configuration disabled\n");
+       return 0;
+    }
+
+    allocated = v3_cfg_val(swap_config,"allocated");
+    if (!allocated) { 
+       PrintError(vm,VCORE_NONE,"swapper: swapping configuration must included allocated block\n");
+       return -1;
+    }
+    alloc = ((uint64_t)atoi(allocated))*1024*1024;
+
+    // make alloc an integer multiple of the memory block size
+    alloc = CEIL_DIV(alloc, v3_mem_block_size) * v3_mem_block_size;
+
+    PrintDebug(vm,VCORE_NONE,"swapper: adjusted allocated size is %llu\n",alloc);
+
+    if (alloc > vm->mem_size) { 
+       PrintError(vm,VCORE_NONE,"swapper: cannot allocate more than the VM's memory size....\n");
+       return -1;
+    }
+
+    
+    file = v3_cfg_val(swap_config,"file");
+    if (!file) { 
+       PrintError(vm,VCORE_NONE,"swapper: swapping configuration must included swap file name\n");
+       return -1;
+    }
+    
+    strategy = v3_cfg_val(swap_config,"strategy");
+    if (!strategy) { 
+       PrintDebug(vm,VCORE_NONE,"swapper: default strategy selected\n");
+       strategy="default";
+    }
+
+    // Can we allocate the file?
+
+    if ((vm->swap_state.swapfd = v3_file_open(vm,file, FILE_OPEN_MODE_READ | FILE_OPEN_MODE_WRITE | FILE_OPEN_MODE_CREATE))<0) {
+       PrintError(vm,VCORE_NONE,"swapper: cannot open or create swap file\n");
+       return -1;
+    } else {
+       // Make sure we can write the whole thing
+       uint64_t addr;
+       char *buf = V3_Malloc(PAGE_SIZE_4KB);
+       if (!buf) { 
+           PrintError(vm,VCORE_NONE,"swapper: unable to allocate space for writing file\n");
+           return -1;
+       }
+       memset(buf,0,PAGE_SIZE_4KB);
+       for (addr=0;addr<vm->mem_size;addr+=PAGE_SIZE_4KB) { 
+           if (write_all(vm->swap_state.swapfd, 
+                         buf,
+                         PAGE_SIZE_4KB,
+                         addr)) { 
+               PrintError(vm,VCORE_NONE,"swapper: unable to write initial swap file\n");
+               V3_Free(buf);
+               v3_file_close(vm->swap_state.swapfd);
+               return -1;
+           }
+       }
+       V3_Free(buf);
+    }
+
+    // We are now set - we have space to swap to
+    vm->swap_state.enable_swapping=1;
+
+    vm->swap_state.strategy = 
+       !strcasecmp(strategy,"next_fit") ? V3_SWAP_NEXT_FIT :
+       !strcasecmp(strategy,"random") ? V3_SWAP_RANDOM :
+       !strcasecmp(strategy,"lru") ? V3_SWAP_LRU :
+       !strcasecmp(strategy,"default") ? V3_SWAP_RANDOM :
+       V3_SWAP_RANDOM;
+
+    vm->swap_state.host_mem_size=alloc;
+    vm->swap_state.swap_count=0;
+    vm->swap_state.last_region_used=0;
+    // already have set swapfd
+
+
+    V3_Print(vm,VCORE_NONE,"swapper: swapping enabled (%llu allocated of %llu using %s on %s)\n",
+            (uint64_t)vm->swap_state.host_mem_size, (uint64_t) vm->mem_size, strategy, file);
+
+    if (vm->swap_state.host_mem_size / v3_mem_block_size < REGION_WARN_THRESH) { 
+       V3_Print(vm,VCORE_NONE,"swapper: WARNING: %llu regions is less than threshold of %llu, GUEST MAY FAIL TO MAKE PROGRESS\n",
+                (uint64_t)vm->swap_state.host_mem_size/v3_mem_block_size, (uint64_t)REGION_WARN_THRESH);
+    }
+
+    return 0;
+    
+}
+
+int v3_deinit_swapping_vm(struct v3_vm_info *vm)
+{
+    PrintDebug(vm, VCORE_NONE, "swapper: vm deinit\n");
+
+    if (vm->swap_state.enable_swapping) {
+       v3_file_close(vm->swap_state.swapfd);
+    }
+
+    v3_lock_deinit(&(vm->swap_state.lock));
+
+    return 0;
+}
+
+
+int v3_pin_region(struct v3_vm_info *vm, struct v3_mem_region *region)
+{
+    unsigned int flags;
+
+    PrintDebug(vm,VCORE_NONE, "Pin Region GPA=%p to %p\n",(void*) region->guest_start, (void*)region->guest_end);
+
+    if (!(region->flags.base)) { 
+       PrintError(vm,VCORE_NONE,"Attempt to pin non-base region\n");
+       return -1;
+    }
+    
+    if (region->flags.pinned) { 
+       return 0;
+    }
+          
+    flags = v3_lock_irqsave(vm->swap_state.lock);
+    
+    if (region->flags.swapped) {
+       // can't pin since it's swapped out, swap it in an try again
+       v3_unlock_irqrestore(vm->swap_state.lock, flags);
+       if (v3_swap_in_region(vm,region)) { 
+           PrintError(vm,VCORE_NONE,"Cannot swap in during a pin operation\n");
+           return -1;
+       } else {
+           return v3_pin_region(vm,region);
+       }
+    }
+    
+    // still holding lock if we got here, so we're the exclusive
+    // manipulator of the swap state
+    region->flags.pinned=1;
+    
+    v3_unlock_irqrestore(vm->swap_state.lock, flags);
+    
+    return 0;
+}
+
+
+int v3_unpin_region(struct v3_vm_info *vm, struct v3_mem_region *region)
+{
+    unsigned int flags = v3_lock_irqsave(vm->swap_state.lock);
+    
+    region->flags.pinned=0;
+
+    v3_unlock_irqrestore(vm->swap_state.lock,flags);
+
+    return 0;
+
+}
+
+
+#define SEARCH_LIMIT 1024
+
+// Must be called with the lock held
+static struct v3_mem_region * choose_random_victim(struct v3_vm_info * vm) 
+{
+    
+    struct v3_mem_map * map = &(vm->mem_map);
+    uint64_t num_base_regions = map->num_base_regions;
+    uint64_t thetime;
+    struct v3_mem_region *reg=0;
+    uint32_t i=0;
+       
+    PrintDebug(vm, VCORE_NONE, "swapper: choosing random victim\n");
+
+    for (i=0, reg=0 ; 
+        i<SEARCH_LIMIT && reg==0 ; 
+        i++) {
+
+       // cycle counter used as pseudorandom number generator
+       rdtscll(thetime);
+       
+       reg = &(map->base_regions[thetime % num_base_regions]);
+
+       if (reg->flags.swapped || reg->flags.pinned) { 
+           // region is already swapped or is pinned - try again
+           reg = 0;
+       } 
+    }
+
+    if (!reg) { 
+       PrintError(vm,VCORE_NONE,"swapper: Unable to find a random victim\n");
+    } else {
+       PrintDebug(vm,VCORE_NONE,"swapper: Random victim GPA=%p to %p\n", (void*)reg->guest_start, (void*)reg->guest_end);
+    }
+    
+    return reg;
+}
+
+
+// Must be called with the lock held
+static struct v3_mem_region * choose_next_victim(struct v3_vm_info * vm) 
+{
+    struct v3_mem_map * map = &(vm->mem_map);
+    uint64_t num_base_regions = map->num_base_regions;
+    struct v3_mem_region *reg=0;
+    uint32_t i=0;
+       
+    PrintDebug(vm, VCORE_NONE, "swapper: choosing next victim\n");
+
+    // forward to end
+    for (i=vm->swap_state.last_region_used+1, reg=0; 
+        i<num_base_regions && reg==0; 
+        i++) {
+
+       reg = &(map->base_regions[i]);
+
+       if (reg->flags.swapped || reg->flags.pinned) { 
+           // region is already swapped or is pinned - try again
+           reg = 0;
+       } 
+    }
+
+    for (i=0; 
+        i < vm->swap_state.last_region_used+1 && reg==0;
+        i++) { 
+       
+       reg = &(map->base_regions[i]);
+
+       if (reg->flags.swapped || reg->flags.pinned) { 
+           // region is already swapped or is pinned - try again
+           reg = 0;
+       } 
+    }
+
+    if (!reg) { 
+       PrintError(vm,VCORE_NONE,"swapper: Unable to find the next victim\n");
+    } else {
+       PrintDebug(vm,VCORE_NONE,"swapper: Next victim GPA=%p to %p\n", (void*)reg->guest_start, (void*)reg->guest_end);
+    }
+    
+    return reg;
+}
+
+// Must be called with the lock held
+static struct v3_mem_region * choose_lru_victim(struct v3_vm_info * vm) 
+{
+    struct v3_mem_map * map = &(vm->mem_map);
+    uint64_t num_base_regions = map->num_base_regions;
+    struct v3_mem_region *reg=0;
+    struct v3_mem_region *oldest_reg=0;
+    uint32_t i=0;
+    uint64_t oldest_time;
+       
+    PrintDebug(vm, VCORE_NONE, "swapper: choosing pseudo-lru victim\n");
+
+
+    for (i=0, oldest_time=0, oldest_reg=0;
+        i<num_base_regions; 
+        i++) {
+
+       reg = &(map->base_regions[i]);
+
+       if (reg->flags.swapped || reg->flags.pinned) {
+           if (!oldest_reg ||
+               reg->swap_state.last_accessed < oldest_time) { 
+
+               oldest_time = reg->swap_state.last_accessed;
+               oldest_reg = reg;
+           }
+       }
+    }
+
+    if (!oldest_reg) { 
+       PrintError(vm,VCORE_NONE,"swapper: Unable to find pseudo-lru victim\n");
+    } else {
+       PrintDebug(vm,VCORE_NONE,"swapper: Pseudo-lru victim GPA=%p to %p\n", (void*)oldest_reg->guest_start, (void*)oldest_reg->guest_end);
+    }
+    
+    return oldest_reg;
+}
+
+
+// Must be called with the lock held
+static struct v3_mem_region * choose_victim(struct v3_vm_info * vm) 
+{
+    switch (vm->swap_state.strategy) { 
+       case V3_SWAP_NEXT_FIT:
+           return choose_next_victim(vm);
+           break;
+       case V3_SWAP_RANDOM:
+           return choose_random_victim(vm);
+           break;
+       case V3_SWAP_LRU:
+           return choose_lru_victim(vm);
+           break;
+       default:
+           return choose_random_victim(vm);
+           break;
+    }
+}
+
+
+// swaps out region, and marks it as swapped and pinned
+// no lock should be held
+static int swap_out_region_internal(struct v3_vm_info *vm, struct v3_mem_region *victim, int ignore_pinning)
+{
+    unsigned int flags;
+    int i; 
+    int fail=0;
+
+    flags = v3_lock_irqsave(vm->swap_state.lock);
+
+    if (victim->flags.swapped) { 
+       v3_unlock_irqrestore(vm->swap_state.lock,flags);
+       PrintDebug(vm,VCORE_NONE,"swapper: swap out already swapped out region\n");
+       return 0;
+    }
+    
+    if (!ignore_pinning && victim->flags.pinned) { 
+       v3_unlock_irqrestore(vm->swap_state.lock,flags);
+       PrintError(vm,VCORE_NONE,"swapper: attempt to swap out pinned region\n");
+       return -1;
+    }
+
+    // now mark it as pinned until we are done with it.
+    victim->flags.pinned=1;
+
+    // release lock - it's marked pinned so nothing else will touch it
+    v3_unlock_irqrestore(vm->swap_state.lock,flags);
+    
+    // do NOT do this without irqs on... 
+    if (write_all(vm->swap_state.swapfd, 
+                 (uint8_t *)V3_VAddr((void *)victim->host_addr), 
+                 victim->guest_end - victim->guest_start, 
+                 victim->guest_start)) {
+       PrintError(vm, VCORE_NONE, "swapper: failed to swap out victim"); //write victim to disk
+       // note write only here - it returns unswapped and unpinned
+       victim->flags.pinned=0;
+       return -1;
+    }
+
+    // Now invalidate it
+    
+    //Invalidate the victim on all cores
+    
+    for (i=0, fail=0; i<vm->num_cores;i++ ) {
+       struct guest_info * core = &(vm->cores[i]);
+       int rc;
+
+       if (core->shdw_pg_mode == SHADOW_PAGING) {
+           v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(core);
+           if (mem_mode == PHYSICAL_MEM) {
+               PrintDebug(vm, VCORE_NONE, "swapper: v3_invalidate_passthrough_addr_range() called for core %d",i);
+               rc = v3_invalidate_passthrough_addr_range(core, victim->guest_start,  victim->guest_end-1,NULL,NULL ); 
+           } else {
+               PrintDebug(vm, VCORE_NONE, "swapper: v3_invalidate_shadow_pts() called for core %d",i);
+               rc = v3_invalidate_shadow_pts(core);
+           }
+       } else if (core->shdw_pg_mode == NESTED_PAGING) {
+           PrintDebug(vm, VCORE_NONE, "swapper: v3_invalidate_nested_addr_range() called for core %d",i);
+           rc = v3_invalidate_nested_addr_range(core,  victim->guest_start,  victim->guest_end-1,NULL,NULL );
+        }
+
+       if (rc) { 
+           PrintError(vm,VCORE_NONE,"swapper: paging invalidation failed for victim on core %d.... continuing, but this is not good.\n", i);
+           fail=1;
+       }
+    }
+
+    victim->flags.swapped=1;   // now it is in "swapped + pinned" state, meaning it has been written and is now holding for future use
+    
+    if (fail) { 
+       return -1;
+    } else {
+       return 0;
+    }
+}
+
+
+// swaps out region, and marks it as swapped
+int v3_swap_out_region(struct v3_vm_info *vm, struct v3_mem_region *victim)
+{
+    if (!victim->flags.base) { 
+       PrintError(vm, VCORE_NONE,"swapper: can only swap out base regions\n");
+       return -1;
+    }
+
+    if (victim->flags.pinned) { 
+       PrintError(vm, VCORE_NONE,"swapper: cannot swap out a pinned region\n");
+       return -1;
+    }
+    
+    if (swap_out_region_internal(vm,victim,0)) { 
+       PrintError(vm, VCORE_NONE,"swapper: failed to swap out victim....  bad\n");
+       return -1;
+    }
+
+    // victim now has its old info, and is marked swapped and pinned
+
+    victim->host_addr = 0;
+    victim->flags.pinned = 0;  
+
+    // now is simply swapped
+    
+    return 0;
+}
+
+
+int v3_swap_in_region(struct v3_vm_info *vm, struct v3_mem_region *perp)
+{
+    unsigned int flags;
+    struct v3_mem_region *victim;
+
+    flags = v3_lock_irqsave(vm->swap_state.lock);
+
+    if (!perp->flags.base) { 
+       v3_unlock_irqrestore(vm->swap_state.lock,flags);
+       PrintError(vm,VCORE_NONE,"swapper: can only swap in base regions\n");
+       return -1;
+    }
+
+    if (!perp->flags.swapped) { 
+       v3_unlock_irqrestore(vm->swap_state.lock,flags);
+       PrintDebug(vm,VCORE_NONE,"swapper: region is already swapped in\n");
+       return 0;
+    }
+
+    // while still holding the lock, we will pin it to make sure no one 
+    // else will attempt to swap in a race with us
+    perp->flags.pinned=1;
+    
+    victim = choose_victim(vm);
+
+    if (!victim) { 
+       perp->flags.pinned=0;  // leave perp swapped 
+       v3_unlock_irqrestore(vm->swap_state.lock,flags);
+       PrintError(vm,VCORE_NONE,"swapper: cannot find victim\n");
+       return -1;
+    }
+
+    victim->flags.pinned=1;
+
+
+    // update the next fit info
+    // pointer arith in units of relevant structs... 
+    vm->swap_state.last_region_used = (victim - &(vm->mem_map.base_regions[0])); 
+    
+
+    // Now we hold both the perp and the victim (pinned)
+    // and so can release the lcok
+    v3_unlock_irqrestore(vm->swap_state.lock,flags);
+
+    
+    if (swap_out_region_internal(vm,victim,1)) {  // ignore that the victim is marked pinned
+       PrintError(vm, VCORE_NONE,"swapper: failed to swap out victim....  bad\n");
+       return -1;
+    }
+
+    // victim is still marked pinned
+
+    // mug the victim
+    perp->host_addr = victim->host_addr;
+    victim->host_addr = 0;
+    // and we're done, so release it
+    victim->flags.swapped=1;
+    victim->flags.pinned=0;
+
+
+    // Now swap in the perp
+    
+    if (read_all(vm->swap_state.swapfd, 
+                (uint8_t *)V3_VAddr((void *)perp->host_addr), 
+                perp->guest_end - perp->guest_start, 
+                perp->guest_start)) {
+       
+       PrintError(vm, VCORE_NONE, "swapper: swap in of region failed!\n"); 
+       // leave it swapped, but unpin the memory... 
+       perp->flags.pinned = 0; 
+       return -1;
+    } else {
+       perp->flags.swapped = 0;  // perp is now OK, so release it
+       perp->flags.pinned = 0; 
+       vm->swap_state.swap_count++;
+       return 0;
+    }
+}
+
+
+
+void v3_touch_region(struct v3_vm_info *vm, struct v3_mem_region *region)
+{
+    // should be uniform host time, not per core...
+    rdtscll(region->swap_state.last_accessed);
+}
+
+
index 28fb36a..04180a6 100755 (executable)
@@ -83,6 +83,7 @@ if ($config{numcores}>1) {
   }
 }
 
+do_swapping(\%config, $pdir);
 
 print "We will give your guest the default performance tuning characteristics\n";
 $config{perftune_block} .= <<PERFTUNE
@@ -242,6 +243,7 @@ $target = PAL;
 print $target "<vm class=\"PC\">\n\n";
 print $target file_setup(\%config), "\n";
 print $target memory_setup(\%config), "\n";
+print $target swapping_setup(\%config), "\n";
 print $target paging_setup(\%config), "\n";
 print $target memmap_setup(\%config), "\n";
 print $target numa_setup(\%config), "\n";
@@ -403,6 +405,28 @@ sub get_numa_data() {
   return %numa;
 }
 
+
+sub do_swapping {
+  my ($cr, $pdir) = @_;
+
+  my $canswap = is_palacios_core_feature_enabled($pdir,"V3_CONFIG_SWAPPING");
+  my $mem = $cr->{mem};
+
+  if ($canswap) { 
+    #Config for swapping
+    $cr->{swapping} = yn_question("Do you want to use swapping?", "n", "y", "n");
+    
+    if ($cr->{swapping} eq "y") { 
+      $cr->{swap_alloc} = quant_question("How much memory do you want to allocate [MB] ?", $mem/2);
+      print "We will use the default swapping strategy.\n";
+      $cr->{swap_strat} = "default";
+      print "What file do you want to swap to? [./swap.bin] ";
+      $cr->{swap_file} = get_user("./swap.bin");
+    }
+  }
+
+} 
+
 sub do_device {
   my ($cr,$pdir,$feature, $class, $id, $hardfail, $optblob, $nestblob) =@_;
 
@@ -984,6 +1008,14 @@ sub extensions_setup  {
   return $s;
 }
 
+sub swapping_setup {
+  my $cr=shift;
+  if (defined($cr->{swapping}) && $cr->{swapping} eq "y") { 
+    return " <swapping enable=\"y\">\n  <allocated>".$cr->{swap_alloc}."</allocated>\n  <file>".$cr->{swap_file}."</file>\n  <strategy>".$cr->{swap_strat}."</strategy>\n </swapping>\n";
+  } else {
+    return " <!-- there is no swapping configuration, but you can add one manually -->\n";
+  }
+}
 sub telemetry_setup  {
   my $cr=shift;
   return " <telemetry>".$cr->{telemetry}."</telemetry>\n";