diff --git a/block/nbd.c b/block/nbd.c index a50d24b50a46335257decb4f2ba71774ba34f703..8b8ba56cdd0be2c3a16291a0136c3744f5deaccd 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -388,6 +388,7 @@ static QemuOptsList nbd_runtime_opts = { .type = QEMU_OPT_STRING, .help = "ID of the TLS credentials to use", }, + { /* end of list */ } }, }; diff --git a/nbd/server.c b/nbd/server.c index 92c0fdd03b651872fda5f5c0a52227c1b77996d1..e443b3cf5c58eb35c01433526f9adb6e0119c128 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1303,6 +1303,7 @@ static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, uint64_t offset, void *data, size_t size, + bool final, Error **errp) { NBDStructuredReadData chunk; @@ -1313,13 +1314,73 @@ static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, assert(size); trace_nbd_co_send_structured_read(handle, offset, data, size); - set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_OFFSET_DATA, - handle, sizeof(chunk) - sizeof(chunk.h) + size); + set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0, + NBD_REPLY_TYPE_OFFSET_DATA, handle, + sizeof(chunk) - sizeof(chunk.h) + size); stq_be_p(&chunk.offset, offset); return nbd_co_send_iov(client, iov, 2, errp); } +static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, + uint64_t handle, + uint64_t offset, + uint8_t *data, + size_t size, + Error **errp) +{ + int ret = 0; + NBDExport *exp = client->exp; + size_t progress = 0; + + while (progress < size) { + int64_t pnum; + int status = bdrv_block_status_above(blk_bs(exp->blk), NULL, + offset + progress, + size - progress, &pnum, NULL, + NULL); + bool final; + + if (status < 0) { + error_setg_errno(errp, -status, "unable to check for holes"); + return status; + } + assert(pnum && pnum <= size - progress); + final = progress + pnum == size; + if (status & BDRV_BLOCK_ZERO) { + NBDStructuredReadHole chunk; + struct iovec iov[] = { + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + }; + + trace_nbd_co_send_structured_read_hole(handle, offset + progress, + pnum); + set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0, + NBD_REPLY_TYPE_OFFSET_HOLE, + handle, sizeof(chunk) - sizeof(chunk.h)); + stq_be_p(&chunk.offset, offset + progress); + stl_be_p(&chunk.length, pnum); + ret = nbd_co_send_iov(client, iov, 1, errp); + } else { + ret = blk_pread(exp->blk, offset + progress + exp->dev_offset, + data + progress, pnum); + if (ret < 0) { + error_setg_errno(errp, -ret, "reading from file failed"); + break; + } + ret = nbd_co_send_structured_read(client, handle, offset + progress, + data + progress, pnum, final, + errp); + } + + if (ret < 0) { + break; + } + progress += pnum; + } + return ret; +} + static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, uint64_t handle, uint32_t error, @@ -1481,6 +1542,17 @@ static coroutine_fn void nbd_trip(void *opaque) } } + if (client->structured_reply && !(request.flags & NBD_CMD_FLAG_DF) && + request.len) { + ret = nbd_co_send_sparse_read(req->client, request.handle, + request.from, req->data, request.len, + &local_err); + if (ret < 0) { + goto reply; + } + goto done; + } + ret = blk_pread(exp->blk, request.from + exp->dev_offset, req->data, request.len); if (ret < 0) { @@ -1561,7 +1633,8 @@ reply: } else if (reply_data_len) { ret = nbd_co_send_structured_read(req->client, request.handle, request.from, req->data, - reply_data_len, &local_err); + reply_data_len, true, + &local_err); } else { ret = nbd_co_send_structured_done(req->client, request.handle, &local_err); diff --git a/nbd/trace-events b/nbd/trace-events index 92568edce506b8b80d4d85e8869150b25bb45e51..2b8268ce8c398c383ca7f6439aab2a96a6ce4e73 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -57,6 +57,7 @@ nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients fr nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, int len) "Send simple reply: handle = %" PRIu64 ", error = %" PRIu32 " (%s), len = %d" nbd_co_send_structured_done(uint64_t handle) "Send structured reply done: handle = %" PRIu64 nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" +nbd_co_send_structured_read_hole(uint64_t handle, uint64_t offset, size_t size) "Send structured read hole reply: handle = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'" nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32