diff options
Diffstat (limited to 'migration')
-rw-r--r-- | migration/Makefile.objs | 6 | ||||
-rw-r--r-- | migration/block.c | 19 | ||||
-rw-r--r-- | migration/channel.c | 67 | ||||
-rw-r--r-- | migration/channel.h | 27 | ||||
-rw-r--r-- | migration/colo-comm.c | 4 | ||||
-rw-r--r-- | migration/colo.c | 9 | ||||
-rw-r--r-- | migration/exec.c | 1 | ||||
-rw-r--r-- | migration/fd.c | 1 | ||||
-rw-r--r-- | migration/migration.c | 149 | ||||
-rw-r--r-- | migration/postcopy-ram.c | 18 | ||||
-rw-r--r-- | migration/qemu-file-channel.c | 1 | ||||
-rw-r--r-- | migration/qemu-file-channel.h | 32 | ||||
-rw-r--r-- | migration/qjson.c | 2 | ||||
-rw-r--r-- | migration/qjson.h | 29 | ||||
-rw-r--r-- | migration/ram.c | 8 | ||||
-rw-r--r-- | migration/rdma.c | 1 | ||||
-rw-r--r-- | migration/savevm.c | 40 | ||||
-rw-r--r-- | migration/socket.c | 1 | ||||
-rw-r--r-- | migration/tls.c | 1 | ||||
-rw-r--r-- | migration/vmstate-types.c | 661 | ||||
-rw-r--r-- | migration/vmstate.c | 656 | ||||
-rw-r--r-- | migration/xbzrle.c | 2 | ||||
-rw-r--r-- | migration/xbzrle.h | 21 |
23 files changed, 1005 insertions, 751 deletions
diff --git a/migration/Makefile.objs b/migration/Makefile.objs index c1920b6fc0..90f8c1f177 100644 --- a/migration/Makefile.objs +++ b/migration/Makefile.objs @@ -1,7 +1,7 @@ common-obj-y += migration.o socket.o fd.o exec.o -common-obj-y += tls.o +common-obj-y += tls.o channel.o savevm.o common-obj-y += colo-comm.o colo.o colo-failover.o -common-obj-y += vmstate.o page_cache.o +common-obj-y += vmstate.o vmstate-types.o page_cache.o common-obj-y += qemu-file.o common-obj-y += qemu-file-channel.o common-obj-y += xbzrle.o postcopy-ram.o @@ -9,5 +9,5 @@ common-obj-y += qjson.o common-obj-$(CONFIG_RDMA) += rdma.o -common-obj-y += block.o +common-obj-$(CONFIG_LIVE_BLOCK_MIGRATION) += block.o diff --git a/migration/block.c b/migration/block.c index 060087fa32..13f90d3f17 100644 --- a/migration/block.c +++ b/migration/block.c @@ -26,6 +26,8 @@ #include "migration/block.h" #include "migration/migration.h" #include "sysemu/blockdev.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" #include "sysemu/block-backend.h" #define BLOCK_SIZE (1 << 20) @@ -94,9 +96,6 @@ typedef struct BlkMigBlock { } BlkMigBlock; typedef struct BlkMigState { - /* Written during setup phase. Can be read without a lock. */ - int blk_enable; - int shared_base; QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list; int64_t total_sector_sum; bool zero_blocks; @@ -425,7 +424,7 @@ static int init_blk_migration(QEMUFile *f) bmds->bulk_completed = 0; bmds->total_sectors = sectors; bmds->completed_sectors = 0; - bmds->shared_base = block_mig_state.shared_base; + bmds->shared_base = migrate_use_block_incremental(); assert(i < num_bs); bmds_bs[i].bmds = bmds; @@ -994,22 +993,12 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void block_set_params(const MigrationParams *params, void *opaque) -{ - block_mig_state.blk_enable = params->blk; - block_mig_state.shared_base = params->shared; - - /* shared base means that blk_enable = 1 */ - block_mig_state.blk_enable |= params->shared; -} - static bool block_is_active(void *opaque) { - return block_mig_state.blk_enable == 1; + return migrate_use_block(); } static SaveVMHandlers savevm_block_handlers = { - .set_params = block_set_params, .save_live_setup = block_save_setup, .save_live_iterate = block_save_iterate, .save_live_complete_precopy = block_save_complete, diff --git a/migration/channel.c b/migration/channel.c new file mode 100644 index 0000000000..2e78905cc7 --- /dev/null +++ b/migration/channel.c @@ -0,0 +1,67 @@ +/* + * QEMU live migration channel operations + * + * Copyright Red Hat, Inc. 2016 + * + * Authors: + * Daniel P. Berrange <berrange@redhat.com> + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "channel.h" +#include "migration/migration.h" +#include "qemu-file-channel.h" +#include "trace.h" +#include "qapi/error.h" +#include "io/channel-tls.h" + +void migration_channel_process_incoming(MigrationState *s, + QIOChannel *ioc) +{ + trace_migration_set_incoming_channel( + ioc, object_get_typename(OBJECT(ioc))); + + if (s->parameters.tls_creds && + *s->parameters.tls_creds && + !object_dynamic_cast(OBJECT(ioc), + TYPE_QIO_CHANNEL_TLS)) { + Error *local_err = NULL; + migration_tls_channel_process_incoming(s, ioc, &local_err); + if (local_err) { + error_report_err(local_err); + } + } else { + QEMUFile *f = qemu_fopen_channel_input(ioc); + migration_fd_process_incoming(f); + } +} + + +void migration_channel_connect(MigrationState *s, + QIOChannel *ioc, + const char *hostname) +{ + trace_migration_set_outgoing_channel( + ioc, object_get_typename(OBJECT(ioc)), hostname); + + if (s->parameters.tls_creds && + *s->parameters.tls_creds && + !object_dynamic_cast(OBJECT(ioc), + TYPE_QIO_CHANNEL_TLS)) { + Error *local_err = NULL; + migration_tls_channel_connect(s, ioc, hostname, &local_err); + if (local_err) { + migrate_fd_error(s, local_err); + error_free(local_err); + } + } else { + QEMUFile *f = qemu_fopen_channel_output(ioc); + + s->to_dst_file = f; + + migrate_fd_connect(s); + } +} diff --git a/migration/channel.h b/migration/channel.h new file mode 100644 index 0000000000..2e0a7e33cc --- /dev/null +++ b/migration/channel.h @@ -0,0 +1,27 @@ +/* + * QEMU live migration channel operations + * + * Copyright Red Hat, Inc. 2016 + * + * Authors: + * Daniel P. Berrange <berrange@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef QEMU_MIGRATION_CHANNEL_H +#define QEMU_MIGRATION_CHANNEL_H + +#include "io/channel.h" + +void migration_channel_process_incoming(MigrationState *s, + QIOChannel *ioc); + +void migration_channel_connect(MigrationState *s, + QIOChannel *ioc, + const char *hostname); +#endif diff --git a/migration/colo-comm.c b/migration/colo-comm.c index 20b60ec384..8bfdf6825a 100644 --- a/migration/colo-comm.c +++ b/migration/colo-comm.c @@ -12,7 +12,9 @@ */ #include "qemu/osdep.h" -#include <migration/colo.h> +#include "migration/migration.h" +#include "migration/colo.h" +#include "migration/vmstate.h" #include "trace.h" typedef struct { diff --git a/migration/colo.c b/migration/colo.c index 963c80256d..929b31c50c 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -13,7 +13,11 @@ #include "qemu/osdep.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "qemu-file-channel.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" #include "migration/colo.h" +#include "migration/block.h" #include "io/channel-buffer.h" #include "trace.h" #include "qemu/error-report.h" @@ -345,10 +349,9 @@ static int colo_do_checkpoint_transaction(MigrationState *s, } /* Disable block migration */ - s->params.blk = 0; - s->params.shared = 0; + migrate_set_block_enabled(false, &local_err); qemu_savevm_state_header(fb); - qemu_savevm_state_begin(fb, &s->params); + qemu_savevm_state_begin(fb); qemu_mutex_lock_iothread(); qemu_savevm_state_complete_precopy(fb, false); qemu_mutex_unlock_iothread(); diff --git a/migration/exec.c b/migration/exec.c index aba9089466..57a93355d1 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "channel.h" #include "migration/migration.h" #include "io/channel-command.h" #include "trace.h" diff --git a/migration/fd.c b/migration/fd.c index 58cb51a9e6..05e0a5cca8 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "channel.h" #include "migration/migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" diff --git a/migration/migration.c b/migration/migration.c index 0304c013f3..ad29e53400 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -19,7 +19,9 @@ #include "qemu/main-loop.h" #include "migration/blocker.h" #include "migration/migration.h" +#include "qemu-file-channel.h" #include "migration/qemu-file.h" +#include "migration/vmstate.h" #include "sysemu/sysemu.h" #include "block/block.h" #include "qapi/qmp/qerror.h" @@ -35,6 +37,7 @@ #include "qom/cpu.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "exec/target_page.h" #include "io/channel-buffer.h" #include "io/channel-tls.h" #include "migration/colo.h" @@ -444,56 +447,6 @@ void migration_fd_process_incoming(QEMUFile *f) qemu_coroutine_enter(co); } - -void migration_channel_process_incoming(MigrationState *s, - QIOChannel *ioc) -{ - trace_migration_set_incoming_channel( - ioc, object_get_typename(OBJECT(ioc))); - - if (s->parameters.tls_creds && - *s->parameters.tls_creds && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { - Error *local_err = NULL; - migration_tls_channel_process_incoming(s, ioc, &local_err); - if (local_err) { - error_report_err(local_err); - } - } else { - QEMUFile *f = qemu_fopen_channel_input(ioc); - migration_fd_process_incoming(f); - } -} - - -void migration_channel_connect(MigrationState *s, - QIOChannel *ioc, - const char *hostname) -{ - trace_migration_set_outgoing_channel( - ioc, object_get_typename(OBJECT(ioc)), hostname); - - if (s->parameters.tls_creds && - *s->parameters.tls_creds && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { - Error *local_err = NULL; - migration_tls_channel_connect(s, ioc, hostname, &local_err); - if (local_err) { - migrate_fd_error(s, local_err); - error_free(local_err); - } - } else { - QEMUFile *f = qemu_fopen_channel_output(ioc); - - s->to_dst_file = f; - - migrate_fd_connect(s); - } -} - - /* * Send a message on the return channel back to the source * of the migration. @@ -547,6 +500,11 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) caps = NULL; /* silence compiler warning */ for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { +#ifndef CONFIG_LIVE_BLOCK_MIGRATION + if (i == MIGRATION_CAPABILITY_BLOCK) { + continue; + } +#endif if (i == MIGRATION_CAPABILITY_X_COLO && !colo_supported()) { continue; } @@ -592,6 +550,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->downtime_limit = s->parameters.downtime_limit; params->has_x_checkpoint_delay = true; params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; + params->has_block_incremental = true; + params->block_incremental = s->parameters.block_incremental; return params; } @@ -761,6 +721,15 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, } for (cap = params; cap; cap = cap->next) { +#ifndef CONFIG_LIVE_BLOCK_MIGRATION + if (cap->value->capability == MIGRATION_CAPABILITY_BLOCK + && cap->value->state) { + error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " + "block migration"); + error_append_hint(errp, "Use drive_mirror+NBD instead.\n"); + continue; + } +#endif if (cap->value->capability == MIGRATION_CAPABILITY_X_COLO) { if (!colo_supported()) { error_setg(errp, "COLO is not currently supported, please" @@ -900,6 +869,9 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) colo_checkpoint_notify(s); } } + if (params->has_block_incremental) { + s->parameters.block_incremental = params->block_incremental; + } } @@ -935,6 +907,33 @@ void migrate_set_state(int *state, int old_state, int new_state) } } +void migrate_set_block_enabled(bool value, Error **errp) +{ + MigrationCapabilityStatusList *cap; + + cap = g_new0(MigrationCapabilityStatusList, 1); + cap->value = g_new0(MigrationCapabilityStatus, 1); + cap->value->capability = MIGRATION_CAPABILITY_BLOCK; + cap->value->state = value; + qmp_migrate_set_capabilities(cap, errp); + qapi_free_MigrationCapabilityStatusList(cap); +} + +static void migrate_set_block_incremental(MigrationState *s, bool value) +{ + s->parameters.block_incremental = value; +} + +static void block_cleanup_parameters(MigrationState *s) +{ + if (s->must_remove_block_options) { + /* setting to false can never fail */ + migrate_set_block_enabled(false, &error_abort); + migrate_set_block_incremental(s, false); + s->must_remove_block_options = false; + } +} + static void migrate_fd_cleanup(void *opaque) { MigrationState *s = opaque; @@ -967,6 +966,7 @@ static void migrate_fd_cleanup(void *opaque) } notifier_list_notify(&migration_state_notifiers, s); + block_cleanup_parameters(s); } void migrate_fd_error(MigrationState *s, const Error *error) @@ -979,6 +979,7 @@ void migrate_fd_error(MigrationState *s, const Error *error) s->error = error_copy(error); } notifier_list_notify(&migration_state_notifiers, s); + block_cleanup_parameters(s); } static void migrate_fd_cancel(MigrationState *s) @@ -1020,6 +1021,7 @@ static void migrate_fd_cancel(MigrationState *s) s->block_inactive = false; } } + block_cleanup_parameters(s); } void add_migration_state_change_notifier(Notifier *notify) @@ -1083,7 +1085,7 @@ bool migration_is_idle(void) return false; } -MigrationState *migrate_init(const MigrationParams *params) +MigrationState *migrate_init(void) { MigrationState *s = migrate_get_current(); @@ -1097,7 +1099,6 @@ MigrationState *migrate_init(const MigrationParams *params) s->cleanup_bh = 0; s->to_dst_file = NULL; s->state = MIGRATION_STATUS_NONE; - s->params = *params; s->rp_state.from_dst_file = NULL; s->rp_state.error = false; s->mbps = 0.0; @@ -1186,12 +1187,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, { Error *local_err = NULL; MigrationState *s = migrate_get_current(); - MigrationParams params; const char *p; - params.blk = has_blk && blk; - params.shared = has_inc && inc; - if (migration_is_setup_or_active(s->state) || s->state == MIGRATION_STATUS_CANCELLING || s->state == MIGRATION_STATUS_COLO) { @@ -1207,7 +1204,25 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, return; } - s = migrate_init(¶ms); + if ((has_blk && blk) || (has_inc && inc)) { + if (migrate_use_block() || migrate_use_block_incremental()) { + error_setg(errp, "Command options are incompatible with " + "current migration capabilities"); + return; + } + migrate_set_block_enabled(true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + s->must_remove_block_options = true; + } + + if (has_inc && inc) { + migrate_set_block_incremental(s, true); + } + + s = migrate_init(); if (strstart(uri, "tcp:", &p)) { tcp_start_outgoing_migration(s, p, &local_err); @@ -1404,6 +1419,24 @@ int64_t migrate_xbzrle_cache_size(void) return s->xbzrle_cache_size; } +bool migrate_use_block(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_BLOCK]; +} + +bool migrate_use_block_incremental(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.block_incremental; +} + /* migration thread support */ /* * Something bad happened to the RP stream, mark an error @@ -1913,7 +1946,7 @@ static void *migration_thread(void *opaque) qemu_savevm_send_postcopy_advise(s->to_dst_file); } - qemu_savevm_state_begin(s->to_dst_file, &s->params); + qemu_savevm_state_begin(s->to_dst_file); s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index a0489f6542..a37620dac6 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -19,7 +19,9 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "exec/target_page.h" #include "migration/migration.h" +#include "migration/qemu-file.h" #include "postcopy-ram.h" #include "sysemu/sysemu.h" #include "sysemu/balloon.h" @@ -96,14 +98,24 @@ static bool ufd_version_check(int ufd) /* Callback from postcopy_ram_supported_by_host block iterator. */ -static int test_range_shared(const char *block_name, void *host_addr, +static int test_ramblock_postcopiable(const char *block_name, void *host_addr, ram_addr_t offset, ram_addr_t length, void *opaque) { - if (qemu_ram_is_shared(qemu_ram_block_by_name(block_name))) { + RAMBlock *rb = qemu_ram_block_by_name(block_name); + size_t pagesize = qemu_ram_pagesize(rb); + + if (qemu_ram_is_shared(rb)) { error_report("Postcopy on shared RAM (%s) is not yet supported", block_name); return 1; } + + if (length % pagesize) { + error_report("Postcopy requires RAM blocks to be a page size multiple," + " block %s is 0x" RAM_ADDR_FMT " bytes with a " + "page size of 0x%zx", block_name, length, pagesize); + return 1; + } return 0; } @@ -140,7 +152,7 @@ bool postcopy_ram_supported_by_host(void) } /* We don't support postcopy with shared RAM yet */ - if (qemu_ram_foreach_block(test_range_shared, NULL)) { + if (qemu_ram_foreach_block(test_ramblock_postcopiable, NULL)) { goto out; } diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c index 45c13f1028..dc991c9051 100644 --- a/migration/qemu-file-channel.c +++ b/migration/qemu-file-channel.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu-file-channel.h" #include "migration/qemu-file.h" #include "io/channel-socket.h" #include "qemu/iov.h" diff --git a/migration/qemu-file-channel.h b/migration/qemu-file-channel.h new file mode 100644 index 0000000000..0028a09eb6 --- /dev/null +++ b/migration/qemu-file-channel.h @@ -0,0 +1,32 @@ +/* + * QEMUFile backend for QIOChannel objects + * + * Copyright (c) 2015-2016 Red Hat, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_FILE_CHANNEL_H +#define QEMU_FILE_CHANNEL_H + +#include "io/channel.h" + +QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc); +QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc); +#endif diff --git a/migration/qjson.c b/migration/qjson.c index f345904919..9d7f6eb9eb 100644 --- a/migration/qjson.c +++ b/migration/qjson.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/qmp/qstring.h" -#include "migration/qjson.h" +#include "qjson.h" struct QJSON { QString *str; diff --git a/migration/qjson.h b/migration/qjson.h new file mode 100644 index 0000000000..2978b5f371 --- /dev/null +++ b/migration/qjson.h @@ -0,0 +1,29 @@ +/* + * QEMU JSON writer + * + * Copyright Alexander Graf + * + * Authors: + * Alexander Graf <agraf@suse.de> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +#ifndef QEMU_QJSON_H +#define QEMU_QJSON_H + +typedef struct QJSON QJSON; + +QJSON *qjson_new(void); +void qjson_destroy(QJSON *json); +void json_prop_str(QJSON *json, const char *name, const char *str); +void json_prop_int(QJSON *json, const char *name, int64_t val); +void json_end_array(QJSON *json); +void json_start_array(QJSON *json, const char *name); +void json_end_object(QJSON *json); +void json_start_object(QJSON *json, const char *name); +const char *qjson_get_str(QJSON *json); +void qjson_finish(QJSON *json); + +#endif /* QEMU_QJSON_H */ diff --git a/migration/ram.c b/migration/ram.c index f59fdd41a4..c07a9c08d9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -35,7 +35,10 @@ #include "qemu/bitmap.h" #include "qemu/timer.h" #include "qemu/main-loop.h" +#include "xbzrle.h" #include "migration/migration.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" #include "postcopy-ram.h" #include "exec/address-spaces.h" #include "migration/page_cache.h" @@ -1312,6 +1315,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, * a host page in which case the remainder of the hostpage is sent. * Only dirty target pages are sent. Note that the host page size may * be a huge page for this block. + * The saving stops at the boundary of the used_length of the block + * if the RAMBlock isn't a multiple of the host page size. * * Returns the number of pages written or negative on error * @@ -1335,7 +1340,8 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, pages += tmppages; pss->page++; - } while (pss->page & (pagesize_bits - 1)); + } while ((pss->page & (pagesize_bits - 1)) && + offset_in_ramblock(pss->block, pss->page << TARGET_PAGE_BITS)); /* The offset we leave with is the last one we looked at */ pss->page--; diff --git a/migration/rdma.c b/migration/rdma.c index 7eaaf96479..166cd60a77 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -20,6 +20,7 @@ #include "migration/migration.h" #include "migration/qemu-file.h" #include "exec/cpu-common.h" +#include "qemu-file-channel.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" diff --git a/migration/savevm.c b/migration/savevm.c index f5e81948e6..d971e5ee47 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -27,7 +27,6 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "hw/boards.h" #include "hw/hw.h" #include "hw/qdev.h" @@ -36,12 +35,14 @@ #include "sysemu/sysemu.h" #include "qemu/timer.h" #include "migration/migration.h" +#include "qemu-file-channel.h" #include "postcopy-ram.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/queue.h" #include "sysemu/cpus.h" #include "exec/memory.h" +#include "exec/target_page.h" #include "qmp-commands.h" #include "trace.h" #include "qemu/bitops.h" @@ -286,7 +287,7 @@ static void configuration_pre_save(void *opaque) state->len = strlen(current_name); state->name = current_name; - state->target_page_bits = TARGET_PAGE_BITS; + state->target_page_bits = qemu_target_page_bits(); } static int configuration_pre_load(void *opaque) @@ -297,7 +298,7 @@ static int configuration_pre_load(void *opaque) * predates the variable-target-page-bits support and is using the * minimum possible value for this CPU. */ - state->target_page_bits = TARGET_PAGE_BITS_MIN; + state->target_page_bits = qemu_target_page_bits_min(); return 0; } @@ -312,9 +313,9 @@ static int configuration_post_load(void *opaque, int version_id) return -EINVAL; } - if (state->target_page_bits != TARGET_PAGE_BITS) { + if (state->target_page_bits != qemu_target_page_bits()) { error_report("Received TARGET_PAGE_BITS is %d but local is %d", - state->target_page_bits, TARGET_PAGE_BITS); + state->target_page_bits, qemu_target_page_bits()); return -EINVAL; } @@ -330,7 +331,8 @@ static int configuration_post_load(void *opaque, int version_id) */ static bool vmstate_target_page_bits_needed(void *opaque) { - return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN; + return qemu_target_page_bits() + > qemu_target_page_bits_min(); } static const VMStateDescription vmstate_target_page_bits = { @@ -966,21 +968,13 @@ void qemu_savevm_state_header(QEMUFile *f) } -void qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params) +void qemu_savevm_state_begin(QEMUFile *f) { SaveStateEntry *se; int ret; trace_savevm_state_begin(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->set_params) { - continue; - } - se->ops->set_params(params, se->opaque); - } - - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_setup) { continue; } @@ -1144,7 +1138,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) } vmdesc = qjson_new(); - json_prop_int(vmdesc, "page_size", TARGET_PAGE_SIZE); + json_prop_int(vmdesc, "page_size", qemu_target_page_size()); json_start_array(vmdesc, "devices"); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1232,11 +1226,7 @@ void qemu_savevm_state_cleanup(void) static int qemu_savevm_state(QEMUFile *f, Error **errp) { int ret; - MigrationParams params = { - .blk = 0, - .shared = 0 - }; - MigrationState *ms = migrate_init(¶ms); + MigrationState *ms = migrate_init(); MigrationStatus status; ms->to_dst_file = f; @@ -1245,9 +1235,15 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) goto done; } + if (migrate_use_block()) { + error_setg(errp, "Block migration and snapshots are incompatible"); + ret = -EINVAL; + goto done; + } + qemu_mutex_unlock_iothread(); qemu_savevm_state_header(f); - qemu_savevm_state_begin(f, ¶ms); + qemu_savevm_state_begin(f); qemu_mutex_lock_iothread(); while (qemu_file_get_error(f) == 0) { diff --git a/migration/socket.c b/migration/socket.c index 1cfbe81e69..53f9d61605 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -19,6 +19,7 @@ #include "qemu-common.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "channel.h" #include "migration/migration.h" #include "migration/qemu-file.h" #include "io/channel-socket.h" diff --git a/migration/tls.c b/migration/tls.c index a33ecb767e..34ad121abf 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "channel.h" #include "migration/migration.h" #include "io/channel-tls.h" #include "crypto/tlscreds.h" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c new file mode 100644 index 0000000000..cc95e47775 --- /dev/null +++ b/migration/vmstate-types.c @@ -0,0 +1,661 @@ +/* + * VMStateInfo's for basic typse + * + * Copyright (c) 2009-2017 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" +#include "qemu/error-report.h" +#include "qemu/queue.h" +#include "trace.h" + +/* bool */ + +static int get_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + bool *v = pv; + *v = qemu_get_byte(f); + return 0; +} + +static int put_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + bool *v = pv; + qemu_put_byte(f, *v); + return 0; +} + +const VMStateInfo vmstate_info_bool = { + .name = "bool", + .get = get_bool, + .put = put_bool, +}; + +/* 8 bit int */ + +static int get_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + int8_t *v = pv; + qemu_get_s8s(f, v); + return 0; +} + +static int put_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + int8_t *v = pv; + qemu_put_s8s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_int8 = { + .name = "int8", + .get = get_int8, + .put = put_int8, +}; + +/* 16 bit int */ + +static int get_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + int16_t *v = pv; + qemu_get_sbe16s(f, v); + return 0; +} + +static int put_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + int16_t *v = pv; + qemu_put_sbe16s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_int16 = { + .name = "int16", + .get = get_int16, + .put = put_int16, +}; + +/* 32 bit int */ + +static int get_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + int32_t *v = pv; + qemu_get_sbe32s(f, v); + return 0; +} + +static int put_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + int32_t *v = pv; + qemu_put_sbe32s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_int32 = { + .name = "int32", + .get = get_int32, + .put = put_int32, +}; + +/* 32 bit int. See that the received value is the same than the one + in the field */ + +static int get_int32_equal(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + int32_t *v = pv; + int32_t v2; + qemu_get_sbe32s(f, &v2); + + if (*v == v2) { + return 0; + } + error_report("%" PRIx32 " != %" PRIx32, *v, v2); + return -EINVAL; +} + +const VMStateInfo vmstate_info_int32_equal = { + .name = "int32 equal", + .get = get_int32_equal, + .put = put_int32, +}; + +/* 32 bit int. Check that the received value is non-negative + * and less than or equal to the one in the field. + */ + +static int get_int32_le(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + int32_t *cur = pv; + int32_t loaded; + qemu_get_sbe32s(f, &loaded); + + if (loaded >= 0 && loaded <= *cur) { + *cur = loaded; + return 0; + } + error_report("Invalid value %" PRId32 + " expecting positive value <= %" PRId32, + loaded, *cur); + return -EINVAL; +} + +const VMStateInfo vmstate_info_int32_le = { + .name = "int32 le", + .get = get_int32_le, + .put = put_int32, +}; + +/* 64 bit int */ + +static int get_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + int64_t *v = pv; + qemu_get_sbe64s(f, v); + return 0; +} + +static int put_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + int64_t *v = pv; + qemu_put_sbe64s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_int64 = { + .name = "int64", + .get = get_int64, + .put = put_int64, +}; + +/* 8 bit unsigned int */ + +static int get_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + uint8_t *v = pv; + qemu_get_8s(f, v); + return 0; +} + +static int put_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + uint8_t *v = pv; + qemu_put_8s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_uint8 = { + .name = "uint8", + .get = get_uint8, + .put = put_uint8, +}; + +/* 16 bit unsigned int */ + +static int get_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + uint16_t *v = pv; + qemu_get_be16s(f, v); + return 0; +} + +static int put_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + uint16_t *v = pv; + qemu_put_be16s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_uint16 = { + .name = "uint16", + .get = get_uint16, + .put = put_uint16, +}; + +/* 32 bit unsigned int */ + +static int get_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + uint32_t *v = pv; + qemu_get_be32s(f, v); + return 0; +} + +static int put_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + uint32_t *v = pv; + qemu_put_be32s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_uint32 = { + .name = "uint32", + .get = get_uint32, + .put = put_uint32, +}; + +/* 32 bit uint. See that the received value is the same than the one + in the field */ + +static int get_uint32_equal(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + uint32_t *v = pv; + uint32_t v2; + qemu_get_be32s(f, &v2); + + if (*v == v2) { + return 0; + } + error_report("%" PRIx32 " != %" PRIx32, *v, v2); + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint32_equal = { + .name = "uint32 equal", + .get = get_uint32_equal, + .put = put_uint32, +}; + +/* 64 bit unsigned int */ + +static int get_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + uint64_t *v = pv; + qemu_get_be64s(f, v); + return 0; +} + +static int put_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + uint64_t *v = pv; + qemu_put_be64s(f, v); + return 0; +} + +const VMStateInfo vmstate_info_uint64 = { + .name = "uint64", + .get = get_uint64, + .put = put_uint64, +}; + +static int get_nullptr(QEMUFile *f, void *pv, size_t size, VMStateField *field) + +{ + if (qemu_get_byte(f) == VMS_NULLPTR_MARKER) { + return 0; + } + error_report("vmstate: get_nullptr expected VMS_NULLPTR_MARKER"); + return -EINVAL; +} + +static int put_nullptr(QEMUFile *f, void *pv, size_t size, + VMStateField *field, QJSON *vmdesc) + +{ + if (pv == NULL) { + qemu_put_byte(f, VMS_NULLPTR_MARKER); + return 0; + } + error_report("vmstate: put_nullptr must be called with pv == NULL"); + return -EINVAL; +} + +const VMStateInfo vmstate_info_nullptr = { + .name = "uint64", + .get = get_nullptr, + .put = put_nullptr, +}; + +/* 64 bit unsigned int. See that the received value is the same than the one + in the field */ + +static int get_uint64_equal(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + uint64_t *v = pv; + uint64_t v2; + qemu_get_be64s(f, &v2); + + if (*v == v2) { + return 0; + } + error_report("%" PRIx64 " != %" PRIx64, *v, v2); + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint64_equal = { + .name = "int64 equal", + .get = get_uint64_equal, + .put = put_uint64, +}; + +/* 8 bit int. See that the received value is the same than the one + in the field */ + +static int get_uint8_equal(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + uint8_t *v = pv; + uint8_t v2; + qemu_get_8s(f, &v2); + + if (*v == v2) { + return 0; + } + error_report("%x != %x", *v, v2); + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint8_equal = { + .name = "uint8 equal", + .get = get_uint8_equal, + .put = put_uint8, +}; + +/* 16 bit unsigned int int. See that the received value is the same than the one + in the field */ + +static int get_uint16_equal(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + uint16_t *v = pv; + uint16_t v2; + qemu_get_be16s(f, &v2); + + if (*v == v2) { + return 0; + } + error_report("%x != %x", *v, v2); + return -EINVAL; +} + +const VMStateInfo vmstate_info_uint16_equal = { + .name = "uint16 equal", + .get = get_uint16_equal, + .put = put_uint16, +}; + +/* floating point */ + +static int get_float64(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + float64 *v = pv; + + *v = make_float64(qemu_get_be64(f)); + return 0; +} + +static int put_float64(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + uint64_t *v = pv; + + qemu_put_be64(f, float64_val(*v)); + return 0; +} + +const VMStateInfo vmstate_info_float64 = { + .name = "float64", + .get = get_float64, + .put = put_float64, +}; + +/* CPU_DoubleU type */ + +static int get_cpudouble(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + CPU_DoubleU *v = pv; + qemu_get_be32s(f, &v->l.upper); + qemu_get_be32s(f, &v->l.lower); + return 0; +} + +static int put_cpudouble(QEMUFile *f, void *pv, size_t size, + VMStateField *field, QJSON *vmdesc) +{ + CPU_DoubleU *v = pv; + qemu_put_be32s(f, &v->l.upper); + qemu_put_be32s(f, &v->l.lower); + return 0; +} + +const VMStateInfo vmstate_info_cpudouble = { + .name = "CPU_Double_U", + .get = get_cpudouble, + .put = put_cpudouble, +}; + +/* uint8_t buffers */ + +static int get_buffer(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + uint8_t *v = pv; + qemu_get_buffer(f, v, size); + return 0; +} + +static int put_buffer(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + uint8_t *v = pv; + qemu_put_buffer(f, v, size); + return 0; +} + +const VMStateInfo vmstate_info_buffer = { + .name = "buffer", + .get = get_buffer, + .put = put_buffer, +}; + +/* unused buffers: space that was used for some fields that are + not useful anymore */ + +static int get_unused_buffer(QEMUFile *f, void *pv, size_t size, + VMStateField *field) +{ + uint8_t buf[1024]; + int block_len; + + while (size > 0) { + block_len = MIN(sizeof(buf), size); + size -= block_len; + qemu_get_buffer(f, buf, block_len); + } + return 0; +} + +static int put_unused_buffer(QEMUFile *f, void *pv, size_t size, + VMStateField *field, QJSON *vmdesc) +{ + static const uint8_t buf[1024]; + int block_len; + + while (size > 0) { + block_len = MIN(sizeof(buf), size); + size -= block_len; + qemu_put_buffer(f, buf, block_len); + } + + return 0; +} + +const VMStateInfo vmstate_info_unused_buffer = { + .name = "unused_buffer", + .get = get_unused_buffer, + .put = put_unused_buffer, +}; + +/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate + * a temporary buffer and the pre_load/pre_save methods in the child vmsd + * copy stuff from the parent into the child and do calculations to fill + * in fields that don't really exist in the parent but need to be in the + * stream. + */ +static int get_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + int ret; + const VMStateDescription *vmsd = field->vmsd; + int version_id = field->version_id; + void *tmp = g_malloc(size); + + /* Writes the parent field which is at the start of the tmp */ + *(void **)tmp = pv; + ret = vmstate_load_state(f, vmsd, tmp, version_id); + g_free(tmp); + return ret; +} + +static int put_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + const VMStateDescription *vmsd = field->vmsd; + void *tmp = g_malloc(size); + + /* Writes the parent field which is at the start of the tmp */ + *(void **)tmp = pv; + vmstate_save_state(f, vmsd, tmp, vmdesc); + g_free(tmp); + + return 0; +} + +const VMStateInfo vmstate_info_tmp = { + .name = "tmp", + .get = get_tmp, + .put = put_tmp, +}; + +/* bitmaps (as defined by bitmap.h). Note that size here is the size + * of the bitmap in bits. The on-the-wire format of a bitmap is 64 + * bit words with the bits in big endian order. The in-memory format + * is an array of 'unsigned long', which may be either 32 or 64 bits. + */ +/* This is the number of 64 bit words sent over the wire */ +#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) +static int get_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field) +{ + unsigned long *bmp = pv; + int i, idx = 0; + for (i = 0; i < BITS_TO_U64S(size); i++) { + uint64_t w = qemu_get_be64(f); + bmp[idx++] = w; + if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { + bmp[idx++] = w >> 32; + } + } + return 0; +} + +static int put_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field, + QJSON *vmdesc) +{ + unsigned long *bmp = pv; + int i, idx = 0; + for (i = 0; i < BITS_TO_U64S(size); i++) { + uint64_t w = bmp[idx++]; + if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { + w |= ((uint64_t)bmp[idx++]) << 32; + } + qemu_put_be64(f, w); + } + + return 0; +} + +const VMStateInfo vmstate_info_bitmap = { + .name = "bitmap", + .get = get_bitmap, + .put = put_bitmap, +}; + +/* get for QTAILQ + * meta data about the QTAILQ is encoded in a VMStateField structure + */ +static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, + VMStateField *field) +{ + int ret = 0; + const VMStateDescription *vmsd = field->vmsd; + /* size of a QTAILQ element */ + size_t size = field->size; + /* offset of the QTAILQ entry in a QTAILQ element */ + size_t entry_offset = field->start; + int version_id = field->version_id; + void *elm; + + trace_get_qtailq(vmsd->name, version_id); + if (version_id > vmsd->version_id) { + error_report("%s %s", vmsd->name, "too new"); + trace_get_qtailq_end(vmsd->name, "too new", -EINVAL); + + return -EINVAL; + } + if (version_id < vmsd->minimum_version_id) { + error_report("%s %s", vmsd->name, "too old"); + trace_get_qtailq_end(vmsd->name, "too old", -EINVAL); + return -EINVAL; + } + + while (qemu_get_byte(f)) { + elm = g_malloc(size); + ret = vmstate_load_state(f, vmsd, elm, version_id); + if (ret) { + return ret; + } + QTAILQ_RAW_INSERT_TAIL(pv, elm, entry_offset); + } + + trace_get_qtailq_end(vmsd->name, "end", ret); + return ret; +} + +/* put for QTAILQ */ +static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size, + VMStateField *field, QJSON *vmdesc) +{ + const VMStateDescription *vmsd = field->vmsd; + /* offset of the QTAILQ entry in a QTAILQ element*/ + size_t entry_offset = field->start; + void *elm; + + trace_put_qtailq(vmsd->name, vmsd->version_id); + + QTAILQ_RAW_FOREACH(elm, pv, entry_offset) { + qemu_put_byte(f, true); + vmstate_save_state(f, vmsd, elm, vmdesc); + } + qemu_put_byte(f, false); + + trace_put_qtailq_end(vmsd->name, "end"); + + return 0; +} +const VMStateInfo vmstate_info_qtailq = { + .name = "qtailq", + .get = get_qtailq, + .put = put_qtailq, +}; diff --git a/migration/vmstate.c b/migration/vmstate.c index 7b4a607c51..ff54531b44 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -1,3 +1,15 @@ +/* + * VMState interpreter + * + * Copyright (c) 2009-2017 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + #include "qemu/osdep.h" #include "qemu-common.h" #include "migration/migration.h" @@ -5,9 +17,8 @@ #include "migration/vmstate.h" #include "qemu/bitops.h" #include "qemu/error-report.h" -#include "qemu/queue.h" #include "trace.h" -#include "migration/qjson.h" +#include "qjson.h" static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, QJSON *vmdesc); @@ -475,644 +486,3 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, json_end_array(vmdesc); } } - -/* bool */ - -static int get_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - bool *v = pv; - *v = qemu_get_byte(f); - return 0; -} - -static int put_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - bool *v = pv; - qemu_put_byte(f, *v); - return 0; -} - -const VMStateInfo vmstate_info_bool = { - .name = "bool", - .get = get_bool, - .put = put_bool, -}; - -/* 8 bit int */ - -static int get_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - int8_t *v = pv; - qemu_get_s8s(f, v); - return 0; -} - -static int put_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - int8_t *v = pv; - qemu_put_s8s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_int8 = { - .name = "int8", - .get = get_int8, - .put = put_int8, -}; - -/* 16 bit int */ - -static int get_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - int16_t *v = pv; - qemu_get_sbe16s(f, v); - return 0; -} - -static int put_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - int16_t *v = pv; - qemu_put_sbe16s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_int16 = { - .name = "int16", - .get = get_int16, - .put = put_int16, -}; - -/* 32 bit int */ - -static int get_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - int32_t *v = pv; - qemu_get_sbe32s(f, v); - return 0; -} - -static int put_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - int32_t *v = pv; - qemu_put_sbe32s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_int32 = { - .name = "int32", - .get = get_int32, - .put = put_int32, -}; - -/* 32 bit int. See that the received value is the same than the one - in the field */ - -static int get_int32_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - int32_t *v = pv; - int32_t v2; - qemu_get_sbe32s(f, &v2); - - if (*v == v2) { - return 0; - } - error_report("%" PRIx32 " != %" PRIx32, *v, v2); - return -EINVAL; -} - -const VMStateInfo vmstate_info_int32_equal = { - .name = "int32 equal", - .get = get_int32_equal, - .put = put_int32, -}; - -/* 32 bit int. Check that the received value is non-negative - * and less than or equal to the one in the field. - */ - -static int get_int32_le(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - int32_t *cur = pv; - int32_t loaded; - qemu_get_sbe32s(f, &loaded); - - if (loaded >= 0 && loaded <= *cur) { - *cur = loaded; - return 0; - } - error_report("Invalid value %" PRId32 - " expecting positive value <= %" PRId32, - loaded, *cur); - return -EINVAL; -} - -const VMStateInfo vmstate_info_int32_le = { - .name = "int32 le", - .get = get_int32_le, - .put = put_int32, -}; - -/* 64 bit int */ - -static int get_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - int64_t *v = pv; - qemu_get_sbe64s(f, v); - return 0; -} - -static int put_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - int64_t *v = pv; - qemu_put_sbe64s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_int64 = { - .name = "int64", - .get = get_int64, - .put = put_int64, -}; - -/* 8 bit unsigned int */ - -static int get_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - uint8_t *v = pv; - qemu_get_8s(f, v); - return 0; -} - -static int put_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - uint8_t *v = pv; - qemu_put_8s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_uint8 = { - .name = "uint8", - .get = get_uint8, - .put = put_uint8, -}; - -/* 16 bit unsigned int */ - -static int get_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - uint16_t *v = pv; - qemu_get_be16s(f, v); - return 0; -} - -static int put_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - uint16_t *v = pv; - qemu_put_be16s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_uint16 = { - .name = "uint16", - .get = get_uint16, - .put = put_uint16, -}; - -/* 32 bit unsigned int */ - -static int get_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - uint32_t *v = pv; - qemu_get_be32s(f, v); - return 0; -} - -static int put_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - uint32_t *v = pv; - qemu_put_be32s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_uint32 = { - .name = "uint32", - .get = get_uint32, - .put = put_uint32, -}; - -/* 32 bit uint. See that the received value is the same than the one - in the field */ - -static int get_uint32_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - uint32_t *v = pv; - uint32_t v2; - qemu_get_be32s(f, &v2); - - if (*v == v2) { - return 0; - } - error_report("%" PRIx32 " != %" PRIx32, *v, v2); - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint32_equal = { - .name = "uint32 equal", - .get = get_uint32_equal, - .put = put_uint32, -}; - -/* 64 bit unsigned int */ - -static int get_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - uint64_t *v = pv; - qemu_get_be64s(f, v); - return 0; -} - -static int put_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - uint64_t *v = pv; - qemu_put_be64s(f, v); - return 0; -} - -const VMStateInfo vmstate_info_uint64 = { - .name = "uint64", - .get = get_uint64, - .put = put_uint64, -}; - -static int get_nullptr(QEMUFile *f, void *pv, size_t size, VMStateField *field) - -{ - if (qemu_get_byte(f) == VMS_NULLPTR_MARKER) { - return 0; - } - error_report("vmstate: get_nullptr expected VMS_NULLPTR_MARKER"); - return -EINVAL; -} - -static int put_nullptr(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) - -{ - if (pv == NULL) { - qemu_put_byte(f, VMS_NULLPTR_MARKER); - return 0; - } - error_report("vmstate: put_nullptr must be called with pv == NULL"); - return -EINVAL; -} - -const VMStateInfo vmstate_info_nullptr = { - .name = "uint64", - .get = get_nullptr, - .put = put_nullptr, -}; - -/* 64 bit unsigned int. See that the received value is the same than the one - in the field */ - -static int get_uint64_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - uint64_t *v = pv; - uint64_t v2; - qemu_get_be64s(f, &v2); - - if (*v == v2) { - return 0; - } - error_report("%" PRIx64 " != %" PRIx64, *v, v2); - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint64_equal = { - .name = "int64 equal", - .get = get_uint64_equal, - .put = put_uint64, -}; - -/* 8 bit int. See that the received value is the same than the one - in the field */ - -static int get_uint8_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - uint8_t *v = pv; - uint8_t v2; - qemu_get_8s(f, &v2); - - if (*v == v2) { - return 0; - } - error_report("%x != %x", *v, v2); - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint8_equal = { - .name = "uint8 equal", - .get = get_uint8_equal, - .put = put_uint8, -}; - -/* 16 bit unsigned int int. See that the received value is the same than the one - in the field */ - -static int get_uint16_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - uint16_t *v = pv; - uint16_t v2; - qemu_get_be16s(f, &v2); - - if (*v == v2) { - return 0; - } - error_report("%x != %x", *v, v2); - return -EINVAL; -} - -const VMStateInfo vmstate_info_uint16_equal = { - .name = "uint16 equal", - .get = get_uint16_equal, - .put = put_uint16, -}; - -/* floating point */ - -static int get_float64(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - float64 *v = pv; - - *v = make_float64(qemu_get_be64(f)); - return 0; -} - -static int put_float64(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - uint64_t *v = pv; - - qemu_put_be64(f, float64_val(*v)); - return 0; -} - -const VMStateInfo vmstate_info_float64 = { - .name = "float64", - .get = get_float64, - .put = put_float64, -}; - -/* CPU_DoubleU type */ - -static int get_cpudouble(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - CPU_DoubleU *v = pv; - qemu_get_be32s(f, &v->l.upper); - qemu_get_be32s(f, &v->l.lower); - return 0; -} - -static int put_cpudouble(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) -{ - CPU_DoubleU *v = pv; - qemu_put_be32s(f, &v->l.upper); - qemu_put_be32s(f, &v->l.lower); - return 0; -} - -const VMStateInfo vmstate_info_cpudouble = { - .name = "CPU_Double_U", - .get = get_cpudouble, - .put = put_cpudouble, -}; - -/* uint8_t buffers */ - -static int get_buffer(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - uint8_t *v = pv; - qemu_get_buffer(f, v, size); - return 0; -} - -static int put_buffer(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - uint8_t *v = pv; - qemu_put_buffer(f, v, size); - return 0; -} - -const VMStateInfo vmstate_info_buffer = { - .name = "buffer", - .get = get_buffer, - .put = put_buffer, -}; - -/* unused buffers: space that was used for some fields that are - not useful anymore */ - -static int get_unused_buffer(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - uint8_t buf[1024]; - int block_len; - - while (size > 0) { - block_len = MIN(sizeof(buf), size); - size -= block_len; - qemu_get_buffer(f, buf, block_len); - } - return 0; -} - -static int put_unused_buffer(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) -{ - static const uint8_t buf[1024]; - int block_len; - - while (size > 0) { - block_len = MIN(sizeof(buf), size); - size -= block_len; - qemu_put_buffer(f, buf, block_len); - } - - return 0; -} - -const VMStateInfo vmstate_info_unused_buffer = { - .name = "unused_buffer", - .get = get_unused_buffer, - .put = put_unused_buffer, -}; - -/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate - * a temporary buffer and the pre_load/pre_save methods in the child vmsd - * copy stuff from the parent into the child and do calculations to fill - * in fields that don't really exist in the parent but need to be in the - * stream. - */ -static int get_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - int ret; - const VMStateDescription *vmsd = field->vmsd; - int version_id = field->version_id; - void *tmp = g_malloc(size); - - /* Writes the parent field which is at the start of the tmp */ - *(void **)tmp = pv; - ret = vmstate_load_state(f, vmsd, tmp, version_id); - g_free(tmp); - return ret; -} - -static int put_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - const VMStateDescription *vmsd = field->vmsd; - void *tmp = g_malloc(size); - - /* Writes the parent field which is at the start of the tmp */ - *(void **)tmp = pv; - vmstate_save_state(f, vmsd, tmp, vmdesc); - g_free(tmp); - - return 0; -} - -const VMStateInfo vmstate_info_tmp = { - .name = "tmp", - .get = get_tmp, - .put = put_tmp, -}; - -/* bitmaps (as defined by bitmap.h). Note that size here is the size - * of the bitmap in bits. The on-the-wire format of a bitmap is 64 - * bit words with the bits in big endian order. The in-memory format - * is an array of 'unsigned long', which may be either 32 or 64 bits. - */ -/* This is the number of 64 bit words sent over the wire */ -#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) -static int get_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field) -{ - unsigned long *bmp = pv; - int i, idx = 0; - for (i = 0; i < BITS_TO_U64S(size); i++) { - uint64_t w = qemu_get_be64(f); - bmp[idx++] = w; - if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { - bmp[idx++] = w >> 32; - } - } - return 0; -} - -static int put_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) -{ - unsigned long *bmp = pv; - int i, idx = 0; - for (i = 0; i < BITS_TO_U64S(size); i++) { - uint64_t w = bmp[idx++]; - if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { - w |= ((uint64_t)bmp[idx++]) << 32; - } - qemu_put_be64(f, w); - } - - return 0; -} - -const VMStateInfo vmstate_info_bitmap = { - .name = "bitmap", - .get = get_bitmap, - .put = put_bitmap, -}; - -/* get for QTAILQ - * meta data about the QTAILQ is encoded in a VMStateField structure - */ -static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, - VMStateField *field) -{ - int ret = 0; - const VMStateDescription *vmsd = field->vmsd; - /* size of a QTAILQ element */ - size_t size = field->size; - /* offset of the QTAILQ entry in a QTAILQ element */ - size_t entry_offset = field->start; - int version_id = field->version_id; - void *elm; - - trace_get_qtailq(vmsd->name, version_id); - if (version_id > vmsd->version_id) { - error_report("%s %s", vmsd->name, "too new"); - trace_get_qtailq_end(vmsd->name, "too new", -EINVAL); - - return -EINVAL; - } - if (version_id < vmsd->minimum_version_id) { - error_report("%s %s", vmsd->name, "too old"); - trace_get_qtailq_end(vmsd->name, "too old", -EINVAL); - return -EINVAL; - } - - while (qemu_get_byte(f)) { - elm = g_malloc(size); - ret = vmstate_load_state(f, vmsd, elm, version_id); - if (ret) { - return ret; - } - QTAILQ_RAW_INSERT_TAIL(pv, elm, entry_offset); - } - - trace_get_qtailq_end(vmsd->name, "end", ret); - return ret; -} - -/* put for QTAILQ */ -static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size, - VMStateField *field, QJSON *vmdesc) -{ - const VMStateDescription *vmsd = field->vmsd; - /* offset of the QTAILQ entry in a QTAILQ element*/ - size_t entry_offset = field->start; - void *elm; - - trace_put_qtailq(vmsd->name, vmsd->version_id); - - QTAILQ_RAW_FOREACH(elm, pv, entry_offset) { - qemu_put_byte(f, true); - vmstate_save_state(f, vmsd, elm, vmdesc); - } - qemu_put_byte(f, false); - - trace_put_qtailq_end(vmsd->name, "end"); - - return 0; -} -const VMStateInfo vmstate_info_qtailq = { - .name = "qtailq", - .get = get_qtailq, - .put = put_qtailq, -}; diff --git a/migration/xbzrle.c b/migration/xbzrle.c index c858339259..1ba482ded9 100644 --- a/migration/xbzrle.c +++ b/migration/xbzrle.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "include/migration/migration.h" +#include "xbzrle.h" /* page = zrun nzrun diff --git a/migration/xbzrle.h b/migration/xbzrle.h new file mode 100644 index 0000000000..a0db507b9c --- /dev/null +++ b/migration/xbzrle.h @@ -0,0 +1,21 @@ +/* + * 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. + * + */ + +#ifndef QEMU_MIGRATION_XBZRLE_H +#define QEMU_MIGRATION_XBZRLE_H + +int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen); + +int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen); +#endif |