@@ -13,10 +13,13 @@
#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "migration/colo.h"
+#include "io/channel-buffer.h"
#include "trace.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
+#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024)
+
bool colo_supported(void)
{
return true;
@@ -55,6 +58,27 @@ static void colo_send_message(QEMUFile *f, COLOMessage msg,
trace_colo_send_message(COLOMessage_lookup[msg]);
}
+static void colo_send_message_value(QEMUFile *f, COLOMessage msg,
+ uint64_t value, Error **errp)
+{
+ Error *local_err = NULL;
+ int ret;
+
+ colo_send_message(f, msg, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ qemu_put_be64(f, value);
+ qemu_fflush(f);
+
+ ret = qemu_file_get_error(f);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to send value for message:%s",
+ COLOMessage_lookup[msg]);
+ }
+}
+
static COLOMessage colo_receive_message(QEMUFile *f, Error **errp)
{
COLOMessage msg;
@@ -91,9 +115,12 @@ static void colo_receive_check_message(QEMUFile *f, COLOMessage expect_msg,
}
}
-static int colo_do_checkpoint_transaction(MigrationState *s)
+static int colo_do_checkpoint_transaction(MigrationState *s,
+ QIOChannelBuffer *bioc,
+ QEMUFile *fb)
{
Error *local_err = NULL;
+ int ret = -1;
colo_send_message(s->to_dst_file, COLO_MESSAGE_CHECKPOINT_REQUEST,
&local_err);
@@ -106,15 +133,46 @@ static int colo_do_checkpoint_transaction(MigrationState *s)
if (local_err) {
goto out;
}
+ /* Reset channel-buffer directly */
+ qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL);
+ bioc->usage = 0;
+
+ qemu_mutex_lock_iothread();
+ vm_stop_force_state(RUN_STATE_COLO);
+ qemu_mutex_unlock_iothread();
+ trace_colo_vm_state_change("run", "stop");
- /* TODO: suspend and save vm state to colo buffer */
+ /* Disable block migration */
+ s->params.blk = 0;
+ s->params.shared = 0;
+ qemu_savevm_state_header(fb);
+ qemu_savevm_state_begin(fb, &s->params);
+ qemu_mutex_lock_iothread();
+ qemu_savevm_state_complete_precopy(fb, false);
+ qemu_mutex_unlock_iothread();
+
+ qemu_fflush(fb);
colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err);
if (local_err) {
goto out;
}
+ /*
+ * We need the size of the VMstate data in Secondary side,
+ * With which we can decide how much data should be read.
+ */
+ colo_send_message_value(s->to_dst_file, COLO_MESSAGE_VMSTATE_SIZE,
+ bioc->usage, &local_err);
+ if (local_err) {
+ goto out;
+ }
- /* TODO: send vmstate to Secondary */
+ qemu_put_buffer(s->to_dst_file, bioc->data, bioc->usage);
+ qemu_fflush(s->to_dst_file);
+ ret = qemu_file_get_error(s->to_dst_file);
+ if (ret < 0) {
+ goto out;
+ }
colo_receive_check_message(s->rp_state.from_dst_file,
COLO_MESSAGE_VMSTATE_RECEIVED, &local_err);
@@ -128,18 +186,24 @@ static int colo_do_checkpoint_transaction(MigrationState *s)
goto out;
}
- /* TODO: resume Primary */
+ ret = 0;
+
+ qemu_mutex_lock_iothread();
+ vm_start();
+ qemu_mutex_unlock_iothread();
+ trace_colo_vm_state_change("stop", "run");
- return 0;
out:
if (local_err) {
error_report_err(local_err);
}
- return -EINVAL;
+ return ret;
}
static void colo_process_checkpoint(MigrationState *s)
{
+ QIOChannelBuffer *bioc;
+ QEMUFile *fb = NULL;
Error *local_err = NULL;
int ret;
@@ -158,6 +222,9 @@ static void colo_process_checkpoint(MigrationState *s)
if (local_err) {
goto out;
}
+ bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE);
+ fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc));
+ object_unref(OBJECT(bioc));
qemu_mutex_lock_iothread();
vm_start();
@@ -165,7 +232,7 @@ static void colo_process_checkpoint(MigrationState *s)
trace_colo_vm_state_change("stop", "run");
while (s->state == MIGRATION_STATUS_COLO) {
- ret = colo_do_checkpoint_transaction(s);
+ ret = colo_do_checkpoint_transaction(s, bioc, fb);
if (ret < 0) {
goto out;
}
@@ -178,7 +245,7 @@ out:
}
migrate_set_state(&s->state, MIGRATION_STATUS_COLO,
MIGRATION_STATUS_COMPLETED);
-
+ qemu_fclose(fb);
if (s->rp_state.from_dst_file) {
qemu_fclose(s->rp_state.from_dst_file);
}
@@ -43,6 +43,7 @@
#include "trace.h"
#include "exec/ram_addr.h"
#include "qemu/rcu_queue.h"
+#include "migration/colo.h"
#ifdef DEBUG_MIGRATION_RAM
#define DPRINTF(fmt, ...) \
@@ -1874,16 +1875,8 @@ err:
return ret;
}
-
-/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has
- * long-running RCU critical section. When rcu-reclaims in the code
- * start to become numerous it will be necessary to reduce the
- * granularity of these critical sections.
- */
-
-static int ram_save_setup(QEMUFile *f, void *opaque)
+static int ram_save_init_globals(void)
{
- RAMBlock *block;
int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */
dirty_rate_high_cnt = 0;
@@ -1949,6 +1942,31 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
migration_bitmap_sync();
qemu_mutex_unlock_ramlist();
qemu_mutex_unlock_iothread();
+ rcu_read_unlock();
+
+ return 0;
+}
+
+/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has
+ * long-running RCU critical section. When rcu-reclaims in the code
+ * start to become numerous it will be necessary to reduce the
+ * granularity of these critical sections.
+ */
+
+static int ram_save_setup(QEMUFile *f, void *opaque)
+{
+ RAMBlock *block;
+
+ /*
+ * migration has already setup the bitmap, reuse it.
+ */
+ if (!migration_in_colo_state()) {
+ if (ram_save_init_globals() < 0) {
+ return -1;
+ }
+ }
+
+ rcu_read_lock();
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
@@ -2050,7 +2068,8 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
while (true) {
int pages;
- pages = ram_find_and_save_block(f, true, &bytes_transferred);
+ pages = ram_find_and_save_block(f, !migration_in_colo_state(),
+ &bytes_transferred);
/* no more blocks to sent */
if (pages == 0) {
break;