From patchwork Thu Dec 6 06:51:49 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Wang X-Patchwork-Id: 204166 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 22F1E2C0199 for ; Thu, 6 Dec 2012 18:39:17 +1100 (EST) Received: from localhost ([::1]:38328 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TgVKa-0004do-R4 for incoming@patchwork.ozlabs.org; Thu, 06 Dec 2012 01:53:12 -0500 Received: from eggs.gnu.org ([208.118.235.92]:60077) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TgVJs-00035A-NI for qemu-devel@nongnu.org; Thu, 06 Dec 2012 01:52:33 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TgVJo-0006VS-D3 for qemu-devel@nongnu.org; Thu, 06 Dec 2012 01:52:28 -0500 Received: from mail-ia0-f170.google.com ([209.85.210.170]:40542) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TgVJo-0006VF-3s for qemu-devel@nongnu.org; Thu, 06 Dec 2012 01:52:24 -0500 Received: by mail-ia0-f170.google.com with SMTP id i1so5724694iaa.1 for ; Wed, 05 Dec 2012 22:52:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=gxSdm6xdOUhUxe/3apr86kfjpApVOpzdPB0QoavPld8=; b=XUburqSOavxdqyy9Z92lX5a8+5RhozmjjqRv5+nZ2mLL9gmnEZh96RRh+8Qz0SksP5 JUFn+EPaA+6qUNpf2bqtlDpgwPyxcXkIoQZx88r8oDfwxgi9UhwRgQFIXF2/mROHVyb2 XlwQNbxgNd/XWCMNxHolTnnnwDdHRFTTiX75uD+jVTbV9o2bJSjp/uCECvqlItayiIEE r0BCu/fkYO1VGvt2laglc+6SpuzQW6MEUguxSS6BS9rF3REyrJMk62xXrDO1LdjAIsxJ C5UM0eHkEWy4+EKvAglHFso1SEjl4sjIenEhZtK496Ohy2YyJlOf7O7ydajexJFgQ6xz GbFw== Received: by 10.50.7.234 with SMTP id m10mr464493iga.43.1354776743158; Wed, 05 Dec 2012 22:52:23 -0800 (PST) Received: from localhost.localdomain ([202.108.130.153]) by mx.google.com with ESMTPS id uj11sm14890561igb.15.2012.12.05.22.52.20 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 05 Dec 2012 22:52:22 -0800 (PST) From: Dong Xu Wang To: qemu-devel@nongnu.org Date: Thu, 6 Dec 2012 14:51:49 +0800 Message-Id: <1354776711-12449-5-git-send-email-wdongxu@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1354776711-12449-1-git-send-email-wdongxu@linux.vnet.ibm.com> References: <1354776711-12449-1-git-send-email-wdongxu@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.210.170 Cc: kwolf@redhat.com, Dong Xu Wang Subject: [Qemu-devel] [PATCH V17 4/6] rename qcow2-cache.c to block-cache.c 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 We will re-use qcow2-cache as block layer common cache code, so change its name and made some changes, define a struct named BlockTableType, pass BlockTableType and table size parameters to block cache initialization function. Signed-off-by: Dong Xu Wang --- block/Makefile.objs | 3 +- block/block-cache.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++ block/block-cache.h | 76 +++++++++++ block/qcow2-cache.c | 323 ------------------------------------------------ block/qcow2-cluster.c | 53 ++++---- block/qcow2-refcount.c | 66 ++++++----- block/qcow2.c | 21 ++-- block/qcow2.h | 24 +--- trace-events | 13 +- 9 files changed, 481 insertions(+), 415 deletions(-) create mode 100644 block/block-cache.c create mode 100644 block/block-cache.h delete mode 100644 block/qcow2-cache.c diff --git a/block/Makefile.objs b/block/Makefile.objs index 7f01510..d23c250 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 += parallels.o blkdebug.o blkverify.o diff --git a/block/block-cache.c b/block/block-cache.c new file mode 100644 index 0000000..bf5c57c --- /dev/null +++ b/block/block-cache.c @@ -0,0 +1,317 @@ +/* + * QEMU Block Layer Cache + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Dong Xu Wang + * + * This file is based on qcow2-cache.c, see its copyrights below: + * + * L2/refcount table cache for the QCOW2 format + * + * Copyright (c) 2010 Kevin Wolf + * + * 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_int.h" +#include "qemu-common.h" +#include "trace.h" +#include "block-cache.h" + +BlockCache *block_cache_create(BlockDriverState *bs, int num_tables, + size_t cluster_size, BlockTableType type) +{ + BlockCache *c; + int i; + + c = g_malloc0(sizeof(*c)); + c->size = num_tables; + c->entries = g_malloc0(sizeof(*c->entries) * num_tables); + c->table_type = type; + c->cluster_size = cluster_size; + + for (i = 0; i < c->size; i++) { + c->entries[i].table = qemu_blockalign(bs, 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) +{ + int ret = 0; + + if (!c->entries[i].dirty || !c->entries[i].offset) { + return 0; + } + + trace_block_cache_entry_flush(qemu_coroutine_self(), c->table_type, 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->table_type == BLOCK_TABLE_REF) { + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); + } else if (c->table_type == BLOCK_TABLE_L2) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + } + + ret = bdrv_pwrite(bs->file, c->entries[i].offset, + c->entries[i].table, c->cluster_size); + if (ret < 0) { + return ret; + } + + c->entries[i].dirty = false; + + return 0; +} + +int block_cache_flush(BlockDriverState *bs, BlockCache *c) +{ + int result = 0; + int ret; + int i; + + trace_block_cache_flush(qemu_coroutine_self(), c->table_type); + + 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) +{ + int i; + int ret; + + trace_block_cache_get(qemu_coroutine_self(), c->table_type, + 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->table_type, 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->table_type, i); + c->entries[i].offset = 0; + if (read_from_disk) { + if (c->table_type == BLOCK_TABLE_L2) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + } + + ret = bdrv_pread(bs->file, offset, c->entries[i].table, + c->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->table_type, 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--; + assert(c->entries[i].ref >= 0); + *table = NULL; + 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/block-cache.h b/block/block-cache.h new file mode 100644 index 0000000..4efa06e --- /dev/null +++ b/block/block-cache.h @@ -0,0 +1,76 @@ +/* + * QEMU Block Layer Cache + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Dong Xu Wang + * + * This file is based on qcow2-cache.c, see its copyrights below: + * + * L2/refcount table cache for the QCOW2 format + * + * Copyright (c) 2010 Kevin Wolf + * + * 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. + */ + +#ifndef BLOCK_CACHE_H +#define BLOCK_CACHE_H + +typedef enum { + BLOCK_TABLE_REF, + BLOCK_TABLE_L2, +} BlockTableType; + +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; + size_t cluster_size; + BlockTableType table_type; + bool depends_on_flush; +}; + +struct BlockCache; +typedef struct BlockCache BlockCache; + +BlockCache *block_cache_create(BlockDriverState *bs, int num_tables, + size_t cluster_size, BlockTableType type); +int block_cache_destroy(BlockDriverState *bs, BlockCache *c); +int block_cache_flush(BlockDriverState *bs, BlockCache *c); +int block_cache_set_dependency(BlockDriverState *bs, + BlockCache *c, + BlockCache *dependency); +void block_cache_depends_on_flush(BlockCache *c); +int block_cache_get(BlockDriverState *bs, BlockCache *c, uint64_t offset, + void **table); +int block_cache_get_empty(BlockDriverState *bs, BlockCache *c, + uint64_t offset, void **table); +int block_cache_put(BlockDriverState *bs, BlockCache *c, void **table); +void block_cache_entry_mark_dirty(BlockCache *c, void *table); +#endif /* BLOCK_CACHE_H */ diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c deleted file mode 100644 index 2d4322a..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 - * - * 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_int.h" -#include "qemu-common.h" -#include "qcow2.h" -#include "trace.h" - -typedef struct Qcow2CachedTable { - void* table; - int64_t offset; - bool dirty; - int cache_hits; - int ref; -} Qcow2CachedTable; - -struct Qcow2Cache { - Qcow2CachedTable* entries; - struct Qcow2Cache* depends; - int size; - bool depends_on_flush; -}; - -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) -{ - BDRVQcowState *s = bs->opaque; - Qcow2Cache *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 qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *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 qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) -{ - int ret; - - ret = qcow2_cache_flush(bs, c->depends); - if (ret < 0) { - return ret; - } - - c->depends = NULL; - c->depends_on_flush = false; - - return 0; -} - -static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) -{ - BDRVQcowState *s = bs->opaque; - int ret = 0; - - if (!c->entries[i].dirty || !c->entries[i].offset) { - return 0; - } - - trace_qcow2_cache_entry_flush(qemu_coroutine_self(), - c == s->l2_table_cache, i); - - if (c->depends) { - ret = qcow2_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 qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) -{ - BDRVQcowState *s = bs->opaque; - int result = 0; - int ret; - int i; - - trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); - - for (i = 0; i < c->size; i++) { - ret = qcow2_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 qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, - Qcow2Cache *dependency) -{ - int ret; - - if (dependency->depends) { - ret = qcow2_cache_flush_dependency(bs, dependency); - if (ret < 0) { - return ret; - } - } - - if (c->depends && (c->depends != dependency)) { - ret = qcow2_cache_flush_dependency(bs, c); - if (ret < 0) { - return ret; - } - } - - c->depends = dependency; - return 0; -} - -void qcow2_cache_depends_on_flush(Qcow2Cache *c) -{ - c->depends_on_flush = true; -} - -static int qcow2_cache_find_entry_to_replace(Qcow2Cache *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 qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, - uint64_t offset, void **table, bool read_from_disk) -{ - BDRVQcowState *s = bs->opaque; - int i; - int ret; - - trace_qcow2_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 = qcow2_cache_find_entry_to_replace(c); - trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), - c == s->l2_table_cache, i); - if (i < 0) { - return i; - } - - ret = qcow2_cache_entry_flush(bs, c, i); - if (ret < 0) { - return ret; - } - - trace_qcow2_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_qcow2_cache_get_done(qemu_coroutine_self(), - c == s->l2_table_cache, i); - - return 0; -} - -int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - void **table) -{ - return qcow2_cache_do_get(bs, c, offset, table, true); -} - -int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - void **table) -{ - return qcow2_cache_do_get(bs, c, offset, table, false); -} - -int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *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 qcow2_cache_entry_mark_dirty(Qcow2Cache *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-cluster.c b/block/qcow2-cluster.c index e179211..b5a0960 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -69,7 +69,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) return new_l1_table_offset; } - ret = qcow2_cache_flush(bs, s->refcount_block_cache); + ret = block_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { goto fail; } @@ -119,7 +119,8 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, BDRVQcowState *s = bs->opaque; int ret; - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); + ret = block_cache_get(bs, s->l2_table_cache, l2_offset, + (void **) l2_table); return ret; } @@ -180,7 +181,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) return l2_offset; } - ret = qcow2_cache_flush(bs, s->refcount_block_cache); + ret = block_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { goto fail; } @@ -188,7 +189,8 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) /* allocate a new entry in the l2 cache */ trace_qcow2_l2_allocate_get_empty(bs, l1_index); - ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); + ret = block_cache_get_empty(bs, s->l2_table_cache, l2_offset, + (void **) table); if (ret < 0) { return ret; } @@ -203,16 +205,16 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) /* if there was an old l2 table, read it from the disk */ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); - ret = qcow2_cache_get(bs, s->l2_table_cache, - old_l2_offset & L1E_OFFSET_MASK, - (void**) &old_table); + ret = block_cache_get(bs, s->l2_table_cache, + old_l2_offset & L1E_OFFSET_MASK, + (void **) &old_table); if (ret < 0) { goto fail; } memcpy(l2_table, old_table, s->cluster_size); - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table); + ret = block_cache_put(bs, s->l2_table_cache, (void **) &old_table); if (ret < 0) { goto fail; } @@ -222,8 +224,8 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); trace_qcow2_l2_allocate_write_l2(bs, l1_index); - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); - ret = qcow2_cache_flush(bs, s->l2_table_cache); + block_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + ret = block_cache_flush(bs, s->l2_table_cache); if (ret < 0) { goto fail; } @@ -242,7 +244,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) fail: trace_qcow2_l2_allocate_done(bs, l1_index, ret); - qcow2_cache_put(bs, s->l2_table_cache, (void**) table); + block_cache_put(bs, s->l2_table_cache, (void **) table); s->l1_table[l1_index] = old_l2_offset; return ret; } @@ -475,7 +477,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, abort(); } - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); nb_available = (c * s->cluster_sectors); @@ -584,13 +586,13 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, * allocated. */ cluster_offset = be64_to_cpu(l2_table[l2_index]); if (cluster_offset & L2E_OFFSET_MASK) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); return 0; } cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); return 0; } @@ -605,9 +607,9 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, /* compressed clusters never have the copied flag */ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + block_cache_entry_mark_dirty(s->l2_table_cache, l2_table); l2_table[l2_index] = cpu_to_be64(cluster_offset); - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + ret = block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); if (ret < 0) { return 0; } @@ -659,18 +661,19 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) * handled. */ if (cow) { - qcow2_cache_depends_on_flush(s->l2_table_cache); + block_cache_depends_on_flush(s->l2_table_cache); } + if (qcow2_need_accurate_refcounts(s)) { - qcow2_cache_set_dependency(bs, s->l2_table_cache, + block_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); } ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index); if (ret < 0) { goto err; } - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + block_cache_entry_mark_dirty(s->l2_table_cache, l2_table); for (i = 0; i < m->nb_clusters; i++) { /* if two concurrent writes happen to the same unallocated cluster @@ -687,7 +690,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) } - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + ret = block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); if (ret < 0) { goto err; } @@ -913,7 +916,7 @@ again: * request to complete. If we still had the reference, we could use up the * whole cache with sleeping requests. */ - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + ret = block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); if (ret < 0) { return ret; } @@ -1077,14 +1080,14 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, } /* First remove L2 entries */ - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + block_cache_entry_mark_dirty(s->l2_table_cache, l2_table); l2_table[l2_index + i] = cpu_to_be64(0); /* Then decrease the refcount */ qcow2_free_any_clusters(bs, old_offset, 1); } - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + ret = block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); if (ret < 0) { return ret; } @@ -1154,7 +1157,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, old_offset = be64_to_cpu(l2_table[l2_index + i]); /* Update L2 entries */ - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + block_cache_entry_mark_dirty(s->l2_table_cache, l2_table); if (old_offset & QCOW_OFLAG_COMPRESSED) { l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); qcow2_free_any_clusters(bs, old_offset, 1); @@ -1163,7 +1166,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, } } - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + ret = block_cache_put(bs, s->l2_table_cache, (void **) &l2_table); if (ret < 0) { return ret; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 96224d1..2bd8391 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -71,8 +71,8 @@ static int load_refcount_block(BlockDriverState *bs, int ret; BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); - ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, - refcount_block); + ret = block_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + refcount_block); return ret; } @@ -98,8 +98,8 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index) if (!refcount_block_offset) return 0; - ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, - (void**) &refcount_block); + ret = block_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + (void **) &refcount_block); if (ret < 0) { return ret; } @@ -108,8 +108,8 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index) ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); refcount = be16_to_cpu(refcount_block[block_index]); - ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + ret = block_cache_put(bs, s->refcount_block_cache, + (void **) &refcount_block); if (ret < 0) { return ret; } @@ -201,7 +201,7 @@ static int alloc_refcount_block(BlockDriverState *bs, *refcount_block = NULL; /* We write to the refcount table, so we might depend on L2 tables */ - qcow2_cache_flush(bs, s->l2_table_cache); + block_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); @@ -217,8 +217,8 @@ static int alloc_refcount_block(BlockDriverState *bs, if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { /* Zero the new refcount block before updating it */ - ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + ret = block_cache_get_empty(bs, s->refcount_block_cache, new_block, + (void **) refcount_block); if (ret < 0) { goto fail_block; } @@ -241,8 +241,8 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ - ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + ret = block_cache_get_empty(bs, s->refcount_block_cache, new_block, + (void **) refcount_block); if (ret < 0) { goto fail_block; } @@ -252,8 +252,8 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Now the new refcount block needs to be written to disk */ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); - qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); - ret = qcow2_cache_flush(bs, s->refcount_block_cache); + block_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); + ret = block_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { goto fail_block; } @@ -273,7 +273,8 @@ static int alloc_refcount_block(BlockDriverState *bs, return 0; } - ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + ret = block_cache_put(bs, s->refcount_block_cache, + (void **) refcount_block); if (ret < 0) { goto fail_block; } @@ -407,7 +408,8 @@ fail_table: g_free(new_table); fail_block: if (*refcount_block != NULL) { - qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + block_cache_put(bs, s->refcount_block_cache, + (void **) refcount_block); } return ret; } @@ -433,8 +435,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, } if (addend < 0) { - qcow2_cache_set_dependency(bs, s->refcount_block_cache, - s->l2_table_cache); + block_cache_set_dependency(bs, s->refcount_block_cache, + s->l2_table_cache); } start = offset & ~(s->cluster_size - 1); @@ -450,8 +452,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, /* 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); + ret = block_cache_put(bs, s->refcount_block_cache, + (void **) &refcount_block); if (ret < 0) { goto fail; } @@ -464,7 +466,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, } old_table_index = table_index; - qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); + block_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); /* we can update the count and save it */ block_index = cluster_index & @@ -487,8 +489,8 @@ fail: /* Write last changed block to disk */ if (refcount_block) { int wret; - wret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + wret = block_cache_put(bs, s->refcount_block_cache, + (void **) &refcount_block); if (wret < 0) { return ret < 0 ? ret : wret; } @@ -764,8 +766,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, old_l2_offset = l2_offset; l2_offset &= L1E_OFFSET_MASK; - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, - (void**) &l2_table); + ret = block_cache_get(bs, s->l2_table_cache, l2_offset, + (void **) &l2_table); if (ret < 0) { goto fail; } @@ -812,16 +814,18 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } if (offset != old_offset) { if (addend > 0) { - qcow2_cache_set_dependency(bs, s->l2_table_cache, + block_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); } l2_table[j] = cpu_to_be64(offset); - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + block_cache_entry_mark_dirty(s->l2_table_cache, + l2_table); } } } - ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + ret = block_cache_put(bs, s->l2_table_cache, + (void **) &l2_table); if (ret < 0) { goto fail; } @@ -848,7 +852,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, ret = 0; fail: if (l2_table) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + block_cache_put(bs, s->l2_table_cache, + (void **) &l2_table); } /* Update L1 only if it isn't deleted anyway (addend = -1) */ @@ -1131,8 +1136,9 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, 0, s->cluster_size); /* current L1 table */ - ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, - s->l1_table_offset, s->l1_size, 1); + ret = check_refcounts_l1(bs, res, refcount_table, + nb_clusters, s->l1_table_offset, + s->l1_size, 1); if (ret < 0) { goto fail; } diff --git a/block/qcow2.c b/block/qcow2.c index 38f467c..6a66e9b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -430,8 +430,11 @@ static int qcow2_open(BlockDriverState *bs, int flags) } /* alloc L2 table/refcount block cache */ - s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE); - s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE); + s->l2_table_cache = block_cache_create(bs, L2_CACHE_SIZE, + s->cluster_size, BLOCK_TABLE_L2); + s->refcount_block_cache = block_cache_create(bs, REFCOUNT_CACHE_SIZE, + s->cluster_size, + BLOCK_TABLE_REF); s->cluster_cache = g_malloc(s->cluster_size); /* one more sector for decompressed data alignment */ @@ -510,7 +513,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) qcow2_refcount_close(bs); g_free(s->l1_table); if (s->l2_table_cache) { - qcow2_cache_destroy(bs, s->l2_table_cache); + block_cache_destroy(bs, s->l2_table_cache); } g_free(s->cluster_cache); qemu_vfree(s->cluster_data); @@ -878,13 +881,13 @@ static void qcow2_close(BlockDriverState *bs) BDRVQcowState *s = bs->opaque; g_free(s->l1_table); - qcow2_cache_flush(bs, s->l2_table_cache); - qcow2_cache_flush(bs, s->refcount_block_cache); + block_cache_flush(bs, s->l2_table_cache); + block_cache_flush(bs, s->refcount_block_cache); qcow2_mark_clean(bs); - qcow2_cache_destroy(bs, s->l2_table_cache); - qcow2_cache_destroy(bs, s->refcount_block_cache); + block_cache_destroy(bs, s->l2_table_cache); + block_cache_destroy(bs, s->refcount_block_cache); g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); @@ -1554,14 +1557,14 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) int ret; qemu_co_mutex_lock(&s->lock); - ret = qcow2_cache_flush(bs, s->l2_table_cache); + ret = block_cache_flush(bs, s->l2_table_cache); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); return ret; } if (qcow2_need_accurate_refcounts(s)) { - ret = qcow2_cache_flush(bs, s->refcount_block_cache); + ret = block_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); return ret; diff --git a/block/qcow2.h b/block/qcow2.h index b4eb654..cb6fd7a 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -27,6 +27,7 @@ #include "aes.h" #include "qemu-coroutine.h" +#include "block-cache.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 @@ -94,8 +95,6 @@ typedef struct QCowSnapshot { uint64_t vm_clock_nsec; } QCowSnapshot; -struct Qcow2Cache; -typedef struct Qcow2Cache Qcow2Cache; typedef struct Qcow2UnknownHeaderExtension { uint32_t magic; @@ -146,8 +145,8 @@ typedef struct BDRVQcowState { uint64_t l1_table_offset; uint64_t *l1_table; - Qcow2Cache* l2_table_cache; - Qcow2Cache* refcount_block_cache; + BlockCache *l2_table_cache; + BlockCache *refcount_block_cache; uint8_t *cluster_cache; uint8_t *cluster_data; @@ -316,21 +315,4 @@ 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 */ -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); -int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); - -void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); -int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); -int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, - Qcow2Cache *dependency); -void qcow2_cache_depends_on_flush(Qcow2Cache *c); - -int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - void **table); -int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - void **table); -int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); - #endif diff --git a/trace-events b/trace-events index 6c6cbf1..be611ba 100644 --- a/trace-events +++ b/trace-events @@ -470,12 +470,13 @@ qcow2_l2_allocate_write_l2(void *bs, int l1_index) "bs %p l1_index %d" qcow2_l2_allocate_write_l1(void *bs, int l1_index) "bs %p l1_index %d" qcow2_l2_allocate_done(void *bs, int l1_index, int ret) "bs %p l1_index %d ret %d" -qcow2_cache_get(void *co, int c, uint64_t offset, bool read_from_disk) "co %p is_l2_cache %d offset %" PRIx64 " read_from_disk %d" -qcow2_cache_get_replace_entry(void *co, int c, int i) "co %p is_l2_cache %d index %d" -qcow2_cache_get_read(void *co, int c, int i) "co %p is_l2_cache %d index %d" -qcow2_cache_get_done(void *co, int c, int i) "co %p is_l2_cache %d index %d" -qcow2_cache_flush(void *co, int c) "co %p is_l2_cache %d" -qcow2_cache_entry_flush(void *co, int c, int i) "co %p is_l2_cache %d index %d" +# block/block-cache.c +block_cache_get(void *co, int c, uint64_t offset, bool read_from_disk) "co %p is_l2_cache %d offset %" PRIx64 " read_from_disk %d" +block_cache_get_replace_entry(void *co, int c, int i) "co %p is_l2_cache %d index %d" +block_cache_get_read(void *co, int c, int i) "co %p is_l2_cache %d index %d" +block_cache_get_done(void *co, int c, int i) "co %p is_l2_cache %d index %d" +block_cache_flush(void *co, int c) "co %p is_l2_cache %d" +block_cache_entry_flush(void *co, int c, int i) "co %p is_l2_cache %d index %d" # block/qed-l2-cache.c qed_alloc_l2_cache_entry(void *l2_cache, void *entry) "l2_cache %p entry %p"