From patchwork Thu Feb 15 13:51:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Sementsov-Ogievskiy X-Patchwork-Id: 873829 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="et3pce4g"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zhyRG2TlXz9sQm for ; Fri, 16 Feb 2018 00:53:30 +1100 (AEDT) Received: from localhost ([::1]:50878 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1emJya-00025q-DN for incoming@patchwork.ozlabs.org; Thu, 15 Feb 2018 08:53:28 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51121) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1emJxU-0001pp-BD for qemu-devel@nongnu.org; Thu, 15 Feb 2018 08:52:22 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1emJxP-0000yO-Jj for qemu-devel@nongnu.org; Thu, 15 Feb 2018 08:52:20 -0500 Received: from mail-db5eur01on0107.outbound.protection.outlook.com ([104.47.2.107]:9504 helo=EUR01-DB5-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1emJxP-0000xE-5u; Thu, 15 Feb 2018 08:52:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=OkDlkLT/xrsg0DcUSBCB9k8I4Lhv2K7gcSnLgE0KcOo=; b=et3pce4gtND1dTLttfHaxLrhKxtN282QUHKUlMyyQmBCh7cF/QKXXEfmblJ2NL26uXkvBShqMtyopmPtogNiS6WCTWh1/9ho/y0/FbIxQbg1zaxMVaf6dlXb7y3RDiRumaCb0f9T5rXCouwnU+otDp/FGowg9EnOdYhuuaSLdI0= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=vsementsov@virtuozzo.com; Received: from vova-pc.sw.ru (195.214.232.6) by AM5PR0801MB2052.eurprd08.prod.outlook.com (2603:10a6:203:4c::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.506.18; Thu, 15 Feb 2018 13:52:12 +0000 From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Thu, 15 Feb 2018 16:51:41 +0300 Message-Id: <1518702707-7077-4-git-send-email-vsementsov@virtuozzo.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1518702707-7077-1-git-send-email-vsementsov@virtuozzo.com> References: <1518702707-7077-1-git-send-email-vsementsov@virtuozzo.com> MIME-Version: 1.0 X-Originating-IP: [195.214.232.6] X-ClientProxiedBy: HE1PR07CA0005.eurprd07.prod.outlook.com (2603:10a6:7:67::15) To AM5PR0801MB2052.eurprd08.prod.outlook.com (2603:10a6:203:4c::14) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: d7efc9db-244b-49ba-1ece-08d5747b50e8 X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652020)(4534165)(7168020)(4627221)(201703031133081)(201702281549075)(5600026)(4604075)(2017052603307)(7153060)(7193020); SRVR:AM5PR0801MB2052; X-Microsoft-Exchange-Diagnostics: 1; AM5PR0801MB2052; 3:4a3/pOJLi31TOlUyhbQBzEiVRkELuzMQMd8pbiDpWbjgh0gN/61vJ5MTkXbNBIlc+m73geSvnkyrQjqW/bo3gWdRd5/VviNwLb79jKNb8DCUnPm18usz67g5MD7OwUDg0qAe89cwTa0Is2tlixC91bzz69eOFpgvBbrmru5yFuAabMFTRAjDsPugKzAc425+AfydwvhGRWR2kuYprzaYsayv3GtBUd4Nl/T1WfVdn8IvPUn/EqsN9/epe+8ZOES/; 25:YE1T3S2IK64HAm+5AuQspfQjOCErq6nRml8ZEMEBYgZqf4Zv2QQSyiTHmhYlgeFGEvWIYUOu3/STtVQGvlp5DwhKonXjAVfb/IF3HoLipNNCxNbLKZgQWK8NrghCcirCOzfGdxs4eD2ixHAOrU/DTofYC0BUouC/6KhoPqDsUKkRvPq21e8YWBJdBs8+6GPueCCvBh6LDuxGkLM5P3ykRVllRDvVVXKZxCQ5iNhv/ZWZCHHdBHtW3xWPN8UeJ07CK1gMkqfZQJOS8dcbVugVmMYa/E2N9rqUE1nut3WyJIaj4/YFGlwmg0pdxykxdfwzOHrTkG8i8oaLVqSBuaE9kA==; 31:YKBqxDuZIuX0ief95ddgPFOQptKh6y3Y+zJVxvC/U+t+l8hpoibpDzJdwJWyGZh+9Q75BsrENQVNidjTSRU4/+xX8tZz+oDe5+F17SSFA/ApmE1uiTtA9TTUdxRKDy0smOXx7kjFVORs7Bk03W4tjwapArff9revlKqpFmV/HcI7kIgJCzECy2pVf3PfAoEw226A+Ec5YfTk2cqW6XzdZthVxW4WY8JvknfnQ2XTBG8= X-MS-TrafficTypeDiagnostic: AM5PR0801MB2052: X-Microsoft-Exchange-Diagnostics: 1; AM5PR0801MB2052; 20:rKAZefNahpC05yk1MUYJcgnPgAhnKzynn3qqKdnyVwO32WNEjzoyP2IdvYVFs3WxKElmFVB2k/pxIsWwbCyt0tdFhlKWPoFXmexr7a86l77llSRKGOm12DS6Hrd1hLYXacLfYjoqX4bP2dbPCsiUr0XqQi7L3HfXAKmZ08aSXRQDx/bHBvFcy24wvNCdxhKMl/vPqbKtqgqWAU0as1qjr9nMpMUZffBm5ufBSaltpVTOLyiNNvBgY31Njv4kVTY4kdwJqSiDqhWWtpwpj8V6SufxRadPDnCv0xG9++tlmY6VXWzmONpMCilVaXPP1xnv61T3e9xTkKw190AoWpCiVyv4JarV8hub44DKiDFzaE40eHkodwLyIX/JcrVNXOvcDG2W9B/nDMvQnGlkaeUPBhNT8RJ8dXPjNnXbw0zwW6bfyr+TgWBHWrVIkouMbczWTudg3BdBbs3trgfqdKo/oWNIqjqNhXtMjydjThicb4fS+dtr2c03UoTGv7FqYtGm; 4:bGhLRJCjrae1oYbDB8VjOVf9W/pDEVukf8fWnPXArY5vDH48On9mK3XILp71KcIR8LFHGhzE80Qnp0/eWHSEt5OqF8yl/9wiH7bGW05qFAo1vbjGBV69NEEVLXKJW3KFYAEJ/d/Q+BGiGXGoRTYih2G72L9fJbxbf6jAj6p7yvbkaPk5VthWTfyA6rKnOCkUAl1Z0UEYS8MbIiL4ec/LG3E4tHMwlStXWNNAsn0p2uUwccFY0MWiIaK5V1j417gNGy4dHmt4qN2CtR9N2KtnmOlkWzt4ZCOtYJUztbbpgFfzPP4vWa7sOTVNYY3QFvUG X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(158342451672863); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040501)(2401047)(8121501046)(5005006)(10201501046)(3002001)(3231101)(944501161)(93006095)(93001095)(6041288)(20161123564045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123558120)(20161123562045)(20161123560045)(6072148)(201708071742011); SRVR:AM5PR0801MB2052; BCL:0; PCL:0; RULEID:; SRVR:AM5PR0801MB2052; X-Forefront-PRVS: 058441C12A X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(396003)(39380400002)(376002)(346002)(39840400004)(366004)(189003)(199004)(105586002)(52116002)(305945005)(8676002)(81166006)(26005)(6512007)(51416003)(76176011)(5660300001)(81156014)(53936002)(25786009)(7736002)(97736004)(55236004)(16586007)(69596002)(6506007)(36756003)(47776003)(86362001)(66066001)(316002)(386003)(59450400001)(6486002)(106356001)(68736007)(50226002)(4326008)(53416004)(3846002)(2906002)(8936002)(478600001)(6116002)(16526019)(186003)(107886003)(48376002)(2950100002)(6666003)(50466002)(16060500001)(309714004); DIR:OUT; SFP:1102; SCL:1; SRVR:AM5PR0801MB2052; H:vova-pc.sw.ru; FPR:; SPF:None; PTR:InfoNoRecords; MX:1; A:1; LANG:en; Received-SPF: None (protection.outlook.com: virtuozzo.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; AM5PR0801MB2052; 23:tDBAGqw6RbIR84oX5PPpb1qj7Xk1la0jRnPslpX?= lcfMZXDK5YThjLJ6mpQjB1MYxd3GkafeCdHF4pVQ7j0cC/JyXGzCwg2r6MNFsU7rpFkkWrtZEtAFMU2gQPftELx5c95AEyT6rxjoGJ6UIouxjqrZJSw2Z1MBd7BpQQ6cgkM5c38qhuKimK7oDrggsv62zLTsDL2+RvmAC0pltoSiYPAv51Qf9c9Th8U7oOEX8Uj3tKk16DpuM/AOqMP3ft4IoyxzkFEl0G75LvJmLixt3l2qWVANeKjv8BDyaMix/5NuzTzpyBUP368qluQLWTy2qNmgFr1FV/g8eADhRHc9Wp86iweP8bXo+JAq/q3pbp2xropz1vn7PmqtnoxQtrWet2oH/08ii2Et0AsziKmhX6xvD1aWAwc0DmP+TNFmph6KlSY3i9qX5fJTVBhdKht0QI1MyJ2HN/7eCuHcSZkn84lEfZHLi89lylRueQFPhJzO4ewEPslQlYMofr9Vhm6X5qvR3OkjxbpeayZsvmBpBW3kDlog2ML7xoU0UNbooo8EFZFYRVlQcWdeNWRdBDr8Q78615miJCNzYP1DdaaK3jsHMl5SqIPZMk2gMNbDJwMr40/vNrgD+YsA+E6a6zwhqPNv7obqqeKTD4W/yvbkcB7jlQsH6aksYD9B6EcfaBGVJCGu8JB84VRO9Xs3vh0mZEjp7+QWGrPb+tSyzTwPRFCIc8XOKDITNCNIMuegOs/xQFtB27Bdh/Gj5BYp0jhuTBCSJ541QHbxrAVS8UYrwGSePuxcEOOeMirruRaQ5pSDalHg3Xp7KOTzumVacpnOgJLhLoljYamCuNvpUIr8u8aqA8u8YXGEkbDcdRi1wjGYJzjM0XYgWb4NFhfcNSqG8fqBBMWaZCsC+VNZc2LvKfkP9nVCdTCqhHKEA/qcNOtvrrW2uRrwx2KwFKxY8V2hZ8f4ah0B+Tiuz+mFAdK18NY4StRaTmSxCKwMXn3eCBh09IwnBlJqxOrkuNJ1z36B25C0/HSu0XuV5VsW9XRYotxBUxNfw4Mh7O/W1vMgSKjMNpFe3XPalOD96UVjm8Cl2WHVt9Olq0KiBIU4cGgmhHqU0tbgG3BXoQzkVQbboKDevonmFba5KOfezg+QAxe7txP1ujXwH9J1PC6BZNuV/FoIL7E5DjuChdQUjJpaSGjRC8PbqemOmSKlkKHoSD3T8m97PVvmKeP77/iOTf7MrJgWUpnmqmddvaXYfFcmSV9Y= X-Microsoft-Exchange-Diagnostics: 1; AM5PR0801MB2052; 6:eJO+8T75xzCl2UkwQP6YNQVXNIV9yWwpFFWfqjjFU8Qqnv9sYtos+XgGlyBgUj94TFt57t/pILPJt0Tgc6bFOTGDMMg1QC0hjfBqTpvIYf3zvr8R2KGXzEI1IbA/+Fk0j7ZwWVv+Uy2ke7J2MdwU18Y1ARMZNsKqTpbm/TD9dLyjKv9NW6IhrG1oLWQ1tsxU/70f8Jtj0Vvx2lY9GrC/cduapo9p1r6Gd9E8fPHl5pTCU7TzV31JB7lpdjk8EzjsVhPwVy/b99PgSelNrfqlFmhrlQjKLrSFdq+HwTKfEEU19vB+o146O868gNunWfbWQNieTMI9lxhW5ur/ps/mtRWduRR8OxlY1z+oDn92HLo=; 5:gMr7SYZey5KwoauWQvAKCmMxrtJnWEmgy8ImqXxYhRsRAOHypXWkjDZcA/jKGbcH4J7drk7Te+qq/C9DKrNR27m4wy0xox+Nm4XEnPLxcbnfZ6Yy0X291NYkz7sUhZcljBC8Lfe2eePSIsfEtj8tPUYkKQ6IXLYwKT3KyyXLI4o=; 24:X3kN8CmoBh3aQkI5ayVm933zwPpfaZPu21lKivvyXDqZyTU7Wnm0mQPi45y+4R7p5BpAhqEiE4HENlxz01/Zym3QAOm+Q67O1c4Sy0W1oNU=; 7:rRTJf2gkj54b91syK+oK90Zt7tuym9Q6CVE8ee7tHS2C39IqiPmUyuKCUPEpDyPmTootVdJskHbXexXTY8nCaJIegNHhSJcLwKExspMGprHR2tMeE0VfQ3p5Czaa3xLWbqxbXOQY5ZQ/h2Bs13yg6wPEEeaY5Mz8XFHWBsSCLXh1igFZ0nzgfRAohvP3acGZ41VPFzspzBCD0USTl51UlR36Xg0rZ2/RN93S7+LxBzUN2NLP6FFwPhN3nIs/GEas SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; AM5PR0801MB2052; 20:E25bw/Qijkvc2+Y+qN0cpTR2P+wdEVvdrkXBP7qVUngsLfbefw/SYHqG8kQVy2cXf8QLljMniiLEMfivGVArD0ESI9TKNU+7T8TpMcqL6UNOXjdgEhzKHpYGF+8EgKTuF8cnphKogAp9Pqxgl+vK1ESyht2iek+SjRXmXjs0lOo= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Feb 2018 13:52:12.4200 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: d7efc9db-244b-49ba-1ece-08d5747b50e8 X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM5PR0801MB2052 X-detected-operating-system: by eggs.gnu.org: Windows 7 or 8 [fuzzy] X-Received-From: 104.47.2.107 Subject: [Qemu-devel] [PATCH 3/9] nbd: BLOCK_STATUS for standard get_block_status function: server 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: kwolf@redhat.com, vsementsov@virtuozzo.com, mreitz@redhat.com, den@openvz.org, pbonzini@redhat.com Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Minimal realization: only one extent in server answer is supported. Signed-off-by: Vladimir Sementsov-Ogievskiy --- include/block/nbd.h | 33 ++++++ nbd/common.c | 10 ++ nbd/server.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 352 insertions(+), 1 deletion(-) diff --git a/include/block/nbd.h b/include/block/nbd.h index ef1698914b..b16215d17a 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -41,6 +41,12 @@ struct NBDOptionReply { } QEMU_PACKED; typedef struct NBDOptionReply NBDOptionReply; +typedef struct NBDOptionReplyMetaContext { + NBDOptionReply h; /* h.type = NBD_REP_META_CONTEXT, h.length > 4 */ + uint32_t context_id; + /* meta context name follows */ +} QEMU_PACKED NBDOptionReplyMetaContext; + /* Transmission phase structs * * Note: these are _NOT_ the same as the network representation of an NBD @@ -105,6 +111,19 @@ typedef struct NBDStructuredError { uint16_t message_length; } QEMU_PACKED NBDStructuredError; +/* Header of NBD_REPLY_TYPE_BLOCK_STATUS */ +typedef struct NBDStructuredMeta { + NBDStructuredReplyChunk h; /* h.length >= 12 (at least one extent) */ + uint32_t context_id; + /* extents follows */ +} QEMU_PACKED NBDStructuredMeta; + +/* Extent chunk for NBD_REPLY_TYPE_BLOCK_STATUS */ +typedef struct NBDExtent { + uint32_t length; + uint32_t flags; /* NBD_STATE_* */ +} QEMU_PACKED NBDExtent; + /* Transmission (export) flags: sent from server to client during handshake, but describe what will happen during transmission */ #define NBD_FLAG_HAS_FLAGS (1 << 0) /* Flags are there */ @@ -136,6 +155,8 @@ typedef struct NBDStructuredError { #define NBD_OPT_INFO (6) #define NBD_OPT_GO (7) #define NBD_OPT_STRUCTURED_REPLY (8) +#define NBD_OPT_LIST_META_CONTEXT (9) +#define NBD_OPT_SET_META_CONTEXT (10) /* Option reply types. */ #define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) @@ -143,6 +164,7 @@ typedef struct NBDStructuredError { #define NBD_REP_ACK (1) /* Data sending finished. */ #define NBD_REP_SERVER (2) /* Export description. */ #define NBD_REP_INFO (3) /* NBD_OPT_INFO/GO. */ +#define NBD_REP_META_CONTEXT (4) /* NBD_OPT_{LIST,SET}_META_CONTEXT */ #define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ #define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ @@ -163,6 +185,10 @@ typedef struct NBDStructuredError { #define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ #define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ #define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ +#define NBD_CMD_FLAG_REQ_ONE (1 << 3) /* only one extent in BLOCK_STATUS + * reply chunk */ + +#define NBD_META_ID_BASE_ALLOCATION 0 /* Supported request types */ enum { @@ -173,6 +199,7 @@ enum { NBD_CMD_TRIM = 4, /* 5 reserved for failed experiment NBD_CMD_CACHE */ NBD_CMD_WRITE_ZEROES = 6, + NBD_CMD_BLOCK_STATUS = 7, }; #define NBD_DEFAULT_PORT 10809 @@ -200,9 +227,15 @@ enum { #define NBD_REPLY_TYPE_NONE 0 #define NBD_REPLY_TYPE_OFFSET_DATA 1 #define NBD_REPLY_TYPE_OFFSET_HOLE 2 +#define NBD_REPLY_TYPE_BLOCK_STATUS 5 #define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) #define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) +/* Flags for extents (NBDExtent.flags) of NBD_REPLY_TYPE_BLOCK_STATUS, + * for base:allocation meta context */ +#define NBD_STATE_HOLE (1 << 0) +#define NBD_STATE_ZERO (1 << 1) + static inline bool nbd_reply_type_is_error(int type) { return type & (1 << 15); diff --git a/nbd/common.c b/nbd/common.c index 6295526dd1..8c95c1d606 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -75,6 +75,10 @@ const char *nbd_opt_lookup(uint32_t opt) return "go"; case NBD_OPT_STRUCTURED_REPLY: return "structured reply"; + case NBD_OPT_LIST_META_CONTEXT: + return "list meta context"; + case NBD_OPT_SET_META_CONTEXT: + return "set meta context"; default: return ""; } @@ -90,6 +94,8 @@ const char *nbd_rep_lookup(uint32_t rep) return "server"; case NBD_REP_INFO: return "info"; + case NBD_REP_META_CONTEXT: + return "meta context"; case NBD_REP_ERR_UNSUP: return "unsupported"; case NBD_REP_ERR_POLICY: @@ -144,6 +150,8 @@ const char *nbd_cmd_lookup(uint16_t cmd) return "trim"; case NBD_CMD_WRITE_ZEROES: return "write zeroes"; + case NBD_CMD_BLOCK_STATUS: + return "block status"; default: return ""; } @@ -159,6 +167,8 @@ const char *nbd_reply_type_lookup(uint16_t type) return "data"; case NBD_REPLY_TYPE_OFFSET_HOLE: return "hole"; + case NBD_REPLY_TYPE_BLOCK_STATUS: + return "block status"; case NBD_REPLY_TYPE_ERROR: return "generic error"; case NBD_REPLY_TYPE_ERROR_OFFSET: diff --git a/nbd/server.c b/nbd/server.c index b9860a6dcf..f2a750eb97 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -82,6 +82,15 @@ struct NBDExport { static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); +/* NBDExportMetaContexts represents list of selected by + * NBD_OPT_SET_META_CONTEXT contexts to be exported. */ +typedef struct NBDExportMetaContexts { + char export_name[NBD_MAX_NAME_SIZE + 1]; + bool valid; /* means that negotiation of the option finished without + errors */ + bool base_allocation; /* export base:allocation context (block status) */ +} NBDExportMetaContexts; + struct NBDClient { int refcount; void (*close_fn)(NBDClient *client, bool negotiated); @@ -102,6 +111,7 @@ struct NBDClient { bool closing; bool structured_reply; + NBDExportMetaContexts export_meta; uint32_t opt; /* Current option being negotiated */ uint32_t optlen; /* remaining length of data in ioc for the option being @@ -636,6 +646,201 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, return QIO_CHANNEL(tioc); } +/* nbd_alloc_read_size_string + * + * Read string in format + * uint32_t len + * len bytes string (not 0-terminated) + * String is allocated and pointer returned as @buf + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_alloc_read_size_string(NBDClient *client, char **buf, + Error **errp) +{ + int ret; + uint32_t len; + + ret = nbd_opt_read(client, &len, sizeof(len), errp); + if (ret <= 0) { + return ret; + } + cpu_to_be32s(&len); + + *buf = g_try_malloc(len + 1); + if (*buf == NULL) { + error_setg(errp, "No memory"); + return -ENOMEM; + } + (*buf)[len] = '\0'; + + ret = nbd_opt_read(client, *buf, len, errp); + if (ret <= 0) { + g_free(*buf); + *buf = NULL; + } + + return ret; +} + +/* nbd_read_size_string + * + * Read string in format + * uint32_t len + * len bytes string (not 0-terminated) + * + * @buf should be enough to store @max_len+1 + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_read_size_string(NBDClient *client, char *buf, + uint32_t max_len, Error **errp) +{ + int ret; + uint32_t len; + + ret = nbd_opt_read(client, &len, sizeof(len), errp); + if (ret <= 0) { + return ret; + } + cpu_to_be32s(&len); + + if (len > max_len) { + return nbd_opt_invalid(client, errp, "Invalid string length: %u", len); + } + + ret = nbd_opt_read(client, buf, len, errp); + if (ret <= 0) { + return ret; + } + buf[len] = '\0'; + + return 1; +} + +static int nbd_negotiate_send_meta_context(NBDClient *client, + const char *context, + uint32_t context_id, + Error **errp) +{ + NBDOptionReplyMetaContext opt; + struct iovec iov[] = { + {.iov_base = &opt, .iov_len = sizeof(opt)}, + {.iov_base = (void *)context, .iov_len = strlen(context)} + }; + + set_be_option_rep(&opt.h, client->opt, NBD_REP_META_CONTEXT, + sizeof(opt) - sizeof(opt.h) + iov[1].iov_len); + stl_be_p(&opt.context_id, context_id); + + return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0; +} + +static void nbd_meta_base_query(NBDExportMetaContexts *meta, const char *query) +{ + if (query[0] == '\0' || strcmp(query, "allocation") == 0) { + /* Note: empty query should select all contexts within base + * namespace. */ + meta->base_allocation = true; + } +} + +/* nbd_negotiate_meta_query + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_negotiate_meta_query(NBDClient *client, + NBDExportMetaContexts *meta, Error **errp) +{ + int ret; + char *query, *colon, *namespace, *subquery; + + ret = nbd_alloc_read_size_string(client, &query, errp); + if (ret <= 0) { + return ret; + } + + colon = strchr(query, ':'); + if (colon == NULL) { + ret = nbd_opt_invalid(client, errp, "no colon in query"); + goto out; + } + *colon = '\0'; + namespace = query; + subquery = colon + 1; + + if (strcmp(namespace, "base") == 0) { + nbd_meta_base_query(meta, subquery); + } + +out: + g_free(query); + return ret; +} + +/* nbd_negotiate_meta_queries + * Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_negotiate_meta_queries(NBDClient *client, + NBDExportMetaContexts *meta, Error **errp) +{ + int ret; + NBDExport *exp; + NBDExportMetaContexts local_meta; + uint32_t nb_queries; + int i; + + assert(client->structured_reply); + + if (meta == NULL) { + meta = &local_meta; + } + + memset(meta, 0, sizeof(*meta)); + + ret = nbd_read_size_string(client, meta->export_name, + NBD_MAX_NAME_SIZE, errp); + if (ret <= 0) { + return ret; + } + + exp = nbd_export_find(meta->export_name); + if (exp == NULL) { + return nbd_opt_invalid(client, errp, + "export '%s' not present", meta->export_name); + } + + ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp); + if (ret <= 0) { + return ret; + } + cpu_to_be32s(&nb_queries); + + for (i = 0; i < nb_queries; ++i) { + ret = nbd_negotiate_meta_query(client, meta, errp); + if (ret <= 0) { + return ret; + } + } + + if (meta->base_allocation) { + ret = nbd_negotiate_send_meta_context(client, "base:allocation", + NBD_META_ID_BASE_ALLOCATION, + errp); + if (ret < 0) { + return ret; + } + } + + ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); + if (ret == 0) { + meta->valid = true; + } + + return ret; +} + /* nbd_negotiate_options * Process all NBD_OPT_* client option commands, during fixed newstyle * negotiation. @@ -826,6 +1031,22 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, } break; + case NBD_OPT_LIST_META_CONTEXT: + case NBD_OPT_SET_META_CONTEXT: + if (!client->structured_reply) { + ret = nbd_opt_invalid( + client, errp, + "request option '%s' when structured reply " + "is not negotiated", nbd_opt_lookup(option)); + } else if (option == NBD_OPT_LIST_META_CONTEXT) { + ret = nbd_negotiate_meta_queries(client, NULL, errp); + } else { + ret = nbd_negotiate_meta_queries(client, + &client->export_meta, + errp); + } + break; + default: ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp, "Unsupported option 0x%" PRIx32 " (%s)", @@ -1446,6 +1667,78 @@ static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp); } +static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, NBDExtent *extent) +{ + uint64_t tail_bytes = bytes; + + while (tail_bytes) { + uint32_t flags; + int64_t num; + int ret = bdrv_block_status_above(bs, NULL, offset, tail_bytes, &num, + NULL, NULL); + if (ret < 0) { + return ret; + } + + flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | + (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); + + if (tail_bytes == bytes) { + extent->flags = flags; + } + + if (flags != extent->flags) { + break; + } + + offset += num; + tail_bytes -= num; + } + + cpu_to_be32s(&extent->flags); + extent->length = cpu_to_be32(bytes - tail_bytes); + + return 0; +} + +/* nbd_co_send_extents + * @extents should be in big-endian */ +static int nbd_co_send_extents(NBDClient *client, uint64_t handle, + NBDExtent *extents, unsigned nb_extents, + uint32_t context_id, Error **errp) +{ + NBDStructuredMeta chunk; + + struct iovec iov[] = { + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} + }; + + set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS, + handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); + stl_be_p(&chunk.context_id, context_id); + + return nbd_co_send_iov(client, iov, 2, errp); +} + +static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + BlockDriverState *bs, uint64_t offset, + uint64_t length, uint32_t context_id, + Error **errp) +{ + int ret; + NBDExtent extent; + + ret = blockstatus_to_extent_be(bs, offset, length, &extent); + if (ret < 0) { + return nbd_co_send_structured_error( + client, handle, -ret, "can't get block status", errp); + } + + return nbd_co_send_extents(client, handle, &extent, 1, context_id, 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 @@ -1523,6 +1816,8 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, valid_flags |= NBD_CMD_FLAG_DF; } else if (request->type == NBD_CMD_WRITE_ZEROES) { valid_flags |= NBD_CMD_FLAG_NO_HOLE; + } else if (request->type == NBD_CMD_BLOCK_STATUS) { + valid_flags |= NBD_CMD_FLAG_REQ_ONE; } if (request->flags & ~valid_flags) { error_setg(errp, "unsupported flags for command %s (got 0x%x)", @@ -1650,6 +1945,19 @@ static coroutine_fn void nbd_trip(void *opaque) } break; + case NBD_CMD_BLOCK_STATUS: + if (client->export_meta.base_allocation) { + ret = nbd_co_send_block_status(req->client, request.handle, + blk_bs(exp->blk), request.from, + request.len, + NBD_META_ID_BASE_ALLOCATION, + &local_err); + } else { + ret = -EINVAL; + error_setg(&local_err, "CMD_BLOCK_STATUS not negotiated"); + } + + break; default: error_setg(&local_err, "invalid request type (%" PRIu32 ") received", request.type); @@ -1680,7 +1988,7 @@ reply: ret = nbd_co_send_structured_done(req->client, request.handle, &local_err); } - } else { + } else if (request.type != NBD_CMD_BLOCK_STATUS) { ret = nbd_co_send_simple_reply(req->client, request.handle, ret < 0 ? -ret : 0, req->data, reply_data_len, &local_err);