1
The following changes since commit 0274f45bdef73283f2c213610f11d4e5dcba43b6:
1
The following changes since commit dd2db39d78431ab5a0b78777afaab3d61e94533e:
2
2
3
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-4.1-pull-request' into staging (2019-07-19 09:44:43 +0100)
3
Merge remote-tracking branch 'remotes/ehabkost-gl/tags/x86-next-pull-request' into staging (2021-06-01 21:23:26 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to 49278ec065da3fbf90f7effcde3b39ac606b2e9e:
9
for you to fetch changes up to b317006a3f1f04191a7981cef83417cb2477213b:
10
10
11
iotests: Test quitting with job on throttled node (2019-07-19 15:17:55 +0200)
11
docs/secure-coding-practices: Describe how to use 'null-co' block driver (2021-06-02 14:29:14 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
15
16
- block: Fix forbidden use of polling in drained_end
16
- NBD server: Fix crashes related to switching between AioContexts
17
- block: Don't wait for I/O throttling while exiting QEMU
17
- file-posix: Workaround for discard/write_zeroes on buggy filesystems
18
- iotests: Use read-zeroes for the null driver to be Valgrind-friendly
18
- Follow-up fixes for the reopen vs. permission changes
19
- quorum: Fix error handling for flush
20
- block-copy: Refactor copy_range handling
21
- docs: Describe how to use 'null-co' block driver
19
22
20
----------------------------------------------------------------
23
----------------------------------------------------------------
21
Andrey Shinkevich (1):
24
Lukas Straub (1):
22
iotests: Set read-zeroes on in null block driver for Valgrind
25
block/quorum: Provide .bdrv_co_flush instead of .bdrv_co_flush_to_disk
23
26
24
Max Reitz (12):
27
Philippe Mathieu-Daudé (1):
25
block: Introduce BdrvChild.parent_quiesce_counter
28
docs/secure-coding-practices: Describe how to use 'null-co' block driver
26
tests: Add job commit by drained_end test
27
block: Add @drained_end_counter
28
block: Make bdrv_parent_drained_[^_]*() static
29
tests: Lock AioContexts in test-block-iothread
30
block: Do not poll in bdrv_do_drained_end()
31
tests: Extend commit by drained_end test
32
block: Loop unsafely in bdrv*drained_end()
33
iotests: Add @has_quit to vm.shutdown()
34
iotests: Test commit with a filter on the chain
35
vl: Drain before (block) job cancel when quitting
36
iotests: Test quitting with job on throttled node
37
29
38
include/block/block.h | 42 ++++++++----
30
Sergio Lopez (2):
39
include/block/block_int.h | 15 ++++-
31
block-backend: add drained_poll
40
block.c | 52 ++++++++++-----
32
nbd/server: Use drained block ops to quiesce the server
41
block/block-backend.c | 6 +-
42
block/io.c | 134 +++++++++++++++++++++++++++----------
43
blockjob.c | 2 +-
44
tests/test-bdrv-drain.c | 147 ++++++++++++++++++++++++++++++++++++++++
45
tests/test-block-iothread.c | 40 +++++++----
46
vl.c | 11 +++
47
python/qemu/machine.py | 5 +-
48
tests/qemu-iotests/040 | 40 ++++++++++-
49
tests/qemu-iotests/040.out | 4 +-
50
tests/qemu-iotests/051 | 10 +--
51
tests/qemu-iotests/051.pc.out | 10 +--
52
tests/qemu-iotests/093 | 9 +--
53
tests/qemu-iotests/136 | 1 +
54
tests/qemu-iotests/186 | 20 +++---
55
tests/qemu-iotests/186.out | 152 +++++++++++++++++++++---------------------
56
tests/qemu-iotests/218 | 55 ++++++++++++++-
57
tests/qemu-iotests/218.out | 4 ++
58
tests/qemu-iotests/227 | 4 +-
59
tests/qemu-iotests/227.out | 4 +-
60
tests/qemu-iotests/238 | 2 +-
61
tests/qemu-iotests/240 | 8 +--
62
tests/qemu-iotests/255 | 2 +-
63
25 files changed, 576 insertions(+), 203 deletions(-)
64
33
34
Thomas Huth (2):
35
block/file-posix: Fix problem with fallocate(PUNCH_HOLE) on GPFS
36
block/file-posix: Try other fallbacks after invalid FALLOC_FL_ZERO_RANGE
37
38
Vladimir Sementsov-Ogievskiy (14):
39
qemu-io-cmds: assert that we don't have .perm requested in no-blk case
40
block/vvfat: child_vvfat_qcow: add .get_parent_aio_context, fix crash
41
block/vvfat: fix vvfat_child_perm crash
42
block: consistently use bdrv_is_read_only()
43
block: drop BlockDriverState::read_only
44
block: drop BlockBackendRootState::read_only
45
block: document child argument of bdrv_attach_child_common()
46
block-backend: improve blk_root_get_parent_desc()
47
block: improve bdrv_child_get_parent_desc()
48
block/vvfat: inherit child_vvfat_qcow from child_of_bds
49
block: simplify bdrv_child_user_desc()
50
block: improve permission conflict error message
51
block-copy: fix block_copy_task_entry() progress update
52
block-copy: refactor copy_range handling
53
54
docs/devel/secure-coding-practices.rst | 9 ++++
55
include/block/block.h | 1 +
56
include/block/block_int.h | 2 -
57
include/sysemu/block-backend.h | 4 ++
58
block.c | 82 ++++++++++++++++++++--------------
59
block/block-backend.c | 26 +++++------
60
block/block-copy.c | 80 ++++++++++++++++++++++-----------
61
block/commit.c | 2 +-
62
block/file-posix.c | 29 ++++++++----
63
block/io.c | 4 +-
64
block/qapi.c | 2 +-
65
block/qcow2-snapshot.c | 2 +-
66
block/qcow2.c | 5 +--
67
block/quorum.c | 2 +-
68
block/snapshot.c | 2 +-
69
block/vhdx-log.c | 2 +-
70
block/vvfat.c | 14 +++---
71
blockdev.c | 3 +-
72
nbd/server.c | 82 +++++++++++++++++++++++++---------
73
qemu-io-cmds.c | 14 +++++-
74
tests/unit/test-block-iothread.c | 6 ---
75
tests/qemu-iotests/283.out | 2 +-
76
tests/qemu-iotests/307.out | 2 +-
77
tests/qemu-iotests/tests/qsd-jobs.out | 2 +-
78
24 files changed, 241 insertions(+), 138 deletions(-)
79
80
diff view generated by jsdifflib
New patch
1
From: Lukas Straub <lukasstraub2@web.de>
1
2
3
The quorum block driver uses a custom flush callback to handle the
4
case when some children return io errors. In that case it still
5
returns success if enough children are healthy.
6
However, it provides it as the .bdrv_co_flush_to_disk callback, not
7
as .bdrv_co_flush. This causes the block layer to do it's own
8
generic flushing for the children instead, which doesn't handle
9
errors properly.
10
11
Fix this by providing .bdrv_co_flush instead of
12
.bdrv_co_flush_to_disk so the block layer uses the custom flush
13
callback.
14
15
Signed-off-by: Lukas Straub <lukasstraub2@web.de>
16
Reported-by: Minghao Yuan <meeho@qq.com>
17
Message-Id: <20210518134214.11ccf05f@gecko.fritz.box>
18
Tested-by: Zhang Chen <chen.zhang@intel.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
21
block/quorum.c | 2 +-
22
1 file changed, 1 insertion(+), 1 deletion(-)
23
24
diff --git a/block/quorum.c b/block/quorum.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/block/quorum.c
27
+++ b/block/quorum.c
28
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
29
.bdrv_dirname = quorum_dirname,
30
.bdrv_co_block_status = quorum_co_block_status,
31
32
- .bdrv_co_flush_to_disk = quorum_co_flush,
33
+ .bdrv_co_flush = quorum_co_flush,
34
35
.bdrv_getlength = quorum_getlength,
36
37
--
38
2.30.2
39
40
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Coverity thinks blk may be NULL. It's a false-positive, as described in
4
a new comment.
5
6
Fixes: Coverity CID 1453194
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20210519090532.3753-1-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
qemu-io-cmds.c | 14 ++++++++++++--
12
1 file changed, 12 insertions(+), 2 deletions(-)
13
14
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qemu-io-cmds.c
17
+++ b/qemu-io-cmds.c
18
@@ -XXX,XX +XXX,XX @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
19
return -EINVAL;
20
}
21
22
- /* Request additional permissions if necessary for this command. The caller
23
+ /*
24
+ * Request additional permissions if necessary for this command. The caller
25
* is responsible for restoring the original permissions afterwards if this
26
- * is what it wants. */
27
+ * is what it wants.
28
+ *
29
+ * Coverity thinks that blk may be NULL in the following if condition. It's
30
+ * not so: in init_check_command() we fail if blk is NULL for command with
31
+ * both CMD_FLAG_GLOBAL and CMD_NOFILE_OK flags unset. And in
32
+ * qemuio_add_command() we assert that command with non-zero .perm field
33
+ * doesn't set this flags. So, the following assertion is to silence
34
+ * Coverity:
35
+ */
36
+ assert(blk || !ct->perm);
37
if (ct->perm && blk_is_available(blk)) {
38
uint64_t orig_perm, orig_shared_perm;
39
blk_get_perm(blk, &orig_perm, &orig_shared_perm);
40
--
41
2.30.2
42
43
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
These functions are not used outside of block/io.c, there is no reason
3
Commit 3ca1f3225727419ba573673b744edac10904276f
4
why they should be globally available.
4
"block: BdrvChildClass: add .get_parent_aio_context handler" introduced
5
new handler and commit 228ca37e12f97788e05bd0c92f89b3e5e4019607
6
"block: drop ctx argument from bdrv_root_attach_child" made a generic
7
use of it. But 3ca1f3225727419ba573673b744edac10904276f didn't update
8
child_vvfat_qcow. Fix that.
5
9
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Before that fix the command
11
12
./build/qemu-system-x86_64 -usb -device usb-storage,drive=fat16 \
13
-drive file=fat:rw:fat-type=16:"<path of a host folder>",id=fat16,format=raw,if=none
14
15
crashes:
16
17
1 bdrv_child_get_parent_aio_context (c=0x559d62426d20)
18
at ../block.c:1440
19
2 bdrv_attach_child_common
20
(child_bs=0x559d62468190, child_name=0x559d606f9e3d "write-target",
21
child_class=0x559d60c58d20 <child_vvfat_qcow>, child_role=3,
22
perm=3, shared_perm=4, opaque=0x559d62445690,
23
child=0x7ffc74c2acc8, tran=0x559d6246ddd0, errp=0x7ffc74c2ae60)
24
at ../block.c:2795
25
3 bdrv_attach_child_noperm
26
(parent_bs=0x559d62445690, child_bs=0x559d62468190,
27
child_name=0x559d606f9e3d "write-target",
28
child_class=0x559d60c58d20 <child_vvfat_qcow>, child_role=3,
29
child=0x7ffc74c2acc8, tran=0x559d6246ddd0, errp=0x7ffc74c2ae60) at
30
../block.c:2855
31
4 bdrv_attach_child
32
(parent_bs=0x559d62445690, child_bs=0x559d62468190,
33
child_name=0x559d606f9e3d "write-target",
34
child_class=0x559d60c58d20 <child_vvfat_qcow>, child_role=3,
35
errp=0x7ffc74c2ae60) at ../block.c:2953
36
5 bdrv_open_child
37
(filename=0x559d62464b80 "/var/tmp/vl.h3TIS4",
38
options=0x559d6246ec20, bdref_key=0x559d606f9e3d "write-target",
39
parent=0x559d62445690, child_class=0x559d60c58d20
40
<child_vvfat_qcow>, child_role=3, allow_none=false,
41
errp=0x7ffc74c2ae60) at ../block.c:3351
42
6 enable_write_target (bs=0x559d62445690, errp=0x7ffc74c2ae60) at
43
../block/vvfat.c:3176
44
7 vvfat_open (bs=0x559d62445690, options=0x559d6244adb0, flags=155650,
45
errp=0x7ffc74c2ae60) at ../block/vvfat.c:1236
46
8 bdrv_open_driver (bs=0x559d62445690, drv=0x559d60d4f7e0
47
<bdrv_vvfat>, node_name=0x0,
48
options=0x559d6244adb0, open_flags=155650,
49
errp=0x7ffc74c2af70) at ../block.c:1557
50
9 bdrv_open_common (bs=0x559d62445690, file=0x0,
51
options=0x559d6244adb0, errp=0x7ffc74c2af70) at
52
...
53
54
(gdb) fr 1
55
#1 0x0000559d603ea3bf in bdrv_child_get_parent_aio_context
56
(c=0x559d62426d20) at ../block.c:1440
57
1440 return c->klass->get_parent_aio_context(c);
58
(gdb) p c->klass
59
$1 = (const BdrvChildClass *) 0x559d60c58d20 <child_vvfat_qcow>
60
(gdb) p c->klass->get_parent_aio_context
61
$2 = (AioContext *(*)(BdrvChild *)) 0x0
62
63
Fixes: 3ca1f3225727419ba573673b744edac10904276f
64
Fixes: 228ca37e12f97788e05bd0c92f89b3e5e4019607
65
Reported-by: John Arbuckle <programmingkidx@gmail.com>
66
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
67
Message-Id: <20210524101257.119377-2-vsementsov@virtuozzo.com>
68
Tested-by: John Arbuckle <programmingkidx@gmail.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
69
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
70
---
9
include/block/block.h | 18 ------------------
71
include/block/block.h | 1 +
10
block/io.c | 8 ++++----
72
block.c | 4 ++--
11
2 files changed, 4 insertions(+), 22 deletions(-)
73
block/vvfat.c | 1 +
74
3 files changed, 4 insertions(+), 2 deletions(-)
12
75
13
diff --git a/include/block/block.h b/include/block/block.h
76
diff --git a/include/block/block.h b/include/block/block.h
14
index XXXXXXX..XXXXXXX 100644
77
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/block.h
78
--- a/include/block/block.h
16
+++ b/include/block/block.h
79
+++ b/include/block/block.h
17
@@ -XXX,XX +XXX,XX @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
80
@@ -XXX,XX +XXX,XX @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
18
void bdrv_io_plug(BlockDriverState *bs);
81
bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
19
void bdrv_io_unplug(BlockDriverState *bs);
82
GSList **ignore, Error **errp);
20
83
AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
21
-/**
84
+AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
22
- * bdrv_parent_drained_begin:
85
23
- *
86
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
24
- * Begin a quiesced section of all users of @bs. This is part of
87
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
25
- * bdrv_drained_begin.
88
diff --git a/block.c b/block.c
26
- */
27
-void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
28
- bool ignore_bds_parents);
29
-
30
/**
31
* bdrv_parent_drained_begin_single:
32
*
33
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
34
*/
35
void bdrv_parent_drained_end_single(BdrvChild *c);
36
37
-/**
38
- * bdrv_parent_drained_end:
39
- *
40
- * End a quiesced section of all users of @bs. This is part of
41
- * bdrv_drained_end.
42
- */
43
-void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
44
- bool ignore_bds_parents);
45
-
46
/**
47
* bdrv_drain_poll:
48
*
49
diff --git a/block/io.c b/block/io.c
50
index XXXXXXX..XXXXXXX 100644
89
index XXXXXXX..XXXXXXX 100644
51
--- a/block/io.c
90
--- a/block.c
52
+++ b/block/io.c
91
+++ b/block.c
53
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs);
92
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
54
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
93
return 0;
55
int64_t offset, int bytes, BdrvRequestFlags flags);
94
}
56
95
57
-void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
96
-static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c)
58
- bool ignore_bds_parents)
97
+AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c)
59
+static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
60
+ bool ignore_bds_parents)
61
{
98
{
62
BdrvChild *c, *next;
99
BlockDriverState *bs = c->opaque;
63
100
64
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c)
101
@@ -XXX,XX +XXX,XX @@ const BdrvChildClass child_of_bds = {
65
}
102
.can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
66
}
103
.set_aio_ctx = bdrv_child_cb_set_aio_ctx,
67
104
.update_filename = bdrv_child_cb_update_filename,
68
-void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
105
- .get_parent_aio_context = bdrv_child_cb_get_parent_aio_context,
69
- bool ignore_bds_parents)
106
+ .get_parent_aio_context = child_of_bds_get_parent_aio_context,
70
+static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
107
};
71
+ bool ignore_bds_parents)
108
72
{
109
AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
73
BdrvChild *c, *next;
110
diff --git a/block/vvfat.c b/block/vvfat.c
74
111
index XXXXXXX..XXXXXXX 100644
112
--- a/block/vvfat.c
113
+++ b/block/vvfat.c
114
@@ -XXX,XX +XXX,XX @@ static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
115
static const BdrvChildClass child_vvfat_qcow = {
116
.parent_is_bds = true,
117
.inherit_options = vvfat_qcow_options,
118
+ .get_parent_aio_context = child_of_bds_get_parent_aio_context,
119
};
120
121
static int enable_write_target(BlockDriverState *bs, Error **errp)
75
--
122
--
76
2.20.1
123
2.30.2
77
124
78
125
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
It's wrong to rely on s->qcow in vvfat_child_perm, as on permission
4
update during bdrv_open_child() call this field is not set yet.
5
6
Still prior to aa5a04c7db27eea6b36de32f241b155f0d9ce34d, it didn't
7
crash, as bdrv_open_child passed NULL as child to bdrv_child_perm(),
8
and NULL was equal to NULL in assertion (still, it was bad guarantee
9
for child being s->qcow, not backing :).
10
11
Since aa5a04c7db27eea6b36de32f241b155f0d9ce34d
12
"add bdrv_attach_child_noperm" bdrv_refresh_perms called on parent node
13
when attaching child, and new correct child pointer is passed to
14
.bdrv_child_perm. Still, s->qcow is NULL at the moment. Let's rely only
15
on role instead.
16
17
Without that fix,
18
./build/qemu-system-x86_64 -usb -device usb-storage,drive=fat16 \
19
-drive \
20
file=fat:rw:fat-type=16:"<path of a host folder>",id=fat16,format=raw,if=none
21
22
crashes:
23
(gdb) bt
24
0 raise () at /lib64/libc.so.6
25
1 abort () at /lib64/libc.so.6
26
2 _nl_load_domain.cold () at /lib64/libc.so.6
27
3 annobin_assert.c_end () at /lib64/libc.so.6
28
4 vvfat_child_perm (bs=0x559186f3d690, c=0x559186f1ed20, role=3,
29
reopen_queue=0x0, perm=0, shared=31,
30
nperm=0x7ffe56f28298, nshared=0x7ffe56f282a0) at
31
../block/vvfat.c:3214
32
5 bdrv_child_perm (bs=0x559186f3d690, child_bs=0x559186f60190,
33
c=0x559186f1ed20, role=3, reopen_queue=0x0,
34
parent_perm=0, parent_shared=31,
35
nperm=0x7ffe56f28298, nshared=0x7ffe56f282a0)
36
at ../block.c:2094
37
6 bdrv_node_refresh_perm (bs=0x559186f3d690, q=0x0,
38
tran=0x559186f65850, errp=0x7ffe56f28530) at
39
../block.c:2336
40
7 bdrv_list_refresh_perms (list=0x559186db5b90 = {...}, q=0x0,
41
tran=0x559186f65850, errp=0x7ffe56f28530)
42
at ../block.c:2358
43
8 bdrv_refresh_perms (bs=0x559186f3d690, errp=0x7ffe56f28530) at
44
../block.c:2419
45
9 bdrv_attach_child
46
(parent_bs=0x559186f3d690, child_bs=0x559186f60190,
47
child_name=0x559184d83e3d "write-target",
48
child_class=0x5591852f3b00 <child_vvfat_qcow>, child_role=3,
49
errp=0x7ffe56f28530) at ../block.c:2959
50
10 bdrv_open_child
51
(filename=0x559186f5cb80 "/var/tmp/vl.7WYmFU",
52
options=0x559186f66c20, bdref_key=0x559184d83e3d "write-target",
53
parent=0x559186f3d690, child_class=0x5591852f3b00
54
<child_vvfat_qcow>, child_role=3, allow_none=false,
55
errp=0x7ffe56f28530) at ../block.c:3351
56
11 enable_write_target (bs=0x559186f3d690, errp=0x7ffe56f28530) at
57
../block/vvfat.c:3177
58
12 vvfat_open (bs=0x559186f3d690, options=0x559186f42db0, flags=155650,
59
errp=0x7ffe56f28530) at ../block/vvfat.c:1236
60
13 bdrv_open_driver (bs=0x559186f3d690, drv=0x5591853d97e0
61
<bdrv_vvfat>, node_name=0x0,
62
options=0x559186f42db0, open_flags=155650,
63
errp=0x7ffe56f28640) at ../block.c:1557
64
14 bdrv_open_common (bs=0x559186f3d690, file=0x0,
65
options=0x559186f42db0, errp=0x7ffe56f28640) at
66
../block.c:1833
67
...
68
69
(gdb) fr 4
70
#4 vvfat_child_perm (bs=0x559186f3d690, c=0x559186f1ed20, role=3,
71
reopen_queue=0x0, perm=0, shared=31,
72
nperm=0x7ffe56f28298, nshared=0x7ffe56f282a0) at
73
../block/vvfat.c:3214
74
3214 assert(c == s->qcow || (role & BDRV_CHILD_COW));
75
(gdb) p role
76
$1 = 3 # BDRV_CHILD_DATA | BDRV_CHILD_METADATA
77
(gdb) p *c
78
$2 = {bs = 0x559186f60190, name = 0x559186f669d0 "write-target", klass
79
= 0x5591852f3b00 <child_vvfat_qcow>, role = 3, opaque =
80
0x559186f3d690, perm = 3, shared_perm = 4, frozen = false,
81
parent_quiesce_counter = 0, next = {le_next = 0x0, le_prev =
82
0x559186f41818}, next_parent = {le_next = 0x0, le_prev =
83
0x559186f64320}}
84
(gdb) p s->qcow
85
$3 = (BdrvChild *) 0x0
86
87
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
88
Message-Id: <20210524101257.119377-3-vsementsov@virtuozzo.com>
89
Tested-by: John Arbuckle <programmingkidx@gmail.com>
90
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
91
---
92
block/vvfat.c | 7 ++-----
93
1 file changed, 2 insertions(+), 5 deletions(-)
94
95
diff --git a/block/vvfat.c b/block/vvfat.c
96
index XXXXXXX..XXXXXXX 100644
97
--- a/block/vvfat.c
98
+++ b/block/vvfat.c
99
@@ -XXX,XX +XXX,XX @@ static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
100
uint64_t perm, uint64_t shared,
101
uint64_t *nperm, uint64_t *nshared)
102
{
103
- BDRVVVFATState *s = bs->opaque;
104
-
105
- assert(c == s->qcow || (role & BDRV_CHILD_COW));
106
-
107
- if (c == s->qcow) {
108
+ if (role & BDRV_CHILD_DATA) {
109
/* This is a private node, nobody should try to attach to it */
110
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
111
*nshared = BLK_PERM_WRITE_UNCHANGED;
112
} else {
113
+ assert(role & BDRV_CHILD_COW);
114
/* The backing file is there so 'commit' can use it. vvfat doesn't
115
* access it in any way. */
116
*nperm = 0;
117
--
118
2.30.2
119
120
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The graph must not change in these loops (or a QLIST_FOREACH_SAFE would
3
It's better to use accessor function instead of bs->read_only directly.
4
not even be enough). We now ensure this by only polling once in the
4
In some places use bdrv_is_writable() instead of
5
root bdrv_drained_end() call, so we can drop the _SAFE suffix. Doing so
5
checking both BDRV_O_RDWR set and BDRV_O_INACTIVE not set.
6
makes it clear that the graph must not change.
7
6
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
In bdrv_open_common() it's a bit strange to add one more variable, but
8
we are going to drop bs->read_only in the next patch, so new ro local
9
variable substitutes it here.
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20210527154056.70294-2-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
14
---
11
block/io.c | 8 ++++----
15
block.c | 11 +++++++----
12
1 file changed, 4 insertions(+), 4 deletions(-)
16
block/block-backend.c | 2 +-
17
block/commit.c | 2 +-
18
block/io.c | 4 ++--
19
block/qapi.c | 2 +-
20
block/qcow2-snapshot.c | 2 +-
21
block/qcow2.c | 5 ++---
22
block/snapshot.c | 2 +-
23
block/vhdx-log.c | 2 +-
24
9 files changed, 17 insertions(+), 15 deletions(-)
13
25
26
diff --git a/block.c b/block.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/block.c
29
+++ b/block.c
30
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
31
QemuOpts *opts;
32
BlockDriver *drv;
33
Error *local_err = NULL;
34
+ bool ro;
35
36
assert(bs->file == NULL);
37
assert(options != NULL && bs->options != options);
38
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
39
40
bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
41
42
- if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
43
- if (!bs->read_only && bdrv_is_whitelisted(drv, true)) {
44
+ ro = bdrv_is_read_only(bs);
45
+
46
+ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) {
47
+ if (!ro && bdrv_is_whitelisted(drv, true)) {
48
ret = bdrv_apply_auto_read_only(bs, NULL, NULL);
49
} else {
50
ret = -ENOTSUP;
51
}
52
if (ret < 0) {
53
error_setg(errp,
54
- !bs->read_only && bdrv_is_whitelisted(drv, true)
55
+ !ro && bdrv_is_whitelisted(drv, true)
56
? "Driver '%s' can only be used for read-only devices"
57
: "Driver '%s' is not whitelisted",
58
drv->format_name);
59
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
60
assert(qatomic_read(&bs->copy_on_read) == 0);
61
62
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
63
- if (!bs->read_only) {
64
+ if (!ro) {
65
bdrv_enable_copy_on_read(bs);
66
} else {
67
error_setg(errp, "Can't use copy-on-read on read-only device");
68
diff --git a/block/block-backend.c b/block/block-backend.c
69
index XXXXXXX..XXXXXXX 100644
70
--- a/block/block-backend.c
71
+++ b/block/block-backend.c
72
@@ -XXX,XX +XXX,XX @@ void blk_update_root_state(BlockBackend *blk)
73
assert(blk->root);
74
75
blk->root_state.open_flags = blk->root->bs->open_flags;
76
- blk->root_state.read_only = blk->root->bs->read_only;
77
+ blk->root_state.read_only = bdrv_is_read_only(blk->root->bs);
78
blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes;
79
}
80
81
diff --git a/block/commit.c b/block/commit.c
82
index XXXXXXX..XXXXXXX 100644
83
--- a/block/commit.c
84
+++ b/block/commit.c
85
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
86
return -EBUSY;
87
}
88
89
- ro = backing_file_bs->read_only;
90
+ ro = bdrv_is_read_only(backing_file_bs);
91
92
if (ro) {
93
if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) {
14
diff --git a/block/io.c b/block/io.c
94
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
95
index XXXXXXX..XXXXXXX 100644
16
--- a/block/io.c
96
--- a/block/io.c
17
+++ b/block/io.c
97
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
98
@@ -XXX,XX +XXX,XX @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes,
19
bool ignore_bds_parents,
99
20
int *drained_end_counter)
100
bdrv_check_request(offset, bytes, &error_abort);
21
{
101
22
- BdrvChild *c, *next;
102
- if (bs->read_only) {
23
+ BdrvChild *c;
103
+ if (bdrv_is_read_only(bs)) {
24
104
return -EPERM;
25
- QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
105
}
26
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
106
27
if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) {
107
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
28
continue;
108
if (new_bytes) {
29
}
109
bdrv_make_request_serialising(&req, 1);
30
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
110
}
31
BdrvChild *parent, bool ignore_bds_parents,
111
- if (bs->read_only) {
32
int *drained_end_counter)
112
+ if (bdrv_is_read_only(bs)) {
33
{
113
error_setg(errp, "Image is read-only");
34
- BdrvChild *child, *next;
114
ret = -EACCES;
35
+ BdrvChild *child;
115
goto out;
36
int old_quiesce_counter;
116
diff --git a/block/qapi.c b/block/qapi.c
37
117
index XXXXXXX..XXXXXXX 100644
38
assert(drained_end_counter != NULL);
118
--- a/block/qapi.c
39
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
119
+++ b/block/qapi.c
40
if (recursive) {
120
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
41
assert(!ignore_bds_parents);
121
42
bs->recursive_quiesce_counter--;
122
info = g_malloc0(sizeof(*info));
43
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
123
info->file = g_strdup(bs->filename);
44
+ QLIST_FOREACH(child, &bs->children, next) {
124
- info->ro = bs->read_only;
45
bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents,
125
+ info->ro = bdrv_is_read_only(bs);
46
drained_end_counter);
126
info->drv = g_strdup(bs->drv->format_name);
47
}
127
info->encrypted = bs->encrypted;
128
129
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
130
index XXXXXXX..XXXXXXX 100644
131
--- a/block/qcow2-snapshot.c
132
+++ b/block/qcow2-snapshot.c
133
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
134
int new_l1_bytes;
135
int ret;
136
137
- assert(bs->read_only);
138
+ assert(bdrv_is_read_only(bs));
139
140
/* Search the snapshot */
141
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
142
diff --git a/block/qcow2.c b/block/qcow2.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/qcow2.c
145
+++ b/block/qcow2.c
146
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
147
148
/* Clear unknown autoclear feature bits */
149
update_header |= s->autoclear_features & ~QCOW2_AUTOCLEAR_MASK;
150
- update_header =
151
- update_header && !bs->read_only && !(flags & BDRV_O_INACTIVE);
152
+ update_header = update_header && bdrv_is_writable(bs);
153
if (update_header) {
154
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
155
}
156
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
157
bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
158
159
/* Repair image if dirty */
160
- if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
161
+ if (!(flags & BDRV_O_CHECK) && bdrv_is_writable(bs) &&
162
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
163
BdrvCheckResult result = {0};
164
165
diff --git a/block/snapshot.c b/block/snapshot.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/snapshot.c
168
+++ b/block/snapshot.c
169
@@ -XXX,XX +XXX,XX @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs,
170
error_setg(errp, "snapshot_id and name are both NULL");
171
return -EINVAL;
172
}
173
- if (!bs->read_only) {
174
+ if (!bdrv_is_read_only(bs)) {
175
error_setg(errp, "Device is not readonly");
176
return -EINVAL;
177
}
178
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/block/vhdx-log.c
181
+++ b/block/vhdx-log.c
182
@@ -XXX,XX +XXX,XX @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
183
}
184
185
if (logs.valid) {
186
- if (bs->read_only) {
187
+ if (bdrv_is_read_only(bs)) {
188
bdrv_refresh_filename(bs);
189
ret = -EPERM;
190
error_setg(errp,
48
--
191
--
49
2.20.1
192
2.30.2
50
193
51
194
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Commit 5cb2737e925042e6c7cd3fb0b01313950b03cddf laid out why
3
This variable is just a cache for !(bs->open_flags & BDRV_O_RDWR),
4
bdrv_do_drained_end() must decrement the quiesce_counter after
4
which we have to synchronize everywhere. Let's just drop it and
5
bdrv_drain_invoke(). It did not give a very good reason why it has to
5
consistently use bdrv_is_read_only().
6
happen after bdrv_parent_drained_end(), instead only claiming symmetry
7
to bdrv_do_drained_begin().
8
6
9
It turns out that delaying it for so long is wrong.
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
8
Message-Id: <20210527154056.70294-3-vsementsov@virtuozzo.com>
11
Situation: We have an active commit job (i.e. a mirror job) from top to
12
base for the following graph:
13
14
filter
15
|
16
[file]
17
|
18
v
19
top --[backing]--> base
20
21
Now the VM is closed, which results in the job being cancelled and a
22
bdrv_drain_all() happening pretty much simultaneously.
23
24
Beginning the drain means the job is paused once whenever one of its
25
nodes is quiesced. This is reversed when the drain ends.
26
27
With how the code currently is, after base's drain ends (which means
28
that it will have unpaused the job once), its quiesce_counter remains at
29
1 while it goes to undrain its parents (bdrv_parent_drained_end()). For
30
some reason or another, undraining filter causes the job to be kicked
31
and enter mirror_exit_common(), where it proceeds to invoke
32
block_job_remove_all_bdrv().
33
34
Now base will be detached from the job. Because its quiesce_counter is
35
still 1, it will unpause the job once more. So in total, undraining
36
base will unpause the job twice. Eventually, this will lead to the
37
job's pause_count going negative -- well, it would, were there not an
38
assertion against this, which crashes qemu.
39
40
The general problem is that if in bdrv_parent_drained_end() we undrain
41
parent A, and then undrain parent B, which then leads to A detaching the
42
child, bdrv_replace_child_noperm() will undrain A as if we had not done
43
so yet; that is, one time too many.
44
45
It follows that we cannot decrement the quiesce_counter after invoking
46
bdrv_parent_drained_end().
47
48
Unfortunately, decrementing it before bdrv_parent_drained_end() would be
49
wrong, too. Imagine the above situation in reverse: Undraining A leads
50
to B detaching the child. If we had already decremented the
51
quiesce_counter by that point, bdrv_replace_child_noperm() would undrain
52
B one time too little; because it expects bdrv_parent_drained_end() to
53
issue this undrain. But bdrv_parent_drained_end() won't do that,
54
because B is no longer a parent.
55
56
Therefore, we have to do something else. This patch opts for
57
introducing a second quiesce_counter that counts how many times a
58
child's parent has been quiesced (though c->role->drained_*). With
59
that, bdrv_replace_child_noperm() just has to undrain the parent exactly
60
that many times when removing a child, and it will always be right.
61
62
Signed-off-by: Max Reitz <mreitz@redhat.com>
63
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
64
---
10
---
65
include/block/block.h | 7 +++++++
11
include/block/block_int.h | 1 -
66
include/block/block_int.h | 9 +++++++++
12
block.c | 7 +------
67
block.c | 15 +++++----------
13
tests/unit/test-block-iothread.c | 6 ------
68
block/io.c | 14 +++++++++++---
14
3 files changed, 1 insertion(+), 13 deletions(-)
69
4 files changed, 32 insertions(+), 13 deletions(-)
70
15
71
diff --git a/include/block/block.h b/include/block/block.h
72
index XXXXXXX..XXXXXXX 100644
73
--- a/include/block/block.h
74
+++ b/include/block/block.h
75
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
76
*/
77
void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
78
79
+/**
80
+ * bdrv_parent_drained_end_single:
81
+ *
82
+ * End a quiesced section for the parent of @c.
83
+ */
84
+void bdrv_parent_drained_end_single(BdrvChild *c);
85
+
86
/**
87
* bdrv_parent_drained_end:
88
*
89
diff --git a/include/block/block_int.h b/include/block/block_int.h
16
diff --git a/include/block/block_int.h b/include/block/block_int.h
90
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
91
--- a/include/block/block_int.h
18
--- a/include/block/block_int.h
92
+++ b/include/block/block_int.h
19
+++ b/include/block/block_int.h
93
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
20
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
21
* locking needed during I/O...
94
*/
22
*/
95
bool frozen;
23
int open_flags; /* flags used to open the file, re-used for re-open */
96
24
- bool read_only; /* if true, the media is read only */
97
+ /*
25
bool encrypted; /* if true, the media is encrypted */
98
+ * How many times the parent of this child has been drained
26
bool sg; /* if true, the device is a /dev/sg* */
99
+ * (through role->drained_*).
27
bool probed; /* if true, format was probed rather than specified */
100
+ * Usually, this is equal to bs->quiesce_counter (potentially
101
+ * reduced by bdrv_drain_all_count). It may differ while the
102
+ * child is entering or leaving a drained section.
103
+ */
104
+ int parent_quiesce_counter;
105
+
106
QLIST_ENTRY(BdrvChild) next;
107
QLIST_ENTRY(BdrvChild) next_parent;
108
};
109
diff --git a/block.c b/block.c
28
diff --git a/block.c b/block.c
110
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
111
--- a/block.c
30
--- a/block.c
112
+++ b/block.c
31
+++ b/block.c
113
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
32
@@ -XXX,XX +XXX,XX @@ void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
114
if (child->role->detach) {
33
* image is inactivated. */
115
child->role->detach(child);
34
bool bdrv_is_read_only(BlockDriverState *bs)
116
}
35
{
117
- if (old_bs->quiesce_counter && child->role->drained_end) {
36
- return bs->read_only;
118
- int num = old_bs->quiesce_counter;
37
+ return !(bs->open_flags & BDRV_O_RDWR);
119
- if (child->role->parent_is_bds) {
38
}
120
- num -= bdrv_drain_all_count;
39
121
- }
40
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
122
- assert(num >= 0);
41
@@ -XXX,XX +XXX,XX @@ int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
123
- for (i = 0; i < num; i++) {
42
goto fail;
124
- child->role->drained_end(child);
125
- }
126
+ while (child->parent_quiesce_counter) {
127
+ bdrv_parent_drained_end_single(child);
128
}
129
QLIST_REMOVE(child, next_parent);
130
+ } else {
131
+ assert(child->parent_quiesce_counter == 0);
132
}
43
}
133
44
134
child->bs = new_bs;
45
- bs->read_only = true;
135
46
bs->open_flags &= ~BDRV_O_RDWR;
136
if (new_bs) {
47
137
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
48
return 0;
138
- if (new_bs->quiesce_counter && child->role->drained_begin) {
49
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
139
+ if (new_bs->quiesce_counter) {
50
}
140
int num = new_bs->quiesce_counter;
51
141
if (child->role->parent_is_bds) {
52
bs->drv = drv;
142
num -= bdrv_drain_all_count;
53
- bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
143
diff --git a/block/io.c b/block/io.c
54
bs->opaque = g_malloc0(drv->instance_size);
55
56
if (drv->bdrv_file_open) {
57
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
58
trace_bdrv_open_common(bs, filename ?: "", bs->open_flags,
59
drv->format_name);
60
61
- bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
62
-
63
ro = bdrv_is_read_only(bs);
64
65
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) {
66
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
67
bs->explicit_options = reopen_state->explicit_options;
68
bs->options = reopen_state->options;
69
bs->open_flags = reopen_state->flags;
70
- bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
71
bs->detect_zeroes = reopen_state->detect_zeroes;
72
73
if (reopen_state->replace_backing_bs) {
74
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
144
index XXXXXXX..XXXXXXX 100644
75
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
76
--- a/tests/unit/test-block-iothread.c
146
+++ b/block/io.c
77
+++ b/tests/unit/test-block-iothread.c
147
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
78
@@ -XXX,XX +XXX,XX @@ static void test_sync_op_truncate(BdrvChild *c)
148
}
79
g_assert_cmpint(ret, ==, -EINVAL);
80
81
/* Error: Read-only image */
82
- c->bs->read_only = true;
83
c->bs->open_flags &= ~BDRV_O_RDWR;
84
85
ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL);
86
g_assert_cmpint(ret, ==, -EACCES);
87
88
- c->bs->read_only = false;
89
c->bs->open_flags |= BDRV_O_RDWR;
149
}
90
}
150
91
151
+void bdrv_parent_drained_end_single(BdrvChild *c)
92
@@ -XXX,XX +XXX,XX @@ static void test_sync_op_flush(BdrvChild *c)
152
+{
93
g_assert_cmpint(ret, ==, 0);
153
+ assert(c->parent_quiesce_counter > 0);
94
154
+ c->parent_quiesce_counter--;
95
/* Early success: Read-only image */
155
+ if (c->role->drained_end) {
96
- c->bs->read_only = true;
156
+ c->role->drained_end(c);
97
c->bs->open_flags &= ~BDRV_O_RDWR;
157
+ }
98
158
+}
99
ret = bdrv_flush(c->bs);
159
+
100
g_assert_cmpint(ret, ==, 0);
160
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
101
161
bool ignore_bds_parents)
102
- c->bs->read_only = false;
162
{
103
c->bs->open_flags |= BDRV_O_RDWR;
163
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
164
if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) {
165
continue;
166
}
167
- if (c->role->drained_end) {
168
- c->role->drained_end(c);
169
- }
170
+ bdrv_parent_drained_end_single(c);
171
}
172
}
104
}
173
105
174
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
106
@@ -XXX,XX +XXX,XX @@ static void test_sync_op_blk_flush(BlockBackend *blk)
175
107
g_assert_cmpint(ret, ==, 0);
176
void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
108
177
{
109
/* Early success: Read-only image */
178
+ c->parent_quiesce_counter++;
110
- bs->read_only = true;
179
if (c->role->drained_begin) {
111
bs->open_flags &= ~BDRV_O_RDWR;
180
c->role->drained_begin(c);
112
181
}
113
ret = blk_flush(blk);
114
g_assert_cmpint(ret, ==, 0);
115
116
- bs->read_only = false;
117
bs->open_flags |= BDRV_O_RDWR;
118
}
119
182
--
120
--
183
2.20.1
121
2.30.2
184
122
185
123
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
We should never poll anywhere in bdrv_do_drained_end() (including its
3
Instead of keeping additional boolean field, let's store the
4
recursive callees like bdrv_drain_invoke()), because it does not cope
4
information in BDRV_O_RDWR bit of BlockBackendRootState::open_flags.
5
well with graph changes. In fact, it has been written based on the
6
postulation that no graph changes will happen in it.
7
5
8
Instead, the callers that want to poll must poll, i.e. all currently
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
globally available wrappers: bdrv_drained_end(),
7
Message-Id: <20210527154056.70294-4-vsementsov@virtuozzo.com>
10
bdrv_subtree_drained_end(), bdrv_unapply_subtree_drain(), and
11
bdrv_drain_all_end(). Graph changes there do not matter.
12
13
They can poll simply by passing a pointer to a drained_end_counter and
14
wait until it reaches 0.
15
16
This patch also adds a non-polling global wrapper for
17
bdrv_do_drained_end() that takes a drained_end_counter pointer. We need
18
such a variant because now no function called anywhere from
19
bdrv_do_drained_end() must poll. This includes
20
BdrvChildRole.drained_end(), which already must not poll according to
21
its interface documentation, but bdrv_child_cb_drained_end() just
22
violates that by invoking bdrv_drained_end() (which does poll).
23
Therefore, BdrvChildRole.drained_end() must take a *drained_end_counter
24
parameter, which bdrv_child_cb_drained_end() can pass on to the new
25
bdrv_drained_end_no_poll() function.
26
27
Note that we now have a pattern of all drained_end-related functions
28
either polling or receiving a *drained_end_counter to let the caller
29
poll based on that.
30
31
A problem with a single poll loop is that when the drained section in
32
bdrv_set_aio_context_ignore() ends, some nodes in the subgraph may be in
33
the old contexts, while others are in the new context already. To let
34
the collective poll in bdrv_drained_end() work correctly, we must not
35
hold a lock to the old context, so that the old context can make
36
progress in case it is different from the current context.
37
38
(In the process, remove the comment saying that the current context is
39
always the old context, because it is wrong.)
40
41
In all other places, all nodes in a subtree must be in the same context,
42
so we can just poll that. The exception of course is
43
bdrv_drain_all_end(), but that always runs in the main context, so we
44
can just poll NULL (like bdrv_drain_all_begin() does).
45
46
Signed-off-by: Max Reitz <mreitz@redhat.com>
47
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
48
---
9
---
49
include/block/block.h | 25 ++++++++++++
10
include/block/block_int.h | 1 -
50
include/block/block_int.h | 6 ++-
11
block/block-backend.c | 10 ++--------
51
block.c | 37 ++++++++++++++----
12
blockdev.c | 3 +--
52
block/block-backend.c | 6 +--
13
3 files changed, 3 insertions(+), 11 deletions(-)
53
block/io.c | 80 ++++++++++++++++++++++++++++-----------
54
blockjob.c | 2 +-
55
6 files changed, 120 insertions(+), 36 deletions(-)
56
14
57
diff --git a/include/block/block.h b/include/block/block.h
58
index XXXXXXX..XXXXXXX 100644
59
--- a/include/block/block.h
60
+++ b/include/block/block.h
61
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
62
* bdrv_parent_drained_end_single:
63
*
64
* End a quiesced section for the parent of @c.
65
+ *
66
+ * This polls @bs's AioContext until all scheduled sub-drained_ends
67
+ * have settled, which may result in graph changes.
68
*/
69
void bdrv_parent_drained_end_single(BdrvChild *c);
70
71
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs);
72
* bdrv_drained_end:
73
*
74
* End a quiescent section started by bdrv_drained_begin().
75
+ *
76
+ * This polls @bs's AioContext until all scheduled sub-drained_ends
77
+ * have settled. On one hand, that may result in graph changes. On
78
+ * the other, this requires that all involved nodes (@bs and all of
79
+ * its parents) are in the same AioContext, and that the caller has
80
+ * acquired it.
81
+ * If there are any nodes that are in different contexts from @bs,
82
+ * these contexts must not be acquired.
83
*/
84
void bdrv_drained_end(BlockDriverState *bs);
85
86
+/**
87
+ * bdrv_drained_end_no_poll:
88
+ *
89
+ * Same as bdrv_drained_end(), but do not poll for the subgraph to
90
+ * actually become unquiesced. Therefore, no graph changes will occur
91
+ * with this function.
92
+ *
93
+ * *drained_end_counter is incremented for every background operation
94
+ * that is scheduled, and will be decremented for every operation once
95
+ * it settles. The caller must poll until it reaches 0. The counter
96
+ * should be accessed using atomic operations only.
97
+ */
98
+void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter);
99
+
100
/**
101
* End a quiescent section started by bdrv_subtree_drained_begin().
102
*/
103
diff --git a/include/block/block_int.h b/include/block/block_int.h
15
diff --git a/include/block/block_int.h b/include/block/block_int.h
104
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/block_int.h
17
--- a/include/block/block_int.h
106
+++ b/include/block/block_int.h
18
+++ b/include/block/block_int.h
107
@@ -XXX,XX +XXX,XX @@ struct BdrvChildRole {
19
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
108
* These functions must not change the graph (and therefore also must not
20
109
* call aio_poll(), which could change the graph indirectly).
21
struct BlockBackendRootState {
110
*
22
int open_flags;
111
+ * If drained_end() schedules background operations, it must atomically
23
- bool read_only;
112
+ * increment *drained_end_counter for each such operation and atomically
24
BlockdevDetectZeroesOptions detect_zeroes;
113
+ * decrement it once the operation has settled.
25
};
114
+ *
26
115
* Note that this can be nested. If drained_begin() was called twice, new
116
* I/O is allowed only after drained_end() was called twice, too.
117
*/
118
void (*drained_begin)(BdrvChild *child);
119
- void (*drained_end)(BdrvChild *child);
120
+ void (*drained_end)(BdrvChild *child, int *drained_end_counter);
121
122
/*
123
* Returns whether the parent has pending requests for the child. This
124
diff --git a/block.c b/block.c
125
index XXXXXXX..XXXXXXX 100644
126
--- a/block.c
127
+++ b/block.c
128
@@ -XXX,XX +XXX,XX @@ static bool bdrv_child_cb_drained_poll(BdrvChild *child)
129
return bdrv_drain_poll(bs, false, NULL, false);
130
}
131
132
-static void bdrv_child_cb_drained_end(BdrvChild *child)
133
+static void bdrv_child_cb_drained_end(BdrvChild *child,
134
+ int *drained_end_counter)
135
{
136
BlockDriverState *bs = child->opaque;
137
- bdrv_drained_end(bs);
138
+ bdrv_drained_end_no_poll(bs, drained_end_counter);
139
}
140
141
static void bdrv_child_cb_attach(BdrvChild *child)
142
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
143
void bdrv_set_aio_context_ignore(BlockDriverState *bs,
144
AioContext *new_context, GSList **ignore)
145
{
146
+ AioContext *old_context = bdrv_get_aio_context(bs);
147
+ AioContext *current_context = qemu_get_current_aio_context();
148
BdrvChild *child;
149
150
- if (bdrv_get_aio_context(bs) == new_context) {
151
+ if (old_context == new_context) {
152
return;
153
}
154
155
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs,
156
157
bdrv_detach_aio_context(bs);
158
159
- /* This function executes in the old AioContext so acquire the new one in
160
- * case it runs in a different thread.
161
- */
162
- aio_context_acquire(new_context);
163
+ /* Acquire the new context, if necessary */
164
+ if (current_context != new_context) {
165
+ aio_context_acquire(new_context);
166
+ }
167
+
168
bdrv_attach_aio_context(bs, new_context);
169
+
170
+ /*
171
+ * If this function was recursively called from
172
+ * bdrv_set_aio_context_ignore(), there may be nodes in the
173
+ * subtree that have not yet been moved to the new AioContext.
174
+ * Release the old one so bdrv_drained_end() can poll them.
175
+ */
176
+ if (current_context != old_context) {
177
+ aio_context_release(old_context);
178
+ }
179
+
180
bdrv_drained_end(bs);
181
- aio_context_release(new_context);
182
+
183
+ if (current_context != old_context) {
184
+ aio_context_acquire(old_context);
185
+ }
186
+ if (current_context != new_context) {
187
+ aio_context_release(new_context);
188
+ }
189
}
190
191
static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx,
192
diff --git a/block/block-backend.c b/block/block-backend.c
27
diff --git a/block/block-backend.c b/block/block-backend.c
193
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
194
--- a/block/block-backend.c
29
--- a/block/block-backend.c
195
+++ b/block/block-backend.c
30
+++ b/block/block-backend.c
196
@@ -XXX,XX +XXX,XX @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
31
@@ -XXX,XX +XXX,XX @@ bool blk_supports_write_perm(BlockBackend *blk)
197
}
32
if (bs) {
198
static void blk_root_drained_begin(BdrvChild *child);
33
return !bdrv_is_read_only(bs);
199
static bool blk_root_drained_poll(BdrvChild *child);
34
} else {
200
-static void blk_root_drained_end(BdrvChild *child);
35
- return !blk->root_state.read_only;
201
+static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
36
+ return blk->root_state.open_flags & BDRV_O_RDWR;
202
203
static void blk_root_change_media(BdrvChild *child, bool load);
204
static void blk_root_resize(BdrvChild *child);
205
@@ -XXX,XX +XXX,XX @@ int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf,
206
207
blk_root_drained_begin(blk->root);
208
ret = blk_pread(blk, offset, buf, count);
209
- blk_root_drained_end(blk->root);
210
+ blk_root_drained_end(blk->root, NULL);
211
return ret;
212
}
213
214
@@ -XXX,XX +XXX,XX @@ static bool blk_root_drained_poll(BdrvChild *child)
215
return !!blk->in_flight;
216
}
217
218
-static void blk_root_drained_end(BdrvChild *child)
219
+static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
220
{
221
BlockBackend *blk = child->opaque;
222
assert(blk->quiesce_counter);
223
diff --git a/block/io.c b/block/io.c
224
index XXXXXXX..XXXXXXX 100644
225
--- a/block/io.c
226
+++ b/block/io.c
227
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
228
}
37
}
229
}
38
}
230
39
231
-void bdrv_parent_drained_end_single(BdrvChild *c)
40
@@ -XXX,XX +XXX,XX @@ void blk_update_root_state(BlockBackend *blk)
232
+static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c,
41
assert(blk->root);
233
+ int *drained_end_counter)
42
43
blk->root_state.open_flags = blk->root->bs->open_flags;
44
- blk->root_state.read_only = bdrv_is_read_only(blk->root->bs);
45
blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes;
46
}
47
48
@@ -XXX,XX +XXX,XX @@ bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk)
49
*/
50
int blk_get_open_flags_from_root_state(BlockBackend *blk)
234
{
51
{
235
assert(c->parent_quiesce_counter > 0);
52
- int bs_flags;
236
c->parent_quiesce_counter--;
53
-
237
if (c->role->drained_end) {
54
- bs_flags = blk->root_state.read_only ? 0 : BDRV_O_RDWR;
238
- c->role->drained_end(c);
55
- bs_flags |= blk->root_state.open_flags & ~BDRV_O_RDWR;
239
+ c->role->drained_end(c, drained_end_counter);
56
-
240
}
57
- return bs_flags;
58
+ return blk->root_state.open_flags;
241
}
59
}
242
60
243
+void bdrv_parent_drained_end_single(BdrvChild *c)
61
BlockBackendRootState *blk_get_root_state(BlockBackend *blk)
244
+{
62
diff --git a/blockdev.c b/blockdev.c
245
+ int drained_end_counter = 0;
246
+ bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter);
247
+ BDRV_POLL_WHILE(c->bs, atomic_read(&drained_end_counter) > 0);
248
+}
249
+
250
static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
251
- bool ignore_bds_parents)
252
+ bool ignore_bds_parents,
253
+ int *drained_end_counter)
254
{
255
BdrvChild *c, *next;
256
257
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
258
if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) {
259
continue;
260
}
261
- bdrv_parent_drained_end_single(c);
262
+ bdrv_parent_drained_end_single_no_poll(c, drained_end_counter);
263
}
264
}
265
266
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
267
atomic_mb_set(&data->done, true);
268
bdrv_dec_in_flight(bs);
269
270
- if (data->drained_end_counter) {
271
+ if (!data->begin) {
272
atomic_dec(data->drained_end_counter);
273
}
274
275
- if (data->begin || data->drained_end_counter) {
276
- g_free(data);
277
- }
278
+ g_free(data);
279
}
280
281
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
282
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
283
.drained_end_counter = drained_end_counter,
284
};
285
286
- if (!begin && drained_end_counter) {
287
+ if (!begin) {
288
atomic_inc(drained_end_counter);
289
}
290
291
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
292
bdrv_inc_in_flight(bs);
293
data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data);
294
aio_co_schedule(bdrv_get_aio_context(bs), data->co);
295
-
296
- /*
297
- * TODO: Drop this and make callers pass @drained_end_counter and poll
298
- * themselves
299
- */
300
- if (!begin && !drained_end_counter) {
301
- BDRV_POLL_WHILE(bs, !data->done);
302
- g_free(data);
303
- }
304
}
305
306
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
307
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
308
}
309
bdrv_dec_in_flight(bs);
310
if (data->begin) {
311
+ assert(!data->drained_end_counter);
312
bdrv_do_drained_begin(bs, data->recursive, data->parent,
313
data->ignore_bds_parents, data->poll);
314
} else {
315
+ assert(!data->poll);
316
bdrv_do_drained_end(bs, data->recursive, data->parent,
317
data->ignore_bds_parents,
318
data->drained_end_counter);
319
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
320
bdrv_do_drained_begin(bs, true, NULL, false, true);
321
}
322
323
+/**
324
+ * This function does not poll, nor must any of its recursively called
325
+ * functions. The *drained_end_counter pointee will be incremented
326
+ * once for every background operation scheduled, and decremented once
327
+ * the operation settles. Therefore, the pointer must remain valid
328
+ * until the pointee reaches 0. That implies that whoever sets up the
329
+ * pointee has to poll until it is 0.
330
+ *
331
+ * We use atomic operations to access *drained_end_counter, because
332
+ * (1) when called from bdrv_set_aio_context_ignore(), the subgraph of
333
+ * @bs may contain nodes in different AioContexts,
334
+ * (2) bdrv_drain_all_end() uses the same counter for all nodes,
335
+ * regardless of which AioContext they are in.
336
+ */
337
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
338
BdrvChild *parent, bool ignore_bds_parents,
339
int *drained_end_counter)
340
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
341
BdrvChild *child, *next;
342
int old_quiesce_counter;
343
344
+ assert(drained_end_counter != NULL);
345
+
346
if (qemu_in_coroutine()) {
347
bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
348
false, drained_end_counter);
349
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
350
351
/* Re-enable things in child-to-parent order */
352
bdrv_drain_invoke(bs, false, drained_end_counter);
353
- bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
354
+ bdrv_parent_drained_end(bs, parent, ignore_bds_parents,
355
+ drained_end_counter);
356
357
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
358
if (old_quiesce_counter == 1) {
359
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
360
361
void bdrv_drained_end(BlockDriverState *bs)
362
{
363
- bdrv_do_drained_end(bs, false, NULL, false, NULL);
364
+ int drained_end_counter = 0;
365
+ bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter);
366
+ BDRV_POLL_WHILE(bs, atomic_read(&drained_end_counter) > 0);
367
+}
368
+
369
+void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter)
370
+{
371
+ bdrv_do_drained_end(bs, false, NULL, false, drained_end_counter);
372
}
373
374
void bdrv_subtree_drained_end(BlockDriverState *bs)
375
{
376
- bdrv_do_drained_end(bs, true, NULL, false, NULL);
377
+ int drained_end_counter = 0;
378
+ bdrv_do_drained_end(bs, true, NULL, false, &drained_end_counter);
379
+ BDRV_POLL_WHILE(bs, atomic_read(&drained_end_counter) > 0);
380
}
381
382
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
383
@@ -XXX,XX +XXX,XX @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
384
385
void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
386
{
387
+ int drained_end_counter = 0;
388
int i;
389
390
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
391
- bdrv_do_drained_end(child->bs, true, child, false, NULL);
392
+ bdrv_do_drained_end(child->bs, true, child, false,
393
+ &drained_end_counter);
394
}
395
+
396
+ BDRV_POLL_WHILE(child->bs, atomic_read(&drained_end_counter) > 0);
397
}
398
399
/*
400
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
401
void bdrv_drain_all_end(void)
402
{
403
BlockDriverState *bs = NULL;
404
+ int drained_end_counter = 0;
405
406
while ((bs = bdrv_next_all_states(bs))) {
407
AioContext *aio_context = bdrv_get_aio_context(bs);
408
409
aio_context_acquire(aio_context);
410
- bdrv_do_drained_end(bs, false, NULL, true, NULL);
411
+ bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter);
412
aio_context_release(aio_context);
413
}
414
415
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
416
+ AIO_WAIT_WHILE(NULL, atomic_read(&drained_end_counter) > 0);
417
+
418
assert(bdrv_drain_all_count > 0);
419
bdrv_drain_all_count--;
420
}
421
diff --git a/blockjob.c b/blockjob.c
422
index XXXXXXX..XXXXXXX 100644
63
index XXXXXXX..XXXXXXX 100644
423
--- a/blockjob.c
64
--- a/blockdev.c
424
+++ b/blockjob.c
65
+++ b/blockdev.c
425
@@ -XXX,XX +XXX,XX @@ static bool child_job_drained_poll(BdrvChild *c)
66
@@ -XXX,XX +XXX,XX @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
426
}
67
427
}
68
blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
428
69
blk_rs = blk_get_root_state(blk);
429
-static void child_job_drained_end(BdrvChild *c)
70
- blk_rs->open_flags = bdrv_flags;
430
+static void child_job_drained_end(BdrvChild *c, int *drained_end_counter)
71
- blk_rs->read_only = read_only;
431
{
72
+ blk_rs->open_flags = bdrv_flags | (read_only ? 0 : BDRV_O_RDWR);
432
BlockJob *job = c->opaque;
73
blk_rs->detect_zeroes = detect_zeroes;
433
job_resume(&job->job);
74
75
qobject_unref(bs_opts);
434
--
76
--
435
2.20.1
77
2.30.2
436
78
437
79
diff view generated by jsdifflib
New patch
1
From: Thomas Huth <thuth@redhat.com>
1
2
3
A customer reported that running
4
5
qemu-img convert -t none -O qcow2 -f qcow2 input.qcow2 output.qcow2
6
7
fails for them with the following error message when the images are
8
stored on a GPFS file system :
9
10
qemu-img: error while writing sector 0: Invalid argument
11
12
After analyzing the strace output, it seems like the problem is in
13
handle_aiocb_write_zeroes(): The call to fallocate(FALLOC_FL_PUNCH_HOLE)
14
returns EINVAL, which can apparently happen if the file system has
15
a different idea of the granularity of the operation. It's arguably
16
a bug in GPFS, since the PUNCH_HOLE mode should not result in EINVAL
17
according to the man-page of fallocate(), but the file system is out
18
there in production and so we have to deal with it. In commit 294682cc3a
19
("block: workaround for unaligned byte range in fallocate()") we also
20
already applied the a work-around for the same problem to the earlier
21
fallocate(FALLOC_FL_ZERO_RANGE) call, so do it now similar with the
22
PUNCH_HOLE call. But instead of silently catching and returning
23
-ENOTSUP (which causes the caller to fall back to writing zeroes),
24
let's rather inform the user once about the buggy file system and
25
try the other fallback instead.
26
27
Signed-off-by: Thomas Huth <thuth@redhat.com>
28
Message-Id: <20210527172020.847617-2-thuth@redhat.com>
29
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
30
---
31
block/file-posix.c | 11 +++++++++++
32
1 file changed, 11 insertions(+)
33
34
diff --git a/block/file-posix.c b/block/file-posix.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/block/file-posix.c
37
+++ b/block/file-posix.c
38
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_write_zeroes(void *opaque)
39
return ret;
40
}
41
s->has_fallocate = false;
42
+ } else if (ret == -EINVAL) {
43
+ /*
44
+ * Some file systems like older versions of GPFS do not like un-
45
+ * aligned byte ranges, and return EINVAL in such a case, though
46
+ * they should not do it according to the man-page of fallocate().
47
+ * Warn about the bad filesystem and try the final fallback instead.
48
+ */
49
+ warn_report_once("Your file system is misbehaving: "
50
+ "fallocate(FALLOC_FL_PUNCH_HOLE) returned EINVAL. "
51
+ "Please report this bug to your file sytem "
52
+ "vendor.");
53
} else if (ret != -ENOTSUP) {
54
return ret;
55
} else {
56
--
57
2.30.2
58
59
diff view generated by jsdifflib
New patch
1
From: Thomas Huth <thuth@redhat.com>
1
2
3
If fallocate(... FALLOC_FL_ZERO_RANGE ...) returns EINVAL, it's likely
4
an indication that the file system is buggy and does not implement
5
unaligned accesses right. We still might be lucky with the other
6
fallback fallocate() calls later in this function, though, so we should
7
not return immediately and try the others first.
8
Since FALLOC_FL_ZERO_RANGE could also return EINVAL if the file descriptor
9
is not a regular file, we ignore this filesystem bug silently, without
10
printing an error message for the user.
11
12
Signed-off-by: Thomas Huth <thuth@redhat.com>
13
Message-Id: <20210527172020.847617-3-thuth@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
block/file-posix.c | 18 +++++++++---------
17
1 file changed, 9 insertions(+), 9 deletions(-)
18
19
diff --git a/block/file-posix.c b/block/file-posix.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/file-posix.c
22
+++ b/block/file-posix.c
23
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_write_zeroes(void *opaque)
24
if (s->has_write_zeroes) {
25
int ret = do_fallocate(s->fd, FALLOC_FL_ZERO_RANGE,
26
aiocb->aio_offset, aiocb->aio_nbytes);
27
- if (ret == -EINVAL) {
28
- /*
29
- * Allow falling back to pwrite for file systems that
30
- * do not support fallocate() for an unaligned byte range.
31
- */
32
- return -ENOTSUP;
33
- }
34
- if (ret == 0 || ret != -ENOTSUP) {
35
+ if (ret == -ENOTSUP) {
36
+ s->has_write_zeroes = false;
37
+ } else if (ret == 0 || ret != -EINVAL) {
38
return ret;
39
}
40
- s->has_write_zeroes = false;
41
+ /*
42
+ * Note: Some file systems do not like unaligned byte ranges, and
43
+ * return EINVAL in such a case, though they should not do it according
44
+ * to the man-page of fallocate(). Thus we simply ignore this return
45
+ * value and try the other fallbacks instead.
46
+ */
47
}
48
#endif
49
50
--
51
2.30.2
52
53
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
The logic around **child is not obvious: this reference is used not
4
only to return resulting child, but also to rollback NULL value on
5
transaction abort.
6
7
So, let's add documentation and some assertions.
8
9
While being here, drop extra declaration of bdrv_attach_child_noperm().
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20210601075218.79249-2-vsementsov@virtuozzo.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
block.c | 24 +++++++++++++++---------
16
1 file changed, 15 insertions(+), 9 deletions(-)
17
18
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block.c
21
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
23
24
static void bdrv_replace_child_noperm(BdrvChild *child,
25
BlockDriverState *new_bs);
26
-static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
27
- BlockDriverState *child_bs,
28
- const char *child_name,
29
- const BdrvChildClass *child_class,
30
- BdrvChildRole child_role,
31
- BdrvChild **child,
32
- Transaction *tran,
33
- Error **errp);
34
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
35
Transaction *tran);
36
37
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_attach_child_common_drv = {
38
39
/*
40
* Common part of attaching bdrv child to bs or to blk or to job
41
+ *
42
+ * Resulting new child is returned through @child.
43
+ * At start *@child must be NULL.
44
+ * @child is saved to a new entry of @tran, so that *@child could be reverted to
45
+ * NULL on abort(). So referenced variable must live at least until transaction
46
+ * end.
47
*/
48
static int bdrv_attach_child_common(BlockDriverState *child_bs,
49
const char *child_name,
50
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
51
return 0;
52
}
53
54
+/*
55
+ * Variable referenced by @child must live at least until transaction end.
56
+ * (see bdrv_attach_child_common() doc for details)
57
+ */
58
static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
59
BlockDriverState *child_bs,
60
const char *child_name,
61
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
62
child_role, perm, shared_perm, opaque,
63
&child, tran, errp);
64
if (ret < 0) {
65
- assert(child == NULL);
66
goto out;
67
}
68
69
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
70
71
out:
72
tran_finalize(tran, ret);
73
+ /* child is unset on failure by bdrv_attach_child_common_abort() */
74
+ assert((ret < 0) == !child);
75
+
76
bdrv_unref(child_bs);
77
return child;
78
}
79
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
80
81
out:
82
tran_finalize(tran, ret);
83
+ /* child is unset on failure by bdrv_attach_child_common_abort() */
84
+ assert((ret < 0) == !child);
85
86
bdrv_unref(child_bs);
87
88
--
89
2.30.2
90
91
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
We have different types of parents: block nodes, block backends and
4
jobs. So, it makes sense to specify type together with name.
5
6
While being here also use g_autofree.
7
8
iotest 307 output is updated.
9
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Alberto Garcia <berto@igalia.com>
12
Message-Id: <20210601075218.79249-3-vsementsov@virtuozzo.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
block/block-backend.c | 9 ++++-----
16
tests/qemu-iotests/307.out | 2 +-
17
2 files changed, 5 insertions(+), 6 deletions(-)
18
19
diff --git a/block/block-backend.c b/block/block-backend.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/block-backend.c
22
+++ b/block/block-backend.c
23
@@ -XXX,XX +XXX,XX @@ static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
24
static char *blk_root_get_parent_desc(BdrvChild *child)
25
{
26
BlockBackend *blk = child->opaque;
27
- char *dev_id;
28
+ g_autofree char *dev_id = NULL;
29
30
if (blk->name) {
31
- return g_strdup(blk->name);
32
+ return g_strdup_printf("block device '%s'", blk->name);
33
}
34
35
dev_id = blk_get_attached_dev_id(blk);
36
if (*dev_id) {
37
- return dev_id;
38
+ return g_strdup_printf("block device '%s'", dev_id);
39
} else {
40
/* TODO Callback into the BB owner for something more detailed */
41
- g_free(dev_id);
42
- return g_strdup("a block device");
43
+ return g_strdup("an unnamed block device");
44
}
45
}
46
47
diff --git a/tests/qemu-iotests/307.out b/tests/qemu-iotests/307.out
48
index XXXXXXX..XXXXXXX 100644
49
--- a/tests/qemu-iotests/307.out
50
+++ b/tests/qemu-iotests/307.out
51
@@ -XXX,XX +XXX,XX @@ exports available: 1
52
53
=== Add a writable export ===
54
{"execute": "block-export-add", "arguments": {"description": "This is the writable second export", "id": "export1", "name": "export1", "node-name": "fmt", "type": "nbd", "writable": true, "writethrough": true}}
55
-{"error": {"class": "GenericError", "desc": "Conflicts with use by sda as 'root', which does not allow 'write' on fmt"}}
56
+{"error": {"class": "GenericError", "desc": "Conflicts with use by block device 'sda' as 'root', which does not allow 'write' on fmt"}}
57
{"execute": "device_del", "arguments": {"id": "sda"}}
58
{"return": {}}
59
{"data": {"device": "sda", "path": "/machine/peripheral/sda"}, "event": "DEVICE_DELETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
60
--
61
2.30.2
62
63
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
When qemu quits, all throttling should be ignored. That means, if there
3
We have different types of parents: block nodes, block backends and
4
is a mirror job running from a throttled node, it should be cancelled
4
jobs. So, it makes sense to specify type together with name.
5
immediately and qemu close without blocking.
6
5
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Next, this handler us used to compose an error message about permission
7
conflict. And permission conflict occurs in a specific place of block
8
graph. We shouldn't report name of parent device (as it refers another
9
place in block graph), but exactly and only the name of the node. So,
10
use bdrv_get_node_name() directly.
11
12
iotest 283 output is updated.
13
14
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
15
Reviewed-by: Alberto Garcia <berto@igalia.com>
16
Message-Id: <20210601075218.79249-4-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
18
---
10
tests/qemu-iotests/218 | 55 ++++++++++++++++++++++++++++++++++++--
19
block.c | 2 +-
11
tests/qemu-iotests/218.out | 4 +++
20
tests/qemu-iotests/283.out | 2 +-
12
2 files changed, 57 insertions(+), 2 deletions(-)
21
2 files changed, 2 insertions(+), 2 deletions(-)
13
22
14
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
23
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100755
24
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/qemu-iotests/218
25
--- a/block.c
17
+++ b/tests/qemu-iotests/218
26
+++ b/block.c
27
@@ -XXX,XX +XXX,XX @@ int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough)
28
static char *bdrv_child_get_parent_desc(BdrvChild *c)
29
{
30
BlockDriverState *parent = c->opaque;
31
- return g_strdup(bdrv_get_device_or_node_name(parent));
32
+ return g_strdup_printf("node '%s'", bdrv_get_node_name(parent));
33
}
34
35
static void bdrv_child_cb_drained_begin(BdrvChild *child)
36
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
37
index XXXXXXX..XXXXXXX 100644
38
--- a/tests/qemu-iotests/283.out
39
+++ b/tests/qemu-iotests/283.out
18
@@ -XXX,XX +XXX,XX @@
40
@@ -XXX,XX +XXX,XX @@
19
# Creator/Owner: Max Reitz <mreitz@redhat.com>
41
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
20
21
import iotests
22
-from iotests import log
23
+from iotests import log, qemu_img, qemu_io_silent
24
25
-iotests.verify_platform(['linux'])
26
+iotests.verify_image_format(supported_fmts=['qcow2', 'raw'])
27
28
29
# Launches the VM, adds two null-co nodes (source and target), and
30
@@ -XXX,XX +XXX,XX @@ with iotests.VM() as vm:
31
32
log(vm.event_wait('BLOCK_JOB_CANCELLED'),
33
filters=[iotests.filter_qmp_event])
34
+
35
+log('')
36
+log('=== Cancel mirror job from throttled node by quitting ===')
37
+log('')
38
+
39
+with iotests.VM() as vm, \
40
+ iotests.FilePath('src.img') as src_img_path:
41
+
42
+ assert qemu_img('create', '-f', iotests.imgfmt, src_img_path, '64M') == 0
43
+ assert qemu_io_silent('-f', iotests.imgfmt, src_img_path,
44
+ '-c', 'write -P 42 0M 64M') == 0
45
+
46
+ vm.launch()
47
+
48
+ ret = vm.qmp('object-add', qom_type='throttle-group', id='tg',
49
+ props={'x-bps-read': 4096})
50
+ assert ret['return'] == {}
51
+
52
+ ret = vm.qmp('blockdev-add',
53
+ node_name='source',
54
+ driver=iotests.imgfmt,
55
+ file={
56
+ 'driver': 'file',
57
+ 'filename': src_img_path
58
+ })
59
+ assert ret['return'] == {}
60
+
61
+ ret = vm.qmp('blockdev-add',
62
+ node_name='throttled-source',
63
+ driver='throttle',
64
+ throttle_group='tg',
65
+ file='source')
66
+ assert ret['return'] == {}
67
+
68
+ ret = vm.qmp('blockdev-add',
69
+ node_name='target',
70
+ driver='null-co',
71
+ size=(64 * 1048576))
72
+ assert ret['return'] == {}
73
+
74
+ ret = vm.qmp('blockdev-mirror',
75
+ job_id='mirror',
76
+ device='throttled-source',
77
+ target='target',
78
+ sync='full')
79
+ assert ret['return'] == {}
80
+
81
+ log(vm.qmp('quit'))
82
+
83
+ with iotests.Timeout(5, 'Timeout waiting for VM to quit'):
84
+ vm.shutdown(has_quit=True)
85
diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out
86
index XXXXXXX..XXXXXXX 100644
87
--- a/tests/qemu-iotests/218.out
88
+++ b/tests/qemu-iotests/218.out
89
@@ -XXX,XX +XXX,XX @@ Cancelling job
90
Cancelling job
91
{"return": {}}
42
{"return": {}}
92
{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
43
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
93
+
44
-{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
94
+=== Cancel mirror job from throttled node by quitting ===
45
+{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by node 'source' as 'image', which does not allow 'write' on base"}}
95
+
46
96
+{"return": {}}
47
=== backup-top should be gone after job-finalize ===
48
97
--
49
--
98
2.20.1
50
2.30.2
99
51
100
52
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
If the main loop cancels all block jobs while the block layer is not
3
Recently we've fixed a crash by adding .get_parent_aio_context handler
4
drained, this cancelling may not happen instantaneously. We can start a
4
to child_vvfat_qcow. Now we want it to support .get_parent_desc as
5
drained section before vm_shutdown(), which entails another
5
well. child_vvfat_qcow wants to implement own .inherit_options, it's
6
bdrv_drain_all(); this nested bdrv_drain_all() will thus be a no-op,
6
not bad. But omitting all other handlers is a bad idea. Let's inherit
7
basically.
7
the class from child_of_bds instead, similar to chain_child_class and
8
detach_by_driver_cb_class in test-bdrv-drain.c.
8
9
9
We do not have to end the drained section, because we actually do not
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
want any requests to happen from this point on.
11
Message-Id: <20210601075218.79249-5-vsementsov@virtuozzo.com>
11
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
13
---
15
vl.c | 11 +++++++++++
14
block/vvfat.c | 8 +++-----
16
1 file changed, 11 insertions(+)
15
1 file changed, 3 insertions(+), 5 deletions(-)
17
16
18
diff --git a/vl.c b/vl.c
17
diff --git a/block/vvfat.c b/block/vvfat.c
19
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
20
--- a/vl.c
19
--- a/block/vvfat.c
21
+++ b/vl.c
20
+++ b/block/vvfat.c
22
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
21
@@ -XXX,XX +XXX,XX @@ static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
23
*/
22
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
24
migration_shutdown();
23
}
25
24
26
+ /*
25
-static const BdrvChildClass child_vvfat_qcow = {
27
+ * We must cancel all block jobs while the block layer is drained,
26
- .parent_is_bds = true,
28
+ * or cancelling will be affected by throttling and thus may block
27
- .inherit_options = vvfat_qcow_options,
29
+ * for an extended period of time.
28
- .get_parent_aio_context = child_of_bds_get_parent_aio_context,
30
+ * vm_shutdown() will bdrv_drain_all(), so we may as well include
29
-};
31
+ * it in the drained section.
30
+static BdrvChildClass child_vvfat_qcow;
32
+ * We do not need to end this section, because we do not want any
31
33
+ * requests happening from here on anyway.
32
static int enable_write_target(BlockDriverState *bs, Error **errp)
34
+ */
33
{
35
+ bdrv_drain_all_begin();
34
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vvfat = {
36
+
35
37
/* No more vcpu or device emulation activity beyond this point */
36
static void bdrv_vvfat_init(void)
38
vm_shutdown();
37
{
38
+ child_vvfat_qcow = child_of_bds;
39
+ child_vvfat_qcow.inherit_options = vvfat_qcow_options;
40
bdrv_register(&bdrv_vvfat);
41
}
39
42
40
--
43
--
41
2.20.1
44
2.30.2
42
45
43
46
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Before the previous patches, the first case resulted in a failed
3
All child classes have this callback. So, drop unreachable code.
4
assertion (which is noted as qemu receiving a SIGABRT in the test
5
output), and the second usually triggered a segmentation fault.
6
4
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
Still add an assertion to bdrv_attach_child_common(), to early detect
6
bad classes.
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20210601075218.79249-6-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
11
---
10
tests/qemu-iotests/040 | 40 +++++++++++++++++++++++++++++++++++++-
12
block.c | 7 ++-----
11
tests/qemu-iotests/040.out | 4 ++--
13
1 file changed, 2 insertions(+), 5 deletions(-)
12
2 files changed, 41 insertions(+), 3 deletions(-)
13
14
14
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
15
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100755
16
--- a/tests/qemu-iotests/040
17
+++ b/tests/qemu-iotests/040
18
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(ImageCommitTestCase):
19
20
self.vm.add_device("scsi-hd,id=scsi0,drive=drive0")
21
self.vm.launch()
22
+ self.has_quit = False
23
24
def tearDown(self):
25
- self.vm.shutdown()
26
+ self.vm.shutdown(has_quit=self.has_quit)
27
os.remove(test_img)
28
os.remove(mid_img)
29
os.remove(backing_img)
30
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(ImageCommitTestCase):
31
self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
32
self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
33
34
+ def test_commit_with_filter_and_quit(self):
35
+ result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg')
36
+ self.assert_qmp(result, 'return', {})
37
+
38
+ # Add a filter outside of the backing chain
39
+ result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid')
40
+ self.assert_qmp(result, 'return', {})
41
+
42
+ result = self.vm.qmp('block-commit', device='drive0')
43
+ self.assert_qmp(result, 'return', {})
44
+
45
+ # Quit immediately, thus forcing a simultaneous cancel of the
46
+ # block job and a bdrv_drain_all()
47
+ result = self.vm.qmp('quit')
48
+ self.assert_qmp(result, 'return', {})
49
+
50
+ self.has_quit = True
51
+
52
+ # Same as above, but this time we add the filter after starting the job
53
+ def test_commit_plus_filter_and_quit(self):
54
+ result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg')
55
+ self.assert_qmp(result, 'return', {})
56
+
57
+ result = self.vm.qmp('block-commit', device='drive0')
58
+ self.assert_qmp(result, 'return', {})
59
+
60
+ # Add a filter outside of the backing chain
61
+ result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid')
62
+ self.assert_qmp(result, 'return', {})
63
+
64
+ # Quit immediately, thus forcing a simultaneous cancel of the
65
+ # block job and a bdrv_drain_all()
66
+ result = self.vm.qmp('quit')
67
+ self.assert_qmp(result, 'return', {})
68
+
69
+ self.has_quit = True
70
+
71
def test_device_not_found(self):
72
result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img)
73
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
74
diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out
75
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
76
--- a/tests/qemu-iotests/040.out
17
--- a/block.c
77
+++ b/tests/qemu-iotests/040.out
18
+++ b/block.c
78
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_writable(BlockDriverState *bs)
79
-...........................................
20
80
+...............................................
21
static char *bdrv_child_user_desc(BdrvChild *c)
81
----------------------------------------------------------------------
22
{
82
-Ran 43 tests
23
- if (c->klass->get_parent_desc) {
83
+Ran 47 tests
24
- return c->klass->get_parent_desc(c);
84
25
- }
85
OK
26
-
27
- return g_strdup("another user");
28
+ return c->klass->get_parent_desc(c);
29
}
30
31
static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
32
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
33
34
assert(child);
35
assert(*child == NULL);
36
+ assert(child_class->get_parent_desc);
37
38
new_child = g_new(BdrvChild, 1);
39
*new_child = (BdrvChild) {
86
--
40
--
87
2.20.1
41
2.30.2
88
42
89
43
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
If a test has issued a quit command already (which may be useful to do
3
Now permissions are updated as follows:
4
explicitly because the test wants to show its effects),
4
1. do graph modifications ignoring permissions
5
QEMUMachine.shutdown() should not do so again. Otherwise, the VM may
5
2. do permission update
6
well return an ECONNRESET which will lead QEMUMachine.shutdown() to
7
killing it, which then turns into a "qemu received signal 9" line.
8
6
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
(of course, we rollback [1] if [2] fails)
8
9
So, on stage [2] we can't say which users are "old" and which are
10
"new" and exist only since [1]. And current error message is a bit
11
outdated. Let's improve it, to make everything clean.
12
13
While being here, add also a comment and some good assertions.
14
15
iotests 283, 307, qsd-jobs outputs are updated.
16
17
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
Message-Id: <20210601075218.79249-7-vsementsov@virtuozzo.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
20
---
12
python/qemu/machine.py | 5 +++--
21
block.c | 29 ++++++++++++++++++++-------
13
tests/qemu-iotests/255 | 2 +-
22
tests/qemu-iotests/283.out | 2 +-
14
2 files changed, 4 insertions(+), 3 deletions(-)
23
tests/qemu-iotests/307.out | 2 +-
24
tests/qemu-iotests/tests/qsd-jobs.out | 2 +-
25
4 files changed, 25 insertions(+), 10 deletions(-)
15
26
16
diff --git a/python/qemu/machine.py b/python/qemu/machine.py
27
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
18
--- a/python/qemu/machine.py
29
--- a/block.c
19
+++ b/python/qemu/machine.py
30
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ class QEMUMachine(object):
31
@@ -XXX,XX +XXX,XX @@ static char *bdrv_child_user_desc(BdrvChild *c)
21
self._load_io_log()
32
return c->klass->get_parent_desc(c);
22
self._post_shutdown()
33
}
23
34
24
- def shutdown(self):
35
+/*
25
+ def shutdown(self, has_quit=False):
36
+ * Check that @a allows everything that @b needs. @a and @b must reference same
26
"""
37
+ * child node.
27
Terminate the VM and clean up
38
+ */
28
"""
39
static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
29
if self.is_running():
40
{
30
try:
41
- g_autofree char *user = NULL;
31
- self._qmp.cmd('quit')
42
- g_autofree char *perm_names = NULL;
32
+ if not has_quit:
43
+ const char *child_bs_name;
33
+ self._qmp.cmd('quit')
44
+ g_autofree char *a_user = NULL;
34
self._qmp.close()
45
+ g_autofree char *b_user = NULL;
35
except:
46
+ g_autofree char *perms = NULL;
36
self._popen.kill()
47
+
37
diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255
48
+ assert(a->bs);
38
index XXXXXXX..XXXXXXX 100755
49
+ assert(a->bs == b->bs);
39
--- a/tests/qemu-iotests/255
50
40
+++ b/tests/qemu-iotests/255
51
if ((b->perm & a->shared_perm) == b->perm) {
41
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('src.qcow2') as src_path, \
52
return true;
42
vm.qmp_log('block-job-cancel', device='job0')
53
}
43
vm.qmp_log('quit')
54
44
55
- perm_names = bdrv_perm_names(b->perm & ~a->shared_perm);
45
- vm.shutdown()
56
- user = bdrv_child_user_desc(a);
46
+ vm.shutdown(has_quit=True)
57
- error_setg(errp, "Conflicts with use by %s as '%s', which does not "
58
- "allow '%s' on %s",
59
- user, a->name, perm_names, bdrv_get_node_name(b->bs));
60
+ child_bs_name = bdrv_get_node_name(b->bs);
61
+ a_user = bdrv_child_user_desc(a);
62
+ b_user = bdrv_child_user_desc(b);
63
+ perms = bdrv_perm_names(b->perm & ~a->shared_perm);
64
+
65
+ error_setg(errp, "Permission conflict on node '%s': permissions '%s' are "
66
+ "both required by %s (uses node '%s' as '%s' child) and "
67
+ "unshared by %s (uses node '%s' as '%s' child).",
68
+ child_bs_name, perms,
69
+ b_user, child_bs_name, b->name,
70
+ a_user, child_bs_name, a->name);
71
72
return false;
73
}
74
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
75
index XXXXXXX..XXXXXXX 100644
76
--- a/tests/qemu-iotests/283.out
77
+++ b/tests/qemu-iotests/283.out
78
@@ -XXX,XX +XXX,XX @@
79
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
80
{"return": {}}
81
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
82
-{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by node 'source' as 'image', which does not allow 'write' on base"}}
83
+{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Permission conflict on node 'base': permissions 'write' are both required by node 'other' (uses node 'base' as 'image' child) and unshared by node 'source' (uses node 'base' as 'image' child)."}}
84
85
=== backup-top should be gone after job-finalize ===
86
87
diff --git a/tests/qemu-iotests/307.out b/tests/qemu-iotests/307.out
88
index XXXXXXX..XXXXXXX 100644
89
--- a/tests/qemu-iotests/307.out
90
+++ b/tests/qemu-iotests/307.out
91
@@ -XXX,XX +XXX,XX @@ exports available: 1
92
93
=== Add a writable export ===
94
{"execute": "block-export-add", "arguments": {"description": "This is the writable second export", "id": "export1", "name": "export1", "node-name": "fmt", "type": "nbd", "writable": true, "writethrough": true}}
95
-{"error": {"class": "GenericError", "desc": "Conflicts with use by block device 'sda' as 'root', which does not allow 'write' on fmt"}}
96
+{"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt': permissions 'write' are both required by an unnamed block device (uses node 'fmt' as 'root' child) and unshared by block device 'sda' (uses node 'fmt' as 'root' child)."}}
97
{"execute": "device_del", "arguments": {"id": "sda"}}
98
{"return": {}}
99
{"data": {"device": "sda", "path": "/machine/peripheral/sda"}, "event": "DEVICE_DELETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
100
diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out
101
index XXXXXXX..XXXXXXX 100644
102
--- a/tests/qemu-iotests/tests/qsd-jobs.out
103
+++ b/tests/qemu-iotests/tests/qsd-jobs.out
104
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
105
{"return": {}}
106
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
107
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
108
-{"error": {"class": "GenericError", "desc": "Conflicts with use by stream job 'job0' as 'intermediate node', which does not allow 'write' on fmt_base"}}
109
+{"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt_base': permissions 'write' are both required by an unnamed block device (uses node 'fmt_base' as 'root' child) and unshared by stream job 'job0' (uses node 'fmt_base' as 'intermediate node' child)."}}
110
{"return": {}}
111
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}}
112
*** done
47
--
113
--
48
2.20.1
114
2.30.2
49
115
50
116
diff view generated by jsdifflib
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
From: Sergio Lopez <slp@redhat.com>
2
2
3
The Valgrind tool reports about the uninitialised buffer 'buf'
3
Allow block backends to poll their devices/users to check if they have
4
instantiated on the stack of the function guess_disk_lchs().
4
been quiesced when entering a drained section.
5
Pass 'read-zeroes=on' to the null block driver to make it deterministic.
5
6
The output of the tests 051, 186 and 227 now includes the parameter
6
This will be used in the next patch to wait for the NBD server to be
7
'read-zeroes'. So, the benchmark output files are being changed too.
7
completely quiesced.
8
8
9
Suggested-by: Kevin Wolf <kwolf@redhat.com>
9
Suggested-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Signed-off-by: Sergio Lopez <slp@redhat.com>
13
Message-Id: <20210602060552.17433-2-slp@redhat.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
16
---
13
tests/qemu-iotests/051 | 10 +--
17
include/sysemu/block-backend.h | 4 ++++
14
tests/qemu-iotests/051.pc.out | 10 +--
18
block/block-backend.c | 7 ++++++-
15
tests/qemu-iotests/093 | 9 +-
19
2 files changed, 10 insertions(+), 1 deletion(-)
16
tests/qemu-iotests/136 | 1 +
17
tests/qemu-iotests/186 | 20 ++---
18
tests/qemu-iotests/186.out | 152 +++++++++++++++++-----------------
19
tests/qemu-iotests/227 | 4 +-
20
tests/qemu-iotests/227.out | 4 +-
21
tests/qemu-iotests/238 | 2 +-
22
tests/qemu-iotests/240 | 8 +-
23
10 files changed, 111 insertions(+), 109 deletions(-)
24
20
25
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
21
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
26
index XXXXXXX..XXXXXXX 100755
27
--- a/tests/qemu-iotests/051
28
+++ b/tests/qemu-iotests/051
29
@@ -XXX,XX +XXX,XX @@ echo
30
# Cannot use the test image because cache=none might not work on the host FS
31
# Use cdrom so that we won't get errors about missing media
32
33
-run_qemu -drive driver=null-co,cache=none
34
-run_qemu -drive driver=null-co,cache=directsync
35
-run_qemu -drive driver=null-co,cache=writeback
36
-run_qemu -drive driver=null-co,cache=writethrough
37
-run_qemu -drive driver=null-co,cache=unsafe
38
+run_qemu -drive driver=null-co,read-zeroes=on,cache=none
39
+run_qemu -drive driver=null-co,read-zeroes=on,cache=directsync
40
+run_qemu -drive driver=null-co,read-zeroes=on,cache=writeback
41
+run_qemu -drive driver=null-co,read-zeroes=on,cache=writethrough
42
+run_qemu -drive driver=null-co,read-zeroes=on,cache=unsafe
43
run_qemu -drive driver=null-co,cache=invalid_value
44
45
# Can't test direct=on here because O_DIRECT might not be supported on this FS
46
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
47
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
48
--- a/tests/qemu-iotests/051.pc.out
23
--- a/include/sysemu/block-backend.h
49
+++ b/tests/qemu-iotests/051.pc.out
24
+++ b/include/sysemu/block-backend.h
50
@@ -XXX,XX +XXX,XX @@ QEMU X.Y.Z monitor - type 'help' for more information
25
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDevOps {
51
26
* Runs when the backend's last drain request ends.
52
=== Cache modes ===
27
*/
53
28
void (*drained_end)(void *opaque);
54
-Testing: -drive driver=null-co,cache=none
29
+ /*
55
+Testing: -drive driver=null-co,read-zeroes=on,cache=none
30
+ * Is the device still busy?
56
QEMU X.Y.Z monitor - type 'help' for more information
31
+ */
57
(qemu) quit
32
+ bool (*drained_poll)(void *opaque);
58
33
} BlockDevOps;
59
-Testing: -drive driver=null-co,cache=directsync
34
60
+Testing: -drive driver=null-co,read-zeroes=on,cache=directsync
35
/* This struct is embedded in (the private) BlockBackend struct and contains
61
QEMU X.Y.Z monitor - type 'help' for more information
36
diff --git a/block/block-backend.c b/block/block-backend.c
62
(qemu) quit
63
64
-Testing: -drive driver=null-co,cache=writeback
65
+Testing: -drive driver=null-co,read-zeroes=on,cache=writeback
66
QEMU X.Y.Z monitor - type 'help' for more information
67
(qemu) quit
68
69
-Testing: -drive driver=null-co,cache=writethrough
70
+Testing: -drive driver=null-co,read-zeroes=on,cache=writethrough
71
QEMU X.Y.Z monitor - type 'help' for more information
72
(qemu) quit
73
74
-Testing: -drive driver=null-co,cache=unsafe
75
+Testing: -drive driver=null-co,read-zeroes=on,cache=unsafe
76
QEMU X.Y.Z monitor - type 'help' for more information
77
(qemu) quit
78
79
diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
80
index XXXXXXX..XXXXXXX 100755
81
--- a/tests/qemu-iotests/093
82
+++ b/tests/qemu-iotests/093
83
@@ -XXX,XX +XXX,XX @@ class ThrottleTestCase(iotests.QMPTestCase):
84
def setUp(self):
85
self.vm = iotests.VM()
86
for i in range(0, self.max_drives):
87
- self.vm.add_drive(self.test_img)
88
+ self.vm.add_drive(self.test_img, "file.read-zeroes=on")
89
self.vm.launch()
90
91
def tearDown(self):
92
@@ -XXX,XX +XXX,XX @@ class ThrottleTestGroupNames(iotests.QMPTestCase):
93
def setUp(self):
94
self.vm = iotests.VM()
95
for i in range(0, self.max_drives):
96
- self.vm.add_drive(self.test_img, "throttling.iops-total=100")
97
+ self.vm.add_drive(self.test_img,
98
+ "throttling.iops-total=100,file.read-zeroes=on")
99
self.vm.launch()
100
101
def tearDown(self):
102
@@ -XXX,XX +XXX,XX @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
103
def test_removable_media(self):
104
# Add a couple of dummy nodes named cd0 and cd1
105
result = self.vm.qmp("blockdev-add", driver="null-aio",
106
- node_name="cd0")
107
+ read_zeroes=True, node_name="cd0")
108
self.assert_qmp(result, 'return', {})
109
result = self.vm.qmp("blockdev-add", driver="null-aio",
110
- node_name="cd1")
111
+ read_zeroes=True, node_name="cd1")
112
self.assert_qmp(result, 'return', {})
113
114
# Attach a CD drive with cd0 inserted
115
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
116
index XXXXXXX..XXXXXXX 100755
117
--- a/tests/qemu-iotests/136
118
+++ b/tests/qemu-iotests/136
119
@@ -XXX,XX +XXX,XX @@ sector = "%d"
120
(self.account_invalid and "on" or "off"))
121
drive_args.append("stats-account-failed=%s" %
122
(self.account_failed and "on" or "off"))
123
+ drive_args.append("file.image.read-zeroes=on")
124
self.create_blkdebug_file()
125
self.vm = iotests.VM().add_drive('blkdebug:%s:%s' %
126
(blkdebug_file, self.test_img),
127
diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186
128
index XXXXXXX..XXXXXXX 100755
129
--- a/tests/qemu-iotests/186
130
+++ b/tests/qemu-iotests/186
131
@@ -XXX,XX +XXX,XX @@ echo "=== -blockdev/-device=<node-name> ==="
132
echo
133
134
for dev in $fixed $removable; do
135
- check_info_block -blockdev driver=null-co,node-name=null -device $dev,drive=null
136
- check_info_block -blockdev driver=null-co,node-name=null -device $dev,drive=null,id=qdev_id
137
+ check_info_block -blockdev driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=null
138
+ check_info_block -blockdev driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=null,id=qdev_id
139
done
140
141
echo
142
@@ -XXX,XX +XXX,XX @@ echo
143
# This creates two BlockBackends that will show up in 'info block'!
144
# A monitor-owned one from -drive, and anonymous one from -device
145
for dev in $fixed $removable; do
146
- check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=null,id=qdev_id
147
+ check_info_block -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=null,id=qdev_id
148
done
149
150
echo
151
@@ -XXX,XX +XXX,XX @@ echo "=== -drive if=none/-device=<bb-name> (with medium) ==="
152
echo
153
154
for dev in $fixed $removable; do
155
- check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=none0
156
- check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=none0,id=qdev_id
157
+ check_info_block -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=none0
158
+ check_info_block -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=none0,id=qdev_id
159
done
160
161
echo
162
@@ -XXX,XX +XXX,XX @@ echo "=== -drive if=... ==="
163
echo
164
165
check_info_block -drive if=floppy
166
-check_info_block -drive if=floppy,driver=null-co
167
+check_info_block -drive if=floppy,driver=null-co,read-zeroes=on
168
169
-check_info_block -drive if=ide,driver=null-co
170
+check_info_block -drive if=ide,driver=null-co,read-zeroes=on
171
check_info_block -drive if=ide,media=cdrom
172
-check_info_block -drive if=ide,driver=null-co,media=cdrom
173
+check_info_block -drive if=ide,driver=null-co,read-zeroes=on,media=cdrom
174
175
-check_info_block -drive if=virtio,driver=null-co
176
+check_info_block -drive if=virtio,driver=null-co,read-zeroes=on
177
178
-check_info_block -drive if=pflash,driver=null-co,size=1M
179
+check_info_block -drive if=pflash,driver=null-co,read-zeroes=on,size=1M
180
181
# success, all done
182
echo "*** done"
183
diff --git a/tests/qemu-iotests/186.out b/tests/qemu-iotests/186.out
184
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100644
185
--- a/tests/qemu-iotests/186.out
38
--- a/block/block-backend.c
186
+++ b/tests/qemu-iotests/186.out
39
+++ b/block/block-backend.c
187
@@ -XXX,XX +XXX,XX @@ qdev_id: [not inserted]
40
@@ -XXX,XX +XXX,XX @@ static void blk_root_drained_begin(BdrvChild *child)
188
41
static bool blk_root_drained_poll(BdrvChild *child)
189
=== -blockdev/-device=<node-name> ===
190
191
-Testing: -blockdev driver=null-co,node-name=null -device ide-hd,drive=null
192
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=null
193
QEMU X.Y.Z monitor - type 'help' for more information
194
(qemu) info block
195
-null: null-co:// (null-co)
196
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
197
Attached to: PATH
198
Cache mode: writeback
199
(qemu) quit
200
201
-Testing: -blockdev driver=null-co,node-name=null -device ide-hd,drive=null,id=qdev_id
202
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=null,id=qdev_id
203
QEMU X.Y.Z monitor - type 'help' for more information
204
(qemu) info block
205
-null: null-co:// (null-co)
206
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
207
Attached to: qdev_id
208
Cache mode: writeback
209
(qemu) quit
210
211
-Testing: -blockdev driver=null-co,node-name=null -device scsi-hd,drive=null
212
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=null
213
QEMU X.Y.Z monitor - type 'help' for more information
214
(qemu) info block
215
-null: null-co:// (null-co)
216
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
217
Attached to: PATH
218
Cache mode: writeback
219
(qemu) quit
220
221
-Testing: -blockdev driver=null-co,node-name=null -device scsi-hd,drive=null,id=qdev_id
222
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=null,id=qdev_id
223
QEMU X.Y.Z monitor - type 'help' for more information
224
(qemu) info block
225
-null: null-co:// (null-co)
226
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
227
Attached to: qdev_id
228
Cache mode: writeback
229
(qemu) quit
230
231
-Testing: -blockdev driver=null-co,node-name=null -device virtio-blk-pci,drive=null
232
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=null
233
QEMU X.Y.Z monitor - type 'help' for more information
234
(qemu) info block
235
-null: null-co:// (null-co)
236
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
237
Attached to: PATH
238
Cache mode: writeback
239
(qemu) quit
240
241
-Testing: -blockdev driver=null-co,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
242
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
243
QEMU X.Y.Z monitor - type 'help' for more information
244
(qemu) info block
245
-null: null-co:// (null-co)
246
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
247
Attached to: PATH
248
Cache mode: writeback
249
(qemu) quit
250
251
-Testing: -blockdev driver=null-co,node-name=null -device floppy,drive=null
252
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=null
253
QEMU X.Y.Z monitor - type 'help' for more information
254
(qemu) info block
255
-null: null-co:// (null-co)
256
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
257
Attached to: PATH
258
Removable device: not locked, tray closed
259
Cache mode: writeback
260
(qemu) quit
261
262
-Testing: -blockdev driver=null-co,node-name=null -device floppy,drive=null,id=qdev_id
263
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=null,id=qdev_id
264
QEMU X.Y.Z monitor - type 'help' for more information
265
(qemu) info block
266
-null: null-co:// (null-co)
267
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
268
Attached to: qdev_id
269
Removable device: not locked, tray closed
270
Cache mode: writeback
271
(qemu) quit
272
273
-Testing: -blockdev driver=null-co,node-name=null -device ide-cd,drive=null
274
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=null
275
QEMU X.Y.Z monitor - type 'help' for more information
276
(qemu) info block
277
-null: null-co:// (null-co)
278
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
279
Attached to: PATH
280
Removable device: not locked, tray closed
281
Cache mode: writeback
282
(qemu) quit
283
284
-Testing: -blockdev driver=null-co,node-name=null -device ide-cd,drive=null,id=qdev_id
285
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=null,id=qdev_id
286
QEMU X.Y.Z monitor - type 'help' for more information
287
(qemu) info block
288
-null: null-co:// (null-co)
289
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
290
Attached to: qdev_id
291
Removable device: not locked, tray closed
292
Cache mode: writeback
293
(qemu) quit
294
295
-Testing: -blockdev driver=null-co,node-name=null -device scsi-cd,drive=null
296
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=null
297
QEMU X.Y.Z monitor - type 'help' for more information
298
(qemu) info block
299
-null: null-co:// (null-co)
300
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
301
Attached to: PATH
302
Removable device: not locked, tray closed
303
Cache mode: writeback
304
(qemu) quit
305
306
-Testing: -blockdev driver=null-co,node-name=null -device scsi-cd,drive=null,id=qdev_id
307
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=null,id=qdev_id
308
QEMU X.Y.Z monitor - type 'help' for more information
309
(qemu) info block
310
-null: null-co:// (null-co)
311
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
312
Attached to: qdev_id
313
Removable device: not locked, tray closed
314
Cache mode: writeback
315
@@ -XXX,XX +XXX,XX @@ null: null-co:// (null-co)
316
317
=== -drive if=none/-device=<node-name> ===
318
319
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=null,id=qdev_id
320
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=null,id=qdev_id
321
QEMU X.Y.Z monitor - type 'help' for more information
322
(qemu) info block
323
-none0 (null): null-co:// (null-co)
324
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
325
Removable device: not locked, tray closed
326
Cache mode: writeback
327
328
-null: null-co:// (null-co)
329
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
330
Attached to: qdev_id
331
Cache mode: writeback
332
(qemu) quit
333
334
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=null,id=qdev_id
335
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=null,id=qdev_id
336
QEMU X.Y.Z monitor - type 'help' for more information
337
(qemu) info block
338
-none0 (null): null-co:// (null-co)
339
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
340
Removable device: not locked, tray closed
341
Cache mode: writeback
342
343
-null: null-co:// (null-co)
344
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
345
Attached to: qdev_id
346
Cache mode: writeback
347
(qemu) quit
348
349
-Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
350
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
351
QEMU X.Y.Z monitor - type 'help' for more information
352
(qemu) info block
353
-none0 (null): null-co:// (null-co)
354
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
355
Removable device: not locked, tray closed
356
Cache mode: writeback
357
358
-null: null-co:// (null-co)
359
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
360
Attached to: PATH
361
Cache mode: writeback
362
(qemu) quit
363
364
-Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=null,id=qdev_id
365
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=null,id=qdev_id
366
QEMU X.Y.Z monitor - type 'help' for more information
367
(qemu) info block
368
-none0 (null): null-co:// (null-co)
369
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
370
Removable device: not locked, tray closed
371
Cache mode: writeback
372
373
-null: null-co:// (null-co)
374
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
375
Attached to: qdev_id
376
Removable device: not locked, tray closed
377
Cache mode: writeback
378
(qemu) quit
379
380
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=null,id=qdev_id
381
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=null,id=qdev_id
382
QEMU X.Y.Z monitor - type 'help' for more information
383
(qemu) info block
384
-none0 (null): null-co:// (null-co)
385
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
386
Removable device: not locked, tray closed
387
Cache mode: writeback
388
389
-null: null-co:// (null-co)
390
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
391
Attached to: qdev_id
392
Removable device: not locked, tray closed
393
Cache mode: writeback
394
(qemu) quit
395
396
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=null,id=qdev_id
397
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=null,id=qdev_id
398
QEMU X.Y.Z monitor - type 'help' for more information
399
(qemu) info block
400
-none0 (null): null-co:// (null-co)
401
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
402
Removable device: not locked, tray closed
403
Cache mode: writeback
404
405
-null: null-co:// (null-co)
406
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
407
Attached to: qdev_id
408
Removable device: not locked, tray closed
409
Cache mode: writeback
410
@@ -XXX,XX +XXX,XX @@ null: null-co:// (null-co)
411
412
=== -drive if=none/-device=<bb-name> (with medium) ===
413
414
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=none0
415
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=none0
416
QEMU X.Y.Z monitor - type 'help' for more information
417
(qemu) info block
418
-none0 (null): null-co:// (null-co)
419
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
420
Attached to: PATH
421
Cache mode: writeback
422
(qemu) quit
423
424
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=none0,id=qdev_id
425
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=none0,id=qdev_id
426
QEMU X.Y.Z monitor - type 'help' for more information
427
(qemu) info block
428
-none0 (null): null-co:// (null-co)
429
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
430
Attached to: qdev_id
431
Cache mode: writeback
432
(qemu) quit
433
434
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=none0
435
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=none0
436
QEMU X.Y.Z monitor - type 'help' for more information
437
(qemu) info block
438
-none0 (null): null-co:// (null-co)
439
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
440
Attached to: PATH
441
Cache mode: writeback
442
(qemu) quit
443
444
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=none0,id=qdev_id
445
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=none0,id=qdev_id
446
QEMU X.Y.Z monitor - type 'help' for more information
447
(qemu) info block
448
-none0 (null): null-co:// (null-co)
449
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
450
Attached to: qdev_id
451
Cache mode: writeback
452
(qemu) quit
453
454
-Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=none0
455
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=none0
456
QEMU X.Y.Z monitor - type 'help' for more information
457
(qemu) info block
458
-none0 (null): null-co:// (null-co)
459
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
460
Attached to: PATH
461
Cache mode: writeback
462
(qemu) quit
463
464
-Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=none0,id=qdev_id
465
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=none0,id=qdev_id
466
QEMU X.Y.Z monitor - type 'help' for more information
467
(qemu) info block
468
-none0 (null): null-co:// (null-co)
469
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
470
Attached to: PATH
471
Cache mode: writeback
472
(qemu) quit
473
474
-Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=none0
475
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=none0
476
QEMU X.Y.Z monitor - type 'help' for more information
477
(qemu) info block
478
-none0 (null): null-co:// (null-co)
479
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
480
Attached to: PATH
481
Removable device: not locked, tray closed
482
Cache mode: writeback
483
(qemu) quit
484
485
-Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=none0,id=qdev_id
486
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=none0,id=qdev_id
487
QEMU X.Y.Z monitor - type 'help' for more information
488
(qemu) info block
489
-none0 (null): null-co:// (null-co)
490
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
491
Attached to: qdev_id
492
Removable device: not locked, tray closed
493
Cache mode: writeback
494
(qemu) quit
495
496
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=none0
497
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=none0
498
QEMU X.Y.Z monitor - type 'help' for more information
499
(qemu) info block
500
-none0 (null): null-co:// (null-co)
501
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
502
Attached to: PATH
503
Removable device: not locked, tray closed
504
Cache mode: writeback
505
(qemu) quit
506
507
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=none0,id=qdev_id
508
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=none0,id=qdev_id
509
QEMU X.Y.Z monitor - type 'help' for more information
510
(qemu) info block
511
-none0 (null): null-co:// (null-co)
512
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
513
Attached to: qdev_id
514
Removable device: not locked, tray closed
515
Cache mode: writeback
516
(qemu) quit
517
518
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=none0
519
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=none0
520
QEMU X.Y.Z monitor - type 'help' for more information
521
(qemu) info block
522
-none0 (null): null-co:// (null-co)
523
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
524
Attached to: PATH
525
Removable device: not locked, tray closed
526
Cache mode: writeback
527
(qemu) quit
528
529
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=none0,id=qdev_id
530
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=none0,id=qdev_id
531
QEMU X.Y.Z monitor - type 'help' for more information
532
(qemu) info block
533
-none0 (null): null-co:// (null-co)
534
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
535
Attached to: qdev_id
536
Removable device: not locked, tray closed
537
Cache mode: writeback
538
@@ -XXX,XX +XXX,XX @@ floppy0: [not inserted]
539
Removable device: not locked, tray closed
540
(qemu) quit
541
542
-Testing: -drive if=floppy,driver=null-co
543
+Testing: -drive if=floppy,driver=null-co,read-zeroes=on
544
QEMU X.Y.Z monitor - type 'help' for more information
545
(qemu) info block
546
-floppy0 (NODE_NAME): null-co:// (null-co)
547
+floppy0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
548
Attached to: PATH
549
Removable device: not locked, tray closed
550
Cache mode: writeback
551
(qemu) quit
552
553
-Testing: -drive if=ide,driver=null-co
554
+Testing: -drive if=ide,driver=null-co,read-zeroes=on
555
QEMU X.Y.Z monitor - type 'help' for more information
556
(qemu) info block
557
-ide0-hd0 (NODE_NAME): null-co:// (null-co)
558
+ide0-hd0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
559
Attached to: PATH
560
Cache mode: writeback
561
(qemu) quit
562
@@ -XXX,XX +XXX,XX @@ ide0-cd0: [not inserted]
563
Removable device: not locked, tray closed
564
(qemu) quit
565
566
-Testing: -drive if=ide,driver=null-co,media=cdrom
567
+Testing: -drive if=ide,driver=null-co,read-zeroes=on,media=cdrom
568
QEMU X.Y.Z monitor - type 'help' for more information
569
(qemu) info block
570
-ide0-cd0 (NODE_NAME): null-co:// (null-co, read-only)
571
+ide0-cd0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co, read-only)
572
Attached to: PATH
573
Removable device: not locked, tray closed
574
Cache mode: writeback
575
(qemu) quit
576
577
-Testing: -drive if=virtio,driver=null-co
578
+Testing: -drive if=virtio,driver=null-co,read-zeroes=on
579
QEMU X.Y.Z monitor - type 'help' for more information
580
(qemu) info block
581
-virtio0 (NODE_NAME): null-co:// (null-co)
582
+virtio0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
583
Attached to: PATH
584
Cache mode: writeback
585
(qemu) quit
586
587
-Testing: -drive if=pflash,driver=null-co,size=1M
588
+Testing: -drive if=pflash,driver=null-co,read-zeroes=on,size=1M
589
QEMU X.Y.Z monitor - type 'help' for more information
590
(qemu) info block
591
-pflash0 (NODE_NAME): json:{"driver": "null-co", "size": "1M"} (null-co)
592
+pflash0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co", "size": "1M"} (null-co)
593
Attached to: PATH
594
Cache mode: writeback
595
(qemu) quit
596
diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227
597
index XXXXXXX..XXXXXXX 100755
598
--- a/tests/qemu-iotests/227
599
+++ b/tests/qemu-iotests/227
600
@@ -XXX,XX +XXX,XX @@ echo
601
echo '=== blockstats with -drive if=virtio ==='
602
echo
603
604
-run_qemu -drive driver=null-co,if=virtio <<EOF
605
+run_qemu -drive driver=null-co,read-zeroes=on,if=virtio <<EOF
606
{ "execute": "qmp_capabilities" }
607
{ "execute": "query-blockstats"}
608
{ "execute": "quit" }
609
@@ -XXX,XX +XXX,XX @@ echo
610
echo '=== blockstats with -blockdev and -device ==='
611
echo
612
613
-run_qemu -blockdev driver=null-co,node-name=null -device virtio-blk,drive=null,id=virtio0 <<EOF
614
+run_qemu -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk,drive=null,id=virtio0 <<EOF
615
{ "execute": "qmp_capabilities" }
616
{ "execute": "query-blockstats"}
617
{ "execute": "quit" }
618
diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out
619
index XXXXXXX..XXXXXXX 100644
620
--- a/tests/qemu-iotests/227.out
621
+++ b/tests/qemu-iotests/227.out
622
@@ -XXX,XX +XXX,XX @@ QA output created by 227
623
624
=== blockstats with -drive if=virtio ===
625
626
-Testing: -drive driver=null-co,if=virtio
627
+Testing: -drive driver=null-co,read-zeroes=on,if=virtio
628
{
42
{
629
QMP_VERSION
43
BlockBackend *blk = child->opaque;
44
+ bool busy = false;
45
assert(blk->quiesce_counter);
46
- return !!blk->in_flight;
47
+
48
+ if (blk->dev_ops && blk->dev_ops->drained_poll) {
49
+ busy = blk->dev_ops->drained_poll(blk->dev_opaque);
50
+ }
51
+ return busy || !!blk->in_flight;
630
}
52
}
631
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev driver=null-co,node-name=null
53
632
54
static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
633
=== blockstats with -blockdev and -device ===
634
635
-Testing: -blockdev driver=null-co,node-name=null -device virtio-blk,drive=null,id=virtio0
636
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk,drive=null,id=virtio0
637
{
638
QMP_VERSION
639
}
640
diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238
641
index XXXXXXX..XXXXXXX 100755
642
--- a/tests/qemu-iotests/238
643
+++ b/tests/qemu-iotests/238
644
@@ -XXX,XX +XXX,XX @@ else:
645
vm = iotests.VM()
646
vm.launch()
647
648
-log(vm.qmp('blockdev-add', node_name='hd0', driver='null-co'))
649
+log(vm.qmp('blockdev-add', node_name='hd0', driver='null-co', read_zeroes=True))
650
log(vm.qmp('object-add', qom_type='iothread', id='iothread0'))
651
log(vm.qmp('device_add', id='scsi0', driver=virtio_scsi_device, iothread='iothread0'))
652
log(vm.qmp('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0'))
653
diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240
654
index XXXXXXX..XXXXXXX 100755
655
--- a/tests/qemu-iotests/240
656
+++ b/tests/qemu-iotests/240
657
@@ -XXX,XX +XXX,XX @@ echo
658
659
run_qemu <<EOF
660
{ "execute": "qmp_capabilities" }
661
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0"}}
662
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0"}}
663
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
664
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
665
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
666
@@ -XXX,XX +XXX,XX @@ echo
667
668
run_qemu <<EOF
669
{ "execute": "qmp_capabilities" }
670
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
671
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
672
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
673
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
674
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
675
@@ -XXX,XX +XXX,XX @@ echo
676
677
run_qemu <<EOF
678
{ "execute": "qmp_capabilities" }
679
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
680
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
681
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
682
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread1"}}
683
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
684
@@ -XXX,XX +XXX,XX @@ echo
685
686
run_qemu <<EOF
687
{ "execute": "qmp_capabilities" }
688
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
689
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
690
{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$TEST_DIR/nbd"}}}}
691
{ "execute": "nbd-server-add", "arguments": {"device":"hd0"}}
692
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
693
--
55
--
694
2.20.1
56
2.30.2
695
57
696
58
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Sergio Lopez <slp@redhat.com>
2
2
3
Callers can now pass a pointer to an integer that bdrv_drain_invoke()
3
Before switching between AioContexts we need to make sure that we're
4
(and its recursive callees) will increment for every
4
fully quiesced ("nb_requests == 0" for every client) when entering the
5
bdrv_drain_invoke_entry() operation they schedule.
5
drained section.
6
bdrv_drain_invoke_entry() in turn will decrement it once it has invoked
7
BlockDriver.bdrv_co_drain_end().
8
6
9
We use atomic operations to access the pointee, because the
7
To do this, we set "quiescing = true" for every client on
10
bdrv_do_drained_end() caller may wish to end drained sections for
8
".drained_begin" to prevent new coroutines from being created, and
11
multiple nodes in different AioContexts (bdrv_drain_all_end() does, for
9
check if "nb_requests == 0" on ".drained_poll". Finally, once we're
12
example).
10
exiting the drained section, on ".drained_end" we set "quiescing =
11
false" and call "nbd_client_receive_next_request()" to resume the
12
processing of new requests.
13
13
14
This is the first step to moving the polling for BdrvCoDrainData.done to
14
With these changes, "blk_aio_attach()" and "blk_aio_detach()" can be
15
become true out of bdrv_drain_invoke() and into the root drained_end
15
reverted to be as simple as they were before f148ae7d36.
16
function.
17
16
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1960137
18
Suggested-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Sergio Lopez <slp@redhat.com>
20
Message-Id: <20210602060552.17433-3-slp@redhat.com>
21
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
23
---
21
block/io.c | 58 +++++++++++++++++++++++++++++++++++++-----------------
24
nbd/server.c | 82 ++++++++++++++++++++++++++++++++++++++--------------
22
1 file changed, 40 insertions(+), 18 deletions(-)
25
1 file changed, 61 insertions(+), 21 deletions(-)
23
26
24
diff --git a/block/io.c b/block/io.c
27
diff --git a/nbd/server.c b/nbd/server.c
25
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
26
--- a/block/io.c
29
--- a/nbd/server.c
27
+++ b/block/io.c
30
+++ b/nbd/server.c
28
@@ -XXX,XX +XXX,XX @@ typedef struct {
31
@@ -XXX,XX +XXX,XX @@ static void nbd_request_put(NBDRequestData *req)
29
bool poll;
32
g_free(req);
30
BdrvChild *parent;
33
31
bool ignore_bds_parents;
34
client->nb_requests--;
32
+ int *drained_end_counter;
35
+
33
} BdrvCoDrainData;
36
+ if (client->quiescing && client->nb_requests == 0) {
34
37
+ aio_wait_kick();
35
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
36
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
37
atomic_mb_set(&data->done, true);
38
bdrv_dec_in_flight(bs);
39
40
- if (data->begin) {
41
+ if (data->drained_end_counter) {
42
+ atomic_dec(data->drained_end_counter);
43
+ }
38
+ }
44
+
39
+
45
+ if (data->begin || data->drained_end_counter) {
40
nbd_client_receive_next_request(client);
46
g_free(data);
41
42
nbd_client_put(client);
43
@@ -XXX,XX +XXX,XX @@ static void blk_aio_attached(AioContext *ctx, void *opaque)
44
QTAILQ_FOREACH(client, &exp->clients, next) {
45
qio_channel_attach_aio_context(client->ioc, ctx);
46
47
+ assert(client->nb_requests == 0);
48
assert(client->recv_coroutine == NULL);
49
assert(client->send_coroutine == NULL);
50
-
51
- if (client->quiescing) {
52
- client->quiescing = false;
53
- nbd_client_receive_next_request(client);
54
- }
47
}
55
}
48
}
56
}
49
57
50
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
58
-static void nbd_aio_detach_bh(void *opaque)
51
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
59
+static void blk_aio_detach(void *opaque)
52
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
53
+ int *drained_end_counter)
54
{
60
{
55
BdrvCoDrainData *data;
61
NBDExport *exp = opaque;
56
62
NBDClient *client;
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
63
58
*data = (BdrvCoDrainData) {
64
+ trace_nbd_blk_aio_detach(exp->name, exp->common.ctx);
59
.bs = bs,
65
+
60
.done = false,
66
QTAILQ_FOREACH(client, &exp->clients, next) {
61
- .begin = begin
67
qio_channel_detach_aio_context(client->ioc);
62
+ .begin = begin,
63
+ .drained_end_counter = drained_end_counter,
64
};
65
66
+ if (!begin && drained_end_counter) {
67
+ atomic_inc(drained_end_counter);
68
+ }
68
+ }
69
+
69
+
70
/* Make sure the driver callback completes during the polling phase for
70
+ exp->common.ctx = NULL;
71
* drain_begin. */
71
+}
72
bdrv_inc_in_flight(bs);
73
data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data);
74
aio_co_schedule(bdrv_get_aio_context(bs), data->co);
75
76
- if (!begin) {
77
+ /*
78
+ * TODO: Drop this and make callers pass @drained_end_counter and poll
79
+ * themselves
80
+ */
81
+ if (!begin && !drained_end_counter) {
82
BDRV_POLL_WHILE(bs, !data->done);
83
g_free(data);
84
}
85
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
86
BdrvChild *parent, bool ignore_bds_parents,
87
bool poll);
88
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
89
- BdrvChild *parent, bool ignore_bds_parents);
90
+ BdrvChild *parent, bool ignore_bds_parents,
91
+ int *drained_end_counter);
92
93
static void bdrv_co_drain_bh_cb(void *opaque)
94
{
95
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
96
data->ignore_bds_parents, data->poll);
97
} else {
98
bdrv_do_drained_end(bs, data->recursive, data->parent,
99
- data->ignore_bds_parents);
100
+ data->ignore_bds_parents,
101
+ data->drained_end_counter);
102
}
103
if (ctx == co_ctx) {
104
aio_context_release(ctx);
105
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
106
bool begin, bool recursive,
107
BdrvChild *parent,
108
bool ignore_bds_parents,
109
- bool poll)
110
+ bool poll,
111
+ int *drained_end_counter)
112
{
113
BdrvCoDrainData data;
114
115
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
116
.parent = parent,
117
.ignore_bds_parents = ignore_bds_parents,
118
.poll = poll,
119
+ .drained_end_counter = drained_end_counter,
120
};
121
+
72
+
122
if (bs) {
73
+static void nbd_drained_begin(void *opaque)
123
bdrv_inc_in_flight(bs);
74
+{
124
}
75
+ NBDExport *exp = opaque;
125
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
76
+ NBDClient *client;
126
}
77
+
127
78
+ QTAILQ_FOREACH(client, &exp->clients, next) {
128
bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
79
client->quiescing = true;
129
- bdrv_drain_invoke(bs, true);
80
+ }
130
+ bdrv_drain_invoke(bs, true, NULL);
81
+}
131
}
82
132
83
- if (client->recv_coroutine) {
133
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
84
- if (client->read_yielding) {
134
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
85
- qemu_aio_coroutine_enter(exp->common.ctx,
135
86
- client->recv_coroutine);
136
if (qemu_in_coroutine()) {
87
- } else {
137
bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents,
88
- AIO_WAIT_WHILE(exp->common.ctx, client->recv_coroutine != NULL);
138
- poll);
89
- }
139
+ poll, NULL);
90
- }
140
return;
91
+static void nbd_drained_end(void *opaque)
141
}
92
+{
142
93
+ NBDExport *exp = opaque;
143
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
94
+ NBDClient *client;
144
}
95
145
96
- if (client->send_coroutine) {
146
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
97
- AIO_WAIT_WHILE(exp->common.ctx, client->send_coroutine != NULL);
147
- BdrvChild *parent, bool ignore_bds_parents)
98
- }
148
+ BdrvChild *parent, bool ignore_bds_parents,
99
+ QTAILQ_FOREACH(client, &exp->clients, next) {
149
+ int *drained_end_counter)
100
+ client->quiescing = false;
150
{
101
+ nbd_client_receive_next_request(client);
151
BdrvChild *child, *next;
152
int old_quiesce_counter;
153
154
if (qemu_in_coroutine()) {
155
bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
156
- false);
157
+ false, drained_end_counter);
158
return;
159
}
160
assert(bs->quiesce_counter > 0);
161
162
/* Re-enable things in child-to-parent order */
163
- bdrv_drain_invoke(bs, false);
164
+ bdrv_drain_invoke(bs, false, drained_end_counter);
165
bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
166
167
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
168
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
169
assert(!ignore_bds_parents);
170
bs->recursive_quiesce_counter--;
171
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
172
- bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents);
173
+ bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents,
174
+ drained_end_counter);
175
}
176
}
102
}
177
}
103
}
178
104
179
void bdrv_drained_end(BlockDriverState *bs)
105
-static void blk_aio_detach(void *opaque)
106
+static bool nbd_drained_poll(void *opaque)
180
{
107
{
181
- bdrv_do_drained_end(bs, false, NULL, false);
108
NBDExport *exp = opaque;
182
+ bdrv_do_drained_end(bs, false, NULL, false, NULL);
109
+ NBDClient *client;
110
111
- trace_nbd_blk_aio_detach(exp->name, exp->common.ctx);
112
+ QTAILQ_FOREACH(client, &exp->clients, next) {
113
+ if (client->nb_requests != 0) {
114
+ /*
115
+ * If there's a coroutine waiting for a request on nbd_read_eof()
116
+ * enter it here so we don't depend on the client to wake it up.
117
+ */
118
+ if (client->recv_coroutine != NULL && client->read_yielding) {
119
+ qemu_aio_coroutine_enter(exp->common.ctx,
120
+ client->recv_coroutine);
121
+ }
122
123
- aio_wait_bh_oneshot(exp->common.ctx, nbd_aio_detach_bh, exp);
124
+ return true;
125
+ }
126
+ }
127
128
- exp->common.ctx = NULL;
129
+ return false;
183
}
130
}
184
131
185
void bdrv_subtree_drained_end(BlockDriverState *bs)
132
static void nbd_eject_notifier(Notifier *n, void *data)
133
@@ -XXX,XX +XXX,XX @@ void nbd_export_set_on_eject_blk(BlockExport *exp, BlockBackend *blk)
134
blk_add_remove_bs_notifier(blk, &nbd_exp->eject_notifier);
135
}
136
137
+static const BlockDevOps nbd_block_ops = {
138
+ .drained_begin = nbd_drained_begin,
139
+ .drained_end = nbd_drained_end,
140
+ .drained_poll = nbd_drained_poll,
141
+};
142
+
143
static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
144
Error **errp)
186
{
145
{
187
- bdrv_do_drained_end(bs, true, NULL, false);
146
@@ -XXX,XX +XXX,XX @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
188
+ bdrv_do_drained_end(bs, true, NULL, false, NULL);
147
189
}
148
exp->allocation_depth = arg->allocation_depth;
190
149
191
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
150
+ /*
192
@@ -XXX,XX +XXX,XX @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
151
+ * We need to inhibit request queuing in the block layer to ensure we can
193
int i;
152
+ * be properly quiesced when entering a drained section, as our coroutines
194
153
+ * servicing pending requests might enter blk_pread().
195
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
154
+ */
196
- bdrv_do_drained_end(child->bs, true, child, false);
155
+ blk_set_disable_request_queuing(blk, true);
197
+ bdrv_do_drained_end(child->bs, true, child, false, NULL);
156
+
157
blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp);
158
159
+ blk_set_dev_ops(blk, &nbd_block_ops, exp);
160
+
161
QTAILQ_INSERT_TAIL(&exports, exp, next);
162
163
return 0;
164
@@ -XXX,XX +XXX,XX @@ static void nbd_export_delete(BlockExport *blk_exp)
165
}
166
blk_remove_aio_context_notifier(exp->common.blk, blk_aio_attached,
167
blk_aio_detach, exp);
168
+ blk_set_disable_request_queuing(exp->common.blk, false);
198
}
169
}
199
}
170
200
171
for (i = 0; i < exp->nr_export_bitmaps; i++) {
201
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
202
BlockDriverState *bs = NULL;
203
204
if (qemu_in_coroutine()) {
205
- bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true);
206
+ bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true, NULL);
207
return;
208
}
209
210
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
211
AioContext *aio_context = bdrv_get_aio_context(bs);
212
213
aio_context_acquire(aio_context);
214
- bdrv_do_drained_end(bs, false, NULL, true);
215
+ bdrv_do_drained_end(bs, false, NULL, true, NULL);
216
aio_context_release(aio_context);
217
}
218
219
--
172
--
220
2.20.1
173
2.30.2
221
174
222
175
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Don't report successful progress on failure, when call_state->ret is
4
set.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Message-Id: <20210528141628.44287-2-vsementsov@virtuozzo.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
tests/test-bdrv-drain.c | 36 ++++++++++++++++++++++++++++++++----
11
block/block-copy.c | 8 +++++---
7
1 file changed, 32 insertions(+), 4 deletions(-)
12
1 file changed, 5 insertions(+), 3 deletions(-)
8
13
9
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
14
diff --git a/block/block-copy.c b/block/block-copy.c
10
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
11
--- a/tests/test-bdrv-drain.c
16
--- a/block/block-copy.c
12
+++ b/tests/test-bdrv-drain.c
17
+++ b/block/block-copy.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct TestDropBackingBlockJob {
18
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
14
BlockJob common;
19
15
bool should_complete;
20
ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
16
bool *did_complete;
21
&error_is_read);
17
+ BlockDriverState *detach_also;
22
- if (ret < 0 && !t->call_state->ret) {
18
} TestDropBackingBlockJob;
23
- t->call_state->ret = ret;
19
24
- t->call_state->error_is_read = error_is_read;
20
static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp)
25
+ if (ret < 0) {
21
@@ -XXX,XX +XXX,XX @@ static void test_drop_backing_job_commit(Job *job)
26
+ if (!t->call_state->ret) {
22
container_of(job, TestDropBackingBlockJob, common.job);
27
+ t->call_state->ret = ret;
23
28
+ t->call_state->error_is_read = error_is_read;
24
bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort);
29
+ }
25
+ bdrv_set_backing_hd(s->detach_also, NULL, &error_abort);
30
} else {
26
31
progress_work_done(t->s->progress, t->bytes);
27
*s->did_complete = true;
32
}
28
}
29
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_drop_backing_job_driver = {
30
* Creates a child node with three parent nodes on it, and then runs a
31
* block job on the final one, parent-node-2.
32
*
33
- * (TODO: parent-node-0 currently serves no purpose, but will as of a
34
- * follow-up patch.)
35
- *
36
* The job is then asked to complete before a section where the child
37
* is drained.
38
*
39
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_drop_backing_job_driver = {
40
*
41
* Ending the drain on parent-node-1 will poll the AioContext, which
42
* lets job_exit() and thus test_drop_backing_job_commit() run. That
43
- * function removes the child as parent-node-2's backing file.
44
+ * function first removes the child as parent-node-2's backing file.
45
*
46
* In old (and buggy) implementations, there are two problems with
47
* that:
48
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_drop_backing_job_driver = {
49
* bdrv_replace_child_noperm() therefore must call drained_end() on
50
* the parent only if it really is still drained because the child is
51
* drained.
52
+ *
53
+ * If removing child from parent-node-2 was successful (as it should
54
+ * be), test_drop_backing_job_commit() will then also remove the child
55
+ * from parent-node-0.
56
+ *
57
+ * With an old version of our drain infrastructure ((A) above), that
58
+ * resulted in the following flow:
59
+ *
60
+ * 1. child attempts to leave its drained section. The call recurses
61
+ * to its parents.
62
+ *
63
+ * 2. parent-node-2 leaves the drained section. Polling in
64
+ * bdrv_drain_invoke() will schedule job_exit().
65
+ *
66
+ * 3. parent-node-1 leaves the drained section. Polling in
67
+ * bdrv_drain_invoke() will run job_exit(), thus disconnecting
68
+ * parent-node-0 from the child node.
69
+ *
70
+ * 4. bdrv_parent_drained_end() uses a QLIST_FOREACH_SAFE() loop to
71
+ * iterate over the parents. Thus, it now accesses the BdrvChild
72
+ * object that used to connect parent-node-0 and the child node.
73
+ * However, that object no longer exists, so it accesses a dangling
74
+ * pointer.
75
+ *
76
+ * The solution is to only poll once when running a bdrv_drained_end()
77
+ * operation, specifically at the end when all drained_end()
78
+ * operations for all involved nodes have been scheduled.
79
+ * Note that this also solves (A) above, thus hiding (B).
80
*/
81
static void test_blockjob_commit_by_drained_end(void)
82
{
83
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_commit_by_drained_end(void)
84
bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL,
85
&error_abort);
86
87
+ job->detach_also = bs_parents[0];
88
job->did_complete = &job_has_completed;
89
90
job_start(&job->common.job);
91
--
33
--
92
2.20.1
34
2.30.2
93
35
94
36
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Currently we update s->use_copy_range and s->copy_size in
4
block_copy_do_copy().
5
6
It's not very good:
7
8
1. block_copy_do_copy() is intended to be a simple function, that wraps
9
bdrv_co_<io> functions for need of block copy. That's why we don't pass
10
BlockCopyTask into it. So, block_copy_do_copy() is bad place for
11
manipulation with generic state of block-copy process
12
13
2. We are going to make block-copy thread-safe. So, it's good to move
14
manipulation with state of block-copy to the places where we'll need
15
critical sections anyway, to not introduce extra synchronization
16
primitives in block_copy_do_copy().
17
18
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
19
Message-Id: <20210528141628.44287-3-vsementsov@virtuozzo.com>
20
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
22
---
6
tests/test-bdrv-drain.c | 119 ++++++++++++++++++++++++++++++++++++++++
23
block/block-copy.c | 72 +++++++++++++++++++++++++++++++---------------
7
1 file changed, 119 insertions(+)
24
1 file changed, 49 insertions(+), 23 deletions(-)
8
25
9
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
26
diff --git a/block/block-copy.c b/block/block-copy.c
10
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
11
--- a/tests/test-bdrv-drain.c
28
--- a/block/block-copy.c
12
+++ b/tests/test-bdrv-drain.c
29
+++ b/block/block-copy.c
13
@@ -XXX,XX +XXX,XX @@ static void test_set_aio_context(void)
30
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyTask {
14
iothread_join(b);
31
int64_t offset;
32
int64_t bytes;
33
bool zeroes;
34
+ bool copy_range;
35
QLIST_ENTRY(BlockCopyTask) list;
36
CoQueue wait_queue; /* coroutines blocked on this task */
37
} BlockCopyTask;
38
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
39
.call_state = call_state,
40
.offset = offset,
41
.bytes = bytes,
42
+ .copy_range = s->use_copy_range,
43
};
44
qemu_co_queue_init(&task->wait_queue);
45
QLIST_INSERT_HEAD(&s->tasks, task, list);
46
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
47
*
48
* No sync here: nor bitmap neighter intersecting requests handling, only copy.
49
*
50
+ * @copy_range is an in-out argument: if *copy_range is false, copy_range is not
51
+ * done. If *copy_range is true, copy_range is attempted. If the copy_range
52
+ * attempt fails, the function falls back to the usual read+write and
53
+ * *copy_range is set to false. *copy_range and zeroes must not be true
54
+ * simultaneously.
55
+ *
56
* Returns 0 on success.
57
*/
58
static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
59
int64_t offset, int64_t bytes,
60
- bool zeroes, bool *error_is_read)
61
+ bool zeroes, bool *copy_range,
62
+ bool *error_is_read)
63
{
64
int ret;
65
int64_t nbytes = MIN(offset + bytes, s->len) - offset;
66
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
67
assert(offset + bytes <= s->len ||
68
offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size));
69
assert(nbytes < INT_MAX);
70
+ assert(!(*copy_range && zeroes));
71
72
if (zeroes) {
73
ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags &
74
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
75
return ret;
76
}
77
78
- if (s->use_copy_range) {
79
+ if (*copy_range) {
80
ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes,
81
0, s->write_flags);
82
if (ret < 0) {
83
trace_block_copy_copy_range_fail(s, offset, ret);
84
- s->use_copy_range = false;
85
- s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
86
+ *copy_range = false;
87
/* Fallback to read+write with allocated buffer */
88
} else {
89
- if (s->use_copy_range) {
90
- /*
91
- * Successful copy-range. Now increase copy_size. copy_range
92
- * does not respect max_transfer (it's a TODO), so we factor
93
- * that in here.
94
- *
95
- * Note: we double-check s->use_copy_range for the case when
96
- * parallel block-copy request unsets it during previous
97
- * bdrv_co_copy_range call.
98
- */
99
- s->copy_size =
100
- MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
101
- QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source,
102
- s->target),
103
- s->cluster_size));
104
- }
105
- goto out;
106
+ return 0;
107
}
108
}
109
110
@@ -XXX,XX +XXX,XX @@ out:
111
return ret;
15
}
112
}
16
113
17
+
114
+static void block_copy_handle_copy_range_result(BlockCopyState *s,
18
+typedef struct TestDropBackingBlockJob {
115
+ bool is_success)
19
+ BlockJob common;
20
+ bool should_complete;
21
+ bool *did_complete;
22
+} TestDropBackingBlockJob;
23
+
24
+static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp)
25
+{
116
+{
26
+ TestDropBackingBlockJob *s =
117
+ if (!s->use_copy_range) {
27
+ container_of(job, TestDropBackingBlockJob, common.job);
118
+ /* already disabled */
28
+
119
+ return;
29
+ while (!s->should_complete) {
30
+ job_sleep_ns(job, 0);
31
+ }
120
+ }
32
+
121
+
33
+ return 0;
122
+ if (is_success) {
123
+ /*
124
+ * Successful copy-range. Now increase copy_size. copy_range
125
+ * does not respect max_transfer (it's a TODO), so we factor
126
+ * that in here.
127
+ */
128
+ s->copy_size =
129
+ MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
130
+ QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source,
131
+ s->target),
132
+ s->cluster_size));
133
+ } else {
134
+ /* Copy-range failed, disable it. */
135
+ s->use_copy_range = false;
136
+ s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
137
+ }
34
+}
138
+}
35
+
139
+
36
+static void test_drop_backing_job_commit(Job *job)
140
static coroutine_fn int block_copy_task_entry(AioTask *task)
37
+{
141
{
38
+ TestDropBackingBlockJob *s =
142
BlockCopyTask *t = container_of(task, BlockCopyTask, task);
39
+ container_of(job, TestDropBackingBlockJob, common.job);
143
bool error_is_read = false;
40
+
144
+ bool copy_range = t->copy_range;
41
+ bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort);
145
int ret;
42
+
146
43
+ *s->did_complete = true;
147
ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
44
+}
148
- &error_is_read);
45
+
149
+ &copy_range, &error_is_read);
46
+static const BlockJobDriver test_drop_backing_job_driver = {
150
+ if (t->copy_range) {
47
+ .job_driver = {
151
+ block_copy_handle_copy_range_result(t->s, copy_range);
48
+ .instance_size = sizeof(TestDropBackingBlockJob),
49
+ .free = block_job_free,
50
+ .user_resume = block_job_user_resume,
51
+ .drain = block_job_drain,
52
+ .run = test_drop_backing_job_run,
53
+ .commit = test_drop_backing_job_commit,
54
+ }
152
+ }
55
+};
153
if (ret < 0) {
56
+
154
if (!t->call_state->ret) {
57
+/**
155
t->call_state->ret = ret;
58
+ * Creates a child node with three parent nodes on it, and then runs a
156
@@ -XXX,XX +XXX,XX @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
59
+ * block job on the final one, parent-node-2.
157
g_free(task);
60
+ *
158
continue;
61
+ * (TODO: parent-node-0 currently serves no purpose, but will as of a
159
}
62
+ * follow-up patch.)
160
- task->zeroes = ret & BDRV_BLOCK_ZERO;
63
+ *
161
+ if (ret & BDRV_BLOCK_ZERO) {
64
+ * The job is then asked to complete before a section where the child
162
+ task->zeroes = true;
65
+ * is drained.
163
+ task->copy_range = false;
66
+ *
164
+ }
67
+ * Ending this section will undrain the child's parents, first
165
68
+ * parent-node-2, then parent-node-1, then parent-node-0 -- the parent
166
if (s->speed) {
69
+ * list is in reverse order of how they were added. Ending the drain
167
if (!call_state->ignore_ratelimit) {
70
+ * on parent-node-2 will resume the job, thus completing it and
71
+ * scheduling job_exit().
72
+ *
73
+ * Ending the drain on parent-node-1 will poll the AioContext, which
74
+ * lets job_exit() and thus test_drop_backing_job_commit() run. That
75
+ * function removes the child as parent-node-2's backing file.
76
+ *
77
+ * In old (and buggy) implementations, there are two problems with
78
+ * that:
79
+ * (A) bdrv_drain_invoke() polls for every node that leaves the
80
+ * drained section. This means that job_exit() is scheduled
81
+ * before the child has left the drained section. Its
82
+ * quiesce_counter is therefore still 1 when it is removed from
83
+ * parent-node-2.
84
+ *
85
+ * (B) bdrv_replace_child_noperm() calls drained_end() on the old
86
+ * child's parents as many times as the child is quiesced. This
87
+ * means it will call drained_end() on parent-node-2 once.
88
+ * Because parent-node-2 is no longer quiesced at this point, this
89
+ * will fail.
90
+ *
91
+ * bdrv_replace_child_noperm() therefore must call drained_end() on
92
+ * the parent only if it really is still drained because the child is
93
+ * drained.
94
+ */
95
+static void test_blockjob_commit_by_drained_end(void)
96
+{
97
+ BlockDriverState *bs_child, *bs_parents[3];
98
+ TestDropBackingBlockJob *job;
99
+ bool job_has_completed = false;
100
+ int i;
101
+
102
+ bs_child = bdrv_new_open_driver(&bdrv_test, "child-node", BDRV_O_RDWR,
103
+ &error_abort);
104
+
105
+ for (i = 0; i < 3; i++) {
106
+ char name[32];
107
+ snprintf(name, sizeof(name), "parent-node-%i", i);
108
+ bs_parents[i] = bdrv_new_open_driver(&bdrv_test, name, BDRV_O_RDWR,
109
+ &error_abort);
110
+ bdrv_set_backing_hd(bs_parents[i], bs_child, &error_abort);
111
+ }
112
+
113
+ job = block_job_create("job", &test_drop_backing_job_driver, NULL,
114
+ bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL,
115
+ &error_abort);
116
+
117
+ job->did_complete = &job_has_completed;
118
+
119
+ job_start(&job->common.job);
120
+
121
+ job->should_complete = true;
122
+ bdrv_drained_begin(bs_child);
123
+ g_assert(!job_has_completed);
124
+ bdrv_drained_end(bs_child);
125
+ g_assert(job_has_completed);
126
+
127
+ bdrv_unref(bs_parents[0]);
128
+ bdrv_unref(bs_parents[1]);
129
+ bdrv_unref(bs_parents[2]);
130
+ bdrv_unref(bs_child);
131
+}
132
+
133
int main(int argc, char **argv)
134
{
135
int ret;
136
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
137
138
g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context);
139
140
+ g_test_add_func("/bdrv-drain/blockjob/commit_by_drained_end",
141
+ test_blockjob_commit_by_drained_end);
142
+
143
ret = g_test_run();
144
qemu_event_destroy(&done_event);
145
return ret;
146
--
168
--
147
2.20.1
169
2.30.2
148
170
149
171
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
2
2
3
When changing a node's AioContext, the caller must acquire the old
3
Document that security reports must use 'null-co,read-zeroes=on'
4
AioContext (unless it currently runs in that old context). Therefore,
4
because otherwise the memory is left uninitialized (which is an
5
unless the node currently is in the main context, we always have to
5
on-purpose performance feature).
6
acquire the old context around calls that may change a node's
7
AioContext.
8
6
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
9
Message-Id: <20210601162548.2076631-1-philmd@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
11
---
12
tests/test-block-iothread.c | 40 ++++++++++++++++++++++++-------------
12
docs/devel/secure-coding-practices.rst | 9 +++++++++
13
1 file changed, 26 insertions(+), 14 deletions(-)
13
1 file changed, 9 insertions(+)
14
14
15
diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c
15
diff --git a/docs/devel/secure-coding-practices.rst b/docs/devel/secure-coding-practices.rst
16
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/test-block-iothread.c
17
--- a/docs/devel/secure-coding-practices.rst
18
+++ b/tests/test-block-iothread.c
18
+++ b/docs/devel/secure-coding-practices.rst
19
@@ -XXX,XX +XXX,XX @@ static void test_sync_op(const void *opaque)
19
@@ -XXX,XX +XXX,XX @@ structures and only process the local copy. This prevents
20
if (t->blkfn) {
20
time-of-check-to-time-of-use (TOCTOU) race conditions that could cause QEMU to
21
t->blkfn(blk);
21
crash when a vCPU thread modifies guest RAM while device emulation is
22
}
22
processing it.
23
- aio_context_release(ctx);
23
+
24
blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort);
24
+Use of null-co block drivers
25
+ aio_context_release(ctx);
25
+----------------------------
26
26
+
27
bdrv_unref(bs);
27
+The ``null-co`` block driver is designed for performance: its read accesses are
28
blk_unref(blk);
28
+not initialized by default. In case this driver has to be used for security
29
@@ -XXX,XX +XXX,XX @@ static void test_propagate_basic(void)
29
+research, it must be used with the ``read-zeroes=on`` option which fills read
30
{
30
+buffers with zeroes. Security issues reported with the default
31
IOThread *iothread = iothread_new();
31
+(``read-zeroes=off``) will be discarded.
32
AioContext *ctx = iothread_get_aio_context(iothread);
33
+ AioContext *main_ctx;
34
BlockBackend *blk;
35
BlockDriverState *bs_a, *bs_b, *bs_verify;
36
QDict *options;
37
@@ -XXX,XX +XXX,XX @@ static void test_propagate_basic(void)
38
g_assert(bdrv_get_aio_context(bs_b) == ctx);
39
40
/* Switch the AioContext back */
41
- ctx = qemu_get_aio_context();
42
- blk_set_aio_context(blk, ctx, &error_abort);
43
- g_assert(blk_get_aio_context(blk) == ctx);
44
- g_assert(bdrv_get_aio_context(bs_a) == ctx);
45
- g_assert(bdrv_get_aio_context(bs_verify) == ctx);
46
- g_assert(bdrv_get_aio_context(bs_b) == ctx);
47
+ main_ctx = qemu_get_aio_context();
48
+ aio_context_acquire(ctx);
49
+ blk_set_aio_context(blk, main_ctx, &error_abort);
50
+ aio_context_release(ctx);
51
+ g_assert(blk_get_aio_context(blk) == main_ctx);
52
+ g_assert(bdrv_get_aio_context(bs_a) == main_ctx);
53
+ g_assert(bdrv_get_aio_context(bs_verify) == main_ctx);
54
+ g_assert(bdrv_get_aio_context(bs_b) == main_ctx);
55
56
bdrv_unref(bs_verify);
57
bdrv_unref(bs_b);
58
@@ -XXX,XX +XXX,XX @@ static void test_propagate_diamond(void)
59
{
60
IOThread *iothread = iothread_new();
61
AioContext *ctx = iothread_get_aio_context(iothread);
62
+ AioContext *main_ctx;
63
BlockBackend *blk;
64
BlockDriverState *bs_a, *bs_b, *bs_c, *bs_verify;
65
QDict *options;
66
@@ -XXX,XX +XXX,XX @@ static void test_propagate_diamond(void)
67
g_assert(bdrv_get_aio_context(bs_c) == ctx);
68
69
/* Switch the AioContext back */
70
- ctx = qemu_get_aio_context();
71
- blk_set_aio_context(blk, ctx, &error_abort);
72
- g_assert(blk_get_aio_context(blk) == ctx);
73
- g_assert(bdrv_get_aio_context(bs_verify) == ctx);
74
- g_assert(bdrv_get_aio_context(bs_a) == ctx);
75
- g_assert(bdrv_get_aio_context(bs_b) == ctx);
76
- g_assert(bdrv_get_aio_context(bs_c) == ctx);
77
+ main_ctx = qemu_get_aio_context();
78
+ aio_context_acquire(ctx);
79
+ blk_set_aio_context(blk, main_ctx, &error_abort);
80
+ aio_context_release(ctx);
81
+ g_assert(blk_get_aio_context(blk) == main_ctx);
82
+ g_assert(bdrv_get_aio_context(bs_verify) == main_ctx);
83
+ g_assert(bdrv_get_aio_context(bs_a) == main_ctx);
84
+ g_assert(bdrv_get_aio_context(bs_b) == main_ctx);
85
+ g_assert(bdrv_get_aio_context(bs_c) == main_ctx);
86
87
blk_unref(blk);
88
bdrv_unref(bs_verify);
89
@@ -XXX,XX +XXX,XX @@ static void test_attach_second_node(void)
90
g_assert(bdrv_get_aio_context(bs) == ctx);
91
g_assert(bdrv_get_aio_context(filter) == ctx);
92
93
+ aio_context_acquire(ctx);
94
blk_set_aio_context(blk, main_ctx, &error_abort);
95
+ aio_context_release(ctx);
96
g_assert(blk_get_aio_context(blk) == main_ctx);
97
g_assert(bdrv_get_aio_context(bs) == main_ctx);
98
g_assert(bdrv_get_aio_context(filter) == main_ctx);
99
@@ -XXX,XX +XXX,XX @@ static void test_attach_preserve_blk_ctx(void)
100
g_assert(bdrv_get_aio_context(bs) == ctx);
101
102
/* Remove the node again */
103
+ aio_context_acquire(ctx);
104
blk_remove_bs(blk);
105
+ aio_context_release(ctx);
106
g_assert(blk_get_aio_context(blk) == ctx);
107
g_assert(bdrv_get_aio_context(bs) == qemu_get_aio_context());
108
109
@@ -XXX,XX +XXX,XX @@ static void test_attach_preserve_blk_ctx(void)
110
g_assert(blk_get_aio_context(blk) == ctx);
111
g_assert(bdrv_get_aio_context(bs) == ctx);
112
113
+ aio_context_acquire(ctx);
114
blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort);
115
+ aio_context_release(ctx);
116
bdrv_unref(bs);
117
blk_unref(blk);
118
}
119
--
32
--
120
2.20.1
33
2.30.2
121
34
122
35
diff view generated by jsdifflib