Skip to content

Commit

Permalink
nbd/client-connection: add possibility of negotiation
Browse files Browse the repository at this point in the history
Add arguments and logic to support nbd negotiation in the same thread
after successful connection.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20210610100802.5888-20-vsementsov@virtuozzo.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
  • Loading branch information
Vladimir Sementsov-Ogievskiy authored and ebblake committed Jun 18, 2021
1 parent e70da5f commit 130d49b
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 9 deletions.
4 changes: 2 additions & 2 deletions block/nbd.c
Expand Up @@ -357,7 +357,7 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s)
s->ioc = NULL;
}

s->sioc = nbd_co_establish_connection(s->conn, NULL);
s->sioc = nbd_co_establish_connection(s->conn, NULL, NULL, NULL);
if (!s->sioc) {
ret = -ECONNREFUSED;
goto out;
Expand Down Expand Up @@ -2035,7 +2035,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}

s->conn = nbd_client_connection_new(s->saddr);
s->conn = nbd_client_connection_new(s->saddr, false, NULL, NULL, NULL);

/*
* establish TCP connection, return error if it fails
Expand Down
9 changes: 7 additions & 2 deletions include/block/nbd.h
Expand Up @@ -409,11 +409,16 @@ const char *nbd_err_lookup(int err);
/* nbd/client-connection.c */
typedef struct NBDClientConnection NBDClientConnection;

NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr);
NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr,
bool do_negotiation,
const char *export_name,
const char *x_dirty_bitmap,
QCryptoTLSCreds *tlscreds);
void nbd_client_connection_release(NBDClientConnection *conn);

QIOChannelSocket *coroutine_fn
nbd_co_establish_connection(NBDClientConnection *conn, Error **errp);
nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info,
QIOChannel **ioc, Error **errp);

void coroutine_fn nbd_co_establish_connection_cancel(NBDClientConnection *conn);

Expand Down
105 changes: 100 additions & 5 deletions nbd/client-connection.c
Expand Up @@ -30,8 +30,11 @@
#include "qapi/clone-visitor.h"

struct NBDClientConnection {
/* Initialization constants */
/* Initialization constants, never change */
SocketAddress *saddr; /* address to connect to */
QCryptoTLSCreds *tlscreds;
NBDExportInfo initial_info;
bool do_negotiation;

QemuMutex mutex;

Expand All @@ -42,7 +45,9 @@ struct NBDClientConnection {
* nbd_co_establish_connection then steals these pointers while
* under the mutex.
*/
NBDExportInfo updated_info;
QIOChannelSocket *sioc;
QIOChannel *ioc;
Error *err;

/* All further fields are accessed only under mutex */
Expand All @@ -56,12 +61,25 @@ struct NBDClientConnection {
Coroutine *wait_co;
};

NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr)
NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr,
bool do_negotiation,
const char *export_name,
const char *x_dirty_bitmap,
QCryptoTLSCreds *tlscreds)
{
NBDClientConnection *conn = g_new(NBDClientConnection, 1);

object_ref(OBJECT(tlscreds));
*conn = (NBDClientConnection) {
.saddr = QAPI_CLONE(SocketAddress, saddr),
.tlscreds = tlscreds,
.do_negotiation = do_negotiation,

.initial_info.request_sizes = true,
.initial_info.structured_reply = true,
.initial_info.base_allocation = true,
.initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap),
.initial_info.name = g_strdup(export_name ?: "")
};

qemu_mutex_init(&conn->mutex);
Expand All @@ -77,9 +95,61 @@ static void nbd_client_connection_do_free(NBDClientConnection *conn)
}
error_free(conn->err);
qapi_free_SocketAddress(conn->saddr);
object_unref(OBJECT(conn->tlscreds));
g_free(conn->initial_info.x_dirty_bitmap);
g_free(conn->initial_info.name);
g_free(conn);
}

