]> xenbits.xensource.com Git - people/dstodden/blktap.git/commitdiff
CA-32254: Rewrite channel/vbd state machine.
authorDaniel Stodden <daniel.stodden@citrix.com>
Thu, 8 Oct 2009 05:47:56 +0000 (22:47 -0700)
committerDaniel Stodden <daniel.stodden@citrix.com>
Thu, 8 Oct 2009 05:47:56 +0000 (22:47 -0700)
 * Refine channel states:

   - Replaces the former IDLE state with a more detailed representation:
       - DEAD (just after spawning the channel)
       - LAUNCHED (just after spawning the channel)
- PID (following a pid response)
- RUNNING (following an open resume response)
- PAUSED (following a pause response)

   - Former channel->open replaced with TAPDISK_CHANNEL_IPC_OPEN(),
     based on the above channel states.

   - Former channel->connected removed. It was used as an indicator
     for pause request legitimacy, but process state is not a criteria
     anymore.

 * Add 'vbd state', reflecting agent control state:
- UNPAUSED (may be RUNNING)
- PAUSING  (following a pause request)
- PAUSED   (finished PAUSING)
- BROKEN   (following a fatal error)
- DEAD     (following channel->path removal)

 * Add 'shutdown state', reflecting kernel control state:
- UP       (tapdisk shall live)
- DOWN     (tapdisk shall die)

 * Reimplement pause/unpause on top of that:
    - mapping vbd and shutdown states to a channel state to be driven.
    - tapdisk-daemon drives vbd DEAD state on path removal
    - allow start-tapdisk to resurrect channels: CLOSED/DEAD -> LAUNCHED
    - the point is that this maintains vbd paused state across tapdisk exits.

daemon/tapdisk-channel.c
daemon/tapdisk-daemon.c
daemon/tapdisk-dispatch.h

index ccc3d29ab08420302aa77e0a2841abd5d7b8d967..c8a421d2314f3dbcacbfc89d390fc7f4fa37a39d 100644 (file)
 #include "disktypes.h"
 #include "tapdisk-dispatch.h"
 
-#define TAPDISK_CHANNEL_IDLE          1
-#define TAPDISK_CHANNEL_WAIT_PID      2
-#define TAPDISK_CHANNEL_WAIT_OPEN     3
-#define TAPDISK_CHANNEL_WAIT_PAUSE    4
-#define TAPDISK_CHANNEL_WAIT_RESUME   5
-#define TAPDISK_CHANNEL_WAIT_CLOSE    6
-#define TAPDISK_CHANNEL_CLOSED        7
+static inline const char*
+tapdisk_channel_state_name(channel_state_t state)
+{
+       switch (state) {
+       case TAPDISK_CHANNEL_DEAD:
+               return "dead";
+       case TAPDISK_CHANNEL_LAUNCHED:
+               return "launched";
+       case TAPDISK_CHANNEL_WAIT_PID:
+               return "wait-pid";
+       case TAPDISK_CHANNEL_PID:
+               return "pid";
+       case TAPDISK_CHANNEL_WAIT_OPEN:
+               return "wait-open";
+       case TAPDISK_CHANNEL_RUNNING:
+               return "running";
+       case TAPDISK_CHANNEL_WAIT_PAUSE:
+               return "wait-pause";
+       case TAPDISK_CHANNEL_PAUSED:
+               return "paused";
+       case TAPDISK_CHANNEL_WAIT_RESUME:
+               return "wait-resume";
+       case TAPDISK_CHANNEL_WAIT_CLOSE:
+               return "wait-close";
+       case TAPDISK_CHANNEL_CLOSED:
+               return "closed";
+       }
+
+       return "unknown";
+}
+
+static inline const char*
+tapdisk_channel_vbd_state_name(vbd_state_t state)
+{
+       switch (state) {
+       case TAPDISK_VBD_UNPAUSED:
+               return "unpaused";
+       case TAPDISK_VBD_PAUSING:
+               return "pausing";
+       case TAPDISK_VBD_PAUSED:
+               return "paused";
+       case TAPDISK_VBD_BROKEN:
+               return "broken";
+       case TAPDISK_VBD_DEAD:
+               return "dead";
+       }
+
+       return "unknown";
+}
+
+static inline int
+tapdisk_channel_enter_vbd_state(tapdisk_channel_t *channel, vbd_state_t state)
+{
+       int err = 0;
+
+       if (channel->vbd_state == TAPDISK_VBD_BROKEN ||
+           channel->vbd_state == TAPDISK_VBD_DEAD)
+               err = -EINVAL;
+
+       DPRINTF("%s: vbd state %s -> %s: %d",
+               channel->path,
+               tapdisk_channel_vbd_state_name(channel->shutdown_state),
+               tapdisk_channel_vbd_state_name(state),
+               err);
+
+       if (!err)
+               channel->vbd_state = state;
+
+       return err;
+}
+
+static inline const char*
+tapdisk_channel_shutdown_state_name(shutdown_state_t state)
+{
+       switch (state) {
+       case TAPDISK_VBD_UP:
+               return "up";
+       case TAPDISK_VBD_DOWN:
+               return "down";
+       }
+
+       return "unknown";
+}
 
 static void tapdisk_channel_error(tapdisk_channel_t *,
                                  const char *fmt, ...)
@@ -55,9 +131,15 @@ static void tapdisk_channel_fatal(tapdisk_channel_t *,
                                  const char *fmt, ...)
   __attribute__((format(printf, 2, 3)));
 static int tapdisk_channel_refresh_params(tapdisk_channel_t *);
+static void tapdisk_channel_start_event(struct xs_handle *,
+                                       struct xenbus_watch *,
+                                       const char *);
 static void tapdisk_channel_pause_event(struct xs_handle *,
                                        struct xenbus_watch *,
                                        const char *);
+static int tapdisk_channel_connect(tapdisk_channel_t *);
+static void tapdisk_channel_close_tapdisk(tapdisk_channel_t *);
+static void tapdisk_channel_destroy(tapdisk_channel_t *);
 
 static int
 tapdisk_channel_check_uuid(tapdisk_channel_t *channel)
