Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 340e01248478ba8b78a6d4d1809b1eff > files > 17

kvm-83-270.el5_11.src.rpm

From d16962dd57fab7c326611c2caa56cd36fcfcf501 Mon Sep 17 00:00:00 2001
From: Yaniv Kamay <yaniv@qumranet.com>
Date: Sun, 28 Dec 2008 19:19:32 +0200
Subject: [PATCH 12/54] qemu: port Uri's migration notifiers

RH-Type: improvement(qxl)
RH-Upstream-status: pending
---
 qemu/console.h        |    2 +-
 qemu/interface.c      |    1 +
 qemu/migration-exec.c |    8 +
 qemu/migration-tcp.c  |  546 ++++++++++++++++++++++++++++++++++++++++++++++---
 qemu/migration.c      |   44 ++--
 qemu/migration.h      |    9 +-
 qemu/osdep.c          |   25 +++
 qemu/qemu_socket.h    |    2 +
 qemu/vl.c             |    2 +
 9 files changed, 582 insertions(+), 57 deletions(-)

diff --git a/qemu/console.h b/qemu/console.h
index 791d32a..d0560bf 100644
--- a/qemu/console.h
+++ b/qemu/console.h
@@ -191,7 +191,7 @@ extern uint8_t _translate_keycode(const int key);
 /* FIXME: term_printf et al should probably go elsewhere so everything
    does not need to include console.h  */
 /* monitor.c */
-void init_monitor_commands();
+void init_monitor_commands(void);
 void monitor_init(CharDriverState *hd, int show_banner);
 void term_puts(const char *str);
 void term_vprintf(const char *fmt, va_list ap);
diff --git a/qemu/interface.c b/qemu/interface.c
index 847573f..b6949d1 100644
--- a/qemu/interface.c
+++ b/qemu/interface.c
@@ -131,6 +131,7 @@ static VDInterface *core_next(CoreInterface *core, VDInterface *prev)
         if (now->interface == prev) {
             return firest_valid_interface(now->next);
         }
+        now = now->next;
     }
     return NULL;
 }
diff --git a/qemu/migration-exec.c b/qemu/migration-exec.c
index caeed4b..8622315 100644
--- a/qemu/migration-exec.c
+++ b/qemu/migration-exec.c
@@ -53,6 +53,12 @@ static int exec_close(FdMigrationState *s)
     return 0;
 }
 
+static void exec_done(struct FdMigrationState* s, int state)
+{
+    s->state = state;
+    migrate_fd_cleanup(s);
+}
+
 MigrationState *exec_start_outgoing_migration(const char *command,
                                              int64_t bandwidth_limit,
                                              int async)
@@ -88,11 +94,13 @@ MigrationState *exec_start_outgoing_migration(const char *command,
     s->close = exec_close;
     s->get_error = file_errno;
     s->write = file_write;
+    s->done = exec_done;
     s->mig_state.cancel = migrate_fd_cancel;
     s->mig_state.get_status = migrate_fd_get_status;
     s->mig_state.release = migrate_fd_release;
 
     s->state = MIG_STATE_ACTIVE;
+    s->save_started = 0;
     s->detach = !async;
     s->bandwidth_limit = bandwidth_limit;
 
diff --git a/qemu/migration-tcp.c b/qemu/migration-tcp.c
index 6fc1943..a0e347d 100644
--- a/qemu/migration-tcp.c
+++ b/qemu/migration-tcp.c
@@ -20,8 +20,67 @@
 #include "buffered_file.h"
 #include "block.h"
 
+#ifdef CONFIG_SPICE
+#include "interface.h"
+#endif
+
 //#define DEBUG_MIGRATION_TCP
 
+typedef struct TCPIncommingState {
+    int listener;
+    int fd;
+    QEMUFile *file;
+} TCPIncommingState;
+
+static TCPIncommingState in_state = { -1, -1, NULL};
+
+#define MIGRATION_MAGIC (*(uint32_t*)"QMIG")
+#define MIGRATION_VERSION 3
+#define MIGRATION_HOOH_END_MAGIC (*(uint32_t*)"HEND")
+
+#define TRUE 1
+#define FALSE 0
+
+/* Migration Notifiers events */
+#define    MIGRATION_NOTIFY_EVENT_NONE     0
+#define    MIGRATION_NOTIFY_EVENT_STARTED  1
+#define    MIGRATION_NOTIFY_EVENT_FINISHED 2
+
+typedef void (*migration_started_t)(void *opaque, const char *cmd);
+typedef void (*migration_finished_t)(void *opaque, int completed);
+typedef void (*migration_recv_t)(void *opaque, int fd);
+
+typedef struct migration_notify_record_s {
+    migration_started_t migration_started;
+    migration_finished_t migration_finished;
+    migration_recv_t migration_recv;
+    void *opaque;
+    char *key;
+    struct migration_notify_record_s *next;
+} migration_notify_record_t;
+
+typedef void (*end_notifiers_call_t)(FdMigrationState *s);
+
+typedef struct {
+    migration_notify_record_t *next_notifier;
+    migration_notify_record_t *notifiers;
+    int notify_in_progress;
+    char *cmd;
+    int  event; /* MIGRATION_NOTIFY_EVENT_? */
+    FdMigrationState *migration_state;
+    end_notifiers_call_t on_notifiers_done;
+} migration_notify_data_t;
+
+static migration_notify_data_t migration_notify_data = {
+    .next_notifier = NULL,
+    .notifiers = NULL,
+    .notify_in_progress = FALSE,
+    .cmd = NULL,
+    .event = MIGRATION_NOTIFY_EVENT_NONE,
+    .migration_state = NULL,
+    .on_notifiers_done = NULL,
+};
+
 #ifdef DEBUG_MIGRATION_TCP
 #define dprintf(fmt, ...) \
     do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0)
