From nobody Wed Dec 17 05:37:10 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1494514579705984.4918258477694; Thu, 11 May 2017 07:56:19 -0700 (PDT) Received: from localhost ([::1]:48637 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d8pVq-0005m7-9S for importer@patchew.org; Thu, 11 May 2017 10:56:18 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:36647) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d8pAG-00023m-F7 for qemu-devel@nongnu.org; Thu, 11 May 2017 10:34:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d8pAF-0007A0-76 for qemu-devel@nongnu.org; Thu, 11 May 2017 10:34:00 -0400 Received: from mx1.redhat.com ([209.132.183.28]:53932) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d8pA9-00074J-Uk; Thu, 11 May 2017 10:33:54 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E87DA624CC; Thu, 11 May 2017 14:33:52 +0000 (UTC) Received: from noname.str.redhat.com (dhcp-192-175.str.redhat.com [10.33.192.175]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2A5157A407; Thu, 11 May 2017 14:33:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com E87DA624CC Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=kwolf@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com E87DA624CC From: Kevin Wolf To: qemu-block@nongnu.org Date: Thu, 11 May 2017 16:32:28 +0200 Message-Id: <1494513181-7900-26-git-send-email-kwolf@redhat.com> In-Reply-To: <1494513181-7900-1-git-send-email-kwolf@redhat.com> References: <1494513181-7900-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Thu, 11 May 2017 14:33:53 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL 25/58] qcow2: Reuse preallocated zero clusters X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, qemu-devel@nongnu.org, stefanha@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Max Reitz Instead of just freeing preallocated zero clusters and completely allocating them from scratch, reuse them. We cannot do this in handle_copied(), however, since this is a COW operation. Therefore, we have to add the new logic to handle_alloc() and simply return the existing offset if it exists. The only catch is that we have to convince qcow2_alloc_cluster_link_l2() not to free the old clusters (because we have reused them). Reported-by: Eric Blake Signed-off-by: Max Reitz Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 80 +++++++++++++++++++++++++++++++++++------------= ---- block/qcow2.h | 3 ++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 100398c..fb91fd8 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -309,14 +309,20 @@ static int count_contiguous_clusters(int nb_clusters,= int cluster_size, uint64_t *l2_table, uint64_t stop_flags) { int i; + int first_cluster_type; uint64_t mask =3D stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; uint64_t first_entry =3D be64_to_cpu(l2_table[0]); uint64_t offset =3D first_entry & mask; =20 - if (!offset) + if (!offset) { return 0; + } =20 - assert(qcow2_get_cluster_type(first_entry) =3D=3D QCOW2_CLUSTER_NORMAL= ); + /* must be allocated */ + first_cluster_type =3D qcow2_get_cluster_type(first_entry); + assert(first_cluster_type =3D=3D QCOW2_CLUSTER_NORMAL || + (first_cluster_type =3D=3D QCOW2_CLUSTER_ZERO && + (first_entry & L2E_OFFSET_MASK) !=3D 0)); =20 for (i =3D 0; i < nb_clusters; i++) { uint64_t l2_entry =3D be64_to_cpu(l2_table[i]) & mask; @@ -835,7 +841,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, Q= CowL2Meta *m) * Don't discard clusters that reach a refcount of 0 (e.g. compressed * clusters), the next write will reuse them anyway. */ - if (j !=3D 0) { + if (!m->keep_old_clusters && j !=3D 0) { for (i =3D 0; i < j; i++) { qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1, QCOW2_DISCARD_NEVER); @@ -1132,8 +1138,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_= t guest_offset, uint64_t entry; uint64_t nb_clusters; int ret; + bool keep_old_clusters =3D false; =20 - uint64_t alloc_cluster_offset; + uint64_t alloc_cluster_offset =3D 0; =20 trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_of= fset, *bytes); @@ -1170,31 +1177,54 @@ static int handle_alloc(BlockDriverState *bs, uint6= 4_t guest_offset, * wrong with our code. */ assert(nb_clusters > 0); =20 - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + if (qcow2_get_cluster_type(entry) =3D=3D QCOW2_CLUSTER_ZERO && + (entry & L2E_OFFSET_MASK) !=3D 0 && (entry & QCOW_OFLAG_COPIED) && + (!*host_offset || + start_of_cluster(s, *host_offset) =3D=3D (entry & L2E_OFFSET_MASK= ))) + { + /* Try to reuse preallocated zero clusters; contiguous normal clus= ters + * would be fine, too, but count_cow_clusters() above has limited + * nb_clusters already to a range of COW clusters */ + int preallocated_nb_clusters =3D + count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], QCOW_OFLAG_COPI= ED); + assert(preallocated_nb_clusters > 0); =20 - /* Allocate, if necessary at a given offset in the image file */ - alloc_cluster_offset =3D start_of_cluster(s, *host_offset); - ret =3D do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offse= t, - &nb_clusters); - if (ret < 0) { - goto fail; - } + nb_clusters =3D preallocated_nb_clusters; + alloc_cluster_offset =3D entry & L2E_OFFSET_MASK; =20 - /* Can't extend contiguous allocation */ - if (nb_clusters =3D=3D 0) { - *bytes =3D 0; - return 0; + /* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2= () + * should not free them. */ + keep_old_clusters =3D true; } =20 - /* !*host_offset would overwrite the image header and is reserved for = "no - * host offset preferred". If 0 was a valid host offset, it'd trigger = the - * following overlap check; do that now to avoid having an invalid val= ue in - * *host_offset. */ + qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); + if (!alloc_cluster_offset) { - ret =3D qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset, - nb_clusters * s->cluster_size); - assert(ret < 0); - goto fail; + /* Allocate, if necessary at a given offset in the image file */ + alloc_cluster_offset =3D start_of_cluster(s, *host_offset); + ret =3D do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_o= ffset, + &nb_clusters); + if (ret < 0) { + goto fail; + } + + /* Can't extend contiguous allocation */ + if (nb_clusters =3D=3D 0) { + *bytes =3D 0; + return 0; + } + + /* !*host_offset would overwrite the image header and is reserved = for + * "no host offset preferred". If 0 was a valid host offset, it'd + * trigger the following overlap check; do that now to avoid havin= g an + * invalid value in *host_offset. */ + if (!alloc_cluster_offset) { + ret =3D qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_off= set, + nb_clusters * s->cluster_s= ize); + assert(ret < 0); + goto fail; + } } =20 /* @@ -1225,6 +1255,8 @@ static int handle_alloc(BlockDriverState *bs, uint64_= t guest_offset, .offset =3D start_of_cluster(s, guest_offset), .nb_clusters =3D nb_clusters, =20 + .keep_old_clusters =3D keep_old_clusters, + .cow_start =3D { .offset =3D 0, .nb_bytes =3D offset_into_cluster(s, guest_offset), diff --git a/block/qcow2.h b/block/qcow2.h index f8aeb08..8731f24 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -322,6 +322,9 @@ typedef struct QCowL2Meta /** Number of newly allocated clusters */ int nb_clusters; =20 + /** Do not free the old clusters */ + bool keep_old_clusters; + /** * Requests that overlap with this allocation and wait to be restarted * when the allocating request has completed. --=20 1.8.3.1