From patchwork Wed Oct 17 16:00:25 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Beno=C3=AEt_Canet?= X-Patchwork-Id: 192089 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 866752C0084 for ; Thu, 18 Oct 2012 03:22:29 +1100 (EST) Received: from localhost ([::1]:36881 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOWO3-0004V3-Jr for incoming@patchwork.ozlabs.org; Wed, 17 Oct 2012 12:22:27 -0400 Received: from eggs.gnu.org ([208.118.235.92]:51295) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOW4Z-00056F-K1 for qemu-devel@nongnu.org; Wed, 17 Oct 2012 12:02:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TOW4N-0003L2-Nz for qemu-devel@nongnu.org; Wed, 17 Oct 2012 12:02:19 -0400 Received: from nodalink.pck.nerim.net ([62.212.105.220]:48824 helo=paradis.irqsave.net) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOW4N-0003K0-BV for qemu-devel@nongnu.org; Wed, 17 Oct 2012 12:02:07 -0400 Received: by paradis.irqsave.net (Postfix, from userid 1002) id AD88D8742F2; Wed, 17 Oct 2012 18:07:36 +0200 (CEST) Received: from localhost.localdomain (unknown [192.168.77.1]) by paradis.irqsave.net (Postfix) with ESMTP id 34BBD8742FC; Wed, 17 Oct 2012 18:05:57 +0200 (CEST) From: =?UTF-8?q?Beno=C3=AEt=20Canet?= To: qemu-devel@nongnu.org Date: Wed, 17 Oct 2012 18:00:25 +0200 Message-Id: <1350489629-1838-17-git-send-email-benoit@irqsave.net> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1350489629-1838-1-git-send-email-benoit@irqsave.net> References: <1350489629-1838-1-git-send-email-benoit@irqsave.net> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 62.212.105.220 Cc: kwolf@redhat.com, =?UTF-8?q?Beno=C3=AEt=20Canet?= , stefanha@redhat.com Subject: [Qemu-devel] [RFC V2 16/20] qcow2: Allow creation of images using deduplication. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org todo: Change qemu-img output so it reflect the dedup cluster size. --- block/qcow2.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- block/qcow2.h | 2 ++ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index b11b6a7..c6879ea 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -277,6 +277,11 @@ static int qcow2_mark_dirty(BlockDriverState *bs) return qcow2_add_feature(bs, QCOW2_INCOMPAT_DIRTY); } +static int qcow2_activate_dedup(BlockDriverState *bs) +{ + return qcow2_add_feature(bs, QCOW2_INCOMPAT_DEDUP); +} + /* * Clears an incompatible feature bit and flushes before if necessary. * Only call this function when there are no pending requests, it does not @@ -911,6 +916,11 @@ static void qcow2_close(BlockDriverState *bs) BDRVQcowState *s = bs->opaque; g_free(s->l1_table); + if (s->has_dedup) { + qcow2_cache_flush(bs, s->dedup_cluster_cache); + qcow2_cache_destroy(bs, s->dedup_cluster_cache); + } + qcow2_cache_flush(bs, s->l2_table_cache); qcow2_cache_flush(bs, s->refcount_block_cache); @@ -1229,7 +1239,8 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, int prealloc, - QEMUOptionParameter *options, int version) + QEMUOptionParameter *options, int version, + bool dedup) { /* Calculate cluster_bits */ int cluster_bits; @@ -1256,6 +1267,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, * size for any qcow2 image. */ BlockDriverState* bs; + BDRVQcowState *s; QCowHeader header; uint8_t* refcount_table; int ret; @@ -1338,6 +1350,26 @@ static int qcow2_create2(const char *filename, int64_t total_size, goto out; } + if (dedup) { + s = bs->opaque; + s->has_dedup = true; + s->dedup_table_offset = qcow2_alloc_clusters(bs, cluster_size); + s->dedup_table_size = cluster_size / sizeof(uint64_t); + + ret = qcow2_activate_dedup(bs); + if (ret < 0) { + goto out; + } + + ret = qcow2_update_header(bs); + if (ret < 0) { + goto out; + } + + /* minimal init */ + s->dedup_cluster_cache = qcow2_cache_create(bs, DEDUP_CACHE_SIZE); + } + /* Want a backing file? There you go.*/ if (backing_file) { ret = bdrv_change_backing_file(bs, backing_file, backing_format); @@ -1363,15 +1395,30 @@ out: return ret; } +static int qcow2_warn_if_version_3_is_needed(int version, + bool has_feature, + const char *feature) +{ + if (version < 3 && has_feature) { + fprintf(stderr, "%s only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)\n", + feature); + return -EINVAL; + } + return 0; +} + static int qcow2_create(const char *filename, QEMUOptionParameter *options) { const char *backing_file = NULL; const char *backing_fmt = NULL; uint64_t sectors = 0; int flags = 0; + int ret; size_t cluster_size = DEFAULT_CLUSTER_SIZE; int prealloc = 0; int version = 2; + bool dedup = false; /* Read out options */ while (options && options->name) { @@ -1409,24 +1456,43 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) } } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) { flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0; + } else if (!strcmp(options->name, BLOCK_OPT_DEDUP)) { + dedup = options->value.n ? true : false; } options++; } + if (dedup && cluster_size != DEFAULT_CLUSTER_SIZE) { + fprintf(stderr, "Deduplication cluster size must be 4096\n"); + return -EINVAL; + } + + if (dedup) { + cluster_size = 4096; + } + if (backing_file && prealloc) { fprintf(stderr, "Backing file and preallocation cannot be used at " "the same time\n"); return -EINVAL; } - if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { - fprintf(stderr, "Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)\n"); - return -EINVAL; + ret = qcow2_warn_if_version_3_is_needed(version, + flags & BLOCK_FLAG_LAZY_REFCOUNTS, + "Lazy refcounts"); + if (ret < 0) { + return ret; + } + ret = qcow2_warn_if_version_3_is_needed(version, + dedup, + "Deduplication"); + if (ret < 0) { + return ret; } return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size, prealloc, options, version); + cluster_size, prealloc, options, version, + dedup); } static int qcow2_make_empty(BlockDriverState *bs) @@ -1729,6 +1795,11 @@ static QEMUOptionParameter qcow2_create_options[] = { .type = OPT_FLAG, .help = "Postpone refcount updates", }, + { + .name = BLOCK_OPT_DEDUP, + .type = OPT_FLAG, + .help = "Live deduplication", + }, { NULL } }; diff --git a/block/qcow2.h b/block/qcow2.h index 5ecea94..0b999fb 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -57,6 +57,8 @@ /* Must be at least 4 to cover all cases of refcount table growth */ #define REFCOUNT_CACHE_SIZE 4 +#define DEDUP_CACHE_SIZE 4 + #define DEFAULT_CLUSTER_SIZE 65536 /* Red Black Tree deduplication node */