Skip to content

Commit

Permalink
qcow2: Make qcow2_update_options() suitable for transactions
Browse files Browse the repository at this point in the history
Before we can allow updating options at runtime with bdrv_reopen(), we
need to split the function into prepare/commit/abort parts.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
  • Loading branch information
kevmw committed Sep 14, 2015
1 parent c1344de commit ee55b17
Showing 1 changed file with 73 additions and 40 deletions.
113 changes: 73 additions & 40 deletions block/qcow2.c
Expand Up @@ -589,18 +589,25 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
}
}

static int qcow2_update_options(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
typedef struct Qcow2ReopenState {
Qcow2Cache *l2_table_cache;
Qcow2Cache *refcount_block_cache;
bool use_lazy_refcounts;
int overlap_check;
bool discard_passthrough[QCOW2_DISCARD_MAX];
uint64_t cache_clean_interval;
} Qcow2ReopenState;

static int qcow2_update_options_prepare(BlockDriverState *bs,
Qcow2ReopenState *r,
QDict *options, int flags,
Error **errp)
{
BDRVQcow2State *s = bs->opaque;
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;
Qcow2Cache *l2_table_cache = NULL;
Qcow2Cache *refcount_block_cache = NULL;
uint64_t cache_clean_interval;
bool use_lazy_refcounts;
int i;
Error *local_err = NULL;
int ret;
Expand Down Expand Up @@ -643,27 +650,27 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
}

/* alloc L2 table/refcount block cache */
l2_table_cache = qcow2_cache_create(bs, l2_cache_size);
refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size);
if (l2_table_cache == NULL || refcount_block_cache == NULL) {
r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size);
r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size);
if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) {
error_setg(errp, "Could not allocate metadata caches");
ret = -ENOMEM;
goto fail;
}

/* New interval for cache cleanup timer */
cache_clean_interval =
r->cache_clean_interval =
qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, 0);
if (cache_clean_interval > UINT_MAX) {
if (r->cache_clean_interval > UINT_MAX) {
error_setg(errp, "Cache clean interval too big");
ret = -EINVAL;
goto fail;
}

/* Enable lazy_refcounts according to image and command line options */
use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS,
r->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS,
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS));
if (use_lazy_refcounts && s->qcow_version < 3) {
if (r->use_lazy_refcounts && s->qcow_version < 3) {
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
"qemu 1.1 compatibility level");
ret = -EINVAL;
Expand Down Expand Up @@ -702,49 +709,75 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
goto fail;
}

/*
* Start updating fields in BDRVQcow2State.
* After this point no failure is allowed any more.
*/
s->overlap_check = 0;
r->overlap_check = 0;
for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) {
/* overlap-check defines a template bitmask, but every flag may be
* overwritten through the associated boolean option */
s->overlap_check |=
r->overlap_check |=
qemu_opt_get_bool(opts, overlap_bool_option_names[i],
overlap_check_template & (1 << i)) << i;
}

s->l2_table_cache = l2_table_cache;
s->refcount_block_cache = refcount_block_cache;

s->use_lazy_refcounts = use_lazy_refcounts;

s->discard_passthrough[QCOW2_DISCARD_NEVER] = false;
s->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true;
s->discard_passthrough[QCOW2_DISCARD_REQUEST] =
r->discard_passthrough[QCOW2_DISCARD_NEVER] = false;
r->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true;
r->discard_passthrough[QCOW2_DISCARD_REQUEST] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST,
flags & BDRV_O_UNMAP);
s->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] =
r->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true);
s->discard_passthrough[QCOW2_DISCARD_OTHER] =
r->discard_passthrough[QCOW2_DISCARD_OTHER] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);

s->cache_clean_interval = cache_clean_interval;
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));

ret = 0;
fail:
if (ret < 0) {
if (l2_table_cache) {
qcow2_cache_destroy(bs, l2_table_cache);
}
if (refcount_block_cache) {
qcow2_cache_destroy(bs, refcount_block_cache);
}
}
qemu_opts_del(opts);
opts = NULL;
return ret;
}

static void qcow2_update_options_commit(BlockDriverState *bs,
Qcow2ReopenState *r)
{
BDRVQcow2State *s = bs->opaque;
int i;

s->l2_table_cache = r->l2_table_cache;
s->refcount_block_cache = r->refcount_block_cache;

s->overlap_check = r->overlap_check;
s->use_lazy_refcounts = r->use_lazy_refcounts;

for (i = 0; i < QCOW2_DISCARD_MAX; i++) {
s->discard_passthrough[i] = r->discard_passthrough[i];
}

s->cache_clean_interval = r->cache_clean_interval;
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));
}

static void qcow2_update_options_abort(BlockDriverState *bs,
Qcow2ReopenState *r)
{
if (r->l2_table_cache) {
qcow2_cache_destroy(bs, r->l2_table_cache);
}
if (r->refcount_block_cache) {
qcow2_cache_destroy(bs, r->refcount_block_cache);
}
}

static int qcow2_update_options(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
{
Qcow2ReopenState r = {};
int ret;

ret = qcow2_update_options_prepare(bs, &r, options, flags, errp);
if (ret >= 0) {
qcow2_update_options_commit(bs, &r);
} else {
qcow2_update_options_abort(bs, &r);
}

return ret;
}
Expand Down

0 comments on commit ee55b17

Please sign in to comment.