From e0e67cbe58f42500e3451c46b3caba572f2a965f Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 10 Jun 2021 13:07:50 +0300 Subject: [PATCH] nbd/client-connection: implement connection retry Add an option for a thread to retry connecting until it succeeds. We'll use nbd/client-connection both for reconnect and for initial connection in nbd_open(), so we need a possibility to use same NBDClientConnection instance to connect once in nbd_open() and then use retry semantics for reconnect. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210610100802.5888-21-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweak] Signed-off-by: Eric Blake --- include/block/nbd.h | 2 ++ nbd/client-connection.c | 56 +++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/include/block/nbd.h b/include/block/nbd.h index 5d86e6a393..5bb54d831c 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -409,6 +409,8 @@ const char *nbd_err_lookup(int err); /* nbd/client-connection.c */ typedef struct NBDClientConnection NBDClientConnection; +void nbd_client_connection_enable_retry(NBDClientConnection *conn); + NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, bool do_negotiation, const char *export_name, diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 4ed37cd73f..032b38ed3e 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -35,6 +35,7 @@ struct NBDClientConnection { QCryptoTLSCreds *tlscreds; NBDExportInfo initial_info; bool do_negotiation; + bool do_retry; QemuMutex mutex; @@ -61,6 +62,15 @@ struct NBDClientConnection { Coroutine *wait_co; }; +/* + * The function isn't protected by any mutex, only call it when the client + * connection attempt has not yet started. + */ +void nbd_client_connection_enable_retry(NBDClientConnection *conn) +{ + conn->do_retry = true; +} + NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, bool do_negotiation, const char *export_name, @@ -155,24 +165,44 @@ static void *connect_thread_func(void *opaque) NBDClientConnection *conn = opaque; int ret; bool do_free; + uint64_t timeout = 1; + uint64_t max_timeout = 16; - conn->sioc = qio_channel_socket_new(); + while (true) { + conn->sioc = qio_channel_socket_new(); - error_free(conn->err); - conn->err = NULL; - conn->updated_info = conn->initial_info; + error_free(conn->err); + conn->err = NULL; + 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; + ret = nbd_connect(conn->sioc, conn->saddr, + conn->do_negotiation ? &conn->updated_info : NULL, + conn->tlscreds, &conn->ioc, &conn->err); + + /* + * conn->updated_info will finally be returned to the user. Clear the + * pointers to our internally allocated strings, which are IN parameters + * of nbd_receive_negotiate() and therefore nbd_connect(). Caller + * shoudn't be interested in these fields. + */ + conn->updated_info.x_dirty_bitmap = NULL; + conn->updated_info.name = NULL; + + if (ret < 0) { + object_unref(OBJECT(conn->sioc)); + conn->sioc = NULL; + if (conn->do_retry) { + sleep(timeout); + if (timeout < max_timeout) { + timeout *= 2; + } + continue; + } + } + + break; } - conn->updated_info.x_dirty_bitmap = NULL; - conn->updated_info.name = NULL; - qemu_mutex_lock(&conn->mutex); assert(conn->running);