minix/minix/servers/vm/rs.c
Ben Gras 10e6ba68d2 vm: restartability improvements (#1)
Two bugs fixed wrt vm restartability.

	. make sure pagetable data is only allocated
	  using dynamic data instead of static spare pages
	  (bootstrap pages). They are needed for bootstrap
	  but now repeat some of the initialization so only
	  dynamic data remains. This solves the problem of
	  physical addresses changing (as static pages are
	  re-allocated for the new instance) after update.
	. pt_ptalloc has to be specified in bytes instead of
	  pde slot numbers. leaving pt_pt NULL causes mapping
	  transfers to fail because NULL happens to be mapped in
	  then and updates then happen there.
	. added some sanity checks against the above happening.

The new state is that VM can update many times, but the system
isn't fully reliable afterwards yet.

Change-Id: I7313602c740cdae8590589132291116ed921aed7
2015-09-17 13:41:26 +00:00

314 lines
7.8 KiB
C

#define _SYSTEM 1
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/ds.h>
#include <minix/endpoint.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <minix/rs.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <assert.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "region.h"
/*===========================================================================*
* do_rs_set_priv *
*===========================================================================*/
int do_rs_set_priv(message *m)
{
int r, n, nr;
struct vmproc *vmp;
bitchunk_t call_mask[VM_CALL_MASK_SIZE], *call_mask_p;
nr = m->VM_RS_NR;
if ((r = vm_isokendpt(nr, &n)) != OK) {
printf("do_rs_set_priv: bad endpoint %d\n", nr);
return EINVAL;
}
vmp = &vmproc[n];
if (m->VM_RS_BUF) {
r = sys_datacopy(m->m_source, (vir_bytes) m->VM_RS_BUF, SELF,
(vir_bytes) call_mask, sizeof(call_mask));
if (r != OK)
return r;
call_mask_p = call_mask;
} else {
if (m->VM_RS_SYS) {
printf("VM: do_rs_set_priv: sys procs don't share!\n");
return EINVAL;
}
call_mask_p = NULL;
}
acl_set(vmp, call_mask_p, m->VM_RS_SYS);
return OK;
}
/*===========================================================================*
* do_rs_update *
*===========================================================================*/
int do_rs_update(message *m_ptr)
{
endpoint_t src_e, dst_e, reply_e;
int src_p, dst_p;
struct vmproc *src_vmp, *dst_vmp;
int r, sys_upd_flags;
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) {
printf("do_rs_update: bad src endpoint %d\n", src_e);
return EINVAL;
}
src_vmp = &vmproc[src_p];
if(vm_isokendpt(dst_e, &dst_p) != OK) {
printf("do_rs_update: bad dst endpoint %d\n", dst_e);
return EINVAL;
}
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);
if(r != OK) {
return r;
}
/* Do the update in VM now. */
r = swap_proc_slot(src_vmp, dst_vmp);
if(r != OK) {
return r;
}
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 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;
}
/*===========================================================================*
* rs_memctl_make_vm_instance *
*===========================================================================*/
static int rs_memctl_make_vm_instance(struct vmproc *new_vm_vmp)
{
int r;
u32_t flags;
int verify;
struct vmproc *this_vm_vmp;
this_vm_vmp = &vmproc[VM_PROC_NR];
pt_assert(&this_vm_vmp->vm_pt);
/* 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) {
return r;
}
/* Preallocate page tables for the entire address space for both
* VM and the new VM instance.
*/
flags = 0;
verify = FALSE;
r = pt_ptalloc_in_range(&this_vm_vmp->vm_pt,
VM_OWN_HEAPBASE, VM_DATATOP, flags, verify);
if(r != OK) {
return r;
}
r = pt_ptalloc_in_range(&new_vm_vmp->vm_pt,
VM_OWN_HEAPBASE, VM_DATATOP, flags, verify);
if(r != OK) {
return r;
}
/* Let the new VM instance map VM's page tables and its own. */
r = pt_ptmap(this_vm_vmp, new_vm_vmp);
if(r != OK) {
return r;
}
r = pt_ptmap(new_vm_vmp, new_vm_vmp);
if(r != OK) {
return r;
}
pt_assert(&this_vm_vmp->vm_pt);
pt_assert(&new_vm_vmp->vm_pt);
return OK;
}
/*===========================================================================*
* rs_memctl_heap_prealloc *
*===========================================================================*/
static int rs_memctl_heap_prealloc(struct vmproc *vmp,
vir_bytes *addr, size_t *len)
{
struct vir_region *data_vr;
vir_bytes bytes;
if(*len <= 0) {
return EINVAL;
}
data_vr = region_search(&vmp->vm_regions_avl, VM_MMAPBASE, AVL_LESS);
*addr = data_vr->vaddr + data_vr->length;
bytes = *addr + *len;
return real_brk(vmp, bytes);
}
/*===========================================================================*
* rs_memctl_map_prealloc *
*===========================================================================*/
static int rs_memctl_map_prealloc(struct vmproc *vmp,
vir_bytes *addr, size_t *len)
{
struct vir_region *vr;
vir_bytes base, top;
int is_vm;
if(*len <= 0) {
return EINVAL;
}
*len = CLICK_CEIL(*len);
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;
}
vr->flags |= VR_PREALLOC_MAP;
*addr = vr->vaddr;
return OK;
}
/*===========================================================================*
* rs_memctl_get_prealloc_map *
*===========================================================================*/
static int rs_memctl_get_prealloc_map(struct vmproc *vmp,
vir_bytes *addr, size_t *len)
{
struct vir_region *vr;
vr = map_region_lookup_type(vmp, VR_PREALLOC_MAP);
if(!vr) {
*addr = 0;
*len = 0;
}
else {
*addr = vr->vaddr;
*len = vr->length;
}
return OK;
}
/*===========================================================================*
* do_rs_memctl *
*===========================================================================*/
int do_rs_memctl(message *m_ptr)
{
endpoint_t ep;
int req, r, proc_nr;
struct vmproc *vmp;
ep = m_ptr->VM_RS_CTL_ENDPT;
req = m_ptr->VM_RS_CTL_REQ;
/* Lookup endpoint. */
if ((r = vm_isokendpt(ep, &proc_nr)) != OK) {
printf("do_rs_memctl: bad endpoint %d\n", ep);
return EINVAL;
}
vmp = &vmproc[proc_nr];
/* Process request. */
switch(req)
{
case VM_RS_MEM_PIN:
/* Only actually pin RS memory if VM can recover from crashes (saves memory). */
if (num_vm_instances <= 1)
return OK;
r = map_pin_memory(vmp);
return r;
case VM_RS_MEM_MAKE_VM:
r = rs_memctl_make_vm_instance(vmp);
return r;
case VM_RS_MEM_HEAP_PREALLOC:
r = rs_memctl_heap_prealloc(vmp, (vir_bytes*) &m_ptr->VM_RS_CTL_ADDR, (size_t*) &m_ptr->VM_RS_CTL_LEN);
return r;
case VM_RS_MEM_MAP_PREALLOC:
r = rs_memctl_map_prealloc(vmp, (vir_bytes*) &m_ptr->VM_RS_CTL_ADDR, (size_t*) &m_ptr->VM_RS_CTL_LEN);
return r;
case VM_RS_MEM_GET_PREALLOC_MAP:
r = rs_memctl_get_prealloc_map(vmp, (vir_bytes*) &m_ptr->VM_RS_CTL_ADDR, (size_t*) &m_ptr->VM_RS_CTL_LEN);
return r;
default:
printf("do_rs_memctl: bad request %d\n", req);
return EINVAL;
}
}