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) {
*addr = m.VM_RS_CTL_ADDR;
}
if(addr) {
if(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);
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. */

View file

@ -40,3 +40,4 @@ EXTERN mem_type_t mem_type_anon, /* anonymous memory */
/* total number of memory 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 */
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;
} sparepagedirs[SPAREPAGEDIRS];
extern char _end;
#define is_staticaddr(v) ((vir_bytes) (v) < (vir_bytes) &_end)
#define is_staticaddr(v) ((vir_bytes) (v) < VM_OWN_HEAPSTART)
#define MAX_KERNMAPPINGS 10
static struct {
@ -154,10 +153,8 @@ static u32_t findhole(int pages)
u32_t holev = NO_MEM;
int holesize = -1;
vmin = (vir_bytes) (&_end); /* marks end of VM BSS */
vmin += 1024*1024*1024; /* reserve 1GB virtual address space for VM heap */
vmin &= ARCH_VM_ADDR_MASK;
vmax = vmin + 100 * 1024 * 1024; /* allow 100MB of address space for VM */
vmin = VM_OWN_MMAPBASE;
vmax = VM_OWN_MMAPTOP;
/* Input sanity check. */
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)
{
/* Transfer mappings to page dir and page tables from source process and
* destination process. Make sure all the mappings are above the stack, not
* to corrupt valid mappings in the data segment of the destination process.
* destination process.
*/
int pde, r;
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 do_info(message *);
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);
/* 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_free_proc(struct vmproc *vmp);
int map_proc_copy(struct vmproc *dst, struct vmproc *src);
int map_proc_copy_from(struct vmproc *dst, struct vmproc *src, struct
vir_region *start_src_vr);
int map_proc_copy_range(struct vmproc *dst, struct vmproc *src, struct
vir_region *start_src_vr, struct vir_region *end_src_vr);
struct vir_region *map_lookup(struct vmproc *vmp, vir_bytes addr,
struct phys_region **pr);
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,
struct phys_region *pr);
struct vir_region * map_region_lookup_tag(struct vmproc *vmp, u32_t
tag);
void map_region_set_tag(struct vir_region *vr, u32_t tag);
u32_t map_region_get_tag(struct vir_region *vr);
struct vir_region* map_region_lookup_type(struct vmproc *vmp, u32_t flags);
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);
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. */
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,
struct vir_region *start_src_vr)
int map_proc_copy_range(struct vmproc *dst, struct vmproc *src,
struct vir_region *start_src_vr, struct vir_region *end_src_vr)
{
struct vir_region *vr;
region_iter v_iter;
if(!start_src_vr)
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);
region_start_iter(&src->vm_regions_avl, &v_iter,
start_src_vr->vaddr, AVL_EQUAL);
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);
@ -982,6 +984,9 @@ int map_proc_copy_from(struct vmproc *dst, struct vmproc *src,
}
}
#endif
if(vr == end_src_vr) {
break;
}
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 *
*========================================================================*/

View file

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

View file

