xref: /openbmc/qemu/nbd/client-connection.c (revision 423be09ab9492735924e73a2d36069784441ebc6)
15276c87cSVladimir Sementsov-Ogievskiy /*
25276c87cSVladimir Sementsov-Ogievskiy  * QEMU Block driver for NBD
35276c87cSVladimir Sementsov-Ogievskiy  *
45276c87cSVladimir Sementsov-Ogievskiy  * Copyright (c) 2021 Virtuozzo International GmbH.
55276c87cSVladimir Sementsov-Ogievskiy  *
65276c87cSVladimir Sementsov-Ogievskiy  * Permission is hereby granted, free of charge, to any person obtaining a copy
75276c87cSVladimir Sementsov-Ogievskiy  * of this software and associated documentation files (the "Software"), to deal
85276c87cSVladimir Sementsov-Ogievskiy  * in the Software without restriction, including without limitation the rights
95276c87cSVladimir Sementsov-Ogievskiy  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
105276c87cSVladimir Sementsov-Ogievskiy  * copies of the Software, and to permit persons to whom the Software is
115276c87cSVladimir Sementsov-Ogievskiy  * furnished to do so, subject to the following conditions:
125276c87cSVladimir Sementsov-Ogievskiy  *
135276c87cSVladimir Sementsov-Ogievskiy  * The above copyright notice and this permission notice shall be included in
145276c87cSVladimir Sementsov-Ogievskiy  * all copies or substantial portions of the Software.
155276c87cSVladimir Sementsov-Ogievskiy  *
165276c87cSVladimir Sementsov-Ogievskiy  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
175276c87cSVladimir Sementsov-Ogievskiy  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
185276c87cSVladimir Sementsov-Ogievskiy  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
195276c87cSVladimir Sementsov-Ogievskiy  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
205276c87cSVladimir Sementsov-Ogievskiy  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
215276c87cSVladimir Sementsov-Ogievskiy  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
225276c87cSVladimir Sementsov-Ogievskiy  * THE SOFTWARE.
235276c87cSVladimir Sementsov-Ogievskiy  */
245276c87cSVladimir Sementsov-Ogievskiy 
255276c87cSVladimir Sementsov-Ogievskiy #include "qemu/osdep.h"
268bb100c9SDenis V. Lunev #include "trace.h"
275276c87cSVladimir Sementsov-Ogievskiy 
285276c87cSVladimir Sementsov-Ogievskiy #include "block/nbd.h"
295276c87cSVladimir Sementsov-Ogievskiy 
305276c87cSVladimir Sementsov-Ogievskiy #include "qapi/qapi-visit-sockets.h"
315276c87cSVladimir Sementsov-Ogievskiy #include "qapi/clone-visitor.h"
3268ba85ceSMarkus Armbruster #include "qemu/coroutine.h"
335276c87cSVladimir Sementsov-Ogievskiy 
345276c87cSVladimir Sementsov-Ogievskiy struct NBDClientConnection {
35130d49baSVladimir Sementsov-Ogievskiy     /* Initialization constants, never change */
365276c87cSVladimir Sementsov-Ogievskiy     SocketAddress *saddr; /* address to connect to */
37130d49baSVladimir Sementsov-Ogievskiy     QCryptoTLSCreds *tlscreds;
38046f98d0SDaniel P. Berrangé     char *tlshostname;
39130d49baSVladimir Sementsov-Ogievskiy     NBDExportInfo initial_info;
40130d49baSVladimir Sementsov-Ogievskiy     bool do_negotiation;
41e0e67cbeSVladimir Sementsov-Ogievskiy     bool do_retry;
425276c87cSVladimir Sementsov-Ogievskiy 
435276c87cSVladimir Sementsov-Ogievskiy     QemuMutex mutex;
445276c87cSVladimir Sementsov-Ogievskiy 
45130d49baSVladimir Sementsov-Ogievskiy     NBDExportInfo updated_info;
46169b9a94SVladimir Sementsov-Ogievskiy     /*
47169b9a94SVladimir Sementsov-Ogievskiy      * @sioc represents a successful result. While thread is running, @sioc is
48169b9a94SVladimir Sementsov-Ogievskiy      * used only by thread and not protected by mutex. When thread is not
49169b9a94SVladimir Sementsov-Ogievskiy      * running, @sioc is stolen by nbd_co_establish_connection() under mutex.
50169b9a94SVladimir Sementsov-Ogievskiy      */
515276c87cSVladimir Sementsov-Ogievskiy     QIOChannelSocket *sioc;
52130d49baSVladimir Sementsov-Ogievskiy     QIOChannel *ioc;
53169b9a94SVladimir Sementsov-Ogievskiy     /*
54169b9a94SVladimir Sementsov-Ogievskiy      * @err represents previous attempt. It may be copied by
55169b9a94SVladimir Sementsov-Ogievskiy      * nbd_co_establish_connection() when it reports failure.
56169b9a94SVladimir Sementsov-Ogievskiy      */
575276c87cSVladimir Sementsov-Ogievskiy     Error *err;
585276c87cSVladimir Sementsov-Ogievskiy 
595276c87cSVladimir Sementsov-Ogievskiy     /* All further fields are accessed only under mutex */
605276c87cSVladimir Sementsov-Ogievskiy     bool running; /* thread is running now */
615276c87cSVladimir Sementsov-Ogievskiy     bool detached; /* thread is detached and should cleanup the state */
625276c87cSVladimir Sementsov-Ogievskiy 
635276c87cSVladimir Sementsov-Ogievskiy     /*
645276c87cSVladimir Sementsov-Ogievskiy      * wait_co: if non-NULL, which coroutine to wake in
655276c87cSVladimir Sementsov-Ogievskiy      * nbd_co_establish_connection() after yield()
665276c87cSVladimir Sementsov-Ogievskiy      */
675276c87cSVladimir Sementsov-Ogievskiy     Coroutine *wait_co;
685276c87cSVladimir Sementsov-Ogievskiy };
695276c87cSVladimir Sementsov-Ogievskiy 
70e0e67cbeSVladimir Sementsov-Ogievskiy /*
71e0e67cbeSVladimir Sementsov-Ogievskiy  * The function isn't protected by any mutex, only call it when the client
72e0e67cbeSVladimir Sementsov-Ogievskiy  * connection attempt has not yet started.
73e0e67cbeSVladimir Sementsov-Ogievskiy  */
nbd_client_connection_enable_retry(NBDClientConnection * conn)74e0e67cbeSVladimir Sementsov-Ogievskiy void nbd_client_connection_enable_retry(NBDClientConnection *conn)
75e0e67cbeSVladimir Sementsov-Ogievskiy {
76e0e67cbeSVladimir Sementsov-Ogievskiy     conn->do_retry = true;
77e0e67cbeSVladimir Sementsov-Ogievskiy }
78e0e67cbeSVladimir Sementsov-Ogievskiy 
nbd_client_connection_new(const SocketAddress * saddr,bool do_negotiation,const char * export_name,const char * x_dirty_bitmap,QCryptoTLSCreds * tlscreds,const char * tlshostname)79130d49baSVladimir Sementsov-Ogievskiy NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr,
80130d49baSVladimir Sementsov-Ogievskiy                                                bool do_negotiation,
81130d49baSVladimir Sementsov-Ogievskiy                                                const char *export_name,
82130d49baSVladimir Sementsov-Ogievskiy                                                const char *x_dirty_bitmap,
83046f98d0SDaniel P. Berrangé                                                QCryptoTLSCreds *tlscreds,
84046f98d0SDaniel P. Berrangé                                                const char *tlshostname)
855276c87cSVladimir Sementsov-Ogievskiy {
865276c87cSVladimir Sementsov-Ogievskiy     NBDClientConnection *conn = g_new(NBDClientConnection, 1);
875276c87cSVladimir Sementsov-Ogievskiy 
88130d49baSVladimir Sementsov-Ogievskiy     object_ref(OBJECT(tlscreds));
895276c87cSVladimir Sementsov-Ogievskiy     *conn = (NBDClientConnection) {
905276c87cSVladimir Sementsov-Ogievskiy         .saddr = QAPI_CLONE(SocketAddress, saddr),
91130d49baSVladimir Sementsov-Ogievskiy         .tlscreds = tlscreds,
92046f98d0SDaniel P. Berrangé         .tlshostname = g_strdup(tlshostname),
93130d49baSVladimir Sementsov-Ogievskiy         .do_negotiation = do_negotiation,
94130d49baSVladimir Sementsov-Ogievskiy 
95130d49baSVladimir Sementsov-Ogievskiy         .initial_info.request_sizes = true,
9656cf9d04SEric Blake         .initial_info.mode = NBD_MODE_EXTENDED,
97130d49baSVladimir Sementsov-Ogievskiy         .initial_info.base_allocation = true,
98130d49baSVladimir Sementsov-Ogievskiy         .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap),
99130d49baSVladimir Sementsov-Ogievskiy         .initial_info.name = g_strdup(export_name ?: "")
1005276c87cSVladimir Sementsov-Ogievskiy     };
1015276c87cSVladimir Sementsov-Ogievskiy 
1025276c87cSVladimir Sementsov-Ogievskiy     qemu_mutex_init(&conn->mutex);
1035276c87cSVladimir Sementsov-Ogievskiy 
1045276c87cSVladimir Sementsov-Ogievskiy     return conn;
1055276c87cSVladimir Sementsov-Ogievskiy }
1065276c87cSVladimir Sementsov-Ogievskiy 
nbd_client_connection_do_free(NBDClientConnection * conn)1075276c87cSVladimir Sementsov-Ogievskiy static void nbd_client_connection_do_free(NBDClientConnection *conn)
1085276c87cSVladimir Sementsov-Ogievskiy {
1095276c87cSVladimir Sementsov-Ogievskiy     if (conn->sioc) {
1105276c87cSVladimir Sementsov-Ogievskiy         qio_channel_close(QIO_CHANNEL(conn->sioc), NULL);
1115276c87cSVladimir Sementsov-Ogievskiy         object_unref(OBJECT(conn->sioc));
1125276c87cSVladimir Sementsov-Ogievskiy     }
1135276c87cSVladimir Sementsov-Ogievskiy     error_free(conn->err);
1145276c87cSVladimir Sementsov-Ogievskiy     qapi_free_SocketAddress(conn->saddr);
115046f98d0SDaniel P. Berrangé     g_free(conn->tlshostname);
116130d49baSVladimir Sementsov-Ogievskiy     object_unref(OBJECT(conn->tlscreds));
117130d49baSVladimir Sementsov-Ogievskiy     g_free(conn->initial_info.x_dirty_bitmap);
118130d49baSVladimir Sementsov-Ogievskiy     g_free(conn->initial_info.name);
1195276c87cSVladimir Sementsov-Ogievskiy     g_free(conn);
1205276c87cSVladimir Sementsov-Ogievskiy }
1215276c87cSVladimir Sementsov-Ogievskiy 
122130d49baSVladimir Sementsov-Ogievskiy /*
123130d49baSVladimir Sementsov-Ogievskiy  * Connect to @addr and do NBD negotiation if @info is not null. If @tlscreds
124130d49baSVladimir Sementsov-Ogievskiy  * are given @outioc is returned. @outioc is provided only on success.  The call
125130d49baSVladimir Sementsov-Ogievskiy  * may be cancelled from other thread by simply qio_channel_shutdown(sioc).
126130d49baSVladimir Sementsov-Ogievskiy  */
nbd_connect(QIOChannelSocket * sioc,SocketAddress * addr,NBDExportInfo * info,QCryptoTLSCreds * tlscreds,const char * tlshostname,QIOChannel ** outioc,Error ** errp)127130d49baSVladimir Sementsov-Ogievskiy static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr,
128130d49baSVladimir Sementsov-Ogievskiy                        NBDExportInfo *info, QCryptoTLSCreds *tlscreds,
129046f98d0SDaniel P. Berrangé                        const char *tlshostname,
130130d49baSVladimir Sementsov-Ogievskiy                        QIOChannel **outioc, Error **errp)
131130d49baSVladimir Sementsov-Ogievskiy {
132130d49baSVladimir Sementsov-Ogievskiy     int ret;
133130d49baSVladimir Sementsov-Ogievskiy 
134130d49baSVladimir Sementsov-Ogievskiy     if (outioc) {
135130d49baSVladimir Sementsov-Ogievskiy         *outioc = NULL;
136130d49baSVladimir Sementsov-Ogievskiy     }
137130d49baSVladimir Sementsov-Ogievskiy 
138130d49baSVladimir Sementsov-Ogievskiy     ret = qio_channel_socket_connect_sync(sioc, addr, errp);
139130d49baSVladimir Sementsov-Ogievskiy     if (ret < 0) {
140130d49baSVladimir Sementsov-Ogievskiy         return ret;
141130d49baSVladimir Sementsov-Ogievskiy     }
142130d49baSVladimir Sementsov-Ogievskiy 
143130d49baSVladimir Sementsov-Ogievskiy     qio_channel_set_delay(QIO_CHANNEL(sioc), false);
144130d49baSVladimir Sementsov-Ogievskiy 
145130d49baSVladimir Sementsov-Ogievskiy     if (!info) {
146130d49baSVladimir Sementsov-Ogievskiy         return 0;
147130d49baSVladimir Sementsov-Ogievskiy     }
148130d49baSVladimir Sementsov-Ogievskiy 
149b84ca91cSStefan Hajnoczi     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, tlshostname,
150130d49baSVladimir Sementsov-Ogievskiy                                 outioc, info, errp);
151130d49baSVladimir Sementsov-Ogievskiy     if (ret < 0) {
152130d49baSVladimir Sementsov-Ogievskiy         /*
153130d49baSVladimir Sementsov-Ogievskiy          * nbd_receive_negotiate() may setup tls ioc and return it even on
154130d49baSVladimir Sementsov-Ogievskiy          * failure path. In this case we should use it instead of original
155130d49baSVladimir Sementsov-Ogievskiy          * channel.
156130d49baSVladimir Sementsov-Ogievskiy          */
157130d49baSVladimir Sementsov-Ogievskiy         if (outioc && *outioc) {
1587d5b0d68SPhilippe Mathieu-Daudé             qio_channel_close(*outioc, NULL);
159130d49baSVladimir Sementsov-Ogievskiy             object_unref(OBJECT(*outioc));
160130d49baSVladimir Sementsov-Ogievskiy             *outioc = NULL;
161130d49baSVladimir Sementsov-Ogievskiy         } else {
162130d49baSVladimir Sementsov-Ogievskiy             qio_channel_close(QIO_CHANNEL(sioc), NULL);
163130d49baSVladimir Sementsov-Ogievskiy         }
164130d49baSVladimir Sementsov-Ogievskiy 
165130d49baSVladimir Sementsov-Ogievskiy         return ret;
166130d49baSVladimir Sementsov-Ogievskiy     }
167130d49baSVladimir Sementsov-Ogievskiy 
168130d49baSVladimir Sementsov-Ogievskiy     return 0;
169130d49baSVladimir Sementsov-Ogievskiy }
170130d49baSVladimir Sementsov-Ogievskiy 
connect_thread_func(void * opaque)1715276c87cSVladimir Sementsov-Ogievskiy static void *connect_thread_func(void *opaque)
1725276c87cSVladimir Sementsov-Ogievskiy {
1735276c87cSVladimir Sementsov-Ogievskiy     NBDClientConnection *conn = opaque;
1745276c87cSVladimir Sementsov-Ogievskiy     int ret;
1755276c87cSVladimir Sementsov-Ogievskiy     bool do_free;
176e0e67cbeSVladimir Sementsov-Ogievskiy     uint64_t timeout = 1;
177e0e67cbeSVladimir Sementsov-Ogievskiy     uint64_t max_timeout = 16;
1785276c87cSVladimir Sementsov-Ogievskiy 
179f58b2dfeSVladimir Sementsov-Ogievskiy     qemu_mutex_lock(&conn->mutex);
180f58b2dfeSVladimir Sementsov-Ogievskiy     while (!conn->detached) {
181169b9a94SVladimir Sementsov-Ogievskiy         Error *local_err = NULL;
182169b9a94SVladimir Sementsov-Ogievskiy 
183f58b2dfeSVladimir Sementsov-Ogievskiy         assert(!conn->sioc);
1845276c87cSVladimir Sementsov-Ogievskiy         conn->sioc = qio_channel_socket_new();
1855276c87cSVladimir Sementsov-Ogievskiy 
186f58b2dfeSVladimir Sementsov-Ogievskiy         qemu_mutex_unlock(&conn->mutex);
187f58b2dfeSVladimir Sementsov-Ogievskiy 
188130d49baSVladimir Sementsov-Ogievskiy         conn->updated_info = conn->initial_info;
189130d49baSVladimir Sementsov-Ogievskiy 
190130d49baSVladimir Sementsov-Ogievskiy         ret = nbd_connect(conn->sioc, conn->saddr,
191130d49baSVladimir Sementsov-Ogievskiy                           conn->do_negotiation ? &conn->updated_info : NULL,
192046f98d0SDaniel P. Berrangé                           conn->tlscreds, conn->tlshostname,
193046f98d0SDaniel P. Berrangé                           &conn->ioc, &local_err);
194e0e67cbeSVladimir Sementsov-Ogievskiy 
195e0e67cbeSVladimir Sementsov-Ogievskiy         /*
196e0e67cbeSVladimir Sementsov-Ogievskiy          * conn->updated_info will finally be returned to the user. Clear the
197e0e67cbeSVladimir Sementsov-Ogievskiy          * pointers to our internally allocated strings, which are IN parameters
198e0e67cbeSVladimir Sementsov-Ogievskiy          * of nbd_receive_negotiate() and therefore nbd_connect(). Caller
1990a19d879SMichael Tokarev          * shouldn't be interested in these fields.
200e0e67cbeSVladimir Sementsov-Ogievskiy          */
201e0e67cbeSVladimir Sementsov-Ogievskiy         conn->updated_info.x_dirty_bitmap = NULL;
202e0e67cbeSVladimir Sementsov-Ogievskiy         conn->updated_info.name = NULL;
203e0e67cbeSVladimir Sementsov-Ogievskiy 
204f58b2dfeSVladimir Sementsov-Ogievskiy         qemu_mutex_lock(&conn->mutex);
205f58b2dfeSVladimir Sementsov-Ogievskiy 
206169b9a94SVladimir Sementsov-Ogievskiy         error_free(conn->err);
207169b9a94SVladimir Sementsov-Ogievskiy         conn->err = NULL;
208169b9a94SVladimir Sementsov-Ogievskiy         error_propagate(&conn->err, local_err);
209169b9a94SVladimir Sementsov-Ogievskiy 
2105276c87cSVladimir Sementsov-Ogievskiy         if (ret < 0) {
2115276c87cSVladimir Sementsov-Ogievskiy             object_unref(OBJECT(conn->sioc));
2125276c87cSVladimir Sementsov-Ogievskiy             conn->sioc = NULL;
213f58b2dfeSVladimir Sementsov-Ogievskiy             if (conn->do_retry && !conn->detached) {
2148bb100c9SDenis V. Lunev                 trace_nbd_connect_thread_sleep(timeout);
215f58b2dfeSVladimir Sementsov-Ogievskiy                 qemu_mutex_unlock(&conn->mutex);
216f58b2dfeSVladimir Sementsov-Ogievskiy 
217e0e67cbeSVladimir Sementsov-Ogievskiy                 sleep(timeout);
218e0e67cbeSVladimir Sementsov-Ogievskiy                 if (timeout < max_timeout) {
219e0e67cbeSVladimir Sementsov-Ogievskiy                     timeout *= 2;
220e0e67cbeSVladimir Sementsov-Ogievskiy                 }
221f58b2dfeSVladimir Sementsov-Ogievskiy 
222f58b2dfeSVladimir Sementsov-Ogievskiy                 qemu_mutex_lock(&conn->mutex);
223e0e67cbeSVladimir Sementsov-Ogievskiy                 continue;
224e0e67cbeSVladimir Sementsov-Ogievskiy             }
2255276c87cSVladimir Sementsov-Ogievskiy         }
2265276c87cSVladimir Sementsov-Ogievskiy 
227e0e67cbeSVladimir Sementsov-Ogievskiy         break;
228e0e67cbeSVladimir Sementsov-Ogievskiy     }
2295276c87cSVladimir Sementsov-Ogievskiy 
230f58b2dfeSVladimir Sementsov-Ogievskiy     /* mutex is locked */
2315276c87cSVladimir Sementsov-Ogievskiy 
2325276c87cSVladimir Sementsov-Ogievskiy     assert(conn->running);
2335276c87cSVladimir Sementsov-Ogievskiy     conn->running = false;
2345276c87cSVladimir Sementsov-Ogievskiy     if (conn->wait_co) {
2355276c87cSVladimir Sementsov-Ogievskiy         aio_co_wake(conn->wait_co);
2365276c87cSVladimir Sementsov-Ogievskiy         conn->wait_co = NULL;
2375276c87cSVladimir Sementsov-Ogievskiy     }
2385276c87cSVladimir Sementsov-Ogievskiy     do_free = conn->detached;
2395276c87cSVladimir Sementsov-Ogievskiy 
2405276c87cSVladimir Sementsov-Ogievskiy     qemu_mutex_unlock(&conn->mutex);
2415276c87cSVladimir Sementsov-Ogievskiy 
2425276c87cSVladimir Sementsov-Ogievskiy     if (do_free) {
2435276c87cSVladimir Sementsov-Ogievskiy         nbd_client_connection_do_free(conn);
2445276c87cSVladimir Sementsov-Ogievskiy     }
2455276c87cSVladimir Sementsov-Ogievskiy 
2465276c87cSVladimir Sementsov-Ogievskiy     return NULL;
2475276c87cSVladimir Sementsov-Ogievskiy }
2485276c87cSVladimir Sementsov-Ogievskiy 
nbd_client_connection_release(NBDClientConnection * conn)2495276c87cSVladimir Sementsov-Ogievskiy void nbd_client_connection_release(NBDClientConnection *conn)
2505276c87cSVladimir Sementsov-Ogievskiy {
2515276c87cSVladimir Sementsov-Ogievskiy     bool do_free = false;
2525276c87cSVladimir Sementsov-Ogievskiy 
2535276c87cSVladimir Sementsov-Ogievskiy     if (!conn) {
2545276c87cSVladimir Sementsov-Ogievskiy         return;
2555276c87cSVladimir Sementsov-Ogievskiy     }
2565276c87cSVladimir Sementsov-Ogievskiy 
257e70da5ffSVladimir Sementsov-Ogievskiy     WITH_QEMU_LOCK_GUARD(&conn->mutex) {
2585276c87cSVladimir Sementsov-Ogievskiy         assert(!conn->detached);
2595276c87cSVladimir Sementsov-Ogievskiy         if (conn->running) {
2605276c87cSVladimir Sementsov-Ogievskiy             conn->detached = true;
2615276c87cSVladimir Sementsov-Ogievskiy         } else {
2625276c87cSVladimir Sementsov-Ogievskiy             do_free = true;
2635276c87cSVladimir Sementsov-Ogievskiy         }
264f58b2dfeSVladimir Sementsov-Ogievskiy         if (conn->sioc) {
265f58b2dfeSVladimir Sementsov-Ogievskiy             qio_channel_shutdown(QIO_CHANNEL(conn->sioc),
266f58b2dfeSVladimir Sementsov-Ogievskiy                                  QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
267f58b2dfeSVladimir Sementsov-Ogievskiy         }
268e70da5ffSVladimir Sementsov-Ogievskiy     }
2695276c87cSVladimir Sementsov-Ogievskiy 
2705276c87cSVladimir Sementsov-Ogievskiy     if (do_free) {
2715276c87cSVladimir Sementsov-Ogievskiy         nbd_client_connection_do_free(conn);
2725276c87cSVladimir Sementsov-Ogievskiy     }
2735276c87cSVladimir Sementsov-Ogievskiy }
2745276c87cSVladimir Sementsov-Ogievskiy 
2755276c87cSVladimir Sementsov-Ogievskiy /*
2765276c87cSVladimir Sementsov-Ogievskiy  * Get a new connection in context of @conn:
2775276c87cSVladimir Sementsov-Ogievskiy  *   if the thread is running, wait for completion
2785276c87cSVladimir Sementsov-Ogievskiy  *   if the thread already succeeded in the background, and user didn't get the
2795276c87cSVladimir Sementsov-Ogievskiy  *     result, just return it now
2805276c87cSVladimir Sementsov-Ogievskiy  *   otherwise the thread is not running, so start a thread and wait for
2815276c87cSVladimir Sementsov-Ogievskiy  *     completion
282130d49baSVladimir Sementsov-Ogievskiy  *
28397cf8925SVladimir Sementsov-Ogievskiy  * If @blocking is false, don't wait for the thread, return immediately.
28497cf8925SVladimir Sementsov-Ogievskiy  *
285130d49baSVladimir Sementsov-Ogievskiy  * If @info is not NULL, also do nbd-negotiation after successful connection.
286130d49baSVladimir Sementsov-Ogievskiy  * In this case info is used only as out parameter, and is fully initialized by
287130d49baSVladimir Sementsov-Ogievskiy  * nbd_co_establish_connection(). "IN" fields of info as well as related only to
288130d49baSVladimir Sementsov-Ogievskiy  * nbd_receive_export_list() would be zero (see description of NBDExportInfo in
289130d49baSVladimir Sementsov-Ogievskiy  * include/block/nbd.h).
2905276c87cSVladimir Sementsov-Ogievskiy  */
29143cb34deSVladimir Sementsov-Ogievskiy QIOChannel *coroutine_fn
nbd_co_establish_connection(NBDClientConnection * conn,NBDExportInfo * info,bool blocking,Error ** errp)292130d49baSVladimir Sementsov-Ogievskiy nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info,
29397cf8925SVladimir Sementsov-Ogievskiy                             bool blocking, Error **errp)
2945276c87cSVladimir Sementsov-Ogievskiy {
2955276c87cSVladimir Sementsov-Ogievskiy     QemuThread thread;
2965276c87cSVladimir Sementsov-Ogievskiy 
297130d49baSVladimir Sementsov-Ogievskiy     if (conn->do_negotiation) {
298130d49baSVladimir Sementsov-Ogievskiy         assert(info);
299130d49baSVladimir Sementsov-Ogievskiy     }
300130d49baSVladimir Sementsov-Ogievskiy 
301e70da5ffSVladimir Sementsov-Ogievskiy     WITH_QEMU_LOCK_GUARD(&conn->mutex) {
3025276c87cSVladimir Sementsov-Ogievskiy         /*
3035276c87cSVladimir Sementsov-Ogievskiy          * Don't call nbd_co_establish_connection() in several coroutines in
3045276c87cSVladimir Sementsov-Ogievskiy          * parallel. Only one call at once is supported.
3055276c87cSVladimir Sementsov-Ogievskiy          */
3065276c87cSVladimir Sementsov-Ogievskiy         assert(!conn->wait_co);
3075276c87cSVladimir Sementsov-Ogievskiy 
3085276c87cSVladimir Sementsov-Ogievskiy         if (!conn->running) {
3095276c87cSVladimir Sementsov-Ogievskiy             if (conn->sioc) {
3105276c87cSVladimir Sementsov-Ogievskiy                 /* Previous attempt finally succeeded in background */
311130d49baSVladimir Sementsov-Ogievskiy                 if (conn->do_negotiation) {
312130d49baSVladimir Sementsov-Ogievskiy                     memcpy(info, &conn->updated_info, sizeof(*info));
31343cb34deSVladimir Sementsov-Ogievskiy                     if (conn->ioc) {
31443cb34deSVladimir Sementsov-Ogievskiy                         /* TLS channel now has own reference to parent */
31543cb34deSVladimir Sementsov-Ogievskiy                         object_unref(OBJECT(conn->sioc));
31643cb34deSVladimir Sementsov-Ogievskiy                         conn->sioc = NULL;
31743cb34deSVladimir Sementsov-Ogievskiy 
31843cb34deSVladimir Sementsov-Ogievskiy                         return g_steal_pointer(&conn->ioc);
319130d49baSVladimir Sementsov-Ogievskiy                     }
32043cb34deSVladimir Sementsov-Ogievskiy                 }
32143cb34deSVladimir Sementsov-Ogievskiy 
32243cb34deSVladimir Sementsov-Ogievskiy                 assert(!conn->ioc);
32343cb34deSVladimir Sementsov-Ogievskiy 
32443cb34deSVladimir Sementsov-Ogievskiy                 return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
3255276c87cSVladimir Sementsov-Ogievskiy             }
3265276c87cSVladimir Sementsov-Ogievskiy 
3275276c87cSVladimir Sementsov-Ogievskiy             conn->running = true;
3285276c87cSVladimir Sementsov-Ogievskiy             qemu_thread_create(&thread, "nbd-connect",
3295276c87cSVladimir Sementsov-Ogievskiy                                connect_thread_func, conn, QEMU_THREAD_DETACHED);
3305276c87cSVladimir Sementsov-Ogievskiy         }
3315276c87cSVladimir Sementsov-Ogievskiy 
33297cf8925SVladimir Sementsov-Ogievskiy         if (!blocking) {
333169b9a94SVladimir Sementsov-Ogievskiy             if (conn->err) {
334169b9a94SVladimir Sementsov-Ogievskiy                 error_propagate(errp, error_copy(conn->err));
335169b9a94SVladimir Sementsov-Ogievskiy             } else {
336f7ca4aadSVladimir Sementsov-Ogievskiy                 error_setg(errp, "No connection at the moment");
337169b9a94SVladimir Sementsov-Ogievskiy             }
338169b9a94SVladimir Sementsov-Ogievskiy 
33997cf8925SVladimir Sementsov-Ogievskiy             return NULL;
34097cf8925SVladimir Sementsov-Ogievskiy         }
34197cf8925SVladimir Sementsov-Ogievskiy 
3425276c87cSVladimir Sementsov-Ogievskiy         conn->wait_co = qemu_coroutine_self();
343e70da5ffSVladimir Sementsov-Ogievskiy     }
3445276c87cSVladimir Sementsov-Ogievskiy 
3455276c87cSVladimir Sementsov-Ogievskiy     /*
3465276c87cSVladimir Sementsov-Ogievskiy      * We are going to wait for connect-thread finish, but
3475276c87cSVladimir Sementsov-Ogievskiy      * nbd_co_establish_connection_cancel() can interrupt.
3485276c87cSVladimir Sementsov-Ogievskiy      */
3495276c87cSVladimir Sementsov-Ogievskiy     qemu_coroutine_yield();
3505276c87cSVladimir Sementsov-Ogievskiy 
351e70da5ffSVladimir Sementsov-Ogievskiy     WITH_QEMU_LOCK_GUARD(&conn->mutex) {
3525276c87cSVladimir Sementsov-Ogievskiy         if (conn->running) {
3535276c87cSVladimir Sementsov-Ogievskiy             /*
3545276c87cSVladimir Sementsov-Ogievskiy              * The connection attempt was canceled and the coroutine resumed
3555276c87cSVladimir Sementsov-Ogievskiy              * before the connection thread finished its job.  Report the
3565276c87cSVladimir Sementsov-Ogievskiy              * attempt as failed, but leave the connection thread running,
3575276c87cSVladimir Sementsov-Ogievskiy              * to reuse it for the next connection attempt.
3585276c87cSVladimir Sementsov-Ogievskiy              */
359169b9a94SVladimir Sementsov-Ogievskiy             if (conn->err) {
360169b9a94SVladimir Sementsov-Ogievskiy                 error_propagate(errp, error_copy(conn->err));
361169b9a94SVladimir Sementsov-Ogievskiy             } else {
3629e14491aSVladimir Sementsov-Ogievskiy                 /*
3639e14491aSVladimir Sementsov-Ogievskiy                  * The only possible case here is cancelling by open_timer
3649e14491aSVladimir Sementsov-Ogievskiy                  * during nbd_open(). So, the error message is for that case.
3659e14491aSVladimir Sementsov-Ogievskiy                  * If we have more use cases, we can refactor
3669e14491aSVladimir Sementsov-Ogievskiy                  * nbd_co_establish_connection_cancel() to take an additional
3679e14491aSVladimir Sementsov-Ogievskiy                  * parameter cancel_reason, that would be passed than to the
3689e14491aSVladimir Sementsov-Ogievskiy                  * caller of cancelled nbd_co_establish_connection().
3699e14491aSVladimir Sementsov-Ogievskiy                  */
3709e14491aSVladimir Sementsov-Ogievskiy                 error_setg(errp, "Connection attempt cancelled by timeout");
371169b9a94SVladimir Sementsov-Ogievskiy             }
372169b9a94SVladimir Sementsov-Ogievskiy 
373e70da5ffSVladimir Sementsov-Ogievskiy             return NULL;
3745276c87cSVladimir Sementsov-Ogievskiy         } else {
375169b9a94SVladimir Sementsov-Ogievskiy             /* Thread finished. There must be either error or sioc */
376169b9a94SVladimir Sementsov-Ogievskiy             assert(!conn->err != !conn->sioc);
377169b9a94SVladimir Sementsov-Ogievskiy 
378169b9a94SVladimir Sementsov-Ogievskiy             if (conn->err) {
379169b9a94SVladimir Sementsov-Ogievskiy                 error_propagate(errp, error_copy(conn->err));
38043cb34deSVladimir Sementsov-Ogievskiy                 return NULL;
381130d49baSVladimir Sementsov-Ogievskiy             }
382169b9a94SVladimir Sementsov-Ogievskiy 
38343cb34deSVladimir Sementsov-Ogievskiy             if (conn->do_negotiation) {
38443cb34deSVladimir Sementsov-Ogievskiy                 memcpy(info, &conn->updated_info, sizeof(*info));
38543cb34deSVladimir Sementsov-Ogievskiy                 if (conn->ioc) {
38643cb34deSVladimir Sementsov-Ogievskiy                     /* TLS channel now has own reference to parent */
38743cb34deSVladimir Sementsov-Ogievskiy                     object_unref(OBJECT(conn->sioc));
38843cb34deSVladimir Sementsov-Ogievskiy                     conn->sioc = NULL;
38943cb34deSVladimir Sementsov-Ogievskiy 
39043cb34deSVladimir Sementsov-Ogievskiy                     return g_steal_pointer(&conn->ioc);
39143cb34deSVladimir Sementsov-Ogievskiy                 }
39243cb34deSVladimir Sementsov-Ogievskiy             }
39343cb34deSVladimir Sementsov-Ogievskiy 
39443cb34deSVladimir Sementsov-Ogievskiy             assert(!conn->ioc);
39543cb34deSVladimir Sementsov-Ogievskiy 
39643cb34deSVladimir Sementsov-Ogievskiy             return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
397e70da5ffSVladimir Sementsov-Ogievskiy         }
3985276c87cSVladimir Sementsov-Ogievskiy     }
3995276c87cSVladimir Sementsov-Ogievskiy 
400e70da5ffSVladimir Sementsov-Ogievskiy     abort(); /* unreachable */
4015276c87cSVladimir Sementsov-Ogievskiy }
4025276c87cSVladimir Sementsov-Ogievskiy 
4035276c87cSVladimir Sementsov-Ogievskiy /*
4045276c87cSVladimir Sementsov-Ogievskiy  * nbd_co_establish_connection_cancel
4055276c87cSVladimir Sementsov-Ogievskiy  * Cancel nbd_co_establish_connection() asynchronously.
4065276c87cSVladimir Sementsov-Ogievskiy  *
4075276c87cSVladimir Sementsov-Ogievskiy  * Note that this function neither directly stops the thread nor closes the
4085276c87cSVladimir Sementsov-Ogievskiy  * socket, but rather safely wakes nbd_co_establish_connection() which is
4095276c87cSVladimir Sementsov-Ogievskiy  * sleeping in yield()
4105276c87cSVladimir Sementsov-Ogievskiy  */
nbd_co_establish_connection_cancel(NBDClientConnection * conn)4115276c87cSVladimir Sementsov-Ogievskiy void nbd_co_establish_connection_cancel(NBDClientConnection *conn)
4125276c87cSVladimir Sementsov-Ogievskiy {
413*73ce9bbfSMarc-André Lureau     Coroutine *wait_co = NULL;
4145276c87cSVladimir Sementsov-Ogievskiy 
415e70da5ffSVladimir Sementsov-Ogievskiy     WITH_QEMU_LOCK_GUARD(&conn->mutex) {
4165276c87cSVladimir Sementsov-Ogievskiy         wait_co = g_steal_pointer(&conn->wait_co);
417e70da5ffSVladimir Sementsov-Ogievskiy     }
4185276c87cSVladimir Sementsov-Ogievskiy 
4195276c87cSVladimir Sementsov-Ogievskiy     if (wait_co) {
4205276c87cSVladimir Sementsov-Ogievskiy         aio_co_wake(wait_co);
4215276c87cSVladimir Sementsov-Ogievskiy     }
4225276c87cSVladimir Sementsov-Ogievskiy }
423