Skip to contentSkip to conversation

commit: Support multiple roots above top node

PATCH In review Kevin Wolf 3 participants 15 messages Sep 25, 2017 – Oct 5, 2017 Block layer

lore
Share & tools
lore

Kevin Wolf

author
#0 lore raw

cover commit: Support multiple roots above top node

This is a step towards making the commit job flexible enough that it can work with any kind of block graph. Currently, it requires that not only the top and base node of the commit operation are specified, but also the active layer of the backing file chain. Of course, the assumption that a single active layer exists is invalid.

This series makes the commit job consider other roots as well so that all parent nodes of the top node get their backing file updated and stay valid after the commit job completes.

With this, we should have all of the prerequisites for a follow-up series that adds a new and clean blockdev-commit QMP command which doesn't require an option for the active layer and which accepts node names instead of file names for base and top.

Kevin Wolf (5):
block: Introduce BdrvChildRole.update_filename
commit: Support multiple roots above top node
qemu-iotests: Allow QMP pretty printing in common.qemu
qemu-iotests: Test commit block job where top has two parents
commit: Remove overlay_bs

 include/block/block.h          |   3 +-
 include/block/block_int.h      |   6 +
 block.c                        |  91 +++--
 block/commit.c                 |  64 +---
 tests/qemu-iotests/030         |   4 -
 tests/qemu-iotests/191         | 152 ++++++++
 tests/qemu-iotests/191.out     | 827 +++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/common.qemu |  14 +-
 tests/qemu-iotests/group       |   1 +
 9 files changed, 1078 insertions(+), 84 deletions(-)
 create mode 100755 tests/qemu-iotests/191
 create mode 100644 tests/qemu-iotests/191.out
Signature · 1 line
2.13.5
author
#1 lore raw

PATCH 1/5 block: Introduce BdrvChildRole.update_filename

There is no good reason for bdrv_drop_intermediate() to know the active layer above the subchain it is operating on - even more so, because the assumption that there is a single active layer above it is not generally true.

In order to prepare removal of the active parameter, use a BdrvChildRole callback to update the backing file string in the overlay image instead of directly calling bdrv_change_backing_file().

---
 include/block/block_int.h |  6 ++++++
 block.c                   | 33 ++++++++++++++++++++++++++++-----
 2 files changed, 34 insertions(+), 5 deletions(-)
include/block/block_int.h +6 -0
index 99abe2ce74..e6d53cc15e 100644
@@ -544,6 +544,12 @@ struct BdrvChildRole {
 
     void (*attach)(BdrvChild *child);
     void (*detach)(BdrvChild *child);
+
+    /* Notifies the parent that the filename of its child has changed (e.g.
+     * because the direct child was removed from the backing chain), so that it
+     * can update its reference. */
+    int (*update_filename)(BdrvChild *child, BlockDriverState *new_base,
+                           const char *filename, Error **errp);
 };
 
 extern const BdrvChildRole child_file;
block.c +28 -5
index 5c65fac672..7ac8cd521b 100644
@@ -981,6 +981,21 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
     *child_flags = flags;
 }
 
+static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
+                                        const char *filename, Error **errp)
+{
+    BlockDriverState *parent = c->opaque;
+    int ret;
+
+    ret = bdrv_change_backing_file(parent, filename,
+                                   base->drv ? base->drv->format_name : "");
+    if (ret < 0) {
+        error_setg_errno(errp, ret, "Could not update backing file link");
+    }
+
+    return ret;
+}
+
 const BdrvChildRole child_backing = {
     .get_parent_desc = bdrv_child_get_parent_desc,
     .attach          = bdrv_backing_attach,
@@ -989,6 +1004,7 @@ const BdrvChildRole child_backing = {
     .drained_begin   = bdrv_child_cb_drained_begin,
     .drained_end     = bdrv_child_cb_drained_end,
     .inactivate      = bdrv_child_cb_inactivate,
+    .update_filename = bdrv_backing_update_filename,
 };
 
 static int bdrv_open_flags(BlockDriverState *bs, int flags)
@@ -3470,6 +3486,8 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
     Error *local_err = NULL;
     int ret = -EIO;
 
+    bdrv_ref(top);
+
     if (!top->drv || !base->drv) {
         goto exit;
     }
@@ -3494,11 +3512,15 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
     }
 
     /* success - we can delete the intermediate states, and link top->base */