@ -16,6 +16,8 @@
#include <minix/bitmap.h>
#include <minix/rs.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <env.h>
@ -77,6 +79,7 @@ int do_rs_update(message *m_ptr)
src_e = m_ptr->m_lsys_vm_update.src;
dst_e = m_ptr->m_lsys_vm_update.dst;
sys_upd_flags = m_ptr->m_lsys_vm_update.flags;
reply_e = m_ptr->m_source;
/* Lookup slots for source and destination process. */
if(vm_isokendpt(src_e, &src_p) != OK) {
@ -90,6 +93,14 @@ int do_rs_update(message *m_ptr)
}
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. */
r = sys_update(src_e, dst_e,
sys_upd_flags & SF_VM_ROLLBACK ? SYS_UPD_ROLLBACK : 0);
@ -102,21 +113,22 @@ int do_rs_update(message *m_ptr)
if(r != OK) {
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) {
return r;
}
pt_bind(&src_vmp->vm_pt, src_vmp);
pt_bind(&dst_vmp->vm_pt, dst_vmp);
/* Reply, update-aware. */
reply_e = m_ptr->m_source;
if(reply_e == src_e) reply_e = dst_e;
else if(reply_e == dst_e) reply_e = src_e;
m_ptr->m_type = OK;
r = ipc_send(reply_e, m_ptr);
if(r != OK) {
panic("ipc_send() error");
/* Reply in case of external request, update-aware. */
if(reply_e != VM_PROC_NR) {
if(reply_e == src_e) reply_e = dst_e;
else if(reply_e == dst_e) reply_e = src_e;
m_ptr->m_type = OK;
r = ipc_send(reply_e, m_ptr);
if(r != OK) {
panic("ipc_send() error");
}
}
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];
/* 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. */
r = map_pin_memory(new_vm_vmp);
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,
vir_bytes *addr, size_t *len)
{
#if 0
struct vir_region *vr;
vir_bytes base, top;
int is_vm;
if(*len <= 0) {
return EINVAL;
}
*len = CLICK_CEIL(*len);
if(!(vr = map_page_region(vmp, VM_DATATOP - *len, VM_DATATOP, *len,
MAP_NONE, VR_ANON|VR_WRITABLE|VR_CONTIG, MF_PREALLOC))) {
is_vm = (vmp->vm_endpoint == VM_PROC_NR);
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;
}
map_region_set_tag(vr, VRT_PREALLOC_MAP);
*addr = arch_map2vir(vmp, vr->vaddr);
#endif
vr->flags |= VR_PREALLOC_MAP;
*addr = vr->vaddr;
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,
vir_bytes *addr, size_t *len)
{
#if 0
struct vir_region *vr;
vr = map_region_lookup_tag(vmp, VRT_PREALLOC_MAP);
vr = map_region_lookup_type(vmp, VR_PREALLOC_MAP);
if(!vr) {
*addr = 0;
*len = 0;
}
else {
*addr = arch_map2vir(vmp, vr->vaddr);
*addr = vr->vaddr;
*len = vr->length;
}
#endif
return OK;
}

View file

@ -18,6 +18,7 @@
#include <minix/syslib.h>
#include <minix/type.h>
#include <minix/bitmap.h>
#include <minix/rs.h>
#include <string.h>
#include <errno.h>
#include <env.h>
@ -219,20 +220,22 @@ int swap_proc_slot(struct vmproc *src_vmp, struct vmproc *dst_vmp)
/*===========================================================================*
* 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 r;
struct vir_region *start_vr, *end_vr;
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 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);
#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) {
printf("swap_proc_dyn_data: pt_map_in_range failed\n");
return r;
@ -249,23 +252,29 @@ int swap_proc_dyn_data(struct vmproc *src_vmp, struct vmproc *dst_vmp)
map_setparent(src_vmp);
map_setparent(dst_vmp);
/* For regular processes, transfer regions above the stack now.
* In case of rollback, we need to skip this step. To sandbox the
* new instance and prevent state corruption on rollback, we share all
* the regions between the two instances as COW.
/* Don't transfer mmapped regions if not required. */
if(is_vm || (sys_upd_flags & (SF_VM_ROLLBACK|SF_VM_NOMMAP))) {
return OK;
}
/* 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) {
struct vir_region *vr;
vr = map_lookup(dst_vmp, VM_STACKTOP, NULL);
if(vr && !map_lookup(src_vmp, VM_STACKTOP, NULL)) {
start_vr = region_search(&dst_vmp->vm_regions_avl, VM_MMAPBASE, AVL_GREATER_EQUAL);
end_vr = region_search(&dst_vmp->vm_regions_avl, VM_MMAPTOP, AVL_LESS);
if(start_vr) {
#if LU_DEBUG
printf("VM: swap_proc_dyn_data: tranferring regions above the stack from %d to %d\n",
src_vmp->vm_endpoint, dst_vmp->vm_endpoint);
printf("VM: swap_proc_dyn_data: tranferring memory mapped regions from %d to %d\n",
dst_vmp->vm_endpoint, src_vmp->vm_endpoint);
#endif
r = map_proc_copy_from(src_vmp, dst_vmp, vr);
if(r != OK) {
return r;
}
assert(end_vr);
r = map_proc_copy_range(src_vmp, dst_vmp, start_vr, end_vr);
if(r != OK) {
return r;
}
}
@ -355,3 +364,25 @@ int do_getrusage(message *m)
return sys_datacopy(SELF, (vir_bytes) &r_usage, m->m_source,
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
#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

View file

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