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