X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/9bf4896e5d565785316d2c179be91fba11fbf3fb..342ab8d1e4f8650a22a3a1c9fade31df3fd9c1c1:/migration.c diff --git a/migration.c b/migration.c index 75d4f02373..efea21983a 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" @@ -41,6 +43,9 @@ enum { #define MAX_THROTTLE (32 << 20) /* Migration speed throttling */ +/* Migration XBZRLE default cache size */ +#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024) + static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -48,23 +53,24 @@ static NotifierList migration_state_notifiers = migrations at once. For now we don't need to add dynamic creation of migration */ -static MigrationState *migrate_get_current(void) +MigrationState *migrate_get_current(void) { static MigrationState current_migration = { .state = MIG_STATE_SETUP, .bandwidth_limit = MAX_THROTTLE, + .xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE, }; return ¤t_migration; } -int qemu_start_incoming_migration(const char *uri) +int qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p; int ret; if (strstart(uri, "tcp:", &p)) - ret = tcp_start_incoming_migration(p); + ret = tcp_start_incoming_migration(p, errp); #if !defined(WIN32) else if (strstart(uri, "exec:", &p)) ret = exec_start_incoming_migration(p); @@ -89,6 +95,7 @@ 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(); @@ -110,6 +117,43 @@ uint64_t migrate_max_downtime(void) return max_downtime; } +MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) +{ + MigrationCapabilityStatusList *head = NULL; + MigrationCapabilityStatusList *caps; + MigrationState *s = migrate_get_current(); + int i; + + for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + if (head == NULL) { + head = g_malloc0(sizeof(*caps)); + caps = head; + } else { + caps->next = g_malloc0(sizeof(*caps)); + caps = caps->next; + } + caps->value = + g_malloc(sizeof(*caps->value)); + caps->value->capability = i; + caps->value->state = s->enabled_capabilities[i]; + } + + return head; +} + +static void get_xbzrle_cache_stats(MigrationInfo *info) +{ + if (migrate_use_xbzrle()) { + info->has_xbzrle_cache = true; + info->xbzrle_cache = g_malloc0(sizeof(*info->xbzrle_cache)); + info->xbzrle_cache->cache_size = migrate_xbzrle_cache_size(); + info->xbzrle_cache->bytes = xbzrle_mig_bytes_transferred(); + info->xbzrle_cache->pages = xbzrle_mig_pages_transferred(); + info->xbzrle_cache->cache_miss = xbzrle_mig_pages_cache_miss(); + info->xbzrle_cache->overflow = xbzrle_mig_pages_overflow(); + } +} + MigrationInfo *qmp_query_migrate(Error **errp) { MigrationInfo *info = g_malloc0(sizeof(*info)); @@ -122,12 +166,22 @@ MigrationInfo *qmp_query_migrate(Error **errp) case MIG_STATE_ACTIVE: info->has_status = true; info->status = g_strdup("active"); + info->has_total_time = true; + info->total_time = qemu_get_clock_ms(rt_clock) + - s->total_time; + info->has_expected_downtime = true; + info->expected_downtime = s->expected_downtime; 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(); + info->ram->duplicate = dup_mig_pages_transferred(); + info->ram->normal = norm_mig_pages_transferred(); + info->ram->normal_bytes = norm_mig_bytes_transferred(); + info->ram->dirty_pages_rate = s->dirty_pages_rate; + if (blk_mig_active()) { info->has_disk = true; @@ -136,10 +190,26 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->disk->remaining = blk_mig_bytes_remaining(); info->disk->total = blk_mig_bytes_total(); } + + get_xbzrle_cache_stats(info); break; case MIG_STATE_COMPLETED: + get_xbzrle_cache_stats(info); + info->has_status = true; info->status = g_strdup("completed"); + info->total_time = s->total_time; + info->has_downtime = true; + info->downtime = s->downtime; + + info->has_ram = true; + info->ram = g_malloc0(sizeof(*info->ram)); + info->ram->transferred = ram_bytes_transferred(); + info->ram->remaining = 0; + info->ram->total = ram_bytes_total(); + info->ram->duplicate = dup_mig_pages_transferred(); + info->ram->normal = norm_mig_pages_transferred(); + info->ram->normal_bytes = norm_mig_bytes_transferred(); break; case MIG_STATE_ERROR: info->has_status = true; @@ -154,34 +224,36 @@ MigrationInfo *qmp_query_migrate(Error **errp) return info; } -/* shared migration helpers */ - -static void migrate_fd_monitor_suspend(MigrationState *s, Monitor *mon) +void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, + Error **errp) { - if (monitor_suspend(mon) == 0) { - DPRINTF("suspending monitor\n"); - } else { - monitor_printf(mon, "terminal does not allow synchronous " - "migration, continuing detached\n"); + MigrationState *s = migrate_get_current(); + MigrationCapabilityStatusList *cap; + + if (s->state == MIG_STATE_ACTIVE) { + error_set(errp, QERR_MIGRATION_ACTIVE); + return; + } + + for (cap = params; cap; cap = cap->next) { + s->enabled_capabilities[cap->value->capability] = cap->value->state; } } +/* shared migration helpers */ + static int migrate_fd_cleanup(MigrationState *s) { int ret = 0; - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->fd != -1) { + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + } 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) { @@ -215,18 +287,18 @@ static void migrate_fd_completed(MigrationState *s) static void migrate_fd_put_notify(void *opaque) { MigrationState *s = opaque; + int ret; qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); - qemu_file_put_notify(s->file); - if (s->file && qemu_file_get_error(s->file)) { + ret = qemu_file_put_notify(s->file); + if (ret) { migrate_fd_error(s); } } -static ssize_t migrate_fd_put_buffer(void *opaque, const void *data, - size_t size) +ssize_t migrate_fd_put_buffer(MigrationState *s, const void *data, + size_t size) { - MigrationState *s = opaque; ssize_t ret; if (s->state != MIG_STATE_ACTIVE) { @@ -247,9 +319,8 @@ static ssize_t migrate_fd_put_buffer(void *opaque, const void *data, return ret; } -static void migrate_fd_put_ready(void *opaque) +void migrate_fd_put_ready(MigrationState *s) { - MigrationState *s = opaque; int ret; if (s->state != MIG_STATE_ACTIVE) { @@ -258,20 +329,26 @@ static 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(); + int64_t start_time, end_time; DPRINTF("done iterating\n"); + start_time = qemu_get_clock_ms(rt_clock); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - if (qemu_savevm_state_complete(s->mon, s->file) < 0) { + if (qemu_savevm_state_complete(s->file) < 0) { migrate_fd_error(s); } else { migrate_fd_completed(s); } + end_time = qemu_get_clock_ms(rt_clock); + s->total_time = end_time - s->total_time; + s->downtime = end_time - start_time; if (s->state != MIG_STATE_COMPLETED) { if (old_vm_running) { vm_start(); @@ -289,19 +366,18 @@ static void migrate_fd_cancel(MigrationState *s) s->state = MIG_STATE_CANCELLED; notifier_list_notify(&migration_state_notifiers, s); - qemu_savevm_state_cancel(s->mon, s->file); + qemu_savevm_state_cancel(s->file); migrate_fd_cleanup(s); } -static void migrate_fd_wait_for_unfreeze(void *opaque) +int migrate_fd_wait_for_unfreeze(MigrationState *s) { - MigrationState *s = opaque; int ret; DPRINTF("wait for unfreeze\n"); if (s->state != MIG_STATE_ACTIVE) - return; + return -EINVAL; do { fd_set wfds; @@ -313,17 +389,13 @@ static void migrate_fd_wait_for_unfreeze(void *opaque) } while (ret == -1 && (s->get_error(s)) == EINTR); if (ret == -1) { - qemu_file_set_error(s->file, -s->get_error(s)); + return -s->get_error(s); } + return 0; } -static int migrate_fd_close(void *opaque) +int migrate_fd_close(MigrationState *s) { - MigrationState *s = opaque; - - if (s->mon) { - monitor_resume(s->mon); - } qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); return s->close(s); } @@ -335,7 +407,7 @@ 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) @@ -359,15 +431,10 @@ 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); + s->file = qemu_fopen_ops_buffered(s); DPRINTF("beginning savevm\n"); - ret = qemu_savevm_state_begin(s->mon, s->file, s->blk, s->shared); + ret = qemu_savevm_state_begin(s->file, &s->params); if (ret < 0) { DPRINTF("failed, %d\n", ret); migrate_fd_error(s); @@ -376,27 +443,26 @@ void migrate_fd_connect(MigrationState *s) migrate_fd_put_ready(s); } -static MigrationState *migrate_init(Monitor *mon, int detach, int blk, int inc) +static MigrationState *migrate_init(const MigrationParams *params) { MigrationState *s = migrate_get_current(); int64_t bandwidth_limit = s->bandwidth_limit; + bool enabled_capabilities[MIGRATION_CAPABILITY_MAX]; + int64_t xbzrle_cache_size = s->xbzrle_cache_size; + + memcpy(enabled_capabilities, s->enabled_capabilities, + sizeof(enabled_capabilities)); memset(s, 0, sizeof(*s)); s->bandwidth_limit = bandwidth_limit; - s->blk = blk; - s->shared = inc; - - /* s->mon is used for two things: - - pass fd in fd migration - - suspend/resume monitor for not detached migration - */ - s->mon = mon; + s->params = *params; + memcpy(s->enabled_capabilities, enabled_capabilities, + sizeof(enabled_capabilities)); + s->xbzrle_cache_size = xbzrle_cache_size; + s->bandwidth_limit = bandwidth_limit; s->state = MIG_STATE_SETUP; - - if (!detach) { - migrate_fd_monitor_suspend(s, mon); - } + s->total_time = qemu_get_clock_ms(rt_clock); return s; } @@ -413,35 +479,37 @@ void migrate_del_blocker(Error *reason) migration_blockers = g_slist_remove(migration_blockers, reason); } -int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_migrate(const char *uri, bool has_blk, bool blk, + bool has_inc, bool inc, bool has_detach, bool detach, + Error **errp) { + Error *local_err = NULL; MigrationState *s = migrate_get_current(); + MigrationParams params; 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"); int ret; + params.blk = blk; + params.shared = inc; + if (s->state == MIG_STATE_ACTIVE) { - monitor_printf(mon, "migration already in progress\n"); - return -1; + error_set(errp, QERR_MIGRATION_ACTIVE); + return; } - if (qemu_savevm_state_blocked(mon)) { - return -1; + if (qemu_savevm_state_blocked(errp)) { + return; } if (migration_blockers) { - Error *err = migration_blockers->data; - qerror_report_err(err); - return -1; + *errp = error_copy(migration_blockers->data); + return; } - s = migrate_init(mon, detach, blk, inc); + s = migrate_init(¶ms); if (strstart(uri, "tcp:", &p)) { - ret = tcp_start_outgoing_migration(s, p); + ret = tcp_start_outgoing_migration(s, p, &local_err); #if !defined(WIN32) } else if (strstart(uri, "exec:", &p)) { ret = exec_start_outgoing_migration(s, p); @@ -451,21 +519,21 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) ret = fd_start_outgoing_migration(s, p); #endif } else { - monitor_printf(mon, "unknown migration protocol: %s\n", uri); - ret = -EINVAL; - } - - if (ret < 0) { - monitor_printf(mon, "migration failed: %s\n", strerror(-ret)); - return ret; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); + return; } - if (detach) { - s->mon = NULL; + if (ret < 0 || local_err) { + migrate_fd_error(s); + if (!local_err) { + error_set_errno(errp, -ret, QERR_UNDEFINED_ERROR); + } else { + error_propagate(errp, local_err); + } + return; } notifier_list_notify(&migration_state_notifiers, s); - return 0; } void qmp_migrate_cancel(Error **errp) @@ -473,6 +541,25 @@ void qmp_migrate_cancel(Error **errp) migrate_fd_cancel(migrate_get_current()); } +void qmp_migrate_set_cache_size(int64_t value, Error **errp) +{ + MigrationState *s = migrate_get_current(); + + /* Check for truncation */ + if (value != (size_t)value) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cache size", + "exceeding address space"); + return; + } + + s->xbzrle_cache_size = xbzrle_cache_resize(value); +} + +int64_t qmp_query_migrate_cache_size(Error **errp) +{ + return migrate_xbzrle_cache_size(); +} + void qmp_migrate_set_speed(int64_t value, Error **errp) { MigrationState *s; @@ -492,3 +579,21 @@ void qmp_migrate_set_downtime(double value, Error **errp) value = MAX(0, MIN(UINT64_MAX, value)); max_downtime = (uint64_t)value; } + +int migrate_use_xbzrle(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_XBZRLE]; +} + +int64_t migrate_xbzrle_cache_size(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->xbzrle_cache_size; +}