1 | v2: | ||
---|---|---|---|
2 | |||
3 | Added the premature_ok logic; | ||
4 | Added compat property for QEMU <9.1; | ||
5 | Refactored the existing handshake code; | ||
6 | |||
7 | CI run: | ||
8 | https://gitlab.com/farosas/qemu/-/pipelines/1660800456 | ||
9 | |||
10 | v1: | ||
11 | https://lore.kernel.org/r/20250206175824.22664-1-farosas@suse.de | ||
12 | |||
1 | Hi, | 13 | Hi, |
2 | 14 | ||
3 | We've been discussing a way to stop multifd recv threads from getting | 15 | We've been discussing a way to stop multifd recv threads from getting |
4 | an error at the end of migration when the source threads close the | 16 | an error at the end of migration when the source threads close the |
5 | iochannel without ending the TLS session. | 17 | iochannel without ending the TLS session. |
... | ... | ||
17 | Aside from stopping the recv thread from seeing an error, this also | 29 | Aside from stopping the recv thread from seeing an error, this also |
18 | creates a contract that all connections should be closed only after | 30 | creates a contract that all connections should be closed only after |
19 | the TLS session is ended. This helps to avoid masking a legitimate | 31 | the TLS session is ended. This helps to avoid masking a legitimate |
20 | issue where the connection is closed prematurely. | 32 | issue where the connection is closed prematurely. |
21 | 33 | ||
22 | Fabiano Rosas (4): | 34 | Fabiano Rosas (8): |
23 | crypto: Allow gracefully ending the TLS session | 35 | crypto: Allow gracefully ending the TLS session |
24 | io: tls: Add qio_channel_tls_bye | 36 | io: tls: Add qio_channel_tls_bye |
25 | migration/multifd: Terminate the TLS connection | 37 | migration/multifd: Terminate the TLS connection |
26 | migration: Check migration error after loadvm | 38 | migration: Check migration error after loadvm |
39 | crypto: Remove qcrypto_tls_session_get_handshake_status | ||
40 | io: Plumb read flags into qio_channel_read_all_eof | ||
41 | io: Add a read flag for relaxed EOF | ||
42 | migration/multifd: Add a compat property for TLS termination | ||
27 | 43 | ||
28 | crypto/tlssession.c | 40 ++++++++++++++++++ | 44 | crypto/tlssession.c | 105 +++++++++++++++++----------- |
29 | include/crypto/tlssession.h | 22 ++++++++++ | 45 | hw/remote/mpqemu-link.c | 2 +- |
30 | include/io/channel-tls.h | 12 ++++++ | 46 | include/crypto/tlssession.h | 46 ++++++------ |
31 | io/channel-tls.c | 84 +++++++++++++++++++++++++++++++++++++ | 47 | include/io/channel-tls.h | 12 ++++ |
32 | io/trace-events | 5 +++ | 48 | include/io/channel.h | 7 ++ |
33 | migration/multifd.c | 34 ++++++++++++++- | 49 | io/channel-tls.c | 92 +++++++++++++++++++++++- |
34 | migration/savevm.c | 6 ++- | 50 | io/channel.c | 13 ++-- |
35 | migration/tls.c | 5 +++ | 51 | io/trace-events | 5 ++ |
36 | migration/tls.h | 2 +- | 52 | migration/migration.h | 33 +++++++++ |
37 | 9 files changed, 207 insertions(+), 3 deletions(-) | 53 | migration/multifd.c | 42 ++++++++++- |
54 | migration/multifd.h | 2 + | ||
55 | migration/options.c | 2 + | ||
56 | migration/savevm.c | 6 +- | ||
57 | migration/tls.c | 5 ++ | ||
58 | migration/tls.h | 2 +- | ||
59 | tests/unit/test-crypto-tlssession.c | 12 ++-- | ||
60 | tools/i386/qemu-vmsr-helper.c | 3 +- | ||
61 | util/vhost-user-server.c | 2 +- | ||
62 | 18 files changed, 308 insertions(+), 83 deletions(-) | ||
38 | 63 | ||
39 | -- | 64 | -- |
40 | 2.35.3 | 65 | 2.35.3 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
13 | 13 | ||
14 | 1- 1d457daf86 ("migration/multifd: Further remove the SYNC on complete") | 14 | 1- 1d457daf86 ("migration/multifd: Further remove the SYNC on complete") |
15 | 15 | ||
16 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | 16 | Signed-off-by: Fabiano Rosas <farosas@suse.de> |
17 | --- | 17 | --- |
18 | crypto/tlssession.c | 40 +++++++++++++++++++++++++++++++++++++ | 18 | crypto/tlssession.c | 41 +++++++++++++++++++++++++++++++++++++ |
19 | include/crypto/tlssession.h | 22 ++++++++++++++++++++ | 19 | include/crypto/tlssession.h | 22 ++++++++++++++++++++ |
20 | 2 files changed, 62 insertions(+) | 20 | 2 files changed, 63 insertions(+) |
21 | 21 | ||
22 | diff --git a/crypto/tlssession.c b/crypto/tlssession.c | 22 | diff --git a/crypto/tlssession.c b/crypto/tlssession.c |
23 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
24 | --- a/crypto/tlssession.c | 24 | --- a/crypto/tlssession.c |
25 | +++ b/crypto/tlssession.c | 25 | +++ b/crypto/tlssession.c |
... | ... | ||
71 | +int | 71 | +int |
72 | +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) | 72 | +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) |
73 | +{ | 73 | +{ |
74 | + return QCRYPTO_TLS_BYE_COMPLETE; | 74 | + return QCRYPTO_TLS_BYE_COMPLETE; |
75 | +} | 75 | +} |
76 | + | ||
76 | + | 77 | + |
77 | int | 78 | int |
78 | qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, | 79 | qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, |
79 | Error **errp) | 80 | Error **errp) |
80 | diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h | 81 | diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h |
... | ... | diff view generated by jsdifflib |
1 | Add a task dispatcher for gnutls_bye similar to the | 1 | Add a task dispatcher for gnutls_bye similar to the |
---|---|---|---|
2 | qio_channel_tls_handshake_task(). The gnutls_bye() call might be | 2 | qio_channel_tls_handshake_task(). The gnutls_bye() call might be |
3 | interrupted and so it needs to be rescheduled. | 3 | interrupted and so it needs to be rescheduled. |
4 | 4 | ||
5 | The migration code will make use of this to help the migration | 5 | The migration code will make use of this to help the migration |
6 | destination identify a premature EOF. Once the session termination is | 6 | destination identify a premature EOF. Once the session termination is |
7 | in place, any EOF that happens before the source issued gnutls_bye() | 7 | in place, any EOF that happens before the source issued gnutls_bye() |
8 | will be considered an error. | 8 | will be considered an error. |
9 | 9 | ||
10 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | 10 | Signed-off-by: Fabiano Rosas <farosas@suse.de> |
11 | --- | 11 | --- |
12 | include/io/channel-tls.h | 12 ++++++ | 12 | include/io/channel-tls.h | 12 ++++++ |
13 | io/channel-tls.c | 84 ++++++++++++++++++++++++++++++++++++++++ | 13 | io/channel-tls.c | 84 ++++++++++++++++++++++++++++++++++++++++ |
14 | io/trace-events | 5 +++ | 14 | io/trace-events | 5 +++ |
15 | 3 files changed, 101 insertions(+) | 15 | 3 files changed, 101 insertions(+) |
16 | 16 | ||
17 | diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h | 17 | diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h |
18 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/include/io/channel-tls.h | 19 | --- a/include/io/channel-tls.h |
20 | +++ b/include/io/channel-tls.h | 20 | +++ b/include/io/channel-tls.h |
21 | @@ -XXX,XX +XXX,XX @@ struct QIOChannelTLS { | 21 | @@ -XXX,XX +XXX,XX @@ struct QIOChannelTLS { |
22 | QCryptoTLSSession *session; | 22 | QCryptoTLSSession *session; |
23 | QIOChannelShutdown shutdown; | 23 | QIOChannelShutdown shutdown; |
24 | guint hs_ioc_tag; | 24 | guint hs_ioc_tag; |
25 | + guint bye_ioc_tag; | 25 | + guint bye_ioc_tag; |
26 | }; | 26 | }; |
27 | 27 | ||
28 | +/** | 28 | +/** |
29 | + * qio_channel_tls_bye: | 29 | + * qio_channel_tls_bye: |
30 | + * @ioc: the TLS channel object | 30 | + * @ioc: the TLS channel object |
31 | + * @errp: pointer to a NULL-initialized error object | 31 | + * @errp: pointer to a NULL-initialized error object |
32 | + * | 32 | + * |
33 | + * Perform the TLS session termination. This method will return | 33 | + * Perform the TLS session termination. This method will return |
34 | + * immediately and the termination will continue in the background, | 34 | + * immediately and the termination will continue in the background, |
35 | + * provided the main loop is running. | 35 | + * provided the main loop is running. |
36 | + */ | 36 | + */ |
37 | +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp); | 37 | +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp); |
38 | + | 38 | + |
39 | /** | 39 | /** |
40 | * qio_channel_tls_new_server: | 40 | * qio_channel_tls_new_server: |
41 | * @master: the underlying channel object | 41 | * @master: the underlying channel object |
42 | diff --git a/io/channel-tls.c b/io/channel-tls.c | 42 | diff --git a/io/channel-tls.c b/io/channel-tls.c |
43 | index XXXXXXX..XXXXXXX 100644 | 43 | index XXXXXXX..XXXXXXX 100644 |
44 | --- a/io/channel-tls.c | 44 | --- a/io/channel-tls.c |
45 | +++ b/io/channel-tls.c | 45 | +++ b/io/channel-tls.c |
46 | @@ -XXX,XX +XXX,XX @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, | 46 | @@ -XXX,XX +XXX,XX @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, |
47 | qio_channel_tls_handshake_task(ioc, task, context); | 47 | qio_channel_tls_handshake_task(ioc, task, context); |
48 | } | 48 | } |
49 | 49 | ||
50 | +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, | 50 | +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, |
51 | + gpointer user_data); | 51 | + gpointer user_data); |
52 | + | 52 | + |
53 | +static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task, | 53 | +static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task, |
54 | + GMainContext *context) | 54 | + GMainContext *context) |
55 | +{ | 55 | +{ |
56 | + GIOCondition condition; | 56 | + GIOCondition condition; |
57 | + QIOChannelTLSData *data; | 57 | + QIOChannelTLSData *data; |
58 | + int status; | 58 | + int status; |
59 | + Error *err = NULL; | 59 | + Error *err = NULL; |
60 | + | 60 | + |
61 | + status = qcrypto_tls_session_bye(ioc->session, &err); | 61 | + status = qcrypto_tls_session_bye(ioc->session, &err); |
62 | + | 62 | + |
63 | + if (status < 0) { | 63 | + if (status < 0) { |
64 | + trace_qio_channel_tls_bye_fail(ioc); | 64 | + trace_qio_channel_tls_bye_fail(ioc); |
65 | + qio_task_set_error(task, err); | 65 | + qio_task_set_error(task, err); |
66 | + qio_task_complete(task); | 66 | + qio_task_complete(task); |
67 | + return; | 67 | + return; |
68 | + } | 68 | + } |
69 | + | 69 | + |
70 | + if (status == QCRYPTO_TLS_BYE_COMPLETE) { | 70 | + if (status == QCRYPTO_TLS_BYE_COMPLETE) { |
71 | + qio_task_complete(task); | 71 | + qio_task_complete(task); |
72 | + return; | 72 | + return; |
73 | + } | 73 | + } |
74 | + | 74 | + |
75 | + data = g_new0(typeof(*data), 1); | 75 | + data = g_new0(typeof(*data), 1); |
76 | + data->task = task; | 76 | + data->task = task; |
77 | + data->context = context; | 77 | + data->context = context; |
78 | + | 78 | + |
79 | + if (context) { | 79 | + if (context) { |
80 | + g_main_context_ref(context); | 80 | + g_main_context_ref(context); |
81 | + } | 81 | + } |
82 | + | 82 | + |
83 | + if (status == QCRYPTO_TLS_BYE_SENDING) { | 83 | + if (status == QCRYPTO_TLS_BYE_SENDING) { |
84 | + condition = G_IO_OUT; | 84 | + condition = G_IO_OUT; |
85 | + } else { | 85 | + } else { |
86 | + condition = G_IO_IN; | 86 | + condition = G_IO_IN; |
87 | + } | 87 | + } |
88 | + | 88 | + |
89 | + trace_qio_channel_tls_bye_pending(ioc, status); | 89 | + trace_qio_channel_tls_bye_pending(ioc, status); |
90 | + ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition, | 90 | + ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition, |
91 | + qio_channel_tls_bye_io, | 91 | + qio_channel_tls_bye_io, |
92 | + data, NULL, context); | 92 | + data, NULL, context); |
93 | +} | 93 | +} |
94 | + | 94 | + |
95 | + | 95 | + |
96 | +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, | 96 | +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, |
97 | + gpointer user_data) | 97 | + gpointer user_data) |
98 | +{ | 98 | +{ |
99 | + QIOChannelTLSData *data = user_data; | 99 | + QIOChannelTLSData *data = user_data; |
100 | + QIOTask *task = data->task; | 100 | + QIOTask *task = data->task; |
101 | + GMainContext *context = data->context; | 101 | + GMainContext *context = data->context; |
102 | + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task)); | 102 | + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task)); |
103 | + | 103 | + |
104 | + tioc->bye_ioc_tag = 0; | 104 | + tioc->bye_ioc_tag = 0; |
105 | + g_free(data); | 105 | + g_free(data); |
106 | + qio_channel_tls_bye_task(tioc, task, context); | 106 | + qio_channel_tls_bye_task(tioc, task, context); |
107 | + | 107 | + |
108 | + if (context) { | 108 | + if (context) { |
109 | + g_main_context_unref(context); | 109 | + g_main_context_unref(context); |
110 | + } | 110 | + } |
111 | + | 111 | + |
112 | + return FALSE; | 112 | + return FALSE; |
113 | +} | 113 | +} |
114 | + | 114 | + |
115 | +static void propagate_error(QIOTask *task, gpointer opaque) | 115 | +static void propagate_error(QIOTask *task, gpointer opaque) |
116 | +{ | 116 | +{ |
117 | + qio_task_propagate_error(task, opaque); | 117 | + qio_task_propagate_error(task, opaque); |
118 | +} | 118 | +} |
119 | + | 119 | + |
120 | +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp) | 120 | +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp) |
121 | +{ | 121 | +{ |
122 | + QIOTask *task; | 122 | + QIOTask *task; |
123 | + | 123 | + |
124 | + task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL); | 124 | + task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL); |
125 | + | 125 | + |
126 | + trace_qio_channel_tls_bye_start(ioc); | 126 | + trace_qio_channel_tls_bye_start(ioc); |
127 | + qio_channel_tls_bye_task(ioc, task, NULL); | 127 | + qio_channel_tls_bye_task(ioc, task, NULL); |
128 | +} | 128 | +} |
129 | 129 | ||
130 | static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) | 130 | static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) |
131 | { | 131 | { |
132 | @@ -XXX,XX +XXX,XX @@ static int qio_channel_tls_close(QIOChannel *ioc, | 132 | @@ -XXX,XX +XXX,XX @@ static int qio_channel_tls_close(QIOChannel *ioc, |
133 | g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); | 133 | g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); |
134 | } | 134 | } |
135 | 135 | ||
136 | + if (tioc->bye_ioc_tag) { | 136 | + if (tioc->bye_ioc_tag) { |
137 | + trace_qio_channel_tls_bye_cancel(ioc); | 137 | + trace_qio_channel_tls_bye_cancel(ioc); |
138 | + g_clear_handle_id(&tioc->bye_ioc_tag, g_source_remove); | 138 | + g_clear_handle_id(&tioc->bye_ioc_tag, g_source_remove); |
139 | + } | 139 | + } |
140 | + | 140 | + |
141 | return qio_channel_close(tioc->master, errp); | 141 | return qio_channel_close(tioc->master, errp); |
142 | } | 142 | } |
143 | 143 | ||
144 | diff --git a/io/trace-events b/io/trace-events | 144 | diff --git a/io/trace-events b/io/trace-events |
145 | index XXXXXXX..XXXXXXX 100644 | 145 | index XXXXXXX..XXXXXXX 100644 |
146 | --- a/io/trace-events | 146 | --- a/io/trace-events |
147 | +++ b/io/trace-events | 147 | +++ b/io/trace-events |
148 | @@ -XXX,XX +XXX,XX @@ qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending | 148 | @@ -XXX,XX +XXX,XX @@ qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending |
149 | qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" | 149 | qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" |
150 | qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" | 150 | qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" |
151 | qio_channel_tls_handshake_cancel(void *ioc) "TLS handshake cancel ioc=%p" | 151 | qio_channel_tls_handshake_cancel(void *ioc) "TLS handshake cancel ioc=%p" |
152 | +qio_channel_tls_bye_start(void *ioc) "TLS termination start ioc=%p" | 152 | +qio_channel_tls_bye_start(void *ioc) "TLS termination start ioc=%p" |
153 | +qio_channel_tls_bye_pending(void *ioc, int status) "TLS termination pending ioc=%p status=%d" | 153 | +qio_channel_tls_bye_pending(void *ioc, int status) "TLS termination pending ioc=%p status=%d" |
154 | +qio_channel_tls_bye_fail(void *ioc) "TLS termination fail ioc=%p" | 154 | +qio_channel_tls_bye_fail(void *ioc) "TLS termination fail ioc=%p" |
155 | +qio_channel_tls_bye_complete(void *ioc) "TLS termination complete ioc=%p" | 155 | +qio_channel_tls_bye_complete(void *ioc) "TLS termination complete ioc=%p" |
156 | +qio_channel_tls_bye_cancel(void *ioc) "TLS termination cancel ioc=%p" | 156 | +qio_channel_tls_bye_cancel(void *ioc) "TLS termination cancel ioc=%p" |
157 | qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" | 157 | qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" |
158 | qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p" | 158 | qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p" |
159 | 159 | ||
160 | -- | 160 | -- |
161 | 2.35.3 | 161 | 2.35.3 | diff view generated by jsdifflib |
1 | The multifd recv side has been getting a TLS error of | 1 | The multifd recv side has been getting a TLS error of |
---|---|---|---|
2 | GNUTLS_E_PREMATURE_TERMINATION at the end of migration when the send | 2 | GNUTLS_E_PREMATURE_TERMINATION at the end of migration when the send |
3 | side closes the sockets without ending the TLS session. This has been | 3 | side closes the sockets without ending the TLS session. This has been |
4 | masked by the code not checking the migration error after loadvm. | 4 | masked by the code not checking the migration error after loadvm. |
5 | 5 | ||
6 | Start ending the TLS session at multifd_send_shutdown() so the recv | 6 | Start ending the TLS session at multifd_send_shutdown() so the recv |
7 | side always sees a clean termination (EOF) and we can start to | 7 | side always sees a clean termination (EOF) and we can start to |
8 | differentiate that from an actual premature termination that might | 8 | differentiate that from an actual premature termination that might |
9 | possibly happen in the middle of the migration. | 9 | possibly happen in the middle of the migration. |
10 | 10 | ||
11 | There's nothing to be done if a previous migration error has already | 11 | There's nothing to be done if a previous migration error has already |
12 | broken the connection, so add a comment explaining it and ignore any | 12 | broken the connection, so add a comment explaining it and ignore any |
13 | errors coming from gnutls_bye(). | 13 | errors coming from gnutls_bye(). |
14 | 14 | ||
15 | This doesn't break compat with older recv-side QEMUs because EOF has | 15 | This doesn't break compat with older recv-side QEMUs because EOF has |
16 | always caused the recv thread to exit cleanly. | 16 | always caused the recv thread to exit cleanly. |
17 | 17 | ||
18 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | 18 | Signed-off-by: Fabiano Rosas <farosas@suse.de> |
19 | --- | 19 | --- |
20 | migration/multifd.c | 34 +++++++++++++++++++++++++++++++++- | 20 | migration/multifd.c | 34 +++++++++++++++++++++++++++++++++- |
21 | migration/tls.c | 5 +++++ | 21 | migration/tls.c | 5 +++++ |
22 | migration/tls.h | 2 +- | 22 | migration/tls.h | 2 +- |
23 | 3 files changed, 39 insertions(+), 2 deletions(-) | 23 | 3 files changed, 39 insertions(+), 2 deletions(-) |
24 | 24 | ||
25 | diff --git a/migration/multifd.c b/migration/multifd.c | 25 | diff --git a/migration/multifd.c b/migration/multifd.c |
26 | index XXXXXXX..XXXXXXX 100644 | 26 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/migration/multifd.c | 27 | --- a/migration/multifd.c |
28 | +++ b/migration/multifd.c | 28 | +++ b/migration/multifd.c |
29 | @@ -XXX,XX +XXX,XX @@ void multifd_send_shutdown(void) | 29 | @@ -XXX,XX +XXX,XX @@ void multifd_send_shutdown(void) |
30 | return; | 30 | return; |
31 | } | 31 | } |
32 | 32 | ||
33 | + for (i = 0; i < migrate_multifd_channels(); i++) { | 33 | + for (i = 0; i < migrate_multifd_channels(); i++) { |
34 | + MultiFDSendParams *p = &multifd_send_state->params[i]; | 34 | + MultiFDSendParams *p = &multifd_send_state->params[i]; |
35 | + | 35 | + |
36 | + /* thread_created implies the TLS handshake has succeeded */ | 36 | + /* thread_created implies the TLS handshake has succeeded */ |
37 | + if (p->tls_thread_created && p->thread_created) { | 37 | + if (p->tls_thread_created && p->thread_created) { |
38 | + Error *local_err = NULL; | 38 | + Error *local_err = NULL; |
39 | + /* | 39 | + /* |
40 | + * The destination expects the TLS session to always be | 40 | + * The destination expects the TLS session to always be |
41 | + * properly terminated. This helps to detect a premature | 41 | + * properly terminated. This helps to detect a premature |
42 | + * termination in the middle of the stream. Note that | 42 | + * termination in the middle of the stream. Note that |
43 | + * older QEMUs always break the connection on the source | 43 | + * older QEMUs always break the connection on the source |
44 | + * and the destination always sees | 44 | + * and the destination always sees |
45 | + * GNUTLS_E_PREMATURE_TERMINATION. | 45 | + * GNUTLS_E_PREMATURE_TERMINATION. |
46 | + */ | 46 | + */ |
47 | + migration_tls_channel_end(p->c, &local_err); | 47 | + migration_tls_channel_end(p->c, &local_err); |
48 | + | 48 | + |
49 | + if (local_err) { | 49 | + if (local_err) { |
50 | + /* | 50 | + /* |
51 | + * The above can fail with broken pipe due to a | 51 | + * The above can fail with broken pipe due to a |
52 | + * previous migration error, ignore the error. | 52 | + * previous migration error, ignore the error. |
53 | + */ | 53 | + */ |
54 | + assert(migration_has_failed(migrate_get_current())); | 54 | + assert(migration_has_failed(migrate_get_current())); |
55 | + } | 55 | + } |
56 | + } | 56 | + } |
57 | + } | 57 | + } |
58 | + | 58 | + |
59 | multifd_send_terminate_threads(); | 59 | multifd_send_terminate_threads(); |
60 | 60 | ||
61 | for (i = 0; i < migrate_multifd_channels(); i++) { | 61 | for (i = 0; i < migrate_multifd_channels(); i++) { |
62 | @@ -XXX,XX +XXX,XX @@ static void *multifd_recv_thread(void *opaque) | 62 | @@ -XXX,XX +XXX,XX @@ static void *multifd_recv_thread(void *opaque) |
63 | 63 | ||
64 | ret = qio_channel_read_all_eof(p->c, (void *)p->packet, | 64 | ret = qio_channel_read_all_eof(p->c, (void *)p->packet, |
65 | p->packet_len, &local_err); | 65 | p->packet_len, &local_err); |
66 | - if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */ | 66 | - if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */ |
67 | + if (!ret) { | 67 | + if (!ret) { |
68 | + /* EOF */ | 68 | + /* EOF */ |
69 | + assert(!local_err); | 69 | + assert(!local_err); |
70 | + break; | 70 | + break; |
71 | + } | 71 | + } |
72 | + | 72 | + |
73 | + if (ret == -1) { | 73 | + if (ret == -1) { |
74 | break; | 74 | break; |
75 | } | 75 | } |
76 | 76 | ||
77 | diff --git a/migration/tls.c b/migration/tls.c | 77 | diff --git a/migration/tls.c b/migration/tls.c |
78 | index XXXXXXX..XXXXXXX 100644 | 78 | index XXXXXXX..XXXXXXX 100644 |
79 | --- a/migration/tls.c | 79 | --- a/migration/tls.c |
80 | +++ b/migration/tls.c | 80 | +++ b/migration/tls.c |
81 | @@ -XXX,XX +XXX,XX @@ void migration_tls_channel_connect(MigrationState *s, | 81 | @@ -XXX,XX +XXX,XX @@ void migration_tls_channel_connect(MigrationState *s, |
82 | NULL); | 82 | NULL); |
83 | } | 83 | } |
84 | 84 | ||
85 | +void migration_tls_channel_end(QIOChannel *ioc, Error **errp) | 85 | +void migration_tls_channel_end(QIOChannel *ioc, Error **errp) |
86 | +{ | 86 | +{ |
87 | + qio_channel_tls_bye(QIO_CHANNEL_TLS(ioc), errp); | 87 | + qio_channel_tls_bye(QIO_CHANNEL_TLS(ioc), errp); |
88 | +} | 88 | +} |
89 | + | 89 | + |
90 | bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) | 90 | bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) |
91 | { | 91 | { |
92 | if (!migrate_tls()) { | 92 | if (!migrate_tls()) { |
93 | diff --git a/migration/tls.h b/migration/tls.h | 93 | diff --git a/migration/tls.h b/migration/tls.h |
94 | index XXXXXXX..XXXXXXX 100644 | 94 | index XXXXXXX..XXXXXXX 100644 |
95 | --- a/migration/tls.h | 95 | --- a/migration/tls.h |
96 | +++ b/migration/tls.h | 96 | +++ b/migration/tls.h |
97 | @@ -XXX,XX +XXX,XX @@ void migration_tls_channel_connect(MigrationState *s, | 97 | @@ -XXX,XX +XXX,XX @@ void migration_tls_channel_connect(MigrationState *s, |
98 | QIOChannel *ioc, | 98 | QIOChannel *ioc, |
99 | const char *hostname, | 99 | const char *hostname, |
100 | Error **errp); | 100 | Error **errp); |
101 | - | 101 | - |
102 | +void migration_tls_channel_end(QIOChannel *ioc, Error **errp); | 102 | +void migration_tls_channel_end(QIOChannel *ioc, Error **errp); |
103 | /* Whether the QIO channel requires further TLS handshake? */ | 103 | /* Whether the QIO channel requires further TLS handshake? */ |
104 | bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); | 104 | bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); |
105 | 105 | ||
106 | -- | 106 | -- |
107 | 2.35.3 | 107 | 2.35.3 | diff view generated by jsdifflib |
1 | We're currently only checking the QEMUFile error after | 1 | We're currently only checking the QEMUFile error after |
---|---|---|---|
2 | qemu_loadvm_state(). Check the migration error as well to avoid | 2 | qemu_loadvm_state(). Check the migration error as well to avoid |
3 | missing errors that might be set by the multifd recv thread. | 3 | missing errors that might be set by the multifd recv thread. |
4 | 4 | ||
5 | This doesn't break compat between 9.2 and 10.0 because 9.2 still has | 5 | This doesn't break compat between 9.2 and 10.0 because 9.2 still has |
6 | the multifd recv threads stuck at sync when the source channel shuts | 6 | the multifd recv threads stuck at sync when the source channel shuts |
7 | down. I.e. it doesn't have commit 1d457daf86 ("migration/multifd: | 7 | down. I.e. it doesn't have commit 1d457daf86 ("migration/multifd: |
8 | Further remove the SYNC on complete"). QEMU versions with that commit | 8 | Further remove the SYNC on complete"). QEMU versions with that commit |
9 | will have compat broken with versions containing this commit. This is | 9 | will have compat broken with versions containing this commit. This is |
10 | not an issue because both will be present in 10.0, but development | 10 | not an issue because both will be present in 10.0, but development |
11 | trees might see a migration error. | 11 | trees might see a migration error. |
12 | 12 | ||
13 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | 13 | Signed-off-by: Fabiano Rosas <farosas@suse.de> |
14 | --- | 14 | --- |
15 | migration/savevm.c | 6 +++++- | 15 | migration/savevm.c | 6 +++++- |
16 | 1 file changed, 5 insertions(+), 1 deletion(-) | 16 | 1 file changed, 5 insertions(+), 1 deletion(-) |
17 | 17 | ||
18 | diff --git a/migration/savevm.c b/migration/savevm.c | 18 | diff --git a/migration/savevm.c b/migration/savevm.c |
19 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/migration/savevm.c | 20 | --- a/migration/savevm.c |
21 | +++ b/migration/savevm.c | 21 | +++ b/migration/savevm.c |
22 | @@ -XXX,XX +XXX,XX @@ int qemu_loadvm_state(QEMUFile *f) | 22 | @@ -XXX,XX +XXX,XX @@ int qemu_loadvm_state(QEMUFile *f) |
23 | 23 | ||
24 | /* When reaching here, it must be precopy */ | 24 | /* When reaching here, it must be precopy */ |
25 | if (ret == 0) { | 25 | if (ret == 0) { |
26 | - ret = qemu_file_get_error(f); | 26 | - ret = qemu_file_get_error(f); |
27 | + if (migrate_has_error(migrate_get_current())) { | 27 | + if (migrate_has_error(migrate_get_current())) { |
28 | + ret = -EINVAL; | 28 | + ret = -EINVAL; |
29 | + } else { | 29 | + } else { |
30 | + ret = qemu_file_get_error(f); | 30 | + ret = qemu_file_get_error(f); |
31 | + } | 31 | + } |
32 | } | 32 | } |
33 | 33 | ||
34 | /* | 34 | /* |
35 | -- | 35 | -- |
36 | 2.35.3 | 36 | 2.35.3 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | The correct way of calling qcrypto_tls_session_handshake() requires | |
2 | calling qcrypto_tls_session_get_handshake_status() right after it so | ||
3 | there's no reason to have a separate method. | ||
4 | |||
5 | Refactor qcrypto_tls_session_handshake() to inform the status in its | ||
6 | own return value and alter the callers accordingly. | ||
7 | |||
8 | No functional change. | ||
9 | |||
10 | Suggested-by: Daniel P. Berrangé <berrange@redhat.com> | ||
11 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | ||
12 | --- | ||
13 | crypto/tlssession.c | 64 +++++++++++------------------ | ||
14 | include/crypto/tlssession.h | 32 ++++----------- | ||
15 | io/channel-tls.c | 7 ++-- | ||
16 | tests/unit/test-crypto-tlssession.c | 12 ++---- | ||
17 | 4 files changed, 39 insertions(+), 76 deletions(-) | ||
18 | |||
19 | diff --git a/crypto/tlssession.c b/crypto/tlssession.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/crypto/tlssession.c | ||
22 | +++ b/crypto/tlssession.c | ||
23 | @@ -XXX,XX +XXX,XX @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, | ||
24 | Error **errp) | ||
25 | { | ||
26 | int ret = gnutls_handshake(session->handle); | ||
27 | - if (ret == 0) { | ||
28 | + if (!ret) { | ||
29 | session->handshakeComplete = true; | ||
30 | - } else { | ||
31 | - if (ret == GNUTLS_E_INTERRUPTED || | ||
32 | - ret == GNUTLS_E_AGAIN) { | ||
33 | - ret = 1; | ||
34 | - } else { | ||
35 | - if (session->rerr || session->werr) { | ||
36 | - error_setg(errp, "TLS handshake failed: %s: %s", | ||
37 | - gnutls_strerror(ret), | ||
38 | - error_get_pretty(session->rerr ? | ||
39 | - session->rerr : session->werr)); | ||
40 | - } else { | ||
41 | - error_setg(errp, "TLS handshake failed: %s", | ||
42 | - gnutls_strerror(ret)); | ||
43 | - } | ||
44 | - ret = -1; | ||
45 | - } | ||
46 | - } | ||
47 | - error_free(session->rerr); | ||
48 | - error_free(session->werr); | ||
49 | - session->rerr = session->werr = NULL; | ||
50 | - | ||
51 | - return ret; | ||
52 | -} | ||
53 | - | ||
54 | - | ||
55 | -QCryptoTLSSessionHandshakeStatus | ||
56 | -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) | ||
57 | -{ | ||
58 | - if (session->handshakeComplete) { | ||
59 | return QCRYPTO_TLS_HANDSHAKE_COMPLETE; | ||
60 | - } else if (gnutls_record_get_direction(session->handle) == 0) { | ||
61 | - return QCRYPTO_TLS_HANDSHAKE_RECVING; | ||
62 | + } | ||
63 | + | ||
64 | + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { | ||
65 | + int direction = gnutls_record_get_direction(session->handle); | ||
66 | + return direction ? QCRYPTO_TLS_HANDSHAKE_SENDING : | ||
67 | + QCRYPTO_TLS_HANDSHAKE_RECVING; | ||
68 | + } | ||
69 | + | ||
70 | + if (session->rerr || session->werr) { | ||
71 | + error_setg(errp, "TLS handshake failed: %s: %s", | ||
72 | + gnutls_strerror(ret), | ||
73 | + error_get_pretty(session->rerr ? | ||
74 | + session->rerr : session->werr)); | ||
75 | } else { | ||
76 | - return QCRYPTO_TLS_HANDSHAKE_SENDING; | ||
77 | + error_setg(errp, "TLS handshake failed: %s", | ||
78 | + gnutls_strerror(ret)); | ||
79 | } | ||
80 | + | ||
81 | + error_free(session->rerr); | ||
82 | + error_free(session->werr); | ||
83 | + session->rerr = session->werr = NULL; | ||
84 | + | ||
85 | + return -1; | ||
86 | } | ||
87 | |||
88 | + | ||
89 | int | ||
90 | qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) | ||
91 | { | ||
92 | @@ -XXX,XX +XXX,XX @@ qcrypto_tls_session_check_pending(QCryptoTLSSession *session) | ||
93 | int | ||
94 | qcrypto_tls_session_handshake(QCryptoTLSSession *sess, | ||
95 | Error **errp) | ||
96 | -{ | ||
97 | - error_setg(errp, "TLS requires GNUTLS support"); | ||
98 | - return -1; | ||
99 | -} | ||
100 | - | ||
101 | - | ||
102 | -QCryptoTLSSessionHandshakeStatus | ||
103 | -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) | ||
104 | { | ||
105 | return QCRYPTO_TLS_HANDSHAKE_COMPLETE; | ||
106 | } | ||
107 | diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h | ||
108 | index XXXXXXX..XXXXXXX 100644 | ||
109 | --- a/include/crypto/tlssession.h | ||
110 | +++ b/include/crypto/tlssession.h | ||
111 | @@ -XXX,XX +XXX,XX @@ | ||
112 | * GINT_TO_POINTER(fd)); | ||
113 | * | ||
114 | * while (1) { | ||
115 | - * if (qcrypto_tls_session_handshake(sess, errp) < 0) { | ||
116 | + * int ret = qcrypto_tls_session_handshake(sess, errp); | ||
117 | + * | ||
118 | + * if (ret < 0) { | ||
119 | * qcrypto_tls_session_free(sess); | ||
120 | * return -1; | ||
121 | * } | ||
122 | * | ||
123 | - * switch(qcrypto_tls_session_get_handshake_status(sess)) { | ||
124 | + * switch(ret) { | ||
125 | * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: | ||
126 | * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { | ||
127 | * qcrypto_tls_session_free(sess); | ||
128 | @@ -XXX,XX +XXX,XX @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) | ||
129 | * | ||
130 | * Validate the peer's credentials after a successful | ||
131 | * TLS handshake. It is an error to call this before | ||
132 | - * qcrypto_tls_session_get_handshake_status() returns | ||
133 | + * qcrypto_tls_session_handshake() returns | ||
134 | * QCRYPTO_TLS_HANDSHAKE_COMPLETE | ||
135 | * | ||
136 | * Returns 0 if the credentials validated, -1 on error | ||
137 | @@ -XXX,XX +XXX,XX @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, | ||
138 | * registered with qcrypto_tls_session_set_callbacks() | ||
139 | * | ||
140 | * It is an error to call this before | ||
141 | - * qcrypto_tls_session_get_handshake_status() returns | ||
142 | + * qcrypto_tls_session_handshake() returns | ||
143 | * QCRYPTO_TLS_HANDSHAKE_COMPLETE | ||
144 | * | ||
145 | * Returns: the number of bytes sent, | ||
146 | @@ -XXX,XX +XXX,XX @@ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, | ||
147 | * opposed to an error. | ||
148 | * | ||
149 | * It is an error to call this before | ||
150 | - * qcrypto_tls_session_get_handshake_status() returns | ||
151 | + * qcrypto_tls_session_handshake() returns | ||
152 | * QCRYPTO_TLS_HANDSHAKE_COMPLETE | ||
153 | * | ||
154 | * Returns: the number of bytes received, | ||
155 | @@ -XXX,XX +XXX,XX @@ size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); | ||
156 | * the underlying data channel is non-blocking, then | ||
157 | * this method may return control before the handshake | ||
158 | * is complete. On non-blocking channels the | ||
159 | - * qcrypto_tls_session_get_handshake_status() method | ||
160 | - * should be used to determine whether the handshake | ||
161 | + * return value determines whether the handshake | ||
162 | * has completed, or is waiting to send or receive | ||
163 | * data. In the latter cases, the caller should setup | ||
164 | * an event loop watch and call this method again | ||
165 | @@ -XXX,XX +XXX,XX @@ typedef enum { | ||
166 | QCRYPTO_TLS_HANDSHAKE_RECVING, | ||
167 | } QCryptoTLSSessionHandshakeStatus; | ||
168 | |||
169 | -/** | ||
170 | - * qcrypto_tls_session_get_handshake_status: | ||
171 | - * @sess: the TLS session object | ||
172 | - * | ||
173 | - * Check the status of the TLS handshake. This | ||
174 | - * is used with non-blocking data channels to | ||
175 | - * determine whether the handshake is waiting | ||
176 | - * to send or receive further data to/from the | ||
177 | - * remote peer. | ||
178 | - * | ||
179 | - * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE | ||
180 | - * it is permitted to send/receive payload data on | ||
181 | - * the channel | ||
182 | - */ | ||
183 | -QCryptoTLSSessionHandshakeStatus | ||
184 | -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); | ||
185 | - | ||
186 | typedef enum { | ||
187 | QCRYPTO_TLS_BYE_COMPLETE, | ||
188 | QCRYPTO_TLS_BYE_SENDING, | ||
189 | diff --git a/io/channel-tls.c b/io/channel-tls.c | ||
190 | index XXXXXXX..XXXXXXX 100644 | ||
191 | --- a/io/channel-tls.c | ||
192 | +++ b/io/channel-tls.c | ||
193 | @@ -XXX,XX +XXX,XX @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, | ||
194 | GMainContext *context) | ||
195 | { | ||
196 | Error *err = NULL; | ||
197 | - QCryptoTLSSessionHandshakeStatus status; | ||
198 | + int status; | ||
199 | |||
200 | - if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { | ||
201 | + status = qcrypto_tls_session_handshake(ioc->session, &err); | ||
202 | + | ||
203 | + if (status < 0) { | ||
204 | trace_qio_channel_tls_handshake_fail(ioc); | ||
205 | qio_task_set_error(task, err); | ||
206 | qio_task_complete(task); | ||
207 | return; | ||
208 | } | ||
209 | |||
210 | - status = qcrypto_tls_session_get_handshake_status(ioc->session); | ||
211 | if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
212 | trace_qio_channel_tls_handshake_complete(ioc); | ||
213 | if (qcrypto_tls_session_check_credentials(ioc->session, | ||
214 | diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c | ||
215 | index XXXXXXX..XXXXXXX 100644 | ||
216 | --- a/tests/unit/test-crypto-tlssession.c | ||
217 | +++ b/tests/unit/test-crypto-tlssession.c | ||
218 | @@ -XXX,XX +XXX,XX @@ static void test_crypto_tls_session_psk(void) | ||
219 | rv = qcrypto_tls_session_handshake(serverSess, | ||
220 | &error_abort); | ||
221 | g_assert(rv >= 0); | ||
222 | - if (qcrypto_tls_session_get_handshake_status(serverSess) == | ||
223 | - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
224 | + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
225 | serverShake = true; | ||
226 | } | ||
227 | } | ||
228 | @@ -XXX,XX +XXX,XX @@ static void test_crypto_tls_session_psk(void) | ||
229 | rv = qcrypto_tls_session_handshake(clientSess, | ||
230 | &error_abort); | ||
231 | g_assert(rv >= 0); | ||
232 | - if (qcrypto_tls_session_get_handshake_status(clientSess) == | ||
233 | - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
234 | + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
235 | clientShake = true; | ||
236 | } | ||
237 | } | ||
238 | @@ -XXX,XX +XXX,XX @@ static void test_crypto_tls_session_x509(const void *opaque) | ||
239 | rv = qcrypto_tls_session_handshake(serverSess, | ||
240 | &error_abort); | ||
241 | g_assert(rv >= 0); | ||
242 | - if (qcrypto_tls_session_get_handshake_status(serverSess) == | ||
243 | - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
244 | + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
245 | serverShake = true; | ||
246 | } | ||
247 | } | ||
248 | @@ -XXX,XX +XXX,XX @@ static void test_crypto_tls_session_x509(const void *opaque) | ||
249 | rv = qcrypto_tls_session_handshake(clientSess, | ||
250 | &error_abort); | ||
251 | g_assert(rv >= 0); | ||
252 | - if (qcrypto_tls_session_get_handshake_status(clientSess) == | ||
253 | - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
254 | + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { | ||
255 | clientShake = true; | ||
256 | } | ||
257 | } | ||
258 | -- | ||
259 | 2.35.3 | ||
260 | |||
261 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | We want to pass flags into qio_channel_tls_readv() but some functions | ||
2 | along the way don't take a flags argument. Plumb the flags through. | ||
1 | 3 | ||
4 | No functional change. | ||
5 | |||
6 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | ||
7 | --- | ||
8 | hw/remote/mpqemu-link.c | 2 +- | ||
9 | include/io/channel.h | 6 ++++++ | ||
10 | io/channel.c | 13 +++++++++---- | ||
11 | migration/multifd.c | 2 +- | ||
12 | tools/i386/qemu-vmsr-helper.c | 3 ++- | ||
13 | util/vhost-user-server.c | 2 +- | ||
14 | 6 files changed, 20 insertions(+), 8 deletions(-) | ||
15 | |||
16 | diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/hw/remote/mpqemu-link.c | ||
19 | +++ b/hw/remote/mpqemu-link.c | ||
20 | @@ -XXX,XX +XXX,XX @@ static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds, | ||
21 | bql_unlock(); | ||
22 | } | ||
23 | |||
24 | - ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, errp); | ||
25 | + ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, 0, errp); | ||
26 | |||
27 | if (drop_bql && !iothread && !qemu_in_coroutine()) { | ||
28 | bql_lock(); | ||
29 | diff --git a/include/io/channel.h b/include/io/channel.h | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/include/io/channel.h | ||
32 | +++ b/include/io/channel.h | ||
33 | @@ -XXX,XX +XXX,XX @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, | ||
34 | * @ioc: the channel object | ||
35 | * @iov: the array of memory regions to read data into | ||
36 | * @niov: the length of the @iov array | ||
37 | + * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) | ||
38 | * @errp: pointer to a NULL-initialized error object | ||
39 | * | ||
40 | * Read data from the IO channel, storing it in the | ||
41 | @@ -XXX,XX +XXX,XX @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, | ||
42 | int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, | ||
43 | const struct iovec *iov, | ||
44 | size_t niov, | ||
45 | + int flags, | ||
46 | Error **errp); | ||
47 | |||
48 | /** | ||
49 | @@ -XXX,XX +XXX,XX @@ ssize_t qio_channel_write(QIOChannel *ioc, | ||
50 | * @ioc: the channel object | ||
51 | * @buf: the memory region to read data into | ||
52 | * @buflen: the number of bytes to @buf | ||
53 | + * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) | ||
54 | * @errp: pointer to a NULL-initialized error object | ||
55 | * | ||
56 | * Reads @buflen bytes into @buf, possibly blocking or (if the | ||
57 | @@ -XXX,XX +XXX,XX @@ ssize_t qio_channel_write(QIOChannel *ioc, | ||
58 | int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, | ||
59 | char *buf, | ||
60 | size_t buflen, | ||
61 | + int flags, | ||
62 | Error **errp); | ||
63 | |||
64 | /** | ||
65 | @@ -XXX,XX +XXX,XX @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, | ||
66 | * @niov: the length of the @iov array | ||
67 | * @fds: an array of file handles to read | ||
68 | * @nfds: number of file handles in @fds | ||
69 | + * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) | ||
70 | * @errp: pointer to a NULL-initialized error object | ||
71 | * | ||
72 | * | ||
73 | @@ -XXX,XX +XXX,XX @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, | ||
74 | const struct iovec *iov, | ||
75 | size_t niov, | ||
76 | int **fds, size_t *nfds, | ||
77 | + int flags, | ||
78 | Error **errp); | ||
79 | |||
80 | /** | ||
81 | diff --git a/io/channel.c b/io/channel.c | ||
82 | index XXXXXXX..XXXXXXX 100644 | ||
83 | --- a/io/channel.c | ||
84 | +++ b/io/channel.c | ||
85 | @@ -XXX,XX +XXX,XX @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, | ||
86 | int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, | ||
87 | const struct iovec *iov, | ||
88 | size_t niov, | ||
89 | + int flags, | ||
90 | Error **errp) | ||
91 | { | ||
92 | - return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp); | ||
93 | + return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, flags, | ||
94 | + errp); | ||
95 | } | ||
96 | |||
97 | int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, | ||
98 | @@ -XXX,XX +XXX,XX @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, | ||
99 | const struct iovec *iov, | ||
100 | size_t niov, | ||
101 | int **fds, size_t *nfds, | ||
102 | + int flags, | ||
103 | Error **errp) | ||
104 | { | ||
105 | int ret = -1; | ||
106 | @@ -XXX,XX +XXX,XX @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, | ||
107 | while ((nlocal_iov > 0) || local_fds) { | ||
108 | ssize_t len; | ||
109 | len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds, | ||
110 | - local_nfds, 0, errp); | ||
111 | + local_nfds, flags, errp); | ||
112 | if (len == QIO_CHANNEL_ERR_BLOCK) { | ||
113 | if (qemu_in_coroutine()) { | ||
114 | qio_channel_yield(ioc, G_IO_IN); | ||
115 | @@ -XXX,XX +XXX,XX @@ int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, | ||
116 | int **fds, size_t *nfds, | ||
117 | Error **errp) | ||
118 | { | ||
119 | - int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp); | ||
120 | + int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, 0, | ||
121 | + errp); | ||
122 | |||
123 | if (ret == 0) { | ||
124 | error_setg(errp, "Unexpected end-of-file before all data were read"); | ||
125 | @@ -XXX,XX +XXX,XX @@ ssize_t qio_channel_write(QIOChannel *ioc, | ||
126 | int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, | ||
127 | char *buf, | ||
128 | size_t buflen, | ||
129 | + int flags, | ||
130 | Error **errp) | ||
131 | { | ||
132 | struct iovec iov = { .iov_base = buf, .iov_len = buflen }; | ||
133 | - return qio_channel_readv_all_eof(ioc, &iov, 1, errp); | ||
134 | + return qio_channel_readv_all_eof(ioc, &iov, 1, flags, errp); | ||
135 | } | ||
136 | |||
137 | |||
138 | diff --git a/migration/multifd.c b/migration/multifd.c | ||
139 | index XXXXXXX..XXXXXXX 100644 | ||
140 | --- a/migration/multifd.c | ||
141 | +++ b/migration/multifd.c | ||
142 | @@ -XXX,XX +XXX,XX @@ static void *multifd_recv_thread(void *opaque) | ||
143 | } | ||
144 | |||
145 | ret = qio_channel_read_all_eof(p->c, (void *)p->packet, | ||
146 | - p->packet_len, &local_err); | ||
147 | + p->packet_len, 0, &local_err); | ||
148 | if (!ret) { | ||
149 | /* EOF */ | ||
150 | assert(!local_err); | ||
151 | diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c | ||
152 | index XXXXXXX..XXXXXXX 100644 | ||
153 | --- a/tools/i386/qemu-vmsr-helper.c | ||
154 | +++ b/tools/i386/qemu-vmsr-helper.c | ||
155 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn vh_co_entry(void *opaque) | ||
156 | * Only RAPL MSR in rapl-msr-index.h is allowed | ||
157 | */ | ||
158 | r = qio_channel_read_all_eof(QIO_CHANNEL(client->ioc), | ||
159 | - (char *) &request, sizeof(request), &local_err); | ||
160 | + (char *) &request, sizeof(request), 0, | ||
161 | + &local_err); | ||
162 | if (r <= 0) { | ||
163 | break; | ||
164 | } | ||
165 | diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c | ||
166 | index XXXXXXX..XXXXXXX 100644 | ||
167 | --- a/util/vhost-user-server.c | ||
168 | +++ b/util/vhost-user-server.c | ||
169 | @@ -XXX,XX +XXX,XX @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) | ||
170 | .iov_len = vmsg->size, | ||
171 | }; | ||
172 | if (vmsg->size) { | ||
173 | - rc = qio_channel_readv_all_eof(ioc, &iov_payload, 1, &local_err); | ||
174 | + rc = qio_channel_readv_all_eof(ioc, &iov_payload, 1, 0, &local_err); | ||
175 | if (rc != 1) { | ||
176 | if (local_err) { | ||
177 | error_report_err(local_err); | ||
178 | -- | ||
179 | 2.35.3 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | Add a read flag that can inform a channel that it's ok to receive an | ||
2 | EOF at any moment. Channels that have some form of strict EOF | ||
3 | tracking, such as TLS session termination, may choose to ignore EOF | ||
4 | errors with the use of this flag. | ||
1 | 5 | ||
6 | This is being added for compatibility with older migration streams | ||
7 | that do not include a TLS termination step. | ||
8 | |||
9 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | ||
10 | --- | ||
11 | include/io/channel.h | 1 + | ||
12 | io/channel-tls.c | 1 + | ||
13 | 2 files changed, 2 insertions(+) | ||
14 | |||
15 | diff --git a/include/io/channel.h b/include/io/channel.h | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/include/io/channel.h | ||
18 | +++ b/include/io/channel.h | ||
19 | @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, | ||
20 | #define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1 | ||
21 | |||
22 | #define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 | ||
23 | +#define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2 | ||
24 | |||
25 | typedef enum QIOChannelFeature QIOChannelFeature; | ||
26 | |||
27 | diff --git a/io/channel-tls.c b/io/channel-tls.c | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/io/channel-tls.c | ||
30 | +++ b/io/channel-tls.c | ||
31 | @@ -XXX,XX +XXX,XX @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, | ||
32 | tioc->session, | ||
33 | iov[i].iov_base, | ||
34 | iov[i].iov_len, | ||
35 | + flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF || | ||
36 | qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, | ||
37 | errp); | ||
38 | if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { | ||
39 | -- | ||
40 | 2.35.3 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | We're currently changing the way the source multifd migration handles | ||
2 | the shutdown of the multifd channels when TLS is in use to perform a | ||
3 | clean termination by calling gnutls_bye(). | ||
1 | 4 | ||
5 | Older src QEMUs will always close the channel without terminating the | ||
6 | TLS session. New dst QEMUs treat an unclean termination as an | ||
7 | error. Due to synchronization conditions, src QEMUs 9.1 and 9.2 are an | ||
8 | exception and can put the destination in a condition where it ignores | ||
9 | the unclean termination. For src QEMUs older than 9.1, we'll need a | ||
10 | compat property on the destination to inform that the src does not | ||
11 | terminate the TLS session. | ||
12 | |||
13 | Add multifd_clean_tls_termination (default true) that can be switched | ||
14 | on the destination whenever a src QEMU <9.1 is in use. | ||
15 | |||
16 | Signed-off-by: Fabiano Rosas <farosas@suse.de> | ||
17 | --- | ||
18 | migration/migration.h | 33 +++++++++++++++++++++++++++++++++ | ||
19 | migration/multifd.c | 8 +++++++- | ||
20 | migration/multifd.h | 2 ++ | ||
21 | migration/options.c | 2 ++ | ||
22 | 4 files changed, 44 insertions(+), 1 deletion(-) | ||
23 | |||
24 | diff --git a/migration/migration.h b/migration/migration.h | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/migration/migration.h | ||
27 | +++ b/migration/migration.h | ||
28 | @@ -XXX,XX +XXX,XX @@ struct MigrationState { | ||
29 | * Default value is false. (since 8.1) | ||
30 | */ | ||
31 | bool multifd_flush_after_each_section; | ||
32 | + | ||
33 | + /* | ||
34 | + * This variable only makes sense when set on the machine that is | ||
35 | + * the destination of a multifd migration with TLS enabled. It | ||
36 | + * affects the behavior of the last send->recv iteration with | ||
37 | + * regards to termination of the TLS session. | ||
38 | + * | ||
39 | + * When set: | ||
40 | + * | ||
41 | + * - the destination QEMU instance can expect to never get a | ||
42 | + * GNUTLS_E_PREMATURE_TERMINATION error. Manifested as the error | ||
43 | + * message: "The TLS connection was non-properly terminated". | ||
44 | + * | ||
45 | + * When clear: | ||
46 | + * | ||
47 | + * - the destination QEMU instance can expect to see a | ||
48 | + * GNUTLS_E_PREMATURE_TERMINATION error in any multifd channel | ||
49 | + * whenever the last recv() call of that channel happens after | ||
50 | + * the source QEMU instance has already issued shutdown() on the | ||
51 | + * channel. | ||
52 | + * | ||
53 | + * Commit 637280aeb2 (since 9.1) introduced a side effect that | ||
54 | + * causes the destination instance to not be affected by the | ||
55 | + * premature termination, while commit 1d457daf86 (since 10.0) | ||
56 | + * causes the premature termination condition to be once again | ||
57 | + * reachable. | ||
58 | + * | ||
59 | + * NOTE: Regardless of the state of this option, a premature | ||
60 | + * termination of the TLS connection might happen due to error at | ||
61 | + * any moment prior to the last send->recv iteration. | ||
62 | + */ | ||
63 | + bool multifd_clean_tls_termination; | ||
64 | + | ||
65 | /* | ||
66 | * This decides the size of guest memory chunk that will be used | ||
67 | * to track dirty bitmap clearing. The size of memory chunk will | ||
68 | diff --git a/migration/multifd.c b/migration/multifd.c | ||
69 | index XXXXXXX..XXXXXXX 100644 | ||
70 | --- a/migration/multifd.c | ||
71 | +++ b/migration/multifd.c | ||
72 | @@ -XXX,XX +XXX,XX @@ void multifd_recv_sync_main(void) | ||
73 | |||
74 | static void *multifd_recv_thread(void *opaque) | ||
75 | { | ||
76 | + MigrationState *s = migrate_get_current(); | ||
77 | MultiFDRecvParams *p = opaque; | ||
78 | Error *local_err = NULL; | ||
79 | bool use_packets = multifd_use_packets(); | ||
80 | @@ -XXX,XX +XXX,XX @@ static void *multifd_recv_thread(void *opaque) | ||
81 | trace_multifd_recv_thread_start(p->id); | ||
82 | rcu_register_thread(); | ||
83 | |||
84 | + if (!s->multifd_clean_tls_termination) { | ||
85 | + p->read_flags = QIO_CHANNEL_READ_FLAG_RELAXED_EOF; | ||
86 | + } | ||
87 | + | ||
88 | while (true) { | ||
89 | uint32_t flags = 0; | ||
90 | bool has_data = false; | ||
91 | @@ -XXX,XX +XXX,XX @@ static void *multifd_recv_thread(void *opaque) | ||
92 | } | ||
93 | |||
94 | ret = qio_channel_read_all_eof(p->c, (void *)p->packet, | ||
95 | - p->packet_len, 0, &local_err); | ||
96 | + p->packet_len, p->read_flags, | ||
97 | + &local_err); | ||
98 | if (!ret) { | ||
99 | /* EOF */ | ||
100 | assert(!local_err); | ||
101 | diff --git a/migration/multifd.h b/migration/multifd.h | ||
102 | index XXXXXXX..XXXXXXX 100644 | ||
103 | --- a/migration/multifd.h | ||
104 | +++ b/migration/multifd.h | ||
105 | @@ -XXX,XX +XXX,XX @@ typedef struct { | ||
106 | uint32_t zero_num; | ||
107 | /* used for de-compression methods */ | ||
108 | void *compress_data; | ||
109 | + /* Flags for the QIOChannel */ | ||
110 | + int read_flags; | ||
111 | } MultiFDRecvParams; | ||
112 | |||
113 | typedef struct { | ||
114 | diff --git a/migration/options.c b/migration/options.c | ||
115 | index XXXXXXX..XXXXXXX 100644 | ||
116 | --- a/migration/options.c | ||
117 | +++ b/migration/options.c | ||
118 | @@ -XXX,XX +XXX,XX @@ const Property migration_properties[] = { | ||
119 | clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT), | ||
120 | DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState, | ||
121 | preempt_pre_7_2, false), | ||
122 | + DEFINE_PROP_BOOL("multifd-clean-tls-termination", MigrationState, | ||
123 | + multifd_clean_tls_termination, true), | ||
124 | |||
125 | /* Migration parameters */ | ||
126 | DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, | ||
127 | -- | ||
128 | 2.35.3 | diff view generated by jsdifflib |