[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on
From: |
Alberto Garcia |
Subject: |
[Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen |
Date: |
Thu, 14 Jun 2018 18:49:03 +0300 |
This patch allows the user to change the backing file of an image that
is being reopened. Here's what it does:
- In bdrv_reopen_queue_child(): if the 'backing' option points to an
image different from the current backing file then it means that
the latter is going be detached so it must not be put in the reopen
queue.
- In bdrv_reopen_prepare(): check that the value of 'backing' points
to an existing node or is null. If it points to an existing node it
also needs to make sure that replacing the backing file will not
create a cycle in the node graph (i.e. you cannot reach the parent
from the new backing file).
- In bdrv_reopen_commit(): perform the actual node replacement by
calling bdrv_set_backing_hd(). It may happen that there are
temporary implicit nodes between the BDS that we are reopening and
the backing file that we want to replace (e.g. a commit fiter node),
so we must skip them.
Although x-blockdev-reopen is meant to be used like blockdev-add,
there's two important things that must be taken into account:
1) The only way to set a new backing file is by using a reference to
an existing node (previously added with e.g. blockdev-add).
If 'backing' contains a dictionary with a new set of options
({"driver": "qcow2", "file": { ... }}) then it is interpreted that
the _existing_ backing file must be reopened with those options.
2) If the 'backing' option is omitted altogether then the existing
backing file (if any) is kept. Unlike blockdev-add this will _not_
open the default backing file specified in the image header.
Signed-off-by: Alberto Garcia <address@hidden>
---
block.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/block.c b/block.c
index 0b9268a48d..91fff6361f 100644
--- a/block.c
+++ b/block.c
@@ -2823,6 +2823,27 @@ BlockDriverState *bdrv_open(const char *filename, const
char *reference,
}
/*
+ * Returns true if @child can be reached recursively from @bs
+ */
+static bool bdrv_recurse_has_child(BlockDriverState *bs,
+ BlockDriverState *child)
+{
+ BdrvChild *c;
+
+ if (bs == child) {
+ return true;
+ }
+
+ QLIST_FOREACH(c, &bs->children, next) {
+ if (bdrv_recurse_has_child(c->bs, child)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
* Adds a BlockDriverState to a simple queue for an atomic, transactional
* reopen of multiple devices.
*
@@ -2957,6 +2978,7 @@ static BlockReopenQueue
*bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
QLIST_FOREACH(child, &bs->children, next) {
QDict *new_child_options;
char *child_key_dot;
+ bool child_keep_old = keep_old_opts;
/* reopen can only change the options of block devices that were
* implicitly created and inherited options. For other (referenced)
@@ -2965,12 +2987,28 @@ static BlockReopenQueue
*bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
continue;
}
+ /* If the 'backing' option is set and it points to a different
+ * node then it means that we want to replace the current one,
+ * so we shouldn't put it in the reopen queue */
+ if (child->role == &child_backing && qdict_haskey(options, "backing"))
{
+ const char *backing = qdict_get_try_str(options, "backing");
+ if (g_strcmp0(backing, child->bs->node_name)) {
+ continue;
+ }
+ }
+
child_key_dot = g_strdup_printf("%s.", child->name);
qdict_extract_subqdict(options, &new_child_options, child_key_dot);
g_free(child_key_dot);
+ /* If the 'backing' option was omitted then we keep the old
+ * set of options */
+ if (child->role == &child_backing && !qdict_size(new_child_options)) {
+ child_keep_old = true;
+ }
+
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, 0,
- child->role, options, flags, keep_old_opts);
+ child->role, options, flags, child_keep_old);
}
return bs_queue;
@@ -3262,7 +3300,34 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue,
* the user can simply omit options which cannot be changed anyway,
* so they will stay unchanged.
*/
- if (!qobject_is_equal(new, old)) {
+ /*
+ * Allow changing the 'backing' option. The new value can be
+ * either a reference to an existing node (using its node name)
+ * or NULL to simply detach the current backing file.
+ */
+ if (!strcmp(entry->key, "backing")) {
+ switch (qobject_type(new)) {
+ case QTYPE_QNULL:
+ break;
+ case QTYPE_QSTRING: {
+ const char *str = qobject_get_try_str(new);
+ BlockDriverState *bs = bdrv_lookup_bs(NULL, str, errp);
+ if (bs == NULL) {
+ ret = -EINVAL;
+ goto error;
+ } else if (bdrv_recurse_has_child(bs, reopen_state->bs)) {
+ error_setg(errp, "Making '%s' a backing file of '%s' "
+ "would create a cycle", str,
+ reopen_state->bs->node_name);
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ }
+ } else if (!qobject_is_equal(new, old)) {
error_setg(errp, "Cannot change the option '%s'", entry->key);
ret = -EINVAL;
goto error;
@@ -3293,6 +3358,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
BlockDriver *drv;
BlockDriverState *bs;
bool old_can_write, new_can_write;
+ QObject *qobj;
assert(reopen_state != NULL);
bs = reopen_state->bs;
@@ -3307,6 +3373,22 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
drv->bdrv_reopen_commit(reopen_state);
}
+ /* Change the backing file if a new one was specified */
+ qobj = qdict_get(reopen_state->options, "backing");
+ if (qobj) {
+ const char *str = qobject_get_try_str(qobj);
+ BlockDriverState *new_backing = str ? bdrv_find_node(str) : NULL;
+ BlockDriverState *overlay = bs;
+ /* If there are implicit nodes between the current BDS and its
+ * "actual" backing file, skip them */
+ while (backing_bs(overlay) && backing_bs(overlay)->implicit) {
+ overlay = backing_bs(overlay);
+ }
+ if (new_backing != backing_bs(overlay)) {
+ bdrv_set_backing_hd(overlay, new_backing, NULL);
+ }
+ }
+
/* set BDS specific flags now */
qobject_unref(bs->explicit_options);
--
2.11.0
- [Qemu-devel] [RFC PATCH 00/10] Add a 'x-blockdev-reopen' QMP command, Alberto Garcia, 2018/06/14
- [Qemu-devel] [RFC PATCH 04/10] block: Allow changing 'force-share' on reopen, Alberto Garcia, 2018/06/14
- [Qemu-devel] [RFC PATCH 03/10] block: Allow changing 'detect-zeroes' on reopen, Alberto Garcia, 2018/06/14
- [Qemu-devel] [RFC PATCH 02/10] block: Allow changing 'discard' on reopen, Alberto Garcia, 2018/06/14
- [Qemu-devel] [RFC PATCH 01/10] file-posix: Forbid trying to change unsupported options during reopen, Alberto Garcia, 2018/06/14
- [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen,
Alberto Garcia <=
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Kevin Wolf, 2018/06/18
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Alberto Garcia, 2018/06/18
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Kevin Wolf, 2018/06/18
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Alberto Garcia, 2018/06/19
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Kevin Wolf, 2018/06/19
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Alberto Garcia, 2018/06/19
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Kevin Wolf, 2018/06/20
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Alberto Garcia, 2018/06/20
- Re: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen, Kevin Wolf, 2018/06/21
[Qemu-devel] [RFC PATCH 09/10] block: Add a 'x-blockdev-reopen' QMP command, Alberto Garcia, 2018/06/14