From 064097d919508e8636b220baedb52b382b9b07c6 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 10 Feb 2016 18:41:01 +0000 Subject: [PATCH] nbd: convert block client to use I/O channels for connection setup This converts the NBD block driver client to use the QIOChannelSocket class for initial connection setup. The NbdClientSession struct has two pointers, one to the master QIOChannelSocket providing the raw data channel, and one to a QIOChannel which is the current channel used for I/O. Initially the two point to the same object, but when TLS support is added, they will point to different objects. The qemu-img & qemu-io tools now need to use MODULE_INIT_QOM to ensure the QIOChannel object classes are registered. The qemu-nbd tool already did this. In this initial conversion though, all I/O is still actually done using the raw POSIX sockets APIs. Signed-off-by: Daniel P. Berrange Message-Id: <1455129674-17255-4-git-send-email-berrange@redhat.com> Signed-off-by: Paolo Bonzini --- Makefile | 6 ++-- block/nbd-client.c | 76 ++++++++++++++++++++++++++++------------------ block/nbd-client.h | 8 +++-- block/nbd.c | 39 ++++++++++++------------ qemu-img.c | 1 + qemu-io.c | 1 + tests/Makefile | 2 +- 7 files changed, 79 insertions(+), 54 deletions(-) diff --git a/Makefile b/Makefile index f9fae3aa16..16db398c6f 100644 --- a/Makefile +++ b/Makefile @@ -234,9 +234,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' qemu-img.o: qemu-img-cmds.h -qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a -qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o diff --git a/block/nbd-client.c b/block/nbd-client.c index 568c56cbb6..c4379a9821 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "nbd-client.h" -#include "qemu/sockets.h" #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) #define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs)) @@ -48,13 +47,21 @@ static void nbd_teardown_connection(BlockDriverState *bs) { NbdClientSession *client = nbd_get_client_session(bs); + if (!client->ioc) { /* Already closed */ + return; + } + /* finish any pending coroutines */ - shutdown(client->sock, 2); + qio_channel_shutdown(client->ioc, + QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); nbd_recv_coroutines_enter_all(client); nbd_client_detach_aio_context(bs); - closesocket(client->sock); - client->sock = -1; + object_unref(OBJECT(client->sioc)); + client->sioc = NULL; + object_unref(OBJECT(client->ioc)); + client->ioc = NULL; } static void nbd_reply_ready(void *opaque) @@ -64,12 +71,16 @@ static void nbd_reply_ready(void *opaque) uint64_t i; int ret; + if (!s->ioc) { /* Already closed */ + return; + } + 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); + ret = nbd_receive_reply(s->sioc->fd, &s->reply); if (ret == -EAGAIN) { return; } @@ -122,30 +133,32 @@ static int nbd_co_send_request(BlockDriverState *bs, assert(i < MAX_NBD_REQUESTS); request->handle = INDEX_TO_HANDLE(s, i); + + if (!s->ioc) { + qemu_co_mutex_unlock(&s->send_mutex); + return -EPIPE; + } + s->send_coroutine = qemu_coroutine_self(); aio_context = bdrv_get_aio_context(bs); - aio_set_fd_handler(aio_context, s->sock, false, + aio_set_fd_handler(aio_context, s->sioc->fd, false, nbd_reply_ready, nbd_restart_write, bs); if (qiov) { - if (!s->is_unix) { - socket_set_cork(s->sock, 1); - } - rc = nbd_send_request(s->sock, request); + qio_channel_set_cork(s->ioc, true); + rc = nbd_send_request(s->sioc->fd, request); if (rc >= 0) { - ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov, + ret = qemu_co_sendv(s->sioc->fd, qiov->iov, qiov->niov, offset, request->len); if (ret != request->len) { rc = -EIO; } } - if (!s->is_unix) { - socket_set_cork(s->sock, 0); - } + qio_channel_set_cork(s->ioc, false); } else { - rc = nbd_send_request(s->sock, request); + rc = nbd_send_request(s->sioc->fd, request); } - aio_set_fd_handler(aio_context, s->sock, false, + aio_set_fd_handler(aio_context, s->sioc->fd, false, nbd_reply_ready, NULL, bs); s->send_coroutine = NULL; qemu_co_mutex_unlock(&s->send_mutex); @@ -162,11 +175,12 @@ static void nbd_co_receive_reply(NbdClientSession *s, * peek at the next reply and avoid yielding if it's ours? */ qemu_coroutine_yield(); *reply = s->reply; - if (reply->handle != request->handle) { + if (reply->handle != request->handle || + !s->ioc) { reply->error = EIO; } else { if (qiov && reply->error == 0) { - ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov, + ret = qemu_co_recvv(s->sioc->fd, qiov->iov, qiov->niov, offset, request->len); if (ret != request->len) { reply->error = EIO; @@ -350,14 +364,14 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, void nbd_client_detach_aio_context(BlockDriverState *bs) { aio_set_fd_handler(bdrv_get_aio_context(bs), - nbd_get_client_session(bs)->sock, + nbd_get_client_session(bs)->sioc->fd, false, NULL, NULL, NULL); } void nbd_client_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { - aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock, + aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd, false, nbd_reply_ready, NULL, bs); } @@ -370,39 +384,43 @@ void nbd_client_close(BlockDriverState *bs) .len = 0 }; - if (client->sock == -1) { + if (client->ioc == NULL) { return; } - nbd_send_request(client->sock, &request); + nbd_send_request(client->sioc->fd, &request); nbd_teardown_connection(bs); } -int nbd_client_init(BlockDriverState *bs, int sock, const char *export, - Error **errp) +int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc, + const char *export, Error **errp) { NbdClientSession *client = nbd_get_client_session(bs); int ret; /* NBD handshake */ logout("session init %s\n", export); - qemu_set_block(sock); - ret = nbd_receive_negotiate(sock, export, + qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); + + ret = nbd_receive_negotiate(sioc->fd, export, &client->nbdflags, &client->size, errp); 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->sock = sock; + client->sioc = sioc; + object_ref(OBJECT(client->sioc)); + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ - qemu_set_nonblock(sock); + qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); + nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); logout("Established connection with NBD server\n"); diff --git a/block/nbd-client.h b/block/nbd-client.h index e8413408b5..e8b3283382 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -4,6 +4,7 @@ #include "qemu-common.h" #include "block/nbd.h" #include "block/block_int.h" +#include "io/channel-socket.h" /* #define DEBUG_NBD */ @@ -17,7 +18,8 @@ #define MAX_NBD_REQUESTS 16 typedef struct NbdClientSession { - int sock; + QIOChannelSocket *sioc; /* The master data channel */ + QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ uint32_t nbdflags; off_t size; @@ -34,7 +36,9 @@ typedef struct NbdClientSession { NbdClientSession *nbd_get_client_session(BlockDriverState *bs); -int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name, +int nbd_client_init(BlockDriverState *bs, + QIOChannelSocket *sock, + const char *export_name, Error **errp); void nbd_client_close(BlockDriverState *bs); diff --git a/block/nbd.c b/block/nbd.c index 1a90bc7855..d7116e2f6b 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -31,7 +31,6 @@ #include "qemu/uri.h" #include "block/block_int.h" #include "qemu/module.h" -#include "qemu/sockets.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qint.h" @@ -238,25 +237,25 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs) return &s->client; } -static int nbd_establish_connection(BlockDriverState *bs, - SocketAddress *saddr, - Error **errp) +static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, + Error **errp) { - BDRVNBDState *s = bs->opaque; - int sock; + QIOChannelSocket *sioc; + Error *local_err = NULL; - sock = socket_connect(saddr, errp, NULL, NULL); + sioc = qio_channel_socket_new(); - if (sock < 0) { - logout("Failed to establish connection to NBD server\n"); - return -EIO; + qio_channel_socket_connect_sync(sioc, + saddr, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; } - if (!s->client.is_unix) { - socket_set_nodelay(sock); - } + qio_channel_set_delay(QIO_CHANNEL(sioc), false); - return sock; + return sioc; } static int nbd_open(BlockDriverState *bs, QDict *options, int flags, @@ -264,7 +263,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, { BDRVNBDState *s = bs->opaque; char *export = NULL; - int result, sock; + int result; + QIOChannelSocket *sioc; SocketAddress *saddr; /* Pop the config into our state object. Exit if invalid. */ @@ -276,15 +276,16 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sock = nbd_establish_connection(bs, saddr, errp); + sioc = nbd_establish_connection(saddr, errp); qapi_free_SocketAddress(saddr); - if (sock < 0) { + if (!sioc) { g_free(export); - return sock; + return -ECONNREFUSED; } /* NBD handshake */ - result = nbd_client_init(bs, sock, export, errp); + result = nbd_client_init(bs, sioc, export, errp); + object_unref(OBJECT(sioc)); g_free(export); return result; } diff --git a/qemu-img.c b/qemu-img.c index 163d8c1664..7030107da2 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3104,6 +3104,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + module_call_init(MODULE_INIT_QOM); bdrv_init(); if (argc < 2) { error_exit("Not enough arguments"); diff --git a/qemu-io.c b/qemu-io.c index 83c48f4149..6c0c028f98 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -394,6 +394,7 @@ int main(int argc, char **argv) progname = basename(argv[0]); qemu_init_exec_dir(argv[0]); + module_call_init(MODULE_INIT_QOM); bdrv_init(); while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { diff --git a/tests/Makefile b/tests/Makefile index 650e654ec2..c1c605fc63 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -390,8 +390,8 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ tests/test-qapi-event.o tests/test-qmp-introspect.o \ $(test-qom-obj-y) test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) -test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y) test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y) +test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y) tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)