From 63483e02e6a5924979f1c177070e18c56323a45c Mon Sep 17 00:00:00 2001 From: Cristiano Giuffrida Date: Wed, 12 Mar 2014 00:13:37 +0100 Subject: [PATCH] vm: Improve live update support. Change-Id: I02da3ea32cd05c4ed84a6e199236e5df6e25cb60 --- minix/lib/libsys/vm_memctl.c | 2 +- minix/servers/vm/exit.c | 4 +++ minix/servers/vm/glo.h | 1 + minix/servers/vm/main.c | 4 +++ minix/servers/vm/pagetable.c | 12 +++---- minix/servers/vm/proto.h | 13 ++++--- minix/servers/vm/region.c | 37 ++++++++++++++++---- minix/servers/vm/region.h | 5 +-- minix/servers/vm/rs.c | 65 ++++++++++++++++++++++++---------- minix/servers/vm/utility.c | 67 ++++++++++++++++++++++++++---------- minix/servers/vm/vm.h | 6 ++++ minix/servers/vm/vmproc.h | 1 + 12 files changed, 156 insertions(+), 61 deletions(-) diff --git a/minix/lib/libsys/vm_memctl.c b/minix/lib/libsys/vm_memctl.c index 0637476cc..35d9b171e 100644 --- a/minix/lib/libsys/vm_memctl.c +++ b/minix/lib/libsys/vm_memctl.c @@ -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; } diff --git a/minix/servers/vm/exit.c b/minix/servers/vm/exit.c index a62a7d490..dd0ee6d63 100644 --- a/minix/servers/vm/exit.c +++ b/minix/servers/vm/exit.c @@ -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. */ diff --git a/minix/servers/vm/glo.h b/minix/servers/vm/glo.h index d4c1e71b1..d808aa213 100644 --- a/minix/servers/vm/glo.h +++ b/minix/servers/vm/glo.h @@ -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; diff --git a/minix/servers/vm/main.c b/minix/servers/vm/main.c index 0fd120372..3e20a2da7 100644 --- a/minix/servers/vm/main.c +++ b/minix/servers/vm/main.c @@ -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; } /*===========================================================================* diff --git a/minix/servers/vm/pagetable.c b/minix/servers/vm/pagetable.c index e0fe60af2..342774d37 100644 --- a/minix/servers/vm/pagetable.c +++ b/minix/servers/vm/pagetable.c @@ -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; diff --git a/minix/servers/vm/proto.h b/minix/servers/vm/proto.h index b96c31e27..79d3ac4c6 100644 --- a/minix/servers/vm/proto.h +++ b/minix/servers/vm/proto.h @@ -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); diff --git a/minix/servers/vm/region.c b/minix/servers/vm/region.c index 84b308698..5816b747c 100644 --- a/minix/servers/vm/region.c +++ b/minix/servers/vm/region.c @@ -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 * *========================================================================*/ diff --git a/minix/servers/vm/region.h b/minix/servers/vm/region.h index 29982abbd..934ec79b6 100644 --- a/minix/servers/vm/region.h +++ b/minix/servers/vm/region.h @@ -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 diff --git a/minix/servers/vm/rs.c b/minix/servers/vm/rs.c index 3587e8617..067eb9331 100644 --- a/minix/servers/vm/rs.c +++ b/minix/servers/vm/rs.c @@ -16,6 +16,8 @@ #include #include +#include + #include #include #include @@ -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; } diff --git a/minix/servers/vm/utility.c b/minix/servers/vm/utility.c index 17edbe2c1..63a6217ec 100644 --- a/minix/servers/vm/utility.c +++ b/minix/servers/vm/utility.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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); + } + } +} + diff --git a/minix/servers/vm/vm.h b/minix/servers/vm/vm.h index 3a6e56acc..751e7a819 100644 --- a/minix/servers/vm/vm.h +++ b/minix/servers/vm/vm.h @@ -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 diff --git a/minix/servers/vm/vmproc.h b/minix/servers/vm/vmproc.h index a174714a4..0ec2a8219 100644 --- a/minix/servers/vm/vmproc.h +++ b/minix/servers/vm/vmproc.h @@ -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