@@ -35,6 +94,112 @@ static int socket_errno(FdMigrationState *s)
     return socket_error();
 }
 
+static void tcp_cleanup(FdMigrationState *s)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+
+    migrate_fd_cleanup(s);
+    data->notify_in_progress = FALSE;
+    data->next_notifier = NULL;
+    data->event = MIGRATION_NOTIFY_EVENT_NONE;
+    data->migration_state = NULL;
+    data->on_notifiers_done = NULL;
+    free(data->cmd);
+    data->cmd = NULL;
+}
+
+static void migration_notify_call_handler(migration_notify_record_t *notifier)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+
+    if (data->event == MIGRATION_NOTIFY_EVENT_STARTED) {
+        printf("%s: START BEFORE curr=%p event=%d opaque=%p fd=%d cmd=%s\n",
+               __FUNCTION__, notifier, data->event, notifier->opaque, data->migration_state->fd, data->cmd);
+        notifier->migration_started(notifier->opaque, data->cmd);
+        printf("%s: START AFTER  curr=%p event=%d opaque=%p fd=%d cmd=%s\n",
+               __FUNCTION__, notifier, data->event, notifier->opaque, data->migration_state->fd, data->cmd);
+    } else if (data->event == MIGRATION_NOTIFY_EVENT_FINISHED) {
+        printf("%s: FINISH curr=%p event=%d opaque=%p\n", __FUNCTION__, notifier, data->event, notifier->opaque);
+        notifier->migration_finished(notifier->opaque, 
+                                     data->migration_state->state == MIG_STATE_COMPLETED);
+    } else {
+        printf("%s: bad event\n", __FUNCTION__);
+        abort();
+    }
+}
+
+static migration_notify_record_t *migration_notify_next(void)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+
+    while (data->next_notifier) {
+        migration_notify_record_t *now = data->next_notifier;
+        data->next_notifier = now->next;
+        if ((data->event == MIGRATION_NOTIFY_EVENT_STARTED && now->migration_started ) || 
+            (data->event == MIGRATION_NOTIFY_EVENT_FINISHED && now->migration_finished)) {
+            return now;
+        }
+    }
+    return NULL;
+}
+
+static void migration_notify_done(void)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+    end_notifiers_call_t on_notifiers_done;
+
+    printf("%s\n", __FUNCTION__);
+    data->notify_in_progress = FALSE;
+    data->next_notifier = NULL;
+    on_notifiers_done = data->on_notifiers_done;
+    data->on_notifiers_done = NULL;
+    free(data->cmd);
+    data->cmd = NULL;
+    data->event = MIGRATION_NOTIFY_EVENT_NONE;
+    on_notifiers_done(data->migration_state);
+}
+
+static void migration_notify(void)
+{
+    migration_notify_record_t *notifier = migration_notify_next();
+    if (notifier)
+        migration_notify_call_handler(notifier);
+    else
+        migration_notify_done();
+}
+
+static void tcp_notify_finish(FdMigrationState *s)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+
+    data->next_notifier = data->notifiers;
+    data->notify_in_progress = TRUE;
+    data->event = MIGRATION_NOTIFY_EVENT_FINISHED;
+    data->on_notifiers_done = tcp_cleanup;
+    migration_notify();    
+}
+
+static void tcp_migration_done(FdMigrationState *s, int state)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+
+    if (s->state != MIG_STATE_ACTIVE) {
+        return;
+    }
+    s->state = state;
+
+    if (!data->migration_state) {
+        tcp_cleanup(s);
+        return;
+    }
+
+    if (data->notify_in_progress) {
+        data->on_notifiers_done = tcp_notify_finish;
+        return;
+    }
+    tcp_notify_finish(s);
+}
+
 static int socket_write(FdMigrationState *s, const void * buf, size_t size)
 {
     return send(s->fd, buf, size, 0);
@@ -50,6 +215,51 @@ static int tcp_close(FdMigrationState *s)
     return 0;
 }
 
