aboutsummaryrefslogtreecommitdiff
path: root/nbd
diff options
context:
space:
mode:
Diffstat (limited to 'nbd')
-rw-r--r--nbd/client.c12
-rw-r--r--nbd/server.c85
-rw-r--r--nbd/trace-events1
3 files changed, 89 insertions, 9 deletions
diff --git a/nbd/client.c b/nbd/client.c
index eea236ca06..89f80f9590 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -66,7 +66,7 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
uint32_t len, const char *data,
Error **errp)
{
- nbd_option req;
+ NBDOption req;
QEMU_BUILD_BUG_ON(sizeof(req) != 16);
if (len == -1) {
@@ -109,7 +109,7 @@ static void nbd_send_opt_abort(QIOChannel *ioc)
* payload. Return 0 if successful, -1 with errp set if it is
* impossible to continue. */
static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
- nbd_opt_reply *reply, Error **errp)
+ NBDOptionReply *reply, Error **errp)
{
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
if (nbd_read(ioc, reply, sizeof(*reply), errp) < 0) {
@@ -146,7 +146,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
* can fall back to other approaches), or -1 with errp set for other
* errors.
*/
-static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
+static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply,
Error **errp)
{
char *msg = NULL;
@@ -239,7 +239,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
Error **errp)
{
- nbd_opt_reply reply;
+ NBDOptionReply reply;
uint32_t len;
uint32_t namelen;
char name[NBD_MAX_NAME_SIZE + 1];
@@ -325,7 +325,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
NBDExportInfo *info, Error **errp)
{
- nbd_opt_reply reply;
+ NBDOptionReply reply;
uint32_t len = strlen(wantname);
uint16_t type;
int error;
@@ -517,7 +517,7 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
*/
static int nbd_request_simple_option(QIOChannel *ioc, int opt, Error **errp)
{
- nbd_opt_reply reply;
+ NBDOptionReply reply;
int error;
if (nbd_send_option_request(ioc, opt, 0, NULL, errp) < 0) {
diff --git a/nbd/server.c b/nbd/server.c
index 92c0fdd03b..6cf2eeb2c1 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1190,6 +1190,12 @@ void nbd_export_put(NBDExport *exp)
nbd_export_close(exp);
}
+ /* nbd_export_close() may theoretically reduce refcount to 0. It may happen
+ * if someone calls nbd_export_put() on named export not through
+ * nbd_export_set_name() when refcount is 1. So, let's assert that
+ * it is > 0.
+ */
+ assert(exp->refcount > 0);
if (--exp->refcount == 0) {
assert(exp->name == NULL);
assert(exp->description == NULL);
@@ -1303,6 +1309,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 +1320,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 +1548,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 +1639,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 92568edce5..2b8268ce8c 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