Migration pull request

- Proper TLS termination for multifd
 - Fixes for bugs while cancelling migration
 - Fix for a hang after migration failure (#2633)
 - Tests for qmp_migrate_cancel
 - Fix for CID 1590980
 - Fixes and improvements to guestperf.py
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCAAuFiEEqhtIsKIjJqWkw2TPx5jcdBvsMZ0FAmevp3oQHGZhcm9zYXNA
 c3VzZS5kZQAKCRDHmNx0G+wxnSgGD/9z2ATsf073wDupwJ7tJIxgZ6D8Dlb7yPZ6
 azRgC7TMv1VGE0cx4r1IiNopFDUodrVO3yXA9D7GVvfkgSr9Oa4oUniQwItM9PT4
 QymGPKIE0nuwPCvCrlKXXGruLMngTeb0kpJeseJ9vEXJlQxLvYCtcELF6j2tzVmx
 nisMgMZiyBwYfS0b7ZctXHqY0+NmNuke/giX6ceUAaj4wqpgFI3zo9OGCHYinYsR
 oMoMLusyUnDBCqV2P3jGGz4W2KmkCxStnH0yRdUN9mwt0KLl82t6e0aCJqkWo6+W
 m68DlZgUFwbz4Aq5M2RDPhXvXgim8Ryi29zRuedx8ngYS9Qz6D5y4Fgp4uv/N7ia
 v8bB6QPZMOkhPq2gkCxPEy47l4tDZhrWRqqEqw4h1nO01KCJ2+y2IZCOBmPFXywT
 B58f7KvmnLLiYbfWxjnQmOXs9PKRsQjJk96BmRCbf03WeNTF+FHuvQZu9h4Bwb2/
 im0kJSq2zR8eSamH2L2dyYhQZ4JqMJa7I3JXqJbAjhk1ya6kX5v899EcktTPDVSG
 xGINVshpfwwFovRqhgYL9fjqrO8DMNZCbS6IEGLuR5lx90Wo5a8XbKX71JmsnZUO
 jnGJ+1InTZcbUvp0tkQzXWwUKx8MCP/OWTb098D8oUmfEumYozzsAW5X9kw+4hVJ
 rpYvw5IYfA==
 =cBl0
 -----END PGP SIGNATURE-----

Merge tag 'migration-20250214-pull-request' of https://gitlab.com/farosas/qemu into staging

Migration pull request

- Proper TLS termination for multifd
- Fixes for bugs while cancelling migration
- Fix for a hang after migration failure (#2633)
- Tests for qmp_migrate_cancel
- Fix for CID 1590980
- Fixes and improvements to guestperf.py

# -----BEGIN PGP SIGNATURE-----
#
# iQJEBAABCAAuFiEEqhtIsKIjJqWkw2TPx5jcdBvsMZ0FAmevp3oQHGZhcm9zYXNA
# c3VzZS5kZQAKCRDHmNx0G+wxnSgGD/9z2ATsf073wDupwJ7tJIxgZ6D8Dlb7yPZ6
# azRgC7TMv1VGE0cx4r1IiNopFDUodrVO3yXA9D7GVvfkgSr9Oa4oUniQwItM9PT4
# QymGPKIE0nuwPCvCrlKXXGruLMngTeb0kpJeseJ9vEXJlQxLvYCtcELF6j2tzVmx
# nisMgMZiyBwYfS0b7ZctXHqY0+NmNuke/giX6ceUAaj4wqpgFI3zo9OGCHYinYsR
# oMoMLusyUnDBCqV2P3jGGz4W2KmkCxStnH0yRdUN9mwt0KLl82t6e0aCJqkWo6+W
# m68DlZgUFwbz4Aq5M2RDPhXvXgim8Ryi29zRuedx8ngYS9Qz6D5y4Fgp4uv/N7ia
# v8bB6QPZMOkhPq2gkCxPEy47l4tDZhrWRqqEqw4h1nO01KCJ2+y2IZCOBmPFXywT
# B58f7KvmnLLiYbfWxjnQmOXs9PKRsQjJk96BmRCbf03WeNTF+FHuvQZu9h4Bwb2/
# im0kJSq2zR8eSamH2L2dyYhQZ4JqMJa7I3JXqJbAjhk1ya6kX5v899EcktTPDVSG
# xGINVshpfwwFovRqhgYL9fjqrO8DMNZCbS6IEGLuR5lx90Wo5a8XbKX71JmsnZUO
# jnGJ+1InTZcbUvp0tkQzXWwUKx8MCP/OWTb098D8oUmfEumYozzsAW5X9kw+4hVJ
# rpYvw5IYfA==
# =cBl0
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 14 Feb 2025 15:28:42 EST
# gpg:                using RSA key AA1B48B0A22326A5A4C364CFC798DC741BEC319D
# gpg:                issuer "farosas@suse.de"
# gpg: Good signature from "Fabiano Rosas <farosas@suse.de>" [unknown]
# gpg:                 aka "Fabiano Almeida Rosas <fabiano.rosas@suse.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: AA1B 48B0 A223 26A5 A4C3  64CF C798 DC74 1BEC 319D

* tag 'migration-20250214-pull-request' of https://gitlab.com/farosas/qemu: (22 commits)
  guestperf: Add test result data into report
  guestperf: Introduce multifd compression option
  guestperf: Nitpick the inconsistent parameters
  guestperf: Support deferred migration for multifd
  migration: use parameters.mode in cpr_state_save
  migration: Update migrate_cancel documentation
  tests/qtest/migration: Add a cancel test
  tests/qtest/migration: Introduce migration_test_add_suffix
  migration: Don't set FAILED state when cancelling
  migration: Reject qmp_migrate_cancel after postcopy
  migration: Fix hang after error in destination setup phase
  migration: Change migrate_fd_ to migration_
  migration: Unify migration_cancel and migrate_fd_cancel
  migration: Set migration error outside of migrate_cancel
  migration: Check migration error after loadvm
  migration/multifd: Add a compat property for TLS termination
  migration/multifd: Terminate the TLS connection
  io: Add a read flag for relaxed EOF
  io: Add flags argument to qio_channel_readv_full_all_eof
  crypto: Remove qcrypto_tls_session_get_handshake_status
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-02-16 20:47:52 -05:00
commit 9af3d9a931
32 changed files with 646 additions and 125 deletions

View File

@ -546,45 +546,69 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session,
Error **errp)
{
int ret = gnutls_handshake(session->handle);
if (ret == 0) {
if (!ret) {
session->handshakeComplete = true;
} else {
if (ret == GNUTLS_E_INTERRUPTED ||
ret == GNUTLS_E_AGAIN) {
ret = 1;
} else {
if (session->rerr || session->werr) {
error_setg(errp, "TLS handshake failed: %s: %s",
gnutls_strerror(ret),
error_get_pretty(session->rerr ?
session->rerr : session->werr));
} else {
error_setg(errp, "TLS handshake failed: %s",
gnutls_strerror(ret));
}
ret = -1;
}
return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
}
if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
int direction = gnutls_record_get_direction(session->handle);
return direction ? QCRYPTO_TLS_HANDSHAKE_SENDING :
QCRYPTO_TLS_HANDSHAKE_RECVING;
}
if (session->rerr || session->werr) {
error_setg(errp, "TLS handshake failed: %s: %s",
gnutls_strerror(ret),
error_get_pretty(session->rerr ?
session->rerr : session->werr));
} else {
error_setg(errp, "TLS handshake failed: %s",
gnutls_strerror(ret));
}
error_free(session->rerr);
error_free(session->werr);
session->rerr = session->werr = NULL;
return ret;
return -1;
}
QCryptoTLSSessionHandshakeStatus
qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session)
int
qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp)
{
if (session->handshakeComplete) {
return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
} else if (gnutls_record_get_direction(session->handle) == 0) {
return QCRYPTO_TLS_HANDSHAKE_RECVING;
} else {
return QCRYPTO_TLS_HANDSHAKE_SENDING;
}
}
int ret;
if (!session->handshakeComplete) {
return 0;
}
ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR);
if (!ret) {
return QCRYPTO_TLS_BYE_COMPLETE;
}
if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
int direction = gnutls_record_get_direction(session->handle);
return direction ? QCRYPTO_TLS_BYE_SENDING : QCRYPTO_TLS_BYE_RECVING;
}
if (session->rerr || session->werr) {
error_setg(errp, "TLS termination failed: %s: %s", gnutls_strerror(ret),
error_get_pretty(session->rerr ?
session->rerr : session->werr));
} else {
error_setg(errp, "TLS termination failed: %s", gnutls_strerror(ret));
}
error_free(session->rerr);
error_free(session->werr);
session->rerr = session->werr = NULL;
return -1;
}
int
qcrypto_tls_session_get_key_size(QCryptoTLSSession *session,
@ -692,10 +716,10 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *sess,
}
QCryptoTLSSessionHandshakeStatus
qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess)
int
qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp)
{
return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
return QCRYPTO_TLS_BYE_COMPLETE;
}

