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.mode = NBD_MODE_EXTENDED, 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(QIO_CHANNEL(sioc), tlscreds, tlshostname, 150 outioc, info, errp); 151 if (ret < 0) { 152 /* 153 * nbd_receive_negotiate() may setup tls ioc and return it even on 154 * failure path. In this case we should use it instead of original 155 * channel. 156 */ 157 if (outioc && *outioc) { 158 qio_channel_close(*outioc, NULL); 159 object_unref(OBJECT(*outioc)); 160 *outioc = NULL; 161 } else { 162 qio_channel_close(QIO_CHANNEL(sioc), NULL); 163 } 164 165 return ret; 166 } 167 168 return 0; 169 } 170 171 static void *connect_thread_func(void *opaque) 172 { 173 NBDClientConnection *conn = opaque; 174 int ret; 175 bool do_free; 176 uint64_t timeout = 1; 177 uint64_t max_timeout = 16; 178 179 qemu_mutex_lock(&conn->mutex); 180 while (!conn->detached) { 181 Error *local_err = NULL; 182 183 assert(!conn->sioc); 184 conn->sioc = qio_channel_socket_new(); 185 186 qemu_mutex_unlock(&conn->mutex); 187 188 conn->updated_info = conn->initial_info; 189 190 ret = nbd_connect(conn->sioc, conn->saddr, 191 conn->do_negotiation ? &conn->updated_info : NULL, 192 conn->tlscreds, conn->tlshostname, 193 &conn->ioc, &local_err); 194 195 /* 196 * conn->updated_info will finally be returned to the user. Clear the 197 * pointers to our internally allocated strings, which are IN parameters 198 * of nbd_receive_negotiate() and therefore nbd_connect(). Caller 199 * shouldn't be interested in these fields. 200 */ 201 conn->updated_info.x_dirty_bitmap = NULL; 202 conn->updated_info.name = NULL; 203 204 qemu_mutex_lock(&conn->mutex); 205 206 error_free(conn->err); 207 conn->err = NULL; 208 error_propagate(&conn->err, local_err); 209 210 if (ret < 0) { 211 object_unref(OBJECT(conn->sioc)); 212 conn->sioc = NULL; 213 if (conn->do_retry && !conn->detached) { 214 trace_nbd_connect_thread_sleep(timeout); 215 qemu_mutex_unlock(&conn->mutex); 216 217 sleep(timeout); 218 if (timeout < max_timeout) { 219 timeout *= 2; 220 } 221 222 qemu_mutex_lock(&conn->mutex); 223 continue; 224 } 225 } 226 227 break; 228 } 229 230 /* mutex is locked */ 231 232 assert(conn->running); 233 conn->running = false; 234 if (conn->wait_co) { 235 aio_co_wake(conn->wait_co); 236 conn->wait_co = NULL; 237 } 238 do_free = conn->detached; 239 240 qemu_mutex_unlock(&conn->mutex); 241 242 if (do_free) { 243 nbd_client_connection_do_free(conn); 244 } 245 246 return NULL; 247 } 248 249 void nbd_client_connection_release(NBDClientConnection *conn) 250 { 251 bool do_free = false; 252 253 if (!conn) { 254 return; 255 } 256 257 WITH_QEMU_LOCK_GUARD(&conn->mutex) { 258 assert(!conn->detached); 259 if (conn->running) { 260 conn->detached = true; 261 } else { 262 do_free = true; 263 } 264 if (conn->sioc) { 265 qio_channel_shutdown(QIO_CHANNEL(conn->sioc), 266 QIO_CHANNEL_SHUTDOWN_BOTH, NULL); 267 } 268 } 269 270 if (do_free) { 271 nbd_client_connection_do_free(conn); 272 } 273 } 274 275 /* 276 * Get a new connection in context of @conn: 277 * if the thread is running, wait for completion 278 * if the thread already succeeded in the background, and user didn't get the 279 * result, just return it now 280 * otherwise the thread is not running, so start a thread and wait for 281 * completion 282 * 283 * If @blocking is false, don't wait for the thread, return immediately. 284 * 285 * If @info is not NULL, also do nbd-negotiation after successful connection. 286 * In this case info is used only as out parameter, and is fully initialized by 287 * nbd_co_establish_connection(). "IN" fields of info as well as related only to 288 * nbd_receive_export_list() would be zero (see description of NBDExportInfo in 289 * include/block/nbd.h). 290 */ 291 QIOChannel *coroutine_fn 292 nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, 293 bool blocking, Error **errp) 294 { 295 QemuThread thread; 296 297 if (conn->do_negotiation) { 298 assert(info); 299 } 300 301 WITH_QEMU_LOCK_GUARD(&conn->mutex) { 302 /* 303 * Don't call nbd_co_establish_connection() in several coroutines in 304 * parallel. Only one call at once is supported. 305 */ 306 assert(!conn->wait_co); 307 308 if (!conn->running) { 309 if (conn->sioc) { 310 /* Previous attempt finally succeeded in background */ 311 if (conn->do_negotiation) { 312 memcpy(info, &conn->updated_info, sizeof(*info)); 313 if (conn->ioc) { 314 /* TLS channel now has own reference to parent */ 315 object_unref(OBJECT(conn->sioc)); 316 conn->sioc = NULL; 317 318 return g_steal_pointer(&conn->ioc); 319 } 320 } 321 322 assert(!conn->ioc); 323 324 return QIO_CHANNEL(g_steal_pointer(&conn->sioc)); 325 } 326 327 conn->running = true; 328 qemu_thread_create(&thread, "nbd-connect", 329 connect_thread_func, conn, QEMU_THREAD_DETACHED); 330 } 331 332 if (!blocking) { 333 if (conn->err) { 334 error_propagate(errp, error_copy(conn->err)); 335 } else { 336 error_setg(errp, "No connection at the moment"); 337 } 338 339 return NULL; 340 } 341 342 conn->wait_co = qemu_coroutine_self(); 343 } 344 345 /* 346 * We are going to wait for connect-thread finish, but 347 * nbd_co_establish_connection_cancel() can interrupt. 348 */ 349 qemu_coroutine_yield(); 350 351 WITH_QEMU_LOCK_GUARD(&conn->mutex) { 352 if (conn->running) { 353 /* 354 * The connection attempt was canceled and the coroutine resumed 355 * before the connection thread finished its job. Report the 356 * attempt as failed, but leave the connection thread running, 357 * to reuse it for the next connection attempt. 358 */ 359 if (conn->err) { 360 error_propagate(errp, error_copy(conn->err)); 361 } else { 362 /* 363 * The only possible case here is cancelling by open_timer 364 * during nbd_open(). So, the error message is for that case. 365 * If we have more use cases, we can refactor 366 * nbd_co_establish_connection_cancel() to take an additional 367 * parameter cancel_reason, that would be passed than to the 368 * caller of cancelled nbd_co_establish_connection(). 369 */ 370 error_setg(errp, "Connection attempt cancelled by timeout"); 371 } 372 373 return NULL; 374 } else { 375 /* Thread finished. There must be either error or sioc */ 376 assert(!conn->err != !conn->sioc); 377 378 if (conn->err) { 379 error_propagate(errp, error_copy(conn->err)); 380 return NULL; 381 } 382 383 if (conn->do_negotiation) { 384 memcpy(info, &conn->updated_info, sizeof(*info)); 385 if (conn->ioc) { 386 /* TLS channel now has own reference to parent */ 387 object_unref(OBJECT(conn->sioc)); 388 conn->sioc = NULL; 389 390 return g_steal_pointer(&conn->ioc); 391 } 392 } 393 394 assert(!conn->ioc); 395 396 return QIO_CHANNEL(g_steal_pointer(&conn->sioc)); 397 } 398 } 399 400 abort(); /* unreachable */ 401 } 402 403 /* 404 * nbd_co_establish_connection_cancel 405 * Cancel nbd_co_establish_connection() asynchronously. 406 * 407 * Note that this function neither directly stops the thread nor closes the 408 * socket, but rather safely wakes nbd_co_establish_connection() which is 409 * sleeping in yield() 410 */ 411 void nbd_co_establish_connection_cancel(NBDClientConnection *conn) 412 { 413 Coroutine *wait_co; 414 415 WITH_QEMU_LOCK_GUARD(&conn->mutex) { 416 wait_co = g_steal_pointer(&conn->wait_co); 417 } 418 419 if (wait_co) { 420 aio_co_wake(wait_co); 421 } 422 } 423