+static void tcp_begin_send_vm(FdMigrationState *s)
+{
+    int ret;
+    qemu_put_be32(s->file, 0);
+    qemu_put_be32(s->file, MIGRATION_HOOH_END_MAGIC);
+    dprintf("beginning savevm\n");
+    s->save_started = TRUE;
+    ret = qemu_savevm_state_begin(s->file);
+    if (ret < 0) {
+        dprintf("failed, %d\n", ret);
+        migrate_fd_error(s);
+        return;
+    }
+     migrate_fd_put_ready(s);
+}
+
+static void tcp_send_begin(FdMigrationState *s)
+{
+    migration_notify_data_t *data = &migration_notify_data;
+
+    qemu_put_be32(s->file, MIGRATION_MAGIC);
+    qemu_put_be32(s->file, MIGRATION_VERSION);
+    data->migration_state = s;
+    data->next_notifier = data->notifiers;
+    data->notify_in_progress = TRUE;
+    data->event = MIGRATION_NOTIFY_EVENT_STARTED;
+    data->on_notifiers_done = tcp_begin_send_vm;
+    migration_notify();
+}
+
+static void tcp_connect_migrate(FdMigrationState *s)
+{
+    s->file = qemu_fopen_ops_buffered(s,
+                                      s->bandwidth_limit,
+                                      migrate_fd_put_buffer,
+                                      migrate_fd_put_ready,
+                                      migrate_fd_wait_for_unfreeze,
+                                      migrate_fd_close);
+    if (!s->file) {
+        printf("%s:  qemu fopen failed\n", __FUNCTION__);
+        tcp_migration_done(s, MIG_STATE_ERROR);
+        return;
+    }
+    tcp_send_begin(s);
+}
 
 static void tcp_wait_for_connect(void *opaque)
 {
@@ -70,13 +280,56 @@ static void tcp_wait_for_connect(void *opaque)
     qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
 
     if (val == 0)
-        migrate_fd_connect(s);
+        tcp_connect_migrate(s);
     else {
         dprintf("error connecting %d\n", val);
         migrate_fd_error(s);
     }
 }
 
+static int tcp_release(MigrationState *mig_state)
+{
+    FdMigrationState *s = migrate_to_fms(mig_state);
+
+    dprintf("releasing state\n");
+    if (s->state == MIG_STATE_ACTIVE) {
+        tcp_migration_done(s, MIG_STATE_CANCELLED);
+        return FALSE;
+    }
+    if (migration_notify_data.migration_state) {
+        return FALSE;
+    }
+    free(s);
+    return TRUE;
+}
+
+static int tcp_get_status(MigrationState *mig_state)
+{
+    FdMigrationState *s = migrate_to_fms(mig_state);
+
+    if (s->state == MIG_STATE_COMPLETED && 
+                            migration_notify_data.notify_in_progress) {
+        return MIG_STATE_ACTIVE;
+    }
+
+    return s->state;
+}
+
+/*
+ * set tcp-keep-alive socket-option, to detect source/destination host crash
+ * timeout upon idle connection is 2 minutes (give or take 10 seconds):
+ *     60 seconds idle + 10 * 6 seconds intervals
+ * returns 0 on success, -1 on error (same as setsockopt)
+ */
+static int tcp_migration_keepalive(int fd)
+{
+    const int idle  =  60; /* seconds */
+    const int cnt   =  10; /* try 10 times */
+    const int intvl =  6; /* wait for 6 seconds between tries */
+
+    return tcp_set_keepalive(fd, idle, cnt, intvl);
+}
+
 MigrationState *tcp_start_outgoing_migration(const char *host_port,
                                              int64_t bandwidth_limit,
                                              int async)
