From patchwork Mon Apr 3 16:09:33 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Reitz X-Patchwork-Id: 9659947 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 0D9F160352 for ; Mon, 3 Apr 2017 16:21:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F31CD28113 for ; Mon, 3 Apr 2017 16:21:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E7BD028364; Mon, 3 Apr 2017 16:21:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 5358D28113 for ; Mon, 3 Apr 2017 16:21:27 +0000 (UTC) Received: from localhost ([::1]:60194 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cv4jO-00014X-En for patchwork-qemu-devel@patchwork.kernel.org; Mon, 03 Apr 2017 12:21:26 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34579) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cv4Yw-0001Pg-8x for qemu-devel@nongnu.org; Mon, 03 Apr 2017 12:10:39 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cv4Yu-0000XS-OU for qemu-devel@nongnu.org; Mon, 03 Apr 2017 12:10:38 -0400 Received: from mx1.redhat.com ([209.132.183.28]:38028) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cv4Yr-0000UT-JE; Mon, 03 Apr 2017 12:10:33 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id A73783B707; Mon, 3 Apr 2017 16:10:32 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com A73783B707 Authentication-Results: ext-mx06.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx06.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com A73783B707 Received: from localhost (ovpn-204-55.brq.redhat.com [10.40.204.55]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 077895C890; Mon, 3 Apr 2017 16:10:31 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Mon, 3 Apr 2017 18:09:33 +0200 Message-Id: <20170403160936.28293-14-mreitz@redhat.com> In-Reply-To: <20170403160936.28293-1-mreitz@redhat.com> References: <20170403160936.28293-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Mon, 03 Apr 2017 16:10:32 +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] [PATCH v2 for-2.10 13/16] block/qcow2: qcow2_calc_size_usage() for truncate 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: Kevin Wolf , qemu-devel@nongnu.org, Stefan Hajnoczi , Max Reitz Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP This patch extends qcow2_calc_size_usage() so it can calculate the additional space needed for preallocating image growth. Signed-off-by: Max Reitz --- block/qcow2.c | 137 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 39 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index aafbc8dbed..12dafcc570 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2108,7 +2108,15 @@ done: return ret; } -static uint64_t qcow2_calc_size_usage(uint64_t new_size, +/** + * Returns the number of bytes that must be allocated in the underlying file + * to accomodate an image growth from @current_size to @new_size. + * + * @current_size must be 0 when creating a new image. In that case, @bs is + * ignored; otherwise it must be valid. + */ +static uint64_t qcow2_calc_size_usage(BlockDriverState *bs, + uint64_t current_size, uint64_t new_size, int cluster_bits, int refcount_order) { size_t cluster_size = 1u << cluster_bits; @@ -2129,47 +2137,97 @@ static uint64_t qcow2_calc_size_usage(uint64_t new_size, refblock_bits = cluster_bits - (refcount_order - 3); refblock_size = 1 << refblock_bits; - /* header: 1 cluster */ - meta_size += cluster_size; - - /* total size of L2 tables */ - nl2e = aligned_total_size / cluster_size; - nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t)); - meta_size += nl2e * sizeof(uint64_t); + if (!current_size) { + /* header: 1 cluster */ + meta_size += cluster_size; + + /* total size of L2 tables */ + nl2e = aligned_total_size / cluster_size; + nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t)); + meta_size += nl2e * sizeof(uint64_t); + + /* total size of L1 tables */ + nl1e = nl2e * sizeof(uint64_t) / cluster_size; + nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t)); + meta_size += nl1e * sizeof(uint64_t); + + /* total size of refcount blocks + * + * note: every host cluster is reference-counted, including metadata + * (even refcount blocks are recursively included). + * Let: + * a = total_size (this is the guest disk size) + * m = meta size not including refcount blocks and refcount tables + * c = cluster size + * y1 = number of refcount blocks entries + * y2 = meta size including everything + * rces = refcount entry size in bytes + * then, + * y1 = (y2 + a)/c + * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m + * we can get y1: + * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c) + */ + nrefblocke = (aligned_total_size + meta_size + cluster_size) + / (cluster_size - rces - rces * sizeof(uint64_t) + / cluster_size); + meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; - /* total size of L1 tables */ - nl1e = nl2e * sizeof(uint64_t) / cluster_size; - nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t)); - meta_size += nl1e * sizeof(uint64_t); + /* total size of refcount tables */ + nreftablee = nrefblocke / refblock_size; + nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); + meta_size += nreftablee * sizeof(uint64_t); - /* total size of refcount blocks - * - * note: every host cluster is reference-counted, including metadata - * (even refcount blocks are recursively included). - * Let: - * a = total_size (this is the guest disk size) - * m = meta size not including refcount blocks and refcount tables - * c = cluster size - * y1 = number of refcount blocks entries - * y2 = meta size including everything - * rces = refcount entry size in bytes - * then, - * y1 = (y2 + a)/c - * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m - * we can get y1: - * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c) - */ - nrefblocke = (aligned_total_size + meta_size + cluster_size) - / (cluster_size - rces - rces * sizeof(uint64_t) - / cluster_size); - meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; + return aligned_total_size + meta_size; + } else { + BDRVQcow2State *s = bs->opaque; + uint64_t aligned_cur_size = align_offset(current_size, cluster_size); + uint64_t creftable_length; + uint64_t i; + + /* new total size of L2 tables */ + nl2e = aligned_total_size / cluster_size; + nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t)); + meta_size += nl2e * sizeof(uint64_t); + + /* Subtract L2 tables which are already present */ + for (i = 0; i < s->l1_size; i++) { + if (s->l1_table[i] & L1E_OFFSET_MASK) { + meta_size -= cluster_size; + } + } - /* total size of refcount tables */ - nreftablee = nrefblocke / refblock_size; - nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); - meta_size += nreftablee * sizeof(uint64_t); + /* Do not add L1 table size because the only caller of this path + * (qcow2_truncate) has increased its size already. */ - return aligned_total_size + meta_size; + /* Calculate size of the additional refblocks (this assumes that all of + * the existing image is covered by refblocks, which is extremely + * likely); this may result in overallocation because parts of the newly + * added space may be covered by existing refblocks, but that is fine. + * + * This only considers the newly added space. Since we cannot update the + * reftable in-place, we will have to able to store both the old and the + * new one at the same time, though. Therefore, we need to add the size + * of the old reftable here. + */ + creftable_length = ROUND_UP(s->refcount_table_size * sizeof(uint64_t), + cluster_size); + nrefblocke = ((aligned_total_size - aligned_cur_size) + meta_size + + creftable_length + cluster_size) + / (cluster_size - rces - + rces * sizeof(uint64_t) / cluster_size); + meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; + + /* total size of the new refcount table (again, may be too much because + * it assumes that the new area is not covered by any refcount blocks + * yet) */ + nreftablee = s->max_refcount_table_index + 1 + + nrefblocke / refblock_size; + nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); + meta_size += nreftablee * sizeof(uint64_t); + + return (aligned_total_size - aligned_cur_size) + meta_size; + } } static int qcow2_create2(const char *filename, int64_t total_size, @@ -2210,7 +2268,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, int ret; if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { - uint64_t file_size = qcow2_calc_size_usage(total_size, cluster_bits, + uint64_t file_size = qcow2_calc_size_usage(NULL, 0, total_size, + cluster_bits, refcount_order); qemu_opt_set_number(opts, BLOCK_OPT_SIZE, file_size, &error_abort);