]> Zhao Yanbai Git Server - minix.git/commitdiff
rs: New RS. 99/3099/2
authorCristiano Giuffrida <giuffrida@cs.vu.nl>
Mon, 27 Oct 2014 21:36:25 +0000 (22:36 +0100)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 16 Sep 2015 15:30:48 +0000 (15:30 +0000)
Change-Id: I46e335d5dac49104028d7cb0706b3e85de752bfe

14 files changed:
etc/rs.inet
minix/commands/service/service.c
minix/include/minix/rs.h
minix/servers/rs/Makefile
minix/servers/rs/const.h
minix/servers/rs/error.c
minix/servers/rs/main.c
minix/servers/rs/manager.c
minix/servers/rs/proto.h
minix/servers/rs/request.c [changed mode: 0755->0644]
minix/servers/rs/type.h
minix/servers/rs/update.c [new file with mode: 0644]
minix/servers/rs/utility.c
minix/servers/vm/rs.c

index 5bd9d6b70b25a9cc1486c788b22eec89932909fe..448915d7db0f7c9bad7f0691539d21152ed688b2 100755 (executable)
@@ -55,6 +55,8 @@ disabled()
 exec > /dev/console
 echo "Arguments: $@"
 
+restarts=$(grep restarts /proc/service/$1 |cut -d: -f2)
+restarts=$(( $restarts + 1 ))
 service down "$1"
 kill_by_name dhcpd
 kill_by_name nonamed
@@ -64,10 +66,10 @@ kill_by_name syslogd
 sleep 3
 if [ X`/bin/sysenv lwip` = Xyes ]
 then
-       service up /service/lwip -script /etc/rs.inet -dev /dev/ip
+       service up /service/lwip -script /etc/rs.inet -dev /dev/ip -restarts $restarts
        dhcpd --lwip &
 else
-       service up /service/inet -script /etc/rs.inet -dev /dev/ip
+       service up /service/inet -script /etc/rs.inet -dev /dev/ip -restarts $restarts
        daemonize dhcpd
 fi
 daemonize nonamed -L
