From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338951; cv=none; d=zohomail.com; s=zohoarc; b=kZ1898oEy2TxkHWc3eTD7OqD3pANhbyu1iZhp4rXK6aHYJvrs0ZhoNpThb1aB1rFnWshWBjUKnavIMNb70Yn3UmDIsU6cgvkOir5jGSn15F+hYWpf5GgYQl3PoyMOQEhYKwQDLXn4lqseAxL3u7NXiib3CxYq/LlDgsQ98JcaWQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338951; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=7UOc3lB593G6YM8JLokUpJXT/bLzE/U+Gwzl1Qe/LpY=; b=fp296MZzlC7NKo1LyhWnNVSPbbWB4xdTw18TLazqFS6W5oUqqROHT2nnTKPeSk7drHCsm2IQMdTHA644M4c3eYfv6fa2ziBXVVItQ/xSzrFXmRIvNrgeXePuJEsc2hmWcgWZ35+uwm4NJtjzALcDkVU6WMxDToKOS+yEXhagoOM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338951002795.0176809896079; Wed, 2 Sep 2026 01:49:11 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdf-0005o1-Tw; Wed, 02 Sep 2026 04:48:11 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdd-0005lP-ID; Wed, 02 Sep 2026 04:48:09 -0400 Received: from forwardcorp1b.mail.yandex.net ([2a02:6b8:c02:900:1:45:d181:df01]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdb-0007AK-Ah; Wed, 02 Sep 2026 04:48:09 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id 108D880CAB; Wed, 02 Sep 2026 11:48:01 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-cVpGw13b; Wed, 02 Sep 2026 11:48:00 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338880; bh=7UOc3lB593G6YM8JLokUpJXT/bLzE/U+Gwzl1Qe/LpY=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=BKjpiCdDVmyWEN91fVHBJYGJ0rRGoFVIvWN5KtVzFzQ2F97oC5D8uZCzXCztBdRdW OIc2o4hR9oLovcDZH+TdBFNBA9K4mY0He+qReJaTQaRtnd6AbbZk6+OQmhSHbm7b5C TGPT31fWIYE4BZ72SRJdcSI8BZuSqxlrCLlFmoUU= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 1/8] block/nbd: clear reply.cookie when the reply is rejected Date: Wed, 2 Sep 2026 11:47:47 +0300 Message-ID: <20260902084754.140103-2-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a02:6b8:c02:900:1:45:d181:df01; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338955060158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" nbd_receive_replies() reads a reply header into s->reply and, when the header turns out to be unusable, reports a channel error and returns without touching it. The cookie stays there until the request which owns the reply clears it, and until then the waiters are explicitly allowed to look at a cookie which is not theirs: if (s->reply.cookie !=3D 0) { ind2 =3D COOKIE_TO_INDEX(s->reply.cookie); assert(!s->requests[ind2].receiving); Two of the error paths leave a value chosen by the server behind: one returns before the cookie is validated at all, the other returns because that validation has failed. A waiter which picks such a cookie up turns it into an index which is not in requests[] and accesses the array out of bounds, at an offset the server controls. The reply is of no use to anybody at this point, so clear the cookie before the mutex is released and keep the invariant that a non-zero s->reply.cookie is always an index of a live request. Observing the stale cookie takes a second thread, which a multiqueue configuration provides. Within one AioContext there is no yield point between the failed read and the clearing done by the owner in nbd_co_receive_one_chunk(), so nothing else of this node runs in between. The parked waiters cannot see it either, as they are woken only after the cookie has been cleared. What can get in is a request entering nbd_receive_replies() afresh, one just sent or one back for its next reply chunk, because that path takes the mutex without looking at the state. Cc: Eric Blake Cc: Vladimir Sementsov-Ogievskiy Signed-off-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260820115228.587427-2-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 5d231d5c4e4..d9b776283f4 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -466,20 +466,19 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDSt= ate *s, uint64_t cookie, error_setg(errp, "server dropped connection"); } if (ret < 0) { - nbd_channel_error(s, ret); - return ret; + goto err; } if (nbd_reply_is_structured(&s->reply) && s->info.mode < NBD_MODE_STRUCTURED) { - nbd_channel_error(s, -EINVAL); + ret =3D -EINVAL; error_setg(errp, "unexpected structured reply"); - return -EINVAL; + goto err; } ind2 =3D COOKIE_TO_INDEX(s->reply.cookie); if (ind2 >=3D MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { - nbd_channel_error(s, -EINVAL); + ret =3D -EINVAL; error_setg(errp, "unexpected cookie value"); - return -EINVAL; + goto err; } if (s->reply.cookie =3D=3D cookie) { /* We are done */ @@ -487,6 +486,13 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDSta= te *s, uint64_t cookie, } nbd_recv_coroutine_wake_one(&s->requests[ind2]); } + +err: + /* Waiters look at this cookie, so do not leave a rejected one behind.= */ + s->reply.cookie =3D 0; + nbd_channel_error(s, ret); + + return ret; } =20 static int coroutine_fn GRAPH_RDLOCK --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338975; cv=none; d=zohomail.com; s=zohoarc; b=ho898YxS19c281RSqCFAg353gWw56JD6vVRaGH09++YTNdwZMkG/T8L9xrdUsdfQWxmLSlj/dbueL/bwZyzgWIIxxGnD9jbx2c0PWx94cLHMxgJQ/A+IF1Dj416C3F/y3PHWY0wwHP36bYYoA2ax7HkOVFUGevyohfvGHKQQm3I= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338975; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=TgxIjFOhuWPMszwvJ2w3DFwhDFqQquG/4E8vpUo8r7w=; b=QPMexdhp07VwujaIBqeGmG31/m7zBunwIBT0drkDf9827LPwiBYQALRObl6sV8NFyj6IPwCM7N5TCaEYUAXXLqsWQUz4g/0u8V5V+esekgDlTvjAnNmVRsKIfqc5T667/954NDK0eYX0nlU0OsZPMnWrtRuvdwrKnN3P3BvMmvs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 178833897529570.94711453662194; Wed, 2 Sep 2026 01:49:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdh-0005pm-0p; Wed, 02 Sep 2026 04:48:13 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdf-0005mo-BG; Wed, 02 Sep 2026 04:48:11 -0400 Received: from forwardcorp1b.mail.yandex.net ([178.154.239.136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdb-0007AL-CN; Wed, 02 Sep 2026 04:48:11 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id AEFCF80CB4; Wed, 02 Sep 2026 11:48:01 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-e6xhaPvL; Wed, 02 Sep 2026 11:48:01 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338881; bh=TgxIjFOhuWPMszwvJ2w3DFwhDFqQquG/4E8vpUo8r7w=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=YOljxP/g98cUZJq4Bpwa1R63JEz+8luvnDa/HySeuqNZlBwPfveIOypZB2qolwghz +dsE2U9qSq+0u6ZvHfta4KoMb/yMWocVBisyzcwAsUmDxXCnv3Y7ZgHbgwIFDWO/SS mipbpabaJ7ovIZxWabd/oGj2eDo+vpp2nFfbFudY= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 2/8] block/nbd: never index requests[] with an unchecked cookie Date: Wed, 2 Sep 2026 11:47:48 +0300 Message-ID: <20260902084754.140103-3-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.136; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338977311158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" Cookies are converted into indices of s->requests[] in several places and the result is used right away, without any check: int i =3D COOKIE_TO_INDEX(cookie); ... return nbd_co_receive_offset_data_payload(s, s->requests[i].offset, COOKIE_TO_INDEX() subtracts one, so a zero cookie becomes an index of -1 and the access lands in front of the array. This is undefined and, depending on the type of the index and on what the compiler has put there, it can as well pass silently: at the site above i is signed, so even a plain i < MAX_NBD_REQUESTS check would happily let -1 through. Route every conversion through a helper which hands out the slot only for a cookie in range and owned by a request in flight, so that the check can not be forgotten again. The helper reports through an Error, as the one cookie which is not ours to trust, the one taken from the wire in nbd_receive_replies(), is a protocol error rather than an internal inconsistency and has to stay a channel error. Every other cookie is one we have issued and still own, so a bad value there is a bug in this file, which is what &error_abort spells out. The cookie of the reply in flight goes through the helper as well. It is not an unchecked value either: it has passed the check on the wire path, or it has been cleared by the previous commit. Cc: Eric Blake Cc: Vladimir Sementsov-Ogievskiy Signed-off-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260820115228.587427-3-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index d9b776283f4..d0a7097034c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -136,6 +136,19 @@ static void nbd_clear_bdrvstate(BlockDriverState *bs) s->x_dirty_bitmap =3D NULL; } =20 +static NBDClientRequest *nbd_request_by_cookie(BDRVNBDState *s, uint64_t c= ookie, + Error **errp) +{ + uint64_t ind =3D COOKIE_TO_INDEX(cookie); + + if (ind >=3D MAX_NBD_REQUESTS || !s->requests[ind].coroutine) { + error_setg(errp, "unexpected cookie value"); + return NULL; + } + + return &s->requests[ind]; +} + /* Called with s->receive_mutex taken. */ static bool coroutine_fn nbd_recv_coroutine_wake_one(NBDClientRequest *req) { @@ -422,7 +435,8 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDStat= e *s, uint64_t cookie, Error **errp) { int ret; - uint64_t ind =3D COOKIE_TO_INDEX(cookie), ind2; + NBDClientRequest *req =3D nbd_request_by_cookie(s, cookie, &error_abor= t); + NBDClientRequest *owner; QEMU_LOCK_GUARD(&s->receive_mutex); =20 while (true) { @@ -437,10 +451,10 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDSt= ate *s, uint64_t cookie, * woken by whoever set s->reply.cookie (or never wait in this * yield). So, we should not wake it here. */ - ind2 =3D COOKIE_TO_INDEX(s->reply.cookie); - assert(!s->requests[ind2].receiving); + owner =3D nbd_request_by_cookie(s, s->reply.cookie, &error_abo= rt); + assert(!owner->receiving); =20 - s->requests[ind].receiving =3D true; + req->receiving =3D true; qemu_co_mutex_unlock(&s->receive_mutex); =20 qemu_coroutine_yield(); @@ -454,7 +468,7 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDStat= e *s, uint64_t cookie, */ =20 qemu_co_mutex_lock(&s->receive_mutex); - assert(!s->requests[ind].receiving); + assert(!req->receiving); continue; } =20 @@ -474,17 +488,16 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDSt= ate *s, uint64_t cookie, error_setg(errp, "unexpected structured reply"); goto err; } - ind2 =3D COOKIE_TO_INDEX(s->reply.cookie); - if (ind2 >=3D MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { + owner =3D nbd_request_by_cookie(s, s->reply.cookie, errp); + if (!owner) { ret =3D -EINVAL; - error_setg(errp, "unexpected cookie value"); goto err; } if (s->reply.cookie =3D=3D cookie) { /* We are done */ return 0; } - nbd_recv_coroutine_wake_one(&s->requests[ind2]); + nbd_recv_coroutine_wake_one(owner); } =20 err: @@ -861,7 +874,6 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( { ERRP_GUARD(); int ret; - int i =3D COOKIE_TO_INDEX(cookie); void *local_payload =3D NULL; NBDStructuredReplyChunk *chunk; =20 @@ -919,8 +931,9 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( return -EINVAL; } =20 - return nbd_co_receive_offset_data_payload(s, s->requests[i].offset, - qiov, errp); + return nbd_co_receive_offset_data_payload( + s, nbd_request_by_cookie(s, cookie, &error_abort)->offset, + qiov, errp); } =20 if (nbd_reply_type_is_error(chunk->type)) { @@ -1067,7 +1080,7 @@ static bool coroutine_fn nbd_reply_chunk_iter_receive= (BDRVNBDState *s, =20 break_loop: qemu_mutex_lock(&s->requests_lock); - s->requests[COOKIE_TO_INDEX(cookie)].coroutine =3D NULL; + nbd_request_by_cookie(s, cookie, &error_abort)->coroutine =3D NULL; s->in_flight--; qemu_co_queue_next(&s->free_sema); qemu_mutex_unlock(&s->requests_lock); --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338942; cv=none; d=zohomail.com; s=zohoarc; b=e3t0TRwWVqcNDWQmyZCq6dOCQHAMw62HqLK1qO6jjnmX7GOsxNpJRs/p/7APX9UOSkLlYJ7qqxL3nxtudITQp6lG0MCZRYthCxbVXdqo0TvTfjj72LpN1k9xcXVQgqSEGDoHKt3Zkb5ak/7D2z5ryXPsWD9fPFq8kIUpUA4EJ0s= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338942; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=MSQwPg9T76w4b9mbdmbrXR5i4SsHZg6DGxvsJNzEM0E=; b=Q5ReIKxLa5mMCKPLDsd29+EYrAl8ntnB4jljBI4Baw/H8WtjR2ctlCz7cEVlQI7XOzBJAVmhgwecD5ZzP4zHILgXjFCd+jxpljYD3a1xx+IkRfwTqIo6wM9M3Kcl6fVRdnzF4L79TiW3QmufLuXayCqb5Ufs4OMD3U/GG1dtd4I= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338941837773.6043213308163; Wed, 2 Sep 2026 01:49:01 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdg-0005ok-FJ; Wed, 02 Sep 2026 04:48:12 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdf-0005mP-3Q; Wed, 02 Sep 2026 04:48:11 -0400 Received: from forwardcorp1b.mail.yandex.net ([2a02:6b8:c02:900:1:45:d181:df01]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdb-0007AO-Ca; Wed, 02 Sep 2026 04:48:10 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id 5804D80C95; Wed, 02 Sep 2026 11:48:02 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-599BR3ea; Wed, 02 Sep 2026 11:48:01 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338881; bh=MSQwPg9T76w4b9mbdmbrXR5i4SsHZg6DGxvsJNzEM0E=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=NbyFVn3VuW3TBuHolJT/UNHmu3X9YF1a57LDfBb6Z3fG2H6/0oUqbCFfOBUN86XEp 8104dqN2ewZWARDPzl66zAykeblaQE2jVZo7evHtCRgF3AdQf6uNZ7keSBq/pycyFt k7YNiko9RYmsJobCTHPYJqOgzSuPz/Ev4nSJc/oI= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 3/8] block/nbd: clear reply.cookie under receive_mutex Date: Wed, 2 Sep 2026 11:47:49 +0300 Message-ID: <20260902084754.140103-4-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a02:6b8:c02:900:1:45:d181:df01; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338948334158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" s->reply is documented as protected by s->receive_mutex, but the cookie is cleared without it once the owning request has consumed its chunk. A waiter in nbd_receive_replies() inspects the very same field under the mutex, and does so with two separate loads: if (s->reply.cookie !=3D 0) { ind2 =3D COOKIE_TO_INDEX(s->reply.cookie); assert(!s->requests[ind2].receiving); Nothing keeps those two loads consistent. If the owner clears the cookie in between, the second one reads 0, COOKIE_TO_INDEX() turns it into an index of -1, and s->requests[] is accessed out of bounds: Assertion `!s->requests[ind2].receiving' failed. (gdb) p cookie $1 =3D 8 (gdb) p s->reply.cookie $2 =3D 0 (gdb) p &((NBDClientRequest *)s->requests)[-1].receiving $3 =3D (_Bool *) 0x5555558416c0 (gdb) p &s->in_flight $4 =3D (unsigned int *) 0x5555558416c0 (gdb) p s->in_flight $5 =3D 8 The cookie we wait for is 8, yet reply.cookie reads 0 one line after it was found non-zero, so the index is -1. requests[-1].receiving lands on in_flight, which is non-zero while requests are outstanding, and that is what the assertion trips over. Hitting this requires two coroutines of one NBD node to run in different threads, as there is no yield point between the two loads for the owner to squeeze into. A multiqueue configuration provides exactly that, with the virtqueues of one disk spread over several iothreads. Note that a compiler is free to merge the two loads into one, in which case the race is invisible, so builds with reduced optimization are much more likely to trip over it. Accessing s->reply without the mutex is fine for the coroutine that owns the reply: a non-zero cookie makes the field private to it. Releasing that ownership is not, as it races with the waiters which are explicitly allowed to look at the cookie. Clear it under the mutex, in the same critical section as the wakeup, and make nbd_recv_coroutines_wake() caller-locked, as CoMutex is not recursive. It has a single caller. The added acquisition cannot block behind the header read in nbd_receive_replies(), because that path is only reachable with reply.cookie =3D=3D 0 while we still own a non-zero cookie. Merging the clear with the wakeup also keeps a newcomer from starting a header read in between, which would stall this already completed request for the duration of that read. There is no cookie to own when we get here after an error, and then a newcomer can indeed be inside that read. It does not hold us for long either, as the channel has been shut down before the error was reported, so the read it sits in returns right away. Fixes: 4ddb5d2fde ("block/nbd: drop connection_co") Cc: Eric Blake Cc: Vladimir Sementsov-Ogievskiy Signed-off-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260820115228.587427-4-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index d0a7097034c..e5e16722ba2 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -161,11 +161,11 @@ static bool coroutine_fn nbd_recv_coroutine_wake_one(= NBDClientRequest *req) return false; } =20 +/* Called with s->receive_mutex taken. */ static void coroutine_fn nbd_recv_coroutines_wake(BDRVNBDState *s) { int i; =20 - QEMU_LOCK_GUARD(&s->receive_mutex); for (i =3D 0; i < MAX_NBD_REQUESTS; i++) { if (nbd_recv_coroutine_wake_one(&s->requests[i])) { return; @@ -974,9 +974,11 @@ static coroutine_fn int nbd_co_receive_one_chunk( /* For assert at loop start in nbd_connection_entry */ *reply =3D s->reply; } - s->reply.cookie =3D 0; =20 - nbd_recv_coroutines_wake(s); + WITH_QEMU_LOCK_GUARD(&s->receive_mutex) { + s->reply.cookie =3D 0; + nbd_recv_coroutines_wake(s); + } =20 return ret; } --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338970; cv=none; d=zohomail.com; s=zohoarc; b=lqXS6vLcgq330cejZhP458dS/Spv8vH2CWPnKz43QPOu+VwiY77eb4DufmmHa+EBcSuBtZxiYHnrsxEi2F2Kj5WTIUtm5o9UAK1BDMrGklVVTOlTEm3GqvELhR/Wy6Cd3LF+jDLNH0ZRUaYqs7VzafuQiE3WISQLLCkjBqGuZi0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338970; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=exZaTsdYoGch2d7FjN2q4hJoPa32EIkgrsRAXKpiKzU=; b=GLdy4a2bPoz2TGRrVmzgKItXLFQLdffDJQRxXf9/FFDd6liBjg/+GDwmjJ9XjvSApAG8nqGCW16wOYYOxC1zriZMzpSW+vDdXE95A+E1uL/kAwqC6RG66zEa+9Eu23xjtovvScODmZjfaKgePe40v8op91ltzKYvtrIRhJTay+w= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338970727490.633390832135; Wed, 2 Sep 2026 01:49:30 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdf-0005n4-Gg; Wed, 02 Sep 2026 04:48:11 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdd-0005lR-Mn; Wed, 02 Sep 2026 04:48:09 -0400 Received: from forwardcorp1b.mail.yandex.net ([178.154.239.136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdb-0007Ab-A1; Wed, 02 Sep 2026 04:48:09 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id 6CB1282A51; Wed, 02 Sep 2026 11:48:03 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-O2q1Wxsv; Wed, 02 Sep 2026 11:48:02 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338882; bh=exZaTsdYoGch2d7FjN2q4hJoPa32EIkgrsRAXKpiKzU=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=xIspBOaGVTV/UeE4IUN6zjixBwXIykitnpyr/uTeO3+YXg6rtwLmnS9zSUk3OAigN Tk+7pgb7PUB2Udt1kqP0vA01gtZnhP76qGO08JXBSeYmibjN3IG1To/JUES9HiQ4T0 0QAVV9+xgTbe8MYCirwm1ol/18Sn5qBO5lObsQTo= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 4/8] iotests: add coverage for NBD transmission commands Date: Wed, 2 Sep 2026 11:47:50 +0300 Message-ID: <20260902084754.140103-5-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.136; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338973610154100 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" NBD_CMD_CACHE has no coverage anywhere in the tree. Nothing ever sends it: our own NBD client does not implement the command, and neither qemu-io nor 'qemu-nbd --list' can issue one, so the only clients reaching this server path are external ones. Add a test driven by libnbd, gated the way nbd-multiconn already is, and start it with the case the command exists for. The export is a qcow2 image over a fully written backing file, so a prefetch has visible work to do. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260827161002.310688-2-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/tests/nbd-commands | 140 ++++++++++++++++++++++ tests/qemu-iotests/tests/nbd-commands.out | 5 + 2 files changed, 145 insertions(+) create mode 100755 tests/qemu-iotests/tests/nbd-commands create mode 100644 tests/qemu-iotests/tests/nbd-commands.out diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tes= ts/nbd-commands new file mode 100755 index 00000000000..4c1cd33db74 --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-commands @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# group: rw auto quick +# +# Test NBD transmission commands against a qemu NBD export +# +# Copyright (C) 2026 Virtuozzo International GmbH +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +from types import ModuleType + +import iotests +from iotests import qemu_img_create, qemu_img_map, qemu_io + + +base =3D os.path.join(iotests.test_dir, 'base') +top =3D os.path.join(iotests.test_dir, 'top') +# Larger than the maximum payload size an export can advertise +size =3D 64 * 1024 * 1024 +pattern =3D 0xa5 +nbd_sock =3D os.path.join(iotests.sock_dir, 'nbd_sock') +nbd_uri =3D 'nbd+unix:///exp?socket=3D' + nbd_sock +nbd: ModuleType + +DEPTH_LOCAL =3D 1 +DEPTH_BACKING =3D 2 + + +class TestNbdCommands(iotests.QMPTestCase): + def setUp(self): + qemu_img_create('-f', iotests.imgfmt, base, str(size)) + qemu_io('-c', f'write -P {pattern} 0 {size}', base) + qemu_img_create('-f', iotests.imgfmt, '-b', base, + '-F', iotests.imgfmt, top, str(size)) + + self.vm =3D iotests.VM() + self.vm.launch() + self.vm.cmd('blockdev-add', { + 'driver': iotests.imgfmt, + 'node-name': 'n', + 'file': {'driver': 'file', 'filename': top}, + 'backing': { + 'driver': iotests.imgfmt, + 'node-name': 'base', + 'file': {'driver': 'file', 'filename': base}, + }, + }) + self.vm.cmd('nbd-server-start', { + 'addr': {'type': 'unix', 'data': {'path': nbd_sock}} + }) + self.vm.cmd('block-export-add', { + 'type': 'nbd', + 'id': 'exp', + 'node-name': 'n', + 'name': 'exp', + 'writable': True, + 'allocation-depth': True, + }) + + self.h =3D None + self.connect() + + def tearDown(self): + self.disconnect() + self.vm.shutdown() + for f in (top, base, nbd_sock): + try: + os.remove(f) + except OSError: + pass + + def connect(self, structured=3DTrue, extended=3DTrue): + self.disconnect() + h =3D nbd.NBD() + h.set_request_structured_replies(structured) + h.set_request_extended_headers(extended) + h.add_meta_context('base:allocation') + h.add_meta_context('qemu:allocation-depth') + # Let the server, not libnbd, reject the out of range requests bel= ow + h.set_strict_mode(h.get_strict_mode() & + ~(nbd.STRICT_BOUNDS | nbd.STRICT_PAYLOAD)) + h.connect_uri(nbd_uri) + self.assertEqual(h.get_structured_replies_negotiated(), structured) + self.assertEqual(h.get_extended_headers_negotiated(), extended) + self.h =3D h + + def disconnect(self): + if self.h is not None: + self.h.shutdown() + self.h =3D None + + def block_status(self, count=3Dsize): + """Map each meta context in the reply to its list of extents.""" + reply =3D {} + + def cb(meta, _offset, entries, _err): + reply.setdefault(meta, []).extend(zip(entries[0::2], + entries[1::2])) + + self.h.block_status(count, 0, cb) + return reply + + def top_extents(self): + """Which parts of the top image are local, once qemu has let go.""" + self.disconnect() + self.vm.shutdown() + return [(e['start'], e['length'], e['depth']) + for e in qemu_img_map(top)] + + def test_cache_copies_on_read(self): + maximum =3D self.h.get_block_size(nbd.SIZE_MAXIMUM) + self.assertLess(maximum, size) + self.assertEqual(self.block_status()['qemu:allocation-depth'], + [(size, DEPTH_BACKING)]) + + self.h.cache(maximum, 0) + + self.assertEqual(self.top_extents(), + [(0, maximum, 0), (maximum, size - maximum, 1)]) + qemu_io('-c', f'read -P {pattern} 0 {size}', top) + + def test_cache_past_end_of_export(self): + self.assertRaises(nbd.Error, self.h.cache, size + 1, 0) + + def test_read_bound_by_max_payload(self): + maximum =3D self.h.get_block_size(nbd.SIZE_MAXIMUM) + self.assertRaises(nbd.Error, self.h.pread, maximum + 65536, 0) + + +if __name__ =3D=3D '__main__': + try: + # Easier to use libnbd than to try and set up parallel + # 'qemu-nbd --list' or 'qemu-io' processes, but not all systems + # have libnbd installed. + import nbd # type: ignore + + iotests.main(supported_fmts=3D['qcow2']) + except ImportError: + iotests.notrun('Python bindings to libnbd are not installed') diff --git a/tests/qemu-iotests/tests/nbd-commands.out b/tests/qemu-iotests= /tests/nbd-commands.out new file mode 100644 index 00000000000..8d7e9967009 --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-commands.out @@ -0,0 +1,5 @@ +... +---------------------------------------------------------------------- +Ran 3 tests + +OK --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338991; cv=none; d=zohomail.com; s=zohoarc; b=W2Lvrujk3VARSO2T7slCba2mfUrAD+d0uiSgQc3gAo4Is0XfZAkCLaVFdunEBvMz7f/OnAMulyiUmw8KoQN0WsOf8r7Gw8aRZA4BSaheufdMoCmXYoeS+jf/OZsg/ElRV61HcGiV1rd+lBva7GO+hNW/QYk+8zTaXR++x5qWZB8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338991; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=r4ooZCwQxoXOa23yi0FK2t075lY3nXixXM3TRGUTv5g=; b=UE1nFFGZimEUGQEMchrT1rMsT9+y8fMTHLxYnWfvkru9ZmDFIhZWhzDdJtdXQDZivyR1hNeOmMi/+ayrd0CPnCEd01xEwS4g7ch69qstZt6ewu4UlH5AWklBqZUQNGTjb/idu5508437u/hNcmJlfEvrWFs2vcFHv5BWLh32mdU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338990897945.1619498039341; Wed, 2 Sep 2026 01:49:50 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdh-0005qR-PS; Wed, 02 Sep 2026 04:48:13 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdf-0005me-6H; Wed, 02 Sep 2026 04:48:11 -0400 Received: from forwardcorp1b.mail.yandex.net ([178.154.239.136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdb-0007Al-Og; Wed, 02 Sep 2026 04:48:10 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id 1AF0580738; Wed, 02 Sep 2026 11:48:04 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-5Q5cgadi; Wed, 02 Sep 2026 11:48:03 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338883; bh=r4ooZCwQxoXOa23yi0FK2t075lY3nXixXM3TRGUTv5g=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=GODSGPjCDuPMzRWbMBqGNYQP7mHRUypacC05dAP6QJOYlpUdw91+RSwe1ECnogrpl rBLtkOFXfU4x3iPn8kOkkzBDsOLJdi/k7xagKDRtZsGI9Fv/BKcKa3hpkpRhaIm0d6 Js1bcc6kSB/rMJihM2/vsZc99i6rc4cw2505yZKI= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 5/8] nbd/server: accept NBD_CMD_CACHE above the maximum payload size Date: Wed, 2 Sep 2026 11:47:51 +0300 Message-ID: <20260902084754.140103-6-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.136; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338993189158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" NBD_CMD_CACHE carries no payload in either direction. The request is a header only, nbd_do_cmd_cache() passes a NULL qiov to blk_co_preadv(), and the reply is a bare status. Still the server rejects any effect length above NBD_MAX_BUFFER_SIZE with EINVAL, which forces a client to split a large prefetch into 32 MiB pieces. The specification does not ask for this. The constraint was renamed from "maximum block size" to "maximum payload size" precisely to separate payload length from effect length, and it says: For commands that do not require a payload in either direction (such as NBD_CMD_TRIM or NBD_CMD_WRITE_ZEROES), the client MAY request an effect length larger than the maximum payload size; the server SHOULD NOT disconnect, but MAY reply with an NBD_EOVERFLOW or NBD_EINVAL error if the oversize request would require too many server resources when compared to the same command with an effect length limited to the maximum payload size (such as an implementation of NBD_CMD_WRITE_ZEROES that utilizes a scratch buffer). We already follow that for NBD_CMD_TRIM and NBD_CMD_WRITE_ZEROES, which carry no length check at all, and our client assumes a server supporting extended headers takes unlimited zero and trim lengths. Handle NBD_CMD_CACHE in the same way. Signed-off-by: Denis V. Lunev Reviewed-by: Vladimir Sementsov-Ogievskiy CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260827161002.310688-3-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- nbd/server.c | 4 +--- tests/qemu-iotests/tests/nbd-commands | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 78ec9844097..e6c47f8c2e3 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2718,7 +2718,6 @@ static int coroutine_fn nbd_co_receive_request(NBDReq= uestData *req, break; =20 case NBD_CMD_CACHE: - check_length =3D true; break; =20 case NBD_CMD_WRITE_ZEROES: @@ -2752,7 +2751,7 @@ static int coroutine_fn nbd_co_receive_request(NBDReq= uestData *req, req->complete =3D true; } if (check_length && request->len > NBD_MAX_BUFFER_SIZE) { - /* READ, WRITE, CACHE */ + /* READ, WRITE */ error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)", request->len, NBD_MAX_BUFFER_SIZE); return -EINVAL; @@ -2908,7 +2907,6 @@ static coroutine_fn int nbd_do_cmd_cache(NBDClient *c= lient, NBDRequest *request, NBDExport *exp =3D client->exp; =20 assert(request->type =3D=3D NBD_CMD_CACHE); - assert(request->len <=3D NBD_MAX_BUFFER_SIZE); =20 ret =3D blk_co_preadv(exp->common.blk, request->from, request->len, NULL, BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH); diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tes= ts/nbd-commands index 4c1cd33db74..cbfc47782ff 100755 --- a/tests/qemu-iotests/tests/nbd-commands +++ b/tests/qemu-iotests/tests/nbd-commands @@ -114,10 +114,9 @@ class TestNbdCommands(iotests.QMPTestCase): self.assertEqual(self.block_status()['qemu:allocation-depth'], [(size, DEPTH_BACKING)]) =20 - self.h.cache(maximum, 0) + self.h.cache(size, 0) =20 - self.assertEqual(self.top_extents(), - [(0, maximum, 0), (maximum, size - maximum, 1)]) + self.assertEqual(self.top_extents(), [(0, size, 0)]) qemu_io('-c', f'read -P {pattern} 0 {size}', top) =20 def test_cache_past_end_of_export(self): --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338966; cv=none; d=zohomail.com; s=zohoarc; b=ZxndUANoE+TmGRM0ZTeWip7Ag0ZTeXr8wfs9VOvDnW8ReGpRPAeGS8V+NFl/ZXZ/6+ehwj2NJPYJGKhaHq7kbOshuh1ATCc3bHCs/An3B3vm69ruUV5Ij5sx3xCNzMqHTLXpu/teiKTmRB7vEgmBW5LDtV0AgNJ/lqZzC9cd6Mk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338966; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=vZa2zXqkRViDrN4KckmOg4GrtxY8Zuwqx3ByJlvFGdw=; b=ID0rNVxMNhGhnhmDLSvz6ZbsX0BTx/gFdqU9p5o+nAvFylppCzk0nybXyR70SezP1b+V+7lrkryCNFsx23sv3e2kJLTsp7MOFg6EI5vxbkYKsbEqKvXXjgv9ZoslmUPncNQbL9CzuINALSAaWqrB1zJOeEVzWpI1fXijgNZpH54= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338966100395.57980261343266; Wed, 2 Sep 2026 01:49:26 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdi-0005qo-Ef; Wed, 02 Sep 2026 04:48:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdg-0005oH-2m; Wed, 02 Sep 2026 04:48:12 -0400 Received: from forwardcorp1b.mail.yandex.net ([178.154.239.136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdc-0007As-78; Wed, 02 Sep 2026 04:48:11 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id A90C180CAD; Wed, 02 Sep 2026 11:48:04 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-ygl8N5rE; Wed, 02 Sep 2026 11:48:04 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338884; bh=vZa2zXqkRViDrN4KckmOg4GrtxY8Zuwqx3ByJlvFGdw=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=b23lq1RBgkTUzlGt5HBUsmR3HvJWC0nhjVMxzaQbHQ5R5MIyJTlq2X8uNKrurhTZa WCa3U9nlLS1kL/4CLyKz5kIQrAEDSjM4JfH6mfRwUfvi6pOOo8/FEdE0xrmFYPcN8Y pDc7rqcJsnH3Q7wlJY6LVApHW6aCunr+PK/vbaco= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 6/8] iotests/nbd-commands: exercise the simple and structured reply modes Date: Wed, 2 Sep 2026 11:47:52 +0300 Message-ID: <20260902084754.140103-7-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.136; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338969536154100 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" Every NBD request the iotests send arrives in NBD_MODE_EXTENDED, so the server paths kept for older clients are never taken. Instrumenting nbd_co_receive_request() over the whole auto group and every test that touches NBD gives mode 4 for all of them, which leaves nbd_co_send_simple_reply() and the compact header handling dead under test. A regression there would only show against a third party client. libnbd can negotiate down, so run the same command set three times, once per mode, and assert the mode that was actually reached. Block status is limited to the two modes that can negotiate a meta context. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260827161002.310688-4-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/tests/nbd-commands | 29 +++++++++++++++++++++++ tests/qemu-iotests/tests/nbd-commands.out | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tes= ts/nbd-commands index cbfc47782ff..3dfe9131900 100755 --- a/tests/qemu-iotests/tests/nbd-commands +++ b/tests/qemu-iotests/tests/nbd-commands @@ -119,6 +119,35 @@ class TestNbdCommands(iotests.QMPTestCase): self.assertEqual(self.top_extents(), [(0, size, 0)]) qemu_io('-c', f'read -P {pattern} 0 {size}', top) =20 + def check_commands(self, structured, extended): + self.connect(structured, extended) + + self.assertEqual(self.h.pread(4096, 4096), bytes([pattern]) * 4096) + + self.h.cache(size, 0) + + self.h.pwrite(b'x' * 4096, 4096) + self.h.flush() + self.assertEqual(self.h.pread(4096, 4096), b'x' * 4096) + + self.h.zero(4096, 8192) + self.assertEqual(self.h.pread(4096, 8192), bytes(4096)) + + self.h.trim(4096, 16384) + + if structured: + self.assertEqual(self.block_status()['qemu:allocation-depth'], + [(size, DEPTH_LOCAL)]) + + def test_commands_simple_replies(self): + self.check_commands(structured=3DFalse, extended=3DFalse) + + def test_commands_structured_replies(self): + self.check_commands(structured=3DTrue, extended=3DFalse) + + def test_commands_extended_headers(self): + self.check_commands(structured=3DTrue, extended=3DTrue) + def test_cache_past_end_of_export(self): self.assertRaises(nbd.Error, self.h.cache, size + 1, 0) =20 diff --git a/tests/qemu-iotests/tests/nbd-commands.out b/tests/qemu-iotests= /tests/nbd-commands.out index 8d7e9967009..3f8a935a082 100644 --- a/tests/qemu-iotests/tests/nbd-commands.out +++ b/tests/qemu-iotests/tests/nbd-commands.out @@ -1,5 +1,5 @@ -... +...... ---------------------------------------------------------------------- -Ran 3 tests +Ran 6 tests =20 OK --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338950; cv=none; d=zohomail.com; s=zohoarc; b=E4/ZzBRFrEvArDIFUiucKoXwfmJeu/vkNY/5j+T58yn4kc/ge89psDls4FhckCS/WHNnlquIdXBht18BixG3DSglfMUiCmvfvZAyHAZ7czHxdJbcCPhkgRs06RKfV15mWTM8wlsUoydwpRBnDeaUEEwVp3uX2r6/A5IH7wozjKo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338950; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=W2dWXaBudcA0BTbqpLGWO9Va0yvNHFfq/w1lvxGW0gY=; b=DDiWMH6KVwsQak590zRJNc5ETbEH3El/jcAj5VR/5HGfp0IywFMJJcIaNEY5lxzJa+OkerYlsC5VX+IOeUqw0XjeQjGc4QePR9sUhPIN8IAA6k+w6zqRpNlYN8RgtuBmysG5pKp3WwEUwEY4/sKSdxUIBmLM5sFknK6aWSsPGCs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338950382164.84028660241881; Wed, 2 Sep 2026 01:49:10 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdj-0005sB-Tz; Wed, 02 Sep 2026 04:48:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdg-0005oi-DK; Wed, 02 Sep 2026 04:48:12 -0400 Received: from forwardcorp1b.mail.yandex.net ([178.154.239.136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdc-0007Av-9c; Wed, 02 Sep 2026 04:48:12 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id 42A4D80CB2; Wed, 02 Sep 2026 11:48:05 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-gAF91yBQ; Wed, 02 Sep 2026 11:48:04 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338884; bh=W2dWXaBudcA0BTbqpLGWO9Va0yvNHFfq/w1lvxGW0gY=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=vL5DZ/3Mef36ZyV8MNUjZC+5FBpWgl2D6IXHziAQl5NtM/0KD0Noqa5nTo7VTP+k3 3bgKmdAGoYh1aT+H4qk8P9pxJpZrLlrftJzQPUo+BwMC+HBCkVhNdetz68H3L9RgbJ dqrZi2DXBsKoJPVHIMui++whqZzK6RooAmmI5Zxg= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 7/8] iotests/nbd-commands: cover NBD_CMD_BLOCK_STATUS with a payload Date: Wed, 2 Sep 2026 11:47:53 +0300 Message-ID: <20260902084754.140103-8-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=178.154.239.136; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338954181154100 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" With extended headers a client may name the meta contexts it wants in a request payload. Our own client never sends one, so the server side of it, nbd_co_block_status_payload_read(), is not exercised anywhere. Export two meta contexts and ask libnbd for each of them in turn, which is only answerable through that path. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260827161002.310688-5-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/tests/nbd-commands | 14 ++++++++++++-- tests/qemu-iotests/tests/nbd-commands.out | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tes= ts/nbd-commands index 3dfe9131900..09e811724b1 100755 --- a/tests/qemu-iotests/tests/nbd-commands +++ b/tests/qemu-iotests/tests/nbd-commands @@ -90,7 +90,7 @@ class TestNbdCommands(iotests.QMPTestCase): self.h.shutdown() self.h =3D None =20 - def block_status(self, count=3Dsize): + def block_status(self, count=3Dsize, wanted=3DNone): """Map each meta context in the reply to its list of extents.""" reply =3D {} =20 @@ -98,7 +98,10 @@ class TestNbdCommands(iotests.QMPTestCase): reply.setdefault(meta, []).extend(zip(entries[0::2], entries[1::2])) =20 - self.h.block_status(count, 0, cb) + if wanted is None: + self.h.block_status(count, 0, cb) + else: + self.h.block_status_filter(count, 0, wanted, cb) return reply =20 def top_extents(self): @@ -148,6 +151,13 @@ class TestNbdCommands(iotests.QMPTestCase): def test_commands_extended_headers(self): self.check_commands(structured=3DTrue, extended=3DTrue) =20 + def test_block_status_payload_filter(self): + self.assertEqual(sorted(self.block_status()), + ['base:allocation', 'qemu:allocation-depth']) + + for wanted in (['base:allocation'], ['qemu:allocation-depth']): + self.assertEqual(sorted(self.block_status(wanted=3Dwanted)), w= anted) + def test_cache_past_end_of_export(self): self.assertRaises(nbd.Error, self.h.cache, size + 1, 0) =20 diff --git a/tests/qemu-iotests/tests/nbd-commands.out b/tests/qemu-iotests= /tests/nbd-commands.out index 3f8a935a082..2f7d3902f23 100644 --- a/tests/qemu-iotests/tests/nbd-commands.out +++ b/tests/qemu-iotests/tests/nbd-commands.out @@ -1,5 +1,5 @@ -...... +....... ---------------------------------------------------------------------- -Ran 6 tests +Ran 7 tests =20 OK --=20 2.43.0 From nobody Sat Sep 26 20:51:56 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1788338982; cv=none; d=zohomail.com; s=zohoarc; b=R2NceD6hEo6qt+jd67pJRSrz9+lC485ZLhSMXR6VIsXC6NjxLOE67Ybp8wCSsjwTZhE3p0qMplJqwG+b5TrpM7lAFc7pWWCK5AZ2T/sE+FcXMES0hlF1cOv3djIOeNcWk2TpKw62rAi6wdm6yHrZNBtxlTwdrduY6leHHN1rb3g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788338982; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Syx2uB5uU+sBiAi4YVEthtJZa+fTMLKKLKJyxkfFWEo=; b=ECtupMYcVQJnlnopn+Pda2pDrKEGXlhEv3/BJZsdS2UBvhbT9DCLh0SJPeFNC3+TxiAWwYNNnq10+Ri4ZaNxL4vNKrj7o3obo9dP/KT0AIYdlygGO6f8h/rN2F/1+MPYfpYxuP0D132jL7LCfOn26O2mTV7B0WtMkBKsioFy4SQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1788338982611694.8099839435894; Wed, 2 Sep 2026 01:49:42 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1gdi-0005qW-61; Wed, 02 Sep 2026 04:48:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdf-0005ms-Bf; Wed, 02 Sep 2026 04:48:11 -0400 Received: from forwardcorp1b.mail.yandex.net ([2a02:6b8:c02:900:1:45:d181:df01]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x1gdb-0007B8-GV; Wed, 02 Sep 2026 04:48:11 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (postfix) with ESMTPS id E025982A62; Wed, 02 Sep 2026 11:48:05 +0300 (MSK) Received: from i115954770.yandex-team.ru (unknown [2a02:6bf:8080:b72::1:17]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp) with ESMTPSA id xlfWR5raWmI0-k6AXxjNG; Wed, 02 Sep 2026 11:48:05 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1788338885; bh=Syx2uB5uU+sBiAi4YVEthtJZa+fTMLKKLKJyxkfFWEo=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=0PlLkEWFrxlvC3NVO4yqV4+tmmJYd8Sw16kqYkILcTe4RWjj8AB+WoEIPmGULP9Z7 lGE8QNSZGOOd2d9b3OXtD80Iteb/VLp11fyTvHS2+0IzILNghIBSTwI0X6z6nuAt2v wzIjrhH971A2wzwvSkr9+toq+rf3lIo/4/L+Ybok= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, den@openvz.org, vsementsov@yandex-team.ru, Eric Blake Subject: [PULL 8/8] iotests/nbd-commands: cover the command flags and sparse replies Date: Wed, 2 Sep 2026 11:47:54 +0300 Message-ID: <20260902084754.140103-9-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260902084754.140103-1-vsementsov@yandex-team.ru> References: <20260902084754.140103-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a02:6b8:c02:900:1:45:d181:df01; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @yandex-team.ru) X-ZM-MESSAGEID: 1788338983566158500 Content-Type: text/plain; charset="utf-8" From: "Denis V. Lunev" No test sends the NBD command flags, so the server paths behind them go unexercised. Send them from the new client. A structured read answers a hole with an offset and a length rather than a cluster of zeroes. Lay out data, a hole and data again, and read the three arrangements a hole can appear in, since a leading hole and a hole between two extents take different turns through nbd_co_send_sparse_read(). NBD_CMD_FLAG_DF asks for one chunk instead, which is the same layout sent as real zeroes. Check the chunk boundaries in both forms, and that the data still reads back. NBD_CMD_FLAG_REQ_ONE caps the extent array at one entry, so the block status reply covers only the first cluster of the three asked about. That a reply may describe less than was requested is also how the server keeps NBD_MAX_BLOCK_STATUS_EXTENTS from being exceeded on a long fragmented range, and a client assuming full coverage believes stale status. NBD_CMD_FLAG_FAST_ZERO becomes BDRV_REQ_NO_FALLBACK. A cluster aligned zero can be done by marking the cluster, while zeroing part of a cluster over a backing file needs the read modify write the flag forbids, so check both the success and the ENOTSUP. NBD_CMD_FLAG_FUA is added to a write and a trim. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20260827161002.310688-6-den@openvz.org> Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/tests/nbd-commands | 72 ++++++++++++++++++++++- tests/qemu-iotests/tests/nbd-commands.out | 4 +- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tes= ts/nbd-commands index 09e811724b1..adfd4a495cb 100755 --- a/tests/qemu-iotests/tests/nbd-commands +++ b/tests/qemu-iotests/tests/nbd-commands @@ -25,6 +25,7 @@ nbd: ModuleType =20 DEPTH_LOCAL =3D 1 DEPTH_BACKING =3D 2 +CLUSTER =3D 65536 =20 =20 class TestNbdCommands(iotests.QMPTestCase): @@ -90,7 +91,7 @@ class TestNbdCommands(iotests.QMPTestCase): self.h.shutdown() self.h =3D None =20 - def block_status(self, count=3Dsize, wanted=3DNone): + def block_status(self, count=3Dsize, wanted=3DNone, flags=3D0): """Map each meta context in the reply to its list of extents.""" reply =3D {} =20 @@ -99,9 +100,9 @@ class TestNbdCommands(iotests.QMPTestCase): entries[1::2])) =20 if wanted is None: - self.h.block_status(count, 0, cb) + self.h.block_status(count, 0, cb, flags) else: - self.h.block_status_filter(count, 0, wanted, cb) + self.h.block_status_filter(count, 0, wanted, cb, flags) return reply =20 def top_extents(self): @@ -158,6 +159,71 @@ class TestNbdCommands(iotests.QMPTestCase): for wanted in (['base:allocation'], ['qemu:allocation-depth']): self.assertEqual(sorted(self.block_status(wanted=3Dwanted)), w= anted) =20 + def read_chunks(self, count, offset, flags=3D0): + chunks =3D [] + + def cb(subbuf, off, status, _err): + chunks.append((off, len(subbuf), status)) + + self.h.pread_structured(count, offset, cb, flags) + return chunks + + def make_sparse(self): + """Lay out data, a hole and data again, one cluster each.""" + self.h.pwrite(b'z' * CLUSTER, 0) + self.h.zero(CLUSTER, CLUSTER) + self.h.pwrite(b'z' * CLUSTER, 2 * CLUSTER) + + def test_read_sparse_chunks(self): + self.make_sparse() + + first =3D (0, CLUSTER, nbd.READ_DATA) + hole =3D (CLUSTER, CLUSTER, nbd.READ_HOLE) + second =3D (2 * CLUSTER, CLUSTER, nbd.READ_DATA) + + # A hole is an offset and a length, not a cluster of zeroes, + # wherever it falls in the reply + self.assertEqual(self.read_chunks(2 * CLUSTER, 0), [first, hole]) + self.assertEqual(self.read_chunks(2 * CLUSTER, CLUSTER), + [hole, second]) + self.assertEqual(self.read_chunks(3 * CLUSTER, 0), + [first, hole, second]) + + self.assertEqual(self.h.pread(CLUSTER, CLUSTER), bytes(CLUSTER)) + self.assertEqual(self.h.pread(CLUSTER, 0), b'z' * CLUSTER) + + def test_read_dont_fragment(self): + self.make_sparse() + + self.assertEqual(self.read_chunks(3 * CLUSTER, 0, nbd.CMD_FLAG_DF), + [(0, 3 * CLUSTER, nbd.READ_DATA)]) + + def test_block_status_req_one(self): + hole =3D nbd.STATE_HOLE | nbd.STATE_ZERO + self.make_sparse() + + alloc =3D self.block_status(3 * CLUSTER)['base:allocation'] + self.assertEqual(alloc, [(CLUSTER, 0), (CLUSTER, hole), (CLUSTER, = 0)]) + + # One extent, so the reply covers less than was asked for and the + # client has to come back for the rest + alloc =3D self.block_status(3 * CLUSTER, flags=3Dnbd.CMD_FLAG_REQ_= ONE) + self.assertEqual(alloc['base:allocation'], [(CLUSTER, 0)]) + + def test_write_and_trim_fua(self): + self.h.pwrite(b'y' * 4096, 4096, nbd.CMD_FLAG_FUA) + self.assertEqual(self.h.pread(4096, 4096), b'y' * 4096) + self.h.trim(4096, 4096, nbd.CMD_FLAG_FUA) + + def test_fast_zero(self): + self.h.zero(CLUSTER, CLUSTER, nbd.CMD_FLAG_FAST_ZERO) + self.assertEqual(self.h.pread(CLUSTER, CLUSTER), bytes(CLUSTER)) + + # Zeroing part of a cluster needs the fallback the flag forbids + with self.assertRaises(nbd.Error) as caught: + self.h.zero(4096, 4096, nbd.CMD_FLAG_FAST_ZERO) + self.assertEqual(caught.exception.errno, 'ENOTSUP') + def test_cache_past_end_of_export(self): self.assertRaises(nbd.Error, self.h.cache, size + 1, 0) =20 diff --git a/tests/qemu-iotests/tests/nbd-commands.out b/tests/qemu-iotests= /tests/nbd-commands.out index 2f7d3902f23..281b69efeac 100644 --- a/tests/qemu-iotests/tests/nbd-commands.out +++ b/tests/qemu-iotests/tests/nbd-commands.out @@ -1,5 +1,5 @@ -....... +............ ---------------------------------------------------------------------- -Ran 7 tests +Ran 12 tests =20 OK --=20 2.43.0