-    backing_file_str = backing_file_str ? backing_file_str : base->filename;
-    ret = bdrv_change_backing_file(new_top_bs, backing_file_str,
-                                   base->drv ? base->drv->format_name : "");
-    if (ret) {
-        goto exit;
+    if (new_top_bs->backing->role->update_filename) {
+        backing_file_str = backing_file_str ? backing_file_str : base->filename;
+        ret = new_top_bs->backing->role->update_filename(new_top_bs->backing,
+                                                         base, backing_file_str,
+                                                         &local_err);
+        if (ret < 0) {
+            bdrv_set_backing_hd(new_top_bs, top, &error_abort);
+            goto exit;
+        }
     }
 
     bdrv_set_backing_hd(new_top_bs, base, &local_err);
@@ -3510,6 +3532,7 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
 
     ret = 0;
 exit:
+    bdrv_unref(top);
     return ret;
 }
 
Signature · 1 line
2.13.5
Reviewed-by on v1 1/5
#2 lore raw

Re: [Qemu-devel] [PATCH 1/5] block: Introduce BdrvChildRole.update_filename

Quoted 9 lines · Kevin Wolf

There is no good reason for bdrv_drop_intermediate() to know the active layer above the subchain it is operating on - even more so, because the assumption that there is a single active layer above it is not generally true.

In order to prepare removal of the active parameter, use a BdrvChildRole callback to update the backing file string in the overlay image instead of directly calling bdrv_change_backing_file().

Quoted 6 lines
---
 include/block/block_int.h |  6 ++++++
 block.c                   | 33 ++++++++++++++++++++++++++++-----
 2 files changed, 34 insertions(+), 5 deletions(-)
Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
author
#3 lore raw

PATCH 2/5 commit: Support multiple roots above top node

In reply to Kevin Wolf

This changes the commit block job to support operation in a graph where there is more than a single active layer that references the top node.

This involves inserting the commit filter node not only on the path between the given active node and the top node, but between the top node and all of its parents.

On completion, bdrv_drop_intermediate() must consider all parents for updating the backing file link. These parents may be backing files themselves and such read-only; reopen them temporarily if necessary. Previously this was achieved by the bdrv_reopen() calls in the commit block job that made overlay_bs read-write for the whole duration of the block job, even though write access is only needed on completion.

Now that we consider all parents, overlay_bs is meaningless. It is left in place in this commit, but we'll remove it soon.

---
 block.c        | 68 ++++++++++++++++++++++++++++++++++------------------------
 block/commit.c |  2 +-
 2 files changed, 41 insertions(+), 29 deletions(-)
block.c +40 -28
index 7ac8cd521b..e57fab501d 100644
@@ -985,14 +985,26 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
                                         const char *filename, Error **errp)
 {
     BlockDriverState *parent = c->opaque;
+    int orig_flags = bdrv_get_flags(parent);
     int ret;
 
+    if (!(orig_flags & BDRV_O_RDWR)) {
+        ret = bdrv_reopen(parent, orig_flags | BDRV_O_RDWR, errp);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
     ret = bdrv_change_backing_file(parent, filename,
                                    base->drv ? base->drv->format_name : "");
     if (ret < 0) {
         error_setg_errno(errp, ret, "Could not update backing file link");
     }
 
+    if (!(orig_flags & BDRV_O_RDWR)) {
+        bdrv_reopen(parent, orig_flags, NULL);
+    }
+
     return ret;
 }
 
@@ -3482,7 +3494,7 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
 int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
                            BlockDriverState *base, const char *backing_file_str)
 {
-    BlockDriverState *new_top_bs = NULL;
+    BdrvChild *c, *next;
     Error *local_err = NULL;
     int ret = -EIO;
 
@@ -3492,42 +3504,42 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
         goto exit;
     }
 
-    new_top_bs = bdrv_find_overlay(active, top);
-
-    if (new_top_bs == NULL) {
-        /* we could not find the image above 'top', this is an error */
-        goto exit;
-    }
-
-    /* special case of new_top_bs->backing->bs already pointing to base - nothing
-     * to do, no intermediate images */
-    if (backing_bs(new_top_bs) == base) {
-        ret = 0;
-        goto exit;
-    }
-
     /* Make sure that base is in the backing chain of top */
     if (!bdrv_chain_contains(top, base)) {
         goto exit;
     }
 
     /* success - we can delete the intermediate states, and link top->base */