@@ -88,6 +341,13 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port,
     if (parse_host_port(&addr, host_port) < 0)
         return NULL;
 
+    free(migration_notify_data.cmd);
+    migration_notify_data.cmd = strdup(host_port);
+    if (!migration_notify_data.cmd) {
+        perror("migration_notify_started: could not copy migration command");
+        return NULL;
+    }
+
     s = qemu_mallocz(sizeof(*s));
     if (s == NULL)
         return NULL;
@@ -95,11 +355,14 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port,
     s->get_error = socket_errno;
     s->write = socket_write;
     s->close = tcp_close;
+    s->done = tcp_migration_done;
+
     s->mig_state.cancel = migrate_fd_cancel;
-    s->mig_state.get_status = migrate_fd_get_status;
-    s->mig_state.release = migrate_fd_release;
+    s->mig_state.get_status = tcp_get_status;
+    s->mig_state.release = tcp_release;
 
     s->state = MIG_STATE_ACTIVE;
+    s->save_started = FALSE;
     s->detach = !async;
     s->bandwidth_limit = bandwidth_limit;
     s->fd = socket(PF_INET, SOCK_STREAM, 0);
@@ -108,6 +371,10 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port,
         return NULL;
     }
 
+    if (tcp_migration_keepalive(s->fd)) {
+        term_printf("tcp_keepalive: setsockopt() failed -  %s\n", strerror(errno));
+    }
+
     socket_set_nonblock(s->fd);
 
     if (s->detach == 1) {
@@ -131,55 +398,135 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port,
         qemu_free(s);
         return NULL;
     } else if (ret >= 0)
-        migrate_fd_connect(s);
+        tcp_connect_migrate(s);
 
     return &s->mig_state;
 }
 
-static void tcp_accept_incoming_migration(void *opaque)
+static int tcp_recive_header(void)
 {
-    struct sockaddr_in addr;
-    socklen_t addrlen = sizeof(addr);
-    int s = (unsigned long)opaque;
-    QEMUFile *f;
-    int c, ret;
-
-    do {
-        c = accept(s, (struct sockaddr *)&addr, &addrlen);
-    } while (c == -1 && socket_error() == EINTR);
+    uint32_t magic = qemu_get_be32(in_state.file);
+    uint32_t version = qemu_get_be32(in_state.file);
+    return magic == MIGRATION_MAGIC && version == MIGRATION_VERSION;
+}
 
