[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH nbd 2/4] nbd: Split out block device state from underlying NBD co
From: |
Richard W.M. Jones |
Subject: |
[PATCH nbd 2/4] nbd: Split out block device state from underlying NBD connections |
Date: |
Thu, 9 Mar 2023 11:39:44 +0000 |
To implement multi-conn, we will put multiple underlying NBD
connections (ie. NBDClientConnection) inside the NBD block device
handle (BDRVNBDState). This requires first breaking the one-to-one
relationship between NBDClientConnection and BDRVNBDState.
To do this a new structure (NBDConnState) is implemented.
NBDConnState takes all the per-connection state out of the block
driver struct. BDRVNBDState now contains a conns[] array of pointers
to NBDConnState, one for each underlying multi-conn connection.
After this change there is still a one-to-one relationship because we
only ever use the zeroth slot in the conns[] array. Thus this does
not implement multi-conn yet.
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
---
block/coroutines.h | 5 +-
block/nbd.c | 674 ++++++++++++++++++++++++---------------------
2 files changed, 358 insertions(+), 321 deletions(-)
diff --git a/block/coroutines.h b/block/coroutines.h
index dd9f3d449b..14b01d8591 100644
--- a/block/coroutines.h
+++ b/block/coroutines.h
@@ -62,7 +62,7 @@ int coroutine_fn GRAPH_RDLOCK
bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
int coroutine_fn
-nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
+nbd_co_do_establish_connection(BlockDriverState *bs, void *cs, bool blocking,
Error **errp);
@@ -86,6 +86,7 @@ bdrv_common_block_status_above(BlockDriverState *bs,
BlockDriverState **file,
int *depth);
int co_wrapper_mixed
-nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp);
+nbd_do_establish_connection(BlockDriverState *bs, void *cs, bool blocking,
+ Error **errp);
#endif /* BLOCK_COROUTINES_H */
diff --git a/block/nbd.c b/block/nbd.c
index 5ffae0b798..84e8a1add0 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -51,8 +51,8 @@
#define MAX_NBD_REQUESTS 16
#define MAX_MULTI_CONN 16
-#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ (uint64_t)(intptr_t)(bs))
-#define INDEX_TO_HANDLE(bs, index) ((index) ^ (uint64_t)(intptr_t)(bs))
+#define HANDLE_TO_INDEX(cs, handle) ((handle) ^ (uint64_t)(intptr_t)(cs))
+#define INDEX_TO_HANDLE(cs, index) ((index) ^ (uint64_t)(intptr_t)(cs))
typedef struct {
Coroutine *coroutine;
@@ -67,7 +67,10 @@ typedef enum NBDClientState {
NBD_CLIENT_QUIT
} NBDClientState;
-typedef struct BDRVNBDState {
+/* A single client connection. */
+typedef struct NBDConnState {
+ struct BDRVNBDState *s; /* Points to enclosing BDRVNBDState */
+
QIOChannel *ioc; /* The current I/O channel */
NBDExportInfo info;
@@ -94,7 +97,12 @@ typedef struct BDRVNBDState {
QEMUTimer *open_timer;
- BlockDriverState *bs;
+ NBDClientConnection *conn;
+} NBDConnState;
+
+typedef struct BDRVNBDState {
+ /* The underlying NBD connections */
+ NBDConnState *conns[MAX_MULTI_CONN];
/* Connection parameters */
uint32_t reconnect_delay;
@@ -108,7 +116,7 @@ typedef struct BDRVNBDState {
char *x_dirty_bitmap;
bool alloc_depth;
- NBDClientConnection *conn;
+ BlockDriverState *bs;
} BDRVNBDState;
static void nbd_yank(void *opaque);
@@ -117,14 +125,16 @@ static void nbd_clear_bdrvstate(BlockDriverState *bs)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
- nbd_client_connection_release(s->conn);
- s->conn = NULL;
+ nbd_client_connection_release(s->conns[0]->conn);
+ s->conns[0]->conn = NULL;
yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name));
/* Must not leave timers behind that would access freed data */
- assert(!s->reconnect_delay_timer);
- assert(!s->open_timer);
+ assert(!s->conns[0]->reconnect_delay_timer);
+ assert(!s->conns[0]->open_timer);
+
+ g_free(s->conns[0]);
object_unref(OBJECT(s->tlscreds));
qapi_free_SocketAddress(s->saddr);
@@ -151,139 +161,143 @@ static bool coroutine_fn
nbd_recv_coroutine_wake_one(NBDClientRequest *req)
return false;
}
-static void coroutine_fn nbd_recv_coroutines_wake(BDRVNBDState *s)
+static void coroutine_fn nbd_recv_coroutines_wake(NBDConnState *cs)
{
int i;
- QEMU_LOCK_GUARD(&s->receive_mutex);
+ QEMU_LOCK_GUARD(&cs->receive_mutex);
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
- if (nbd_recv_coroutine_wake_one(&s->requests[i])) {
+ if (nbd_recv_coroutine_wake_one(&cs->requests[i])) {
return;
}
}
}
/* Called with s->requests_lock held. */
-static void coroutine_fn nbd_channel_error_locked(BDRVNBDState *s, int ret)
+static void coroutine_fn nbd_channel_error_locked(NBDConnState *cs, int ret)
{
- if (s->state == NBD_CLIENT_CONNECTED) {
- qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ if (cs->state == NBD_CLIENT_CONNECTED) {
+ qio_channel_shutdown(cs->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
}
if (ret == -EIO) {
- if (s->state == NBD_CLIENT_CONNECTED) {
- s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT :
- NBD_CLIENT_CONNECTING_NOWAIT;
+ if (cs->state == NBD_CLIENT_CONNECTED) {
+ cs->state = cs->s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT :
+ NBD_CLIENT_CONNECTING_NOWAIT;
}
} else {
- s->state = NBD_CLIENT_QUIT;
+ cs->state = NBD_CLIENT_QUIT;
}
}
-static void coroutine_fn nbd_channel_error(BDRVNBDState *s, int ret)
+static void coroutine_fn nbd_channel_error(NBDConnState *cs, int ret)
{
- QEMU_LOCK_GUARD(&s->requests_lock);
- nbd_channel_error_locked(s, ret);
+ QEMU_LOCK_GUARD(&cs->requests_lock);
+ nbd_channel_error_locked(cs, ret);
}
-static void reconnect_delay_timer_del(BDRVNBDState *s)
+static void reconnect_delay_timer_del(NBDConnState *cs)
{
- if (s->reconnect_delay_timer) {
- timer_free(s->reconnect_delay_timer);
- s->reconnect_delay_timer = NULL;
+ if (cs->reconnect_delay_timer) {
+ timer_free(cs->reconnect_delay_timer);
+ cs->reconnect_delay_timer = NULL;
}
}
static void reconnect_delay_timer_cb(void *opaque)
{
- BDRVNBDState *s = opaque;
+ NBDConnState *cs = opaque;
- reconnect_delay_timer_del(s);
- WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
- if (s->state != NBD_CLIENT_CONNECTING_WAIT) {
+ reconnect_delay_timer_del(cs);
+ WITH_QEMU_LOCK_GUARD(&cs->requests_lock) {
+ if (cs->state != NBD_CLIENT_CONNECTING_WAIT) {
return;
}
- s->state = NBD_CLIENT_CONNECTING_NOWAIT;
+ cs->state = NBD_CLIENT_CONNECTING_NOWAIT;
}
- nbd_co_establish_connection_cancel(s->conn);
+ nbd_co_establish_connection_cancel(cs->conn);
}
-static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t
expire_time_ns)
+static void reconnect_delay_timer_init(NBDConnState *cs,
+ uint64_t expire_time_ns)
{
- assert(!s->reconnect_delay_timer);
- s->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(s->bs),
- QEMU_CLOCK_REALTIME,
- SCALE_NS,
- reconnect_delay_timer_cb, s);
- timer_mod(s->reconnect_delay_timer, expire_time_ns);
+ assert(!cs->reconnect_delay_timer);
+ cs->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(cs->s->bs),
+ QEMU_CLOCK_REALTIME,
+ SCALE_NS,
+ reconnect_delay_timer_cb, cs);
+ timer_mod(cs->reconnect_delay_timer, expire_time_ns);
}
-static void nbd_teardown_connection(BlockDriverState *bs)
+static void nbd_teardown_connection(NBDConnState *cs)
{
- BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
+ assert(!cs->in_flight);
- assert(!s->in_flight);
-
- if (s->ioc) {
- qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
- yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
- nbd_yank, s->bs);
- object_unref(OBJECT(s->ioc));
- s->ioc = NULL;
+ if (cs->ioc) {
+ qio_channel_shutdown(cs->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(cs->s->bs->node_name),
+ nbd_yank, cs);
+ object_unref(OBJECT(cs->ioc));
+ cs->ioc = NULL;
}
- WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
- s->state = NBD_CLIENT_QUIT;
+ WITH_QEMU_LOCK_GUARD(&cs->requests_lock) {
+ cs->state = NBD_CLIENT_QUIT;
}
}
-static void open_timer_del(BDRVNBDState *s)
+static void open_timer_del(NBDConnState *cs)
{
- if (s->open_timer) {
- timer_free(s->open_timer);
- s->open_timer = NULL;
+ if (cs->open_timer) {
+ timer_free(cs->open_timer);
+ cs->open_timer = NULL;
}
}
static void open_timer_cb(void *opaque)
{
- BDRVNBDState *s = opaque;
+ NBDConnState *cs = opaque;
- nbd_co_establish_connection_cancel(s->conn);
- open_timer_del(s);
+ nbd_co_establish_connection_cancel(cs->conn);
+ open_timer_del(cs);
}
-static void open_timer_init(BDRVNBDState *s, uint64_t expire_time_ns)
+static void open_timer_init(NBDConnState *cs, uint64_t expire_time_ns)
{
- assert(!s->open_timer);
- s->open_timer = aio_timer_new(bdrv_get_aio_context(s->bs),
- QEMU_CLOCK_REALTIME,
- SCALE_NS,
- open_timer_cb, s);
- timer_mod(s->open_timer, expire_time_ns);
+ assert(!cs->open_timer);
+ cs->open_timer = aio_timer_new(bdrv_get_aio_context(cs->s->bs),
+ QEMU_CLOCK_REALTIME,
+ SCALE_NS,
+ open_timer_cb, cs);
+ timer_mod(cs->open_timer, expire_time_ns);
}
-static bool nbd_client_will_reconnect(BDRVNBDState *s)
+static bool nbd_client_will_reconnect(NBDConnState *cs)
{
/*
* Called only after a socket error, so this is not performance sensitive.
*/
- QEMU_LOCK_GUARD(&s->requests_lock);
- return s->state == NBD_CLIENT_CONNECTING_WAIT;
+ QEMU_LOCK_GUARD(&cs->requests_lock);
+ return cs->state == NBD_CLIENT_CONNECTING_WAIT;
}
/*
* Update @bs with information learned during a completed negotiation process.
* Return failure if the server's advertised options are incompatible with the
* client's needs.
+ *
+ * Note that we are only called for the first connection (s->conns[0])
+ * because multi-conn assumes that all other connections are alike.
+ * We don't check that assumption but probably should (XXX).
*/
-static int nbd_handle_updated_info(BlockDriverState *bs, Error **errp)
+static int nbd_handle_updated_info(BlockDriverState *bs,
+ NBDConnState *cs, Error **errp)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
int ret;
if (s->x_dirty_bitmap) {
- if (!s->info.base_allocation) {
+ if (!cs->info.base_allocation) {
error_setg(errp, "requested x-dirty-bitmap %s not found",
s->x_dirty_bitmap);
return -EINVAL;
@@ -293,21 +307,21 @@ static int nbd_handle_updated_info(BlockDriverState *bs,
Error **errp)
}
}
- if (s->info.flags & NBD_FLAG_READ_ONLY) {
+ if (cs->info.flags & NBD_FLAG_READ_ONLY) {
ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
if (ret < 0) {
return ret;
}
}
- if (s->info.flags & NBD_FLAG_SEND_FUA) {
+ if (cs->info.flags & NBD_FLAG_SEND_FUA) {
bs->supported_write_flags = BDRV_REQ_FUA;
bs->supported_zero_flags |= BDRV_REQ_FUA;
}
- if (s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) {
+ if (cs->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) {
bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP;
- if (s->info.flags & NBD_FLAG_SEND_FAST_ZERO) {
+ if (cs->info.flags & NBD_FLAG_SEND_FAST_ZERO) {
bs->supported_zero_flags |= BDRV_REQ_NO_FALLBACK;
}
}
@@ -318,129 +332,132 @@ static int nbd_handle_updated_info(BlockDriverState
*bs, Error **errp)
}
int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
+ void *csvp,
bool blocking, Error **errp)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
+ NBDConnState *cs = csvp;
int ret;
IO_CODE();
- assert(!s->ioc);
+ assert(!cs->ioc);
- s->ioc = nbd_co_establish_connection(s->conn, &s->info, blocking, errp);
- if (!s->ioc) {
+ cs->ioc = nbd_co_establish_connection(cs->conn, &cs->info, blocking, errp);
+ if (!cs->ioc) {
return -ECONNREFUSED;
}
- yank_register_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank,
- bs);
+ yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), nbd_yank,
cs);
- ret = nbd_handle_updated_info(s->bs, NULL);
- if (ret < 0) {
- /*
- * We have connected, but must fail for other reasons.
- * Send NBD_CMD_DISC as a courtesy to the server.
- */
- NBDRequest request = { .type = NBD_CMD_DISC };
+ if (cs == s->conns[0]) {
+ ret = nbd_handle_updated_info(s->bs, cs, NULL);
+ if (ret < 0) {
+ /*
+ * We have connected, but must fail for other reasons.
+ * Send NBD_CMD_DISC as a courtesy to the server.
+ */
+ NBDRequest request = { .type = NBD_CMD_DISC };
- nbd_send_request(s->ioc, &request);
+ nbd_send_request(cs->ioc, &request);
- yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
- nbd_yank, bs);
- object_unref(OBJECT(s->ioc));
- s->ioc = NULL;
+ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
+ nbd_yank, cs);
+ object_unref(OBJECT(cs->ioc));
+ cs->ioc = NULL;
- return ret;
+ return ret;
+ }
}
- qio_channel_set_blocking(s->ioc, false, NULL);
- qio_channel_attach_aio_context(s->ioc, bdrv_get_aio_context(bs));
+ qio_channel_set_blocking(cs->ioc, false, NULL);
+ qio_channel_attach_aio_context(cs->ioc, bdrv_get_aio_context(bs));
/* successfully connected */
- WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
- s->state = NBD_CLIENT_CONNECTED;
+ WITH_QEMU_LOCK_GUARD(&cs->requests_lock) {
+ cs->state = NBD_CLIENT_CONNECTED;
}
return 0;
}
/* Called with s->requests_lock held. */
-static bool nbd_client_connecting(BDRVNBDState *s)
+static bool nbd_client_connecting(NBDConnState *cs)
{
- return s->state == NBD_CLIENT_CONNECTING_WAIT ||
- s->state == NBD_CLIENT_CONNECTING_NOWAIT;
+ return cs->state == NBD_CLIENT_CONNECTING_WAIT ||
+ cs->state == NBD_CLIENT_CONNECTING_NOWAIT;
}
/* Called with s->requests_lock taken. */
-static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s)
+static coroutine_fn void nbd_reconnect_attempt(NBDConnState *cs)
{
int ret;
- bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT;
+ bool blocking = cs->state == NBD_CLIENT_CONNECTING_WAIT;
/*
* Now we are sure that nobody is accessing the channel, and no one will
* try until we set the state to CONNECTED.
*/
- assert(nbd_client_connecting(s));
- assert(s->in_flight == 1);
+ assert(nbd_client_connecting(cs));
+ assert(cs->in_flight == 1);
- trace_nbd_reconnect_attempt(s->bs->in_flight);
+ trace_nbd_reconnect_attempt(cs->s->bs->in_flight);
- if (blocking && !s->reconnect_delay_timer) {
+ if (blocking && !cs->reconnect_delay_timer) {
/*
* It's the first reconnect attempt after switching to
* NBD_CLIENT_CONNECTING_WAIT
*/
- g_assert(s->reconnect_delay);
- reconnect_delay_timer_init(s,
+ g_assert(cs->s->reconnect_delay);
+ reconnect_delay_timer_init(cs,
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
- s->reconnect_delay * NANOSECONDS_PER_SECOND);
+ cs->s->reconnect_delay * NANOSECONDS_PER_SECOND);
}
/* Finalize previous connection if any */
- if (s->ioc) {
- qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc));
- yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
- nbd_yank, s->bs);
- object_unref(OBJECT(s->ioc));
- s->ioc = NULL;
+ if (cs->ioc) {
+ qio_channel_detach_aio_context(QIO_CHANNEL(cs->ioc));
+ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(cs->s->bs->node_name),
+ nbd_yank, cs);
+ object_unref(OBJECT(cs->ioc));
+ cs->ioc = NULL;
}
- qemu_mutex_unlock(&s->requests_lock);
- ret = nbd_co_do_establish_connection(s->bs, blocking, NULL);
- trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight);
- qemu_mutex_lock(&s->requests_lock);
+ qemu_mutex_unlock(&cs->requests_lock);
+ ret = nbd_co_do_establish_connection(cs->s->bs, cs, blocking, NULL);
+ trace_nbd_reconnect_attempt_result(ret, cs->s->bs->in_flight);
+ qemu_mutex_lock(&cs->requests_lock);
/*
* The reconnect attempt is done (maybe successfully, maybe not), so
* we no longer need this timer. Delete it so it will not outlive
* this I/O request (so draining removes all timers).
*/
- reconnect_delay_timer_del(s);
+ reconnect_delay_timer_del(cs);
}
-static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle)
+static coroutine_fn int nbd_receive_replies(NBDConnState *cs, uint64_t handle)
{
int ret;
- uint64_t ind = HANDLE_TO_INDEX(s, handle), ind2;
- QEMU_LOCK_GUARD(&s->receive_mutex);
+ uint64_t ind = HANDLE_TO_INDEX(cs, handle), ind2;
+ QEMU_LOCK_GUARD(&cs->receive_mutex);
while (true) {
- if (s->reply.handle == handle) {
+ if (cs->reply.handle == handle) {
/* We are done */
return 0;
}
- if (s->reply.handle != 0) {
+ if (cs->reply.handle != 0) {
/*
* Some other request is being handled now. It should already be
- * woken by whoever set s->reply.handle (or never wait in this
+ * woken by whoever set cs->reply.handle (or never wait in this
* yield). So, we should not wake it here.
*/
- ind2 = HANDLE_TO_INDEX(s, s->reply.handle);
- assert(!s->requests[ind2].receiving);
+ ind2 = HANDLE_TO_INDEX(cs, cs->reply.handle);
+ assert(!cs->requests[ind2].receiving);
- s->requests[ind].receiving = true;
- qemu_co_mutex_unlock(&s->receive_mutex);
+ cs->requests[ind].receiving = true;
+ qemu_co_mutex_unlock(&cs->receive_mutex);
qemu_coroutine_yield();
/*
@@ -448,105 +465,105 @@ static coroutine_fn int
nbd_receive_replies(BDRVNBDState *s, uint64_t handle)
* 1. From this function, executing in parallel coroutine, when our
* handle is received.
* 2. From nbd_co_receive_one_chunk(), when previous request is
- * finished and s->reply.handle set to 0.
+ * finished and cs->reply.handle set to 0.
* Anyway, it's OK to lock the mutex and go to the next iteration.
*/
- qemu_co_mutex_lock(&s->receive_mutex);
- assert(!s->requests[ind].receiving);
+ qemu_co_mutex_lock(&cs->receive_mutex);
+ assert(!cs->requests[ind].receiving);
continue;
}
/* We are under mutex and handle is 0. We have to do the dirty work. */
- assert(s->reply.handle == 0);
- ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, NULL);
+ assert(cs->reply.handle == 0);
+ ret = nbd_receive_reply(cs->s->bs, cs->ioc, &cs->reply, NULL);
if (ret <= 0) {
ret = ret ? ret : -EIO;
- nbd_channel_error(s, ret);
+ nbd_channel_error(cs, ret);
return ret;
}
- if (nbd_reply_is_structured(&s->reply) && !s->info.structured_reply) {
- nbd_channel_error(s, -EINVAL);
+ if (nbd_reply_is_structured(&cs->reply) &&
+ !cs->info.structured_reply) {
+ nbd_channel_error(cs, -EINVAL);
return -EINVAL;
}
- ind2 = HANDLE_TO_INDEX(s, s->reply.handle);
- if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) {
- nbd_channel_error(s, -EINVAL);
+ ind2 = HANDLE_TO_INDEX(cs, cs->reply.handle);
+ if (ind2 >= MAX_NBD_REQUESTS || !cs->requests[ind2].coroutine) {
+ nbd_channel_error(cs, -EINVAL);
return -EINVAL;
}
- if (s->reply.handle == handle) {
+ if (cs->reply.handle == handle) {
/* We are done */
return 0;
}
- nbd_recv_coroutine_wake_one(&s->requests[ind2]);
+ nbd_recv_coroutine_wake_one(&cs->requests[ind2]);
}
}
-static int coroutine_fn nbd_co_send_request(BlockDriverState *bs,
+static int coroutine_fn nbd_co_send_request(NBDConnState *cs,
NBDRequest *request,
QEMUIOVector *qiov)
{
- BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
int rc, i = -1;
- qemu_mutex_lock(&s->requests_lock);
- while (s->in_flight == MAX_NBD_REQUESTS ||
- (s->state != NBD_CLIENT_CONNECTED && s->in_flight > 0)) {
- qemu_co_queue_wait(&s->free_sema, &s->requests_lock);
+ qemu_mutex_lock(&cs->requests_lock);
+ while (cs->in_flight == MAX_NBD_REQUESTS ||
+ (cs->state != NBD_CLIENT_CONNECTED && cs->in_flight > 0)) {
+ qemu_co_queue_wait(&cs->free_sema, &cs->requests_lock);
}
- s->in_flight++;
- if (s->state != NBD_CLIENT_CONNECTED) {
- if (nbd_client_connecting(s)) {
- nbd_reconnect_attempt(s);
- qemu_co_queue_restart_all(&s->free_sema);
+ cs->in_flight++;
+ if (cs->state != NBD_CLIENT_CONNECTED) {
+ if (nbd_client_connecting(cs)) {
+ nbd_reconnect_attempt(cs);
+ qemu_co_queue_restart_all(&cs->free_sema);
}
- if (s->state != NBD_CLIENT_CONNECTED) {
+ if (cs->state != NBD_CLIENT_CONNECTED) {
rc = -EIO;
goto err;
}
}
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
- if (s->requests[i].coroutine == NULL) {
+ if (cs->requests[i].coroutine == NULL) {
break;
}
}
assert(i < MAX_NBD_REQUESTS);
- s->requests[i].coroutine = qemu_coroutine_self();
- s->requests[i].offset = request->from;
- s->requests[i].receiving = false;
- qemu_mutex_unlock(&s->requests_lock);
+ cs->requests[i].coroutine = qemu_coroutine_self();
+ cs->requests[i].offset = request->from;
+ cs->requests[i].receiving = false;
+ qemu_mutex_unlock(&cs->requests_lock);
- qemu_co_mutex_lock(&s->send_mutex);
- request->handle = INDEX_TO_HANDLE(s, i);
+ qemu_co_mutex_lock(&cs->send_mutex);
+ request->handle = INDEX_TO_HANDLE(cs, i);
- assert(s->ioc);
+ assert(cs->ioc);
if (qiov) {
- qio_channel_set_cork(s->ioc, true);
- rc = nbd_send_request(s->ioc, request);
- if (rc >= 0 && qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov,
+ qio_channel_set_cork(cs->ioc, true);
+ rc = nbd_send_request(cs->ioc, request);
+ if (rc >= 0 && qio_channel_writev_all(cs->ioc, qiov->iov, qiov->niov,
NULL) < 0) {
rc = -EIO;
}
- qio_channel_set_cork(s->ioc, false);
+ qio_channel_set_cork(cs->ioc, false);
} else {
- rc = nbd_send_request(s->ioc, request);
+ rc = nbd_send_request(cs->ioc, request);
}
- qemu_co_mutex_unlock(&s->send_mutex);
+ qemu_co_mutex_unlock(&cs->send_mutex);
if (rc < 0) {
- qemu_mutex_lock(&s->requests_lock);
+ qemu_mutex_lock(&cs->requests_lock);
err:
- nbd_channel_error_locked(s, rc);
+ nbd_channel_error_locked(cs, rc);
if (i != -1) {
- s->requests[i].coroutine = NULL;
+ cs->requests[i].coroutine = NULL;
}
- s->in_flight--;
- qemu_co_queue_next(&s->free_sema);
- qemu_mutex_unlock(&s->requests_lock);
+ cs->in_flight--;
+ qemu_co_queue_next(&cs->free_sema);
+ qemu_mutex_unlock(&cs->requests_lock);
}
return rc;
}
@@ -569,7 +586,7 @@ static inline uint64_t payload_advance64(uint8_t **payload)
return ldq_be_p(*payload - 8);
}
-static int nbd_parse_offset_hole_payload(BDRVNBDState *s,
+static int nbd_parse_offset_hole_payload(NBDConnState *cs,
NBDStructuredReplyChunk *chunk,
uint8_t *payload, uint64_t
orig_offset,
QEMUIOVector *qiov, Error **errp)
@@ -592,8 +609,8 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s,
" region");
return -EINVAL;
}
- if (s->info.min_block &&
- !QEMU_IS_ALIGNED(hole_size, s->info.min_block)) {
+ if (cs->info.min_block &&
+ !QEMU_IS_ALIGNED(hole_size, cs->info.min_block)) {
trace_nbd_structured_read_compliance("hole");
}
@@ -607,7 +624,7 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s,
* Based on our request, we expect only one extent in reply, for the
* base:allocation context.
*/
-static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
+static int nbd_parse_blockstatus_payload(NBDConnState *cs,
NBDStructuredReplyChunk *chunk,
uint8_t *payload, uint64_t
orig_length,
NBDExtent *extent, Error **errp)
@@ -622,11 +639,11 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
}
context_id = payload_advance32(&payload);
- if (s->info.context_id != context_id) {
+ if (cs->info.context_id != context_id) {
error_setg(errp, "Protocol error: unexpected context id %d for "
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context
"
"id is %d", context_id,
- s->info.context_id);
+ cs->info.context_id);
return -EINVAL;
}
@@ -651,14 +668,14 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
* up to the full block and change the status to fully-allocated
* (always a safe status, even if it loses information).
*/
- if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length,
- s->info.min_block)) {
+ if (cs->info.min_block &&
+ !QEMU_IS_ALIGNED(extent->length, cs->info.min_block)) {
trace_nbd_parse_blockstatus_compliance("extent length is unaligned");
- if (extent->length > s->info.min_block) {
+ if (extent->length > cs->info.min_block) {
extent->length = QEMU_ALIGN_DOWN(extent->length,
- s->info.min_block);
+ cs->info.min_block);
} else {
- extent->length = s->info.min_block;
+ extent->length = cs->info.min_block;
extent->flags = 0;
}
}
@@ -685,7 +702,7 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s,
* since nbd_client_co_block_status is only expecting the low two
* bits to be set.
*/
- if (s->alloc_depth && extent->flags > 2) {
+ if (cs->s->alloc_depth && extent->flags > 2) {
extent->flags = 2;
}
@@ -735,16 +752,16 @@ static int
nbd_parse_error_payload(NBDStructuredReplyChunk *chunk,
}
static int coroutine_fn
-nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset,
+nbd_co_receive_offset_data_payload(NBDConnState *cs, uint64_t orig_offset,
QEMUIOVector *qiov, Error **errp)
{
QEMUIOVector sub_qiov;
uint64_t offset;
size_t data_size;
int ret;
- NBDStructuredReplyChunk *chunk = &s->reply.structured;
+ NBDStructuredReplyChunk *chunk = &cs->reply.structured;
- assert(nbd_reply_is_structured(&s->reply));
+ assert(nbd_reply_is_structured(&cs->reply));
/* The NBD spec requires at least one byte of payload */
if (chunk->length <= sizeof(offset)) {
@@ -753,7 +770,7 @@ nbd_co_receive_offset_data_payload(BDRVNBDState *s,
uint64_t orig_offset,
return -EINVAL;
}
- if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) {
+ if (nbd_read64(cs->ioc, &offset, "OFFSET_DATA offset", errp) < 0) {
return -EIO;
}
@@ -765,13 +782,14 @@ nbd_co_receive_offset_data_payload(BDRVNBDState *s,
uint64_t orig_offset,
" region");
return -EINVAL;
}
- if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) {
+ if (cs->info.min_block &&
+ !QEMU_IS_ALIGNED(data_size, cs->info.min_block)) {
trace_nbd_structured_read_compliance("data");
}
qemu_iovec_init(&sub_qiov, qiov->niov);
qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size);
- ret = qio_channel_readv_all(s->ioc, sub_qiov.iov, sub_qiov.niov, errp);
+ ret = qio_channel_readv_all(cs->ioc, sub_qiov.iov, sub_qiov.niov, errp);
qemu_iovec_destroy(&sub_qiov);
return ret < 0 ? -EIO : 0;
@@ -779,14 +797,14 @@ nbd_co_receive_offset_data_payload(BDRVNBDState *s,
uint64_t orig_offset,
#define NBD_MAX_MALLOC_PAYLOAD 1000
static coroutine_fn int nbd_co_receive_structured_payload(
- BDRVNBDState *s, void **payload, Error **errp)
+ NBDConnState *cs, void **payload, Error **errp)
{
int ret;
uint32_t len;
- assert(nbd_reply_is_structured(&s->reply));
+ assert(nbd_reply_is_structured(&cs->reply));
- len = s->reply.structured.length;
+ len = cs->reply.structured.length;
if (len == 0) {
return 0;
@@ -803,7 +821,7 @@ static coroutine_fn int nbd_co_receive_structured_payload(
}
*payload = g_new(char, len);
- ret = nbd_read(s->ioc, *payload, len, "structured payload", errp);
+ ret = nbd_read(cs->ioc, *payload, len, "structured payload", errp);
if (ret < 0) {
g_free(*payload);
*payload = NULL;
@@ -829,11 +847,11 @@ static coroutine_fn int nbd_co_receive_structured_payload(
* corresponding to the server's error reply), and errp is unchanged.
*/
static coroutine_fn int nbd_co_do_receive_one_chunk(
- BDRVNBDState *s, uint64_t handle, bool only_structured,
+ NBDConnState *cs, uint64_t handle, bool only_structured,
int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp)
{
int ret;
- int i = HANDLE_TO_INDEX(s, handle);
+ int i = HANDLE_TO_INDEX(cs, handle);
void *local_payload = NULL;
NBDStructuredReplyChunk *chunk;
@@ -842,34 +860,34 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
}
*request_ret = 0;
- ret = nbd_receive_replies(s, handle);
+ ret = nbd_receive_replies(cs, handle);
if (ret < 0) {
error_setg(errp, "Connection closed");
return -EIO;
}
- assert(s->ioc);
+ assert(cs->ioc);
- assert(s->reply.handle == handle);
+ assert(cs->reply.handle == handle);
- if (nbd_reply_is_simple(&s->reply)) {
+ if (nbd_reply_is_simple(&cs->reply)) {
if (only_structured) {
error_setg(errp, "Protocol error: simple reply when structured "
"reply chunk was expected");
return -EINVAL;
}
- *request_ret = -nbd_errno_to_system_errno(s->reply.simple.error);
+ *request_ret = -nbd_errno_to_system_errno(cs->reply.simple.error);
if (*request_ret < 0 || !qiov) {
return 0;
}
- return qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov,
+ return qio_channel_readv_all(cs->ioc, qiov->iov, qiov->niov,
errp) < 0 ? -EIO : 0;
}
/* handle structured reply chunk */
- assert(s->info.structured_reply);
- chunk = &s->reply.structured;
+ assert(cs->info.structured_reply);
+ chunk = &cs->reply.structured;
if (chunk->type == NBD_REPLY_TYPE_NONE) {
if (!(chunk->flags & NBD_REPLY_FLAG_DONE)) {
@@ -891,7 +909,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
return -EINVAL;
}
- return nbd_co_receive_offset_data_payload(s, s->requests[i].offset,
+ return nbd_co_receive_offset_data_payload(cs, cs->requests[i].offset,
qiov, errp);
}
@@ -899,7 +917,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
payload = &local_payload;
}
- ret = nbd_co_receive_structured_payload(s, payload, errp);
+ ret = nbd_co_receive_structured_payload(cs, payload, errp);
if (ret < 0) {
return ret;
}
@@ -919,23 +937,23 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
* Return value is a fatal error code or normal nbd reply error code
*/
static coroutine_fn int nbd_co_receive_one_chunk(
- BDRVNBDState *s, uint64_t handle, bool only_structured,
+ NBDConnState *cs, uint64_t handle, bool only_structured,
int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload,
Error **errp)
{
- int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
+ int ret = nbd_co_do_receive_one_chunk(cs, handle, only_structured,
request_ret, qiov, payload, errp);
if (ret < 0) {
memset(reply, 0, sizeof(*reply));
- nbd_channel_error(s, ret);
+ nbd_channel_error(cs, ret);
} else {
/* For assert at loop start in nbd_connection_entry */
- *reply = s->reply;
+ *reply = cs->reply;
}
- s->reply.handle = 0;
+ cs->reply.handle = 0;
- nbd_recv_coroutines_wake(s);
+ nbd_recv_coroutines_wake(cs);
return ret;
}
@@ -976,16 +994,16 @@ static void nbd_iter_request_error(NBDReplyChunkIter
*iter, int ret)
* NBD_FOREACH_REPLY_CHUNK
* The pointer stored in @payload requires g_free() to free it.
*/
-#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \
+#define NBD_FOREACH_REPLY_CHUNK(cs, iter, handle, structured, \
qiov, reply, payload) \
for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \
- nbd_reply_chunk_iter_receive(s, &iter, handle, qiov, reply, payload);)
+ nbd_reply_chunk_iter_receive(cs, &iter, handle, qiov, reply,
payload);)
/*
* nbd_reply_chunk_iter_receive
* The pointer stored in @payload requires g_free() to free it.
*/
-static bool coroutine_fn nbd_reply_chunk_iter_receive(BDRVNBDState *s,
+static bool coroutine_fn nbd_reply_chunk_iter_receive(NBDConnState *cs,
NBDReplyChunkIter *iter,
uint64_t handle,
QEMUIOVector *qiov,
@@ -1006,7 +1024,7 @@ static bool coroutine_fn
nbd_reply_chunk_iter_receive(BDRVNBDState *s,
reply = &local_reply;
}
- ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
+ ret = nbd_co_receive_one_chunk(cs, handle, iter->only_structured,
&request_ret, qiov, reply, payload,
&local_err);
if (ret < 0) {
@@ -1038,21 +1056,21 @@ static bool coroutine_fn
nbd_reply_chunk_iter_receive(BDRVNBDState *s,
return true;
break_loop:
- qemu_mutex_lock(&s->requests_lock);
- s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL;
- s->in_flight--;
- qemu_co_queue_next(&s->free_sema);
- qemu_mutex_unlock(&s->requests_lock);
+ qemu_mutex_lock(&cs->requests_lock);
+ cs->requests[HANDLE_TO_INDEX(cs, handle)].coroutine = NULL;
+ cs->in_flight--;
+ qemu_co_queue_next(&cs->free_sema);
+ qemu_mutex_unlock(&cs->requests_lock);
return false;
}
-static int coroutine_fn nbd_co_receive_return_code(BDRVNBDState *s, uint64_t
handle,
+static int coroutine_fn nbd_co_receive_return_code(NBDConnState *cs, uint64_t
handle,
int *request_ret, Error
**errp)
{
NBDReplyChunkIter iter;
- NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, NULL, NULL) {
+ NBD_FOREACH_REPLY_CHUNK(cs, iter, handle, false, NULL, NULL, NULL) {
/* nbd_reply_chunk_iter_receive does all the work */
}
@@ -1061,7 +1079,7 @@ static int coroutine_fn
nbd_co_receive_return_code(BDRVNBDState *s, uint64_t han
return iter.ret;
}
-static int coroutine_fn nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t
handle,
+static int coroutine_fn nbd_co_receive_cmdread_reply(NBDConnState *cs,
uint64_t handle,
uint64_t offset,
QEMUIOVector *qiov,
int *request_ret, Error
**errp)
{
@@ -1070,7 +1088,7 @@ static int coroutine_fn
nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t h
void *payload = NULL;
Error *local_err = NULL;
- NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
+ NBD_FOREACH_REPLY_CHUNK(cs, iter, handle, cs->info.structured_reply,
qiov, &reply, &payload)
{
int ret;
@@ -1086,17 +1104,17 @@ static int coroutine_fn
nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t h
*/
break;
case NBD_REPLY_TYPE_OFFSET_HOLE:
- ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload,
+ ret = nbd_parse_offset_hole_payload(cs, &reply.structured, payload,
offset, qiov, &local_err);
if (ret < 0) {
- nbd_channel_error(s, ret);
+ nbd_channel_error(cs, ret);
nbd_iter_channel_error(&iter, ret, &local_err);
}
break;
default:
if (!nbd_reply_type_is_error(chunk->type)) {
/* not allowed reply type */
- nbd_channel_error(s, -EINVAL);
+ nbd_channel_error(cs, -EINVAL);
error_setg(&local_err,
"Unexpected reply type: %d (%s) for CMD_READ",
chunk->type, nbd_reply_type_lookup(chunk->type));
@@ -1113,7 +1131,7 @@ static int coroutine_fn
nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t h
return iter.ret;
}
-static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
+static int coroutine_fn nbd_co_receive_blockstatus_reply(NBDConnState *cs,
uint64_t handle,
uint64_t length,
NBDExtent *extent,
int *request_ret,
Error **errp)
@@ -1125,7 +1143,7 @@ static int coroutine_fn
nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
bool received = false;
assert(!extent->length);
- NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, &reply, &payload) {
+ NBD_FOREACH_REPLY_CHUNK(cs, iter, handle, false, NULL, &reply, &payload) {
int ret;
NBDStructuredReplyChunk *chunk = &reply.structured;
@@ -1134,23 +1152,23 @@ static int coroutine_fn
nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
switch (chunk->type) {
case NBD_REPLY_TYPE_BLOCK_STATUS:
if (received) {
- nbd_channel_error(s, -EINVAL);
+ nbd_channel_error(cs, -EINVAL);
error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
nbd_iter_channel_error(&iter, -EINVAL, &local_err);
}
received = true;
- ret = nbd_parse_blockstatus_payload(s, &reply.structured,
+ ret = nbd_parse_blockstatus_payload(cs, &reply.structured,
payload, length, extent,
&local_err);
if (ret < 0) {
- nbd_channel_error(s, ret);
+ nbd_channel_error(cs, ret);
nbd_iter_channel_error(&iter, ret, &local_err);
}
break;
default:
if (!nbd_reply_type_is_error(chunk->type)) {
- nbd_channel_error(s, -EINVAL);
+ nbd_channel_error(cs, -EINVAL);
error_setg(&local_err,
"Unexpected reply type: %d (%s) "
"for CMD_BLOCK_STATUS",
@@ -1173,12 +1191,11 @@ static int coroutine_fn
nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
return iter.ret;
}
-static int coroutine_fn nbd_co_request(BlockDriverState *bs, NBDRequest
*request,
+static int coroutine_fn nbd_co_request(NBDConnState *cs, NBDRequest *request,
QEMUIOVector *write_qiov)
{
int ret, request_ret;
Error *local_err = NULL;
- BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
assert(request->type != NBD_CMD_READ);
if (write_qiov) {
@@ -1189,12 +1206,12 @@ static int coroutine_fn nbd_co_request(BlockDriverState
*bs, NBDRequest *request
}
do {
- ret = nbd_co_send_request(bs, request, write_qiov);
+ ret = nbd_co_send_request(cs, request, write_qiov);
if (ret < 0) {
continue;
}
- ret = nbd_co_receive_return_code(s, request->handle,
+ ret = nbd_co_receive_return_code(cs, request->handle,
&request_ret, &local_err);
if (local_err) {
trace_nbd_co_request_fail(request->from, request->len,
@@ -1205,7 +1222,7 @@ static int coroutine_fn nbd_co_request(BlockDriverState
*bs, NBDRequest *request
error_free(local_err);
local_err = NULL;
}
- } while (ret < 0 && nbd_client_will_reconnect(s));
+ } while (ret < 0 && nbd_client_will_reconnect(cs));
return ret ? ret : request_ret;
}
@@ -1222,6 +1239,7 @@ static int coroutine_fn
nbd_client_co_preadv(BlockDriverState *bs, int64_t offse
.from = offset,
.len = bytes,
};
+ NBDConnState * const cs = s->conns[0];
assert(bytes <= NBD_MAX_BUFFER_SIZE);
@@ -1234,13 +1252,13 @@ static int coroutine_fn
nbd_client_co_preadv(BlockDriverState *bs, int64_t offse
* advertised size because the block layer rounded size up, then
* truncate the request to the server and tail-pad with zero.
*/
- if (offset >= s->info.size) {
+ if (offset >= cs->info.size) {
assert(bytes < BDRV_SECTOR_SIZE);
qemu_iovec_memset(qiov, 0, 0, bytes);
return 0;
}
- if (offset + bytes > s->info.size) {
- uint64_t slop = offset + bytes - s->info.size;
+ if (offset + bytes > cs->info.size) {
+ uint64_t slop = offset + bytes - cs->info.size;
assert(slop < BDRV_SECTOR_SIZE);
qemu_iovec_memset(qiov, bytes - slop, 0, slop);
@@ -1248,12 +1266,12 @@ static int coroutine_fn
nbd_client_co_preadv(BlockDriverState *bs, int64_t offse
}
do {
- ret = nbd_co_send_request(bs, &request, NULL);
+ ret = nbd_co_send_request(cs, &request, NULL);
if (ret < 0) {
continue;
}
- ret = nbd_co_receive_cmdread_reply(s, request.handle, offset, qiov,
+ ret = nbd_co_receive_cmdread_reply(cs, request.handle, offset, qiov,
&request_ret, &local_err);
if (local_err) {
trace_nbd_co_request_fail(request.from, request.len,
request.handle,
@@ -1263,7 +1281,7 @@ static int coroutine_fn
nbd_client_co_preadv(BlockDriverState *bs, int64_t offse
error_free(local_err);
local_err = NULL;
}
- } while (ret < 0 && nbd_client_will_reconnect(s));
+ } while (ret < 0 && nbd_client_will_reconnect(cs));
return ret ? ret : request_ret;
}
@@ -1278,10 +1296,11 @@ static int coroutine_fn
nbd_client_co_pwritev(BlockDriverState *bs, int64_t offs
.from = offset,
.len = bytes,
};
+ NBDConnState * const cs = s->conns[0];
- assert(!(s->info.flags & NBD_FLAG_READ_ONLY));
+ assert(!(cs->info.flags & NBD_FLAG_READ_ONLY));
if (flags & BDRV_REQ_FUA) {
- assert(s->info.flags & NBD_FLAG_SEND_FUA);
+ assert(cs->info.flags & NBD_FLAG_SEND_FUA);
request.flags |= NBD_CMD_FLAG_FUA;
}
@@ -1290,7 +1309,7 @@ static int coroutine_fn
nbd_client_co_pwritev(BlockDriverState *bs, int64_t offs
if (!bytes) {
return 0;
}
- return nbd_co_request(bs, &request, qiov);
+ return nbd_co_request(cs, &request, qiov);
}
static int coroutine_fn nbd_client_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset,
@@ -1302,45 +1321,47 @@ static int coroutine_fn
nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_
.from = offset,
.len = bytes, /* .len is uint32_t actually */
};
+ NBDConnState * const cs = s->conns[0];
assert(bytes <= UINT32_MAX); /* rely on max_pwrite_zeroes */
- assert(!(s->info.flags & NBD_FLAG_READ_ONLY));
- if (!(s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) {
+ assert(!(cs->info.flags & NBD_FLAG_READ_ONLY));
+ if (!(cs->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) {
return -ENOTSUP;
}
if (flags & BDRV_REQ_FUA) {
- assert(s->info.flags & NBD_FLAG_SEND_FUA);
+ assert(cs->info.flags & NBD_FLAG_SEND_FUA);
request.flags |= NBD_CMD_FLAG_FUA;
}
if (!(flags & BDRV_REQ_MAY_UNMAP)) {
request.flags |= NBD_CMD_FLAG_NO_HOLE;
}
if (flags & BDRV_REQ_NO_FALLBACK) {
- assert(s->info.flags & NBD_FLAG_SEND_FAST_ZERO);
+ assert(cs->info.flags & NBD_FLAG_SEND_FAST_ZERO);
request.flags |= NBD_CMD_FLAG_FAST_ZERO;
}
if (!bytes) {
return 0;
}
- return nbd_co_request(bs, &request, NULL);
+ return nbd_co_request(cs, &request, NULL);
}
static int coroutine_fn nbd_client_co_flush(BlockDriverState *bs)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
NBDRequest request = { .type = NBD_CMD_FLUSH };
+ NBDConnState * const cs = s->conns[0];
- if (!(s->info.flags & NBD_FLAG_SEND_FLUSH)) {
+ if (!(cs->info.flags & NBD_FLAG_SEND_FLUSH)) {
return 0;
}
request.from = 0;
request.len = 0;
- return nbd_co_request(bs, &request, NULL);
+ return nbd_co_request(cs, &request, NULL);
}
static int coroutine_fn nbd_client_co_pdiscard(BlockDriverState *bs, int64_t
offset,
@@ -1352,15 +1373,16 @@ static int coroutine_fn
nbd_client_co_pdiscard(BlockDriverState *bs, int64_t off
.from = offset,
.len = bytes, /* len is uint32_t */
};
+ NBDConnState * const cs = s->conns[0];
assert(bytes <= UINT32_MAX); /* rely on max_pdiscard */
- assert(!(s->info.flags & NBD_FLAG_READ_ONLY));
- if (!(s->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) {
+ assert(!(cs->info.flags & NBD_FLAG_READ_ONLY));
+ if (!(cs->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) {
return 0;
}
- return nbd_co_request(bs, &request, NULL);
+ return nbd_co_request(cs, &request, NULL);
}
static int coroutine_fn nbd_client_co_block_status(
@@ -1371,16 +1393,17 @@ static int coroutine_fn nbd_client_co_block_status(
NBDExtent extent = { 0 };
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
Error *local_err = NULL;
+ NBDConnState * const cs = s->conns[0];
NBDRequest request = {
.type = NBD_CMD_BLOCK_STATUS,
.from = offset,
.len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment),
- MIN(bytes, s->info.size - offset)),
+ MIN(bytes, cs->info.size - offset)),
.flags = NBD_CMD_FLAG_REQ_ONE,
};
- if (!s->info.base_allocation) {
+ if (!cs->info.base_allocation) {
*pnum = bytes;
*map = offset;
*file = bs;
@@ -1394,23 +1417,23 @@ static int coroutine_fn nbd_client_co_block_status(
* up, we truncated the request to the server (above), or are
* called on just the hole.
*/
- if (offset >= s->info.size) {
+ if (offset >= cs->info.size) {
*pnum = bytes;
assert(bytes < BDRV_SECTOR_SIZE);
/* Intentionally don't report offset_valid for the hole */
return BDRV_BLOCK_ZERO;
}
- if (s->info.min_block) {
- assert(QEMU_IS_ALIGNED(request.len, s->info.min_block));
+ if (cs->info.min_block) {
+ assert(QEMU_IS_ALIGNED(request.len, cs->info.min_block));
}
do {
- ret = nbd_co_send_request(bs, &request, NULL);
+ ret = nbd_co_send_request(cs, &request, NULL);
if (ret < 0) {
continue;
}
- ret = nbd_co_receive_blockstatus_reply(s, request.handle, bytes,
+ ret = nbd_co_receive_blockstatus_reply(cs, request.handle, bytes,
&extent, &request_ret,
&local_err);
if (local_err) {
@@ -1421,7 +1444,7 @@ static int coroutine_fn nbd_client_co_block_status(
error_free(local_err);
local_err = NULL;
}
- } while (ret < 0 && nbd_client_will_reconnect(s));
+ } while (ret < 0 && nbd_client_will_reconnect(cs));
if (ret < 0 || request_ret < 0) {
return ret ? ret : request_ret;
@@ -1440,8 +1463,10 @@ static int nbd_client_reopen_prepare(BDRVReopenState
*state,
BlockReopenQueue *queue, Error **errp)
{
BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque;
+ NBDConnState * const cs = s->conns[0];
- if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) {
+ if ((state->flags & BDRV_O_RDWR) &&
+ (cs->info.flags & NBD_FLAG_READ_ONLY)) {
error_setg(errp, "Can't reopen read-only NBD mount as read/write");
return -EACCES;
}
@@ -1450,24 +1475,23 @@ static int nbd_client_reopen_prepare(BDRVReopenState
*state,
static void nbd_yank(void *opaque)
{
- BlockDriverState *bs = opaque;
- BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
+ NBDConnState *cs = opaque;
- QEMU_LOCK_GUARD(&s->requests_lock);
- qio_channel_shutdown(QIO_CHANNEL(s->ioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
- s->state = NBD_CLIENT_QUIT;
+ QEMU_LOCK_GUARD(&cs->requests_lock);
+ qio_channel_shutdown(QIO_CHANNEL(cs->ioc),
+ QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ cs->state = NBD_CLIENT_QUIT;
}
-static void nbd_client_close(BlockDriverState *bs)
+static void nbd_client_close(NBDConnState *cs)
{
- BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
NBDRequest request = { .type = NBD_CMD_DISC };
- if (s->ioc) {
- nbd_send_request(s->ioc, &request);
+ if (cs->ioc) {
+ nbd_send_request(cs->ioc, &request);
}
- nbd_teardown_connection(bs);
+ nbd_teardown_connection(cs);
}
@@ -1888,10 +1912,13 @@ static int nbd_open(BlockDriverState *bs, QDict
*options, int flags,
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
s->bs = bs;
- qemu_mutex_init(&s->requests_lock);
- qemu_co_queue_init(&s->free_sema);
- qemu_co_mutex_init(&s->send_mutex);
- qemu_co_mutex_init(&s->receive_mutex);
+
+ s->conns[0] = g_new0(NBDConnState, 1);
+ s->conns[0]->s = s;
+ qemu_mutex_init(&s->conns[0]->requests_lock);
+ qemu_co_queue_init(&s->conns[0]->free_sema);
+ qemu_co_mutex_init(&s->conns[0]->send_mutex);
+ qemu_co_mutex_init(&s->conns[0]->receive_mutex);
if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) {
return -EEXIST;
@@ -1902,18 +1929,19 @@ static int nbd_open(BlockDriverState *bs, QDict
*options, int flags,
goto fail;
}
- s->conn = nbd_client_connection_new(s->saddr, true, s->export,
- s->x_dirty_bitmap, s->tlscreds,
- s->tlshostname);
+ s->conns[0]->conn =
+ nbd_client_connection_new(s->saddr, true, s->export,
+ s->x_dirty_bitmap, s->tlscreds,
+ s->tlshostname);
if (s->open_timeout) {
- nbd_client_connection_enable_retry(s->conn);
- open_timer_init(s, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
+ nbd_client_connection_enable_retry(s->conns[0]->conn);
+ open_timer_init(s->conns[0], qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
s->open_timeout * NANOSECONDS_PER_SECOND);
}
- s->state = NBD_CLIENT_CONNECTING_WAIT;
- ret = nbd_do_establish_connection(bs, true, errp);
+ s->conns[0]->state = NBD_CLIENT_CONNECTING_WAIT;
+ ret = nbd_do_establish_connection(bs, s->conns[0], true, errp);
if (ret < 0) {
goto fail;
}
@@ -1923,23 +1951,23 @@ static int nbd_open(BlockDriverState *bs, QDict
*options, int flags,
* Delete it, because we do not want it to be around when this node
* is drained or closed.
*/
- open_timer_del(s);
+ open_timer_del(s->conns[0]);
- nbd_client_connection_enable_retry(s->conn);
+ nbd_client_connection_enable_retry(s->conns[0]->conn);
/*
* We set s->multi_conn in nbd_process_options above, but now that
* we have connected if the server doesn't advertise that it is
* safe for multi-conn, force it to 1.
*/
- if (!(s->info.flags & NBD_FLAG_CAN_MULTI_CONN)) {
+ if (!(s->conns[0]->info.flags & NBD_FLAG_CAN_MULTI_CONN)) {
s->multi_conn = 1;
}
return 0;
fail:
- open_timer_del(s);
+ open_timer_del(s->conns[0]);
nbd_clear_bdrvstate(bs);
return ret;
}
@@ -1952,8 +1980,9 @@ static int coroutine_fn nbd_co_flush(BlockDriverState *bs)
static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
- uint32_t min = s->info.min_block;
- uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block);
+ NBDConnState * const cs = s->conns[0];
+ uint32_t min = cs->info.min_block;
+ uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, cs->info.max_block);
/*
* If the server did not advertise an alignment:
@@ -1968,8 +1997,8 @@ static void nbd_refresh_limits(BlockDriverState *bs,
Error **errp)
* sub-sector requests
*/
if (!min) {
- min = (!QEMU_IS_ALIGNED(s->info.size, BDRV_SECTOR_SIZE) ||
- s->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE;
+ min = (!QEMU_IS_ALIGNED(cs->info.size, BDRV_SECTOR_SIZE) ||
+ cs->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE;
}
bs->bl.request_alignment = min;
@@ -1977,15 +2006,17 @@ static void nbd_refresh_limits(BlockDriverState *bs,
Error **errp)
bs->bl.max_pwrite_zeroes = max;
bs->bl.max_transfer = max;
- if (s->info.opt_block &&
- s->info.opt_block > bs->bl.opt_transfer) {
- bs->bl.opt_transfer = s->info.opt_block;
+ if (cs->info.opt_block &&
+ cs->info.opt_block > bs->bl.opt_transfer) {
+ bs->bl.opt_transfer = cs->info.opt_block;
}
}
static void nbd_close(BlockDriverState *bs)
{
- nbd_client_close(bs);
+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
+
+ nbd_client_close(s->conns[0]);
nbd_clear_bdrvstate(bs);
}
@@ -2002,13 +2033,14 @@ static int coroutine_fn
nbd_co_truncate(BlockDriverState *bs, int64_t offset,
BdrvRequestFlags flags, Error **errp)
{
BDRVNBDState *s = bs->opaque;
+ NBDConnState * const cs = s->conns[0];
- if (offset != s->info.size && exact) {
+ if (offset != cs->info.size && exact) {
error_setg(errp, "Cannot resize NBD nodes");
return -ENOTSUP;
}
- if (offset > s->info.size) {
+ if (offset > cs->info.size) {
error_setg(errp, "Cannot grow NBD nodes");
return -EINVAL;
}
@@ -2019,8 +2051,9 @@ static int coroutine_fn nbd_co_truncate(BlockDriverState
*bs, int64_t offset,
static int64_t coroutine_fn nbd_co_getlength(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
+ NBDConnState * const cs = s->conns[0];
- return s->info.size;
+ return cs->info.size;
}
static void nbd_refresh_filename(BlockDriverState *bs)
@@ -2083,25 +2116,27 @@ static const char *const nbd_strong_runtime_opts[] = {
static void nbd_cancel_in_flight(BlockDriverState *bs)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
+ NBDConnState * const cs = s->conns[0];
- reconnect_delay_timer_del(s);
+ reconnect_delay_timer_del(cs);
- qemu_mutex_lock(&s->requests_lock);
- if (s->state == NBD_CLIENT_CONNECTING_WAIT) {
- s->state = NBD_CLIENT_CONNECTING_NOWAIT;
+ qemu_mutex_lock(&cs->requests_lock);
+ if (cs->state == NBD_CLIENT_CONNECTING_WAIT) {
+ cs->state = NBD_CLIENT_CONNECTING_NOWAIT;
}
- qemu_mutex_unlock(&s->requests_lock);
+ qemu_mutex_unlock(&cs->requests_lock);
- nbd_co_establish_connection_cancel(s->conn);
+ nbd_co_establish_connection_cancel(cs->conn);
}
static void nbd_attach_aio_context(BlockDriverState *bs,
AioContext *new_context)
{
BDRVNBDState *s = bs->opaque;
+ NBDConnState * const cs = s->conns[0];
/* The open_timer is used only during nbd_open() */
- assert(!s->open_timer);
+ assert(!cs->open_timer);
/*
* The reconnect_delay_timer is scheduled in I/O paths when the
@@ -2112,22 +2147,23 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
* Since the AioContext can only be changed when a node is drained,
* the reconnect_delay_timer cannot be active here.
*/
- assert(!s->reconnect_delay_timer);
+ assert(!cs->reconnect_delay_timer);
- if (s->ioc) {
- qio_channel_attach_aio_context(s->ioc, new_context);
+ if (cs->ioc) {
+ qio_channel_attach_aio_context(cs->ioc, new_context);
}
}
static void nbd_detach_aio_context(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
+ NBDConnState * const cs = s->conns[0];
- assert(!s->open_timer);
- assert(!s->reconnect_delay_timer);
+ assert(!cs->open_timer);
+ assert(!cs->reconnect_delay_timer);
- if (s->ioc) {
- qio_channel_detach_aio_context(s->ioc);
+ if (cs->ioc) {
+ qio_channel_detach_aio_context(cs->ioc);
}
}
--
2.39.2
- [PATCH nbd 0/4] Enable multi-conn NBD [for discussion only], Richard W.M. Jones, 2023/03/09
- [PATCH nbd 1/4] nbd: Add multi-conn option, Richard W.M. Jones, 2023/03/09
- [PATCH nbd 3/4] nbd: Open multiple NBD connections if multi-conn is set, Richard W.M. Jones, 2023/03/09
- [PATCH nbd 2/4] nbd: Split out block device state from underlying NBD connections,
Richard W.M. Jones <=
- [PATCH nbd 4/4] nbd: Enable multi-conn using round-robin, Richard W.M. Jones, 2023/03/09
- Re: [PATCH nbd 0/4] Enable multi-conn NBD [for discussion only], Vladimir Sementsov-Ogievskiy, 2023/03/10
- Re: [PATCH nbd 0/4] Enable multi-conn NBD [for discussion only], Eric Blake, 2023/03/10