-    if (new_top_bs->backing->role->update_filename) {
-        backing_file_str = backing_file_str ? backing_file_str : base->filename;
-        ret = new_top_bs->backing->role->update_filename(new_top_bs->backing,
-                                                         base, backing_file_str,
-                                                         &local_err);
-        if (ret < 0) {
-            bdrv_set_backing_hd(new_top_bs, top, &error_abort);
+    backing_file_str = backing_file_str ? backing_file_str : base->filename;
+
+    QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
+        /* Check whether we are allowed to switch c from top to base */
+        GSList *ignore_children = g_slist_prepend(NULL, c);
+        bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
+                               ignore_children, &local_err);
+        if (local_err) {
+            ret = -EPERM;
+            error_report_err(local_err);
             goto exit;
         }
-    }
+        g_slist_free(ignore_children);
 
-    bdrv_set_backing_hd(new_top_bs, base, &local_err);
-    if (local_err) {
-        ret = -EPERM;
-        error_report_err(local_err);
-        goto exit;
+        /* If so, update the backing file path in the image file */
+        if (c->role->update_filename) {
+            ret = c->role->update_filename(c, base, backing_file_str,
+                                           &local_err);
+            if (ret < 0) {
+                bdrv_abort_perm_update(base);
+                error_report_err(local_err);
+                goto exit;
+            }
+        }
+
+        /* Do the actual switch in the in-memory graph.
+         * Completes bdrv_check_update_perm() transaction internally. */
+        bdrv_ref(base);
+        bdrv_replace_child(c, base);
+        bdrv_unref(top);
     }
 
     ret = 0;
block/commit.c +1 -1
index 8f0e83578a..610e1cd8f5 100644
@@ -350,7 +350,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
         error_propagate(errp, local_err);
         goto fail;
     }