-    dprintf("accepted migration\n");
+static migration_notify_record_t* migration_notifier_find_by_key(char *key)
+{
+    migration_notify_record_t* notifier = migration_notify_data.notifiers;
 
-    if (c == -1) {
-        fprintf(stderr, "could not accept migration connection\n");
-        return;
+    for (; notifier; notifier = notifier->next) {
+        if (!strcmp(notifier->key,key)) {
+            return notifier;
+        }
     }
+    return NULL;
+}
 
-    f = qemu_fopen_socket(c);
-    if (f == NULL) {
-        fprintf(stderr, "could not qemu_fopen socket\n");
-        goto out;
+static void tcp_incoming_cleanup(void)
+{
+    if (in_state.file) {
+        qemu_fclose(in_state.file);
+        in_state.file = NULL;
+    }
+    if (in_state.fd != -1) {
+        qemu_set_fd_handler2(in_state.fd, NULL, NULL, NULL, NULL);
+        close(in_state.fd);
+        in_state.fd = -1;
     }
+    monitor_resume();
+}
 
-    vm_stop(0); /* just in case */
-    ret = qemu_loadvm_state(f);
+static void tcp_incoming_load_vm(void)
+{
+    int ret = qemu_loadvm_state(in_state.file);
     if (ret < 0) {
         fprintf(stderr, "load of migration failed\n");
-        goto out_fopen;
+        goto error;
     }
     qemu_announce_self();
     dprintf("successfully loaded vm state\n");
 
     /* we've successfully migrated, close the server socket */
-    qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
-    close(s);
-
+    qemu_set_fd_handler2(in_state.listener, NULL, NULL, NULL, NULL);
+    close(in_state.listener);
+    in_state.listener = -1;
     vm_start();
 
-out_fopen:
-    qemu_fclose(f);
-out:
-    close(c);
+error:
+    tcp_incoming_cleanup();
+}
+
+static void tcp_incomming_run_hook(void)
+{
+    uint32_t len = qemu_get_be32(in_state.file);
+    migration_notify_record_t* notifier;
+    char *hook_key = NULL;
+
+    if (!len) {
+        uint32_t hooks_end_magic = qemu_get_be32(in_state.file);
+        if (hooks_end_magic != MIGRATION_HOOH_END_MAGIC) {
+            fprintf(stderr, "%s: bad end of hooks\n", __FUNCTION__);
+            goto error;
+           
+        }
+        tcp_incoming_load_vm();
+        return;
+    }
+    if (!(hook_key = malloc(len)) || qemu_get_buffer(in_state.file, (uint8_t *)hook_key, len) != len) {
+        fprintf(stderr, "recive hook key failed\n");
+        goto error;
+    }
+    notifier = migration_notifier_find_by_key(hook_key);
+    if (!notifier || !notifier->migration_recv) {
+        fprintf(stderr, "%s: bad hook %s notifier %p recv %p\n", __FUNCTION__, hook_key, 
+                notifier, (notifier) ? notifier->migration_recv : NULL);
+        goto error;
+    }
+    free(hook_key);
+
+    //using both fd and QEMUFile is a potential bug.
+    notifier->migration_recv(notifier->opaque, in_state.fd);
+    return;
+
+error:
+    free(hook_key);
+    tcp_incoming_cleanup();
+}
+
+static void tcp_accept_incoming_migration(void *opaque)
+{
+    struct sockaddr_in addr;
+    socklen_t addrlen = sizeof(addr);
+
+    in_state.listener = (unsigned long)opaque;
+    monitor_suspend();
+
+    do {
+        in_state.fd = accept(in_state.listener, (struct sockaddr *)&addr, &addrlen);
+    } while (in_state.fd == -1 && socket_error() == EINTR);
+
+    dprintf("accepted migration\n");
+    
+    if (in_state.fd == -1) {
+        fprintf(stderr, "could not accept migration connection\n");
+        goto error;
+    }
+
+    in_state.file = qemu_fopen_socket(in_state.fd);
+    if (in_state.file == NULL) {
+        fprintf(stderr, "could not qemu_fopen socket\n");
+        goto error;
+    }
+
+    if (!tcp_recive_header()) {
+        fprintf(stderr, "recice migration header failed\n");
+        goto error;
+    }
+    tcp_incomming_run_hook();
+    return;
+
+error:
+    tcp_incoming_cleanup();
 }
 
 int tcp_start_incoming_migration(const char *host_port)
@@ -215,3 +562,138 @@ err:
     close(s);
     return -socket_error();
 }
+
+static migration_notify_record_t *migration_notify_register(void *opaque,
+                                                     const char* key,
+                                                     migration_started_t mig_started,
+                                                     migration_finished_t mig_finished,
+                                                     migration_recv_t mig_recv)
+{
+    migration_notify_record_t *new;
+    int extra_size;
+
+    if (!key || !strlen(key)) {
+        printf("%s: bad key\n", __FUNCTION__);
+        return NULL;
+    }
+    
+    extra_size = strlen(key) + 1;
+    new = (migration_notify_record_t *) qemu_mallocz(sizeof(migration_notify_record_t) + extra_size);
+
+    if (!new)
+        return NULL;
+    printf("%s: record=%p opaque=%p key=%s mstarted=%p mfinished=%p mig_recv=%p\n",
+           __FUNCTION__, new, opaque, key, mig_started, mig_finished, mig_recv);
+    new->opaque = opaque;
+    new->key = (char *)(new + 1);
+    strcpy(new->key, key);
+    new->migration_started  = mig_started;
+    new->migration_finished = mig_finished;
+    new->migration_recv = mig_recv;
+    new->next = NULL;
+    if (migration_notify_data.notifiers == NULL)
+        migration_notify_data.notifiers = new;
+    else {
+        migration_notify_record_t *prev = migration_notify_data.notifiers;
+        while (prev->next != NULL)
+            prev = prev->next;
+        prev->next = new;
+    }
+    return new;
+}
+
+#ifdef CONFIG_SPICE
+
+static VDObjectRef interface_register_notifiers(MigrationInterface* mig, const char *key,
+                                                migration_notify_started_t mig_started,
+                                                migration_notify_finished_t mig_finished,
+                                                migration_notify_recv_t mig_recv,
+                                                void *opaque)
+{
+    return (VDObjectRef)migration_notify_register(opaque, key, mig_started, mig_finished, mig_recv);
+}
+
+
+static void interface_unregister_notifiers(MigrationInterface* mig, VDObjectRef notifier_ref)
+{
+    migration_notify_record_t* notifier = (migration_notify_record_t *)notifier_ref;
+    migration_notify_record_t** now = &migration_notify_data.notifiers;
+
+    if (!notifier) {
+        return;
+    }
+
+    while (*now) {
+        if (*now == notifier) {
+            *now = notifier->next;
+            if (notifier == migration_notify_data.next_notifier) {
+                migration_notify_data.next_notifier = notifier->next;
+            }
+            free(notifier);
+            return;
+        }
+    }
+}
+
+static void continue_recv_hooks(void *opaque)
+{
+    qemu_set_fd_handler(in_state.fd, NULL, NULL, NULL);
+    tcp_incomming_run_hook();
+}
+
+static void continue_nofify(void *opaque)
+{
+    QEMUBH **qemu_bh_link = (QEMUBH **)opaque;
+    qemu_bh_delete(*qemu_bh_link);
+    free(qemu_bh_link);
+    migration_notify();
+}
+
+static void interface_notifier_done(MigrationInterface *mig, VDObjectRef notifier_ref)
+{
+    if (!migration_notify_data.migration_state) {
+        qemu_set_fd_handler(in_state.fd, continue_recv_hooks, NULL, NULL);
+        return;
+    }
+    QEMUBH **qemu_bh_link = malloc(sizeof(QEMUBH *));
+    QEMUBH *qemu_bh = qemu_bh_new(continue_nofify, qemu_bh_link);
+    *qemu_bh_link = qemu_bh;
+    qemu_bh_schedule(qemu_bh);
+}
+
+static int interface_begin_hook(MigrationInterface *mig, VDObjectRef notifier_ref)
+{
+    migration_notify_record_t* notifier = (migration_notify_record_t *)notifier_ref;
+    QEMUFile *file = migration_notify_data.migration_state->file;
+    int len = strlen(notifier->key) + 1;
+
+    qemu_put_be32(file, len);
+    qemu_put_buffer(file, (uint8_t *)notifier->key, len);
+    qemu_fflush(file);
+    return migration_notify_data.migration_state->fd;
+}   
+
+void tcp_migration_register_interface()
+{
+    MigrationInterface *interface = (MigrationInterface *)qemu_mallocz(sizeof(*interface));
+
+    if (!interface) {
+        printf("%s: malloc failed\n", __FUNCTION__);
+        exit(-1);
+    }
+    interface->base.base_vertion = VM_INTERFACE_VERTION;
+    interface->base.type = VD_INTERFACE_MIGRATION;
+    interface->base.id = 0;
+    interface->base.description = "qemue migration";
+    interface->base.major_vertion = VD_INTERFACE_MIGRATION_MAJOR;
+    interface->base.minor_vertion = VD_INTERFACE_MIGRATION_MINOR;
+    interface->register_notifiers = interface_register_notifiers;
+    interface->unregister_notifiers = interface_unregister_notifiers;
+    interface->notifier_done = interface_notifier_done;
+    interface->begin_hook = interface_begin_hook;
+    add_interface(&interface->base);
+}
+
+#endif
+
+
diff --git a/qemu/migration.c b/qemu/migration.c
index 0ef777a..60d5ca0 100644
--- a/qemu/migration.c
+++ b/qemu/migration.c
@@ -50,25 +50,26 @@ void qemu_start_incoming_migration(const char *uri)
 
 void do_migrate(int detach, const char *uri)
 {
-    MigrationState *s = NULL;
     const char *p;
 
+    if (current_migration && 
+                    !current_migration->release(current_migration)) {
+        term_printf("can't start. aborting previos migration\n");
+        return;
+    }
+    current_migration = NULL;
+
     if (strstart(uri, "tcp:", &p))
-        s = tcp_start_outgoing_migration(p, max_throttle, detach);
+        current_migration = tcp_start_outgoing_migration(p, max_throttle, detach);
 #if !defined(WIN32)
     else if (strstart(uri, "exec:", &p))
-        s = exec_start_outgoing_migration(p, max_throttle, detach);
+        current_migration = exec_start_outgoing_migration(p, max_throttle, detach);
 #endif
     else
         term_printf("unknown migration protocol: %s\n", uri);
 
-    if (s == NULL)
+    if (!current_migration) {
         term_printf("migration failed\n");
-    else {
-        if (current_migration)
-            current_migration->release(current_migration);
-
-        current_migration = s;
     }
 }
 
@@ -128,8 +129,7 @@ void do_info_migrate(void)
 void migrate_fd_error(FdMigrationState *s)
 {
     dprintf("setting error state\n");
-    s->state = MIG_STATE_ERROR;
-    migrate_fd_cleanup(s);
+    s->done(s, MIG_STATE_ERROR);
 }
 
 void migrate_fd_cleanup(FdMigrationState *s)
@@ -139,18 +139,19 @@ void migrate_fd_cleanup(FdMigrationState *s)
     if (s->file) {
         dprintf("closing file\n");
         qemu_fclose(s->file);
+        s->file = NULL;
     }
 
-    if (s->fd != -1)
+    if (s->fd != -1) {
         close(s->fd);
+        s->fd = -1;
+    }
 
     /* Don't resume monitor until we've flushed all of the buffers */
     if (s->detach == 2) {
         monitor_resume();
         s->detach = 0;
     }
-
-    s->fd = -1;
 }
 
 void migrate_fd_put_notify(void *opaque)
@@ -191,6 +192,7 @@ void migrate_fd_connect(FdMigrationState *s)
                                       migrate_fd_close);
 
     dprintf("beginning savevm\n");
