summaryrefslogtreecommitdiff
path: root/block/qcow2-refcount.c
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2011-01-10 17:17:28 +0100
committerKevin Wolf <kwolf@redhat.com>2011-01-24 16:41:49 +0100
commit29c1a7301af752de6721e031d31faa48887204bd (patch)
tree9a094ebc84619f99d14166f596094042a3d358eb /block/qcow2-refcount.c
parent493810940bfaad0fd5dd9bfb79cdc89519f89588 (diff)
downloadqemu-29c1a7301af752de6721e031d31faa48887204bd.tar.gz
qemu-29c1a7301af752de6721e031d31faa48887204bd.tar.bz2
qemu-29c1a7301af752de6721e031d31faa48887204bd.zip
qcow2: Use QcowCache
Use the new functions of qcow2-cache.c for everything that works on refcount block and L2 tables. Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'block/qcow2-refcount.c')
-rw-r--r--block/qcow2-refcount.c260
1 files changed, 107 insertions, 153 deletions
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index a10453c875..e37e2268af 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -32,27 +32,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
int addend);
-static int cache_refcount_updates = 0;
-
-static int write_refcount_block(BlockDriverState *bs)
-{
- BDRVQcowState *s = bs->opaque;
- size_t size = s->cluster_size;
-
- if (s->refcount_block_cache_offset == 0) {
- return 0;
- }
-
- BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE);
- if (bdrv_pwrite_sync(bs->file, s->refcount_block_cache_offset,
- s->refcount_block_cache, size) < 0)
- {
- return -EIO;
- }
-
- return 0;
-}
-
/*********************************************************/
/* refcount handling */
@@ -61,7 +40,6 @@ int qcow2_refcount_init(BlockDriverState *bs)
BDRVQcowState *s = bs->opaque;
int ret, refcount_table_size2, i;
- s->refcount_block_cache = qemu_malloc(s->cluster_size);
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
s->refcount_table = qemu_malloc(refcount_table_size2);
if (s->refcount_table_size > 0) {
@@ -81,34 +59,22 @@ int qcow2_refcount_init(BlockDriverState *bs)
void qcow2_refcount_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
- qemu_free(s->refcount_block_cache);
qemu_free(s->refcount_table);
}
static int load_refcount_block(BlockDriverState *bs,
- int64_t refcount_block_offset)
+ int64_t refcount_block_offset,
+ void **refcount_block)
{
BDRVQcowState *s = bs->opaque;
int ret;
- if (cache_refcount_updates) {
- ret = write_refcount_block(bs);
- if (ret < 0) {
- return ret;
- }
- }
-
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
- ret = bdrv_pread(bs->file, refcount_block_offset, s->refcount_block_cache,
- s->cluster_size);
- if (ret < 0) {
- s->refcount_block_cache_offset = 0;
- return ret;
- }
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
+ refcount_block);
- s->refcount_block_cache_offset = refcount_block_offset;
- return 0;
+ return ret;
}
/*
@@ -122,6 +88,8 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
int refcount_table_index, block_index;
int64_t refcount_block_offset;
int ret;
+ uint16_t *refcount_block;
+ uint16_t refcount;
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
if (refcount_table_index >= s->refcount_table_size)
@@ -129,16 +97,24 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
refcount_block_offset = s->refcount_table[refcount_table_index];
if (!refcount_block_offset)
return 0;
- if (refcount_block_offset != s->refcount_block_cache_offset) {
- /* better than nothing: return allocated if read error */
- ret = load_refcount_block(bs, refcount_block_offset);
- if (ret < 0) {
- return ret;
- }
+
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ return ret;
}
+
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
- return be16_to_cpu(s->refcount_block_cache[block_index]);
+ refcount = be16_to_cpu(refcount_block[block_index]);
+
+ ret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return refcount;
}
/*
@@ -174,9 +150,10 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
* Loads a refcount block. If it doesn't exist yet, it is allocated first
* (including growing the refcount table if needed).
*
- * Returns the offset of the refcount block on success or -errno in error case
+ * Returns 0 on success or -errno in error case
*/
-static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
+static int alloc_refcount_block(BlockDriverState *bs,
+ int64_t cluster_index, uint16_t **refcount_block)
{
BDRVQcowState *s = bs->opaque;
unsigned int refcount_table_index;
@@ -194,13 +171,8 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
/* If it's already there, we're done */
if (refcount_block_offset) {
- if (refcount_block_offset != s->refcount_block_cache_offset) {
- ret = load_refcount_block(bs, refcount_block_offset);
- if (ret < 0) {
- return ret;
- }
- }
- return refcount_block_offset;
+ return load_refcount_block(bs, refcount_block_offset,
+ (void**) refcount_block);
}
}
@@ -226,12 +198,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
* refcount block into the cache
*/
- if (cache_refcount_updates) {
- ret = write_refcount_block(bs);
- if (ret < 0) {
- return ret;
- }
- }
+ *refcount_block = NULL;
+
+ /* We write to the refcount table, so we might depend on L2 tables */
+ qcow2_cache_flush(bs, s->l2_table_cache);
/* Allocate the refcount block itself and mark it as used */
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
@@ -247,13 +217,18 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
/* Zero the new refcount block before updating it */
- memset(s->refcount_block_cache, 0, s->cluster_size);
- s->refcount_block_cache_offset = new_block;
+ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
+ (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ memset(*refcount_block, 0, s->cluster_size);
/* The block describes itself, need to update the cache */
int block_index = (new_block >> s->cluster_bits) &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
- s->refcount_block_cache[block_index] = cpu_to_be16(1);
+ (*refcount_block)[block_index] = cpu_to_be16(1);
} else {
/* Described somewhere else. This can recurse at most twice before we
* arrive at a block that describes itself. */
@@ -266,14 +241,19 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
/* Initialize the new refcount block only after updating its refcount,
* update_refcount uses the refcount cache itself */
- memset(s->refcount_block_cache, 0, s->cluster_size);
- s->refcount_block_cache_offset = new_block;
+ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
+ (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ memset(*refcount_block, 0, s->cluster_size);
}
/* Now the new refcount block needs to be written to disk */
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE);
- ret = bdrv_pwrite_sync(bs->file, new_block, s->refcount_block_cache,
- s->cluster_size);
+ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block);
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
goto fail_block;
}
@@ -290,7 +270,12 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
}
s->refcount_table[refcount_table_index] = new_block;
- return new_block;
+ return 0;
+ }
+
+ ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
}
/*
@@ -410,9 +395,9 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
s->free_cluster_index = old_free_cluster_index;
- ret = load_refcount_block(bs, new_block);
+ ret = load_refcount_block(bs, new_block, (void**) refcount_block);
if (ret < 0) {
- goto fail_block;
+ return ret;
}
return new_block;
@@ -420,41 +405,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
fail_table:
qemu_free(new_table);
fail_block:
- s->refcount_block_cache_offset = 0;
- return ret;
-}
-
-#define REFCOUNTS_PER_SECTOR (512 >> REFCOUNT_SHIFT)
-static int write_refcount_block_entries(BlockDriverState *bs,
- int64_t refcount_block_offset, int first_index, int last_index)
-{
- BDRVQcowState *s = bs->opaque;
- size_t size;
- int ret;
-
- if (cache_refcount_updates) {
- return 0;
- }
-
- if (first_index < 0) {
- return 0;
- }
-
- first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
- last_index = (last_index + REFCOUNTS_PER_SECTOR)
- & ~(REFCOUNTS_PER_SECTOR - 1);
-
- size = (last_index - first_index) << REFCOUNT_SHIFT;
-
- BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
- ret = bdrv_pwrite(bs->file,
- refcount_block_offset + (first_index << REFCOUNT_SHIFT),
- &s->refcount_block_cache[first_index], size);
- if (ret < 0) {
- return ret;
+ if (*refcount_block != NULL) {
+ qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
}
-
- return 0;
+ return ret;
}
/* XXX: cache several refcount block clusters ? */
@@ -463,9 +417,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
int64_t start, last, cluster_offset;
- int64_t refcount_block_offset = 0;
- int64_t table_index = -1, old_table_index;
- int first_index = -1, last_index = -1;
+ uint16_t *refcount_block = NULL;
+ int64_t old_table_index = -1;
int ret;
#ifdef DEBUG_ALLOC2
@@ -478,6 +431,11 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
return 0;
}
+ if (addend < 0) {
+ qcow2_cache_set_dependency(bs, s->refcount_block_cache,
+ s->l2_table_cache);
+ }
+
start = offset & ~(s->cluster_size - 1);
last = (offset + length - 1) & ~(s->cluster_size - 1);
for(cluster_offset = start; cluster_offset <= last;
@@ -485,42 +443,33 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
{
int block_index, refcount;
int64_t cluster_index = cluster_offset >> s->cluster_bits;
- int64_t new_block;
+ int64_t table_index =
+ cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
- /* Only write refcount block to disk when we are done with it */
- old_table_index = table_index;
- table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
- if ((old_table_index >= 0) && (table_index != old_table_index)) {
+ /* Load the refcount block and allocate it if needed */
+ if (table_index != old_table_index) {
+ if (refcount_block) {
+ ret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
- ret = write_refcount_block_entries(bs, refcount_block_offset,
- first_index, last_index);
+ ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
if (ret < 0) {
- return ret;
+ goto fail;
}
-
- first_index = -1;
- last_index = -1;
}
+ old_table_index = table_index;
- /* Load the refcount block and allocate it if needed */
- new_block = alloc_refcount_block(bs, cluster_index);
- if (new_block < 0) {
- ret = new_block;
- goto fail;
- }
- refcount_block_offset = new_block;
+ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block);
/* we can update the count and save it */
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
- if (first_index == -1 || block_index < first_index) {
- first_index = block_index;
- }
- if (block_index > last_index) {
- last_index = block_index;
- }
- refcount = be16_to_cpu(s->refcount_block_cache[block_index]);
+ refcount = be16_to_cpu(refcount_block[block_index]);
refcount += addend;
if (refcount < 0 || refcount > 0xffff) {
ret = -EINVAL;
@@ -529,17 +478,16 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
if (refcount == 0 && cluster_index < s->free_cluster_index) {
s->free_cluster_index = cluster_index;
}
- s->refcount_block_cache[block_index] = cpu_to_be16(refcount);
+ refcount_block[block_index] = cpu_to_be16(refcount);
}
ret = 0;
fail:
-
/* Write last changed block to disk */
- if (refcount_block_offset != 0) {
+ if (refcount_block) {
int wret;
- wret = write_refcount_block_entries(bs, refcount_block_offset,
- first_index, last_index);
+ wret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
if (wret < 0) {
return ret < 0 ? ret : wret;
}
@@ -758,9 +706,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
int64_t old_offset, old_l2_offset;
int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount;
-
- qcow2_l2_cache_reset(bs);
- cache_refcount_updates = 1;
+ int ret;
l2_table = NULL;
l1_table = NULL;
@@ -784,7 +730,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
l2_size = s->l2_size * sizeof(uint64_t);
- l2_table = qemu_malloc(l2_size);
l1_modified = 0;
for(i = 0; i < l1_size; i++) {
l2_offset = l1_table[i];
@@ -792,8 +737,13 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
old_l2_offset = l2_offset;
l2_offset &= ~QCOW_OFLAG_COPIED;
l2_modified = 0;
- if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size)
+
+ ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
+ (void**) &l2_table);
+ if (ret < 0) {
goto fail;
+ }
+
for(j = 0; j < s->l2_size; j++) {
offset = be64_to_cpu(l2_table[j]);
if (offset != 0) {
@@ -833,17 +783,23 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
offset |= QCOW_OFLAG_COPIED;
}
if (offset != old_offset) {
+ if (addend > 0) {
+ qcow2_cache_set_dependency(bs, s->l2_table_cache,
+ s->refcount_block_cache);
+ }
l2_table[j] = cpu_to_be64(offset);
l2_modified = 1;
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
}
}
}
- if (l2_modified) {
- if (bdrv_pwrite_sync(bs->file,
- l2_offset, l2_table, l2_size) < 0)
- goto fail;
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ goto fail;
}
+
if (addend != 0) {
refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
} else {
@@ -871,16 +827,14 @@ 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(bs);
return 0;
fail:
+ if (l2_table) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ }
+
if (l1_allocated)
qemu_free(l1_table);
- qemu_free(l2_table);
- cache_refcount_updates = 0;
- write_refcount_block(bs);
return -EIO;
}