Skip to content

Commit

Permalink
block: Skip implicit nodes in query-block/blockstats
Browse files Browse the repository at this point in the history
Commits 0db832f and 6cdbceb introduced the automatic insertion of filter
nodes above the top layer of mirror and commit block jobs. The
assumption made there was that since libvirt doesn't do node-level
management of the block layer yet, it shouldn't be affected by added
nodes.

This is true as far as commands issued by libvirt are concerned. It only
uses BlockBackend names to address nodes, so any operations it performs
still operate on the root of the tree as intended.

However, the assumption breaks down when you consider query commands,
which return data for the wrong node now. These commands also return
information on some child nodes (bs->file and/or bs->backing), which
libvirt does make use of, and which refer to the wrong nodes, too.

One of the consequences is that oVirt gets wrong information about the
image size and stops the VM in response as long as a mirror or commit
job is running:

https://bugzilla.redhat.com/show_bug.cgi?id=1470634

This patch fixes the problem by hiding the implicit nodes created
automatically by the mirror and commit block jobs in the output of
query-block and BlockBackend-based query-blockstats as long as the user
doesn't indicate that they are aware of those nodes by providing a node
name for them in the QMP command to start the block job.

The node-based commands query-named-block-nodes and query-blockstats
with query-nodes=true still show all nodes, including implicit ones.
This ensures that users that are capable of node-level management can
still access the full information; users that only know BlockBackends
won't use these commands.

Cc: qemu-stable@nongnu.org
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Tested-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit d3c8c67)
 Conflicts:
	block/qapi.c
	include/block/block_int.h
* fix context deps on 46eade7 and 5a9347c
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
  • Loading branch information
kevmw authored and mdroth committed Aug 24, 2017
1 parent d445e0a commit a0ddbcf
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 28 deletions.
13 changes: 0 additions & 13 deletions block.c
Expand Up @@ -3863,19 +3863,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
return retval;
}

int bdrv_get_backing_file_depth(BlockDriverState *bs)
{
if (!bs->drv) {
return 0;
}

if (!bs->backing) {
return 0;
}

return 1 + bdrv_get_backing_file_depth(bs->backing->bs);
}

void bdrv_init(void)
{
module_call_init(MODULE_INIT_BLOCK);
Expand Down
3 changes: 3 additions & 0 deletions block/commit.c
Expand Up @@ -351,6 +351,9 @@ void commit_start(const char *job_id, BlockDriverState *bs,
if (commit_top_bs == NULL) {
goto fail;
}
if (!filter_node_name) {
commit_top_bs->implicit = true;
}
commit_top_bs->total_sectors = top->total_sectors;
bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top));

Expand Down
3 changes: 3 additions & 0 deletions block/mirror.c
Expand Up @@ -1152,6 +1152,9 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
if (mirror_top_bs == NULL) {
return;
}
if (!filter_node_name) {
mirror_top_bs->implicit = true;
}
mirror_top_bs->total_sectors = bs->total_sectors;
bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs));

Expand Down
34 changes: 28 additions & 6 deletions block/qapi.c
Expand Up @@ -64,7 +64,6 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
info->backing_file = g_strdup(bs->backing_file);
}

info->backing_file_depth = bdrv_get_backing_file_depth(bs);
info->detect_zeroes = bs->detect_zeroes;

if (blk && blk_get_public(blk)->throttle_state) {
Expand Down Expand Up @@ -125,6 +124,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,

bs0 = bs;
p_image_info = &info->image;
info->backing_file_depth = 0;
while (1) {
Error *local_err = NULL;
bdrv_query_image_info(bs0, p_image_info, &local_err);
Expand All @@ -133,13 +133,21 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
qapi_free_BlockDeviceInfo(info);
return NULL;
}

if (bs0->drv && bs0->backing) {
info->backing_file_depth++;
bs0 = bs0->backing->bs;
(*p_image_info)->has_backing_image = true;
p_image_info = &((*p_image_info)->backing_image);
} else {
break;
}

/* Skip automatically inserted nodes that the user isn't aware of for
* query-block (blk != NULL), but not for query-named-block-nodes */
while (blk && bs0 && bs0->drv && bs0->implicit) {
bs0 = backing_bs(bs0);
}
}

return info;
Expand Down Expand Up @@ -322,6 +330,12 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
{
BlockInfo *info = g_malloc0(sizeof(*info));
BlockDriverState *bs = blk_bs(blk);

/* Skip automatically inserted nodes that the user isn't aware of */
while (bs && bs->drv && bs->implicit) {
bs = backing_bs(bs);
}

info->device = g_strdup(blk_name(blk));
info->type = g_strdup("unknown");
info->locked = blk_dev_is_medium_locked(blk);
Expand Down Expand Up @@ -424,8 +438,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
}
}

static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
bool query_backing)
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
bool blk_level)
{
BlockStats *s = NULL;

Expand All @@ -436,6 +450,14 @@ static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
return s;
}

/* Skip automatically inserted nodes that the user isn't aware of in
* a BlockBackend-level command. Stay at the exact node for a node-level
* command. */
while (blk_level && bs->drv && bs->implicit) {
bs = backing_bs(bs);
assert(bs);
}

