static void nbd_teardown_connection(NbdClientSession *client) { struct nbd_request request = { .type = NBD_CMD_DISC, .from = 0, .len = 0 }; nbd_send_request(client->sock, &request); /* finish any pending coroutines */ shutdown(client->sock, 2); nbd_recv_coroutines_enter_all(client); qemu_aio_set_fd_handler(client->sock, NULL, NULL, NULL); closesocket(client->sock); client->sock = -1; } void nbd_client_session_close(NbdClientSession *client) { if (!client->bs) { return; } nbd_teardown_connection(client); client->bs = NULL; } int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, int sock, const char *export) { int ret; /* NBD handshake */ logout("session init %s\n", export); qemu_set_block(sock); ret = nbd_receive_negotiate(sock, export, &client->nbdflags, &client->size, &client->blocksize); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); closesocket(sock); return ret; } qemu_co_mutex_init(&client->send_mutex); qemu_co_mutex_init(&client->free_sema); client->bs = bs; client->sock = sock; /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ qemu_set_nonblock(sock); qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, client); logout("Established connection with NBD server\n"); return 0; }
static void nbd_teardown_connection(BlockDriverState *bs) { NbdClientSession *client = nbd_get_client_session(bs); /* finish any pending coroutines */ shutdown(client->sock, 2); nbd_recv_coroutines_enter_all(client); nbd_client_detach_aio_context(bs); closesocket(client->sock); client->sock = -1; }
static coroutine_fn void nbd_read_reply_entry(void *opaque) { NBDClientSession *s = opaque; uint64_t i; int ret; Error *local_err = NULL; for (;;) { assert(s->reply.handle == 0); ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); if (ret < 0) { error_report_err(local_err); } if (ret <= 0) { break; } /* There's no need for a mutex on the receive side, because the * handler acts as a synchronization point and ensures that only * one coroutine is called until the reply finishes. */ i = HANDLE_TO_INDEX(s, s->reply.handle); if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) { break; } /* We're woken up by the recv_coroutine itself. Note that there * is no race between yielding and reentering read_reply_co. This * is because: * * - if recv_coroutine[i] runs on the same AioContext, it is only * entered after we yield * * - if recv_coroutine[i] runs on a different AioContext, reentering * read_reply_co happens through a bottom half, which can only * run after we yield. */ aio_co_wake(s->recv_coroutine[i]); qemu_coroutine_yield(); } nbd_recv_coroutines_enter_all(s); s->read_reply_co = NULL; }
static void nbd_teardown_connection(BlockDriverState *bs) { NbdClientSession *client = nbd_get_client_session(bs); if (!client->ioc) { /* Already closed */ return; } /* finish any pending coroutines */ qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); nbd_recv_coroutines_enter_all(client); nbd_client_detach_aio_context(bs); object_unref(OBJECT(client->sioc)); client->sioc = NULL; object_unref(OBJECT(client->ioc)); client->ioc = NULL; }
static void nbd_reply_ready(void *opaque) { NbdClientSession *s = opaque; uint64_t i; int ret; if (s->reply.handle == 0) { /* No reply already in flight. Fetch a header. It is possible * that another thread has done the same thing in parallel, so * the socket is not readable anymore. */ ret = nbd_receive_reply(s->sock, &s->reply); if (ret == -EAGAIN) { return; } if (ret < 0) { s->reply.handle = 0; goto fail; } } /* There's no need for a mutex on the receive side, because the * handler acts as a synchronization point and ensures that only * one coroutine is called until the reply finishes. */ i = HANDLE_TO_INDEX(s, s->reply.handle); if (i >= MAX_NBD_REQUESTS) { goto fail; } if (s->recv_coroutine[i]) { qemu_coroutine_enter(s->recv_coroutine[i], NULL); return; } fail: nbd_recv_coroutines_enter_all(s); }