-    bdrv_set_backing_hd(overlay_bs, commit_top_bs, &local_err);
+    bdrv_replace_node(top, commit_top_bs, &local_err);
     if (local_err) {
         bdrv_unref(commit_top_bs);
         commit_top_bs = NULL;
Signature · 1 line
2.13.5
#4 lore raw
Quoted 10 lines · Kevin Wolf

This changes the commit block job to support operation in a graph where there is more than a single active layer that references the top node.

This involves inserting the commit filter node not only on the path between the given active node and the top node, but between the top node and all of its parents.

On completion, bdrv_drop_intermediate() must consider all parents for updating the backing file link. These parents may be backing files themselves and such read-only; reopen them temporarily if necessary.

s/such/as such/

Quoted 3 lines

Previously this was achieved by the bdrv_reopen() calls in the commit block job that made overlay_bs read-write for the whole duration of the block job, even though write access is only needed on completion.

Do we know, at the start of the operation, enough to reject attempts that will eventually fail because we cannot obtain write access at completion, without having to wait for all the intermediate work before the completion phase is reached? If not, that might be a bit of a regression (where a command used to fail up front, we now waste a lot of effort, and possibly modify the backing chain irreversably, before finding out we still fail because we lack write access).

Quoted 23 lines

Now that we consider all parents, overlay_bs is meaningless. It is left in place in this commit, but we'll remove it soon.

---
 block.c        | 68 ++++++++++++++++++++++++++++++++++------------------------
 block/commit.c |  2 +-
 2 files changed, 41 insertions(+), 29 deletions(-)
     /* success - we can delete the intermediate states, and link top->base */
block.c +4 -7
-    if (new_top_bs->backing->role->update_filename) {
-        backing_file_str = backing_file_str ? backing_file_str : base->filename;
-        ret = new_top_bs->backing->role->update_filename(new_top_bs->backing,
-                                                         base, backing_file_str,
-                                                         &local_err);
-        if (ret < 0) {
-            bdrv_set_backing_hd(new_top_bs, top, &error_abort);
+    backing_file_str = backing_file_str ? backing_file_str : base->filename;
+
+    QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
+        /* Check whether we are allowed to switch c from top to base */

block.c int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, · line 3525

Quoted 10 lines
block.c +7 -1
+        GSList *ignore_children = g_slist_prepend(NULL, c);
+        bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
+                               ignore_children, &local_err);
+        if (local_err) {
+            ret = -EPERM;
+            error_report_err(local_err);
             goto exit;
         }
-    }
+        g_slist_free(ignore_children);

And it looks like you DO check up front that permissions are sufficient for later on.

Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
author
#5 lore raw

Am 25.09.2017 um 21:38 hat Eric Blake geschrieben:

Quoted 8 lines · Kevin Wolf
Quoted 7 lines

This changes the commit block job to support operation in a graph where there is more than a single active layer that references the top node.

This involves inserting the commit filter node not only on the path between the given active node and the top node, but between the top node and all of its parents.

Quoted 5 lines
Quoted 3 lines

On completion, bdrv_drop_intermediate() must consider all parents for updating the backing file link. These parents may be backing files themselves and such read-only; reopen them temporarily if necessary.

s/such/as such/

Yes, thanks.

Quoted 11 lines
Quoted 3 lines

Previously this was achieved by the bdrv_reopen() calls in the commit block job that made overlay_bs read-write for the whole duration of the block job, even though write access is only needed on completion.

Do we know, at the start of the operation, enough to reject attempts that will eventually fail because we cannot obtain write access at completion, without having to wait for all the intermediate work before the completion phase is reached? If not, that might be a bit of a regression (where a command used to fail up front, we now waste a lot of effort, and possibly modify the backing chain irreversably, before finding out we still fail because we lack write access).

It is kind of a change in behaviour, but I wouldn't call it a regression. The reason is that today I'm looking at it with a completely different perspective than when the command was added.

Originally, we assumed a more or less static block node graph, which in fact was only a mostly degenerated tree - a backing file chain. Block jobs were kind of an exceptional thing and we allowed only one per chain. In this restricted setting, checking at the start of the operation made sense because we wouldn't allow things to change while the job was running anyway.

A big part of the recent blockdev work, however, is about getting rid of arbitrary restrictions like this. We want to allow running two commit operations in the same backing chain as long as they don't conflict. We don't want to prevent adding new users to an existing node unless there is a good reason.

If you consider this, the set of nodes that will get their backing file rewritten at the end of the block job isn't known yet when the job starts; and even if we knew it, keeping the nodes writable all the time while the job runs feels like it could easily conflict with other callers of bdrv_reopen().

Quoted 33 lines
Quoted 22 lines

Now that we consider all parents, overlay_bs is meaningless. It is left in place in this commit, but we'll remove it soon.

---
 block.c        | 68 ++++++++++++++++++++++++++++++++++------------------------
 block/commit.c |  2 +-
 2 files changed, 41 insertions(+), 29 deletions(-)
     /* success - we can delete the intermediate states, and link top->base */
block.c +4 -7
-    if (new_top_bs->backing->role->update_filename) {
-        backing_file_str = backing_file_str ? backing_file_str : base->filename;
-        ret = new_top_bs->backing->role->update_filename(new_top_bs->backing,
-                                                         base, backing_file_str,
-                                                         &local_err);
-        if (ret < 0) {
-            bdrv_set_backing_hd(new_top_bs, top, &error_abort);
+    backing_file_str = backing_file_str ? backing_file_str : base->filename;
+
+    QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
+        /* Check whether we are allowed to switch c from top to base */

block.c int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, · line 3525

Quoted 10 lines
block.c +7 -1
+        GSList *ignore_children = g_slist_prepend(NULL, c);
+        bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
+                               ignore_children, &local_err);
+        if (local_err) {
+            ret = -EPERM;
+            error_report_err(local_err);
             goto exit;
         }
-    }
+        g_slist_free(ignore_children);
Quoted 2 lines

And it looks like you DO check up front that permissions are sufficient for later on.

This is already during completion. The completion code uses the transactional nature of permission updates to make sure that the in-memory graph stays consistent with the on-disk backing file links even in case of errors, but it's not really upfront in the sense of checking when the block job is started. (You also need to hold the BQL between starting and completing a permission change transaction, so just moving this code wouldn't work.)

Kevin

author
#6 lore raw

PATCH 3/5 qemu-iotests: Allow QMP pretty printing in common.qemu

In reply to Kevin Wolf

QMP responses to certain commands can become quite long, which doesn't only make reading them hard, but also means that the maximum line length in patch emails can be exceeded. Allow tests to switch to QMP pretty printing, which results in more, but shorter lines.

We also need to make sure to keep indentation in the response for this to work as expected.

