mirror of https://github.com/xemu-project/xemu.git
migration/next for 20171023
-----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJZ7hM6AAoJEPSH7xhYctcjk4AP/2UFM3yd6Nl5OhJtvVHXaJFx if9+rPzX47i/HwJMOayPIKwYN7rcFAFlhRf9vkK3n5E0fOJABcPOVxPnvXyIfHVb ASFNEtiHx3sImVQBBNPR4hrlMs4LpsWWpovjazRR2lFp5QBSNJexprO1w7AORC3k /z09jvIkZmFm1+9YjQuUhLWqrB6DLpIXR6Pw/eHiPiz69x/GrOxythM/jPCLs/H4 dmPcM06YPvUBc/SkMkVvDqDRfg8XjOqCnhX1zLBF7FcC+ego68orqIDCc56bptTy KJaWJR3sRvcvTtWX4H2dOdyYaMWs5Pjad3pTxGxid+VA9NyaBgn6tkXWwkuuLwgk c/R1Sfl07WbE1eLruaj0CPYsXDhgfEXyKrKjDb3x5mF1lpWACUqg+cAuNVOQ1HqE tKJiXZRbJXI6+oJu1g7h4JmqiSliAuqSfAKH7eb+wiBOZ8Kl7u41zCZ6bqS4QI1Y nv9CotsvAHb5P6ErmBMEBPzK2akcXRxOz0JWCl2YYQwQHB2z2eLMOqbyVd4U4Rzb L+PqgrnkmpETXWQE6axyU8Gwg1+5dNsiI2OkltqM5bJBWgurjTEdFci4/A2/RXid 2Pn63iKl9VnRHhqzzAH5ZJybM+YGmisnSHf5F6YojuRwMqQGIz6U4FpnVJlC0NX7 sCtDKht6YhDSMCWoOLzm =cCF/ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/juanquintela/tags/migration/20171023' into staging migration/next for 20171023 # gpg: Signature made Mon 23 Oct 2017 17:05:14 BST # gpg: using RSA key 0xF487EF185872D723 # gpg: Good signature from "Juan Quintela <quintela@redhat.com>" # gpg: aka "Juan Quintela <quintela@trasno.org>" # Primary key fingerprint: 1899 FF8E DEBF 58CC EE03 4B82 F487 EF18 5872 D723 * remotes/juanquintela/tags/migration/20171023: (21 commits) migration: Improve migration thread error handling qapi: Fix grammar in x-multifd-page-count descriptions migration: add bitmap for received page migration: introduce qemu_ufd_copy_ioctl helper migration: postcopy_place_page factoring out migration: new ram_init_bitmaps() migration: clean up xbzrle cache init/destroy migration: provide ram_state_cleanup migration: provide ram_state_init() migration: pause-before-switchover for postcopy migration: allow cancel to unpause migrate: HMP migate_continue migration: migrate-continue migration: Wait for semaphore before completing migration migration: Add 'pre-switchover' and 'device' statuses migration: Add 'pause-before-switchover' capability migration: Make cache_init() take an error parameter migration: Move xbzrle cache resize error handling to xbzrle_cache_resize migration: Make cache size elements use the right types migratiom: Remove max_item_age parameter ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
4e1b31dba8
|
@ -959,7 +959,19 @@ STEXI
|
|||
@item migrate_cancel
|
||||
@findex migrate_cancel
|
||||
Cancel the current VM migration.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "migrate_continue",
|
||||
.args_type = "state:s",
|
||||
.params = "state",
|
||||
.help = "Continue migration from the given paused state",
|
||||
.cmd = hmp_migrate_continue,
|
||||
},
|
||||
STEXI
|
||||
@item migrate_continue @var{state}
|
||||
@findex migrate_continue
|
||||
Continue migration from the paused state @var{state}
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
|
13
hmp.c
13
hmp.c
|
@ -1495,6 +1495,19 @@ void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
|
|||
qmp_migrate_cancel(NULL);
|
||||
}
|
||||
|
||||
void hmp_migrate_continue(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *err = NULL;
|
||||
const char *state = qdict_get_str(qdict, "state");
|
||||
int val = qapi_enum_parse(&MigrationStatus_lookup, state, -1, &err);
|
||||
|
||||
if (val >= 0) {
|
||||
qmp_migrate_continue(val, &err);
|
||||
}
|
||||
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
|
1
hmp.h
1
hmp.h
|
@ -68,6 +68,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict);
|
|||
void hmp_delvm(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_snapshots(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate_continue(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate_incoming(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
|
||||
|
|
|
@ -47,6 +47,8 @@ struct RAMBlock {
|
|||
* of the postcopy phase
|
||||
*/
|
||||
unsigned long *unsentmap;
|
||||
/* bitmap of already received pages in postcopy */
|
||||
unsigned long *receivedmap;
|
||||
};
|
||||
|
||||
static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset)
|
||||
|
@ -60,6 +62,14 @@ static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset)
|
|||
return (char *)block->host + offset;
|
||||
}
|
||||
|
||||
static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr,
|
||||
RAMBlock *rb)
|
||||
{
|
||||
uint64_t host_addr_offset =
|
||||
(uint64_t)(uintptr_t)(host_addr - (void *)rb->host);
|
||||
return host_addr_offset >> TARGET_PAGE_BITS;
|
||||
}
|
||||
|
||||
long qemu_getrampagesize(void);
|
||||
unsigned long last_ram_page(void);
|
||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
|
|
|
@ -104,6 +104,9 @@ enum mig_rp_message_type {
|
|||
static MigrationState *current_migration;
|
||||
|
||||
static bool migration_object_check(MigrationState *ms, Error **errp);
|
||||
static int migration_maybe_pause(MigrationState *s,
|
||||
int *current_active_state,
|
||||
int new_state);
|
||||
|
||||
void migration_object_init(void)
|
||||
{
|
||||
|
@ -526,6 +529,8 @@ static bool migration_is_setup_or_active(int state)
|
|||
case MIGRATION_STATUS_ACTIVE:
|
||||
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
|
||||
case MIGRATION_STATUS_SETUP:
|
||||
case MIGRATION_STATUS_PRE_SWITCHOVER:
|
||||
case MIGRATION_STATUS_DEVICE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -600,6 +605,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
|
|||
case MIGRATION_STATUS_ACTIVE:
|
||||
case MIGRATION_STATUS_CANCELLING:
|
||||
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
|
||||
case MIGRATION_STATUS_PRE_SWITCHOVER:
|
||||
case MIGRATION_STATUS_DEVICE:
|
||||
/* TODO add some postcopy stats */
|
||||
info->has_status = true;
|
||||
info->has_total_time = true;
|
||||
|
@ -865,6 +872,12 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
|
|||
if (params->has_block_incremental) {
|
||||
dest->block_incremental = params->block_incremental;
|
||||
}
|
||||
if (params->has_x_multifd_channels) {
|
||||
dest->x_multifd_channels = params->x_multifd_channels;
|
||||
}
|
||||
if (params->has_x_multifd_page_count) {
|
||||
dest->x_multifd_page_count = params->x_multifd_page_count;
|
||||
}
|
||||
}
|
||||
|
||||
static void migrate_params_apply(MigrateSetParameters *params)
|
||||
|
@ -1071,19 +1084,30 @@ static void migrate_fd_cleanup(void *opaque)
|
|||
MIGRATION_STATUS_CANCELLED);
|
||||
}
|
||||
|
||||
if (s->error) {
|
||||
/* It is used on info migrate. We can't free it */
|
||||
error_report_err(error_copy(s->error));
|
||||
}
|
||||
notifier_list_notify(&migration_state_notifiers, s);
|
||||
block_cleanup_parameters(s);
|
||||
}
|
||||
|
||||
void migrate_set_error(MigrationState *s, const Error *error)
|
||||
{
|
||||
qemu_mutex_lock(&s->error_mutex);
|
||||
if (!s->error) {
|
||||
s->error = error_copy(error);
|
||||
}
|
||||
qemu_mutex_unlock(&s->error_mutex);
|
||||
}
|
||||
|
||||
void migrate_fd_error(MigrationState *s, const Error *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);
|
||||
if (!s->error) {
|
||||
s->error = error_copy(error);
|
||||
}
|
||||
migrate_set_error(s, error);
|
||||
notifier_list_notify(&migration_state_notifiers, s);
|
||||
block_cleanup_parameters(s);
|
||||
}
|
||||
|
@ -1104,6 +1128,10 @@ static void migrate_fd_cancel(MigrationState *s)
|
|||
if (!migration_is_setup_or_active(old_state)) {
|
||||
break;
|
||||
}
|
||||
/* If the migration is paused, kick it out of the pause */
|
||||
if (old_state == MIGRATION_STATUS_PRE_SWITCHOVER) {
|
||||
qemu_sem_post(&s->pause_sem);
|
||||
}
|
||||
migrate_set_state(&s->state, old_state, MIGRATION_STATUS_CANCELLING);
|
||||
} while (s->state != MIGRATION_STATUS_CANCELLING);
|
||||
|
||||
|
@ -1183,6 +1211,8 @@ bool migration_is_idle(void)
|
|||
case MIGRATION_STATUS_ACTIVE:
|
||||
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
|
||||
case MIGRATION_STATUS_COLO:
|
||||
case MIGRATION_STATUS_PRE_SWITCHOVER:
|
||||
case MIGRATION_STATUS_DEVICE:
|
||||
return false;
|
||||
case MIGRATION_STATUS__MAX:
|
||||
g_assert_not_reached();
|
||||
|
@ -1362,29 +1392,24 @@ void qmp_migrate_cancel(Error **errp)
|
|||
migrate_fd_cancel(migrate_get_current());
|
||||
}
|
||||
|
||||
void qmp_migrate_continue(MigrationStatus state, Error **errp)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
if (s->state != state) {
|
||||
error_setg(errp, "Migration not in expected state: %s",
|
||||
MigrationStatus_str(s->state));
|
||||
return;
|
||||
}
|
||||
qemu_sem_post(&s->pause_sem);
|
||||
}
|
||||
|
||||
void qmp_migrate_set_cache_size(int64_t value, Error **errp)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
int64_t new_size;
|
||||
|
||||
/* Check for truncation */
|
||||
if (value != (size_t)value) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"exceeding address space");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Cache should not be larger than guest ram size */
|
||||
if (value > ram_bytes_total()) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"exceeds guest ram size ");
|
||||
return;
|
||||
}
|
||||
|
||||
new_size = xbzrle_cache_resize(value);
|
||||
new_size = xbzrle_cache_resize(value, errp);
|
||||
if (new_size < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"is smaller than page size");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1521,6 +1546,16 @@ bool migrate_use_multifd(void)
|
|||
return s->enabled_capabilities[MIGRATION_CAPABILITY_X_MULTIFD];
|
||||
}
|
||||
|
||||
bool migrate_pause_before_switchover(void)
|
||||
{
|
||||
MigrationState *s;
|
||||
|
||||
s = migrate_get_current();
|
||||
|
||||
return s->enabled_capabilities[
|
||||
MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER];
|
||||
}
|
||||
|
||||
int migrate_multifd_channels(void)
|
||||
{
|
||||
MigrationState *s;
|
||||
|
@ -1799,8 +1834,11 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
|
|||
QEMUFile *fb;
|
||||
int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||
bool restart_block = false;
|
||||
migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE,
|
||||
MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
||||
int cur_state = MIGRATION_STATUS_ACTIVE;
|
||||
if (!migrate_pause_before_switchover()) {
|
||||
migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE,
|
||||
MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
||||
}
|
||||
|
||||
trace_postcopy_start();
|
||||
qemu_mutex_lock_iothread();
|
||||
|
@ -1814,6 +1852,12 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
ret = migration_maybe_pause(ms, &cur_state,
|
||||
MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_inactivate_all();
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
|
@ -1951,6 +1995,41 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* migration_maybe_pause: Pause if required to by
|
||||
* migrate_pause_before_switchover called with the iothread locked
|
||||
* Returns: 0 on success
|
||||
*/
|
||||
static int migration_maybe_pause(MigrationState *s,
|
||||
int *current_active_state,
|
||||
int new_state)
|
||||
{
|
||||
if (!migrate_pause_before_switchover()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Since leaving this state is not atomic with posting the semaphore
|
||||
* it's possible that someone could have issued multiple migrate_continue
|
||||
* and the semaphore is incorrectly positive at this point;
|
||||
* the docs say it's undefined to reinit a semaphore that's already
|
||||
* init'd, so use timedwait to eat up any existing posts.
|
||||
*/
|
||||
while (qemu_sem_timedwait(&s->pause_sem, 1) == 0) {
|
||||
/* This block intentionally left blank */
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
migrate_set_state(&s->state, *current_active_state,
|
||||
MIGRATION_STATUS_PRE_SWITCHOVER);
|
||||
qemu_sem_wait(&s->pause_sem);
|
||||
migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER,
|
||||
new_state);
|
||||
*current_active_state = new_state;
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
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.
|
||||
|
@ -1976,6 +2055,10 @@ static void migration_completion(MigrationState *s, int current_active_state,
|
|||
if (!ret) {
|
||||
bool inactivate = !migrate_colo_enabled();
|
||||
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
||||
if (ret >= 0) {
|
||||
ret = migration_maybe_pause(s, ¤t_active_state,
|
||||
MIGRATION_STATUS_DEVICE);
|
||||
}
|
||||
if (ret >= 0) {
|
||||
qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX);
|
||||
ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false,
|
||||
|
@ -2355,8 +2438,10 @@ static void migration_instance_finalize(Object *obj)
|
|||
MigrationState *ms = MIGRATION_OBJ(obj);
|
||||
MigrationParameters *params = &ms->parameters;
|
||||
|
||||
qemu_mutex_destroy(&ms->error_mutex);
|
||||
g_free(params->tls_hostname);
|
||||
g_free(params->tls_creds);
|
||||
qemu_sem_destroy(&ms->pause_sem);
|
||||
}
|
||||
|
||||
static void migration_instance_init(Object *obj)
|
||||
|
@ -2367,6 +2452,8 @@ static void migration_instance_init(Object *obj)
|
|||
ms->state = MIGRATION_STATUS_NONE;
|
||||
ms->xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE;
|
||||
ms->mbps = -1;
|
||||
qemu_sem_init(&ms->pause_sem, 0);
|
||||
qemu_mutex_init(&ms->error_mutex);
|
||||
|
||||
params->tls_hostname = g_strdup("");
|
||||
params->tls_creds = g_strdup("");
|
||||
|
|
|
@ -121,6 +121,9 @@ struct MigrationState
|
|||
/* Flag set once the migration thread called bdrv_inactivate_all */
|
||||
bool block_inactive;
|
||||
|
||||
/* Migration is paused due to pause-before-switchover */
|
||||
QemuSemaphore pause_sem;
|
||||
|
||||
/* The semaphore is used to notify COLO thread that failover is finished */
|
||||
QemuSemaphore colo_exit_sem;
|
||||
|
||||
|
@ -129,8 +132,12 @@ struct MigrationState
|
|||
int64_t colo_checkpoint_time;
|
||||
QEMUTimer *colo_delay_timer;
|
||||
|
||||
/* The last error that occurred */
|
||||
/* The first error that has occurred.
|
||||
We used the mutex to be able to return the 1st error message */
|
||||
Error *error;
|
||||
/* mutex to protect errp */
|
||||
QemuMutex error_mutex;
|
||||
|
||||
/* Do we have to clean up -b/-i from old migrate parameters */
|
||||
/* This feature is deprecated and will be removed */
|
||||
bool must_remove_block_options;
|
||||
|
@ -159,6 +166,7 @@ bool migration_has_all_channels(void);
|
|||
|
||||
uint64_t migrate_max_downtime(void);
|
||||
|
||||
void migrate_set_error(MigrationState *s, const Error *error);
|
||||
void migrate_fd_error(MigrationState *s, const Error *error);
|
||||
|
||||
void migrate_fd_connect(MigrationState *s);
|
||||
|
@ -177,6 +185,7 @@ bool migrate_zero_blocks(void);
|
|||
|
||||
bool migrate_auto_converge(void);
|
||||
bool migrate_use_multifd(void);
|
||||
bool migrate_pause_before_switchover(void);
|
||||
int migrate_multifd_channels(void);
|
||||
int migrate_multifd_page_count(void);
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "migration/page_cache.h"
|
||||
|
@ -39,27 +41,28 @@ struct CacheItem {
|
|||
|
||||
struct PageCache {
|
||||
CacheItem *page_cache;
|
||||
unsigned int page_size;
|
||||
int64_t max_num_items;
|
||||
uint64_t max_item_age;
|
||||
int64_t num_items;
|
||||
size_t page_size;
|
||||
size_t max_num_items;
|
||||
size_t num_items;
|
||||
};
|
||||
|
||||
PageCache *cache_init(int64_t num_pages, unsigned int page_size)
|
||||
PageCache *cache_init(int64_t new_size, size_t page_size, Error **errp)
|
||||
{
|
||||
int64_t i;
|
||||
|
||||
size_t num_pages = new_size / page_size;
|
||||
PageCache *cache;
|
||||
|
||||
if (num_pages <= 0) {
|
||||
DPRINTF("invalid number of pages\n");
|
||||
if (new_size < page_size) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"is smaller than one target page size");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We prefer not to abort if there is no memory */
|
||||
cache = g_try_malloc(sizeof(*cache));
|
||||
if (!cache) {
|
||||
DPRINTF("Failed to allocate cache\n");
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"Failed to allocate cache");
|
||||
return NULL;
|
||||
}
|
||||
/* round down to the nearest power of 2 */
|
||||
|
@ -69,7 +72,6 @@ PageCache *cache_init(int64_t num_pages, unsigned int page_size)
|
|||
}
|
||||
cache->page_size = page_size;
|
||||
cache->num_items = 0;
|
||||
cache->max_item_age = 0;
|
||||
cache->max_num_items = num_pages;
|
||||
|
||||
DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items);
|
||||
|
@ -78,7 +80,8 @@ PageCache *cache_init(int64_t num_pages, unsigned int page_size)
|
|||
cache->page_cache = g_try_malloc((cache->max_num_items) *
|
||||
sizeof(*cache->page_cache));
|
||||
if (!cache->page_cache) {
|
||||
DPRINTF("Failed to allocate cache->page_cache\n");
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"Failed to allocate page cache");
|
||||
g_free(cache);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -24,12 +24,11 @@ typedef struct PageCache PageCache;
|
|||
*
|
||||
* Returns new allocated cache or NULL on error
|
||||
*
|
||||
* @cache pointer to the PageCache struct
|
||||
* @num_pages: cache maximal number of cached pages
|
||||
* @cache_size: cache size in bytes
|
||||
* @page_size: cache page size
|
||||
* @errp: set *errp if the check failed, with reason
|
||||
*/
|
||||
PageCache *cache_init(int64_t num_pages, unsigned int page_size);
|
||||
|
||||
PageCache *cache_init(int64_t cache_size, size_t page_size, Error **errp);
|
||||
/**
|
||||
* cache_fini: free all cache resources
|
||||
* @cache pointer to the PageCache struct
|
||||
|
|
|
@ -641,26 +641,46 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qemu_ufd_copy_ioctl(int userfault_fd, void *host_addr,
|
||||
void *from_addr, uint64_t pagesize, RAMBlock *rb)
|
||||
{
|
||||
int ret;
|
||||
if (from_addr) {
|
||||
struct uffdio_copy copy_struct;
|
||||
copy_struct.dst = (uint64_t)(uintptr_t)host_addr;
|
||||
copy_struct.src = (uint64_t)(uintptr_t)from_addr;
|
||||
copy_struct.len = pagesize;
|
||||
copy_struct.mode = 0;
|
||||
ret = ioctl(userfault_fd, UFFDIO_COPY, ©_struct);
|
||||
} else {
|
||||
struct uffdio_zeropage zero_struct;
|
||||
zero_struct.range.start = (uint64_t)(uintptr_t)host_addr;
|
||||
zero_struct.range.len = pagesize;
|
||||
zero_struct.mode = 0;
|
||||
ret = ioctl(userfault_fd, UFFDIO_ZEROPAGE, &zero_struct);
|
||||
}
|
||||
if (!ret) {
|
||||
ramblock_recv_bitmap_set_range(rb, host_addr,
|
||||
pagesize / qemu_target_page_size());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Place a host page (from) at (host) atomically
|
||||
* returns 0 on success
|
||||
*/
|
||||
int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from,
|
||||
size_t pagesize)
|
||||
RAMBlock *rb)
|
||||
{
|
||||
struct uffdio_copy copy_struct;
|
||||
|
||||
copy_struct.dst = (uint64_t)(uintptr_t)host;
|
||||
copy_struct.src = (uint64_t)(uintptr_t)from;
|
||||
copy_struct.len = pagesize;
|
||||
copy_struct.mode = 0;
|
||||
size_t pagesize = qemu_ram_pagesize(rb);
|
||||
|
||||
/* copy also acks to the kernel waking the stalled thread up
|
||||
* TODO: We can inhibit that ack and only do it if it was requested
|
||||
* which would be slightly cheaper, but we'd have to be careful
|
||||
* of the order of updating our page state.
|
||||
*/
|
||||
if (ioctl(mis->userfault_fd, UFFDIO_COPY, ©_struct)) {
|
||||
if (qemu_ufd_copy_ioctl(mis->userfault_fd, host, from, pagesize, rb)) {
|
||||
int e = errno;
|
||||
error_report("%s: %s copy host: %p from: %p (size: %zd)",
|
||||
__func__, strerror(e), host, from, pagesize);
|
||||
|
@ -677,17 +697,13 @@ int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from,
|
|||
* returns 0 on success
|
||||
*/
|
||||
int postcopy_place_page_zero(MigrationIncomingState *mis, void *host,
|
||||
size_t pagesize)
|
||||
RAMBlock *rb)
|
||||
{
|
||||
trace_postcopy_place_page_zero(host);
|
||||
|
||||
if (pagesize == getpagesize()) {
|
||||
struct uffdio_zeropage zero_struct;
|
||||
zero_struct.range.start = (uint64_t)(uintptr_t)host;
|
||||
zero_struct.range.len = getpagesize();
|
||||
zero_struct.mode = 0;
|
||||
|
||||
if (ioctl(mis->userfault_fd, UFFDIO_ZEROPAGE, &zero_struct)) {
|
||||
if (qemu_ram_pagesize(rb) == getpagesize()) {
|
||||
if (qemu_ufd_copy_ioctl(mis->userfault_fd, host, NULL, getpagesize(),
|
||||
rb)) {
|
||||
int e = errno;
|
||||
error_report("%s: %s zero host: %p",
|
||||
__func__, strerror(e), host);
|
||||
|
@ -711,7 +727,7 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host,
|
|||
memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
|
||||
}
|
||||
return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page,
|
||||
pagesize);
|
||||
rb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -774,14 +790,14 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
|
|||
}
|
||||
|
||||
int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from,
|
||||
size_t pagesize)
|
||||
RAMBlock *rb)
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int postcopy_place_page_zero(MigrationIncomingState *mis, void *host,
|
||||
size_t pagesize)
|
||||
RAMBlock *rb)
|
||||
{
|
||||
assert(0);
|
||||
return -1;
|
||||
|
|
|
@ -72,14 +72,14 @@ void postcopy_discard_send_finish(MigrationState *ms,
|
|||
* returns 0 on success
|
||||
*/
|
||||
int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from,
|
||||
size_t pagesize);
|
||||
RAMBlock *rb);
|
||||
|
||||
/*
|
||||
* Place a zero page at (host) atomically
|
||||
* returns 0 on success
|
||||
*/
|
||||
int postcopy_place_page_zero(MigrationIncomingState *mis, void *host,
|
||||
size_t pagesize);
|
||||
RAMBlock *rb);
|
||||
|
||||
/* The current postcopy state is read/set by postcopy_state_get/set
|
||||
* which update it atomically.
|
||||
|
|
261
migration/ram.c
261
migration/ram.c
|
@ -42,8 +42,10 @@
|
|||
#include "postcopy-ram.h"
|
||||
#include "migration/page_cache.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "trace.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "qemu/rcu_queue.h"
|
||||
#include "migration/colo.h"
|
||||
#include "migration/block.h"
|
||||
|
@ -113,13 +115,24 @@ static void XBZRLE_cache_unlock(void)
|
|||
* Returns the new_size or negative in case of error.
|
||||
*
|
||||
* @new_size: new cache size
|
||||
* @errp: set *errp if the check failed, with reason
|
||||
*/
|
||||
int64_t xbzrle_cache_resize(int64_t new_size)
|
||||
int64_t xbzrle_cache_resize(int64_t new_size, Error **errp)
|
||||
{
|
||||
PageCache *new_cache;
|
||||
int64_t ret;
|
||||
|
||||
if (new_size < TARGET_PAGE_SIZE) {
|
||||
/* Check for truncation */
|
||||
if (new_size != (size_t)new_size) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"exceeding address space");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cache should not be larger than guest ram size */
|
||||
if (new_size > ram_bytes_total()) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
||||
"exceeds guest ram size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -129,10 +142,8 @@ int64_t xbzrle_cache_resize(int64_t new_size)
|
|||
if (pow2floor(new_size) == migrate_xbzrle_cache_size()) {
|
||||
goto out_new_size;
|
||||
}
|
||||
new_cache = cache_init(new_size / TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE);
|
||||
new_cache = cache_init(new_size, TARGET_PAGE_SIZE, errp);
|
||||
if (!new_cache) {
|
||||
error_report("Error creating cache");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
@ -148,6 +159,35 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ramblock_recv_map_init(void)
|
||||
{
|
||||
RAMBlock *rb;
|
||||
|
||||
RAMBLOCK_FOREACH(rb) {
|
||||
assert(!rb->receivedmap);
|
||||
rb->receivedmap = bitmap_new(rb->max_length >> qemu_target_page_bits());
|
||||
}
|
||||
}
|
||||
|
||||
int ramblock_recv_bitmap_test(RAMBlock *rb, void *host_addr)
|
||||
{
|
||||
return test_bit(ramblock_recv_bitmap_offset(host_addr, rb),
|
||||
rb->receivedmap);
|
||||
}
|
||||
|
||||
void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr)
|
||||
{
|
||||
set_bit_atomic(ramblock_recv_bitmap_offset(host_addr, rb), rb->receivedmap);
|
||||
}
|
||||
|
||||
void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr,
|
||||
size_t nr)
|
||||
{
|
||||
bitmap_set_atomic(rb->receivedmap,
|
||||
ramblock_recv_bitmap_offset(host_addr, rb),
|
||||
nr);
|
||||
}
|
||||
|
||||
/*
|
||||
* An outstanding page request, on the source, having been received
|
||||
* and queued
|
||||
|
@ -1566,6 +1606,31 @@ static void xbzrle_load_cleanup(void)
|
|||
XBZRLE.decoded_buf = NULL;
|
||||
}
|
||||
|
||||
static void ram_state_cleanup(RAMState **rsp)
|
||||
{
|
||||
migration_page_queue_free(*rsp);
|
||||
qemu_mutex_destroy(&(*rsp)->bitmap_mutex);
|
||||
qemu_mutex_destroy(&(*rsp)->src_page_req_mutex);
|
||||
g_free(*rsp);
|
||||
*rsp = NULL;
|
||||
}
|
||||
|
||||
static void xbzrle_cleanup(void)
|
||||
{
|
||||
XBZRLE_cache_lock();
|
||||
if (XBZRLE.cache) {
|
||||
cache_fini(XBZRLE.cache);
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
g_free(XBZRLE.current_buf);
|
||||
g_free(XBZRLE.zero_target_page);
|
||||
XBZRLE.cache = NULL;
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
XBZRLE.current_buf = NULL;
|
||||
XBZRLE.zero_target_page = NULL;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
}
|
||||
|
||||
static void ram_save_cleanup(void *opaque)
|
||||
{
|
||||
RAMState **rsp = opaque;
|
||||
|
@ -1583,22 +1648,9 @@ static void ram_save_cleanup(void *opaque)
|
|||
block->unsentmap = NULL;
|
||||
}
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
if (XBZRLE.cache) {
|
||||
cache_fini(XBZRLE.cache);
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
g_free(XBZRLE.current_buf);
|
||||
g_free(XBZRLE.zero_target_page);
|
||||
XBZRLE.cache = NULL;
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
XBZRLE.current_buf = NULL;
|
||||
XBZRLE.zero_target_page = NULL;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
migration_page_queue_free(*rsp);
|
||||
xbzrle_cleanup();
|
||||
compress_threads_save_cleanup();
|
||||
g_free(*rsp);
|
||||
*rsp = NULL;
|
||||
ram_state_cleanup(rsp);
|
||||
}
|
||||
|
||||
static void ram_state_reset(RAMState *rs)
|
||||
|
@ -1999,6 +2051,8 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length)
|
|||
goto err;
|
||||
}
|
||||
|
||||
bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(),
|
||||
length >> qemu_target_page_bits());
|
||||
ret = ram_block_discard_range(rb, start, length);
|
||||
|
||||
err:
|
||||
|
@ -2007,63 +2061,96 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For every allocation, we will try not to crash the VM if the
|
||||
* allocation failed.
|
||||
*/
|
||||
static int xbzrle_init(void)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!migrate_use_xbzrle()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
XBZRLE_cache_lock();
|
||||
|
||||
XBZRLE.zero_target_page = g_try_malloc0(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.zero_target_page) {
|
||||
error_report("%s: Error allocating zero page", __func__);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size(),
|
||||
TARGET_PAGE_SIZE, &local_err);
|
||||
if (!XBZRLE.cache) {
|
||||
error_report_err(local_err);
|
||||
goto free_zero_page;
|
||||
}
|
||||
|
||||
XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.encoded_buf) {
|
||||
error_report("%s: Error allocating encoded_buf", __func__);
|
||||
goto free_cache;
|
||||
}
|
||||
|
||||
XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.current_buf) {
|
||||
error_report("%s: Error allocating current_buf", __func__);
|
||||
goto free_encoded_buf;
|
||||
}
|
||||
|
||||
/* We are all good */
|
||||
XBZRLE_cache_unlock();
|
||||
return 0;
|
||||
|
||||
free_encoded_buf:
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
free_cache:
|
||||
cache_fini(XBZRLE.cache);
|
||||
XBZRLE.cache = NULL;
|
||||
free_zero_page:
|
||||
g_free(XBZRLE.zero_target_page);
|
||||
XBZRLE.zero_target_page = NULL;
|
||||
err_out:
|
||||
XBZRLE_cache_unlock();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int ram_state_init(RAMState **rsp)
|
||||
{
|
||||
*rsp = g_new0(RAMState, 1);
|
||||
*rsp = g_try_new0(RAMState, 1);
|
||||
|
||||
if (!*rsp) {
|
||||
error_report("%s: Init ramstate fail", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&(*rsp)->bitmap_mutex);
|
||||
qemu_mutex_init(&(*rsp)->src_page_req_mutex);
|
||||
QSIMPLEQ_INIT(&(*rsp)->src_page_requests);
|
||||
|
||||
if (migrate_use_xbzrle()) {
|
||||
XBZRLE_cache_lock();
|
||||
XBZRLE.zero_target_page = g_malloc0(TARGET_PAGE_SIZE);
|
||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
||||
TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.cache) {
|
||||
XBZRLE_cache_unlock();
|
||||
error_report("Error creating cache");
|
||||
g_free(*rsp);
|
||||
*rsp = NULL;
|
||||
return -1;
|
||||
}
|
||||
XBZRLE_cache_unlock();
|
||||
/*
|
||||
* Count the total number of pages used by ram blocks not including any
|
||||
* gaps due to alignment or unplugs.
|
||||
*/
|
||||
(*rsp)->migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS;
|
||||
|
||||
/* We prefer not to abort if there is no memory */
|
||||
XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.encoded_buf) {
|
||||
error_report("Error allocating encoded_buf");
|
||||
g_free(*rsp);
|
||||
*rsp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
|
||||
if (!XBZRLE.current_buf) {
|
||||
error_report("Error allocating current_buf");
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
g_free(*rsp);
|
||||
*rsp = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* For memory_global_dirty_log_start below. */
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
qemu_mutex_lock_ramlist();
|
||||
rcu_read_lock();
|
||||
ram_state_reset(*rsp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ram_list_init_bitmaps(void)
|
||||
{
|
||||
RAMBlock *block;
|
||||
unsigned long pages;
|
||||
|
||||
/* Skip setting bitmap if there is no RAM */
|
||||
if (ram_bytes_total()) {
|
||||
RAMBlock *block;
|
||||
|
||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
||||
unsigned long pages = block->max_length >> TARGET_PAGE_BITS;
|
||||
|
||||
pages = block->max_length >> TARGET_PAGE_BITS;
|
||||
block->bmap = bitmap_new(pages);
|
||||
bitmap_set(block->bmap, 0, pages);
|
||||
if (migrate_postcopy_ram()) {
|
||||
|
@ -2072,18 +2159,36 @@ static int ram_state_init(RAMState **rsp)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the total number of pages used by ram blocks not including any
|
||||
* gaps due to alignment or unplugs.
|
||||
*/
|
||||
(*rsp)->migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS;
|
||||
static void ram_init_bitmaps(RAMState *rs)
|
||||
{
|
||||
/* For memory_global_dirty_log_start below. */
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_mutex_lock_ramlist();
|
||||
rcu_read_lock();
|
||||
|
||||
ram_list_init_bitmaps();
|
||||
memory_global_dirty_log_start();
|
||||
migration_bitmap_sync(*rsp);
|
||||
migration_bitmap_sync(rs);
|
||||
|
||||
rcu_read_unlock();
|
||||
qemu_mutex_unlock_ramlist();
|
||||
qemu_mutex_unlock_iothread();
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int ram_init_all(RAMState **rsp)
|
||||
{
|
||||
if (ram_state_init(rsp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xbzrle_init()) {
|
||||
ram_state_cleanup(rsp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ram_init_bitmaps(*rsp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2110,7 +2215,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
|||
|
||||
/* migration has already setup the bitmap, reuse it. */
|
||||
if (!migration_in_colo_state()) {
|
||||
if (ram_state_init(rsp) != 0) {
|
||||
if (ram_init_all(rsp) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -2534,13 +2639,20 @@ static int ram_load_setup(QEMUFile *f, void *opaque)
|
|||
{
|
||||
xbzrle_load_setup();
|
||||
compress_threads_load_setup();
|
||||
ramblock_recv_map_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ram_load_cleanup(void *opaque)
|
||||
{
|
||||
RAMBlock *rb;
|
||||
xbzrle_load_cleanup();
|
||||
compress_threads_load_cleanup();
|
||||
|
||||
RAMBLOCK_FOREACH(rb) {
|
||||
g_free(rb->receivedmap);
|
||||
rb->receivedmap = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2680,10 +2792,10 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||
|
||||
if (all_zero) {
|
||||
ret = postcopy_place_page_zero(mis, place_dest,
|
||||
block->page_size);
|
||||
block);
|
||||
} else {
|
||||
ret = postcopy_place_page(mis, place_dest,
|
||||
place_source, block->page_size);
|
||||
place_source, block);
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
|
@ -2755,6 +2867,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
|||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ramblock_recv_bitmap_set(block, host);
|
||||
trace_ram_load_loop(block->idstr, (uint64_t)addr, flags, host);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
extern MigrationStats ram_counters;
|
||||
extern XBZRLECacheStats xbzrle_counters;
|
||||
|
||||
int64_t xbzrle_cache_resize(int64_t new_size);
|
||||
int64_t xbzrle_cache_resize(int64_t new_size, Error **errp);
|
||||
uint64_t ram_bytes_remaining(void);
|
||||
uint64_t ram_bytes_total(void);
|
||||
|
||||
|
@ -57,4 +57,9 @@ int ram_discard_range(const char *block_name, uint64_t start, size_t length);
|
|||
int ram_postcopy_incoming_init(MigrationIncomingState *mis);
|
||||
|
||||
void ram_handle_compressed(void *host, uint8_t ch, uint64_t size);
|
||||
|
||||
int ramblock_recv_bitmap_test(RAMBlock *rb, void *host_addr);
|
||||
void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr);
|
||||
void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -119,7 +119,6 @@ static void migration_tls_outgoing_handshake(QIOTask *task,
|
|||
if (qio_task_propagate_error(task, &err)) {
|
||||
trace_migration_tls_outgoing_handshake_error(error_get_pretty(err));
|
||||
migrate_fd_error(s, err);
|
||||
error_free(err);
|
||||
} else {
|
||||
trace_migration_tls_outgoing_handshake_complete();
|
||||
migration_channel_connect(s, ioc, NULL);
|
||||
|
|
|
@ -96,12 +96,18 @@
|
|||
# @colo: VM is in the process of fault tolerance, VM can not get into this
|
||||
# state unless colo capability is enabled for migration. (since 2.8)
|
||||
#
|
||||
# @pre-switchover: Paused before device serialisation. (since 2.11)
|
||||
#
|
||||
# @device: During device serialisation when pause-before-switchover is enabled
|
||||
# (since 2.11)
|
||||
#
|
||||
# Since: 2.3
|
||||
#
|
||||
##
|
||||
{ 'enum': 'MigrationStatus',
|
||||
'data': [ 'none', 'setup', 'cancelling', 'cancelled',
|
||||
'active', 'postcopy-active', 'completed', 'failed', 'colo' ] }
|
||||
'active', 'postcopy-active', 'completed', 'failed', 'colo',
|
||||
'pre-switchover', 'device' ] }
|
||||
|
||||
##
|
||||
# @MigrationInfo:
|
||||
|
@ -341,6 +347,9 @@
|
|||
# @return-path: If enabled, migration will use the return path even
|
||||
# for precopy. (since 2.10)
|
||||
#
|
||||
# @pause-before-switchover: Pause outgoing migration before serialising device
|
||||
# state and before disabling block IO (since 2.11)
|
||||
#
|
||||
# @x-multifd: Use more than one fd for migration (since 2.11)
|
||||
#
|
||||
# Since: 1.2
|
||||
|
@ -348,7 +357,7 @@
|
|||
{ 'enum': 'MigrationCapability',
|
||||
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
|
||||
'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram',
|
||||
'block', 'return-path', 'x-multifd' ] }
|
||||
'block', 'return-path', 'pause-before-switchover', 'x-multifd' ] }
|
||||
|
||||
##
|
||||
# @MigrationCapabilityStatus:
|
||||
|
@ -471,7 +480,7 @@
|
|||
# number of sockets used for migration. The
|
||||
# default value is 2 (since 2.11)
|
||||
#
|
||||
# @x-multifd-page-count: Number of pages sent together to a thread
|
||||
# @x-multifd-page-count: Number of pages sent together to a thread.
|
||||
# The default value is 16 (since 2.11)
|
||||
#
|
||||
# Since: 2.4
|
||||
|
@ -542,7 +551,7 @@
|
|||
# number of sockets used for migration. The
|
||||
# default value is 2 (since 2.11)
|
||||
#
|
||||
# @x-multifd-page-count: Number of pages sent together to a thread
|
||||
# @x-multifd-page-count: Number of pages sent together to a thread.
|
||||
# The default value is 16 (since 2.11)
|
||||
#
|
||||
# Since: 2.4
|
||||
|
@ -638,7 +647,7 @@
|
|||
# number of sockets used for migration.
|
||||
# The default value is 2 (since 2.11)
|
||||
#
|
||||
# @x-multifd-page-count: Number of pages sent together to a thread
|
||||
# @x-multifd-page-count: Number of pages sent together to a thread.
|
||||
# The default value is 16 (since 2.11)
|
||||
#
|
||||
# Since: 2.4
|
||||
|
@ -867,6 +876,23 @@
|
|||
##
|
||||
{ 'command': 'migrate_cancel' }
|
||||
|
||||
##
|
||||
# @migrate-continue:
|
||||
#
|
||||
# Continue migration when it's in a paused state.
|
||||
#
|
||||
# @state: The state the migration is currently expected to be in
|
||||
#
|
||||
# Returns: nothing on success
|
||||
# Since: 2.11
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "migrate-continue" , "arguments":
|
||||
# { "state": "pre-switchover" } }
|
||||
# <- { "return": {} }
|
||||
##
|
||||
{ 'command': 'migrate-continue', 'data': {'state': 'MigrationStatus'} }
|
||||
|
||||
##
|
||||
# @migrate_set_downtime:
|
||||
#
|
||||
|
|
Loading…
Reference in New Issue