+    s->save_started = 1;
     ret = qemu_savevm_state_begin(s->file);
     if (ret < 0) {
         dprintf("failed, %d\n", ret);
@@ -205,7 +207,7 @@ void migrate_fd_put_ready(void *opaque)
 {
     FdMigrationState *s = opaque;
 
-    if (s->state != MIG_STATE_ACTIVE) {
+    if (!s->save_started || s->state != MIG_STATE_ACTIVE) {
         dprintf("put_ready returning because of non-active state\n");
         return;
     }
@@ -217,8 +219,7 @@ void migrate_fd_put_ready(void *opaque)
 
         bdrv_flush_all();
         qemu_savevm_state_complete(s->file);
-        s->state = MIG_STATE_COMPLETED;
-        migrate_fd_cleanup(s);
+        s->done(s, MIG_STATE_COMPLETED);
     }
 }
 
@@ -236,13 +237,10 @@ void migrate_fd_cancel(MigrationState *mig_state)
         return;
 
     dprintf("cancelling migration\n");
-
-    s->state = MIG_STATE_CANCELLED;
-
-    migrate_fd_cleanup(s);
+    s->done(s, MIG_STATE_CANCELLED);
 }
 
-void migrate_fd_release(MigrationState *mig_state)
+int migrate_fd_release(MigrationState *mig_state)
 {
     FdMigrationState *s = migrate_to_fms(mig_state);
 
@@ -253,6 +251,7 @@ void migrate_fd_release(MigrationState *mig_state)
         migrate_fd_cleanup(s);
     }
     free(s);