---
 tests/qemu-iotests/common.qemu | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
tests/qemu-iotests/common.qemu +11 -3
index 7645f1dc72..92739a1eac 100644
@@ -55,13 +55,13 @@ function _timed_wait_for()
     shift
 
     QEMU_STATUS[$h]=0
-    while read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]}
+    while IFS= read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]}
     do
         if [ -z "${silent}" ]; then
             echo "${resp}" | _filter_testdir | _filter_qemu \
                            | _filter_qemu_io | _filter_qmp | _filter_hmp
         fi
-        grep -q "${*}" < <(echo ${resp})
+        grep -q "${*}" < <(echo "${resp}")
         if [ $? -eq 0 ]; then
             return
         fi
@@ -129,6 +129,7 @@ function _send_qemu_cmd()
 # $qemu_comm_method: set this variable to 'monitor' (case insensitive)
 #                    to use the QEMU HMP monitor for communication.
 #                    Otherwise, the default of QMP is used.
+# $qmp_pretty: Set this variable to 'y' to enable QMP pretty printing.
 # $keep_stderr: Set this variable to 'y' to keep QEMU's stderr output on stderr.
 #               If this variable is empty, stderr will be redirected to stdout.
 # Returns:
@@ -145,7 +146,11 @@ function _launch_qemu()
         comm="-monitor stdio"
     else
         local qemu_comm_method="qmp"
-        comm="-monitor none -qmp stdio"
+        if [ "$qmp_pretty" = "y" ]; then
+            comm="-monitor none -qmp-pretty stdio"
+        else
+            comm="-monitor none -qmp stdio"
+        fi
     fi
 
     fifo_out=${QEMU_FIFO_OUT}_${_QEMU_HANDLE}
@@ -192,6 +197,9 @@ function _launch_qemu()
     then
         # Don't print response, since it has version information in it
         silent=yes _timed_wait_for ${_QEMU_HANDLE} "capabilities"
+        if [ "$qmp_pretty" = "y" ]; then
+            silent=yes _timed_wait_for ${_QEMU_HANDLE} "^}"
+        fi
     fi
     QEMU_HANDLE=${_QEMU_HANDLE}
     let _QEMU_HANDLE++
Signature · 1 line
2.13.5
Reviewed-by on v1 3/5
#7 lore raw

Re: [Qemu-devel] [PATCH 3/5] qemu-iotests: Allow QMP pretty printing in common.qemu

Quoted 5 lines · Kevin Wolf

QMP responses to certain commands can become quite long, which doesn't only make reading them hard, but also means that the maximum line length in patch emails can be exceeded. Allow tests to switch to QMP pretty printing, which results in more, but shorter lines.

Quoted 7 lines

We also need to make sure to keep indentation in the response for this to work as expected.

---
 tests/qemu-iotests/common.qemu | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

Nice!

Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
author
#8 lore raw

PATCH 4/5 qemu-iotests: Test commit block job where top has two parents

In reply to Kevin Wolf
---
 tests/qemu-iotests/191     | 152 +++++++++
 tests/qemu-iotests/191.out | 827 +++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/group   |   1 +
 3 files changed, 980 insertions(+)
 create mode 100755 tests/qemu-iotests/191
 create mode 100644 tests/qemu-iotests/191.out
