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