minix/minix/lib/libsys/sef_init.c
David van Moolenbroek 2867e60add SEF: query VM about holes during state transfer
The 'memory' service has holes in its data section, which causes
problems during state transfer.  Since VM cannot handle page faults
during a multicomponent-with-VM live update, the state transfer must
ensure that no page faults occur during copying.  Therefore, we now
query VM about the regions to copy, thus skipping holes.  While the
solution is not ideal, it is sufficiently generic that it can be used
for the data section state transfer of all processes, and possibly
for state transfer of other regions in the future as well.

Change-Id: I2a71383a18643ebd36956c396fbd22c8fd137202
2015-09-17 13:43:06 +00:00

476 lines
16 KiB
C

#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <machine/vmparam.h>
#include <minix/sysutil.h>
#include "syslib.h"
/* SEF Init callbacks. */
static struct sef_init_cbs {
sef_cb_init_t sef_cb_init_fresh;
sef_cb_init_t sef_cb_init_lu;
sef_cb_init_t sef_cb_init_restart;
sef_cb_init_response_t sef_cb_init_response;
} sef_init_cbs = {
SEF_CB_INIT_FRESH_DEFAULT,
SEF_CB_INIT_LU_DEFAULT,
SEF_CB_INIT_RESTART_DEFAULT,
SEF_CB_INIT_RESPONSE_DEFAULT
};
/* SEF Init prototypes for sef_startup(). */
int do_sef_rs_init(endpoint_t old_endpoint);
int do_sef_init_request(message *m_ptr);
/* Debug. */
EXTERN char* sef_debug_header(void);
/* Information about SELF. */
EXTERN endpoint_t sef_self_endpoint;
EXTERN endpoint_t sef_self_priv_flags;
EXTERN endpoint_t sef_self_init_flags;
#ifndef ST_STACK_REFS_BUFF_SIZE
#define ST_STACK_REFS_BUFF_SIZE 1024
#endif
/*===========================================================================*
* process_init *
*===========================================================================*/
static int process_init(int type, sef_init_info_t *info)
{
/* Process initialization. */
int r, result, debug_result_found, is_def_cb;
cp_grant_id_t gid;
message m;
/* Debug. */
#if SEF_INIT_DEBUG
sef_init_debug_begin();
sef_init_dprint("%s. Got a SEF Init request of type %d, flags 0x%08x, rproctab_gid %d, ep %d, old ep %d, restarts %d. About to init.\n",
sef_debug_header(), type, info->flags, info->rproctab_gid, info->endpoint, info->old_endpoint, info->restarts);
sef_init_debug_end();
#endif
/* Clear any IPC filter. */
r = sys_statectl(SYS_STATE_CLEAR_IPC_FILTERS, 0, 0);
assert(r == OK);
/* Create grant for state transfer. */
gid = cpf_grant_direct(sef_self_endpoint, 0, ULONG_MAX, CPF_READ);
if(!GRANT_VALID(gid)) {
panic("unable to create grant for state transfer");
}
if(gid != SEF_STATE_TRANSFER_GID) {
panic("bad state transfer gid");
}
/* If debug init flags are allowed, process them first. */
debug_result_found = 0;
if(SEF_INIT_ALLOW_DEBUG_INIT_FLAGS) {
int flags = info->flags;
if(flags & SEF_INIT_CRASH) {
result = sef_cb_init_crash(type, info);
debug_result_found = 1;
}
else if(flags & SEF_INIT_FAIL) {
result = sef_cb_init_fail(type, info);
debug_result_found = 1;
}
else if(flags & SEF_INIT_TIMEOUT) {
result = sef_cb_init_timeout(type, info);
debug_result_found = 1;
}
}
if(!debug_result_found) {
/* Let the callback code handle the specific initialization type. */
is_def_cb = info->flags & SEF_INIT_DEFCB;
switch(type) {
case SEF_INIT_FRESH:
result = is_def_cb ? SEF_CB_INIT_FRESH_DEFAULT(type, info)
: sef_init_cbs.sef_cb_init_fresh(type, info);
break;
case SEF_INIT_LU:
result = is_def_cb ? SEF_CB_INIT_LU_DEFAULT(type, info)
: sef_init_cbs.sef_cb_init_lu(type, info);
break;
case SEF_INIT_RESTART:
result = is_def_cb ? SEF_CB_INIT_RESTART_DEFAULT(type, info)
: sef_init_cbs.sef_cb_init_restart(type, info);
break;
default:
/* Not a valid SEF init type. */
result = EINVAL;
break;
}
}
memset(&m, 0, sizeof(m));
m.m_source = sef_self_endpoint;
m.m_type = RS_INIT;
m.m_rs_init.result = result;
r = sef_init_cbs.sef_cb_init_response(&m);
if (r != OK) {
return r;
}
/* See if we need to unmap the initialization buffer. */
if(info->init_buff_cleanup_start) {
void *addrstart = info->init_buff_cleanup_start;
size_t len = info->init_buff_len - (size_t)((char*)info->init_buff_cleanup_start - (char*)info->init_buff_start);
r = sef_munmap(addrstart, len, VM_MUNMAP);
if(r != OK) {
printf("process_init: warning: munmap failed for init buffer\n");
}
}
/* Tell the kernel about the grant table. */
cpf_reload();
/* Tell the kernel about the senda table. */
r = senda_reload();
if(r != OK) {
printf("process_init: warning: senda_reload failed\n");
}
/* Tell the kernel about the state table. */
sys_statectl(SYS_STATE_SET_STATE_TABLE, sef_llvm_state_table_addr(), 0);
return r;
}
/*===========================================================================*
* do_sef_rs_init *
*===========================================================================*/
int do_sef_rs_init(endpoint_t old_endpoint)
{
/* Special SEF Init for RS. */
int r;
int type;
sef_init_info_t info;
memset(&info, 0, sizeof(info));
/* Get init parameters from SEF. */
type = SEF_INIT_FRESH;
if(sef_self_priv_flags & LU_SYS_PROC) {
type = SEF_INIT_LU;
}
else if(sef_self_priv_flags & RST_SYS_PROC) {
type = SEF_INIT_RESTART;
}
info.flags = sef_self_init_flags;
info.rproctab_gid = GRANT_INVALID;
info.endpoint = sef_self_endpoint;
info.old_endpoint = old_endpoint;
info.restarts = 0;
/* Get init buffer details from VM. */
info.init_buff_start = NULL;
info.init_buff_len = 0;
if(type != SEF_INIT_FRESH) {
r = vm_memctl(RS_PROC_NR, VM_RS_MEM_GET_PREALLOC_MAP,
&info.init_buff_start, &info.init_buff_len);
if(r != OK) {
printf("do_sef_rs_init: vm_memctl failed\n");
}
}
info.init_buff_cleanup_start = info.init_buff_start;
/* Peform initialization. */
r = process_init(type, &info);
return r;
}
/*===========================================================================*
* do_sef_init_request *
*===========================================================================*/
int do_sef_init_request(message *m_ptr)
{
/* Handle a SEF Init request. */
int r;
int type;
sef_init_info_t info;
memset(&info, 0, sizeof(info));
/* Get init parameters from message. */
type = m_ptr->m_rs_init.type;
info.flags = m_ptr->m_rs_init.flags;
info.rproctab_gid = m_ptr->m_rs_init.rproctab_gid;
info.endpoint = sef_self_endpoint;
info.old_endpoint = m_ptr->m_rs_init.old_endpoint;
info.restarts = m_ptr->m_rs_init.restarts;
info.init_buff_start = (void*) m_ptr->m_rs_init.buff_addr;
info.init_buff_cleanup_start = info.init_buff_start;
info.init_buff_len = m_ptr->m_rs_init.buff_len;
/* Peform initialization. */
r = process_init(type, &info);
return r;
}
/*===========================================================================*
* sef_setcb_init_fresh *
*===========================================================================*/
void sef_setcb_init_fresh(sef_cb_init_t cb)
{
assert(cb != NULL);
sef_init_cbs.sef_cb_init_fresh = cb;
}
/*===========================================================================*
* sef_setcb_init_lu *
*===========================================================================*/
void sef_setcb_init_lu(sef_cb_init_t cb)
{
assert(cb != NULL);
sef_init_cbs.sef_cb_init_lu = cb;
}
/*===========================================================================*
* sef_setcb_init_restart *
*===========================================================================*/
void sef_setcb_init_restart(sef_cb_init_t cb)
{
assert(cb != NULL);
sef_init_cbs.sef_cb_init_restart = cb;
}
/*===========================================================================*
* sef_setcb_init_response *
*===========================================================================*/
void sef_setcb_init_response(sef_cb_init_response_t cb)
{
assert(cb != NULL);
sef_init_cbs.sef_cb_init_response = cb;
}
/*===========================================================================*
* sef_cb_init_null *
*===========================================================================*/
int sef_cb_init_null(int UNUSED(type),
sef_init_info_t *UNUSED(info))
{
return OK;
}
/*===========================================================================*
* sef_cb_init_response_null *
*===========================================================================*/
int sef_cb_init_response_null(message * UNUSED(m_ptr))
{
return ENOSYS;
}
/*===========================================================================*
* sef_cb_init_fail *
*===========================================================================*/
int sef_cb_init_fail(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
return ENOSYS;
}
/*===========================================================================*
* sef_cb_init_reset *
*===========================================================================*/
int sef_cb_init_reset(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
/* Tell RS to reincarnate us, with no old resources, and a new endpoint. */
return ERESTART;
}
/*===========================================================================*
* sef_cb_init_crash *
*===========================================================================*/
int sef_cb_init_crash(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
panic("Simulating a crash at initialization time...\n");
return OK;
}
/*===========================================================================*
* sef_cb_init_timeout *
*===========================================================================*/
int sef_cb_init_timeout(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
message m;
int status;
printf("Simulating a timeout at initialization time...\n");
ipc_receive(IDLE, &m, &status);
return EBADCALL;
}
/*===========================================================================*
* sef_cb_init_restart_generic *
*===========================================================================*/
int sef_cb_init_restart_generic(int type, sef_init_info_t *info)
{
/* Always resort to simple identity transfer for self updates. */
if (type == SEF_INIT_LU && (info->flags & SEF_LU_SELF))
return sef_cb_init_identity_state_transfer(type, info);
/* Can only handle restart otherwise. */
if(type != SEF_INIT_RESTART) {
printf("sef_cb_init_restart_generic: init failed\n");
return ENOSYS;
}
/* Perform instrumentation-supported checkpoint-restart. */
return sef_llvm_ltckpt_restart(type, info);
}
/*===========================================================================*
* sef_cb_init_identity_state_transfer *
*===========================================================================*/
int sef_cb_init_identity_state_transfer(int type, sef_init_info_t *info)
{
extern char *_brksize;
extern char *_etext;
int r;
char *old_brksize, *new_brksize;
char stack_buff[ST_STACK_REFS_BUFF_SIZE];
vir_bytes data_start;
size_t size;
/* Identity state transfer is for crash recovery and self update only. */
if(type != SEF_INIT_RESTART && (type != SEF_INIT_LU || !(info->flags & SEF_LU_SELF))) {
printf("sef_cb_init_identity_state_transfer: state transfer failed\n");
return ENOSYS;
}
/* Save stack refs. */
sef_llvm_stack_refs_save(stack_buff);
old_brksize = _brksize;
data_start = (vir_bytes)&_etext;
#if SEF_ST_DEBUG
printf("sef_cb_init_identity_state_transfer: _brksize = 0x%08x, _etext = 0x%08x, data_start = 0x%08x\n",
_brksize, &_etext, data_start);
#endif
/* Transfer data. */
size = (size_t)(_brksize - data_start);
r = sef_copy_state_region(info, data_start, size, data_start,
TRUE /*may_have_holes*/);
if (r != OK)
return r;
new_brksize = _brksize;
/* Transfer heap if necessary. */
if(sef_self_endpoint != VM_PROC_NR && old_brksize != new_brksize) {
#if SEF_ST_DEBUG
printf("sef_cb_init_identity_state_transfer: brk() for new_brksize = 0x%08x\n",
new_brksize);
#endif
/* Extend heap first. */
_brksize = old_brksize;
r = sef_llvm_real_brk(new_brksize);
if(r != OK) {
printf("sef_cb_init_identity_state_transfer: brk failed\n");
return EFAULT;
}
/* Transfer state on the heap. */
assert(_brksize == new_brksize);
size = (size_t)(_brksize - old_brksize);
r = sef_copy_state_region(info, (vir_bytes) old_brksize, size,
(vir_bytes) old_brksize, FALSE /*may_have_holes*/);
if(r != OK) {
printf("sef_cb_init_identity_state_transfer: extended heap transfer failed\n");
return r;
}
}
/* Restore stack refs. */
sef_llvm_stack_refs_restore(stack_buff);
return OK;
}
/*===========================================================================*
* sef_cb_init_lu_identity_as_restart *
*===========================================================================*/
int sef_cb_init_lu_identity_as_restart(int type, sef_init_info_t *info)
{
/* Can only handle live update. */
if(type != SEF_INIT_LU) {
printf("sef_cb_init_lu_identity_as_restart: init failed\n");
return ENOSYS;
}
/* Resort to restart callback only for identity updates, ignore other cases. */
if(SEF_LU_IS_IDENTITY_UPDATE(info->flags)) {
if((info->flags & (SEF_INIT_DEFCB|SEF_INIT_SCRIPT_RESTART))
|| sef_init_cbs.sef_cb_init_restart == sef_cb_init_reset) {
/* Use stateful restart callback when necessary. */
return SEF_CB_INIT_RESTART_STATEFUL(type, info);
}
return sef_init_cbs.sef_cb_init_restart(type, info);
}
return ENOSYS;
}
/*===========================================================================*
* sef_cb_init_lu_generic *
*===========================================================================*/
int sef_cb_init_lu_generic(int type, sef_init_info_t *info)
{
/* Can only handle live update. */
if(type != SEF_INIT_LU) {
printf("sef_cb_init_lu_generic: init failed\n");
return ENOSYS;
}
/* Resort to restart callback for identity updates. */
if(SEF_LU_IS_IDENTITY_UPDATE(info->flags)) {
return sef_cb_init_lu_identity_as_restart(type, info);
}
/* Perform state transfer updates in all the other cases. */
return sef_st_state_transfer(info);
}
/*===========================================================================*
* sef_cb_init_response_rs_reply *
*===========================================================================*/
int sef_cb_init_response_rs_reply(message *m_ptr)
{
int r;
/* Inform RS that we completed initialization with the given result. */
r = ipc_sendrec(RS_PROC_NR, m_ptr);
return r;
}
/*===========================================================================*
* sef_cb_init_response_rs_asyn_once *
*===========================================================================*/
int sef_cb_init_response_rs_asyn_once(message *m_ptr)
{
/* This response function is used by VM to avoid a boot-time deadlock. */
int r;
/* Inform RS that we completed initialization, asynchronously. */
r = asynsend3(RS_PROC_NR, m_ptr, AMF_NOREPLY);
/* Use a blocking reply call next time. */
sef_setcb_init_response(SEF_CB_INIT_RESPONSE_DEFAULT);
return r;
}