tests/qemu-iotests/191 new file +152 -0
new file mode 100755
index 0000000000..a4e0d691fa
@@ -0,0 +1,152 @@
+#!/bin/bash
+#
+# Test commit block job where top has two parents
+#
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
[email protected]
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+status=1 # failure is the default!
+
+MIG_SOCKET="${TEST_DIR}/migrate"
+
+_cleanup()
+{
+    rm -f "${TEST_IMG}.mid"
+    rm -f "${TEST_IMG}.ovl2"
+    rm -f "${TEST_IMG}.ovl3"
+    _cleanup_test_img
+    _cleanup_qemu
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.qemu
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+size=64M
+
+echo
+echo === Preparing and starting VM ===
+echo
+
+TEST_IMG="${TEST_IMG}.base" _make_test_img $size
+TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base"
+_make_test_img -b "${TEST_IMG}.mid"
+TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid"
+
+$QEMU_IO -c 'write -P 0x55 1M 64k' "${TEST_IMG}.mid" | _filter_qemu_io
+
+qemu_comm_method="qmp"
+qmp_pretty="y"
+
+_launch_qemu \
+    -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG}.base,node-name=base" \
+    -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG}.mid,node-name=mid,backing=base" \
+    -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG},node-name=top,backing=mid" \
+    -blockdev "driver=${IMGFMT},file.driver=file,file.filename=${TEST_IMG}.ovl2,node-name=top2,backing=mid"
+h=$QEMU_HANDLE
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" '^}'
+
+echo
+echo === Perform commit job ===
+echo
+
+_send_qemu_cmd $h \
+    "{ 'execute': 'block-commit',
+       'arguments': { 'job-id': 'commit0',
+                      'device': 'top',
+                      'base':'$TEST_IMG.base',
+                      'top': '$TEST_IMG.mid' } }" \
+    "BLOCK_JOB_COMPLETED"
+_send_qemu_cmd $h "" "^}"
+
+echo
+echo === Check that both top and top2 point to base now ===
+echo
+
+_send_qemu_cmd $h "{ 'execute': 'query-named-block-nodes' }" "^}" |
+    _filter_generated_node_ids
+
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "^}"
+wait=1 _cleanup_qemu
+
+_img_info
+TEST_IMG="$TEST_IMG.ovl2" _img_info
+
+
+echo
+echo === Preparing and starting VM with -drive ===
+echo
+
+TEST_IMG="${TEST_IMG}.base" _make_test_img $size
+TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base"
+_make_test_img -b "${TEST_IMG}.mid"
+TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid"
+TEST_IMG="${TEST_IMG}.ovl3" _make_test_img -b "${TEST_IMG}.ovl2"
+
+$QEMU_IO -c 'write -P 0x55 1M 64k' "${TEST_IMG}.mid" | _filter_qemu_io
+
+qemu_comm_method="qmp"
+qmp_pretty="y"
+
+_launch_qemu \
+    -drive "driver=${IMGFMT},file=${TEST_IMG},node-name=top,backing.node-name=mid" \
+    -drive "driver=${IMGFMT},file=${TEST_IMG}.ovl3,node-name=top2,backing.backing=mid"
+h=$QEMU_HANDLE
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" '^}'
+
+echo
+echo === Perform commit job ===
+echo
+
+_send_qemu_cmd $h \
+    "{ 'execute': 'block-commit',
+       'arguments': { 'job-id': 'commit0',
+                      'device': 'top',
+                      'base':'$TEST_IMG.base',
+                      'top': '$TEST_IMG.mid' } }" \
+    "BLOCK_JOB_COMPLETED"
+_send_qemu_cmd $h "" "^}"
+
+echo
+echo === Check that both top and top2 point to base now ===
+echo
+
+_send_qemu_cmd $h "{ 'execute': 'query-named-block-nodes' }" "^}" |
+    _filter_generated_node_ids
+
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "^}"
+wait=1 _cleanup_qemu
+
+_img_info
+TEST_IMG="$TEST_IMG.ovl2" _img_info
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
tests/qemu-iotests/191.out new file +827 -0

827 lines not on this page · raw message

tests/qemu-iotests/group +1 -0
index 108339cd03..63d4ee052c 100644
@@ -187,6 +187,7 @@
 188 rw auto quick
 189 rw auto
 190 rw auto quick
+191 rw auto
 192 rw auto quick
 194 rw auto migration quick
 195 rw auto quick
Signature · 1 line
2.13.5
#9 lore raw

Re: [Qemu-devel] [PATCH 4/5] qemu-iotests: Test commit block job where top has two parents

Quoted 10 lines · Kevin Wolf
---
 tests/qemu-iotests/191     | 152 +++++++++
 tests/qemu-iotests/191.out | 827 +++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/group   |   1 +
 3 files changed, 980 insertions(+)
 create mode 100755 tests/qemu-iotests/191
 create mode 100644 tests/qemu-iotests/191.out

tests/qemu-iotests/191 line 40