@@ -87,6 +169,9 @@ tapdisk_channel_validate_watch(tapdisk_channel_t *channel, const char *path)
        if (len < 0)
                return -EINVAL;
 
+       if (!xs_exists(channel->xsh, channel->path))
+               return -ENOENT;
+
        err = tapdisk_channel_check_uuid(channel);
        if (err)
                return err;
@@ -135,7 +220,6 @@ tapdisk_channel_validate_message(tapdisk_channel_t *channel,
                return 0;
        }
 
-       channel->state = TAPDISK_CHANNEL_IDLE;
        return 0;
 }
 
@@ -152,15 +236,16 @@ tapdisk_channel_send_message(tapdisk_channel_t *channel,
        offset     = 0;
        len        = sizeof(tapdisk_message_t);
 
-       DPRINTF("%s: sending '%s' message to %d:%d\n",
+       DPRINTF("%s: sending '%s' message to %d:%d, state %s\n",
                channel->path, tapdisk_message_name(message->type),
-               channel->channel_id, channel->cookie);
+               channel->channel_id, channel->cookie,
+               tapdisk_channel_state_name(channel->state));
 
-       if (channel->state != TAPDISK_CHANNEL_IDLE &&
-           message->type  != TAPDISK_MESSAGE_CLOSE &&
-           message->type  != TAPDISK_MESSAGE_FORCE_SHUTDOWN)
-               EPRINTF("%s: writing message to non-idle channel (%d)\n",
-                       channel->path, channel->state);
+       if (!TAPDISK_CHANNEL_IPC_IDLE(channel))
+               EPRINTF("%s: writing message to non-idle channel, state %s (%d)\n",
+                       channel->path,
+                       tapdisk_channel_state_name(channel->state),
+                       channel->state);
 
        while (offset < len) {
                FD_ZERO(&writefds);
@@ -272,11 +357,11 @@ tapdisk_channel_fatal(tapdisk_channel_t *channel, const char *fmt, ...)
 {
        va_list ap;
 
+       tapdisk_channel_enter_vbd_state(channel, TAPDISK_VBD_BROKEN);
+
        va_start(ap, fmt);
        __tapdisk_channel_error(channel, fmt, ap);
        va_end(ap);
-
-       tapdisk_channel_close(channel);
 }
 
 static int
@@ -387,7 +472,6 @@ tapdisk_channel_complete_connection(tapdisk_channel_t *channel)
        if (err)
                goto clean;
 
-       channel->connected = 1;
        return 0;
 
  clean:
@@ -463,6 +547,8 @@ tapdisk_channel_receive_open_response(tapdisk_channel_t *channel,
 {
        int err;
 
+       channel->state = TAPDISK_CHANNEL_RUNNING;
+
        channel->image.size    = message->u.image.sectors;
        channel->image.secsize = message->u.image.sector_size;
        channel->image.info    = message->u.image.info;
@@ -510,9 +596,8 @@ static int
 tapdisk_channel_receive_shutdown_response(tapdisk_channel_t *channel,
                                          tapdisk_message_t *message)
 {
-       channel->open  = 0;
        channel->state = TAPDISK_CHANNEL_CLOSED;
-       tapdisk_channel_close(channel);
+       tapdisk_channel_close_tapdisk(channel);
        return 0;
 }
 
@@ -539,9 +624,6 @@ tapdisk_channel_send_pid_request(tapdisk_channel_t *channel)
 
        err = tapdisk_channel_send_message(channel, &message, 2);
 
-       if (!err)
-               channel->open = 1;
-
        return err;
 }
 
@@ -551,8 +633,8 @@ tapdisk_channel_receive_pid_response(tapdisk_channel_t *channel,
 {
        int err;
 
+       channel->state       = TAPDISK_CHANNEL_PID;
        channel->tapdisk_pid = message->u.tapdisk_pid;
-
        DPRINTF("%s: tapdisk pid: %d\n", channel->path, channel->tapdisk_pid);
 
        err = setpriority(PRIO_PROCESS, channel->tapdisk_pid, PRIO_SPECIAL_IO);
@@ -562,13 +644,6 @@ tapdisk_channel_receive_pid_response(tapdisk_channel_t *channel,
                return err;
        }
 
-       err = tapdisk_channel_send_open_request(channel);
-       if (err) {
-               tapdisk_channel_fatal(channel,
-                                     "sending open request: %d", err);
-               return err;
-       }
-
        return 0;
 }
 
@@ -626,7 +701,8 @@ static int
 tapdisk_channel_receive_pause_response(tapdisk_channel_t *channel,
                                       tapdisk_message_t *message)
 {
-       return tapdisk_channel_signal_paused(channel);
+       channel->state = TAPDISK_CHANNEL_PAUSED;
+       return 0;
 }
 
 static int
@@ -654,27 +730,48 @@ static int
 tapdisk_channel_receive_resume_response(tapdisk_channel_t *channel,
                                        tapdisk_message_t *message)
 {
-       return tapdisk_channel_signal_unpaused(channel);
+       channel->state = TAPDISK_CHANNEL_RUNNING;
+       return 0;
+}
+
+static int
+tapdisk_channel_check_shutdown_request(tapdisk_channel_t *channel)
+{
+       char *s;
+       size_t len;
+       int force;
+
+       force = 0;
+       s = xs_read(channel->xsh, XBT_NULL, channel->shutdown_str, &len);
+       if (!s) {
+               if (errno == ENOENT) {
+                       channel->shutdown_state = TAPDISK_VBD_UP;
+                       return 0;
+               }
+
+               return -errno;
+       }
+
+       force = (len == strlen("force")) && !memcmp(s, "force", len);
+       free(s);
+
+       channel->shutdown_state = TAPDISK_VBD_DOWN;
+       channel->shutdown_force = force;
+
+       return 0;
 }
 
 static void
 tapdisk_channel_shutdown_event(struct xs_handle *xsh,
                               struct xenbus_watch *watch, const char *path)
 {
-       int err, force;
        tapdisk_channel_t *channel;
-       char *s;
-       size_t len;
+       int err;
 
        channel = watch->data;
 
        DPRINTF("%s: got watch on %s\n", channel->path, path);
 
-       if (!xs_exists(channel->xsh, channel->path)) {
-               tapdisk_channel_close(channel);
-               return;
-       }
-
        err = tapdisk_channel_validate_watch(channel, path);
        if (err) {
                if (err == -EINVAL)
@@ -682,102 +779,278 @@ tapdisk_channel_shutdown_event(struct xs_handle *xsh,
                return;
        }
 
-       force = 0;
-       s = xs_read(channel->xsh, XBT_NULL, path, &len);
-       if (s) {
-               force = (len == strlen("force")) && !memcmp(s, "force", len);
-               free(s);
-       }
-
-       if (force)
-               tapdisk_channel_send_force_shutdown_request(channel);
+       err = tapdisk_channel_check_shutdown_request(channel);
+       if (err)
+               tapdisk_channel_error(channel, "shutdown event failed: %d", err);
        else
-               tapdisk_channel_send_shutdown_request(channel);
+               tapdisk_channel_drive_vbd_state(channel);
 }
 
-static void
-tapdisk_channel_pause_event(struct xs_handle *xsh,
-                           struct xenbus_watch *watch, const char *path)
+static int
+tapdisk_channel_drive_paused(tapdisk_channel_t *channel)
 {
-       int err, count, pause, pausing, paused, resuming;
-       tapdisk_channel_t *channel;
+       int err;
 
-       channel = watch->data;
+       switch (channel->state) {
+       case TAPDISK_CHANNEL_WAIT_PID:
+       case TAPDISK_CHANNEL_WAIT_OPEN:
+       case TAPDISK_CHANNEL_WAIT_PAUSE:
+       case TAPDISK_CHANNEL_WAIT_RESUME:
+       case TAPDISK_CHANNEL_WAIT_CLOSE:
+               return -EAGAIN;
+
+       case TAPDISK_CHANNEL_PID:
+       case TAPDISK_CHANNEL_PAUSED:
+       case TAPDISK_CHANNEL_CLOSED:
+       case TAPDISK_CHANNEL_DEAD:
+               return 0;
 
-       count = channel->pause_watch_count++;
-       DPRINTF("%s: got watch %d on %s\n", channel->path, count, path);
+       case TAPDISK_CHANNEL_RUNNING:
+               err = tapdisk_channel_send_pause_request(channel);
+               if (err)
+                       goto fail_msg;
+               return -EAGAIN;
 
-       if (!xs_exists(channel->xsh, channel->path)) {
-               tapdisk_channel_close(channel);
-               return;
+       default:
+               EPRINTF("%s: invalid channel state %d\n",
+                       __func__, channel->state);
+               return -EINVAL;
        }
 
-       /* 
-        * We are not considered online until after OPEN_RSP. If the
-        * pause request is valid:
-        *
-        *  - Safe to ignore the initial 'spurious' event. Pause
-        *    requests won't preempt watch registration.
-        *
-        *  - No need to defer. If the request if valid, we're
-        *    guaranteed to be ready.
-        */
+fail_msg:
+       tapdisk_channel_fatal(channel, "sending message: %d", err);
+       return -EIO;
+}
 
-       if (!count)
-               return;
+static int
+tapdisk_channel_drive_shutdown(tapdisk_channel_t *channel)
+{
+       int err;
 
-       err = tapdisk_channel_validate_watch(channel, path);
-       if (err) {
-               if (err == -EINVAL)
-                       tapdisk_channel_fatal(channel, "bad pause watch");
+       switch (channel->state) {
 
-               if (err != -ENOENT)
-                       return;
+       case TAPDISK_CHANNEL_DEAD:
+               return 0;
 
-               err = 0;
+       case TAPDISK_CHANNEL_CLOSED:
+               if (channel->shared)
+                       return 0;
+               /* let's duely wait for a clean exit */
+               return -EAGAIN;
+
+       case TAPDISK_CHANNEL_LAUNCHED:
+       case TAPDISK_CHANNEL_WAIT_PID:
+       case TAPDISK_CHANNEL_WAIT_OPEN:
+       case TAPDISK_CHANNEL_WAIT_PAUSE:
+       case TAPDISK_CHANNEL_WAIT_RESUME:
+       case TAPDISK_CHANNEL_WAIT_CLOSE:
+               return -EAGAIN;
+
+       case TAPDISK_CHANNEL_PID:
+       case TAPDISK_CHANNEL_RUNNING:
+       case TAPDISK_CHANNEL_PAUSED:
+               if (channel->shutdown_force)
+                       err = tapdisk_channel_send_force_shutdown_request(channel);
+               else
+                       err = tapdisk_channel_send_shutdown_request(channel);
+               if (err)
+                       goto fail_msg;
+               return -EAGAIN;
+
+       default:
+               EPRINTF("%s: invalid channel state %d\n",
+                       __func__, channel->state);
+               return -EINVAL;
        }
 
-       pause    = xs_exists(xsh, channel->pause_str);
-       paused   = xs_exists(xsh, channel->pause_done_str);
-       pausing  = channel->state == TAPDISK_CHANNEL_WAIT_PAUSE;
-       resuming = channel->state == TAPDISK_CHANNEL_WAIT_RESUME;
-       
-       if (!channel->connected) {
-               EPRINTF("bad %s event %s: channel not connected\n", 
-                       pause ? "pause" : "unpause", path);
-               return;
+fail_msg:
+       tapdisk_channel_fatal(channel, "sending message: %d", err);
+       return -EIO;
+}
+
+static int
+tapdisk_channel_drive_running(tapdisk_channel_t *channel)
+{
+       int err;
+
+       switch (channel->state) {
+       case TAPDISK_CHANNEL_DEAD:
+       case TAPDISK_CHANNEL_CLOSED:
+               err = tapdisk_channel_connect(channel);
+               if (err) {
+                       tapdisk_channel_fatal(channel, "failed connect: %d", err);
+                       return err;
+               }
+               return -EAGAIN;
+
+       case TAPDISK_CHANNEL_LAUNCHED:
+       case TAPDISK_CHANNEL_WAIT_PID:
+       case TAPDISK_CHANNEL_WAIT_OPEN:
+       case TAPDISK_CHANNEL_WAIT_PAUSE:
+       case TAPDISK_CHANNEL_WAIT_RESUME:
+       case TAPDISK_CHANNEL_WAIT_CLOSE:
+               return -EAGAIN;
+
+       case TAPDISK_CHANNEL_PID:
+               err = tapdisk_channel_send_open_request(channel);
+               if (err)
+                       goto fail_msg;
+               return -EAGAIN;
+
+       case TAPDISK_CHANNEL_RUNNING:
+               return 0;
+
+       case TAPDISK_CHANNEL_PAUSED:
+               err = tapdisk_channel_send_resume_request(channel);
+               if (err)
+                       goto fail_msg;
+               return -EAGAIN;
+
+       default:
+               EPRINTF("%s: invalid channel state %d\n",
+                       __func__, channel->state);
+               return -EINVAL;
        }
 
-       if (pause) {
-               if (paused || pausing || resuming) {
-                       EPRINTF("bad pause event %s: channel %s", 
-                               path,
-                               pausing ? "pausing" : resuming ? "resuming"
-                               : "paused");
-                       return;
+fail_msg:
+       tapdisk_channel_fatal(channel, "sending message: %d", err);
+       return -EIO;
+}
+
+static channel_state_t
+tapdisk_channel_map_vbd_state(tapdisk_channel_t *channel)
+{
+       channel_state_t next;
+
+       switch (channel->shutdown_state) {
+       case TAPDISK_VBD_DOWN:
+               return TAPDISK_CHANNEL_CLOSED;
+
+       case TAPDISK_VBD_UP:
+               switch (channel->vbd_state) {
+               case TAPDISK_VBD_UNPAUSED:
+                       return TAPDISK_CHANNEL_RUNNING;
+
+               case TAPDISK_VBD_PAUSING:
+               case TAPDISK_VBD_PAUSED:
+                       return TAPDISK_CHANNEL_PAUSED;
+
+               case TAPDISK_VBD_BROKEN:
+               case TAPDISK_VBD_DEAD:
+                       return TAPDISK_CHANNEL_CLOSED;
+
+               default:
+                       EPRINTF("%s: invalid vbd state %d\n",
+                               __func__, channel->vbd_state);
+                       return -EINVAL;
                }
+               break;
+       default:
+               EPRINTF("%s: invalid shutdown state %d\n",
+                       __func__, channel->shutdown_state);
+               return -EINVAL;
+       }
+}
 
-               err = tapdisk_channel_send_pause_request(channel);
+void
+tapdisk_channel_drive_vbd_state(tapdisk_channel_t *channel)
+{
+       channel_state_t next;
+
+       next = tapdisk_channel_map_vbd_state(channel);
+       DPRINTF("driving channel state %s, vbd %s, %s to %s (%d)\n",
+               tapdisk_channel_state_name(channel->state),
+               tapdisk_channel_shutdown_state_name(channel->shutdown_state),
+               tapdisk_channel_vbd_state_name(channel->vbd_state),
+               tapdisk_channel_state_name(next), next);
+       if (next < 0)
+               return;
 
-       } else {
-               if (!paused || pausing || resuming) {
-                       EPRINTF("bad resume event %s: channel %s", 
-                               path,
-                               pausing ? "pausing" : resuming ? "resuming" 
-                               : "not paused");
-                       return;
+       if (channel->state != next) {
+               int err = 0;
+
+               switch (next) {
+               case TAPDISK_CHANNEL_RUNNING:
+                       err = tapdisk_channel_drive_running(channel);
+                       break;
+               case TAPDISK_CHANNEL_PAUSED:
+                       err = tapdisk_channel_drive_paused(channel);
+                       break;
+               case TAPDISK_CHANNEL_CLOSED:
+                       err = tapdisk_channel_drive_shutdown(channel);
+                       break;
+               default:
+                       EPRINTF("%s: invalid target state %d\n", __func__, next);
+                       err = -EINVAL;
+                       break;
                }
+               if (err)
+                       /* -EAGAIN: not there yet */
+                       return;
+       }
+
+       switch (channel->vbd_state) {
+       case TAPDISK_VBD_UNPAUSED:
+       case TAPDISK_VBD_PAUSED:
+       case TAPDISK_VBD_BROKEN:
+               break;
+
+       case TAPDISK_VBD_PAUSING:
+               channel->vbd_state = TAPDISK_VBD_PAUSED;
+               tapdisk_channel_signal_paused(channel);
+               break;
+
+       case TAPDISK_VBD_DEAD:
+               tapdisk_channel_destroy(channel);
+       }
+}
+
+static int
+tapdisk_channel_check_pause_request(tapdisk_channel_t *channel)
+{
+       int pause, err = 0;
 
+       pause = xs_exists(channel->xsh, channel->pause_str);
+       if (pause)
+               tapdisk_channel_enter_vbd_state(channel, TAPDISK_VBD_PAUSING);
+       else {
                err = tapdisk_channel_refresh_params(channel);
-               if (err)
-                       goto out;
+               if (!err)
+                       err = tapdisk_channel_enter_vbd_state(channel, TAPDISK_VBD_UNPAUSED);
+               if (!err)
+                       err = tapdisk_channel_signal_unpaused(channel);
+       }
 
-               err = tapdisk_channel_send_resume_request(channel);
+       return err;
+}
+
+static void
+tapdisk_channel_pause_event(struct xs_handle *xsh,
+                           struct xenbus_watch *watch, const char *path)
+{
+       int err, count;
+       tapdisk_channel_t *channel;
+
+       channel = watch->data;
+
+       DPRINTF("%s: got watch on %s\n", channel->path, path);
+
+       err = tapdisk_channel_validate_watch(channel, path);
+       if (err) {
+               if (err == -EINVAL)
+                       tapdisk_channel_fatal(channel, "bad pause watch");
+
+               if (err != -ENOENT)
+                       return;
+
+               err = 0;
        }
 
-out:
+       err = tapdisk_channel_check_pause_request(channel);
        if (err)
                tapdisk_channel_error(channel, "pause event failed: %d", err);
+       else
+               tapdisk_channel_drive_vbd_state(channel);
 }
 
 static int
@@ -968,7 +1241,6 @@ tapdisk_channel_launch_tapdisk(tapdisk_channel_t *channel)
                goto fail;
        }
 
-       channel->open       = 1;
        channel->channel_id = channel->write_fd;
 
        free(read_dev);
@@ -977,6 +1249,7 @@ tapdisk_channel_launch_tapdisk(tapdisk_channel_t *channel)
        DPRINTF("process launched, channel = %d:%d\n",
                channel->channel_id, channel->cookie);
 
+       channel->state = TAPDISK_CHANNEL_LAUNCHED;
        return tapdisk_channel_send_pid_request(channel);
 
 fail:
@@ -991,9 +1264,10 @@ tapdisk_channel_connect(tapdisk_channel_t *channel)
 {
        int err;
 
-       tapdisk_daemon_find_channel(channel);
-
-       if (!channel->tapdisk_pid)
+       tapdisk_daemon_maybe_clone_channel(channel);
+       if (channel->tapdisk_pid)
+               channel->state = TAPDISK_CHANNEL_PID;
+       else
                return tapdisk_channel_launch_tapdisk(channel);
 
        DPRINTF("%s: process exists: %d, channel = %d:%d\n",
@@ -1043,6 +1317,12 @@ tapdisk_channel_init(tapdisk_channel_t *channel)
                goto fail;
        }
 
+       err = asprintf(&channel->start_str, "%s/start-tapdisk", channel->path);
+       if (err == -1) {
+               channel->start_str = NULL;
+               goto fail;
+       }
+
        err = asprintf(&channel->pause_str, "%s/pause", channel->path);
        if (err == -1) {
                channel->pause_str = NULL;
@@ -1075,6 +1355,11 @@ fail:
 static void
 tapdisk_channel_clear_watches(tapdisk_channel_t *channel)
 {
+       if (channel->start_watch.node) {
+               unregister_xenbus_watch(channel->xsh, &channel->start_watch);
+               channel->start_watch.node    = NULL;
+       }
+
        if (channel->pause_watch.node) {
                unregister_xenbus_watch(channel->xsh, &channel->pause_watch);
                channel->pause_watch.node    = NULL;
@@ -1091,6 +1376,16 @@ tapdisk_channel_set_watches(tapdisk_channel_t *channel)
 {
        int err;
 
+       /* watch for start events */
+       channel->start_watch.node            = channel->start_str;
+       channel->start_watch.callback        = tapdisk_channel_start_event;
+       channel->start_watch.data            = channel;
+       err = register_xenbus_watch(channel->xsh, &channel->start_watch);
+       if (err) {
+               channel->start_watch.node    = NULL;
+               goto fail;
+       }
+
        /* watch for pause events */
        channel->pause_watch.node            = channel->pause_str;
        channel->pause_watch.callback        = tapdisk_channel_pause_event;
@@ -1146,28 +1441,6 @@ out:
        free(stype);
 }
 
-static int
-tapdisk_channel_get_busid(tapdisk_channel_t *channel)
-{
-       int len, end;
-       const char *ptr;
-       char *tptr, num[10];
-
-       len = strsep_len(channel->path, '/', 6);
-       end = strlen(channel->path);
-       if(len < 0 || end < 0) {
-               EPRINTF("invalid path: %s\n", channel->path);
-               return -EINVAL;
-       }
-       
-       ptr = channel->path + len + 1;
-       strncpy(num, ptr, end - len);
-       tptr = num + (end - (len + 1));
-       *tptr = '\0';
-
-       channel->busid = atoi(num);
-       return 0;
-}
 
 static int
 tapdisk_channel_parse_params(tapdisk_channel_t *channel)
@@ -1246,7 +1519,6 @@ tapdisk_channel_gather_info(tapdisk_channel_t *channel)
 
        err = xs_gather(channel->xsh, channel->path,
                        "frontend", NULL, &channel->frontpath,
-                       "frontend-id", "%li", &channel->domid,
                        "params", NULL, &channel->params,
                        "mode", "%c", &channel->mode, NULL);
        if (err) {
@@ -1258,10 +1530,6 @@ tapdisk_channel_gather_info(tapdisk_channel_t *channel)
        if (err)
                goto fail;
 
-       err = tapdisk_channel_get_busid(channel);
-       if (err)
-               goto fail;
-
        tapdisk_channel_get_storage_type(channel);
 
        return 0;
@@ -1297,14 +1565,6 @@ tapdisk_channel_verify_start_request(tapdisk_channel_t *channel)
        char *path;
        unsigned int err;
 
-       err = asprintf(&path, "%s/start-tapdisk", channel->path);
-       if (err == -1)
-               goto mem_fail;
-
-       if (!xs_exists(channel->xsh, path))
-               goto fail;
-
-       free(path);
        err = asprintf(&path, "%s/shutdown-request", channel->path);
        if (err == -1)
                goto mem_fail;
@@ -1328,8 +1588,7 @@ tapdisk_channel_verify_start_request(tapdisk_channel_t *channel)
        return 0;
 
 fail:
-       free(path);
-       EPRINTF("%s:%s: invalid start request\n", __func__, channel->path);
+       EPRINTF("%s:%s: invalid start request: %s\n", __func__, channel->path, path);
        return -EINVAL;
 
 mem_fail:
@@ -1340,6 +1599,10 @@ mem_fail:
 static void
 tapdisk_channel_destroy(tapdisk_channel_t *channel)
 {
+       DPRINTF("destroying channel %d:%d, state %s\n",
+               channel->channel_id, channel->cookie,
+               tapdisk_channel_state_name(channel->state));
+
        tapdisk_channel_clear_watches(channel);
        tapdisk_daemon_close_channel(channel);
        tapdisk_channel_release_info(channel);
@@ -1348,23 +1611,37 @@ tapdisk_channel_destroy(tapdisk_channel_t *channel)
        free(channel);
 }
 
-void
-tapdisk_channel_close(tapdisk_channel_t *channel)
+static void
+tapdisk_channel_start_event(struct xs_handle *xsh,
+                           struct xenbus_watch *watch, const char *path)
 {
-       if (channel->channel_id)
-               DPRINTF("%s: closing channel %d:%d\n",
-                       channel->path, channel->channel_id, channel->cookie);
+       tapdisk_channel_t *channel;
+       int err;
 
-       if (channel->open)
-               tapdisk_channel_send_shutdown_request(channel);
+       channel = watch->data;
 
-       tapdisk_channel_clear_watches(channel);
+       DPRINTF("%s: got watch on %s\n", channel->path, path);
+
+       err = tapdisk_channel_validate_watch(channel, path);
+       if (err) {
+               if (err == -EINVAL)
+                       tapdisk_channel_fatal(channel, "bad start watch");
+               return;
+       }
+
+       err = tapdisk_channel_verify_start_request(channel);
+       if (err)
+               return;
+
+       channel->shutdown_state = TAPDISK_VBD_UP;
+       tapdisk_channel_drive_vbd_state(channel);
 }
 
 int
 tapdisk_channel_open(tapdisk_channel_t **_channel,
-                    char *path, struct xs_handle *xsh,
-                    int blktap_fd, uint16_t cookie)
+                    const char *path, struct xs_handle *xsh,
+                    int blktap_fd, uint16_t cookie,
+                    int domid, int busid)
 {
        int err;
        char *msg;
@@ -1380,7 +1657,9 @@ tapdisk_channel_open(tapdisk_channel_t **_channel,
        channel->xsh       = xsh;
        channel->blktap_fd = blktap_fd;
        channel->cookie    = cookie;
-       channel->state     = TAPDISK_CHANNEL_IDLE;
+       channel->domid     = domid;
+       channel->busid     = busid;
+       channel->state     = TAPDISK_CHANNEL_DEAD;
        channel->read_fd   = -1;
        channel->write_fd  = -1;
 
@@ -1410,21 +1689,21 @@ tapdisk_channel_open(tapdisk_channel_t **_channel,
                goto fail;
        }
 
-       err = tapdisk_channel_verify_start_request(channel);
+       err = tapdisk_channel_set_watches(channel);
        if (err) {
-               msg = "invalid start request";
+               msg = "registering xenstore watches";
                goto fail;
        }
 
-       err = tapdisk_channel_set_watches(channel);
+       err = tapdisk_channel_check_shutdown_request(channel);
        if (err) {
-               msg = "registering xenstore watches";
+               msg = "initializing shutdown state";
                goto fail;
        }
 
-       err = tapdisk_channel_connect(channel);
+       err = tapdisk_channel_check_pause_request(channel);
        if (err) {
-               msg = "connecting to tapdisk";
+               msg = "initializing pause state";
                goto fail;
        }
 
@@ -1439,21 +1718,36 @@ fail:
 void
 tapdisk_channel_reap(tapdisk_channel_t *channel, int status)
 {
-       /* No IPC after this point */
-       channel->open = 0;
+       const char *chn_state, *vbd_state, *krn_state;
+
+       chn_state = tapdisk_channel_state_name(channel->state);
+       vbd_state = tapdisk_channel_vbd_state_name(channel->vbd_state);
+       krn_state = tapdisk_channel_shutdown_state_name(channel->shutdown_state);
 
        if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
-               tapdisk_channel_error(channel,
-                                     "tapdisk died with status %d", 
-                                     WEXITSTATUS(status));
+               tapdisk_channel_fatal(channel,
+                                     "tapdisk died with status %d,"
+                                     " channel state %s, vbd %s, %s",
+                                     WEXITSTATUS(status),
+                                     chn_state, vbd_state, krn_state);
        } else if (WIFSIGNALED(status)) {
-               tapdisk_channel_error(channel,
-                                     "tapdisk killed by signal %d", 
-                                     WTERMSIG(status));
+               tapdisk_channel_fatal(channel,
+                                     "tapdisk killed by signal %d,"
+                                     " channel state %s, vbd %s, %s",
+                                     WTERMSIG(status),
+                                     chn_state, vbd_state, krn_state);
+       } else {
+               DPRINTF("tapdisk exit, status %x,"
+                       " channel state %s, vbd %s, %s\n", status,
+                       chn_state, vbd_state, krn_state);
        }
 
        tapdisk_channel_close_tapdisk(channel);
-       tapdisk_channel_destroy(channel);
+       channel->state = TAPDISK_CHANNEL_DEAD;
+
+       /* NB. we're in VBD_BROKEN state if we didn't exit properly,
+          implicitly avoiding an unwanted restart */
+       tapdisk_channel_drive_vbd_state(channel);
 }
 
 int
@@ -1467,26 +1761,36 @@ tapdisk_channel_receive_message(tapdisk_channel_t *c, tapdisk_message_t *m)
 
        switch (m->type) {
        case TAPDISK_MESSAGE_PID_RSP:
-               return tapdisk_channel_receive_pid_response(c, m);
+               err = tapdisk_channel_receive_pid_response(c, m);
+               break;
 
        case TAPDISK_MESSAGE_OPEN_RSP:
-               return tapdisk_channel_receive_open_response(c, m);
+               err = tapdisk_channel_receive_open_response(c, m);
+               break;
 
        case TAPDISK_MESSAGE_PAUSE_RSP:
-               return tapdisk_channel_receive_pause_response(c, m);
+               err = tapdisk_channel_receive_pause_response(c, m);
+               break;
 
        case TAPDISK_MESSAGE_RESUME_RSP:
-               return tapdisk_channel_receive_resume_response(c, m);
+               err = tapdisk_channel_receive_resume_response(c, m);
+               break;
 
        case TAPDISK_MESSAGE_CLOSE_RSP:
-               return tapdisk_channel_receive_shutdown_response(c, m);
+               err = tapdisk_channel_receive_shutdown_response(c, m);
+               break;
 
        case TAPDISK_MESSAGE_RUNTIME_ERROR:
-               return tapdisk_channel_receive_runtime_error(c, m);
+               err = tapdisk_channel_receive_runtime_error(c, m);
+               break;
+
+       default:
+       fail:
+               tapdisk_channel_fatal(c, "received unexpected message %s in state %d",
+                                     tapdisk_message_name(m->type), c->state);
+               return -EINVAL;
        }
 
-fail:
-       tapdisk_channel_fatal(c, "received unexpected message %s in state %d",
-                             tapdisk_message_name(m->type), c->state);
-       return -EINVAL;
+       tapdisk_channel_drive_vbd_state(c);
+       return 0;
 }
index 7fd78f5a20704cac726640d0dd5680a664385176..4b3d24565c1f4742a84e8fb617e03b5fb88ddb82 100644 (file)
@@ -286,14 +286,8 @@ tapdisk_daemon_wait_for_domid(void)
        return err;
 }
 
-static inline int
-tapdisk_daemon_new_vbd_event(const char *node)
-{
-       return (!strcmp(node, "start-tapdisk"));
-}
-
 static int
-tapdisk_daemon_write_uuid(char *path, uint32_t uuid)
+tapdisk_daemon_write_uuid(const char *path, uint32_t uuid)
 {
        int err;
        char *cpath, uuid_str[12];
@@ -311,54 +305,89 @@ tapdisk_daemon_write_uuid(char *path, uint32_t uuid)
        return (err ? 0 : -errno);
 }
 
-static void
-tapdisk_daemon_probe(struct xs_handle *xsh,
-                    struct xenbus_watch *watch, const char *path)
+static tapdisk_channel_t*
+tapdisk_daemon_find_channel(int domid, int busid)
 {
-       char *cpath;
-       int len, err;
-       uint32_t cookie;
-       const char *node;
-       tapdisk_channel_t *channel;
+       tapdisk_channel_t *channel, *next;
 
-       len = strsep_len(path, '/', 7);
-       if (len < 0)
-               return;
-
-       node = path + len + 1;
+       tapdisk_daemon_for_each_channel(channel, next)
+               if (channel->domid == domid &&
+                   channel->busid == busid)
+                       return channel;
 
-       if (!tapdisk_daemon_new_vbd_event(node))
-               return;
+       return NULL;
+}
 
-       if (!xs_exists(xsh, path))
-               return;
+static void
+tapdisk_daemon_probe_vbd(int domid, int busid, const char *path)
+{
+       tapdisk_channel_t *channel;
+       uint32_t cookie;
+       int err;
 
-       cpath = strdup(path);
-       if (!cpath) {
-               EPRINTF("failed to allocate control path for %s\n", path);
+       channel = tapdisk_daemon_find_channel(domid, busid);
+       if (channel) {
+               DPRINTF("%s: ignoring duplicate probe event:"
+                       " channel %d:%d, state %d\n",
+                       path, channel->channel_id, channel->cookie, 
+                       channel->vbd_state);
                return;
        }
-       cpath[len] = '\0';
 
        cookie = tapdisk_daemon.cookie++;
-       err    = tapdisk_daemon_write_uuid(cpath, cookie);
+       err    = tapdisk_daemon_write_uuid(path, cookie);
        if (err)
-               goto out;
+               return;
 
-       DPRINTF("%s: got watch on %s, uuid = %u\n", __func__, path, cookie);
+       DPRINTF("%s: creating channel, uuid %u\n", path, cookie);
 
-       err = tapdisk_channel_open(&channel, cpath,
+       err = tapdisk_channel_open(&channel, path,
                                   tapdisk_daemon.xsh,
                                   tapdisk_daemon.blktap_fd,
-                                  cookie);
+                                  cookie, domid, busid);
        if (!err)
                list_add(&channel->list, &tapdisk_daemon.channels);
        else
                EPRINTF("failed to open tapdisk channel for %s: %d\n",
                        path, err);
+}
 
-out:
-       free(cpath);
+static void
+tapdisk_daemon_remove_vbd(int domid, int busid, const char *path)
+{
+       tapdisk_channel_t *channel;
+
+       channel = tapdisk_daemon_find_channel(domid, busid);
+       if (!channel) {
+               DPRINTF("%s: ignoring remove event:"
+                       " no channel.\n",
+                       path);
+               return;
+       }
+
+       DPRINTF("%s: marking channel dead, uuid %u\n", path, channel->cookie);
+
+       channel->vbd_state = TAPDISK_VBD_DEAD;
+       tapdisk_channel_drive_vbd_state(channel);
+}
+
+static void
+tapdisk_daemon_node_event(struct xs_handle *xsh,
+                         struct xenbus_watch *watch, const char *path)
+{
+       int count, domid, busid, offset, exists;
+       char slash;
+
+       count = sscanf(path, "/local/domain/%*d/backend/tap/%d/%d%c",
+                      &domid, &busid, &slash);
+
+       if (count == 2) {
+               exists = xs_exists(xsh, path);
+               if (exists)
+                       tapdisk_daemon_probe_vbd(domid, busid, path);
+               else
+                       tapdisk_daemon_remove_vbd(domid, busid, path);
+       }
 }
 
 static int
@@ -371,7 +400,7 @@ tapdisk_daemon_start(void)
                return err;
 
        tapdisk_daemon.watch.node     = tapdisk_daemon.node;
-       tapdisk_daemon.watch.callback = tapdisk_daemon_probe;
+       tapdisk_daemon.watch.callback = tapdisk_daemon_node_event;
 
        err = register_xenbus_watch(tapdisk_daemon.xsh, &tapdisk_daemon.watch);
        if (err)
@@ -545,6 +574,8 @@ tapdisk_daemon_set_fds(fd_set *readfds)
        FD_SET(max, readfds);
 
        tapdisk_daemon_for_each_channel(channel, tmp) {
+               if (!TAPDISK_CHANNEL_IPC_OPEN(channel))
+                       continue;
                fd  = channel->read_fd;
                max = MAX(fd, max);
                FD_SET(fd, readfds);
@@ -562,11 +593,15 @@ tapdisk_daemon_check_fds(fd_set *readfds)
        if (FD_ISSET(xs_fileno(tapdisk_daemon.xsh), readfds))
                xs_fire_next_watch(tapdisk_daemon.xsh);
 
-       tapdisk_daemon_for_each_channel(channel, tmp)
+       tapdisk_daemon_for_each_channel(channel, tmp) {
+               if (!TAPDISK_CHANNEL_IPC_OPEN(channel))
+                       continue;
+
                if (FD_ISSET(channel->read_fd, readfds)) {
                        tapdisk_daemon_receive_message(channel->read_fd);
                        return;
                }
+       }
 }
 
 static int
@@ -595,7 +630,7 @@ tapdisk_daemon_run(void)
 }
 
 void
-tapdisk_daemon_find_channel(tapdisk_channel_t *channel)
+tapdisk_daemon_maybe_clone_channel(tapdisk_channel_t *channel)
 {
        tapdisk_channel_t *c, *tmp;
 
index 77d4248b78684f72225e7aef7613478d0988f7d7..5f3b006063c0c972b0f4835d08ece2f73dcef2a7 100644 (file)
 #include "blktaplib.h"
 #include "tapdisk-message.h"
 
+typedef enum {
+       TAPDISK_CHANNEL_DEAD          = 1,
+       TAPDISK_CHANNEL_LAUNCHED      = 2,
+       TAPDISK_CHANNEL_WAIT_PID      = 3,
+       TAPDISK_CHANNEL_PID           = 4,
+       TAPDISK_CHANNEL_WAIT_OPEN     = 5,
+       TAPDISK_CHANNEL_RUNNING       = 6,
+       TAPDISK_CHANNEL_WAIT_PAUSE    = 7,
+       TAPDISK_CHANNEL_PAUSED        = 8,
+       TAPDISK_CHANNEL_WAIT_RESUME   = 9,
+       TAPDISK_CHANNEL_WAIT_CLOSE    = 10,
+       TAPDISK_CHANNEL_CLOSED        = 11,
+} channel_state_t;
+
+#define TAPDISK_CHANNEL_IPC_OPEN(_c)               \
+       ((_c)->state != TAPDISK_CHANNEL_DEAD     && \
+        (_c)->state != TAPDISK_CHANNEL_CLOSED)
+
+#define TAPDISK_CHANNEL_IPC_IDLE(_c)              \
+       ((_c)->state == TAPDISK_CHANNEL_LAUNCHED || \
+        (_c)->state == TAPDISK_CHANNEL_PID      || \
+        (_c)->state == TAPDISK_CHANNEL_RUNNING  || \
+        (_c)->state == TAPDISK_CHANNEL_PAUSED)
+
+typedef enum {
+       TAPDISK_VBD_UNPAUSED        = 1,
+       TAPDISK_VBD_PAUSING         = 2,
+       TAPDISK_VBD_PAUSED          = 3,
+       TAPDISK_VBD_BROKEN          = 4,
+       TAPDISK_VBD_DEAD            = 5,
+} vbd_state_t;
+
+typedef enum {
+       TAPDISK_VBD_UP              = 1,
+       TAPDISK_VBD_DOWN            = 2,
+} shutdown_state_t;
+
 struct tapdisk_channel {
-       int                       state;
+       channel_state_t           state;
+       vbd_state_t               vbd_state;
+       shutdown_state_t          shutdown_state;
+       int                       shutdown_force;
 
        int                       read_fd;
        int                       write_fd;
@@ -42,7 +82,6 @@ struct tapdisk_channel {
 
        char                      mode;
        char                      shared;
-       char                      open;
        unsigned int              domid;
        unsigned int              busid;
        unsigned int              major;
@@ -52,17 +91,12 @@ struct tapdisk_channel {
        uint16_t                  cookie;
        pid_t                     tapdisk_pid;
 
-       /*
-        * special accounting needed to handle pause
-        * requests received before tapdisk process is ready
-        */
-       char                      connected;
-
        char                     *path;
        char                     *frontpath;
        char                     *params;
        char                     *vdi_path;
        char                     *uuid_str;
+       char                     *start_str;
        char                     *pause_str;
        char                     *pause_done_str;
        char                     *shutdown_str;
@@ -71,9 +105,9 @@ struct tapdisk_channel {
        image_t                   image;
 
        struct list_head          list;
+       struct xenbus_watch       start_watch;
        struct xenbus_watch       pause_watch;
        struct xenbus_watch       shutdown_watch;
-       int                       pause_watch_count;
 
        struct xs_handle         *xsh;
 };
@@ -84,14 +118,16 @@ int strsep_len(const char *str, char c, unsigned int len);
 int make_blktap_device(char *devname, int major, int minor, int perm);
 
 int tapdisk_channel_open(tapdisk_channel_t **,
-                        char *node, struct xs_handle *,
-                        int blktap_fd, uint16_t cookie);
-void tapdisk_channel_close(tapdisk_channel_t *);
+                        const char *node, struct xs_handle *,
+                        int blktap_fd, uint16_t cookie,
+                        int domid, int busid);
 
-void tapdisk_daemon_find_channel(tapdisk_channel_t *);
+void tapdisk_daemon_maybe_clone_channel(tapdisk_channel_t *);
 void tapdisk_daemon_close_channel(tapdisk_channel_t *);
 
 int tapdisk_channel_receive_message(tapdisk_channel_t *, tapdisk_message_t *);
-void tapdisk_channel_reap(tapdisk_channel_t *channel, int status);
+void tapdisk_channel_reap(tapdisk_channel_t *, int status);
+int tapdisk_channel_change_vbd_state(tapdisk_channel_t *, vbd_state_t);
+void tapdisk_channel_drive_vbd_state(tapdisk_channel_t *);
 
 #endif