Signed-off-by: Max Reitz <mre...@redhat.com> --- block/nbd-client.c | 35 +++++++++++++++++++++++++++++++---- include/block/nbd.h | 2 ++ nbd.c | 12 ++++++++++-- 3 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/block/nbd-client.c b/block/nbd-client.c index e1bb919..be6803d 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -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; } diff --git a/include/block/nbd.h b/include/block/nbd.h index 65f409d..5e31986 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -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); diff --git a/nbd.c b/nbd.c index 5764fd1..a05fd02 100644 --- a/nbd.c +++ b/nbd.c @@ -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; -- 2.1.0