Quoted 10 lines
tests/qemu-iotests/191 new file +10 -0
+
+_cleanup()
+{
+    rm -f "${TEST_IMG}.mid"
+    rm -f "${TEST_IMG}.ovl2"
+    rm -f "${TEST_IMG}.ovl3"
+    _cleanup_test_img
+    _cleanup_qemu
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15

Semantic conflict with Jeff's work to allow preservation of intermediate files via './check -s'.

Quoted 6 lines

+++ b/tests/qemu-iotests/191.out

tests/qemu-iotests/191.out new file +4 -0
+{
+    "timestamp": {
+        "seconds":  TIMESTAMP,
+        "microseconds":  TIMESTAMP

tests/qemu-iotests/191.out line 35

Quoted 10 lines
tests/qemu-iotests/191.out new file +10 -0
+    },
+    "event": "BLOCK_JOB_COMPLETED",
+    "data": {
+        "device": "commit0",
+        "len": 67108864,
+        "offset": 67108864,
+        "speed": 0,
+        "type": "commit"
+    }
+}

This may be sensitive to ordering if I ever finish my patches that allow for QAPI->JSON conversion without having to first go through QObject (as it is, it's dependent on QDict's hash being stable, whereas my code switches things to be stable according to QAPI ordering). But that's not a problem for your patch.

Review

Quoted 2 lines
(quoted diff) +2 -0
+                        "data": {
+                            "compat": "1.1",

You should make the test specifically exclude compat=0.10 images, or else have further filtering in place if we can still support this on old images.

Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
author
#10 lore raw

Re: [Qemu-devel] [PATCH 4/5] qemu-iotests: Test commit block job where top has two parents

Am 25.09.2017 um 22:19 hat Eric Blake geschrieben:

Quoted 7 lines · Kevin Wolf

Review

Quoted 2 lines
(quoted diff) +2 -0
+                        "data": {
+                            "compat": "1.1",

You should make the test specifically exclude compat=0.10 images, or else have further filtering in place if we can still support this on old images.

Squashed in a line to this effect:

_unsupported_imgopts compat=0.10

And applied the series to the block branch.

Kevin

author
#11 lore raw

PATCH 5/5 commit: Remove overlay_bs

In reply to Kevin Wolf

We don't need to make any assumptions about the graph layout above the top node of the commit operation any more. Remove the use of bdrv_find_overlay() and related variables from the commit job code.

bdrv_drop_intermediate() doesn't use the 'active' parameter any more, so we can just drop it.

The overlay node was previously added to the block job to get a BLK_PERM_GRAPH_MOD. We really need to respect those permissions in bdrv_drop_intermediate() now, but as long as we haven't figured out yet how BLK_PERM_GRAPH_MOD is actually supposed to work, just leave a TODO comment there.

With this change, it is now possible to perform another block job on an overlay node without conflicts. qemu-iotests 030 is changed accordingly.

---
 include/block/block.h  |  3 +--
 block.c                |  6 +++--
 block/commit.c         | 62 ++++++++++++--------------------------------------
 tests/qemu-iotests/030 |  4 ----
 4 files changed, 20 insertions(+), 55 deletions(-)
include/block/block.h +1 -2
index 3c3af462e4..d5c2731a03 100644
@@ -315,8 +315,7 @@ int bdrv_commit(BlockDriverState *bs);
 int bdrv_change_backing_file(BlockDriverState *bs,
     const char *backing_file, const char *backing_fmt);
 void bdrv_register(BlockDriver *bdrv);
-int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
-                           BlockDriverState *base,
+int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
                            const char *backing_file_str);
 BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
                                     BlockDriverState *bs);
block.c +4 -2
index e57fab501d..5f2d7222a5 100644
@@ -3491,8 +3491,8 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
  *  if active == top, that is considered an error
  *
  */
-int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
-                           BlockDriverState *base, const char *backing_file_str)
+int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
+                           const char *backing_file_str)
 {
     BdrvChild *c, *next;
     Error *local_err = NULL;
@@ -3510,6 +3510,8 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
     }
 
     /* success - we can delete the intermediate states, and link top->base */
+    /* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
+     * we've figured out how they should work. */
     backing_file_str = backing_file_str ? backing_file_str : base->filename;
 
     QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
block/commit.c +15 -47

149 lines not on this page · raw message

tests/qemu-iotests/030 +0 -4
index d745cb4cde..18838948fa 100755
@@ -287,10 +287,6 @@ class TestParallelOps(iotests.QMPTestCase):
         result = self.vm.qmp('block-stream', device='node6', base=self.imgs[4], job_id='stream-node6-v2')
         self.assert_qmp(result, 'error/class', 'GenericError')
 
