aboutsummaryrefslogtreecommitdiff
path: root/nbd/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'nbd/server.c')
-rw-r--r--nbd/server.c78
1 files changed, 75 insertions, 3 deletions
diff --git a/nbd/server.c b/nbd/server.c
index 92c0fdd03b..be7310cb41 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);
+
+ if (status < 0) {
+ error_setg_errno(errp, -status, "unable to check for holes");
+ return status;
+ }
+ assert(pnum && pnum <= size - progress);
+ 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, 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, false,
+ errp);
+ }
+
+ if (ret < 0) {
+ break;
+ }
+ progress += pnum;
+ }
+ if (!ret) {
+ ret = nbd_co_send_structured_done(client, handle, errp);
+ }
+ return ret;
+}
+
static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
uint64_t handle,
uint32_t error,
@@ -1481,6 +1542,16 @@ static coroutine_fn void nbd_trip(void *opaque)
}
}
+ if (client->structured_reply && !(request.flags & NBD_CMD_FLAG_DF)) {
+ 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 +1632,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);