#include <machine/archtypes.h>
#include <minix/timers.h>
#include <minix/sysutil.h>
+#include <minix/vm.h>
#include "kernel/config.h"
#include "kernel/const.h"
#include "kernel/type.h"
#include "kernel/proc.h"
+EXTERN endpoint_t sef_self_endpoint;
+
/* SEF Live update prototypes for sef_receive(). */
void do_sef_st_before_receive(void);
* sef_copy_state_region *
*===========================================================================*/
int sef_copy_state_region(sef_init_info_t *info,
- vir_bytes address, size_t size, vir_bytes dst_address)
+ vir_bytes address, size_t size, vir_bytes dst_address, int may_have_holes)
{
+ vir_bytes base, top, target;
+ struct vm_region_info vri;
int r;
+
+ base = address;
+
if(sef_copy_state_region_ctl(info, &address, &dst_address)) {
#if STATE_TRANS_DEBUG
printf("sef_copy_state_region: memcpy %d bytes, addr = 0x%08x -> 0x%08x...\n",
#endif
/* memcpy region from current state */
memcpy((void*) dst_address, (void *)address, size);
+ } else if (may_have_holes && sef_self_endpoint != VM_PROC_NR &&
+ vm_info_region(info->old_endpoint, &vri, 1, &base) == 1) {
+ /* Perform a safe copy of a region of the old state. The section may
+ * contain holes, so ask VM for the actual regions within the data
+ * section and transfer each one separately. The alternative, just
+ * copying until a page fault happens, is not possible in the multi-
+ * component-with-VM live update case, where VM may not receive page
+ * faults during the live update window. For now, we use the region
+ * iteration approach for the data section only; other cases have not
+ * been tested, but may work as well.
+ */
+#if STATE_TRANS_DEBUG
+ printf("sef_copy_state_region: copying %d bytes, addr = 0x%08x -> "
+ "0x%08x, gid = %d, source = %d, with holes...\n", size, address,
+ dst_address, SEF_STATE_TRANSFER_GID, info->old_endpoint);
+#endif
+
+ /* The following is somewhat of a hack: the start of the data section
+ * may in fact not be page-aligned and may be part of the last page of
+ * of the preceding (text) section. Therefore, if the first region we
+ * find starts above the known base address, blindly copy the area in
+ * between.
+ */
+ if (vri.vri_addr > address) {
+ if ((r = sys_safecopyfrom(info->old_endpoint, SEF_STATE_TRANSFER_GID,
+ address, dst_address, vri.vri_addr - address)) != OK) {
+#if STATE_TRANS_DEBUG
+ printf("sef_copy_state_region: sys_safecopyfrom failed\n");
+#endif
+ return r;
+ }
+ }
+
+ top = address + size;
+ do {
+ assert(vri.vri_addr >= address);
+ if (vri.vri_addr >= top)
+ break;
+ if (vri.vri_length > top - vri.vri_addr)
+ vri.vri_length = top - vri.vri_addr;
+ target = dst_address + (vri.vri_addr - address);
+ if ((r = sys_safecopyfrom(info->old_endpoint,
+ SEF_STATE_TRANSFER_GID, vri.vri_addr, target,
+ vri.vri_length)) != OK) {
+#if STATE_TRANS_DEBUG
+ printf("sef_copy_state_region: sys_safecopyfrom failed\n");
+#endif
+ return r;
+ }
+ /* Save on a VM call if the next address is already too high. */
+ if (base >= top)
+ break;
+ } while (vm_info_region(info->old_endpoint, &vri, 1, &base) == 1);
} else {
+ /* Perform a safe copy of a region of the old state, without taking into
+ * account any holes. This is the default for anything but the data
+ * section, with a few additioanl exceptions: VM can't query VM, so
+ * simply assume there are no holes; also, if we fail to get one region
+ * for the old process (and this is presumably possible if its heap is
+ * so small it fits in the last text page, see above), we also just
+ * blindly copy over the entire data section.
+ */
#if STATE_TRANS_DEBUG
- printf("sef_copy_state_region: copying %d bytes, addr = 0x%08x -> 0x%08x, gid = %d, source = %d...\n",
- size, address, dst_address, SEF_STATE_TRANSFER_GID, info->old_endpoint);
+ printf("sef_copy_state_region: copying %d bytes, addr = 0x%08x -> "
+ "0x%08x, gid = %d, source = %d, without holes...\n", size, address,
+ dst_address, SEF_STATE_TRANSFER_GID, info->old_endpoint);
#endif
- /* Perform a safe copy of a region of the old state. */
- if((r = sys_safecopyfrom(info->old_endpoint, SEF_STATE_TRANSFER_GID, address,
- dst_address, size)) != OK) {
+ if ((r = sys_safecopyfrom(info->old_endpoint, SEF_STATE_TRANSFER_GID,
+ address, dst_address, size)) != OK) {
#if STATE_TRANS_DEBUG
printf("sef_copy_state_region: sys_safecopyfrom failed\n");
#endif
return r;
- }
+ }
}
return OK;
}
if (sef_copy_state_region(info, old_priv.s_state_table
- , sef_llvm_state_table_size(), (vir_bytes) addr))
+ , sef_llvm_state_table_size(), (vir_bytes) addr, FALSE /*may_have_holes*/))
{
printf("ERROR. state table transfer failed\n");
return EGENERIC;
{
assert(info_opaque != NULL && "Invalid info_opaque pointer.");
return sef_copy_state_region((sef_init_info_t *)(info_opaque),
- (vir_bytes) address, size, (vir_bytes) dst_address);
+ (vir_bytes) address, size, (vir_bytes) dst_address,
+ FALSE /*may_have_holes*/);
}
/*===========================================================================*
m.m_source = VM_PROC_NR;
for(i=0;i < NR_SYS_PROCS;i++) {
if(rprocpub[i].in_use && rprocpub[i].old_endpoint != NONE) {
- if(num_elements <= IPCF_MAX_ELEMENTS-3) {
+ if(num_elements <= IPCF_MAX_ELEMENTS-5) {
+ /* VM_BRK is needed for normal operation during the live
+ * update. VM_INFO is needed for state transfer in the
+ * light of holes. Pagefaults and handle-memory requests
+ * are blocked intentionally, as handling these would
+ * prevent VM from being able to roll back.
+ */
ipc_filter[num_elements].flags = IPCF_MATCH_M_SOURCE;
ipc_filter[num_elements].m_source = rprocpub[i].old_endpoint;
if(!(info->flags & SEF_LU_UNSAFE)) {
ipc_filter[num_elements].m_type = VM_BRK;
}
num_elements++;
+ if(!(info->flags & SEF_LU_UNSAFE)) {
+ ipc_filter[num_elements].flags = IPCF_MATCH_M_SOURCE | IPCF_MATCH_M_TYPE;
+ ipc_filter[num_elements].m_source = rprocpub[i].old_endpoint;
+ ipc_filter[num_elements++].m_type = VM_INFO;
+ ipc_filter[num_elements].flags = IPCF_MATCH_M_SOURCE | IPCF_MATCH_M_TYPE;
+ ipc_filter[num_elements].m_source = rprocpub[i].new_endpoint;
+ ipc_filter[num_elements++].m_type = VM_INFO;
+ }
/* Make sure we can talk to any RS instance. */
if(rprocpub[i].old_endpoint == RS_PROC_NR) {
ipc_filter[num_elements].flags = IPCF_MATCH_M_SOURCE;