Compare commits
11 Commits
qdev-array
...
migration-
Author | SHA1 | Date | |
---|---|---|---|
|
12b4a9c05e | ||
|
69f57525d2 | ||
|
e00d1a4213 | ||
|
afa710ea20 | ||
|
6f4e4392d0 | ||
|
7670030d45 | ||
|
879822f1e7 | ||
|
28f7429b27 | ||
|
4f34243b94 | ||
|
f6d4af8ec4 | ||
|
aa86afaa1a |
103
migration/file.c
Normal file
103
migration/file.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023 Oracle and/or its affiliates.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "channel.h"
|
||||
#include "file.h"
|
||||
#include "migration.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "io/channel-util.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define OFFSET_OPTION ",offset="
|
||||
|
||||
/* Remove the offset option from @filespec and return it in @offsetp. */
|
||||
|
||||
static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
|
||||
{
|
||||
char *option = strstr(filespec, OFFSET_OPTION);
|
||||
int ret;
|
||||
|
||||
if (option) {
|
||||
*option = 0;
|
||||
option += sizeof(OFFSET_OPTION) - 1;
|
||||
ret = qemu_strtosz(option, NULL, offsetp);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "file URI has bad offset %s", option);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void file_start_outgoing_migration(MigrationState *s, const char *filespec,
|
||||
Error **errp)
|
||||
{
|
||||
g_autofree char *filename = g_strdup(filespec);
|
||||
g_autoptr(QIOChannelFile) fioc = NULL;
|
||||
uint64_t offset = 0;
|
||||
QIOChannel *ioc;
|
||||
|
||||
trace_migration_file_outgoing(filename);
|
||||
|
||||
if (file_parse_offset(filename, &offset, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC,
|
||||
0600, errp);
|
||||
if (!fioc) {
|
||||
return;
|
||||
}
|
||||
|
||||
ioc = QIO_CHANNEL(fioc);
|
||||
if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
qio_channel_set_name(ioc, "migration-file-outgoing");
|
||||
migration_channel_connect(s, ioc, NULL, NULL);
|
||||
}
|
||||
|
||||
static gboolean file_accept_incoming_migration(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
gpointer opaque)
|
||||
{
|
||||
migration_channel_process_incoming(ioc);
|
||||
object_unref(OBJECT(ioc));
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void file_start_incoming_migration(const char *filespec, Error **errp)
|
||||
{
|
||||
g_autofree char *filename = g_strdup(filespec);
|
||||
QIOChannelFile *fioc = NULL;
|
||||
uint64_t offset = 0;
|
||||
QIOChannel *ioc;
|
||||
|
||||
trace_migration_file_incoming(filename);
|
||||
|
||||
if (file_parse_offset(filename, &offset, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp);
|
||||
if (!fioc) {
|
||||
return;
|
||||
}
|
||||
|
||||
ioc = QIO_CHANNEL(fioc);
|
||||
if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
qio_channel_set_name(QIO_CHANNEL(ioc), "migration-file-incoming");
|
||||
qio_channel_add_watch_full(ioc, G_IO_IN,
|
||||
file_accept_incoming_migration,
|
||||
NULL, NULL,
|
||||
g_main_context_get_thread_default());
|
||||
}
|
14
migration/file.h
Normal file
14
migration/file.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023 Oracle and/or its affiliates.
|
||||
*
|
||||
* 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_FILE_H
|
||||
#define QEMU_MIGRATION_FILE_H
|
||||
void file_start_incoming_migration(const char *filename, Error **errp);
|
||||
|
||||
void file_start_outgoing_migration(MigrationState *s, const char *filename,
|
||||
Error **errp);
|
||||
#endif
|
@@ -16,6 +16,7 @@ system_ss.add(files(
|
||||
'dirtyrate.c',
|
||||
'exec.c',
|
||||
'fd.c',
|
||||
'file.c',
|
||||
'global_state.c',
|
||||
'migration-hmp-cmds.c',
|
||||
'migration.c',
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "migration/blocker.h"
|
||||
#include "exec.h"
|
||||
#include "fd.h"
|
||||
#include "file.h"
|
||||
#include "socket.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
@@ -424,13 +425,16 @@ void migrate_add_address(SocketAddress *address)
|
||||
static void qemu_start_incoming_migration(const char *uri, Error **errp)
|
||||
{
|
||||
const char *p = NULL;
|
||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||
|
||||
/* URI is not suitable for migration? */
|
||||
if (!migration_channels_and_uri_compatible(uri, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
qapi_event_send_migration(MIGRATION_STATUS_SETUP);
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
||||
MIGRATION_STATUS_SETUP);
|
||||
|
||||
if (strstart(uri, "tcp:", &p) ||
|
||||
strstart(uri, "unix:", NULL) ||
|
||||
strstart(uri, "vsock:", NULL)) {
|
||||
@@ -443,6 +447,8 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp)
|
||||
exec_start_incoming_migration(p, errp);
|
||||
} else if (strstart(uri, "fd:", &p)) {
|
||||
fd_start_incoming_migration(p, errp);
|
||||
} else if (strstart(uri, "file:", &p)) {
|
||||
file_start_incoming_migration(p, errp);
|
||||
} else {
|
||||
error_setg(errp, "unknown migration protocol: %s", uri);
|
||||
}
|
||||
@@ -522,7 +528,7 @@ process_incoming_migration_co(void *opaque)
|
||||
|
||||
mis->largest_page_size = qemu_ram_pagesize_largest();
|
||||
postcopy_state_set(POSTCOPY_INCOMING_NONE);
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
||||
migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP,
|
||||
MIGRATION_STATUS_ACTIVE);
|
||||
|
||||
mis->loadvm_co = qemu_coroutine_self();
|
||||
@@ -1670,6 +1676,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
|
||||
exec_start_outgoing_migration(s, p, &local_err);
|
||||
} else if (strstart(uri, "fd:", &p)) {
|
||||
fd_start_outgoing_migration(s, p, &local_err);
|
||||
} else if (strstart(uri, "file:", &p)) {
|
||||
file_start_outgoing_migration(s, p, &local_err);
|
||||
} else {
|
||||
if (!(has_resume && resume)) {
|
||||
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
|
||||
@@ -2951,7 +2959,7 @@ static void *migration_thread(void *opaque)
|
||||
MigThrError thr_error;
|
||||
bool urgent = false;
|
||||
|
||||
thread = MigrationThreadAdd("live_migration", qemu_get_thread_id());
|
||||
thread = migration_threads_add("live_migration", qemu_get_thread_id());
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
@@ -3029,7 +3037,7 @@ static void *migration_thread(void *opaque)
|
||||
migration_iteration_finish(s);
|
||||
object_unref(OBJECT(s));
|
||||
rcu_unregister_thread();
|
||||
MigrationThreadDel(thread);
|
||||
migration_threads_remove(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@@ -651,7 +651,7 @@ static void *multifd_send_thread(void *opaque)
|
||||
int ret = 0;
|
||||
bool use_zero_copy_send = migrate_zero_copy_send();
|
||||
|
||||
thread = MigrationThreadAdd(p->name, qemu_get_thread_id());
|
||||
thread = migration_threads_add(p->name, qemu_get_thread_id());
|
||||
|
||||
trace_multifd_send_thread_start(p->id);
|
||||
rcu_register_thread();
|
||||
@@ -767,7 +767,7 @@ out:
|
||||
qemu_mutex_unlock(&p->mutex);
|
||||
|
||||
rcu_unregister_thread();
|
||||
MigrationThreadDel(thread);
|
||||
migration_threads_remove(thread);
|
||||
trace_multifd_send_thread_end(p->id, p->num_packets, p->total_normal_pages);
|
||||
|
||||
return NULL;
|
||||
|
@@ -10,23 +10,35 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "threadinfo.h"
|
||||
|
||||
QemuMutex migration_threads_lock;
|
||||
static QLIST_HEAD(, MigrationThread) migration_threads;
|
||||
|
||||
MigrationThread *MigrationThreadAdd(const char *name, int thread_id)
|
||||
static void __attribute__((constructor)) migration_threads_init(void)
|
||||
{
|
||||
qemu_mutex_init(&migration_threads_lock);
|
||||
}
|
||||
|
||||
MigrationThread *migration_threads_add(const char *name, int thread_id)
|
||||
{
|
||||
MigrationThread *thread = g_new0(MigrationThread, 1);
|
||||
thread->name = name;
|
||||
thread->thread_id = thread_id;
|
||||
|
||||
QLIST_INSERT_HEAD(&migration_threads, thread, node);
|
||||
WITH_QEMU_LOCK_GUARD(&migration_threads_lock) {
|
||||
QLIST_INSERT_HEAD(&migration_threads, thread, node);
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
void MigrationThreadDel(MigrationThread *thread)
|
||||
void migration_threads_remove(MigrationThread *thread)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&migration_threads_lock);
|
||||
if (thread) {
|
||||
QLIST_REMOVE(thread, node);
|
||||
g_free(thread);
|
||||
@@ -39,6 +51,7 @@ MigrationThreadInfoList *qmp_query_migrationthreads(Error **errp)
|
||||
MigrationThreadInfoList **tail = &head;
|
||||
MigrationThread *thread = NULL;
|
||||
|
||||
QEMU_LOCK_GUARD(&migration_threads_lock);
|
||||
QLIST_FOREACH(thread, &migration_threads, node) {
|
||||
MigrationThreadInfo *info = g_new0(MigrationThreadInfo, 1);
|
||||
info->name = g_strdup(thread->name);
|
||||
|
@@ -10,8 +10,6 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-migration.h"
|
||||
|
||||
@@ -23,6 +21,5 @@ struct MigrationThread {
|
||||
QLIST_ENTRY(MigrationThread) node;
|
||||
};
|
||||
|
||||
MigrationThread *MigrationThreadAdd(const char *name, int thread_id);
|
||||
|
||||
void MigrationThreadDel(MigrationThread *info);
|
||||
MigrationThread *migration_threads_add(const char *name, int thread_id);
|
||||
void migration_threads_remove(MigrationThread *info);
|
||||
|
@@ -310,6 +310,10 @@ migration_exec_incoming(const char *cmd) "cmd=%s"
|
||||
migration_fd_outgoing(int fd) "fd=%d"
|
||||
migration_fd_incoming(int fd) "fd=%d"
|
||||
|
||||
# file.c
|
||||
migration_file_outgoing(const char *filename) "filename=%s"
|
||||
migration_file_incoming(const char *filename) "filename=%s"
|
||||
|
||||
# socket.c
|
||||
migration_socket_incoming_accepted(void) ""
|
||||
migration_socket_outgoing_connected(const char *hostname) "hostname=%s"
|
||||
|
@@ -4622,6 +4622,7 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
|
||||
" prepare for incoming migration, listen on\n" \
|
||||
" specified protocol and socket address\n" \
|
||||
"-incoming fd:fd\n" \
|
||||
"-incoming file:filename[,offset=offset]\n" \
|
||||
"-incoming exec:cmdline\n" \
|
||||
" accept incoming migration on given file descriptor\n" \
|
||||
" or from given external command\n" \
|
||||
@@ -4638,7 +4639,11 @@ SRST
|
||||
Prepare for incoming migration, listen on a given unix socket.
|
||||
|
||||
``-incoming fd:fd``
|
||||
Accept incoming migration from a given filedescriptor.
|
||||
Accept incoming migration from a given file descriptor.
|
||||
|
||||
``-incoming file:filename[,offset=offset]``
|
||||
Accept incoming migration from a given file starting at offset.
|
||||
offset allows the common size suffixes, or a 0x prefix, but not both.
|
||||
|
||||
``-incoming exec:cmdline``
|
||||
Accept incoming migration as an output from specified external
|
||||
|
@@ -1248,6 +1248,28 @@ void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
|
||||
qtest_rsp(s);
|
||||
}
|
||||
|
||||
QDict *qtest_vqmp_assert_failure_ref(QTestState *qts,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
QDict *response;
|
||||
QDict *ret;
|
||||
|
||||
response = qtest_vqmp(qts, fmt, args);
|
||||
|
||||
g_assert(response);
|
||||
if (!qdict_haskey(response, "error")) {
|
||||
g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true);
|
||||
g_test_message("%s", s->str);
|
||||
}
|
||||
g_assert(qdict_haskey(response, "error"));
|
||||
g_assert(!qdict_haskey(response, "return"));
|
||||
ret = qdict_get_qdict(response, "error");
|
||||
qobject_ref(ret);
|
||||
qobject_unref(response);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QDict *qtest_vqmp_assert_success_ref(QTestState *qts,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
@@ -1310,6 +1332,17 @@ void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds,
|
||||
}
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
QDict *qtest_qmp_assert_failure_ref(QTestState *qts, const char *fmt, ...)
|
||||
{
|
||||
QDict *response;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
response = qtest_vqmp_assert_failure_ref(qts, fmt, ap);
|
||||
va_end(ap);
|
||||
return response;
|
||||
}
|
||||
|
||||
QDict *qtest_qmp_assert_success_ref(QTestState *qts, const char *fmt, ...)
|
||||
{
|
||||
QDict *response;
|
||||
|
@@ -799,6 +799,34 @@ void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds,
|
||||
G_GNUC_PRINTF(4, 0);
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
/**
|
||||
* qtest_qmp_assert_failure_ref:
|
||||
* @qts: QTestState instance to operate on
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Sends a QMP message to QEMU, asserts that an 'error' key is present in
|
||||
* the response, and returns the response.
|
||||
*/
|
||||
QDict *qtest_qmp_assert_failure_ref(QTestState *qts, const char *fmt, ...)
|
||||
G_GNUC_PRINTF(2, 3);
|
||||
|
||||
/**
|
||||
* qtest_vqmp_assert_failure_ref:
|
||||
* @qts: QTestState instance to operate on
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
* @args: variable arguments for @fmt
|
||||
*
|
||||
* Sends a QMP message to QEMU, asserts that an 'error' key is present in
|
||||
* the response, and returns the response.
|
||||
*/
|
||||
QDict *qtest_vqmp_assert_failure_ref(QTestState *qts,
|
||||
const char *fmt, va_list args)
|
||||
G_GNUC_PRINTF(2, 0);
|
||||
|
||||
/**
|
||||
* qtest_qmp_assert_success_ref:
|
||||
* @qts: QTestState instance to operate on
|
||||
|
@@ -313,6 +313,7 @@ qtests = {
|
||||
'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'],
|
||||
'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
|
||||
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
|
||||
'virtio-net-failover': files('migration-helpers.c'),
|
||||
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
|
||||
'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'),
|
||||
}
|
||||
|
@@ -49,6 +49,26 @@ bool migrate_watch_for_resume(QTestState *who, const char *name,
|
||||
return false;
|
||||
}
|
||||
|
||||
void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
QDict *args, *err;
|
||||
|
||||
va_start(ap, fmt);
|
||||
args = qdict_from_vjsonf_nofail(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
g_assert(!qdict_haskey(args, "uri"));
|
||||
qdict_put_str(args, "uri", uri);
|
||||
|
||||
err = qtest_qmp_assert_failure_ref(
|
||||
who, "{ 'execute': 'migrate', 'arguments': %p}", args);
|
||||
|
||||
g_assert(qdict_haskey(err, "desc"));
|
||||
|
||||
qobject_unref(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send QMP command "migrate".
|
||||
* Arguments are built from @fmt... (formatted like
|
||||
@@ -70,6 +90,46 @@ void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
|
||||
"{ 'execute': 'migrate', 'arguments': %p}", args);
|
||||
}
|
||||
|
||||
void migrate_set_capability(QTestState *who, const char *capability,
|
||||
bool value)
|
||||
{
|
||||
qtest_qmp_assert_success(who,
|
||||
"{ 'execute': 'migrate-set-capabilities',"
|
||||
"'arguments': { "
|
||||
"'capabilities': [ { "
|
||||
"'capability': %s, 'state': %i } ] } }",
|
||||
capability, value);
|
||||
}
|
||||
|
||||
void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
QDict *args, *rsp, *data;
|
||||
|
||||
va_start(ap, fmt);
|
||||
args = qdict_from_vjsonf_nofail(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
g_assert(!qdict_haskey(args, "uri"));
|
||||
qdict_put_str(args, "uri", uri);
|
||||
|
||||
migrate_set_capability(to, "events", true);
|
||||
|
||||
rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
|
||||
args);
|
||||
g_assert(qdict_haskey(rsp, "return"));
|
||||
qobject_unref(rsp);
|
||||
|
||||
rsp = qtest_qmp_eventwait_ref(to, "MIGRATION");
|
||||
g_assert(qdict_haskey(rsp, "data"));
|
||||
|
||||
data = qdict_get_qdict(rsp, "data");
|
||||
g_assert(qdict_haskey(data, "status"));
|
||||
g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup");
|
||||
|
||||
qobject_unref(rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: caller is responsible to free the returned object via
|
||||
* qobject_unref() after use
|
||||
|
@@ -23,6 +23,16 @@ bool migrate_watch_for_resume(QTestState *who, const char *name,
|
||||
G_GNUC_PRINTF(3, 4)
|
||||
void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...);
|
||||
|
||||
G_GNUC_PRINTF(3, 4)
|
||||
void migrate_incoming_qmp(QTestState *who, const char *uri,
|
||||
const char *fmt, ...);
|
||||
|
||||
G_GNUC_PRINTF(3, 4)
|
||||
void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...);
|
||||
|
||||
void migrate_set_capability(QTestState *who, const char *capability,
|
||||
bool value);
|
||||
|
||||
QDict *migrate_query(QTestState *who);
|
||||
QDict *migrate_query_not_failed(QTestState *who);
|
||||
|
||||
|
@@ -52,6 +52,10 @@ static bool got_dst_resume;
|
||||
*/
|
||||
#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */
|
||||
|
||||
#define QEMU_VM_FILE_MAGIC 0x5145564d
|
||||
#define FILE_TEST_FILENAME "migfile"
|
||||
#define FILE_TEST_OFFSET 0x1000
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/vfs.h>
|
||||
@@ -472,17 +476,6 @@ static void migrate_cancel(QTestState *who)
|
||||
qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
|
||||
}
|
||||
|
||||
static void migrate_set_capability(QTestState *who, const char *capability,
|
||||
bool value)
|
||||
{
|
||||
qtest_qmp_assert_success(who,
|
||||
"{ 'execute': 'migrate-set-capabilities',"
|
||||
"'arguments': { "
|
||||
"'capabilities': [ { "
|
||||
"'capability': %s, 'state': %i } ] } }",
|
||||
capability, value);
|
||||
}
|
||||
|
||||
static void migrate_postcopy_start(QTestState *from, QTestState *to)
|
||||
{
|
||||
qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }");
|
||||
@@ -575,6 +568,8 @@ typedef struct {
|
||||
MIG_TEST_FAIL,
|
||||
/* This test should fail, dest qemu should fail with abnormal status */
|
||||
MIG_TEST_FAIL_DEST_QUIT_ERR,
|
||||
/* The QMP command for this migration should fail with an error */
|
||||
MIG_TEST_QMP_ERROR,
|
||||
} result;
|
||||
|
||||
/* Optional: set number of migration passes to wait for, if live==true */
|
||||
@@ -771,6 +766,7 @@ static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
|
||||
cleanup("migsocket");
|
||||
cleanup("src_serial");
|
||||
cleanup("dest_serial");
|
||||
cleanup(FILE_TEST_FILENAME);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
@@ -1390,6 +1386,7 @@ static void test_precopy_common(MigrateCommon *args)
|
||||
{
|
||||
QTestState *from, *to;
|
||||
void *data_hook = NULL;
|
||||
g_autofree char *connect_uri = NULL;
|
||||
|
||||
if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) {
|
||||
return;
|
||||
@@ -1430,13 +1427,17 @@ static void test_precopy_common(MigrateCommon *args)
|
||||
}
|
||||
|
||||
if (!args->connect_uri) {
|
||||
g_autofree char *local_connect_uri =
|
||||
migrate_get_socket_address(to, "socket-address");
|
||||
migrate_qmp(from, local_connect_uri, "{}");
|
||||
connect_uri = migrate_get_socket_address(to, "socket-address");
|
||||
} else {
|
||||
migrate_qmp(from, args->connect_uri, "{}");
|
||||
connect_uri = g_strdup(args->connect_uri);
|
||||
}
|
||||
|
||||
if (args->result == MIG_TEST_QMP_ERROR) {
|
||||
migrate_qmp_fail(from, connect_uri, "{}");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
migrate_qmp(from, connect_uri, "{}");
|
||||
|
||||
if (args->result != MIG_TEST_SUCCEED) {
|
||||
bool allow_active = args->result == MIG_TEST_FAIL;
|
||||
@@ -1463,11 +1464,28 @@ static void test_precopy_common(MigrateCommon *args)
|
||||
*/
|
||||
wait_for_migration_complete(from);
|
||||
|
||||
/*
|
||||
* For file based migration the target must begin its
|
||||
* migration after the source has finished.
|
||||
*/
|
||||
if (strstr(connect_uri, "file:")) {
|
||||
migrate_incoming_qmp(to, connect_uri, "{}");
|
||||
}
|
||||
|
||||
if (!got_src_stop) {
|
||||
qtest_qmp_eventwait(from, "STOP");
|
||||
}
|
||||
} else {
|
||||
wait_for_migration_complete(from);
|
||||
|
||||
/*
|
||||
* For file based migration the target must begin its
|
||||
* migration after the source has finished.
|
||||
*/
|
||||
if (strstr(connect_uri, "file:")) {
|
||||
migrate_incoming_qmp(to, connect_uri, "{}");
|
||||
}
|
||||
|
||||
/*
|
||||
* Must wait for dst to finish reading all incoming
|
||||
* data on the socket before issuing 'cont' otherwise
|
||||
@@ -1485,6 +1503,7 @@ static void test_precopy_common(MigrateCommon *args)
|
||||
wait_for_serial("dest_serial");
|
||||
}
|
||||
|
||||
finish:
|
||||
if (args->finish_hook) {
|
||||
args->finish_hook(from, to, data_hook);
|
||||
}
|
||||
@@ -1684,6 +1703,75 @@ static void test_precopy_unix_compress_nowait(void)
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_precopy_file(void)
|
||||
{
|
||||
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
|
||||
FILE_TEST_FILENAME);
|
||||
MigrateCommon args = {
|
||||
.connect_uri = uri,
|
||||
.listen_uri = "defer",
|
||||
};
|
||||
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void file_offset_finish_hook(QTestState *from, QTestState *to, void *opaque)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
|
||||
size_t size = FILE_TEST_OFFSET + sizeof(QEMU_VM_FILE_MAGIC);
|
||||
uintptr_t *addr, *p;
|
||||
int fd;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
g_assert(fd != -1);
|
||||
addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
g_assert(addr != MAP_FAILED);
|
||||
|
||||
/*
|
||||
* Ensure the skipped offset contains zeros and the migration
|
||||
* stream starts at the right place.
|
||||
*/
|
||||
p = addr;
|
||||
while (p < addr + FILE_TEST_OFFSET / sizeof(uintptr_t)) {
|
||||
g_assert(*p == 0);
|
||||
p++;
|
||||
}
|
||||
g_assert_cmpint(cpu_to_be32(*p), ==, QEMU_VM_FILE_MAGIC);
|
||||
|
||||
munmap(addr, size);
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_precopy_file_offset(void)
|
||||
{
|
||||
g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs,
|
||||
FILE_TEST_FILENAME,
|
||||
FILE_TEST_OFFSET);
|
||||
MigrateCommon args = {
|
||||
.connect_uri = uri,
|
||||
.listen_uri = "defer",
|
||||
.finish_hook = file_offset_finish_hook,
|
||||
};
|
||||
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_precopy_file_offset_bad(void)
|
||||
{
|
||||
/* using a value not supported by qemu_strtosz() */
|
||||
g_autofree char *uri = g_strdup_printf("file:%s/migfile,offset=0x20M",
|
||||
tmpfs);
|
||||
MigrateCommon args = {
|
||||
.connect_uri = uri,
|
||||
.listen_uri = "defer",
|
||||
.result = MIG_TEST_QMP_ERROR,
|
||||
};
|
||||
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_precopy_tcp_plain(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
@@ -1857,8 +1945,7 @@ static void *test_migrate_fd_start_hook(QTestState *from,
|
||||
close(pair[0]);
|
||||
|
||||
/* Start incoming migration from the 1st socket */
|
||||
qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
|
||||
" 'arguments': { 'uri': 'fd:fd-mig' }}");
|
||||
migrate_incoming_qmp(to, "fd:fd-mig", "{}");
|
||||
|
||||
/* Send the 2nd socket to the target */
|
||||
qtest_qmp_fds_assert_success(from, &pair[1], 1,
|
||||
@@ -2080,8 +2167,7 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from,
|
||||
migrate_set_capability(to, "multifd", true);
|
||||
|
||||
/* Start incoming migration from the 1st socket */
|
||||
qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
|
||||
" 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}");
|
||||
migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2333,8 +2419,7 @@ static void test_multifd_tcp_cancel(void)
|
||||
migrate_set_capability(to, "multifd", true);
|
||||
|
||||
/* Start incoming migration from the 1st socket */
|
||||
qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
|
||||
" 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}");
|
||||
migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}");
|
||||
|
||||
/* Wait for the first serial output from the source */
|
||||
wait_for_serial("src_serial");
|
||||
@@ -2364,8 +2449,7 @@ static void test_multifd_tcp_cancel(void)
|
||||
migrate_set_capability(to2, "multifd", true);
|
||||
|
||||
/* Start incoming migration from the 1st socket */
|
||||
qtest_qmp_assert_success(to2, "{ 'execute': 'migrate-incoming',"
|
||||
" 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}");
|
||||
migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}");
|
||||
|
||||
g_free(uri);
|
||||
uri = migrate_get_socket_address(to2, "socket-address");
|
||||
@@ -2737,6 +2821,14 @@ int main(int argc, char **argv)
|
||||
qtest_add_func("/migration/precopy/unix/compress/nowait",
|
||||
test_precopy_unix_compress_nowait);
|
||||
}
|
||||
|
||||
qtest_add_func("/migration/precopy/file",
|
||||
test_precopy_file);
|
||||
qtest_add_func("/migration/precopy/file/offset",
|
||||
test_precopy_file_offset);
|
||||
qtest_add_func("/migration/precopy/file/offset/bad",
|
||||
test_precopy_file_offset_bad);
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
qtest_add_func("/migration/precopy/unix/tls/psk",
|
||||
test_precopy_unix_tls_psk);
|
||||
@@ -2809,14 +2901,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
qtest_add_func("/migration/multifd/tcp/plain/none",
|
||||
test_multifd_tcp_none);
|
||||
/*
|
||||
* This test is flaky and sometimes fails in CI and otherwise:
|
||||
* don't run unless user opts in via environment variable.
|
||||
*/
|
||||
if (getenv("QEMU_TEST_FLAKY_TESTS")) {
|
||||
qtest_add_func("/migration/multifd/tcp/plain/cancel",
|
||||
test_multifd_tcp_cancel);
|
||||
}
|
||||
qtest_add_func("/migration/multifd/tcp/plain/cancel",
|
||||
test_multifd_tcp_cancel);
|
||||
qtest_add_func("/migration/multifd/tcp/plain/zlib",
|
||||
test_multifd_tcp_zlib);
|
||||
#ifdef CONFIG_ZSTD
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "libqtest.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/pci-pc.h"
|
||||
#include "migration-helpers.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
@@ -736,26 +737,10 @@ static void test_migrate_out(gconstpointer opaque)
|
||||
machine_stop(qts);
|
||||
}
|
||||
|
||||
static QDict *get_migration_event(QTestState *qts)
|
||||
{
|
||||
QDict *resp;
|
||||
QDict *data;
|
||||
|
||||
resp = qtest_qmp_eventwait_ref(qts, "MIGRATION");
|
||||
g_assert(qdict_haskey(resp, "data"));
|
||||
|
||||
data = qdict_get_qdict(resp, "data");
|
||||
g_assert(qdict_haskey(data, "status"));
|
||||
qobject_ref(data);
|
||||
qobject_unref(resp);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void test_migrate_in(gconstpointer opaque)
|
||||
{
|
||||
QTestState *qts;
|
||||
QDict *resp, *args, *ret;
|
||||
QDict *resp, *ret;
|
||||
g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
|
||||
|
||||
qts = machine_start(BASE_MACHINE
|
||||
@@ -787,18 +772,7 @@ static void test_migrate_in(gconstpointer opaque)
|
||||
check_one_card(qts, true, "standby0", MAC_STANDBY0);
|
||||
check_one_card(qts, false, "primary0", MAC_PRIMARY0);
|
||||
|
||||
args = qdict_from_jsonf_nofail("{}");
|
||||
g_assert_nonnull(args);
|
||||
qdict_put_str(args, "uri", uri);
|
||||
|
||||
resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
|
||||
args);
|
||||
g_assert(qdict_haskey(resp, "return"));
|
||||
qobject_unref(resp);
|
||||
|
||||
resp = get_migration_event(qts);
|
||||
g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
|
||||
qobject_unref(resp);
|
||||
migrate_incoming_qmp(qts, uri, "{}");
|
||||
|
||||
resp = get_failover_negociated_event(qts);
|
||||
g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
|
||||
@@ -888,7 +862,7 @@ static void test_off_migrate_out(gconstpointer opaque)
|
||||
static void test_off_migrate_in(gconstpointer opaque)
|
||||
{
|
||||
QTestState *qts;
|
||||
QDict *resp, *args, *ret;
|
||||
QDict *ret;
|
||||
g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
|
||||
|
||||
qts = machine_start(BASE_MACHINE
|
||||
@@ -920,18 +894,7 @@ static void test_off_migrate_in(gconstpointer opaque)
|
||||
check_one_card(qts, true, "standby0", MAC_STANDBY0);
|
||||
check_one_card(qts, true, "primary0", MAC_PRIMARY0);
|
||||
|
||||
args = qdict_from_jsonf_nofail("{}");
|
||||
g_assert_nonnull(args);
|
||||
qdict_put_str(args, "uri", uri);
|
||||
|
||||
resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
|
||||
args);
|
||||
g_assert(qdict_haskey(resp, "return"));
|
||||
qobject_unref(resp);
|
||||
|
||||
resp = get_migration_event(qts);
|
||||
g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
|
||||
qobject_unref(resp);
|
||||
migrate_incoming_qmp(qts, uri, "{}");
|
||||
|
||||
check_one_card(qts, true, "standby0", MAC_STANDBY0);
|
||||
check_one_card(qts, true, "primary0", MAC_PRIMARY0);
|
||||
@@ -1026,7 +989,7 @@ static void test_guest_off_migrate_out(gconstpointer opaque)
|
||||
static void test_guest_off_migrate_in(gconstpointer opaque)
|
||||
{
|
||||
QTestState *qts;
|
||||
QDict *resp, *args, *ret;
|
||||
QDict *ret;
|
||||
g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
|
||||
|
||||
qts = machine_start(BASE_MACHINE
|
||||
@@ -1058,18 +1021,7 @@ static void test_guest_off_migrate_in(gconstpointer opaque)
|
||||
check_one_card(qts, true, "standby0", MAC_STANDBY0);
|
||||
check_one_card(qts, false, "primary0", MAC_PRIMARY0);
|
||||
|
||||
args = qdict_from_jsonf_nofail("{}");
|
||||
g_assert_nonnull(args);
|
||||
qdict_put_str(args, "uri", uri);
|
||||
|
||||
resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
|
||||
args);
|
||||
g_assert(qdict_haskey(resp, "return"));
|
||||
qobject_unref(resp);
|
||||
|
||||
resp = get_migration_event(qts);
|
||||
g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
|
||||
qobject_unref(resp);
|
||||
migrate_incoming_qmp(qts, uri, "{}");
|
||||
|
||||
check_one_card(qts, true, "standby0", MAC_STANDBY0);
|
||||
check_one_card(qts, false, "primary0", MAC_PRIMARY0);
|
||||
@@ -1728,7 +1680,7 @@ static void test_multi_out(gconstpointer opaque)
|
||||
static void test_multi_in(gconstpointer opaque)
|
||||
{
|
||||
QTestState *qts;
|
||||
QDict *resp, *args, *ret;
|
||||
QDict *resp, *ret;
|
||||
g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
|
||||
|
||||
qts = machine_start(BASE_MACHINE
|
||||
@@ -1794,18 +1746,7 @@ static void test_multi_in(gconstpointer opaque)
|
||||
check_one_card(qts, true, "standby1", MAC_STANDBY1);
|
||||
check_one_card(qts, false, "primary1", MAC_PRIMARY1);
|
||||
|
||||
args = qdict_from_jsonf_nofail("{}");
|
||||
g_assert_nonnull(args);
|
||||
qdict_put_str(args, "uri", uri);
|
||||
|
||||
resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
|
||||
args);
|
||||
g_assert(qdict_haskey(resp, "return"));
|
||||
qobject_unref(resp);
|
||||
|
||||
resp = get_migration_event(qts);
|
||||
g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
|
||||
qobject_unref(resp);
|
||||
migrate_incoming_qmp(qts, uri, "{}");
|
||||
|
||||
resp = get_failover_negociated_event(qts);
|
||||
g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
|
||||
|
Reference in New Issue
Block a user