From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59889) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XAju4-0002G1-Qh for qemu-devel@nongnu.org; Fri, 25 Jul 2014 14:07:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XAjtx-0005In-AM for qemu-devel@nongnu.org; Fri, 25 Jul 2014 14:07:36 -0400 Received: from mx1.redhat.com ([209.132.183.28]:64445) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XAjtx-0005Id-3C for qemu-devel@nongnu.org; Fri, 25 Jul 2014 14:07:29 -0400 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s6PI7Smh022041 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Fri, 25 Jul 2014 14:07:28 -0400 From: Max Reitz Date: Fri, 25 Jul 2014 20:07:44 +0200 Message-Id: <1406311665-2814-8-git-send-email-mreitz@redhat.com> In-Reply-To: <1406311665-2814-1-git-send-email-mreitz@redhat.com> References: <1406311665-2814-1-git-send-email-mreitz@redhat.com> Subject: [Qemu-devel] [PATCH 7/8] block/qcow2: Speed up zero cluster expansion List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Kevin Wolf , Stefan Hajnoczi , Max Reitz Actually, we do not need to allocate a new data cluster for every zero cluster to be expanded: It is completely sufficient to rely on qcow2's COW part and instead create a single zero cluster and reuse it as much as possible. Signed-off-by: Max Reitz --- block/qcow2-cluster.c | 119 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 905beb6..867db03 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1558,6 +1558,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, BDRVQcowState *s = bs->opaque; bool is_active_l1 = (l1_table == s->l1_table); uint64_t *l2_table = NULL; + int64_t zeroed_cluster_offset = 0; + int zeroed_cluster_refcount = 0; + int last_zeroed_cluster_l1i = 0, last_zeroed_cluster_l2i = 0; int ret; int i, j; @@ -1617,47 +1620,79 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, continue; } - offset = qcow2_alloc_clusters(bs, s->cluster_size); - if (offset < 0) { - ret = offset; - goto fail; + if (zeroed_cluster_offset) { + zeroed_cluster_refcount += l2_refcount; + if (zeroed_cluster_refcount > 0xffff) { + zeroed_cluster_refcount = 0; + zeroed_cluster_offset = 0; + } } + if (!zeroed_cluster_offset) { + offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + ret = offset; + goto fail; + } - if (l2_refcount > 1) { - /* For shared L2 tables, set the refcount accordingly (it is - * already 1 and needs to be l2_refcount) */ - ret = qcow2_update_cluster_refcount(bs, - offset >> s->cluster_bits, l2_refcount - 1, - QCOW2_DISCARD_OTHER); + ret = qcow2_pre_write_overlap_check(bs, 0, offset, + s->cluster_size); + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_OTHER); + goto fail; + } + + ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, + s->cluster_sectors, 0); if (ret < 0) { qcow2_free_clusters(bs, offset, s->cluster_size, QCOW2_DISCARD_OTHER); goto fail; } + + if (l2_refcount > 1) { + ret = qcow2_update_cluster_refcount(bs, + offset >> s->cluster_bits, l2_refcount - 1, + QCOW2_DISCARD_OTHER); + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_OTHER); + goto fail; + } + } + + zeroed_cluster_offset = offset; + zeroed_cluster_refcount = l2_refcount; + } else { + ret = qcow2_update_cluster_refcount(bs, + zeroed_cluster_offset >> s->cluster_bits, + l2_refcount, QCOW2_DISCARD_OTHER); + if (ret < 0) { + goto fail; + } } + + offset = zeroed_cluster_offset; + last_zeroed_cluster_l1i = i; + last_zeroed_cluster_l2i = j; } - ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); - if (ret < 0) { - if (!preallocated) { - qcow2_free_clusters(bs, offset, s->cluster_size, - QCOW2_DISCARD_ALWAYS); + if (preallocated) { + ret = qcow2_pre_write_overlap_check(bs, 0, offset, + s->cluster_size); + if (ret < 0) { + goto fail; } - goto fail; - } - ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, - s->cluster_sectors, 0); - if (ret < 0) { - if (!preallocated) { - qcow2_free_clusters(bs, offset, s->cluster_size, - QCOW2_DISCARD_ALWAYS); + ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, + s->cluster_sectors, 0); + if (ret < 0) { + goto fail; } - goto fail; } - if (l2_refcount == 1) { - l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + if (preallocated) { + l2_table[j] = cpu_to_be64(offset | (l2_entry & QCOW_OFLAG_COPIED)); } else { l2_table[j] = cpu_to_be64(offset); } @@ -1670,8 +1705,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, qcow2_cache_depends_on_flush(s->l2_table_cache); } ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + l2_table = NULL; if (ret < 0) { - l2_table = NULL; goto fail; } } else { @@ -1697,6 +1732,36 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } } + /* Fix COPIED (only valid for active L2 tables) */ + if (is_active_l1 && zeroed_cluster_refcount == 1) { + uint64_t l2_offset, l2_entry; + + l2_offset = l1_table[last_zeroed_cluster_l1i] & L1E_OFFSET_MASK; + assert(l2_offset); + + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void **)&l2_table); + if (ret < 0) { + goto fail; + } + + l2_entry = be64_to_cpu(l2_table[last_zeroed_cluster_l2i]); + + assert(!(l2_entry & QCOW_OFLAG_COPIED)); + l2_entry |= QCOW_OFLAG_COPIED; + + l2_table[last_zeroed_cluster_l2i] = cpu_to_be64(l2_entry); + + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + qcow2_cache_depends_on_flush(s->l2_table_cache); + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + l2_table = NULL; + if (ret < 0) { + goto fail; + } + } + ret = 0; fail: -- 2.0.1