From nobody Sun May 24 23:37:32 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=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1778435515; cv=none; d=zohomail.com; s=zohoarc; b=iwasni5FCCQSvI7u4fnYWXLoYgYqjFHnd6CKAihyn+uFy3HN61guIPPH0/BtOM/JM7ZHBv5KDoTiRK7U25UUnegEwZ+eGMrJOVHL23QRVy8vITP9FrbBvz9Vx01M+k8DMK/Zrb1eC44yaDOOd9/uUozw8ldze06yv118RoL9N1Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778435515; 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=c67CZxLMUIcBPR1yl/CBxq3zsoOSpsyIfsbCNt9sOuw=; b=KISEIYswCL9Pdj/uMeYBC02LZkokTF9lpc5z3JwEugdvvmtAk70+HiFRAjCepbb8Mzpn6P7rBOKU3VRQQbidMEs3i8PJLM7JVuzF7al1rZUW5dEmjPTKPm2N7aJBEKPWCwYZjbi4KS5khwIpIOVbMoXyDsslVzHxB//pl1VnNGw= 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 1778435515595900.2633836876696; Sun, 10 May 2026 10:51:55 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wM8J5-0000LN-4u; Sun, 10 May 2026 13:51: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 1wM8J3-0000Kb-Sn for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:10 -0400 Received: from mail-wm1-x32e.google.com ([2a00:1450:4864:20::32e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wM8J1-0001hk-8h for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:09 -0400 Received: by mail-wm1-x32e.google.com with SMTP id 5b1f17b1804b1-4891f625344so34345285e9.0 for ; Sun, 10 May 2026 10:51:05 -0700 (PDT) Received: from groves.. ([95.90.240.94]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e702e0bf2sm225215565e9.4.2026.05.10.10.51.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 10:51:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778435464; x=1779040264; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=c67CZxLMUIcBPR1yl/CBxq3zsoOSpsyIfsbCNt9sOuw=; b=VIV1KH9Jp41aqvQom4paITXezSEYghDLO4IkFkzk/U5FvR0dIRkQqw0nvGiPmlQDGl 2IZcFKVti9k5LbBPBWqkuH6FA52VRPvBc6zeUauxn74AVXpE+ShxfwO1b98aCK+BoT6O UVKYaUmaiLMS7tK1pE/puJDw9Um+MrWetemLZEMr0vNhCaekDKH9ftP9skI23la6CygS bpc4E/EOIl7lHVeIbGf43u86bUt774NXKJii2eYafVchkOBLK/82hBnXyubUaxZD6toT Z1cP12V+wPAlleKAYKU6iPq6OgCTs46ab9PpGtZAqqOnQ73AOHEFnUZQ8GJqSMTw/1Iz h1PQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778435464; x=1779040264; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=c67CZxLMUIcBPR1yl/CBxq3zsoOSpsyIfsbCNt9sOuw=; b=mpMcBVwCNNh1eH5YeAHHScyi0GkDoVuF9q50HvygIreFppdYaQjaFsFOO6x3fPyaHJ +rTjTCDi/LmRC4bTtE1ROB07r5z4Ym5K0tnSiFKKaDCHdUTlzc1U4L6wbpVfHiio35By SP4QZjmY9Ad+Iy1d/WLEMidDxagWnWpcbuyTVlNAF9J7son59pT1VfO0Sl1zFRVtmtfC tERp733qo4gZ51AuLWQY8cFrj5gv997VXpCaHIrB8n7+gejQ0LF8SuCYNXf3/tTCFGYz CuQoVGtlgNykxfh3VHmODeaUcWC3A+X/Zw0I3md/4R7F7HvBS21xnmtd3AAEAFg5wpz0 E0xQ== X-Gm-Message-State: AOJu0Yyujqhqa4PZC9Lb0DdK9ITYI4zflLCXG8eIzvqcG7jkTlorU9qH N01ndzHVl194qPCTfVsSSwNHZjPqSLLVFJ0zRRGgL00rVMfi5OXu2XFbYaFMNg== X-Gm-Gg: Acq92OF8AM6wb1naaZlHYTMhxzMA20toWAwxhqf7xrfGljKzChZV9ZjB01Ig8b/ZdXI ykjH2qimc5VvmsCa7pYReZz4TgstFdRAJux+6/hDqC3ONBZxCJfX6KQxOiplcteJ0zmr2tk3HRJ EKzS9V2pJ/PSL9AI24rcw8UoaUG8hsgiaBUnmeS2L/XJqJE/04XCzsFafZh8j5BOCWVXM3JLrZ7 EYszA6SnWYoAZvI/8wFJbN8aK8vQuza736blXztWtrvNIbWm0w2RD1+J8F1eQ2xCtgEDLU2hiM5 Ey4MbzH8oKcleLmcNiv40EDfazFgUEERNESj0NC+qAnmD9/BUZmOIsMsK3oDFvkxapvvkcP6ruZ roR6Sthn3mbtF84VoA7Lo7iK1BZNH3h5cWS8v0ikeyaM2Wo1+CnlR+Bt0DFeIgimB6PDh6TIPGP NIn2h7/mZXo24ZuG/fxHcW8SEcpav+jCP02ZAo1dnWX+h3HiV1B6bAPtYRhPa4pec= X-Received: by 2002:a05:600c:3b20:b0:489:1d7a:4537 with SMTP id 5b1f17b1804b1-48e5dfcd4a9mr223442745e9.3.1778435463930; Sun, 10 May 2026 10:51:03 -0700 (PDT) From: Sam Li To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , dmitry.fomichev@wdc.com, Kevin Wolf , cassel@kernel.org, Markus Armbruster , Stefan Hajnoczi , qemu-block@nongnu.org, dlemoal@kernel.org, Eric Blake , "Michael S. Tsirkin" , hare@suse.de, Hanna Reitz , Sam Li Subject: [PATCH v10 1/4] docs/qcow2: add the zoned format feature Date: Sun, 10 May 2026 19:50:56 +0200 Message-ID: <20260510175059.311814-2-faithilikerun@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260510175059.311814-1-faithilikerun@gmail.com> References: <20260510175059.311814-1-faithilikerun@gmail.com> 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=2a00:1450:4864:20::32e; envelope-from=faithilikerun@gmail.com; helo=mail-wm1-x32e.google.com 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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, 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 @gmail.com) X-ZM-MESSAGEID: 1778435517761154100 Content-Type: text/plain; charset="utf-8" Add the specs for the zoned format feature of the qcow2 driver. The qcow2 file then can emulate real zoned devices, either passed through by virtio-blk device or NVMe ZNS drive to the guest given zoned information. Signed-off-by: Sam Li Reviewed-by: Stefan Hajnoczi --- docs/system/qemu-block-drivers.rst.inc | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-bloc= k-drivers.rst.inc index 384e95ba76..5066c943d5 100644 --- a/docs/system/qemu-block-drivers.rst.inc +++ b/docs/system/qemu-block-drivers.rst.inc @@ -172,6 +172,48 @@ This section describes each format and the options tha= t are supported for it. filename`` to check if the NOCOW flag is set or not (Capital 'C' is NOCOW flag). =20 + .. option:: zone.mode + If this is set to ``host-managed``, the image is an emulated zoned + block device. This option is only valid to emulated zoned device files. + + .. option:: zone.size + + The size of a zone in bytes. The device is divided into zones of this + size with the exception of the last zone, which may be smaller. + + .. option:: zone.capacity + + The initial capacity value, in bytes, for all zones. The capacity must + be less than or equal to zone size. If the last zone is smaller, then + its capacity is capped. + + The zone capacity is per zone and may be different between zones in re= al + devices. QCow2 sets all zones to the same capacity. + + .. option:: zone.conventional_zones + + The number of conventional zones of the zoned device. + + .. option:: zone.max_active_zones + + The limit of the zones with implicit open, explicit open or closed sta= te. + + The max active zones must be less or equal to the number of SWR + (sequential write required) zones of the device. + + .. option:: zone.max_open_zones + + The maximal allowed open zones. The max open zones must not be larger = than + the max active zones. + + If the limits of open zones or active zones are equal to the number of + SWR zones, then it is the same as having no limits. + + .. option:: zone.max_append_bytes + + The number of bytes in a zone append request that can be issued to the + device. It must be 512-byte aligned and less than the zone capacity. + .. program:: image-formats .. option:: qed =20 --=20 2.43.0 From nobody Sun May 24 23:37:32 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=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1778435503; cv=none; d=zohomail.com; s=zohoarc; b=LzFzDLS7uxKqb0asJvEnCZ6NyIrIRBwVy3KSEKM43UkvjA7R6pMx7tDjYeJjesfI2DXMIre1yyeMtNWqgWi+e+yQqa8T/W71mDyROIYpWCSV+l/RA3QM805zRE3IWOvjTNk7ndjZX1NsS4L3RhlTCO1HgY9DmrSelMxL5LnKM0U= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778435503; 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=7CbTgBpTALfPDOCVS8LUTYnbTLmuJhgho732OZlO89k=; b=WCljA5ePHeDBTZnt4FCA43kX7RW6mPD1FbdHj/6nFRI2f5OxIaBgnSH0SNfFopSR/ulFClaE4UqsFoC4JndaaSGMVvcJX2qL2GuCewglgPJnA+JBo2qmHMAIJrVY+RKkpRM/RcIBZ1JVf3ZUDbdX1uFzyq0Q+xFmcrUywfIxA2g= 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 1778435503295840.6723310882975; Sun, 10 May 2026 10:51:43 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wM8JA-0000OP-P3; Sun, 10 May 2026 13:51:16 -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 1wM8J6-0000Lo-O6 for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:12 -0400 Received: from mail-wm1-x336.google.com ([2a00:1450:4864:20::336]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wM8J1-0001hx-BL for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:12 -0400 Received: by mail-wm1-x336.google.com with SMTP id 5b1f17b1804b1-48d102471a4so34993975e9.2 for ; Sun, 10 May 2026 10:51:06 -0700 (PDT) Received: from groves.. ([95.90.240.94]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e702e0bf2sm225215565e9.4.2026.05.10.10.51.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 10:51:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778435465; x=1779040265; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7CbTgBpTALfPDOCVS8LUTYnbTLmuJhgho732OZlO89k=; b=KDkX5EL3q1euOXoscEL684KfgfijDwSPU5cuif6ShsXW2kGoEQCXEiENLGd5Ri4pX+ HxhY1IWxsUsI9mgwdn3g+5mxVNTsfERkTyzbTAEsA6FNZvFwwfClhrAp1bWpFlCcAYIu G6wh5btnOW43SDRFsgThPTCwn9oeoUn8eb2pvCHAJYqDvnfCF/ne0MTeGmn7AoL/ab8q ASGq9lfAPzX8QQtLlaFhiQYqzvBxbL54OYh1s81Bfh36Gm2RQoBQlUxC2AU2kPvLVC2f eGBJd3IITzSFoD4998giXWulln3hzpfpPuV1Jc5a8R9om/6ueiQyG1WrXYODNyZfV/dT 4JhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778435465; x=1779040265; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=7CbTgBpTALfPDOCVS8LUTYnbTLmuJhgho732OZlO89k=; b=HoVuRx7C9QccW5pApRlbj1qlqEPV9KrlWfcBhhWHsi2yxcM38R0k3gNiaTWLOKl1MC RaWZLFrE8LXWdMhDu0swQfVCTpDlgptsh+LvDSnPSP3FAUQrbUXqIiFHvqXgEJQ5nMcy 1U7OX404577bg4KPF8VfShe4hmrygfHM/1MJ3YLZRnxmHlb2appncwXMaIx5Zc+NFAiT Vo2YAp1ztRP2h8lf/Fd3TYSPKQ7m2vQmElRKzulXqRc6ETwdOnQXspFlRmuBzu8dNr4g a4yJp1Upxz8H4lxGNwhxPkx0D+Iln2X/xUfIzbLxd7GfEleeOWahmuPfYs+QVqL94xkt HD3g== X-Gm-Message-State: AOJu0Yz8nfhoWBWmrwgkH+opJGnEAXlBHdLssrEuWWuEAcUTMOEDS7NU JBhHmmm3n6bRKNrO5Wk1jFiubajumljIwshLcgvv5Av7fn9tDaqsTEQl+XFHfQ== X-Gm-Gg: Acq92OFoR/0oc0yPUXsIn/LWIKVUasO0kbvxJFZFzsqjhxQ/NlzppCChsdqIyav3S1a sgHlL5JePJiUz42hdO5oMZT4mctFry1imefI5GtDu1vilTrUucpsC/UeCp953IhPi6gjzA2bItK T3Kezy0PFMGJxO8mOx2RfKzJIrwJpvx0JLg4X+agwC7ekWKQTDoePpBFFHVf9718VouYouyr6Eh hTq+xuXwfCFdBE4FkFqd8Vt7OZhwjpqcCgCxakfj2E530ed57AwXfurMJC5FsWBS7fCZgfI8Fx0 NT0zsvUpUTJ8XlPt4P1iNyra1ss51o3WsSae7q5rCU3slQWpZ5kpBWB5iQ/HAWNJi1xWi8oucV9 hmxK8mV7yo2TcG+e6Okw/lLSJZIYF28MgNpjjZsk/0kZfS/v57PeiZeuFwYoXbEPhHvAoby+gUA GbZG3JW+kseBw0zcjjpT2OCzC3e21rUyI9iEgTtTn8BSel+EZmQcqWvMiPH9JTzDM= X-Received: by 2002:a05:600c:8908:b0:48e:6275:27ec with SMTP id 5b1f17b1804b1-48e676b6393mr116157715e9.24.1778435465249; Sun, 10 May 2026 10:51:05 -0700 (PDT) From: Sam Li To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , dmitry.fomichev@wdc.com, Kevin Wolf , cassel@kernel.org, Markus Armbruster , Stefan Hajnoczi , qemu-block@nongnu.org, dlemoal@kernel.org, Eric Blake , "Michael S. Tsirkin" , hare@suse.de, Hanna Reitz , Sam Li Subject: [PATCH v10 2/4] qcow2: add configurations for zoned format extension Date: Sun, 10 May 2026 19:50:57 +0200 Message-ID: <20260510175059.311814-3-faithilikerun@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260510175059.311814-1-faithilikerun@gmail.com> References: <20260510175059.311814-1-faithilikerun@gmail.com> 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=2a00:1450:4864:20::336; envelope-from=faithilikerun@gmail.com; helo=mail-wm1-x336.google.com 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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @gmail.com) X-ZM-MESSAGEID: 1778435505470154100 Content-Type: text/plain; charset="utf-8" To configure the zoned format feature on the qcow2 driver, it requires settings as: the device size, zone model, zone size, zone capacity, number of conventional zones, limits on zone resources (max append bytes, max open zones, and max_active_zones). To create a qcow2 image with zoned format feature, use command like this: qemu-img create -f qcow2 zbc.qcow2 -o size=3D768M \ -o zone.size=3D64M -o zone.capacity=3D64M -o zone.conventional_zones=3D0 \ -o zone.max_append_bytes=3D4096 -o zone.max_open_zones=3D6 \ -o zone.max_active_zones=3D8 -o zone.mode=3Dhost-managed Signed-off-by: Sam Li --- block/file-posix.c | 2 +- block/qcow2.c | 264 ++++++++++++++++++++++++++++++- block/qcow2.h | 32 +++- docs/interop/qcow2.rst | 110 ++++++++++++- include/block/block_int-common.h | 15 +- qapi/block-core.json | 70 +++++++- 6 files changed, 485 insertions(+), 8 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index e49b13d6ab..14278785b9 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3607,7 +3607,7 @@ raw_co_zone_append(BlockDriverState *bs, =20 if (*offset & zone_size_mask) { error_report("sector offset %" PRId64 " is not aligned to zone siz= e " - "%" PRId32 "", *offset / 512, bs->bl.zone_size / 512); + "%" PRId64 "", *offset / 512, bs->bl.zone_size / 512); return -EINVAL; } =20 diff --git a/block/qcow2.c b/block/qcow2.c index 81fd299b4c..b543bcf3e3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -73,6 +73,7 @@ typedef struct { #define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77 #define QCOW2_EXT_MAGIC_BITMAPS 0x23852875 #define QCOW2_EXT_MAGIC_DATA_FILE 0x44415441 +#define QCOW2_EXT_MAGIC_ZONED_FORMAT 0x007a6264 =20 static int coroutine_fn qcow2_co_preadv_compressed(BlockDriverState *bs, @@ -194,6 +195,74 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char *= fmt, Error **errp) return cryptoopts_qdict; } =20 +/* + * Passing by the zoned device configurations by a zoned_header struct, ch= eck + * if the zone device options are under constraints. Return false when some + * option is invalid + */ +static inline bool +qcow2_check_zone_options(Qcow2ZonedHeaderExtension *zone_opt, Error **errp) +{ + if (zone_opt) { + uint32_t sequential_zones; + + if (zone_opt->zone_size =3D=3D 0) { + error_setg(errp, "Zoned extension header zone_size field " + "can not be 0"); + return false; + } + + if (zone_opt->zone_capacity > zone_opt->zone_size) { + error_setg(errp, "zone capacity %" PRIu64 "B exceeds zone size= " + "%" PRIu64 "B", zone_opt->zone_capacity, + zone_opt->zone_size); + return false; + } + + if (zone_opt->max_append_bytes + BDRV_SECTOR_SIZE >=3D + zone_opt->zone_capacity) { + error_setg(errp, "max append bytes %" PRIu64 "B exceeds zone " + "capacity %" PRIu32 "B by more than block size", + zone_opt->zone_capacity, + zone_opt->max_append_bytes); + return false; + } + + if (zone_opt->conventional_zones >=3D zone_opt->nr_zones) { + error_setg(errp, "Conventional_zones %" PRIu32 " exceeds " + "nr_zones %" PRIu32 ".", + zone_opt->conventional_zones, zone_opt->nr_zones); + return false; + } + + if (zone_opt->max_active_zones > zone_opt->nr_zones) { + error_setg(errp, "Max_active_zones %" PRIu32 " exceeds " + "nr_zones %" PRIu32 ". Set it to nr_zones.", + zone_opt->max_active_zones, zone_opt->nr_zones); + zone_opt->max_active_zones =3D zone_opt->nr_zones; + } + + sequential_zones =3D zone_opt->nr_zones - zone_opt->conventional_z= ones; + if (zone_opt->max_open_zones > sequential_zones) { + error_setg(errp, "Max_open_zones field can not be larger than" + "the number of SWR zones. Set it to number of SWR" + "zones %" PRIu32 ".", sequential_zones); + zone_opt->max_open_zones =3D sequential_zones; + } + if (zone_opt->max_open_zones > zone_opt->max_active_zones) { + error_setg(errp, "Max_open_zones %" PRIu32 " exceeds " + "max_active_zones %" PRIu32 ". Set it to " + "max_active_zones.", + zone_opt->max_open_zones, + zone_opt->max_active_zones); + zone_opt->max_open_zones =3D zone_opt->max_active_zones; + } + + return true; + } + return false; +} + /* * read qcow2 extension and fill bs * start reading from start_offset @@ -211,6 +280,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t st= art_offset, uint64_t offset; int ret; Qcow2BitmapHeaderExt bitmaps_ext; + Qcow2ZonedHeaderExtension zoned_ext; =20 if (need_update_header !=3D NULL) { *need_update_header =3D false; @@ -432,6 +502,51 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t s= tart_offset, break; } =20 + case QCOW2_EXT_MAGIC_ZONED_FORMAT: + { + if (ext.len < sizeof(zoned_ext)) { + /* Missing fields */ + error_setg(errp, "zoned_ext: len=3D%" PRIu32 " too small " + "(<%zu)", ext.len, sizeof(zoned_ext)); + return -EINVAL; + } + ret =3D bdrv_pread(bs->file, offset, ext.len, &zoned_ext, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "zoned_ext: " + "Could not read ext header"); + return ret; + } + + zoned_ext.zone_size =3D be64_to_cpu(zoned_ext.zone_size); + zoned_ext.zone_capacity =3D be64_to_cpu(zoned_ext.zone_capacit= y); + zoned_ext.conventional_zones =3D + be32_to_cpu(zoned_ext.conventional_zones); + zoned_ext.nr_zones =3D be32_to_cpu(zoned_ext.nr_zones); + zoned_ext.max_open_zones =3D be32_to_cpu(zoned_ext.max_open_zo= nes); + zoned_ext.max_active_zones =3D + be32_to_cpu(zoned_ext.max_active_zones); + zoned_ext.max_append_bytes =3D + be32_to_cpu(zoned_ext.max_append_bytes); + s->zoned_header =3D zoned_ext; + + /* refuse to open broken images */ + if (zoned_ext.nr_zones !=3D DIV_ROUND_UP(bs->total_sectors * + BDRV_SECTOR_SIZE, zoned_ext.zone_size)) { + error_setg(errp, "Zoned extension header nr_zones field " + "is wrong"); + return -EINVAL; + } + if (!qcow2_check_zone_options(&zoned_ext, errp)) { + return -EINVAL; + } + +#ifdef DEBUG_EXT + printf("Qcow2: Got zoned format extension: " + "offset=3D%" PRIu32 "\n", offset); +#endif + break; + } + default: /* unknown magic - save it in case we need to rewrite the head= er */ /* If you add a new feature, make sure to also update the fast @@ -2068,6 +2183,15 @@ static void qcow2_refresh_limits(BlockDriverState *b= s, Error **errp) } bs->bl.pwrite_zeroes_alignment =3D s->subcluster_size; bs->bl.pdiscard_alignment =3D s->cluster_size; + bs->bl.zoned =3D s->zoned_header.zoned; + bs->bl.nr_zones =3D s->zoned_header.nr_zones; + bs->bl.max_append_sectors =3D s->zoned_header.max_append_bytes + >> BDRV_SECTOR_BITS; + bs->bl.max_active_zones =3D s->zoned_header.max_active_zones; + bs->bl.max_open_zones =3D s->zoned_header.max_open_zones; + bs->bl.zone_size =3D s->zoned_header.zone_size; + bs->bl.zone_capacity =3D s->zoned_header.zone_capacity; + bs->bl.write_granularity =3D BDRV_SECTOR_SIZE; } =20 static int GRAPH_UNLOCKED @@ -3170,6 +3294,11 @@ int qcow2_update_header(BlockDriverState *bs) .bit =3D QCOW2_INCOMPAT_EXTL2_BITNR, .name =3D "extended L2 entries", }, + { + .type =3D QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit =3D QCOW2_INCOMPAT_ZONED_FORMAT_BITNR, + .name =3D "zoned format", + }, { .type =3D QCOW2_FEAT_TYPE_COMPATIBLE, .bit =3D QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, @@ -3215,6 +3344,31 @@ int qcow2_update_header(BlockDriverState *bs) buflen -=3D ret; } =20 + /* Zoned devices header extension */ + if (s->zoned_header.zoned =3D=3D BLK_Z_HM) { + Qcow2ZonedHeaderExtension zoned_header =3D { + .zoned =3D s->zoned_header.zoned, + .zone_size =3D cpu_to_be64(s->zoned_header.zone_size), + .zone_capacity =3D cpu_to_be64(s->zoned_header.zone_capac= ity), + .conventional_zones =3D + cpu_to_be32(s->zoned_header.conventional_zones), + .nr_zones =3D cpu_to_be32(s->zoned_header.nr_zones), + .max_open_zones =3D cpu_to_be32(s->zoned_header.max_open_z= ones), + .max_active_zones =3D + cpu_to_be32(s->zoned_header.max_active_zones), + .max_append_bytes =3D + cpu_to_be32(s->zoned_header.max_append_bytes) + }; + ret =3D header_ext_add(buf, QCOW2_EXT_MAGIC_ZONED_FORMAT, + &zoned_header, sizeof(zoned_header), + buflen); + if (ret < 0) { + goto fail; + } + buf +=3D ret; + buflen -=3D ret; + } + /* Keep unknown header extensions */ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { ret =3D header_ext_add(buf, uext->magic, uext->data, uext->len, bu= flen); @@ -3589,6 +3743,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options= , Error **errp) ERRP_GUARD(); BlockdevCreateOptionsQcow2 *qcow2_opts; QDict *options; + Qcow2ZoneCreateOptions *zone_struct; + Qcow2ZoneHostManaged *zone_host_managed; =20 /* * Open the image file and write a minimal qcow2 header. @@ -3615,6 +3771,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options= , Error **errp) =20 assert(create_options->driver =3D=3D BLOCKDEV_DRIVER_QCOW2); qcow2_opts =3D &create_options->u.qcow2; + zone_struct =3D create_options->u.qcow2.zone; + zone_host_managed =3D &create_options->u.qcow2.zone->u.host_managed; =20 bs =3D bdrv_co_open_blockdev_ref(qcow2_opts->file, errp); if (bs =3D=3D NULL) { @@ -3828,6 +3986,14 @@ qcow2_co_create(BlockdevCreateOptions *create_option= s, Error **errp) header->incompatible_features |=3D cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE); } + if (zone_struct && zone_struct->mode =3D=3D QCOW2_ZONE_MODEL_HOST_MANA= GED) { + /* + * The incompatible bit must be set when the zone model is + * host-managed + */ + header->incompatible_features |=3D + cpu_to_be64(QCOW2_INCOMPAT_ZONED_FORMAT); + } if (qcow2_opts->data_file_raw) { header->autoclear_features |=3D cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW); @@ -3885,10 +4051,9 @@ qcow2_co_create(BlockdevCreateOptions *create_option= s, Error **errp) bdrv_graph_co_rdlock(); ret =3D qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size); if (ret < 0) { - bdrv_graph_co_rdunlock(); error_setg_errno(errp, -ret, "Could not allocate clusters for qcow= 2 " "header and refcount table"); - goto out; + goto unlock; =20 } else if (ret !=3D 0) { error_report("Huh, first cluster in empty image is already in use?= "); @@ -3896,11 +4061,62 @@ qcow2_co_create(BlockdevCreateOptions *create_optio= ns, Error **errp) } =20 /* Set the external data file if necessary */ + BDRVQcow2State *s =3D blk_bs(blk)->opaque; if (data_bs) { - BDRVQcow2State *s =3D blk_bs(blk)->opaque; s->image_data_file =3D g_strdup(data_bs->filename); } =20 + if (zone_struct && zone_struct->mode =3D=3D QCOW2_ZONE_MODEL_HOST_MANA= GED) { + s->zoned_header.zoned =3D BLK_Z_HM; + + if (!zone_host_managed->has_size) { + s->zoned_header.zone_size =3D DEFAULT_ZONE_SIZE; + } else { + s->zoned_header.zone_size =3D zone_host_managed->size; + } + s->zoned_header.nr_zones =3D DIV_ROUND_UP(qcow2_opts->size, + s->zoned_header.zone_size); + + if (zone_host_managed->has_capacity) { + s->zoned_header.zone_capacity =3D zone_host_managed->capacity; + } else { + s->zoned_header.zone_capacity =3D s->zoned_header.zone_size; + } + + if (zone_host_managed->has_conventional_zones) { + s->zoned_header.conventional_zones =3D + zone_host_managed->conventional_zones; + } + + if (zone_host_managed->has_max_active_zones) { + s->zoned_header.max_active_zones =3D + zone_host_managed->max_active_zones; + + if (zone_host_managed->has_max_open_zones) { + s->zoned_header.max_open_zones =3D + zone_host_managed->max_open_zones; + } else { + s->zoned_header.max_open_zones =3D + zone_host_managed->max_active_zones; + } + } + + if (zone_host_managed->has_max_append_bytes) { + s->zoned_header.max_append_bytes =3D + zone_host_managed->max_append_bytes; + } else { + s->zoned_header.max_append_bytes =3D DEFAULT_MAX_APPEND_BYTES; + } + + if (!qcow2_check_zone_options(&s->zoned_header, errp)) { + s->zoned_header.zoned =3D BLK_Z_NONE; + ret =3D -EINVAL; + goto unlock; + } + } else { + s->zoned_header.zoned =3D BLK_Z_NONE; + } + /* Create a full header (including things like feature table) */ ret =3D qcow2_update_header(blk_bs(blk)); bdrv_graph_co_rdunlock(); @@ -3974,6 +4190,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options= , Error **errp) } =20 ret =3D 0; + goto out; +unlock: + bdrv_graph_co_rdunlock(); out: blk_co_unref(blk); bdrv_co_unref(bs); @@ -4052,6 +4271,10 @@ qcow2_co_create_opts(BlockDriver *drv, const char *f= ilename, QemuOpts *opts, { BLOCK_OPT_COMPAT_LEVEL, "version" }, { BLOCK_OPT_DATA_FILE_RAW, "data-file-raw" }, { BLOCK_OPT_COMPRESSION_TYPE, "compression-type" }, + { BLOCK_OPT_CONVENTIONAL_ZONES, "zone.conventional-zones" }, + { BLOCK_OPT_MAX_OPEN_ZONES, "zone.max-open-zones" }, + { BLOCK_OPT_MAX_ACTIVE_ZONES, "zone.max-active-zones" }, + { BLOCK_OPT_MAX_APPEND_BYTES, "zone.max-append-bytes" }, { NULL, NULL }, }; =20 @@ -6265,6 +6488,41 @@ static QemuOptsList qcow2_create_opts =3D { .type =3D QEMU_OPT_BOOL, \ .help =3D "Assume the external data file already exists and " \ "do not overwrite it" \ + }, \ + { \ + .name =3D BLOCK_OPT_ZONE_MODEL, \ + .type =3D QEMU_OPT_STRING, \ + .help =3D "zone model modes, mode choice: host-managed", \ + }, \ + { \ + .name =3D BLOCK_OPT_ZONE_SIZE, \ + .type =3D QEMU_OPT_SIZE, \ + .help =3D "zone size", \ + }, \ + { \ + .name =3D BLOCK_OPT_ZONE_CAPACITY, \ + .type =3D QEMU_OPT_SIZE, \ + .help =3D "zone capacity", \ + }, \ + { \ + .name =3D BLOCK_OPT_CONVENTIONAL_ZONES, \ + .type =3D QEMU_OPT_NUMBER, \ + .help =3D "numbers of conventional zones", \ + }, \ + { \ + .name =3D BLOCK_OPT_MAX_APPEND_BYTES, \ + .type =3D QEMU_OPT_SIZE, \ + .help =3D "max append bytes", \ + }, \ + { \ + .name =3D BLOCK_OPT_MAX_ACTIVE_ZONES, \ + .type =3D QEMU_OPT_NUMBER, \ + .help =3D "max active zones", \ + }, \ + { \ + .name =3D BLOCK_OPT_MAX_OPEN_ZONES, \ + .type =3D QEMU_OPT_NUMBER, \ + .help =3D "max open zones", \ }, QCOW_COMMON_OPTIONS, { /* end of list */ } diff --git a/block/qcow2.h b/block/qcow2.h index 192a45d596..743f2e3e79 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -129,6 +129,9 @@ =20 #define DEFAULT_CLUSTER_SIZE 65536 =20 +#define DEFAULT_ZONE_SIZE (256 * MiB) +#define DEFAULT_MAX_APPEND_BYTES (64 * KiB) + #define QCOW2_OPT_DATA_FILE "data-file" #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" @@ -237,6 +240,27 @@ typedef struct Qcow2CryptoHeaderExtension { uint64_t length; } QEMU_PACKED Qcow2CryptoHeaderExtension; =20 +typedef struct Qcow2ZonedHeaderExtension { + /* Zoned device attributes */ + uint8_t zoned; + uint8_t reserved[3]; + uint64_t zone_size; + uint64_t zone_capacity; + uint32_t conventional_zones; + uint32_t nr_zones; + uint32_t max_active_zones; + uint32_t max_open_zones; + uint32_t max_append_bytes; + uint64_t zonedmeta_size; + uint64_t zonedmeta_offset; +} QEMU_PACKED Qcow2ZonedHeaderExtension; + +typedef struct Qcow2ZoneListEntry { + QLIST_ENTRY(Qcow2ZoneListEntry) exp_open_zone_entry; + QLIST_ENTRY(Qcow2ZoneListEntry) imp_open_zone_entry; + QLIST_ENTRY(Qcow2ZoneListEntry) closed_zone_entry; +} Qcow2ZoneListEntry; + typedef struct Qcow2UnknownHeaderExtension { uint32_t magic; uint32_t len; @@ -257,17 +281,20 @@ enum { QCOW2_INCOMPAT_DATA_FILE_BITNR =3D 2, QCOW2_INCOMPAT_COMPRESSION_BITNR =3D 3, QCOW2_INCOMPAT_EXTL2_BITNR =3D 4, + QCOW2_INCOMPAT_ZONED_FORMAT_BITNR =3D 5, QCOW2_INCOMPAT_DIRTY =3D 1 << QCOW2_INCOMPAT_DIRTY_BITNR, QCOW2_INCOMPAT_CORRUPT =3D 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, QCOW2_INCOMPAT_DATA_FILE =3D 1 << QCOW2_INCOMPAT_DATA_FILE_BITN= R, QCOW2_INCOMPAT_COMPRESSION =3D 1 << QCOW2_INCOMPAT_COMPRESSION_BI= TNR, QCOW2_INCOMPAT_EXTL2 =3D 1 << QCOW2_INCOMPAT_EXTL2_BITNR, + QCOW2_INCOMPAT_ZONED_FORMAT =3D 1 << QCOW2_INCOMPAT_ZONED_FORMAT_B= ITNR, =20 QCOW2_INCOMPAT_MASK =3D QCOW2_INCOMPAT_DIRTY | QCOW2_INCOMPAT_CORRUPT | QCOW2_INCOMPAT_DATA_FILE | QCOW2_INCOMPAT_COMPRESSION - | QCOW2_INCOMPAT_EXTL2, + | QCOW2_INCOMPAT_EXTL2 + | QCOW2_INCOMPAT_ZONED_FORMAT, }; =20 /* Compatible feature bits */ @@ -426,6 +453,9 @@ typedef struct BDRVQcow2State { * is to convert the image with the desired compression type set. */ Qcow2CompressionType compression_type; + + /* States of zoned device */ + Qcow2ZonedHeaderExtension zoned_header; } BDRVQcow2State; =20 typedef struct Qcow2COWRegion { diff --git a/docs/interop/qcow2.rst b/docs/interop/qcow2.rst index 5948591107..7bf3a00e77 100644 --- a/docs/interop/qcow2.rst +++ b/docs/interop/qcow2.rst @@ -128,7 +128,26 @@ the next fields through ``header_length``. allows subcluster-based allocation. See the Extended L2 Entries section for more detai= ls. =20 - Bits 5-63: Reserved (set to 0) + Bit 5: Zoned extension bit. If this bit is set th= en + the file is an emulated zoned device. The + zoned extension must be present. + Implementations that do not support zoned + emulation cannot open this file because it + generally only make sense to interpret the + data along with the zone information and + write pointers. + + It is unsafe when any qcow2 user without + knowing the zoned extension reads or edits + a file with the zoned extension. The write + pointer tracking can be corrupted when a + writer edits a file, like overwriting beyo= nd + the write pointer locations. Or a reader t= ries + to access a file without knowing write + pointers where the software setup will cau= se + invalid reads. + + Bits 6-63: Reserved (set to 0) =20 80 - 87: compatible_features Bitmask of compatible features. An implementation can @@ -259,6 +278,7 @@ be stored. Each extension has a structure like the foll= owing:: 0x23852875 - Bitmaps extension 0x0537be77 - Full disk encryption header pointer 0x44415441 - External data file name string + 0x007a6264 - Zoned extension other - Unknown header extension, can be safe= ly ignored =20 @@ -344,6 +364,94 @@ The fields of the bitmaps extension are:: Offset into the image file at which the bitmap directory starts. Must be aligned to a cluster boundary. =20 +Zoned extension +--------------- + +The zoned extension must be present if the incompatible bit is set, and +omitted when the incompatible bit is clear. It contains fields for +emulating the zoned storage model (https://zonedstorage.io/). When the +zone model mode is not host-managed, it is regarded as incompatible +and reports an error to users. + +The write pointers for each zone are stored in an area called zonedmeta +clusters. It is 8 bytes per zone. The offset and size of the zonedmeta +are kept in the zoned extension header. + +The fields of the zoned extension are:: + + Byte 0: zoned + The byte represents the zoned model of the device. 0 is= for + a non-zoned device (all other information in this header + is ignored). 1 is for a host-managed device, which only + allows for sequential writes within each zone. Other + values may be added later, the implementation must refu= se + to open a device containing an unknown zone model. + + 1 - 3: Reserved, must be zero. + + 4 - 11: zone_size + Total size of each zone, in bytes. The 64-bit field is = to + satisfy the virtio-blk zone_size range and emulate a re= ad + zoned device, whose maximum zone size can be as large as + 2TB. + + The value must be power of 2. Linux currently requires + the zone size to be a power of 2 number of LBAs. Qcow2 + following this is mainly to allow emulating a real + ZNS drive configuration. It is not relevant to the clus= ter + size. + + 12 - 19: zone_capacity + The number of writable bytes within the zones. The bytes + between zone capacity and zone size are unusable: reads + will return 0s and writes will fail. + + A zone capacity is always smaller or equal to the zone + size. It is for emulating a real ZNS drive configuratio= n, + which has the constraint of aligning to some hardware e= rase + block size. + + 20 - 23: conventional_zones + The number of conventional zones. The conventional zones + allow sequential writes and random writes. While the + sequential zones only allow sequential writes. + + 24 - 27: nr_zones + The number of zones. It is the sum of conventional zones + and sequential zones. The maximum value for nr_zones is + (2^32 - 1)/8 =3D 536870911. + + 28 - 31: max_active_zones + The number of the zones that can be in the implicit ope= n, + explicit open or closed state. The max active zones can= not + be larger than the max open zones. + + 32 - 35: max_open_zones + The maximal number of open (implicitly open or explicit= ly + open) zones. It cannot be larger than the number of SWR + zones of the device. + + If the limits of open zones or active zones are equal to + the total number of SWR zones, then it's the same as ha= ving + no limits therefore max open zones and max active zones= are + set to 0. + + 36 - 39: max_append_bytes + The number of bytes of a zone append request that can be + issued to the device. It must be 512-byte aligned and l= ess + than the zone capacity. + + 40 - 47: zonedmeta_size + The size of zoned metadata in bytes. It contains no more + than 4GB. The zoned metadata structure is the write + pointers for each zone whose size is the number of zones + multiplied by the zone size. + + 48 - 55: zonedmeta_offset + The offset of zoned metadata structure in the contained + image, in bytes. + + Full disk encryption header pointer ----------------------------------- =20 diff --git a/include/block/block_int-common.h b/include/block/block_int-com= mon.h index 147c08155f..349876bc65 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -57,6 +57,13 @@ #define BLOCK_OPT_COMPRESSION_TYPE "compression_type" #define BLOCK_OPT_EXTL2 "extended_l2" #define BLOCK_OPT_KEEP_DATA_FILE "keep_data_file" +#define BLOCK_OPT_ZONE_MODEL "zone.mode" +#define BLOCK_OPT_ZONE_SIZE "zone.size" +#define BLOCK_OPT_ZONE_CAPACITY "zone.capacity" +#define BLOCK_OPT_CONVENTIONAL_ZONES "zone.conventional_zones" +#define BLOCK_OPT_MAX_APPEND_BYTES "zone.max_append_bytes" +#define BLOCK_OPT_MAX_ACTIVE_ZONES "zone.max_active_zones" +#define BLOCK_OPT_MAX_OPEN_ZONES "zone.max_open_zones" =20 #define BLOCK_PROBE_BUF_SIZE 512 =20 @@ -901,7 +908,13 @@ typedef struct BlockLimits { BlockZoneModel zoned; =20 /* zone size expressed in bytes */ - uint32_t zone_size; + uint64_t zone_size; + + /* + * The number of usable logical blocks within the zone, expressed + * in bytes. A zone capacity is smaller or equal to the zone size. + */ + uint64_t zone_capacity; =20 /* total number of zones */ uint32_t nr_zones; diff --git a/qapi/block-core.json b/qapi/block-core.json index 508b081ac1..d771dfb4a1 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -5268,6 +5268,70 @@ { 'enum': 'Qcow2CompressionType', 'data': [ 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } =20 +## +# @Qcow2ZoneModel: +# +# Zoned device model used in qcow2 image file +# +# @host-managed: The host-managed model only allows sequential write +# over the device zones. +# +# Since 11.0 +## +{ 'enum': 'Qcow2ZoneModel', + 'data': [ 'host-managed'] } + +## +# @Qcow2ZoneHostManaged: +# +# The host-managed zone model. It only allows sequential writes. +# +# @size: Total number of bytes within zones (default 256 MB). +# +# @capacity: The number of usable logical blocks within zones +# in bytes. A zone capacity is always smaller or equal to the +# zone size (default to zone size). +# +# @conventional-zones: The number of conventional zones of the +# zoned device (default 0). +# +# @max-open-zones: The maximal number of open zones. It is less than +# or equal to the number of sequential write required zones of +# the device (default 0). +# +# @max-active-zones: The maximal number of zones in the implicit +# open, explicit open or closed state. It is less than or equal +# to the max open zones (default 0). +# +# @max-append-bytes: The maximal number of bytes of a zone +# append request that can be issued to the device. It must be +# 512-byte aligned and less than the zone capacity +# (default 64 KB). +# +# Since 11.0 +## +{ 'struct': 'Qcow2ZoneHostManaged', + 'data': { '*size': 'size', + '*capacity': 'size', + '*conventional-zones': 'uint32', + '*max-open-zones': 'uint32', + '*max-active-zones': 'uint32', + '*max-append-bytes': 'size' } } + +## +# @Qcow2ZoneCreateOptions: +# +# The zone device model for the qcow2 image. +# +# @mode: The zone device model modes. +# +# Since 11.0 +## +{ 'union': 'Qcow2ZoneCreateOptions', + 'base': { 'mode': 'Qcow2ZoneModel' }, + 'discriminator': 'mode', + 'data': { 'host-managed': 'Qcow2ZoneHostManaged' } } + ## # @BlockdevCreateOptionsQcow2: # @@ -5310,6 +5374,9 @@ # @compression-type: The image cluster compression method # (default: zlib, since 5.1) # +# @zone: The zone device model modes. The default is that the +# device is not zoned. (since 11.0) +# # Since: 2.12 ## { 'struct': 'BlockdevCreateOptionsQcow2', @@ -5326,7 +5393,8 @@ '*preallocation': 'PreallocMode', '*lazy-refcounts': 'bool', '*refcount-bits': 'int', - '*compression-type':'Qcow2CompressionType' } } + '*compression-type':'Qcow2CompressionType', + '*zone': 'Qcow2ZoneCreateOptions' } } =20 ## # @BlockdevCreateOptionsQed: --=20 2.43.0 From nobody Sun May 24 23:37:32 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=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1778435568; cv=none; d=zohomail.com; s=zohoarc; b=NV4CXMYdyWIq7SUbbhU5g6KTes6+jAjcrNcW6QybUjGyxlqW9VJd+wbxoCap1/UnXRKToldMVuu8Vltc0fakg9qZhI0WmliXupHC/ukEuSC5vRLysycU13ep1BDnJAIROFY6sz2YaYZp3rwUJYqCYKYGp8bnmAGrXgMWittyeTU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778435568; 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=6asQtjSOrATSZvEtJu9LWCvOtIx9fUaOu0uUsKhRx3I=; b=OyFteotj/24FaJUdmGjUtSOsO9iN2MQu1kYKcGb/C7Ee/RqzBvz6ApeBOGXSOpkoln6bIVctIWBWDxw+KcOPh78tdmbPcaVHe2ZObFxickSMVqrRd4e6W79g+E3l0ldUSCv2LeLlVjMYNolyx2L2NOFygb1PZhaeAYkGd70hJSI= 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 1778435568443389.2739904642932; Sun, 10 May 2026 10:52:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wM8J9-0000NJ-IM; Sun, 10 May 2026 13:51: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 1wM8J7-0000MF-GZ for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:13 -0400 Received: from mail-wm1-x336.google.com ([2a00:1450:4864:20::336]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wM8J2-0001iH-Re for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:13 -0400 Received: by mail-wm1-x336.google.com with SMTP id 5b1f17b1804b1-488af96f6b2so41067395e9.0 for ; Sun, 10 May 2026 10:51:08 -0700 (PDT) Received: from groves.. ([95.90.240.94]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e702e0bf2sm225215565e9.4.2026.05.10.10.51.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 10:51:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778435467; x=1779040267; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6asQtjSOrATSZvEtJu9LWCvOtIx9fUaOu0uUsKhRx3I=; b=B0gciJe0PwXIQCt9Za7JUlyvQU1RaDpVnIbGncVPv8RcFlPE7PGgIPsqIR/3Oyg3rM 76/bBKyAjVLoLp3QGwGP0oePIfzcebZhZ+HKThcxV7PQ9YQBZ+eI1B3kEkJU+icFtIbZ fA/Mhq+Muz+X71XICIn2CyYuXiSoVcfDZiD4welz3pjMU8qdsXeM/l9kJFWrRr5g/Rpg MAiWVVNITJELZdc5EUQZD1LwqG+VApMqwU3xCz0FS8rqUhL5KXehmm6owfYyLu2rkHzy RNAlJmJRTjo4LylnSPpy2U3tQouPposztBAtDcWFxGuQUg9XXygk7GYzjdUuGlP5qSqg xEjg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778435467; x=1779040267; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=6asQtjSOrATSZvEtJu9LWCvOtIx9fUaOu0uUsKhRx3I=; b=IO6FCLoTLC/5ZFFzJxS6ZGVWsslGkfK6Dh3Q+CcG+zkwqVuh6bq/XxisRptZdgq91i JrbhdS5yrsRcdRQL6WK6OdZ9e41ymGMbHvDS/4hrdggdOJVvYErjHn/IV/2flY2tqdSW cfc/tdKl+7hdtL+3jcVfOeDl2LPw7lILqk1SBE95ndr7GtIgQXIV9xkLweAp3CaMshL4 UgK8d4vaUiGGRNxCBdGNGT/QJ8Xrqldzg+/hXLKsq6IWyeWy6R+zeLWb9zgUqDvIuG7C BCR9J1ZoeCXY6FjexlOAPQ9XNR77MXLjPuJn7BjxMb5Y5RJJTpI3NqTBMXMoUsk4NE+N UycA== X-Gm-Message-State: AOJu0YzULConzQtdsjuBuViZyD1E5mtNav3yUIC1L9AO4beRI0vONIGs 7wa9PFe0z8hufXtlOE9uWQxlamXtcks0gPOUyg5QBoxghMq410Ok6EWOdWl9/w== X-Gm-Gg: Acq92OFnpW7q8IRE7UZxR0KhBcOoB2yPyaDqQe/GB6y4wouDzrCeXs16PWoOGGBUGk4 +Ol8w/E9GknnCdMgQTkSxSd58WwBcZtga3L3hVYHoRwAjoF8CRgMLkKD9m8mygxc+pzyHP+cjCC Vld3526t/kvuI8NcpRW3SkDDvjH4FlrQNXVZtvj0VarDVafUbJAkZJ7KPg4wHE5MXmfbBvHLN/t 2LpXQQvjq9Ncz5p+1rTjI2mqRiZmUnc9N+CfeQ5Dy6EHFVyMPHKys+8lLMQXoKKkVWZME3XwHVf FMpqsQotU+58qDMdDWy4LYRpgl8P0naNzs+rG7dVZjVXxFbvi2iSqXRFVpV1iBR78AYUSOA85oe sv+Tzo/RyLsTB8FNL76kcTo9C0IVNgGrRxb0l+x1hyCieVdhTYkacLWfa4+BvE3HUfZRcFJ7CDv 15Zz+sgIx0g5zv9slaOBhz03im7MwMvytcSN04tjvMB6pkcvnIjm2S X-Received: by 2002:a05:600c:8286:b0:485:3cf3:1010 with SMTP id 5b1f17b1804b1-48e706ad01emr111276075e9.2.1778435466690; Sun, 10 May 2026 10:51:06 -0700 (PDT) From: Sam Li To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , dmitry.fomichev@wdc.com, Kevin Wolf , cassel@kernel.org, Markus Armbruster , Stefan Hajnoczi , qemu-block@nongnu.org, dlemoal@kernel.org, Eric Blake , "Michael S. Tsirkin" , hare@suse.de, Hanna Reitz , Sam Li Subject: [PATCH v10 3/4] qcow2: add zoned emulation capability Date: Sun, 10 May 2026 19:50:58 +0200 Message-ID: <20260510175059.311814-4-faithilikerun@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260510175059.311814-1-faithilikerun@gmail.com> References: <20260510175059.311814-1-faithilikerun@gmail.com> 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=2a00:1450:4864:20::336; envelope-from=faithilikerun@gmail.com; helo=mail-wm1-x336.google.com 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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @gmail.com) X-ZM-MESSAGEID: 1778435570356158500 Content-Type: text/plain; charset="utf-8" By adding zone operations and zoned metadata, the zoned emulation capability enables full emulation support of zoned device using a qcow2 file. The zoned device metadata includes zone type, zoned device state and write pointer of each zone, which is stored to an array of unsigned integers. Each zone of a zoned device makes state transitions following the zone state machine. The zone state machine mainly describes five states, IMPLICIT OPEN, EXPLICIT OPEN, FULL, EMPTY and CLOSED. READ ONLY and OFFLINE states will generally be affected by device internal events. The operations on zones cause corresponding state changing. Zoned devices have limits on zone resources, which put constraints on write operations on zones. It is managed by active zone queues following LRU policy. Signed-off-by: Sam Li --- block/qcow2.c | 851 +++++++++++++++++++++++++++++++++++++++++- block/qcow2.h | 13 +- block/trace-events | 2 + hw/block/virtio-blk.c | 22 +- include/qemu/queue.h | 1 + include/qemu/range.h | 4 + 6 files changed, 884 insertions(+), 9 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index b543bcf3e3..c84b67976e 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -195,6 +195,293 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char = *fmt, Error **errp) return cryptoopts_qdict; } =20 +#define QCOW2_ZT_IS_CONV(wp) (wp & 1ULL << 59) +#define QCOW2_GET_WP(wp) ((wp << 5) >> 5) + +/* + * To emulate a real zoned device, closed, empty and full states are + * preserved after a power cycle. The open states are in-memory and will + * be lost after closing the device. Read-only and offline states are + * device-internal events, which are not considered for simplicity. + */ +static inline BlockZoneState qcow2_get_zone_state(BlockDriverState *bs, + uint32_t index) +{ + BDRVQcow2State *s =3D bs->opaque; + Qcow2ZoneListEntry *zone_entry =3D &s->zone_list_entries[index]; + uint64_t zone_wp =3D bs->wps->wp[index]; + uint64_t zone_start; + + if (QCOW2_ZT_IS_CONV(zone_wp)) { + return BLK_ZS_NOT_WP; + } + + if (QTAILQ_IN_USE(zone_entry, exp_open_zone_entry)) { + return BLK_ZS_EOPEN; + } + if (QTAILQ_IN_USE(zone_entry, imp_open_zone_entry)) { + return BLK_ZS_IOPEN; + } + + zone_start =3D index * bs->bl.zone_size; + if (zone_wp =3D=3D zone_start) { + return BLK_ZS_EMPTY; + } + if (zone_wp >=3D zone_start + bs->bl.zone_capacity) { + return BLK_ZS_FULL; + } + if (zone_wp > zone_start) { + if (!QTAILQ_IN_USE(zone_entry, closed_zone_entry)) { + /* + * The number of closed zones is not always updated in time wh= en + * the device is closed. However, it only matters when doing + * zone report. Refresh the count and list of closed zones to + * provide correct zone states for zone report. + */ + QTAILQ_INSERT_HEAD(&s->closed_zones, zone_entry, closed_zone_e= ntry); + s->nr_zones_closed++; + } + return BLK_ZS_CLOSED; + } + return BLK_ZS_NOT_WP; +} + +static void qcow2_rm_exp_open_zone(BDRVQcow2State *s, + uint32_t index) +{ + Qcow2ZoneListEntry *zone_entry =3D &s->zone_list_entries[index]; + + QTAILQ_REMOVE(&s->exp_open_zones, zone_entry, exp_open_zone_entry); + s->nr_zones_exp_open--; +} + +static void qcow2_rm_imp_open_zone(BDRVQcow2State *s, + int32_t index) +{ + Qcow2ZoneListEntry *zone_entry; + if (index < 0) { + /* Apply LRU when the index is not specified. */ + zone_entry =3D QTAILQ_LAST(&s->imp_open_zones); + } else { + zone_entry =3D &s->zone_list_entries[index]; + } + + QTAILQ_REMOVE(&s->imp_open_zones, zone_entry, imp_open_zone_entry); + s->nr_zones_imp_open--; +} + +static void qcow2_rm_open_zone(BDRVQcow2State *s, + uint32_t index) +{ + Qcow2ZoneListEntry *zone_entry =3D &s->zone_list_entries[index]; + + if (QTAILQ_IN_USE(zone_entry, exp_open_zone_entry)) { + qcow2_rm_exp_open_zone(s, index); + } else if (QTAILQ_IN_USE(zone_entry, imp_open_zone_entry)) { + qcow2_rm_imp_open_zone(s, index); + } +} + +static void qcow2_rm_closed_zone(BDRVQcow2State *s, + uint32_t index) +{ + Qcow2ZoneListEntry *zone_entry =3D &s->zone_list_entries[index]; + + QTAILQ_REMOVE(&s->closed_zones, zone_entry, closed_zone_entry); + s->nr_zones_closed--; +} + +static void qcow2_do_imp_open_zone(BDRVQcow2State *s, + uint32_t index, + BlockZoneState zs) +{ + Qcow2ZoneListEntry *zone_entry =3D &s->zone_list_entries[index]; + + switch (zs) { + case BLK_ZS_EMPTY: + break; + case BLK_ZS_CLOSED: + qcow2_rm_closed_zone(s, index); + break; + case BLK_ZS_IOPEN: + /* + * The LRU policy: update the zone that is most recently + * used to the head of the zone list + */ + if (zone_entry =3D=3D QTAILQ_FIRST(&s->imp_open_zones)) { + return; + } + QTAILQ_REMOVE(&s->imp_open_zones, zone_entry, imp_open_zone_entry); + s->nr_zones_imp_open--; + break; + default: + return; + } + + QTAILQ_INSERT_HEAD(&s->imp_open_zones, zone_entry, imp_open_zone_entry= ); + s->nr_zones_imp_open++; +} + +static void qcow2_do_exp_open_zone(BDRVQcow2State *s, + uint32_t index) +{ + Qcow2ZoneListEntry *zone_entry =3D &s->zone_list_entries[index]; + + QTAILQ_INSERT_HEAD(&s->exp_open_zones, zone_entry, exp_open_zone_entry= ); + s->nr_zones_exp_open++; +} + +/* + * The list of zones is managed using an LRU policy: the last + * zone of the list is always the one that was least recently used + * for writing and is chosen as the zone to close to be able to + * implicitly open another zone. + * + * We can only close the open zones. The index is not specified + * when it is less than 0. + */ +static void qcow2_do_close_zone(BlockDriverState *bs, + int32_t index, + BlockZoneState zs) +{ + BDRVQcow2State *s =3D bs->opaque; + Qcow2ZoneListEntry *zone_entry; + + if (index >=3D 0) { + zone_entry =3D &s->zone_list_entries[index]; + } else { + /* before removal of the last implicitly open zone */ + zone_entry =3D QTAILQ_LAST(&s->imp_open_zones); + } + + if (zs =3D=3D BLK_ZS_IOPEN) { + qcow2_rm_imp_open_zone(s, index); + goto close_zone; + } + + if (index >=3D 0 && zs =3D=3D BLK_ZS_EOPEN) { + qcow2_rm_exp_open_zone(s, index); + /* + * The zone state changes when the zone is removed from the list of + * open zones (explicitly open -> empty). The closed zone list is + * refreshed during get_zone_state(). + */ + qcow2_get_zone_state(bs, index); + } + return; + +close_zone: + QTAILQ_INSERT_HEAD(&s->closed_zones, zone_entry, closed_zone_entry); + s->nr_zones_closed++; +} + +/* + * Read/Write the new wp value to the dedicated location of the image file. + */ +static int coroutine_fn GRAPH_RDLOCK +qcow2_rw_wp_at(BlockDriverState *bs, uint64_t *wp, + int32_t index, bool is_write) { + BDRVQcow2State *s =3D bs->opaque; + g_autofree uint64_t *temp =3D NULL; + uint64_t wpv =3D *wp; + int ret; + + if (is_write) { + ret =3D bdrv_pwrite(bs->file, s->zoned_header.zonedmeta_offset + + sizeof(uint64_t) * index, sizeof(uint64_t), wp= , 0); + if (ret < 0) { + *wp =3D wpv; + goto exit; + } + } else { + temp =3D g_new(uint64_t, 1); + ret =3D bdrv_pread(bs->file, s->zoned_header.zonedmeta_offset + + sizeof(uint64_t) * index, sizeof(uint64_t), tem= p, 0); + if (ret < 0) { + goto exit; + } + + *wp =3D *temp; + } + + trace_qcow2_wp_tracking(index, *wp >> BDRV_SECTOR_BITS); + return ret; + +exit: + error_report("Failed to %s metadata to file", is_write ? "write" : "re= ad"); + return ret; +} + +static bool qcow2_can_activate_zone(BlockDriverState *bs) +{ + BDRVQcow2State *s =3D bs->opaque; + + /* When the max active zone is zero, there is no limit on active zones= */ + if (!s->zoned_header.max_active_zones) { + return true; + } + + /* Active zones are zones that are open or closed */ + return s->nr_zones_exp_open + s->nr_zones_imp_open + s->nr_zones_closed + < s->zoned_header.max_active_zones; +} + +/* + * This function manages open zones under active zones limit. It checks + * if a zone can transition to open state while maintaining max open and + * active zone limits. + */ +static bool qcow2_can_open_zone(BlockDriverState *bs) +{ + BDRVQcow2State *s =3D bs->opaque; + + /* When the max open zone is zero, there is no limit on open zones */ + if (!s->zoned_header.max_open_zones) { + return true; + } + + /* + * The open zones are zones with the states of explicitly and + * implicitly open. + */ + if (s->nr_zones_imp_open + s->nr_zones_exp_open < + s->zoned_header.max_open_zones) { + return true; + } + + /* + * Zones are managed one at a time. Thus, the number of implicitly open + * zone can never be over the open zone limit. When the active zone li= mit + * is not reached, close only one implicitly open zone. + */ + if (qcow2_can_activate_zone(bs)) { + qcow2_do_close_zone(bs, -1, BLK_ZS_IOPEN); + trace_qcow2_imp_open_zones(0x23, s->nr_zones_imp_open); + return true; + } + return false; +} + +static inline int coroutine_fn GRAPH_RDLOCK +qcow2_refresh_zonedmeta(BlockDriverState *bs) +{ + int ret; + BDRVQcow2State *s =3D bs->opaque; + uint64_t wps_size =3D s->zoned_header.zonedmeta_size; + g_autofree uint64_t *temp =3D NULL; + + temp =3D g_new(uint64_t, s->zoned_header.nr_zones); + ret =3D bdrv_pread(bs->file, s->zoned_header.zonedmeta_offset, + wps_size, temp, 0); + if (ret < 0) { + error_report("Cannot read metadata"); + return ret; + } + + memcpy(bs->wps->wp, temp, wps_size); + return 0; +} + /* * Passing by the zoned device configurations by a zoned_header struct, ch= eck * if the zone device options are under constraints. Return false when some @@ -527,7 +814,23 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t s= tart_offset, be32_to_cpu(zoned_ext.max_active_zones); zoned_ext.max_append_bytes =3D be32_to_cpu(zoned_ext.max_append_bytes); + zoned_ext.zonedmeta_offset =3D + be64_to_cpu(zoned_ext.zonedmeta_offset); + zoned_ext.zonedmeta_size =3D be64_to_cpu(zoned_ext.zonedmeta_s= ize); s->zoned_header =3D zoned_ext; + bs->wps =3D g_malloc(sizeof(BlockZoneWps) + + s->zoned_header.zonedmeta_size); + ret =3D qcow2_refresh_zonedmeta(bs); + if (ret < 0) { + return ret; + } + + s->zone_list_entries =3D g_new0(Qcow2ZoneListEntry, + zoned_ext.nr_zones); + QTAILQ_INIT(&s->exp_open_zones); + QTAILQ_INIT(&s->imp_open_zones); + QTAILQ_INIT(&s->closed_zones); + qemu_co_mutex_init(&bs->wps->colock); =20 /* refuse to open broken images */ if (zoned_ext.nr_zones !=3D DIV_ROUND_UP(bs->total_sectors * @@ -2883,21 +3186,119 @@ static coroutine_fn GRAPH_RDLOCK int qcow2_co_pwri= tev_task_entry(AioTask *task) t->l2meta); } =20 +/* + * If it is an append write request, the offset pointer needs to be update= d to + * the wp value of that zone after the IO completion. The unique pointer is + * passed on to this function to prevent the value being changed in condit= ion of + * multiple concurrent writes. + */ static int coroutine_fn GRAPH_RDLOCK -qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags) +qcow2_co_pwv_part(BlockDriverState *bs, int64_t *offset_ptr, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, bool is_append, + BdrvRequestFlags flags) { BDRVQcow2State *s =3D bs->opaque; int offset_in_cluster; int ret; unsigned int cur_bytes; /* number of sectors in current iteration */ uint64_t host_offset; + int64_t offset =3D *offset_ptr; QCowL2Meta *l2meta =3D NULL; AioTaskPool *aio =3D NULL; + int64_t start_offset, start_bytes; + BlockZoneState zs; + int64_t end_zone, end_offset; + uint64_t *wp; + int64_t zone_size =3D bs->bl.zone_size; + int64_t zone_capacity =3D bs->bl.zone_capacity; + int index; =20 trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); =20 + start_offset =3D offset; + start_bytes =3D bytes; + if (bs->bl.zoned =3D=3D BLK_Z_HM) { + index =3D start_offset / zone_size; + wp =3D &bs->wps->wp[index]; + if (!QCOW2_ZT_IS_CONV(*wp)) { + if (offset !=3D *wp && !is_append) { + /* The write offset must be equal to the zone write pointe= r */ + error_report("Offset 0x%" PRIx64 " of regular writes must = be " + "equal to the zone write pointer 0x%" PRIx64 = "", + offset, *wp); + return -EINVAL; + } + + if (is_append) { + /* + * The offset of append write is the write pointer value of + * that zone. + */ + start_offset =3D *wp; + } + + end_offset =3D start_offset + start_bytes; + + /* Only allow writes when there are zone resources left */ + zs =3D qcow2_get_zone_state(bs, index); + if (zs =3D=3D BLK_ZS_CLOSED || zs =3D=3D BLK_ZS_EMPTY) { + if (!qcow2_can_open_zone(bs)) { + error_report("no more open zones available"); + return -EINVAL; + } + } + + /* + * Align up (start_offset, zone_size), the start offset is not + * necessarily power of two. + */ + end_zone =3D index * zone_size + zone_capacity; + /* Write cannot exceed the zone capacity. */ + if (end_offset > end_zone) { + error_report("write exceeds zone capacity with end_offset:" + "0x%lx, end_zone: 0x%lx", + end_offset / 512, end_zone / 512); + return -EINVAL; + } + + /* + * Real drives change states before it can write to the zone. = If + * the write fails, the zone state may have changed. + * + * The zone state transitions to implicit open when the origin= al + * state is empty or closed. When the wp reaches the end, the + * open states (explicit open, implicit open) become full. + */ + zs =3D qcow2_get_zone_state(bs, index); + if (!(end_offset & (zone_capacity - 1))) { + /* Being aligned to zone capacity implies full state */ + qcow2_rm_open_zone(s, index); + trace_qcow2_imp_open_zones(0x24, + s->nr_zones_imp_open); + } else { + qcow2_do_imp_open_zone(s, index, zs); + trace_qcow2_imp_open_zones(0x24, + s->nr_zones_imp_open); + } + + /* + * The write pointer is update before IO completion, with the + * assumption that the write IO will succeed. + */ + qemu_co_mutex_lock(&bs->wps->colock); + if (is_append) { + *offset_ptr =3D *wp; + } + *wp =3D end_offset; + ret =3D qcow2_rw_wp_at(bs, wp, index, true); + qemu_co_mutex_unlock(&bs->wps->colock); + if (ret < 0) { + error_report("failed to update write pointer"); + return -EINVAL; + } + } + } + while (bytes !=3D 0 && aio_task_pool_status(aio) =3D=3D 0) { =20 l2meta =3D NULL; @@ -2943,6 +3344,7 @@ qcow2_co_pwritev_part(BlockDriverState *bs, int64_t o= ffset, int64_t bytes, qiov_offset +=3D cur_bytes; trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); } + ret =3D 0; =20 qemu_co_mutex_lock(&s->lock); @@ -2961,11 +3363,32 @@ fail_nometa: g_free(aio); } =20 + if (ret < 0 && bs->bl.zoned =3D=3D BLK_Z_HM) { + /* update the wp when write IO failed */ + qemu_co_mutex_lock(&bs->wps->colock); + index =3D start_offset / zone_size; + wp =3D &bs->wps->wp[index]; + if (!QCOW2_ZT_IS_CONV(*wp)) { + ret =3D qcow2_rw_wp_at(bs, wp, index, false); + } + qemu_co_mutex_unlock(&bs->wps->colock); + } + trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); =20 return ret; } =20 +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) +{ + return qcow2_co_pwv_part(bs, &offset, bytes, qiov, qiov_offset, false, + flags); +} + + static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) { BDRVQcow2State *s =3D bs->opaque; @@ -3001,6 +3424,25 @@ static int GRAPH_RDLOCK qcow2_inactivate(BlockDriver= State *bs) return result; } =20 +static void qcow2_do_close_all_zone(BDRVQcow2State *s) +{ + Qcow2ZoneListEntry *zone_entry, *next; + + QTAILQ_FOREACH_SAFE(zone_entry, &s->imp_open_zones, imp_open_zone_entr= y, + next) { + QTAILQ_REMOVE(&s->imp_open_zones, zone_entry, imp_open_zone_entry); + s->nr_zones_imp_open--; + } + + QTAILQ_FOREACH_SAFE(zone_entry, &s->exp_open_zones, exp_open_zone_entr= y, + next) { + QTAILQ_REMOVE(&s->exp_open_zones, zone_entry, exp_open_zone_entry); + s->nr_zones_exp_open--; + } + + assert(s->nr_zones_imp_open + s->nr_zones_exp_open =3D=3D 0); +} + static void coroutine_mixed_fn GRAPH_RDLOCK qcow2_do_close(BlockDriverState *bs, bool close_data_file) { @@ -3040,6 +3482,8 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_= file) =20 qcow2_refcount_close(bs); qcow2_free_snapshots(bs); + qcow2_do_close_all_zone(s); + g_free(bs->wps); } =20 static void GRAPH_UNLOCKED qcow2_close(BlockDriverState *bs) @@ -3357,7 +3801,10 @@ int qcow2_update_header(BlockDriverState *bs) .max_active_zones =3D cpu_to_be32(s->zoned_header.max_active_zones), .max_append_bytes =3D - cpu_to_be32(s->zoned_header.max_append_bytes) + cpu_to_be32(s->zoned_header.max_append_bytes), + .zonedmeta_offset =3D + cpu_to_be64(s->zoned_header.zonedmeta_offset), + .zonedmeta_size =3D cpu_to_be64(s->zoned_header.zonedmeta_= size), }; ret =3D header_ext_add(buf, QCOW2_EXT_MAGIC_ZONED_FORMAT, &zoned_header, sizeof(zoned_header), @@ -3766,7 +4213,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options= , Error **errp) int version; int refcount_order; uint64_t *refcount_table; - int ret; + uint64_t zoned_meta_size, zoned_clusterlen; + int ret, offset, i; uint8_t compression_type =3D QCOW2_COMPRESSION_TYPE_ZLIB; =20 assert(create_options->driver =3D=3D BLOCKDEV_DRIVER_QCOW2); @@ -4113,6 +4561,42 @@ qcow2_co_create(BlockdevCreateOptions *create_option= s, Error **errp) ret =3D -EINVAL; goto unlock; } + + uint32_t nrz =3D s->zoned_header.nr_zones; + zoned_meta_size =3D sizeof(uint64_t) * nrz; + g_autofree uint64_t *meta =3D NULL; + meta =3D g_new0(uint64_t, nrz); + + for (i =3D 0; i < s->zoned_header.conventional_zones; ++i) { + meta[i] =3D i * s->zoned_header.zone_size; + meta[i] |=3D 1ULL << 59; + } + + for (; i < nrz; ++i) { + meta[i] =3D i * s->zoned_header.zone_size; + } + + offset =3D qcow2_alloc_clusters(blk_bs(blk), zoned_meta_size); + if (offset < 0) { + ret =3D offset; + error_setg_errno(errp, -ret, "Could not allocate clusters " + "for zoned metadata size"); + goto unlock; + } + s->zoned_header.zonedmeta_offset =3D offset; + s->zoned_header.zonedmeta_size =3D zoned_meta_size; + + zoned_clusterlen =3D size_to_clusters(s, zoned_meta_size) + * s->cluster_size; + ret =3D qcow2_pre_write_overlap_check(blk_bs(blk), 0, offset, + zoned_clusterlen, false); + assert(ret =3D=3D 0); + ret =3D bdrv_pwrite(blk_bs(blk)->file, offset, zoned_meta_size, me= ta, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write zoned metadata " + "to disk"); + goto unlock; + } } else { s->zoned_header.zoned =3D BLK_Z_NONE; } @@ -4512,6 +4996,359 @@ qcow2_co_pdiscard(BlockDriverState *bs, int64_t off= set, int64_t bytes) return ret; } =20 +static int coroutine_fn +qcow2_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, BlockZoneDescriptor *zones) +{ + BDRVQcow2State *s =3D bs->opaque; + uint64_t zone_size =3D s->zoned_header.zone_size; + int64_t capacity =3D bs->total_sectors << BDRV_SECTOR_BITS; + int64_t size =3D bs->bl.nr_zones * zone_size; + unsigned int nrz; + int i =3D 0; + int si; + + if (offset >=3D capacity) { + error_report("offset %" PRId64 " is equal to or greater than the " + "device capacity %" PRId64 "", offset, capacity); + return -EINVAL; + } + + nrz =3D ((*nr_zones) < bs->bl.nr_zones) ? (*nr_zones) : bs->bl.nr_zone= s; + si =3D offset / zone_size; /* Zone size cannot be 0 for zoned device */ + qemu_co_mutex_lock(&bs->wps->colock); + for (; i < nrz; ++i) { + if (i + si >=3D bs->bl.nr_zones) { + break; + } + + zones[i].start =3D (si + i) * zone_size; + + /* The last zone can be smaller than the zone size */ + if ((si + i + 1) =3D=3D bs->bl.nr_zones && size > capacity) { + uint32_t l =3D zone_size - (size - capacity); + zones[i].length =3D l; + zones[i].cap =3D l; + } else { + zones[i].length =3D zone_size; + zones[i].cap =3D zone_size; + } + + uint64_t wp =3D bs->wps->wp[si + i]; + if (QCOW2_ZT_IS_CONV(wp)) { + zones[i].type =3D BLK_ZT_CONV; + zones[i].state =3D BLK_ZS_NOT_WP; + /* Clear masking bits */ + wp =3D QCOW2_GET_WP(wp); + } else { + zones[i].type =3D BLK_ZT_SWR; + zones[i].state =3D qcow2_get_zone_state(bs, si + i); + } + zones[i].wp =3D wp; + } + qemu_co_mutex_unlock(&bs->wps->colock); + *nr_zones =3D i; + return 0; +} + +static int coroutine_fn GRAPH_RDLOCK +qcow2_open_zone(BlockDriverState *bs, uint32_t index) { + BDRVQcow2State *s =3D bs->opaque; + int ret; + + qemu_co_mutex_lock(&bs->wps->colock); + BlockZoneState zs =3D qcow2_get_zone_state(bs, index); + trace_qcow2_imp_open_zones(BLK_ZO_OPEN, s->nr_zones_imp_open); + + switch (zs) { + case BLK_ZS_EMPTY: + if (!qcow2_can_activate_zone(bs)) { + ret =3D -EBUSY; + goto unlock; + } + break; + case BLK_ZS_IOPEN: + qcow2_rm_imp_open_zone(s, index); + break; + case BLK_ZS_EOPEN: + return 0; + case BLK_ZS_CLOSED: + if (!qcow2_can_open_zone(bs)) { + ret =3D -EINVAL; + goto unlock; + } + qcow2_rm_closed_zone(s, index); + break; + case BLK_ZS_FULL: + break; + default: + ret =3D -EINVAL; + goto unlock; + } + + qcow2_do_exp_open_zone(s, index); + ret =3D 0; + +unlock: + qemu_co_mutex_unlock(&bs->wps->colock); + return ret; +} + +static int qcow2_close_zone(BlockDriverState *bs, uint32_t index) +{ + int ret; + + qemu_co_mutex_lock(&bs->wps->colock); + BlockZoneState zs =3D qcow2_get_zone_state(bs, index); + + switch (zs) { + case BLK_ZS_EMPTY: + break; + case BLK_ZS_IOPEN: + break; + case BLK_ZS_EOPEN: + break; + case BLK_ZS_CLOSED: + /* Closing a closed zone is not an error */ + ret =3D 0; + goto unlock; + case BLK_ZS_FULL: + break; + default: + ret =3D -EINVAL; + goto unlock; + } + qcow2_do_close_zone(bs, index, zs); + ret =3D 0; + +unlock: + qemu_co_mutex_unlock(&bs->wps->colock); + return ret; +} + +static int coroutine_fn GRAPH_RDLOCK +qcow2_finish_zone(BlockDriverState *bs, uint32_t index) { + BDRVQcow2State *s =3D bs->opaque; + int ret; + + qemu_co_mutex_lock(&bs->wps->colock); + uint64_t *wp =3D &bs->wps->wp[index]; + BlockZoneState zs =3D qcow2_get_zone_state(bs, index); + + switch (zs) { + case BLK_ZS_EMPTY: + if (!qcow2_can_activate_zone(bs)) { + ret =3D -EBUSY; + goto unlock; + } + break; + case BLK_ZS_IOPEN: + qcow2_rm_imp_open_zone(s, index); + trace_qcow2_imp_open_zones(BLK_ZO_FINISH, s->nr_zones_imp_open); + break; + case BLK_ZS_EOPEN: + qcow2_rm_exp_open_zone(s, index); + break; + case BLK_ZS_CLOSED: + if (!qcow2_can_open_zone(bs)) { + ret =3D -EINVAL; + goto unlock; + } + qcow2_rm_closed_zone(s, index); + break; + case BLK_ZS_FULL: + ret =3D 0; + goto unlock; + default: + ret =3D -EINVAL; + goto unlock; + } + + *wp =3D ((uint64_t)index + 1) * s->zoned_header.zone_size; + ret =3D qcow2_rw_wp_at(bs, wp, index, true); + +unlock: + qemu_co_mutex_unlock(&bs->wps->colock); + return ret; +} + +static int coroutine_fn GRAPH_RDLOCK +qcow2_reset_zone(BlockDriverState *bs, uint32_t index, + int64_t len) { + BDRVQcow2State *s =3D bs->opaque; + int nrz =3D bs->bl.nr_zones; + int zone_size =3D bs->bl.zone_size; + int n, ret =3D 0; + + qemu_co_mutex_lock(&bs->wps->colock); + uint64_t *wp =3D &bs->wps->wp[index]; + if (len =3D=3D bs->total_sectors << BDRV_SECTOR_BITS) { + n =3D nrz; + index =3D 0; + } else { + n =3D len / zone_size; + } + + for (int i =3D 0; i < n; ++i) { + uint64_t *wp_i =3D (uint64_t *)(wp + i); + uint64_t wpi_v =3D *wp_i; + if (QCOW2_ZT_IS_CONV(wpi_v)) { + continue; + } + + BlockZoneState zs =3D qcow2_get_zone_state(bs, index + i); + switch (zs) { + case BLK_ZS_EMPTY: + break; + case BLK_ZS_IOPEN: + qcow2_rm_imp_open_zone(s, index + i); + trace_qcow2_imp_open_zones(BLK_ZO_RESET, s->nr_zones_imp_open); + break; + case BLK_ZS_EOPEN: + qcow2_rm_exp_open_zone(s, index + i); + break; + case BLK_ZS_CLOSED: + qcow2_rm_closed_zone(s, index + i); + break; + case BLK_ZS_FULL: + break; + default: + ret =3D -EINVAL; + goto unlock; + } + + if (zs =3D=3D BLK_ZS_EMPTY) { + continue; + } + + *wp_i =3D ((uint64_t)index + i) * zone_size; + ret =3D qcow2_rw_wp_at(bs, wp_i, index + i, true); + if (ret < 0) { + goto unlock; + } + /* clear data */ + ret =3D qcow2_co_pwrite_zeroes(bs, *wp_i, zone_size, 0); + if (ret < 0) { + error_report("Failed to reset zone at 0x%" PRIx64 "", *wp_i); + } + } + +unlock: + qemu_co_mutex_unlock(&bs->wps->colock); + return ret; +} + +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, + int64_t offset, int64_t len) +{ + BDRVQcow2State *s =3D bs->opaque; + int ret =3D 0; + int64_t capacity =3D bs->total_sectors << BDRV_SECTOR_BITS; + int64_t zone_size =3D s->zoned_header.zone_size; + int64_t zone_size_mask =3D zone_size - 1; + uint32_t index =3D offset / zone_size; + BlockZoneWps *wps =3D bs->wps; + + if (offset >=3D capacity) { + error_report("offset %" PRId64 " is equal to or greater than the" + "device capacity %" PRId64 "", offset, capacity); + return -EINVAL; + } + + if (offset & zone_size_mask) { + error_report("sector offset %" PRId64 " is not aligned to zone siz= e" + " %" PRId64 "", offset / 512, zone_size / 512); + return -EINVAL; + } + + if (((offset + len) < capacity && len & zone_size_mask) || + offset + len > capacity) { + error_report("number of sectors %" PRId64 " is not aligned to zone" + " size %" PRId64 "", len / 512, zone_size / 512); + return -EINVAL; + } + + qemu_co_mutex_lock(&wps->colock); + uint64_t wpv =3D wps->wp[index]; + if (QCOW2_ZT_IS_CONV(wpv) && len !=3D capacity) { + error_report("zone mgmt operations are not allowed for " + "conventional zones"); + ret =3D -EIO; + goto unlock; + } + qemu_co_mutex_unlock(&wps->colock); + + switch (op) { + case BLK_ZO_OPEN: + ret =3D qcow2_open_zone(bs, index); + break; + case BLK_ZO_CLOSE: + ret =3D qcow2_close_zone(bs, index); + break; + case BLK_ZO_FINISH: + ret =3D qcow2_finish_zone(bs, index); + break; + case BLK_ZO_RESET: + ret =3D qcow2_reset_zone(bs, index, len); + break; + default: + error_report("Unsupported zone op: 0x%x", op); + ret =3D -ENOTSUP; + break; + } + return ret; + +unlock: + qemu_co_mutex_unlock(&wps->colock); + return ret; +} + +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_zone_append(BlockDriverState *bs, int64_t *offset, QEMUIOVector *= qiov, + BdrvRequestFlags flags) +{ + assert(flags =3D=3D 0); + int64_t capacity =3D bs->total_sectors << BDRV_SECTOR_BITS; + int64_t zone_size_mask =3D bs->bl.zone_size - 1; + int64_t iov_len =3D 0; + int64_t len =3D 0; + + if (*offset >=3D capacity) { + error_report("*offset %" PRId64 " is equal to or greater than the" + "device capacity %" PRId64 "", *offset, capacity); + return -EINVAL; + } + + /* offset + len should not pass the end of that zone starting from off= set */ + if (*offset & zone_size_mask) { + error_report("sector offset %" PRId64 " is not aligned to zone siz= e " + "%" PRId64 "", *offset / 512, bs->bl.zone_size / 512); + return -EINVAL; + } + + int64_t wg =3D bs->bl.write_granularity; + int64_t wg_mask =3D wg - 1; + for (int i =3D 0; i < qiov->niov; i++) { + iov_len =3D qiov->iov[i].iov_len; + if (iov_len & wg_mask) { + error_report("len of IOVector[%d] 0x%" PRIx64 " is not aligned= to " + "block size 0x%" PRIx64 "", i, iov_len, wg); + return -EINVAL; + } + } + len =3D qiov->size; + + if ((len >> BDRV_SECTOR_BITS) > bs->bl.max_append_sectors) { + error_report("len 0x%" PRIx64 " in sectors is greater than " + "max_append_sectors 0x%" PRIx32 "", + len >> BDRV_SECTOR_BITS, bs->bl.max_append_sectors); + return -EINVAL; + } + + return qcow2_co_pwv_part(bs, offset, len, qiov, 0, true, 0); +} + static int coroutine_fn GRAPH_RDLOCK qcow2_co_copy_range_from(BlockDriverState *bs, BdrvChild *src, int64_t src_offset, @@ -6578,6 +7415,10 @@ BlockDriver bdrv_qcow2 =3D { .bdrv_co_pwritev_compressed_part =3D qcow2_co_pwritev_compressed_pa= rt, .bdrv_make_empty =3D qcow2_make_empty, =20 + .bdrv_co_zone_report =3D qcow2_co_zone_report, + .bdrv_co_zone_mgmt =3D qcow2_co_zone_mgmt, + .bdrv_co_zone_append =3D qcow2_co_zone_append, + .bdrv_snapshot_create =3D qcow2_snapshot_create, .bdrv_snapshot_goto =3D qcow2_snapshot_goto, .bdrv_snapshot_delete =3D qcow2_snapshot_delete, diff --git a/block/qcow2.h b/block/qcow2.h index 743f2e3e79..4f72b8d62a 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -256,9 +256,9 @@ typedef struct Qcow2ZonedHeaderExtension { } QEMU_PACKED Qcow2ZonedHeaderExtension; =20 typedef struct Qcow2ZoneListEntry { - QLIST_ENTRY(Qcow2ZoneListEntry) exp_open_zone_entry; - QLIST_ENTRY(Qcow2ZoneListEntry) imp_open_zone_entry; - QLIST_ENTRY(Qcow2ZoneListEntry) closed_zone_entry; + QTAILQ_ENTRY(Qcow2ZoneListEntry) exp_open_zone_entry; + QTAILQ_ENTRY(Qcow2ZoneListEntry) imp_open_zone_entry; + QTAILQ_ENTRY(Qcow2ZoneListEntry) closed_zone_entry; } Qcow2ZoneListEntry; =20 typedef struct Qcow2UnknownHeaderExtension { @@ -456,6 +456,13 @@ typedef struct BDRVQcow2State { =20 /* States of zoned device */ Qcow2ZonedHeaderExtension zoned_header; + QTAILQ_HEAD(, Qcow2ZoneListEntry) exp_open_zones; + QTAILQ_HEAD(, Qcow2ZoneListEntry) imp_open_zones; + QTAILQ_HEAD(, Qcow2ZoneListEntry) closed_zones; + Qcow2ZoneListEntry *zone_list_entries; + uint32_t nr_zones_exp_open; + uint32_t nr_zones_imp_open; + uint32_t nr_zones_closed; } BDRVQcow2State; =20 typedef struct Qcow2COWRegion { diff --git a/block/trace-events b/block/trace-events index 950c82d4b8..30a3e303ca 100644 --- a/block/trace-events +++ b/block/trace-events @@ -76,6 +76,8 @@ qcow2_writev_data(void *co, uint64_t offset) "co %p offse= t 0x%" PRIx64 qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int64_t bytes) "co= %p offset 0x%" PRIx64 " bytes %" PRId64 qcow2_pwrite_zeroes(void *co, int64_t offset, int64_t bytes) "co %p offset= 0x%" PRIx64 " bytes %" PRId64 qcow2_skip_cow(void *co, uint64_t offset, int nb_clusters) "co %p offset 0= x%" PRIx64 " nb_clusters %d" +qcow2_wp_tracking(int index, uint64_t wp) "wps[%d]: 0x%" PRIx64 +qcow2_imp_open_zones(uint8_t op, int nrz) "nr_imp_open_zones after op 0x%x= : %d" =20 # qcow2-cluster.c qcow2_alloc_clusters_offset(void *co, uint64_t offset, int bytes) "co %p o= ffset 0x%" PRIx64 " bytes %d" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 9cb9f1fb2b..285db19ac7 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -288,6 +288,9 @@ static void virtio_blk_submit_multireq(VirtIOBlock *s, = MultiReqBuffer *mrb) int i =3D 0, start =3D 0, num_reqs =3D 0, niov =3D 0, nb_sectors =3D 0; uint32_t max_transfer; int64_t sector_num =3D 0; + BlockDriverState *bs =3D blk_bs(s->blk); + bool zone_cross; + int64_t zone_sector, end_sector; =20 if (mrb->num_reqs =3D=3D 1) { submit_requests(s, mrb, 0, 1, -1); @@ -303,17 +306,34 @@ static void virtio_blk_submit_multireq(VirtIOBlock *s= , MultiReqBuffer *mrb) for (i =3D 0; i < mrb->num_reqs; i++) { VirtIOBlockReq *req =3D mrb->reqs[i]; if (num_reqs > 0) { + zone_cross =3D false; + + /* + * On zoned backends, a single backend write must not span a z= one + * boundary. Bail out of merging if combining req into the cur= rent + * batch would straddle a zone. + */ + if (bs && bs->bl.zone_size > 0) { + zone_sector =3D bs->bl.zone_size / BDRV_SECTOR_SIZE; + end_sector =3D req->sector_num + + req->qiov.size / BDRV_SECTOR_SIZE - 1; + zone_cross =3D (sector_num / zone_sector) !=3D + (end_sector / zone_sector); + } + /* * NOTE: We cannot merge the requests in below situations: * 1. requests are not sequential * 2. merge would exceed maximum number of IOVs * 3. merge would exceed maximum transfer length of backend de= vice + * 4. merge would cross a zone boundary on a zoned backend */ if (sector_num + nb_sectors !=3D req->sector_num || niov > blk_get_max_iov(s->blk) - req->qiov.niov || req->qiov.size > max_transfer || nb_sectors > (max_transfer - - req->qiov.size) / BDRV_SECTOR_SIZE) { + req->qiov.size) / BDRV_SECTOR_SIZE || + zone_cross) { submit_requests(s, mrb, start, num_reqs, niov); num_reqs =3D 0; } diff --git a/include/qemu/queue.h b/include/qemu/queue.h index e029e7bf66..3f0a48740e 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -179,6 +179,7 @@ struct { = \ #define QLIST_EMPTY(head) ((head)->lh_first =3D=3D NULL) #define QLIST_FIRST(head) ((head)->lh_first) #define QLIST_NEXT(elm, field) ((elm)->field.le_next) +#define QLIST_LAST(head, field) (*(head)->lh_first->field.le_prev) =20 =20 /* diff --git a/include/qemu/range.h b/include/qemu/range.h index d446ad885d..d39ba68407 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -213,6 +213,10 @@ static inline int range_covers_byte(uint64_t offset, u= int64_t len, static inline bool ranges_overlap(uint64_t first1, uint64_t len1, uint64_t first2, uint64_t len2) { + if (first1 + len1 =3D=3D 0 || first2 + len2 =3D=3D 0) { + return false; + } + uint64_t last1 =3D range_get_last(first1, len1); uint64_t last2 =3D range_get_last(first2, len2); =20 --=20 2.43.0 From nobody Sun May 24 23:37:32 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=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1778435498; cv=none; d=zohomail.com; s=zohoarc; b=D6bsJ57GOLjEOWg4hIuN1DRiQ5NDzSgGBqgy9Nj18cqe1b3bq8UltRQDqTdiExiomYqPrbhAWHtF0r0sKzX+OmSDl7tInHcoYwEe3gqbUtMmP9HlCTuNKrrSJwAB67zHAzSPaVgUaX7SVCB0ueHdTJJ84eouJVgPGQ34aWDqcEA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778435498; 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=2DttOEJ477/3YCXgnp8XAqK4FdjbVhf/EynbTKcVc2w=; b=PJHxJw37CA0O7xncVWMUaZWEwBS21X/ptosIqC/25pJFHV4RyNlYiYpa4K5aCGXHKnsxa66l8GzleB1SUW8Xo8Dk8tGhgjk7fUNGTZiMDV9FG5q6fl1/4Nc4YvTNWTbDy3mLvetPC0/7nGFcmHx/6CTCryMUDSuaHXBDapKThi0= 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 177843549868691.48794866505409; Sun, 10 May 2026 10:51:38 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wM8JB-0000OQ-B5; Sun, 10 May 2026 13:51:17 -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 1wM8J9-0000ND-0X for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:15 -0400 Received: from mail-wm1-x32d.google.com ([2a00:1450:4864:20::32d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wM8J4-0001ig-8x for qemu-devel@nongnu.org; Sun, 10 May 2026 13:51:14 -0400 Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-488ad135063so28403145e9.0 for ; Sun, 10 May 2026 10:51:09 -0700 (PDT) Received: from groves.. ([95.90.240.94]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e702e0bf2sm225215565e9.4.2026.05.10.10.51.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 10:51:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778435468; x=1779040268; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2DttOEJ477/3YCXgnp8XAqK4FdjbVhf/EynbTKcVc2w=; b=KmnPk7QOzkImNh97ajKdndi0Osvmqhpro5y9XmssmBu6kpj+VI3puns38yQdddFejz pt1YsG3OgeHpmQmde6G7f+L7x+aEsZm+cUG4m8GveOfgdaZXMUfFvD69IPHVv4FFdOrT hQFo1CZVRDMeOoA7bCh6R7VdoJ9FHyDBEzZMRy2NK854pS9JaOhq3qDKPYQ5rOSvd+tB Ww5Q4l7f73FUpFLVDBKvWxyFoS5h/JJO2fm2bcqAU9aVk9BQZWAkmFhdQXMcI/pWwO0J K+JKj6R+rLpEUb+TEUSMjHEcnYjoDR4FOq+wF6Sql5nhgaQf5nbY1vwcwedKeO0Eop3T wEqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778435468; x=1779040268; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=2DttOEJ477/3YCXgnp8XAqK4FdjbVhf/EynbTKcVc2w=; b=NiJzH/smUNZ906mBd3E+/QSUIMVW0XZc1Rf8e3LGky6EktFgRQwl/Cf21NN0AxEHzv fxQR7ERg09vj0XFW7N5DowF7SJZGAXvgpYke2DKnJXxMGj0fNTytaI06/b7dopvqQLnc 8zBaHEy5gguAyub9jfJlfPH/ulUJG83g9E/b3ajTxxn8fYNlvOBXDMhUTZOvaqv3ADOW zhOGSfZ20W8tcnOFJjpMCeUsQcdjBFwR4XPeE7hK2HydClwtysJeSi6HqKb18eFLG2Cm NN1D3OoYWqI4+cnvz8IwTfp/VP83I4zvIf1mxRTBUeXpTQSKbA7iFWKgz9Dd//108LFY pSDA== X-Gm-Message-State: AOJu0YyuvCtWrrp7aZ3ATcpFJ//p1QiH2LKePoQ0tTkqF1dP3COXAAB6 B1CX9id3l6ij26BMQ+MjROXaJK2+52qciwfgBFM/HjJr609sRjYMwP4CLzD6sA== X-Gm-Gg: Acq92OHQdf7Eb1yFAJ2Hrq5AgUHTYZevg4zPBXAkDRRHyYuQGvb03wtsDcw6ecqKJLj stqmjQC7BdZdfLkPdEXEBt3gPrr1ACRlLt4kXA0tYp3pB6qX25R225zKm2OUzceDpqxtpZG/jUQ hurC652SP9tZtBuaj+KSG/t4EvzyqSQMprN2uw3sKgf62Mfp/WJoTrqtOZChtR65s1cIFZHS3GE JsfEWPbgVJLnne2FbPb5RDZ/hSHokg2rBpQXWHmN6/dVLhVt2DKtaIkeQ7p6kOLTFq9j0YUymhq 0KZ85Hxgig4MGuN9fINYJ+TZqc9/31i8zRbl+6qGbj1znow6YsgGitdaKrtGkbr+rsarY6PBG4c ZfFYhbvjiwuEurb52EypEz3TKX5gM9hVtya2PFpIKYC2InrKjzslWdMyNjMf391DvvL91zU5PtL i/nvEtvF2r2qO2RBBuJ/y9kwfktV92FpZ+koFkr9NV7uh6jMNr7G0R63XBeMuWIeg= X-Received: by 2002:a05:600c:35d4:b0:48e:74dc:999f with SMTP id 5b1f17b1804b1-48e74dc9b4cmr111107035e9.6.1778435468034; Sun, 10 May 2026 10:51:08 -0700 (PDT) From: Sam Li To: qemu-devel@nongnu.org Cc: Pierrick Bouvier , dmitry.fomichev@wdc.com, Kevin Wolf , cassel@kernel.org, Markus Armbruster , Stefan Hajnoczi , qemu-block@nongnu.org, dlemoal@kernel.org, Eric Blake , "Michael S. Tsirkin" , hare@suse.de, Hanna Reitz , Sam Li Subject: [PATCH v10 4/4] iotests: test the zoned format feature for qcow2 file Date: Sun, 10 May 2026 19:50:59 +0200 Message-ID: <20260510175059.311814-5-faithilikerun@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260510175059.311814-1-faithilikerun@gmail.com> References: <20260510175059.311814-1-faithilikerun@gmail.com> 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=2a00:1450:4864:20::32d; envelope-from=faithilikerun@gmail.com; helo=mail-wm1-x32d.google.com 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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @gmail.com) X-ZM-MESSAGEID: 1778435502030154100 Content-Type: text/plain; charset="utf-8" The zoned format feature can be tested by: $ tests/qemu-iotests/check -qcow2 zoned-qcow2 Signed-off-by: Sam Li Reviewed-by: Stefan Hajnoczi --- tests/qemu-iotests/tests/zoned-qcow2 | 150 ++++++++++++++++++++ tests/qemu-iotests/tests/zoned-qcow2.out | 173 +++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100755 tests/qemu-iotests/tests/zoned-qcow2 create mode 100644 tests/qemu-iotests/tests/zoned-qcow2.out diff --git a/tests/qemu-iotests/tests/zoned-qcow2 b/tests/qemu-iotests/test= s/zoned-qcow2 new file mode 100755 index 0000000000..5dc76cb8eb --- /dev/null +++ b/tests/qemu-iotests/tests/zoned-qcow2 @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test zone management operations for qcow2 file. +# + +seq=3D"$(basename $0)" +echo "QA output created by $seq" +status=3D1 # failure is the default! + +file_name=3D"zbc.qcow2" +_cleanup() +{ + _cleanup_test_img + _rm_test_img "$file_name" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter +. ../common.qemu + +# This test only runs on Linux hosts with qcow2 image files. +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +echo +echo "=3D=3D=3D Initial image setup =3D=3D=3D" +echo + +$QEMU_IMG create -f qcow2 $file_name -o size=3D768M -o zone.size=3D64M -o \ +zone.capacity=3D64M -o zone.conventional_zones=3D0 -o zone.max_append_byte= s=3D32M \ +-o zone.max_open_zones=3D6 -o zone.max_active_zones=3D8 -o zone.mode=3Dhos= t-managed + +IMG=3D"--image-opts -n driver=3Dqcow2,file.driver=3Dfile,file.filename=3D$= file_name" +QEMU_IO_OPTIONS=3D$QEMU_IO_OPTIONS_NO_FMT + +echo +echo "=3D=3D=3D Testing a qcow2 img with zoned format =3D=3D=3D" +echo +echo "case 1: test zone operations one by one" + +echo "(1) report zones[0]:" +$QEMU_IO $IMG -c "zrp 0 1" +echo +echo "report zones[0~9]:" +$QEMU_IO $IMG -c "zrp 0 10" +echo +echo "report zones[-1]:" # zones[-1] dictates the last zone +$QEMU_IO $IMG -c "zrp 0x2C000000 2" # 0x2C000000 / 512 =3D 0x160000 +echo +echo +echo "(2) open zones[0], zones[1], zones[-1] then close, finish, reset:" +$QEMU_IO $IMG << EOF +zo 0 0x4000000 +zrp 0 1 +zo 0x4000000 0x4000000 +zrp 0x4000000 1 +zo 0x2C000000 0x4000000 +zrp 0x2C000000 2 +zc 0 0x4000000 +zrp 0 1 +zc 0x4000000 0x4000000 +zrp 0x4000000 1 +zc 0x2C000000 0x4000000 +zrp 0x2C000000 2 +zf 0 0x4000000 +zrp 0 1 +zf 64M 64M +zrp 0x4000000 2 +zf 0x2C000000 0x4000000 +zrp 0x2C000000 2 +zrs 0 0x4000000 +zrp 0 1 +zrs 0x4000000 0x4000000 +zrp 0x4000000 1 +zrs 0x2C000000 0x4000000 +zrp 0x2C000000 2 +EOF + +echo +echo "(3) append write with (4k, 8k) data" +$QEMU_IO $IMG -c "zrp 0 12" # the physical block size of the device is 4096 +echo "Append write zones[0], zones[1] twice" +$QEMU_IO $IMG << EOF +zap -p 0 0x1000 0x2000 +zrp 0 1 +zap -p 0 0x1000 0x2000 +zrp 0 1 +zap -p 0x4000000 0x1000 0x2000 +zrp 0x4000000 1 +zap -p 0x4000000 0x1000 0x2000 +zrp 0x4000000 1 +EOF + +echo +echo "Reset all:" +$QEMU_IO $IMG -c "zrp 0 12" -c "zrs 0 768M" -c "zrp 0 12" +echo +echo + +echo "case 2: test a sets of ops that works or not" +echo "(1) append write (4k, 4k) and then write to full" +$QEMU_IO $IMG << EOF +zap -p 0 0x1000 0x1000 +zrp 0 1 +zap -p 0 0x1000 0x1ffd000 +zap -p 0 0x1000000 0x1000000 +zrp 0 1 +EOF + +echo "Reset zones[0]:" +$QEMU_IO $IMG -c "zrs 0 64M" -c "zrp 0 1" + +echo "(2) write in zones[0], zones[3], zones[8], and then reset all" +$QEMU_IO $IMG << EOF +zap -p 0 0x1000 0x1000 +zap -p 0xc000000 0x1000 0x1000 +zap -p 0x20000000 0x1000 0x1000 +zrp 0 12 +zrs 0 768M +zrp 0 12 +EOF + +echo "case 3: test zone resource management" +echo "(1) write in zones[0], zones[1], zones[2] and then close it" +$QEMU_IO $IMG << EOF +zap -p 0 0x1000 0x1000 +zap -p 0x4000000 0x1000 0x1000 +zap -p 0x8000000 0x1000 0x1000 +zrp 0 12 +zc 0 64M +zc 0x4000000 64M +zc 0x8000000 64M +zrp 0 12 +EOF + +echo "(2) reset all after 3(1)" +$QEMU_IO $IMG << EOF +zrs 0 768M +zrp 0 12 +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=3D0 diff --git a/tests/qemu-iotests/tests/zoned-qcow2.out b/tests/qemu-iotests/= tests/zoned-qcow2.out new file mode 100644 index 0000000000..b62865a487 --- /dev/null +++ b/tests/qemu-iotests/tests/zoned-qcow2.out @@ -0,0 +1,173 @@ +QA output created by zoned-qcow2 + +=3D=3D=3D Initial image setup =3D=3D=3D + +Formatting 'zbc.qcow2', fmt=3Dqcow2 cluster_size=3D65536 extended_l2=3Doff= compression_type=3Dzlib zone.mode=3Dhost-managed zone.size=3D67108864 zone= .capacity=3D67108864 zone.conventional_zones=3D0 zone.max_append_bytes=3D33= 554432 zone.max_active_zones=3D8 zone.max_open_zones=3D6 size=3D805306368 l= azy_refcounts=3Doff refcount_bits=3D16 + +=3D=3D=3D Testing a qcow2 img with zoned format =3D=3D=3D + +case 1: test zone operations one by one +(1) report zones[0]: +start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1, [type: 2] + +report zones[0~9]: +start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1, [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, zcond:1, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] + +report zones[-1]: +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] + + +(2) open zones[0], zones[1], zones[-1] then close, finish, reset: +qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:3,= [type: 2] +qemu-io> qemu-io> start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, = zcond:3, [type: 2] +qemu-io> qemu-io> start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000= , zcond:3, [type: 2] +qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1,= [type: 2] +qemu-io> qemu-io> start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, = zcond:1, [type: 2] +qemu-io> qemu-io> start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000= , zcond:1, [type: 2] +qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x20000, zcon= d:14, [type: 2] +qemu-io> qemu-io> start: 0x20000, len 0x20000, cap 0x20000, wptr 0x40000, = zcond:14, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +qemu-io> qemu-io> start: 0x160000, len 0x20000, cap 0x20000, wptr 0x180000= , zcond:14, [type: 2] +qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1,= [type: 2] +qemu-io> qemu-io> start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, = zcond:1, [type: 2] +qemu-io> qemu-io> start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000= , zcond:1, [type: 2] +qemu-io>=20 +(3) append write with (4k, 8k) data +start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1, [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, zcond:1, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +Append write zones[0], zones[1] twice +qemu-io> After zap done, the append sector is 0x0 +qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x18, zcond:2, [type: = 2] +qemu-io> After zap done, the append sector is 0x18 +qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x30, zcond:2, [type: = 2] +qemu-io> After zap done, the append sector is 0x20000 +qemu-io> start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20018, zcond:2, = [type: 2] +qemu-io> After zap done, the append sector is 0x20018 +qemu-io> start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20030, zcond:2, = [type: 2] +qemu-io>=20 +Reset all: +start: 0x0, len 0x20000, cap 0x20000, wptr 0x30, zcond:4, [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20030, zcond:4, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1, [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, zcond:1, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] + + +case 2: test a sets of ops that works or not +(1) append write (4k, 4k) and then write to full +qemu-io> After zap done, the append sector is 0x0 +qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x10, zcond:2, [type: = 2] +qemu-io> After zap done, the append sector is 0x10 +qemu-io> After zap done, the append sector is 0x10000 +qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x20000, zcond:14, [ty= pe: 2] +qemu-io> Reset zones[0]: +start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1, [type: 2] +(2) write in zones[0], zones[3], zones[8], and then reset all +qemu-io> After zap done, the append sector is 0x0 +qemu-io> After zap done, the append sector is 0x60000 +qemu-io> After zap done, the append sector is 0x100000 +qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x10, zcond:2, [type: = 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, zcond:1, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60010, zcond:2, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100010, zcond:2, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1,= [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, zcond:1, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +qemu-io> case 3: test zone resource management +(1) write in zones[0], zones[1], zones[2] and then close it +qemu-io> After zap done, the append sector is 0x0 +qemu-io> After zap done, the append sector is 0x20000 +qemu-io> After zap done, the append sector is 0x40000 +qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x10, zcond:2, [type: = 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20010, zcond:2, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40010, zcond:2, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +qemu-io> qemu-io> qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, = wptr 0x10, zcond:4, [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20010, zcond:4, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40010, zcond:4, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +qemu-io> (2) reset all after 3(1) +qemu-io> qemu-io> start: 0x0, len 0x20000, cap 0x20000, wptr 0x0, zcond:1,= [type: 2] +start: 0x20000, len 0x20000, cap 0x20000, wptr 0x20000, zcond:1, [type: 2] +start: 0x40000, len 0x20000, cap 0x20000, wptr 0x40000, zcond:1, [type: 2] +start: 0x60000, len 0x20000, cap 0x20000, wptr 0x60000, zcond:1, [type: 2] +start: 0x80000, len 0x20000, cap 0x20000, wptr 0x80000, zcond:1, [type: 2] +start: 0xa0000, len 0x20000, cap 0x20000, wptr 0xa0000, zcond:1, [type: 2] +start: 0xc0000, len 0x20000, cap 0x20000, wptr 0xc0000, zcond:1, [type: 2] +start: 0xe0000, len 0x20000, cap 0x20000, wptr 0xe0000, zcond:1, [type: 2] +start: 0x100000, len 0x20000, cap 0x20000, wptr 0x100000, zcond:1, [type: = 2] +start: 0x120000, len 0x20000, cap 0x20000, wptr 0x120000, zcond:1, [type: = 2] +start: 0x140000, len 0x20000, cap 0x20000, wptr 0x140000, zcond:1, [type: = 2] +start: 0x160000, len 0x20000, cap 0x20000, wptr 0x160000, zcond:1, [type: = 2] +qemu-io> *** done --=20 2.43.0