X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=migration.c;h=94f7839e8b54114870eb418071b8bc6778fbe039;hb=15552c4ad38b9ec66792b044cf5ae74538c32717;hp=c233b522ff27ab02bedbea31fceb5802a54554cd;hpb=624b9cc20922fe7b046cd79c839d1f3a5a5fc27e;p=qemu.git diff --git a/migration.c b/migration.c index c233b522f..94f7839e8 100644 --- a/migration.c +++ b/migration.c @@ -9,6 +9,8 @@ * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu-common.h" @@ -19,7 +21,7 @@ #include "block.h" #include "qemu_socket.h" #include "block-migration.h" -#include "qemu-objects.h" +#include "qmp-commands.h" //#define DEBUG_MIGRATION @@ -31,14 +33,33 @@ do { } while (0) #endif -/* Migration speed throttling */ -static int64_t max_throttle = (32 << 20); +enum { + MIG_STATE_ERROR, + MIG_STATE_SETUP, + MIG_STATE_CANCELLED, + MIG_STATE_ACTIVE, + MIG_STATE_COMPLETED, +}; -static MigrationState *current_migration; +#define MAX_THROTTLE (32 << 20) /* Migration speed throttling */ static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); +/* When we add fault tolerance, we could have several + migrations at once. For now we don't need to add + dynamic creation of migration */ + +static MigrationState *migrate_get_current(void) +{ + static MigrationState current_migration = { + .state = MIG_STATE_SETUP, + .bandwidth_limit = MAX_THROTTLE, + }; + + return ¤t_migration; +} + int qemu_start_incoming_migration(const char *uri) { const char *p; @@ -70,6 +91,10 @@ void process_incoming_migration(QEMUFile *f) qemu_announce_self(); DPRINTF("successfully loaded vm state\n"); + bdrv_clear_incoming_migration_all(); + /* Make sure all file formats flush their mutable metadata */ + bdrv_invalidate_cache_all(); + if (autostart) { vm_start(); } else { @@ -77,87 +102,6 @@ void process_incoming_migration(QEMUFile *f) } } -int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - MigrationState *s = NULL; - const char *p; - int detach = qdict_get_try_bool(qdict, "detach", 0); - int blk = qdict_get_try_bool(qdict, "blk", 0); - int inc = qdict_get_try_bool(qdict, "inc", 0); - const char *uri = qdict_get_str(qdict, "uri"); - - if (current_migration && - current_migration->get_status(current_migration) == MIG_STATE_ACTIVE) { - monitor_printf(mon, "migration already in progress\n"); - return -1; - } - - if (qemu_savevm_state_blocked(mon)) { - return -1; - } - - if (strstart(uri, "tcp:", &p)) { - s = tcp_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); -#if !defined(WIN32) - } else if (strstart(uri, "exec:", &p)) { - s = exec_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); - } else if (strstart(uri, "unix:", &p)) { - s = unix_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); - } else if (strstart(uri, "fd:", &p)) { - s = fd_start_outgoing_migration(mon, p, max_throttle, detach, - blk, inc); -#endif - } else { - monitor_printf(mon, "unknown migration protocol: %s\n", uri); - return -1; - } - - if (s == NULL) { - monitor_printf(mon, "migration failed\n"); - return -1; - } - - if (current_migration) { - current_migration->release(current_migration); - } - - current_migration = s; - notifier_list_notify(&migration_state_notifiers, NULL); - return 0; -} - -int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - MigrationState *s = current_migration; - - if (s && s->get_status(s) == MIG_STATE_ACTIVE) { - s->cancel(s); - } - return 0; -} - -int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - int64_t d; - FdMigrationState *s; - - d = qdict_get_int(qdict, "value"); - if (d < 0) { - d = 0; - } - max_throttle = d; - - s = migrate_to_fms(current_migration); - if (s && s->file) { - qemu_file_set_rate_limit(s->file, max_throttle); - } - - return 0; -} - /* amount of nanoseconds we are willing to wait for migration to be down. * the choice of nanoseconds is because it is the maximum resolution that * get_clock() can achieve. It is an internal measure. All user-visible @@ -169,119 +113,53 @@ uint64_t migrate_max_downtime(void) return max_downtime; } -int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, - QObject **ret_data) -{ - double d; - - d = qdict_get_double(qdict, "value") * 1e9; - d = MAX(0, MIN(UINT64_MAX, d)); - max_downtime = (uint64_t)d; - - return 0; -} - -static void migrate_print_status(Monitor *mon, const char *name, - const QDict *status_dict) -{ - QDict *qdict; - - qdict = qobject_to_qdict(qdict_get(status_dict, name)); - - monitor_printf(mon, "transferred %s: %" PRIu64 " kbytes\n", name, - qdict_get_int(qdict, "transferred") >> 10); - monitor_printf(mon, "remaining %s: %" PRIu64 " kbytes\n", name, - qdict_get_int(qdict, "remaining") >> 10); - monitor_printf(mon, "total %s: %" PRIu64 " kbytes\n", name, - qdict_get_int(qdict, "total") >> 10); -} - -void do_info_migrate_print(Monitor *mon, const QObject *data) -{ - QDict *qdict; - - qdict = qobject_to_qdict(data); - - monitor_printf(mon, "Migration status: %s\n", - qdict_get_str(qdict, "status")); - - if (qdict_haskey(qdict, "ram")) { - migrate_print_status(mon, "ram", qdict); - } - - if (qdict_haskey(qdict, "disk")) { - migrate_print_status(mon, "disk", qdict); - } -} - -static void migrate_put_status(QDict *qdict, const char *name, - uint64_t trans, uint64_t rem, uint64_t total) -{ - QObject *obj; - - obj = qobject_from_jsonf("{ 'transferred': %" PRId64 ", " - "'remaining': %" PRId64 ", " - "'total': %" PRId64 " }", trans, rem, total); - qdict_put_obj(qdict, name, obj); -} - -void do_info_migrate(Monitor *mon, QObject **ret_data) +MigrationInfo *qmp_query_migrate(Error **errp) { - QDict *qdict; - MigrationState *s = current_migration; - - if (s) { - switch (s->get_status(s)) { - case MIG_STATE_ACTIVE: - qdict = qdict_new(); - qdict_put(qdict, "status", qstring_from_str("active")); - - migrate_put_status(qdict, "ram", ram_bytes_transferred(), - ram_bytes_remaining(), ram_bytes_total()); - - if (blk_mig_active()) { - migrate_put_status(qdict, "disk", blk_mig_bytes_transferred(), - blk_mig_bytes_remaining(), - blk_mig_bytes_total()); - } - - *ret_data = QOBJECT(qdict); - break; - case MIG_STATE_COMPLETED: - *ret_data = qobject_from_jsonf("{ 'status': 'completed' }"); - break; - case MIG_STATE_ERROR: - *ret_data = qobject_from_jsonf("{ 'status': 'failed' }"); - break; - case MIG_STATE_CANCELLED: - *ret_data = qobject_from_jsonf("{ 'status': 'cancelled' }"); - break; + MigrationInfo *info = g_malloc0(sizeof(*info)); + MigrationState *s = migrate_get_current(); + + switch (s->state) { + case MIG_STATE_SETUP: + /* no migration has happened ever */ + break; + case MIG_STATE_ACTIVE: + info->has_status = true; + info->status = g_strdup("active"); + + info->has_ram = true; + info->ram = g_malloc0(sizeof(*info->ram)); + info->ram->transferred = ram_bytes_transferred(); + info->ram->remaining = ram_bytes_remaining(); + info->ram->total = ram_bytes_total(); + + if (blk_mig_active()) { + info->has_disk = true; + info->disk = g_malloc0(sizeof(*info->disk)); + info->disk->transferred = blk_mig_bytes_transferred(); + info->disk->remaining = blk_mig_bytes_remaining(); + info->disk->total = blk_mig_bytes_total(); } + break; + case MIG_STATE_COMPLETED: + info->has_status = true; + info->status = g_strdup("completed"); + break; + case MIG_STATE_ERROR: + info->has_status = true; + info->status = g_strdup("failed"); + break; + case MIG_STATE_CANCELLED: + info->has_status = true; + info->status = g_strdup("cancelled"); + break; } -} - -/* shared migration helpers */ -void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon) -{ - s->mon = mon; - if (monitor_suspend(mon) == 0) { - DPRINTF("suspending monitor\n"); - } else { - monitor_printf(mon, "terminal does not allow synchronous " - "migration, continuing detached\n"); - } + return info; } -void migrate_fd_error(FdMigrationState *s) -{ - DPRINTF("setting error state\n"); - s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers, NULL); - migrate_fd_cleanup(s); -} +/* shared migration helpers */ -int migrate_fd_cleanup(FdMigrationState *s) +static int migrate_fd_cleanup(MigrationState *s) { int ret = 0; @@ -289,14 +167,8 @@ int migrate_fd_cleanup(FdMigrationState *s) if (s->file) { DPRINTF("closing file\n"); - if (qemu_fclose(s->file) != 0) { - ret = -1; - } + ret = qemu_fclose(s->file); s->file = NULL; - } else { - if (s->mon) { - monitor_resume(s->mon); - } } if (s->fd != -1) { @@ -307,20 +179,41 @@ int migrate_fd_cleanup(FdMigrationState *s) return ret; } -void migrate_fd_put_notify(void *opaque) +void migrate_fd_error(MigrationState *s) { - FdMigrationState *s = opaque; + DPRINTF("setting error state\n"); + s->state = MIG_STATE_ERROR; + notifier_list_notify(&migration_state_notifiers, s); + migrate_fd_cleanup(s); +} + +static void migrate_fd_completed(MigrationState *s) +{ + DPRINTF("setting completed state\n"); + if (migrate_fd_cleanup(s) < 0) { + s->state = MIG_STATE_ERROR; + } else { + s->state = MIG_STATE_COMPLETED; + runstate_set(RUN_STATE_POSTMIGRATE); + } + notifier_list_notify(&migration_state_notifiers, s); +} + +static void migrate_fd_put_notify(void *opaque) +{ + MigrationState *s = opaque; qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); qemu_file_put_notify(s->file); - if (qemu_file_get_error(s->file)) { + if (s->file && qemu_file_get_error(s->file)) { migrate_fd_error(s); } } -ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) +static ssize_t migrate_fd_put_buffer(void *opaque, const void *data, + size_t size) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; ssize_t ret; if (s->state != MIG_STATE_ACTIVE) { @@ -341,32 +234,9 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) return ret; } -void migrate_fd_connect(FdMigrationState *s) -{ - int ret; - - 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); - - DPRINTF("beginning savevm\n"); - ret = qemu_savevm_state_begin(s->mon, s->file, s->mig_state.blk, - s->mig_state.shared); - if (ret < 0) { - DPRINTF("failed, %d\n", ret); - migrate_fd_error(s); - return; - } - - migrate_fd_put_ready(s); -} - -void migrate_fd_put_ready(void *opaque) +static void migrate_fd_put_ready(void *opaque) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; int ret; if (s->state != MIG_STATE_ACTIVE) { @@ -375,74 +245,45 @@ void migrate_fd_put_ready(void *opaque) } DPRINTF("iterate\n"); - ret = qemu_savevm_state_iterate(s->mon, s->file); + ret = qemu_savevm_state_iterate(s->file); if (ret < 0) { migrate_fd_error(s); } else if (ret == 1) { int old_vm_running = runstate_is_running(); DPRINTF("done iterating\n"); - vm_stop(RUN_STATE_FINISH_MIGRATE); + vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) { - if (old_vm_running) { - vm_start(); - } - s->state = MIG_STATE_ERROR; + if (qemu_savevm_state_complete(s->file) < 0) { + migrate_fd_error(s); + } else { + migrate_fd_completed(s); } - if (migrate_fd_cleanup(s) < 0) { + if (s->state != MIG_STATE_COMPLETED) { if (old_vm_running) { vm_start(); } - s->state = MIG_STATE_ERROR; - } - if (s->state == MIG_STATE_ACTIVE) { - s->state = MIG_STATE_COMPLETED; - runstate_set(RUN_STATE_POSTMIGRATE); } - notifier_list_notify(&migration_state_notifiers, NULL); } } -int migrate_fd_get_status(MigrationState *mig_state) -{ - FdMigrationState *s = migrate_to_fms(mig_state); - return s->state; -} - -void migrate_fd_cancel(MigrationState *mig_state) +static void migrate_fd_cancel(MigrationState *s) { - FdMigrationState *s = migrate_to_fms(mig_state); - if (s->state != MIG_STATE_ACTIVE) return; DPRINTF("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers, NULL); - qemu_savevm_state_cancel(s->mon, s->file); + notifier_list_notify(&migration_state_notifiers, s); + qemu_savevm_state_cancel(s->file); migrate_fd_cleanup(s); } -void migrate_fd_release(MigrationState *mig_state) +static void migrate_fd_wait_for_unfreeze(void *opaque) { - FdMigrationState *s = migrate_to_fms(mig_state); - - DPRINTF("releasing state\n"); - - if (s->state == MIG_STATE_ACTIVE) { - s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers, NULL); - migrate_fd_cleanup(s); - } - g_free(s); -} - -void migrate_fd_wait_for_unfreeze(void *opaque) -{ - FdMigrationState *s = opaque; + MigrationState *s = opaque; int ret; DPRINTF("wait for unfreeze\n"); @@ -463,13 +304,10 @@ void migrate_fd_wait_for_unfreeze(void *opaque) } } -int migrate_fd_close(void *opaque) +static int migrate_fd_close(void *opaque) { - FdMigrationState *s = opaque; + MigrationState *s = opaque; - if (s->mon) { - monitor_resume(s->mon); - } qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); return s->close(s); } @@ -481,14 +319,145 @@ void add_migration_state_change_notifier(Notifier *notify) void remove_migration_state_change_notifier(Notifier *notify) { - notifier_list_remove(&migration_state_notifiers, notify); + notifier_remove(notify); +} + +bool migration_is_active(MigrationState *s) +{ + return s->state == MIG_STATE_ACTIVE; +} + +bool migration_has_finished(MigrationState *s) +{ + return s->state == MIG_STATE_COMPLETED; +} + +bool migration_has_failed(MigrationState *s) +{ + return (s->state == MIG_STATE_CANCELLED || + s->state == MIG_STATE_ERROR); +} + +void migrate_fd_connect(MigrationState *s) +{ + int ret; + + s->state = MIG_STATE_ACTIVE; + 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); + + DPRINTF("beginning savevm\n"); + ret = qemu_savevm_state_begin(s->file, s->blk, s->shared); + if (ret < 0) { + DPRINTF("failed, %d\n", ret); + migrate_fd_error(s); + return; + } + migrate_fd_put_ready(s); +} + +static MigrationState *migrate_init(int blk, int inc) +{ + MigrationState *s = migrate_get_current(); + int64_t bandwidth_limit = s->bandwidth_limit; + + memset(s, 0, sizeof(*s)); + s->bandwidth_limit = bandwidth_limit; + s->blk = blk; + s->shared = inc; + + s->bandwidth_limit = bandwidth_limit; + s->state = MIG_STATE_SETUP; + + return s; +} + +static GSList *migration_blockers; + +void migrate_add_blocker(Error *reason) +{ + migration_blockers = g_slist_prepend(migration_blockers, reason); +} + +void migrate_del_blocker(Error *reason) +{ + migration_blockers = g_slist_remove(migration_blockers, reason); } -int get_migration_state(void) +void qmp_migrate(const char *uri, bool has_blk, bool blk, + bool has_inc, bool inc, bool has_detach, bool detach, + Error **errp) { - if (current_migration) { - return migrate_fd_get_status(current_migration); + MigrationState *s = migrate_get_current(); + const char *p; + int ret; + + if (s->state == MIG_STATE_ACTIVE) { + error_set(errp, QERR_MIGRATION_ACTIVE); + return; + } + + if (qemu_savevm_state_blocked(errp)) { + return; + } + + if (migration_blockers) { + *errp = error_copy(migration_blockers->data); + return; + } + + s = migrate_init(blk, inc); + + if (strstart(uri, "tcp:", &p)) { + ret = tcp_start_outgoing_migration(s, p); +#if !defined(WIN32) + } else if (strstart(uri, "exec:", &p)) { + ret = exec_start_outgoing_migration(s, p); + } else if (strstart(uri, "unix:", &p)) { + ret = unix_start_outgoing_migration(s, p); + } else if (strstart(uri, "fd:", &p)) { + ret = fd_start_outgoing_migration(s, p); +#endif } else { - return MIG_STATE_ERROR; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); + return; + } + + if (ret < 0) { + DPRINTF("migration failed: %s\n", strerror(-ret)); + /* FIXME: we should return meaningful errors */ + error_set(errp, QERR_UNDEFINED_ERROR); + return; } + + notifier_list_notify(&migration_state_notifiers, s); +} + +void qmp_migrate_cancel(Error **errp) +{ + migrate_fd_cancel(migrate_get_current()); +} + +void qmp_migrate_set_speed(int64_t value, Error **errp) +{ + MigrationState *s; + + if (value < 0) { + value = 0; + } + + s = migrate_get_current(); + s->bandwidth_limit = value; + qemu_file_set_rate_limit(s->file, s->bandwidth_limit); +} + +void qmp_migrate_set_downtime(double value, Error **errp) +{ + value *= 1e9; + value = MAX(0, MIN(UINT64_MAX, value)); + max_downtime = (uint64_t)value; }