View File

@ -42,6 +42,7 @@ GlobalProperty hw_compat_9_2[] = {
{ "virtio-balloon-pci-transitional", "vectors", "0" },
{ "virtio-balloon-pci-non-transitional", "vectors", "0" },
{ "virtio-mem-pci", "vectors", "0" },
{ "migration", "multifd-clean-tls-termination", "false" },
};
const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2);

View File

@ -110,7 +110,7 @@ static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds,
bql_unlock();
}
ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, errp);
ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, 0, errp);
if (drop_bql && !iothread && !qemu_in_coroutine()) {
bql_lock();

View File

@ -75,12 +75,14 @@
* GINT_TO_POINTER(fd));
*
* while (1) {
* if (qcrypto_tls_session_handshake(sess, errp) < 0) {
* int ret = qcrypto_tls_session_handshake(sess, errp);
*
* if (ret < 0) {
* qcrypto_tls_session_free(sess);
* return -1;
* }
*
* switch(qcrypto_tls_session_get_handshake_status(sess)) {
* switch(ret) {
* case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
* if (qcrypto_tls_session_check_credentials(sess, errp) < )) {
* qcrypto_tls_session_free(sess);
@ -170,7 +172,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free)
*
* Validate the peer's credentials after a successful
* TLS handshake. It is an error to call this before
* qcrypto_tls_session_get_handshake_status() returns
* qcrypto_tls_session_handshake() returns
* QCRYPTO_TLS_HANDSHAKE_COMPLETE
*
* Returns 0 if the credentials validated, -1 on error
@ -226,7 +228,7 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess,
* registered with qcrypto_tls_session_set_callbacks()
*
* It is an error to call this before
* qcrypto_tls_session_get_handshake_status() returns
* qcrypto_tls_session_handshake() returns
* QCRYPTO_TLS_HANDSHAKE_COMPLETE
*
* Returns: the number of bytes sent,
@ -256,7 +258,7 @@ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess,
* opposed to an error.
*
* It is an error to call this before
* qcrypto_tls_session_get_handshake_status() returns
* qcrypto_tls_session_handshake() returns
* QCRYPTO_TLS_HANDSHAKE_COMPLETE
*
* Returns: the number of bytes received,
@ -289,8 +291,7 @@ size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess);
* the underlying data channel is non-blocking, then
* this method may return control before the handshake
* is complete. On non-blocking channels the
* qcrypto_tls_session_get_handshake_status() method
* should be used to determine whether the handshake
* return value determines whether the handshake
* has completed, or is waiting to send or receive
* data. In the latter cases, the caller should setup
* an event loop watch and call this method again
@ -306,22 +307,27 @@ typedef enum {
QCRYPTO_TLS_HANDSHAKE_RECVING,
} QCryptoTLSSessionHandshakeStatus;
typedef enum {
QCRYPTO_TLS_BYE_COMPLETE,
QCRYPTO_TLS_BYE_SENDING,
QCRYPTO_TLS_BYE_RECVING,
} QCryptoTLSSessionByeStatus;
/**
* qcrypto_tls_session_get_handshake_status:
* @sess: the TLS session object
* qcrypto_tls_session_bye:
* @session: the TLS session object
* @errp: pointer to a NULL-initialized error object
*
* Check the status of the TLS handshake. This
* is used with non-blocking data channels to
* determine whether the handshake is waiting
* to send or receive further data to/from the
* remote peer.
*
* Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE
* it is permitted to send/receive payload data on
* the channel
* Start, or continue, a TLS termination sequence. If the underlying
* data channel is non-blocking, then this method may return control
* before the termination is complete. The return value will indicate
* whether the termination has completed, or is waiting to send or
* receive data. In the latter cases, the caller should setup an event
* loop watch and call this method again once the underlying data
* channel is ready to read or write again.
*/
QCryptoTLSSessionHandshakeStatus
qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess);
int
qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp);
/**
* qcrypto_tls_session_get_key_size:

View File

@ -49,8 +49,20 @@ struct QIOChannelTLS {
QCryptoTLSSession *session;
QIOChannelShutdown shutdown;
guint hs_ioc_tag;
guint bye_ioc_tag;
};
/**
* qio_channel_tls_bye:
* @ioc: the TLS channel object
* @errp: pointer to a NULL-initialized error object
*
* Perform the TLS session termination. This method will return
* immediately and the termination will continue in the background,
* provided the main loop is running.
*/
void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp);
/**
* qio_channel_tls_new_server:
* @master: the underlying channel object

View File

@ -35,6 +35,7 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass,
#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1
#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1
#define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2
typedef enum QIOChannelFeature QIOChannelFeature;
@ -885,6 +886,7 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
* @niov: the length of the @iov array
* @fds: an array of file handles to read
* @nfds: number of file handles in @fds
* @flags: read flags (QIO_CHANNEL_READ_FLAG_*)
* @errp: pointer to a NULL-initialized error object
*
*
@ -903,6 +905,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds, size_t *nfds,
int flags,
Error **errp);
/**

View File

@ -162,16 +162,17 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
GMainContext *context)
{
Error *err = NULL;
QCryptoTLSSessionHandshakeStatus status;
int status;
if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) {
status = qcrypto_tls_session_handshake(ioc->session, &err);
if (status < 0) {
trace_qio_channel_tls_handshake_fail(ioc);
qio_task_set_error(task, err);
qio_task_complete(task);
return;
}
status = qcrypto_tls_session_get_handshake_status(ioc->session);
if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
trace_qio_channel_tls_handshake_complete(ioc);
if (qcrypto_tls_session_check_credentials(ioc->session,
@ -247,6 +248,85 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc,
qio_channel_tls_handshake_task(ioc, task, context);
}
static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition,
gpointer user_data);
static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
GMainContext *context)
{
GIOCondition condition;
QIOChannelTLSData *data;
int status;
Error *err = NULL;
status = qcrypto_tls_session_bye(ioc->session, &err);
if (status < 0) {
trace_qio_channel_tls_bye_fail(ioc);
qio_task_set_error(task, err);
qio_task_complete(task);
return;
}
if (status == QCRYPTO_TLS_BYE_COMPLETE) {
qio_task_complete(task);
return;
}
data = g_new0(typeof(*data), 1);
data->task = task;
data->context = context;
if (context) {
g_main_context_ref(context);
}
if (status == QCRYPTO_TLS_BYE_SENDING) {
condition = G_IO_OUT;
} else {
condition = G_IO_IN;
}
trace_qio_channel_tls_bye_pending(ioc, status);
ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition,
qio_channel_tls_bye_io,
data, NULL, context);
}
static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition,
gpointer user_data)
{
QIOChannelTLSData *data = user_data;
QIOTask *task = data->task;
GMainContext *context = data->context;
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task));
tioc->bye_ioc_tag = 0;
g_free(data);
qio_channel_tls_bye_task(tioc, task, context);
if (context) {
g_main_context_unref(context);
}
return FALSE;
}
static void propagate_error(QIOTask *task, gpointer opaque)
{
qio_task_propagate_error(task, opaque);
}
void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp)
{
QIOTask *task;
task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL);
trace_qio_channel_tls_bye_start(ioc);
qio_channel_tls_bye_task(ioc, task, NULL);
}
static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED)
{
@ -279,6 +359,7 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
tioc->session,
iov[i].iov_base,
iov[i].iov_len,
flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF ||
qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ,
errp);
if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
@ -379,6 +460,11 @@ static int qio_channel_tls_close(QIOChannel *ioc,
g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove);
}
if (tioc->bye_ioc_tag) {
trace_qio_channel_tls_bye_cancel(ioc);
g_clear_handle_id(&tioc->bye_ioc_tag, g_source_remove);
}
return qio_channel_close(tioc->master, errp);
}

View File

@ -115,7 +115,8 @@ int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc,
size_t niov,
Error **errp)
{
return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp);
return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, 0,
errp);
}
int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc,
@ -130,6 +131,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds, size_t *nfds,
int flags,
Error **errp)
{
int ret = -1;
@ -155,7 +157,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc,
while ((nlocal_iov > 0) || local_fds) {
ssize_t len;
len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
local_nfds, 0, errp);
local_nfds, flags, errp);
if (len == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) {
qio_channel_yield(ioc, G_IO_IN);
@ -222,7 +224,8 @@ int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc,
int **fds, size_t *nfds,
Error **errp)
{
int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp);
int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, 0,
errp);
if (ret == 0) {
error_setg(errp, "Unexpected end-of-file before all data were read");

View File

@ -44,6 +44,11 @@ qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending
qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p"
qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p"
qio_channel_tls_handshake_cancel(void *ioc) "TLS handshake cancel ioc=%p"
qio_channel_tls_bye_start(void *ioc) "TLS termination start ioc=%p"
qio_channel_tls_bye_pending(void *ioc, int status) "TLS termination pending ioc=%p status=%d"
qio_channel_tls_bye_fail(void *ioc) "TLS termination fail ioc=%p"
qio_channel_tls_bye_complete(void *ioc) "TLS termination complete ioc=%p"
qio_channel_tls_bye_cancel(void *ioc) "TLS termination cancel ioc=%p"
qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p"
qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p"

View File

@ -33,6 +33,7 @@
void migration_channel_process_incoming(QIOChannel *ioc)
{
MigrationState *s = migrate_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
Error *local_err = NULL;
trace_migration_set_incoming_channel(
@ -47,6 +48,10 @@ void migration_channel_process_incoming(QIOChannel *ioc)
if (local_err) {
error_report_err(local_err);
migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
if (mis->exit_on_error) {
exit(EXIT_FAILURE);
}
}
}
@ -74,7 +79,7 @@ void migration_channel_connect(MigrationState *s,
if (!error) {
/* tls_channel_connect will call back to this
* function after the TLS handshake,
* so we mustn't call migrate_fd_connect until then
* so we mustn't call migration_connect until then
*/
return;
@ -89,7 +94,7 @@ void migration_channel_connect(MigrationState *s,
qemu_mutex_unlock(&s->qemu_file_lock);
}
}
migrate_fd_connect(s, error);
migration_connect(s, error);
error_free(error);
}