-        # This fails because block-commit needs to block node6, the overlay of the 'top' image
-        result = self.vm.qmp('block-stream', device='node7', base=self.imgs[5], job_id='stream-node6-v3')
-        self.assert_qmp(result, 'error/class', 'GenericError')
-
         # This fails because block-commit currently blocks the active layer even if it's not used
         result = self.vm.qmp('block-stream', device='drive0', base=self.imgs[5], job_id='stream-drive0')
         self.assert_qmp(result, 'error/class', 'GenericError')
Signature · 1 line
2.13.5
Reviewed-by on v1 5/5
#12 lore raw

Re: [Qemu-devel] [PATCH 5/5] commit: Remove overlay_bs

Quoted 16 lines · Kevin Wolf

We don't need to make any assumptions about the graph layout above the top node of the commit operation any more. Remove the use of bdrv_find_overlay() and related variables from the commit job code.

bdrv_drop_intermediate() doesn't use the 'active' parameter any more, so we can just drop it.

The overlay node was previously added to the block job to get a BLK_PERM_GRAPH_MOD. We really need to respect those permissions in bdrv_drop_intermediate() now, but as long as we haven't figured out yet how BLK_PERM_GRAPH_MOD is actually supposed to work, just leave a TODO comment there.

With this change, it is now possible to perform another block job on an overlay node without conflicts. qemu-iotests 030 is changed accordingly.

Quoted 8 lines
---
 include/block/block.h  |  3 +--
 block.c                |  6 +++--
 block/commit.c         | 62 ++++++++++++--------------------------------------
 tests/qemu-iotests/030 |  4 ----
 4 files changed, 20 insertions(+), 55 deletions(-)
Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
#13 lore raw
In reply to Kevin Wolf
Quoted 22 lines · Kevin Wolf

This is a step towards making the commit job flexible enough that it can work with any kind of block graph. Currently, it requires that not only the top and base node of the commit operation are specified, but also the active layer of the backing file chain. Of course, the assumption that a single active layer exists is invalid.

This series makes the commit job consider other roots as well so that all parent nodes of the top node get their backing file updated and stay valid after the commit job completes.

With this, we should have all of the prerequisites for a follow-up series that adds a new and clean blockdev-commit QMP command which doesn't require an option for the active layer and which accepts node names instead of file names for base and top.

Kevin Wolf (5):
block: Introduce BdrvChildRole.update_filename
commit: Support multiple roots above top node
qemu-iotests: Allow QMP pretty printing in common.qemu
qemu-iotests: Test commit block job where top has two parents
commit: Remove overlay_bs

Quoted 13 lines
 include/block/block.h          |   3 +-
 include/block/block_int.h      |   6 +
 block.c                        |  91 +++--
 block/commit.c                 |  64 +---
 tests/qemu-iotests/030         |   4 -
 tests/qemu-iotests/191         | 152 ++++++++
 tests/qemu-iotests/191.out     | 827 +++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/common.qemu |  14 +-
 tests/qemu-iotests/group       |   1 +
 9 files changed, 1078 insertions(+), 84 deletions(-)
 create mode 100755 tests/qemu-iotests/191
 create mode 100644 tests/qemu-iotests/191.out

Does this depend on another series?

author
#14 lore raw

Am 25.09.2017 um 22:02 hat John Snow geschrieben:

Quoted 16 lines · Kevin Wolf
Quoted 15 lines

This is a step towards making the commit job flexible enough that it can work with any kind of block graph. Currently, it requires that not only the top and base node of the commit operation are specified, but also the active layer of the backing file chain. Of course, the assumption that a single active layer exists is invalid.

This series makes the commit job consider other roots as well so that all parent nodes of the top node get their backing file updated and stay valid after the commit job completes.

With this, we should have all of the prerequisites for a follow-up series that adds a new and clean blockdev-commit QMP command which doesn't require an option for the active layer and which accepts node names instead of file names for base and top.

Quoted 8 lines
Quoted 6 lines

Kevin Wolf (5):
block: Introduce BdrvChildRole.update_filename
commit: Support multiple roots above top node
qemu-iotests: Allow QMP pretty printing in common.qemu
qemu-iotests: Test commit block job where top has two parents
commit: Remove overlay_bs

Does this depend on another series?

It is based on my block branch. I think specifically the series "block:
Fix permissions after ro/rw reopen" might be needed for the patches to work correctly.

Kevin