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