nbd client refactoring and fixing
v2 cover nbd client refactoring and fixing
v2: Actually - tail of v1 series, with changes, additions and rebased
on recent work in upstream (which includes some patches from v1 too)
Vladimir Sementsov-Ogievskiy (7):
block/nbd-client: refactor nbd_co_receive_reply block/nbd-client: exit reply-reading coroutine on incorrect handle block/nbd-client: refactor reading reply
block/nbd-client: drop reply field from NBDClientSession block/nbd-client: nbd_co_send_request: return -EIO if s->quit was set
in parallel
block/nbd-client: early fail nbd_read_reply_entry if s->quit is set block/nbd-client: do not yield from nbd_read_reply_entry
block/nbd-client.h | 4 ++- block/nbd-client.c | 103 ++++++++++++++++++++++++++--------------------------- 2 files changed, 54 insertions(+), 53 deletions(-)
Signature · 1 line
2.11.1
PATCH v2 1/7 block/nbd-client: refactor nbd_co_receive_reply
"NBDReply *reply" parameter of nbd_co_receive_reply is used only to pass return value for nbd_co_request (reply.error). Remove it and use function return value instead.
--- block/nbd-client.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-)
block/nbd-client.c +15 -15
@@ -178,26 +178,26 @@ err:return rc; } -static void nbd_co_receive_reply (NBDClientSession *s, -NBDRequest *request, -NBDReply *reply, -QEMUIOVector *qiov) +static int nbd_co_receive_reply (NBDClientSession *s, +NBDRequest *request, +QEMUIOVector *qiov) { +int ret;int i =HANDLE_TO_INDEX (s, request->handle);/* Wait until we're woken up by nbd_read_reply_entry. */ s->requests[i].receiving = true;qemu_coroutine_yield (); s->requests[i].receiving = false; - *reply = s->reply; -if (reply->handle != request->handle || !s->ioc || s->quit) { - reply->error =EIO ; +if (s->reply.handle != request->handle || !s->ioc || s->quit) { + ret = -EIO ; }else { -if (qiov && reply->error ==0 ) { + ret = -s->reply.error; +if (qiov && s->reply.error ==0 ) {assert (request->len ==iov_size (qiov->iov, qiov->niov));if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov,NULL ) <0 ) { - reply->error =EIO ; + ret = -EIO ; s->quit = true; } }
@@ -217,6 +217,8 @@ static void nbd_co_receive_reply(NBDClientSession *s, s->in_flight--;qemu_co_queue_next (&s->free_sema);qemu_co_mutex_unlock (&s->send_mutex); + +return ret; } static int nbd_co_request(BlockDriverState *bs,
@@ -224,7 +226,6 @@ static int nbd_co_request(BlockDriverState *bs, QEMUIOVector *qiov) {NBDClientSession *client =nbd_get_client_session (bs); -NBDReply reply;int ret; assert(!qiov || request->type == NBD_CMD_WRITE ||
@@ -232,12 +233,11 @@ static int nbd_co_request(BlockDriverState *bs, ret =nbd_co_send_request (bs, request, request->type ==NBD_CMD_WRITE ? qiov :NULL );if (ret <0 ) { - reply.error = -ret; - }else { -nbd_co_receive_reply (client, request, &reply, - request->type ==NBD_CMD_READ ? qiov :NULL ); +return ret; } -return -reply.error; + +return nbd_co_receive_reply (client, request, + request->type ==NBD_CMD_READ ? qiov :NULL ); } int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 1/7] block/nbd-client: refactor nbd_co_receive_reply
Quoted 9 lines · Vladimir Sementsov-Ogievskiy
"NBDReply *reply" parameter of
nbd_co_receive_replyis used only to pass return value fornbd_co_request(reply.error). Remove it and use function return value instead.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-)
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
PATCH v2 2/7 block/nbd-client: exit reply-reading coroutine on incorrect handle
In reply to Vladimir Sementsov-OgievskiyCheck reply-handle == request-handle in the same place, where recv coroutine number is calculated from reply->handle and it's correctness checked - in nbd_read_reply_entry.
Also finish nbd_read_reply_entry in case of reply-handle != request-handle in the same way as in case of incorrect reply-handle.
--- block/nbd-client.h | 1 + block/nbd-client.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-)
block/nbd-client.h +1 -0
@@ -20,6 +20,7 @@typedef struct {Coroutine *coroutine;bool receiving;/* waiting for read_reply_co? */ +NBDRequest *request; }NBDClientRequest ; typedef struct NBDClientSession {
block/nbd-client.c +6 -2
@@ -92,7 +92,9 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) i =HANDLE_TO_INDEX (s, s->reply.handle);if (i >=MAX_NBD_REQUESTS || !s->requests[i].coroutine || - !s->requests[i].receiving) { + !s->requests[i].receiving || + s->reply.handle != s->requests[i].request->handle) + {break ; }
@@ -142,6 +144,7 @@ static int nbd_co_send_request(BlockDriverState *bs, s->requests[i].receiving = false; request->handle =INDEX_TO_HANDLE (s, i); + s->requests[i].request = request;if (s->quit) { rc = -EIO ;
@@ -189,9 +192,10 @@ static int nbd_co_receive_reply(NBDClientSession *s, s->requests[i].receiving = true;qemu_coroutine_yield (); s->requests[i].receiving = false; -if (s->reply.handle != request->handle || !s->ioc || s->quit) { +if (!s->ioc || s->quit) { ret = -EIO ; }else { +assert (s->reply.handle == request->handle); ret = -s->reply.error;if (qiov && s->reply.error ==0 ) {assert (request->len ==iov_size (qiov->iov, qiov->niov));
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 2/7] block/nbd-client: exit reply-reading coroutine on incorrect handle
Quoted 1 line · Vladimir Sementsov-Ogievskiy
Check reply-handle == request-handle in the same place, where
s/,//
Quoted 12 lines
recv coroutine number is calculated from
reply->handleand it's correctness checked - innbd_read_reply_entry.Also finish
nbd_read_reply_entryin case of reply-handle != request-handle in the same way as in case of incorrect reply-handle.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-)
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v2 2/7] block/nbd-client: exit reply-reading coroutine on incorrect handle
In reply to Vladimir Sementsov-OgievskiyQuoted 4 lines · Vladimir Sementsov-Ogievskiy
Check reply-handle == request-handle in the same place, where recv coroutine number is calculated from
reply->handleand it's correctness checked - innbd_read_reply_entry.
Quoted 8 lines
Also finish
nbd_read_reply_entryin case of reply-handle != request-handle in the same way as in case of incorrect reply-handle.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-)
On second thought:
block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 96
Quoted 8 lines
+++
b/block/nbd-client.cblock/nbd-client.c +2
-1@@ -92,7 +92,9 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) i =HANDLE_TO_INDEX (s, s->reply.handle);if (i >=MAX_NBD_REQUESTS || !s->requests[i].coroutine || - !s->requests[i].receiving) { + !s->requests[i].receiving || + s->reply.handle != s->requests[i].request->handle)
How can this condition ever be false? s->reply.handle only ever contains two values: 0 when it is being reused for the next iteration of the loop, or the (munged) address of the request pointer. But we are careful that we never write s->reply.handle = 0 until after s->requests[i].receiving is false. So we will never see s->reply.handle equal to 0 here. (It may have been possible prior to the introduction of reply.receiving, in commit 40f4a218, but I'm not seeing it now)
If I'm right, then this patch can be simplified - we don't need to track s.requests[i].request, and can merely:
block/nbd-client.c static int nbd_co_receive_reply(NBDClientSession *s, · line 192
Quoted 5 lines
block/nbd-client.c +0
-1@@ -189,9 +192,10 @@ static int nbd_co_receive_reply(NBDClientSession *s, s->requests[i].receiving = true;qemu_coroutine_yield (); s->requests[i].receiving = false; -if (s->reply.handle != request->handle || !s->ioc || s->quit) {
shorten this conditional to remove the now-dead condition.
block/nbd-client.c static int nbd_co_receive_reply(NBDClientSession *s, · line 201
Quoted 8 lines
block/nbd-client.c +2
-0+if (!s->ioc || s->quit) { ret = -EIO ; }else { +assert (s->reply.handle == request->handle); ret = -s->reply.error;if (qiov && s->reply.error ==0 ) {assert (request->len ==iov_size (qiov->iov, qiov->niov));
[Oh, and I just noticed HANDLE_TO_INDEX() and INDEX_TO_HANDLE() are improperly parenthesized, although to no ill effect with current clients]
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
PATCH v2 3/7 block/nbd-client: refactor reading reply
In reply to Vladimir Sementsov-OgievskiyRead the whole reply in one place - in nbd_read_reply_entry.
--- block/nbd-client.h | 1 + block/nbd-client.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-)
block/nbd-client.h +1 -0
@@ -21,6 +21,7 @@ typedef struct {Coroutine *coroutine;bool receiving;/* waiting for read_reply_co? */ NBDRequest *request; +QEMUIOVector *qiov; } NBDClientRequest; typedef struct NBDClientSession {
block/nbd-client.c +24 -18
88 lines not on this page · raw message
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 3/7] block/nbd-client: refactor reading reply
Quoted 32 lines · Vladimir Sementsov-Ogievskiy
Read the whole reply in one place - in
nbd_read_reply_entry.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-)block/nbd-client.h +1
-0@@ -21,6 +21,7 @@ typedef struct {Coroutine *coroutine;bool receiving;/* waiting for read_reply_co? */ NBDRequest *request; +QEMUIOVector *qiov; } NBDClientRequest; typedef struct NBDClientSession {block/nbd-client.c +4
-0@@ -98,6 +98,21 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)break ; } +if (s->reply.error ==0 && + s->requests[i].request->type ==NBD_CMD_READ ) + { +QEMUIOVector *qiov = s->requests[i].qiov;
block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 114
Quoted 10 lines
block/nbd-client.c +10
-0+assert (qiov !=NULL ); +assert (s->requests[i].request->len == +iov_size (qiov->iov, qiov->niov)); +if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, +NULL ) <0 ) + { + s->reply.error =EIO ; +break ; + } + }
I am not sure this is an improvement. In principle you could have commands that read replies a bit at a time without using a QEMUIOVector.
Paolo
Quoted 77 lines
block/nbd-client.c +9
-18/* We're woken up again by the request itself. Note that there * is no race between yielding and reentering read_reply_co. This * is because: @@ -118,6 +133,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) s->read_reply_co =NULL ; } +/* qiov should be provided for READ and WRITE */ static int nbd_co_send_request (BlockDriverState *bs,NBDRequest *request,QEMUIOVector *qiov)@@ -145,6 +161,7 @@ static int nbd_co_send_request(BlockDriverState *bs, request->handle =INDEX_TO_HANDLE (s, i); s->requests[i].request = request; + s->requests[i].qiov = qiov;if (s->quit) { rc = -EIO ;@@ -155,7 +172,8 @@ static int nbd_co_send_request(BlockDriverState *bs, goto err; } -if (qiov) { +if (s->requests[i].request->type ==NBD_CMD_WRITE ) { +assert (qiov);qio_channel_set_cork (s->ioc, true); rc =nbd_send_request (s->ioc, request);if (rc >=0 && !s->quit) {@@ -181,9 +199,7 @@ err:return rc; } -static int nbd_co_receive_reply (NBDClientSession *s, -NBDRequest *request, -QEMUIOVector *qiov) +static int nbd_co_receive_reply (NBDClientSession *s,NBDRequest *request) {int ret;int i =HANDLE_TO_INDEX (s, request->handle);@@ -197,14 +213,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, }else {assert (s->reply.handle == request->handle); ret = -s->reply.error; -if (qiov && s->reply.error ==0 ) { -assert (request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) { - ret = -EIO ; - s->quit = true; - } - }/* Tell the read handler to read another header. */ s->reply.handle =0 ;@@ -232,16 +240,14 @@ static int nbd_co_request(BlockDriverState *bs,NBDClientSession *client =nbd_get_client_session (bs);int ret; -assert (!qiov || request->type ==NBD_CMD_WRITE || - request->type ==NBD_CMD_READ ); - ret =nbd_co_send_request (bs, request, - request->type ==NBD_CMD_WRITE ? qiov :NULL ); +assert ((qiov ==NULL ) != + (request->type ==NBD_CMD_WRITE || request->type ==NBD_CMD_READ )); + ret =nbd_co_send_request (bs, request, qiov);if (ret <0 ) {return ret; } -return nbd_co_receive_reply (client, request, - request->type ==NBD_CMD_READ ? qiov :NULL ); +return nbd_co_receive_reply (client, request); } int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
Re: [Qemu-devel] [PATCH v2 3/7] block/nbd-client: refactor reading reply
Quoted 11 lines · Paolo Bonzini
Quoted 8 lines · Vladimir Sementsov-Ogievskiy
Read the whole reply in one place - in
nbd_read_reply_entry.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-)
Quoted 2 lines
I am not sure this is an improvement. In principle you could have commands that read replies a bit at a time without using a QEMUIOVector.
Right now we don't, but the most likely point where this would be an issue is the fact that we want to implement structured replies (the server can send more than one response to a single request from the client) in order to then implement block status queries (where the server can send piecemeal information in response to a query, and the client could very easily want to handle information as it comes in rather than waiting for the entire server response, especially if the amount of information returned by the server is not known a priori by the client, the way the length is known in advance for NBD_CMD_READ, but instead learned partway through the reply). I guess the question becomes a matter of whether we are over-constraining future additions by making this refactoring, or whether we can still implement block status queries using a single QEMUIOVector.
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v2 3/7] block/nbd-client: refactor reading reply
In reply to Paolo BonziniQuoted 46 lines · Paolo Bonzini
Quoted 32 lines · Vladimir Sementsov-Ogievskiy
Read the whole reply in one place - in
nbd_read_reply_entry.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-)block/nbd-client.h +1
-0@@ -21,6 +21,7 @@ typedef struct {Coroutine *coroutine;bool receiving;/* waiting for read_reply_co? */ NBDRequest *request; +QEMUIOVector *qiov; } NBDClientRequest; typedef struct NBDClientSession {block/nbd-client.c +4
-0@@ -98,6 +98,21 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)break ; } +if (s->reply.error ==0 && + s->requests[i].request->type ==NBD_CMD_READ ) + { +QEMUIOVector *qiov = s->requests[i].qiov;block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 114
Quoted 10 lines
block/nbd-client.c +10
-0+assert (qiov !=NULL ); +assert (s->requests[i].request->len == +iov_size (qiov->iov, qiov->niov)); +if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, +NULL ) <0 ) + { + s->reply.error =EIO ; +break ; + } + }I am not sure this is an improvement. In principle you could have commands that read replies a bit at a time without using a QEMUIOVector.
Quoted 1 line
Paolo
Hm, what do you mean? In this patch I don't change "how do we read it", I only move the reading code to one coroutine, to make it simple to extend it in future (block status, etc). nbd_read_reply_enty has all information it need (s->requests[i].request) to handle the whole reply.. It's an improvement, because it leads to separating reply_entry coroutine - it don't need any synchronization (yield/wake) more with request coroutines.
Quoted 78 lines
Quoted 78 lines
block/nbd-client.c +9
-18/* We're woken up again by the request itself. Note that there * is no race between yielding and reentering read_reply_co. This * is because: @@ -118,6 +133,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) s->read_reply_co =NULL ; } +/* qiov should be provided for READ and WRITE */ static int nbd_co_send_request (BlockDriverState *bs,NBDRequest *request,QEMUIOVector *qiov)@@ -145,6 +161,7 @@ static int nbd_co_send_request(BlockDriverState *bs, request->handle =INDEX_TO_HANDLE (s, i); s->requests[i].request = request; + s->requests[i].qiov = qiov;if (s->quit) { rc = -EIO ;@@ -155,7 +172,8 @@ static int nbd_co_send_request(BlockDriverState *bs, goto err; } -if (qiov) { +if (s->requests[i].request->type ==NBD_CMD_WRITE ) { +assert (qiov);qio_channel_set_cork (s->ioc, true); rc =nbd_send_request (s->ioc, request);if (rc >=0 && !s->quit) {@@ -181,9 +199,7 @@ err:return rc; } -static int nbd_co_receive_reply (NBDClientSession *s, -NBDRequest *request, -QEMUIOVector *qiov) +static int nbd_co_receive_reply (NBDClientSession *s,NBDRequest *request) {int ret;int i =HANDLE_TO_INDEX (s, request->handle);@@ -197,14 +213,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, }else {assert (s->reply.handle == request->handle); ret = -s->reply.error; -if (qiov && s->reply.error ==0 ) { -assert (request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) { - ret = -EIO ; - s->quit = true; - } - }/* Tell the read handler to read another header. */ s->reply.handle =0 ;@@ -232,16 +240,14 @@ static int nbd_co_request(BlockDriverState *bs,NBDClientSession *client =nbd_get_client_session (bs);int ret; -assert (!qiov || request->type ==NBD_CMD_WRITE || - request->type ==NBD_CMD_READ ); - ret =nbd_co_send_request (bs, request, - request->type ==NBD_CMD_WRITE ? qiov :NULL ); +assert ((qiov ==NULL ) != + (request->type ==NBD_CMD_WRITE || request->type ==NBD_CMD_READ )); + ret =nbd_co_send_request (bs, request, qiov);if (ret <0 ) {return ret; } -return nbd_co_receive_reply (client, request, - request->type ==NBD_CMD_READ ? qiov :NULL ); +return nbd_co_receive_reply (client, request); } int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
Signature · 2 lines
Best regards, Vladimir
Re: [Qemu-devel] [PATCH v2 3/7] block/nbd-client: refactor reading reply
Quoted 46 lines · Vladimir Sementsov-Ogievskiy
Quoted 45 lines · Paolo Bonzini
Quoted 31 lines · Vladimir Sementsov-Ogievskiy
Read the whole reply in one place - in
nbd_read_reply_entry.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-)block/nbd-client.h +1
-0@@ -21,6 +21,7 @@ typedef struct {Coroutine *coroutine;bool receiving;/* waiting for read_reply_co? */ NBDRequest *request; +QEMUIOVector *qiov; } NBDClientRequest; typedef struct NBDClientSession {block/nbd-client.c +3
-0@@ -98,6 +98,21 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)break ; } + if (s->reply.error == 0 && + s->requests[i].request->type == NBD_CMD_READ) + { +QEMUIOVector *qiov = s->requests[i].qiov;block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void · line 114
Quoted 10 lines
block/nbd-client.c +10
-0+assert (qiov !=NULL ); +assert (s->requests[i].request->len == +iov_size (qiov->iov, qiov->niov)); +if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, +NULL ) <0 ) + { + s->reply.error =EIO ; +break ; + } + }I am not sure this is an improvement. In principle you could have commands that read replies a bit at a time without using a QEMUIOVector.
Quoted 5 lines
Quoted 1 line
Paolo
Hm, what do you mean? In this patch I don't change "how do we read it", I only move the reading code to one coroutine, to make it simple to extend it in future (block status, etc).
I disagree that it is easier to extend it in the future. If some commands in the future need a different "how do we read it" (e.g. for structured reply), nbd_read_reply_entry may not have all the information it needs anymore.
In fact, you're pushing knowledge of the commands from nbd_co_request to nbd_read_reply_entry:
+ if (s->reply.error == 0 &&
+ s->requests[i].request->type == NBD_CMD_READ)
and knowledge of NBD_CMD_READ simply doesn't belong there. So there is an example of that right now, it is not some arbitrary bad thing that could happen in the future.
Paolo
Quoted 84 lines
nbd_read_reply_entyhas all
information it need (s->requests[i].request) to handle the whole reply.. It's an improvement, because it leads to separatingreply_entrycoroutine - it don't need any synchronization (yield/wake) more with request coroutines.Quoted 77 lines
Quoted 77 lines
block/nbd-client.c +0
-0/* We're woken up again by the request itself. Note that there
* is no race between yielding and reenteringread_reply_co. This
* is because:block/nbd-client.c +8
-14@@ -118,6 +133,7 @@ static coroutine_fn voidnbd_read_reply_entry (void *opaque) s->read_reply_co = NULL; } +/* qiov should be provided for READ and WRITE */ static int nbd_co_send_request(BlockDriverState *bs,NBDRequest *request,QEMUIOVector *qiov)@@ -145,6 +161,7 @@ static int nbd_co_send_request(BlockDriverState *bs, request->handle =INDEX_TO_HANDLE (s, i); s->requests[i].request = request; + s->requests[i].qiov = qiov;if (s->quit) { rc = -EIO ;@@ -155,7 +172,8 @@ static int nbd_co_send_request(BlockDriverState *bs, goto err; } - if (qiov) { +if (s->requests[i].request->type ==NBD_CMD_WRITE ) { +assert (qiov);qio_channel_set_cork (s->ioc, true); rc =nbd_send_request (s->ioc, request);if (rc >=0 && !s->quit) {@@ -181,9 +199,7 @@ err:return rc; } -static int nbd_co_receive_reply(NBDClientSession *s, -NBDRequest *request, -QEMUIOVector *qiov) +static int nbd_co_receive_reply (NBDClientSession *s,NBDRequest *request) {int ret;int i =HANDLE_TO_INDEX (s, request->handle);@@ -197,14 +213,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, }else {assert (s->reply.handle == request->handle); ret = -s->reply.error; -if (qiov && s->reply.error ==0 ) { -assert (request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) { - ret = -EIO ; - s->quit = true; - } - }/* Tell the read handler to read another header. */ s->reply.handle =0 ;@@ -232,16 +240,14 @@ static int nbd_co_request(BlockDriverState *bs,NBDClientSession *client =nbd_get_client_session (bs);int ret; -assert (!qiov || request->type ==NBD_CMD_WRITE || - request->type ==NBD_CMD_READ ); - ret =nbd_co_send_request (bs, request, - request->type ==NBD_CMD_WRITE ? qiov : NULL); +assert ((qiov ==NULL ) != + (request->type ==NBD_CMD_WRITE || request->type ==NBD_CMD_READ )); + ret =nbd_co_send_request (bs, request, qiov);if (ret <0 ) {return ret; } - return nbd_co_receive_reply(client, request, - request->type ==NBD_CMD_READ ? qiov : NULL); +return nbd_co_receive_reply (client, request); } int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
Re: [Qemu-devel] [PATCH v2 3/7] block/nbd-client: refactor reading reply
Quoted 47 lines · Paolo Bonzini
Quoted 46 lines · Vladimir Sementsov-Ogievskiy
Quoted 45 lines · Paolo Bonzini
Quoted 31 lines · Vladimir Sementsov-Ogievskiy
Read the whole reply in one place - in
nbd_read_reply_entry.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 1 + block/nbd-client.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-)block/nbd-client.h +1
-0@@ -21,6 +21,7 @@ typedef struct {Coroutine *coroutine;bool receiving;/* waiting for read_reply_co? */ NBDRequest *request; +QEMUIOVector *qiov; } NBDClientRequest; typedef struct NBDClientSession {block/nbd-client.c +3
-0@@ -98,6 +98,21 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)break ; } + if (s->reply.error == 0 && + s->requests[i].request->type == NBD_CMD_READ) + { +QEMUIOVector *qiov = s->requests[i].qiov;block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void · line 114
Quoted 10 lines
block/nbd-client.c +10
-0+assert (qiov !=NULL ); +assert (s->requests[i].request->len == +iov_size (qiov->iov, qiov->niov)); +if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, +NULL ) <0 ) + { + s->reply.error =EIO ; +break ; + } + }I am not sure this is an improvement. In principle you could have commands that read replies a bit at a time without using a QEMUIOVector.
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
Paolo
Hm, what do you mean? In this patch I don't change "how do we read it", I only move the reading code to one coroutine, to make it simple to extend it in future (block status, etc).
I disagree that it is easier to extend it in the future. If some commands in the future need a different "how do we read it" (e.g. for structured reply),
nbd_read_reply_entrymay not have all the information it needs anymore.
how is it possible? all information is in requests[i].
Quoted 7 lines
In fact, you're pushing knowledge of the commands from
nbd_co_requesttonbd_read_reply_entry:block/nbd-client.c +2
-0+if (s->reply.error ==0 && + s->requests[i].request->type ==NBD_CMD_READ )
Quoted 5 lines
and knowledge of
NBD_CMD_READsimply doesn't belong there. So there is an example of that right now, it is not some arbitrary bad thing that could happen in the future.Paolo
I can't agree that my point of view is wrong, it's just different.
You want commands, reading from socket, each knows what it should read.
I want the receiver - one sequential reader, that can read all kinds of replies and just wake requesting coroutines when all reading is done. For me it looks simpler than switching.
We can add reading callbacks for commands which have some payload, like s->requests[i].read_payload(ioc, request) or may be s->requests[i].request->read_payload(...), and call them from reply_entry instead of if (s->... == NBD_CMD_READ).
How do you see the refactoring for introducing structured reply?
Quoted 84 lines
Quoted 84 lines
nbd_read_reply_entyhas all
information it need (s->requests[i].request) to handle the whole reply.. It's an improvement, because it leads to separatingreply_entrycoroutine - it don't need any synchronization (yield/wake) more with request coroutines.Quoted 76 lines
Quoted 76 lines
block/nbd-client.c +0
-0/* We're woken up again by the request itself. Note that there
* is no race between yielding and reenteringread_reply_co. This
* is because:block/nbd-client.c +8
-14@@ -118,6 +133,7 @@ static coroutine_fn voidnbd_read_reply_entry (void *opaque) s->read_reply_co = NULL; } +/* qiov should be provided for READ and WRITE */ static int nbd_co_send_request(BlockDriverState *bs,NBDRequest *request,QEMUIOVector *qiov)@@ -145,6 +161,7 @@ static int nbd_co_send_request(BlockDriverState *bs, request->handle =INDEX_TO_HANDLE (s, i); s->requests[i].request = request; + s->requests[i].qiov = qiov;if (s->quit) { rc = -EIO ;@@ -155,7 +172,8 @@ static int nbd_co_send_request(BlockDriverState *bs, goto err; } - if (qiov) { +if (s->requests[i].request->type ==NBD_CMD_WRITE ) { +assert (qiov);qio_channel_set_cork (s->ioc, true); rc =nbd_send_request (s->ioc, request);if (rc >=0 && !s->quit) {@@ -181,9 +199,7 @@ err:return rc; } -static int nbd_co_receive_reply(NBDClientSession *s, -NBDRequest *request, -QEMUIOVector *qiov) +static int nbd_co_receive_reply (NBDClientSession *s,NBDRequest *request) {int ret;int i =HANDLE_TO_INDEX (s, request->handle);@@ -197,14 +213,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, }else {assert (s->reply.handle == request->handle); ret = -s->reply.error; -if (qiov && s->reply.error ==0 ) { -assert (request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) { - ret = -EIO ; - s->quit = true; - } - }/* Tell the read handler to read another header. */ s->reply.handle =0 ;@@ -232,16 +240,14 @@ static int nbd_co_request(BlockDriverState *bs,NBDClientSession *client =nbd_get_client_session (bs);int ret; -assert (!qiov || request->type ==NBD_CMD_WRITE || - request->type ==NBD_CMD_READ ); - ret =nbd_co_send_request (bs, request, - request->type ==NBD_CMD_WRITE ? qiov : NULL); +assert ((qiov ==NULL ) != + (request->type ==NBD_CMD_WRITE || request->type ==NBD_CMD_READ )); + ret =nbd_co_send_request (bs, request, qiov);if (ret <0 ) {return ret; } - return nbd_co_receive_reply(client, request, - request->type ==NBD_CMD_READ ? qiov : NULL); +return nbd_co_receive_reply (client, request); } int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
Signature · 2 lines
Best regards, Vladimir
Re: [Qemu-devel] [PATCH v2 3/7] block/nbd-client: refactor reading reply
Quoted 7 lines · Vladimir Sementsov-Ogievskiy
Quoted 5 lines
Quoted 1 line
I disagree that it is easier to extend it in the future. If some commands in the future need a different "how do we read it" (e.g. for structured reply),
nbd_read_reply_entrymay not have all the information it needs anymore.how is it possible? all information is in requests[i].
If you are okay with violating information hiding, then it is.
Quoted 11 lines
Quoted 10 lines
In fact, you're pushing knowledge of the commands from
nbd_co_requesttonbd_read_reply_entry:block/nbd-client.c +2
-0+if (s->reply.error ==0 && + s->requests[i].request->type ==NBD_CMD_READ )and knowledge of
NBD_CMD_READsimply doesn't belong there. So there is an example of that right now, it is not some arbitrary bad thing that could happen in the future.
Quoted 5 lines
I can't agree that my point of view is wrong, it's just different. You want commands, reading from socket, each knows what it should read. I want the receiver - one sequential reader, that can read all kinds of replies and just wake requesting coroutines when all reading is done. For me it looks simpler than switching.
It may look simpler, but I think it goes against the principle of coroutines which is to have related code in the same function. Here you have the command function that takes care of sending the request payload but not receiving the reply payload (except that for commands that aren't as simple as read or write, it will have to _process_ the reply payload). What to do with the reply payload is in another function that has to peek at the command in order to find out what to do. It doesn't seem like a better design, honestly.
Paolo
PATCH v2 4/7 block/nbd-client: drop reply field from NBDClientSession
In reply to Vladimir Sementsov-OgievskiyDrop 'reply' from NBDClientSession. It's used to only deliver request return code to receiving coroutine. Instead introduce per-request ret variable to simplify the scheme and make further refactoring possible.
--- block/nbd-client.h | 2 +- block/nbd-client.c | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-)
block/nbd-client.h +1 -1
@@ -22,6 +22,7 @@ typedef struct {bool receiving;/* waiting for read_reply_co? */ NBDRequest *request;QEMUIOVector *qiov; +int ret; } NBDClientRequest; typedef struct NBDClientSession {
@@ -35,7 +36,6 @@ typedef struct NBDClientSession {int in_flight;NBDClientRequest requests[MAX_NBD_REQUESTS ]; -NBDReply reply;bool quit; } NBDClientSession;
block/nbd-client.c +9 -13
@@ -74,10 +74,10 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)uint64_t i;int ret =0 ;Error *local_err =NULL ; +NBDReply reply;while (!s->quit) { -assert (s->reply.handle ==0 ); - ret =nbd_receive_reply (s->ioc, &s->reply, &local_err); + ret =nbd_receive_reply (s->ioc, &reply, &local_err);if (ret <0 ) {error_report_err (local_err); }
@@ -89,18 +89,18 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)* handler acts as a synchronization point and ensures that only * one coroutine is called until the reply finishes. */ - i =HANDLE_TO_INDEX (s, s->reply.handle); + i =HANDLE_TO_INDEX (s, reply.handle);if (i >=MAX_NBD_REQUESTS || !s->requests[i].coroutine || !s->requests[i].receiving || - s->reply.handle != s->requests[i].request->handle) + reply.handle != s->requests[i].request->handle) {break ; } -if (s->reply.error ==0 && - s->requests[i].request->type ==NBD_CMD_READ ) - { + s->requests[i].ret = -reply.error; + +if (reply.error ==0 && s->requests[i].request->type ==NBD_CMD_READ ) {QEMUIOVector *qiov = s->requests[i].qiov;assert (qiov !=NULL ); assert(s->requests[i].request->len ==
@@ -108,7 +108,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov,NULL ) <0 ) { - s->reply.error =EIO ; + s->requests[i].ret = -EIO ;break ; } }
@@ -211,11 +211,7 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request)if (!s->ioc || s->quit) { ret = -EIO ; }else { -assert (s->reply.handle == request->handle); - ret = -s->reply.error; - -/* Tell the read handler to read another header. */ - s->reply.handle =0 ; + ret = s->requests[i].ret; } s->requests[i].coroutine =NULL ;
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 4/7] block/nbd-client: drop reply field from NBDClientSession
Quoted 15 lines · Vladimir Sementsov-Ogievskiy
Drop 'reply' from NBDClientSession. It's used to only deliver request return code to receiving coroutine. Instead introduce per-request ret variable to simplify the scheme and make further refactoring possible.
Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.h | 2 +- block/nbd-client.c | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-)block/nbd-client.h +0
-0
block/nbd-client.h typedef struct { · line 26
Quoted 5 lines
block/nbd-client.h +1
-0@@ -22,6 +22,7 @@ typedef struct {bool receiving;/* waiting for read_reply_co? */ NBDRequest *request;QEMUIOVector *qiov; +int ret; } NBDClientRequest;
I like this idea. However, I note that:
block/nbd-client.h static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) · line 214
Quoted 10 lines
block/nbd-client.h +1
-5@@ -211,11 +211,7 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request)if (!s->ioc || s->quit) { ret = -EIO ; }else { -assert (s->reply.handle == request->handle); - ret = -s->reply.error; - -/* Tell the read handler to read another header. */ - s->reply.handle =0 ; + ret = s->requests[i].ret;
you are removing the assert you added in 2/7, where I questioned whether we needed NBDClientRequest.request in the first place. So there may be some rebase churn, depending on how that conversation pans out.
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
PATCH v2 5/7 block/nbd-client: nbd_co_send_request: return -EIO if s->quit was set in parallel
In reply to Vladimir Sementsov-OgievskiyIt's incorrect to return success rc >= 0 if we skip qio_channel_writev_all() call due to s->quit.
--- block/nbd-client.c | 3 +++ 1 file changed, 3 insertions(+)
block/nbd-client.c +3 -0
@@ -189,6 +189,9 @@ static int nbd_co_send_request(BlockDriverState *bs, } err: +if (rc >=0 && s->quit) { + rc = -EIO ; + }if (rc <0 ) { s->quit = true; s->requests[i].coroutine =NULL ;
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 5/7] block/nbd-client: nbd_co_send_request: return -EIO if s->quit was set in parallel
Quoted 2 lines · Vladimir Sementsov-Ogievskiy
It's incorrect to return success rc >= 0 if we skip
qio_channel_writev_all()call due tos->quit.
Does this need to cc: qemu-stable for 2.10.1 (or put another way, can we come up with some scenario of EAGAIN or other handling that would actually set s->quit in a parallel coroutine when a client sends out multiple requests at once)?
Quoted 11 lines
Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 3 +++ 1 file changed, 3 insertions(+)block/nbd-client.c +0
-0
block/nbd-client.c static int nbd_co_send_request(BlockDriverState *bs, · line 194
Quoted 6 lines
block/nbd-client.c +3
-0@@ -189,6 +189,9 @@ static int nbd_co_send_request(BlockDriverState *bs, } err: +if (rc >=0 && s->quit) { + rc = -EIO ; + }
I'm still not convinced this is in the right place. This fails the send_request regardless of whether we skipped qio_channel_writev_all(); shouldn't the rc be set only in the case that we actually skipped writing the full command because s->quit was detected at that point in time?
Quoted 4 lines
block/nbd-client.c +0
-0if (rc <0 ) { s->quit = true; s->requests[i].coroutine =NULL ;
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v2 5/7] block/nbd-client: nbd_co_send_request: return -EIO if s->quit was set in parallel
Quoted 8 lines · Eric Blake
Quoted 2 lines · Vladimir Sementsov-Ogievskiy
It's incorrect to return success rc >= 0 if we skip
qio_channel_writev_all()call due tos->quit.Does this need to cc: qemu-stable for 2.10.1 (or put another way, can we come up with some scenario of EAGAIN or other handling that would actually set
s->quitin a parallel coroutine when a client sends out multiple requests at once)?
Also, long subject line (longer than 80 columns!). I'd suggest something like:
nbd-client: fail with -EIO on early exit of nbd_co_send_request
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v2 5/7] block/nbd-client: nbd_co_send_request: return -EIO if s->quit was set in parallel
In reply to Eric BlakeQuoted 7 lines · Eric Blake
Quoted 2 lines · Vladimir Sementsov-Ogievskiy
It's incorrect to return success rc >= 0 if we skip
qio_channel_writev_all()call due tos->quit.Does this need to cc: qemu-stable for 2.10.1 (or put another way, can we come up with some scenario of EAGAIN or other handling that would actually set
s->quitin a parallel coroutine when a client sends out multiple requests at once)?
Intuitively, I think it should not be possible, first nbd_send_request should not do something serious (should not yield) after set_cork..
Quoted 26 lines
Quoted 11 lines
Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 3 +++ 1 file changed, 3 insertions(+)block/nbd-client.c +0
-0block/nbd-client.c static int nbd_co_send_request(BlockDriverState *bs, · line 194
Quoted 6 lines
block/nbd-client.c +3
-0@@ -189,6 +189,9 @@ static int nbd_co_send_request(BlockDriverState *bs, } err: +if (rc >=0 && s->quit) { + rc = -EIO ; + }I'm still not convinced this is in the right place. This fails the
send_requestregardless of whether we skippedqio_channel_writev_all(); shouldn't the rc be set only in the case that we actually skipped writing the full command becauses->quitwas detected at that point in time?Quoted 4 lines
block/nbd-client.c +0
-0if (rc <0 ) { s->quit = true; s->requests[i].coroutine =NULL ;
Signature · 2 lines
Best regards, Vladimir
PATCH v2 6/7 block/nbd-client: early fail nbd_read_reply_entry if s->quit is set
In reply to Vladimir Sementsov-OgievskiyDo not continue any operation if s->quit is set in parallel.
--- block/nbd-client.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
block/nbd-client.c +3 -4
@@ -81,7 +81,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)if (ret <0 ) {error_report_err (local_err); } -if (ret <=0 ) { +if (ret <=0 || s->quit) {break ; }
@@ -105,9 +105,8 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)assert (qiov !=NULL );assert (s->requests[i].request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) - { + ret =qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov,NULL ); +if (ret <0 || s->quit) { s->requests[i].ret = -EIO ;break ; }
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 6/7] block/nbd-client: early fail nbd_read_reply_entry if s->quit is set
Quoted 12 lines · Vladimir Sementsov-Ogievskiy
Do not continue any operation if
s->quitis set in parallel.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)block/nbd-client.c +0
-0
block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 86
Quoted 7 lines
block/nbd-client.c +1
-1@@ -81,7 +81,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)if (ret <0 ) {error_report_err (local_err); } -if (ret <=0 ) { +if (ret <=0 || s->quit) {break ; }
I'm still not convinced this helps: either nbd_receive_reply() already returned an error, or we got a successful reply header, at which point either the command is done (no need to abort the command early - it succeeded) or it is a read command (and we should detect at the point where we try to populate the qiov that we don't want to read any more). It depends on your 3/7 patch for the fact that we even try to read the qiov directly in this while loop rather than in the coroutine handler, where Paolo questioned whether we need that change.
block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 112
Quoted 12 lines
block/nbd-client.c +2
-3@@ -105,9 +105,8 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)assert (qiov !=NULL );assert (s->requests[i].request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) - { + ret =qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov,NULL ); +if (ret <0 || s->quit) { s->requests[i].ret = -EIO ;break ; }
The placement here looks odd. Either we should not attempt the read because s->quit was already set (okay, your first addition makes sense in that light), or we succeeded at the read (at which point, why do we need to claim it failed?).
I'm trying to look forward to structured reads, where we will have to deal with more than one server message in reply to a single client request. For read, we just piece together portions of the qiov until the server has sent us all the pieces. But for block query, I really do think we'll want to defer to specialized handlers for doing further reads (the amount of data to be read from the server is not known by the client a priori, so it is a two-part read, one to get the length, and another to read that remaining length); if we need some sort of callback function rather than cramming all the logic here in the main read loop, then the qio_channel_readv_all for read commands would belong in the callback, and it is more a question of the main read loop checking whether there is an early quit condition before calling into the callback.
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v2 6/7] block/nbd-client: early fail nbd_read_reply_entry if s->quit is set
Quoted 46 lines · Eric Blake
Quoted 12 lines · Vladimir Sementsov-Ogievskiy
Do not continue any operation if
s->quitis set in parallel.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)block/nbd-client.c +0
-0block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 86
Quoted 7 lines
block/nbd-client.c +1
-1@@ -81,7 +81,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)if (ret <0 ) {error_report_err (local_err); } -if (ret <=0 ) { +if (ret <=0 || s->quit) {break ; }I'm still not convinced this helps: either
nbd_receive_reply()already returned an error, or we got a successful reply header, at which point either the command is done (no need to abort the command early - it succeeded) or it is a read command (and we should detect at the point where we try to populate the qiov that we don't want to read any more). It depends on your 3/7 patch for the fact that we even try to read the qiov directly in this while loop rather than in the coroutine handler, where Paolo questioned whether we need that change.block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 112
Quoted 12 lines
block/nbd-client.c +2
-3@@ -105,9 +105,8 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)assert (qiov !=NULL );assert (s->requests[i].request->len ==iov_size (qiov->iov, qiov->niov)); -if (qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov, -NULL ) <0 ) - { + ret =qio_channel_readv_all (s->ioc, qiov->iov, qiov->niov,NULL ); +if (ret <0 || s->quit) { s->requests[i].ret = -EIO ;break ; }The placement here looks odd. Either we should not attempt the read because
s->quitwas already set (okay, your first addition makes sense in that light), or we succeeded at the read (at which point, why do we need to claim it failed?).
Quoted 9 lines
I'm trying to look forward to structured reads, where we will have to deal with more than one server message in reply to a single client request. For read, we just piece together portions of the qiov until the server has sent us all the pieces. But for block query, I really do think we'll want to defer to specialized handlers for doing further reads (the amount of data to be read from the server is not known by the client a priori, so it is a two-part read, one to get the length, and another to read that remaining length); if we need some sort of callback function rather than cramming all the logic here in the main read loop,
by patch 3 I do not mean in any way that I want to have all reading staff in one function, ofcourse it should be refactored for block-status addition. I just want to have all reading staff in one coroutine. Reading from ioc is sequential, so it's native to do it sequentially in one coroutine, without switches.
Quoted 4 lines
then the
qio_channel_readv_allfor read commands would belong in the callback, and it is more a question of the main read loop checking whether there is an early quit condition before calling into the callback.
If I understand correctly, the discussion is:
me: if something fails - fail all other parallel operations - it's simple and clear, but we need to check s->quit after every possible yield
you: .... _not_ all other parallel operations. - may be it is better, but in this case we need carefully define, which parallel operation we fail
on s->quit and which not and understand all effects of this division.
Signature · 2 lines
Best regards, Vladimir
Re: [Qemu-devel] [PATCH v2 6/7] block/nbd-client: early fail nbd_read_reply_entry if s->quit is set
Quoted 11 lines · Vladimir Sementsov-Ogievskiy
Quoted 10 lines
I'm trying to look forward to structured reads, where we will have to deal with more than one server message in reply to a single client request. For read, we just piece together portions of the qiov until the server has sent us all the pieces. But for block query, I really do think we'll want to defer to specialized handlers for doing further reads (the amount of data to be read from the server is not known by the client a priori, so it is a two-part read, one to get the length, and another to read that remaining length); if we need some sort of callback function rather than cramming all the logic here in the main read loop,
Quoted 5 lines
by patch 3 I do not mean in any way that I want to have all reading staff in one function, ofcourse it should be refactored
for block-status addition. I just want to have all reading staff in one coroutine. Reading from ioc is sequential, so it's
native to do it sequentially in one coroutine, without switches.
But why is that a goal? See it as a state machine that goes between the "waiting for header" and "waiting for payload" states. Reading header corresponds to read_reply_co waiting on the socket (and reading when it's ready); reading payload corresponds to the request coroutine waiting on the socket and reading when it's ready.
Paolo
Re: [Qemu-devel] [PATCH v2 6/7] block/nbd-client: early fail nbd_read_reply_entry if s->quit is set
Quoted 15 lines · Paolo Bonzini
Quoted 9 lines · Vladimir Sementsov-Ogievskiy
Quoted 9 lines
I'm trying to look forward to structured reads, where we will have to deal with more than one server message in reply to a single client request. For read, we just piece together portions of the qiov until the server has sent us all the pieces. But for block query, I really do think we'll want to defer to specialized handlers for doing further reads (the amount of data to be read from the server is not known by the client a priori, so it is a two-part read, one to get the length, and another to read that remaining length); if we need some sort of callback function rather than cramming all the logic here in the main read loop,
Quoted 5 lines
by patch 3 I do not mean in any way that I want to have all reading staff in one function, ofcourse it should be refactored
for block-status addition. I just want to have all reading staff in one coroutine. Reading from ioc is sequential, so it's
native to do it sequentially in one coroutine, without switches.
Quoted 7 lines
But why is that a goal? See it as a state machine that goes between the "waiting for header" and "waiting for payload" states. Reading header corresponds to
read_reply_cowaiting on the socket (and reading when it's ready); reading payload corresponds to the request coroutine waiting on the socket and reading when it's ready.Paolo
I just dislike public access to ioc for commands and extra coroutine switching and synchronization.
Signature · 2 lines
Best regards, Vladimir
PATCH v2 7/7 block/nbd-client: do not yield from nbd_read_reply_entry
In reply to Vladimir Sementsov-OgievskiyRefactor nbd client to not yield from nbd_read_reply_entry. It's possible now as all reading is done in nbd_read_reply_entry and all request-related data is stored in per-request s->requests[i].
We need here some additional work with s->requests[i].ret and s->quit to not fail requests on normal disconnet and to not report reading errors on normal disconnect.
--- block/nbd-client.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-)
block/nbd-client.c +10 -19
@@ -79,7 +79,11 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)while (!s->quit) { ret =nbd_receive_reply (s->ioc, &reply, &local_err);if (ret <0 ) { -error_report_err (local_err); +if (s->quit) { +error_free (local_err); + }else { +error_report_err (local_err); + } }if (ret <=0 || s->quit) {break ;
@@ -112,19 +116,8 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) } } -/* We're woken up again by the request itself. Note that there -* is no race between yielding and reentering read_reply_co. This -* is because: -* -* - if the request runs on the same AioContext, it is only -* entered after we yield -* -* - if the request runs on a different AioContext, reentering -* read_reply_co happens through a bottom half, which can only -* run after we yield. -*/ + s->requests[i].receiving = false;aio_co_wake (s->requests[i].coroutine); -qemu_coroutine_yield (); } s->quit = true;
@@ -157,6 +150,7 @@ static int nbd_co_send_request(BlockDriverState *bs, s->requests[i].coroutine =qemu_coroutine_self (); s->requests[i].receiving = false; + s->requests[i].ret = -EIO ; request->handle =INDEX_TO_HANDLE (s, i); s->requests[i].request = request;
@@ -210,7 +204,7 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) s->requests[i].receiving = true;qemu_coroutine_yield (); s->requests[i].receiving = false; -if (!s->ioc || s->quit) { +if (!s->ioc) { ret = -EIO ; }else { ret = s->requests[i].ret;
@@ -218,11 +212,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) s->requests[i].coroutine =NULL ; -/* Kick the read_reply_co to get the next reply. */ -if (s->read_reply_co) { -aio_co_wake (s->read_reply_co); - } -qemu_co_mutex_lock (&s->send_mutex); s->in_flight--;qemu_co_queue_next (&s->free_sema);
@@ -364,6 +353,8 @@ void nbd_client_close(BlockDriverState *bs)nbd_send_request (client->ioc, &request); + client->quit = true; +nbd_teardown_connection (bs); }
Signature · 1 line
2.11.1
Re: [Qemu-devel] [PATCH v2 7/7] block/nbd-client: do not yield from nbd_read_reply_entry
Quoted 4 lines · Vladimir Sementsov-Ogievskiy
Refactor nbd client to not yield from
nbd_read_reply_entry. It's possible now as all reading is done innbd_read_reply_entryand all request-related data is stored in per-request s->requests[i].
Quoted 8 lines
We need here some additional work with s->requests[i].ret and
s->quitto not fail requests on normal disconnet and to not report reading errors on normal disconnect.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-)
The diffstat may have merit, but I'm wondering:
block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 83
Quoted 12 lines
block/nbd-client.c +2
-1@@ -79,7 +79,11 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)while (!s->quit) { ret =nbd_receive_reply (s->ioc, &reply, &local_err);if (ret <0 ) { -error_report_err (local_err); +if (s->quit) { +error_free (local_err);
This discards errors on all remaining coroutines after we detect an early exit. Are we sure the coroutine that set s->quit will have reported an appropriate error, and thus explaining why we can discard all secondary errors? A comment in the code would be helpful.
block/nbd-client.c static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) · line 208
Quoted 12 lines
block/nbd-client.c +4
-1+ }else { +error_report_err (local_err); + } }@@ -210,7 +204,7 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) s->requests[i].receiving = true;qemu_coroutine_yield (); s->requests[i].receiving = false; -if (!s->ioc || s->quit) { +if (!s->ioc) { ret = -EIO ;
Why don't we need to check s->quit any more? That was just added earlier in the series; why the churn?
Quoted 15 lines
block/nbd-client.c +0
-5}else { ret = s->requests[i].ret;@@ -218,11 +212,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) s->requests[i].coroutine =NULL ; -/* Kick the read_reply_co to get the next reply. */ -if (s->read_reply_co) { -aio_co_wake (s->read_reply_co); - } -qemu_co_mutex_lock (&s->send_mutex); s->in_flight--;qemu_co_queue_next (&s->free_sema);
block/nbd-client.c void nbd_client_close(BlockDriverState *bs) · line 356
Quoted 4 lines
block/nbd-client.c +1
-0@@ -364,6 +353,8 @@ void nbd_client_close(BlockDriverState *bs)nbd_send_request (client->ioc, &request); + client->quit = true;
Previously, client->quit was only set when detecting a broken server, now it is also set for a clean exit. Do we need to change any documentation of the field?
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v2 7/7] block/nbd-client: do not yield from nbd_read_reply_entry
Quoted 15 lines · Eric Blake
Quoted 4 lines · Vladimir Sementsov-Ogievskiy
Refactor nbd client to not yield from
nbd_read_reply_entry. It's possible now as all reading is done innbd_read_reply_entryand all request-related data is stored in per-request s->requests[i].Quoted 8 lines
We need here some additional work with s->requests[i].ret and
s->quitto not fail requests on normal disconnet and to not report reading errors on normal disconnect.Signed-off-by Vladimir Sementsov-Ogievskiy <[email protected]>--- block/nbd-client.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-)The diffstat may have merit, but I'm wondering:
Quoted 15 lines
block/nbd-client.c static coroutine_fn void nbd_read_reply_entry(void *opaque) · line 83
Quoted 11 lines
block/nbd-client.c +2
-1@@ -79,7 +79,11 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)while (!s->quit) { ret =nbd_receive_reply (s->ioc, &reply, &local_err);if (ret <0 ) { -error_report_err (local_err); +if (s->quit) { +error_free (local_err);This discards errors on all remaining coroutines after we detect an early exit. Are we sure the coroutine that set
s->quitwill have reported an appropriate error, and thus explaining why we can discard all secondary errors? A comment in the code would be helpful.
I'll think about it.
Quoted 14 lines
block/nbd-client.c static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) · line 208
Quoted 12 lines
block/nbd-client.c +4
-1+ }else { +error_report_err (local_err); + } }@@ -210,7 +204,7 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) s->requests[i].receiving = true;qemu_coroutine_yield (); s->requests[i].receiving = false; -if (!s->ioc || s->quit) { +if (!s->ioc) { ret = -EIO ;Why don't we need to check
s->quitany more? That was just added earlier in the series; why the churn?
it is "to not fail requests on normal disconnet". Because reply_entry may be already finished. Initializing "+ s->requests[i].ret = -EIO;" is enough.
Quoted 17 lines
Quoted 17 lines
block/nbd-client.c +0
-5}else { ret = s->requests[i].ret;@@ -218,11 +212,6 @@ static int nbd_co_receive_reply(NBDClientSession *s, NBDRequest *request) s->requests[i].coroutine =NULL ; -/* Kick the read_reply_co to get the next reply. */ -if (s->read_reply_co) { -aio_co_wake (s->read_reply_co); - } -qemu_co_mutex_lock (&s->send_mutex); s->in_flight--;qemu_co_queue_next (&s->free_sema);@@ -364,6 +353,8 @@ void nbd_client_close(BlockDriverState *bs)
Quoted 6 lines
block/nbd-client.c void nbd_client_close(BlockDriverState *bs) · line 356
Quoted 3 lines
block/nbd-client.c +1
-0nbd_send_request (client->ioc, &request); + client->quit = true;Previously,
client->quitwas only set when detecting a broken server, now it is also set for a clean exit. Do we need to change any documentation of the field?
It has documentation?
Quoted 1 line
Signature · 2 lines
Best regards, Vladimir
Re: [Qemu-devel] [PATCH v2 7/7] block/nbd-client: do not yield from nbd_read_reply_entry
Quoted 11 lines · Vladimir Sementsov-Ogievskiy
Quoted 9 lines · Eric Blake
Quoted 8 lines · Vladimir Sementsov-Ogievskiy
Refactor nbd client to not yield from
nbd_read_reply_entry. It's possible now as all reading is done innbd_read_reply_entryand all request-related data is stored in per-request s->requests[i].We need here some additional work with s->requests[i].ret and
s->quitto not fail requests on normal disconnet and to not report reading errors on normal disconnect.
Quoted 8 lines
Quoted 6 lines
Review void nbd_client_close(BlockDriverState *bs) · line 354
Quoted 3 lines
(quoted diff) +0
-0@@ -364,6 +353,8 @@ void nbd_client_close(BlockDriverState *bs) nbd_send_request(client->ioc, &request); + client->quit = true;Previously,
client->quitwas only set when detecting a broken server, now it is also set for a clean exit. Do we need to change any documentation of the field?It has documentation?
Touche. But it probably should, especially if we are changing its semantics, to make it easier to understand from looking at the struct what semantics to expect.
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org