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) Error **errp)
{ {
int ret = gnutls_handshake(session->handle); int ret = gnutls_handshake(session->handle);
if (ret == 0) { if (!ret) {
session->handshakeComplete = true; session->handshakeComplete = true;
} else { return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
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;
}
} }
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->rerr);
error_free(session->werr); error_free(session->werr);
session->rerr = session->werr = NULL; session->rerr = session->werr = NULL;
return ret; return -1;
} }
QCryptoTLSSessionHandshakeStatus int
qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp)
{ {
if (session->handshakeComplete) { int ret;
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;
}
}
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 int
qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, qcrypto_tls_session_get_key_size(QCryptoTLSSession *session,
@ -692,10 +716,10 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *sess,
} }
QCryptoTLSSessionHandshakeStatus int
qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 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-transitional", "vectors", "0" },
{ "virtio-balloon-pci-non-transitional", "vectors", "0" }, { "virtio-balloon-pci-non-transitional", "vectors", "0" },
{ "virtio-mem-pci", "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); 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(); 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()) { if (drop_bql && !iothread && !qemu_in_coroutine()) {
bql_lock(); bql_lock();

View File

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

View File

@ -49,8 +49,20 @@ struct QIOChannelTLS {
QCryptoTLSSession *session; QCryptoTLSSession *session;
QIOChannelShutdown shutdown; QIOChannelShutdown shutdown;
guint hs_ioc_tag; 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: * qio_channel_tls_new_server:
* @master: the underlying channel object * @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_WRITE_FLAG_ZERO_COPY 0x1
#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 #define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1
#define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2
typedef enum QIOChannelFeature QIOChannelFeature; typedef enum QIOChannelFeature QIOChannelFeature;
@ -885,6 +886,7 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
* @niov: the length of the @iov array * @niov: the length of the @iov array
* @fds: an array of file handles to read * @fds: an array of file handles to read
* @nfds: number of file handles in @fds * @nfds: number of file handles in @fds
* @flags: read flags (QIO_CHANNEL_READ_FLAG_*)
* @errp: pointer to a NULL-initialized error object * @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, const struct iovec *iov,
size_t niov, size_t niov,
int **fds, size_t *nfds, int **fds, size_t *nfds,
int flags,
Error **errp); Error **errp);
/** /**

View File

@ -162,16 +162,17 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
GMainContext *context) GMainContext *context)
{ {
Error *err = NULL; 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); trace_qio_channel_tls_handshake_fail(ioc);
qio_task_set_error(task, err); qio_task_set_error(task, err);
qio_task_complete(task); qio_task_complete(task);
return; return;
} }
status = qcrypto_tls_session_get_handshake_status(ioc->session);
if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
trace_qio_channel_tls_handshake_complete(ioc); trace_qio_channel_tls_handshake_complete(ioc);
if (qcrypto_tls_session_check_credentials(ioc->session, 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); 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) 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, tioc->session,
iov[i].iov_base, iov[i].iov_base,
iov[i].iov_len, iov[i].iov_len,
flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF ||
qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ,
errp); errp);
if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { 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); 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); 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, size_t niov,
Error **errp) 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, 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, const struct iovec *iov,
size_t niov, size_t niov,
int **fds, size_t *nfds, int **fds, size_t *nfds,
int flags,
Error **errp) Error **errp)
{ {
int ret = -1; 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) { while ((nlocal_iov > 0) || local_fds) {
ssize_t len; ssize_t len;
len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds, 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 (len == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) { if (qemu_in_coroutine()) {
qio_channel_yield(ioc, G_IO_IN); 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, int **fds, size_t *nfds,
Error **errp) 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) { if (ret == 0) {
error_setg(errp, "Unexpected end-of-file before all data were read"); 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_fail(void *ioc) "TLS handshake fail ioc=%p"
qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete 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_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_allow(void *ioc) "TLS credentials allow ioc=%p"
qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny 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) void migration_channel_process_incoming(QIOChannel *ioc)
{ {
MigrationState *s = migrate_get_current(); MigrationState *s = migrate_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
Error *local_err = NULL; Error *local_err = NULL;
trace_migration_set_incoming_channel( trace_migration_set_incoming_channel(
@ -47,6 +48,10 @@ void migration_channel_process_incoming(QIOChannel *ioc)
if (local_err) { if (local_err) {
error_report_err(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) { if (!error) {
/* tls_channel_connect will call back to this /* tls_channel_connect will call back to this
* function after the TLS handshake, * 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; return;
@ -89,7 +94,7 @@ void migration_channel_connect(MigrationState *s,
qemu_mutex_unlock(&s->qemu_file_lock); qemu_mutex_unlock(&s->qemu_file_lock);
} }
} }
migrate_fd_connect(s, error); migration_connect(s, error);
error_free(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)); trace_cpr_state_save(MigMode_str(mode));
if (mode == MIG_MODE_CPR_TRANSFER) { if (mode == MIG_MODE_CPR_TRANSFER) {
g_assert(channel);
f = cpr_transfer_output(channel, errp); f = cpr_transfer_output(channel, errp);
} else { } else {
return 0; 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_object_check(MigrationState *ms, Error **errp);
static bool migration_switchover_start(MigrationState *s, 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 bool close_return_path_on_source(MigrationState *s);
static void migration_completion_end(MigrationState *s); static void migration_completion_end(MigrationState *s);
static void migrate_hup_delete(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); 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) void migration_shutdown(void)
{ {
/* /*
@ -365,7 +353,7 @@ void migration_shutdown(void)
* Cancel the current migration - that will (eventually) * Cancel the current migration - that will (eventually)
* stop the migration using this structure * stop the migration using this structure
*/ */
migration_cancel(NULL); migration_cancel();
object_unref(OBJECT(current_migration)); 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); g_clear_pointer(&s->vmdesc, json_writer_free);
} }
static void migrate_fd_cleanup(MigrationState *s) static void migration_cleanup(MigrationState *s)
{ {
MigrationEventType type; MigrationEventType type;
QEMUFile *tmp = NULL; QEMUFile *tmp = NULL;
trace_migrate_fd_cleanup(); trace_migration_cleanup();
migration_cleanup_json_writer(s); migration_cleanup_json_writer(s);
@ -1497,9 +1485,9 @@ static void migrate_fd_cleanup(MigrationState *s)
yank_unregister_instance(MIGRATION_YANK_INSTANCE); 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) 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 current = s->state;
MigrationStatus next; MigrationStatus next;
@ -1558,12 +1546,17 @@ static void migrate_fd_error(MigrationState *s, const Error *error)
migrate_set_error(s, 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 ; int old_state ;
bool setup = (s->state == MIGRATION_STATUS_SETUP); 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) { WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) {
if (s->rp_state.from_dst_file) { if (s->rp_state.from_dst_file) {
@ -2205,7 +2198,7 @@ void qmp_migrate(const char *uri, bool has_channels,
out: out:
if (local_err) { if (local_err) {
migrate_fd_error(s, local_err); migration_connect_set_error(s, local_err);
error_propagate(errp, local_err); error_propagate(errp, local_err);
} }
} }
@ -2250,7 +2243,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
if (!resume_requested) { if (!resume_requested) {
yank_unregister_instance(MIGRATION_YANK_INSTANCE); yank_unregister_instance(MIGRATION_YANK_INSTANCE);
} }
migrate_fd_error(s, local_err); migration_connect_set_error(s, local_err);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
@ -2258,7 +2251,18 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
void qmp_migrate_cancel(Error **errp) 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) void qmp_migrate_continue(MigrationStatus state, Error **errp)
@ -2644,7 +2648,10 @@ static int postcopy_start(MigrationState *ms, Error **errp)
if (migrate_postcopy_preempt()) { if (migrate_postcopy_preempt()) {
migration_wait_main_channel(ms); migration_wait_main_channel(ms);
if (postcopy_preempt_establish_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", error_setg(errp, "%s: Failed to establish preempt channel",
__func__); __func__);
return -1; return -1;
@ -2982,7 +2989,9 @@ fail:
error_free(local_err); 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_put_buffer(s->to_dst_file, s->bioc->data, s->bioc->usage);
qemu_fflush(s->to_dst_file); qemu_fflush(s->to_dst_file);
} else if (s->state == MIGRATION_STATUS_CANCELLING) { } else if (s->state == MIGRATION_STATUS_CANCELLING) {
goto fail; return;
} }
if (qemu_file_get_error(s->to_dst_file)) { if (qemu_file_get_error(s->to_dst_file)) {
@ -3434,7 +3443,7 @@ static void migration_iteration_finish(MigrationState *s)
break; break;
} }
migration_bh_schedule(migrate_fd_cleanup_bh, s); migration_bh_schedule(migration_cleanup_bh, s);
bql_unlock(); bql_unlock();
} }
@ -3462,7 +3471,7 @@ static void bg_migration_iteration_finish(MigrationState *s)
break; break;
} }
migration_bh_schedule(migrate_fd_cleanup_bh, s); migration_bh_schedule(migration_cleanup_bh, s);
bql_unlock(); bql_unlock();
} }
@ -3844,7 +3853,7 @@ fail_setup:
return NULL; return NULL;
} }
void migrate_fd_connect(MigrationState *s, Error *error_in) void migration_connect(MigrationState *s, Error *error_in)
{ {
Error *local_err = NULL; Error *local_err = NULL;
uint64_t rate_limit; 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. * If there's a previous error, free it and prepare for another one.
* Meanwhile if migration completes successfully, there won't have an error * 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); migrate_error_free(s);
s->expected_downtime = migrate_downtime_limit(); s->expected_downtime = migrate_downtime_limit();
if (error_in) { if (error_in) {
migrate_fd_error(s, error_in); migration_connect_set_error(s, error_in);
if (resume) { if (resume) {
/* /*
* Don't do cleanup for resume if channel is invalid, but only dump * 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. We wait for another channel connect from the user.
* The error_report still gives HMP user a hint on what failed. * 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. * explicitly.
*/ */
error_report_err(error_copy(s->error)); error_report_err(error_copy(s->error));
} else { } else {
migrate_fd_cleanup(s); migration_cleanup(s);
} }
return; return;
} }
@ -3949,9 +3958,11 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
fail: fail:
migrate_set_error(s, local_err); 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); error_report_err(local_err);
migrate_fd_cleanup(s); migration_cleanup(s);
} }
static void migration_class_init(ObjectClass *klass, void *data) static void migration_class_init(ObjectClass *klass, void *data)

