vm: Improve live update support.

Change-Id: I02da3ea32cd05c4ed84a6e199236e5df6e25cb60
This commit is contained in:
Cristiano Giuffrida 2014-03-12 00:13:37 +01:00 committed by David van Moolenbroek
parent 53398d733f
commit 63483e02e6
12 changed files with 156 additions and 61 deletions

View file

@ -21,7 +21,7 @@ vm_memctl(endpoint_t ep, int req, void** addr, size_t *len)
if(addr) { if(addr) {
*addr = m.VM_RS_CTL_ADDR; *addr = m.VM_RS_CTL_ADDR;
} }
if(addr) { if(len) {
*len = m.VM_RS_CTL_LEN; *len = m.VM_RS_CTL_LEN;
} }

View file

@ -75,6 +75,10 @@ SANITYCHECK(SCL_FUNCTIONS);
printf("VM: unannounced VM_EXIT %d\n", msg->VME_ENDPOINT); printf("VM: unannounced VM_EXIT %d\n", msg->VME_ENDPOINT);
return EINVAL; return EINVAL;
} }
if(vmp->vm_flags & VMF_VM_INSTANCE) {
vmp->vm_flags &= ~VMF_VM_INSTANCE;
num_vm_instances--;
}
{ {
/* Free pagetable and pages allocated by pt code. */ /* Free pagetable and pages allocated by pt code. */

View file

@ -40,3 +40,4 @@ EXTERN mem_type_t mem_type_anon, /* anonymous memory */
/* total number of memory pages */ /* total number of memory pages */
EXTERN int total_pages; EXTERN int total_pages;
EXTERN int num_vm_instances;

View file

@ -515,6 +515,10 @@ void init_vm(void)
/* Initialize the structures for queryexit */ /* Initialize the structures for queryexit */
init_query_exit(); init_query_exit();
/* Mark VM instances. */
num_vm_instances = 1;
vmproc[VM_PROC_NR].vm_flags |= VMF_VM_INSTANCE;
} }
/*===========================================================================* /*===========================================================================*

View file

@ -83,8 +83,7 @@ static struct {
phys_bytes phys; phys_bytes phys;
} sparepagedirs[SPAREPAGEDIRS]; } sparepagedirs[SPAREPAGEDIRS];
extern char _end; #define is_staticaddr(v) ((vir_bytes) (v) < VM_OWN_HEAPSTART)
#define is_staticaddr(v) ((vir_bytes) (v) < (vir_bytes) &_end)
#define MAX_KERNMAPPINGS 10 #define MAX_KERNMAPPINGS 10
static struct { static struct {
@ -154,10 +153,8 @@ static u32_t findhole(int pages)
u32_t holev = NO_MEM; u32_t holev = NO_MEM;
int holesize = -1; int holesize = -1;
vmin = (vir_bytes) (&_end); /* marks end of VM BSS */ vmin = VM_OWN_MMAPBASE;
vmin += 1024*1024*1024; /* reserve 1GB virtual address space for VM heap */ vmax = VM_OWN_MMAPTOP;
vmin &= ARCH_VM_ADDR_MASK;
vmax = vmin + 100 * 1024 * 1024; /* allow 100MB of address space for VM */
/* Input sanity check. */ /* Input sanity check. */
assert(vmin + VM_PAGE_SIZE >= vmin); assert(vmin + VM_PAGE_SIZE >= vmin);
@ -678,8 +675,7 @@ int pt_map_in_range(struct vmproc *src_vmp, struct vmproc *dst_vmp,
int pt_ptmap(struct vmproc *src_vmp, struct vmproc *dst_vmp) int pt_ptmap(struct vmproc *src_vmp, struct vmproc *dst_vmp)
{ {
/* Transfer mappings to page dir and page tables from source process and /* Transfer mappings to page dir and page tables from source process and
* destination process. Make sure all the mappings are above the stack, not * destination process.
* to corrupt valid mappings in the data segment of the destination process.
*/ */
int pde, r; int pde, r;
phys_bytes physaddr; phys_bytes physaddr;

View file

@ -47,7 +47,9 @@ int vm_isokendpt(endpoint_t ep, int *proc);
int get_stack_ptr(int proc_nr, vir_bytes *sp); int get_stack_ptr(int proc_nr, vir_bytes *sp);
int do_info(message *); int do_info(message *);
int swap_proc_slot(struct vmproc *src_vmp, struct vmproc *dst_vmp); int swap_proc_slot(struct vmproc *src_vmp, struct vmproc *dst_vmp);
int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp); int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp,
int sys_upd_flags);
void adjust_proc_refs(void);
int do_getrusage(message *m); int do_getrusage(message *m);
/* exit.c */ /* exit.c */
@ -149,8 +151,8 @@ int map_unmap_region(struct vmproc *vmp, struct vir_region *vr,
int map_unmap_range(struct vmproc *vmp, vir_bytes, vir_bytes); int map_unmap_range(struct vmproc *vmp, vir_bytes, vir_bytes);
int map_free_proc(struct vmproc *vmp); int map_free_proc(struct vmproc *vmp);
int map_proc_copy(struct vmproc *dst, struct vmproc *src); int map_proc_copy(struct vmproc *dst, struct vmproc *src);
int map_proc_copy_from(struct vmproc *dst, struct vmproc *src, struct int map_proc_copy_range(struct vmproc *dst, struct vmproc *src, struct
vir_region *start_src_vr); vir_region *start_src_vr, struct vir_region *end_src_vr);
struct vir_region *map_lookup(struct vmproc *vmp, vir_bytes addr, struct vir_region *map_lookup(struct vmproc *vmp, vir_bytes addr,
struct phys_region **pr); struct phys_region **pr);
int map_pf(struct vmproc *vmp, struct vir_region *region, vir_bytes int map_pf(struct vmproc *vmp, struct vir_region *region, vir_bytes
@ -172,10 +174,7 @@ void physblock_set(struct vir_region *region, vir_bytes offset,
int map_ph_writept(struct vmproc *vmp, struct vir_region *vr, int map_ph_writept(struct vmproc *vmp, struct vir_region *vr,
struct phys_region *pr); struct phys_region *pr);
struct vir_region * map_region_lookup_tag(struct vmproc *vmp, u32_t struct vir_region* map_region_lookup_type(struct vmproc *vmp, u32_t flags);
tag);
void map_region_set_tag(struct vir_region *vr, u32_t tag);
u32_t map_region_get_tag(struct vir_region *vr);
int map_get_phys(struct vmproc *vmp, vir_bytes addr, phys_bytes *r); int map_get_phys(struct vmproc *vmp, vir_bytes addr, phys_bytes *r);
int map_get_ref(struct vmproc *vmp, vir_bytes addr, u8_t *cnt); int map_get_ref(struct vmproc *vmp, vir_bytes addr, u8_t *cnt);
unsigned int physregions(struct vir_region *vr); unsigned int physregions(struct vir_region *vr);

View file

@ -933,28 +933,30 @@ int map_proc_copy(struct vmproc *dst, struct vmproc *src)
/* Copy all the memory regions from the src process to the dst process. */ /* Copy all the memory regions from the src process to the dst process. */
region_init(&dst->vm_regions_avl); region_init(&dst->vm_regions_avl);
return map_proc_copy_from(dst, src, NULL); return map_proc_copy_range(dst, src, NULL, NULL);
} }
/*========================================================================* /*========================================================================*
* map_proc_copy_from * * map_proc_copy_range *
*========================================================================*/ *========================================================================*/
int map_proc_copy_from(struct vmproc *dst, struct vmproc *src, int map_proc_copy_range(struct vmproc *dst, struct vmproc *src,
struct vir_region *start_src_vr) struct vir_region *start_src_vr, struct vir_region *end_src_vr)
{ {
struct vir_region *vr; struct vir_region *vr;
region_iter v_iter; region_iter v_iter;
if(!start_src_vr) if(!start_src_vr)
start_src_vr = region_search_least(&src->vm_regions_avl); start_src_vr = region_search_least(&src->vm_regions_avl);
if(!end_src_vr)
end_src_vr = region_search_greatest(&src->vm_regions_avl);
assert(start_src_vr); assert(start_src_vr && end_src_vr);
assert(start_src_vr->parent == src); assert(start_src_vr->parent == src);
region_start_iter(&src->vm_regions_avl, &v_iter, region_start_iter(&src->vm_regions_avl, &v_iter,
start_src_vr->vaddr, AVL_EQUAL); start_src_vr->vaddr, AVL_EQUAL);
assert(region_get_iter(&v_iter) == start_src_vr); assert(region_get_iter(&v_iter) == start_src_vr);
/* Copy source regions after the destination's last region (if any). */ /* Copy source regions into the destination. */
SANITYCHECK(SCL_FUNCTIONS); SANITYCHECK(SCL_FUNCTIONS);
@ -982,6 +984,9 @@ int map_proc_copy_from(struct vmproc *dst, struct vmproc *src,
} }
} }
#endif #endif
if(vr == end_src_vr) {
break;
}
region_incr_iter(&v_iter); region_incr_iter(&v_iter);
} }
@ -1290,6 +1295,26 @@ int map_unmap_range(struct vmproc *vmp, vir_bytes unmap_start, vir_bytes length)
} }
/*========================================================================*
* map_region_lookup_type *
*========================================================================*/
struct vir_region* map_region_lookup_type(struct vmproc *vmp, u32_t type)
{
struct vir_region *vr;
struct phys_region *pr;
vir_bytes used = 0, weighted = 0;
region_iter v_iter;
region_start_iter_least(&vmp->vm_regions_avl, &v_iter);
while((vr = region_get_iter(&v_iter))) {
region_incr_iter(&v_iter);
if(vr->flags & type)
return vr;
}
return NULL;
}
/*========================================================================* /*========================================================================*
* map_get_phys * * map_get_phys *
*========================================================================*/ *========================================================================*/

