#include "qemu/osdep.h"
#include "cpu.h"
-#include <zlib.h>
#include "qemu/cutils.h"
#include "qemu/bitops.h"
#include "qemu/bitmap.h"
#include "xbzrle.h"
#include "ram.h"
#include "migration.h"
-#include "socket.h"
#include "migration/register.h"
#include "migration/misc.h"
#include "qemu-file.h"
#include "page_cache.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
+#include "qapi/qapi-types-migration.h"
#include "qapi/qapi-events-migration.h"
#include "qapi/qmp/qerror.h"
#include "trace.h"
#include "migration/colo.h"
#include "block.h"
#include "sysemu/sysemu.h"
-#include "qemu/uuid.h"
+#include "sysemu/cpu-throttle.h"
#include "savevm.h"
#include "qemu/iov.h"
+#include "multifd.h"
/***********************************************************/
/* ram save/restore */
return ret;
}
-static bool ramblock_is_ignored(RAMBlock *block)
+bool ramblock_is_ignored(RAMBlock *block)
{
return !qemu_ram_is_migratable(block) ||
(migrate_ignore_shared() && qemu_ram_is_shared(block));
}
-/* Should be holding either ram_list.mutex, or the RCU lock. */
-#define RAMBLOCK_FOREACH_NOT_IGNORED(block) \
- INTERNAL_RAMBLOCK_FOREACH(block) \
- if (ramblock_is_ignored(block)) {} else
-
-#define RAMBLOCK_FOREACH_MIGRATABLE(block) \
- INTERNAL_RAMBLOCK_FOREACH(block) \
- if (!qemu_ram_is_migratable(block)) {} else
-
#undef RAMBLOCK_FOREACH
int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque)
/*
* Always use little endian when sending the bitmap. This is
* required that when source and destination VMs are not using the
- * same endianess. (Note: big endian won't work.)
+ * same endianness. (Note: big endian won't work.)
*/
bitmap_to_le(le_bitmap, block->receivedmap, nbits);
qemu_put_buffer(file, (const uint8_t *)le_bitmap, size);
/*
* Mark as an end, in case the middle part is screwed up due to
- * some "misterious" reason.
+ * some "mysterious" reason.
*/
qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING);
qemu_fflush(file);
uint64_t num_dirty_pages_period;
/* xbzrle misses since the beginning of the period */
uint64_t xbzrle_cache_miss_prev;
+ /* Amount of xbzrle pages since the beginning of the period */
+ uint64_t xbzrle_pages_prev;
+ /* Amount of xbzrle encoded bytes since the beginning of the period */
+ uint64_t xbzrle_bytes_prev;
/* compression statistics since the beginning of the period */
/* amount of count that no free thread to compress data */
return -1;
}
-/* Multiple fd's */
-
-#define MULTIFD_MAGIC 0x11223344U
-#define MULTIFD_VERSION 1
-
-#define MULTIFD_FLAG_SYNC (1 << 0)
-
-/* This value needs to be a multiple of qemu_target_page_size() */
-#define MULTIFD_PACKET_SIZE (512 * 1024)
-
-typedef struct {
- uint32_t magic;
- uint32_t version;
- unsigned char uuid[16]; /* QemuUUID */
- uint8_t id;
- uint8_t unused1[7]; /* Reserved for future use */
- uint64_t unused2[4]; /* Reserved for future use */
-} __attribute__((packed)) MultiFDInit_t;
-
-typedef struct {
- uint32_t magic;
- uint32_t version;
- uint32_t flags;
- /* maximum number of allocated pages */
- uint32_t pages_alloc;
- uint32_t pages_used;
- /* size of the next packet that contains pages */
- uint32_t next_packet_size;
- uint64_t packet_num;
- uint64_t unused[4]; /* Reserved for future use */
- char ramblock[256];
- uint64_t offset[];
-} __attribute__((packed)) MultiFDPacket_t;
-
-typedef struct {
- /* number of used pages */
- uint32_t used;
- /* number of allocated pages */
- uint32_t allocated;
- /* global number of generated multifd packets */
- uint64_t packet_num;
- /* offset of each page */
- ram_addr_t *offset;
- /* pointer to each page */
- struct iovec *iov;
- RAMBlock *block;
-} MultiFDPages_t;
-
-typedef struct {
- /* this fields are not changed once the thread is created */
- /* channel number */
- uint8_t id;
- /* channel thread name */
- char *name;
- /* channel thread id */
- QemuThread thread;
- /* communication channel */
- QIOChannel *c;
- /* sem where to wait for more work */
- QemuSemaphore sem;
- /* this mutex protects the following parameters */
- QemuMutex mutex;
- /* is this channel thread running */
- bool running;
- /* should this thread finish */
- bool quit;
- /* thread has work to do */
- int pending_job;
- /* array of pages to sent */
- MultiFDPages_t *pages;
- /* packet allocated len */
- uint32_t packet_len;
- /* pointer to the packet */
- MultiFDPacket_t *packet;
- /* multifd flags for each packet */
- uint32_t flags;
- /* size of the next packet that contains pages */
- uint32_t next_packet_size;
- /* global number of generated multifd packets */
- uint64_t packet_num;
- /* thread local variables */
- /* packets sent through this channel */
- uint64_t num_packets;
- /* pages sent through this channel */
- uint64_t num_pages;
- /* syncs main thread and channels */
- QemuSemaphore sem_sync;
-} MultiFDSendParams;
-
-typedef struct {
- /* this fields are not changed once the thread is created */
- /* channel number */
- uint8_t id;
- /* channel thread name */
- char *name;
- /* channel thread id */
- QemuThread thread;
- /* communication channel */
- QIOChannel *c;
- /* this mutex protects the following parameters */
- QemuMutex mutex;
- /* is this channel thread running */
- bool running;
- /* should this thread finish */
- bool quit;
- /* array of pages to receive */
- MultiFDPages_t *pages;
- /* packet allocated len */
- uint32_t packet_len;
- /* pointer to the packet */
- MultiFDPacket_t *packet;
- /* multifd flags for each packet */
- uint32_t flags;
- /* global number of generated multifd packets */
- uint64_t packet_num;
- /* thread local variables */
- /* size of the next packet that contains pages */
- uint32_t next_packet_size;
- /* packets sent through this channel */
- uint64_t num_packets;
- /* pages sent through this channel */
- uint64_t num_pages;
- /* syncs main thread and channels */
- QemuSemaphore sem_sync;
-} MultiFDRecvParams;
-
-static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp)
-{
- MultiFDInit_t msg = {};
- int ret;
-
- msg.magic = cpu_to_be32(MULTIFD_MAGIC);
- msg.version = cpu_to_be32(MULTIFD_VERSION);
- msg.id = p->id;
- memcpy(msg.uuid, &qemu_uuid.data, sizeof(msg.uuid));
-
- ret = qio_channel_write_all(p->c, (char *)&msg, sizeof(msg), errp);
- if (ret != 0) {
- return -1;
- }
- return 0;
-}
-
-static int multifd_recv_initial_packet(QIOChannel *c, Error **errp)
-{
- MultiFDInit_t msg;
- int ret;
-
- ret = qio_channel_read_all(c, (char *)&msg, sizeof(msg), errp);
- if (ret != 0) {
- return -1;
- }
-
- msg.magic = be32_to_cpu(msg.magic);
- msg.version = be32_to_cpu(msg.version);
-
- if (msg.magic != MULTIFD_MAGIC) {
- error_setg(errp, "multifd: received packet magic %x "
- "expected %x", msg.magic, MULTIFD_MAGIC);
- return -1;
- }
-
- if (msg.version != MULTIFD_VERSION) {
- error_setg(errp, "multifd: received packet version %d "
- "expected %d", msg.version, MULTIFD_VERSION);
- return -1;
- }
-
- if (memcmp(msg.uuid, &qemu_uuid, sizeof(qemu_uuid))) {
- char *uuid = qemu_uuid_unparse_strdup(&qemu_uuid);
- char *msg_uuid = qemu_uuid_unparse_strdup((const QemuUUID *)msg.uuid);
-
- error_setg(errp, "multifd: received uuid '%s' and expected "
- "uuid '%s' for channel %hhd", msg_uuid, uuid, msg.id);
- g_free(uuid);
- g_free(msg_uuid);
- return -1;
- }
-
- if (msg.id > migrate_multifd_channels()) {
- error_setg(errp, "multifd: received channel version %d "
- "expected %d", msg.version, MULTIFD_VERSION);
- return -1;
- }
-
- return msg.id;
-}
-
-static MultiFDPages_t *multifd_pages_init(size_t size)
-{
- MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1);
-
- pages->allocated = size;
- pages->iov = g_new0(struct iovec, size);
- pages->offset = g_new0(ram_addr_t, size);
-
- return pages;
-}
-
-static void multifd_pages_clear(MultiFDPages_t *pages)
-{
- pages->used = 0;
- pages->allocated = 0;
- pages->packet_num = 0;
- pages->block = NULL;
- g_free(pages->iov);
- pages->iov = NULL;
- g_free(pages->offset);
- pages->offset = NULL;
- g_free(pages);
-}
-
-static void multifd_send_fill_packet(MultiFDSendParams *p)
-{
- MultiFDPacket_t *packet = p->packet;
- int i;
-
- packet->flags = cpu_to_be32(p->flags);
- packet->pages_alloc = cpu_to_be32(p->pages->allocated);
- packet->pages_used = cpu_to_be32(p->pages->used);
- packet->next_packet_size = cpu_to_be32(p->next_packet_size);
- packet->packet_num = cpu_to_be64(p->packet_num);
-
- if (p->pages->block) {
- strncpy(packet->ramblock, p->pages->block->idstr, 256);
- }
-
- for (i = 0; i < p->pages->used; i++) {
- /* there are architectures where ram_addr_t is 32 bit */
- uint64_t temp = p->pages->offset[i];
-
- packet->offset[i] = cpu_to_be64(temp);
- }
-}
-
-static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
-{
- MultiFDPacket_t *packet = p->packet;
- uint32_t pages_max = MULTIFD_PACKET_SIZE / qemu_target_page_size();
- RAMBlock *block;
- int i;
-
- packet->magic = be32_to_cpu(packet->magic);
- if (packet->magic != MULTIFD_MAGIC) {
- error_setg(errp, "multifd: received packet "
- "magic %x and expected magic %x",
- packet->magic, MULTIFD_MAGIC);
- return -1;
- }
-
- packet->version = be32_to_cpu(packet->version);
- if (packet->version != MULTIFD_VERSION) {
- error_setg(errp, "multifd: received packet "
- "version %d and expected version %d",
- packet->version, MULTIFD_VERSION);
- return -1;
- }
-
- p->flags = be32_to_cpu(packet->flags);
-
- packet->pages_alloc = be32_to_cpu(packet->pages_alloc);
- /*
- * If we received a packet that is 100 times bigger than expected
- * just stop migration. It is a magic number.
- */
- if (packet->pages_alloc > pages_max * 100) {
- error_setg(errp, "multifd: received packet "
- "with size %d and expected a maximum size of %d",
- packet->pages_alloc, pages_max * 100) ;
- return -1;
- }
- /*
- * We received a packet that is bigger than expected but inside
- * reasonable limits (see previous comment). Just reallocate.
- */
- if (packet->pages_alloc > p->pages->allocated) {
- multifd_pages_clear(p->pages);
- p->pages = multifd_pages_init(packet->pages_alloc);
- }
-
- p->pages->used = be32_to_cpu(packet->pages_used);
- if (p->pages->used > packet->pages_alloc) {
- error_setg(errp, "multifd: received packet "
- "with %d pages and expected maximum pages are %d",
- p->pages->used, packet->pages_alloc) ;
- return -1;
- }
-
- p->next_packet_size = be32_to_cpu(packet->next_packet_size);
- p->packet_num = be64_to_cpu(packet->packet_num);
-
- if (p->pages->used == 0) {
- return 0;
- }
-
- /* make sure that ramblock is 0 terminated */
- packet->ramblock[255] = 0;
- block = qemu_ram_block_by_name(packet->ramblock);
- if (!block) {
- error_setg(errp, "multifd: unknown ram block %s",
- packet->ramblock);
- return -1;
- }
-
- for (i = 0; i < p->pages->used; i++) {
- uint64_t offset = be64_to_cpu(packet->offset[i]);
-
- if (offset > (block->used_length - TARGET_PAGE_SIZE)) {
- error_setg(errp, "multifd: offset too long %" PRIu64
- " (max " RAM_ADDR_FMT ")",
- offset, block->max_length);
- return -1;
- }
- p->pages->iov[i].iov_base = block->host + offset;
- p->pages->iov[i].iov_len = TARGET_PAGE_SIZE;
- }
-
- return 0;
-}
-
-struct {
- MultiFDSendParams *params;
- /* array of pages to sent */
- MultiFDPages_t *pages;
- /* global number of generated multifd packets */
- uint64_t packet_num;
- /* send channels ready */
- QemuSemaphore channels_ready;
- /*
- * Have we already run terminate threads. There is a race when it
- * happens that we got one error while we are exiting.
- * We will use atomic operations. Only valid values are 0 and 1.
- */
- int exiting;
-} *multifd_send_state;
-
-/*
- * How we use multifd_send_state->pages and channel->pages?
- *
- * We create a pages for each channel, and a main one. Each time that
- * we need to send a batch of pages we interchange the ones between
- * multifd_send_state and the channel that is sending it. There are
- * two reasons for that:
- * - to not have to do so many mallocs during migration
- * - to make easier to know what to free at the end of migration
- *
- * This way we always know who is the owner of each "pages" struct,
- * and we don't need any locking. It belongs to the migration thread
- * or to the channel thread. Switching is safe because the migration
- * thread is using the channel mutex when changing it, and the channel
- * have to had finish with its own, otherwise pending_job can't be
- * false.
- */
-
-static int multifd_send_pages(QEMUFile *f)
-{
- int i;
- static int next_channel;
- MultiFDSendParams *p = NULL; /* make happy gcc */
- MultiFDPages_t *pages = multifd_send_state->pages;
- uint64_t transferred;
-
- if (atomic_read(&multifd_send_state->exiting)) {
- return -1;
- }
-
- qemu_sem_wait(&multifd_send_state->channels_ready);
- for (i = next_channel;; i = (i + 1) % migrate_multifd_channels()) {
- p = &multifd_send_state->params[i];
-
- qemu_mutex_lock(&p->mutex);
- if (p->quit) {
- error_report("%s: channel %d has already quit!", __func__, i);
- qemu_mutex_unlock(&p->mutex);
- return -1;
- }
- if (!p->pending_job) {
- p->pending_job++;
- next_channel = (i + 1) % migrate_multifd_channels();
- break;
- }
- qemu_mutex_unlock(&p->mutex);
- }
- assert(!p->pages->used);
- assert(!p->pages->block);
-
- p->packet_num = multifd_send_state->packet_num++;
- multifd_send_state->pages = p->pages;
- p->pages = pages;
- transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len;
- qemu_file_update_transfer(f, transferred);
- ram_counters.multifd_bytes += transferred;
- ram_counters.transferred += transferred;;
- qemu_mutex_unlock(&p->mutex);
- qemu_sem_post(&p->sem);
-
- return 1;
-}
-
-static int multifd_queue_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
-{
- MultiFDPages_t *pages = multifd_send_state->pages;
-
- if (!pages->block) {
- pages->block = block;
- }
-
- if (pages->block == block) {
- pages->offset[pages->used] = offset;
- pages->iov[pages->used].iov_base = block->host + offset;
- pages->iov[pages->used].iov_len = TARGET_PAGE_SIZE;
- pages->used++;
-
- if (pages->used < pages->allocated) {
- return 1;
- }
- }
-
- if (multifd_send_pages(rs->f) < 0) {
- return -1;
- }
-
- if (pages->block != block) {
- return multifd_queue_page(rs, block, offset);
- }
-
- return 1;
-}
-
-static void multifd_send_terminate_threads(Error *err)
-{
- int i;
-
- trace_multifd_send_terminate_threads(err != NULL);
-
- if (err) {
- MigrationState *s = migrate_get_current();
- migrate_set_error(s, err);
- if (s->state == MIGRATION_STATUS_SETUP ||
- s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
- s->state == MIGRATION_STATUS_DEVICE ||
- s->state == MIGRATION_STATUS_ACTIVE) {
- migrate_set_state(&s->state, s->state,
- MIGRATION_STATUS_FAILED);
- }
- }
-
- /*
- * We don't want to exit each threads twice. Depending on where
- * we get the error, or if there are two independent errors in two
- * threads at the same time, we can end calling this function
- * twice.
- */
- if (atomic_xchg(&multifd_send_state->exiting, 1)) {
- return;
- }
-
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDSendParams *p = &multifd_send_state->params[i];
-
- qemu_mutex_lock(&p->mutex);
- p->quit = true;
- qemu_sem_post(&p->sem);
- qemu_mutex_unlock(&p->mutex);
- }
-}
-
-void multifd_save_cleanup(void)
-{
- int i;
-
- if (!migrate_use_multifd()) {
- return;
- }
- multifd_send_terminate_threads(NULL);
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDSendParams *p = &multifd_send_state->params[i];
-
- if (p->running) {
- qemu_thread_join(&p->thread);
- }
- }
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDSendParams *p = &multifd_send_state->params[i];
-
- socket_send_channel_destroy(p->c);
- p->c = NULL;
- qemu_mutex_destroy(&p->mutex);
- qemu_sem_destroy(&p->sem);
- qemu_sem_destroy(&p->sem_sync);
- g_free(p->name);
- p->name = NULL;
- multifd_pages_clear(p->pages);
- p->pages = NULL;
- p->packet_len = 0;
- g_free(p->packet);
- p->packet = NULL;
- }
- qemu_sem_destroy(&multifd_send_state->channels_ready);
- g_free(multifd_send_state->params);
- multifd_send_state->params = NULL;
- multifd_pages_clear(multifd_send_state->pages);
- multifd_send_state->pages = NULL;
- g_free(multifd_send_state);
- multifd_send_state = NULL;
-}
-
-static void multifd_send_sync_main(RAMState *rs)
-{
- int i;
-
- if (!migrate_use_multifd()) {
- return;
- }
- if (multifd_send_state->pages->used) {
- if (multifd_send_pages(rs->f) < 0) {
- error_report("%s: multifd_send_pages fail", __func__);
- return;
- }
- }
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDSendParams *p = &multifd_send_state->params[i];
-
- trace_multifd_send_sync_main_signal(p->id);
-
- qemu_mutex_lock(&p->mutex);
-
- if (p->quit) {
- error_report("%s: channel %d has already quit", __func__, i);
- qemu_mutex_unlock(&p->mutex);
- return;
- }
-
- p->packet_num = multifd_send_state->packet_num++;
- p->flags |= MULTIFD_FLAG_SYNC;
- p->pending_job++;
- qemu_file_update_transfer(rs->f, p->packet_len);
- ram_counters.multifd_bytes += p->packet_len;
- ram_counters.transferred += p->packet_len;
- qemu_mutex_unlock(&p->mutex);
- qemu_sem_post(&p->sem);
- }
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDSendParams *p = &multifd_send_state->params[i];
-
- trace_multifd_send_sync_main_wait(p->id);
- qemu_sem_wait(&p->sem_sync);
- }
- trace_multifd_send_sync_main(multifd_send_state->packet_num);
-}
-
-static void *multifd_send_thread(void *opaque)
-{
- MultiFDSendParams *p = opaque;
- Error *local_err = NULL;
- int ret = 0;
- uint32_t flags = 0;
-
- trace_multifd_send_thread_start(p->id);
- rcu_register_thread();
-
- if (multifd_send_initial_packet(p, &local_err) < 0) {
- ret = -1;
- goto out;
- }
- /* initial packet */
- p->num_packets = 1;
-
- while (true) {
- qemu_sem_wait(&p->sem);
-
- if (atomic_read(&multifd_send_state->exiting)) {
- break;
- }
- qemu_mutex_lock(&p->mutex);
-
- if (p->pending_job) {
- uint32_t used = p->pages->used;
- uint64_t packet_num = p->packet_num;
- flags = p->flags;
-
- p->next_packet_size = used * qemu_target_page_size();
- multifd_send_fill_packet(p);
- p->flags = 0;
- p->num_packets++;
- p->num_pages += used;
- p->pages->used = 0;
- p->pages->block = NULL;
- qemu_mutex_unlock(&p->mutex);
-
- trace_multifd_send(p->id, packet_num, used, flags,
- p->next_packet_size);
-
- ret = qio_channel_write_all(p->c, (void *)p->packet,
- p->packet_len, &local_err);
- if (ret != 0) {
- break;
- }
-
- if (used) {
- ret = qio_channel_writev_all(p->c, p->pages->iov,
- used, &local_err);
- if (ret != 0) {
- break;
- }
- }
-
- qemu_mutex_lock(&p->mutex);
- p->pending_job--;
- qemu_mutex_unlock(&p->mutex);
-
- if (flags & MULTIFD_FLAG_SYNC) {
- qemu_sem_post(&p->sem_sync);
- }
- qemu_sem_post(&multifd_send_state->channels_ready);
- } else if (p->quit) {
- qemu_mutex_unlock(&p->mutex);
- break;
- } else {
- qemu_mutex_unlock(&p->mutex);
- /* sometimes there are spurious wakeups */
- }
- }
-
-out:
- if (local_err) {
- trace_multifd_send_error(p->id);
- multifd_send_terminate_threads(local_err);
- }
-
- /*
- * Error happen, I will exit, but I can't just leave, tell
- * who pay attention to me.
- */
- if (ret != 0) {
- qemu_sem_post(&p->sem_sync);
- qemu_sem_post(&multifd_send_state->channels_ready);
- }
-
- qemu_mutex_lock(&p->mutex);
- p->running = false;
- qemu_mutex_unlock(&p->mutex);
-
- rcu_unregister_thread();
- trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages);
-
- return NULL;
-}
-
-static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
-{
- MultiFDSendParams *p = opaque;
- QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task));
- Error *local_err = NULL;
-
- trace_multifd_new_send_channel_async(p->id);
- if (qio_task_propagate_error(task, &local_err)) {
- migrate_set_error(migrate_get_current(), local_err);
- /* Error happen, we need to tell who pay attention to me */
- qemu_sem_post(&multifd_send_state->channels_ready);
- qemu_sem_post(&p->sem_sync);
- /*
- * Although multifd_send_thread is not created, but main migration
- * thread neet to judge whether it is running, so we need to mark
- * its status.
- */
- p->quit = true;
- } else {
- p->c = QIO_CHANNEL(sioc);
- qio_channel_set_delay(p->c, false);
- p->running = true;
- qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
- QEMU_THREAD_JOINABLE);
- }
-}
-
-int multifd_save_setup(void)
-{
- int thread_count;
- uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
- uint8_t i;
-
- if (!migrate_use_multifd()) {
- return 0;
- }
- thread_count = migrate_multifd_channels();
- multifd_send_state = g_malloc0(sizeof(*multifd_send_state));
- multifd_send_state->params = g_new0(MultiFDSendParams, thread_count);
- multifd_send_state->pages = multifd_pages_init(page_count);
- qemu_sem_init(&multifd_send_state->channels_ready, 0);
- atomic_set(&multifd_send_state->exiting, 0);
-
- for (i = 0; i < thread_count; i++) {
- MultiFDSendParams *p = &multifd_send_state->params[i];
-
- qemu_mutex_init(&p->mutex);
- qemu_sem_init(&p->sem, 0);
- qemu_sem_init(&p->sem_sync, 0);
- p->quit = false;
- p->pending_job = 0;
- p->id = i;
- p->pages = multifd_pages_init(page_count);
- p->packet_len = sizeof(MultiFDPacket_t)
- + sizeof(uint64_t) * page_count;
- p->packet = g_malloc0(p->packet_len);
- p->packet->magic = cpu_to_be32(MULTIFD_MAGIC);
- p->packet->version = cpu_to_be32(MULTIFD_VERSION);
- p->name = g_strdup_printf("multifdsend_%d", i);
- socket_send_channel_create(multifd_new_send_channel_async, p);
- }
- return 0;
-}
-
-struct {
- MultiFDRecvParams *params;
- /* number of created threads */
- int count;
- /* syncs main thread and channels */
- QemuSemaphore sem_sync;
- /* global number of generated multifd packets */
- uint64_t packet_num;
-} *multifd_recv_state;
-
-static void multifd_recv_terminate_threads(Error *err)
-{
- int i;
-
- trace_multifd_recv_terminate_threads(err != NULL);
-
- if (err) {
- MigrationState *s = migrate_get_current();
- migrate_set_error(s, err);
- if (s->state == MIGRATION_STATUS_SETUP ||
- s->state == MIGRATION_STATUS_ACTIVE) {
- migrate_set_state(&s->state, s->state,
- MIGRATION_STATUS_FAILED);
- }
- }
-
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDRecvParams *p = &multifd_recv_state->params[i];
-
- qemu_mutex_lock(&p->mutex);
- p->quit = true;
- /* We could arrive here for two reasons:
- - normal quit, i.e. everything went fine, just finished
- - error quit: We close the channels so the channel threads
- finish the qio_channel_read_all_eof() */
- if (p->c) {
- qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
- }
- qemu_mutex_unlock(&p->mutex);
- }
-}
-
-int multifd_load_cleanup(Error **errp)
-{
- int i;
- int ret = 0;
-
- if (!migrate_use_multifd()) {
- return 0;
- }
- multifd_recv_terminate_threads(NULL);
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDRecvParams *p = &multifd_recv_state->params[i];
-
- if (p->running) {
- p->quit = true;
- /*
- * multifd_recv_thread may hung at MULTIFD_FLAG_SYNC handle code,
- * however try to wakeup it without harm in cleanup phase.
- */
- qemu_sem_post(&p->sem_sync);
- qemu_thread_join(&p->thread);
- }
- }
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDRecvParams *p = &multifd_recv_state->params[i];
-
- object_unref(OBJECT(p->c));
- p->c = NULL;
- qemu_mutex_destroy(&p->mutex);
- qemu_sem_destroy(&p->sem_sync);
- g_free(p->name);
- p->name = NULL;
- multifd_pages_clear(p->pages);
- p->pages = NULL;
- p->packet_len = 0;
- g_free(p->packet);
- p->packet = NULL;
- }
- qemu_sem_destroy(&multifd_recv_state->sem_sync);
- g_free(multifd_recv_state->params);
- multifd_recv_state->params = NULL;
- g_free(multifd_recv_state);
- multifd_recv_state = NULL;
-
- return ret;
-}
-
-static void multifd_recv_sync_main(void)
-{
- int i;
-
- if (!migrate_use_multifd()) {
- return;
- }
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDRecvParams *p = &multifd_recv_state->params[i];
-
- trace_multifd_recv_sync_main_wait(p->id);
- qemu_sem_wait(&multifd_recv_state->sem_sync);
- }
- for (i = 0; i < migrate_multifd_channels(); i++) {
- MultiFDRecvParams *p = &multifd_recv_state->params[i];
-
- qemu_mutex_lock(&p->mutex);
- if (multifd_recv_state->packet_num < p->packet_num) {
- multifd_recv_state->packet_num = p->packet_num;
- }
- qemu_mutex_unlock(&p->mutex);
- trace_multifd_recv_sync_main_signal(p->id);
- qemu_sem_post(&p->sem_sync);
- }
- trace_multifd_recv_sync_main(multifd_recv_state->packet_num);
-}
-
-static void *multifd_recv_thread(void *opaque)
-{
- MultiFDRecvParams *p = opaque;
- Error *local_err = NULL;
- int ret;
-
- trace_multifd_recv_thread_start(p->id);
- rcu_register_thread();
-
- while (true) {
- uint32_t used;
- uint32_t flags;
-
- if (p->quit) {
- break;
- }
-
- ret = qio_channel_read_all_eof(p->c, (void *)p->packet,
- p->packet_len, &local_err);
- if (ret == 0) { /* EOF */
- break;
- }
- if (ret == -1) { /* Error */
- break;
- }
-
- qemu_mutex_lock(&p->mutex);
- ret = multifd_recv_unfill_packet(p, &local_err);
- if (ret) {
- qemu_mutex_unlock(&p->mutex);
- break;
- }
-
- used = p->pages->used;
- flags = p->flags;
- trace_multifd_recv(p->id, p->packet_num, used, flags,
- p->next_packet_size);
- p->num_packets++;
- p->num_pages += used;
- qemu_mutex_unlock(&p->mutex);
-
- if (used) {
- ret = qio_channel_readv_all(p->c, p->pages->iov,
- used, &local_err);
- if (ret != 0) {
- break;
- }
- }
-
- if (flags & MULTIFD_FLAG_SYNC) {
- qemu_sem_post(&multifd_recv_state->sem_sync);
- qemu_sem_wait(&p->sem_sync);
- }
- }
-
- if (local_err) {
- multifd_recv_terminate_threads(local_err);
- }
- qemu_mutex_lock(&p->mutex);
- p->running = false;
- qemu_mutex_unlock(&p->mutex);
-
- rcu_unregister_thread();
- trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages);
-
- return NULL;
-}
-
-int multifd_load_setup(void)
-{
- int thread_count;
- uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
- uint8_t i;
-
- if (!migrate_use_multifd()) {
- return 0;
- }
- thread_count = migrate_multifd_channels();
- multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state));
- multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count);
- atomic_set(&multifd_recv_state->count, 0);
- qemu_sem_init(&multifd_recv_state->sem_sync, 0);
-
- for (i = 0; i < thread_count; i++) {
- MultiFDRecvParams *p = &multifd_recv_state->params[i];
-
- qemu_mutex_init(&p->mutex);
- qemu_sem_init(&p->sem_sync, 0);
- p->quit = false;
- p->id = i;
- p->pages = multifd_pages_init(page_count);
- p->packet_len = sizeof(MultiFDPacket_t)
- + sizeof(uint64_t) * page_count;
- p->packet = g_malloc0(p->packet_len);
- p->name = g_strdup_printf("multifdrecv_%d", i);
- }
- return 0;
-}
-
-bool multifd_recv_all_channels_created(void)
-{
- int thread_count = migrate_multifd_channels();
-
- if (!migrate_use_multifd()) {
- return true;
- }
-
- return thread_count == atomic_read(&multifd_recv_state->count);
-}
-
-/*
- * Try to receive all multifd channels to get ready for the migration.
- * - Return true and do not set @errp when correctly receving all channels;
- * - Return false and do not set @errp when correctly receiving the current one;
- * - Return false and set @errp when failing to receive the current channel.
- */
-bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
-{
- MultiFDRecvParams *p;
- Error *local_err = NULL;
- int id;
-
- id = multifd_recv_initial_packet(ioc, &local_err);
- if (id < 0) {
- multifd_recv_terminate_threads(local_err);
- error_propagate_prepend(errp, local_err,
- "failed to receive packet"
- " via multifd channel %d: ",
- atomic_read(&multifd_recv_state->count));
- return false;
- }
- trace_multifd_recv_new_channel(id);
-
- p = &multifd_recv_state->params[id];
- if (p->c != NULL) {
- error_setg(&local_err, "multifd: received id '%d' already setup'",
- id);
- multifd_recv_terminate_threads(local_err);
- error_propagate(errp, local_err);
- return false;
- }
- p->c = ioc;
- object_ref(OBJECT(ioc));
- /* initial packet */
- p->num_packets = 1;
-
- p->running = true;
- qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
- QEMU_THREAD_JOINABLE);
- atomic_inc(&multifd_recv_state->count);
- return atomic_read(&multifd_recv_state->count) ==
- migrate_multifd_channels();
-}
-
/**
* save_page_header: write page header to wire
*
* able to complete migration. Some workloads dirty memory way too
* fast and will not effectively converge, even with auto-converge.
*/
-static void mig_throttle_guest_down(void)
+static void mig_throttle_guest_down(uint64_t bytes_dirty_period,
+ uint64_t bytes_dirty_threshold)
{
MigrationState *s = migrate_get_current();
uint64_t pct_initial = s->parameters.cpu_throttle_initial;
- uint64_t pct_icrement = s->parameters.cpu_throttle_increment;
+ uint64_t pct_increment = s->parameters.cpu_throttle_increment;
+ bool pct_tailslow = s->parameters.cpu_throttle_tailslow;
int pct_max = s->parameters.max_cpu_throttle;
+ uint64_t throttle_now = cpu_throttle_get_percentage();
+ uint64_t cpu_now, cpu_ideal, throttle_inc;
+
/* We have not started throttling yet. Let's start it. */
if (!cpu_throttle_active()) {
cpu_throttle_set(pct_initial);
} else {
/* Throttling already on, just increase the rate */
- cpu_throttle_set(MIN(cpu_throttle_get_percentage() + pct_icrement,
- pct_max));
+ if (!pct_tailslow) {
+ throttle_inc = pct_increment;
+ } else {
+ /* Compute the ideal CPU percentage used by Guest, which may
+ * make the dirty rate match the dirty rate threshold. */
+ cpu_now = 100 - throttle_now;
+ cpu_ideal = cpu_now * (bytes_dirty_threshold * 1.0 /
+ bytes_dirty_period);
+ throttle_inc = MIN(cpu_now - cpu_ideal, pct_increment);
+ }
+ cpu_throttle_set(MIN(throttle_now + throttle_inc, pct_max));
}
}
return -1;
}
+ /*
+ * Reaching here means the page has hit the xbzrle cache, no matter what
+ * encoding result it is (normal encoding, overflow or skipping the page),
+ * count the page as encoded. This is used to calculate the encoding rate.
+ *
+ * Example: 2 pages (8KB) being encoded, first page encoding generates 2KB,
+ * 2nd page turns out to be skipped (i.e. no new bytes written to the
+ * page), the overall encoding rate will be 8KB / 2KB = 4, which has the
+ * skipped page included. In this way, the encoding rate can tell if the
+ * guest page is good for xbzrle encoding.
+ */
+ xbzrle_counters.pages++;
prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);
/* save current buffer into memory */
} else if (encoded_len == -1) {
trace_save_xbzrle_page_overflow();
xbzrle_counters.overflow++;
+ xbzrle_counters.bytes += TARGET_PAGE_SIZE;
return -1;
}
qemu_put_be16(rs->f, encoded_len);
qemu_put_buffer(rs->f, XBZRLE.encoded_buf, encoded_len);
bytes_xbzrle += encoded_len + 1 + 2;
- xbzrle_counters.pages++;
- xbzrle_counters.bytes += bytes_xbzrle;
+ /*
+ * Like compressed_size (please see update_compress_thread_counts),
+ * the xbzrle encoded bytes don't count the 8 byte header with
+ * RAM_SAVE_FLAG_CONTINUE.
+ */
+ xbzrle_counters.bytes += bytes_xbzrle - 8;
ram_counters.transferred += bytes_xbzrle;
return 1;
/* Called with RCU critical section */
static void ramblock_sync_dirty_bitmap(RAMState *rs, RAMBlock *rb)
{
- rs->migration_dirty_pages +=
- cpu_physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length,
- &rs->num_dirty_pages_period);
+ uint64_t new_dirty_pages =
+ cpu_physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length);
+
+ rs->migration_dirty_pages += new_dirty_pages;
+ rs->num_dirty_pages_period += new_dirty_pages;
}
/**
}
if (migrate_use_xbzrle()) {
+ double encoded_size, unencoded_size;
+
xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss -
rs->xbzrle_cache_miss_prev) / page_count;
rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss;
+ unencoded_size = (xbzrle_counters.pages - rs->xbzrle_pages_prev) *
+ TARGET_PAGE_SIZE;
+ encoded_size = xbzrle_counters.bytes - rs->xbzrle_bytes_prev;
+ if (xbzrle_counters.pages == rs->xbzrle_pages_prev || !encoded_size) {
+ xbzrle_counters.encoding_rate = 0;
+ } else {
+ xbzrle_counters.encoding_rate = unencoded_size / encoded_size;
+ }
+ rs->xbzrle_pages_prev = xbzrle_counters.pages;
+ rs->xbzrle_bytes_prev = xbzrle_counters.bytes;
}
if (migrate_use_compression()) {
}
}
+static void migration_trigger_throttle(RAMState *rs)
+{
+ MigrationState *s = migrate_get_current();
+ uint64_t threshold = s->parameters.throttle_trigger_threshold;
+
+ uint64_t bytes_xfer_period = ram_counters.transferred - rs->bytes_xfer_prev;
+ uint64_t bytes_dirty_period = rs->num_dirty_pages_period * TARGET_PAGE_SIZE;
+ uint64_t bytes_dirty_threshold = bytes_xfer_period * threshold / 100;
+
+ /* During block migration the auto-converge logic incorrectly detects
+ * that ram migration makes no progress. Avoid this by disabling the
+ * throttling logic during the bulk phase of block migration. */
+ if (migrate_auto_converge() && !blk_mig_bulk_active()) {
+ /* The following detection logic can be refined later. For now:
+ Check to see if the ratio between dirtied bytes and the approx.
+ amount of bytes that just got transferred since the last time
+ we were in this routine reaches the threshold. If that happens
+ twice, start or increase throttling. */
+
+ if ((bytes_dirty_period > bytes_dirty_threshold) &&
+ (++rs->dirty_rate_high_cnt >= 2)) {
+ trace_migration_throttle();
+ rs->dirty_rate_high_cnt = 0;
+ mig_throttle_guest_down(bytes_dirty_period,
+ bytes_dirty_threshold);
+ }
+ }
+}
+
static void migration_bitmap_sync(RAMState *rs)
{
RAMBlock *block;
int64_t end_time;
- uint64_t bytes_xfer_now;
ram_counters.dirty_sync_count++;
/* more than 1 second = 1000 millisecons */
if (end_time > rs->time_last_bitmap_sync + 1000) {
- bytes_xfer_now = ram_counters.transferred;
-
- /* During block migration the auto-converge logic incorrectly detects
- * that ram migration makes no progress. Avoid this by disabling the
- * throttling logic during the bulk phase of block migration. */
- if (migrate_auto_converge() && !blk_mig_bulk_active()) {
- /* The following detection logic can be refined later. For now:
- Check to see if the dirtied bytes is 50% more than the approx.
- amount of bytes that just got transferred since the last time we
- were in this routine. If that happens twice, start or increase
- throttling */
-
- if ((rs->num_dirty_pages_period * TARGET_PAGE_SIZE >
- (bytes_xfer_now - rs->bytes_xfer_prev) / 2) &&
- (++rs->dirty_rate_high_cnt >= 2)) {
- trace_migration_throttle();
- rs->dirty_rate_high_cnt = 0;
- mig_throttle_guest_down();
- }
- }
+ migration_trigger_throttle(rs);
migration_update_rates(rs, end_time);
/* reset period counters */
rs->time_last_bitmap_sync = end_time;
rs->num_dirty_pages_period = 0;
- rs->bytes_xfer_prev = bytes_xfer_now;
+ rs->bytes_xfer_prev = ram_counters.transferred;
}
if (migrate_use_events()) {
qapi_event_send_migration_pass(ram_counters.dirty_sync_count);
*/
if (precopy_notify(PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC, &local_err)) {
error_report_err(local_err);
+ local_err = NULL;
}
migration_bitmap_sync(rs);
static int ram_save_multifd_page(RAMState *rs, RAMBlock *block,
ram_addr_t offset)
{
- if (multifd_queue_page(rs, block, offset) < 0) {
+ if (multifd_queue_page(rs->f, block, offset) < 0) {
return -1;
}
ram_counters.normal++;
return NULL;
}
- qemu_mutex_lock(&rs->src_page_req_mutex);
+ QEMU_LOCK_GUARD(&rs->src_page_req_mutex);
if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
struct RAMSrcPageRequest *entry =
QSIMPLEQ_FIRST(&rs->src_page_requests);
migration_consume_urgent_request();
}
}
- qemu_mutex_unlock(&rs->src_page_req_mutex);
return block;
}
}
trace_ram_postcopy_send_discard_bitmap();
- ret = postcopy_each_ram_send_discard(ms);
-
- return ret;
+ return postcopy_each_ram_send_discard(ms);
}
/**
ram_control_before_iterate(f, RAM_CONTROL_SETUP);
ram_control_after_iterate(f, RAM_CONTROL_SETUP);
- multifd_send_sync_main(*rsp);
+ multifd_send_sync_main(f);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f);
out:
if (ret >= 0
&& migration_is_setup_or_active(migrate_get_current()->state)) {
- multifd_send_sync_main(rs);
+ multifd_send_sync_main(rs->f);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f);
ram_counters.transferred += 8;
}
if (ret >= 0) {
- multifd_send_sync_main(rs);
+ multifd_send_sync_main(rs->f);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f);
}
}
static inline void *colo_cache_from_block_offset(RAMBlock *block,
- ram_addr_t offset)
+ ram_addr_t offset, bool record_bitmap)
{
if (!offset_in_ramblock(block, offset)) {
return NULL;
* It help us to decide which pages in ram cache should be flushed
* into VM's RAM later.
*/
- if (!test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) {
+ if (record_bitmap &&
+ !test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) {
ram_state->migration_dirty_pages++;
}
return block->colo_cache + offset;
}
return -errno;
}
- memcpy(block->colo_cache, block->host, block->used_length);
}
}
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
unsigned long pages = block->max_length >> TARGET_PAGE_BITS;
-
block->bmap = bitmap_new(pages);
- bitmap_set(block->bmap, 0, pages);
}
}
- ram_state = g_new0(RAMState, 1);
- ram_state->migration_dirty_pages = 0;
- qemu_mutex_init(&ram_state->bitmap_mutex);
- memory_global_dirty_log_start();
+ ram_state_init(&ram_state);
return 0;
}
+/* TODO: duplicated with ram_init_bitmaps */
+void colo_incoming_start_dirty_log(void)
+{
+ RAMBlock *block = NULL;
+ /* For memory_global_dirty_log_start below. */
+ qemu_mutex_lock_iothread();
+ qemu_mutex_lock_ramlist();
+
+ memory_global_dirty_log_sync();
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ ramblock_sync_dirty_bitmap(ram_state, block);
+ /* Discard this dirty bitmap record */
+ bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS);
+ }
+ memory_global_dirty_log_start();
+ }
+ ram_state->migration_dirty_pages = 0;
+ qemu_mutex_unlock_ramlist();
+ qemu_mutex_unlock_iothread();
+}
+
/* It is need to hold the global lock to call this helper */
void colo_release_ram_cache(void)
{
}
}
}
- qemu_mutex_destroy(&ram_state->bitmap_mutex);
- g_free(ram_state);
- ram_state = NULL;
+ ram_state_cleanup(&ram_state);
}
/**
/* Temporary page that is later 'placed' */
void *postcopy_host_page = mis->postcopy_tmp_page;
void *this_host = NULL;
- bool all_zero = false;
+ bool all_zero = true;
int target_pages = 0;
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
addr &= TARGET_PAGE_MASK;
trace_ram_load_postcopy_loop((uint64_t)addr, flags);
- place_needed = false;
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
RAM_SAVE_FLAG_COMPRESS_PAGE)) {
block = ram_block_from_stream(f, flags);
*/
page_buffer = postcopy_host_page +
((uintptr_t)host & (block->page_size - 1));
- /* If all TP are zero then we can optimise the place */
if (target_pages == 1) {
- all_zero = true;
this_host = (void *)QEMU_ALIGN_DOWN((uintptr_t)host,
block->page_size);
} else {
*/
if (target_pages == (block->page_size / TARGET_PAGE_SIZE)) {
place_needed = true;
- target_pages = 0;
}
place_source = postcopy_host_page;
}
ret = postcopy_place_page(mis, place_dest,
place_source, block);
}
+ place_needed = false;
+ target_pages = 0;
+ /* Assume we have a zero page until we detect something different */
+ all_zero = true;
}
}
* Flush content of RAM cache into SVM's memory.
* Only flush the pages that be dirtied by PVM or SVM or both.
*/
-static void colo_flush_ram_cache(void)
+void colo_flush_ram_cache(void)
{
RAMBlock *block = NULL;
void *dst_host;
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
ram_addr_t addr, total_ram_bytes;
- void *host = NULL;
+ void *host = NULL, *host_bak = NULL;
uint8_t ch;
/*
RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) {
RAMBlock *block = ram_block_from_stream(f, flags);
+ host = host_from_ram_block_offset(block, addr);
/*
- * After going into COLO, we should load the Page into colo_cache.
+ * After going into COLO stage, we should not load the page
+ * into SVM's memory directly, we put them into colo_cache firstly.
+ * NOTE: We need to keep a copy of SVM's ram in colo_cache.
+ * Previously, we copied all these memory in preparing stage of COLO
+ * while we need to stop VM, which is a time-consuming process.
+ * Here we optimize it by a trick, back-up every page while in
+ * migration process while COLO is enabled, though it affects the
+ * speed of the migration, but it obviously reduce the downtime of
+ * back-up all SVM'S memory in COLO preparing stage.
*/
- if (migration_incoming_in_colo_state()) {
- host = colo_cache_from_block_offset(block, addr);
- } else {
- host = host_from_ram_block_offset(block, addr);
+ if (migration_incoming_colo_enabled()) {
+ if (migration_incoming_in_colo_state()) {
+ /* In COLO stage, put all pages into cache temporarily */
+ host = colo_cache_from_block_offset(block, addr, true);
+ } else {
+ /*
+ * In migration stage but before COLO stage,
+ * Put all pages into both cache and SVM's memory.
+ */
+ host_bak = colo_cache_from_block_offset(block, addr, false);
+ }
}
if (!host) {
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
ret = -EINVAL;
break;
}
-
if (!migration_incoming_in_colo_state()) {
ramblock_recv_bitmap_set(block, host);
}
if (!ret) {
ret = qemu_file_get_error(f);
}
+ if (!ret && host_bak) {
+ memcpy(host_bak, host, TARGET_PAGE_SIZE);
+ }
}
ret |= wait_for_decompress_done();
}
trace_ram_load_complete(ret, seq_iter);
- if (!ret && migration_incoming_in_colo_state()) {
- colo_flush_ram_cache();
- }
return ret;
}
/*
* Note: see comments in ramblock_recv_bitmap_send() on why we
- * need the endianess convertion, and the paddings.
+ * need the endianness conversion, and the paddings.
*/
local_size = ROUND_UP(local_size, 8);
}
/*
- * Endianess convertion. We are during postcopy (though paused).
+ * Endianness conversion. We are during postcopy (though paused).
* The dirty bitmap won't change. We can directly modify it.
*/
bitmap_from_le(block->bmap, le_bitmap, nbits);