vm: Improve live update support.
Change-Id: I02da3ea32cd05c4ed84a6e199236e5df6e25cb60
This commit is contained in:
parent
53398d733f
commit
63483e02e6
12 changed files with 156 additions and 61 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 *
|
||||
*========================================================================*/
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue