aboutsummaryrefslogtreecommitdiff
path: root/nbd/client.c
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2019-02-18 14:56:01 +0100
committerKevin Wolf <kwolf@redhat.com>2019-02-25 15:03:19 +0100
commitd3bd5b90890f6715bcee38e00745112157dfbe59 (patch)
tree3f712b4e248b6902231e755db75cb49ef80e41a5 /nbd/client.c
parenta7b78fc944b20b953d68426b7db2c81fc6a5b5af (diff)
nbd: Use low-level QIOChannel API in nbd_read_eof()
Instead of using the convenience wrapper qio_channel_read_all_eof(), use the lower level QIOChannel API. This means duplicating some code, but we'll need this because this coroutine yield is special: We want it to be interruptible so that nbd_client_attach_aio_context() can correctly reenter the coroutine. This moves the bdrv_dec/inc_in_flight() pair into nbd_read_eof(), so that connection_co will always sit in this exact qio_channel_yield() call when bdrv_drain() returns. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
Diffstat (limited to 'nbd/client.c')
-rw-r--r--nbd/client.c46
1 files changed, 37 insertions, 9 deletions
diff --git a/nbd/client.c b/nbd/client.c
index 28d174c0f3..de7da48246 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -1394,30 +1394,58 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc,
* negative errno on failure (errp is set)
*/
static inline int coroutine_fn
-nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size, Error **errp)
+nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size,
+ Error **errp)
{
- int ret;
+ bool partial = false;
assert(size);
- ret = qio_channel_read_all_eof(ioc, buffer, size, errp);
- if (ret < 0) {
- ret = -EIO;
+ while (size > 0) {
+ struct iovec iov = { .iov_base = buffer, .iov_len = size };
+ ssize_t len;
+
+ len = qio_channel_readv(ioc, &iov, 1, errp);
+ if (len == QIO_CHANNEL_ERR_BLOCK) {
+ bdrv_dec_in_flight(bs);
+ qio_channel_yield(ioc, G_IO_IN);
+ bdrv_inc_in_flight(bs);
+ continue;
+ } else if (len < 0) {
+ return -EIO;
+ } else if (len == 0) {
+ if (partial) {
+ error_setg(errp,
+ "Unexpected end-of-file before all bytes were read");
+ return -EIO;
+ } else {
+ return 0;
+ }
+ }
+
+ partial = true;
+ size -= len;
+ buffer = (uint8_t*) buffer + len;
}
- return ret;
+ return 1;
}
/* nbd_receive_reply
+ *
+ * Decreases bs->in_flight while waiting for a new reply. This yield is where
+ * we wait indefinitely and the coroutine must be able to be safely reentered
+ * for nbd_client_attach_aio_context().
+ *
* Returns 1 on success
* 0 on eof, when no data was read (errp is not set)
* negative errno on failure (errp is set)
*/
-int coroutine_fn nbd_receive_reply(QIOChannel *ioc, NBDReply *reply,
- Error **errp)
+int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc,
+ NBDReply *reply, Error **errp)
{
int ret;
const char *type;
- ret = nbd_read_eof(ioc, &reply->magic, sizeof(reply->magic), errp);
+ ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp);
if (ret <= 0) {
return ret;
}