From nobody Tue Feb 10 15:01:13 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; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1520974371663965.1151723691007; Tue, 13 Mar 2018 13:52:51 -0700 (PDT) Received: from localhost ([::1]:42755 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1evqug-0003Wj-S7 for importer@patchew.org; Tue, 13 Mar 2018 16:52:50 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57822) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1evqtE-0002mu-SR for qemu-devel@nongnu.org; Tue, 13 Mar 2018 16:51:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1evqtD-0003je-4g for qemu-devel@nongnu.org; Tue, 13 Mar 2018 16:51:20 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47754 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1evqt7-0003iM-5T; Tue, 13 Mar 2018 16:51:13 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9F8218182D16; Tue, 13 Mar 2018 20:51:12 +0000 (UTC) Received: from red.redhat.com (ovpn-121-135.rdu2.redhat.com [10.10.121.135]) by smtp.corp.redhat.com (Postfix) with ESMTP id 03C3350333; Tue, 13 Mar 2018 20:51:11 +0000 (UTC) From: Eric Blake To: qemu-devel@nongnu.org Date: Tue, 13 Mar 2018 15:50:20 -0500 Message-Id: <20180313205023.711304-15-eblake@redhat.com> In-Reply-To: <20180313205023.711304-1-eblake@redhat.com> References: <20180313205023.711304-1-eblake@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Tue, 13 Mar 2018 20:51:12 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Tue, 13 Mar 2018 20:51:12 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'eblake@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PULL v2 14/17] nbd: BLOCK_STATUS for standard get_block_status function: client part 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: Kevin Wolf , Paolo Bonzini , Vladimir Sementsov-Ogievskiy , "open list:Network Block Dev..." , Max Reitz 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" From: Vladimir Sementsov-Ogievskiy Minimal realization: only one extent in server answer is supported. Flag NBD_CMD_FLAG_REQ_ONE is used to force this behavior. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20180312152126.286890-6-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake [eblake: grammar tweaks, fix min_block check and 32-bit cap, use -1 instead of errno on failure in nbd_negotiate_simple_meta_context, ensure that block status makes progress on success] Signed-off-by: Eric Blake --- block/nbd-client.h | 6 +++ include/block/nbd.h | 3 ++ block/nbd-client.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++= ++++ block/nbd.c | 3 ++ nbd/client.c | 117 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 279 insertions(+) diff --git a/block/nbd-client.h b/block/nbd-client.h index 612c4c21a0c..0ece76e5aff 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -61,4 +61,10 @@ void nbd_client_detach_aio_context(BlockDriverState *bs); void nbd_client_attach_aio_context(BlockDriverState *bs, AioContext *new_context); +int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file); + #endif /* NBD_CLIENT_H */ diff --git a/include/block/nbd.h b/include/block/nbd.h index 2285637e673..fcdcd545023 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -260,6 +260,7 @@ struct NBDExportInfo { /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ bool structured_reply; + bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STA= TUS */ /* Set by server results during nbd_receive_negotiate() */ uint64_t size; @@ -267,6 +268,8 @@ struct NBDExportInfo { uint32_t min_block; uint32_t opt_block; uint32_t max_block; + + uint32_t meta_base_allocation_id; }; typedef struct NBDExportInfo NBDExportInfo; diff --git a/block/nbd-client.c b/block/nbd-client.c index 0d9f73a137f..e64e346d690 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -228,6 +228,48 @@ static int nbd_parse_offset_hole_payload(NBDStructured= ReplyChunk *chunk, return 0; } +/* nbd_parse_blockstatus_payload + * support only one extent in reply and only for + * base:allocation context + */ +static int nbd_parse_blockstatus_payload(NBDClientSession *client, + NBDStructuredReplyChunk *chunk, + uint8_t *payload, uint64_t orig_l= ength, + NBDExtent *extent, Error **errp) +{ + uint32_t context_id; + + if (chunk->length !=3D sizeof(context_id) + sizeof(extent)) { + error_setg(errp, "Protocol error: invalid payload for " + "NBD_REPLY_TYPE_BLOCK_STATUS"); + return -EINVAL; + } + + context_id =3D payload_advance32(&payload); + if (client->info.meta_base_allocation_id !=3D context_id) { + error_setg(errp, "Protocol error: unexpected context id %d for " + "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated con= text " + "id is %d", context_id, + client->info.meta_base_allocation_id); + return -EINVAL; + } + + extent->length =3D payload_advance32(&payload); + extent->flags =3D payload_advance32(&payload); + + if (extent->length =3D=3D 0 || + (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, + client->info.min_block= )) || + extent->length > orig_length) + { + error_setg(errp, "Protocol error: server sent status chunk with " + "invalid length"); + return -EINVAL; + } + + return 0; +} + /* nbd_parse_error_payload * on success @errp contains message describing nbd error reply */ @@ -642,6 +684,68 @@ static int nbd_co_receive_cmdread_reply(NBDClientSessi= on *s, uint64_t handle, return iter.ret; } +static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + uint64_t handle, uint64_t leng= th, + NBDExtent *extent, Error **err= p) +{ + NBDReplyChunkIter iter; + NBDReply reply; + void *payload =3D NULL; + Error *local_err =3D NULL; + bool received =3D false; + + assert(!extent->length); + NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, + NULL, &reply, &payload) + { + int ret; + NBDStructuredReplyChunk *chunk =3D &reply.structured; + + assert(nbd_reply_is_structured(&reply)); + + switch (chunk->type) { + case NBD_REPLY_TYPE_BLOCK_STATUS: + if (received) { + s->quit =3D true; + error_setg(&local_err, "Several BLOCK_STATUS chunks in rep= ly"); + nbd_iter_error(&iter, true, -EINVAL, &local_err); + } + received =3D true; + + ret =3D nbd_parse_blockstatus_payload(s, &reply.structured, + payload, length, extent, + &local_err); + if (ret < 0) { + s->quit =3D true; + nbd_iter_error(&iter, true, ret, &local_err); + } + break; + default: + if (!nbd_reply_type_is_error(chunk->type)) { + s->quit =3D true; + error_setg(&local_err, + "Unexpected reply type: %d (%s) " + "for CMD_BLOCK_STATUS", + chunk->type, nbd_reply_type_lookup(chunk->type)= ); + nbd_iter_error(&iter, true, -EINVAL, &local_err); + } + } + + g_free(payload); + payload =3D NULL; + } + + if (!extent->length && !iter.err) { + error_setg(&iter.err, + "Server did not reply with any status extents"); + if (!iter.ret) { + iter.ret =3D -EIO; + } + } + error_propagate(errp, iter.err); + return iter.ret; +} + static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, QEMUIOVector *write_qiov) { @@ -784,6 +888,51 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64= _t offset, int bytes) return nbd_co_request(bs, &request, NULL); } +int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + int64_t ret; + NBDExtent extent =3D { 0 }; + NBDClientSession *client =3D nbd_get_client_session(bs); + Error *local_err =3D NULL; + + NBDRequest request =3D { + .type =3D NBD_CMD_BLOCK_STATUS, + .from =3D offset, + .len =3D MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, + bs->bl.request_alignment), + client->info.max_block), bytes), + .flags =3D NBD_CMD_FLAG_REQ_ONE, + }; + + if (!client->info.base_allocation) { + *pnum =3D bytes; + return BDRV_BLOCK_DATA; + } + + ret =3D nbd_co_send_request(bs, &request, NULL); + if (ret < 0) { + return ret; + } + + ret =3D nbd_co_receive_blockstatus_reply(client, request.handle, bytes, + &extent, &local_err); + if (local_err) { + error_report_err(local_err); + } + if (ret < 0) { + return ret; + } + + assert(extent.length); + *pnum =3D extent.length; + return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | + (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0); +} + void nbd_client_detach_aio_context(BlockDriverState *bs) { NBDClientSession *client =3D nbd_get_client_session(bs); @@ -828,6 +977,7 @@ int nbd_client_init(BlockDriverState *bs, client->info.request_sizes =3D true; client->info.structured_reply =3D true; + client->info.base_allocation =3D true; ret =3D nbd_receive_negotiate(QIO_CHANNEL(sioc), export, tlscreds, hostname, &client->ioc, &client->info, errp); diff --git a/block/nbd.c b/block/nbd.c index d4e4172c08c..1e2b3ba2d3b 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -585,6 +585,7 @@ static BlockDriver bdrv_nbd =3D { .bdrv_detach_aio_context =3D nbd_detach_aio_context, .bdrv_attach_aio_context =3D nbd_attach_aio_context, .bdrv_refresh_filename =3D nbd_refresh_filename, + .bdrv_co_block_status =3D nbd_client_co_block_status, }; static BlockDriver bdrv_nbd_tcp =3D { @@ -604,6 +605,7 @@ static BlockDriver bdrv_nbd_tcp =3D { .bdrv_detach_aio_context =3D nbd_detach_aio_context, .bdrv_attach_aio_context =3D nbd_attach_aio_context, .bdrv_refresh_filename =3D nbd_refresh_filename, + .bdrv_co_block_status =3D nbd_client_co_block_status, }; static BlockDriver bdrv_nbd_unix =3D { @@ -623,6 +625,7 @@ static BlockDriver bdrv_nbd_unix =3D { .bdrv_detach_aio_context =3D nbd_detach_aio_context, .bdrv_attach_aio_context =3D nbd_attach_aio_context, .bdrv_refresh_filename =3D nbd_refresh_filename, + .bdrv_co_block_status =3D nbd_client_co_block_status, }; static void bdrv_nbd_init(void) diff --git a/nbd/client.c b/nbd/client.c index dcad23a0531..9b9b7f0ea29 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -595,6 +595,111 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *i= oc, return QIO_CHANNEL(tioc); } +/* nbd_negotiate_simple_meta_context: + * Set one meta context. Simple means that reply must contain zero (not + * negotiated) or one (negotiated) contexts. More contexts would be consid= ered + * as a protocol error. It's also implied that meta-data query equals quer= ied + * context name, so, if server replies with something different then @cont= ext, + * it considered as error too. + * return 1 for successful negotiation, context_id is set + * 0 if operation is unsupported, + * -1 with errp set for any other error + */ +static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + const char *export, + const char *context, + uint32_t *context_id, + Error **errp) +{ + int ret; + NBDOptionReply reply; + uint32_t received_id; + bool received; + uint32_t export_len =3D strlen(export); + uint32_t context_len =3D strlen(context); + uint32_t data_len =3D sizeof(export_len) + export_len + + sizeof(uint32_t) + /* number of queries */ + sizeof(context_len) + context_len; + char *data =3D g_malloc(data_len); + char *p =3D data; + + stl_be_p(p, export_len); + memcpy(p +=3D sizeof(export_len), export, export_len); + stl_be_p(p +=3D export_len, 1); + stl_be_p(p +=3D sizeof(uint32_t), context_len); + memcpy(p +=3D sizeof(context_len), context, context_len); + + ret =3D nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_le= n, data, + errp); + g_free(data); + if (ret < 0) { + return ret; + } + + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, + errp) < 0) + { + return -1; + } + + ret =3D nbd_handle_reply_err(ioc, &reply, errp); + if (ret <=3D 0) { + return ret; + } + + if (reply.type =3D=3D NBD_REP_META_CONTEXT) { + char *name; + size_t len; + + if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { + return -1; + } + be32_to_cpus(&received_id); + + len =3D reply.length - sizeof(received_id); + name =3D g_malloc(len + 1); + if (nbd_read(ioc, name, len, errp) < 0) { + g_free(name); + return -1; + } + name[len] =3D '\0'; + if (strcmp(context, name)) { + error_setg(errp, "Failed to negotiate meta context '%s', serve= r " + "answered with different context '%s'", context, + name); + g_free(name); + return -1; + } + g_free(name); + + received =3D true; + + /* receive NBD_REP_ACK */ + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, + errp) < 0) + { + return -1; + } + + ret =3D nbd_handle_reply_err(ioc, &reply, errp); + if (ret <=3D 0) { + return ret; + } + } + + if (reply.type !=3D NBD_REP_ACK) { + error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", + reply.type, NBD_REP_ACK); + return -1; + } + + if (received) { + *context_id =3D received_id; + return 1; + } + + return 0; +} int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, @@ -606,10 +711,12 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char= *name, int rc; bool zeroes =3D true; bool structured_reply =3D info->structured_reply; + bool base_allocation =3D info->base_allocation; trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : ""); info->structured_reply =3D false; + info->base_allocation =3D false; rc =3D -EINVAL; if (outioc) { @@ -700,6 +807,16 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char = *name, info->structured_reply =3D result =3D=3D 1; } + if (info->structured_reply && base_allocation) { + result =3D nbd_negotiate_simple_meta_context( + ioc, name, "base:allocation", + &info->meta_base_allocation_id, errp); + if (result < 0) { + goto fail; + } + info->base_allocation =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.14.3