View file

@ -76,8 +76,9 @@ typedef struct vir_region {
/* Mapping type: */ /* Mapping type: */
#define VR_ANON 0x100 /* Memory to be cleared and allocated */ #define VR_ANON 0x100 /* Memory to be cleared and allocated */
#define VR_DIRECT 0x200 /* Mapped, but not managed by VM */ #define VR_DIRECT 0x200 /* Mapped, but not managed by VM */
#define VR_PREALLOC_MAP 0x400 /* Preallocated map. */
/* map_page_region flags */ /* map_page_region_flags */
#define MF_PREALLOC 0x01 #define MF_PREALLOC 0x01
#endif #endif

View file

@ -16,6 +16,8 @@
#include <minix/bitmap.h> #include <minix/bitmap.h>
#include <minix/rs.h> #include <minix/rs.h>
#include <sys/mman.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <env.h> #include <env.h>
@ -77,6 +79,7 @@ int do_rs_update(message *m_ptr)
src_e = m_ptr->m_lsys_vm_update.src; src_e = m_ptr->m_lsys_vm_update.src;
dst_e = m_ptr->m_lsys_vm_update.dst; dst_e = m_ptr->m_lsys_vm_update.dst;
sys_upd_flags = m_ptr->m_lsys_vm_update.flags; sys_upd_flags = m_ptr->m_lsys_vm_update.flags;
reply_e = m_ptr->m_source;
/* Lookup slots for source and destination process. */ /* Lookup slots for source and destination process. */
if(vm_isokendpt(src_e, &src_p) != OK) { if(vm_isokendpt(src_e, &src_p) != OK) {
@ -90,6 +93,14 @@ int do_rs_update(message *m_ptr)
} }
dst_vmp = &vmproc[dst_p]; dst_vmp = &vmproc[dst_p];
/* Check flags. */
if((sys_upd_flags & (SF_VM_ROLLBACK|SF_VM_NOMMAP)) == 0) {
/* Can't preallocate when transfering mmapped regions. */
if(map_region_lookup_type(dst_vmp, VR_PREALLOC_MAP)) {
return ENOSYS;
}
}
/* Let the kernel do the update first. */ /* Let the kernel do the update first. */
r = sys_update(src_e, dst_e, r = sys_update(src_e, dst_e,
sys_upd_flags & SF_VM_ROLLBACK ? SYS_UPD_ROLLBACK : 0); sys_upd_flags & SF_VM_ROLLBACK ? SYS_UPD_ROLLBACK : 0);
@ -102,15 +113,15 @@ int do_rs_update(message *m_ptr)
if(r != OK) { if(r != OK) {
return r; return r;
} }
r = swap_proc_dyn_data(src_vmp, dst_vmp); r = swap_proc_dyn_data(src_vmp, dst_vmp, sys_upd_flags);
if(r != OK) { if(r != OK) {
return r; return r;
} }
pt_bind(&src_vmp->vm_pt, src_vmp); pt_bind(&src_vmp->vm_pt, src_vmp);
pt_bind(&dst_vmp->vm_pt, dst_vmp); pt_bind(&dst_vmp->vm_pt, dst_vmp);
/* Reply, update-aware. */ /* Reply in case of external request, update-aware. */
reply_e = m_ptr->m_source; if(reply_e != VM_PROC_NR) {
if(reply_e == src_e) reply_e = dst_e; if(reply_e == src_e) reply_e = dst_e;
else if(reply_e == dst_e) reply_e = src_e; else if(reply_e == dst_e) reply_e = src_e;
m_ptr->m_type = OK; m_ptr->m_type = OK;
@ -118,6 +129,7 @@ int do_rs_update(message *m_ptr)
if(r != OK) { if(r != OK) {
panic("ipc_send() error"); panic("ipc_send() error");
} }
}
return SUSPEND; return SUSPEND;
} }
@ -134,6 +146,17 @@ static int rs_memctl_make_vm_instance(struct vmproc *new_vm_vmp)
this_vm_vmp = &vmproc[VM_PROC_NR]; this_vm_vmp = &vmproc[VM_PROC_NR];
/* Check if the operation is allowed. */
assert(num_vm_instances == 1 || num_vm_instances == 2);
if(num_vm_instances == 2) {
printf("VM can currently support no more than 2 VM instances at the time.");
return EPERM;
}
/* Copy settings from current VM. */
new_vm_vmp->vm_flags |= VMF_VM_INSTANCE;
num_vm_instances++;
/* Pin memory for the new VM instance. */ /* Pin memory for the new VM instance. */
r = map_pin_memory(new_vm_vmp); r = map_pin_memory(new_vm_vmp);
if(r != OK) { if(r != OK) {
@ -187,20 +210,26 @@ static int rs_memctl_heap_prealloc(struct vmproc *vmp,
static int rs_memctl_map_prealloc(struct vmproc *vmp, static int rs_memctl_map_prealloc(struct vmproc *vmp,
vir_bytes *addr, size_t *len) vir_bytes *addr, size_t *len)
{ {
#if 0
struct vir_region *vr; struct vir_region *vr;
vir_bytes base, top;
int is_vm;
if(*len <= 0) { if(*len <= 0) {
return EINVAL; return EINVAL;
} }
*len = CLICK_CEIL(*len); *len = CLICK_CEIL(*len);
if(!(vr = map_page_region(vmp, VM_DATATOP - *len, VM_DATATOP, *len, is_vm = (vmp->vm_endpoint == VM_PROC_NR);
MAP_NONE, VR_ANON|VR_WRITABLE|VR_CONTIG, MF_PREALLOC))) { base = is_vm ? VM_OWN_MMAPBASE : VM_MMAPBASE;
top = is_vm ? VM_OWN_MMAPTOP : VM_MMAPTOP;
if (!(vr = map_page_region(vmp, base, top, *len,
VR_ANON|VR_WRITABLE|VR_UNINITIALIZED, MF_PREALLOC,
&mem_type_anon))) {
return ENOMEM; return ENOMEM;
} }
map_region_set_tag(vr, VRT_PREALLOC_MAP); vr->flags |= VR_PREALLOC_MAP;
*addr = arch_map2vir(vmp, vr->vaddr); *addr = vr->vaddr;
#endif
return OK; return OK;
} }
@ -210,19 +239,17 @@ static int rs_memctl_map_prealloc(struct vmproc *vmp,
static int rs_memctl_get_prealloc_map(struct vmproc *vmp, static int rs_memctl_get_prealloc_map(struct vmproc *vmp,
vir_bytes *addr, size_t *len) vir_bytes *addr, size_t *len)
{ {
#if 0
struct vir_region *vr; struct vir_region *vr;
vr = map_region_lookup_tag(vmp, VRT_PREALLOC_MAP); vr = map_region_lookup_type(vmp, VR_PREALLOC_MAP);
if(!vr) { if(!vr) {
*addr = 0; *addr = 0;
*len = 0; *len = 0;
} }
else { else {
*addr = arch_map2vir(vmp, vr->vaddr); *addr = vr->vaddr;
*len = vr->length; *len = vr->length;
} }
#endif
return OK; return OK;
} }

View file

@ -18,6 +18,7 @@
#include <minix/syslib.h> #include <minix/syslib.h>
#include <minix/type.h> #include <minix/type.h>
#include <minix/bitmap.h> #include <minix/bitmap.h>
#include <minix/rs.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <env.h> #include <env.h>
@ -219,20 +220,22 @@ int swap_proc_slot(struct vmproc *src_vmp, struct vmproc *dst_vmp)
/*===========================================================================* /*===========================================================================*
* swap_proc_dyn_data * * swap_proc_dyn_data *
*===========================================================================*/ *===========================================================================*/
int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp) int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp,
int sys_upd_flags)
{ {
int is_vm; int is_vm;
int r; int r;
struct vir_region *start_vr, *end_vr;
is_vm = (dst_vmp->vm_endpoint == VM_PROC_NR); is_vm = (dst_vmp->vm_endpoint == VM_PROC_NR);
/* For VM, transfer memory regions above the stack first. */ /* For VM, transfer memory mapped regions first. */
if(is_vm) { if(is_vm) {
#if LU_DEBUG #if LU_DEBUG
printf("VM: swap_proc_dyn_data: tranferring regions above the stack from old VM (%d) to new VM (%d)\n", printf("VM: swap_proc_dyn_data: tranferring memory mapped regions from old (%d) to new VM (%d)\n",
src_vmp->vm_endpoint, dst_vmp->vm_endpoint); src_vmp->vm_endpoint, dst_vmp->vm_endpoint);
#endif #endif
r = pt_map_in_range(src_vmp, dst_vmp, VM_STACKTOP, 0); r = pt_map_in_range(src_vmp, dst_vmp, VM_OWN_HEAPBASE, VM_OWN_MMAPTOP);
if(r != OK) { if(r != OK) {
printf("swap_proc_dyn_data: pt_map_in_range failed\n"); printf("swap_proc_dyn_data: pt_map_in_range failed\n");
return r; return r;
@ -249,25 +252,31 @@ int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp)
map_setparent(src_vmp); map_setparent(src_vmp);
map_setparent(dst_vmp); map_setparent(dst_vmp);
/* For regular processes, transfer regions above the stack now. /* Don't transfer mmapped regions if not required. */
* In case of rollback, we need to skip this step. To sandbox the if(is_vm || (sys_upd_flags & (SF_VM_ROLLBACK|SF_VM_NOMMAP))) {
* new instance and prevent state corruption on rollback, we share all return OK;
* the regions between the two instances as COW. }
/* Make sure regions are consistent. */
assert(region_search_root(&src_vmp->vm_regions_avl) && region_search_root(&dst_vmp->vm_regions_avl));
/* Transfer memory mapped regions now. To sandbox the new instance and
* prevent state corruption on rollback, we share all the regions
* between the two instances as COW.
*/ */
if(!is_vm) { start_vr = region_search(&dst_vmp->vm_regions_avl, VM_MMAPBASE, AVL_GREATER_EQUAL);
struct vir_region *vr; end_vr = region_search(&dst_vmp->vm_regions_avl, VM_MMAPTOP, AVL_LESS);
vr = map_lookup(dst_vmp, VM_STACKTOP, NULL); if(start_vr) {
if(vr && !map_lookup(src_vmp, VM_STACKTOP, NULL)) {
#if LU_DEBUG #if LU_DEBUG
printf("VM: swap_proc_dyn_data: tranferring regions above the stack from %d to %d\n", printf("VM: swap_proc_dyn_data: tranferring memory mapped regions from %d to %d\n",
src_vmp->vm_endpoint, dst_vmp->vm_endpoint); dst_vmp->vm_endpoint, src_vmp->vm_endpoint);
#endif #endif
r = map_proc_copy_from(src_vmp, dst_vmp, vr); assert(end_vr);
r = map_proc_copy_range(src_vmp, dst_vmp, start_vr, end_vr);
if(r != OK) { if(r != OK) {
return r; return r;
} }
} }
}
return OK; return OK;
} }
@ -355,3 +364,25 @@ int do_getrusage(message *m)
return sys_datacopy(SELF, (vir_bytes) &r_usage, m->m_source, return sys_datacopy(SELF, (vir_bytes) &r_usage, m->m_source,
m->m_lc_vm_rusage.addr, (vir_bytes) sizeof(r_usage)); m->m_lc_vm_rusage.addr, (vir_bytes) sizeof(r_usage));
} }
/*===========================================================================*
* adjust_proc_refs *
*===========================================================================*/
void adjust_proc_refs()
{
struct vmproc *vmp;
region_iter iter;
/* Fix up region parents. */
for(vmp = vmproc; vmp < &vmproc[VMP_NR]; vmp++) {
struct vir_region *vr;
if(!(vmp->vm_flags & VMF_INUSE))
continue;
region_start_iter_least(&vmp->vm_regions_avl, &iter);
while((vr = region_get_iter(&iter))) {
USE(vr, vr->parent = vmp;);
region_incr_iter(&iter);
}
}
}

View file

@ -79,4 +79,10 @@
#define VM_MMAPBASE VM_PAGE_SIZE #define VM_MMAPBASE VM_PAGE_SIZE
#endif #endif
extern char _end;
#define VM_OWN_HEAPSTART ((vir_bytes) (&_end))
#define VM_OWN_HEAPBASE roundup(VM_OWN_HEAPSTART, VM_PAGE_SIZE)
#define VM_OWN_MMAPBASE (VM_OWN_HEAPBASE+1024*1024*1024)
#define VM_OWN_MMAPTOP (VM_OWN_MMAPBASE+100 * 1024 * 1024)
#endif #endif

View file

@ -35,5 +35,6 @@ struct vmproc {
#define VMF_INUSE 0x001 /* slot contains a process */ #define VMF_INUSE 0x001 /* slot contains a process */
#define VMF_EXITING 0x002 /* PM is cleaning up this process */ #define VMF_EXITING 0x002 /* PM is cleaning up this process */
#define VMF_WATCHEXIT 0x008 /* Store in queryexit table */ #define VMF_WATCHEXIT 0x008 /* Store in queryexit table */
#define VMF_VM_INSTANCE 0x010 /* This is a VM process instance */
#endif #endif