1 /*
2 * QEMU Block driver for NBD
3 *
4 * Copyright (c) 2021 Virtuozzo International GmbH.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "qemu/osdep.h"
26 #include "trace.h"
27
28 #include "block/nbd.h"
29
30 #include "qapi/qapi-visit-sockets.h"
31 #include "qapi/clone-visitor.h"
32 #include "qemu/coroutine.h"
33
34 #include "nbd/nbd-internal.h"
35
36 struct NBDClientConnection {
37 /* Initialization constants, never change */
38 SocketAddress *saddr; /* address to connect to */
39 QCryptoTLSCreds *tlscreds;
40 char *tlshostname;
41 NBDExportInfo initial_info;
42 bool do_negotiation;
43 bool do_retry;
44
45 QemuMutex mutex;
46
47 NBDExportInfo updated_info;
48 /*
49 * @sioc represents a successful result. While thread is running, @sioc is
50 * used only by thread and not protected by mutex. When thread is not
51 * running, @sioc is stolen by nbd_co_establish_connection() under mutex.
52 */
53 QIOChannelSocket *sioc;
54 QIOChannel *ioc;
55 /*
56 * @err represents previous attempt. It may be copied by
57 * nbd_co_establish_connection() when it reports failure.
58 */
59 Error *err;
60
61 /* All further fields are accessed only under mutex */
62 bool running; /* thread is running now */
63 bool detached; /* thread is detached and should cleanup the state */
64
65 /*
66 * wait_co: if non-NULL, which coroutine to wake in
67 * nbd_co_establish_connection() after yield()
68 */
69 Coroutine *wait_co;
70 };
71
72 /*
73 * The function isn't protected by any mutex, only call it when the client
74 * connection attempt has not yet started.
75 */
nbd_client_connection_enable_retry(NBDClientConnection * conn)76 void nbd_client_connection_enable_retry(NBDClientConnection *conn)
77 {
78 conn->do_retry = true;
79 }
80
nbd_client_connection_new(const SocketAddress * saddr,bool do_negotiation,const char * export_name,const char * x_dirty_bitmap,QCryptoTLSCreds * tlscreds,const char * tlshostname)81 NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr,
82 bool do_negotiation,
83 const char *export_name,
84 const char *x_dirty_bitmap,
85 QCryptoTLSCreds *tlscreds,
86 const char *tlshostname)
87 {
88 NBDClientConnection *conn = g_new(NBDClientConnection, 1);
89
90 object_ref(OBJECT(tlscreds));
91 *conn = (NBDClientConnection) {
92 .saddr = QAPI_CLONE(SocketAddress, saddr),
93 .tlscreds = tlscreds,
94 .tlshostname = g_strdup(tlshostname),
95 .do_negotiation = do_negotiation,
96
97 .initial_info.request_sizes = true,
98 .initial_info.mode = NBD_MODE_EXTENDED,
99 .initial_info.base_allocation = true,
100 .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap),
101 .initial_info.name = g_strdup(export_name ?: "")
102 };
103
104 qemu_mutex_init(&conn->mutex);
105
106 return conn;
107 }
108
nbd_client_connection_do_free(NBDClientConnection * conn)109 static void nbd_client_connection_do_free(NBDClientConnection *conn)
110 {
111 if (conn->sioc) {
112 qio_channel_close(QIO_CHANNEL(conn->sioc), NULL);
113 object_unref(OBJECT(conn->sioc));
114 }
115 error_free(conn->err);
116 qapi_free_SocketAddress(conn->saddr);
117 g_free(conn->tlshostname);
118 object_unref(OBJECT(conn->tlscreds));
119 g_free(conn->initial_info.x_dirty_bitmap);
120 g_free(conn->initial_info.name);
121 g_free(conn);
122 }
123
124 /*
125 * Connect to @addr and do NBD negotiation if @info is not null. If @tlscreds
126 * are given @outioc is returned. @outioc is provided only on success. The call
127 * may be cancelled from other thread by simply qio_channel_shutdown(sioc).
128 */
nbd_connect(QIOChannelSocket * sioc,SocketAddress * addr,NBDExportInfo * info,QCryptoTLSCreds * tlscreds,const char * tlshostname,QIOChannel ** outioc,Error ** errp)129 static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr,
130 NBDExportInfo *info, QCryptoTLSCreds *tlscreds,
131 const char *tlshostname,
132 QIOChannel **outioc, Error **errp)
133 {
134 int ret;
135
136 if (outioc) {
137 *outioc = NULL;
138 }
139
140 ret = qio_channel_socket_connect_sync(sioc, addr, errp);
141 if (ret < 0) {
142 return ret;
143 }
144
145 nbd_set_socket_send_buffer(sioc);
146 qio_channel_set_delay(QIO_CHANNEL(sioc), false);
147
148 if (!info) {
149 return 0;
150 }
151
152 ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, tlshostname,
153 outioc, info, errp);
154 if (ret < 0) {
155 /*
156 * nbd_receive_negotiate() may setup tls ioc and return it even on
157 * failure path. In this case we should use it instead of original
158 * channel.
159 */
160 if (outioc && *outioc) {
161 qio_channel_close(*outioc, NULL);
162 object_unref(OBJECT(*outioc));
163 *outioc = NULL;
164 } else {
165 qio_channel_close(QIO_CHANNEL(sioc), NULL);
166 }
167
168 return ret;
169 }
170
171 return 0;
172 }
173
connect_thread_func(void * opaque)174 static void *connect_thread_func(void *opaque)
175 {
176 NBDClientConnection *conn = opaque;
177 int ret;
178 bool do_free;
179 uint64_t timeout = 1;
180 uint64_t max_timeout = 16;
181
182 qemu_mutex_lock(&conn->mutex);
183 while (!conn->detached) {
184 Error *local_err = NULL;
185
186 assert(!conn->sioc);
187 conn->sioc = qio_channel_socket_new();
188
189 qemu_mutex_unlock(&conn->mutex);
190
191 conn->updated_info = conn->initial_info;
192
193 ret = nbd_connect(conn->sioc, conn->saddr,
194 conn->do_negotiation ? &conn->updated_info : NULL,
195 conn->tlscreds, conn->tlshostname,
196 &conn->ioc, &local_err);
197
198 /*
199 * conn->updated_info will finally be returned to the user. Clear the
200 * pointers to our internally allocated strings, which are IN parameters
201 * of nbd_receive_negotiate() and therefore nbd_connect(). Caller
202 * shouldn't be interested in these fields.
203 */
204 conn->updated_info.x_dirty_bitmap = NULL;
205 conn->updated_info.name = NULL;
206
207 qemu_mutex_lock(&conn->mutex);
208
209 error_free(conn->err);
210 conn->err = NULL;
211 error_propagate(&conn->err, local_err);
212
213 if (ret < 0) {
214 object_unref(OBJECT(conn->sioc));
215 conn->sioc = NULL;
216 if (conn->do_retry && !conn->detached) {
217 trace_nbd_connect_thread_sleep(timeout);
218 qemu_mutex_unlock(&conn->mutex);
219
220 sleep(timeout);
221 if (timeout < max_timeout) {
222 timeout *= 2;
223 }
224
225 qemu_mutex_lock(&conn->mutex);
226 continue;
227 }
228 }
229
230 break;
231 }
232
233 /* mutex is locked */
234
235 assert(conn->running);
236 conn->running = false;
237 if (conn->wait_co) {
238 aio_co_wake(conn->wait_co);
239 conn->wait_co = NULL;
240 }
241 do_free = conn->detached;
242
243 qemu_mutex_unlock(&conn->mutex);
244
245 if (do_free) {
246 nbd_client_connection_do_free(conn);
247 }
248
249 return NULL;
250 }
251
nbd_client_connection_release(NBDClientConnection * conn)252 void nbd_client_connection_release(NBDClientConnection *conn)
253 {
254 bool do_free = false;
255
256 if (!conn) {
257 return;
258 }
259
260 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
261 assert(!conn->detached);
262 if (conn->running) {
263 conn->detached = true;
264 } else {
265 do_free = true;
266 }
267 if (conn->sioc) {
268 qio_channel_shutdown(QIO_CHANNEL(conn->sioc),
269 QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
270 }
271 }
272
273 if (do_free) {
274 nbd_client_connection_do_free(conn);
275 }
276 }
277
278 /*
279 * Get a new connection in context of @conn:
280 * if the thread is running, wait for completion
281 * if the thread already succeeded in the background, and user didn't get the
282 * result, just return it now
283 * otherwise the thread is not running, so start a thread and wait for
284 * completion
285 *
286 * If @blocking is false, don't wait for the thread, return immediately.
287 *
288 * If @info is not NULL, also do nbd-negotiation after successful connection.
289 * In this case info is used only as out parameter, and is fully initialized by
290 * nbd_co_establish_connection(). "IN" fields of info as well as related only to
291 * nbd_receive_export_list() would be zero (see description of NBDExportInfo in
292 * include/block/nbd.h).
293 */
294 QIOChannel *coroutine_fn
nbd_co_establish_connection(NBDClientConnection * conn,NBDExportInfo * info,bool blocking,Error ** errp)295 nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info,
296 bool blocking, Error **errp)
297 {
298 QemuThread thread;
299
300 if (conn->do_negotiation) {
301 assert(info);
302 }
303
304 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
305 /*
306 * Don't call nbd_co_establish_connection() in several coroutines in
307 * parallel. Only one call at once is supported.
308 */
309 assert(!conn->wait_co);
310
311 if (!conn->running) {
312 if (conn->sioc) {
313 /* Previous attempt finally succeeded in background */
314 if (conn->do_negotiation) {
315 memcpy(info, &conn->updated_info, sizeof(*info));
316 if (conn->ioc) {
317 /* TLS channel now has own reference to parent */
318 object_unref(OBJECT(conn->sioc));
319 conn->sioc = NULL;
320
321 return g_steal_pointer(&conn->ioc);
322 }
323 }
324
325 assert(!conn->ioc);
326
327 return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
328 }
329
330 conn->running = true;
331 qemu_thread_create(&thread, "nbd-connect",
332 connect_thread_func, conn, QEMU_THREAD_DETACHED);
333 }
334
335 if (!blocking) {
336 if (conn->err) {
337 error_propagate(errp, error_copy(conn->err));
338 } else {
339 error_setg(errp, "No connection at the moment");
340 }
341
342 return NULL;
343 }
344
345 conn->wait_co = qemu_coroutine_self();
346 }
347
348 /*
349 * We are going to wait for connect-thread finish, but
350 * nbd_co_establish_connection_cancel() can interrupt.
351 */
352 qemu_coroutine_yield();
353
354 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
355 if (conn->running) {
356 /*
357 * The connection attempt was canceled and the coroutine resumed
358 * before the connection thread finished its job. Report the
359 * attempt as failed, but leave the connection thread running,
360 * to reuse it for the next connection attempt.
361 */
362 if (conn->err) {
363 error_propagate(errp, error_copy(conn->err));
364 } else {
365 /*
366 * The only possible case here is cancelling by open_timer
367 * during nbd_open(). So, the error message is for that case.
368 * If we have more use cases, we can refactor
369 * nbd_co_establish_connection_cancel() to take an additional
370 * parameter cancel_reason, that would be passed than to the
371 * caller of cancelled nbd_co_establish_connection().
372 */
373 error_setg(errp, "Connection attempt cancelled by timeout");
374 }
375
376 return NULL;
377 } else {
378 /* Thread finished. There must be either error or sioc */
379 assert(!conn->err != !conn->sioc);
380
381 if (conn->err) {
382 error_propagate(errp, error_copy(conn->err));
383 return NULL;
384 }
385
386 if (conn->do_negotiation) {
387 memcpy(info, &conn->updated_info, sizeof(*info));
388 if (conn->ioc) {
389 /* TLS channel now has own reference to parent */
390 object_unref(OBJECT(conn->sioc));
391 conn->sioc = NULL;
392
393 return g_steal_pointer(&conn->ioc);
394 }
395 }
396
397 assert(!conn->ioc);
398
399 return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
400 }
401 }
402
403 abort(); /* unreachable */
404 }
405
406 /*
407 * nbd_co_establish_connection_cancel
408 * Cancel nbd_co_establish_connection() asynchronously.
409 *
410 * Note that this function neither directly stops the thread nor closes the
411 * socket, but rather safely wakes nbd_co_establish_connection() which is
412 * sleeping in yield()
413 */
nbd_co_establish_connection_cancel(NBDClientConnection * conn)414 void nbd_co_establish_connection_cancel(NBDClientConnection *conn)
415 {
416 Coroutine *wait_co = NULL;
417
418 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
419 wait_co = g_steal_pointer(&conn->wait_co);
420 }
421
422 if (wait_co) {
423 aio_co_wake(wait_co);
424 }
425 }
426