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