/*
* Connect to @addr and do NBD negotiation if @info is not null. If @tlscreds
* are given @outioc is returned. @outioc is provided only on success. The call
* may be cancelled from other thread by simply qio_channel_shutdown(sioc).
*/
static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr,
NBDExportInfo *info, QCryptoTLSCreds *tlscreds,
QIOChannel **outioc, Error **errp)
{
int ret;

if (outioc) {
*outioc = NULL;
}

ret = qio_channel_socket_connect_sync(sioc, addr, errp);
if (ret < 0) {
return ret;
}

qio_channel_set_delay(QIO_CHANNEL(sioc), false);

if (!info) {
return 0;
}

ret = nbd_receive_negotiate(NULL, QIO_CHANNEL(sioc), tlscreds,
tlscreds ? addr->u.inet.host : NULL,
outioc, info, errp);
if (ret < 0) {
/*
* nbd_receive_negotiate() may setup tls ioc and return it even on
* failure path. In this case we should use it instead of original
* channel.
*/
if (outioc && *outioc) {
qio_channel_close(QIO_CHANNEL(*outioc), NULL);
object_unref(OBJECT(*outioc));
*outioc = NULL;
} else {
qio_channel_close(QIO_CHANNEL(sioc), NULL);
}

return ret;
}

return 0;
}

static void *connect_thread_func(void *opaque)
{
NBDClientConnection *conn = opaque;
Expand All @@ -90,13 +160,18 @@ static void *connect_thread_func(void *opaque)

error_free(conn->err);
conn->err = NULL;
ret = qio_channel_socket_connect_sync(conn->sioc, conn->saddr, &conn->err);
conn->updated_info = conn->initial_info;

ret = nbd_connect(conn->sioc, conn->saddr,
conn->do_negotiation ? &conn->updated_info : NULL,
conn->tlscreds, &conn->ioc, &conn->err);
if (ret < 0) {
object_unref(OBJECT(conn->sioc));
conn->sioc = NULL;
}

qio_channel_set_delay(QIO_CHANNEL(conn->sioc), false);
conn->updated_info.x_dirty_bitmap = NULL;
conn->updated_info.name = NULL;

qemu_mutex_lock(&conn->mutex);

Expand Down Expand Up @@ -146,12 +221,24 @@ void nbd_client_connection_release(NBDClientConnection *conn)
* result, just return it now
* otherwise the thread is not running, so start a thread and wait for
* completion
*
* If @info is not NULL, also do nbd-negotiation after successful connection.
* In this case info is used only as out parameter, and is fully initialized by
* nbd_co_establish_connection(). "IN" fields of info as well as related only to
* nbd_receive_export_list() would be zero (see description of NBDExportInfo in
* include/block/nbd.h).
*/
QIOChannelSocket *coroutine_fn
nbd_co_establish_connection(NBDClientConnection *conn, Error **errp)
nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info,
QIOChannel **ioc, Error **errp)
{
QemuThread thread;

if (conn->do_negotiation) {
assert(info);
assert(ioc);
}

WITH_QEMU_LOCK_GUARD(&conn->mutex) {
/*
* Don't call nbd_co_establish_connection() in several coroutines in
Expand All @@ -162,6 +249,10 @@ nbd_co_establish_connection(NBDClientConnection *conn, Error **errp)
if (!conn->running) {
if (conn->sioc) {
/* Previous attempt finally succeeded in background */
if (conn->do_negotiation) {
*ioc = g_steal_pointer(&conn->ioc);
memcpy(info, &conn->updated_info, sizeof(*info));
}
return g_steal_pointer(&conn->sioc);
}

Expand Down Expand Up @@ -194,6 +285,10 @@ nbd_co_establish_connection(NBDClientConnection *conn, Error **errp)
} else {
error_propagate(errp, conn->err);
conn->err = NULL;
if (conn->sioc && conn->do_negotiation) {
*ioc = g_steal_pointer(&conn->ioc);
memcpy(info, &conn->updated_info, sizeof(*info));
}
return g_steal_pointer(&conn->sioc);
}
}
Expand Down

0 comments on commit 130d49b

Please sign in to comment.