nbd/client-connection: add possibility of negotiation

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>
This commit is contained in:
Vladimir Sementsov-Ogievskiy 2021-06-10 13:07:49 +03:00 committed by Eric Blake
parent e70da5ff64
commit 130d49baa5
3 changed files with 109 additions and 9 deletions

View file

@ -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;
@ -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

View file

@ -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);

View file

@ -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;
@ -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 */
@ -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);
@ -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;
@ -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);
@ -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
@ -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);
}
@ -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);
}
}