aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2009-06-26 20:19:38 +0200
committerAnthony Liguori <aliguori@us.ibm.com>2009-06-29 14:18:07 -0500
commit3b88e52b41fe77728b4accb68e14bed98bdc75d3 (patch)
treebb12b001b4a7334cac55fd0e4c3eea3b77b46133
parent22afa7b5b67bb8bc58939d100363ee6753a50467 (diff)
qcow2: Cache refcount blocks during snapshot creation
The really time consuming part of snapshotting is to adjust the reference count of all clusters. Currently after each adjusted cluster the refcount block is written to disk. Don't write each single byte immediately to disk but cache all writes to the refcount block and write them out once we're done with the block. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
-rw-r--r--block/qcow2-refcount.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index dd6e293d05..d42c6e6461 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -31,6 +31,26 @@ static int update_refcount(BlockDriverState *bs,
int64_t offset, int64_t length,
int addend);
+
+static int cache_refcount_updates = 0;
+
+static int write_refcount_block(BDRVQcowState *s)
+{
+ size_t size = s->cluster_size;
+
+ if (s->refcount_block_cache_offset == 0) {
+ return 0;
+ }
+
+ if (bdrv_pwrite(s->hd, s->refcount_block_cache_offset,
+ s->refcount_block_cache, size) != size)
+ {
+ return -EIO;
+ }
+
+ return 0;
+}
+
/*********************************************************/
/* refcount handling */
@@ -68,6 +88,11 @@ static int load_refcount_block(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
int ret;
+
+ if (cache_refcount_updates) {
+ write_refcount_block(s);
+ }
+
ret = bdrv_pread(s->hd, refcount_block_offset, s->refcount_block_cache,
s->cluster_size);
if (ret != s->cluster_size)
@@ -169,6 +194,7 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
int64_t offset, refcount_block_offset;
int ret, refcount_table_index;
uint64_t data64;
+ int cache = cache_refcount_updates;
/* Find L1 index and grow refcount table if needed */
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
@@ -181,6 +207,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
/* Load or allocate the refcount block */
refcount_block_offset = s->refcount_table[refcount_table_index];
if (!refcount_block_offset) {
+ if (cache_refcount_updates) {
+ write_refcount_block(s);
+ cache_refcount_updates = 0;
+ }
/* create a new refcount block */
/* Note: we cannot update the refcount now to avoid recursion */
offset = alloc_clusters_noref(bs, s->cluster_size);
@@ -199,6 +229,7 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
refcount_block_offset = offset;
s->refcount_block_cache_offset = offset;
update_refcount(bs, offset, s->cluster_size, 1);
+ cache_refcount_updates = cache;
} else {
if (refcount_block_offset != s->refcount_block_cache_offset) {
if (load_refcount_block(bs, refcount_block_offset) < 0)
@@ -215,6 +246,10 @@ static int write_refcount_block_entries(BDRVQcowState *s,
{
size_t size;
+ if (cache_refcount_updates) {
+ return 0;
+ }
+
first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
last_index = (last_index + REFCOUNTS_PER_SECTOR)
& ~(REFCOUNTS_PER_SECTOR - 1);
@@ -471,6 +506,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount;
qcow2_l2_cache_reset(bs);
+ cache_refcount_updates = 1;
l2_table = NULL;
l1_table = NULL;
@@ -563,11 +599,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
if (l1_allocated)
qemu_free(l1_table);
qemu_free(l2_table);
+ cache_refcount_updates = 0;
+ write_refcount_block(s);
return 0;
fail:
if (l1_allocated)
qemu_free(l1_table);
qemu_free(l2_table);
+ cache_refcount_updates = 0;
+ write_refcount_block(s);
return -EIO;
}