#include "exec/address-spaces.h"
#include "io/channel-buffer.h"
#include "io/channel-tls.h"
+#include "migration/colo.h"
#define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */
#define BUFFER_DELAY 100
#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY)
+/* Time in milliseconds we are allowed to stop the source,
+ * for sending the last part */
+#define DEFAULT_MIGRATE_SET_DOWNTIME 300
+
/* Default compression thread count */
#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8
/* Default decompression thread count, usually decompression is at
/* Migration XBZRLE default cache size */
#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024)
+/* The delay time (in ms) between two COLO checkpoints
+ * Note: Please change this default value to 10000 when we support hybrid mode.
+ */
+#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY 200
+
static NotifierList migration_state_notifiers =
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
static bool once;
static MigrationState current_migration = {
.state = MIGRATION_STATUS_NONE,
- .bandwidth_limit = MAX_THROTTLE,
.xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE,
.mbps = -1,
.parameters = {
.decompress_threads = DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT,
.cpu_throttle_initial = DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL,
.cpu_throttle_increment = DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT,
+ .max_bandwidth = MAX_THROTTLE,
+ .downtime_limit = DEFAULT_MIGRATE_SET_DOWNTIME,
+ .x_checkpoint_delay = DEFAULT_MIGRATE_X_CHECKPOINT_DELAY,
},
};
/* Else if something went wrong then just fall out of the normal exit */
}
+ /* we get COLO info, and know if we are in COLO mode */
+ if (!ret && migration_incoming_enable_colo()) {
+ mis->migration_incoming_co = qemu_coroutine_self();
+ qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
+ colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE);
+ mis->have_colo_incoming_thread = true;
+ qemu_coroutine_yield();
+
+ /* Wait checkpoint incoming thread exit before free resource */
+ qemu_thread_join(&mis->colo_incoming_thread);
+ }
+
qemu_fclose(f);
free_xbzrle_decoded_buf();
migrate_send_rp_message(mis, MIG_RP_MSG_PONG, sizeof(buf), &buf);
}
-/* 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
- * units must be in seconds */
-static uint64_t max_downtime = 300000000;
-
-uint64_t migrate_max_downtime(void)
-{
- return max_downtime;
-}
-
MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
{
MigrationCapabilityStatusList *head = NULL;
caps = NULL; /* silence compiler warning */
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
+ if (i == MIGRATION_CAPABILITY_X_COLO && !colo_supported()) {
+ continue;
+ }
if (head == NULL) {
head = g_malloc0(sizeof(*caps));
caps = head;
params->tls_creds = g_strdup(s->parameters.tls_creds);
params->has_tls_hostname = !!s->parameters.tls_hostname;
params->tls_hostname = g_strdup(s->parameters.tls_hostname);
+ params->has_max_bandwidth = true;
+ params->max_bandwidth = s->parameters.max_bandwidth;
+ params->has_downtime_limit = true;
+ params->downtime_limit = s->parameters.downtime_limit;
+ params->has_x_checkpoint_delay = true;
+ params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
return params;
}
get_xbzrle_cache_stats(info);
break;
+ case MIGRATION_STATUS_COLO:
+ info->has_status = true;
+ /* TODO: display COLO specific information (checkpoint info etc.) */
+ break;
case MIGRATION_STATUS_COMPLETED:
get_xbzrle_cache_stats(info);
}
for (cap = params; cap; cap = cap->next) {
+ if (cap->value->capability == MIGRATION_CAPABILITY_X_COLO) {
+ if (!colo_supported()) {
+ error_setg(errp, "COLO is not currently supported, please"
+ " configure with --enable-colo option in order to"
+ " support COLO feature");
+ continue;
+ }
+ }
s->enabled_capabilities[cap->value->capability] = cap->value->state;
}
"an integer in the range of 1 to 99");
return;
}
+ if (params->has_max_bandwidth &&
+ (params->max_bandwidth < 0 || params->max_bandwidth > SIZE_MAX)) {
+ error_setg(errp, "Parameter 'max_bandwidth' expects an integer in the"
+ " range of 0 to %zu bytes/second", SIZE_MAX);
+ return;
+ }
+ if (params->has_downtime_limit &&
+ (params->downtime_limit < 0 || params->downtime_limit > 2000000)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+ "downtime_limit",
+ "an integer in the range of 0 to 2000000 milliseconds");
+ return;
+ }
+ if (params->has_x_checkpoint_delay && (params->x_checkpoint_delay < 0)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+ "x_checkpoint_delay",
+ "is invalid, it should be positive");
+ }
if (params->has_compress_level) {
s->parameters.compress_level = params->compress_level;
g_free(s->parameters.tls_hostname);
s->parameters.tls_hostname = g_strdup(params->tls_hostname);
}
+ if (params->has_max_bandwidth) {
+ s->parameters.max_bandwidth = params->max_bandwidth;
+ if (s->to_dst_file) {
+ qemu_file_set_rate_limit(s->to_dst_file,
+ s->parameters.max_bandwidth / XFER_LIMIT_RATIO);
+ }
+ }
+ if (params->has_downtime_limit) {
+ s->parameters.downtime_limit = params->downtime_limit;
+ }
+
+ if (params->has_x_checkpoint_delay) {
+ s->parameters.x_checkpoint_delay = params->x_checkpoint_delay;
+ }
}
void migrate_fd_error(MigrationState *s, const Error *error)
{
- trace_migrate_fd_error(error ? error_get_pretty(error) : "");
+ trace_migrate_fd_error(error_get_pretty(error));
assert(s->to_dst_file == NULL);
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_FAILED);
params.shared = has_inc && inc;
if (migration_is_setup_or_active(s->state) ||
- s->state == MIGRATION_STATUS_CANCELLING) {
+ s->state == MIGRATION_STATUS_CANCELLING ||
+ s->state == MIGRATION_STATUS_COLO) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return;
}
void qmp_migrate_set_speed(int64_t value, Error **errp)
{
- MigrationState *s;
-
- if (value < 0) {
- value = 0;
- }
- if (value > SIZE_MAX) {
- value = SIZE_MAX;
- }
+ MigrationParameters p = {
+ .has_max_bandwidth = true,
+ .max_bandwidth = value,
+ };
- s = migrate_get_current();
- s->bandwidth_limit = value;
- if (s->to_dst_file) {
- qemu_file_set_rate_limit(s->to_dst_file,
- s->bandwidth_limit / XFER_LIMIT_RATIO);
- }
+ qmp_migrate_set_parameters(&p, errp);
}
void qmp_migrate_set_downtime(double value, Error **errp)
{
- value *= 1e9;
- value = MAX(0, MIN(UINT64_MAX, value));
- max_downtime = (uint64_t)value;
+ value *= 1000; /* Convert to milliseconds */
+ value = MAX(0, MIN(INT64_MAX, value));
+
+ MigrationParameters p = {
+ .has_downtime_limit = true,
+ .downtime_limit = value,
+ };
+
+ qmp_migrate_set_parameters(&p, errp);
}
bool migrate_postcopy_ram(void)
* to do this we use a qemu_buf to hold the whole of the device state.
*/
bioc = qio_channel_buffer_new(4096);
+ qio_channel_set_name(QIO_CHANNEL(bioc), "migration-postcopy-buffer");
fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc));
object_unref(OBJECT(bioc));
if (!ret) {
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
- if (ret >= 0) {
+ /*
+ * Don't mark the image with BDRV_O_INACTIVE flag if
+ * we will go into COLO stage later.
+ */
+ if (ret >= 0 && !migrate_colo_enabled()) {
ret = bdrv_inactivate_all();
}
if (ret >= 0) {
goto fail_invalidate;
}
- migrate_set_state(&s->state, current_active_state,
- MIGRATION_STATUS_COMPLETED);
+ if (!migrate_colo_enabled()) {
+ migrate_set_state(&s->state, current_active_state,
+ MIGRATION_STATUS_COMPLETED);
+ }
+
return;
fail_invalidate:
MIGRATION_STATUS_FAILED);
}
+bool migrate_colo_enabled(void)
+{
+ MigrationState *s = migrate_get_current();
+ return s->enabled_capabilities[MIGRATION_CAPABILITY_X_COLO];
+}
+
/*
* Master migration thread on the source VM.
* It drives the migration and pumps the data down the outgoing channel.
bool entered_postcopy = false;
/* The active state we expect to be in; ACTIVE or POSTCOPY_ACTIVE */
enum MigrationStatus current_active_state = MIGRATION_STATUS_ACTIVE;
+ bool enable_colo = migrate_colo_enabled();
rcu_register_thread();
initial_bytes;
uint64_t time_spent = current_time - initial_time;
double bandwidth = (double)transferred_bytes / time_spent;
- max_size = bandwidth * migrate_max_downtime() / 1000000;
+ max_size = bandwidth * s->parameters.downtime_limit;
s->mbps = (((double) transferred_bytes * 8.0) /
((double) time_spent / 1000.0)) / 1000.0 / 1000.0;
end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
qemu_mutex_lock_iothread();
- qemu_savevm_state_cleanup();
+ /*
+ * The resource has been allocated by migration will be reused in COLO
+ * process, so don't release them.
+ */
+ if (!enable_colo) {
+ qemu_savevm_state_cleanup();
+ }
if (s->state == MIGRATION_STATUS_COMPLETED) {
uint64_t transferred_bytes = qemu_ftell(s->to_dst_file);
s->total_time = end_time - s->total_time;
}
runstate_set(RUN_STATE_POSTMIGRATE);
} else {
+ if (s->state == MIGRATION_STATUS_ACTIVE && enable_colo) {
+ migrate_start_colo_process(s);
+ qemu_savevm_state_cleanup();
+ /*
+ * Fixme: we will run VM in COLO no matter its old running state.
+ * After exited COLO, we will keep running.
+ */
+ old_vm_running = true;
+ }
if (old_vm_running && !entered_postcopy) {
vm_start();
} else {
void migrate_fd_connect(MigrationState *s)
{
- /* This is a best 1st approximation. ns to ms */
- s->expected_downtime = max_downtime/1000000;
+ s->expected_downtime = s->parameters.downtime_limit;
s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s);
qemu_file_set_blocking(s->to_dst_file, true);
qemu_file_set_rate_limit(s->to_dst_file,
- s->bandwidth_limit / XFER_LIMIT_RATIO);
+ s->parameters.max_bandwidth / XFER_LIMIT_RATIO);
/* Notify before starting migration thread */
notifier_list_notify(&migration_state_notifiers, s);