if (bdrv_get_node_name(bs)[0]) {
s->has_node_name = true;
s->node_name = g_strdup(bdrv_get_node_name(bs));
Expand All @@ -445,12 +467,12 @@ static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,

if (bs->file) {
s->has_parent = true;
s->parent = bdrv_query_bds_stats(bs->file->bs, query_backing);
s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
}

if (query_backing && bs->backing) {
if (blk_level && bs->backing) {
s->has_backing = true;
s->backing = bdrv_query_bds_stats(bs->backing->bs, query_backing);
s->backing = bdrv_query_bds_stats(bs->backing->bs, blk_level);
}

return s;
Expand Down
1 change: 0 additions & 1 deletion include/block/block.h
Expand Up @@ -292,7 +292,6 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset,
int count, BdrvRequestFlags flags);
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
const char *backing_file);
int bdrv_get_backing_file_depth(BlockDriverState *bs);
void bdrv_refresh_filename(BlockDriverState *bs);
int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp);
int64_t bdrv_nb_sectors(BlockDriverState *bs);
Expand Down
1 change: 1 addition & 0 deletions include/block/block_int.h
Expand Up @@ -518,6 +518,7 @@ struct BlockDriverState {
bool valid_key; /* if true, a valid encryption key has been set */
bool sg; /* if true, the device is a /dev/sg* */
bool probed; /* if true, format was probed rather than specified */
bool implicit; /* if true, this filter node was automatically inserted */

BlockDriver *drv; /* NULL means no media */
void *opaque;
Expand Down
6 changes: 4 additions & 2 deletions qapi/block-core.json
Expand Up @@ -467,7 +467,8 @@
#
# Get a list of BlockInfo for all virtual block devices.
#
# Returns: a list of @BlockInfo describing each virtual block device
# Returns: a list of @BlockInfo describing each virtual block device. Filter
# nodes that were created implicitly are skipped over.
#
# Since: 0.14.0
#
Expand Down Expand Up @@ -723,7 +724,8 @@
# information, but not "backing".
# If false or omitted, the behavior is as before - query all the
# device backends, recursively including their "parent" and
# "backing". (Since 2.3)
# "backing". Filter nodes that were created implicitly are
# skipped over in this mode. (Since 2.3)
#
# Returns: A list of @BlockStats for each virtual block devices.
#
Expand Down
30 changes: 29 additions & 1 deletion tests/qemu-iotests/040
Expand Up @@ -81,7 +81,7 @@ class TestSingleDrive(ImageCommitTestCase):
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
self.vm = iotests.VM().add_drive(test_img, interface="none")
self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none")
self.vm.add_device("virtio-scsi-pci")
self.vm.add_device("scsi-hd,id=scsi0,drive=drive0")
self.vm.launch()
Expand Down Expand Up @@ -163,6 +163,34 @@ class TestSingleDrive(ImageCommitTestCase):

self.assert_no_active_block_jobs()

# Tests that the insertion of the commit_top filter node doesn't make a
# difference to query-blockstat
def test_implicit_node(self):
if self.image_len == 0:
return

self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top=mid_img,
base=backing_img, speed=(self.image_len / 4))
self.assert_qmp(result, 'return', {})

result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
self.assert_qmp(result, 'return[0]/inserted/backing_file', mid_img)
self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 2)
self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', mid_img)
self.assert_qmp(result, 'return[0]/inserted/image/backing-image/backing-image/filename', backing_img)

result = self.vm.qmp('query-blockstats')
self.assert_qmp(result, 'return[0]/node-name', 'top')
self.assert_qmp(result, 'return[0]/backing/node-name', 'mid')
self.assert_qmp(result, 'return[0]/backing/backing/node-name', 'base')

self.cancel_and_wait()
self.assert_no_active_block_jobs()

class TestRelativePaths(ImageCommitTestCase):
image_len = 1 * 1024 * 1024
test_len = 1 * 1024 * 256
Expand Down
4 changes: 2 additions & 2 deletions tests/qemu-iotests/040.out
@@ -1,5 +1,5 @@
...........................
.............................
----------------------------------------------------------------------
Ran 27 tests
Ran 29 tests

OK
38 changes: 37 additions & 1 deletion tests/qemu-iotests/041
Expand Up @@ -42,7 +42,7 @@ class TestSingleDrive(iotests.QMPTestCase):
def setUp(self):
iotests.create_image(backing_img, self.image_len)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=base")
if iotests.qemu_default_machine == 'pc':
self.vm.add_drive(None, 'media=cdrom', 'ide')
self.vm.launch()
Expand Down Expand Up @@ -169,6 +169,42 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring')

# Tests that the insertion of the mirror_top filter node doesn't make a
# difference to query-block
def test_implicit_node(self):
self.assert_no_active_block_jobs()

result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
target=self.qmp_target)
self.assert_qmp(result, 'return', {})

result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img)
self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1)
self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img)

result = self.vm.qmp('query-blockstats')
self.assert_qmp(result, 'return[0]/node-name', 'top')
self.assert_qmp(result, 'return[0]/backing/node-name', 'base')

self.cancel_and_wait(force=True)
result = self.vm.qmp('query-block')
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img)
self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1)
self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img)

result = self.vm.qmp('query-blockstats')
self.assert_qmp(result, 'return[0]/node-name', 'top')
self.assert_qmp(result, 'return[0]/backing/node-name', 'base')

self.vm.shutdown()

def test_medium_not_found(self):
if iotests.qemu_default_machine != 'pc':
return
Expand Down
4 changes: 2 additions & 2 deletions tests/qemu-iotests/041.out
@@ -1,5 +1,5 @@
...............................................................................
.....................................................................................
----------------------------------------------------------------------
Ran 79 tests
Ran 85 tests

OK

0 comments on commit a0ddbcf

Please sign in to comment.