+    return 1;
 }
 
 void migrate_fd_wait_for_unfreeze(void *opaque)
@@ -279,3 +278,4 @@ int migrate_fd_close(void *opaque)
     FdMigrationState *s = opaque;
     return s->close(s);
 }
+
diff --git a/qemu/migration.h b/qemu/migration.h
index d9771ad..4d63a85 100644
--- a/qemu/migration.h
+++ b/qemu/migration.h
@@ -26,7 +26,7 @@ struct MigrationState
     /* FIXME: add more accessors to print migration info */
     void (*cancel)(MigrationState *s);
     int (*get_status)(MigrationState *s);
-    void (*release)(MigrationState *s);
+    int (*release)(MigrationState *s);
 };
 
 typedef struct FdMigrationState FdMigrationState;
@@ -39,9 +39,11 @@ struct FdMigrationState
     int fd;
     int detach;
     int state;
+    int save_started;
     int (*get_error)(struct FdMigrationState*);
     int (*close)(struct FdMigrationState*);
     int (*write)(struct FdMigrationState*, const void *, size_t);
+    void (*done)(struct FdMigrationState*, int state);
     void *opaque;
 };
 
@@ -62,6 +64,9 @@ MigrationState *exec_start_outgoing_migration(const char *host_port,
 					     int detach);
 
 int tcp_start_incoming_migration(const char *host_port);
