mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'migration-20231017-pull-request' of https://gitlab.com/juan.quintela/qemu into staging
Migration Pull request (20231017) Hi Same that yesterday one, except: - rebased to latest (clean rebase) - fixed 64 bits read on big endian host CI: https://gitlab.com/juan.quintela/qemu/-/pipelines/1039214198 Please, apply. # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEGJn/jt6/WMzuA0uC9IfvGFhy1yMFAmUuReUACgkQ9IfvGFhy # 1yO+FQ/+Nx2botbrUVJb3vLeG6f+x5xeWJjB0boOqhk7227cKmAA33Oqwx5l4UtL # oLOHA6P4ThqacpaluGOMMp44BSr/jOMDC/HUDVJtSplTD+droPiklIIGUfYScLbA # oYx6lXfSB2jMpSuSU19STbjwBRvd4bjJix3zDGwEIgXYqYt0tY0FY/nnGTmImnM1 # KDjRerf1lg4Rt0vvwg7I0onIDvh3CKX26Sj5a3wSRaLoocUe3jpsuBNH7MMqroHs # WpocBIsLiBAf/CbeLZsQlhbVeOi1R+kSAR5hDPvvJCPWHIrd2wf8+3NXjcFepb7d # M4wE2jLjCvHhzwYwSc0ir4n74jwD22IirEPQs8ONHrjLCb5VoBKYV5bqsFUHF55N # SbFvcZIzJFiOm2anEWiiqiNTLtYAdQCKtUvbyJ7Mq4ck6icIInLdX9zrm4voofYJ # 02lX/IIGlT3C3dGSz09LBoJ6E82zmQWNHmov8A90+3RYvMF9uSpxi0z40lhj6jWC # 6Q2AHxrJJ040ZboeOfJQG78BtvZ/9PQ2ORhJ3ceRDND4kSTDtfe/TSNAZ3thM33y # Sv99o+F/HaqrKnxK8eTJrvIEWxojDu3lnqJERWAm2AOxTnQ+6mgGtsCfLEdrv5D1 # xVsY2QczB1quRjaU2ml/7Cxe4Q1urTtfl82IEXGded6UL+cmF/I= # =br93 # -----END PGP SIGNATURE----- # gpg: Signature made Tue 17 Oct 2023 04:29:25 EDT # gpg: using RSA key 1899FF8EDEBF58CCEE034B82F487EF185872D723 # gpg: Good signature from "Juan Quintela <quintela@redhat.com>" [full] # gpg: aka "Juan Quintela <quintela@trasno.org>" [full] # Primary key fingerprint: 1899 FF8E DEBF 58CC EE03 4B82 F487 EF18 5872 D723 * tag 'migration-20231017-pull-request' of https://gitlab.com/juan.quintela/qemu: (38 commits) migration/multifd: Clarify Error usage in multifd_channel_connect migration/multifd: Unify multifd_send_thread error paths migration/multifd: Remove direct "socket" references migration/ram: Merge save_zero_page functions migration/ram: Move xbzrle zero page handling into save_zero_page migration/ram: Stop passing QEMUFile around in save_zero_page migration/ram: Remove RAMState from xbzrle_cache_zero_page migration/ram: Refactor precopy ram loading code multifd: reset next_packet_len after sending pages multifd: fix counters in multifd_send_thread migration: check for rate_limit_max for RATE_LIMIT_DISABLED migration: Improve json and formatting migration/rdma: Remove all "ret" variables that are used only once migration/rdma: Declare for index variables local migration/rdma: Use i as for index instead of idx migration/rdma: Check sooner if we are in postcopy for save_page() migration/rdma: Remove qemu_ prefix from exported functions migration/rdma: Move rdma constants from qemu-file.h to rdma.h qemu-file: Remove QEMUFileHooks migration/rdma: Create rdma_control_save_page() ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
@@ -25,6 +25,7 @@ typedef struct SaveVMHandlers {
|
||||
* used to perform early checks.
|
||||
*/
|
||||
int (*save_prepare)(void *opaque, Error **errp);
|
||||
int (*save_setup)(QEMUFile *f, void *opaque);
|
||||
void (*save_cleanup)(void *opaque);
|
||||
int (*save_live_complete_postcopy)(QEMUFile *f, void *opaque);
|
||||
int (*save_live_complete_precopy)(QEMUFile *f, void *opaque);
|
||||
@@ -50,7 +51,6 @@ typedef struct SaveVMHandlers {
|
||||
int (*save_live_iterate)(QEMUFile *f, void *opaque);
|
||||
|
||||
/* This runs outside the iothread lock! */
|
||||
int (*save_setup)(QEMUFile *f, void *opaque);
|
||||
/* Note for save_live_pending:
|
||||
* must_precopy:
|
||||
* - must be migrated in precopy or in stopped state
|
||||
|
||||
@@ -1214,9 +1214,7 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque)
|
||||
DBMSaveState *s = &((DBMState *)opaque)->save;
|
||||
SaveBitmapState *dbms = NULL;
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
if (init_dirty_bitmap_migration(s) < 0) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1224,7 +1222,6 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque)
|
||||
send_bitmap_start(f, s, dbms);
|
||||
}
|
||||
qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
|
||||
qemu_mutex_unlock_iothread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -731,18 +731,13 @@ static int block_save_setup(QEMUFile *f, void *opaque)
|
||||
trace_migration_block_save("setup", block_mig_state.submitted,
|
||||
block_mig_state.transferred);
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
ret = init_blk_migration(f);
|
||||
if (ret < 0) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* start track dirty blocks */
|
||||
ret = set_dirty_tracking();
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -321,6 +321,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
||||
monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH),
|
||||
params->max_bandwidth);
|
||||
assert(params->has_avail_switchover_bandwidth);
|
||||
monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH),
|
||||
params->avail_switchover_bandwidth);
|
||||
assert(params->has_downtime_limit);
|
||||
monitor_printf(mon, "%s: %" PRIu64 " ms\n",
|
||||
MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT),
|
||||
@@ -574,6 +578,16 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
p->max_bandwidth = valuebw;
|
||||
break;
|
||||
case MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH:
|
||||
p->has_avail_switchover_bandwidth = true;
|
||||
ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw);
|
||||
if (ret < 0 || valuebw > INT64_MAX
|
||||
|| (size_t)valuebw != valuebw) {
|
||||
error_setg(&err, "Invalid size %s", valuestr);
|
||||
break;
|
||||
}
|
||||
p->avail_switchover_bandwidth = valuebw;
|
||||
break;
|
||||
case MIGRATION_PARAMETER_DOWNTIME_LIMIT:
|
||||
p->has_downtime_limit = true;
|
||||
visit_type_size(v, param, &p->downtime_limit, &err);
|
||||
|
||||
@@ -24,14 +24,15 @@ bool migration_rate_exceeded(QEMUFile *f)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t rate_limit_start = stat64_get(&mig_stats.rate_limit_start);
|
||||
uint64_t rate_limit_current = migration_transferred_bytes(f);
|
||||
uint64_t rate_limit_used = rate_limit_current - rate_limit_start;
|
||||
uint64_t rate_limit_max = stat64_get(&mig_stats.rate_limit_max);
|
||||
|
||||
uint64_t rate_limit_max = migration_rate_get();
|
||||
if (rate_limit_max == RATE_LIMIT_DISABLED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t rate_limit_start = stat64_get(&mig_stats.rate_limit_start);
|
||||
uint64_t rate_limit_current = migration_transferred_bytes(f);
|
||||
uint64_t rate_limit_used = rate_limit_current - rate_limit_start;
|
||||
|
||||
if (rate_limit_max > 0 && rate_limit_used > rate_limit_max) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+127
-80
@@ -99,7 +99,7 @@ static int migration_maybe_pause(MigrationState *s,
|
||||
int *current_active_state,
|
||||
int new_state);
|
||||
static void migrate_fd_cancel(MigrationState *s);
|
||||
static int await_return_path_close_on_source(MigrationState *s);
|
||||
static int close_return_path_on_source(MigrationState *s);
|
||||
|
||||
static bool migration_needs_multiple_sockets(void)
|
||||
{
|
||||
@@ -1191,7 +1191,7 @@ static void migrate_fd_cleanup(MigrationState *s)
|
||||
* We already cleaned up to_dst_file, so errors from the return
|
||||
* path might be due to that, ignore them.
|
||||
*/
|
||||
await_return_path_close_on_source(s);
|
||||
close_return_path_on_source(s);
|
||||
|
||||
assert(!migration_is_active(s));
|
||||
|
||||
@@ -1442,6 +1442,7 @@ int migrate_init(MigrationState *s, Error **errp)
|
||||
error_free(s->error);
|
||||
s->error = NULL;
|
||||
s->hostname = NULL;
|
||||
s->vmdesc = NULL;
|
||||
|
||||
migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP);
|
||||
|
||||
@@ -1451,6 +1452,7 @@ int migrate_init(MigrationState *s, Error **errp)
|
||||
s->iteration_initial_bytes = 0;
|
||||
s->threshold_size = 0;
|
||||
s->switchover_acked = false;
|
||||
s->rdma_migration = false;
|
||||
/*
|
||||
* set mig_stats compression_counters memory to zero for a
|
||||
* new migration
|
||||
@@ -2049,8 +2051,7 @@ static int open_return_path_on_source(MigrationState *ms)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0 if the RP was ok, otherwise there was an error on the RP */
|
||||
static int await_return_path_close_on_source(MigrationState *ms)
|
||||
static int close_return_path_on_source(MigrationState *ms)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -2317,90 +2318,65 @@ static int migration_maybe_pause(MigrationState *s,
|
||||
return s->state == new_state ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* migration_completion: Used by migration_thread when there's not much left.
|
||||
* The caller 'breaks' the loop when this returns.
|
||||
*
|
||||
* @s: Current migration state
|
||||
*/
|
||||
static void migration_completion(MigrationState *s)
|
||||
static int migration_completion_precopy(MigrationState *s,
|
||||
int *current_active_state)
|
||||
{
|
||||
int ret;
|
||||
int current_active_state = s->state;
|
||||
|
||||
if (s->state == MIGRATION_STATUS_ACTIVE) {
|
||||
qemu_mutex_lock_iothread();
|
||||
s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
|
||||
qemu_mutex_lock_iothread();
|
||||
s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
|
||||
|
||||
s->vm_old_state = runstate_get();
|
||||
global_state_store();
|
||||
s->vm_old_state = runstate_get();
|
||||
global_state_store();
|
||||
|
||||
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
||||
trace_migration_completion_vm_stop(ret);
|
||||
if (ret >= 0) {
|
||||
ret = migration_maybe_pause(s, ¤t_active_state,
|
||||
MIGRATION_STATUS_DEVICE);
|
||||
}
|
||||
if (ret >= 0) {
|
||||
/*
|
||||
* Inactivate disks except in COLO, and track that we
|
||||
* have done so in order to remember to reactivate
|
||||
* them if migration fails or is cancelled.
|
||||
*/
|
||||
s->block_inactive = !migrate_colo();
|
||||
migration_rate_set(RATE_LIMIT_DISABLED);
|
||||
ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false,
|
||||
s->block_inactive);
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
} else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
||||
trace_migration_completion_postcopy_end();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_savevm_state_complete_postcopy(s->to_dst_file);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
/*
|
||||
* Shutdown the postcopy fast path thread. This is only needed
|
||||
* when dest QEMU binary is old (7.1/7.2). QEMU 8.0+ doesn't need
|
||||
* this.
|
||||
*/
|
||||
if (migrate_postcopy_preempt() && s->preempt_pre_7_2) {
|
||||
postcopy_preempt_shutdown_file(s);
|
||||
}
|
||||
|
||||
trace_migration_completion_postcopy_end_after_complete();
|
||||
} else {
|
||||
goto fail;
|
||||
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
||||
trace_migration_completion_vm_stop(ret);
|
||||
if (ret < 0) {
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (await_return_path_close_on_source(s)) {
|
||||
goto fail;
|
||||
ret = migration_maybe_pause(s, current_active_state,
|
||||
MIGRATION_STATUS_DEVICE);
|
||||
if (ret < 0) {
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (qemu_file_get_error(s->to_dst_file)) {
|
||||
trace_migration_completion_file_err();
|
||||
goto fail;
|
||||
/*
|
||||
* Inactivate disks except in COLO, and track that we have done so in order
|
||||
* to remember to reactivate them if migration fails or is cancelled.
|
||||
*/
|
||||
s->block_inactive = !migrate_colo();
|
||||
migration_rate_set(RATE_LIMIT_DISABLED);
|
||||
ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false,
|
||||
s->block_inactive);
|
||||
out_unlock:
|
||||
qemu_mutex_unlock_iothread();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void migration_completion_postcopy(MigrationState *s)
|
||||
{
|
||||
trace_migration_completion_postcopy_end();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_savevm_state_complete_postcopy(s->to_dst_file);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
/*
|
||||
* Shutdown the postcopy fast path thread. This is only needed when dest
|
||||
* QEMU binary is old (7.1/7.2). QEMU 8.0+ doesn't need this.
|
||||
*/
|
||||
if (migrate_postcopy_preempt() && s->preempt_pre_7_2) {
|
||||
postcopy_preempt_shutdown_file(s);
|
||||
}
|
||||
|
||||
if (migrate_colo() && s->state == MIGRATION_STATUS_ACTIVE) {
|
||||
/* COLO does not support postcopy */
|
||||
migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
|
||||
MIGRATION_STATUS_COLO);
|
||||
} else {
|
||||
migrate_set_state(&s->state, current_active_state,
|
||||
MIGRATION_STATUS_COMPLETED);
|
||||
}
|
||||
trace_migration_completion_postcopy_end_after_complete();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
static void migration_completion_failed(MigrationState *s,
|
||||
int current_active_state)
|
||||
{
|
||||
if (s->block_inactive && (s->state == MIGRATION_STATUS_ACTIVE ||
|
||||
s->state == MIGRATION_STATUS_DEVICE)) {
|
||||
/*
|
||||
@@ -2423,6 +2399,53 @@ fail:
|
||||
MIGRATION_STATUS_FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* migration_completion: Used by migration_thread when there's not much left.
|
||||
* The caller 'breaks' the loop when this returns.
|
||||
*
|
||||
* @s: Current migration state
|
||||
*/
|
||||
static void migration_completion(MigrationState *s)
|
||||
{
|
||||
int ret = 0;
|
||||
int current_active_state = s->state;
|
||||
|
||||
if (s->state == MIGRATION_STATUS_ACTIVE) {
|
||||
ret = migration_completion_precopy(s, ¤t_active_state);
|
||||
} else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
||||
migration_completion_postcopy(s);
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (close_return_path_on_source(s)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (qemu_file_get_error(s->to_dst_file)) {
|
||||
trace_migration_completion_file_err();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (migrate_colo() && s->state == MIGRATION_STATUS_ACTIVE) {
|
||||
/* COLO does not support postcopy */
|
||||
migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
|
||||
MIGRATION_STATUS_COLO);
|
||||
} else {
|
||||
migrate_set_state(&s->state, current_active_state,
|
||||
MIGRATION_STATUS_COMPLETED);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
migration_completion_failed(s, current_active_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* bg_migration_completion: Used by bg_migration_thread when after all the
|
||||
* RAM has been saved. The caller 'breaks' the loop when this returns.
|
||||
@@ -2563,7 +2586,7 @@ static MigThrError postcopy_pause(MigrationState *s)
|
||||
* path and just wait for the thread to finish. It will be
|
||||
* re-created when we resume.
|
||||
*/
|
||||
await_return_path_close_on_source(s);
|
||||
close_return_path_on_source(s);
|
||||
|
||||
migrate_set_state(&s->state, s->state,
|
||||
MIGRATION_STATUS_POSTCOPY_PAUSED);
|
||||
@@ -2689,17 +2712,33 @@ static void migration_update_counters(MigrationState *s,
|
||||
{
|
||||
uint64_t transferred, transferred_pages, time_spent;
|
||||
uint64_t current_bytes; /* bytes transferred since the beginning */
|
||||
uint64_t switchover_bw;
|
||||
/* Expected bandwidth when switching over to destination QEMU */
|
||||
double expected_bw_per_ms;
|
||||
double bandwidth;
|
||||
|
||||
if (current_time < s->iteration_start_time + BUFFER_DELAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
switchover_bw = migrate_avail_switchover_bandwidth();
|
||||
current_bytes = migration_transferred_bytes(s->to_dst_file);
|
||||
transferred = current_bytes - s->iteration_initial_bytes;
|
||||
time_spent = current_time - s->iteration_start_time;
|
||||
bandwidth = (double)transferred / time_spent;
|
||||
s->threshold_size = bandwidth * migrate_downtime_limit();
|
||||
|
||||
if (switchover_bw) {
|
||||
/*
|
||||
* If the user specified a switchover bandwidth, let's trust the
|
||||
* user so that can be more accurate than what we estimated.
|
||||
*/
|
||||
expected_bw_per_ms = switchover_bw / 1000;
|
||||
} else {
|
||||
/* If the user doesn't specify bandwidth, we use the estimated */
|
||||
expected_bw_per_ms = bandwidth;
|
||||
}
|
||||
|
||||
s->threshold_size = expected_bw_per_ms * migrate_downtime_limit();
|
||||
|
||||
s->mbps = (((double) transferred * 8.0) /
|
||||
((double) time_spent / 1000.0)) / 1000.0 / 1000.0;
|
||||
@@ -2716,7 +2755,7 @@ static void migration_update_counters(MigrationState *s,
|
||||
if (stat64_get(&mig_stats.dirty_pages_rate) &&
|
||||
transferred > 10000) {
|
||||
s->expected_downtime =
|
||||
stat64_get(&mig_stats.dirty_bytes_last_sync) / bandwidth;
|
||||
stat64_get(&mig_stats.dirty_bytes_last_sync) / expected_bw_per_ms;
|
||||
}
|
||||
|
||||
migration_rate_reset(s->to_dst_file);
|
||||
@@ -2724,7 +2763,9 @@ static void migration_update_counters(MigrationState *s,
|
||||
update_iteration_initial_status(s);
|
||||
|
||||
trace_migrate_transferred(transferred, time_spent,
|
||||
bandwidth, s->threshold_size);
|
||||
/* Both in unit bytes/ms */
|
||||
bandwidth, switchover_bw / 1000,
|
||||
s->threshold_size);
|
||||
}
|
||||
|
||||
static bool migration_can_switchover(MigrationState *s)
|
||||
@@ -2980,7 +3021,9 @@ static void *migration_thread(void *opaque)
|
||||
object_ref(OBJECT(s));
|
||||
update_iteration_initial_status(s);
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_savevm_state_header(s->to_dst_file);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
/*
|
||||
* If we opened the return path, we need to make sure dst has it
|
||||
@@ -3008,7 +3051,9 @@ static void *migration_thread(void *opaque)
|
||||
qemu_savevm_send_colo_enable(s->to_dst_file);
|
||||
}
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_savevm_state_setup(s->to_dst_file);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,
|
||||
MIGRATION_STATUS_ACTIVE);
|
||||
@@ -3119,8 +3164,10 @@ static void *bg_migration_thread(void *opaque)
|
||||
ram_write_tracking_prepare();
|
||||
#endif
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_savevm_state_header(s->to_dst_file);
|
||||
qemu_savevm_state_setup(s->to_dst_file);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,
|
||||
MIGRATION_STATUS_ACTIVE);
|
||||
|
||||
@@ -294,7 +294,7 @@ struct MigrationState {
|
||||
/*
|
||||
* The final stage happens when the remaining data is smaller than
|
||||
* this threshold; it's calculated from the requested downtime and
|
||||
* measured bandwidth
|
||||
* measured bandwidth, or avail-switchover-bandwidth if specified.
|
||||
*/
|
||||
int64_t threshold_size;
|
||||
|
||||
@@ -469,6 +469,8 @@ struct MigrationState {
|
||||
* switchover has been received.
|
||||
*/
|
||||
bool switchover_acked;
|
||||
/* Is this a rdma migration */
|
||||
bool rdma_migration;
|
||||
};
|
||||
|
||||
void migrate_set_state(int *state, int old_state, int new_state);
|
||||
|
||||
+51
-50
@@ -510,6 +510,11 @@ static void multifd_send_terminate_threads(Error *err)
|
||||
}
|
||||
}
|
||||
|
||||
static int multifd_send_channel_destroy(QIOChannel *send)
|
||||
{
|
||||
return socket_send_channel_destroy(send);
|
||||
}
|
||||
|
||||
void multifd_save_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
@@ -532,7 +537,7 @@ void multifd_save_cleanup(void)
|
||||
if (p->registered_yank) {
|
||||
migration_ioc_unregister_yank(p->c);
|
||||
}
|
||||
socket_send_channel_destroy(p->c);
|
||||
multifd_send_channel_destroy(p->c);
|
||||
p->c = NULL;
|
||||
qemu_mutex_destroy(&p->mutex);
|
||||
qemu_sem_destroy(&p->sem);
|
||||
@@ -714,8 +719,6 @@ static void *multifd_send_thread(void *opaque)
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
stat64_add(&mig_stats.multifd_bytes, p->packet_len);
|
||||
stat64_add(&mig_stats.transferred, p->packet_len);
|
||||
} else {
|
||||
/* Send header using the same writev call */
|
||||
p->iov[0].iov_len = p->packet_len;
|
||||
@@ -728,8 +731,11 @@ static void *multifd_send_thread(void *opaque)
|
||||
break;
|
||||
}
|
||||
|
||||
stat64_add(&mig_stats.multifd_bytes, p->next_packet_size);
|
||||
stat64_add(&mig_stats.transferred, p->next_packet_size);
|
||||
stat64_add(&mig_stats.multifd_bytes,
|
||||
p->next_packet_size + p->packet_len);
|
||||
stat64_add(&mig_stats.transferred,
|
||||
p->next_packet_size + p->packet_len);
|
||||
p->next_packet_size = 0;
|
||||
qemu_mutex_lock(&p->mutex);
|
||||
p->pending_job--;
|
||||
qemu_mutex_unlock(&p->mutex);
|
||||
@@ -747,19 +753,13 @@ static void *multifd_send_thread(void *opaque)
|
||||
}
|
||||
|
||||
out:
|
||||
if (local_err) {
|
||||
if (ret) {
|
||||
assert(local_err);
|
||||
trace_multifd_send_error(p->id);
|
||||
multifd_send_terminate_threads(local_err);
|
||||
error_free(local_err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Error happen, I will exit, but I can't just leave, tell
|
||||
* who pay attention to me.
|
||||
*/
|
||||
if (ret != 0) {
|
||||
qemu_sem_post(&p->sem_sync);
|
||||
qemu_sem_post(&multifd_send_state->channels_ready);
|
||||
error_free(local_err);
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&p->mutex);
|
||||
@@ -775,7 +775,7 @@ out:
|
||||
|
||||
static bool multifd_channel_connect(MultiFDSendParams *p,
|
||||
QIOChannel *ioc,
|
||||
Error *error);
|
||||
Error **errp);
|
||||
|
||||
static void multifd_tls_outgoing_handshake(QIOTask *task,
|
||||
gpointer opaque)
|
||||
@@ -784,21 +784,22 @@ static void multifd_tls_outgoing_handshake(QIOTask *task,
|
||||
QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task));
|
||||
Error *err = NULL;
|
||||
|
||||
if (qio_task_propagate_error(task, &err)) {
|
||||
trace_multifd_tls_outgoing_handshake_error(ioc, error_get_pretty(err));
|
||||
} else {
|
||||
if (!qio_task_propagate_error(task, &err)) {
|
||||
trace_multifd_tls_outgoing_handshake_complete(ioc);
|
||||
if (multifd_channel_connect(p, ioc, &err)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!multifd_channel_connect(p, ioc, err)) {
|
||||
/*
|
||||
* Error happen, mark multifd_send_thread status as 'quit' although it
|
||||
* is not created, and then tell who pay attention to me.
|
||||
*/
|
||||
p->quit = true;
|
||||
qemu_sem_post(&multifd_send_state->channels_ready);
|
||||
qemu_sem_post(&p->sem_sync);
|
||||
}
|
||||
trace_multifd_tls_outgoing_handshake_error(ioc, error_get_pretty(err));
|
||||
|
||||
/*
|
||||
* Error happen, mark multifd_send_thread status as 'quit' although it
|
||||
* is not created, and then tell who pay attention to me.
|
||||
*/
|
||||
p->quit = true;
|
||||
qemu_sem_post(&multifd_send_state->channels_ready);
|
||||
qemu_sem_post(&p->sem_sync);
|
||||
}
|
||||
|
||||
static void *multifd_tls_handshake_thread(void *opaque)
|
||||
@@ -814,7 +815,7 @@ static void *multifd_tls_handshake_thread(void *opaque)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void multifd_tls_channel_connect(MultiFDSendParams *p,
|
||||
static bool multifd_tls_channel_connect(MultiFDSendParams *p,
|
||||
QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -824,7 +825,7 @@ static void multifd_tls_channel_connect(MultiFDSendParams *p,
|
||||
|
||||
tioc = migration_tls_client_create(ioc, hostname, errp);
|
||||
if (!tioc) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
object_unref(OBJECT(ioc));
|
||||
@@ -834,31 +835,25 @@ static void multifd_tls_channel_connect(MultiFDSendParams *p,
|
||||
qemu_thread_create(&p->thread, "multifd-tls-handshake-worker",
|
||||
multifd_tls_handshake_thread, p,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool multifd_channel_connect(MultiFDSendParams *p,
|
||||
QIOChannel *ioc,
|
||||
Error *error)
|
||||
Error **errp)
|
||||
{
|
||||
trace_multifd_set_outgoing_channel(
|
||||
ioc, object_get_typename(OBJECT(ioc)),
|
||||
migrate_get_current()->hostname, error);
|
||||
migrate_get_current()->hostname);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
if (migrate_channel_requires_tls_upgrade(ioc)) {
|
||||
multifd_tls_channel_connect(p, ioc, &error);
|
||||
if (!error) {
|
||||
/*
|
||||
* tls_channel_connect will call back to this
|
||||
* function after the TLS handshake,
|
||||
* so we mustn't call multifd_send_thread until then
|
||||
*/
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* tls_channel_connect will call back to this
|
||||
* function after the TLS handshake,
|
||||
* so we mustn't call multifd_send_thread until then
|
||||
*/
|
||||
return multifd_tls_channel_connect(p, ioc, errp);
|
||||
|
||||
} else {
|
||||
migration_ioc_register_yank(ioc);
|
||||
p->registered_yank = true;
|
||||
@@ -889,20 +884,26 @@ static void multifd_new_send_channel_cleanup(MultiFDSendParams *p,
|
||||
static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
|
||||
{
|
||||
MultiFDSendParams *p = opaque;
|
||||
QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task));
|
||||
QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task));
|
||||
Error *local_err = NULL;
|
||||
|
||||
trace_multifd_new_send_channel_async(p->id);
|
||||
if (!qio_task_propagate_error(task, &local_err)) {
|
||||
p->c = sioc;
|
||||
p->c = ioc;
|
||||
qio_channel_set_delay(p->c, false);
|
||||
p->running = true;
|
||||
if (multifd_channel_connect(p, sioc, local_err)) {
|
||||
if (multifd_channel_connect(p, ioc, &local_err)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
multifd_new_send_channel_cleanup(p, sioc, local_err);
|
||||
trace_multifd_new_send_channel_async_error(p->id, local_err);
|
||||
multifd_new_send_channel_cleanup(p, ioc, local_err);
|
||||
}
|
||||
|
||||
static void multifd_new_send_channel_create(gpointer opaque)
|
||||
{
|
||||
socket_send_channel_create(multifd_new_send_channel_async, opaque);
|
||||
}
|
||||
|
||||
int multifd_save_setup(Error **errp)
|
||||
@@ -951,7 +952,7 @@ int multifd_save_setup(Error **errp)
|
||||
p->write_flags = 0;
|
||||
}
|
||||
|
||||
socket_send_channel_create(multifd_new_send_channel_async, p);
|
||||
multifd_new_send_channel_create(p);
|
||||
}
|
||||
|
||||
for (i = 0; i < thread_count; i++) {
|
||||
|
||||
@@ -125,6 +125,8 @@ Property migration_properties[] = {
|
||||
parameters.cpu_throttle_tailslow, false),
|
||||
DEFINE_PROP_SIZE("x-max-bandwidth", MigrationState,
|
||||
parameters.max_bandwidth, MAX_THROTTLE),
|
||||
DEFINE_PROP_SIZE("avail-switchover-bandwidth", MigrationState,
|
||||
parameters.avail_switchover_bandwidth, 0),
|
||||
DEFINE_PROP_UINT64("x-downtime-limit", MigrationState,
|
||||
parameters.downtime_limit,
|
||||
DEFAULT_MIGRATE_SET_DOWNTIME),
|
||||
@@ -376,6 +378,13 @@ bool migrate_postcopy(void)
|
||||
return migrate_postcopy_ram() || migrate_dirty_bitmaps();
|
||||
}
|
||||
|
||||
bool migrate_rdma(void)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
|
||||
return s->rdma_migration;
|
||||
}
|
||||
|
||||
bool migrate_tls(void)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
@@ -780,6 +789,13 @@ uint64_t migrate_max_bandwidth(void)
|
||||
return s->parameters.max_bandwidth;
|
||||
}
|
||||
|
||||
uint64_t migrate_avail_switchover_bandwidth(void)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
|
||||
return s->parameters.avail_switchover_bandwidth;
|
||||
}
|
||||
|
||||
uint64_t migrate_max_postcopy_bandwidth(void)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
@@ -917,6 +933,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
|
||||
s->parameters.tls_authz : "");
|
||||
params->has_max_bandwidth = true;
|
||||
params->max_bandwidth = s->parameters.max_bandwidth;
|
||||
params->has_avail_switchover_bandwidth = true;
|
||||
params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth;
|
||||
params->has_downtime_limit = true;
|
||||
params->downtime_limit = s->parameters.downtime_limit;
|
||||
params->has_x_checkpoint_delay = true;
|
||||
@@ -1056,6 +1074,15 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params->has_avail_switchover_bandwidth &&
|
||||
(params->avail_switchover_bandwidth > SIZE_MAX)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"avail_switchover_bandwidth",
|
||||
"an integer in the range of 0 to "stringify(SIZE_MAX)
|
||||
" bytes/second");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params->has_downtime_limit &&
|
||||
(params->downtime_limit > MAX_MIGRATE_DOWNTIME)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
@@ -1225,6 +1252,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
||||
dest->max_bandwidth = params->max_bandwidth;
|
||||
}
|
||||
|
||||
if (params->has_avail_switchover_bandwidth) {
|
||||
dest->avail_switchover_bandwidth = params->avail_switchover_bandwidth;
|
||||
}
|
||||
|
||||
if (params->has_downtime_limit) {
|
||||
dest->downtime_limit = params->downtime_limit;
|
||||
}
|
||||
@@ -1341,6 +1372,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (params->has_avail_switchover_bandwidth) {
|
||||
s->parameters.avail_switchover_bandwidth = params->avail_switchover_bandwidth;
|
||||
}
|
||||
|
||||
if (params->has_downtime_limit) {
|
||||
s->parameters.downtime_limit = params->downtime_limit;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ bool migrate_zero_copy_send(void);
|
||||
|
||||
bool migrate_multifd_flush_after_each_section(void);
|
||||
bool migrate_postcopy(void);
|
||||
bool migrate_rdma(void);
|
||||
bool migrate_tls(void);
|
||||
|
||||
/* capabilities helpers */
|
||||
@@ -80,6 +81,7 @@ int migrate_decompress_threads(void);
|
||||
uint64_t migrate_downtime_limit(void);
|
||||
uint8_t migrate_max_cpu_throttle(void);
|
||||
uint64_t migrate_max_bandwidth(void);
|
||||
uint64_t migrate_avail_switchover_bandwidth(void);
|
||||
uint64_t migrate_max_postcopy_bandwidth(void);
|
||||
int migrate_multifd_channels(void);
|
||||
MultiFDCompression migrate_multifd_compression(void);
|
||||
|
||||
+1
-60
@@ -32,12 +32,12 @@
|
||||
#include "trace.h"
|
||||
#include "options.h"
|
||||
#include "qapi/error.h"
|
||||
#include "rdma.h"
|
||||
|
||||
#define IO_BUF_SIZE 32768
|
||||
#define MAX_IOV_SIZE MIN_CONST(IOV_MAX, 64)
|
||||
|
||||
struct QEMUFile {
|
||||
const QEMUFileHooks *hooks;
|
||||
QIOChannel *ioc;
|
||||
bool is_writable;
|
||||
|
||||
@@ -132,11 +132,6 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc)
|
||||
return qemu_file_new_impl(ioc, false);
|
||||
}
|
||||
|
||||
void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks)
|
||||
{
|
||||
f->hooks = hooks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get last error for stream f with optional Error*
|
||||
*
|
||||
@@ -297,60 +292,6 @@ void qemu_fflush(QEMUFile *f)
|
||||
f->iovcnt = 0;
|
||||
}
|
||||
|
||||
void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (f->hooks && f->hooks->before_ram_iterate) {
|
||||
ret = f->hooks->before_ram_iterate(f, flags, NULL);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (f->hooks && f->hooks->after_ram_iterate) {
|
||||
ret = f->hooks->after_ram_iterate(f, flags, NULL);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data)
|
||||
{
|
||||
if (f->hooks && f->hooks->hook_ram_load) {
|
||||
int ret = f->hooks->hook_ram_load(f, flags, data);
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
|
||||
ram_addr_t offset, size_t size)
|
||||
{
|
||||
if (f->hooks && f->hooks->save_page) {
|
||||
int ret = f->hooks->save_page(f, block_offset, offset, size);
|
||||
/*
|
||||
* RAM_SAVE_CONTROL_* are negative values
|
||||
*/
|
||||
if (ret != RAM_SAVE_CONTROL_DELAYED &&
|
||||
ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||
if (ret < 0) {
|
||||
qemu_file_set_error(f, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return RAM_SAVE_CONTROL_NOT_SUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to fill the buffer from the underlying file
|
||||
* Returns the number of bytes read, or negative value for an error.
|
||||
|
||||
@@ -29,41 +29,8 @@
|
||||
#include "exec/cpu-common.h"
|
||||
#include "io/channel.h"
|
||||
|
||||
/*
|
||||
* This function provides hooks around different
|
||||
* stages of RAM migration.
|
||||
* 'data' is call specific data associated with the 'flags' value
|
||||
*/
|
||||
typedef int (QEMURamHookFunc)(QEMUFile *f, uint64_t flags, void *data);
|
||||
|
||||
/*
|
||||
* Constants used by ram_control_* hooks
|
||||
*/
|
||||
#define RAM_CONTROL_SETUP 0
|
||||
#define RAM_CONTROL_ROUND 1
|
||||
#define RAM_CONTROL_HOOK 2
|
||||
#define RAM_CONTROL_FINISH 3
|
||||
#define RAM_CONTROL_BLOCK_REG 4
|
||||
|
||||
/*
|
||||
* This function allows override of where the RAM page
|
||||
* is saved (such as RDMA, for example.)
|
||||
*/
|
||||
typedef int (QEMURamSaveFunc)(QEMUFile *f,
|
||||
ram_addr_t block_offset,
|
||||
ram_addr_t offset,
|
||||
size_t size);
|
||||
|
||||
typedef struct QEMUFileHooks {
|
||||
QEMURamHookFunc *before_ram_iterate;
|
||||
QEMURamHookFunc *after_ram_iterate;
|
||||
QEMURamHookFunc *hook_ram_load;
|
||||
QEMURamSaveFunc *save_page;
|
||||
} QEMUFileHooks;
|
||||
|
||||
QEMUFile *qemu_file_new_input(QIOChannel *ioc);
|
||||
QEMUFile *qemu_file_new_output(QIOChannel *ioc);
|
||||
void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks);
|
||||
int qemu_fclose(QEMUFile *f);
|
||||
|
||||
/*
|
||||
@@ -127,22 +94,6 @@ void qemu_fflush(QEMUFile *f);
|
||||
void qemu_file_set_blocking(QEMUFile *f, bool block);
|
||||
int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size);
|
||||
|
||||
void ram_control_before_iterate(QEMUFile *f, uint64_t flags);
|
||||
void ram_control_after_iterate(QEMUFile *f, uint64_t flags);
|
||||
void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data);
|
||||
|
||||
/* Whenever this is found in the data stream, the flags
|
||||
* will be passed to ram_control_load_hook in the incoming-migration
|
||||
* side. This lets before_ram_iterate/after_ram_iterate add
|
||||
* transport-specific sections to the RAM migration data.
|
||||
*/
|
||||
#define RAM_SAVE_FLAG_HOOK 0x80
|
||||
|
||||
#define RAM_SAVE_CONTROL_NOT_SUPP -1000
|
||||
#define RAM_SAVE_CONTROL_DELAYED -2000
|
||||
|
||||
int ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
|
||||
ram_addr_t offset, size_t size);
|
||||
QIOChannel *qemu_file_get_ioc(QEMUFile *file);
|
||||
|
||||
#endif
|
||||
|
||||
+169
-137
File diff suppressed because it is too large
Load Diff
+111
-148
File diff suppressed because it is too large
Load Diff
@@ -17,9 +17,51 @@
|
||||
#ifndef QEMU_MIGRATION_RDMA_H
|
||||
#define QEMU_MIGRATION_RDMA_H
|
||||
|
||||
#include "exec/memory.h"
|
||||
|
||||
void rdma_start_outgoing_migration(void *opaque, const char *host_port,
|
||||
Error **errp);
|
||||
|
||||
void rdma_start_incoming_migration(const char *host_port, Error **errp);
|
||||
|
||||
/*
|
||||
* Constants used by rdma return codes
|
||||
*/
|
||||
#define RAM_CONTROL_SETUP 0
|
||||
#define RAM_CONTROL_ROUND 1
|
||||
#define RAM_CONTROL_FINISH 3
|
||||
|
||||
/*
|
||||
* Whenever this is found in the data stream, the flags
|
||||
* will be passed to rdma functions in the incoming-migration
|
||||
* side.
|
||||
*/
|
||||
#define RAM_SAVE_FLAG_HOOK 0x80
|
||||
|
||||
#define RAM_SAVE_CONTROL_NOT_SUPP -1000
|
||||
#define RAM_SAVE_CONTROL_DELAYED -2000
|
||||
|
||||
#ifdef CONFIG_RDMA
|
||||
int rdma_registration_handle(QEMUFile *f);
|
||||
int rdma_registration_start(QEMUFile *f, uint64_t flags);
|
||||
int rdma_registration_stop(QEMUFile *f, uint64_t flags);
|
||||
int rdma_block_notification_handle(QEMUFile *f, const char *name);
|
||||
int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset,
|
||||
ram_addr_t offset, size_t size);
|
||||
#else
|
||||
static inline
|
||||
int rdma_registration_handle(QEMUFile *f) { return 0; }
|
||||
static inline
|
||||
int rdma_registration_start(QEMUFile *f, uint64_t flags) { return 0; }
|
||||
static inline
|
||||
int rdma_registration_stop(QEMUFile *f, uint64_t flags) { return 0; }
|
||||
static inline
|
||||
int rdma_block_notification_handle(QEMUFile *f, const char *name) { return 0; }
|
||||
static inline
|
||||
int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset,
|
||||
ram_addr_t offset, size_t size)
|
||||
{
|
||||
return RAM_SAVE_CONTROL_NOT_SUPP;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+16
-6
@@ -1217,13 +1217,27 @@ void qemu_savevm_non_migratable_list(strList **reasons)
|
||||
|
||||
void qemu_savevm_state_header(QEMUFile *f)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
|
||||
s->vmdesc = json_writer_new(false);
|
||||
|
||||
trace_savevm_state_header();
|
||||
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
|
||||
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
|
||||
|
||||
if (migrate_get_current()->send_configuration) {
|
||||
if (s->send_configuration) {
|
||||
qemu_put_byte(f, QEMU_VM_CONFIGURATION);
|
||||
vmstate_save_state(f, &vmstate_configuration, &savevm_state, 0);
|
||||
|
||||
/*
|
||||
* This starts the main json object and is paired with the
|
||||
* json_writer_end_object in
|
||||
* qemu_savevm_state_complete_precopy_non_iterable
|
||||
*/
|
||||
json_writer_start_object(s->vmdesc, NULL);
|
||||
|
||||
json_writer_start_object(s->vmdesc, "configuration");
|
||||
vmstate_save_state(f, &vmstate_configuration, &savevm_state, s->vmdesc);
|
||||
json_writer_end_object(s->vmdesc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,8 +1286,6 @@ void qemu_savevm_state_setup(QEMUFile *f)
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ms->vmdesc = json_writer_new(false);
|
||||
json_writer_start_object(ms->vmdesc, NULL);
|
||||
json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size());
|
||||
json_writer_start_array(ms->vmdesc, "devices");
|
||||
|
||||
@@ -1660,10 +1672,8 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
|
||||
}
|
||||
ms->to_dst_file = f;
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
qemu_savevm_state_header(f);
|
||||
qemu_savevm_state_setup(f);
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
while (qemu_file_get_error(f) == 0) {
|
||||
if (qemu_savevm_state_iterate(f, false) > 0) {
|
||||
|
||||
+17
-16
@@ -125,6 +125,7 @@ postcopy_preempt_reset_channel(void) ""
|
||||
|
||||
# multifd.c
|
||||
multifd_new_send_channel_async(uint8_t id) "channel %u"
|
||||
multifd_new_send_channel_async_error(uint8_t id, void *err) "channel=%u err=%p"
|
||||
multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u"
|
||||
multifd_recv_new_channel(uint8_t id) "channel %u"
|
||||
multifd_recv_sync_main(long packet_num) "packet num %ld"
|
||||
@@ -144,7 +145,7 @@ multifd_send_thread_start(uint8_t id) "%u"
|
||||
multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s"
|
||||
multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s"
|
||||
multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p"
|
||||
multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p"
|
||||
multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s"
|
||||
|
||||
# migration.c
|
||||
await_return_path_close_on_source_close(void) ""
|
||||
@@ -186,7 +187,7 @@ source_return_path_thread_shut(uint32_t val) "0x%x"
|
||||
source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32
|
||||
source_return_path_thread_switchover_acked(void) ""
|
||||
migration_thread_low_pending(uint64_t pending) "%" PRIu64
|
||||
migrate_transferred(uint64_t transferred, uint64_t time_spent, uint64_t bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " max_size %" PRId64
|
||||
migrate_transferred(uint64_t transferred, uint64_t time_spent, uint64_t bandwidth, uint64_t avail_bw, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " switchover_bw %" PRIu64 " max_size %" PRId64
|
||||
process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d"
|
||||
process_incoming_migration_co_postcopy_end_main(void) ""
|
||||
postcopy_preempt_enabled(bool value) "%d"
|
||||
@@ -231,20 +232,6 @@ qemu_rdma_post_send_control(const char *desc) "CONTROL: sending %s.."
|
||||
qemu_rdma_register_and_get_keys(uint64_t len, void *start) "Registering %" PRIu64 " bytes @ %p"
|
||||
qemu_rdma_register_odp_mr(const char *name) "Try to register On-Demand Paging memory region: %s"
|
||||
qemu_rdma_advise_mr(const char *name, uint32_t len, uint64_t addr, const char *res) "Try to advise block %s prefetch at %" PRIu32 "@0x%" PRIx64 ": %s"
|
||||
qemu_rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64
|
||||
qemu_rdma_registration_handle_finished(void) ""
|
||||
qemu_rdma_registration_handle_ram_blocks(void) ""
|
||||
qemu_rdma_registration_handle_ram_blocks_loop(const char *name, uint64_t offset, uint64_t length, void *local_host_addr, unsigned int src_index) "%s: @0x%" PRIx64 "/%" PRIu64 " host:@%p src_index: %u"
|
||||
qemu_rdma_registration_handle_register(int requests) "%d requests"
|
||||
qemu_rdma_registration_handle_register_loop(int req, int index, uint64_t addr, uint64_t chunks) "Registration request (%d): index %d, current_addr %" PRIu64 " chunks: %" PRIu64
|
||||
qemu_rdma_registration_handle_register_rkey(int rkey) "0x%x"
|
||||
qemu_rdma_registration_handle_unregister(int requests) "%d requests"
|
||||
qemu_rdma_registration_handle_unregister_loop(int count, int index, uint64_t chunk) "Unregistration request (%d): index %d, chunk %" PRIu64
|
||||
qemu_rdma_registration_handle_unregister_success(uint64_t chunk) "%" PRIu64
|
||||
qemu_rdma_registration_handle_wait(void) ""
|
||||
qemu_rdma_registration_start(uint64_t flags) "%" PRIu64
|
||||
qemu_rdma_registration_stop(uint64_t flags) "%" PRIu64
|
||||
qemu_rdma_registration_stop_ram(void) ""
|
||||
qemu_rdma_resolve_host_trying(const char *host, const char *ip) "Trying %s => %s"
|
||||
qemu_rdma_signal_unregister_append(uint64_t chunk, int pos) "Appending unregister chunk %" PRIu64 " at position %d"
|
||||
qemu_rdma_signal_unregister_already(uint64_t chunk) "Unregister chunk %" PRIu64 " already in queue"
|
||||
@@ -263,6 +250,20 @@ qemu_rdma_write_one_zero(uint64_t chunk, int len, int index, int64_t offset) "En
|
||||
rdma_add_block(const char *block_name, int block, uint64_t addr, uint64_t offset, uint64_t len, uint64_t end, uint64_t bits, int chunks) "Added Block: '%s':%d, addr: %" PRIu64 ", offset: %" PRIu64 " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d"
|
||||
rdma_block_notification_handle(const char *name, int index) "%s at %d"
|
||||
rdma_delete_block(void *block, uint64_t addr, uint64_t offset, uint64_t len, uint64_t end, uint64_t bits, int chunks) "Deleted Block: %p, addr: %" PRIu64 ", offset: %" PRIu64 " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d"
|
||||
rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64
|
||||
rdma_registration_handle_finished(void) ""
|
||||
rdma_registration_handle_ram_blocks(void) ""
|
||||
rdma_registration_handle_ram_blocks_loop(const char *name, uint64_t offset, uint64_t length, void *local_host_addr, unsigned int src_index) "%s: @0x%" PRIx64 "/%" PRIu64 " host:@%p src_index: %u"
|
||||
rdma_registration_handle_register(int requests) "%d requests"
|
||||
rdma_registration_handle_register_loop(int req, int index, uint64_t addr, uint64_t chunks) "Registration request (%d): index %d, current_addr %" PRIu64 " chunks: %" PRIu64
|
||||
rdma_registration_handle_register_rkey(int rkey) "0x%x"
|
||||
rdma_registration_handle_unregister(int requests) "%d requests"
|
||||
rdma_registration_handle_unregister_loop(int count, int index, uint64_t chunk) "Unregistration request (%d): index %d, chunk %" PRIu64
|
||||
rdma_registration_handle_unregister_success(uint64_t chunk) "%" PRIu64
|
||||
rdma_registration_handle_wait(void) ""
|
||||
rdma_registration_start(uint64_t flags) "%" PRIu64
|
||||
rdma_registration_stop(uint64_t flags) "%" PRIu64
|
||||
rdma_registration_stop_ram(void) ""
|
||||
rdma_start_incoming_migration(void) ""
|
||||
rdma_start_incoming_migration_after_dest_init(void) ""
|
||||
rdma_start_incoming_migration_after_rdma_listen(void) ""
|
||||
|
||||
+36
-5
@@ -73,7 +73,7 @@
|
||||
{ 'struct': 'MigrationStats',
|
||||
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
|
||||
'duplicate': 'int',
|
||||
'skipped': { 'type': 'int', 'features': ['deprecated'] },
|
||||
'skipped': { 'type': 'int', 'features': [ 'deprecated' ] },
|
||||
'normal': 'int',
|
||||
'normal-bytes': 'int', 'dirty-pages-rate': 'int',
|
||||
'mbps': 'number', 'dirty-sync-count': 'int',
|
||||
@@ -440,10 +440,9 @@
|
||||
# compress and xbzrle are both on, compress only takes effect in
|
||||
# the ram bulk stage, after that, it will be disabled and only
|
||||
# xbzrle takes effect, this can help to minimize migration
|
||||
# traffic. The feature is disabled by default. (since 2.4 )
|
||||
# traffic. The feature is disabled by default. (since 2.4)
|
||||
#
|
||||
# @events: generate events for each migration state change (since 2.4
|
||||
# )
|
||||
# @events: generate events for each migration state change (since 2.4)
|
||||
#
|
||||
# @auto-converge: If enabled, QEMU will automatically throttle down
|
||||
# the guest to speed up convergence of RAM migration. (since 1.6)
|
||||
@@ -758,6 +757,16 @@
|
||||
# @max-bandwidth: to set maximum speed for migration. maximum speed
|
||||
# in bytes per second. (Since 2.8)
|
||||
#
|
||||
# @avail-switchover-bandwidth: to set the available bandwidth that
|
||||
# migration can use during switchover phase. NOTE! This does not
|
||||
# limit the bandwidth during switchover, but only for calculations when
|
||||
# making decisions to switchover. By default, this value is zero,
|
||||
# which means QEMU will estimate the bandwidth automatically. This can
|
||||
# be set when the estimated value is not accurate, while the user is
|
||||
# able to guarantee such bandwidth is available when switching over.
|
||||
# When specified correctly, this can make the switchover decision much
|
||||
# more accurate. (Since 8.2)
|
||||
#
|
||||
# @downtime-limit: set maximum tolerated downtime for migration.
|
||||
# maximum downtime in milliseconds (Since 2.8)
|
||||
#
|
||||
@@ -839,7 +848,7 @@
|
||||
'cpu-throttle-initial', 'cpu-throttle-increment',
|
||||
'cpu-throttle-tailslow',
|
||||
'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
|
||||
'downtime-limit',
|
||||
'avail-switchover-bandwidth', 'downtime-limit',
|
||||
{ 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
|
||||
'block-incremental',
|
||||
'multifd-channels',
|
||||
@@ -924,6 +933,16 @@
|
||||
# @max-bandwidth: to set maximum speed for migration. maximum speed
|
||||
# in bytes per second. (Since 2.8)
|
||||
#
|
||||
# @avail-switchover-bandwidth: to set the available bandwidth that
|
||||
# migration can use during switchover phase. NOTE! This does not
|
||||
# limit the bandwidth during switchover, but only for calculations when
|
||||
# making decisions to switchover. By default, this value is zero,
|
||||
# which means QEMU will estimate the bandwidth automatically. This can
|
||||
# be set when the estimated value is not accurate, while the user is
|
||||
# able to guarantee such bandwidth is available when switching over.
|
||||
# When specified correctly, this can make the switchover decision much
|
||||
# more accurate. (Since 8.2)
|
||||
#
|
||||
# @downtime-limit: set maximum tolerated downtime for migration.
|
||||
# maximum downtime in milliseconds (Since 2.8)
|
||||
#
|
||||
@@ -1017,6 +1036,7 @@
|
||||
'*tls-hostname': 'StrOrNull',
|
||||
'*tls-authz': 'StrOrNull',
|
||||
'*max-bandwidth': 'size',
|
||||
'*avail-switchover-bandwidth': 'size',
|
||||
'*downtime-limit': 'uint64',
|
||||
'*x-checkpoint-delay': { 'type': 'uint32',
|
||||
'features': [ 'unstable' ] },
|
||||
@@ -1127,6 +1147,16 @@
|
||||
# @max-bandwidth: to set maximum speed for migration. maximum speed
|
||||
# in bytes per second. (Since 2.8)
|
||||
#
|
||||
# @avail-switchover-bandwidth: to set the available bandwidth that
|
||||
# migration can use during switchover phase. NOTE! This does not
|
||||
# limit the bandwidth during switchover, but only for calculations when
|
||||
# making decisions to switchover. By default, this value is zero,
|
||||
# which means QEMU will estimate the bandwidth automatically. This can
|
||||
# be set when the estimated value is not accurate, while the user is
|
||||
# able to guarantee such bandwidth is available when switching over.
|
||||
# When specified correctly, this can make the switchover decision much
|
||||
# more accurate. (Since 8.2)
|
||||
#
|
||||
# @downtime-limit: set maximum tolerated downtime for migration.
|
||||
# maximum downtime in milliseconds (Since 2.8)
|
||||
#
|
||||
@@ -1217,6 +1247,7 @@
|
||||
'*tls-hostname': 'str',
|
||||
'*tls-authz': 'str',
|
||||
'*max-bandwidth': 'size',
|
||||
'*avail-switchover-bandwidth': 'size',
|
||||
'*downtime-limit': 'uint64',
|
||||
'*x-checkpoint-delay': { 'type': 'uint32',
|
||||
'features': [ 'unstable' ] },
|
||||
|
||||
@@ -38,13 +38,13 @@ class MigrationFile(object):
|
||||
self.file = open(self.filename, "rb")
|
||||
|
||||
def read64(self):
|
||||
return int.from_bytes(self.file.read(8), byteorder='big', signed=True)
|
||||
return int.from_bytes(self.file.read(8), byteorder='big', signed=False)
|
||||
|
||||
def read32(self):
|
||||
return int.from_bytes(self.file.read(4), byteorder='big', signed=True)
|
||||
return int.from_bytes(self.file.read(4), byteorder='big', signed=False)
|
||||
|
||||
def read16(self):
|
||||
return int.from_bytes(self.file.read(2), byteorder='big', signed=True)
|
||||
return int.from_bytes(self.file.read(2), byteorder='big', signed=False)
|
||||
|
||||
def read8(self):
|
||||
return int.from_bytes(self.file.read(1), byteorder='big', signed=True)
|
||||
@@ -123,6 +123,7 @@ class RamSection(object):
|
||||
self.TARGET_PAGE_SIZE = ramargs['page_size']
|
||||
self.dump_memory = ramargs['dump_memory']
|
||||
self.write_memory = ramargs['write_memory']
|
||||
self.ignore_shared = ramargs['ignore_shared']
|
||||
self.sizeinfo = collections.OrderedDict()
|
||||
self.data = collections.OrderedDict()
|
||||
self.data['section sizes'] = self.sizeinfo
|
||||
@@ -169,6 +170,8 @@ class RamSection(object):
|
||||
f.truncate(0)
|
||||
f.truncate(len)
|
||||
self.files[self.name] = f
|
||||
if self.ignore_shared:
|
||||
mr_addr = self.file.read64()
|
||||
flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE
|
||||
|
||||
if flags & self.RAM_SAVE_FLAG_COMPRESS:
|
||||
@@ -261,12 +264,41 @@ class HTABSection(object):
|
||||
|
||||
|
||||
class ConfigurationSection(object):
|
||||
def __init__(self, file):
|
||||
def __init__(self, file, desc):
|
||||
self.file = file
|
||||
self.desc = desc
|
||||
self.caps = []
|
||||
|
||||
def parse_capabilities(self, vmsd_caps):
|
||||
if not vmsd_caps:
|
||||
return
|
||||
|
||||
ncaps = vmsd_caps.data['caps_count'].data
|
||||
self.caps = vmsd_caps.data['capabilities']
|
||||
|
||||
if type(self.caps) != list:
|
||||
self.caps = [self.caps]
|
||||
|
||||
if len(self.caps) != ncaps:
|
||||
raise Exception("Number of capabilities doesn't match "
|
||||
"caps_count field")
|
||||
|
||||
def has_capability(self, cap):
|
||||
return any([str(c) == cap for c in self.caps])
|
||||
|
||||
def read(self):
|
||||
name_len = self.file.read32()
|
||||
name = self.file.readstr(len = name_len)
|
||||
if self.desc:
|
||||
version_id = self.desc['version']
|
||||
section = VMSDSection(self.file, version_id, self.desc,
|
||||
'configuration')
|
||||
section.read()
|
||||
self.parse_capabilities(
|
||||
section.data.get("configuration/capabilities"))
|
||||
else:
|
||||
# backward compatibility for older streams that don't have
|
||||
# the configuration section in the json
|
||||
name_len = self.file.read32()
|
||||
name = self.file.readstr(len = name_len)
|
||||
|
||||
class VMSDFieldGeneric(object):
|
||||
def __init__(self, desc, file):
|
||||
@@ -288,6 +320,23 @@ class VMSDFieldGeneric(object):
|
||||
self.data = self.file.readvar(size)
|
||||
return self.data
|
||||
|
||||
class VMSDFieldCap(object):
|
||||
def __init__(self, desc, file):
|
||||
self.file = file
|
||||
self.desc = desc
|
||||
self.data = ""
|
||||
|
||||
def __repr__(self):
|
||||
return self.data
|
||||
|
||||
def __str__(self):
|
||||
return self.data
|
||||
|
||||
def read(self):
|
||||
len = self.file.read8()
|
||||
self.data = self.file.readstr(len)
|
||||
|
||||
|
||||
class VMSDFieldInt(VMSDFieldGeneric):
|
||||
def __init__(self, desc, file):
|
||||
super(VMSDFieldInt, self).__init__(desc, file)
|
||||
@@ -462,6 +511,7 @@ vmsd_field_readers = {
|
||||
"unused_buffer" : VMSDFieldGeneric,
|
||||
"bitmap" : VMSDFieldGeneric,
|
||||
"struct" : VMSDFieldStruct,
|
||||
"capability": VMSDFieldCap,
|
||||
"unknown" : VMSDFieldGeneric,
|
||||
}
|
||||
|
||||
@@ -525,6 +575,7 @@ class MigrationDump(object):
|
||||
ramargs['page_size'] = self.vmsd_desc['page_size']
|
||||
ramargs['dump_memory'] = dump_memory
|
||||
ramargs['write_memory'] = write_memory
|
||||
ramargs['ignore_shared'] = False
|
||||
self.section_classes[('ram',0)][1] = ramargs
|
||||
|
||||
while True:
|
||||
@@ -532,8 +583,10 @@ class MigrationDump(object):
|
||||
if section_type == self.QEMU_VM_EOF:
|
||||
break
|
||||
elif section_type == self.QEMU_VM_CONFIGURATION:
|
||||
section = ConfigurationSection(file)
|
||||
config_desc = self.vmsd_desc.get('configuration')
|
||||
section = ConfigurationSection(file, config_desc)
|
||||
section.read()
|
||||
ramargs['ignore_shared'] = section.has_capability('x-ignore-shared')
|
||||
elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL:
|
||||
section_id = file.read32()
|
||||
name = file.readstr()
|
||||
|
||||
@@ -357,6 +357,8 @@ foreach dir : target_dirs
|
||||
test_deps += [qsd]
|
||||
endif
|
||||
|
||||
qtest_env.set('PYTHON', python.full_path())
|
||||
|
||||
foreach test : target_qtests
|
||||
# Executables are shared across targets, declare them only the first time we
|
||||
# encounter them
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user