From: Frediano Ziglio <freddy77@gmail.com>
To: kwolf@redhat.com
Cc: qemu-devel@nongnu.org, Frediano Ziglio <freddy77@gmail.com>
Subject: [Qemu-devel] [PATCH][RFC][1/2] qcow2: optimize refminus updates
Date: Tue, 13 Sep 2011 09:53:07 +0200 [thread overview]
Message-ID: <1315900388-6448-2-git-send-email-freddy77@gmail.com> (raw)
In-Reply-To: <1315900388-6448-1-git-send-email-freddy77@gmail.com>
Cache refcount decrement in an array to trade-off between
leaks and speed.
Signed-off-by: Frediano Ziglio <freddy77@gmail.com>
---
block/qcow2-refcount.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++--
block/qcow2.c | 1 +
block/qcow2.h | 14 +++++
3 files changed, 153 insertions(+), 4 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 9605367..7d59b68 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -40,6 +40,13 @@ int qcow2_refcount_init(BlockDriverState *bs)
BDRVQcowState *s = bs->opaque;
int ret, refcount_table_size2, i;
+ s->refm_cache_index = 0;
+ s->refm_cache_len = 1024;
+ s->refm_cache = g_malloc(s->refm_cache_len * sizeof(uint64));
+ if (!s->refm_cache) {
+ goto fail;
+ }
+
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
s->refcount_table = g_malloc(refcount_table_size2);
if (s->refcount_table_size > 0) {
@@ -53,12 +60,14 @@ int qcow2_refcount_init(BlockDriverState *bs)
}
return 0;
fail:
+ g_free(s->refm_cache);
return -ENOMEM;
}
void qcow2_refcount_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
+ g_free(s->refm_cache);
g_free(s->refcount_table);
}
@@ -634,13 +643,21 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
void qcow2_free_clusters(BlockDriverState *bs,
int64_t offset, int64_t size)
{
+ BDRVQcowState *s = bs->opaque;
int ret;
+ int64_t start, last;
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE);
- ret = update_refcount(bs, offset, size, -1);
- if (ret < 0) {
- fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret));
- /* TODO Remember the clusters to free them later and avoid leaking */
+ start = offset & ~(s->cluster_size - 1);
+ last = (offset + size - 1) & ~(s->cluster_size - 1);
+ for (; start <= last; start += s->cluster_size) {
+ ret = qcow2_refm_add(bs, start);
+ if (ret < 0) {
+ fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret));
+ /* TODO Remember the clusters to free them later
+ * and avoid leaking */
+ break;
+ }
}
}
@@ -1165,3 +1182,120 @@ fail:
return ret;
}
+int qcow2_refm_add_any(BlockDriverState *bs, int64_t offset)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ offset &= ~QCOW_OFLAG_COPIED;
+ if (s->refm_cache_index + 2 > s->refm_cache_len) {
+ int ret = qcow2_refm_flush(bs);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if ((offset & QCOW_OFLAG_COMPRESSED)) {
+ int nb_csectors = ((offset >> s->csize_shift) & s->csize_mask) + 1;
+ int64_t last;
+
+ offset = (offset & s->cluster_offset_mask) & ~511;
+ last = offset + nb_csectors * 512 - 1;
+ if (!in_same_refcount_block(s, offset, last)) {
+ s->refm_cache[s->refm_cache_index++] = last;
+ }
+ }
+ s->refm_cache[s->refm_cache_index++] = offset;
+ return 0;
+}
+
+static int uint64_cmp(const void *a, const void *b)
+{
+#define A (*((const uint64_t *)a))
+#define B (*((const uint64_t *)b))
+ if (A == B) {
+ return 0;
+ }
+ return A > B ? 1 : -1;
+#undef A
+#undef B
+}
+
+int qcow2_refm_flush(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint16_t *refcount_block = NULL;
+ int64_t old_table_index = -1;
+ int ret, i, saved_index = 0;
+ int len = s->refm_cache_index;
+
+ /* sort cache */
+ qsort(s->refm_cache, len, sizeof(uint64_t), uint64_cmp);
+
+ /* save */
+ for (i = 0; i < len; ++i) {
+ uint64_t cluster_offset = s->refm_cache[i];
+ int block_index, refcount;
+ int64_t cluster_index = cluster_offset >> s->cluster_bits;
+ int64_t table_index =
+ cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+
+ /* 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;
+ }
+ saved_index = i;
+ refcount_block = NULL;
+ }
+
+ ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+ old_table_index = table_index;
+
+ 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);
+
+ refcount = be16_to_cpu(refcount_block[block_index]);
+ refcount--;
+ if (refcount < 0) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (refcount == 0 && cluster_index < s->free_cluster_index) {
+ s->free_cluster_index = cluster_index;
+ }
+ refcount_block[block_index] = cpu_to_be16(refcount);
+ }
+
+ saved_index = len = 0;
+ s->refm_cache_index = 0;
+ ret = 0;
+fail:
+ /* Write last changed block to disk */
+ if (refcount_block) {
+ int wret;
+ wret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void **) &refcount_block);
+ if (wret < 0) {
+ return ret < 0 ? ret : wret;
+ }
+ }
+
+ if (saved_index < len) {
+ memmove(s->refm_cache, s->refm_cache + saved_index,
+ (len - saved_index) * sizeof(uint64_t));
+ s->refm_cache_index = len - saved_index;
+ }
+
+ return ret;
+}
+
diff --git a/block/qcow2.c b/block/qcow2.c
index 510ff68..89ae765 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -622,6 +622,7 @@ static void qcow2_close(BlockDriverState *bs)
g_free(s->l1_table);
qcow2_cache_flush(bs, s->l2_table_cache);
+ qcow2_refm_flush(bs);
qcow2_cache_flush(bs, s->refcount_block_cache);
qcow2_cache_destroy(bs, s->l2_table_cache);
diff --git a/block/qcow2.h b/block/qcow2.h
index 531af39..49d3d55 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -103,6 +103,8 @@ typedef struct BDRVQcowState {
Qcow2Cache* l2_table_cache;
Qcow2Cache* refcount_block_cache;
+ int refm_cache_len, refm_cache_index;
+ uint64_t *refm_cache;
uint8_t *cluster_cache;
uint8_t *cluster_data;
@@ -181,6 +183,18 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
+int qcow2_refm_add_any(BlockDriverState *bs, int64_t offset);
+int qcow2_refm_flush(BlockDriverState *bs);
+static inline int qcow2_refm_add(BlockDriverState *bs, int64_t offset)
+{
+ BDRVQcowState *s = bs->opaque;
+ if (s->refm_cache_index < s->refm_cache_len) {
+ s->refm_cache[s->refm_cache_index++] = offset;
+ return 0;
+ }
+ return qcow2_refm_add_any(bs, offset);
+}
+
int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
void qcow2_free_clusters(BlockDriverState *bs,
--
1.7.1
next prev parent reply other threads:[~2011-09-13 7:52 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-13 7:53 [Qemu-devel] [PATCH][RFC][0/2] REF+/REF- optimization Frediano Ziglio
2011-09-13 7:53 ` Frediano Ziglio [this message]
2011-09-13 7:53 ` [Qemu-devel] [PATCH][RFC][2/2] qcow2: ref+ optimization Frediano Ziglio
2011-09-13 10:37 ` [Qemu-devel] [PATCH][RFC][0/2] REF+/REF- optimization Kevin Wolf
2011-09-13 13:36 ` Frediano Ziglio
2011-09-14 9:10 ` Kevin Wolf
2011-09-14 9:52 ` Frediano Ziglio
2011-09-14 10:21 ` Kevin Wolf
2011-09-14 11:49 ` Frediano Ziglio
2011-09-15 7:24 ` Frediano Ziglio
2011-09-13 14:55 ` Frediano Ziglio
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1315900388-6448-2-git-send-email-freddy77@gmail.com \
--to=freddy77@gmail.com \
--cc=kwolf@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).