index 0d6c454d4b5f290856e726190d8e61d6bd1197d3..78a2944997883a69925e2a0df14e5ab7b920c078 100644 (file)
@@ -84,7 +84,8 @@ static int known_request_types[] = {
 #define OPT_REUSE      "-r"            /* reuse executable image */
 #define OPT_NOBLOCK    "-n"            /* unblock caller immediately */
 #define OPT_REPLICA    "-p"            /* create replica for the service */
-#define OPT_BATCH      "-b"            /* batch mode */
+#define OPT_NO_BIN_EXP "-b"            /* no binary exponential backoff */
+#define OPT_BATCH      "-q"            /* batch mode */
 #define OPT_ASR_LU     "-a"            /* asr update */
 #define OPT_PREPARE_ONLY_LU    "-o"    /* prepare-only update */
 #define OPT_FORCE_SELF_LU      "-s"    /* force self update */
@@ -134,6 +135,7 @@ static int known_request_types[] = {
 #define ARG_TRG_LABELNAME "-trg-label" /* target label name */
 #define ARG_LU_IPC_BL  "-ipc_bl"       /* IPC blacklist filter */
 #define ARG_LU_IPC_WL  "-ipc_wl"       /* IPC whitelist filter */
+#define ARG_RESTARTS   "-restarts"    /* number of restarts */
 
 /* The function parse_arguments() verifies and parses the command line 
  * parameters passed to this utility. Request parameters that are needed
@@ -154,6 +156,7 @@ static char *req_config = PATH_CONFIG;
 static int custom_config_file = 0;
 static int req_lu_state = DEFAULT_LU_STATE;
 static int req_lu_maxtime = DEFAULT_LU_MAXTIME;
+static int req_restarts = 0;
 static long req_heap_prealloc = 0;
 static long req_map_prealloc = 0;
 static int req_sysctl_type = 0;
@@ -171,15 +174,16 @@ static void print_usage(char *app_name, char *problem)
   fprintf(stderr, "Warning, %s\n", problem);
   fprintf(stderr, "Usage:\n");
   fprintf(stderr,
-      "    %s [%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s] (up|run|edit|update) <binary|%s> [%s <args>] [%s <special>] [%s <major_nr>] [%s <dev_id>] [%s <ticks>] [%s <path>] [%s <name>] [%s <path>] [%s <state value|eval_expression>] [%s <time>] [%s <bytes>] [%s <bytes>] [%s <name>] [(%s|%s <src_label1,src_type1:src_label2,:,src_type3:...>)*]\n", 
-       app_name, OPT_COPY, OPT_REUSE, OPT_NOBLOCK, OPT_REPLICA, 
+      "    %s [%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s] (up|run|edit|update) <binary|%s> [%s <args>] [%s <special>] [%s <major_nr>] [%s <dev_id>] [%s <ticks>] [%s <path>] [%s <name>] [%s <path>] [%s <state value|eval_expression>] [%s <time>] [%s <bytes>] [%s <bytes>] [%s <name>] [(%s|%s <src_label1,src_type1:src_label2,:,src_type3:...>)*] [%s <restarts>]\n",
+       app_name, OPT_COPY, OPT_REUSE, OPT_NOBLOCK, OPT_REPLICA, OPT_NO_BIN_EXP,
        OPT_BATCH, OPT_ASR_LU, OPT_PREPARE_ONLY_LU, OPT_FORCE_SELF_LU,
        OPT_FORCE_INIT_CRASH, OPT_FORCE_INIT_FAIL, OPT_FORCE_INIT_TIMEOUT,
        OPT_FORCE_INIT_DEFCB, OPT_UNSAFE_LU, OPT_NOMMAP_LU, OPT_DETACH,
        OPT_NORESTART, OPT_FORCE_INIT_ST, SELF_BINARY,
        ARG_ARGS, ARG_DEV, ARG_MAJOR, ARG_DEVMANID, ARG_PERIOD,
        ARG_SCRIPT, ARG_LABELNAME, ARG_CONFIG, ARG_LU_STATE, ARG_LU_MAXTIME,
-       ARG_HEAP_PREALLOC, ARG_MAP_PREALLOC, ARG_TRG_LABELNAME, ARG_LU_IPC_BL, ARG_LU_IPC_WL);
+       ARG_HEAP_PREALLOC, ARG_MAP_PREALLOC, ARG_TRG_LABELNAME, ARG_LU_IPC_BL, ARG_LU_IPC_WL,
+       ARG_RESTARTS);
   fprintf(stderr, "    %s down <label>\n", app_name);
   fprintf(stderr, "    %s refresh <label>\n", app_name);
   fprintf(stderr, "    %s restart <label>\n", app_name);
@@ -627,6 +631,14 @@ static int parse_arguments(int argc, char **argv, u32_t *rss_flags)
                   exit(r);
               }
           }
+          else if (strcmp(argv[i], ARG_RESTARTS)==0) {
+              errno=0;
+              req_restarts = strtol(argv[i+1], &buff, 10);
+              if(errno || strcmp(buff, "") || req_restarts<0) {
+                  print_usage(argv[ARG_NAME], "bad number of restarts");
+                  exit(EINVAL);
+              }
+          }
           else {
               print_usage(argv[ARG_NAME], "unknown optional argument given");
               exit(EINVAL);
@@ -726,6 +738,8 @@ int main(int argc, char **argv)
        memset(&config, 0, sizeof(config));
        if(!parse_config(progname, custom_config_file, req_config, &config))
                errx(1, "couldn't parse config");
+       assert(config.rs_start.rss_priority < NR_SCHED_QUEUES);
+       assert(config.rs_start.rss_quantum > 0);
       }
 
       /* Set specifics */
@@ -734,6 +748,7 @@ int main(int argc, char **argv)
       config.rs_start.rss_major= req_major;
       config.rs_start.rss_period= req_period;
       config.rs_start.rss_script= req_script;
+      config.rs_start.rss_restarts= req_restarts;
       config.rs_start.devman_id= devman_id;
       config.rs_start.rss_heap_prealloc_bytes= req_heap_prealloc;
       config.rs_start.rss_map_prealloc_bytes= req_map_prealloc;
@@ -757,9 +772,6 @@ int main(int argc, char **argv)
       else
              config.rs_start.rss_scriptlen= 0;
 
-      assert(config.rs_start.rss_priority < NR_SCHED_QUEUES);
-      assert(config.rs_start.rss_quantum > 0);
-
       /* State-related data. */
       config.rs_start.rss_state_data.size =
         sizeof(config.rs_start.rss_state_data);
index 0de043e037d8b2a9d076a7eb02df7fc343e53a29..928ecc80d92e610d1749d07828a35f939f745a4b 100644 (file)
@@ -114,6 +114,7 @@ struct rs_start
        long rss_period;
        char *rss_script;
        size_t rss_scriptlen;
+       long rss_restarts;
        long rss_heap_prealloc_bytes;
        long rss_map_prealloc_bytes;
        int rss_nr_irq;
@@ -191,9 +192,10 @@ struct rprocpub {
 #define SF_USE_SCRIPT   0x200    /* set when process has restart script */
 #define SF_DET_RESTART  0x400    /* set when process detaches on restart */
 #define SF_NORESTART    0x800    /* set when process should not be restarted */
+#define SF_NO_BIN_EXP  0x1000    /* set when we should ignore binary exp. offset */
 
 #define IMM_SF          \
-    (SF_CORE_SRV | SF_SYNCH_BOOT | SF_NEED_COPY | SF_NEED_REPL) /* immutable */
+    (SF_NO_BIN_EXP | SF_CORE_SRV | SF_SYNCH_BOOT | SF_NEED_COPY | SF_NEED_REPL) /* immutable */
 
 int minix_rs_lookup(const char *name, endpoint_t *value);
 
index a19d0fa7252dbd431acb049a551d8099e64c963b..3edcb53f4b988e027f546b6ccf357d6d6356f950 100644 (file)
@@ -2,7 +2,11 @@
 
 # Makefile for Reincarnation Server (RS)
 PROG=  rs
-SRCS=  exec.c main.c request.c manager.c table.c utility.c error.c
+SRCS=  exec.c main.c request.c manager.c table.c utility.c error.c update.c
+
+.if ${USE_PCI} != "no"
+CPPFLAGS+= -DUSE_PCI
+.endif
 
 .if ${USE_PCI} != "no"
 CPPFLAGS+= -DUSE_PCI
index 6063c9d0f41c111ac444a5faa266b13e81f16e6d..050c3007d552ddd344b882d3ffb995c60d56b359 100644 (file)
@@ -22,6 +22,7 @@
 #define MAX_IPC_LIST        256         /* Max size of list for IPC target
                                          * process names
                                          */
+#define MAX_DET_RESTART      10         /* maximum number of detached restarts. */
 
 /* Flag values. */
 #define RS_IN_USE       0x001    /* set when process slot is in use */
 #define RS_LATEREPLY    0x020    /* no reply sent to RS_DOWN caller yet */
 #define RS_INITIALIZING 0x040    /* set when init is in progress */
 #define RS_UPDATING     0x080    /* set when update is in progress */
-#define RS_ACTIVE       0x100    /* set for the active instance of a service */
-#define RS_REINCARNATE  0x200    /* after exit, restart with a new endpoint */
-
-/* Sys flag values. */
-#define SF_CORE_SRV     0x001    /* set for core system services */
-#define SF_SYNCH_BOOT   0X002    /* set when process needs synch boot init */
-#define SF_NEED_COPY    0x004    /* set when process needs copy to start */
-#define SF_USE_COPY     0x008    /* set when process has a copy in memory */
-#define SF_NEED_REPL    0x010    /* set when process needs replica to start */
-#define SF_USE_REPL     0x020    /* set when process has a replica */
-#define SF_NO_BIN_EXP  0x040    /* set when we should ignore binary exp. offset */
+#define RS_PREPARE_DONE 0x100    /* set when updating and preparation is done */
+#define RS_INIT_DONE    0x200    /* set when updating and init is done */
+#define RS_INIT_PENDING 0x400    /* set when updating and init is pending */
+#define RS_ACTIVE       0x800    /* set for the active instance of a service */
+#define RS_DEAD         0x1000   /* set for an instance ready to be cleaned up */
+#define RS_CLEANUP_DETACH 0x2000   /* detach at cleanup time */
+#define RS_CLEANUP_SCRIPT 0x4000   /* run script at cleanup time */
+#define RS_REINCARNATE    0x8000   /* after exit, restart with a new endpoint */
+
+#define RS_SRV_IS_IDLE(S) (((S)->r_flags & RS_DEAD) || ((S)->r_flags & ~(RS_IN_USE|RS_ACTIVE|RS_CLEANUP_DETACH|RS_CLEANUP_SCRIPT)) == 0)
 
 /* Constants determining RS period and binary exponential backoff. */
 #define RS_INIT_T      (system_hz * 10)        /* allow T ticks for init */
@@ -56,8 +56,6 @@
 
 /* Constants for live update. */
 #define RS_DEFAULT_PREPARE_MAXTIME 2*RS_DELTA_T   /* default prepare max time */
-#define RS_MAX_PREPARE_MAXTIME     20*RS_DELTA_T  /* max prepare max time */
-
 
 /* Definitions for boot info tables. */
 #define NULL_BOOT_NR    NR_BOOT_PROCS        /* marks a null boot entry */
 /* Reply flags. */
 #define RS_DONTREPLY    0
 #define RS_REPLY        1
+#define RS_CANCEL       2
 
 /* Swap flags. */
 #define RS_DONTSWAP     0
 #define RS_SWAP         1
 
+/* Configuration constants */
+#define RS_VM_DEFAULT_MAP_PREALLOC_LEN  (1024*1024*8)
+#define RS_USE_PAGING                   0
+
+/* Update macros. */
+#define RUPDATE_INIT() memset(&rupdate, 0, sizeof(rupdate))
+#define RUPDATE_CLEAR() RUPDATE_INIT()
+
+#define RUPDATE_ITER(HEAD, RPUPD_PREV, RPUPD, B) do { \
+        RPUPD = HEAD; \
+        RPUPD_PREV = NULL; \
+        while(RPUPD) { \
+            B \
+            RPUPD_PREV = RPUPD; \
+            RPUPD = RPUPD->next_rpupd; \
+        } \
+     } while(0)
+#define RUPDATE_REV_ITER(TAIL, RPUPD_PREV, RPUPD, B) do { \
+        RPUPD = TAIL; \
+        while(RPUPD) { \
+            RPUPD_PREV = RPUPD->prev_rpupd; \
+            B \
+            RPUPD = RPUPD->prev_rpupd; \
+        } \
+     } while(0)
+
+#define RUPDATE_IS_UPDATING() (rupdate.flags & RS_UPDATING)
+#define RUPDATE_IS_VM_UPDATING() ((rupdate.flags & RS_UPDATING) && rupdate.vm_rpupd)
+#define RUPDATE_IS_VM_INIT_DONE() (rproc_ptr[_ENDPOINT_P(VM_PROC_NR)]->r_flags & RS_INIT_DONE)
+#define RUPDATE_IS_RS_UPDATING() ((rupdate.flags & RS_UPDATING) && rupdate.rs_rpupd)
+#define RUPDATE_IS_RS_INIT_DONE() (rproc_ptr[_ENDPOINT_P(RS_PROC_NR)]->r_flags & RS_INIT_DONE)
+#define RUPDATE_IS_INITIALIZING() (rupdate.flags & RS_INITIALIZING)
+#define RUPDATE_IS_UPD_SCHEDULED() (rupdate.num_rpupds > 0 && !RUPDATE_IS_UPDATING())
+#define RUPDATE_IS_UPD_MULTI() (rupdate.num_rpupds > 1)
+#define RUPDATE_IS_UPD_VM_MULTI() (rupdate.vm_rpupd && RUPDATE_IS_UPD_MULTI())
+#define SRV_IS_UPDATING(RP) ((RP)->r_flags & RS_UPDATING)
+#define SRV_IS_UPDATING_AND_INITIALIZING(RP) (((RP)->r_flags & (RS_UPDATING|RS_INITIALIZING)) == (RS_UPDATING|RS_INITIALIZING))
+#define UPD_INIT_MAXTIME(RPUPD) ((RPUPD)->prepare_maxtime != RS_DEFAULT_PREPARE_MAXTIME ? (RPUPD)->prepare_maxtime : RS_INIT_T)
+#define UPD_IS_PREPARING_ONLY(RPUPD) ((RPUPD)->lu_flags & SEF_LU_PREPARE_ONLY)
+#define SRV_IS_PREPARING_ONLY(RP) ((RP)->r_upd.rp && UPD_IS_PREPARING_ONLY(&(RP)->r_upd))
+#define UPD_IS_UPD_SCHEDULED(RPUPD) (RUPDATE_IS_UPD_SCHEDULED() && (RPUPD)->rp)
+#define SRV_IS_UPD_SCHEDULED(RP) UPD_IS_UPD_SCHEDULED(&(RP)->r_upd)
+
 #endif /* RS_CONST_H */
 
index 24d1342dfb65268655c8b80bff17d95fdacaca20..5da62acb2ddeb1f849f8b6af1647c76d3b49a7b0 100644 (file)
@@ -13,7 +13,8 @@ struct errentry {
 
 /* Initialization errors. */
 static struct errentry init_errlist[] = {
-  { ENOSYS,     "service does not support the requested initialization type"  }
+  { ENOSYS,     "service does not support the requested initialization type"  },
+  { ERESTART,     "service requested an initialization reset"  }
 };
 static const int init_nerr = sizeof(init_errlist) / sizeof(init_errlist[0]);
 
index 6b7e032a7457417090a1de73a8e65a0d9d73911f..ce3f320c2e14f3537a7f659f7bc92f28292d936a 100644 (file)
@@ -24,6 +24,10 @@ static void get_work(message *m_ptr, int *status_ptr);
 /* SEF functions and variables. */
 static void sef_local_startup(void);
 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
+static int sef_cb_init_restart(int type, sef_init_info_t *info);
+static int sef_cb_init_lu(int type, sef_init_info_t *info);
+static int sef_cb_init_response(message *m_ptr);
+static int sef_cb_lu_response(message *m_ptr);
 static void sef_cb_signal_handler(int signo);
 static int sef_cb_signal_manager(endpoint_t target, int signo);
 
@@ -54,6 +58,8 @@ int main(void)
 
   /* Main loop - get work and do it, forever. */         
   while (TRUE) {              
+      /* Perform sensitive background operations when RS is idle. */
+      rs_idle_period();
 
       /* Wait for request message. */
       get_work(&m, &ipc_status);
@@ -103,8 +109,10 @@ int main(void)
           case RS_SHUTDOWN:    result = do_shutdown(&m);       break;
           case RS_UPDATE:      result = do_update(&m);         break;
           case RS_CLONE:       result = do_clone(&m);          break;
+         case RS_UNCLONE:      result = do_unclone(&m);        break;
           case RS_EDIT:        result = do_edit(&m);           break;
-          case RS_GETSYSINFO:  result = do_getsysinfo(&m);     break;
+         case RS_SYSCTL:       result = do_sysctl(&m);         break;
+          case RS_GETSYSINFO:  result = do_getsysinfo(&m);     break;
          case RS_LOOKUP:       result = do_lookup(&m);         break;
          /* Ready messages. */
          case RS_INIT:         result = do_init_ready(&m);     break;
@@ -130,12 +138,15 @@ int main(void)
 static void sef_local_startup()
 {
   /* Register init callbacks. */
-  sef_setcb_init_response(do_init_ready);
   sef_setcb_init_fresh(sef_cb_init_fresh);
-  sef_setcb_init_restart(sef_cb_init_fail);
+  sef_setcb_init_restart(sef_cb_init_restart);
+  sef_setcb_init_lu(sef_cb_init_lu);
 
-  /* Register live update callbacks. */
-  sef_setcb_lu_response(do_upd_ready);
+  /* Register response callbacks. */
+  sef_setcb_init_response(sef_cb_init_response);
+  sef_setcb_lu_response(sef_cb_lu_response);
+
+  /* No live update support for now. */
 
   /* Register signal callbacks. */
   sef_setcb_signal_handler(sef_cb_signal_handler);
@@ -155,11 +166,14 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
   int s,i;
   int nr_image_srvs, nr_image_priv_srvs, nr_uncaught_init_srvs;
   struct rproc *rp;
+  struct rproc *replica_rp;
   struct rprocpub *rpub;
   struct boot_image image[NR_BOOT_PROCS];
   struct boot_image_priv *boot_image_priv;
   struct boot_image_sys *boot_image_sys;
   struct boot_image_dev *boot_image_dev;
+  int pid, replica_pid;
+  endpoint_t replica_endpoint;
   int ipc_to;
   int *calls;
   int all_c[] = { ALL_C, NULL_C };
@@ -179,7 +193,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
   }
 
   /* Initialize some global variables. */
-  rupdate.flags = 0;
+  RUPDATE_INIT();
   shutting_down = FALSE;
 
   /* Get a copy of the boot image table. */
@@ -219,8 +233,11 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
   /* Reset the system process table. */
   for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
       rp->r_flags = 0;
+      rp->r_init_err = ERESTART;
       rp->r_pub = &rprocpub[rp - rproc];
       rp->r_pub->in_use = FALSE;
+      rp->r_pub->old_endpoint = NONE;
+      rp->r_pub->new_endpoint = NONE;
   }
 
   /* Initialize the system process table in 4 steps, each of them following
@@ -254,6 +271,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
       
       /* Initialize privilege bitmaps and signal manager. */
       rp->r_priv.s_flags = boot_image_priv->flags;          /* priv flags */
+      rp->r_priv.s_init_flags = SRV_OR_USR(rp, SRV_I, USR_I); /* init flags */
       rp->r_priv.s_trap_mask= SRV_OR_USR(rp, SRV_T, USR_T); /* traps */
       ipc_to = SRV_OR_USR(rp, SRV_M, USR_M);                /* targets */
       fill_send_mask(&rp->r_priv.s_ipc_to, ipc_to == ALL_M);
@@ -345,7 +363,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
       /* RS/VM are already running as we speak. */
       if(boot_image_priv->endpoint == RS_PROC_NR ||
          boot_image_priv->endpoint == VM_PROC_NR) {
-          if ((s = init_service(rp, SEF_INIT_FRESH)) != OK) {
+          if ((s = init_service(rp, SEF_INIT_FRESH, rp->r_priv.s_init_flags)) != OK) {
               panic("unable to initialize %d: %d", boot_image_priv->endpoint, s);
           }
           /* VM will still send an RS_INIT message, though. */
@@ -367,7 +385,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
        * back to us here at boot time.
        */
       if(boot_image_priv->flags & SYS_PROC) {
-          if ((s = init_service(rp, SEF_INIT_FRESH)) != OK) {
+          if ((s = init_service(rp, SEF_INIT_FRESH, rp->r_priv.s_init_flags)) != OK) {
               panic("unable to initialize service: %d", s);
           }
           if(rpub->sys_flags & SF_SYNCH_BOOT) {
@@ -440,7 +458,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
       /* New RS instance running. */
 
       /* Live update the old instance into the new one. */
-      s = update_service(&rp, &replica_rp, RS_SWAP);
+      s = update_service(&rp, &replica_rp, RS_SWAP, 0);
       if(s != OK) {
           panic("unable to live update RS: %d", s);
       }
@@ -450,7 +468,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
       cleanup_service(rp);
 
       /* Ask VM to pin memory for the new RS instance. */
-      if((s = vm_memctl(RS_PROC_NR, VM_RS_MEM_PIN)) != OK) {
+      if((s = vm_memctl(RS_PROC_NR, VM_RS_MEM_PIN, 0, 0)) != OK) {
           panic("unable to pin memory for the new RS instance: %d", s);
       }
   }
@@ -476,6 +494,138 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
   return(OK);
 }
 
+/*===========================================================================*
+ *                         sef_cb_init_restart                              *
+ *===========================================================================*/
+static int sef_cb_init_restart(int type, sef_init_info_t *info)
+{
+/* Restart the reincarnation server. */
+  int r;
+  struct rproc *old_rs_rp, *new_rs_rp;
+
+  assert(info->endpoint == RS_PROC_NR);
+
+  /* Perform default state transfer first. */
+  r = SEF_CB_INIT_RESTART_DEFAULT(type, info);
+  if(r != OK) {
+      printf("SEF_CB_INIT_RESTART_DEFAULT failed: %d\n", r);
+      return r;
+  }
+
+  /* New RS takes over. */
+  old_rs_rp = rproc_ptr[_ENDPOINT_P(RS_PROC_NR)];
+  new_rs_rp = rproc_ptr[_ENDPOINT_P(info->old_endpoint)];
+  if(rs_verbose)
+      printf("RS: %s is the new RS after restart\n", srv_to_string(new_rs_rp));
+
+  /* If an update was in progress, end it. */
+  if(SRV_IS_UPDATING(old_rs_rp)) {
+      end_update(ERESTART, RS_REPLY);
+  }
+
+  /* Update the service into the replica. */
+  r = update_service(&old_rs_rp, &new_rs_rp, RS_DONTSWAP, 0);
+  if(r != OK) {
+      printf("update_service failed: %d\n", r);
+      return r;
+  }
+
+  /* Initialize the new RS instance. */
+  r = init_service(new_rs_rp, SEF_INIT_RESTART, 0);
+  if(r != OK) {
+      printf("init_service failed: %d\n", r);
+      return r;
+  }
+
+  /* Reschedule a synchronous alarm for the next period. */
+  if (OK != (r=sys_setalarm(RS_DELTA_T, 0)))
+      panic("couldn't set alarm: %d", r);
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                           sef_cb_init_lu                                 *
+ *===========================================================================*/
+static int sef_cb_init_lu(int type, sef_init_info_t *info)
+{
+/* Start a new version of the reincarnation server. */
+  int r;
+  struct rproc *old_rs_rp, *new_rs_rp;
+
+  assert(info->endpoint == RS_PROC_NR);
+
+  /* Perform default state transfer first. */
+  sef_setcb_init_restart(SEF_CB_INIT_RESTART_DEFAULT);
+  r = SEF_CB_INIT_LU_DEFAULT(type, info);
+  if(r != OK) {
+      printf("SEF_CB_INIT_LU_DEFAULT failed: %d\n", r);
+      return r;
+  }
+
+  /* New RS takes over. */
+  old_rs_rp = rproc_ptr[_ENDPOINT_P(RS_PROC_NR)];
+  new_rs_rp = rproc_ptr[_ENDPOINT_P(info->old_endpoint)];
+  if(rs_verbose)
+      printf("RS: %s is the new RS after live update\n",
+          srv_to_string(new_rs_rp));
+
+  /* Update the service into the replica. */
+  r = update_service(&old_rs_rp, &new_rs_rp, RS_DONTSWAP, 0);
+  if(r != OK) {
+      printf("update_service failed: %d\n", r);
+      return r;
+  }
+
+  /* Check if everything is as expected. */
+  assert(RUPDATE_IS_UPDATING());
+  assert(RUPDATE_IS_INITIALIZING());
+  assert(rupdate.num_rpupds > 0);
+  assert(rupdate.num_init_ready_pending > 0);
+
+  return OK;
+}
+
+/*===========================================================================*
+*                          sef_cb_init_response                             *
+ *===========================================================================*/
+int sef_cb_init_response(message *m_ptr)
+{
+  int r;
+
+  /* Return now if RS initialization failed. */
+  r = m_ptr->m_rs_init.result;
+  if(r != OK) {
+      return r;
+  }
+
+  /* Simulate an RS-to-RS init message. */
+  r = do_init_ready(m_ptr);
+
+  /* Assume everything is OK if EDONTREPLY was returned. */
+  if(r == EDONTREPLY) {
+      r = OK;
+  }
+  return r;
+}
+
+/*===========================================================================*
+*                           sef_cb_lu_response                              *
+ *===========================================================================*/
+int sef_cb_lu_response(message *m_ptr)
+{
+  int r;
+
+  /* Simulate an RS-to-RS update ready message. */
+  r = do_upd_ready(m_ptr);
+
+  /* If we get this far, we didn't get updated for some reason. Report error. */
+  if(r == EDONTREPLY) {
+      r = EGENERIC;
+  }
+  return r;
+}
+
 /*===========================================================================*
  *                         sef_cb_signal_handler                            *
  *===========================================================================*/
@@ -500,7 +650,6 @@ static int sef_cb_signal_manager(endpoint_t target, int signo)
 /* Process system signal on behalf of the kernel. */
   int target_p;
   struct rproc *rp;
-  struct rprocpub *rpub;
   message m;
 
   /* Lookup slot. */
@@ -511,7 +660,6 @@ static int sef_cb_signal_manager(endpoint_t target, int signo)
       return OK; /* clear the signal */
   }
   rp = rproc_ptr[target_p];
-  rpub = rp->r_pub;
 
   /* Don't bother if a termination signal has already been processed. */
   if((rp->r_flags & RS_TERMINATED) && !(rp->r_flags & RS_EXITING)) {
@@ -539,14 +687,19 @@ static int sef_cb_signal_manager(endpoint_t target, int signo)
   if(SIGS_IS_TERMINATION(signo)) {
       rp->r_flags |= RS_TERMINATED;
       terminate_service(rp);
+      rs_idle_period();
 
       return EDEADEPT; /* process is now gone */
   }
+  /* Never deliver signals to VM. */
+  if (rp->r_pub->endpoint == VM_PROC_NR) {
+      return OK;
+  }
 
   /* Translate every non-termination signal into a message. */
   m.m_type = SIGS_SIGNAL_RECEIVED;
   m.m_pm_lsys_sigs_signal.num = signo;
-  asynsend3(rpub->endpoint, &m, AMF_NOREPLY);
+  rs_asynsend(rp, &m, 1);
 
   return OK; /* signal has been delivered */
 }
index 8c3f027cb6a2004fe589c551506ecf0fcaae6dd8..48d0c986aea093662990818abbae069cfd2fb20c 100644 (file)
@@ -13,6 +13,8 @@
 
 #include "kernel/proc.h"
 
+static int run_script(struct rproc *rp);
+
 /*===========================================================================*
  *                             caller_is_root                               *
  *===========================================================================*/
@@ -102,9 +104,14 @@ struct rproc *rp;
           if(call != RS_EDIT) return EPERM;
       }
 
+      /* Disallow the call if an update is in progress. */
+      if(RUPDATE_IS_UPDATING()) {
+         return EBUSY;
+      }
+
       /* Disallow the call if another call is in progress for the service. */
       if((rp->r_flags & RS_LATEREPLY)
-          || (rp->r_flags & RS_INITIALIZING) || (rp->r_flags & RS_UPDATING)) {
+          || (rp->r_flags & RS_INITIALIZING)) {
           return EBUSY;
       }
 
@@ -161,6 +168,121 @@ size_t dst_len;
   return OK;
 }
 
+/*===========================================================================*
+ *                           init_state_data                                *
+ *===========================================================================*/
+int init_state_data(endpoint_t src_e, int prepare_state,
+    struct rs_state_data *src_rs_state_data,
+    struct rs_state_data *dst_rs_state_data)
+{
+  int s, i, j, num_ipc_filters = 0;
+  struct rs_ipc_filter_el (*rs_ipc_filter_els)[IPCF_MAX_ELEMENTS];
+  struct rs_ipc_filter_el rs_ipc_filter[IPCF_MAX_ELEMENTS];
+  size_t rs_ipc_filter_size = sizeof(rs_ipc_filter);
+  ipc_filter_el_t (*ipcf_els_buff)[IPCF_MAX_ELEMENTS];
+  size_t ipcf_els_buff_size;
+
+  dst_rs_state_data->size = 0;
+  dst_rs_state_data->eval_addr = NULL;
+  dst_rs_state_data->eval_len = 0;
+  dst_rs_state_data->ipcf_els = NULL;
+  dst_rs_state_data->ipcf_els_size  = 0;
+  if(src_rs_state_data->size != sizeof(struct rs_state_data)) {
+      return E2BIG;
+  }
+
+  /* Initialize eval expression. */
+  if(prepare_state == SEF_LU_STATE_EVAL) {
+      if(src_rs_state_data->eval_len == 0 || !src_rs_state_data->eval_addr) {
+          return EINVAL;
+      }
+      dst_rs_state_data->eval_addr = malloc(src_rs_state_data->eval_len+1);
+      dst_rs_state_data->eval_len = src_rs_state_data->eval_len;
+      if(!dst_rs_state_data->eval_addr) {
+          return ENOMEM;
+      }
+      s = sys_datacopy(src_e, (vir_bytes) src_rs_state_data->eval_addr,
+          SELF, (vir_bytes) dst_rs_state_data->eval_addr,
+          dst_rs_state_data->eval_len);
+      if(s != OK) {
+          return s;
+      }
+      *((char*)dst_rs_state_data->eval_addr + dst_rs_state_data->eval_len) = '\0';
+      dst_rs_state_data->size = src_rs_state_data->size;
+  }
+
+  /* Initialize ipc filters. */
+  if(src_rs_state_data->ipcf_els_size % rs_ipc_filter_size) {
+      return E2BIG;
+  }
+  rs_ipc_filter_els = src_rs_state_data->ipcf_els;
+  num_ipc_filters = src_rs_state_data->ipcf_els_size / rs_ipc_filter_size;
+  if(!rs_ipc_filter_els) {
+      return OK;
+  }
+
+  ipcf_els_buff_size = sizeof(ipc_filter_el_t)*IPCF_MAX_ELEMENTS*num_ipc_filters;
+  if(src_e == VM_PROC_NR) {
+      ipcf_els_buff_size += sizeof(ipc_filter_el_t)*IPCF_MAX_ELEMENTS;
+  }
+  ipcf_els_buff = malloc(ipcf_els_buff_size);
+  if(!ipcf_els_buff) {
+      return ENOMEM;
+  }
+  memset(ipcf_els_buff, 0, ipcf_els_buff_size);
+  for(i=0;i<num_ipc_filters;i++) {
+      s = sys_datacopy(src_e, (vir_bytes) rs_ipc_filter_els[i],
+          SELF, (vir_bytes) rs_ipc_filter, rs_ipc_filter_size);
+      if(s != OK) {
+          return s;
+      }
+      for(j=0;j<IPCF_MAX_ELEMENTS && rs_ipc_filter[j].flags;j++) {
+          endpoint_t m_source = 0;
+          int m_type = 0;
+          int flags = rs_ipc_filter[j].flags;
+          if(flags & IPCF_MATCH_M_TYPE) {
+              m_type = rs_ipc_filter[j].m_type;
+          }
+          if(flags & IPCF_MATCH_M_SOURCE) {
+              if(ds_retrieve_label_endpt(rs_ipc_filter[j].m_label,&m_source) != OK) {
+                  /* try to see if an endpoint was provided as label */
+                  char *buff;
+                  if(!strcmp("ANY_USR", rs_ipc_filter[j].m_label)) {
+                      m_source = ANY_USR;
+                  }
+                  else if(!strcmp("ANY_SYS", rs_ipc_filter[j].m_label)) {
+                      m_source = ANY_SYS;
+                  }
+                  else if(!strcmp("ANY_TSK", rs_ipc_filter[j].m_label)) {
+                      m_source = ANY_TSK;
+                  }
+                  else {
+                      errno=0;
+                      m_source = strtol(rs_ipc_filter[j].m_label, &buff, 10);
+                      if(errno || strcmp(buff, "")) {
+                            return ESRCH;
+                      }
+                  }
+              }
+          }
+          ipcf_els_buff[i][j].flags = flags;
+          ipcf_els_buff[i][j].m_source = m_source;
+          ipcf_els_buff[i][j].m_type = m_type;
+      }
+  }
+  if(src_e == VM_PROC_NR) {
+      /* Make sure VM can still talk to us at update time. */
+      ipcf_els_buff[i][0].flags = (IPCF_EL_WHITELIST|IPCF_MATCH_M_SOURCE|IPCF_MATCH_M_TYPE);
+      ipcf_els_buff[i][0].m_source = RS_PROC_NR;
+      ipcf_els_buff[i][0].m_type = VM_RS_UPDATE;
+  }
+  dst_rs_state_data->size = src_rs_state_data->size;
+  dst_rs_state_data->ipcf_els = ipcf_els_buff;
+  dst_rs_state_data->ipcf_els_size = ipcf_els_buff_size;
+
+  return OK;
+}
+
 /*===========================================================================*
  *                             build_cmd_dep                                *
  *===========================================================================*/
@@ -213,122 +335,35 @@ void build_cmd_dep(struct rproc *rp)
 }
 
 /*===========================================================================*
- *                              srv_update                                  *
+ *                             end_srv_init                                 *
  *===========================================================================*/
-int srv_update(endpoint_t src_e, endpoint_t dst_e)
+void end_srv_init(struct rproc *rp)
 {
-  int r;
-  int sys_upd_flags = 0;
-
-  /* Ask VM to swap the slots of the two processes and tell the kernel to
-   * do the same. If VM is the service being updated, only perform the kernel
-   * part of the call. The new instance of VM will do the rest at
-   * initialization time.
-   */
-  if(src_e != VM_PROC_NR) {
-      r = vm_update(src_e, dst_e, sys_upd_flags);
-  }
-  else {
-      r = sys_update(src_e, dst_e, sys_upd_flags);
-  }
-
-  return r;
-}
-
-/*===========================================================================*
- *                             update_period                                *
- *===========================================================================*/
-void update_period(message *m_ptr)
-{
-  clock_t now = m_ptr->m_notify.timestamp;
-  short has_update_timed_out;
-  message m;
   struct rprocpub *rpub;
+  int r;
 
-  rpub = rupdate.rp->r_pub;
+  rpub = rp->r_pub;
 
-  /* See if a timeout has occurred. */
-  has_update_timed_out = (now - rupdate.prepare_tm > rupdate.prepare_maxtime);
+  /* See if a late reply has to be sent. */
+  late_reply(rp, OK);
 
-  /* If an update timed out, end the update process and notify
-   * the old version that the update has been canceled. From now on, the old
-   * version will continue executing.
+  /* If the service has completed initialization after a crash
+   * make the new instance active and cleanup the old replica.
+   * If the service was part of a scheduled update, schedule the new
+   * replica for the same update.
    */
-  if(has_update_timed_out) {
-      printf("RS: update failed: maximum prepare time reached\n");
-      end_update(EINTR, RS_DONTREPLY);
-
-      /* Prepare cancel request. */
-      m.m_type = RS_LU_PREPARE;
-      m.m_rs_update.state = SEF_LU_STATE_NULL;
-      if(rpub->endpoint == RS_PROC_NR) {
-          /* RS can process the request directly. */
-          do_sef_lu_request(&m);
-      }
-      else {
-          /* Send request message to the system service. */
-          asynsend(rpub->endpoint, &m);
+  if(rp->r_prev_rp) {
+      if(SRV_IS_UPD_SCHEDULED(rp->r_prev_rp)) {
+          rupdate_upd_move(rp->r_prev_rp, rp);
       }
-  }
-}
-
-/*===========================================================================*
- *                             end_update                                   *
- *===========================================================================*/
-void end_update(int result, int reply_flag)
-{
-/* End the update process. There are two possibilities:
- * 1) the update succeeded. In that case, cleanup the old version and mark the
- *    new version as no longer under update.
- * 2) the update failed. In that case, cleanup the new version and mark the old
- *    version as no longer under update. Eventual late ready to update
- *    messages (if any) will simply be ignored and the service can
- *    continue executing. In addition, reset the check timestamp, so that if the
- *    service has a period, a status request will be forced in the next period.
- */
-  struct rproc *old_rp, *new_rp, *exiting_rp, *surviving_rp;
-  struct rproc **rps;
-  int nr_rps, i;
-
-  old_rp = rupdate.rp;
-  new_rp = old_rp->r_new_rp;
-
-  if(rs_verbose)
-      printf("RS: ending update from %s to %s with result: %d\n",
-          srv_to_string(old_rp), srv_to_string(new_rp), result);
-
-  /* Decide which version has to die out and which version has to survive. */
-  surviving_rp = (result == OK ? new_rp : old_rp);
-  exiting_rp =   (result == OK ? old_rp : new_rp);
-
-  /* End update. */
-  rupdate.flags &= ~RS_UPDATING;
-  rupdate.rp = NULL;
-  old_rp->r_new_rp = NULL;
-  new_rp->r_old_rp = NULL;
-  old_rp->r_check_tm = 0;
-
-  /* Send a late reply if necessary. */
-  late_reply(old_rp, result);
+      cleanup_service(rp->r_prev_rp);
+      rp->r_prev_rp = NULL;
+      rp->r_restarts += 1;
 
-  /* Mark the version that has to survive as no longer updating and
-   * reply when asked to.
-   */
-  surviving_rp->r_flags &= ~RS_UPDATING;
-  if(reply_flag == RS_REPLY) {
-      message m;
-      m.m_type = result;
-      reply(surviving_rp->r_pub->endpoint, surviving_rp, &m);
-  }
-
-  /* Cleanup the version that has to die out. */
-  get_service_instances(exiting_rp, &rps, &nr_rps);
-  for(i=0;i<nr_rps;i++) {
-      cleanup_service(rps[i]);
+      if(rs_verbose)
+          printf("RS: %s completed restart\n", srv_to_string(rp));
   }
-
-  if(rs_verbose)
-      printf("RS: %s ended the update\n", srv_to_string(surviving_rp));
+  rp->r_next_rp = NULL;
 }
 
 /*===========================================================================*
@@ -385,30 +420,120 @@ int line;
 struct rproc *rp;
 {
   struct rprocpub *rpub;
+  int detach, cleanup_script;
   int s;
 
   rpub = rp->r_pub;
 
-  if(rs_verbose)
-      printf("RS: %s cleaned up at %s:%d\n", srv_to_string(rp),
-          file, line);
+  if(!(rp->r_flags & RS_DEAD)) {
+      if(rs_verbose)
+          printf("RS: %s marked for cleanup at %s:%d\n", srv_to_string(rp),
+              file, line);
+
+      /* Unlink service the first time. */
+      if(rp->r_next_rp) {
+          rp->r_next_rp->r_prev_rp = NULL;
+          rp->r_next_rp = NULL;
+      }
+      if(rp->r_prev_rp) {
+          rp->r_prev_rp->r_next_rp = NULL;
+          rp->r_prev_rp = NULL;
+      }
+      if(rp->r_new_rp) {
+          rp->r_new_rp->r_old_rp = NULL;
+          rp->r_new_rp = NULL;
+      }
+      if(rp->r_old_rp) {
+          rp->r_old_rp->r_new_rp = NULL;
+          rp->r_old_rp = NULL;
+      }
+      rp->r_flags |= RS_DEAD;
+
+      /* Make sure the service can no longer run and unblock IPC callers. */
+      sys_privctl(rpub->endpoint, SYS_PRIV_DISALLOW, NULL);
+      sys_privctl(rpub->endpoint, SYS_PRIV_CLEAR_IPC_REFS, NULL);
+      rp->r_flags &= ~RS_ACTIVE;
+
+      /* Send a late reply if there is any pending. */
+      late_reply(rp, OK);
 
-  /* Tell scheduler this process is finished */
-  if ((s = sched_stop(rp->r_scheduler, rpub->endpoint)) != OK) {
-       printf("RS: warning: scheduler won't give up process: %d\n", s);
+      return;
   }
 
-  /* Ask PM to exit the service */
-  if(rp->r_pid == -1) {
-      printf("RS: warning: attempt to kill pid -1!\n");
+  cleanup_script = rp->r_flags & RS_CLEANUP_SCRIPT;
+  detach = rp->r_flags & RS_CLEANUP_DETACH;
+
+  /* Cleanup the service when not detaching. */
+  if(!detach) {
+      if(rs_verbose)
+          printf("RS: %s cleaned up at %s:%d\n", srv_to_string(rp),
+              file, line);
+
+      /* Tell scheduler this process is finished */
+      if ((s = sched_stop(rp->r_scheduler, rpub->endpoint)) != OK) {
+            printf("RS: warning: scheduler won't give up process: %d\n", s);
+      }
+
+      /* Ask PM to exit the service */
+      if(rp->r_pid == -1) {
+          printf("RS: warning: attempt to kill pid -1!\n");
+      }
+      else {
+          srv_kill(rp->r_pid, SIGKILL);
+      }
+  }
+
+  /* See if we need to run a script now. */
+  if(cleanup_script) {
+      rp->r_flags &= ~RS_CLEANUP_SCRIPT;
+      s = run_script(rp);
+      if(s != OK) {
+          printf("RS: warning: cannot run cleanup script: %d\n", s);
+      }
+  }
+
+  if(detach) {
+      /* Detach service when asked to. */
+      detach_service(rp);
   }
   else {
-      srv_kill(rp->r_pid, SIGKILL);
+      /* Free slot otherwise, unless we're about to reuse it */
+      if (!(rp->r_flags & RS_REINCARNATE))
+          free_slot(rp);
   }
+}
 
-  /* Free slot, unless we're about to reuse it */
-  if (!(rp->r_flags & RS_REINCARNATE))
-      free_slot(rp);
+/*===========================================================================*
+ *                          detach_service_debug                            *
+ *===========================================================================*/
+void detach_service_debug(file, line, rp)
+char *file;
+int line;
+struct rproc *rp;
+{
+/* Detach the given system service. */
+  static unsigned long detach_counter = 0;
+  char label[RS_MAX_LABEL_LEN];
+  struct rprocpub *rpub;
+
+  rpub = rp->r_pub;
+
+  /* Publish a new unique label for the system service. */
+  rpub->label[RS_MAX_LABEL_LEN-1] = '\0';
+  strcpy(label, rpub->label);
+  snprintf(rpub->label, RS_MAX_LABEL_LEN, "%lu.%s", ++detach_counter, label);
+  ds_publish_label(rpub->label, rpub->endpoint, DSF_OVERWRITE);
+
+  if(rs_verbose)
+      printf("RS: %s detached at %s:%d\n", srv_to_string(rp),
+          file, line);
+
+  /* Allow the service to run. */
+  rp->r_flags = RS_IN_USE | RS_ACTIVE;
+  rpub->sys_flags &= ~(SF_CORE_SRV|SF_DET_RESTART);
+  rp->r_period = 0;
+  rpub->dev_nr = 0;
+  sys_privctl(rpub->endpoint, SYS_PRIV_ALLOW, NULL);
 }
 
 /*===========================================================================*
@@ -495,7 +620,7 @@ struct rproc *rp;
   if ((s = sched_init_proc(rp)) != OK) {
        printf("RS: unable to start scheduling: %d\n", s);
        cleanup_service(rp);
-       vm_memctl(RS_PROC_NR, VM_RS_MEM_PIN,0,0);
+       vm_memctl(RS_PROC_NR, VM_RS_MEM_PIN, 0, 0);
        return s;
   }
 
@@ -529,8 +654,37 @@ struct rproc *rp;
         free_exec(rp);
   }
 
+  /* The purpose of non-blocking forks is to avoid involving VFS in the forking
+   * process, because VFS may be blocked on a sendrec() to a MFS that is
+   * waiting for a endpoint update for a dead driver. We have just published
+   * that update, but VFS may still be blocked. As a result, VFS may not yet
+   * have received PM's fork message. Hence, if we call mapdriver()
+   * immediately, VFS may not know about the process and thus refuse to add the
+   * driver entry. The following temporary hack works around this by forcing
+   * blocking communication from PM to VFS. Once VFS has been made non-blocking
+   * towards MFS instances, this hack and the big part of srv_fork() can go.
+   */
+  setuid(0);
+
+  /* If this is a RS instance, pin memory. */
+  if(rp->r_priv.s_flags & ROOT_SYS_PROC) {
+      if(rs_verbose)
+          printf("RS: pinning memory of RS instance %s\n", srv_to_string(rp));
+
+      s = vm_memctl(rpub->endpoint, VM_RS_MEM_PIN, 0, 0);
+      if(s != OK) {
+          printf("vm_memctl failed: %d\n", s);
+          cleanup_service(rp);
+          return s;
+      }
+  }
+
   /* If this is a VM instance, let VM know now. */
   if(rp->r_priv.s_flags & VM_SYS_PROC) {
+      struct rproc *rs_rp;
+      struct rproc **rs_rps;
+      int i, nr_rs_rps;
+
       if(rs_verbose)
           printf("RS: informing VM of instance %s\n", srv_to_string(rp));
 
@@ -540,6 +694,15 @@ struct rproc *rp;
           cleanup_service(rp);
           return s;
       }
+
+      /* VM may start actually pinning memory for us only now.
+       * Ask again for all our instances.
+       */
+      rs_rp = rproc_ptr[_ENDPOINT_P(RS_PROC_NR)];
+      get_service_instances(rs_rp, &rs_rps, &nr_rs_rps);
+      for(i=0;i<nr_rs_rps;i++) {
+          vm_memctl(rs_rps[i]->r_pub->endpoint, VM_RS_MEM_PIN, 0, 0);
+      }
   }
 
   /* Tell VM about allowed calls. */
@@ -558,9 +721,7 @@ struct rproc *rp;
 /*===========================================================================*
  *                             clone_service                                *
  *===========================================================================*/
-int clone_service(rp, instance_flag)
-struct rproc *rp;
-int instance_flag;
+int clone_service(struct rproc *rp, int instance_flag, int init_flags)
 {
 /* Clone the given system service instance. */
   struct rproc *replica_rp;
@@ -572,7 +733,16 @@ int instance_flag;
   int r;
 
   if(rs_verbose)
-      printf("RS: creating a replica for %s\n", srv_to_string(rp));
+      printf("RS: %s creating a replica\n", srv_to_string(rp));
+
+  /* VM can only reliably support one replica at the time for now.
+   * XXX TO-DO: Fix VM's rs_memctl_make_vm_instance to allow multiple replicas.
+   */
+  if(rp->r_pub->endpoint == VM_PROC_NR && instance_flag == LU_SYS_PROC
+      && rp->r_next_rp) {
+      cleanup_service_now(rp->r_next_rp);
+      rp->r_next_rp = NULL;
+  }
 
   /* Clone slot. */
   if((r = clone_slot(rp, &replica_rp)) != OK) {
@@ -590,6 +760,7 @@ int instance_flag;
       replica_link = &replica_rp->r_prev_rp;
   }
   replica_rp->r_priv.s_flags |= instance_flag;
+  replica_rp->r_priv.s_init_flags |= init_flags;
 
   /* Link the two slots. */
   *rp_link = replica_rp;
@@ -737,7 +908,7 @@ struct rproc *rp;                           /* pointer to service slot */
          r = ds_retrieve_label_endpt("devman",&ep);
   
          if (r != OK) {
-               printf("RS: devman not running?");
+                  printf("RS: devman not running?");
          } else {
                m.m_type = DEVMAN_UNBIND;
                m.DEVMAN_ENDPOINT  = rpub->endpoint;
@@ -759,9 +930,7 @@ struct rproc *rp;                           /* pointer to service slot */
 /*===========================================================================*
  *                             run_service                                  *
  *===========================================================================*/
-int run_service(rp, init_type)
-struct rproc *rp;
-int init_type;
+int run_service(struct rproc *rp, int init_type, int init_flags)
 {
 /* Let a newly created service run. */
   struct rprocpub *rpub;
@@ -775,7 +944,7 @@ int init_type;
   }
 
   /* Initialize service. */
-  if((s = init_service(rp, init_type)) != OK) {
+  if((s = init_service(rp, init_type, init_flags)) != OK) {
       return kill_service(rp, "unable to initialize service", s);
   }
 
@@ -788,16 +957,16 @@ int init_type;
 /*===========================================================================*
  *                             start_service                                *
  *===========================================================================*/
-int start_service(rp)
-struct rproc *rp;
+int start_service(struct rproc *rp, int init_flags)
 {
 /* Start a system service. */
-  int r, init_type;
+  int r;
   struct rprocpub *rpub;
 
   rpub = rp->r_pub;
 
   /* Create and make active. */
+  rp->r_priv.s_init_flags |= init_flags;
   r = create_service(rp);
   if(r != OK) {
       return r;
@@ -811,8 +980,7 @@ struct rproc *rp;
   }
 
   /* Run. */
-  init_type = SEF_INIT_FRESH;
-  r = run_service(rp, init_type);
+  r = run_service(rp, SEF_INIT_FRESH, init_flags);
   if(r != OK) {
       return r;
   }
@@ -849,66 +1017,6 @@ void stop_service(struct rproc *rp,int how)
   getticks(&rp->r_stop_tm);                    /* record current time */
 }
 
-/*===========================================================================*
- *                             update_service                               *
- *===========================================================================*/
-int update_service(src_rpp, dst_rpp, swap_flag)
-struct rproc **src_rpp;
-struct rproc **dst_rpp;
-int swap_flag;
-{
-/* Update an existing service. */
-  int r;
-  struct rproc *src_rp;
-  struct rproc *dst_rp;
-  struct rprocpub *src_rpub;
-  struct rprocpub *dst_rpub;
-  int pid;
-  endpoint_t endpoint;
-
-  src_rp = *src_rpp;
-  dst_rp = *dst_rpp;
-  src_rpub = src_rp->r_pub;
-  dst_rpub = dst_rp->r_pub;
-
-  if(rs_verbose)
-      printf("RS: %s updating into %s\n",
-          srv_to_string(src_rp), srv_to_string(dst_rp));
-
-  /* Swap the slots of the two processes when asked to. */
-  if(swap_flag == RS_SWAP) {
-      if((r = srv_update(src_rpub->endpoint, dst_rpub->endpoint)) != OK) {
-          return r;
-      }
-  }
-
-  /* Swap slots here as well. */
-  pid = src_rp->r_pid;
-  endpoint = src_rpub->endpoint;
-  swap_slot(&src_rp, &dst_rp);
-
-  /* Reassign pids and endpoints. */
-  src_rp->r_pid = dst_rp->r_pid;
-  src_rp->r_pub->endpoint = dst_rp->r_pub->endpoint;
-  rproc_ptr[_ENDPOINT_P(src_rp->r_pub->endpoint)] = src_rp;
-  dst_rp->r_pid = pid;
-  dst_rp->r_pub->endpoint = endpoint;
-  rproc_ptr[_ENDPOINT_P(dst_rp->r_pub->endpoint)] = dst_rp;
-
-  /* Adjust input pointers. */
-  *src_rpp = src_rp;
-  *dst_rpp = dst_rp;
-
-  /* Make the new version active. */
-  activate_service(dst_rp, src_rp);
-
-  if(rs_verbose)
-      printf("RS: %s updated into %s\n",
-          srv_to_string(src_rp), srv_to_string(dst_rp));
-
-  return OK;
-}
-
 /*===========================================================================*
  *                           activate_service                               *
  *===========================================================================*/
@@ -932,35 +1040,23 @@ void activate_service(struct rproc *rp, struct rproc *ex_rp)
 /*===========================================================================*
  *                           reincarnate_service                            *
  *===========================================================================*/
-void reincarnate_service(struct rproc *rp)
+void reincarnate_service(struct rproc *old_rp)
 {
 /* Restart a service as if it were never started before. */
-  struct rprocpub *rpub;
-  int i;
-
-  rpub = rp->r_pub;
+  struct rproc *rp;
+  int r, restarts;
 
-  rp->r_flags &= RS_IN_USE;
-  rp->r_pid = -1;
-  rproc_ptr[_ENDPOINT_P(rpub->endpoint)] = NULL;
+  if ((r = clone_slot(old_rp, &rp)) != OK) {
+      printf("RS: Failed to clone the slot: %d\n", r);
+      return;
+  }
 
-  /* Restore original IRQ and I/O range tables in the priv struct. This is the
-   * only part of the privilege structure that can be modified by processes
-   * other than RS itself.
-   */
-  rp->r_priv.s_nr_irq = rp->r_nr_irq;
-  for (i = 0; i < rp->r_nr_irq; i++)
-      rp->r_priv.s_irq_tab[i] = rp->r_irq_tab[i];
-  rp->r_priv.s_nr_io_range = rp->r_nr_io_range;
-  for (i = 0; i < rp->r_nr_io_range; i++)
-      rp->r_priv.s_io_tab[i] = rp->r_io_tab[i];
-
-  rp->r_old_rp = NULL;
-  rp->r_new_rp = NULL;
-  rp->r_prev_rp = NULL;
-  rp->r_next_rp = NULL;
+  rp->r_flags = RS_IN_USE;
+  rproc_ptr[_ENDPOINT_P(rp->r_pub->endpoint)] = NULL;
 
-  start_service(rp);
+  restarts = rp->r_restarts;
+  start_service(rp, SEF_INIT_FRESH);
+  rp->r_restarts = restarts + 1;
 }
 
 /*===========================================================================*
@@ -971,7 +1067,7 @@ void terminate_service(struct rproc *rp)
 /* Handle a termination event for a system service. */
   struct rproc **rps;
   struct rprocpub *rpub;
-  int nr_rps;
+  int nr_rps, norestart;
   int i, r;
 
   rpub = rp->r_pub;
@@ -981,6 +1077,14 @@ void terminate_service(struct rproc *rp)
 
   /* Deal with failures during initialization. */
   if(rp->r_flags & RS_INITIALIZING) {
+      /* If updating, rollback. */
+      if(SRV_IS_UPDATING(rp)) {
+          printf("RS: update failed: state transfer failed. Rolling back...\n");
+          end_update(rp->r_init_err, RS_REPLY);
+          rp->r_init_err = ERESTART;
+          return;
+      }
+
       if (rpub->sys_flags & SF_NO_BIN_EXP) {
           /* If service was deliberately started with binary exponential offset
           * disabled, we're going to assume we want to refresh a service upon
@@ -993,33 +1097,51 @@ void terminate_service(struct rproc *rp)
       } else {
           if(rs_verbose)
               printf("RS: service '%s' exited during initialization; "
-                     "not restarting\n", rpub->label);
+                     "exiting\n", rpub->label);
           rp->r_flags |= RS_EXITING; /* don't restart. */
       }
+  }
 
-      /* If updating, rollback. */
-      if(rp->r_flags & RS_UPDATING) {
-          struct rproc *old_rp, *new_rp;
-          printf("RS: update failed: state transfer failed. Rolling back...\n");
-          new_rp = rp;
-          old_rp = new_rp->r_old_rp;
-          new_rp->r_flags &= ~RS_INITIALIZING;
-          r = update_service(&new_rp, &old_rp, RS_SWAP);
-          assert(r == OK); /* can't fail */
-          end_update(ERESTART, RS_REPLY);
-          return;
+  /* If an update process is in progress, end it before doing anything else.
+   * This is to be on the safe side, since there may be some weird dependencies
+   * with services under update, while we perform recovery actions.
+   */
+  if(RUPDATE_IS_UPDATING()) {
+      printf("RS: aborting the update after a crash...\n");
+      abort_update_proc(ERESTART);
+  }
+
+  /* Force exit when no restart is requested. */
+  norestart = !(rp->r_flags & RS_EXITING) && (rp->r_pub->sys_flags & SF_NORESTART);
+  if(norestart) {
+      rp->r_flags |= RS_EXITING;
+      if((rp->r_pub->sys_flags & SF_DET_RESTART)
+          && (rp->r_restarts < MAX_DET_RESTART)) {
+          /* Detach at cleanup time. */
+          rp->r_flags |= RS_CLEANUP_DETACH;
+      }
+      if(rp->r_script[0] != '\0') {
+          /* Run script at cleanup time. */
+          rp->r_flags |= RS_CLEANUP_SCRIPT;
       }
   }
 
   if (rp->r_flags & RS_EXITING) {
       /* If a core system service is exiting, we are in trouble. */
-      if (rp->r_pub->sys_flags & SF_CORE_SRV && !shutting_down) {
+      if ((rp->r_pub->sys_flags & SF_CORE_SRV) && !shutting_down) {
           printf("core system service died: %s\n", srv_to_string(rp));
          _exit(1);
       }
 
+      /* If this service was scheduled for the update, abort the update now. */
+      if(SRV_IS_UPD_SCHEDULED(rp)) {
+          printf("RS: aborting the scheduled update, one of the services part of it is exiting...\n");
+          abort_update_proc(EDEADSRCDST);
+      }
+
       /* See if a late reply has to be sent. */
-      r = (rp->r_caller_request == RS_DOWN ? OK : EDEADEPT);
+      r = (rp->r_caller_request == RS_DOWN
+          || (rp->r_caller_request == RS_REFRESH && norestart) ? OK : EDEADEPT);
       late_reply(rp, r);
 
       /* Unpublish the service. */
@@ -1036,6 +1158,7 @@ void terminate_service(struct rproc *rp)
        * If this fails, start_service() itself will perform cleanup.
        */
       if (rp->r_flags & RS_REINCARNATE) {
+          rp->r_flags &= ~RS_REINCARNATE;
           reincarnate_service(rp);
       }
   }
@@ -1044,13 +1167,6 @@ void terminate_service(struct rproc *rp)
       restart_service(rp);
   }
   else {
-      /* If an update is in progress, end it. The old version
-       * that just exited will continue executing.
-       */
-      if(rp->r_flags & RS_UPDATING) {
-          end_update(ERESTART, RS_DONTREPLY);
-      }
-
       /* Determine what to do. If this is the first unexpected 
        * exit, immediately restart this service. Otherwise use
        * a binary exponential backoff.
@@ -1104,7 +1220,7 @@ static int run_script(struct rproc *rp)
        switch(pid)
        {
        case -1:
-               return kill_service(rp, "unable to fork script", errno);
+               return errno;
        case 0:
                execle(_PATH_BSHELL, "sh", rp->r_script, rpub->label, reason,
                        incarnation_str, (char*) NULL, envp);
@@ -1146,25 +1262,19 @@ void restart_service(struct rproc *rp)
   /* See if a late reply has to be sent. */
   late_reply(rp, OK);
 
-  /* This hack disables restarting of file servers, which at the moment always
-   * cause VFS to hang indefinitely. As soon as VFS no longer blocks on calls
-   * to file servers, this exception can be removed again.
-   */
-  if (!strncmp(rp->r_pub->label, "fs_", 3)) {
-      kill_service(rp, "file servers cannot be restarted yet", ENOSYS);
-      return;
-  }
-
   /* Run a recovery script if available. */
   if (rp->r_script[0] != '\0') {
-      run_script(rp);
+      r = run_script(rp);
+      if(r != OK) {
+          kill_service(rp, "unable to run script", errno);
+      }
       return;
   }
 
   /* Restart directly. We need a replica if not already available. */
   if(rp->r_next_rp == NULL) {
       /* Create the replica. */
-      r = clone_service(rp, RST_SYS_PROC);
+      r = clone_service(rp, RST_SYS_PROC, 0);
       if(r != OK) {
           kill_service(rp, "unable to clone service", r);
           return;
@@ -1173,19 +1283,25 @@ void restart_service(struct rproc *rp)
   replica_rp = rp->r_next_rp;
 
   /* Update the service into the replica. */
-  r = update_service(&rp, &replica_rp, RS_SWAP);
+  r = update_service(&rp, &replica_rp, RS_SWAP, 0);
   if(r != OK) {
       kill_service(rp, "unable to update into new replica", r);
       return;
   }
 
   /* Let the new replica run. */
-  r = run_service(replica_rp, SEF_INIT_RESTART);
+  r = run_service(replica_rp, SEF_INIT_RESTART, 0);
   if(r != OK) {
       kill_service(rp, "unable to let the replica run", r);
       return;
   }
 
+  /* See if the old version needs to be detached. */
+  if((rp->r_pub->sys_flags & SF_DET_RESTART)
+      && (rp->r_restarts < MAX_DET_RESTART)) {
+      rp->r_flags |= RS_CLEANUP_DETACH;
+  }
+
   if(rs_verbose)
       printf("RS: %s restarted into %s\n",
           srv_to_string(rp), srv_to_string(replica_rp));
@@ -1499,11 +1615,13 @@ endpoint_t source;
 
   /* Update recovery script. */
   if (rs_start->rss_scriptlen > MAX_SCRIPT_LEN-1) return(E2BIG);
-  if (rs_start->rss_script != NULL && !(rpub->sys_flags & SF_CORE_SRV)) {
+  if (rs_start->rss_script != NULL && rs_start->rss_scriptlen > 0
+      && !(rpub->sys_flags & SF_CORE_SRV)) {
       s=sys_datacopy(source, (vir_bytes) rs_start->rss_script, 
           SELF, (vir_bytes) rp->r_script, rs_start->rss_scriptlen);
       if (s != OK) return(s);
       rp->r_script[rs_start->rss_scriptlen] = '\0';
+      rpub->sys_flags |= SF_USE_SCRIPT;
   }
 
   /* Update system flags and in-memory copy. */
@@ -1518,9 +1636,6 @@ endpoint_t source;
 
           for(i = 0; i < NR_SYS_PROCS; i++) {
               rp2 = &rproc[i];
-              if (!(rp2->r_flags & RS_IN_USE)) {
-                 continue;
-              }
               rpub2 = rproc[i].r_pub;
               if(strcmp(rpub->proc_name, rpub2->proc_name) == 0 &&
                   (rpub2->sys_flags & SF_USE_COPY)) {
@@ -1549,12 +1664,32 @@ endpoint_t source;
   if (rs_start->rss_flags & RSS_NO_BIN_EXP) {
       rpub->sys_flags |= SF_NO_BIN_EXP;
   }
+  if (rs_start->rss_flags & RSS_DETACH) {
+      rpub->sys_flags |= SF_DET_RESTART;
+  }
+  else {
+      rpub->sys_flags &= ~SF_DET_RESTART;
+  }
+  if (rs_start->rss_flags & RSS_NORESTART) {
+      if(rpub->sys_flags & SF_CORE_SRV) {
+          return EPERM;
+      }
+      rpub->sys_flags |= SF_NORESTART;
+  }
+  else {
+      rpub->sys_flags &= ~SF_NORESTART;
+  }
 
   /* Update period. */
   if(rpub->endpoint != RS_PROC_NR) {
       rp->r_period = rs_start->rss_period;
   }
 
+  /* Update restarts. */
+  if(rs_start->rss_restarts) {
+      rp->r_restarts = rs_start->rss_restarts;
+  }
+
   /* (Re)initialize privilege settings. */
   init_privs(rp, &rp->r_priv);
 
@@ -1581,6 +1716,7 @@ endpoint_t source;
    */
   rpub->sys_flags = DSRV_SF;             /* system flags */
   rp->r_priv.s_flags = DSRV_F;           /* privilege flags */
+  rp->r_priv.s_init_flags = DSRV_I;      /* init flags */
   rp->r_priv.s_trap_mask = DSRV_T;       /* allowed traps */
   rp->r_priv.s_bak_sig_mgr = NONE;       /* backup signal manager */
 
@@ -1622,7 +1758,7 @@ endpoint_t source;
               (unsigned int) rpub->pci_acl.rsp_class[i].pciclass,
               (unsigned int) rpub->pci_acl.rsp_class[i].mask);
   }
-
+  
   /* Initialize some fields. */
   rp->r_restarts = 0;                          /* no restarts yet */
   rp->r_old_rp = NULL;                         /* no old version yet */
@@ -1635,6 +1771,9 @@ endpoint_t source;
   rpub->label[0]= '\0';                         /* no label yet */
   rp->r_scheduler = -1;                         /* no scheduler yet */
   rp->r_priv.s_sig_mgr = -1;                    /* no signal manager yet */
+  rp->r_map_prealloc_addr = 0;                  /* no preallocated memory */
+  rp->r_map_prealloc_len = 0;
+  rp->r_init_err = ERESTART;                    /* default init error `*/
 
   /* Initialize editable slot settings. */
   return edit_slot(rp, rs_start, source);
@@ -1671,6 +1810,7 @@ struct rproc **clone_rpp;
   *clone_rpub = *rpub;
 
   /* Deep copy. */
+  clone_rp->r_init_err = ERESTART; /* default init error */
   clone_rp->r_flags &= ~RS_ACTIVE; /* the clone is not active yet */
   clone_rp->r_pid = -1;            /* no pid yet */
   clone_rpub->endpoint = -1;       /* no endpoint yet */
@@ -1689,6 +1829,7 @@ struct rproc **clone_rpp;
 
   /* Clear instance flags. */
   clone_rp->r_priv.s_flags &= ~(LU_SYS_PROC | RST_SYS_PROC);
+  clone_rp->r_priv.s_init_flags = 0;
 
   *clone_rpp = clone_rp;
   return OK;
@@ -1716,12 +1857,11 @@ struct rproc **src_rpp;
 struct rproc **dst_rpp;
 {
 /* Swap two service slots. */
-  struct rproc *src_rp;
-  struct rproc *dst_rp;
-  struct rprocpub *src_rpub;
-  struct rprocpub *dst_rpub;
+  struct rproc *src_rp, *dst_rp;
+  struct rprocpub *src_rpub, *dst_rpub;
   struct rproc orig_src_rproc, orig_dst_rproc;
   struct rprocpub orig_src_rprocpub, orig_dst_rprocpub;
+  struct rprocupd *prev_rpupd, *rpupd;
 
   src_rp = *src_rpp;
   dst_rp = *dst_rpp;
@@ -1740,9 +1880,11 @@ struct rproc **dst_rpp;
   *dst_rp = orig_src_rproc;
   *dst_rpub = orig_src_rprocpub;
 
-  /* Restore public entries. */
+  /* Restore public entries and update descriptors. */
   src_rp->r_pub = orig_src_rproc.r_pub;
   dst_rp->r_pub = orig_dst_rproc.r_pub;
+  src_rp->r_upd = orig_src_rproc.r_upd;
+  dst_rp->r_upd = orig_dst_rproc.r_upd;
 
   /* Rebuild command dependencies. */
   build_cmd_dep(src_rp);
@@ -1759,7 +1901,9 @@ struct rproc **dst_rpp;
   swap_slot_pointer(&dst_rp->r_new_rp, src_rp, dst_rp);
 
   /* Swap global slot pointers. */
-  swap_slot_pointer(&rupdate.rp, src_rp, dst_rp);
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      swap_slot_pointer(&rpupd->rp, src_rp, dst_rp);
+  );
   swap_slot_pointer(&rproc_ptr[_ENDPOINT_P(src_rp->r_pub->endpoint)],
       src_rp, dst_rp);
   swap_slot_pointer(&rproc_ptr[_ENDPOINT_P(dst_rp->r_pub->endpoint)],
index b8c47c097b9512ac2e64a2e5aac9f9797aa31f4a..4e9e088b8de4472ba7a448f610411da1dcf94e38 100644 (file)
@@ -2,6 +2,7 @@
 
 /* Structs used in prototypes must be declared as such first. */
 struct rproc;
+struct rprocupd;
 
 /* exec.c */
 int srv_execve(int proc_e, char *exec, size_t exec_len, char *argv[],
@@ -16,6 +17,7 @@ int do_down(message *m);
 int do_refresh(message *m);
 int do_restart(message *m);
 int do_clone(message *m);
+int do_unclone(message *m);
 int do_edit(message *m);
 int do_shutdown(message *m);
 void do_period(message *m);
@@ -25,6 +27,7 @@ int do_upd_ready(message *m);
 void do_sigchld(void);
 int do_getsysinfo(message *m);
 int do_lookup(message *m);
+int do_sysctl(message *m);
 
 /* manager.c */
 int check_call_permission(endpoint_t caller, int call, struct rproc
@@ -33,8 +36,10 @@ int copy_rs_start(endpoint_t src_e, char *src_rs_start, struct rs_start
        *rs_start);
 int copy_label(endpoint_t src_e, char *src_label, size_t src_len, char
        *dst_label, size_t dst_len);
+int init_state_data(endpoint_t src_e, int prepare_state,
+       struct rs_state_data *src_rs_state_data,
+       struct rs_state_data *dst_rs_state_data);
 void build_cmd_dep(struct rproc *rp);
-int srv_update(endpoint_t src_e, endpoint_t dst_e);
 #define kill_service(rp, errstr, err) \
        kill_service_debug(__FILE__, __LINE__, rp, errstr, err)
 int kill_service_debug(char *file, int line, struct rproc *rp, char
@@ -44,29 +49,35 @@ int kill_service_debug(char *file, int line, struct rproc *rp, char
 int crash_service_debug(char *file, int line, struct rproc *rp);
 #define cleanup_service(rp) \
        cleanup_service_debug(__FILE__, __LINE__, rp)
-void cleanup_service_debug(char *file, int line, struct rproc *rp);
+#define cleanup_service_now(rp) \
+       do { struct rproc *rpt = rp; cleanup_service(rpt); cleanup_service(rpt); } while(0)
+void cleanup_service_debug(char *file, int line,
+       struct rproc *rp);
+#define detach_service(rp) \
+       detach_service_debug(__FILE__, __LINE__, rp)
+void detach_service_debug(char *file, int line,
+       struct rproc *rp);
 int create_service(struct rproc *rp);
-int clone_service(struct rproc *rp, int instance_flag);
+int clone_service(struct rproc *rp, int instance_flag, int init_flags);
 int publish_service(struct rproc *rp);
 int unpublish_service(struct rproc *rp);
-int run_service(struct rproc *rp, int init_type);
-int start_service(struct rproc *rp);
+int run_service(struct rproc *rp, int init_type, int init_flags);
+int start_service(struct rproc *rp, int init_flags);
 void stop_service(struct rproc *rp,int how);
-int update_service(struct rproc **src_rpp, struct rproc **dst_rpp, int
-       swap_flag);
 void activate_service(struct rproc *rp, struct rproc *ex_rp);
 void terminate_service(struct rproc *rp);
 void restart_service(struct rproc *rp);
-void inherit_service_defaults(struct rproc *def_rp, struct rproc *rp);
-void get_service_instances(struct rproc *rp, struct rproc ***rps, int
-       *length);
+void inherit_service_defaults(struct rproc *def_rp,
+       struct rproc *rp);
+void get_service_instances(struct rproc *rp, struct rproc ***rps,
+       int *length);
 int read_exec(struct rproc *rp);
 void share_exec(struct rproc *rp_src, struct rproc *rp_dst);
 void free_exec(struct rproc *rp);
-int init_slot(struct rproc *rp, struct rs_start *rs_start, endpoint_t
-       source);
-int edit_slot(struct rproc *rp, struct rs_start *rs_start, endpoint_t
-       source);
+int init_slot(struct rproc *rp, struct rs_start *rs_start,
+       endpoint_t source);
+int edit_slot(struct rproc *rp, struct rs_start *rs_start,
+       endpoint_t source);
 int clone_slot(struct rproc *rp, struct rproc **clone_rpp);
 void swap_slot(struct rproc **src_rpp, struct rproc **dst_rpp);
 struct rproc* lookup_slot_by_label(char *label);
@@ -79,21 +90,58 @@ char *get_next_label(char *ptr, char *label, char *caller_label);
 void add_forward_ipc(struct rproc *rp, struct priv *privp);
 void add_backward_ipc(struct rproc *rp, struct priv *privp);
 void init_privs(struct rproc *rp, struct priv *privp);
+void end_srv_init(struct rproc *rp);
+
+/* update.c */
+void rupdate_clear_upds(void);
+void rupdate_add_upd(struct rprocupd* rpupd);
+void rupdate_set_new_upd_flags(struct rprocupd* rpupd);
+void rupdate_upd_init(struct rprocupd* rpupd, struct rproc *rp);
+void rupdate_upd_clear(struct rprocupd* rpupd);
+void rupdate_upd_move(struct rproc* src_rp, struct rproc* dst_rp);
+#define request_prepare_update_service(rp, state) \
+       request_prepare_update_service_debug(__FILE__, __LINE__, rp, state)
+void request_prepare_update_service_debug(char *file, int line,
+       struct rproc *rp, int state);
+int srv_update(endpoint_t src_e, endpoint_t dst_e, int sys_upd_flags);
+int update_service(struct rproc **src_rpp,
+       struct rproc **dst_rpp, int swap_flag, int sys_upd_flags);
+void rollback_service(struct rproc **src_rpp,
+       struct rproc **dst_rpp);
 void update_period(message *m_ptr);
-void end_update(int result, int reply_flag);
+int start_update_prepare(int allow_retries);
+struct rprocupd* start_update_prepare_next(void);
+int start_update(void);
+int start_srv_update(struct rprocupd *rpupd);
+int complete_srv_update(struct rprocupd *rpupd);
+void end_srv_update(struct rprocupd *rpupd, int result, int reply_flag);
+int abort_update_proc(int reason);
+#define end_update(result, reply_flag) \
+       end_update_debug(__FILE__, __LINE__, result, reply_flag)
+void end_update_debug(char *file, int line,
+        int result, int reply_flag);
 
 /* utility.c */
-int init_service(struct rproc *rp, int type);
+int init_service(struct rproc *rp, int type, int flags);
 void fill_send_mask(sys_map_t *send_mask, int set_bits);
-void fill_call_mask( int *calls, int tot_nr_calls, bitchunk_t
-       *call_mask, int call_base, int is_init);
-char* srv_to_string(struct rproc *rp);
+void fill_call_mask( int *calls, int tot_nr_calls,
+       bitchunk_t *call_mask, int call_base, int is_init);
+#define srv_to_string(RP) srv_to_string_gen(RP, DEBUG)
+char* srv_to_string_gen(struct rproc *rp, int is_verbose);
+char* srv_upd_to_string(struct rprocupd *rpupd);
+int rs_asynsend(struct rproc *rp, message *m_ptr, int no_reply);
+int rs_receive_ticks(endpoint_t src, message *m_ptr,
+    int *status_ptr, int ticks);
 void reply(endpoint_t who, struct rproc *rp, message *m_ptr);
 void late_reply(struct rproc *rp, int code);
 int rs_isokendpt(endpoint_t endpoint, int *proc);
 int sched_init_proc(struct rproc *rp);
-int update_sig_mgrs(struct rproc *rp, endpoint_t sig_mgr, endpoint_t
-       bak_sig_mgr);
+int update_sig_mgrs(struct rproc *rp, endpoint_t sig_mgr,
+       endpoint_t bak_sig_mgr);
+int rs_is_idle(void);
+void rs_idle_period(void);
+void print_services_status(void);
+void print_update_status(void);
 
 /* error.c */
 char * init_strerror(int errnum);
old mode 100755 (executable)
new mode 100644 (file)
index 39a9981..8d3f612
@@ -21,6 +21,7 @@ message *m_ptr;                                       /* request message pointer */
   int r;
   struct rs_start rs_start;
   int noblock;
+  int init_flags = 0;
 
   /* Check if the call can be allowed. */
   if((r = check_call_permission(m_ptr->m_source, RS_UP, NULL)) != OK)
@@ -43,7 +44,21 @@ message *m_ptr;                                      /* request message pointer */
   if (r != OK) {
       return r;
   }
+
+  /* Check flags. */
   noblock = (rs_start.rss_flags & RSS_NOBLOCK);
+  if(rs_start.rss_flags & RSS_FORCE_INIT_CRASH) {
+      init_flags |= SEF_INIT_CRASH;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_FAIL) {
+      init_flags |= SEF_INIT_FAIL;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_TIMEOUT) {
+      init_flags |= SEF_INIT_TIMEOUT;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_DEFCB) {
+      init_flags |= SEF_INIT_DEFCB;
+  }
 
   /* Initialize the slot as requested. */
   r = init_slot(rp, &rs_start, m_ptr->m_source);
@@ -65,7 +80,7 @@ message *m_ptr;                                       /* request message pointer */
   }
 
   /* All information was gathered. Now try to start the system service. */
-  r = start_service(rp);
+  r = start_service(rp, init_flags);
   if(r != OK) {
       return r;
   }
@@ -217,7 +232,7 @@ int do_clone(message *m_ptr)
 
   /* Clone the service as requested. */
   rpub->sys_flags |= SF_USE_REPL;
-  if ((r = clone_service(rp, RST_SYS_PROC)) != OK) {
+  if ((r = clone_service(rp, RST_SYS_PROC, 0)) != OK) {
       rpub->sys_flags &= ~SF_USE_REPL;
       return r;
   }
@@ -225,6 +240,51 @@ int do_clone(message *m_ptr)
   return OK;
 }
 
+/*===========================================================================*
+ *                             do_unclone                                   *
+ *===========================================================================*/
+int do_unclone(message *m_ptr)
+{
+  struct rproc *rp;
+  struct rprocpub *rpub;
+  int s, r;
+  char label[RS_MAX_LABEL_LEN];
+
+  /* Copy label. */
+  s = copy_label(m_ptr->m_source, m_ptr->m_rs_req.addr,
+      m_ptr->m_rs_req.len, label, sizeof(label));
+  if(s != OK) {
+      return s;
+  }
+
+  /* Lookup slot by label. */
+  rp = lookup_slot_by_label(label);
+  if(!rp) {
+      if(rs_verbose)
+          printf("RS: do_unclone: service '%s' not found\n", label);
+      return(ESRCH);
+  }
+  rpub = rp->r_pub;
+
+  /* Check if the call can be allowed. */
+  if((r = check_call_permission(m_ptr->m_source, RS_UNCLONE, rp)) != OK)
+      return r;
+
+  /* Don't unclone if no replica is available. */
+  if(!(rpub->sys_flags & SF_USE_REPL)) {
+      return ENOENT;
+  }
+
+  /* Unclone the service as requested. */
+  rpub->sys_flags &= ~SF_USE_REPL;
+  if(rp->r_next_rp) {
+      cleanup_service_now(rp->r_next_rp);
+      rp->r_next_rp = NULL;
+  }
+
+  return OK;
+}
+
 /*===========================================================================*
  *                                 do_edit                                  *
  *===========================================================================*/
@@ -309,7 +369,7 @@ int do_edit(message *m_ptr)
           cleanup_service(rp->r_next_rp);
           rp->r_next_rp = NULL;
       }
-      if ((r = clone_service(rp, RST_SYS_PROC)) != OK) {
+      if ((r = clone_service(rp, RST_SYS_PROC, 0)) != OK) {
           printf("RS: warning: unable to clone %s\n", srv_to_string(rp));
       }
   }
@@ -350,7 +410,12 @@ int do_refresh(message *m_ptr)
       printf("RS: %s refreshing\n", srv_to_string(rp));
   stop_service(rp,RS_REFRESHING);
 
-  return OK;
+  /* Late reply - send a reply when refresh completes. */
+  rp->r_flags |= RS_LATEREPLY;
+  rp->r_caller = m_ptr->m_source;
+  rp->r_caller_request = RS_REFRESH;
+
+  return EDONTREPLY;
 }
 
 /*===========================================================================*
@@ -391,20 +456,14 @@ int do_init_ready(message *m_ptr)
 {
   int who_p;
   message m;
-  struct rproc *rp;
+  struct rproc *rp, *new_rp;
   struct rprocpub *rpub;
-  int result, is_rs;
+  int result;
   int r;
 
-  is_rs = (m_ptr->m_source == RS_PROC_NR);
   who_p = _ENDPOINT_P(m_ptr->m_source);
   result = m_ptr->m_rs_init.result;
 
-  /* Check for RS failing initialization first. */
-  if(is_rs && result != OK) {
-      return result;
-  }
-
   rp = rproc_ptr[who_p];
   rpub = rp->r_pub;
 
@@ -423,55 +482,43 @@ int do_init_ready(message *m_ptr)
       if(rs_verbose)
           printf("RS: %s initialization error: %s\n", srv_to_string(rp),
               init_strerror(result));
-      if (result == ERESTART)
+      if (result == ERESTART && !SRV_IS_UPDATING(rp))
           rp->r_flags |= RS_REINCARNATE;
       crash_service(rp); /* simulate crash */
+      rp->r_init_err = result;
       return EDONTREPLY;
   }
 
-  /* Mark the slot as no longer initializing. */
-  rp->r_flags &= ~RS_INITIALIZING;
-  rp->r_check_tm = 0;
-  getticks(&rp->r_alive_tm);
-
-  /* Reply and unblock the service before doing anything else. */
-  m.m_type = OK;
-  reply(rpub->endpoint, rp, &m);
-
-  /* See if a late reply has to be sent. */
-  late_reply(rp, OK);
-
   if(rs_verbose)
       printf("RS: %s initialized\n", srv_to_string(rp));
 
-  /* If the service has completed initialization after a live
-   * update, end the update now.
-   */
-  if(rp->r_flags & RS_UPDATING) {
-      printf("RS: update succeeded\n");
-      end_update(OK, RS_DONTREPLY);
-  }
-
-  /* If the service has completed initialization after a crash
-   * make the new instance active and cleanup the old replica.
+  /* If updating, check if there is no service to update left. In that case,
+   * end the update process. If VM has completed initialization as part of
+   * multi-component live update, let the other services under update run now.
    */
-  if(rp->r_prev_rp) {
-      cleanup_service(rp->r_prev_rp);
-      rp->r_prev_rp = NULL;
-      rp->r_restarts += 1;
-
-      if(rs_verbose)
-          printf("RS: %s completed restart\n", srv_to_string(rp));
-  }
-
-  /* If we must keep a replica of this system service, create it now. */
-  if(rpub->sys_flags & SF_USE_REPL) {
-      if ((r = clone_service(rp, RST_SYS_PROC)) != OK) {
-          printf("RS: warning: unable to clone %s\n", srv_to_string(rp));
+  if(SRV_IS_UPDATING(rp)) {
+      rupdate.num_init_ready_pending--;
+      rp->r_flags |= RS_INIT_DONE;
+      if(rupdate.num_init_ready_pending == 0) {
+          printf("RS: update succeeded\n");
+          end_update(OK, RS_REPLY);
       }
   }
+  else {
+      /* Mark the slot as no longer initializing. */
+      rp->r_flags &= ~RS_INITIALIZING;
+      rp->r_check_tm = 0;
+      getticks(&rp->r_alive_tm);
+    
+      /* Reply and unblock the service before doing anything else. */
+      m.m_type = OK;
+      reply(rpub->endpoint, rp, &m);
+    
+      /* Finalize initialization. */
+      end_srv_init(rp);
+  }
 
-  return is_rs ? OK : EDONTREPLY; /* return what the caller expects */
+  return EDONTREPLY;
 }
 
 /*===========================================================================*
@@ -480,26 +527,25 @@ int do_init_ready(message *m_ptr)
 int do_update(message *m_ptr)
 {
   struct rproc *rp;
+  struct rproc *trg_rp;
   struct rproc *new_rp;
   struct rprocpub *rpub;
+  struct rprocupd *rpupd;
   struct rs_start rs_start;
-  int noblock, do_self_update;
+  int noblock, do_self_update, force_self_update, batch_mode, prepare_only;
   int s;
   char label[RS_MAX_LABEL_LEN];
-  int lu_state;
-  int prepare_maxtime;
+  int prepare_state, prepare_maxtime;
+  endpoint_t state_endpoint;
+  int lu_flags = 0;
+  int init_flags = 0;
+  int allow_retries = 0;
 
   /* Copy the request structure. */
   s = copy_rs_start(m_ptr->m_source, m_ptr->m_rs_req.addr, &rs_start);
   if (s != OK) {
       return s;
   }
-  noblock = (rs_start.rss_flags & RSS_NOBLOCK);
-  do_self_update = (rs_start.rss_flags & RSS_SELF_LU);
-  s = check_request(&rs_start);
-  if (s != OK) {
-      return s;
-  }
 
   /* Copy label. */
   s = copy_label(m_ptr->m_source, rs_start.rss_label.l_addr,
@@ -517,131 +563,324 @@ int do_update(message *m_ptr)
   }
   rpub = rp->r_pub;
 
+  /* Check flags. */
+  noblock = (rs_start.rss_flags & RSS_NOBLOCK);
+  do_self_update = (rs_start.rss_flags & RSS_SELF_LU);
+  force_self_update = (rs_start.rss_flags & RSS_FORCE_SELF_LU);
+  batch_mode = (rs_start.rss_flags & RSS_BATCH);
+  prepare_only = (rs_start.rss_flags & RSS_PREPARE_ONLY_LU);
+  if(do_self_update || force_self_update) {
+      lu_flags |= SEF_LU_SELF;
+  }
+  if(prepare_only) {
+      lu_flags |= SEF_LU_PREPARE_ONLY;
+  }
+  if(rs_start.rss_flags & RSS_ASR_LU) {
+      lu_flags |= SEF_LU_ASR;
+  }
+  if(rs_start.rss_flags & RSS_UNSAFE_LU) {
+      lu_flags |= SEF_LU_UNSAFE;
+  }
+  if(!prepare_only && (rs_start.rss_flags & RSS_DETACH)) {
+      lu_flags |= SEF_LU_DETACHED;
+  }
+  if(rs_start.rss_map_prealloc_bytes <= 0
+      && rpub->endpoint == VM_PROC_NR
+      && (((lu_flags & (SEF_LU_SELF|SEF_LU_ASR)) != SEF_LU_SELF) || rs_start.rss_flags & RSS_FORCE_INIT_ST)
+      && RS_VM_DEFAULT_MAP_PREALLOC_LEN > 0) {
+      /* Give VM some mmapped regions by default on non-identical updates.*/
+      rs_start.rss_map_prealloc_bytes = RS_VM_DEFAULT_MAP_PREALLOC_LEN;
+      if(rs_verbose)
+          printf("RS: %s gets %ld default mmap bytes\n", srv_to_string(rp),
+              rs_start.rss_map_prealloc_bytes);
+  }
+  if((rs_start.rss_flags & RSS_NOMMAP_LU) || rs_start.rss_map_prealloc_bytes) {
+      /* Don't inherit mmapped regions at update time if requested or if
+       * mmap preallocation is used.
+       */
+      lu_flags |= SEF_LU_NOMMAP;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_CRASH) {
+      init_flags |= SEF_INIT_CRASH;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_FAIL) {
+      init_flags |= SEF_INIT_FAIL;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_TIMEOUT) {
+      init_flags |= SEF_INIT_TIMEOUT;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_DEFCB) {
+      init_flags |= SEF_INIT_DEFCB;
+  }
+  if(rs_start.rss_flags & RSS_FORCE_INIT_ST) {
+      init_flags |= SEF_INIT_ST;
+  }
+  init_flags |= lu_flags;
+
+  /* Lookup target label (if any). */
+  trg_rp = NULL;
+  state_endpoint = NONE;
+  if(rs_start.rss_trg_label.l_len > 0) {
+      s = copy_label(m_ptr->m_source, rs_start.rss_trg_label.l_addr,
+          rs_start.rss_trg_label.l_len, label, sizeof(label));
+      if(s != OK) {
+          return s;
+      }
+      trg_rp = lookup_slot_by_label(label);
+      if(!trg_rp) {
+          if(rs_verbose)
+              printf("RS: do_update: target service '%s' not found\n", label);
+          return ESRCH;
+      }
+      state_endpoint = trg_rp->r_pub->endpoint;
+  }
+
   /* Check if the call can be allowed. */
   if((s = check_call_permission(m_ptr->m_source, RS_UPDATE, rp)) != OK)
       return s;
 
   /* Retrieve live update state. */
-  lu_state = m_ptr->m_rs_update.state;
-  if(lu_state == SEF_LU_STATE_NULL) {
+  prepare_state = m_ptr->m_rs_update.state;
+  if(prepare_state == SEF_LU_STATE_NULL) {
       return(EINVAL);
   }
 
   /* Retrieve prepare max time. */
   prepare_maxtime = m_ptr->m_rs_update.prepare_maxtime;
-  if(prepare_maxtime) {
-      if(prepare_maxtime < 0 || prepare_maxtime > RS_MAX_PREPARE_MAXTIME) {
-          return(EINVAL);
-      }
-  }
-  else {
+  if(prepare_maxtime == 0) {
       prepare_maxtime = RS_DEFAULT_PREPARE_MAXTIME;
   }
 
   /* Make sure we are not already updating. */
-  if(rupdate.flags & RS_UPDATING) {
-      if(rs_verbose)
-         printf("RS: do_update: an update is already in progress\n");
+  if(RUPDATE_IS_UPDATING()) {
+      printf("RS: an update is already in progress\n");
       return EBUSY;
   }
 
+  /* If an update is already scheduled, check constraints. */
+  if(RUPDATE_IS_UPD_SCHEDULED()) {
+      if(!batch_mode) {
+          printf("RS: an update is already scheduled, cannot start a new one\n");
+          return EBUSY;
+      }
+      if(SRV_IS_UPD_SCHEDULED(rp)) {
+          printf("RS: the specified process is already part of the currently scheduled update\n");
+          return EINVAL;
+      }
+      if(rupdate.last_rpupd->rp->r_pub->endpoint == RS_PROC_NR) {
+          printf("RS: RS should always be the last service to update in a multi-component update\n");
+          return EINVAL;
+      }
+  }
+
+  /* Prepare-only update for VM, PM, and VFS is only supported with an unreachable state. */
+  if(prepare_only
+      && (rp->r_pub->endpoint == VM_PROC_NR || rp->r_pub->endpoint == PM_PROC_NR || rp->r_pub->endpoint == VFS_PROC_NR)) {
+      if(prepare_state != SEF_LU_STATE_UNREACHABLE) {
+          printf("RS: prepare-only update for VM, PM and VFS is only supported with state %d\n", SEF_LU_STATE_UNREACHABLE);
+          return EINVAL;
+      }
+  }
+
+  /* Prepare-only update for RS is not supported. */
+  if(prepare_only && rp->r_pub->endpoint == RS_PROC_NR) {
+      printf("RS: prepare-only update for RS is not supported\n");
+      return EINVAL;
+  }
+
+  /* Initialize update descriptor. */
+  rpupd = &rp->r_upd;
+  rupdate_upd_init(rpupd, rp);
+  rpupd->lu_flags |= lu_flags;
+  rpupd->init_flags |= init_flags;
+  rupdate_set_new_upd_flags(rpupd);
+
   /* A self update live updates a service instance into a replica, a regular
    * update live updates a service instance into a new version, as specified
    * by the given binary.
    */
-  if(do_self_update) {
-      if(rs_verbose)
-          printf("RS: %s performs self update\n", srv_to_string(rp));
+  if(!prepare_only) {
+      if(do_self_update) {
+          if(rs_verbose)
+              printf("RS: %s requested to perform self update\n", srv_to_string(rp));
+    
+          /* Clone the system service and use the replica as the new version. */
+          s = clone_service(rp, LU_SYS_PROC, rpupd->init_flags);
+          if(s != OK) {
+              printf("RS: do_update: unable to clone service: %d\n", s);
+              return s;
+          }
+          new_rp = rp->r_new_rp;
+      }
+      else {
+          if(rs_verbose)
+              printf("RS: %s requested to perform %s update\n", srv_to_string(rp),
+                  force_self_update ? "(forced) self" : "regular");
+    
+          /* Allocate a system service slot for the new version. */
+          s = alloc_slot(&new_rp);
+          if(s != OK) {
+              printf("RS: do_update: unable to allocate a new slot: %d\n", s);
+              return s;
+          }
+    
+          /* Initialize the slot as requested. */
+          s = init_slot(new_rp, &rs_start, m_ptr->m_source);
+          if(s != OK) {
+              printf("RS: do_update: unable to init the new slot: %d\n", s);
+              return s;
+          }
+    
+          /* Let the new version inherit defaults from the old one. */
+          inherit_service_defaults(rp, new_rp);
+    
+          /* Link the two versions. */
+          rp->r_new_rp = new_rp;
+          new_rp->r_old_rp = rp;
+    
+          /* Create new version of the service but don't let it run. */
+          new_rp->r_priv.s_flags |= LU_SYS_PROC;
+          new_rp->r_priv.s_init_flags |= rpupd->init_flags;
+          s = create_service(new_rp);
+          if(s != OK) {
+              printf("RS: do_update: unable to create a new service: %d\n", s);
+              return s;
+          }
+      }
 
-      /* Clone the system service and use the replica as the new version. */
-      s = clone_service(rp, LU_SYS_PROC);
-      if(s != OK) {
-          printf("RS: do_update: unable to clone service: %d\n", s);
-          return s;
+      /* Set default state endpoint. */
+      if(state_endpoint == NONE) {
+          state_endpoint = new_rp->r_pub->endpoint;
       }
-  }
-  else {
-      if(rs_verbose)
-          printf("RS: %s performs regular update\n", srv_to_string(rp));
 
-      /* Allocate a system service slot for the new version. */
-      s = alloc_slot(&new_rp);
-      if(s != OK) {
-          printf("RS: do_update: unable to allocate a new slot: %d\n", s);
-          return s;
+      /* If RS is updating, set up signal managers for the new instance.
+       * The current RS instance must be made the backup signal manager to
+       * support rollback in case of a crash during initialization.
+       */
+      if(rp->r_priv.s_flags & ROOT_SYS_PROC) {
+          s = update_sig_mgrs(new_rp, SELF, new_rp->r_pub->endpoint);
+          if(s != OK) {
+              cleanup_service(new_rp);
+              return s;
+          }
       }
 
-      /* Initialize the slot as requested. */
-      s = init_slot(new_rp, &rs_start, m_ptr->m_source);
-      if(s != OK) {
-          printf("RS: do_update: unable to init the new slot: %d\n", s);
-          return s;
+      /* Preallocate heap regions if requested. */
+      if(rs_start.rss_heap_prealloc_bytes < 0) {
+          rs_start.rss_heap_prealloc_bytes = 0;
+      }
+      if(rs_start.rss_heap_prealloc_bytes) {
+          size_t len;
+          if(rs_verbose)
+              printf("RS: %s preallocating %ld heap bytes\n", srv_to_string(new_rp),
+                  rs_start.rss_heap_prealloc_bytes);
+
+          len = rs_start.rss_heap_prealloc_bytes;
+          s = vm_memctl(new_rp->r_pub->endpoint, VM_RS_MEM_HEAP_PREALLOC,
+              NULL, &len);
+          if(s != OK) {
+              printf("vm_memctl(VM_RS_MEM_HEAP_PREALLOC) failed: %d\n", s);
+              cleanup_service(new_rp);
+              return s;
+          }
+          if(rp->r_priv.s_flags & ROOT_SYS_PROC) {
+              vm_memctl(new_rp->r_pub->endpoint, VM_RS_MEM_PIN, 0, 0);
+          }
       }
 
-      /* Let the new version inherit defaults from the old one. */
-      inherit_service_defaults(rp, new_rp);
+      /* Preallocate mmapped regions if requested. */
+      if(rs_start.rss_map_prealloc_bytes < 0) {
+          rs_start.rss_map_prealloc_bytes = 0;
+      }
+      if(rs_start.rss_map_prealloc_bytes) {
+          void *addr = NULL;
+          if(rs_verbose)
+              printf("RS: %s preallocating %ld mmap bytes\n", srv_to_string(new_rp),
+                  rs_start.rss_map_prealloc_bytes);
+
+          new_rp->r_map_prealloc_len = rs_start.rss_map_prealloc_bytes;
+          s = vm_memctl(new_rp->r_pub->endpoint, VM_RS_MEM_MAP_PREALLOC,
+              &addr, &new_rp->r_map_prealloc_len);
+          if(s != OK) {
+              printf("vm_memctl(VM_RS_MEM_MAP_PREALLOC) failed: %d\n", s);
+              cleanup_service(new_rp);
+              return s;
+          }
+          new_rp->r_map_prealloc_addr = (vir_bytes) addr;
+      }
+  }
 
-      /* Link the two versions. */
-      rp->r_new_rp = new_rp;
-      new_rp->r_old_rp = rp;
+  /* Process state data. */
+  s = init_state_data(m_ptr->m_source, prepare_state, &rs_start.rss_state_data, &rpupd->prepare_state_data);
+  if(s != OK) {
+      rupdate_upd_clear(rpupd);
+      return s;
+  }
 
-      /* Create new version of the service but don't let it run. */
-      new_rp->r_priv.s_flags |= LU_SYS_PROC;
-      s = create_service(new_rp);
-      if(s != OK) {
-          printf("RS: do_update: unable to create a new service: %d\n", s);
-          return s;
+  /* Create update grants. */
+  if(rpupd->prepare_state_data.size > 0) {
+      struct rs_state_data *state_data = &rpupd->prepare_state_data;
+      rpupd->prepare_state_data_gid = cpf_grant_direct(rpub->endpoint, (vir_bytes) state_data,
+          state_data->size, CPF_READ);
+      if(rpupd->prepare_state_data_gid == GRANT_INVALID) {
+          rupdate_upd_clear(rpupd);
+          return ENOMEM;
+      }
+      state_data->ipcf_els_gid = GRANT_INVALID;
+      if(state_data->ipcf_els) {
+          state_data->ipcf_els_gid = (int) cpf_grant_direct(rpub->endpoint, (vir_bytes) state_data->ipcf_els,
+              state_data->ipcf_els_size, CPF_READ);
+          if(state_data->ipcf_els_gid == GRANT_INVALID) {
+              rupdate_upd_clear(rpupd);
+              return ENOMEM;
+          }
+      }
+      state_data->eval_gid = GRANT_INVALID;
+      if(state_data->eval_addr) {
+          state_data->eval_gid = (int) cpf_grant_direct(rpub->endpoint, (vir_bytes) state_data->eval_addr,
+              state_data->eval_len, CPF_READ);
+          if(state_data->eval_gid == GRANT_INVALID) {
+              rupdate_upd_clear(rpupd);
+              return ENOMEM;
+          }
       }
   }
 
-  /* Mark both versions as updating. */
-  rp->r_flags |= RS_UPDATING;
-  rp->r_new_rp->r_flags |= RS_UPDATING;
-  rupdate.flags |= RS_UPDATING;
-  getticks(&rupdate.prepare_tm);
-  rupdate.prepare_maxtime = prepare_maxtime;
-  rupdate.rp = rp;
+  /* Fill the new update descriptor and add it to the update chain. */
+  rpupd->prepare_state = prepare_state;
+  rpupd->state_endpoint = state_endpoint;
+  getticks(&rpupd->prepare_tm);
+  rpupd->prepare_maxtime = prepare_maxtime;
+  rupdate_add_upd(rpupd);
 
   if(rs_verbose)
-    printf("RS: %s updating\n", srv_to_string(rp));
-
-  /* If RS is updating, set up signal managers for the new instance.
-   * The current RS instance must be made the backup signal manager to
-   * support rollback in case of a crash during initialization.
-   */
-  if(rp->r_priv.s_flags & ROOT_SYS_PROC) {
-      new_rp = rp->r_new_rp;
+      printf("RS: %s scheduled for %s\n", srv_to_string(rp), srv_upd_to_string(rpupd));
 
-      s = update_sig_mgrs(new_rp, SELF, new_rp->r_pub->endpoint);
-      if(s != OK) {
-          cleanup_service(new_rp);
-          return s;
-      }
+  /* If batch mode, reply immediately. More services to update will follow. */
+  if(batch_mode) {
+      return OK;
   }
 
-  if(noblock) {
-      /* Unblock the caller immediately if requested. */
-      m_ptr->m_type = OK;
-      reply(m_ptr->m_source, NULL, m_ptr);
+  /* Start preparing for the update process. */
+  s = start_update_prepare(allow_retries);
+  if(s == ESRCH) {
+      /* No process left in the update chain. We are done already. */
+      return OK;
   }
-  else {
-      /* Send a reply when the new version completes initialization. */
-      rp->r_flags |= RS_LATEREPLY;
-      rp->r_caller = m_ptr->m_source;
-      rp->r_caller_request = RS_UPDATE;
+  if(s != OK) {
+      return s;
   }
 
-  /* Request to update. */
-  m_ptr->m_type = RS_LU_PREPARE;
-  if(rpub->endpoint == RS_PROC_NR) {
-      /* RS can process the request directly. */
-      do_sef_lu_request(m_ptr);
-  }
-  else {
-      /* Send request message to the system service. */
-      asynsend3(rpub->endpoint, m_ptr, AMF_NOREPLY);
+  /* Unblock the caller immediately if requested. */
+  if(noblock) {
+      return OK;
   }
 
+  /* Otherwise, send a reply when the new version completes initialization. */
+  rupdate.last_rpupd->rp->r_flags |= RS_LATEREPLY;
+  rupdate.last_rpupd->rp->r_caller = m_ptr->m_source;
+  rupdate.last_rpupd->rp->r_caller_request = RS_UPDATE;
+
   return EDONTREPLY;
 }
 
@@ -650,74 +889,50 @@ int do_update(message *m_ptr)
  *===========================================================================*/
 int do_upd_ready(message *m_ptr)
 {
-  struct rproc *rp, *old_rp, *new_rp;
+  struct rproc *rp;
+  struct rprocupd *prev_rpupd, *rpupd;
   int who_p;
   int result;
   int is_rs;
-  int r;
+  int i;
 
   who_p = _ENDPOINT_P(m_ptr->m_source);
   rp = rproc_ptr[who_p];
   result = m_ptr->m_rs_update.result;
-  is_rs = (m_ptr->m_source == RS_PROC_NR);
 
   /* Make sure the originating service was requested to prepare for update. */
-  if(rp != rupdate.rp) {
+  rpupd = rupdate.curr_rpupd;
+  if(!rpupd || rp != rpupd->rp || RUPDATE_IS_INITIALIZING()) {
       if(rs_verbose)
-          printf("RS: do_upd_ready: got unexpected update ready msg from %d\n",
-              m_ptr->m_source);
+          printf("RS: %s sent late/unexpected update ready msg\n",
+              srv_to_string(rp));
       return EINVAL;
   }
+  rp->r_flags |= RS_PREPARE_DONE;
 
   /* Check if something went wrong and the service failed to prepare
    * for the update. In that case, end the update process. The old version will
    * be replied to and continue executing.
    */
   if(result != OK) {
+      printf("RS: update failed: %s\n", lu_strerror(result));
       end_update(result, RS_REPLY);
 
-      printf("RS: update failed: %s\n", lu_strerror(result));
-      return is_rs ? result : EDONTREPLY; /* return what the caller expects */
+      return EDONTREPLY;
   }
 
-  old_rp = rp;
-  new_rp = rp->r_new_rp;
-
-  /* If RS itself is updating, yield control to the new version immediately. */
-  if(is_rs) {
-      r = init_service(new_rp, SEF_INIT_LU);
-      if(r != OK) {
-          panic("unable to initialize the new RS instance: %d", r);
-      }
-      r = sys_privctl(new_rp->r_pub->endpoint, SYS_PRIV_YIELD, NULL);
-      if(r != OK) {
-          panic("unable to yield control to the new RS instance: %d", r);
-      }
-      /* If we get this far, the new version failed to initialize. Rollback. */
-      r = srv_update(RS_PROC_NR, new_rp->r_pub->endpoint);
-      assert(r == OK); /* can't fail */
-      end_update(ERESTART, RS_REPLY);
-      return ERESTART;
-  }
+  if(rs_verbose)
+      printf("RS: %s ready to update\n", srv_to_string(rp));
 
-  /* Perform the update. */
-  r = update_service(&old_rp, &new_rp, RS_SWAP);
-  if(r != OK) {
-      end_update(r, RS_REPLY);
-      printf("RS: update failed: error %d\n", r);
+  /* If this is a multi-component update and this is not the last service
+   * in the update, request the next process to update.
+   */
+  if(start_update_prepare_next() != NULL) {
       return EDONTREPLY;
   }
 
-  /* Let the new version run. */
-  r = run_service(new_rp, SEF_INIT_LU);
-  if(r != OK) {
-      /* Something went wrong. Rollback. */
-      r = update_service(&new_rp, &old_rp, RS_SWAP);
-      assert(r == OK); /* can't fail */
-      end_update(r, RS_REPLY);
-      printf("RS: update failed: error %d\n", r);
-      return EDONTREPLY;
-  }
+  /* Now perform the update and request each new instance to initialize. */
+  start_update();
 
   return EDONTREPLY;
 }
@@ -735,7 +950,7 @@ message *m_ptr;
   long period;
 
   /* If an update is in progress, check its status. */
-  if(rupdate.flags & RS_UPDATING) {
+  if(RUPDATE_IS_UPDATING() && !RUPDATE_IS_INITIALIZING()) {
       update_period(m_ptr);
   }
 
@@ -744,12 +959,13 @@ message *m_ptr;
    */
   for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
       rpub = rp->r_pub;
-      if ((rp->r_flags & RS_ACTIVE) && !(rp->r_flags & RS_UPDATING)) {
+
+      if ((rp->r_flags & RS_ACTIVE) && (!SRV_IS_UPDATING(rp) || ((rp->r_flags & (RS_INITIALIZING|RS_INIT_DONE|RS_INIT_PENDING)) == RS_INITIALIZING))) {
 
           /* Compute period. */
           period = rp->r_period;
           if(rp->r_flags & RS_INITIALIZING) {
-              period = RS_INIT_T;
+              period = SRV_IS_UPDATING(rp) ? UPD_INIT_MAXTIME(&rp->r_upd) : RS_INIT_T;
           }
 
           /* If the service is to be revived (because it repeatedly exited, 
@@ -787,10 +1003,16 @@ message *m_ptr;
               */
               if (rp->r_alive_tm < rp->r_check_tm) { 
                  if (now - rp->r_alive_tm > 2*period &&
-                     rp->r_pid > 0 && !(rp->r_flags & RS_NOPINGREPLY)) { 
+                     rp->r_pid > 0 && !(rp->r_flags & RS_NOPINGREPLY)) {
+                     struct rproc *rp2;
+                     int init_flag;
                      if(rs_verbose)
                            printf("RS: %s reported late\n", srv_to_string(rp)); 
-                     if(lookup_slot_by_flags(RS_INITIALIZING)) {
+                      init_flag = rp->r_flags & RS_INITIALIZING;
+                      rp->r_flags &= ~RS_INITIALIZING;
+                      rp2 = lookup_slot_by_flags(RS_INITIALIZING);
+                      rp->r_flags |= init_flag;
+                     if(rp2 != NULL && !SRV_IS_UPDATING(rp)) {
                            /* Skip for now. */
                            if(rs_verbose)
                                printf("RS: %s gets a free pass\n",
@@ -801,6 +1023,9 @@ message *m_ptr;
                      }
                      rp->r_flags |= RS_NOPINGREPLY;
                       crash_service(rp); /* simulate crash */
+                      if(rp->r_flags & RS_INITIALIZING) {
+                          rp->r_init_err = EINTR;
+                      }
                  }
              }
 
@@ -830,7 +1055,7 @@ void do_sigchld()
   int status;
   struct rproc *rp;
   struct rproc **rps;
-  int i, nr_rps;
+  int i, nr_rps, found;
 
   if(rs_verbose)
      printf("RS: got SIGCHLD signal, cleaning up dead children\n");
@@ -848,13 +1073,18 @@ void do_sigchld()
            * free slots for all the service instances and send a late
            * reply if necessary.
            */
+          found = 0;
           get_service_instances(rp, &rps, &nr_rps);
           for(i=0;i<nr_rps;i++) {
-              if(rupdate.flags & RS_UPDATING) {
-                  rupdate.flags &= ~RS_UPDATING;
+              if(SRV_IS_UPDATING(rps[i])) {
+                  rps[i]->r_flags &= ~(RS_UPDATING|RS_PREPARE_DONE|RS_INIT_DONE|RS_INIT_PENDING);
+                  found = 1;
               }
               free_slot(rps[i]);
           }
+          if(found) {
+              rupdate_clear_upds();
+          }
       }
   }
 }
@@ -945,6 +1175,54 @@ message *m_ptr;
        return OK;
 }
 
+/*===========================================================================*
+ *                             do_sysctl                                    *
+ *===========================================================================*/
+int do_sysctl(message *m_ptr)
+{
+       int request_type = m_ptr->m_rs_req.subtype;
+       int r, allow_retries = 1;
+       switch(request_type) {
+               case RS_SYSCTL_SRV_STATUS:
+                       print_services_status();
+               break;
+               case RS_SYSCTL_UPD_START:
+               case RS_SYSCTL_UPD_RUN:
+                       r = start_update_prepare(allow_retries);
+                       print_update_status();
+                       if(r != OK) {
+                           if(r == ESRCH) {
+                               /* We are done already. */
+                                r = OK;
+                           }
+                           return r;
+                       }
+                       if(request_type == RS_SYSCTL_UPD_START) {
+                           return OK;
+                       }
+                        /* Send a reply when done. */
+                        rupdate.last_rpupd->rp->r_flags |= RS_LATEREPLY;
+                        rupdate.last_rpupd->rp->r_caller = m_ptr->m_source;
+                        rupdate.last_rpupd->rp->r_caller_request = RS_UPDATE;
+                       return EDONTREPLY;
+               break;
+               case RS_SYSCTL_UPD_STOP:
+                       r = abort_update_proc(EINTR);
+                       print_update_status();
+                       return r;
+               break;
+               case RS_SYSCTL_UPD_STATUS:
+                       print_update_status();
+               break;
+               default:
+                       printf("RS: bad sysctl type\n");
+                       return EINVAL;
+               break;
+       }
+
+       return OK;
+}
+
 /*===========================================================================*
  *                                check_request                             *
  *===========================================================================*/
index dcc21a6cbc3534e8ab483c8ee20fdeb5c7057d9b..121ba74622150b2d34b396394e7bc548872a7b6d 100644 (file)
@@ -25,6 +25,32 @@ struct boot_image_dev {
   dev_t dev_nr;                /* major device number */
 };
 
+/* Definition of the update descriptors. */
+struct rproc;
+struct rprocupd {
+  int lu_flags;                   /* user-specified live update flags */
+  int init_flags;                 /* user-specified init flags */
+  int prepare_state;       /* the state the process has to prepare for the update */
+  endpoint_t state_endpoint; /* the custom process to transfer the state from (if any). */
+  clock_t prepare_tm;      /* timestamp of when the update was scheduled */
+  clock_t prepare_maxtime; /* max time to wait for the process to be ready */
+  struct rproc *rp;        /* the process under update */
+  struct rs_state_data prepare_state_data; /* state data for the update */
+  cp_grant_id_t prepare_state_data_gid; /* state data gid */
+  struct rprocupd *prev_rpupd;   /* the previous process under update */
+  struct rprocupd *next_rpupd;   /* the next process under update */
+};
+struct rupdate {
+  int flags;               /* flags to keep track of the status of the update */
+  int num_rpupds;          /* number of descriptors scheduled for the update */
+  int num_init_ready_pending;   /* number of pending init ready messages */
+  struct rprocupd *curr_rpupd;  /* the current descriptor under update */
+  struct rprocupd *first_rpupd; /* first descriptor scheduled for the update */
+  struct rprocupd *last_rpupd;  /* last descriptor scheduled for the update */
+  struct rprocupd *vm_rpupd;    /* VM descriptor scheduled for the update */
+  struct rprocupd *rs_rpupd;    /* RS descriptor scheduled for the update */
+};
+
 /* Definition of an entry of the system process table. */
 struct rproc {
   struct rprocpub *r_pub;       /* pointer to the corresponding public entry */
@@ -32,11 +58,13 @@ struct rproc {
   struct rproc *r_new_rp;       /* pointer to the slot with the new version */
   struct rproc *r_prev_rp;      /* pointer to the slot with the prev replica */
   struct rproc *r_next_rp;      /* pointer to the slot with the next replica */
+  struct rprocupd r_upd;        /* update descriptor */
   pid_t r_pid;                 /* process id, -1 if the process is not there */
 
   int r_restarts;              /* number of restarts (initially zero) */
   long r_backoff;              /* number of periods to wait before revive */
   unsigned r_flags;            /* status and policy flags */
+  int r_init_err;               /* error code at initialization time */
 
   long r_period;               /* heartbeat period (or zero) */
   clock_t r_check_tm;          /* timestamp of last check */
@@ -63,6 +91,8 @@ struct rproc {
   int r_priority;              /* negative values are reserved for special meanings */
   int r_quantum;
   int r_cpu;
+  vir_bytes r_map_prealloc_addr; /* preallocated mmap address */
+  size_t r_map_prealloc_len;     /* preallocated mmap len */
 
   /* Backup values from the privilege structure. */
   struct io_range r_io_tab[NR_IO_RANGE];
@@ -75,12 +105,6 @@ struct rproc {
   char r_control[RS_NR_CONTROL][RS_MAX_LABEL_LEN];
 };
 
-/* Definition of the global update descriptor. */
-struct rupdate {
-  int flags;               /* flags to keep track of the status of the update */
-  clock_t prepare_tm;      /* timestamp of when the update was scheduled */
-  clock_t prepare_maxtime; /* max time to wait for the process to be ready */
-  struct rproc *rp;        /* the process under update */
-};
-
 #endif /* RS_TYPE_H */
+
+
diff --git a/minix/servers/rs/update.c b/minix/servers/rs/update.c
new file mode 100644 (file)
index 0000000..b964936
--- /dev/null
@@ -0,0 +1,960 @@
+
+#include "inc.h"
+
+/*===========================================================================*
+ *                           rupdate_clear_upds                             *
+ *===========================================================================*/
+void rupdate_clear_upds()
+{
+  /* Clear the update chain and the global update descriptor. */
+  struct rprocupd *prev_rpupd, *rpupd;
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      if(prev_rpupd) {
+          rupdate_upd_clear(prev_rpupd);
+      }
+  );
+  rupdate_upd_clear(rupdate.last_rpupd);
+  RUPDATE_CLEAR();
+}
+
+/*===========================================================================*
+ *                            rupdate_add_upd                               *
+ *===========================================================================*/
+void rupdate_add_upd(struct rprocupd* rpupd)
+{
+  /* Add an update descriptor to the update chain. */
+  struct rprocupd* prev_rpupd;
+  int lu_flags;
+
+  rpupd->prev_rpupd = rupdate.last_rpupd;
+  if(rupdate.num_rpupds == 0) {
+      rupdate.first_rpupd = rpupd;
+      rupdate.curr_rpupd = rpupd;
+  }
+  else {
+      rupdate.last_rpupd->next_rpupd = rpupd;
+  }
+  rupdate.last_rpupd = rpupd;
+  rupdate.num_rpupds++;
+
+  /* Propagate relevant flags from the new descriptor. */
+  lu_flags = rpupd->lu_flags & (SEF_LU_INCLUDES_VM|SEF_LU_INCLUDES_RS|SEF_LU_UNSAFE|SEF_LU_MULTI);
+  if(lu_flags) {
+      RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+          rpupd->lu_flags |= lu_flags;
+          rpupd->init_flags |= lu_flags;
+      );
+  }
+
+  /* Set VM/RS update descriptor pointers. */
+  if(!rupdate.vm_rpupd && (lu_flags & SEF_LU_INCLUDES_VM)) {
+      rupdate.vm_rpupd = rupdate.last_rpupd;
+  }
+  else if(!rupdate.rs_rpupd && (lu_flags & SEF_LU_INCLUDES_RS)) {
+      rupdate.rs_rpupd = rupdate.last_rpupd;
+  }
+}
+
+/*===========================================================================*
+ *                       rupdate_set_new_upd_flags                          *
+ *===========================================================================*/
+void rupdate_set_new_upd_flags(struct rprocupd* rpupd)
+{
+  /* Set multi-component update flags. */
+  if(rupdate.num_rpupds > 0) {
+      rpupd->lu_flags |= SEF_LU_MULTI;
+      rpupd->init_flags |= SEF_LU_MULTI;
+  }
+
+  /* Propagate relevant flags from last service under update (if any). */
+  if(rupdate.last_rpupd) {
+      int lu_flags = rupdate.last_rpupd->lu_flags & (SEF_LU_INCLUDES_VM|SEF_LU_INCLUDES_RS|SEF_LU_UNSAFE);
+      rpupd->lu_flags |= lu_flags;
+      rpupd->init_flags |= lu_flags;
+  }
+
+  if(UPD_IS_PREPARING_ONLY(rpupd)) {
+      return;
+  }
+
+  /* Set VM/RS update flags. */
+  if(rpupd->rp->r_pub->endpoint == VM_PROC_NR) {
+      rpupd->lu_flags |= SEF_LU_INCLUDES_VM;
+      rpupd->init_flags |= SEF_LU_INCLUDES_VM;
+  }
+  else if(rpupd->rp->r_pub->endpoint == RS_PROC_NR) {
+      rpupd->lu_flags |= SEF_LU_INCLUDES_RS;
+      rpupd->init_flags |= SEF_LU_INCLUDES_RS;
+  }
+}
+
+/*===========================================================================*
+ *                           rupdate_upd_init                               *
+ *===========================================================================*/
+void rupdate_upd_init(struct rprocupd* rpupd, struct rproc *rp)
+{
+  /* Initialize an update descriptor for a given service. */
+  memset(rpupd, 0, sizeof(*(rpupd)));
+  rpupd->prepare_state_data_gid = GRANT_INVALID;
+  rpupd->prepare_state_data.ipcf_els_gid = GRANT_INVALID;
+  rpupd->prepare_state_data.eval_gid = GRANT_INVALID;
+  rpupd->state_endpoint = NONE;
+  rpupd->rp = rp;
+}
+
+/*===========================================================================*
+ *                           rupdate_upd_clear                              *
+ *===========================================================================*/
+void rupdate_upd_clear(struct rprocupd* rpupd)
+{
+  /* Clear an update descriptor. */
+  if(rpupd->rp->r_new_rp) {
+      cleanup_service(rpupd->rp->r_new_rp);
+  }
+  if(rpupd->prepare_state_data_gid != GRANT_INVALID) {
+      cpf_revoke(rpupd->prepare_state_data_gid);
+  }
+  if(rpupd->prepare_state_data.size > 0) {
+      if(rpupd->prepare_state_data.ipcf_els_gid != GRANT_INVALID) {
+          cpf_revoke(rpupd->prepare_state_data.ipcf_els_gid);
+      }
+      if(rpupd->prepare_state_data.eval_gid != GRANT_INVALID) {
+          cpf_revoke(rpupd->prepare_state_data.eval_gid);
+      }
+      if(rpupd->prepare_state_data.ipcf_els) {
+          free(rpupd->prepare_state_data.ipcf_els);
+      }
+      if(rpupd->prepare_state_data.eval_addr) {
+          free(rpupd->prepare_state_data.eval_addr);
+      }
+  }
+  rupdate_upd_init(rpupd,NULL);
+}
+
+/*===========================================================================*
+ *                            rupdate_upd_move                              *
+ *===========================================================================*/
+void rupdate_upd_move(struct rproc* src_rp, struct rproc* dst_rp)
+{
+  /* Move an update descriptor from one service instance to another. */
+  dst_rp->r_upd = src_rp->r_upd;
+  dst_rp->r_upd.rp = dst_rp;
+  if(src_rp->r_new_rp) {
+      assert(!dst_rp->r_new_rp);
+      dst_rp->r_new_rp = src_rp->r_new_rp;
+      dst_rp->r_new_rp->r_old_rp = dst_rp;
+  }
+  if(dst_rp->r_upd.prev_rpupd) dst_rp->r_upd.prev_rpupd->next_rpupd = &dst_rp->r_upd;
+  if(dst_rp->r_upd.next_rpupd) dst_rp->r_upd.next_rpupd->prev_rpupd = &dst_rp->r_upd;
+  if(rupdate.first_rpupd == &src_rp->r_upd) rupdate.first_rpupd = &dst_rp->r_upd;
+  if(rupdate.last_rpupd == &src_rp->r_upd) rupdate.last_rpupd = &dst_rp->r_upd;
+  rupdate_upd_init(&src_rp->r_upd, NULL);
+  src_rp->r_new_rp = NULL;
+}
+
+/*===========================================================================*
+ *                  request_prepare_update_service_debug                    *
+ *===========================================================================*/
+void request_prepare_update_service_debug(char *file, int line,
+  struct rproc *rp, int state)
+{
+  /* Request a service to prepare/cancel the update. */
+  message m;
+  struct rprocpub *rpub;
+  int no_reply;
+
+  rpub = rp->r_pub;
+
+  if(state != SEF_LU_STATE_NULL) {
+      struct rprocupd *rpupd = &rp->r_upd;
+      getticks(&rpupd->prepare_tm);
+      if(!UPD_IS_PREPARING_ONLY(rpupd)) {
+          assert(rp->r_new_rp);
+          rp->r_flags |= RS_UPDATING;
+          rp->r_new_rp->r_flags |= RS_UPDATING;
+      }
+      else {
+          assert(!rp->r_new_rp);
+      }
+
+      m.m_rs_update.flags = rpupd->lu_flags;
+      m.m_rs_update.state_data_gid = rpupd->prepare_state_data_gid;
+
+      if(rs_verbose)
+          printf("RS: %s being requested to prepare for the %s at %s:%d\n", 
+              srv_to_string(rp), srv_upd_to_string(rpupd), file, line);
+  }
+  else {
+      if(rs_verbose)
+          printf("RS: %s being requested to cancel the update at %s:%d\n", 
+              srv_to_string(rp), file, line);
+  }
+
+  /* Request to prepare for the update or cancel the update. */
+  m.m_type = RS_LU_PREPARE;
+  m.m_rs_update.state = state;
+  no_reply = !(rp->r_flags & RS_PREPARE_DONE);
+  rs_asynsend(rp, &m, no_reply);
+}
+
+/*===========================================================================*
+ *                              srv_update                                  *
+ *===========================================================================*/
+int srv_update(endpoint_t src_e, endpoint_t dst_e, int sys_upd_flags)
+{
+  int r = OK;
+
+  /* Ask VM to swap the slots of the two processes and tell the kernel to
+   * do the same. If VM is being updated, only perform the kernel
+   * part of the call. The new instance of VM will do the rest at
+   * initialization time. If a multi-component update includes VM, let VM
+   * handle updates at state transfer time and rollbacks afterwards.
+   */
+  if(src_e == VM_PROC_NR) {
+      if(rs_verbose)
+          printf("RS: executing sys_update(%d, %d)\n", src_e, dst_e);
+      r = sys_update(src_e, dst_e,
+          sys_upd_flags & SF_VM_ROLLBACK ? SYS_UPD_ROLLBACK : 0);
+  }
+  else if(!RUPDATE_IS_UPD_VM_MULTI() || RUPDATE_IS_VM_INIT_DONE()) {
+       if(rs_verbose)
+           printf("RS: executing vm_update(%d, %d)\n", src_e, dst_e);
+       r = vm_update(src_e, dst_e, sys_upd_flags);
+   }
+   else {
+       if(rs_verbose)
+           printf("RS: skipping srv_update(%d, %d)\n", src_e, dst_e);
+   }
+
+  return r;
+}
+
+/*===========================================================================*
+ *                             update_service                               *
+ *===========================================================================*/
+int update_service(src_rpp, dst_rpp, swap_flag, sys_upd_flags)
+struct rproc **src_rpp;
+struct rproc **dst_rpp;
+int swap_flag;
+int sys_upd_flags;
+{
+/* Update an existing service. */
+  int r;
+  struct rproc *src_rp;
+  struct rproc *dst_rp;
+  struct rprocpub *src_rpub;
+  struct rprocpub *dst_rpub;
+  int pid;
+  endpoint_t endpoint;
+
+  src_rp = *src_rpp;
+  dst_rp = *dst_rpp;
+  src_rpub = src_rp->r_pub;
+  dst_rpub = dst_rp->r_pub;
+
+  if(rs_verbose)
+      printf("RS: %s updating into %s\n",
+          srv_to_string(src_rp), srv_to_string(dst_rp));
+
+  /* Swap the slots of the two processes when asked to. */
+  if(swap_flag == RS_SWAP) {
+      if((r = srv_update(src_rpub->endpoint, dst_rpub->endpoint, sys_upd_flags)) != OK) {
+          return r;
+      }
+  }
+
+  /* Swap slots here as well. */
+  pid = src_rp->r_pid;
+  endpoint = src_rpub->endpoint;
+  swap_slot(&src_rp, &dst_rp);
+
+  /* Reassign pids and endpoints. */
+  src_rp->r_pid = dst_rp->r_pid;
+  src_rp->r_pub->endpoint = dst_rp->r_pub->endpoint;
+  rproc_ptr[_ENDPOINT_P(src_rp->r_pub->endpoint)] = src_rp;
+  dst_rp->r_pid = pid;
+  dst_rp->r_pub->endpoint = endpoint;
+  rproc_ptr[_ENDPOINT_P(dst_rp->r_pub->endpoint)] = dst_rp;
+
+  /* Adjust input pointers. */
+  *src_rpp = src_rp;
+  *dst_rpp = dst_rp;
+
+  /* Make the new version active. */
+  activate_service(dst_rp, src_rp);
+
+  if(rs_verbose)
+      printf("RS: %s updated into %s\n",
+          srv_to_string(src_rp), srv_to_string(dst_rp));
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                           rollback_service                               *
+ *===========================================================================*/
+void rollback_service(struct rproc **new_rpp, struct rproc **old_rpp)
+{
+  /* Rollback an updated service. */
+  int r = OK;
+
+  /* RS is special, we may only need to swap the slots to rollback. */
+  if((*old_rpp)->r_pub->endpoint == RS_PROC_NR) {
+      endpoint_t me = NONE;
+      char name[20];
+      int priv_flags, init_flags;
+
+      r = sys_whoami(&me, name, sizeof(name), &priv_flags, &init_flags);
+      assert(r == OK);
+      if(me != RS_PROC_NR) {
+          r = vm_update((*new_rpp)->r_pub->endpoint, (*old_rpp)->r_pub->endpoint, SF_VM_ROLLBACK);
+          if(rs_verbose)
+              printf("RS: %s performed rollback\n", srv_to_string(*new_rpp));
+      }
+  }
+  else {
+      int swap_flag = ((*new_rpp)->r_flags & RS_INIT_PENDING ? RS_DONTSWAP : RS_SWAP);
+      if(rs_verbose)
+          printf("RS: %s performs rollback\n", srv_to_string(*new_rpp));
+      if(swap_flag == RS_SWAP) {
+          /* Freeze the new instance to rollback safely. */
+          sys_privctl((*new_rpp)->r_pub->endpoint, SYS_PRIV_DISALLOW, NULL);
+      }
+      r = update_service(new_rpp, old_rpp, swap_flag, SF_VM_ROLLBACK);
+  }
+
+  assert(r == OK); /* can't fail */
+}
+
+/*===========================================================================*
+ *                             update_period                                *
+ *===========================================================================*/
+void update_period(message *m_ptr)
+{
+  /* Periodically check the status of the update (preparation phase). */
+  clock_t now = m_ptr->m_notify.timestamp;
+  short has_update_timed_out;
+  message m;
+  struct rprocupd *rpupd;
+  struct rproc *rp;
+  struct rprocpub *rpub;
+
+  rpupd = rupdate.curr_rpupd;
+  rp = rpupd->rp;
+  rpub = rp->r_pub;
+
+  /* See if a timeout has occurred. */
+  has_update_timed_out = (rpupd->prepare_maxtime > 0) && (now - rpupd->prepare_tm > rpupd->prepare_maxtime);
+
+  /* If an update timed out, end the update process and notify
+   * the old version that the update has been canceled. From now on, the old
+   * version will continue executing.
+   */
+  if(has_update_timed_out) {
+      printf("RS: update failed: maximum prepare time reached\n");
+      end_update(EINTR, RS_CANCEL);
+  }
+}
+
+/*===========================================================================*
+ *                         start_update_prepare                             *
+ *===========================================================================*/
+int start_update_prepare(int allow_retries)
+{
+  /* Start the preparation phase of the update process. */
+  struct rprocupd *prev_rpupd, *rpupd;
+  struct rproc *rp, *new_rp;
+  int r;
+
+  if(!RUPDATE_IS_UPD_SCHEDULED()) {
+      return EINVAL;
+  }
+  if(!rs_is_idle()) {
+      printf("RS: not idle now, try again\n");
+      if(!allow_retries) {
+          abort_update_proc(EAGAIN);
+      }
+      return EAGAIN;
+  }
+
+  if(rs_verbose)
+      printf("RS: starting the preparation phase of the update process\n");
+
+  if(rupdate.rs_rpupd) {
+      assert(rupdate.rs_rpupd == rupdate.last_rpupd);
+      assert(rupdate.rs_rpupd->rp->r_pub->endpoint == RS_PROC_NR);
+      assert(!UPD_IS_PREPARING_ONLY(rupdate.rs_rpupd));
+  }
+  if(rupdate.vm_rpupd) {
+      assert(rupdate.vm_rpupd->rp->r_pub->endpoint == VM_PROC_NR);
+      assert(!UPD_IS_PREPARING_ONLY(rupdate.vm_rpupd));
+  }
+
+  /* If a multi-component update includes VM, fill information about old
+   * and new endpoints, as well as update flags. VM needs this to complete
+   * the update internally at state transfer time.
+   */
+  if(RUPDATE_IS_UPD_VM_MULTI()) {
+      RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+          if(!UPD_IS_PREPARING_ONLY(rpupd)) {
+              rp = rpupd->rp;
+              new_rp = rp->r_new_rp;
+              assert(rp && new_rp);
+              rp->r_pub->old_endpoint = rpupd->state_endpoint;
+              rp->r_pub->new_endpoint = rp->r_pub->endpoint;
+              if(rpupd != rupdate.vm_rpupd && rpupd != rupdate.rs_rpupd) {
+                  rp->r_pub->sys_flags |= SF_VM_UPDATE;
+                  if(rpupd->lu_flags & SEF_LU_NOMMAP) {
+                      rp->r_pub->sys_flags |= SF_VM_NOMMAP;
+                  }
+                  if(!(rpupd->lu_flags & SEF_LU_UNSAFE)) {
+                      if(rs_verbose)
+                          printf("RS: %s pinning memory\n", srv_to_string(rp));
+                      vm_memctl(rp->r_pub->new_endpoint, VM_RS_MEM_PIN, 0, 0);
+                      if(rs_verbose)
+                          printf("RS: %s pinning memory\n", srv_to_string(new_rp));
+                      vm_memctl(new_rp->r_pub->endpoint, VM_RS_MEM_PIN, 0, 0);
+                  }
+              }
+          }
+      );
+  }
+
+  /* Request the first service to prepare for the update. */
+  if(start_update_prepare_next() == NULL) {
+      /* If we are done already, end the update now. */
+      end_update(OK, RS_REPLY);
+      return ESRCH;
+  }
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                       start_update_prepare_next                          *
+ *===========================================================================*/
+struct rprocupd* start_update_prepare_next()
+{
+  /* Request the next service in the update chain to prepare for the update. */
+  struct rprocupd *rpupd = NULL;
+  if(!RUPDATE_IS_UPDATING()) {
+      rpupd = rupdate.first_rpupd;
+  }
+  else {
+      rpupd = rupdate.curr_rpupd->next_rpupd;
+  }
+  if(!rpupd) {
+      return NULL;
+  }
+  rupdate.flags |= RS_UPDATING;
+
+  while(1) {
+      rupdate.curr_rpupd = rpupd;
+      request_prepare_update_service(rupdate.curr_rpupd->rp, rupdate.curr_rpupd->prepare_state);
+      if(!UPD_IS_PREPARING_ONLY(rpupd)) {
+          /* Continue only if the current service requires a prepare-only update. */
+          break;
+      }
+      if(!rupdate.curr_rpupd->next_rpupd) {
+          /* Continue only if there are services left. */
+          break;
+      }
+      rpupd = rupdate.curr_rpupd->next_rpupd;
+  }
+
+  return rpupd;
+}
+
+/*===========================================================================*
+ *                             start_update                                 *
+ *===========================================================================*/
+int start_update()
+{
+  /* Start the update phase of the update process. */
+  struct rprocupd *prev_rpupd, *rpupd;
+  int r, init_ready_pending=0;
+
+  if(rs_verbose)
+      printf("RS: starting a %s-component update process\n",
+          RUPDATE_IS_UPD_MULTI() ? "multi" : "single");
+
+  assert(RUPDATE_IS_UPDATING());
+  assert(rupdate.num_rpupds > 0);
+  assert(rupdate.num_init_ready_pending == 0);
+  assert(rupdate.first_rpupd);
+  assert(rupdate.last_rpupd);
+  assert(rupdate.curr_rpupd == rupdate.last_rpupd);
+  rupdate.flags |= RS_INITIALIZING;
+
+  /* Cancel the update for the prepare-only services now. */
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      if(UPD_IS_PREPARING_ONLY(rpupd)) {
+          request_prepare_update_service(rpupd->rp, SEF_LU_STATE_NULL);
+      }
+  );
+
+  /* Iterate over all the processes scheduled for the update. Update each
+   * service and initialize the new instance. If VM is part of a
+   * multi-component live update, initialize VM first.
+   */
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      rupdate.curr_rpupd = rpupd;
+      if(!UPD_IS_PREPARING_ONLY(rpupd)) {
+          init_ready_pending=1;
+          r = start_srv_update(rpupd);
+          if(r != OK) {
+              return r;
+          }
+          if(!RUPDATE_IS_UPD_VM_MULTI() || rpupd == rupdate.vm_rpupd) {
+              r = complete_srv_update(rpupd);
+              if(r != OK) {
+                  return r;
+              }
+          }
+      }
+  );
+
+  /* End update if there is nothing more to do. */
+  if (!init_ready_pending) {
+      end_update(OK, 0);
+      return OK;
+  }
+
+  /* Handle multi-component live updates including VM. */
+  if(RUPDATE_IS_UPD_VM_MULTI()) {
+      message m;
+      /* Check VM initialization, assume failure after timeout. */
+      if (rs_verbose)
+          printf("RS: waiting for VM to initialize...\n");
+      r = rs_receive_ticks(VM_PROC_NR, &m, NULL, UPD_INIT_MAXTIME(rupdate.vm_rpupd));
+      if(r != OK || m.m_type != RS_INIT || m.m_rs_init.result != OK) {
+          r = (r == OK && m.m_type == RS_INIT ? m.m_rs_init.result : EINTR);
+          m.m_source = VM_PROC_NR;
+          m.m_type = RS_INIT;
+          m.m_rs_init.result = r;
+      }
+      do_init_ready(&m);
+      /* If initialization was successfull, complete the update. */
+      if(r == OK) {
+          /* Reply and unblock VM immediately. */
+          m.m_type = OK;
+          reply(VM_PROC_NR, NULL, &m);
+          /* Initialize other services. */
+          RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+              if(!UPD_IS_PREPARING_ONLY(rpupd) && rpupd != rupdate.vm_rpupd) {
+                  r = complete_srv_update(rpupd);
+                  if(r != OK) {
+                      return r;
+                  }
+              }
+          );
+      }
+  }
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                           start_srv_update                               *
+ *===========================================================================*/
+int start_srv_update(struct rprocupd *rpupd)
+{
+  /* Start updating a single service given its update descriptor. */
+  struct rproc *old_rp, *new_rp;
+  int r, sys_upd_flags = 0;
+
+  old_rp = rpupd->rp;
+  new_rp = old_rp->r_new_rp;
+  assert(old_rp && new_rp);
+
+  if(rs_verbose)
+      printf("RS: %s starting the %s\n", srv_to_string(old_rp), srv_upd_to_string(rpupd));
+
+  rupdate.num_init_ready_pending++;
+  new_rp->r_flags |= RS_INITIALIZING;
+  new_rp->r_flags |= RS_INIT_PENDING;
+  if(rpupd->lu_flags & SEF_LU_NOMMAP) {
+      sys_upd_flags |= SF_VM_NOMMAP;
+  }
+
+  /* Perform the update, skip for RS. */
+  if(old_rp->r_pub->endpoint != RS_PROC_NR) {
+      r = update_service(&old_rp, &new_rp, RS_SWAP, sys_upd_flags);
+      if(r != OK) {
+          end_update(r, RS_REPLY);
+          printf("RS: update failed: error %d\n", r);
+          return r;
+      }
+  }
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                        complete_srv_update                               *
+ *===========================================================================*/
+int complete_srv_update(struct rprocupd *rpupd)
+{
+  /* Complete update of a service given its update descriptor. */
+  struct rproc *old_rp, *new_rp;
+  int r;
+
+  old_rp = rpupd->rp;
+  new_rp = old_rp->r_new_rp;
+  assert(old_rp && new_rp);
+
+  if(rs_verbose)
+      printf("RS: %s completing the %s\n", srv_to_string(old_rp), srv_upd_to_string(rpupd));
+
+  new_rp->r_flags &= ~RS_INIT_PENDING;
+
+  /* If RS itself is updating, yield control to the new version immediately. */
+  if(old_rp->r_pub->endpoint == RS_PROC_NR) {
+      r = init_service(new_rp, SEF_INIT_LU, rpupd->init_flags);
+      if(r != OK) {
+          panic("unable to initialize the new RS instance: %d", r);
+      }
+      if(rs_verbose)
+         printf("RS: %s is the new RS instance we'll yield control to\n", srv_to_string(new_rp));
+      r = sys_privctl(new_rp->r_pub->endpoint, SYS_PRIV_YIELD, NULL);
+      if(r != OK) {
+          panic("unable to yield control to the new RS instance: %d", r);
+      }
+      /* If we get this far, the new version failed to initialize. Rollback. */
+      rollback_service(&new_rp, &old_rp);
+      end_update(ERESTART, RS_REPLY);
+      printf("RS: update failed: state transfer failed for the new RS instance\n");
+      return ERESTART;
+  }
+
+  /* Let the new version run. */
+  r = run_service(new_rp, SEF_INIT_LU, rpupd->init_flags);
+  if(r != OK) {
+      /* Something went wrong. Rollback. */
+      rollback_service(&new_rp, &old_rp);
+      end_update(r, RS_REPLY);
+      printf("RS: update failed: error %d\n", r);
+      return r;
+  }
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                         abort_update_proc                                *
+ *===========================================================================*/
+int abort_update_proc(int reason)
+{
+  /* This function is called to abort a scheduled/in-progress update process
+   * indiscriminately. If the update is in progress, simply pretend the
+   * current service is causing premature termination of the update.
+   */
+  int is_updating = RUPDATE_IS_UPDATING();
+  assert(reason != OK);
+
+  if(!is_updating && !RUPDATE_IS_UPD_SCHEDULED()) {
+      return EINVAL;
+  }
+
+  if(rs_verbose)
+      printf("RS: aborting the %s update process prematurely\n",
+          is_updating ? "in-progress" : "scheduled");
+
+  if(!is_updating) {
+      rupdate_clear_upds();
+      return OK;
+  }
+
+  if(rupdate.flags & RS_INITIALIZING) {
+      /* Pretend the current service under update failed to initialize. */
+      end_update(reason, RS_REPLY); 
+  }
+  else {
+      /* Pretend the current service under update failed to prepare. */
+      end_update(reason, RS_CANCEL);
+  }
+
+  return OK;
+}
+
+/*===========================================================================*
+ *                         end_update_curr                                  *
+ *===========================================================================*/
+static void end_update_curr(struct rprocupd *rpupd, int result, int reply_flag)
+{
+  /* Execute the requested action on the current service under update. */
+  struct rproc *old_rp, *new_rp;
+  assert(rpupd == rupdate.curr_rpupd);
+
+  old_rp = rpupd->rp;
+  new_rp = old_rp->r_new_rp;
+  assert(old_rp && new_rp);
+  if(result != OK && SRV_IS_UPDATING_AND_INITIALIZING(new_rp) && rpupd != rupdate.rs_rpupd) {
+      /* Rollback in case of failures at initialization time. */
+      rollback_service(&new_rp, &old_rp);
+  }
+  end_srv_update(rpupd, result, reply_flag);
+}
+
+/*===========================================================================*
+ *                     end_update_before_prepare                            *
+ *===========================================================================*/
+static void end_update_before_prepare(struct rprocupd *rpupd, int result)
+{
+  /* The service is still waiting for the update. Cleanup the new version and
+   * keep the old version running.
+   */
+  struct rproc *old_rp, *new_rp;
+  assert(result != OK);
+
+  old_rp = rpupd->rp;
+  new_rp = old_rp->r_new_rp;
+  assert(old_rp && new_rp);
+  cleanup_service(new_rp);
+}
+
+/*===========================================================================*
+ *                      end_update_prepare_done                             *
+ *===========================================================================*/
+static void end_update_prepare_done(struct rprocupd *rpupd, int result)
+{
+  /* The service is blocked after preparing for the update. Unblock it
+   * and cleanup the new version.
+   */
+  assert(!RUPDATE_IS_INITIALIZING());
+  assert(result != OK);
+  assert(!(rpupd->rp->r_flags & RS_INITIALIZING));
+
+  end_srv_update(rpupd, result, RS_REPLY);
+}
+
+/*===========================================================================*
+ *                      end_update_initializing                             *
+ *===========================================================================*/
+static void end_update_initializing(struct rprocupd *rpupd, int result)
+{
+  /* The service is initializing after a live udate. Cleanup the version that
+   * has to die out and let the other version run.
+   */
+  struct rproc *old_rp, *new_rp;
+
+  old_rp = rpupd->rp;
+  new_rp = old_rp->r_new_rp;
+  assert(old_rp && new_rp);
+  assert(SRV_IS_UPDATING_AND_INITIALIZING(new_rp));
+  if(result != OK && rpupd != rupdate.rs_rpupd) {
+      /* Rollback in case of failures at initialization time. */
+      rollback_service(&new_rp, &old_rp);
+  }
+  end_srv_update(rpupd, result, RS_REPLY);
+}
+
+/*===========================================================================*
+ *                         end_update_rev_iter                              *
+ *===========================================================================*/
+static void end_update_rev_iter(int result, int reply_flag,
+    struct rprocupd *skip_rpupd, struct rprocupd *only_rpupd)
+{
+  /* End the update for all the requested services. */
+  struct rprocupd *prev_rpupd, *rpupd;
+  short is_curr, is_before_curr, is_after_curr;
+
+  is_after_curr = 1;
+  RUPDATE_REV_ITER(rupdate.last_rpupd, prev_rpupd, rpupd,
+      is_curr = (rupdate.curr_rpupd == rpupd);
+      is_after_curr = is_after_curr && !is_curr;
+      if(!UPD_IS_PREPARING_ONLY(rpupd)) {
+          short is_before_prepare;
+          short is_prepare_done;
+          short is_initializing;
+          is_before_curr = !is_curr && !is_after_curr;
+          if(RUPDATE_IS_INITIALIZING()) {
+              is_before_prepare = 0;
+              is_prepare_done = is_after_curr;
+              is_initializing = is_before_curr;
+          }
+          else {
+              is_before_prepare = is_after_curr;
+              is_prepare_done = is_before_curr;
+              is_initializing = 0;
+          }
+          if((!skip_rpupd || rpupd != skip_rpupd) && (!only_rpupd || rpupd == only_rpupd)) {
+              /* Analyze different cases. */
+              if(is_curr) {
+                  end_update_curr(rpupd, result, reply_flag);
+              }
+              else if(is_before_prepare) {
+                  end_update_before_prepare(rpupd, result);
+              }
+              else if(is_prepare_done) {
+                  end_update_prepare_done(rpupd, result);
+              }
+              else {
+                  assert(is_initializing);
+                  end_update_initializing(rpupd, result);
+              }
+          }
+      }
+  );
+}
+
+/*===========================================================================*
+ *                         end_update_debug                                 *
+ *===========================================================================*/
+void end_update_debug(char *file, int line,
+    int result, int reply_flag)
+{
+  /* End an in-progress update process. */
+  struct rprocupd *prev_rpupd, *rpupd, *rpupd_it;
+  struct rproc *rp, *old_rp, *new_rp;
+  int i, r, slot_nr;
+
+  assert(RUPDATE_IS_UPDATING());
+
+  if(rs_verbose)
+      printf("RS: %s ending the update: result=%d, reply=%d at %s:%d\n",
+          srv_to_string(rupdate.curr_rpupd->rp), result, (reply_flag==RS_REPLY),
+          file, line);
+
+  /* If the new instance of RS is active and the update failed, ending
+   * the update couldn't be any easier.
+   */
+  if(result != OK && RUPDATE_IS_RS_INIT_DONE()) {
+      if(rs_verbose)
+          printf("RS: update failed, new RS instance will now exit\n");
+      exit(1);
+  }
+
+  /* If VM is updated as part of a multi-component live update and something
+   * goes wrong after VM has completed initialization, rollback is only
+   * supported in a best-effort way in unsafe mode. The new VM instance might
+   * have important state changes that won't be reflected in the old version
+   * once we rollback.
+   */
+  if(result != OK && RUPDATE_IS_UPD_VM_MULTI() && RUPDATE_IS_VM_INIT_DONE() && (rupdate.vm_rpupd->lu_flags & SEF_LU_UNSAFE)) {
+      printf("RS: Warning rollbacking in unsafe multi-component update including VM!\n");
+  }
+
+  /* Handle prepare-only services first: simply cancel the update. */
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      if(UPD_IS_PREPARING_ONLY(rpupd)) {
+          if(!RUPDATE_IS_INITIALIZING()) {
+              request_prepare_update_service(rpupd->rp, SEF_LU_STATE_NULL);
+          }
+          rpupd->rp->r_flags &= ~RS_PREPARE_DONE;
+      }
+  );
+
+  /* Handle all the other services now, VM always last to support rollback. */
+  end_update_rev_iter(result, reply_flag, rupdate.vm_rpupd, NULL);
+  if(rupdate.vm_rpupd) {
+      end_update_rev_iter(result, reply_flag, NULL, rupdate.vm_rpupd);
+  }
+
+  /* End the update and complete initialization in case of success. */
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      if(prev_rpupd) {
+          rupdate_upd_clear(prev_rpupd);
+      }
+      if(result == OK && !UPD_IS_PREPARING_ONLY(rpupd)) {
+          /* The rp pointer points to the new instance in this case. */
+          new_rp = rpupd->rp;
+          end_srv_init(new_rp);
+      }
+  );
+  late_reply(rupdate.last_rpupd->rp, result);
+  rupdate_upd_clear(rupdate.last_rpupd);
+  RUPDATE_CLEAR();
+
+  /* Clear all the old/new endpoints and update flags in the public entries. */
+  for(slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
+      rp = &rproc[slot_nr];
+      rp->r_pub->old_endpoint = NONE;
+      rp->r_pub->new_endpoint = NONE;
+      rp->r_pub->sys_flags &= ~(SF_VM_UPDATE|SF_VM_ROLLBACK|SF_VM_NOMMAP);
+  }
+}
+
+/*===========================================================================*
+*                            end_srv_update                                 *
+ *===========================================================================*/
+void end_srv_update(struct rprocupd *rpupd, int result, int reply_flag)
+{
+/* End the update for the given service. There are two possibilities:
+ * 1) the update succeeded. In that case, cleanup the old version and mark the
+ *    new version as no longer under update.
+ * 2) the update failed. In that case, cleanup the new version and mark the old
+ *    version as no longer under update. Eventual late ready to update
+ *    messages (if any) will simply be ignored and the service can
+ *    continue executing. In addition, reset the check timestamp, so that if the
+ *    service has a period, a status request will be forced in the next period.
+ */
+  struct rproc *old_rp, *new_rp, *exiting_rp, *surviving_rp;
+  struct rproc **rps;
+  
+  struct rprocpub *rpub;
+  int nr_rps, i;
+
+  old_rp = rpupd->rp;
+  new_rp = old_rp->r_new_rp;
+  assert(old_rp && new_rp);
+  if(result == OK && new_rp->r_pub->endpoint == VM_PROC_NR && RUPDATE_IS_UPD_MULTI()) {
+      /* VM has already been replied to in case of multi-component live update.
+       * Send an update cancel message to trigger cleanup.
+       */
+      reply_flag = RS_CANCEL;
+  }
+
+  if(rs_verbose)
+      printf("RS: ending update from %s to %s with result=%d, reply=%d\n",
+          srv_to_string(old_rp), srv_to_string(new_rp), result, (reply_flag==RS_REPLY));
+
+  /* Decide which version has to die out and which version has to survive. */
+  surviving_rp = (result == OK ? new_rp : old_rp);
+  exiting_rp =   (result == OK ? old_rp : new_rp);
+  surviving_rp->r_flags &= ~RS_INITIALIZING;
+  surviving_rp->r_check_tm = 0;
+  getticks(&surviving_rp->r_alive_tm);
+
+  /* Keep track of the surviving process in the update descriptor from now on. */
+  rpupd->rp = surviving_rp;
+
+  /* Unlink the two versions. */
+  old_rp->r_new_rp = NULL;
+  new_rp->r_old_rp = NULL;
+
+  /* Mark the version that has to survive as no longer updating and
+   * reply when asked to.
+   */
+  surviving_rp->r_flags &= ~(RS_UPDATING|RS_PREPARE_DONE|RS_INIT_DONE|RS_INIT_PENDING);
+  if(reply_flag == RS_REPLY) {
+      message m;
+      m.m_type = result;
+      reply(surviving_rp->r_pub->endpoint, surviving_rp, &m);
+  }
+  else if(reply_flag == RS_CANCEL) {
+      if(!(surviving_rp->r_flags & RS_TERMINATED)) {
+          request_prepare_update_service(surviving_rp, SEF_LU_STATE_NULL);
+      }
+  }
+
+  /* Cleanup or detach the version that has to die out. */
+  get_service_instances(exiting_rp, &rps, &nr_rps);
+  for(i=0;i<nr_rps;i++) {
+      if(rps[i] == old_rp && (rpupd->lu_flags & SEF_LU_DETACHED)) {
+          message m;
+          m.m_type = EDEADEPT;
+          rps[i]->r_flags |= RS_CLEANUP_DETACH;
+          cleanup_service(rps[i]);
+          reply(rps[i]->r_pub->endpoint, rps[i], &m);
+      }
+      else {
+          cleanup_service(rps[i]);
+      }
+  }
+
+  if(rs_verbose)
+      printf("RS: %s ended the %s\n", srv_to_string(surviving_rp),
+          srv_upd_to_string(rpupd));
+}
+
index 62aad62e06dd63700ef26975805662171a3267e5..e8a4011b358458a71bc70fb5d3ed30bb66fbb597 100644 (file)
 #include <minix/sched.h>
 #include "kernel/proc.h"
 
+#define PRINT_SEP() printf("---------------------------------------------------------------------------------\n")
+
 /*===========================================================================*
  *                              init_service                                *
  *===========================================================================*/
-int init_service(rp, type)
-struct rproc *rp;                              /* pointer to process slot */
-int type;                                      /* type of initialization */
+int init_service(struct rproc *rp, int type, int flags)
 {
   int r;
   message m;
-  struct rprocpub *rpub;
   endpoint_t old_endpoint;
 
-  rpub = rp->r_pub;
-
   rp->r_flags |= RS_INITIALIZING;              /* now initializing */
+  getticks(&rp->r_alive_tm);
   rp->r_check_tm = rp->r_alive_tm + 1;         /* expect reply within period */
 
   /* In case of RS initialization, we are done. */
@@ -35,19 +33,29 @@ int type;                                   /* type of initialization */
   /* Determine the old endpoint if this is a new instance. */
   old_endpoint = NONE;
   if(rp->r_old_rp) {
-      old_endpoint = rp->r_old_rp->r_pub->endpoint;
+      old_endpoint = rp->r_upd.state_endpoint;
   }
   else if(rp->r_prev_rp) {
       old_endpoint = rp->r_prev_rp->r_pub->endpoint;
   }
 
+  /* Check flags. */
+  if(rp->r_pub->sys_flags & SF_USE_SCRIPT) {
+      flags |= SEF_INIT_SCRIPT_RESTART;
+  }
+
   /* Send initialization message. */
-  memset(&m, 0, sizeof(message));
   m.m_type = RS_INIT;
-  m.m_rs_init.type = type;
+  m.m_rs_init.type = (short) type;
+  m.m_rs_init.flags = flags;
   m.m_rs_init.rproctab_gid = rinit.rproctab_gid;
   m.m_rs_init.old_endpoint = old_endpoint;
-  r = asynsend(rpub->endpoint, &m);
+  m.m_rs_init.restarts = (short) rp->r_restarts+1;
+  m.m_rs_init.buff_addr = rp->r_map_prealloc_addr;
+  m.m_rs_init.buff_len  = rp->r_map_prealloc_len;
+  rp->r_map_prealloc_addr = 0;
+  rp->r_map_prealloc_len = 0;
+  r = rs_asynsend(rp, &m, 0);
 
   return r;
 }
@@ -113,15 +121,14 @@ int is_init;                    /* set when initializing a call mask */
 }
 
 /*===========================================================================*
- *                          srv_to_string                                   *
+ *                          srv_to_string_gen                               *
  *===========================================================================*/
-char* srv_to_string(rp)
-struct rproc *rp;                      /* pointer to process slot */
+char* srv_to_string_gen(struct rproc *rp, int is_verbose)
 {
   struct rprocpub *rpub;
   int slot_nr;
   char *srv_string;
-  static char srv_string_pool[3][RS_MAX_LABEL_LEN + (DEBUG ? 256 : 64)];
+  static char srv_string_pool[3][RS_MAX_LABEL_LEN + 256];
   static int srv_string_pool_index = 0;
 
   rpub = rp->r_pub;
@@ -130,26 +137,94 @@ struct rproc *rp;                 /* pointer to process slot */
   srv_string_pool_index = (srv_string_pool_index + 1) % 3;
 
 #define srv_str(cmd) ((cmd) == NULL || (cmd)[0] == '\0' ? "_" : (cmd))
-#define srv_ep_str(rp) (itoa((rp)->r_pub->endpoint))
 #define srv_active_str(rp) ((rp)->r_flags & RS_ACTIVE ? "*" : " ")
 #define srv_version_str(rp) ((rp)->r_new_rp || (rp)->r_next_rp ? "-" : \
     ((rp)->r_old_rp || (rp)->r_prev_rp ? "+" : " "))
 
-#if DEBUG
-  sprintf(srv_string, "service '%s'%s%s(slot %d, ep %d, pid %d, cmd %s, script %s, proc %s, major %d, flags 0x%03x, sys_flags 0x%02x)",
-      rpub->label, srv_active_str(rp), srv_version_str(rp),
-      slot_nr, rpub->endpoint, rp->r_pid, srv_str(rp->r_cmd),
-      srv_str(rp->r_script), srv_str(rpub->proc_name), rpub->dev_nr,
-      rp->r_flags, rpub->sys_flags);
-#else
-  sprintf(srv_string, "service '%s'%s%s(slot %d, ep %d, pid %d)",
-      rpub->label, srv_active_str(rp), srv_version_str(rp),
-      slot_nr, rpub->endpoint, rp->r_pid);
-#endif
+  if(is_verbose) {
+      sprintf(srv_string, "service '%s'%s%s(slot %d, ep %d, pid %d, cmd %s, script %s, proc %s, major %d, flags 0x%03x, sys_flags 0x%02x)",
+          rpub->label, srv_active_str(rp), srv_version_str(rp),
+          slot_nr, rpub->endpoint, rp->r_pid, srv_str(rp->r_cmd),
+          srv_str(rp->r_script), srv_str(rpub->proc_name), rpub->dev_nr,
+          rp->r_flags, rpub->sys_flags);
+  }
+  else {
+      sprintf(srv_string, "service '%s'%s%s(slot %d, ep %d, pid %d)",
+          rpub->label, srv_active_str(rp), srv_version_str(rp),
+          slot_nr, rpub->endpoint, rp->r_pid);
+  }
+
+#undef srv_str
+#undef srv_active_str
+#undef srv_version_str
 
   return srv_string;
 }
 
+/*===========================================================================*
+ *                          srv_upd_to_string                               *
+ *===========================================================================*/
+char* srv_upd_to_string(struct rprocupd *rpupd)
+{
+   static char srv_upd_string[256];
+   struct rprocpub *rpub, *next_rpub, *prev_rpub;
+   rpub = rpupd->rp ? rpupd->rp->r_pub : NULL;
+   next_rpub = rpupd->next_rpupd && rpupd->next_rpupd->rp ? rpupd->next_rpupd->rp->r_pub : NULL;
+   prev_rpub = rpupd->prev_rpupd && rpupd->prev_rpupd->rp ? rpupd->prev_rpupd->rp->r_pub : NULL;
+
+#define srv_ep(RPUB) (RPUB ? (RPUB)->endpoint : -1)
+#define srv_upd_luflag_c(F) (rpupd->lu_flags & F ? '1' : '0')
+#define srv_upd_iflag_c(F) (rpupd->init_flags & F ? '1' : '0')
+
+   sprintf(srv_upd_string, "update (lu_flags(SAMPUNDRV)=%c%c%c%c%c%c%c%c%c, init_flags=(FCTD)=%c%c%c%c, state %d (%s), tm %lu, maxtime %lu, endpoint %d, state_data_gid %d, prev_ep %d, next_ep %d)",
+       srv_upd_luflag_c(SEF_LU_SELF), srv_upd_luflag_c(SEF_LU_ASR),
+       srv_upd_luflag_c(SEF_LU_MULTI), srv_upd_luflag_c(SEF_LU_PREPARE_ONLY),
+       srv_upd_luflag_c(SEF_LU_UNSAFE), srv_upd_luflag_c(SEF_LU_NOMMAP),
+       srv_upd_luflag_c(SEF_LU_DETACHED), srv_upd_luflag_c(SEF_LU_INCLUDES_RS),
+       srv_upd_luflag_c(SEF_LU_INCLUDES_VM), srv_upd_iflag_c(SEF_INIT_FAIL),
+       srv_upd_iflag_c(SEF_INIT_CRASH), srv_upd_iflag_c(SEF_INIT_TIMEOUT),
+       srv_upd_iflag_c(SEF_INIT_DEFCB), rpupd->prepare_state, 
+       rpupd->prepare_state_data.eval_addr ? rpupd->prepare_state_data.eval_addr : "", rpupd->prepare_tm,
+       rpupd->prepare_maxtime, srv_ep(rpub), rpupd->prepare_state_data_gid,
+       srv_ep(prev_rpub), srv_ep(next_rpub));
+
+   return srv_upd_string;
+}
+
+/*===========================================================================*
+ *                          rs_asynsend                                     *
+ *===========================================================================*/
+int rs_asynsend(struct rproc *rp, message *m_ptr, int no_reply)
+{
+  struct rprocpub *rpub;
+  int r;
+
+  rpub = rp->r_pub;
+
+  if(no_reply) {
+      r = asynsend3(rpub->endpoint, m_ptr, AMF_NOREPLY);
+  }
+  else {
+      r = asynsend(rpub->endpoint, m_ptr);
+  }
+
+  if(rs_verbose)
+      printf("RS: %s being asynsent to with message type %d, noreply=%d, result=%d\n",
+          srv_to_string(rp), m_ptr->m_type, no_reply, r);
+
+  return r;
+}
+
+/*===========================================================================*
+ *                          rs_receive_ticks                                *
+ *===========================================================================*/
+int rs_receive_ticks(endpoint_t src, message *m_ptr,
+    int *status_ptr, int ticks)
+{
+  printf("RS: rs_receive_ticks not implemented\n");
+  return ENOSYS;
+}
+
 /*===========================================================================*
  *                             reply                                        *
  *===========================================================================*/
@@ -166,7 +241,7 @@ message *m_ptr;                         /* reply message */
   }
 
   if(rs_verbose && rp)
-      printf("RS: %s being replied to\n", srv_to_string(rp));
+      printf("RS: %s being replied to with message type %d\n", srv_to_string(rp), m_ptr->m_type);
 
   r = ipc_sendnb(who, m_ptr);          /* send the message */
   if (r != OK)
@@ -265,3 +340,125 @@ int update_sig_mgrs(struct rproc *rp, endpoint_t sig_mgr,
   return OK;
 }
 
+/*===========================================================================*
+ *                             rs_is_idle                                   *
+ *===========================================================================*/
+int rs_is_idle()
+{
+  int slot_nr;
+  struct rproc *rp;
+  for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
+      rp = &rproc[slot_nr];
+      if (!(rp->r_flags & RS_IN_USE)) {
+          continue;
+      }
+      if(!RS_SRV_IS_IDLE(rp)) {
+          return 0;
+      }
+  }
+  return 1;
+}
+
+/*===========================================================================*
+ *                             rs_idle_period                               *
+ *===========================================================================*/
+void rs_idle_period()
+{
+  struct rproc *rp;
+  struct rprocpub *rpub;
+  int r;
+
+  /* Not much to do when RS is not idle. */
+  if(!rs_is_idle()) {
+      return;
+  }
+
+  /* Cleanup dead services. */
+  for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
+      if((rp->r_flags & (RS_IN_USE|RS_DEAD)) == (RS_IN_USE|RS_DEAD)) {
+          cleanup_service(rp);
+      }
+  }
+
+  /* Create missing replicas when necessary. */
+  for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
+      rpub = rp->r_pub;
+      if((rp->r_flags & RS_ACTIVE) && (rpub->sys_flags & SF_USE_REPL) && rp->r_next_rp == NULL) {
+          if(rpub->endpoint == VM_PROC_NR && (rp->r_old_rp || rp->r_new_rp)) {
+              /* Only one replica at the time for VM. */
+              continue;
+          }
+          if ((r = clone_service(rp, RST_SYS_PROC, 0)) != OK) {
+              printf("RS: warning: unable to clone %s (error %d)\n",
+                  srv_to_string(rp), r);
+          }
+      }
+  }
+}
+
+/*===========================================================================*
+ *                        print_services_status                             *
+ *===========================================================================*/
+void print_services_status()
+{
+  int slot_nr;
+  struct rproc *rp;
+  int num_services = 0;
+  int num_service_instances = 0;
+  int is_verbose = 1;
+
+  PRINT_SEP();
+  printf("Printing information about all the system service instances:\n");
+  PRINT_SEP();
+  for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
+      rp = &rproc[slot_nr];
+      if (!(rp->r_flags & RS_IN_USE)) {
+          continue;
+      }
+      if (rp->r_flags & RS_ACTIVE) {
+          num_services++;
+      }
+      num_service_instances++;
+      printf("%s\n", srv_to_string_gen(rp, is_verbose));
+  }
+  PRINT_SEP();
+  printf("Found %d service instances, of which %d are active services\n",
+         num_service_instances, num_services);
+  PRINT_SEP();
+}
+
+/*===========================================================================*
+ *                         print_update_status                              *
+ *===========================================================================*/
+void print_update_status()
+{
+  struct rprocupd *prev_rpupd, *rpupd;
+  int is_updating = RUPDATE_IS_UPDATING();
+  int i;
+
+#define rupdate_flag_c(F) (rupdate.flags & F ? '1' : '0')
+
+  if(!is_updating && !RUPDATE_IS_UPD_SCHEDULED()) {
+      PRINT_SEP();
+      printf("No update is in progress or scheduled\n");
+      PRINT_SEP();
+      return;
+  }
+
+  PRINT_SEP();
+  i = 1;
+  printf("A %s-component update is %s, flags(UIRV)=%c%c%c%c:\n", RUPDATE_IS_UPD_MULTI() ? "multi" : "single",
+      is_updating ? "in progress" : "scheduled",
+      rupdate_flag_c(RS_UPDATING), rupdate_flag_c(RS_INITIALIZING),
+      rupdate.rs_rpupd ? '1' : '0', rupdate.vm_rpupd ? '1' : '0');
+  PRINT_SEP();
+  RUPDATE_ITER(rupdate.first_rpupd, prev_rpupd, rpupd,
+      printf("%d. %s %s %s\n", i++, srv_to_string(rpupd->rp),
+          is_updating ? "updating with" : "scheduled for",
+          srv_upd_to_string(rpupd));
+  );
+  PRINT_SEP();
+
+#undef rupdate_flag_c
+}
+
index 067eb93318dd236a690d84a8e3d5ac2ae417e68e..0890a4bdfa7f94619364e0185d8881215c01f671 100644 (file)
@@ -276,20 +276,11 @@ int do_rs_memctl(message *m_ptr)
        switch(req)
        {
        case VM_RS_MEM_PIN:
-
-               /* Do not perform VM_RS_MEM_PIN yet - it costs the full
-                * size of the RS stack (64MB by default) in memory,
-                * and it's needed for functionality that isn't complete /
-                * merged in current Minix (surviving VM crashes).
-                */
-
-#if 0
+               /* 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;
-#else
-               return OK;
-#endif
-
        case VM_RS_MEM_MAKE_VM:
                r = rs_memctl_make_vm_instance(vmp);
                return r;