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:
commit
9af3d9a931
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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()) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 ")"
|
||||||
|
@ -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.
|
||||||
|
@ -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"),
|
||||||
|
]),
|
||||||
]
|
]
|
||||||
|
@ -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:
|
||||||
|
@ -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"],
|
||||||
|
@ -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"])
|
||||||
|
@ -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=\
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user