#include "hw/qdev-properties.h"
#include "monitor/monitor.h"
#include "net/announce.h"
+#include "qemu/queue.h"
#define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */
case MIGRATION_STATUS_SETUP:
case MIGRATION_STATUS_PRE_SWITCHOVER:
case MIGRATION_STATUS_DEVICE:
+ case MIGRATION_STATUS_WAIT_UNPLUG:
return true;
default:
}
}
+static void populate_time_info(MigrationInfo *info, MigrationState *s)
+{
+ info->has_status = true;
+ info->has_setup_time = true;
+ info->setup_time = s->setup_time;
+ if (s->state == MIGRATION_STATUS_COMPLETED) {
+ info->has_total_time = true;
+ info->total_time = s->total_time;
+ info->has_downtime = true;
+ info->downtime = s->downtime;
+ } else {
+ info->has_total_time = true;
+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
+ s->start_time;
+ info->has_expected_downtime = true;
+ info->expected_downtime = s->expected_downtime;
+ }
+}
+
static void populate_ram_info(MigrationInfo *info, MigrationState *s)
{
info->has_ram = true;
case MIGRATION_STATUS_DEVICE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER:
- /* TODO add some postcopy stats */
- info->has_status = true;
- info->has_total_time = true;
- info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
- - s->start_time;
- info->has_expected_downtime = true;
- info->expected_downtime = s->expected_downtime;
- info->has_setup_time = true;
- info->setup_time = s->setup_time;
-
+ /* TODO add some postcopy stats */
+ populate_time_info(info, s);
populate_ram_info(info, s);
populate_disk_info(info);
break;
/* TODO: display COLO specific information (checkpoint info etc.) */
break;
case MIGRATION_STATUS_COMPLETED:
- info->has_status = true;
- info->has_total_time = true;
- info->total_time = s->total_time;
- info->has_downtime = true;
- info->downtime = s->downtime;
- info->has_setup_time = true;
- info->setup_time = s->setup_time;
-
+ populate_time_info(info, s);
populate_ram_info(info, s);
break;
case MIGRATION_STATUS_FAILED:
case MIGRATION_STATUS_CANCELLED:
info->has_status = true;
break;
+ case MIGRATION_STATUS_WAIT_UNPLUG:
+ info->has_status = true;
+ break;
}
info->status = s->state;
}
#endif
if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
- if (cap_list[MIGRATION_CAPABILITY_COMPRESS]) {
- /* The decompression threads asynchronously write into RAM
- * rather than use the atomic copies needed to avoid
- * userfaulting. It should be possible to fix the decompression
- * threads for compatibility in future.
- */
- error_setg(errp, "Postcopy is not currently compatible "
- "with compression");
- return false;
- }
-
/* This check is reasonably expensive, so only when it's being
* set the first time, also it's only the destination that needs
* special support.
qemu_fclose(tmp);
}
- assert((s->state != MIGRATION_STATUS_ACTIVE) &&
- (s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE));
+ assert(!migration_is_active(s));
if (s->state == MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
{
MigrationState *s = migrate_get_current();
- return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ switch (s->state) {
+ case MIGRATION_STATUS_POSTCOPY_ACTIVE:
+ case MIGRATION_STATUS_POSTCOPY_PAUSED:
+ case MIGRATION_STATUS_POSTCOPY_RECOVER:
+ return true;
+ default:
+ return false;
+ }
}
bool migration_in_postcopy_after_devices(MigrationState *s)
case MIGRATION_STATUS_COLO:
case MIGRATION_STATUS_PRE_SWITCHOVER:
case MIGRATION_STATUS_DEVICE:
+ case MIGRATION_STATUS_WAIT_UNPLUG:
return false;
case MIGRATION_STATUS__MAX:
g_assert_not_reached();
return false;
}
+bool migration_is_active(MigrationState *s)
+{
+ return (s->state == MIGRATION_STATUS_ACTIVE ||
+ s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+}
+
void migrate_init(MigrationState *s)
{
/*
* parameters/capabilities that the user set, and
* locks.
*/
- s->bytes_xfer = 0;
s->cleanup_bh = 0;
s->to_dst_file = NULL;
s->state = MIGRATION_STATUS_NONE;
}
if (!once) {
error_setg(errp, "The incoming migration has already been started");
+ return;
}
qemu_start_incoming_migration(uri, &local_err);
}
migrate_init(s);
+ /*
+ * set ram_counters memory to zero for a
+ * new migration
+ */
+ memset(&ram_counters, 0, sizeof(ram_counters));
return true;
}
}
value *= 1000; /* Convert to milliseconds */
- value = MAX(0, MIN(INT64_MAX, value));
MigrateSetParameters p = {
.has_downtime_limit = true,
- .downtime_limit = value,
+ .downtime_limit = (int64_t)value,
};
qmp_migrate_set_parameters(&p, errp);
return s->enabled_capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED];
}
+bool migrate_validate_uuid(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->enabled_capabilities[MIGRATION_CAPABILITY_VALIDATE_UUID];
+}
+
bool migrate_use_events(void)
{
MigrationState *s;
static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname,
ram_addr_t start, size_t len)
{
- long our_host_ps = getpagesize();
+ long our_host_ps = qemu_real_host_page_size;
trace_migrate_handle_rp_req_pages(rbname, start, len);
out:
res = qemu_file_get_error(rp);
if (res) {
- if (res == -EIO) {
+ if (res == -EIO && migration_in_postcopy()) {
/*
* Maybe there is something we can do: it looks like a
* network down issue, and we pause for a recovery.
{
int ret;
int state = s->state;
+ Error *local_error = NULL;
if (state == MIGRATION_STATUS_CANCELLING ||
state == MIGRATION_STATUS_CANCELLED) {
}
/* Try to detect any file errors */
- ret = qemu_file_get_error(s->to_dst_file);
-
+ ret = qemu_file_get_error_obj(s->to_dst_file, &local_error);
if (!ret) {
/* Everything is fine */
+ assert(!local_error);
return MIG_THR_ERR_NONE;
}
+ if (local_error) {
+ migrate_set_error(s, local_error);
+ error_free(local_error);
+ }
+
if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) {
/*
* For postcopy, we allow the network to be down for a
}
}
-/* How many bytes have we transferred since the beggining of the migration */
+/* How many bytes have we transferred since the beginning of the migration */
static uint64_t migration_total_bytes(MigrationState *s)
{
return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes;
}
}
+static void update_iteration_initial_status(MigrationState *s)
+{
+ /*
+ * Update these three fields at the same time to avoid mismatch info lead
+ * wrong speed calculation.
+ */
+ s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ s->iteration_initial_bytes = migration_total_bytes(s);
+ s->iteration_initial_pages = ram_get_total_transferred_pages();
+}
+
static void migration_update_counters(MigrationState *s,
int64_t current_time)
{
qemu_file_reset_rate_limit(s->to_dst_file);
- s->iteration_start_time = current_time;
- s->iteration_initial_bytes = current_bytes;
- s->iteration_initial_pages = ram_get_total_transferred_pages();
+ update_iteration_initial_status(s);
trace_migrate_transferred(transferred, time_spent,
bandwidth, s->threshold_size);
if (pending_size && pending_size >= s->threshold_size) {
/* Still a significant amount to transfer */
- if (migrate_postcopy() && !in_postcopy &&
- pend_pre <= s->threshold_size &&
+ if (!in_postcopy && pend_pre <= s->threshold_size &&
atomic_read(&s->start_postcopy)) {
if (postcopy_start(s)) {
error_report("%s: postcopy failed to start", __func__);
return MIG_ITERATE_SKIP;
}
/* Just another iteration step */
- qemu_savevm_state_iterate(s->to_dst_file,
- s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ qemu_savevm_state_iterate(s->to_dst_file, in_postcopy);
} else {
trace_migration_thread_low_pending(pending_size);
migration_completion(s);
qemu_sem_wait(&migrate_get_current()->rate_limit_sem);
}
+/* Returns true if the rate limiting was broken by an urgent request */
+bool migration_rate_limit(void)
+{
+ int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ MigrationState *s = migrate_get_current();
+
+ bool urgent = false;
+ migration_update_counters(s, now);
+ if (qemu_file_rate_limit(s->to_dst_file)) {
+ /*
+ * Wait for a delay to do rate limiting OR
+ * something urgent to post the semaphore.
+ */
+ int ms = s->iteration_start_time + BUFFER_DELAY - now;
+ trace_migration_rate_limit_pre(ms);
+ if (qemu_sem_timedwait(&s->rate_limit_sem, ms) == 0) {
+ /*
+ * We were woken by one or more urgent things but
+ * the timedwait will have consumed one of them.
+ * The service routine for the urgent wake will dec
+ * the semaphore itself for each item it consumes,
+ * so add this one we just eat back.
+ */
+ qemu_sem_post(&s->rate_limit_sem);
+ urgent = true;
+ }
+ trace_migration_rate_limit_post(urgent);
+ }
+ return urgent;
+}
+
/*
* Master migration thread on the source VM.
* It drives the migration and pumps the data down the outgoing channel.
rcu_register_thread();
object_ref(OBJECT(s));
- s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ update_iteration_initial_status(s);
qemu_savevm_state_header(s->to_dst_file);
qemu_savevm_state_setup(s->to_dst_file);
+ if (qemu_savevm_nr_failover_devices()) {
+ migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
+ MIGRATION_STATUS_WAIT_UNPLUG);
+
+ while (s->state == MIGRATION_STATUS_WAIT_UNPLUG &&
+ qemu_savevm_state_guest_unplug_pending()) {
+ qemu_sem_timedwait(&s->wait_unplug_sem, 250);
+ }
+
+ migrate_set_state(&s->state, MIGRATION_STATUS_WAIT_UNPLUG,
+ MIGRATION_STATUS_ACTIVE);
+ }
+
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_ACTIVE);
trace_migration_thread_setup_complete();
- while (s->state == MIGRATION_STATUS_ACTIVE ||
- s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
- int64_t current_time;
-
+ while (migration_is_active(s)) {
if (urgent || !qemu_file_rate_limit(s->to_dst_file)) {
MigIterateState iter_state = migration_iteration_run(s);
if (iter_state == MIG_ITERATE_SKIP) {
* the local variables. This is important to avoid
* breaking transferred_bytes and bandwidth calculation
*/
- s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
- s->iteration_initial_bytes = 0;
+ update_iteration_initial_status(s);
}
- current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
-
- migration_update_counters(s, current_time);
-
- urgent = false;
- if (qemu_file_rate_limit(s->to_dst_file)) {
- /* Wait for a delay to do rate limiting OR
- * something urgent to post the semaphore.
- */
- int ms = s->iteration_start_time + BUFFER_DELAY - current_time;
- trace_migration_thread_ratelimit_pre(ms);
- if (qemu_sem_timedwait(&s->rate_limit_sem, ms) == 0) {
- /* We were worken by one or more urgent things but
- * the timedwait will have consumed one of them.
- * The service routine for the urgent wake will dec
- * the semaphore itself for each item it consumes,
- * so add this one we just eat back.
- */
- qemu_sem_post(&s->rate_limit_sem);
- urgent = true;
- }
- trace_migration_thread_ratelimit_post(urgent);
- }
+ urgent = migration_rate_limit();
}
trace_migration_thread_after_loop();
if (resume) {
/* This is a resumed migration */
- rate_limit = INT64_MAX;
+ rate_limit = s->parameters.max_postcopy_bandwidth /
+ XFER_LIMIT_RATIO;
} else {
/* This is a fresh new migration */
rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO;
DeviceClass *dc = DEVICE_CLASS(klass);
dc->user_creatable = false;
- dc->props = migration_properties;
+ device_class_set_props(dc, migration_properties);
}
static void migration_instance_finalize(Object *obj)
qemu_mutex_destroy(&ms->qemu_file_lock);
g_free(params->tls_hostname);
g_free(params->tls_creds);
+ qemu_sem_destroy(&ms->wait_unplug_sem);
qemu_sem_destroy(&ms->rate_limit_sem);
qemu_sem_destroy(&ms->pause_sem);
qemu_sem_destroy(&ms->postcopy_pause_sem);
qemu_sem_init(&ms->postcopy_pause_rp_sem, 0);
qemu_sem_init(&ms->rp_state.rp_sem, 0);
qemu_sem_init(&ms->rate_limit_sem, 0);
+ qemu_sem_init(&ms->wait_unplug_sem, 0);
qemu_mutex_init(&ms->qemu_file_lock);
}