From patchwork Mon Aug 17 13:50:10 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 31518 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by bilbo.ozlabs.org (Postfix) with ESMTPS id F3562B7080 for ; Tue, 18 Aug 2009 00:08:07 +1000 (EST) Received: from localhost ([127.0.0.1]:33930 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Md2sT-0002D5-RN for incoming@patchwork.ozlabs.org; Mon, 17 Aug 2009 10:08:01 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Md2cP-0002Qi-Qr for qemu-devel@nongnu.org; Mon, 17 Aug 2009 09:51:25 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Md2cK-0002O1-7r for qemu-devel@nongnu.org; Mon, 17 Aug 2009 09:51:24 -0400 Received: from [199.232.76.173] (port=38002 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Md2cJ-0002Nt-9i for qemu-devel@nongnu.org; Mon, 17 Aug 2009 09:51:19 -0400 Received: from mx2.redhat.com ([66.187.237.31]:32816) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1Md2cI-0003MT-Im for qemu-devel@nongnu.org; Mon, 17 Aug 2009 09:51:18 -0400 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n7HDpHIF032301 for ; Mon, 17 Aug 2009 09:51:17 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n7HDpGGr031394; Mon, 17 Aug 2009 09:51:16 -0400 Received: from localhost.localdomain (dhcp-5-217.str.redhat.com [10.32.5.217]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n7HDpE75001956; Mon, 17 Aug 2009 09:51:15 -0400 From: Kevin Wolf To: qemu-devel@nongnu.org Date: Mon, 17 Aug 2009 15:50:10 +0200 Message-Id: <1250517010-11352-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.58 on 172.16.27.26 X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6 (newer, 3) Cc: Kevin Wolf Subject: [Qemu-devel] [PATCH v2] qcow2: Metadata preallocation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This introduces a qemu-img create option for qcow2 which allows the metadata to be preallocated, i.e. clusters are reserved in the refcount table and L1/L2 tables, but no data is written to them. Metadata is quite small, so this happens in almost no time. Especially with qcow2 on virtio this helps to gain a bit of performance during the initial writes. However, as soon as create a snapshot, we're back to the normal slow speed, obviously. So this isn't the real fix, but kind of a cheat while we're still having trouble with qcow2 on virtio. Note that the option is disabled by default and needs to be specified explicitly using qemu-img create -f qcow2 -o preallocation=metadata. Signed-off-by: Kevin Wolf --- v2: Use bdrv_truncate() to set the right file size block/qcow2.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- block_int.h | 1 + 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index a5bf205..a9c39bd 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -638,9 +638,54 @@ static int get_bits_from_size(size_t size) return res; } + +static int preallocate(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t cluster_offset; + uint64_t nb_sectors; + uint64_t offset; + int num; + QCowL2Meta meta; + + nb_sectors = bdrv_getlength(bs) >> 9; + offset = 0; + + while (nb_sectors) { + num = MIN(nb_sectors, INT_MAX >> 9); + cluster_offset = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, + &meta); + + if (cluster_offset == 0) { + return -1; + } + + if (qcow2_alloc_cluster_link_l2(bs, cluster_offset, &meta) < 0) { + qcow2_free_any_clusters(bs, cluster_offset, meta.nb_clusters); + return -1; + } + + /* TODO Preallocate data if requested */ + + nb_sectors -= num; + offset += num << 9; + } + + /* + * It is expected that the image file is large enough to actually contain + * all of the allocated clusters (otherwise we get failing reads after + * EOF). Extend the image to the last allocated sector. + */ + if (cluster_offset != 0) { + bdrv_truncate(s->hd, cluster_offset + (num << 9)); + } + + return 0; +} + static int qcow_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, - int flags, size_t cluster_size) + int flags, size_t cluster_size, int prealloc) { int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits; @@ -762,6 +807,16 @@ static int qcow_create2(const char *filename, int64_t total_size, qemu_free(s->refcount_table); qemu_free(s->refcount_block); close(fd); + + /* Preallocate metadata */ + if (prealloc) { + BlockDriverState *bs; + bs = bdrv_new(""); + bdrv_open(bs, filename, BDRV_O_CACHE_WB); + preallocate(bs); + bdrv_close(bs); + } + return 0; } @@ -772,6 +827,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) uint64_t sectors = 0; int flags = 0; size_t cluster_size = 65536; + int prealloc = 0; /* Read out options */ while (options && options->name) { @@ -787,12 +843,28 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) if (options->value.n) { cluster_size = options->value.n; } + } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) { + if (!options->value.s || !strcmp(options->value.s, "off")) { + prealloc = 0; + } else if (!strcmp(options->value.s, "metadata")) { + prealloc = 1; + } else { + fprintf(stderr, "Invalid preallocation mode: '%s'\n", + options->value.s); + return -EINVAL; + } } options++; } + if (backing_file && prealloc) { + fprintf(stderr, "Backing file and preallocation cannot be used at " + "the same time\n"); + return -EINVAL; + } + return qcow_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size); + cluster_size, prealloc); } static int qcow_make_empty(BlockDriverState *bs) @@ -982,6 +1054,11 @@ static QEMUOptionParameter qcow_create_options[] = { .type = OPT_SIZE, .help = "qcow2 cluster size" }, + { + .name = BLOCK_OPT_PREALLOC, + .type = OPT_STRING, + .help = "Preallocation mode (allowed values: off, metadata)" + }, { NULL } }; diff --git a/block_int.h b/block_int.h index 8898d91..0902fd4 100644 --- a/block_int.h +++ b/block_int.h @@ -37,6 +37,7 @@ #define BLOCK_OPT_BACKING_FILE "backing_file" #define BLOCK_OPT_BACKING_FMT "backing_fmt" #define BLOCK_OPT_CLUSTER_SIZE "cluster_size" +#define BLOCK_OPT_PREALLOC "preallocation" typedef struct AIOPool { void (*cancel)(BlockDriverAIOCB *acb);