aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/qcow2-cache.c10
-rw-r--r--block/qcow2.c34
-rw-r--r--block/qcow2.h6
-rw-r--r--qapi/block-core.json6
4 files changed, 45 insertions, 11 deletions
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
index b1aa42477e..d9dafa31e5 100644
--- a/block/qcow2-cache.c
+++ b/block/qcow2-cache.c
@@ -120,14 +120,20 @@ void qcow2_cache_clean_unused(Qcow2Cache *c)
c->cache_clean_lru_counter = c->lru_counter;
}
-Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
+Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
+ unsigned table_size)
{
BDRVQcow2State *s = bs->opaque;
Qcow2Cache *c;
+ assert(num_tables > 0);
+ assert(is_power_of_2(table_size));
+ assert(table_size >= (1 << MIN_CLUSTER_BITS));
+ assert(table_size <= s->cluster_size);
+
c = g_new0(Qcow2Cache, 1);
c->size = num_tables;
- c->table_size = s->cluster_size;
+ c->table_size = table_size;
c->entries = g_try_new0(Qcow2CachedTable, num_tables);
c->table_array = qemu_try_blockalign(bs->file->bs,
(size_t) num_tables * c->table_size);
diff --git a/block/qcow2.c b/block/qcow2.c
index 953254a004..57a517e2bd 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -676,6 +676,11 @@ static QemuOptsList qcow2_runtime_opts = {
.help = "Maximum L2 table cache size",
},
{
+ .name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Size of each entry in the L2 cache",
+ },
+ {
.name = QCOW2_OPT_REFCOUNT_CACHE_SIZE,
.type = QEMU_OPT_SIZE,
.help = "Maximum refcount block cache size",
@@ -747,6 +752,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs,
static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
uint64_t *l2_cache_size,
+ uint64_t *l2_cache_entry_size,
uint64_t *refcount_cache_size, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
@@ -762,6 +768,9 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
*refcount_cache_size = qemu_opt_get_size(opts,
QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0);
+ *l2_cache_entry_size = qemu_opt_get_size(
+ opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size);
+
if (combined_cache_size_set) {
if (l2_cache_size_set && refcount_cache_size_set) {
error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE
@@ -802,6 +811,15 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO;
}
}
+
+ if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) ||
+ *l2_cache_entry_size > s->cluster_size ||
+ !is_power_of_2(*l2_cache_entry_size)) {
+ error_setg(errp, "L2 cache entry size must be a power of two "
+ "between %d and the cluster size (%d)",
+ 1 << MIN_CLUSTER_BITS, s->cluster_size);
+ return;
+ }
}
typedef struct Qcow2ReopenState {
@@ -824,7 +842,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
QemuOpts *opts = NULL;
const char *opt_overlap_check, *opt_overlap_check_template;
int overlap_check_template = 0;
- uint64_t l2_cache_size, refcount_cache_size;
+ uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size;
int i;
const char *encryptfmt;
QDict *encryptopts = NULL;
@@ -843,15 +861,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
}
/* get L2 table/refcount block cache size from command line options */
- read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
- &local_err);
+ read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size,
+ &refcount_cache_size, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
- l2_cache_size /= s->cluster_size;
+ l2_cache_size /= l2_cache_entry_size;
if (l2_cache_size < MIN_L2_CACHE_SIZE) {
l2_cache_size = MIN_L2_CACHE_SIZE;
}
@@ -889,9 +907,11 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
}
}
- r->l2_slice_size = s->cluster_size / sizeof(uint64_t);
- r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size);
- r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size);
+ r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t);
+ r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size,
+ l2_cache_entry_size);
+ r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size,
+ s->cluster_size);
if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) {
error_setg(errp, "Could not allocate metadata caches");
ret = -ENOMEM;
diff --git a/block/qcow2.h b/block/qcow2.h
index d956a6cdd2..883802241f 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -68,7 +68,7 @@
#define MAX_CLUSTER_BITS 21
/* Must be at least 2 to cover COW */
-#define MIN_L2_CACHE_SIZE 2 /* clusters */
+#define MIN_L2_CACHE_SIZE 2 /* cache entries */
/* Must be at least 4 to cover all cases of refcount table growth */
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
@@ -100,6 +100,7 @@
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
#define QCOW2_OPT_CACHE_SIZE "cache-size"
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
+#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size"
#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval"
@@ -647,7 +648,8 @@ 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);
+Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
+ unsigned table_size);
int qcow2_cache_destroy(Qcow2Cache *c);
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 2c107823fe..5c5921bfb7 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2521,6 +2521,11 @@
# @l2-cache-size: the maximum size of the L2 table cache in
# bytes (since 2.2)
#
+# @l2-cache-entry-size: the size of each entry in the L2 cache in
+# bytes. It must be a power of two between 512
+# and the cluster size. The default value is
+# the cluster size (since 2.12)
+#
# @refcount-cache-size: the maximum size of the refcount block cache
# in bytes (since 2.2)
#
@@ -2542,6 +2547,7 @@
'*overlap-check': 'Qcow2OverlapChecks',
'*cache-size': 'int',
'*l2-cache-size': 'int',
+ '*l2-cache-entry-size': 'int',
'*refcount-cache-size': 'int',
'*cache-clean-interval': 'int',
'*encrypt': 'BlockdevQcow2Encryption' } }