From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:51032) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOW3t-0003qw-7Y for qemu-devel@nongnu.org; Wed, 17 Oct 2012 12:01:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TOW3l-0003C5-Hi for qemu-devel@nongnu.org; Wed, 17 Oct 2012 12:01:36 -0400 Received: from nodalink.pck.nerim.net ([62.212.105.220]:48805 helo=paradis.irqsave.net) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOW3l-0003BZ-3s for qemu-devel@nongnu.org; Wed, 17 Oct 2012 12:01:29 -0400 From: =?UTF-8?q?Beno=C3=AEt=20Canet?= Date: Wed, 17 Oct 2012 18:00:16 +0200 Message-Id: <1350489629-1838-8-git-send-email-benoit@irqsave.net> In-Reply-To: <1350489629-1838-1-git-send-email-benoit@irqsave.net> References: <1350489629-1838-1-git-send-email-benoit@irqsave.net> Subject: [Qemu-devel] [RFC V2 07/20] qcow2: Add qcow2_dedup_write_new_hashes. List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: kwolf@redhat.com, =?UTF-8?q?Beno=C3=AEt=20Canet?= , stefanha@redhat.com --- block/qcow2-dedup.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++ block/qcow2.h | 3 + 2 files changed, 164 insertions(+) diff --git a/block/qcow2-dedup.c b/block/qcow2-dedup.c index ae45130..50d61f2 100644 --- a/block/qcow2-dedup.c +++ b/block/qcow2-dedup.c @@ -467,3 +467,164 @@ exit: } return deduped_clusters_nr * s->cluster_sectors - begining_index; } + +/* Read a hash cluster from disk or allocate it if it doesn't exist yet + * + * @in_dedup_table_index: The index of the hash cluster in the dedup table + * @hash_block: the place where the cluster will be loaded + * @create: set to true if dedup table entries must be created + * when not found + * @ret: 0 on success, errno on error + */ +static int get_hash_cluster_from_cache(BlockDriverState *bs, + int32_t in_dedup_table_index, + uint8_t **hash_block, bool create) +{ + BDRVQcowState *s = bs->opaque; + int ret = -ENOSPC; + uint64_t hash_cluster_offset; + + if (in_dedup_table_index > s->dedup_table_size) { + goto fail; + } + + if (!s->dedup_table[in_dedup_table_index] && create) { + /* the dedup table entry doesn't exists and we must create it */ + uint64_t data64; + /* allocate a new dedup table cluster */ + hash_cluster_offset = qcow2_alloc_clusters(bs, s->cluster_size); + s->dedup_table[in_dedup_table_index] = hash_cluster_offset; + /* get an empty cluster from the dedup cache */ + ret = qcow2_cache_get_empty(bs, s->dedup_cluster_cache, + hash_cluster_offset, + (void **) hash_block); + if (ret < 0) { + goto fail; + } + /* clear it */ + memset(*hash_block, 0, s->cluster_size); + /* write the new block offset in the dedup table */ + data64 = cpu_to_be64(hash_cluster_offset); + ret = bdrv_pwrite_sync(bs->file, + s->dedup_table_offset * BDRV_SECTOR_SIZE + + in_dedup_table_index * sizeof(uint64_t), + &data64, sizeof(data64)); + if (ret < 0) { + goto fail; + } + } else if (!s->dedup_table[in_dedup_table_index] && !create) { + /* the dedup table entry doesn't exits and we must _not_ create */ + *hash_block = g_malloc0(s->cluster_size); + return 1; + } else { + /* the entry exists get it */ + hash_cluster_offset = s->dedup_table[in_dedup_table_index]; + ret = qcow2_cache_get(bs, s->dedup_cluster_cache, + hash_cluster_offset, (void **) hash_block); + if (ret < 0) { + goto fail; + } + } + + return 0; + +fail: + return ret; +} + +/* Read/write a given hash and cluster_offset from/to the dedup table + * + * This function doesn't flush the dedup cache to disk + * + * @hash: the hash to read or store + * @physical_cluster_offset: offset of the cluster in QCOW2 file (in sectors) + * @write: true to write, false to read + * @ret: 0 on succes, errno on error + */ +static int qcow2_dedup_read_write_hash(BlockDriverState *bs, + uint8_t **hash, + uint64_t physical_cluster_offset, + bool write) +{ + BDRVQcowState *s = bs->opaque; + uint8_t *hash_block = NULL; + int ret; + int64_t cluster_number; + int64_t in_dedup_table_index; + int hash_block_offset; + int nb_entries_by_dedup_table_cluster = s->cluster_size / sizeof(uint64_t); + int nb_hash_in_dedup_cluster = s->cluster_size / HASH_LENGTH; + + cluster_number = physical_cluster_offset / s->cluster_sectors; + in_dedup_table_index = cluster_number / + (nb_entries_by_dedup_table_cluster * + nb_hash_in_dedup_cluster); + + /* if we are doing a write this will create missing dedup table entries */ + ret = get_hash_cluster_from_cache(bs, in_dedup_table_index, + &hash_block, write); + if (ret < 0) { + return ret; + } + + hash_block_offset = (cluster_number % nb_hash_in_dedup_cluster) * + HASH_LENGTH; + if (write) { + memcpy(hash_block + hash_block_offset , *hash, HASH_LENGTH); + } else { + *hash = g_malloc(HASH_LENGTH); + memcpy(*hash, hash_block + hash_block_offset, HASH_LENGTH); + } + + if (!ret) { + qcow2_cache_put(bs, s->dedup_cluster_cache, (void **) &hash_block); + } + + return 0; +} + +/* This function write the hashes of the clusters which are not duplicated + * + * @physical_cluster_offset: offset of the first cluster (in sectors) + * @nr: the number of clusters to do + * @ret: 0 on succes, errno on error + */ +int qcow2_dedup_write_new_hashes(BlockDriverState *bs, + uint64_t physical_cluster_offset, + int nr) +{ + int ret; + BDRVQcowState *s = bs->opaque; + QCowHashElement *dedup_hash, *next_dedup_hash; + QCowHashNode *hash_node; + + int i = 0; + + QTAILQ_FOREACH_SAFE(dedup_hash, &s->undedupable_hashes, + next, next_dedup_hash) { + ret = qcow2_dedup_read_write_hash(bs, &dedup_hash->hash, + physical_cluster_offset + i * + s->cluster_sectors, + true); + if (ret < 0) { + goto fail; + } + + hash_node = qcow2_dedup_lookup_hash_in_rb_tree(bs, dedup_hash->hash); + if (hash_node->offset == (uint64_t) 1 << + (sizeof(uint64_t) * 8 - 1)) { + hash_node->offset = physical_cluster_offset + + i * s->cluster_sectors; + } + QTAILQ_REMOVE(&s->undedupable_hashes, dedup_hash, next); + g_free(dedup_hash); + i++; + if (i == nr) { + break; + } + } + + ret = qcow2_cache_flush(bs, s->dedup_cluster_cache); +fail: + return ret; +} diff --git a/block/qcow2.h b/block/qcow2.h index 6292d4e..58aee77 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -372,5 +372,8 @@ int qcow2_dedup(BlockDriverState *bs, int *skip_clusters_nr, int *next_non_dedupable_sectors_nr, uint8_t **next_call_first_hash); +int qcow2_dedup_write_new_hashes(BlockDriverState *bs, + uint64_t cluster_offset, + int count); #endif -- 1.7.10.4