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