View File

@ -443,6 +443,39 @@ struct MigrationState {
* Default value is false. (since 8.1) * Default value is false. (since 8.1)
*/ */
bool multifd_flush_after_each_section; 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 * This decides the size of guest memory chunk that will be used
* to track dirty bitmap clearing. The size of memory chunk will * 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); void migrate_set_error(MigrationState *s, const Error *error);
bool migrate_has_error(MigrationState *s); 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, int migration_call_notifiers(MigrationState *s, MigrationEventType type,
Error **errp); Error **errp);
@ -530,7 +563,7 @@ void migration_make_urgent_request(void);
void migration_consume_urgent_request(void); void migration_consume_urgent_request(void);
bool migration_rate_limit(void); bool migration_rate_limit(void);
void migration_bh_schedule(QEMUBHFunc *cb, void *opaque); 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_populate_vfio_info(MigrationInfo *info);
void migration_reset_vfio_bytes_transferred(void); 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 * channels have no I/O handler callback registered when reaching
* here, because migration thread will wait for all multifd channel * here, because migration thread will wait for all multifd channel
* establishments to complete during setup. Since * 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 * previous callbacks should guarantee to be completed when
* reaching here. See multifd_send_state.channels_created and its * reaching here. See multifd_send_state.channels_created and its
* usage. In the future, we could replace this with an assert * usage. In the future, we could replace this with an assert
@ -490,6 +490,36 @@ void multifd_send_shutdown(void)
return; 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(); multifd_send_terminate_threads();
for (i = 0; i < migrate_multifd_channels(); i++) { 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) static void *multifd_recv_thread(void *opaque)
{ {
MigrationState *s = migrate_get_current();
MultiFDRecvParams *p = opaque; MultiFDRecvParams *p = opaque;
Error *local_err = NULL; Error *local_err = NULL;
bool use_packets = multifd_use_packets(); bool use_packets = multifd_use_packets();
@ -1129,19 +1160,34 @@ static void *multifd_recv_thread(void *opaque)
trace_multifd_recv_thread_start(p->id); trace_multifd_recv_thread_start(p->id);
rcu_register_thread(); rcu_register_thread();
if (!s->multifd_clean_tls_termination) {
p->read_flags = QIO_CHANNEL_READ_FLAG_RELAXED_EOF;
}
while (true) { while (true) {
uint32_t flags = 0; uint32_t flags = 0;
bool has_data = false; bool has_data = false;
p->normal_num = 0; p->normal_num = 0;
if (use_packets) { if (use_packets) {
struct iovec iov = {
.iov_base = (void *)p->packet,
.iov_len = p->packet_len
};
if (multifd_recv_should_exit()) { if (multifd_recv_should_exit()) {
break; break;
} }
ret = qio_channel_read_all_eof(p->c, (void *)p->packet, ret = qio_channel_readv_full_all_eof(p->c, &iov, 1, NULL, NULL,
p->packet_len, &local_err); p->read_flags, &local_err);
if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */ if (!ret) {
/* EOF */
assert(!local_err);
break;
}
if (ret == -1) {
break; break;
} }

View File

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

View File

@ -99,6 +99,8 @@ const Property migration_properties[] = {
clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT), clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT),
DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState, DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState,
preempt_pre_7_2, false), preempt_pre_7_2, false),
DEFINE_PROP_BOOL("multifd-clean-tls-termination", MigrationState,
multifd_clean_tls_termination, true),
/* Migration parameters */ /* Migration parameters */
DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, 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. * Abort and indicate a proper reason.
*/ */
error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr); error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr);
migration_cancel(err); migrate_set_error(migrate_get_current(), err);
error_free(err); error_free(err);
migration_cancel();
} }
switch (ps) { switch (ps) {

View File

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

View File

@ -2940,7 +2940,11 @@ int qemu_loadvm_state(QEMUFile *f)
/* When reaching here, it must be precopy */ /* When reaching here, it must be precopy */
if (ret == 0) { 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); 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) bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc)
{ {
if (!migrate_tls()) { if (!migrate_tls()) {

View File

@ -36,7 +36,7 @@ void migration_tls_channel_connect(MigrationState *s,
QIOChannel *ioc, QIOChannel *ioc,
const char *hostname, const char *hostname,
Error **errp); Error **errp);
void migration_tls_channel_end(QIOChannel *ioc, Error **errp);
/* Whether the QIO channel requires further TLS handshake? */ /* Whether the QIO channel requires further TLS handshake? */
bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); 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 # migration.c
migrate_set_state(const char *new_state) "new state %s" 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_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_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_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 ")" 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: # @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 # .. note:: This command succeeds even if there is no migration
# process running. # process running.

View File

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

View File

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

View File

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

View File

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

View File

@ -236,6 +236,7 @@ char *resolve_machine_version(const char *alias, const char *var1,
typedef struct { typedef struct {
char *name; char *name;
void (*func)(void); void (*func)(void);
void (*func_full)(void *);
} MigrationTest; } MigrationTest;
static void migration_test_destroy(gpointer data) static void migration_test_destroy(gpointer data)
@ -265,6 +266,29 @@ void migration_test_add(const char *path, void (*fn)(void))
migration_test_destroy); 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 #ifdef O_DIRECT
/* /*
* Probe for O_DIRECT support on the filesystem. Since this is used * 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 ufd_version_check(bool *uffd_feature_thread_id);
bool kvm_dirty_ring_supported(void); bool kvm_dirty_ring_supported(void);
void migration_test_add(const char *path, void (*fn)(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); char *migrate_get_connect_uri(QTestState *who);
void migrate_set_ports(QTestState *to, QList *channel_list); void migrate_set_ports(QTestState *to, QList *channel_list);

View File

@ -20,6 +20,7 @@
#include "migration/migration-util.h" #include "migration/migration-util.h"
#include "ppc-util.h" #include "ppc-util.h"
#include "qobject/qlist.h" #include "qobject/qlist.h"
#include "qapi-types-migration.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/range.h" #include "qemu/range.h"
@ -536,6 +537,161 @@ static void test_multifd_tcp_cancel(void)
migrate_end(from, to2, true); 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) static void calc_dirty_rate(QTestState *who, uint64_t calc_time)
{ {
qtest_qmp_assert_success(who, qtest_qmp_assert_success(who,
@ -1018,4 +1174,24 @@ void migration_test_add_precopy(MigrationTestEnv *env)
test_vcpu_dirty_limit); 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, rv = qcrypto_tls_session_handshake(serverSess,
&error_abort); &error_abort);
g_assert(rv >= 0); g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(serverSess) == if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
serverShake = true; serverShake = true;
} }
} }
@ -167,8 +166,7 @@ static void test_crypto_tls_session_psk(void)
rv = qcrypto_tls_session_handshake(clientSess, rv = qcrypto_tls_session_handshake(clientSess,
&error_abort); &error_abort);
g_assert(rv >= 0); g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(clientSess) == if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
clientShake = true; clientShake = true;
} }
} }
@ -352,8 +350,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
rv = qcrypto_tls_session_handshake(serverSess, rv = qcrypto_tls_session_handshake(serverSess,
&error_abort); &error_abort);
g_assert(rv >= 0); g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(serverSess) == if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
serverShake = true; serverShake = true;
} }
} }
@ -361,8 +358,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
rv = qcrypto_tls_session_handshake(clientSess, rv = qcrypto_tls_session_handshake(clientSess,
&error_abort); &error_abort);
g_assert(rv >= 0); g_assert(rv >= 0);
if (qcrypto_tls_session_get_handshake_status(clientSess) == if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
clientShake = true; clientShake = true;
} }
} }