View File

@ -137,6 +137,7 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
trace_cpr_state_save(MigMode_str(mode));
if (mode == MIG_MODE_CPR_TRANSFER) {
g_assert(channel);
f = cpr_transfer_output(channel, errp);
} else {
return 0;

View File

@ -106,7 +106,6 @@ static GSList *migration_blockers[MIG_MODE__MAX];
static bool migration_object_check(MigrationState *ms, Error **errp);
static bool migration_switchover_start(MigrationState *s, Error **errp);
static void migrate_fd_cancel(MigrationState *s);
static bool close_return_path_on_source(MigrationState *s);
static void migration_completion_end(MigrationState *s);
static void migrate_hup_delete(MigrationState *s);
@ -342,17 +341,6 @@ void migration_bh_schedule(QEMUBHFunc *cb, void *opaque)
qemu_bh_schedule(bh);
}
void migration_cancel(const Error *error)
{
if (error) {
migrate_set_error(current_migration, error);
}
if (migrate_dirty_limit()) {
qmp_cancel_vcpu_dirty_limit(false, -1, NULL);
}
migrate_fd_cancel(current_migration);
}
void migration_shutdown(void)
{
/*
@ -365,7 +353,7 @@ void migration_shutdown(void)
* Cancel the current migration - that will (eventually)
* stop the migration using this structure
*/
migration_cancel(NULL);
migration_cancel();
object_unref(OBJECT(current_migration));
/*
@ -1435,12 +1423,12 @@ static void migration_cleanup_json_writer(MigrationState *s)
g_clear_pointer(&s->vmdesc, json_writer_free);
}
static void migrate_fd_cleanup(MigrationState *s)
static void migration_cleanup(MigrationState *s)
{
MigrationEventType type;
QEMUFile *tmp = NULL;
trace_migrate_fd_cleanup();
trace_migration_cleanup();
migration_cleanup_json_writer(s);
@ -1497,9 +1485,9 @@ static void migrate_fd_cleanup(MigrationState *s)
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
}
static void migrate_fd_cleanup_bh(void *opaque)
static void migration_cleanup_bh(void *opaque)
{
migrate_fd_cleanup(opaque);
migration_cleanup(opaque);
}
void migrate_set_error(MigrationState *s, const Error *error)
@ -1529,7 +1517,7 @@ static void migrate_error_free(MigrationState *s)
}
}
static void migrate_fd_error(MigrationState *s, const Error *error)
static void migration_connect_set_error(MigrationState *s, const Error *error)
{
MigrationStatus current = s->state;
MigrationStatus next;
@ -1558,12 +1546,17 @@ static void migrate_fd_error(MigrationState *s, const Error *error)
migrate_set_error(s, error);
}
static void migrate_fd_cancel(MigrationState *s)
void migration_cancel(void)
{
MigrationState *s = migrate_get_current();
int old_state ;
bool setup = (s->state == MIGRATION_STATUS_SETUP);
trace_migrate_fd_cancel();
trace_migration_cancel();
if (migrate_dirty_limit()) {
qmp_cancel_vcpu_dirty_limit(false, -1, NULL);
}
WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) {
if (s->rp_state.from_dst_file) {
@ -2205,7 +2198,7 @@ void qmp_migrate(const char *uri, bool has_channels,
out:
if (local_err) {
migrate_fd_error(s, local_err);
migration_connect_set_error(s, local_err);
error_propagate(errp, local_err);
}
}
@ -2250,7 +2243,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
if (!resume_requested) {
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
}
migrate_fd_error(s, local_err);
migration_connect_set_error(s, local_err);
error_propagate(errp, local_err);
return;
}
@ -2258,7 +2251,18 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
void qmp_migrate_cancel(Error **errp)
{
migration_cancel(NULL);
/*
* After postcopy migration has started, the source machine is not
* recoverable in case of a migration error. This also means the
* cancel command cannot be used as cancel should allow the
* machine to continue operation.
*/
if (migration_in_postcopy()) {
error_setg(errp, "Postcopy migration in progress, cannot cancel.");
return;
}
migration_cancel();
}
void qmp_migrate_continue(MigrationStatus state, Error **errp)
@ -2644,7 +2648,10 @@ static int postcopy_start(MigrationState *ms, Error **errp)
if (migrate_postcopy_preempt()) {
migration_wait_main_channel(ms);
if (postcopy_preempt_establish_channel(ms)) {
migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED);
if (ms->state != MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&ms->state, ms->state,
MIGRATION_STATUS_FAILED);
}
error_setg(errp, "%s: Failed to establish preempt channel",
__func__);
return -1;
@ -2982,7 +2989,9 @@ fail:
error_free(local_err);
}
migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
if (s->state != MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
}
}
/**
@ -3005,7 +3014,7 @@ static void bg_migration_completion(MigrationState *s)
qemu_put_buffer(s->to_dst_file, s->bioc->data, s->bioc->usage);
qemu_fflush(s->to_dst_file);
} else if (s->state == MIGRATION_STATUS_CANCELLING) {
goto fail;
return;
}
if (qemu_file_get_error(s->to_dst_file)) {
@ -3434,7 +3443,7 @@ static void migration_iteration_finish(MigrationState *s)
break;
}
migration_bh_schedule(migrate_fd_cleanup_bh, s);
migration_bh_schedule(migration_cleanup_bh, s);
bql_unlock();
}
@ -3462,7 +3471,7 @@ static void bg_migration_iteration_finish(MigrationState *s)
break;
}
migration_bh_schedule(migrate_fd_cleanup_bh, s);
migration_bh_schedule(migration_cleanup_bh, s);
bql_unlock();
}
@ -3844,7 +3853,7 @@ fail_setup:
return NULL;
}
void migrate_fd_connect(MigrationState *s, Error *error_in)
void migration_connect(MigrationState *s, Error *error_in)
{
Error *local_err = NULL;
uint64_t rate_limit;
@ -3854,24 +3863,24 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
/*
* If there's a previous error, free it and prepare for another one.
* Meanwhile if migration completes successfully, there won't have an error
* dumped when calling migrate_fd_cleanup().
* dumped when calling migration_cleanup().
*/
migrate_error_free(s);
s->expected_downtime = migrate_downtime_limit();
if (error_in) {
migrate_fd_error(s, error_in);
migration_connect_set_error(s, error_in);
if (resume) {
/*
* Don't do cleanup for resume if channel is invalid, but only dump
* the error. We wait for another channel connect from the user.
* The error_report still gives HMP user a hint on what failed.
* It's normally done in migrate_fd_cleanup(), but call it here
* It's normally done in migration_cleanup(), but call it here
* explicitly.
*/
error_report_err(error_copy(s->error));
} else {
migrate_fd_cleanup(s);
migration_cleanup(s);
}
return;
}
@ -3949,9 +3958,11 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
fail:
migrate_set_error(s, local_err);
migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
if (s->state != MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
}
error_report_err(local_err);
migrate_fd_cleanup(s);
migration_cleanup(s);
}
static void migration_class_init(ObjectClass *klass, void *data)

