mirror of https://github.com/xemu-project/xemu.git
Merge remote-tracking branch 'qmp/queue/qmp' into staging
* qmp/queue/qmp: qapi: Convert migrate Purge migration of (almost) everything to do with monitors Error: Introduce error_copy() QError: Introduce new errors for the migration command
This commit is contained in:
commit
5c20f4e54a
|
@ -260,7 +260,7 @@ static void sort_ram_list(void)
|
|||
g_free(blocks);
|
||||
}
|
||||
|
||||
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
||||
int ram_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
{
|
||||
ram_addr_t addr;
|
||||
uint64_t bytes_transferred_last;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "hw/hw.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "monitor.h"
|
||||
#include "block-migration.h"
|
||||
#include "migration.h"
|
||||
#include "blockdev.h"
|
||||
|
@ -204,8 +203,7 @@ static void blk_mig_read_cb(void *opaque, int ret)
|
|||
assert(block_mig_state.submitted >= 0);
|
||||
}
|
||||
|
||||
static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
|
||||
BlkMigDevState *bmds)
|
||||
static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
||||
{
|
||||
int64_t total_sectors = bmds->total_sectors;
|
||||
int64_t cur_sector = bmds->cur_sector;
|
||||
|
@ -272,7 +270,6 @@ static void set_dirty_tracking(int enable)
|
|||
|
||||
static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
BlkMigDevState *bmds;
|
||||
int64_t sectors;
|
||||
|
||||
|
@ -295,19 +292,17 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
|||
block_mig_state.total_sector_sum += sectors;
|
||||
|
||||
if (bmds->shared_base) {
|
||||
monitor_printf(mon, "Start migration for %s with shared base "
|
||||
"image\n",
|
||||
bs->device_name);
|
||||
DPRINTF("Start migration for %s with shared base image\n",
|
||||
bs->device_name);
|
||||
} else {
|
||||
monitor_printf(mon, "Start full migration for %s\n",
|
||||
bs->device_name);
|
||||
DPRINTF("Start full migration for %s\n", bs->device_name);
|
||||
}
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_blk_migration(Monitor *mon, QEMUFile *f)
|
||||
static void init_blk_migration(QEMUFile *f)
|
||||
{
|
||||
block_mig_state.submitted = 0;
|
||||
block_mig_state.read_done = 0;
|
||||
|
@ -318,10 +313,10 @@ static void init_blk_migration(Monitor *mon, QEMUFile *f)
|
|||
block_mig_state.total_time = 0;
|
||||
block_mig_state.reads = 0;
|
||||
|
||||
bdrv_iterate(init_blk_migration_it, mon);
|
||||
bdrv_iterate(init_blk_migration_it, NULL);
|
||||
}
|
||||
|
||||
static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
|
||||
static int blk_mig_save_bulked_block(QEMUFile *f)
|
||||
{
|
||||
int64_t completed_sector_sum = 0;
|
||||
BlkMigDevState *bmds;
|
||||
|
@ -330,7 +325,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
|
|||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (bmds->bulk_completed == 0) {
|
||||
if (mig_save_device_bulk(mon, f, bmds) == 1) {
|
||||
if (mig_save_device_bulk(f, bmds) == 1) {
|
||||
/* completed bulk section for this device */
|
||||
bmds->bulk_completed = 1;
|
||||
}
|
||||
|
@ -352,8 +347,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
|
|||
block_mig_state.prev_progress = progress;
|
||||
qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
|
||||
| BLK_MIG_FLAG_PROGRESS);
|
||||
monitor_printf(mon, "Completed %d %%\r", progress);
|
||||
monitor_flush(mon);
|
||||
DPRINTF("Completed %d %%\r", progress);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -368,8 +362,8 @@ static void blk_mig_reset_dirty_cursor(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
|
||||
BlkMigDevState *bmds, int is_async)
|
||||
static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
|
||||
int is_async)
|
||||
{
|
||||
BlkMigBlock *blk;
|
||||
int64_t total_sectors = bmds->total_sectors;
|
||||
|
@ -428,20 +422,20 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
|
|||
return (bmds->cur_dirty >= bmds->total_sectors);
|
||||
|
||||
error:
|
||||
monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector);
|
||||
DPRINTF("Error reading sector %" PRId64 "\n", sector);
|
||||
qemu_file_set_error(f, ret);
|
||||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async)
|
||||
static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
int ret = 0;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) {
|
||||
if (mig_save_device_dirty(f, bmds, is_async) == 0) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -520,7 +514,7 @@ static int is_stage2_completed(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void blk_mig_cleanup(Monitor *mon)
|
||||
static void blk_mig_cleanup(void)
|
||||
{
|
||||
BlkMigDevState *bmds;
|
||||
BlkMigBlock *blk;
|
||||
|
@ -540,11 +534,9 @@ static void blk_mig_cleanup(Monitor *mon)
|
|||
g_free(blk->buf);
|
||||
g_free(blk);
|
||||
}
|
||||
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
||||
static int block_save_live(QEMUFile *f, int stage, void *opaque)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -552,7 +544,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
stage, block_mig_state.submitted, block_mig_state.transferred);
|
||||
|
||||
if (stage < 0) {
|
||||
blk_mig_cleanup(mon);
|
||||
blk_mig_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -563,7 +555,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
}
|
||||
|
||||
if (stage == 1) {
|
||||
init_blk_migration(mon, f);
|
||||
init_blk_migration(f);
|
||||
|
||||
/* start track dirty blocks */
|
||||
set_dirty_tracking(1);
|
||||
|
@ -573,7 +565,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup(mon);
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -586,12 +578,12 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
qemu_file_get_rate_limit(f)) {
|
||||
if (block_mig_state.bulk_completed == 0) {
|
||||
/* first finish the bulk phase */
|
||||
if (blk_mig_save_bulked_block(mon, f) == 0) {
|
||||
if (blk_mig_save_bulked_block(f) == 0) {
|
||||
/* finished saving bulk on all devices */
|
||||
block_mig_state.bulk_completed = 1;
|
||||
}
|
||||
} else {
|
||||
if (blk_mig_save_dirty_block(mon, f, 1) == 0) {
|
||||
if (blk_mig_save_dirty_block(f, 1) == 0) {
|
||||
/* no more dirty blocks */
|
||||
break;
|
||||
}
|
||||
|
@ -602,7 +594,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret) {
|
||||
blk_mig_cleanup(mon);
|
||||
blk_mig_cleanup();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -612,8 +604,8 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
all async read completed */
|
||||
assert(block_mig_state.submitted == 0);
|
||||
|
||||
while (blk_mig_save_dirty_block(mon, f, 0) != 0);
|
||||
blk_mig_cleanup(mon);
|
||||
while (blk_mig_save_dirty_block(f, 0) != 0);
|
||||
blk_mig_cleanup();
|
||||
|
||||
/* report completion */
|
||||
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
|
||||
|
@ -623,7 +615,7 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
|
|||
return ret;
|
||||
}
|
||||
|
||||
monitor_printf(mon, "Block migration completed\n");
|
||||
DPRINTF("Block migration completed\n");
|
||||
}
|
||||
|
||||
qemu_put_be64(f, BLK_MIG_FLAG_EOS);
|
||||
|
|
13
error.c
13
error.c
|
@ -43,6 +43,19 @@ void error_set(Error **errp, const char *fmt, ...)
|
|||
*errp = err;
|
||||
}
|
||||
|
||||
Error *error_copy(const Error *err)
|
||||
{
|
||||
Error *err_new;
|
||||
|
||||
err_new = g_malloc0(sizeof(*err));
|
||||
err_new->msg = g_strdup(err->msg);
|
||||
err_new->fmt = err->fmt;
|
||||
err_new->obj = err->obj;
|
||||
QINCREF(err_new->obj);
|
||||
|
||||
return err_new;
|
||||
}
|
||||
|
||||
bool error_is_set(Error **errp)
|
||||
{
|
||||
return (errp && *errp);
|
||||
|
|
5
error.h
5
error.h
|
@ -34,6 +34,11 @@ void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
|||
*/
|
||||
bool error_is_set(Error **err);
|
||||
|
||||
/**
|
||||
* Returns an exact copy of the error passed as an argument.
|
||||
*/
|
||||
Error *error_copy(const Error *err);
|
||||
|
||||
/**
|
||||
* Get a human readable representation of an error object.
|
||||
*/
|
||||
|
|
|
@ -806,8 +806,7 @@ ETEXI
|
|||
" full copy of disk\n\t\t\t -i for migration without "
|
||||
"shared storage with incremental copy of disk "
|
||||
"(base image shared between src and destination)",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd_new = do_migrate,
|
||||
.mhandler.cmd = hmp_migrate,
|
||||
},
|
||||
|
||||
|
||||
|
|
74
hmp.c
74
hmp.c
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include "hmp.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "qmp-commands.h"
|
||||
|
||||
static void hmp_handle_error(Monitor *mon, Error **errp)
|
||||
|
@ -860,3 +861,76 @@ void hmp_block_job_cancel(Monitor *mon, const QDict *qdict)
|
|||
|
||||
hmp_handle_error(mon, &error);
|
||||
}
|
||||
|
||||
typedef struct MigrationStatus
|
||||
{
|
||||
QEMUTimer *timer;
|
||||
Monitor *mon;
|
||||
bool is_block_migration;
|
||||
} MigrationStatus;
|
||||
|
||||
static void hmp_migrate_status_cb(void *opaque)
|
||||
{
|
||||
MigrationStatus *status = opaque;
|
||||
MigrationInfo *info;
|
||||
|
||||
info = qmp_query_migrate(NULL);
|
||||
if (!info->has_status || strcmp(info->status, "active") == 0) {
|
||||
if (info->has_disk) {
|
||||
int progress;
|
||||
|
||||
if (info->disk->remaining) {
|
||||
progress = info->disk->transferred * 100 / info->disk->total;
|
||||
} else {
|
||||
progress = 100;
|
||||
}
|
||||
|
||||
monitor_printf(status->mon, "Completed %d %%\r", progress);
|
||||
monitor_flush(status->mon);
|
||||
}
|
||||
|
||||
qemu_mod_timer(status->timer, qemu_get_clock_ms(rt_clock) + 1000);
|
||||
} else {
|
||||
if (status->is_block_migration) {
|
||||
monitor_printf(status->mon, "\n");
|
||||
}
|
||||
monitor_resume(status->mon);
|
||||
qemu_del_timer(status->timer);
|
||||
g_free(status);
|
||||
}
|
||||
|
||||
qapi_free_MigrationInfo(info);
|
||||
}
|
||||
|
||||
void hmp_migrate(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
int detach = qdict_get_try_bool(qdict, "detach", 0);
|
||||
int blk = qdict_get_try_bool(qdict, "blk", 0);
|
||||
int inc = qdict_get_try_bool(qdict, "inc", 0);
|
||||
const char *uri = qdict_get_str(qdict, "uri");
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "migrate: %s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!detach) {
|
||||
MigrationStatus *status;
|
||||
|
||||
if (monitor_suspend(mon) < 0) {
|
||||
monitor_printf(mon, "terminal does not allow synchronous "
|
||||
"migration, continuing detached\n");
|
||||
return;
|
||||
}
|
||||
|
||||
status = g_malloc0(sizeof(*status));
|
||||
status->mon = mon;
|
||||
status->is_block_migration = blk || inc;
|
||||
status->timer = qemu_new_timer_ms(rt_clock, hmp_migrate_status_cb,
|
||||
status);
|
||||
qemu_mod_timer(status->timer, qemu_get_clock_ms(rt_clock));
|
||||
}
|
||||
}
|
||||
|
|
1
hmp.h
1
hmp.h
|
@ -59,5 +59,6 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
|
|||
void hmp_block_stream(Monitor *mon, const QDict *qdict);
|
||||
void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
|
||||
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
|
||||
void hmp_migrate(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -75,7 +75,7 @@ static int fd_close(MigrationState *s)
|
|||
|
||||
int fd_start_outgoing_migration(MigrationState *s, const char *fdname)
|
||||
{
|
||||
s->fd = monitor_get_fd(s->mon, fdname);
|
||||
s->fd = monitor_get_fd(cur_mon, fdname);
|
||||
if (s->fd == -1) {
|
||||
DPRINTF("fd_migration: invalid file descriptor identifier\n");
|
||||
goto err_after_get_fd;
|
||||
|
|
74
migration.c
74
migration.c
|
@ -158,16 +158,6 @@ MigrationInfo *qmp_query_migrate(Error **errp)
|
|||
|
||||
/* shared migration helpers */
|
||||
|
||||
static void migrate_fd_monitor_suspend(MigrationState *s, Monitor *mon)
|
||||
{
|
||||
if (monitor_suspend(mon) == 0) {
|
||||
DPRINTF("suspending monitor\n");
|
||||
} else {
|
||||
monitor_printf(mon, "terminal does not allow synchronous "
|
||||
"migration, continuing detached\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int migrate_fd_cleanup(MigrationState *s)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -178,10 +168,6 @@ static int migrate_fd_cleanup(MigrationState *s)
|
|||
DPRINTF("closing file\n");
|
||||
ret = qemu_fclose(s->file);
|
||||
s->file = NULL;
|
||||
} else {
|
||||
if (s->mon) {
|
||||
monitor_resume(s->mon);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->fd != -1) {
|
||||
|
@ -258,7 +244,7 @@ static void migrate_fd_put_ready(void *opaque)
|
|||
}
|
||||
|
||||
DPRINTF("iterate\n");
|
||||
ret = qemu_savevm_state_iterate(s->mon, s->file);
|
||||
ret = qemu_savevm_state_iterate(s->file);
|
||||
if (ret < 0) {
|
||||
migrate_fd_error(s);
|
||||
} else if (ret == 1) {
|
||||
|
@ -267,7 +253,7 @@ static void migrate_fd_put_ready(void *opaque)
|
|||
DPRINTF("done iterating\n");
|
||||
vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
||||
|
||||
if (qemu_savevm_state_complete(s->mon, s->file) < 0) {
|
||||
if (qemu_savevm_state_complete(s->file) < 0) {
|
||||
migrate_fd_error(s);
|
||||
} else {
|
||||
migrate_fd_completed(s);
|
||||
|
@ -289,7 +275,7 @@ static void migrate_fd_cancel(MigrationState *s)
|
|||
|
||||
s->state = MIG_STATE_CANCELLED;
|
||||
notifier_list_notify(&migration_state_notifiers, s);
|
||||
qemu_savevm_state_cancel(s->mon, s->file);
|
||||
qemu_savevm_state_cancel(s->file);
|
||||
|
||||
migrate_fd_cleanup(s);
|
||||
}
|
||||
|
@ -321,9 +307,6 @@ static int migrate_fd_close(void *opaque)
|
|||
{
|
||||
MigrationState *s = opaque;
|
||||
|
||||
if (s->mon) {
|
||||
monitor_resume(s->mon);
|
||||
}
|
||||
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
|
||||
return s->close(s);
|
||||
}
|
||||
|
@ -367,7 +350,7 @@ void migrate_fd_connect(MigrationState *s)
|
|||
migrate_fd_close);
|
||||
|
||||
DPRINTF("beginning savevm\n");
|
||||
ret = qemu_savevm_state_begin(s->mon, s->file, s->blk, s->shared);
|
||||
ret = qemu_savevm_state_begin(s->file, s->blk, s->shared);
|
||||
if (ret < 0) {
|
||||
DPRINTF("failed, %d\n", ret);
|
||||
migrate_fd_error(s);
|
||||
|
@ -376,7 +359,7 @@ void migrate_fd_connect(MigrationState *s)
|
|||
migrate_fd_put_ready(s);
|
||||
}
|
||||
|
||||
static MigrationState *migrate_init(Monitor *mon, int detach, int blk, int inc)
|
||||
static MigrationState *migrate_init(int blk, int inc)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
int64_t bandwidth_limit = s->bandwidth_limit;
|
||||
|
@ -386,18 +369,9 @@ static MigrationState *migrate_init(Monitor *mon, int detach, int blk, int inc)
|
|||
s->blk = blk;
|
||||
s->shared = inc;
|
||||
|
||||
/* s->mon is used for two things:
|
||||
- pass fd in fd migration
|
||||
- suspend/resume monitor for not detached migration
|
||||
*/
|
||||
s->mon = mon;
|
||||
s->bandwidth_limit = bandwidth_limit;
|
||||
s->state = MIG_STATE_SETUP;
|
||||
|
||||
if (!detach) {
|
||||
migrate_fd_monitor_suspend(s, mon);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -413,32 +387,29 @@ void migrate_del_blocker(Error *reason)
|
|||
migration_blockers = g_slist_remove(migration_blockers, reason);
|
||||
}
|
||||
|
||||
int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
void qmp_migrate(const char *uri, bool has_blk, bool blk,
|
||||
bool has_inc, bool inc, bool has_detach, bool detach,
|
||||
Error **errp)
|
||||
{
|
||||
MigrationState *s = migrate_get_current();
|
||||
const char *p;
|
||||
int detach = qdict_get_try_bool(qdict, "detach", 0);
|
||||
int blk = qdict_get_try_bool(qdict, "blk", 0);
|
||||
int inc = qdict_get_try_bool(qdict, "inc", 0);
|
||||
const char *uri = qdict_get_str(qdict, "uri");
|
||||
int ret;
|
||||
|
||||
if (s->state == MIG_STATE_ACTIVE) {
|
||||
monitor_printf(mon, "migration already in progress\n");
|
||||
return -1;
|
||||
error_set(errp, QERR_MIGRATION_ACTIVE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (qemu_savevm_state_blocked(mon)) {
|
||||
return -1;
|
||||
if (qemu_savevm_state_blocked(errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (migration_blockers) {
|
||||
Error *err = migration_blockers->data;
|
||||
qerror_report_err(err);
|
||||
return -1;
|
||||
*errp = error_copy(migration_blockers->data);
|
||||
return;
|
||||
}
|
||||
|
||||
s = migrate_init(mon, detach, blk, inc);
|
||||
s = migrate_init(blk, inc);
|
||||
|
||||
if (strstart(uri, "tcp:", &p)) {
|
||||
ret = tcp_start_outgoing_migration(s, p);
|
||||
|
@ -451,21 +422,18 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|||
ret = fd_start_outgoing_migration(s, p);
|
||||
#endif
|
||||
} else {
|
||||
monitor_printf(mon, "unknown migration protocol: %s\n", uri);
|
||||
ret = -EINVAL;
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
monitor_printf(mon, "migration failed: %s\n", strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
s->mon = NULL;
|
||||
DPRINTF("migration failed: %s\n", strerror(-ret));
|
||||
/* FIXME: we should return meaningful errors */
|
||||
error_set(errp, QERR_UNDEFINED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
notifier_list_notify(&migration_state_notifiers, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qmp_migrate_cancel(Error **errp)
|
||||
|
|
|
@ -26,7 +26,6 @@ struct MigrationState
|
|||
int64_t bandwidth_limit;
|
||||
QEMUFile *file;
|
||||
int fd;
|
||||
Monitor *mon;
|
||||
int state;
|
||||
int (*get_error)(MigrationState *s);
|
||||
int (*close)(MigrationState *s);
|
||||
|
@ -40,8 +39,6 @@ void process_incoming_migration(QEMUFile *f);
|
|||
|
||||
int qemu_start_incoming_migration(const char *uri);
|
||||
|
||||
int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data);
|
||||
|
||||
uint64_t migrate_max_downtime(void);
|
||||
|
||||
void do_info_migrate_print(Monitor *mon, const QObject *data);
|
||||
|
@ -78,7 +75,7 @@ uint64_t ram_bytes_remaining(void);
|
|||
uint64_t ram_bytes_transferred(void);
|
||||
uint64_t ram_bytes_total(void);
|
||||
|
||||
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque);
|
||||
int ram_save_live(QEMUFile *f, int stage, void *opaque);
|
||||
int ram_load(QEMUFile *f, void *opaque, int version_id);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1663,3 +1663,24 @@
|
|||
{ 'command': 'qom-list-types',
|
||||
'data': { '*implements': 'str', '*abstract': 'bool' },
|
||||
'returns': [ 'ObjectTypeInfo' ] }
|
||||
|
||||
##
|
||||
# @migrate
|
||||
#
|
||||
# Migrates the current running guest to another Virtual Machine.
|
||||
#
|
||||
# @uri: the Uniform Resource Identifier of the destination VM
|
||||
#
|
||||
# @blk: #optional do block migration (full disk copy)
|
||||
#
|
||||
# @inc: #optional incremental disk copy migration
|
||||
#
|
||||
# @detach: this argument exists only for compatibility reasons and
|
||||
# is ignored by QEMU
|
||||
#
|
||||
# Returns: nothing on success
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'command': 'migrate',
|
||||
'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } }
|
||||
|
|
8
qerror.c
8
qerror.c
|
@ -192,6 +192,14 @@ static const QErrorStringTable qerror_table[] = {
|
|||
.error_fmt = QERR_KVM_MISSING_CAP,
|
||||
.desc = "Using KVM without %(capability), %(feature) unavailable",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_MIGRATION_ACTIVE,
|
||||
.desc = "There's a migration process in progress",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_MIGRATION_NOT_SUPPORTED,
|
||||
.desc = "State blocked by non-migratable device '%(device)'",
|
||||
},
|
||||
{
|
||||
.error_fmt = QERR_MIGRATION_EXPECTED,
|
||||
.desc = "An incoming migration is expected before this command can be executed",
|
||||
|
|
6
qerror.h
6
qerror.h
|
@ -166,6 +166,12 @@ QError *qobject_to_qerror(const QObject *obj);
|
|||
#define QERR_KVM_MISSING_CAP \
|
||||
"{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }"
|
||||
|
||||
#define QERR_MIGRATION_ACTIVE \
|
||||
"{ 'class': 'MigrationActive', 'data': {} }"
|
||||
|
||||
#define QERR_MIGRATION_NOT_SUPPORTED \
|
||||
"{ 'class': 'MigrationNotSupported', 'data': {'device': %s} }"
|
||||
|
||||
#define QERR_MIGRATION_EXPECTED \
|
||||
"{ 'class': 'MigrationExpected', 'data': {} }"
|
||||
|
||||
|
|
|
@ -446,14 +446,7 @@ EQMP
|
|||
{
|
||||
.name = "migrate",
|
||||
.args_type = "detach:-d,blk:-b,inc:-i,uri:s",
|
||||
.params = "[-d] [-b] [-i] uri",
|
||||
.help = "migrate to URI (using -d to not wait for completion)"
|
||||
"\n\t\t\t -b for migration without shared storage with"
|
||||
" full copy of disk\n\t\t\t -i for migration without "
|
||||
"shared storage with incremental copy of disk "
|
||||
"(base image shared between src and destination)",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd_new = do_migrate,
|
||||
.mhandler.cmd_new = qmp_marshal_input_migrate,
|
||||
},
|
||||
|
||||
SQMP
|
||||
|
|
42
savevm.c
42
savevm.c
|
@ -1540,22 +1540,20 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
|
|||
#define QEMU_VM_SECTION_FULL 0x04
|
||||
#define QEMU_VM_SUBSECTION 0x05
|
||||
|
||||
bool qemu_savevm_state_blocked(Monitor *mon)
|
||||
bool qemu_savevm_state_blocked(Error **errp)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
|
||||
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
|
||||
if (se->no_migrate) {
|
||||
monitor_printf(mon, "state blocked by non-migratable device '%s'\n",
|
||||
se->idstr);
|
||||
error_set(errp, QERR_MIGRATION_NOT_SUPPORTED, se->idstr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
|
||||
int shared)
|
||||
int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
int ret;
|
||||
|
@ -1588,15 +1586,15 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
|
|||
qemu_put_be32(f, se->instance_id);
|
||||
qemu_put_be32(f, se->version_id);
|
||||
|
||||
ret = se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
|
||||
ret = se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
|
||||
if (ret < 0) {
|
||||
qemu_savevm_state_cancel(mon, f);
|
||||
qemu_savevm_state_cancel(f);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret != 0) {
|
||||
qemu_savevm_state_cancel(mon, f);
|
||||
qemu_savevm_state_cancel(f);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1609,7 +1607,7 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
|
|||
* 0 : We haven't finished, caller have to go again
|
||||
* 1 : We have finished, we can go to complete phase
|
||||
*/
|
||||
int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
|
||||
int qemu_savevm_state_iterate(QEMUFile *f)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
int ret = 1;
|
||||
|
@ -1622,7 +1620,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
|
|||
qemu_put_byte(f, QEMU_VM_SECTION_PART);
|
||||
qemu_put_be32(f, se->section_id);
|
||||
|
||||
ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque);
|
||||
ret = se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
|
||||
if (ret <= 0) {
|
||||
/* Do not proceed to the next vmstate before this one reported
|
||||
completion of the current stage. This serializes the migration
|
||||
|
@ -1636,12 +1634,12 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
|
|||
}
|
||||
ret = qemu_file_get_error(f);
|
||||
if (ret != 0) {
|
||||
qemu_savevm_state_cancel(mon, f);
|
||||
qemu_savevm_state_cancel(f);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
|
||||
int qemu_savevm_state_complete(QEMUFile *f)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
int ret;
|
||||
|
@ -1656,7 +1654,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
|
|||
qemu_put_byte(f, QEMU_VM_SECTION_END);
|
||||
qemu_put_be32(f, se->section_id);
|
||||
|
||||
ret = se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
|
||||
ret = se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -1688,37 +1686,37 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
|
|||
return qemu_file_get_error(f);
|
||||
}
|
||||
|
||||
void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f)
|
||||
void qemu_savevm_state_cancel(QEMUFile *f)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
|
||||
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
|
||||
if (se->save_live_state) {
|
||||
se->save_live_state(mon, f, -1, se->opaque);
|
||||
se->save_live_state(f, -1, se->opaque);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qemu_savevm_state(Monitor *mon, QEMUFile *f)
|
||||
static int qemu_savevm_state(QEMUFile *f)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (qemu_savevm_state_blocked(mon)) {
|
||||
if (qemu_savevm_state_blocked(NULL)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_savevm_state_begin(mon, f, 0, 0);
|
||||
ret = qemu_savevm_state_begin(f, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
ret = qemu_savevm_state_iterate(mon, f);
|
||||
ret = qemu_savevm_state_iterate(f);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
} while (ret == 0);
|
||||
|
||||
ret = qemu_savevm_state_complete(mon, f);
|
||||
ret = qemu_savevm_state_complete(f);
|
||||
|
||||
out:
|
||||
if (ret == 0) {
|
||||
|
@ -1837,7 +1835,7 @@ int qemu_loadvm_state(QEMUFile *f)
|
|||
unsigned int v;
|
||||
int ret;
|
||||
|
||||
if (qemu_savevm_state_blocked(default_mon)) {
|
||||
if (qemu_savevm_state_blocked(NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -2081,7 +2079,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
|||
monitor_printf(mon, "Could not open VM state file\n");
|
||||
goto the_end;
|
||||
}
|
||||
ret = qemu_savevm_state(mon, f);
|
||||
ret = qemu_savevm_state(f);
|
||||
vm_state_size = qemu_ftell(f);
|
||||
qemu_fclose(f);
|
||||
if (ret < 0) {
|
||||
|
|
11
sysemu.h
11
sysemu.h
|
@ -76,12 +76,11 @@ void do_info_snapshots(Monitor *mon);
|
|||
|
||||
void qemu_announce_self(void);
|
||||
|
||||
bool qemu_savevm_state_blocked(Monitor *mon);
|
||||
int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
|
||||
int shared);
|
||||
int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f);
|
||||
int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f);
|
||||
void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f);
|
||||
bool qemu_savevm_state_blocked(Error **errp);
|
||||
int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared);
|
||||
int qemu_savevm_state_iterate(QEMUFile *f);
|
||||
int qemu_savevm_state_complete(QEMUFile *f);
|
||||
void qemu_savevm_state_cancel(QEMUFile *f);
|
||||
int qemu_loadvm_state(QEMUFile *f);
|
||||
|
||||
/* SLIRP */
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
|
||||
typedef void SaveSetParamsHandler(int blk_enable, int shared, void * opaque);
|
||||
typedef void SaveStateHandler(QEMUFile *f, void *opaque);
|
||||
typedef int SaveLiveStateHandler(Monitor *mon, QEMUFile *f, int stage,
|
||||
void *opaque);
|
||||
typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque);
|
||||
typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
|
||||
|
||||
int register_savevm(DeviceState *dev,
|
||||
|
|
Loading…
Reference in New Issue