diff options
author | Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 2017-10-27 12:40:32 +0200 |
---|---|---|
committer | Eric Blake <eblake@redhat.com> | 2017-10-30 21:48:06 +0100 |
commit | 5c54e7fa714fdee68ede764610adda806162080f (patch) | |
tree | e24013fb642d1314a49cc1e85d402473e0138dd4 /nbd/server.c | |
parent | e68c35cfb8088a11300371751e3987f67cac15b1 (diff) |
nbd: Minimal structured read for server
Minimal implementation of structured read: one structured reply chunk,
no segmentation.
Minimal structured error implementation: no text message.
Support DF flag, but just ignore it, as there is no segmentation any
way.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171027104037.8319-8-eblake@redhat.com>
Diffstat (limited to 'nbd/server.c')
-rw-r--r-- | nbd/server.c | 107 |
1 files changed, 97 insertions, 10 deletions
diff --git a/nbd/server.c b/nbd/server.c index cf815603a6..3261fd1bd7 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -100,6 +100,8 @@ struct NBDClient { QTAILQ_ENTRY(NBDClient) next; int nb_requests; bool closing; + + bool structured_reply; }; /* That's all folks */ @@ -769,6 +771,23 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, "TLS not configured"); } break; + + case NBD_OPT_STRUCTURED_REPLY: + if (length) { + ret = nbd_reject_length(client, length, option, false, + errp); + } else if (client->structured_reply) { + ret = nbd_negotiate_send_rep_err( + client->ioc, NBD_REP_ERR_INVALID, option, errp, + "structured reply already negotiated"); + } else { + ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, + option, errp); + client->structured_reply = true; + myflags |= NBD_FLAG_SEND_DF; + } + break; + default: if (nbd_drop(client->ioc, length, errp) < 0) { return -EIO; @@ -1243,6 +1262,60 @@ static int nbd_co_send_simple_reply(NBDClient *client, return nbd_co_send_iov(client, iov, len ? 2 : 1, errp); } +static inline void set_be_chunk(NBDStructuredReplyChunk *chunk, uint16_t flags, + uint16_t type, uint64_t handle, uint32_t length) +{ + stl_be_p(&chunk->magic, NBD_STRUCTURED_REPLY_MAGIC); + stw_be_p(&chunk->flags, flags); + stw_be_p(&chunk->type, type); + stq_be_p(&chunk->handle, handle); + stl_be_p(&chunk->length, length); +} + +static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, + uint64_t handle, + uint64_t offset, + void *data, + size_t size, + Error **errp) +{ + NBDStructuredRead chunk; + struct iovec iov[] = { + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + {.iov_base = data, .iov_len = 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); + stq_be_p(&chunk.offset, offset); + + return nbd_co_send_iov(client, iov, 2, errp); +} + +static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, + uint64_t handle, + uint32_t error, + Error **errp) +{ + NBDStructuredError chunk; + int nbd_err = system_errno_to_nbd_errno(error); + struct iovec iov[] = { + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + /* FIXME: Support human-readable error message */ + }; + + assert(nbd_err); + trace_nbd_co_send_structured_error(handle, nbd_err, + nbd_err_lookup(nbd_err)); + set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle, + sizeof(chunk) - sizeof(chunk.h)); + stl_be_p(&chunk.error, nbd_err); + stw_be_p(&chunk.message_length, 0); + + return nbd_co_send_iov(client, iov, 1, errp); +} + /* nbd_co_receive_request * Collect a client request. Return 0 if request looks valid, -EIO to drop * connection right away, and any other negative value to report an error to @@ -1253,6 +1326,7 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, Error **errp) { NBDClient *client = req->client; + int valid_flags; g_assert(qemu_in_coroutine()); assert(client->recv_coroutine == qemu_coroutine_self()); @@ -1314,13 +1388,15 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, (uint64_t)client->exp->size); return request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL; } - if (request->flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) { - error_setg(errp, "unsupported flags (got 0x%x)", request->flags); - return -EINVAL; + valid_flags = NBD_CMD_FLAG_FUA; + if (request->type == NBD_CMD_READ && client->structured_reply) { + valid_flags |= NBD_CMD_FLAG_DF; + } else if (request->type == NBD_CMD_WRITE_ZEROES) { + valid_flags |= NBD_CMD_FLAG_NO_HOLE; } - if (request->type != NBD_CMD_WRITE_ZEROES && - (request->flags & NBD_CMD_FLAG_NO_HOLE)) { - error_setg(errp, "unexpected flags (got 0x%x)", request->flags); + if (request->flags & ~valid_flags) { + error_setg(errp, "unsupported flags for command %s (got 0x%x)", + nbd_cmd_lookup(request->type), request->flags); return -EINVAL; } @@ -1458,10 +1534,21 @@ reply: local_err = NULL; } - if (nbd_co_send_simple_reply(req->client, request.handle, - ret < 0 ? -ret : 0, - req->data, reply_data_len, &local_err) < 0) - { + if (client->structured_reply && request.type == NBD_CMD_READ) { + if (ret < 0) { + ret = nbd_co_send_structured_error(req->client, request.handle, + -ret, &local_err); + } else { + ret = nbd_co_send_structured_read(req->client, request.handle, + request.from, req->data, + reply_data_len, &local_err); + } + } else { + ret = nbd_co_send_simple_reply(req->client, request.handle, + ret < 0 ? -ret : 0, + req->data, reply_data_len, &local_err); + } + if (ret < 0) { error_prepend(&local_err, "Failed to send reply: "); goto disconnect; } |