View File

@ -443,6 +443,39 @@ struct MigrationState {
* Default value is false. (since 8.1)
*/
bool multifd_flush_after_each_section;
/*
* This variable only makes sense when set on the machine that is
* the destination of a multifd migration with TLS enabled. It
* affects the behavior of the last send->recv iteration with
* regards to termination of the TLS session.
*
* When set:
*
* - the destination QEMU instance can expect to never get a
* GNUTLS_E_PREMATURE_TERMINATION error. Manifested as the error
* message: "The TLS connection was non-properly terminated".
*
* When clear:
*
* - the destination QEMU instance can expect to see a
* GNUTLS_E_PREMATURE_TERMINATION error in any multifd channel
* whenever the last recv() call of that channel happens after
* the source QEMU instance has already issued shutdown() on the
* channel.
*
* Commit 637280aeb2 (since 9.1) introduced a side effect that
* causes the destination instance to not be affected by the
* premature termination, while commit 1d457daf86 (since 10.0)
* causes the premature termination condition to be once again
* reachable.
*
* NOTE: Regardless of the state of this option, a premature
* termination of the TLS connection might happen due to error at
* any moment prior to the last send->recv iteration.
*/
bool multifd_clean_tls_termination;
/*
* This decides the size of guest memory chunk that will be used
* to track dirty bitmap clearing. The size of memory chunk will
@ -484,7 +517,7 @@ bool migration_has_all_channels(void);
void migrate_set_error(MigrationState *s, const Error *error);
bool migrate_has_error(MigrationState *s);
void migrate_fd_connect(MigrationState *s, Error *error_in);
void migration_connect(MigrationState *s, Error *error_in);
int migration_call_notifiers(MigrationState *s, MigrationEventType type,
Error **errp);
@ -530,7 +563,7 @@ void migration_make_urgent_request(void);
void migration_consume_urgent_request(void);
bool migration_rate_limit(void);
void migration_bh_schedule(QEMUBHFunc *cb, void *opaque);
void migration_cancel(const Error *error);
void migration_cancel(void);
void migration_populate_vfio_info(MigrationInfo *info);
void migration_reset_vfio_bytes_transferred(void);

View File

@ -444,7 +444,7 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp)
* channels have no I/O handler callback registered when reaching
* here, because migration thread will wait for all multifd channel
* establishments to complete during setup. Since
* migrate_fd_cleanup() will be scheduled in main thread too, all
* migration_cleanup() will be scheduled in main thread too, all
* previous callbacks should guarantee to be completed when
* reaching here. See multifd_send_state.channels_created and its
* usage. In the future, we could replace this with an assert
@ -490,6 +490,36 @@ void multifd_send_shutdown(void)
return;
}
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDSendParams *p = &multifd_send_state->params[i];
/* thread_created implies the TLS handshake has succeeded */
if (p->tls_thread_created && p->thread_created) {
Error *local_err = NULL;
/*
* The destination expects the TLS session to always be
* properly terminated. This helps to detect a premature
* termination in the middle of the stream. Note that
* older QEMUs always break the connection on the source
* and the destination always sees
* GNUTLS_E_PREMATURE_TERMINATION.
*/
migration_tls_channel_end(p->c, &local_err);
/*
* The above can return an error in case the migration has
* already failed. If the migration succeeded, errors are
* not expected but there's no need to kill the source.
*/
if (local_err && !migration_has_failed(migrate_get_current())) {
warn_report(
"multifd_send_%d: Failed to terminate TLS connection: %s",
p->id, error_get_pretty(local_err));
break;
}
}
}
multifd_send_terminate_threads();
for (i = 0; i < migrate_multifd_channels(); i++) {
@ -1121,6 +1151,7 @@ void multifd_recv_sync_main(void)
static void *multifd_recv_thread(void *opaque)
{
MigrationState *s = migrate_get_current();
MultiFDRecvParams *p = opaque;
Error *local_err = NULL;
bool use_packets = multifd_use_packets();
@ -1129,19 +1160,34 @@ static void *multifd_recv_thread(void *opaque)
trace_multifd_recv_thread_start(p->id);
rcu_register_thread();
if (!s->multifd_clean_tls_termination) {
p->read_flags = QIO_CHANNEL_READ_FLAG_RELAXED_EOF;
}
while (true) {
uint32_t flags = 0;
bool has_data = false;
p->normal_num = 0;
if (use_packets) {
struct iovec iov = {
.iov_base = (void *)p->packet,
.iov_len = p->packet_len
};
if (multifd_recv_should_exit()) {
break;
}
ret = qio_channel_read_all_eof(p->c, (void *)p->packet,
p->packet_len, &local_err);
if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */
ret = qio_channel_readv_full_all_eof(p->c, &iov, 1, NULL, NULL,
p->read_flags, &local_err);
if (!ret) {
/* EOF */
assert(!local_err);
break;
}
if (ret == -1) {
break;
}

View File

@ -244,6 +244,8 @@ typedef struct {
uint32_t zero_num;
/* used for de-compression methods */
void *compress_data;
/* Flags for the QIOChannel */
int read_flags;
} MultiFDRecvParams;
typedef struct {

View File

@ -99,6 +99,8 @@ const Property migration_properties[] = {
clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT),
DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState,
preempt_pre_7_2, false),
DEFINE_PROP_BOOL("multifd-clean-tls-termination", MigrationState,
multifd_clean_tls_termination, true),
/* Migration parameters */
DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState,

View File

@ -4465,8 +4465,10 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host,
* Abort and indicate a proper reason.
*/
error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr);
migration_cancel(err);
migrate_set_error(migrate_get_current(), err);
error_free(err);
migration_cancel();
}
switch (ps) {

View File

@ -4174,7 +4174,7 @@ void rdma_start_outgoing_migration(void *opaque,
s->to_dst_file = rdma_new_output(rdma);
s->rdma_migration = true;
migrate_fd_connect(s, NULL);
migration_connect(s, NULL);
return;
return_path_err:
qemu_rdma_cleanup(rdma);

View File

@ -2940,7 +2940,11 @@ int qemu_loadvm_state(QEMUFile *f)
/* When reaching here, it must be precopy */
if (ret == 0) {
ret = qemu_file_get_error(f);
if (migrate_has_error(migrate_get_current())) {
ret = -EINVAL;
} else {
ret = qemu_file_get_error(f);
}
}
/*

View File

@ -156,6 +156,11 @@ void migration_tls_channel_connect(MigrationState *s,
NULL);
}
void migration_tls_channel_end(QIOChannel *ioc, Error **errp)
{
qio_channel_tls_bye(QIO_CHANNEL_TLS(ioc), errp);
}
bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc)
{
if (!migrate_tls()) {

View File

@ -36,7 +36,7 @@ void migration_tls_channel_connect(MigrationState *s,
QIOChannel *ioc,
const char *hostname,
Error **errp);
void migration_tls_channel_end(QIOChannel *ioc, Error **errp);
/* Whether the QIO channel requires further TLS handshake? */
bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc);

View File

@ -154,9 +154,9 @@ multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostnam
# migration.c
migrate_set_state(const char *new_state) "new state %s"
migrate_fd_cleanup(void) ""
migration_cleanup(void) ""
migrate_error(const char *error_desc) "error=%s"
migrate_fd_cancel(void) ""
migration_cancel(void) ""
migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx"
migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")"
migrate_pending_estimate(uint64_t size, uint64_t pre, uint64_t post) "estimate pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")"

View File

@ -1524,7 +1524,9 @@
##
# @migrate_cancel:
#
# Cancel the current executing migration process.
# Cancel the currently executing migration process. Allows a new
# migration to be started right after. When postcopy-ram is in use,
# cancelling is not allowed after the postcopy phase has started.
#
# .. note:: This command succeeds even if there is no migration
# process running.

View File

@ -127,7 +127,7 @@ COMPARISONS = [
# varying numbers of channels
Comparison("compr-multifd", scenarios = [
Scenario("compr-multifd-channels-4",
multifd=True, multifd_channels=2),
multifd=True, multifd_channels=4),
Scenario("compr-multifd-channels-8",
multifd=True, multifd_channels=8),
Scenario("compr-multifd-channels-32",
@ -158,4 +158,17 @@ COMPARISONS = [
Scenario("compr-dirty-limit-50MB",
dirty_limit=True, vcpu_dirty_limit=50),
]),
# Looking at effect of multifd with
# different compression algorithms
Comparison("compr-multifd-compression", scenarios = [
Scenario("compr-multifd-compression-zlib",
multifd=True, multifd_channels=2, multifd_compression="zlib"),
Scenario("compr-multifd-compression-zstd",
multifd=True, multifd_channels=2, multifd_compression="zstd"),
Scenario("compr-multifd-compression-qpl",
multifd=True, multifd_channels=2, multifd_compression="qpl"),
Scenario("compr-multifd-compression-uadk",
multifd=True, multifd_channels=2, multifd_compression="uadk"),
]),
]

View File

@ -24,13 +24,15 @@ import sys
import time
from guestperf.progress import Progress, ProgressStats
from guestperf.report import Report
from guestperf.report import Report, ReportResult
from guestperf.timings import TimingRecord, Timings
sys.path.append(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'python'))
from qemu.machine import QEMUMachine
# multifd supported compression algorithms
MULTIFD_CMP_ALGS = ("zlib", "zstd", "qpl", "uadk")
class Engine(object):
@ -106,7 +108,8 @@ class Engine(object):
info.get("dirty-limit-ring-full-time", 0),
)
def _migrate(self, hardware, scenario, src, dst, connect_uri):
def _migrate(self, hardware, scenario, src,
dst, connect_uri, defer_migrate):
src_qemu_time = []
src_vcpu_time = []
src_pid = src.get_pid()
@ -190,6 +193,12 @@ class Engine(object):
scenario._compression_xbzrle_cache))
if scenario._multifd:
if (scenario._multifd_compression and
(scenario._multifd_compression not in MULTIFD_CMP_ALGS)):
raise Exception("unsupported multifd compression "
"algorithm: %s" %
scenario._multifd_compression)
resp = src.cmd("migrate-set-capabilities",
capabilities = [
{ "capability": "multifd",
@ -205,6 +214,12 @@ class Engine(object):
resp = dst.cmd("migrate-set-parameters",
multifd_channels=scenario._multifd_channels)
if scenario._multifd_compression:
resp = src.cmd("migrate-set-parameters",
multifd_compression=scenario._multifd_compression)
resp = dst.cmd("migrate-set-parameters",
multifd_compression=scenario._multifd_compression)
if scenario._dirty_limit:
if not hardware._dirty_ring_size:
raise Exception("dirty ring size must be configured when "
@ -220,6 +235,8 @@ class Engine(object):
resp = src.cmd("migrate-set-parameters",
vcpu_dirty_limit=scenario._vcpu_dirty_limit)
if defer_migrate:
resp = dst.cmd("migrate-incoming", uri=connect_uri)
resp = src.cmd("migrate", uri=connect_uri)
post_copy = False
@ -259,7 +276,11 @@ class Engine(object):
src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads))
sleep_secs -= 1
return [progress_history, src_qemu_time, src_vcpu_time]
result = ReportResult()
if progress._status == "completed" and not paused:
result = ReportResult(True)
return [progress_history, src_qemu_time, src_vcpu_time, result]
if self._verbose and (loop % 20) == 0:
print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % (
@ -373,11 +394,14 @@ class Engine(object):
def _get_src_args(self, hardware):
return self._get_common_args(hardware)
def _get_dst_args(self, hardware, uri):
def _get_dst_args(self, hardware, uri, defer_migrate):
tunnelled = False
if self._dst_host != "localhost":
tunnelled = True
argv = self._get_common_args(hardware, tunnelled)
if defer_migrate:
return argv + ["-incoming", "defer"]
return argv + ["-incoming", uri]
@staticmethod
@ -424,6 +448,7 @@ class Engine(object):
def run(self, hardware, scenario, result_dir=os.getcwd()):
abs_result_dir = os.path.join(result_dir, scenario._name)
defer_migrate = False
if self._transport == "tcp":
uri = "tcp:%s:9000" % self._dst_host
@ -439,6 +464,9 @@ class Engine(object):
except:
pass
if scenario._multifd:
defer_migrate = True
if self._dst_host != "localhost":
dstmonaddr = ("localhost", 9001)
else:
@ -452,7 +480,7 @@ class Engine(object):
monitor_address=srcmonaddr)
dst = QEMUMachine(self._binary,
args=self._get_dst_args(hardware, uri),
args=self._get_dst_args(hardware, uri, defer_migrate),
wrapper=self._get_dst_wrapper(hardware),
name="qemu-dst-%d" % os.getpid(),
monitor_address=dstmonaddr)
@ -461,10 +489,12 @@ class Engine(object):
src.launch()
dst.launch()
ret = self._migrate(hardware, scenario, src, dst, uri)
ret = self._migrate(hardware, scenario, src,
dst, uri, defer_migrate)
progress_history = ret[0]
qemu_timings = ret[1]
vcpu_timings = ret[2]
result = ret[3]
if uri[0:5] == "unix:" and os.path.exists(uri[5:]):
os.remove(uri[5:])
@ -484,6 +514,7 @@ class Engine(object):
Timings(self._get_timings(src) + self._get_timings(dst)),
Timings(qemu_timings),
Timings(vcpu_timings),
result,
self._binary, self._dst_host, self._kernel,
self._initrd, self._transport, self._sleep)
except Exception as e:

View File

@ -24,6 +24,22 @@ from guestperf.scenario import Scenario
from guestperf.progress import Progress
from guestperf.timings import Timings
class ReportResult(object):
def __init__(self, success=False):
self._success = success
def serialize(self):
return {
"success": self._success,
}
@classmethod
def deserialize(cls, data):
return cls(
data["success"])
class Report(object):
def __init__(self,
@ -33,6 +49,7 @@ class Report(object):
guest_timings,
qemu_timings,
vcpu_timings,
result,
binary,
dst_host,
kernel,
@ -46,6 +63,7 @@ class Report(object):
self._guest_timings = guest_timings
self._qemu_timings = qemu_timings
self._vcpu_timings = vcpu_timings
self._result = result
self._binary = binary
self._dst_host = dst_host
self._kernel = kernel
@ -61,6 +79,7 @@ class Report(object):
"guest_timings": self._guest_timings.serialize(),
"qemu_timings": self._qemu_timings.serialize(),
"vcpu_timings": self._vcpu_timings.serialize(),
"result": self._result.serialize(),
"binary": self._binary,
"dst_host": self._dst_host,
"kernel": self._kernel,
@ -78,6 +97,7 @@ class Report(object):
Timings.deserialize(data["guest_timings"]),
Timings.deserialize(data["qemu_timings"]),
Timings.deserialize(data["vcpu_timings"]),
ReportResult.deserialize(data["result"]),
data["binary"],
data["dst_host"],
data["kernel"],

View File

@ -30,7 +30,7 @@ class Scenario(object):
auto_converge=False, auto_converge_step=10,
compression_mt=False, compression_mt_threads=1,
compression_xbzrle=False, compression_xbzrle_cache=10,
multifd=False, multifd_channels=2,
multifd=False, multifd_channels=2, multifd_compression="",
dirty_limit=False, x_vcpu_dirty_limit_period=500,
vcpu_dirty_limit=1):
@ -61,6 +61,7 @@ class Scenario(object):
self._multifd = multifd
self._multifd_channels = multifd_channels
self._multifd_compression = multifd_compression
self._dirty_limit = dirty_limit
self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period
@ -85,6 +86,7 @@ class Scenario(object):
"compression_xbzrle_cache": self._compression_xbzrle_cache,
"multifd": self._multifd,
"multifd_channels": self._multifd_channels,
"multifd_compression": self._multifd_compression,
"dirty_limit": self._dirty_limit,
"x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period,
"vcpu_dirty_limit": self._vcpu_dirty_limit,
@ -109,4 +111,5 @@ class Scenario(object):
data["compression_xbzrle"],
data["compression_xbzrle_cache"],
data["multifd"],
data["multifd_channels"])
data["multifd_channels"],
data["multifd_compression"])