+#ifdef CONFIG_SPICE
+void tcp_migration_register_interface(void);
+#endif
 
 MigrationState *tcp_start_outgoing_migration(const char *host_port,
 					     int64_t bandwidth_limit,
@@ -83,7 +88,7 @@ int migrate_fd_get_status(MigrationState *mig_state);
 
 void migrate_fd_cancel(MigrationState *mig_state);
 
-void migrate_fd_release(MigrationState *mig_state);
+int migrate_fd_release(MigrationState *mig_state);
 
 void migrate_fd_wait_for_unfreeze(void *opaque);
 
diff --git a/qemu/osdep.c b/qemu/osdep.c
index 6e751c5..6a76b52 100644
--- a/qemu/osdep.c
+++ b/qemu/osdep.c
@@ -312,4 +312,29 @@ void socket_set_nonblock(int fd)
     f = fcntl(fd, F_GETFL);
     fcntl(fd, F_SETFL, f | O_NONBLOCK);
 }
+
+int tcp_set_keepalive(int fd, int idle, int cnt, int intvl)
+{
+    int enable = 1;
+
+    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,  &enable, sizeof(int))) {
+        perror("setsockopt(alive) failed");
+        return -1;
+    }
+    if (setsockopt(fd, SOL_TCP,    TCP_KEEPIDLE,  &idle,  sizeof(int))) {
+        perror("setsockopt(idle) failed");
+        return -1;
+    }
+    if (setsockopt(fd, SOL_TCP,    TCP_KEEPCNT,   &cnt,   sizeof(int))) {
+        perror("setsockopt(cnt) failed");
+        return -1;
+    }
+    if (setsockopt(fd, SOL_TCP,    TCP_KEEPINTVL, &intvl, sizeof(int))) {
+        perror("setsockopt(intvl) failed");
+        return -1;
+    }
+
+    return 0;
+}
+
 #endif
diff --git a/qemu/qemu_socket.h b/qemu/qemu_socket.h
index c8ca07e..f042021 100644
--- a/qemu/qemu_socket.h
+++ b/qemu/qemu_socket.h
@@ -43,6 +43,8 @@ int inet_connect(const char *str, int socktype);
 int unix_listen(const char *path, char *ostr, int olen);
 int unix_connect(const char *path);
 
+int tcp_set_keepalive(int fd, int idle, int cnt, int intvl);
+
 /* Old, ipv4 only bits.  Don't use for new code. */
 int parse_host_port(struct sockaddr_in *saddr, const char *str);
 int parse_host_src_port(struct sockaddr_in *haddr,
diff --git a/qemu/vl.c b/qemu/vl.c
index f7c1f74..b02a85c 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -156,6 +156,7 @@
 #ifdef CONFIG_SPICE
 #include "spice.h"
 #include "interface.h" 
+#include "migration.h" 
 #endif
 
 //#define DEBUG_UNUSED_IOPORT
@@ -5921,6 +5922,7 @@ int main(int argc, char **argv, char **envp)
 
 #ifdef CONFIG_SPICE
     if(using_spice) {
+        tcp_migration_register_interface();
         spice_init(&core_interface);
     }
 #endif
-- 
1.6.1