From nobody Mon Feb 9 04:31:56 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1507802624827314.4414968618112; Thu, 12 Oct 2017 03:03:44 -0700 (PDT) Received: from localhost ([::1]:44565 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e2aL7-00015l-F8 for importer@patchew.org; Thu, 12 Oct 2017 06:03:41 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:35519) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e2aBL-0001Gq-Ee for qemu-devel@nongnu.org; Thu, 12 Oct 2017 05:53:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1e2aBF-0000Hb-Eg for qemu-devel@nongnu.org; Thu, 12 Oct 2017 05:53:35 -0400 Received: from mailhub.sw.ru ([195.214.232.25]:18428 helo=relay.sw.ru) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1e2aBE-0000DY-Tt for qemu-devel@nongnu.org; Thu, 12 Oct 2017 05:53:29 -0400 Received: from kvm.sw.ru (msk-vpn.virtuozzo.com [195.214.232.6]) by relay.sw.ru (8.13.4/8.13.4) with ESMTP id v9C9rJVt025088; Thu, 12 Oct 2017 12:53:21 +0300 (MSK) From: Vladimir Sementsov-Ogievskiy To: qemu-block@nongnu.org, qemu-devel@nongnu.org Date: Thu, 12 Oct 2017 12:53:19 +0300 Message-Id: <20171012095319.136610-14-vsementsov@virtuozzo.com> X-Mailer: git-send-email 2.11.1 In-Reply-To: <20171012095319.136610-1-vsementsov@virtuozzo.com> References: <20171012095319.136610-1-vsementsov@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: OpenBSD 3.x [fuzzy] X-Received-From: 195.214.232.25 Subject: [Qemu-devel] [PATCH v3 13/13] nbd: Minimal structured read for client X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, mreitz@redhat.com, den@openvz.org, pbonzini@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Minimal implementation: for structured error only error_report error message. Signed-off-by: Vladimir Sementsov-Ogievskiy --- include/block/nbd.h | 6 + block/nbd-client.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++= ---- nbd/client.c | 7 + 3 files changed, 379 insertions(+), 29 deletions(-) diff --git a/include/block/nbd.h b/include/block/nbd.h index 1ef8c8897f..e3350b67a4 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -203,6 +203,11 @@ enum { #define NBD_SREP_TYPE_ERROR NBD_SREP_ERR(1) #define NBD_SREP_TYPE_ERROR_OFFSET NBD_SREP_ERR(2) =20 +static inline bool nbd_srep_type_is_error(int type) +{ + return type & (1 << 15); +} + /* NBD errors are based on errno numbers, so there is a 1:1 mapping, * but only a limited set of errno values is specified in the protocol. * Everything else is squashed to EINVAL. @@ -241,6 +246,7 @@ static inline int nbd_errno_to_system_errno(int err) struct NBDExportInfo { /* Set by client before nbd_receive_negotiate() */ bool request_sizes; + bool structured_reply; /* Set by server results during nbd_receive_negotiate() */ uint64_t size; uint16_t flags; diff --git a/block/nbd-client.c b/block/nbd-client.c index 58493b7ac4..4d08cf3fd3 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -29,6 +29,7 @@ =20 #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "nbd-client.h" =20 #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ (uint64_t)(intptr_t)(bs)) @@ -93,7 +94,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaqu= e) if (i >=3D MAX_NBD_REQUESTS || !s->requests[i].coroutine || !s->requests[i].receiving || - nbd_reply_is_structured(&s->reply)) + (nbd_reply_is_structured(&s->reply) && !s->info.structured_rep= ly)) { break; } @@ -181,75 +182,406 @@ err: return rc; } =20 -static int nbd_co_receive_reply(NBDClientSession *s, - uint64_t handle, - QEMUIOVector *qiov) +static inline void payload_advance16(uint8_t **payload, uint16_t **ptr) +{ + *ptr =3D (uint16_t *)*payload; + be16_to_cpus(*ptr); + *payload +=3D sizeof(**ptr); +} + +static inline void payload_advance32(uint8_t **payload, uint32_t **ptr) +{ + *ptr =3D (uint32_t *)*payload; + be32_to_cpus(*ptr); + *payload +=3D sizeof(**ptr); +} + +static inline void payload_advance64(uint8_t **payload, uint64_t **ptr) +{ + *ptr =3D (uint64_t *)*payload; + be64_to_cpus(*ptr); + *payload +=3D sizeof(**ptr); +} + +static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, + uint8_t *payload, QEMUIOVector *q= iov) +{ + uint64_t *offset; + uint32_t *hole_size; + + if (chunk->length !=3D sizeof(*offset) + sizeof(*hole_size)) { + return -EINVAL; + } + + payload_advance64(&payload, &offset); + payload_advance32(&payload, &hole_size); + + if (*offset + *hole_size > qiov->size) { + return -EINVAL; + } + + qemu_iovec_memset(qiov, *offset, 0, *hole_size); + + return 0; +} + +static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk, + uint8_t *payload, int *request_ret) +{ + uint32_t *error; + uint16_t *message_size; + + assert(chunk->type & (1 << 15)); + + if (chunk->length < sizeof(error) + sizeof(message_size)) { + return -EINVAL; + } + + payload_advance32(&payload, &error); + payload_advance16(&payload, &message_size); + + error_report("%.*s", *message_size, payload); + + /* TODO add special case for ERROR_OFFSET */ + + *request_ret =3D nbd_errno_to_system_errno(*error); + + return 0; +} + +static int nbd_co_receive_offset_data_payload(NBDClientSession *s, + QEMUIOVector *qiov) +{ + QEMUIOVector sub_qiov; + uint64_t offset; + size_t data_size; + int ret; + NBDStructuredReplyChunk *chunk =3D &s->reply.structured; + + assert(nbd_reply_is_structured(&s->reply)); + + if (chunk->length < sizeof(offset)) { + return -EINVAL; + } + + if (nbd_read(s->ioc, &offset, sizeof(offset), NULL) < 0) { + return -EIO; + } + be64_to_cpus(&offset); + + data_size =3D chunk->length - sizeof(offset); + if (offset + data_size > qiov->size) { + return -EINVAL; + } + + qemu_iovec_init(&sub_qiov, qiov->niov); + qemu_iovec_concat(&sub_qiov, qiov, offset, data_size); + ret =3D qio_channel_readv_all(s->ioc, sub_qiov.iov, sub_qiov.niov, NUL= L); + qemu_iovec_destroy(&sub_qiov); + + return ret < 0 ? -EIO : 0; +} + +#define NBD_MAX_MALLOC_PAYLOAD 1000 +static int nbd_co_receive_structured_payload(NBDClientSession *s, + void **payload) +{ + int ret; + uint32_t len; + + assert(nbd_reply_is_structured(&s->reply)); + + len =3D s->reply.structured.length; + + if (len =3D=3D 0) { + return 0; + } + + if (payload =3D=3D NULL) { + return -EINVAL; + } + + if (len > NBD_MAX_MALLOC_PAYLOAD) { + return -EINVAL; + } + + *payload =3D qemu_memalign(8, len); + ret =3D nbd_read(s->ioc, *payload, len, NULL); + if (ret < 0) { + qemu_vfree(*payload); + *payload =3D NULL; + return ret; + } + + return 0; +} + +/* nbd_co_do_receive_one_chunk + * for simple reply: + * set request_ret to received reply error + * if qiov is not NULL: read payload to @qiov + * for structured reply chunk: + * if error chunk: read payload, set @request_ret, do not set @payload + * else if offset_data chunk: read payload data to @qiov, do not set @pa= yload + * else: read payload to @payload + */ +static int nbd_co_do_receive_one_chunk(NBDClientSession *s, uint64_t handl= e, + bool only_structured, int *request_= ret, + QEMUIOVector *qiov, void **payload) { int ret; int i =3D HANDLE_TO_INDEX(s, handle); + void *local_payload =3D NULL; + + if (payload) { + *payload =3D NULL; + } + *request_ret =3D 0; =20 /* Wait until we're woken up by nbd_read_reply_entry. */ s->requests[i].receiving =3D true; qemu_coroutine_yield(); s->requests[i].receiving =3D false; if (!s->ioc || s->quit) { - ret =3D -EIO; - } else { - assert(s->reply.handle =3D=3D handle); - ret =3D -nbd_errno_to_system_errno(s->reply.simple.error); - if (qiov && ret =3D=3D 0) { - if (qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov, - NULL) < 0) { - ret =3D -EIO; - s->quit =3D true; - } + return -EIO; + } + + assert(s->reply.handle =3D=3D handle); + + if (nbd_reply_is_simple(&s->reply)) { + if (only_structured) { + return -EINVAL; } =20 - /* Tell the read handler to read another header. */ - s->reply.handle =3D 0; + *request_ret =3D -nbd_errno_to_system_errno(s->reply.simple.error); + if (*request_ret < 0 || !qiov) { + return 0; + } + + return qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov, + NULL) < 0 ? -EIO : 0; + } + + /* handle structured reply chunk */ + assert(s->info.structured_reply); + + if (s->reply.structured.type =3D=3D NBD_SREP_TYPE_NONE) { + return 0; + } + + if (s->reply.structured.type =3D=3D NBD_SREP_TYPE_OFFSET_DATA) { + if (!qiov) { + return -EINVAL; + } + + return nbd_co_receive_offset_data_payload(s, qiov); + } + + if (nbd_srep_type_is_error(s->reply.structured.type)) { + payload =3D &local_payload; + } + + ret =3D nbd_co_receive_structured_payload(s, payload); + if (ret < 0) { + return ret; } =20 - s->requests[i].coroutine =3D NULL; + if (nbd_srep_type_is_error(s->reply.structured.type)) { + ret =3D nbd_parse_error_payload(&s->reply.structured, local_payloa= d, + request_ret); + qemu_vfree(local_payload); + return ret; + } + + return 0; +} + +/* nbd_co_receive_one_chunk + * Read reply, wake up read_reply_co and set s->quit if needed. + * Return value is a fatal error code or normal nbd reply error code + */ +static int nbd_co_receive_one_chunk(NBDClientSession *s, uint64_t handle, + bool only_structured, + QEMUIOVector *qiov, NBDReply *reply, + void **payload) +{ + int request_ret; + int ret =3D nbd_co_do_receive_one_chunk(s, handle, only_structured, + &request_ret, qiov, payload); + + if (ret < 0) { + s->quit =3D true; + } else { + /* For assert at loop start in nbd_read_reply_entry */ + if (reply) { + *reply =3D s->reply; + } + s->reply.handle =3D 0; + ret =3D request_ret; + } =20 - /* Kick the read_reply_co to get the next reply. */ if (s->read_reply_co) { aio_co_wake(s->read_reply_co); } =20 + return ret; +} + +typedef struct NBDReplyChunkIter { + int ret; + bool done, only_structured; +} NBDReplyChunkIter; + +#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ + qiov, reply, payload) \ + for (iter =3D (NBDReplyChunkIter) { .only_structured =3D structured };= \ + nbd_reply_chunk_iter_receive(s, &iter, handle, qiov, reply, paylo= ad);) + +static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, + NBDReplyChunkIter *iter, + uint64_t handle, + QEMUIOVector *qiov, NBDReply *rep= ly, + void **payload) +{ + int ret; + NBDReply local_reply; + NBDStructuredReplyChunk *chunk; + if (s->quit) { + if (iter->ret =3D=3D 0) { + iter->ret =3D -EIO; + } + goto break_loop; + } + + if (iter->done) { + /* Previous iteration was last. */ + goto break_loop; + } + + if (reply =3D=3D NULL) { + reply =3D &local_reply; + } + + ret =3D nbd_co_receive_one_chunk(s, handle, iter->only_structured, + qiov, reply, payload); + if (ret < 0 && iter->ret =3D=3D 0) { + /* If it is a fatal error s->qiov is set by nbd_co_receive_one_chu= nk */ + iter->ret =3D ret; + } + + /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply= . */ + if (nbd_reply_is_simple(&s->reply) || s->quit) { + goto break_loop; + } + + chunk =3D &reply->structured; + iter->only_structured =3D true; + + if (chunk->type =3D=3D NBD_SREP_TYPE_NONE) { + if (!(chunk->flags & NBD_SREP_FLAG_DONE)) { + /* protocol error */ + s->quit =3D true; + if (iter->ret =3D=3D 0) { + iter->ret =3D -EIO; + } + } + goto break_loop; + } + + if (chunk->flags & NBD_SREP_FLAG_DONE) { + /* This iteration is last. */ + iter->done =3D true; + } + + /* Execute the loop body */ + return true; + +break_loop: + s->requests[HANDLE_TO_INDEX(s, handle)].coroutine =3D NULL; + qemu_co_mutex_lock(&s->send_mutex); s->in_flight--; qemu_co_queue_next(&s->free_sema); qemu_co_mutex_unlock(&s->send_mutex); =20 - return ret; + return false; +} + +static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle) +{ + NBDReplyChunkIter iter; + + NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, NULL, NULL) { + /* nbd_reply_chunk_iter_receive does all the work */ + ; + } + + return iter.ret; +} + +static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t hand= le, + QEMUIOVector *qiov) +{ + NBDReplyChunkIter iter; + NBDReply reply; + void *payload =3D NULL; + + NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, + qiov, &reply, &payload) + { + int ret; + + switch (reply.structured.type) { + case NBD_SREP_TYPE_OFFSET_DATA: + /* special cased in nbd_co_receive_one_chunk, data is already + * in qiov */ + break; + case NBD_SREP_TYPE_OFFSET_HOLE: + ret =3D nbd_parse_offset_hole_payload(&reply.structured, paylo= ad, + qiov); + if (ret < 0) { + s->quit =3D true; + } + break; + default: + /* not allowed reply type */ + s->quit =3D true; + } + + qemu_vfree(payload); + payload =3D NULL; + } + + return iter.ret; } =20 static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, - QEMUIOVector *qiov) + QEMUIOVector *write_qiov) { NBDClientSession *client =3D nbd_get_client_session(bs); int ret; =20 - if (qiov) { - assert(request->type =3D=3D NBD_CMD_WRITE || request->type =3D=3D = NBD_CMD_READ); - assert(request->len =3D=3D iov_size(qiov->iov, qiov->niov)); + assert(request->type !=3D NBD_CMD_READ); + if (write_qiov) { + assert(request->type =3D=3D NBD_CMD_WRITE); + assert(request->len =3D=3D iov_size(write_qiov->iov, write_qiov->n= iov)); } else { - assert(request->type !=3D NBD_CMD_WRITE && request->type !=3D NBD_= CMD_READ); + assert(request->type !=3D NBD_CMD_WRITE); } - ret =3D nbd_co_send_request(bs, request, - request->type =3D=3D NBD_CMD_WRITE ? qiov : = NULL); + ret =3D nbd_co_send_request(bs, request, write_qiov); if (ret < 0) { return ret; } =20 - return nbd_co_receive_reply(client, request->handle, - request->type =3D=3D NBD_CMD_READ ? qiov := NULL); + return nbd_co_receive_return_code(client, request->handle); } =20 int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { + int ret; + NBDClientSession *client =3D nbd_get_client_session(bs); NBDRequest request =3D { .type =3D NBD_CMD_READ, .from =3D offset, @@ -259,7 +591,12 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_= t offset, assert(bytes <=3D NBD_MAX_BUFFER_SIZE); assert(!flags); =20 - return nbd_co_request(bs, &request, qiov); + ret =3D nbd_co_send_request(bs, &request, NULL); + if (ret < 0) { + return ret; + } + + return nbd_co_receive_cmdread_reply(client, request.handle, qiov); } =20 int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, diff --git a/nbd/client.c b/nbd/client.c index a38e1a7d8e..2f256ee771 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -687,6 +687,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char = *name, if (fixedNewStyle) { int result; =20 + result =3D nbd_request_simple_option(ioc, NBD_OPT_STRUCTURED_R= EPLY, + errp); + if (result < 0) { + goto fail; + } + info->structured_reply =3D result =3D=3D 1; + /* Try NBD_OPT_GO first - if it works, we are done (it * also gives us a good message if the server requires * TLS). If it is not available, fall back to --=20 2.11.1