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 <vsementsov@virtuozzo.com>
Message-Id: <20210610100802.5888-21-vsementsov@virtuozzo.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: grammar tweak]
Signed-off-by: Eric Blake <eblake@redhat.com>
master
Vladimir Sementsov-Ogievskiy 2021-06-10 13:07:50 +03:00 committed by Eric Blake
parent 130d49baa5
commit e0e67cbe58
2 changed files with 45 additions and 13 deletions

View File

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

View File

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