From nobody Sat May 18 04:13:35 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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 ARC-Seal: i=1; a=rsa-sha256; t=1602088630; cv=none; d=zohomail.com; s=zohoarc; b=gswHJjHvs4JVvDWznN8Csnt5aMY2HWuqbbW8IDephKIpCanSCOL9K0HMkUtJnDVoBNJu9+HfL3wKk69iX17w5ACwU6kYdTJLkQd+EzdVXwVRFpIhpNMasMXXHTc1I5xQEZu3bNeLF9xwlYiUU7I03JIUkHyvv4vfr/CBUKukrpg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1602088630; h=Content-Transfer-Encoding:Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:To; bh=SaVLv1MCCvjJEnd1v6sDBIjAvrIK/IZkqswk6U92goc=; b=WxsuC4JHWVv0yyG9LnjBp7UcT0QyCbB6t94sJm4ACTtlxmRDV3bqJ0P1Or8pBeUI+RqXDAGwZTNgND6Lq2h7mCQ8EsEJpP9SfsgJiXIXqX+CyQg5f9zPeZ+yst/fqPMpsxGnXXbvRgCy4FNu3nSoUXzyorbGiJkdYH8+gNlc8wY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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 Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1602088629166876.335179555022; Wed, 7 Oct 2020 09:37:09 -0700 (PDT) Received: from localhost ([::1]:47282 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kQCR9-0000oZ-Do for importer@patchew.org; Wed, 07 Oct 2020 12:37:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:37690) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kQC4Y-00084M-RU; Wed, 07 Oct 2020 12:13:47 -0400 Received: from fanzine.igalia.com ([178.60.130.6]:49535) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kQC4W-00040U-1q; Wed, 07 Oct 2020 12:13:46 -0400 Received: from [81.0.34.65] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1kQC4P-0007iY-P5; Wed, 07 Oct 2020 18:13:37 +0200 Received: from berto by perseus.local with local (Exim 4.92) (envelope-from ) id 1kQC4C-0001E3-PT; Wed, 07 Oct 2020 18:13:24 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:Message-Id:Date:Subject:Cc:To:From; bh=SaVLv1MCCvjJEnd1v6sDBIjAvrIK/IZkqswk6U92goc=; b=Po7FEq3fYVYTjURB6tcT7V+G7eb9Oct4B302GWqDzeZnh90twP92rkYxiNtYe6fA4dwPeW+6wLV3+4UgvxmSo2oos/5VPSoNAJsF5ppvg+BNYFEMdRupkThacKUPHEJeZTKfvyp/nxGitcglVUrwqR2kltiq3aEHRLW9z7SBXAaWAN19rFDi6OVwCQw1+SgINp5nlBxI5/rWQCIadHUvZJGFBLvEr+ypyGYczM3VtUXZrlwmJ9ohpy1b+99WI/xxCCOge4iwH09xbMQGjWy7pImKPeVc161s+FsJ8+J6/e40iKo8ytpc5N+DdZOM4JD7CD79ML0GnJw5FHW60sRpkw==; From: Alberto Garcia To: qemu-devel@nongnu.org Subject: [PATCH v2] qcow2: Document and enforce the QCowL2Meta invariants Date: Wed, 7 Oct 2020 18:13:23 +0200 Message-Id: <20201007161323.4667-1-berto@igalia.com> X-Mailer: git-send-email 2.20.1 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=lists.gnu.org; Received-SPF: pass client-ip=178.60.130.6; envelope-from=berto@igalia.com; helo=fanzine.igalia.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/10/07 11:38:02 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Vladimir Sementsov-Ogievskiy , Alberto Garcia , qemu-block@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" The QCowL2Meta structure is used to store information about a part of a write request that touches clusters that need changes in their L2 entries. This happens with newly-allocated clusters or subclusters. This structure has changed a bit since it was first created and its current documentation is not quite up-to-date. A write request can span a region consisting of a combination of clusters of different types, and qcow2_alloc_host_offset() can repeatedly call handle_copied() and handle_alloc() to add more clusters to the mix as long as they all are contiguous on the image file. Because of this a write request has a list of QCowL2Meta structures, one for each part of the request that needs changes in the L2 metadata. Each one of them spans nb_clusters and has two copy-on-write regions located immediately before and after the middle region touched by that part of the write request. Even when those regions themselves are empty their offsets must be correct because they are used to know the location of the middle region. This was not always the case but it is not a problem anymore because the only two places where QCowL2Meta structures are created (calculate_l2_meta() and qcow2_co_truncate()) ensure that the copy-on-write regions are correctly defined, and so do assertions like the ones in perform_cow(). The conditional initialization of the 'written_to' variable is therefore unnecessary and is removed by this patch. Signed-off-by: Alberto Garcia Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/qcow2.h | 19 +++++++++++-------- block/qcow2-cluster.c | 5 +++-- block/qcow2.c | 19 +++++++++++++++---- 3 files changed, 29 insertions(+), 14 deletions(-) v2: Fix mistakes on the commit messages [Eric] diff --git a/block/qcow2.h b/block/qcow2.h index 125ea9679b..2e0272a7b8 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -435,17 +435,18 @@ typedef struct Qcow2COWRegion { =20 /** * Describes an in-flight (part of a) write request that writes to clusters - * that are not referenced in their L2 table yet. + * that need to have their L2 table entries updated (because they are + * newly allocated or need changes in their L2 bitmaps) */ typedef struct QCowL2Meta { - /** Guest offset of the first newly allocated cluster */ + /** Guest offset of the first updated cluster */ uint64_t offset; =20 - /** Host offset of the first newly allocated cluster */ + /** Host offset of the first updated cluster */ uint64_t alloc_offset; =20 - /** Number of newly allocated clusters */ + /** Number of updated clusters */ int nb_clusters; =20 /** Do not free the old clusters */ @@ -458,14 +459,16 @@ typedef struct QCowL2Meta CoQueue dependent_requests; =20 /** - * The COW Region between the start of the first allocated cluster and= the - * area the guest actually writes to. + * The COW Region immediately before the area the guest actually + * writes to. This (part of the) write request starts at + * cow_start.offset + cow_start.nb_bytes. */ Qcow2COWRegion cow_start; =20 /** - * The COW Region between the area the guest actually writes to and the - * end of the last allocated cluster. + * The COW Region immediately after the area the guest actually + * writes to. This (part of the) write request ends at cow_end.offset + * (which must always be set even when cow_end.nb_bytes is 0). */ Qcow2COWRegion cow_end; =20 diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index aa87d3e99b..485b4cb92e 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1049,6 +1049,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs,= QCowL2Meta *m) qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); =20 assert(l2_index + m->nb_clusters <=3D s->l2_slice_size); + assert(m->cow_end.offset + m->cow_end.nb_bytes <=3D + m->nb_clusters << s->cluster_bits); for (i =3D 0; i < m->nb_clusters; i++) { uint64_t offset =3D cluster_offset + ((uint64_t)i << s->cluster_bi= ts); /* if two concurrent writes happen to the same unallocated cluster @@ -1070,8 +1072,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs,= QCowL2Meta *m) if (has_subclusters(s) && !m->prealloc) { uint64_t l2_bitmap =3D get_l2_bitmap(s, l2_slice, l2_index + i= ); unsigned written_from =3D m->cow_start.offset; - unsigned written_to =3D m->cow_end.offset + m->cow_end.nb_byte= s ?: - m->nb_clusters << s->cluster_bits; + unsigned written_to =3D m->cow_end.offset + m->cow_end.nb_byte= s; int first_sc, last_sc; /* Narrow written_from and written_to down to the current clus= ter */ written_from =3D MAX(written_from, i << s->cluster_bits); diff --git a/block/qcow2.c b/block/qcow2.c index b05512718c..e7b27fdf25 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2361,15 +2361,26 @@ static bool merge_cow(uint64_t offset, unsigned byt= es, continue; } =20 - /* The data (middle) region must be immediately after the - * start region */ + /* + * The write request should start immediately after the first + * COW region. This does not always happen because the area + * touched by the request can be larger than the one defined + * by @m (a single request can span an area consisting of a + * mix of previously unallocated and allocated clusters, that + * is why @l2meta is a list). + */ if (l2meta_cow_start(m) + m->cow_start.nb_bytes !=3D offset) { + /* In this case the request starts before this region */ + assert(offset < l2meta_cow_start(m)); + assert(m->cow_start.nb_bytes =3D=3D 0); continue; } =20 - /* The end region must be immediately after the data (middle) - * region */ + /* The write request should end immediately before the second + * COW region (see above for why it does not always happen) */ if (m->offset + m->cow_end.offset !=3D offset + bytes) { + assert(offset + bytes > m->offset + m->cow_end.offset); + assert(m->cow_end.nb_bytes =3D=3D 0); continue; } =20 --=20 2.20.1