2008-10-13 03:12:02 +00:00
|
|
|
/*
|
|
|
|
* QEMU live migration
|
|
|
|
*
|
|
|
|
* Copyright IBM, Corp. 2008
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
|
|
* the COPYING file in the top-level directory.
|
|
|
|
*
|
2012-01-13 16:44:23 +00:00
|
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
2008-10-13 03:12:02 +00:00
|
|
|
*/
|
|
|
|
|
2016-01-26 18:16:54 +00:00
|
|
|
#include "qemu/osdep.h"
|
2016-03-20 17:16:19 +00:00
|
|
|
#include "qemu/cutils.h"
|
2015-03-17 17:29:20 +00:00
|
|
|
#include "qemu/error-report.h"
|
2013-08-21 15:02:47 +00:00
|
|
|
#include "qemu/main-loop.h"
|
2012-12-17 17:19:50 +00:00
|
|
|
#include "migration/migration.h"
|
2012-10-03 12:18:33 +00:00
|
|
|
#include "migration/qemu-file.h"
|
2012-12-17 17:20:04 +00:00
|
|
|
#include "sysemu/sysemu.h"
|
2012-12-17 17:19:44 +00:00
|
|
|
#include "block/block.h"
|
2015-03-17 16:22:46 +00:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2015-11-05 18:11:08 +00:00
|
|
|
#include "qapi/util.h"
|
2012-12-17 17:20:00 +00:00
|
|
|
#include "qemu/sockets.h"
|
2015-07-09 06:55:38 +00:00
|
|
|
#include "qemu/rcu.h"
|
2012-12-17 17:19:50 +00:00
|
|
|
#include "migration/block.h"
|
2015-11-05 18:11:02 +00:00
|
|
|
#include "migration/postcopy-ram.h"
|
2012-07-23 03:45:29 +00:00
|
|
|
#include "qemu/thread.h"
|
2011-09-13 20:37:16 +00:00
|
|
|
#include "qmp-commands.h"
|
2013-02-22 16:36:19 +00:00
|
|
|
#include "trace.h"
|
2015-05-20 10:16:15 +00:00
|
|
|
#include "qapi-event.h"
|
2015-09-08 17:12:35 +00:00
|
|
|
#include "qom/cpu.h"
|
2015-11-05 18:11:08 +00:00
|
|
|
#include "exec/memory.h"
|
|
|
|
#include "exec/address-spaces.h"
|
2016-04-27 10:05:01 +00:00
|
|
|
#include "io/channel-buffer.h"
|
2008-11-11 16:46:33 +00:00
|
|
|
|
2015-09-08 17:12:37 +00:00
|
|
|
#define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */
|
2008-10-13 03:12:02 +00:00
|
|
|
|
2012-12-19 09:40:48 +00:00
|
|
|
/* Amount of time to allocate to each "chunk" of bandwidth-throttled
|
|
|
|
* data. */
|
|
|
|
#define BUFFER_DELAY 100
|
|
|
|
#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY)
|
|
|
|
|
2015-03-23 08:32:17 +00:00
|
|
|
/* Default compression thread count */
|
|
|
|
#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8
|
2015-03-23 08:32:18 +00:00
|
|
|
/* Default decompression thread count, usually decompression is at
|
|
|
|
* least 4 times as fast as compression.*/
|
|
|
|
#define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2
|
2015-03-23 08:32:17 +00:00
|
|
|
/*0: means nocompress, 1: best speed, ... 9: best compress ratio */
|
|
|
|
#define DEFAULT_MIGRATE_COMPRESS_LEVEL 1
|
2015-09-08 17:12:34 +00:00
|
|
|
/* Define default autoconverge cpu throttle migration parameters */
|
2016-04-21 18:07:18 +00:00
|
|
|
#define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20
|
|
|
|
#define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10
|
2015-03-23 08:32:17 +00:00
|
|
|
|
2012-08-06 18:42:53 +00:00
|
|
|
/* Migration XBZRLE default cache size */
|
|
|
|
#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024)
|
|
|
|
|
2010-12-13 16:30:12 +00:00
|
|
|
static NotifierList migration_state_notifiers =
|
|
|
|
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
|
|
|
|
|
2015-02-19 11:40:27 +00:00
|
|
|
static bool deferred_incoming;
|
|
|
|
|
2015-11-05 18:10:52 +00:00
|
|
|
/*
|
|
|
|
* Current state of incoming postcopy; note this is not part of
|
|
|
|
* MigrationIncomingState since it's state is used during cleanup
|
|
|
|
* at the end as MIS is being freed.
|
|
|
|
*/
|
|
|
|
static PostcopyState incoming_postcopy_state;
|
|
|
|
|
2011-10-05 11:50:43 +00:00
|
|
|
/* When we add fault tolerance, we could have several
|
|
|
|
migrations at once. For now we don't need to add
|
|
|
|
dynamic creation of migration */
|
|
|
|
|
2015-05-21 12:24:14 +00:00
|
|
|
/* For outgoing */
|
2012-08-13 07:42:49 +00:00
|
|
|
MigrationState *migrate_get_current(void)
|
2011-10-05 11:50:43 +00:00
|
|
|
{
|
2015-11-05 18:11:08 +00:00
|
|
|
static bool once;
|
2011-10-05 11:50:43 +00:00
|
|
|
static MigrationState current_migration = {
|
2015-03-13 08:08:38 +00:00
|
|
|
.state = MIGRATION_STATUS_NONE,
|
2011-02-22 23:33:19 +00:00
|
|
|
.bandwidth_limit = MAX_THROTTLE,
|
2012-08-06 18:42:53 +00:00
|
|
|
.xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE,
|
2013-06-26 01:35:30 +00:00
|
|
|
.mbps = -1,
|
2016-04-27 10:05:14 +00:00
|
|
|
.parameters = {
|
|
|
|
.compress_level = DEFAULT_MIGRATE_COMPRESS_LEVEL,
|
|
|
|
.compress_threads = DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT,
|
|
|
|
.decompress_threads = DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT,
|
|
|
|
.cpu_throttle_initial = DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL,
|
|
|
|
.cpu_throttle_increment = DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT,
|
|
|
|
},
|
2011-10-05 11:50:43 +00:00
|
|
|
};
|
|
|
|
|
2015-11-05 18:11:08 +00:00
|
|
|
if (!once) {
|
|
|
|
qemu_mutex_init(¤t_migration.src_page_req_mutex);
|
|
|
|
once = true;
|
|
|
|
}
|
2011-10-05 11:50:43 +00:00
|
|
|
return ¤t_migration;
|
|
|
|
}
|
|
|
|
|
2015-05-21 12:24:14 +00:00
|
|
|
/* For incoming */
|
|
|
|
static MigrationIncomingState *mis_current;
|
|
|
|
|
|
|
|
MigrationIncomingState *migration_incoming_get_current(void)
|
|
|
|
{
|
|
|
|
return mis_current;
|
|
|
|
}
|
|
|
|
|
|
|
|
MigrationIncomingState *migration_incoming_state_new(QEMUFile* f)
|
|
|
|
{
|
2015-09-14 11:51:31 +00:00
|
|
|
mis_current = g_new0(MigrationIncomingState, 1);
|
2015-11-05 18:10:34 +00:00
|
|
|
mis_current->from_src_file = f;
|
2015-12-16 11:47:34 +00:00
|
|
|
mis_current->state = MIGRATION_STATUS_NONE;
|
2015-05-21 12:24:16 +00:00
|
|
|
QLIST_INIT(&mis_current->loadvm_handlers);
|
2015-11-05 18:10:47 +00:00
|
|
|
qemu_mutex_init(&mis_current->rp_mutex);
|
2015-11-05 18:10:50 +00:00
|
|
|
qemu_event_init(&mis_current->main_thread_load_event, false);
|
2015-05-21 12:24:14 +00:00
|
|
|
|
|
|
|
return mis_current;
|
|
|
|
}
|
|
|
|
|
|
|
|
void migration_incoming_state_destroy(void)
|
|
|
|
{
|
2015-11-05 18:10:50 +00:00
|
|
|
qemu_event_destroy(&mis_current->main_thread_load_event);
|
2015-05-21 12:24:16 +00:00
|
|
|
loadvm_free_handlers(mis_current);
|
2015-05-21 12:24:14 +00:00
|
|
|
g_free(mis_current);
|
|
|
|
mis_current = NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-08 08:58:10 +00:00
|
|
|
|
|
|
|
typedef struct {
|
2014-10-08 11:58:24 +00:00
|
|
|
bool optional;
|
2014-10-08 08:58:10 +00:00
|
|
|
uint32_t size;
|
|
|
|
uint8_t runstate[100];
|
2015-07-08 11:56:26 +00:00
|
|
|
RunState state;
|
|
|
|
bool received;
|
2014-10-08 08:58:10 +00:00
|
|
|
} GlobalState;
|
|
|
|
|
|
|
|
static GlobalState global_state;
|
|
|
|
|
2015-07-15 07:53:46 +00:00
|
|
|
int global_state_store(void)
|
2014-10-08 08:58:10 +00:00
|
|
|
{
|
|
|
|
if (!runstate_store((char *)global_state.runstate,
|
|
|
|
sizeof(global_state.runstate))) {
|
|
|
|
error_report("runstate name too big: %s", global_state.runstate);
|
|
|
|
trace_migrate_state_too_big();
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-03 14:29:19 +00:00
|
|
|
void global_state_store_running(void)
|
|
|
|
{
|
|
|
|
const char *state = RunState_lookup[RUN_STATE_RUNNING];
|
|
|
|
strncpy((char *)global_state.runstate,
|
|
|
|
state, sizeof(global_state.runstate));
|
|
|
|
}
|
|
|
|
|
2015-07-08 11:56:26 +00:00
|
|
|
static bool global_state_received(void)
|
2014-10-08 08:58:10 +00:00
|
|
|
{
|
2015-07-08 11:56:26 +00:00
|
|
|
return global_state.received;
|
|
|
|
}
|
|
|
|
|
|
|
|
static RunState global_state_get_runstate(void)
|
|
|
|
{
|
|
|
|
return global_state.state;
|
2014-10-08 08:58:10 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 11:58:24 +00:00
|
|
|
void global_state_set_optional(void)
|
|
|
|
{
|
|
|
|
global_state.optional = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool global_state_needed(void *opaque)
|
|
|
|
{
|
|
|
|
GlobalState *s = opaque;
|
|
|
|
char *runstate = (char *)s->runstate;
|
|
|
|
|
|
|
|
/* If it is not optional, it is mandatory */
|
|
|
|
|
|
|
|
if (s->optional == false) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If state is running or paused, it is not needed */
|
|
|
|
|
|
|
|
if (strcmp(runstate, "running") == 0 ||
|
|
|
|
strcmp(runstate, "paused") == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for any other state it is needed */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-10-08 08:58:10 +00:00
|
|
|
static int global_state_post_load(void *opaque, int version_id)
|
|
|
|
{
|
|
|
|
GlobalState *s = opaque;
|
2015-07-08 11:56:26 +00:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int r;
|
2014-10-08 08:58:10 +00:00
|
|
|
char *runstate = (char *)s->runstate;
|
|
|
|
|
2015-07-08 11:56:26 +00:00
|
|
|
s->received = true;
|
2014-10-08 08:58:10 +00:00
|
|
|
trace_migrate_global_state_post_load(runstate);
|
|
|
|
|
qapi: Don't let implicit enum MAX member collide
Now that we guarantee the user doesn't have any enum values
beginning with a single underscore, we can use that for our
own purposes. Renaming ENUM_MAX to ENUM__MAX makes it obvious
that the sentinel is generated.
This patch was mostly generated by applying a temporary patch:
|diff --git a/scripts/qapi.py b/scripts/qapi.py
|index e6d014b..b862ec9 100644
|--- a/scripts/qapi.py
|+++ b/scripts/qapi.py
|@@ -1570,6 +1570,7 @@ const char *const %(c_name)s_lookup[] = {
| max_index = c_enum_const(name, 'MAX', prefix)
| ret += mcgen('''
| [%(max_index)s] = NULL,
|+// %(max_index)s
| };
| ''',
| max_index=max_index)
then running:
$ cat qapi-{types,event}.c tests/test-qapi-types.c |
sed -n 's,^// \(.*\)MAX,s|\1MAX|\1_MAX|g,p' > list
$ git grep -l _MAX | xargs sed -i -f list
The only things not generated are the changes in scripts/qapi.py.
Rejecting enum members named 'MAX' is now useless, and will be dropped
in the next patch.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1447836791-369-23-git-send-email-eblake@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
[Rebased to current master, commit message tweaked]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-11-18 08:52:57 +00:00
|
|
|
r = qapi_enum_parse(RunState_lookup, runstate, RUN_STATE__MAX,
|
2014-10-08 08:58:10 +00:00
|
|
|
-1, &local_err);
|
|
|
|
|
2015-07-08 11:56:26 +00:00
|
|
|
if (r == -1) {
|
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
2014-10-08 08:58:10 +00:00
|
|
|
}
|
2015-07-08 11:56:26 +00:00
|
|
|
return -EINVAL;
|
2014-10-08 08:58:10 +00:00
|
|
|
}
|
2015-07-08 11:56:26 +00:00
|
|
|
s->state = r;
|
2014-10-08 08:58:10 +00:00
|
|
|
|
2015-07-08 11:56:26 +00:00
|
|
|
return 0;
|
2014-10-08 08:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void global_state_pre_save(void *opaque)
|
|
|
|
{
|
|
|
|
GlobalState *s = opaque;
|
|
|
|
|
|
|
|
trace_migrate_global_state_pre_save((char *)s->runstate);
|
|
|
|
s->size = strlen((char *)s->runstate) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateDescription vmstate_globalstate = {
|
|
|
|
.name = "globalstate",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.post_load = global_state_post_load,
|
|
|
|
.pre_save = global_state_pre_save,
|
2014-10-08 11:58:24 +00:00
|
|
|
.needed = global_state_needed,
|
2014-10-08 08:58:10 +00:00
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT32(size, GlobalState),
|
|
|
|
VMSTATE_BUFFER(runstate, GlobalState),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
void register_global_state(void)
|
|
|
|
{
|
|
|
|
/* We would use it independently that we receive it */
|
|
|
|
strcpy((char *)&global_state.runstate, "");
|
2015-07-08 11:56:26 +00:00
|
|
|
global_state.received = false;
|
2014-10-08 08:58:10 +00:00
|
|
|
vmstate_register(NULL, 0, &vmstate_globalstate, &global_state);
|
|
|
|
}
|
|
|
|
|
2015-07-07 12:44:05 +00:00
|
|
|
static void migrate_generate_event(int new_state)
|
|
|
|
{
|
|
|
|
if (migrate_use_events()) {
|
|
|
|
qapi_event_send_migration(new_state, &error_abort);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:40:27 +00:00
|
|
|
/*
|
|
|
|
* Called on -incoming with a defer: uri.
|
|
|
|
* The migration can be started later after any parameters have been
|
|
|
|
* changed.
|
|
|
|
*/
|
|
|
|
static void deferred_incoming_migration(Error **errp)
|
|
|
|
{
|
|
|
|
if (deferred_incoming) {
|
|
|
|
error_setg(errp, "Incoming migration already deferred");
|
|
|
|
}
|
|
|
|
deferred_incoming = true;
|
|
|
|
}
|
|
|
|
|
2015-11-05 18:11:07 +00:00
|
|
|
/* Request a range of pages from the source VM at the given
|
|
|
|
* start address.
|
|
|
|
* rbname: Name of the RAMBlock to request the page in, if NULL it's the same
|
|
|
|
* as the last request (a name must have been given previously)
|
|
|
|
* Start: Address offset within the RB
|
|
|
|
* Len: Length in bytes required - must be a multiple of pagesize
|
|
|
|
*/
|
|
|
|
void migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname,
|
|
|
|
ram_addr_t start, size_t len)
|
|
|
|
{
|
2016-03-23 14:59:57 +00:00
|
|
|
uint8_t bufc[12 + 1 + 255]; /* start (8), len (4), rbname up to 256 */
|
2015-11-05 18:11:07 +00:00
|
|
|
size_t msglen = 12; /* start + len */
|
|
|
|
|
|
|
|
*(uint64_t *)bufc = cpu_to_be64((uint64_t)start);
|
|
|
|
*(uint32_t *)(bufc + 8) = cpu_to_be32((uint32_t)len);
|
|
|
|
|
|
|
|
if (rbname) {
|
|
|
|
int rbname_len = strlen(rbname);
|
|
|
|
assert(rbname_len < 256);
|
|
|
|
|
|
|
|
bufc[msglen++] = rbname_len;
|
|
|
|
memcpy(bufc + msglen, rbname, rbname_len);
|
|
|
|
msglen += rbname_len;
|
|
|
|
migrate_send_rp_message(mis, MIG_RP_MSG_REQ_PAGES_ID, msglen, bufc);
|
|
|
|
} else {
|
|
|
|
migrate_send_rp_message(mis, MIG_RP_MSG_REQ_PAGES, msglen, bufc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-02 16:21:18 +00:00
|
|
|
void qemu_start_incoming_migration(const char *uri, Error **errp)
|
2008-10-13 03:12:02 +00:00
|
|
|
{
|
2008-10-13 03:14:31 +00:00
|
|
|
const char *p;
|
|
|
|
|
2015-05-20 15:15:42 +00:00
|
|
|
qapi_event_send_migration(MIGRATION_STATUS_SETUP, &error_abort);
|
2015-02-19 11:40:27 +00:00
|
|
|
if (!strcmp(uri, "defer")) {
|
|
|
|
deferred_incoming_migration(errp);
|
|
|
|
} else if (strstart(uri, "tcp:", &p)) {
|
2012-10-02 16:21:18 +00:00
|
|
|
tcp_start_incoming_migration(p, errp);
|
2013-07-22 14:01:54 +00:00
|
|
|
#ifdef CONFIG_RDMA
|
2015-02-19 11:40:27 +00:00
|
|
|
} else if (strstart(uri, "rdma:", &p)) {
|
2013-07-22 14:01:54 +00:00
|
|
|
rdma_start_incoming_migration(p, errp);
|
|
|
|
#endif
|
2015-02-19 11:40:27 +00:00
|
|
|
} else if (strstart(uri, "exec:", &p)) {
|
2012-10-02 16:21:18 +00:00
|
|
|
exec_start_incoming_migration(p, errp);
|
2015-02-19 11:40:27 +00:00
|
|
|
} else if (strstart(uri, "unix:", &p)) {
|
2012-10-02 16:21:18 +00:00
|
|
|
unix_start_incoming_migration(p, errp);
|
2015-02-19 11:40:27 +00:00
|
|
|
} else if (strstart(uri, "fd:", &p)) {
|
2012-10-02 16:21:18 +00:00
|
|
|
fd_start_incoming_migration(p, errp);
|
2015-02-19 11:40:27 +00:00
|
|
|
} else {
|
error: Strip trailing '\n' from error string arguments (again)
Commit 6daf194d and be62a2eb got rid of a bunch, but they keep coming
back. Tracked down with this Coccinelle semantic patch:
@r@
expression err, eno, cls, fmt;
position p;
@@
(
error_report(fmt, ...)@p
|
error_set(err, cls, fmt, ...)@p
|
error_set_errno(err, eno, cls, fmt, ...)@p
|
error_setg(err, fmt, ...)@p
|
error_setg_errno(err, eno, fmt, ...)@p
)
@script:python@
fmt << r.fmt;
p << r.p;
@@
if "\\n" in str(fmt):
print "%s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-id: 1360354939-10994-4-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-08 20:22:16 +00:00
|
|
|
error_setg(errp, "unknown migration protocol: %s", uri);
|
2010-06-09 12:10:54 +00:00
|
|
|
}
|
2008-10-13 03:12:02 +00:00
|
|
|
}
|
|
|
|
|
2016-02-24 08:53:38 +00:00
|
|
|
static void process_incoming_migration_bh(void *opaque)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
MigrationIncomingState *mis = opaque;
|
|
|
|
|
|
|
|
/* Make sure all file formats flush their mutable metadata */
|
|
|
|
bdrv_invalidate_cache_all(&local_err);
|
|
|
|
if (local_err) {
|
|
|
|
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
|
|
|
MIGRATION_STATUS_FAILED);
|
|
|
|
error_report_err(local_err);
|
|
|
|
migrate_decompress_threads_join();
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This must happen after all error conditions are dealt with and
|
|
|
|
* we're sure the VM is going to be running on this host.
|
|
|
|
*/
|
|
|
|
qemu_announce_self();
|
|
|
|
|
|
|
|
/* If global state section was not received or we are in running
|
|
|
|
state, we need to obey autostart. Any other state is set with
|
|
|
|
runstate_set. */
|
|
|
|
|
|
|
|
if (!global_state_received() ||
|
|
|
|
global_state_get_runstate() == RUN_STATE_RUNNING) {
|
|
|
|
if (autostart) {
|
|
|
|
vm_start();
|
|
|
|
} else {
|
|
|
|
runstate_set(RUN_STATE_PAUSED);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
runstate_set(global_state_get_runstate());
|
|
|
|
}
|
|
|
|
migrate_decompress_threads_join();
|
|
|
|
/*
|
|
|
|
* This must happen after any state changes since as soon as an external
|
|
|
|
* observer sees this event they might start to prod at the VM assuming
|
|
|
|
* it's ready to use.
|
|
|
|
*/
|
|
|
|
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
|
|
|
MIGRATION_STATUS_COMPLETED);
|
|
|
|
qemu_bh_delete(mis->bh);
|
|
|
|
migration_incoming_state_destroy();
|
|
|
|
}
|
|
|
|
|
2012-08-07 08:57:43 +00:00
|
|
|
static void process_incoming_migration_co(void *opaque)
|
2010-06-09 12:10:55 +00:00
|
|
|
{
|
2012-08-07 08:57:43 +00:00
|
|
|
QEMUFile *f = opaque;
|
2015-11-05 18:11:21 +00:00
|
|
|
MigrationIncomingState *mis;
|
|
|
|
PostcopyState ps;
|
2012-08-07 08:51:51 +00:00
|
|
|
int ret;
|
|
|
|
|
2015-11-05 18:11:21 +00:00
|
|
|
mis = migration_incoming_state_new(f);
|
2015-11-05 18:10:52 +00:00
|
|
|
postcopy_state_set(POSTCOPY_INCOMING_NONE);
|
2015-12-16 11:47:34 +00:00
|
|
|
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
|
|
|
MIGRATION_STATUS_ACTIVE);
|
2012-08-07 08:51:51 +00:00
|
|
|
ret = qemu_loadvm_state(f);
|
2015-05-21 12:24:14 +00:00
|
|
|
|
2015-11-05 18:11:21 +00:00
|
|
|
ps = postcopy_state_get();
|
|
|
|
trace_process_incoming_migration_co_end(ret, ps);
|
|
|
|
if (ps != POSTCOPY_INCOMING_NONE) {
|
|
|
|
if (ps == POSTCOPY_INCOMING_ADVISE) {
|
|
|
|
/*
|
|
|
|
* Where a migration had postcopy enabled (and thus went to advise)
|
|
|
|
* but managed to complete within the precopy period, we can use
|
|
|
|
* the normal exit.
|
|
|
|
*/
|
|
|
|
postcopy_ram_incoming_cleanup(mis);
|
|
|
|
} else if (ret >= 0) {
|
|
|
|
/*
|
|
|
|
* Postcopy was started, cleanup should happen at the end of the
|
|
|
|
* postcopy thread.
|
|
|
|
*/
|
|
|
|
trace_process_incoming_migration_co_postcopy_end_main();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Else if something went wrong then just fall out of the normal exit */
|
|
|
|
}
|
|
|
|
|
2012-08-07 08:51:51 +00:00
|
|
|
qemu_fclose(f);
|
2014-01-30 18:08:35 +00:00
|
|
|
free_xbzrle_decoded_buf();
|
2015-05-21 12:24:14 +00:00
|
|
|
|
2012-08-07 08:51:51 +00:00
|
|
|
if (ret < 0) {
|
2015-12-16 11:47:34 +00:00
|
|
|
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
|
|
|
MIGRATION_STATUS_FAILED);
|
2014-06-10 09:29:16 +00:00
|
|
|
error_report("load of migration failed: %s", strerror(-ret));
|
2015-03-23 08:32:18 +00:00
|
|
|
migrate_decompress_threads_join();
|
2013-04-16 21:50:41 +00:00
|
|
|
exit(EXIT_FAILURE);
|
2010-06-09 12:10:55 +00:00
|
|
|
}
|
|
|
|
|
2016-02-24 08:53:38 +00:00
|
|
|
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
|
|
|
|
qemu_bh_schedule(mis->bh);
|
2010-06-09 12:10:55 +00:00
|
|
|
}
|
|
|
|
|
2012-08-07 08:57:43 +00:00
|
|
|
void process_incoming_migration(QEMUFile *f)
|
|
|
|
{
|
|
|
|
Coroutine *co = qemu_coroutine_create(process_incoming_migration_co);
|
|
|
|
|
2015-03-23 08:32:18 +00:00
|
|
|
migrate_decompress_threads_create();
|
2016-04-27 10:04:56 +00:00
|
|
|
qemu_file_set_blocking(f, false);
|
2012-08-07 08:57:43 +00:00
|
|
|
qemu_coroutine_enter(co, f);
|
|
|
|
}
|
|
|
|
|
2016-04-27 10:04:59 +00:00
|
|
|
|
|
|
|
void migration_set_incoming_channel(MigrationState *s,
|
|
|
|
QIOChannel *ioc)
|
|
|
|
{
|
|
|
|
QEMUFile *f = qemu_fopen_channel_input(ioc);
|
|
|
|
|
|
|
|
process_incoming_migration(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void migration_set_outgoing_channel(MigrationState *s,
|
|
|
|
QIOChannel *ioc)
|
|
|
|
{
|
|
|
|
QEMUFile *f = qemu_fopen_channel_output(ioc);
|
|
|
|
|
|
|
|
s->to_dst_file = f;
|
|
|
|
|
|
|
|
migrate_fd_connect(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-05 18:10:47 +00:00
|
|
|
/*
|
|
|
|
* Send a message on the return channel back to the source
|
|
|
|
* of the migration.
|
|
|
|
*/
|
|
|
|
void migrate_send_rp_message(MigrationIncomingState *mis,
|
|
|
|
enum mig_rp_message_type message_type,
|
|
|
|
uint16_t len, void *data)
|
|
|
|
{
|
|
|
|
trace_migrate_send_rp_message((int)message_type, len);
|
|
|
|
qemu_mutex_lock(&mis->rp_mutex);
|
|
|
|
qemu_put_be16(mis->to_src_file, (unsigned int)message_type);
|
|
|
|
qemu_put_be16(mis->to_src_file, len);
|
|
|
|
qemu_put_buffer(mis->to_src_file, data, len);
|
|
|
|
qemu_fflush(mis->to_src_file);
|
|
|
|
qemu_mutex_unlock(&mis->rp_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a 'SHUT' message on the return channel with the given value
|
|
|
|
* to indicate that we've finished with the RP. Non-0 value indicates
|
|
|
|
* error.
|
|
|
|
*/
|
|
|
|
void migrate_send_rp_shut(MigrationIncomingState *mis,
|
|
|
|
uint32_t value)
|
|
|
|
{
|
|
|
|
uint32_t buf;
|
|
|
|
|
|
|
|
buf = cpu_to_be32(value);
|
|
|
|
migrate_send_rp_message(mis, MIG_RP_MSG_SHUT, sizeof(buf), &buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a 'PONG' message on the return channel with the given value
|
|
|
|
* (normally in response to a 'PING')
|
|
|
|
*/
|
|
|
|
void migrate_send_rp_pong(MigrationIncomingState *mis,
|
|
|
|
uint32_t value)
|
|
|
|
{
|
|
|
|
uint32_t buf;
|
|
|
|
|
|
|
|
buf = cpu_to_be32(value);
|
|
|
|
migrate_send_rp_message(mis, MIG_RP_MSG_PONG, sizeof(buf), &buf);
|
|
|
|
}
|
|
|
|
|
2009-05-28 19:22:57 +00:00
|
|
|
/* amount of nanoseconds we are willing to wait for migration to be down.
|
|
|
|
* the choice of nanoseconds is because it is the maximum resolution that
|
|
|
|
* get_clock() can achieve. It is an internal measure. All user-visible
|
|
|
|
* units must be in seconds */
|
2014-03-27 03:57:26 +00:00
|
|
|
static uint64_t max_downtime = 300000000;
|
2009-05-28 19:22:57 +00:00
|
|
|
|
|
|
|
uint64_t migrate_max_downtime(void)
|
|
|
|
{
|
|
|
|
return max_downtime;
|
|
|
|
}
|
|
|
|
|
2012-08-06 18:42:47 +00:00
|
|
|
MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
|
|
|
|
{
|
|
|
|
MigrationCapabilityStatusList *head = NULL;
|
|
|
|
MigrationCapabilityStatusList *caps;
|
|
|
|
MigrationState *s = migrate_get_current();
|
|
|
|
int i;
|
|
|
|
|
2013-10-05 09:18:28 +00:00
|
|
|
caps = NULL; /* silence compiler warning */
|
qapi: Don't let implicit enum MAX member collide
Now that we guarantee the user doesn't have any enum values
beginning with a single underscore, we can use that for our
own purposes. Renaming ENUM_MAX to ENUM__MAX makes it obvious
that the sentinel is generated.
This patch was mostly generated by applying a temporary patch:
|diff --git a/scripts/qapi.py b/scripts/qapi.py
|index e6d014b..b862ec9 100644
|--- a/scripts/qapi.py
|+++ b/scripts/qapi.py
|@@ -1570,6 +1570,7 @@ const char *const %(c_name)s_lookup[] = {
| max_index = c_enum_const(name, 'MAX', prefix)
| ret += mcgen('''
| [%(max_index)s] = NULL,
|+// %(max_index)s
| };
| ''',
| max_index=max_index)
then running:
$ cat qapi-{types,event}.c tests/test-qapi-types.c |
sed -n 's,^// \(.*\)MAX,s|\1MAX|\1_MAX|g,p' > list
$ git grep -l _MAX | xargs sed -i -f list
The only things not generated are the changes in scripts/qapi.py.
Rejecting enum members named 'MAX' is now useless, and will be dropped
in the next patch.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1447836791-369-23-git-send-email-eblake@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
[Rebased to current master, commit message tweaked]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-11-18 08:52:57 +00:00
|
|
|
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
|
2012-08-06 18:42:47 +00:00
|
|
|
if (head == NULL) {
|
|
|
|
head = g_malloc0(sizeof(*caps));
|
|
|
|
caps = head;
|
|
|
|
} else {
|
|
|
|
caps->next = g_malloc0(sizeof(*caps));
|
|
|
|
caps = caps->next;
|
|
|
|
}
|
|
|
|
caps->value =
|
|
|
|
g_malloc(sizeof(*caps->value));
|
|
|
|
caps->value->capability = i;
|
|
|
|
caps->value->state = s->enabled_capabilities[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
2015-03-23 08:32:28 +00:00
|
|
|
MigrationParameters *qmp_query_migrate_parameters(Error **errp)
|
|
|
|
{
|
|
|
|
MigrationParameters *params;
|
|
|
|
MigrationState *s = migrate_get_current();
|
|
|
|
|
|
|
|
params = g_malloc0(sizeof(*params));
|
2016-04-27 10:05:14 +00:00
|
|
|
params->compress_level = s->parameters.compress_level;
|
|
|
|
params->compress_threads = s->parameters.compress_threads;
|
|
|
|
params->decompress_threads = s->parameters.decompress_threads;
|
|
|
|
params->cpu_throttle_initial = s->parameters.cpu_throttle_initial;
|
|
|
|
params->cpu_throttle_increment = s->parameters.cpu_throttle_increment;
|
2016-04-27 10:05:15 +00:00
|
|
|
params->tls_creds = g_strdup(s->parameters.tls_creds);
|
|
|
|
params->tls_hostname = g_strdup(s->parameters.tls_hostname);
|
2015-03-23 08:32:28 +00:00
|
|
|
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:48 +00:00
|
|
|
/*
|
|
|
|
* Return true if we're already in the middle of a migration
|
|
|
|
* (i.e. any of the active or setup states)
|
|
|
|
*/
|
|
|
|
static bool migration_is_setup_or_active(int state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case MIGRATION_STATUS_ACTIVE:
|
2015-11-05 18:10:58 +00:00
|
|
|
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
|
2015-11-05 18:10:48 +00:00
|
|
|
case MIGRATION_STATUS_SETUP:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-06 18:42:57 +00:00
|
|
|
static void get_xbzrle_cache_stats(MigrationInfo *info)
|
|
|
|
{
|
|
|
|
if (migrate_use_xbzrle()) {
|
|
|
|
info->has_xbzrle_cache = true;
|
|
|
|
info->xbzrle_cache = g_malloc0(sizeof(*info->xbzrle_cache));
|
|
|
|
info->xbzrle_cache->cache_size = migrate_xbzrle_cache_size();
|
|
|
|
info->xbzrle_cache->bytes = xbzrle_mig_bytes_transferred();
|
|
|
|
info->xbzrle_cache->pages = xbzrle_mig_pages_transferred();
|
|
|
|
info->xbzrle_cache->cache_miss = xbzrle_mig_pages_cache_miss();
|
2014-04-04 09:57:56 +00:00
|
|
|
info->xbzrle_cache->cache_miss_rate = xbzrle_mig_cache_miss_rate();
|
2012-08-06 18:42:57 +00:00
|
|
|
info->xbzrle_cache->overflow = xbzrle_mig_pages_overflow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-13 20:37:16 +00:00
|
|
|
MigrationInfo *qmp_query_migrate(Error **errp)
|
2008-10-13 03:12:02 +00:00
|
|
|
{
|
2011-09-13 20:37:16 +00:00
|
|
|
MigrationInfo *info = g_malloc0(sizeof(*info));
|
2011-10-05 11:50:43 +00:00
|
|
|
MigrationState *s = migrate_get_current();
|
|
|
|
|
|
|
|
switch (s->state) {
|
2015-03-13 08:08:38 +00:00
|
|
|
case MIGRATION_STATUS_NONE:
|
2011-10-05 11:50:43 +00:00
|
|
|
/* no migration has happened ever */
|
|
|
|
break;
|
2015-03-13 08:08:38 +00:00
|
|
|
case MIGRATION_STATUS_SETUP:
|
rdma: introduce MIG_STATE_NONE and change MIG_STATE_SETUP state transition
As described in the previous patch, until now, the MIG_STATE_SETUP
state was not really a 'formal' state. It has been used as a 'zero' state
(what we're calling 'NONE' here) and QEMU has been unconditionally transitioning
into this state when the QMP migration command was called. Instead we want to
introduce MIG_STATE_NONE, which is our starting state in the state machine, and
then immediately transition into the MIG_STATE_SETUP state when the QMP migrate
command is issued.
In order to do this, we must delay the transition into MIG_STATE_ACTIVE until
later in the migration_thread(). This is done to be able to timestamp the amount of
time spent in the SETUP state for proper accounting to the user during
an RDMA migration.
Furthermore, the management software, until now, has never been aware of the
existence of the SETUP state whatsoever. This must change, because, timing of this
state implies that the state actually exists.
These two patches cannot be separated because the 'query_migrate' QMP
switch statement needs to know how to handle this new state transition.
Reviewed-by: Juan Quintela <quintela@redhat.com>
Tested-by: Michael R. Hines <mrhines@us.ibm.com>
Signed-off-by: Michael R. Hines <mrhines@us.ibm.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
2013-07-22 14:01:57 +00:00
|
|
|
info->has_status = true;
|
2013-07-22 14:01:58 +00:00
|
|
|
info->has_total_time = false;
|
rdma: introduce MIG_STATE_NONE and change MIG_STATE_SETUP state transition
As described in the previous patch, until now, the MIG_STATE_SETUP
state was not really a 'formal' state. It has been used as a 'zero' state
(what we're calling 'NONE' here) and QEMU has been unconditionally transitioning
into this state when the QMP migration command was called. Instead we want to
introduce MIG_STATE_NONE, which is our starting state in the state machine, and
then immediately transition into the MIG_STATE_SETUP state when the QMP migrate
command is issued.
In order to do this, we must delay the transition into MIG_STATE_ACTIVE until
later in the migration_thread(). This is done to be able to timestamp the amount of
time spent in the SETUP state for proper accounting to the user during
an RDMA migration.
Furthermore, the management software, until now, has never been aware of the
existence of the SETUP state whatsoever. This must change, because, timing of this
state implies that the state actually exists.
These two patches cannot be separated because the 'query_migrate' QMP
switch statement needs to know how to handle this new state transition.
Reviewed-by: Juan Quintela <quintela@redhat.com>
Tested-by: Michael R. Hines <mrhines@us.ibm.com>
Signed-off-by: Michael R. Hines <mrhines@us.ibm.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
2013-07-22 14:01:57 +00:00
|
|
|
break;
|
2015-03-13 08:08:38 +00:00
|
|
|
case MIGRATION_STATUS_ACTIVE:
|
|
|
|
case MIGRATION_STATUS_CANCELLING:
|
2011-09-13 20:37:16 +00:00
|
|
|
info->has_status = true;
|
2012-08-18 11:17:10 +00:00
|
|
|
info->has_total_time = true;
|
2013-08-21 15:03:08 +00:00
|
|
|
info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
|
2012-08-18 11:17:10 +00:00
|
|
|
- s->total_time;
|
2012-08-13 07:53:12 +00:00
|
|
|
info->has_expected_downtime = true;
|
|
|
|
info->expected_downtime = s->expected_downtime;
|
2013-07-22 14:01:58 +00:00
|
|
|
info->has_setup_time = true;
|
|
|
|
info->setup_time = s->setup_time;
|
2011-10-05 11:50:43 +00:00
|
|
|
|
2011-09-13 20:37:16 +00:00
|
|
|
info->has_ram = true;
|
|
|
|
info->ram = g_malloc0(sizeof(*info->ram));
|
|
|
|
info->ram->transferred = ram_bytes_transferred();
|
|
|
|
info->ram->remaining = ram_bytes_remaining();
|
|
|
|
info->ram->total = ram_bytes_total();
|
2012-08-06 18:42:56 +00:00
|
|
|
info->ram->duplicate = dup_mig_pages_transferred();
|
2013-03-26 09:58:37 +00:00
|
|
|
info->ram->skipped = skipped_mig_pages_transferred();
|
2012-08-06 18:42:56 +00:00
|
|
|
info->ram->normal = norm_mig_pages_transferred();
|
|
|
|
info->ram->normal_bytes = norm_mig_bytes_transferred();
|
2012-08-13 10:31:25 +00:00
|
|
|
info->ram->dirty_pages_rate = s->dirty_pages_rate;
|
2013-06-26 01:35:30 +00:00
|
|
|
info->ram->mbps = s->mbps;
|
2014-04-04 09:57:55 +00:00
|
|
|
info->ram->dirty_sync_count = s->dirty_sync_count;
|
2012-08-13 10:31:25 +00:00
|
|
|
|
2011-10-05 11:50:43 +00:00
|
|
|
if (blk_mig_active()) {
|
2011-09-13 20:37:16 +00:00
|
|
|
info->has_disk = true;
|
|
|
|
info->disk = g_malloc0(sizeof(*info->disk));
|
|
|
|
info->disk->transferred = blk_mig_bytes_transferred();
|
|
|
|
info->disk->remaining = blk_mig_bytes_remaining();
|
|
|
|
info->disk->total = blk_mig_bytes_total();
|
2008-10-24 22:10:31 +00:00
|
|
|
}
|
2012-08-06 18:42:57 +00:00
|
|
|
|
2015-09-08 17:12:36 +00:00
|
|
|
if (cpu_throttle_active()) {
|
2016-04-21 18:07:18 +00:00
|
|
|
info->has_cpu_throttle_percentage = true;
|
|
|
|
info->cpu_throttle_percentage = cpu_throttle_get_percentage();
|
2015-09-08 17:12:36 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:58 +00:00
|
|
|
get_xbzrle_cache_stats(info);
|
|
|
|
break;
|
|
|
|
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
|
|
|
|
/* Mostly the same as active; TODO add some postcopy stats */
|
|
|
|
info->has_status = true;
|
|
|
|
info->has_total_time = true;
|
|
|
|
info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
|
|
|
|
- s->total_time;
|
|
|
|
info->has_expected_downtime = true;
|
|
|
|
info->expected_downtime = s->expected_downtime;
|
|
|
|
info->has_setup_time = true;
|
|
|
|
info->setup_time = s->setup_time;
|
|
|
|
|
|
|
|
info->has_ram = true;
|
|
|
|
info->ram = g_malloc0(sizeof(*info->ram));
|
|
|
|
info->ram->transferred = ram_bytes_transferred();
|
|
|
|
info->ram->remaining = ram_bytes_remaining();
|
|
|
|
info->ram->total = ram_bytes_total();
|
|
|
|
info->ram->duplicate = dup_mig_pages_transferred();
|
|
|
|
info->ram->skipped = skipped_mig_pages_transferred();
|
|
|
|
info->ram->normal = norm_mig_pages_transferred();
|
|
|
|
info->ram->normal_bytes = norm_mig_bytes_transferred();
|
|
|
|
info->ram->dirty_pages_rate = s->dirty_pages_rate;
|
|
|
|
info->ram->mbps = s->mbps;
|
2016-02-25 10:03:51 +00:00
|
|
|
info->ram->dirty_sync_count = s->dirty_sync_count;
|
2015-11-05 18:10:58 +00:00
|
|
|
|
|
|
|
if (blk_mig_active()) {
|
|
|
|
info->has_disk = true;
|
|
|
|
info->disk = g_malloc0(sizeof(*info->disk));
|
|
|
|
info->disk->transferred = blk_mig_bytes_transferred();
|
|
|
|
info->disk->remaining = blk_mig_bytes_remaining();
|
|
|
|
info->disk->total = blk_mig_bytes_total();
|
|
|
|
}
|
|
|
|
|
2012-08-06 18:42:57 +00:00
|
|
|
get_xbzrle_cache_stats(info);
|
2011-10-05 11:50:43 +00:00
|
|
|
break;
|
2015-03-13 08:08:38 +00:00
|
|
|
case MIGRATION_STATUS_COMPLETED:
|
2012-08-06 18:42:57 +00:00
|
|
|
get_xbzrle_cache_stats(info);
|
|
|
|
|
2011-09-13 20:37:16 +00:00
|
|
|
info->has_status = true;
|
2013-07-19 02:23:45 +00:00
|
|
|
info->has_total_time = true;
|
2012-08-18 11:17:10 +00:00
|
|
|
info->total_time = s->total_time;
|
2012-08-13 07:35:16 +00:00
|
|
|
info->has_downtime = true;
|
|
|
|
info->downtime = s->downtime;
|
2013-07-22 14:01:58 +00:00
|
|
|
info->has_setup_time = true;
|
|
|
|
info->setup_time = s->setup_time;
|
2012-05-21 20:01:07 +00:00
|
|
|
|
|
|
|
info->has_ram = true;
|
|
|
|
info->ram = g_malloc0(sizeof(*info->ram));
|
|
|
|
info->ram->transferred = ram_bytes_transferred();
|
|
|
|
info->ram->remaining = 0;
|
|
|
|
info->ram->total = ram_bytes_total();
|
2012-08-06 18:42:56 +00:00
|
|
|
info->ram->duplicate = dup_mig_pages_transferred();
|
2013-03-26 09:58:37 +00:00
|
|
|
info->ram->skipped = skipped_mig_pages_transferred();
|
2012-08-06 18:42:56 +00:00
|
|
|
info->ram->normal = norm_mig_pages_transferred();
|
|
|
|
info->ram->normal_bytes = norm_mig_bytes_transferred();
|
2013-06-26 01:35:30 +00:00
|
|
|
info->ram->mbps = s->mbps;
|
2014-04-04 09:57:55 +00:00
|
|
|
info->ram->dirty_sync_count = s->dirty_sync_count;
|
2011-10-05 11:50:43 +00:00
|
|
|
break;
|
2015-03-13 08:08:38 +00:00
|
|
|
case MIGRATION_STATUS_FAILED:
|
2011-09-13 20:37:16 +00:00
|
|
|
info->has_status = true;
|
migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).
In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.
Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.
This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':
(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds
In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.
(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused
Or with QMP
{
"execute": "query-migrate",
"arguments": {}
}
{
"return": {
"status": "failed",
"error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
}
}
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-04-27 10:05:00 +00:00
|
|
|
if (s->error) {
|
|
|
|
info->has_error_desc = true;
|
|
|
|
info->error_desc = g_strdup(error_get_pretty(s->error));
|
|
|
|
}
|
2011-10-05 11:50:43 +00:00
|
|
|
break;
|
2015-03-13 08:08:38 +00:00
|
|
|
case MIGRATION_STATUS_CANCELLED:
|
2011-09-13 20:37:16 +00:00
|
|
|
info->has_status = true;
|
2011-10-05 11:50:43 +00:00
|
|
|
break;
|
2008-10-13 03:12:02 +00:00
|
|
|
}
|
2015-03-13 08:08:41 +00:00
|
|
|
info->status = s->state;
|
2011-09-13 20:37:16 +00:00
|
|
|
|
|
|
|
return info;
|
2008-10-13 03:12:02 +00:00
|
|
|
}
|
|
|
|
|
2012-08-06 18:42:48 +00:00
|
|
|
void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
MigrationState *s = migrate_get_current();
|
|
|
|
MigrationCapabilityStatusList *cap;
|
|
|
|
|
2015-11-05 18:10:48 +00:00
|
|
|
if (migration_is_setup_or_active(s->state)) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_MIGRATION_ACTIVE);
|
2012-08-06 18:42:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cap = params; cap; cap = cap->next) {
|
|
|
|
s->enabled_capabilities[cap->value->capability] = cap->value->state;
|
|
|
|
}
|
2015-11-05 18:10:51 +00:00
|
|
|
|
|
|
|
if (migrate_postcopy_ram()) {
|
|
|
|
if (migrate_use_compression()) {
|
|
|
|
/* The decompression threads asynchronously write into RAM
|
|
|
|
* rather than use the atomic copies needed to avoid
|
|
|
|
* userfaulting. It should be possible to fix the decompression
|
|
|
|
* threads for compatibility in future.
|
|
|
|
*/
|
|
|
|
error_report("Postcopy is not currently compatible with "
|
|
|
|
"compression");
|
2016-03-11 09:53:36 +00:00
|
|
|
s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM] =
|
2015-11-05 18:10:51 +00:00
|
|
|
false;
|
|
|
|
}
|
|
|
|
}
|
2012-08-06 18:42:48 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 08:32:28 +00:00
|
|
|
void qmp_migrate_set_parameters(bool has_compress_level,
|
|
|
|
int64_t compress_level,
|
|
|
|
bool has_compress_threads,
|
|
|
|
int64_t compress_threads,
|
|
|
|
bool has_decompress_threads,
|
2015-09-08 17:12:34 +00:00
|
|
|
int64_t decompress_threads,
|
2016-04-21 18:07:18 +00:00
|
|
|
bool has_cpu_throttle_initial,
|
|
|
|
int64_t cpu_throttle_initial,
|
|
|
|
bool has_cpu_throttle_increment,
|
2016-04-27 10:05:14 +00:00
|
|
|
int64_t cpu_throttle_increment,
|
2016-04-27 10:05:15 +00:00
|
|
|
bool has_tls_creds,
|
|
|
|
const char *tls_creds,
|
|
|
|
bool has_tls_hostname,
|
|
|
|
const char *tls_hostname,
|
2016-04-27 10:05:14 +00:00
|
|
|
Error **errp)
|
2015-03-23 08:32:28 +00:00
|
|
|
{
|
|
|
|
MigrationState *s = migrate_get_current();
|
|
|
|
|
|
|
|
if (has_compress_level && (compress_level < 0 || compress_level > 9)) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level",
|
|
|
|
"is invalid, it should be in the range of 0 to 9");
|
2015-03-23 08:32:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (has_compress_threads &&
|
|
|
|
(compress_threads < 1 || compress_threads > 255)) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
|
|
|
"compress_threads",
|
|
|
|
"is invalid, it should be in the range of 1 to 255");
|
2015-03-23 08:32:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (has_decompress_threads &&
|
|
|
|
(decompress_threads < 1 || decompress_threads > 255)) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
|
|
|
"decompress_threads",
|
|
|
|
"is invalid, it should be in the range of 1 to 255");
|
2015-03-23 08:32:28 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-04-21 18:07:18 +00:00
|
|
|
if (has_cpu_throttle_initial &&
|
|
|
|
(cpu_throttle_initial < 1 || cpu_throttle_initial > 99)) {
|
2015-09-08 17:12:34 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
2016-04-21 18:07:18 +00:00
|
|
|
"cpu_throttle_initial",
|
2015-09-08 17:12:34 +00:00
|
|
|
"an integer in the range of 1 to 99");
|
|
|
|
}
|
2016-04-21 18:07:18 +00:00
|
|
|
if (has_cpu_throttle_increment &&
|
|
|
|
(cpu_throttle_increment < 1 || cpu_throttle_increment > 99)) {
|
2015-09-08 17:12:34 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
2016-04-21 18:07:18 +00:00
|
|
|
"cpu_throttle_increment",
|
2015-09-08 17:12:34 +00:00
|
|
|
"an integer in the range of 1 to 99");
|
|
|
|
}
|
2015-03-23 08:32:28 +00:00
|
|
|
|
|
|
|
if (has_compress_level) {
|
2016-04-27 10:05:14 +00:00
|
|
|
s->parameters.compress_level = compress_level;
|
2015-03-23 08:32:28 +00:00
|
|
|
}
|
|
|
|
if (has_compress_threads) {
|
2016-04-27 10:05:14 +00:00
|
|
|
s->parameters.compress_threads = compress_threads;
|
2015-03-23 08:32:28 +00:00
|
|
|
}
|
|
|
|
if (has_decompress_threads) {
|
2016-04-27 10:05:14 +00:00
|
|
|
s->parameters.decompress_threads = decompress_threads;
|
2015-03-23 08:32:28 +00:00
|
|
|
}
|
2016-04-21 18:07:18 +00:00
|
|
|
if (has_cpu_throttle_initial) {
|
2016-04-27 10:05:14 +00:00
|
|
|
s->parameters.cpu_throttle_initial = cpu_throttle_initial;
|
2015-09-08 17:12:34 +00:00
|
|
|
}
|
2016-04-21 18:07:18 +00:00
|
|
|
if (has_cpu_throttle_increment) {
|
2016-04-27 10:05:14 +00:00
|
|
|
s->parameters.cpu_throttle_increment = cpu_throttle_increment;
|
2015-09-08 17:12:34 +00:00
|
|
|
}
|
2016-04-27 10:05:15 +00:00
|
|
|
if (has_tls_creds) {
|
|
|
|
g_free(s->parameters.tls_creds);
|
|
|
|
s->parameters.tls_creds = g_strdup(tls_creds);
|
|
|
|
}
|
|
|
|
if (has_tls_hostname) {
|
|
|
|
g_free(s->parameters.tls_hostname);
|
|
|
|
s->parameters.tls_hostname = g_strdup(tls_hostname);
|
|
|
|
}
|
2015-03-23 08:32:28 +00:00
|
|
|
}
|
|
|
|
|
2016-04-27 10:05:14 +00:00
|
|
|
|
2015-11-05 18:10:56 +00:00
|
|
|
void qmp_migrate_start_postcopy(Error **errp)
|
|
|
|
{
|
|
|
|
MigrationState *s = migrate_get_current();
|
|
|
|
|
|
|
|
if (!migrate_postcopy_ram()) {
|
2015-11-12 11:34:44 +00:00
|
|
|
error_setg(errp, "Enable postcopy with migrate_set_capability before"
|
2015-11-05 18:10:56 +00:00
|
|
|
" the start of migration");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->state == MIGRATION_STATUS_NONE) {
|
|
|
|
error_setg(errp, "Postcopy must be started after migration has been"
|
|
|
|
" started");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* we don't error if migration has finished since that would be racy
|
|
|
|
* with issuing this command.
|
|
|
|
*/
|
|
|
|
atomic_set(&s->start_postcopy, true);
|
|
|
|
}
|
|
|
|
|
2008-11-11 16:46:33 +00:00
|
|
|
/* shared migration helpers */
|
|
|
|
|
2015-12-16 11:47:33 +00:00
|
|
|
void migrate_set_state(int *state, int old_state, int new_state)
|
2013-11-07 11:01:15 +00:00
|
|
|
{
|
2015-12-16 11:47:33 +00:00
|
|
|
if (atomic_cmpxchg(state, old_state, new_state) == old_state) {
|
2015-07-08 11:58:27 +00:00
|
|
|
trace_migrate_set_state(new_state);
|
2015-07-07 12:44:05 +00:00
|
|
|
migrate_generate_event(new_state);
|
2013-11-07 11:01:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-22 16:36:21 +00:00
|
|
|
static void migrate_fd_cleanup(void *opaque)
|
2008-11-11 16:46:33 +00:00
|
|
|
{
|
2013-02-22 16:36:21 +00:00
|
|
|
MigrationState *s = opaque;
|
|
|
|
|
|
|
|
qemu_bh_delete(s->cleanup_bh);
|
|
|
|
s->cleanup_bh = NULL;
|
|
|
|
|
2015-11-05 18:11:08 +00:00
|
|
|
flush_page_queue(s);
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
if (s->to_dst_file) {
|
2014-03-10 23:42:29 +00:00
|
|
|
trace_migrate_fd_cleanup();
|
2013-02-22 16:36:46 +00:00
|
|
|
qemu_mutex_unlock_iothread();
|
2015-11-05 18:11:05 +00:00
|
|
|
if (s->migration_thread_running) {
|
|
|
|
qemu_thread_join(&s->thread);
|
|
|
|
s->migration_thread_running = false;
|
|
|
|
}
|
2013-02-22 16:36:46 +00:00
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
|
2015-03-23 08:32:17 +00:00
|
|
|
migrate_compress_threads_join();
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_fclose(s->to_dst_file);
|
|
|
|
s->to_dst_file = NULL;
|
2008-11-11 16:46:33 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:58 +00:00
|
|
|
assert((s->state != MIGRATION_STATUS_ACTIVE) &&
|
|
|
|
(s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE));
|
2013-02-22 16:36:09 +00:00
|
|
|
|
2015-11-02 07:37:00 +00:00
|
|
|
if (s->state == MIGRATION_STATUS_CANCELLING) {
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
|
2015-11-02 07:37:00 +00:00
|
|
|
MIGRATION_STATUS_CANCELLED);
|
2013-02-22 16:36:09 +00:00
|
|
|
}
|
2013-02-22 16:36:18 +00:00
|
|
|
|
|
|
|
notifier_list_notify(&migration_state_notifiers, s);
|
2008-11-11 16:46:33 +00:00
|
|
|
}
|
|
|
|
|
migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).
In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.
Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.
This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':
(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds
In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.
(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused
Or with QMP
{
"execute": "query-migrate",
"arguments": {}
}
{
"return": {
"status": "failed",
"error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
}
}
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-04-27 10:05:00 +00:00
|
|
|
void migrate_fd_error(MigrationState *s, const Error *error)
|
2008-11-11 16:46:33 +00:00
|
|
|
{
|
migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).
In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.
Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.
This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':
(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds
In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.
(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused
Or with QMP
{
"execute": "query-migrate",
"arguments": {}
}
{
"return": {
"status": "failed",
"error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
}
}
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-04-27 10:05:00 +00:00
|
|
|
trace_migrate_fd_error(error ? error_get_pretty(error) : "");
|
2016-01-15 03:37:42 +00:00
|
|
|
assert(s->to_dst_file == NULL);
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
|
|
|
|
MIGRATION_STATUS_FAILED);
|
migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).
In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.
Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.
This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':
(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds
In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.
(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused
Or with QMP
{
"execute": "query-migrate",
"arguments": {}
}
{
"return": {
"status": "failed",
"error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
}
}
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-04-27 10:05:00 +00:00
|
|
|
if (!s->error) {
|
|
|
|
s->error = error_copy(error);
|
|
|
|
}
|
2013-02-22 16:36:21 +00:00
|
|
|
notifier_list_notify(&migration_state_notifiers, s);
|
2011-02-22 22:32:54 +00:00
|
|
|
}
|
|
|
|
|
2010-05-11 14:28:39 +00:00
|
|
|
static void migrate_fd_cancel(MigrationState *s)
|
2008-11-11 16:46:33 +00:00
|
|
|
{
|
2013-11-07 08:21:23 +00:00
|
|
|
int old_state ;
|
2016-01-15 03:37:42 +00:00
|
|
|
QEMUFile *f = migrate_get_current()->to_dst_file;
|
2014-03-10 23:42:29 +00:00
|
|
|
trace_migrate_fd_cancel();
|
2008-11-11 16:46:33 +00:00
|
|
|
|
2015-11-05 18:10:49 +00:00
|
|
|
if (s->rp_state.from_dst_file) {
|
|
|
|
/* shutdown the rp socket, so causing the rp thread to shutdown */
|
|
|
|
qemu_file_shutdown(s->rp_state.from_dst_file);
|
|
|
|
}
|
|
|
|
|
2013-11-07 08:21:23 +00:00
|
|
|
do {
|
|
|
|
old_state = s->state;
|
2015-11-05 18:10:48 +00:00
|
|
|
if (!migration_is_setup_or_active(old_state)) {
|
2013-11-07 08:21:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, old_state, MIGRATION_STATUS_CANCELLING);
|
2015-03-13 08:08:38 +00:00
|
|
|
} while (s->state != MIGRATION_STATUS_CANCELLING);
|
2015-01-08 11:11:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're unlucky the migration code might be stuck somewhere in a
|
|
|
|
* send/write while the network has failed and is waiting to timeout;
|
|
|
|
* if we've got shutdown(2) available then we can force it to quit.
|
|
|
|
* The outgoing qemu file gets closed in migrate_fd_cleanup that is
|
|
|
|
* called in a bh, so there is no race against this cancel.
|
|
|
|
*/
|
2015-03-13 08:08:38 +00:00
|
|
|
if (s->state == MIGRATION_STATUS_CANCELLING && f) {
|
2015-01-08 11:11:32 +00:00
|
|
|
qemu_file_shutdown(f);
|
|
|
|
}
|
2008-11-11 16:46:33 +00:00
|
|
|
}
|
|
|
|
|
2010-12-13 16:30:12 +00:00
|
|
|
void add_migration_state_change_notifier(Notifier *notify)
|
|
|
|
{
|
|
|
|
notifier_list_add(&migration_state_notifiers, notify);
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_migration_state_change_notifier(Notifier *notify)
|
|
|
|
{
|
2012-01-13 16:34:01 +00:00
|
|
|
notifier_remove(notify);
|
2010-12-13 16:30:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-29 13:01:58 +00:00
|
|
|
bool migration_in_setup(MigrationState *s)
|
2011-10-25 11:50:11 +00:00
|
|
|
{
|
2015-03-13 08:08:38 +00:00
|
|
|
return s->state == MIGRATION_STATUS_SETUP;
|
2011-10-25 11:50:11 +00:00
|
|
|
}
|
|
|
|
|
2011-02-22 23:43:59 +00:00
|
|
|
bool migration_has_finished(MigrationState *s)
|
2010-12-13 16:30:12 +00:00
|
|
|
{
|
2015-03-13 08:08:38 +00:00
|
|
|
return s->state == MIGRATION_STATUS_COMPLETED;
|
2010-12-13 16:30:12 +00:00
|
|
|
}
|
2010-05-11 14:28:39 +00:00
|
|
|
|
2011-10-25 11:50:11 +00:00
|
|
|
bool migration_has_failed(MigrationState *s)
|
|
|
|
{
|
2015-03-13 08:08:38 +00:00
|
|
|
return (s->state == MIGRATION_STATUS_CANCELLED ||
|
|
|
|
s->state == MIGRATION_STATUS_FAILED);
|
2011-10-25 11:50:11 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:58 +00:00
|
|
|
bool migration_in_postcopy(MigrationState *s)
|
|
|
|
{
|
|
|
|
return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
|
|
|
}
|
|
|
|
|
2016-02-22 17:17:32 +00:00
|
|
|
bool migration_in_postcopy_after_devices(MigrationState *s)
|
|
|
|
{
|
|
|
|
return migration_in_postcopy(s) && s->postcopy_after_devices;
|
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:40 +00:00
|
|
|
MigrationState *migrate_init(const MigrationParams *params)
|
2010-05-11 14:28:39 +00:00
|
|
|
{
|
2011-10-05 11:50:43 +00:00
|
|
|
MigrationState *s = migrate_get_current();
|
2012-08-06 18:42:47 +00:00
|
|
|
|
2015-11-12 15:38:27 +00:00
|
|
|
/*
|
|
|
|
* Reinitialise all migration state, except
|
|
|
|
* parameters/capabilities that the user set, and
|
|
|
|
* locks.
|
|
|
|
*/
|
|
|
|
s->bytes_xfer = 0;
|
|
|
|
s->xfer_limit = 0;
|
|
|
|
s->cleanup_bh = 0;
|
2016-01-15 03:37:42 +00:00
|
|
|
s->to_dst_file = NULL;
|
2015-11-12 15:38:27 +00:00
|
|
|
s->state = MIGRATION_STATUS_NONE;
|
2012-06-19 15:43:09 +00:00
|
|
|
s->params = *params;
|
2015-11-12 15:38:27 +00:00
|
|
|
s->rp_state.from_dst_file = NULL;
|
|
|
|
s->rp_state.error = false;
|
|
|
|
s->mbps = 0.0;
|
|
|
|
s->downtime = 0;
|
|
|
|
s->expected_downtime = 0;
|
|
|
|
s->dirty_pages_rate = 0;
|
|
|
|
s->dirty_bytes_rate = 0;
|
|
|
|
s->setup_time = 0;
|
|
|
|
s->dirty_sync_count = 0;
|
|
|
|
s->start_postcopy = false;
|
2016-02-22 17:17:32 +00:00
|
|
|
s->postcopy_after_devices = false;
|
2015-11-12 15:38:27 +00:00
|
|
|
s->migration_thread_running = false;
|
|
|
|
s->last_req_rb = NULL;
|
migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).
In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.
Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.
This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':
(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds
In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.
(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused
Or with QMP
{
"execute": "query-migrate",
"arguments": {}
}
{
"return": {
"status": "failed",
"error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
}
}
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-04-27 10:05:00 +00:00
|
|
|
error_free(s->error);
|
|
|
|
s->error = NULL;
|
2015-11-12 15:38:27 +00:00
|
|
|
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP);
|
2010-05-11 14:28:39 +00:00
|
|
|
|
2015-11-05 18:11:08 +00:00
|
|
|
QSIMPLEQ_INIT(&s->src_page_requests);
|
|
|
|
|
2013-08-21 15:03:08 +00:00
|
|
|
s->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
2010-05-11 14:28:39 +00:00
|
|
|
return s;
|
|
|
|
}
|
2011-02-22 22:54:21 +00:00
|
|
|
|
2011-11-14 21:09:43 +00:00
|
|
|
static GSList *migration_blockers;
|
|
|
|
|
|
|
|
void migrate_add_blocker(Error *reason)
|
|
|
|
{
|
|
|
|
migration_blockers = g_slist_prepend(migration_blockers, reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
void migrate_del_blocker(Error *reason)
|
|
|
|
{
|
|
|
|
migration_blockers = g_slist_remove(migration_blockers, reason);
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:40:28 +00:00
|
|
|
void qmp_migrate_incoming(const char *uri, Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
2015-02-26 14:54:41 +00:00
|
|
|
static bool once = true;
|
2015-02-19 11:40:28 +00:00
|
|
|
|
|
|
|
if (!deferred_incoming) {
|
2015-02-26 14:54:41 +00:00
|
|
|
error_setg(errp, "For use with '-incoming defer'");
|
2015-02-19 11:40:28 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-02-26 14:54:41 +00:00
|
|
|
if (!once) {
|
|
|
|
error_setg(errp, "The incoming migration has already been started");
|
|
|
|
}
|
2015-02-19 11:40:28 +00:00
|
|
|
|
|
|
|
qemu_start_incoming_migration(uri, &local_err);
|
|
|
|
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-26 14:54:41 +00:00
|
|
|
once = false;
|
2015-02-19 11:40:28 +00:00
|
|
|
}
|
|
|
|
|
2016-05-04 19:44:19 +00:00
|
|
|
bool migration_is_blocked(Error **errp)
|
|
|
|
{
|
|
|
|
if (qemu_savevm_state_blocked(errp)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (migration_blockers) {
|
|
|
|
*errp = error_copy(migration_blockers->data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-12-05 16:48:01 +00:00
|
|
|
void qmp_migrate(const char *uri, bool has_blk, bool blk,
|
|
|
|
bool has_inc, bool inc, bool has_detach, bool detach,
|
|
|
|
Error **errp)
|
2011-02-22 22:54:21 +00:00
|
|
|
{
|
2012-10-03 12:34:33 +00:00
|
|
|
Error *local_err = NULL;
|
2011-10-05 11:50:43 +00:00
|
|
|
MigrationState *s = migrate_get_current();
|
2012-06-19 15:43:09 +00:00
|
|
|
MigrationParams params;
|
2011-02-22 22:54:21 +00:00
|
|
|
const char *p;
|
|
|
|
|
2013-07-29 23:39:52 +00:00
|
|
|
params.blk = has_blk && blk;
|
|
|
|
params.shared = has_inc && inc;
|
2012-06-19 15:43:09 +00:00
|
|
|
|
2015-11-05 18:10:48 +00:00
|
|
|
if (migration_is_setup_or_active(s->state) ||
|
2015-03-13 08:08:38 +00:00
|
|
|
s->state == MIGRATION_STATUS_CANCELLING) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_MIGRATION_ACTIVE);
|
2011-12-05 16:48:01 +00:00
|
|
|
return;
|
2011-02-22 22:54:21 +00:00
|
|
|
}
|
2014-04-14 16:03:59 +00:00
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
error_setg(errp, "Guest is waiting for an incoming migration");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-04 19:44:19 +00:00
|
|
|
if (migration_is_blocked(errp)) {
|
2011-12-05 16:48:01 +00:00
|
|
|
return;
|
2011-11-14 21:09:43 +00:00
|
|
|
}
|
|
|
|
|
2012-06-19 15:43:09 +00:00
|
|
|
s = migrate_init(¶ms);
|
2011-02-22 22:54:21 +00:00
|
|
|
|
|
|
|
if (strstart(uri, "tcp:", &p)) {
|
2012-10-02 08:02:46 +00:00
|
|
|
tcp_start_outgoing_migration(s, p, &local_err);
|
2013-07-22 14:01:54 +00:00
|
|
|
#ifdef CONFIG_RDMA
|
2013-12-18 20:52:01 +00:00
|
|
|
} else if (strstart(uri, "rdma:", &p)) {
|
2013-07-22 14:01:54 +00:00
|
|
|
rdma_start_outgoing_migration(s, p, &local_err);
|
|
|
|
#endif
|
2011-02-22 22:54:21 +00:00
|
|
|
} else if (strstart(uri, "exec:", &p)) {
|
2012-10-02 08:02:46 +00:00
|
|
|
exec_start_outgoing_migration(s, p, &local_err);
|
2011-02-22 22:54:21 +00:00
|
|
|
} else if (strstart(uri, "unix:", &p)) {
|
2012-10-02 08:02:46 +00:00
|
|
|
unix_start_outgoing_migration(s, p, &local_err);
|
2011-02-22 22:54:21 +00:00
|
|
|
} else if (strstart(uri, "fd:", &p)) {
|
2012-10-02 08:02:46 +00:00
|
|
|
fd_start_outgoing_migration(s, p, &local_err);
|
2010-12-13 16:30:12 +00:00
|
|
|
} else {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "uri",
|
|
|
|
"a valid migration protocol");
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
|
|
|
|
MIGRATION_STATUS_FAILED);
|
2011-12-05 16:48:01 +00:00
|
|
|
return;
|
2011-02-22 22:54:21 +00:00
|
|
|
}
|
|
|
|
|
2012-10-02 08:02:46 +00:00
|
|
|
if (local_err) {
|
migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).
In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.
Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.
This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':
(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds
In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.
(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused
Or with QMP
{
"execute": "query-migrate",
"arguments": {}
}
{
"return": {
"status": "failed",
"error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
}
}
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-04-27 10:05:00 +00:00
|
|
|
migrate_fd_error(s, local_err);
|
2012-10-02 08:02:46 +00:00
|
|
|
error_propagate(errp, local_err);
|
2011-12-05 16:48:01 +00:00
|
|
|
return;
|
2011-11-09 20:29:01 +00:00
|
|
|
}
|
2011-02-22 22:54:21 +00:00
|
|
|
}
|
|
|
|
|
2011-11-28 00:54:09 +00:00
|
|
|
void qmp_migrate_cancel(Error **errp)
|
2011-02-22 22:54:21 +00:00
|
|
|
{
|
2011-10-05 11:50:43 +00:00
|
|
|
migrate_fd_cancel(migrate_get_current());
|
2011-02-22 22:54:21 +00:00
|
|
|
}
|
|
|
|
|
2012-08-06 18:42:54 +00:00
|
|
|
void qmp_migrate_set_cache_size(int64_t value, Error **errp)
|
|
|
|
{
|
|
|
|
MigrationState *s = migrate_get_current();
|
2014-01-30 18:08:34 +00:00
|
|
|
int64_t new_size;
|
2012-08-06 18:42:54 +00:00
|
|
|
|
|
|
|
/* Check for truncation */
|
|
|
|
if (value != (size_t)value) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
|
|
|
"exceeding address space");
|
2012-08-06 18:42:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-30 18:08:36 +00:00
|
|
|
/* Cache should not be larger than guest ram size */
|
|
|
|
if (value > ram_bytes_total()) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
|
|
|
"exceeds guest ram size ");
|
2014-01-30 18:08:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-30 18:08:34 +00:00
|
|
|
new_size = xbzrle_cache_resize(value);
|
|
|
|
if (new_size < 0) {
|
2015-03-17 10:54:50 +00:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
|
|
|
|
"is smaller than page size");
|
2014-01-30 18:08:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->xbzrle_cache_size = new_size;
|
2012-08-06 18:42:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int64_t qmp_query_migrate_cache_size(Error **errp)
|
|
|
|
{
|
|
|
|
return migrate_xbzrle_cache_size();
|
|
|
|
}
|
|
|
|
|
2011-11-28 13:59:37 +00:00
|
|
|
void qmp_migrate_set_speed(int64_t value, Error **errp)
|
2011-02-22 22:54:21 +00:00
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
2011-11-28 13:59:37 +00:00
|
|
|
if (value < 0) {
|
|
|
|
value = 0;
|
2010-12-13 16:30:12 +00:00
|
|
|
}
|
2013-02-22 16:36:44 +00:00
|
|
|
if (value > SIZE_MAX) {
|
|
|
|
value = SIZE_MAX;
|
|
|
|
}
|
2011-02-22 22:54:21 +00:00
|
|
|
|
2011-10-05 11:50:43 +00:00
|
|
|
s = migrate_get_current();
|
2011-11-28 13:59:37 +00:00
|
|
|
s->bandwidth_limit = value;
|
2016-01-15 03:37:42 +00:00
|
|
|
if (s->to_dst_file) {
|
|
|
|
qemu_file_set_rate_limit(s->to_dst_file,
|
|
|
|
s->bandwidth_limit / XFER_LIMIT_RATIO);
|
2013-02-22 16:36:44 +00:00
|
|
|
}
|
2011-02-22 22:54:21 +00:00
|
|
|
}
|
|
|
|
|
2011-11-28 01:18:01 +00:00
|
|
|
void qmp_migrate_set_downtime(double value, Error **errp)
|
2011-02-22 22:54:21 +00:00
|
|
|
{
|
2011-11-28 01:18:01 +00:00
|
|
|
value *= 1e9;
|
|
|
|
value = MAX(0, MIN(UINT64_MAX, value));
|
|
|
|
max_downtime = (uint64_t)value;
|
2010-12-13 16:30:12 +00:00
|
|
|
}
|
2012-08-06 18:42:53 +00:00
|
|
|
|
2015-11-05 18:10:51 +00:00
|
|
|
bool migrate_postcopy_ram(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
2016-03-11 09:53:36 +00:00
|
|
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM];
|
2015-11-05 18:10:51 +00:00
|
|
|
}
|
|
|
|
|
2013-06-24 09:49:42 +00:00
|
|
|
bool migrate_auto_converge(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
|
|
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE];
|
|
|
|
}
|
|
|
|
|
2013-07-18 07:48:50 +00:00
|
|
|
bool migrate_zero_blocks(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
|
|
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS];
|
|
|
|
}
|
|
|
|
|
2015-03-23 08:32:17 +00:00
|
|
|
bool migrate_use_compression(void)
|
|
|
|
{
|
2015-03-23 08:32:26 +00:00
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
|
|
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_COMPRESS];
|
2015-03-23 08:32:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int migrate_compress_level(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
2016-04-27 10:05:14 +00:00
|
|
|
return s->parameters.compress_level;
|
2015-03-23 08:32:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int migrate_compress_threads(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
2016-04-27 10:05:14 +00:00
|
|
|
return s->parameters.compress_threads;
|
2015-03-23 08:32:17 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 08:32:18 +00:00
|
|
|
int migrate_decompress_threads(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
2016-04-27 10:05:14 +00:00
|
|
|
return s->parameters.decompress_threads;
|
2015-03-23 08:32:18 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 12:44:05 +00:00
|
|
|
bool migrate_use_events(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
|
|
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_EVENTS];
|
|
|
|
}
|
|
|
|
|
2012-08-06 18:42:53 +00:00
|
|
|
int migrate_use_xbzrle(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
|
|
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_XBZRLE];
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t migrate_xbzrle_cache_size(void)
|
|
|
|
{
|
|
|
|
MigrationState *s;
|
|
|
|
|
|
|
|
s = migrate_get_current();
|
|
|
|
|
|
|
|
return s->xbzrle_cache_size;
|
|
|
|
}
|
2012-10-03 12:18:33 +00:00
|
|
|
|
2015-11-05 18:10:49 +00:00
|
|
|
/* migration thread support */
|
|
|
|
/*
|
|
|
|
* Something bad happened to the RP stream, mark an error
|
|
|
|
* The caller shall print or trace something to indicate why
|
|
|
|
*/
|
|
|
|
static void mark_source_rp_bad(MigrationState *s)
|
|
|
|
{
|
|
|
|
s->rp_state.error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rp_cmd_args {
|
|
|
|
ssize_t len; /* -1 = variable */
|
|
|
|
const char *name;
|
|
|
|
} rp_cmd_args[] = {
|
|
|
|
[MIG_RP_MSG_INVALID] = { .len = -1, .name = "INVALID" },
|
|
|
|
[MIG_RP_MSG_SHUT] = { .len = 4, .name = "SHUT" },
|
|
|
|
[MIG_RP_MSG_PONG] = { .len = 4, .name = "PONG" },
|
2015-11-05 18:11:07 +00:00
|
|
|
[MIG_RP_MSG_REQ_PAGES] = { .len = 12, .name = "REQ_PAGES" },
|
|
|
|
[MIG_RP_MSG_REQ_PAGES_ID] = { .len = -1, .name = "REQ_PAGES_ID" },
|
2015-11-05 18:10:49 +00:00
|
|
|
[MIG_RP_MSG_MAX] = { .len = -1, .name = "MAX" },
|
|
|
|
};
|
|
|
|
|
2015-11-05 18:11:07 +00:00
|
|
|
/*
|
|
|
|
* Process a request for pages received on the return path,
|
|
|
|
* We're allowed to send more than requested (e.g. to round to our page size)
|
|
|
|
* and we don't need to send pages that have already been sent.
|
|
|
|
*/
|
|
|
|
static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname,
|
|
|
|
ram_addr_t start, size_t len)
|
|
|
|
{
|
2015-11-05 18:11:08 +00:00
|
|
|
long our_host_ps = getpagesize();
|
|
|
|
|
2015-11-05 18:11:07 +00:00
|
|
|
trace_migrate_handle_rp_req_pages(rbname, start, len);
|
2015-11-05 18:11:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we currently insist on matching page sizes, just sanity check
|
|
|
|
* we're being asked for whole host pages.
|
|
|
|
*/
|
|
|
|
if (start & (our_host_ps-1) ||
|
|
|
|
(len & (our_host_ps-1))) {
|
|
|
|
error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT
|
|
|
|
" len: %zd", __func__, start, len);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ram_save_queue_pages(ms, rbname, start, len)) {
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
}
|
2015-11-05 18:11:07 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:49 +00:00
|
|
|
/*
|
|
|
|
* Handles messages sent on the return path towards the source VM
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static void *source_return_path_thread(void *opaque)
|
|
|
|
{
|
|
|
|
MigrationState *ms = opaque;
|
|
|
|
QEMUFile *rp = ms->rp_state.from_dst_file;
|
|
|
|
uint16_t header_len, header_type;
|
2016-03-09 06:12:12 +00:00
|
|
|
uint8_t buf[512];
|
2015-11-05 18:10:49 +00:00
|
|
|
uint32_t tmp32, sibling_error;
|
2015-11-05 18:11:07 +00:00
|
|
|
ram_addr_t start = 0; /* =0 to silence warning */
|
|
|
|
size_t len = 0, expected_len;
|
2015-11-05 18:10:49 +00:00
|
|
|
int res;
|
|
|
|
|
|
|
|
trace_source_return_path_thread_entry();
|
|
|
|
while (!ms->rp_state.error && !qemu_file_get_error(rp) &&
|
|
|
|
migration_is_setup_or_active(ms->state)) {
|
|
|
|
trace_source_return_path_thread_loop_top();
|
|
|
|
header_type = qemu_get_be16(rp);
|
|
|
|
header_len = qemu_get_be16(rp);
|
|
|
|
|
|
|
|
if (header_type >= MIG_RP_MSG_MAX ||
|
|
|
|
header_type == MIG_RP_MSG_INVALID) {
|
|
|
|
error_report("RP: Received invalid message 0x%04x length 0x%04x",
|
|
|
|
header_type, header_len);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((rp_cmd_args[header_type].len != -1 &&
|
|
|
|
header_len != rp_cmd_args[header_type].len) ||
|
2016-03-09 06:12:12 +00:00
|
|
|
header_len > sizeof(buf)) {
|
2015-11-05 18:10:49 +00:00
|
|
|
error_report("RP: Received '%s' message (0x%04x) with"
|
|
|
|
"incorrect length %d expecting %zu",
|
|
|
|
rp_cmd_args[header_type].name, header_type, header_len,
|
|
|
|
(size_t)rp_cmd_args[header_type].len);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We know we've got a valid header by this point */
|
|
|
|
res = qemu_get_buffer(rp, buf, header_len);
|
|
|
|
if (res != header_len) {
|
|
|
|
error_report("RP: Failed reading data for message 0x%04x"
|
|
|
|
" read %d expected %d",
|
|
|
|
header_type, res, header_len);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, we have the message and the data */
|
|
|
|
switch (header_type) {
|
|
|
|
case MIG_RP_MSG_SHUT:
|
|
|
|
sibling_error = be32_to_cpup((uint32_t *)buf);
|
|
|
|
trace_source_return_path_thread_shut(sibling_error);
|
|
|
|
if (sibling_error) {
|
|
|
|
error_report("RP: Sibling indicated error %d", sibling_error);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We'll let the main thread deal with closing the RP
|
|
|
|
* we could do a shutdown(2) on it, but we're the only user
|
|
|
|
* anyway, so there's nothing gained.
|
|
|
|
*/
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
case MIG_RP_MSG_PONG:
|
|
|
|
tmp32 = be32_to_cpup((uint32_t *)buf);
|
|
|
|
trace_source_return_path_thread_pong(tmp32);
|
|
|
|
break;
|
|
|
|
|
2015-11-05 18:11:07 +00:00
|
|
|
case MIG_RP_MSG_REQ_PAGES:
|
|
|
|
start = be64_to_cpup((uint64_t *)buf);
|
|
|
|
len = be32_to_cpup((uint32_t *)(buf + 8));
|
|
|
|
migrate_handle_rp_req_pages(ms, NULL, start, len);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MIG_RP_MSG_REQ_PAGES_ID:
|
|
|
|
expected_len = 12 + 1; /* header + termination */
|
|
|
|
|
|
|
|
if (header_len >= expected_len) {
|
|
|
|
start = be64_to_cpup((uint64_t *)buf);
|
|
|
|
len = be32_to_cpup((uint32_t *)(buf + 8));
|
|
|
|
/* Now we expect an idstr */
|
|
|
|
tmp32 = buf[12]; /* Length of the following idstr */
|
|
|
|
buf[13 + tmp32] = '\0';
|
|
|
|
expected_len += tmp32;
|
|
|
|
}
|
|
|
|
if (header_len != expected_len) {
|
|
|
|
error_report("RP: Req_Page_id with length %d expecting %zd",
|
|
|
|
header_len, expected_len);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len);
|
|
|
|
break;
|
|
|
|
|
2015-11-05 18:10:49 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-11-18 11:48:41 +00:00
|
|
|
if (qemu_file_get_error(rp)) {
|
2015-11-05 18:10:49 +00:00
|
|
|
trace_source_return_path_thread_bad_end();
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_source_return_path_thread_end();
|
|
|
|
out:
|
|
|
|
ms->rp_state.from_dst_file = NULL;
|
|
|
|
qemu_fclose(rp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int open_return_path_on_source(MigrationState *ms)
|
|
|
|
{
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file);
|
2015-11-05 18:10:49 +00:00
|
|
|
if (!ms->rp_state.from_dst_file) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_open_return_path_on_source();
|
|
|
|
qemu_thread_create(&ms->rp_state.rp_thread, "return path",
|
|
|
|
source_return_path_thread, ms, QEMU_THREAD_JOINABLE);
|
|
|
|
|
|
|
|
trace_open_return_path_on_source_continue();
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If this is a normal exit then the destination will send a SHUT and the
|
|
|
|
* rp_thread will exit, however if there's an error we need to cause
|
|
|
|
* it to exit.
|
|
|
|
*/
|
2016-01-15 03:37:42 +00:00
|
|
|
if (qemu_file_get_error(ms->to_dst_file) && ms->rp_state.from_dst_file) {
|
2015-11-05 18:10:49 +00:00
|
|
|
/*
|
|
|
|
* shutdown(2), if we have it, will cause it to unblock if it's stuck
|
|
|
|
* waiting for the destination.
|
|
|
|
*/
|
|
|
|
qemu_file_shutdown(ms->rp_state.from_dst_file);
|
|
|
|
mark_source_rp_bad(ms);
|
|
|
|
}
|
|
|
|
trace_await_return_path_close_on_source_joining();
|
|
|
|
qemu_thread_join(&ms->rp_state.rp_thread);
|
|
|
|
trace_await_return_path_close_on_source_close();
|
|
|
|
return ms->rp_state.error;
|
|
|
|
}
|
|
|
|
|
2015-11-05 18:11:05 +00:00
|
|
|
/*
|
|
|
|
* Switch from normal iteration to postcopy
|
|
|
|
* Returns non-0 on error
|
|
|
|
*/
|
|
|
|
static int postcopy_start(MigrationState *ms, bool *old_vm_running)
|
|
|
|
{
|
|
|
|
int ret;
|
2016-04-27 10:05:01 +00:00
|
|
|
QIOChannelBuffer *bioc;
|
|
|
|
QEMUFile *fb;
|
2015-11-05 18:11:05 +00:00
|
|
|
int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE,
|
2015-11-05 18:11:05 +00:00
|
|
|
MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
|
|
|
|
|
|
|
trace_postcopy_start();
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
trace_postcopy_start_set_run();
|
|
|
|
|
|
|
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
|
|
|
|
*old_vm_running = runstate_is_running();
|
|
|
|
global_state_store();
|
|
|
|
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
2015-12-22 13:07:08 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2015-11-05 18:11:05 +00:00
|
|
|
|
2015-12-22 13:07:08 +00:00
|
|
|
ret = bdrv_inactivate_all();
|
2015-11-05 18:11:05 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-11-11 14:02:27 +00:00
|
|
|
/*
|
|
|
|
* Cause any non-postcopiable, but iterative devices to
|
|
|
|
* send out their final data.
|
|
|
|
*/
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_state_complete_precopy(ms->to_dst_file, true);
|
2015-11-11 14:02:27 +00:00
|
|
|
|
2015-11-05 18:11:05 +00:00
|
|
|
/*
|
|
|
|
* in Finish migrate and with the io-lock held everything should
|
|
|
|
* be quiet, but we've potentially still got dirty pages and we
|
|
|
|
* need to tell the destination to throw any pages it's already received
|
|
|
|
* that are dirty
|
|
|
|
*/
|
|
|
|
if (ram_postcopy_send_discard_bitmap(ms)) {
|
|
|
|
error_report("postcopy send discard bitmap failed");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* send rest of state - note things that are doing postcopy
|
|
|
|
* will notice we're in POSTCOPY_ACTIVE and not actually
|
|
|
|
* wrap their state up here
|
|
|
|
*/
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX);
|
2015-11-05 18:11:05 +00:00
|
|
|
/* Ping just for debugging, helps line traces up */
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_send_ping(ms->to_dst_file, 2);
|
2015-11-05 18:11:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* While loading the device state we may trigger page transfer
|
|
|
|
* requests and the fd must be free to process those, and thus
|
|
|
|
* the destination must read the whole device state off the fd before
|
|
|
|
* it starts processing it. Unfortunately the ad-hoc migration format
|
|
|
|
* doesn't allow the destination to know the size to read without fully
|
|
|
|
* parsing it through each devices load-state code (especially the open
|
|
|
|
* coded devices that use get/put).
|
|
|
|
* So we wrap the device state up in a package with a length at the start;
|
|
|
|
* to do this we use a qemu_buf to hold the whole of the device state.
|
|
|
|
*/
|
2016-04-27 10:05:01 +00:00
|
|
|
bioc = qio_channel_buffer_new(4096);
|
|
|
|
fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc));
|
|
|
|
object_unref(OBJECT(bioc));
|
2015-11-05 18:11:05 +00:00
|
|
|
|
2015-11-05 18:11:18 +00:00
|
|
|
/*
|
|
|
|
* Make sure the receiver can get incoming pages before we send the rest
|
|
|
|
* of the state
|
|
|
|
*/
|
|
|
|
qemu_savevm_send_postcopy_listen(fb);
|
|
|
|
|
2015-11-11 14:02:27 +00:00
|
|
|
qemu_savevm_state_complete_precopy(fb, false);
|
2015-11-05 18:11:05 +00:00
|
|
|
qemu_savevm_send_ping(fb, 3);
|
|
|
|
|
|
|
|
qemu_savevm_send_postcopy_run(fb);
|
|
|
|
|
|
|
|
/* <><> end of stuff going into the package */
|
|
|
|
|
|
|
|
/* Now send that blob */
|
2016-04-27 10:05:01 +00:00
|
|
|
if (qemu_savevm_send_packaged(ms->to_dst_file, bioc->data, bioc->usage)) {
|
2015-11-05 18:11:05 +00:00
|
|
|
goto fail_closefb;
|
|
|
|
}
|
|
|
|
qemu_fclose(fb);
|
2016-02-22 17:17:32 +00:00
|
|
|
|
|
|
|
/* Send a notify to give a chance for anything that needs to happen
|
|
|
|
* at the transition to postcopy and after the device state; in particular
|
|
|
|
* spice needs to trigger a transition now
|
|
|
|
*/
|
|
|
|
ms->postcopy_after_devices = true;
|
|
|
|
notifier_list_notify(&migration_state_notifiers, ms);
|
|
|
|
|
2015-11-05 18:11:05 +00:00
|
|
|
ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop;
|
|
|
|
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Although this ping is just for debug, it could potentially be
|
|
|
|
* used for getting a better measurement of downtime at the source.
|
|
|
|
*/
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_send_ping(ms->to_dst_file, 4);
|
2015-11-05 18:11:05 +00:00
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
ret = qemu_file_get_error(ms->to_dst_file);
|
2015-11-05 18:11:05 +00:00
|
|
|
if (ret) {
|
|
|
|
error_report("postcopy_start: Migration stream errored");
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
|
2015-11-05 18:11:05 +00:00
|
|
|
MIGRATION_STATUS_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
fail_closefb:
|
|
|
|
qemu_fclose(fb);
|
|
|
|
fail:
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
|
2015-11-05 18:11:05 +00:00
|
|
|
MIGRATION_STATUS_FAILED);
|
|
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-08-13 10:51:31 +00:00
|
|
|
/**
|
|
|
|
* migration_completion: Used by migration_thread when there's not much left.
|
|
|
|
* The caller 'breaks' the loop when this returns.
|
|
|
|
*
|
|
|
|
* @s: Current migration state
|
2015-11-05 18:10:57 +00:00
|
|
|
* @current_active_state: The migration state we expect to be in
|
2015-08-13 10:51:31 +00:00
|
|
|
* @*old_vm_running: Pointer to old_vm_running flag
|
|
|
|
* @*start_time: Pointer to time to update
|
|
|
|
*/
|
2015-11-05 18:10:57 +00:00
|
|
|
static void migration_completion(MigrationState *s, int current_active_state,
|
|
|
|
bool *old_vm_running,
|
2015-08-13 10:51:31 +00:00
|
|
|
int64_t *start_time)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2015-11-05 18:11:06 +00:00
|
|
|
if (s->state == MIGRATION_STATUS_ACTIVE) {
|
|
|
|
qemu_mutex_lock_iothread();
|
|
|
|
*start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
|
|
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
|
|
|
|
*old_vm_running = runstate_is_running();
|
|
|
|
ret = global_state_store();
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
2015-12-22 13:07:08 +00:00
|
|
|
if (ret >= 0) {
|
|
|
|
ret = bdrv_inactivate_all();
|
|
|
|
}
|
2015-11-05 18:11:06 +00:00
|
|
|
if (ret >= 0) {
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX);
|
|
|
|
qemu_savevm_state_complete_precopy(s->to_dst_file, false);
|
2015-11-05 18:11:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
qemu_mutex_unlock_iothread();
|
2015-08-13 10:51:31 +00:00
|
|
|
|
2015-11-05 18:11:06 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
goto fail;
|
2015-08-13 10:51:31 +00:00
|
|
|
}
|
2015-11-05 18:11:06 +00:00
|
|
|
} else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
|
|
|
trace_migration_completion_postcopy_end();
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_state_complete_postcopy(s->to_dst_file);
|
2015-11-05 18:11:06 +00:00
|
|
|
trace_migration_completion_postcopy_end_after_complete();
|
2015-08-13 10:51:31 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:11:06 +00:00
|
|
|
/*
|
|
|
|
* If rp was opened we must clean up the thread before
|
|
|
|
* cleaning everything else up (since if there are no failures
|
|
|
|
* it will wait for the destination to send it's status in
|
|
|
|
* a SHUT command).
|
|
|
|
* Postcopy opens rp if enabled (even if it's not avtivated)
|
|
|
|
*/
|
|
|
|
if (migrate_postcopy_ram()) {
|
|
|
|
int rp_error;
|
|
|
|
trace_migration_completion_postcopy_end_before_rp();
|
|
|
|
rp_error = await_return_path_close_on_source(s);
|
|
|
|
trace_migration_completion_postcopy_end_after_rp(rp_error);
|
|
|
|
if (rp_error) {
|
2016-05-18 13:44:36 +00:00
|
|
|
goto fail_invalidate;
|
2015-11-05 18:11:06 +00:00
|
|
|
}
|
2015-08-13 10:51:31 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
if (qemu_file_get_error(s->to_dst_file)) {
|
2015-08-13 10:51:31 +00:00
|
|
|
trace_migration_completion_file_err();
|
2016-05-18 13:44:36 +00:00
|
|
|
goto fail_invalidate;
|
2015-08-13 10:51:31 +00:00
|
|
|
}
|
|
|
|
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, current_active_state,
|
|
|
|
MIGRATION_STATUS_COMPLETED);
|
2015-08-13 10:51:31 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-18 13:44:36 +00:00
|
|
|
fail_invalidate:
|
|
|
|
/* If not doing postcopy, vm_start() will be called: let's regain
|
|
|
|
* control on images.
|
|
|
|
*/
|
|
|
|
if (s->state == MIGRATION_STATUS_ACTIVE) {
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
bdrv_invalidate_cache_all(&local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-13 10:51:31 +00:00
|
|
|
fail:
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, current_active_state,
|
|
|
|
MIGRATION_STATUS_FAILED);
|
2015-08-13 10:51:31 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:10:49 +00:00
|
|
|
/*
|
|
|
|
* Master migration thread on the source VM.
|
|
|
|
* It drives the migration and pumps the data down the outgoing channel.
|
|
|
|
*/
|
2013-02-22 16:36:30 +00:00
|
|
|
static void *migration_thread(void *opaque)
|
2012-10-03 12:18:33 +00:00
|
|
|
{
|
2012-12-19 08:55:50 +00:00
|
|
|
MigrationState *s = opaque;
|
2015-11-05 18:11:05 +00:00
|
|
|
/* Used by the bandwidth calcs, updated later */
|
2013-08-21 15:03:08 +00:00
|
|
|
int64_t initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
|
|
|
int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
2013-02-22 16:36:43 +00:00
|
|
|
int64_t initial_bytes = 0;
|
2012-10-03 12:18:33 +00:00
|
|
|
int64_t max_size = 0;
|
2013-02-22 16:36:18 +00:00
|
|
|
int64_t start_time = initial_time;
|
2015-11-02 07:37:00 +00:00
|
|
|
int64_t end_time;
|
2013-02-22 16:36:18 +00:00
|
|
|
bool old_vm_running = false;
|
2015-11-05 18:11:05 +00:00
|
|
|
bool entered_postcopy = false;
|
|
|
|
/* The active state we expect to be in; ACTIVE or POSTCOPY_ACTIVE */
|
|
|
|
enum MigrationStatus current_active_state = MIGRATION_STATUS_ACTIVE;
|
2012-10-03 18:16:24 +00:00
|
|
|
|
2015-07-09 06:55:38 +00:00
|
|
|
rcu_register_thread();
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_state_header(s->to_dst_file);
|
2015-11-05 18:11:05 +00:00
|
|
|
|
|
|
|
if (migrate_postcopy_ram()) {
|
|
|
|
/* Now tell the dest that it should open its end so it can reply */
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_send_open_return_path(s->to_dst_file);
|
2015-11-05 18:11:05 +00:00
|
|
|
|
|
|
|
/* And do a ping that will make stuff easier to debug */
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_send_ping(s->to_dst_file, 1);
|
2015-11-05 18:11:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the destination that we *might* want to do postcopy later;
|
|
|
|
* if the other end can't do postcopy it should fail now, nice and
|
|
|
|
* early.
|
|
|
|
*/
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_send_postcopy_advise(s->to_dst_file);
|
2015-11-05 18:11:05 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_state_begin(s->to_dst_file, &s->params);
|
2012-10-03 12:18:33 +00:00
|
|
|
|
2013-08-21 15:03:08 +00:00
|
|
|
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
|
2015-11-05 18:11:05 +00:00
|
|
|
current_active_state = MIGRATION_STATUS_ACTIVE;
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
|
|
|
|
MIGRATION_STATUS_ACTIVE);
|
rdma: introduce MIG_STATE_NONE and change MIG_STATE_SETUP state transition
As described in the previous patch, until now, the MIG_STATE_SETUP
state was not really a 'formal' state. It has been used as a 'zero' state
(what we're calling 'NONE' here) and QEMU has been unconditionally transitioning
into this state when the QMP migration command was called. Instead we want to
introduce MIG_STATE_NONE, which is our starting state in the state machine, and
then immediately transition into the MIG_STATE_SETUP state when the QMP migrate
command is issued.
In order to do this, we must delay the transition into MIG_STATE_ACTIVE until
later in the migration_thread(). This is done to be able to timestamp the amount of
time spent in the SETUP state for proper accounting to the user during
an RDMA migration.
Furthermore, the management software, until now, has never been aware of the
existence of the SETUP state whatsoever. This must change, because, timing of this
state implies that the state actually exists.
These two patches cannot be separated because the 'query_migrate' QMP
switch statement needs to know how to handle this new state transition.
Reviewed-by: Juan Quintela <quintela@redhat.com>
Tested-by: Michael R. Hines <mrhines@us.ibm.com>
Signed-off-by: Michael R. Hines <mrhines@us.ibm.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
2013-07-22 14:01:57 +00:00
|
|
|
|
2015-11-05 18:10:58 +00:00
|
|
|
trace_migration_thread_setup_complete();
|
|
|
|
|
|
|
|
while (s->state == MIGRATION_STATUS_ACTIVE ||
|
|
|
|
s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
2013-02-01 11:39:08 +00:00
|
|
|
int64_t current_time;
|
2012-10-03 18:33:34 +00:00
|
|
|
uint64_t pending_size;
|
2012-10-03 12:18:33 +00:00
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
if (!qemu_file_rate_limit(s->to_dst_file)) {
|
2015-11-05 18:10:54 +00:00
|
|
|
uint64_t pend_post, pend_nonpost;
|
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_state_pending(s->to_dst_file, max_size, &pend_nonpost,
|
2015-11-05 18:10:54 +00:00
|
|
|
&pend_post);
|
|
|
|
pending_size = pend_nonpost + pend_post;
|
|
|
|
trace_migrate_pending(pending_size, max_size,
|
|
|
|
pend_post, pend_nonpost);
|
2012-10-17 19:06:31 +00:00
|
|
|
if (pending_size && pending_size >= max_size) {
|
2015-11-05 18:11:05 +00:00
|
|
|
/* Still a significant amount to transfer */
|
|
|
|
|
|
|
|
if (migrate_postcopy_ram() &&
|
|
|
|
s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE &&
|
|
|
|
pend_nonpost <= max_size &&
|
|
|
|
atomic_read(&s->start_postcopy)) {
|
|
|
|
|
|
|
|
if (!postcopy_start(s, &old_vm_running)) {
|
|
|
|
current_active_state = MIGRATION_STATUS_POSTCOPY_ACTIVE;
|
|
|
|
entered_postcopy = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Just another iteration step */
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_savevm_state_iterate(s->to_dst_file, entered_postcopy);
|
2012-10-03 18:33:34 +00:00
|
|
|
} else {
|
2015-08-13 10:51:31 +00:00
|
|
|
trace_migration_thread_low_pending(pending_size);
|
2015-11-05 18:11:05 +00:00
|
|
|
migration_completion(s, current_active_state,
|
2015-11-05 18:10:57 +00:00
|
|
|
&old_vm_running, &start_time);
|
2015-08-13 10:51:31 +00:00
|
|
|
break;
|
2012-10-03 18:33:34 +00:00
|
|
|
}
|
|
|
|
}
|
2013-02-22 16:36:20 +00:00
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
if (qemu_file_get_error(s->to_dst_file)) {
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, current_active_state,
|
|
|
|
MIGRATION_STATUS_FAILED);
|
2015-11-05 18:11:05 +00:00
|
|
|
trace_migration_thread_file_err();
|
2013-02-22 16:36:33 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-08-21 15:03:08 +00:00
|
|
|
current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
2012-10-03 12:18:33 +00:00
|
|
|
if (current_time >= initial_time + BUFFER_DELAY) {
|
2016-01-15 03:37:42 +00:00
|
|
|
uint64_t transferred_bytes = qemu_ftell(s->to_dst_file) -
|
|
|
|
initial_bytes;
|
Revert "migration: don't account sleep time for calculating bandwidth"
This reverts commit 7161082c8d8cf167c508976887a0a63f4db92b51.
Reverting this patch fixes a divide-by-zero error in qemu that can be
fairly reliably triggered by doing block migration. In this case, the
configuration/error was:
source: temp/x86_64-softmmu/qemu-system-x86_64 -enable-kvm -L temp-bios
-M pc-i440fx-1.4 -m 512M -kernel boot/vmlinuz-x86_64 -initrd
boot/test-initramfs-x86_64.img.gz -vga std -append seed=1234 -drive
file=disk1.img,if=virtio -drive file=disk2.img,if=virtio -device
virtio-net-pci,netdev=net0 -netdev user,id=net0 -monitor
unix:/tmp/vm-hmp.sock,server,nowait -qmp
unix:/tmp/vm-qmp.sock,server,nowait -vnc :100
16837 Floating point exception(core dumped)
target: temp/x86_64-softmmu/qemu-system-x86_64 -enable-kvm -L temp-bios
-M pc-i440fx-1.4 -m 512M -kernel boot/vmlinuz-x86_64 -initrd
boot/test-initramfs-x86_64.img.gz -vga std -append seed=1234 -drive
file=target_disk1.img,if=virtio -drive file=target_disk2.img,if=virtio
-device virtio-net-pci,netdev=net0 -netdev user,id=net0 -incoming
unix:/tmp/migrate.sock -monitor
unix:/tmp/vm-hmp-incoming.sock,server,nowait -qmp
unix:/tmp/vm-qmp-incoming.sock,server,nowait -vnc :101
Receiving block device images
20 %
21 %
load of migration failed
This revert potentially re-introduces a bug that was present in 1.4,
but fixes a prevalent issue with block migration so we should revert
it for now and take an updated patch later.
Conflicts:
migration.c
* fixed up to remove logic introduced in 7161082c while leaving
changes in HEAD intact
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1368739544-31021-1-git-send-email-mdroth@linux.vnet.ibm.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-05-16 21:25:44 +00:00
|
|
|
uint64_t time_spent = current_time - initial_time;
|
2015-01-26 11:12:27 +00:00
|
|
|
double bandwidth = (double)transferred_bytes / time_spent;
|
2012-10-03 12:18:33 +00:00
|
|
|
max_size = bandwidth * migrate_max_downtime() / 1000000;
|
|
|
|
|
2016-01-24 14:09:57 +00:00
|
|
|
s->mbps = (((double) transferred_bytes * 8.0) /
|
|
|
|
((double) time_spent / 1000.0)) / 1000.0 / 1000.0;
|
2013-06-26 01:35:30 +00:00
|
|
|
|
2014-03-10 23:42:29 +00:00
|
|
|
trace_migrate_transferred(transferred_bytes, time_spent,
|
|
|
|
bandwidth, max_size);
|
2013-02-01 12:22:37 +00:00
|
|
|
/* if we haven't sent anything, we don't want to recalculate
|
|
|
|
10000 is a small enough number for our purposes */
|
|
|
|
if (s->dirty_bytes_rate && transferred_bytes > 10000) {
|
|
|
|
s->expected_downtime = s->dirty_bytes_rate / bandwidth;
|
|
|
|
}
|
2012-10-03 12:18:33 +00:00
|
|
|
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_file_reset_rate_limit(s->to_dst_file);
|
2012-10-03 12:18:33 +00:00
|
|
|
initial_time = current_time;
|
2016-01-15 03:37:42 +00:00
|
|
|
initial_bytes = qemu_ftell(s->to_dst_file);
|
2012-10-03 12:18:33 +00:00
|
|
|
}
|
2016-01-15 03:37:42 +00:00
|
|
|
if (qemu_file_rate_limit(s->to_dst_file)) {
|
2012-10-03 12:18:33 +00:00
|
|
|
/* usleep expects microseconds */
|
|
|
|
g_usleep((initial_time + BUFFER_DELAY - current_time)*1000);
|
|
|
|
}
|
2013-02-22 16:36:18 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 18:11:05 +00:00
|
|
|
trace_migration_thread_after_loop();
|
2015-09-08 17:12:35 +00:00
|
|
|
/* If we enabled cpu throttling for auto-converge, turn it off. */
|
|
|
|
cpu_throttle_stop();
|
2015-11-02 07:37:00 +00:00
|
|
|
end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
2015-09-08 17:12:35 +00:00
|
|
|
|
2013-02-22 16:36:20 +00:00
|
|
|
qemu_mutex_lock_iothread();
|
2015-11-02 07:37:01 +00:00
|
|
|
qemu_savevm_state_cleanup();
|
2015-03-13 08:08:38 +00:00
|
|
|
if (s->state == MIGRATION_STATUS_COMPLETED) {
|
2016-01-15 03:37:42 +00:00
|
|
|
uint64_t transferred_bytes = qemu_ftell(s->to_dst_file);
|
2013-02-22 16:36:18 +00:00
|
|
|
s->total_time = end_time - s->total_time;
|
2015-11-05 18:11:05 +00:00
|
|
|
if (!entered_postcopy) {
|
|
|
|
s->downtime = end_time - start_time;
|
|
|
|
}
|
2014-05-12 08:46:00 +00:00
|
|
|
if (s->total_time) {
|
|
|
|
s->mbps = (((double) transferred_bytes * 8.0) /
|
|
|
|
((double) s->total_time)) / 1000;
|
|
|
|
}
|
2013-02-22 16:36:18 +00:00
|
|
|
runstate_set(RUN_STATE_POSTMIGRATE);
|
|
|
|
} else {
|
2015-11-05 18:11:05 +00:00
|
|
|
if (old_vm_running && !entered_postcopy) {
|
2013-02-22 16:36:18 +00:00
|
|
|
vm_start();
|
2013-02-22 16:36:17 +00:00
|
|
|
}
|
2012-10-03 12:18:33 +00:00
|
|
|
}
|
2013-02-22 16:36:21 +00:00
|
|
|
qemu_bh_schedule(s->cleanup_bh);
|
2013-02-22 16:36:17 +00:00
|
|
|
qemu_mutex_unlock_iothread();
|
2013-02-22 16:36:20 +00:00
|
|
|
|
2015-07-09 06:55:38 +00:00
|
|
|
rcu_unregister_thread();
|
2012-10-03 12:18:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-19 08:55:50 +00:00
|
|
|
void migrate_fd_connect(MigrationState *s)
|
2012-10-03 12:18:33 +00:00
|
|
|
{
|
2013-02-01 10:12:26 +00:00
|
|
|
/* This is a best 1st approximation. ns to ms */
|
|
|
|
s->expected_downtime = max_downtime/1000000;
|
2013-02-22 16:36:21 +00:00
|
|
|
s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s);
|
2012-10-03 12:18:33 +00:00
|
|
|
|
2016-04-27 10:04:57 +00:00
|
|
|
qemu_file_set_blocking(s->to_dst_file, true);
|
2016-01-15 03:37:42 +00:00
|
|
|
qemu_file_set_rate_limit(s->to_dst_file,
|
2013-02-22 16:36:44 +00:00
|
|
|
s->bandwidth_limit / XFER_LIMIT_RATIO);
|
|
|
|
|
2013-07-29 13:01:57 +00:00
|
|
|
/* Notify before starting migration thread */
|
|
|
|
notifier_list_notify(&migration_state_notifiers, s);
|
|
|
|
|
2015-11-05 18:11:05 +00:00
|
|
|
/*
|
|
|
|
* Open the return path; currently for postcopy but other things might
|
|
|
|
* also want it.
|
|
|
|
*/
|
|
|
|
if (migrate_postcopy_ram()) {
|
|
|
|
if (open_return_path_on_source(s)) {
|
|
|
|
error_report("Unable to open return-path for postcopy");
|
2015-12-16 11:47:33 +00:00
|
|
|
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
|
2015-11-05 18:11:05 +00:00
|
|
|
MIGRATION_STATUS_FAILED);
|
|
|
|
migrate_fd_cleanup(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-23 08:32:17 +00:00
|
|
|
migrate_compress_threads_create();
|
2014-01-30 10:20:32 +00:00
|
|
|
qemu_thread_create(&s->thread, "migration", migration_thread, s,
|
2013-02-22 16:36:21 +00:00
|
|
|
QEMU_THREAD_JOINABLE);
|
2015-11-05 18:11:05 +00:00
|
|
|
s->migration_thread_running = true;
|
2012-10-03 12:18:33 +00:00
|
|
|
}
|
2015-11-05 18:10:52 +00:00
|
|
|
|
|
|
|
PostcopyState postcopy_state_get(void)
|
|
|
|
{
|
|
|
|
return atomic_mb_read(&incoming_postcopy_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the state and return the old state */
|
|
|
|
PostcopyState postcopy_state_set(PostcopyState new_state)
|
|
|
|
{
|
|
|
|
return atomic_xchg(&incoming_postcopy_state, new_state);
|
|
|
|
}
|
|
|
|
|