View File

@ -131,6 +131,8 @@ class Shell(BaseShell):
action="store_true")
parser.add_argument("--multifd-channels", dest="multifd_channels",
default=2, type=int)
parser.add_argument("--multifd-compression", dest="multifd_compression",
default="")
parser.add_argument("--dirty-limit", dest="dirty_limit", default=False,
action="store_true")
@ -167,6 +169,7 @@ class Shell(BaseShell):
multifd=args.multifd,
multifd_channels=args.multifd_channels,
multifd_compression=args.multifd_compression,
dirty_limit=args.dirty_limit,
x_vcpu_dirty_limit_period=\

View File

@ -236,6 +236,7 @@ char *resolve_machine_version(const char *alias, const char *var1,
typedef struct {
char *name;
void (*func)(void);
void (*func_full)(void *);
} MigrationTest;
static void migration_test_destroy(gpointer data)
@ -265,6 +266,29 @@ void migration_test_add(const char *path, void (*fn)(void))
migration_test_destroy);
}
static void migration_test_wrapper_full(const void *data)
{
MigrationTest *test = (MigrationTest *)data;
g_test_message("Running /%s%s", qtest_get_arch(), test->name);
test->func_full(test->name);
}
void migration_test_add_suffix(const char *path, const char *suffix,
void (*fn)(void *))
{
MigrationTest *test = g_new0(MigrationTest, 1);
g_assert(g_str_has_suffix(path, "/"));
g_assert(!g_str_has_prefix(suffix, "/"));
test->func_full = fn;
test->name = g_strconcat(path, suffix, NULL);
qtest_add_data_func_full(test->name, test, migration_test_wrapper_full,
migration_test_destroy);
}
#ifdef O_DIRECT
/*
* Probe for O_DIRECT support on the filesystem. Since this is used

View File

@ -51,6 +51,8 @@ static inline bool probe_o_direct_support(const char *tmpfs)
bool ufd_version_check(bool *uffd_feature_thread_id);
bool kvm_dirty_ring_supported(void);
void migration_test_add(const char *path, void (*fn)(void));
void migration_test_add_suffix(const char *path, const char *suffix,
void (*fn)(void *));
char *migrate_get_connect_uri(QTestState *who);
void migrate_set_ports(QTestState *to, QList *channel_list);

View File

@ -20,6 +20,7 @@
#include "migration/migration-util.h"
#include "ppc-util.h"
#include "qobject/qlist.h"
#include "qapi-types-migration.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/range.h"
@ -536,6 +537,161 @@ static void test_multifd_tcp_cancel(void)
migrate_end(from, to2, true);
}
static void test_cancel_src_after_failed(QTestState *from, QTestState *to,
const char *uri, const char *phase)
{
/*
* No migrate_incoming_qmp() at the start to force source into
* failed state during migrate_qmp().
*/
wait_for_serial("src_serial");
migrate_ensure_converge(from);
migrate_qmp(from, to, uri, NULL, "{}");
migration_event_wait(from, phase);
migrate_cancel(from);
/* cancelling will not move the migration out of 'failed' */
wait_for_migration_status(from, "failed",
(const char * []) { "completed", NULL });
/*
* Not waiting for the destination because it never started
* migration.
*/
}
static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to,
const char *uri, const char *phase)
{
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
wait_for_serial("src_serial");
migrate_ensure_converge(from);
migrate_qmp(from, to, uri, NULL, "{}");
/* To move to cancelled/cancelling */
migrate_cancel(from);
migration_event_wait(from, phase);
/* The migrate_cancel under test */
migrate_cancel(from);
wait_for_migration_status(from, "cancelled",
(const char * []) { "completed", NULL });
wait_for_migration_status(to, "failed",
(const char * []) { "completed", NULL });
}
static void test_cancel_src_after_complete(QTestState *from, QTestState *to,
const char *uri, const char *phase)
{
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
wait_for_serial("src_serial");
migrate_ensure_converge(from);
migrate_qmp(from, to, uri, NULL, "{}");
migration_event_wait(from, phase);
migrate_cancel(from);
/*
* qmp_migrate_cancel() exits early if migration is not running
* anymore, the status will not change to cancelled.
*/
wait_for_migration_complete(from);
wait_for_migration_complete(to);
}
static void test_cancel_src_after_none(QTestState *from, QTestState *to,
const char *uri, const char *phase)
{
/*
* Test that cancelling without a migration happening does not
* affect subsequent migrations
*/
migrate_cancel(to);
wait_for_serial("src_serial");
migrate_cancel(from);
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
migrate_ensure_converge(from);
migrate_qmp(from, to, uri, NULL, "{}");
wait_for_migration_complete(from);
wait_for_migration_complete(to);
}
static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to,
const char *uri, const char *phase)
{
migrate_set_capability(from, "pause-before-switchover", true);
migrate_set_capability(to, "pause-before-switchover", true);
migrate_set_capability(from, "multifd", true);
migrate_set_capability(to, "multifd", true);
migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }");
wait_for_serial("src_serial");
migrate_ensure_converge(from);
migrate_qmp(from, to, uri, NULL, "{}");
migration_event_wait(from, phase);
migrate_cancel(from);
migration_event_wait(from, "cancelling");
wait_for_migration_status(from, "cancelled",
(const char * []) { "completed", NULL });
wait_for_migration_status(to, "failed",
(const char * []) { "completed", NULL });
}
static void test_cancel_src_after_status(void *opaque)
{
const char *test_path = opaque;
g_autofree char *phase = g_path_get_basename(test_path);
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
MigrateStart args = {
.hide_stderr = true,
};
if (migrate_start(&from, &to, "defer", &args)) {
return;
}
if (g_str_equal(phase, "cancelling") ||
g_str_equal(phase, "cancelled")) {
test_cancel_src_after_cancelled(from, to, uri, phase);
} else if (g_str_equal(phase, "completed")) {
test_cancel_src_after_complete(from, to, uri, phase);
} else if (g_str_equal(phase, "failed")) {
test_cancel_src_after_failed(from, to, uri, phase);
} else if (g_str_equal(phase, "none")) {
test_cancel_src_after_none(from, to, uri, phase);
} else {
/* any state that comes before pre-switchover */
test_cancel_src_pre_switchover(from, to, uri, phase);
}
migrate_end(from, to, false);
}
static void calc_dirty_rate(QTestState *who, uint64_t calc_time)
{
qtest_qmp_assert_success(who,
@ -1018,4 +1174,24 @@ void migration_test_add_precopy(MigrationTestEnv *env)
test_vcpu_dirty_limit);
}
}
/* ensure new status don't go unnoticed */
assert(MIGRATION_STATUS__MAX == 15);
for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) {
switch (i) {
case MIGRATION_STATUS_DEVICE: /* happens too fast */
case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */
case MIGRATION_STATUS_COLO: /* no support in tests */
case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
case MIGRATION_STATUS_POSTCOPY_RECOVER:
continue;
default:
migration_test_add_suffix("/migration/cancel/src/after/",
MigrationStatus_str(i),
test_cancel_src_after_status);
}
}
}

View File

@ -158,8 +158,7 @@ static void test_crypto_tls_session_psk(void)
rv = qcrypto_tls_session_handshake(serverSess,
&error_abort);
g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(serverSess) ==
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
serverShake = true;
}
}
@ -167,8 +166,7 @@ static void test_crypto_tls_session_psk(void)
rv = qcrypto_tls_session_handshake(clientSess,
&error_abort);
g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(clientSess) ==
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
clientShake = true;
}
}
@ -352,8 +350,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
rv = qcrypto_tls_session_handshake(serverSess,
&error_abort);
g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(serverSess) ==
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
serverShake = true;
}
}
@ -361,8 +358,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
rv = qcrypto_tls_session_handshake(clientSess,
&error_abort);
g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(clientSess) ==
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
clientShake = true;
}
}