@@ -28,6 +28,7 @@
#include "nbd-client.h"
#include "qemu/sockets.h"
+#include "qemu/timer.h"
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
@@ -158,15 +159,21 @@ static void nbd_co_receive_reply(NbdClientSession *s,
/* Wait until we're woken up by the read handler. TODO: perhaps
* peek at the next reply and avoid yielding if it's ours? */
- qemu_coroutine_yield();
+ if (co_yield_timeout(QEMU_CLOCK_REALTIME, NBD_TIMEOUT)) {
+ reply->error = ETIMEDOUT;
+ return;
+ }
+
*reply = s->reply;
if (reply->handle != request->handle) {
reply->error = EIO;
} else {
if (qiov && reply->error == 0) {
- ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
- offset, request->len);
- if (ret != request->len) {
+ ret = qemu_co_recvv_timeout(s->sock, qiov->iov, qiov->niov,
+ offset, request->len, NBD_TIMEOUT);
+ if (ret < 0) {
+ reply->error = -ret;
+ } else if (ret != request->len) {
reply->error = EIO;
}
}
@@ -220,6 +227,11 @@ static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
nbd_co_receive_reply(client, &request, &reply, qiov, offset);
}
nbd_coroutine_end(client, &request);
+
+ if (reply.error == ETIMEDOUT) {
+ nbd_teardown_connection(bs);
+ }
+
return -reply.error;
}
@@ -249,6 +261,11 @@ static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
nbd_co_receive_reply(client, &request, &reply, NULL, 0);
}
nbd_coroutine_end(client, &request);
+
+ if (reply.error == ETIMEDOUT) {
+ nbd_teardown_connection(bs);
+ }
+
return -reply.error;
}
@@ -316,6 +333,11 @@ int nbd_client_co_flush(BlockDriverState *bs)
nbd_co_receive_reply(client, &request, &reply, NULL, 0);
}
nbd_coroutine_end(client, &request);
+
+ if (reply.error == ETIMEDOUT) {
+ nbd_teardown_connection(bs);
+ }
+
return -reply.error;
}
@@ -341,6 +363,11 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
nbd_co_receive_reply(client, &request, &reply, NULL, 0);
}
nbd_coroutine_end(client, &request);
+
+ if (reply.error == ETIMEDOUT) {
+ nbd_teardown_connection(bs);
+ }
+
return -reply.error;
}
@@ -73,6 +73,8 @@ enum {
/* Maximum size of a single READ/WRITE data buffer */
#define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024)
+#define NBD_TIMEOUT (INT64_C(10) * 1000 * 1000 * 1000) /* ns */
+
ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
off_t *size, Error **errp);
@@ -140,12 +140,13 @@ static void nbd_update_can_read(NBDClient *client);
ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
{
+ int64_t deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + NBD_TIMEOUT;
size_t offset = 0;
int err;
if (qemu_in_coroutine()) {
if (do_read) {
- return qemu_co_recv(fd, buffer, size);
+ return qemu_co_recv_timeout(fd, buffer, size, NBD_TIMEOUT);
} else {
return qemu_co_send(fd, buffer, size);
}
@@ -154,6 +155,10 @@ ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
while (offset < size) {
ssize_t len;
+ if (do_read && qemu_clock_get_ns(QEMU_CLOCK_REALTIME) >= deadline) {
+ return -ETIMEDOUT;
+ }
+
if (do_read) {
len = qemu_recv(fd, buffer + offset, size - offset, 0);
} else {
@@ -178,6 +183,7 @@ ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
}
offset += len;
+ deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + NBD_TIMEOUT;
}
return offset;
@@ -1208,7 +1214,9 @@ static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *reque
if (command == NBD_CMD_WRITE) {
TRACE("Reading %u byte(s)", request->len);
- if (qemu_co_recv(csock, req->data, request->len) != request->len) {
+ if (qemu_co_recv_timeout(csock, req->data, request->len, NBD_TIMEOUT)
+ != request->len)
+ {
LOG("reading from socket failed");
rc = -EIO;
goto out;
Signed-off-by: Max Reitz <mreitz@redhat.com> --- block/nbd-client.c | 35 +++++++++++++++++++++++++++++++---- include/block/nbd.h | 2 ++ nbd.c | 12 ++++++++++-- 3 files changed, 43 insertions(+), 6 deletions(-)