mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
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:
parent
e70da5ff64
commit
130d49baa5
3 changed files with 109 additions and 9 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue