Will use block cache as common interface, so move qcow2-cache.c to block-cache.c,
Signed-off-by: Dong Xu Wang <wdon...@linux.vnet.ibm.com> --- block/Makefile.objs | 3 +- block/block-cache.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++++ block/qcow2-cache.c | 323 ---------------------------------------------------- block/qcow2.h | 2 +- 4 files changed, 326 insertions(+), 325 deletions(-) create mode 100644 block/block-cache.c delete mode 100644 block/qcow2-cache.c diff --git a/block/Makefile.objs b/block/Makefile.objs index 4cf9aa4..69e5584 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -1,5 +1,6 @@ block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o -block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o +block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o +block-obj-y += block-cache.o block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-y += vhdx.o diff --git a/block/block-cache.c b/block/block-cache.c new file mode 100644 index 0000000..bc057a8 --- /dev/null +++ b/block/block-cache.c @@ -0,0 +1,323 @@ +/* + * L2/refcount table cache for the QCOW2 format + * + * Copyright (c) 2010 Kevin Wolf <kw...@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block/block_int.h" +#include "qemu-common.h" +#include "qcow2.h" +#include "trace.h" + +typedef struct BlockCachedTable { + void* table; + int64_t offset; + bool dirty; + int cache_hits; + int ref; +} BlockCachedTable; + +struct BlockCache { + BlockCachedTable* entries; + struct BlockCache* depends; + int size; + bool depends_on_flush; +}; + +BlockCache *block_cache_create(BlockDriverState *bs, int num_tables) +{ + BDRVQcowState *s = bs->opaque; + BlockCache *c; + int i; + + c = g_malloc0(sizeof(*c)); + c->size = num_tables; + c->entries = g_malloc0(sizeof(*c->entries) * num_tables); + + for (i = 0; i < c->size; i++) { + c->entries[i].table = qemu_blockalign(bs, s->cluster_size); + } + + return c; +} + +int block_cache_destroy(BlockDriverState* bs, BlockCache *c) +{ + int i; + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + qemu_vfree(c->entries[i].table); + } + + g_free(c->entries); + g_free(c); + + return 0; +} + +static int block_cache_flush_dependency(BlockDriverState *bs, BlockCache *c) +{ + int ret; + + ret = block_cache_flush(bs, c->depends); + if (ret < 0) { + return ret; + } + + c->depends = NULL; + c->depends_on_flush = false; + + return 0; +} + +static int block_cache_entry_flush(BlockDriverState *bs, BlockCache *c, int i) +{ + BDRVQcowState *s = bs->opaque; + int ret = 0; + + if (!c->entries[i].dirty || !c->entries[i].offset) { + return 0; + } + + trace_block_cache_entry_flush(qemu_coroutine_self(), + c == s->l2_table_cache, i); + + if (c->depends) { + ret = block_cache_flush_dependency(bs, c); + } else if (c->depends_on_flush) { + ret = bdrv_flush(bs->file); + if (ret >= 0) { + c->depends_on_flush = false; + } + } + + if (ret < 0) { + return ret; + } + + if (c == s->refcount_block_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); + } else if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + } + + ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, + s->cluster_size); + if (ret < 0) { + return ret; + } + + c->entries[i].dirty = false; + + return 0; +} + +int block_cache_flush(BlockDriverState *bs, BlockCache *c) +{ + BDRVQcowState *s = bs->opaque; + int result = 0; + int ret; + int i; + + trace_block_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); + + for (i = 0; i < c->size; i++) { + ret = block_cache_entry_flush(bs, c, i); + if (ret < 0 && result != -ENOSPC) { + result = ret; + } + } + + if (result == 0) { + ret = bdrv_flush(bs->file); + if (ret < 0) { + result = ret; + } + } + + return result; +} + +int block_cache_set_dependency(BlockDriverState *bs, BlockCache *c, + BlockCache *dependency) +{ + int ret; + + if (dependency->depends) { + ret = block_cache_flush_dependency(bs, dependency); + if (ret < 0) { + return ret; + } + } + + if (c->depends && (c->depends != dependency)) { + ret = block_cache_flush_dependency(bs, c); + if (ret < 0) { + return ret; + } + } + + c->depends = dependency; + return 0; +} + +void block_cache_depends_on_flush(BlockCache *c) +{ + c->depends_on_flush = true; +} + +static int block_cache_find_entry_to_replace(BlockCache *c) +{ + int i; + int min_count = INT_MAX; + int min_index = -1; + + + for (i = 0; i < c->size; i++) { + if (c->entries[i].ref) { + continue; + } + + if (c->entries[i].cache_hits < min_count) { + min_index = i; + min_count = c->entries[i].cache_hits; + } + + /* Give newer hits priority */ + /* TODO Check how to optimize the replacement strategy */ + c->entries[i].cache_hits /= 2; + } + + if (min_index == -1) { + /* This can't happen in current synchronous code, but leave the check + * here as a reminder for whoever starts using AIO with the cache */ + abort(); + } + return min_index; +} + +static int block_cache_do_get(BlockDriverState *bs, BlockCache *c, + uint64_t offset, void **table, bool read_from_disk) +{ + BDRVQcowState *s = bs->opaque; + int i; + int ret; + + trace_block_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, + offset, read_from_disk); + + /* Check if the table is already cached */ + for (i = 0; i < c->size; i++) { + if (c->entries[i].offset == offset) { + goto found; + } + } + + /* If not, write a table back and replace it */ + i = block_cache_find_entry_to_replace(c); + trace_block_cache_get_replace_entry(qemu_coroutine_self(), + c == s->l2_table_cache, i); + if (i < 0) { + return i; + } + + ret = block_cache_entry_flush(bs, c, i); + if (ret < 0) { + return ret; + } + + trace_block_cache_get_read(qemu_coroutine_self(), + c == s->l2_table_cache, i); + c->entries[i].offset = 0; + if (read_from_disk) { + if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + } + + ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); + if (ret < 0) { + return ret; + } + } + + /* Give the table some hits for the start so that it won't be replaced + * immediately. The number 32 is completely arbitrary. */ + c->entries[i].cache_hits = 32; + c->entries[i].offset = offset; + + /* And return the right table */ +found: + c->entries[i].cache_hits++; + c->entries[i].ref++; + *table = c->entries[i].table; + + trace_block_cache_get_done(qemu_coroutine_self(), + c == s->l2_table_cache, i); + + return 0; +} + +int block_cache_get(BlockDriverState *bs, BlockCache *c, uint64_t offset, + void **table) +{ + return block_cache_do_get(bs, c, offset, table, true); +} + +int block_cache_get_empty(BlockDriverState *bs, BlockCache *c, uint64_t offset, + void **table) +{ + return block_cache_do_get(bs, c, offset, table, false); +} + +int block_cache_put(BlockDriverState *bs, BlockCache *c, void **table) +{ + int i; + + for (i = 0; i < c->size; i++) { + if (c->entries[i].table == *table) { + goto found; + } + } + return -ENOENT; + +found: + c->entries[i].ref--; + *table = NULL; + + assert(c->entries[i].ref >= 0); + return 0; +} + +void block_cache_entry_mark_dirty(BlockCache *c, void *table) +{ + int i; + + for (i = 0; i < c->size; i++) { + if (c->entries[i].table == table) { + goto found; + } + } + abort(); + +found: + c->entries[i].dirty = true; +} diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c deleted file mode 100644 index bc057a8..0000000 --- a/block/qcow2-cache.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * L2/refcount table cache for the QCOW2 format - * - * Copyright (c) 2010 Kevin Wolf <kw...@redhat.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "block/block_int.h" -#include "qemu-common.h" -#include "qcow2.h" -#include "trace.h" - -typedef struct BlockCachedTable { - void* table; - int64_t offset; - bool dirty; - int cache_hits; - int ref; -} BlockCachedTable; - -struct BlockCache { - BlockCachedTable* entries; - struct BlockCache* depends; - int size; - bool depends_on_flush; -}; - -BlockCache *block_cache_create(BlockDriverState *bs, int num_tables) -{ - BDRVQcowState *s = bs->opaque; - BlockCache *c; - int i; - - c = g_malloc0(sizeof(*c)); - c->size = num_tables; - c->entries = g_malloc0(sizeof(*c->entries) * num_tables); - - for (i = 0; i < c->size; i++) { - c->entries[i].table = qemu_blockalign(bs, s->cluster_size); - } - - return c; -} - -int block_cache_destroy(BlockDriverState* bs, BlockCache *c) -{ - int i; - - for (i = 0; i < c->size; i++) { - assert(c->entries[i].ref == 0); - qemu_vfree(c->entries[i].table); - } - - g_free(c->entries); - g_free(c); - - return 0; -} - -static int block_cache_flush_dependency(BlockDriverState *bs, BlockCache *c) -{ - int ret; - - ret = block_cache_flush(bs, c->depends); - if (ret < 0) { - return ret; - } - - c->depends = NULL; - c->depends_on_flush = false; - - return 0; -} - -static int block_cache_entry_flush(BlockDriverState *bs, BlockCache *c, int i) -{ - BDRVQcowState *s = bs->opaque; - int ret = 0; - - if (!c->entries[i].dirty || !c->entries[i].offset) { - return 0; - } - - trace_block_cache_entry_flush(qemu_coroutine_self(), - c == s->l2_table_cache, i); - - if (c->depends) { - ret = block_cache_flush_dependency(bs, c); - } else if (c->depends_on_flush) { - ret = bdrv_flush(bs->file); - if (ret >= 0) { - c->depends_on_flush = false; - } - } - - if (ret < 0) { - return ret; - } - - if (c == s->refcount_block_cache) { - BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); - } else if (c == s->l2_table_cache) { - BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); - } - - ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, - s->cluster_size); - if (ret < 0) { - return ret; - } - - c->entries[i].dirty = false; - - return 0; -} - -int block_cache_flush(BlockDriverState *bs, BlockCache *c) -{ - BDRVQcowState *s = bs->opaque; - int result = 0; - int ret; - int i; - - trace_block_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); - - for (i = 0; i < c->size; i++) { - ret = block_cache_entry_flush(bs, c, i); - if (ret < 0 && result != -ENOSPC) { - result = ret; - } - } - - if (result == 0) { - ret = bdrv_flush(bs->file); - if (ret < 0) { - result = ret; - } - } - - return result; -} - -int block_cache_set_dependency(BlockDriverState *bs, BlockCache *c, - BlockCache *dependency) -{ - int ret; - - if (dependency->depends) { - ret = block_cache_flush_dependency(bs, dependency); - if (ret < 0) { - return ret; - } - } - - if (c->depends && (c->depends != dependency)) { - ret = block_cache_flush_dependency(bs, c); - if (ret < 0) { - return ret; - } - } - - c->depends = dependency; - return 0; -} - -void block_cache_depends_on_flush(BlockCache *c) -{ - c->depends_on_flush = true; -} - -static int block_cache_find_entry_to_replace(BlockCache *c) -{ - int i; - int min_count = INT_MAX; - int min_index = -1; - - - for (i = 0; i < c->size; i++) { - if (c->entries[i].ref) { - continue; - } - - if (c->entries[i].cache_hits < min_count) { - min_index = i; - min_count = c->entries[i].cache_hits; - } - - /* Give newer hits priority */ - /* TODO Check how to optimize the replacement strategy */ - c->entries[i].cache_hits /= 2; - } - - if (min_index == -1) { - /* This can't happen in current synchronous code, but leave the check - * here as a reminder for whoever starts using AIO with the cache */ - abort(); - } - return min_index; -} - -static int block_cache_do_get(BlockDriverState *bs, BlockCache *c, - uint64_t offset, void **table, bool read_from_disk) -{ - BDRVQcowState *s = bs->opaque; - int i; - int ret; - - trace_block_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, - offset, read_from_disk); - - /* Check if the table is already cached */ - for (i = 0; i < c->size; i++) { - if (c->entries[i].offset == offset) { - goto found; - } - } - - /* If not, write a table back and replace it */ - i = block_cache_find_entry_to_replace(c); - trace_block_cache_get_replace_entry(qemu_coroutine_self(), - c == s->l2_table_cache, i); - if (i < 0) { - return i; - } - - ret = block_cache_entry_flush(bs, c, i); - if (ret < 0) { - return ret; - } - - trace_block_cache_get_read(qemu_coroutine_self(), - c == s->l2_table_cache, i); - c->entries[i].offset = 0; - if (read_from_disk) { - if (c == s->l2_table_cache) { - BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); - } - - ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); - if (ret < 0) { - return ret; - } - } - - /* Give the table some hits for the start so that it won't be replaced - * immediately. The number 32 is completely arbitrary. */ - c->entries[i].cache_hits = 32; - c->entries[i].offset = offset; - - /* And return the right table */ -found: - c->entries[i].cache_hits++; - c->entries[i].ref++; - *table = c->entries[i].table; - - trace_block_cache_get_done(qemu_coroutine_self(), - c == s->l2_table_cache, i); - - return 0; -} - -int block_cache_get(BlockDriverState *bs, BlockCache *c, uint64_t offset, - void **table) -{ - return block_cache_do_get(bs, c, offset, table, true); -} - -int block_cache_get_empty(BlockDriverState *bs, BlockCache *c, uint64_t offset, - void **table) -{ - return block_cache_do_get(bs, c, offset, table, false); -} - -int block_cache_put(BlockDriverState *bs, BlockCache *c, void **table) -{ - int i; - - for (i = 0; i < c->size; i++) { - if (c->entries[i].table == *table) { - goto found; - } - } - return -ENOENT; - -found: - c->entries[i].ref--; - *table = NULL; - - assert(c->entries[i].ref >= 0); - return 0; -} - -void block_cache_entry_mark_dirty(BlockCache *c, void *table) -{ - int i; - - for (i = 0; i < c->size; i++) { - if (c->entries[i].table == table) { - goto found; - } - } - abort(); - -found: - c->entries[i].dirty = true; -} diff --git a/block/qcow2.h b/block/qcow2.h index ed25806..8cc851a 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -418,7 +418,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); void qcow2_free_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs); -/* qcow2-cache.c functions */ +/* block-cache.c functions */ BlockCache *block_cache_create(BlockDriverState *bs, int num_tables); int block_cache_destroy(BlockDriverState* bs, BlockCache *c); -- 1.7.11.7