1
The following changes since commit 7b1db0908d88f0c9cfac24e214ff72a860692e23:
1
The following changes since commit d922088eb4ba6bc31a99f17b32cf75e59dd306cd:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180323' into staging (2018-03-25 13:51:33 +0100)
3
Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging (2025-02-03 13:42:02 -0500)
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
https://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to 0b7e7f66813a7e346e12d47be977a32a530a6316:
9
for you to fetch changes up to fc4e394b2887e15d5f83994e4fc7b26c895c627a:
10
10
11
qemu-iotests: Test vhdx image creation with QMP (2018-03-26 12:17:43 +0200)
11
block: remove unused BLOCK_OP_TYPE_DATAPLANE (2025-02-06 14:51:10 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches
14
Block layer patches
15
15
16
- Managing inactive nodes (enables QSD migration with shared storage)
17
- Fix swapped values for BLOCK_IO_ERROR 'device' and 'qom-path'
18
- vpc: Read images exported from Azure correctly
19
- scripts/qemu-gdb: Support coroutine dumps in coredumps
20
- Minor cleanups
21
16
----------------------------------------------------------------
22
----------------------------------------------------------------
17
Alberto Garcia (1):
23
Fabiano Rosas (1):
18
qcow2: Reset free_cluster_index when allocating a new refcount block
24
block: Fix leak in send_qmp_error_event
19
25
20
Eric Blake (1):
26
Kevin Wolf (16):
21
iotests: 163 is not quick
27
block: Add 'active' field to BlockDeviceInfo
28
block: Allow inactivating already inactive nodes
29
block: Inactivate external snapshot overlays when necessary
30
migration/block-active: Remove global active flag
31
block: Don't attach inactive child to active node
32
block: Fix crash on block_resize on inactive node
33
block: Add option to create inactive nodes
34
block: Add blockdev-set-active QMP command
35
block: Support inactive nodes in blk_insert_bs()
36
block/export: Don't ignore image activation error in blk_exp_add()
37
block: Drain nodes before inactivating them
38
block/export: Add option to allow export of inactive nodes
39
nbd/server: Support inactive nodes
40
iotests: Add filter_qtest()
41
iotests: Add qsd-migrate case
42
iotests: Add (NBD-based) tests for inactive nodes
22
43
23
Fabiano Rosas (5):
44
Peter Krempa (1):
24
block/replication: Remove protocol_name field
45
block-backend: Fix argument order when calling 'qapi_event_send_block_io_error()'
25
block/quorum: Remove protocol-related fields
26
block/throttle: Remove protocol-related fields
27
block/blkreplay: Remove protocol-related fields
28
include/block/block_int: Document protocol related functions
29
46
30
Kevin Wolf (12):
47
Peter Xu (3):
31
vdi: Change 'static' create option to 'preallocation' in QMP
48
scripts/qemu-gdb: Always do full stack dump for python errors
32
vdi: Fix build with CONFIG_VDI_DEBUG
49
scripts/qemu-gdb: Simplify fs_base fetching for coroutines
33
qemu-iotests: Test vdi image creation with QMP
50
scripts/qemu-gdb: Support coroutine dumps in coredumps
34
qemu-iotests: Enable 025 for luks
35
luks: Turn another invalid assertion into check
36
qemu-iotests: Test invalid resize on luks
37
parallels: Check maximum cluster size on create
38
qemu-iotests: Test parallels image creation with QMP
39
vhdx: Require power-of-two block size on create
40
vhdx: Don't use error_setg_errno() with constant errno
41
vhdx: Check for 4 GB maximum log size on creation
42
qemu-iotests: Test vhdx image creation with QMP
43
51
44
qapi/block-core.json | 7 +-
52
Philippe Mathieu-Daudé (1):
45
include/block/block_int.h | 8 ++
53
block: Improve blk_get_attached_dev_id() docstring
46
replication.h | 1 -
47
block/blkreplay.c | 3 +-
48
block/crypto.c | 6 +-
49
block/parallels.c | 5 +
50
block/qcow2-refcount.c | 7 +
51
block/quorum.c | 3 +-
52
block/replication.c | 1 -
53
block/throttle.c | 3 +-
54
block/vdi.c | 46 ++++--
55
block/vhdx.c | 17 ++-
56
tests/qemu-iotests/025 | 9 +-
57
tests/qemu-iotests/026.out | 6 +-
58
tests/qemu-iotests/121 | 20 +++
59
tests/qemu-iotests/121.out | 10 ++
60
tests/qemu-iotests/210 | 37 +++++
61
tests/qemu-iotests/210.out | 16 +++
62
tests/qemu-iotests/211 | 246 ++++++++++++++++++++++++++++++++
63
tests/qemu-iotests/211.out | 97 +++++++++++++
64
tests/qemu-iotests/212 | 326 ++++++++++++++++++++++++++++++++++++++++++
65
tests/qemu-iotests/212.out | 111 ++++++++++++++
66
tests/qemu-iotests/213 | 349 +++++++++++++++++++++++++++++++++++++++++++++
67
tests/qemu-iotests/213.out | 121 ++++++++++++++++
68
tests/qemu-iotests/group | 5 +-
69
25 files changed, 1423 insertions(+), 37 deletions(-)
70
create mode 100755 tests/qemu-iotests/211
71
create mode 100644 tests/qemu-iotests/211.out
72
create mode 100755 tests/qemu-iotests/212
73
create mode 100644 tests/qemu-iotests/212.out
74
create mode 100755 tests/qemu-iotests/213
75
create mode 100644 tests/qemu-iotests/213.out
76
54
55
Stefan Hajnoczi (1):
56
block: remove unused BLOCK_OP_TYPE_DATAPLANE
57
58
Vitaly Kuznetsov (2):
59
vpc: Split off vpc_ignore_current_size() helper
60
vpc: Read images exported from Azure correctly
61
62
qapi/block-core.json | 44 +++-
63
qapi/block-export.json | 10 +-
64
include/block/block-common.h | 2 +-
65
include/block/block-global-state.h | 6 +
66
include/block/export.h | 3 +
67
include/system/block-backend-io.h | 7 +
68
migration/migration.h | 3 -
69
block.c | 64 +++++-
70
block/block-backend.c | 32 ++-
71
block/export/export.c | 29 ++-
72
block/monitor/block-hmp-cmds.c | 5 +-
73
block/qapi.c | 1 +
74
block/replication.c | 1 -
75
block/vpc.c | 65 +++---
76
blockdev.c | 48 ++++
77
blockjob.c | 2 -
78
hw/block/virtio-blk.c | 9 -
79
hw/scsi/virtio-scsi.c | 3 -
80
migration/block-active.c | 46 ----
81
migration/migration.c | 8 -
82
nbd/server.c | 17 ++
83
scripts/qemu-gdb.py | 2 +
84
scripts/qemugdb/coroutine.py | 102 ++++++---
85
tests/qemu-iotests/iotests.py | 8 +
86
tests/qemu-iotests/041 | 4 +-
87
tests/qemu-iotests/165 | 4 +-
88
tests/qemu-iotests/184.out | 2 +
89
tests/qemu-iotests/191.out | 16 ++
90
tests/qemu-iotests/273.out | 5 +
91
tests/qemu-iotests/tests/copy-before-write | 3 +-
92
tests/qemu-iotests/tests/inactive-node-nbd | 303 +++++++++++++++++++++++++
93
tests/qemu-iotests/tests/inactive-node-nbd.out | 239 +++++++++++++++++++
94
tests/qemu-iotests/tests/migrate-bitmaps-test | 7 +-
95
tests/qemu-iotests/tests/qsd-migrate | 140 ++++++++++++
96
tests/qemu-iotests/tests/qsd-migrate.out | 59 +++++
97
35 files changed, 1133 insertions(+), 166 deletions(-)
98
create mode 100755 tests/qemu-iotests/tests/inactive-node-nbd
99
create mode 100644 tests/qemu-iotests/tests/inactive-node-nbd.out
100
create mode 100755 tests/qemu-iotests/tests/qsd-migrate
101
create mode 100644 tests/qemu-iotests/tests/qsd-migrate.out
102
103
diff view generated by jsdifflib
1
From: Fabiano Rosas <farosas@linux.vnet.ibm.com>
1
From: Vitaly Kuznetsov <vkuznets@redhat.com>
2
2
3
The throttle driver is not a protocol so it should implement bdrv_open
3
In preparation to making changes to the logic deciding whether CHS or
4
instead of bdrv_file_open and not provide a protocol_name.
4
'current_size' need to be used in determining the image size, split off
5
vpc_ignore_current_size() helper.
5
6
6
Attempts to invoke this driver using protocol syntax
7
No functional change intended.
7
(i.e. throttle:<filename:options:...>) will now fail gracefully:
8
8
9
$ qemu-img info throttle:foo
9
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
10
qemu-img: Could not open 'throttle:foo': Unknown protocol 'throttle'
10
Message-ID: <20241212134504.1983757-2-vkuznets@redhat.com>
11
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
12
Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: Alberto Garcia <berto@igalia.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
14
---
17
block/throttle.c | 3 +--
15
block/vpc.c | 67 +++++++++++++++++++++++++++++------------------------
18
1 file changed, 1 insertion(+), 2 deletions(-)
16
1 file changed, 37 insertions(+), 30 deletions(-)
19
17
20
diff --git a/block/throttle.c b/block/throttle.c
18
diff --git a/block/vpc.c b/block/vpc.c
21
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
22
--- a/block/throttle.c
20
--- a/block/vpc.c
23
+++ b/block/throttle.c
21
+++ b/block/vpc.c
24
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
22
@@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
25
23
}
26
static BlockDriver bdrv_throttle = {
24
}
27
.format_name = "throttle",
25
28
- .protocol_name = "throttle",
26
+/*
29
.instance_size = sizeof(ThrottleGroupMember),
27
+ * Microsoft Virtual PC and Microsoft Hyper-V produce and read
30
28
+ * VHD image sizes differently. VPC will rely on CHS geometry,
31
- .bdrv_file_open = throttle_open,
29
+ * while Hyper-V and disk2vhd use the size specified in the footer.
32
+ .bdrv_open = throttle_open,
30
+ *
33
.bdrv_close = throttle_close,
31
+ * We use a couple of approaches to try and determine the correct method:
34
.bdrv_co_flush = throttle_co_flush,
32
+ * look at the Creator App field, and look for images that have CHS
35
33
+ * geometry that is the maximum value.
34
+ *
35
+ * If the CHS geometry is the maximum CHS geometry, then we assume that
36
+ * the size is the footer->current_size to avoid truncation. Otherwise,
37
+ * we follow the table based on footer->creator_app:
38
+ *
39
+ * Known creator apps:
40
+ * 'vpc ' : CHS Virtual PC (uses disk geometry)
41
+ * 'qemu' : CHS QEMU (uses disk geometry)
42
+ * 'qem2' : current_size QEMU (uses current_size)
43
+ * 'win ' : current_size Hyper-V
44
+ * 'd2v ' : current_size Disk2vhd
45
+ * 'tap\0' : current_size XenServer
46
+ * 'CTXS' : current_size XenConverter
47
+ *
48
+ * The user can override the table values via drive options, however
49
+ * even with an override we will still use current_size for images
50
+ * that have CHS geometry of the maximum size.
51
+ */
52
+static bool vpc_ignore_current_size(VHDFooter *footer)
53
+{
54
+ return !!strncmp(footer->creator_app, "win ", 4) &&
55
+ !!strncmp(footer->creator_app, "qem2", 4) &&
56
+ !!strncmp(footer->creator_app, "d2v ", 4) &&
57
+ !!strncmp(footer->creator_app, "CTXS", 4) &&
58
+ !!memcmp(footer->creator_app, "tap", 4));
59
+}
60
+
61
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
62
Error **errp)
63
{
64
@@ -XXX,XX +XXX,XX @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
65
bs->total_sectors = (int64_t)
66
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
67
68
- /* Microsoft Virtual PC and Microsoft Hyper-V produce and read
69
- * VHD image sizes differently. VPC will rely on CHS geometry,
70
- * while Hyper-V and disk2vhd use the size specified in the footer.
71
- *
72
- * We use a couple of approaches to try and determine the correct method:
73
- * look at the Creator App field, and look for images that have CHS
74
- * geometry that is the maximum value.
75
- *
76
- * If the CHS geometry is the maximum CHS geometry, then we assume that
77
- * the size is the footer->current_size to avoid truncation. Otherwise,
78
- * we follow the table based on footer->creator_app:
79
- *
80
- * Known creator apps:
81
- * 'vpc ' : CHS Virtual PC (uses disk geometry)
82
- * 'qemu' : CHS QEMU (uses disk geometry)
83
- * 'qem2' : current_size QEMU (uses current_size)
84
- * 'win ' : current_size Hyper-V
85
- * 'd2v ' : current_size Disk2vhd
86
- * 'tap\0' : current_size XenServer
87
- * 'CTXS' : current_size XenConverter
88
- *
89
- * The user can override the table values via drive options, however
90
- * even with an override we will still use current_size for images
91
- * that have CHS geometry of the maximum size.
92
- */
93
- use_chs = (!!strncmp(footer->creator_app, "win ", 4) &&
94
- !!strncmp(footer->creator_app, "qem2", 4) &&
95
- !!strncmp(footer->creator_app, "d2v ", 4) &&
96
- !!strncmp(footer->creator_app, "CTXS", 4) &&
97
- !!memcmp(footer->creator_app, "tap", 4)) || s->force_use_chs;
98
+ /* Use CHS or current_size to determine the image size. */
99
+ use_chs = vpc_ignore_current_size(footer) || s->force_use_chs;
100
101
if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) {
102
bs->total_sectors = be64_to_cpu(footer->current_size) /
36
--
103
--
37
2.13.6
104
2.48.1
38
105
39
106
diff view generated by jsdifflib
New patch
1
From: Vitaly Kuznetsov <vkuznets@redhat.com>
1
2
3
It was found that 'qemu-nbd' is not able to work with some disk images
4
exported from Azure. Looking at the 512b footer (which contains VPC
5
metadata):
6
7
00000000 63 6f 6e 65 63 74 69 78 00 00 00 02 00 01 00 00 |conectix........|
8
00000010 ff ff ff ff ff ff ff ff 2e c7 9b 96 77 61 00 00 |............wa..|
9
00000020 00 07 00 00 57 69 32 6b 00 00 00 01 40 00 00 00 |....Wi2k....@...|
10
00000030 00 00 00 01 40 00 00 00 28 a2 10 3f 00 00 00 02 |....@...(..?....|
11
00000040 ff ff e7 47 8c 54 df 94 bd 35 71 4c 94 5f e5 44 |...G.T...5qL._.D|
12
00000050 44 53 92 1a 00 00 00 00 00 00 00 00 00 00 00 00 |DS..............|
13
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
14
15
we can see that Azure uses a different 'Creator application' --
16
'wa\0\0' (offset 0x1c, likely reads as 'Windows Azure') and QEMU uses this
17
field to determine how it can get image size. Apparently, Azure uses 'new'
18
method, just like Hyper-V.
19
20
Overall, it seems that only VPC and old QEMUs need to be ignored as all new
21
creator apps seem to have reliable current_size. Invert the logic and make
22
'current_size' method the default to avoid adding every new creator app to
23
the list.
24
25
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
26
Message-ID: <20241212134504.1983757-3-vkuznets@redhat.com>
27
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
28
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
---
30
block/vpc.c | 8 +++-----
31
1 file changed, 3 insertions(+), 5 deletions(-)
32
33
diff --git a/block/vpc.c b/block/vpc.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/block/vpc.c
36
+++ b/block/vpc.c
37
@@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
38
* 'd2v ' : current_size Disk2vhd
39
* 'tap\0' : current_size XenServer
40
* 'CTXS' : current_size XenConverter
41
+ * 'wa\0\0': current_size Azure
42
*
43
* The user can override the table values via drive options, however
44
* even with an override we will still use current_size for images
45
@@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
46
*/
47
static bool vpc_ignore_current_size(VHDFooter *footer)
48
{
49
- return !!strncmp(footer->creator_app, "win ", 4) &&
50
- !!strncmp(footer->creator_app, "qem2", 4) &&
51
- !!strncmp(footer->creator_app, "d2v ", 4) &&
52
- !!strncmp(footer->creator_app, "CTXS", 4) &&
53
- !!memcmp(footer->creator_app, "tap", 4));
54
+ return !strncmp(footer->creator_app, "vpc ", 4) ||
55
+ !strncmp(footer->creator_app, "qemu", 4);
56
}
57
58
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
59
--
60
2.48.1
diff view generated by jsdifflib
1
From: Fabiano Rosas <farosas@linux.vnet.ibm.com>
1
From: Philippe Mathieu-Daudé <philmd@linaro.org>
2
2
3
The blkreplay driver is not a protocol so it should implement bdrv_open
3
Expose the method docstring in the header, and mention
4
instead of bdrv_file_open and not provide a protocol_name.
4
returned value must be free'd by caller.
5
5
6
Attempts to invoke this driver using protocol syntax
6
Reported-by: Fabiano Rosas <farosas@suse.de>
7
(i.e. blkreplay:<filename:options:...>) will now fail gracefully:
7
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
8
8
Message-ID: <20241111170333.43833-2-philmd@linaro.org>
9
$ qemu-img info blkreplay:foo
10
qemu-img: Could not open 'blkreplay:foo': Unknown protocol 'blkreplay'
11
12
Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
13
Reviewed-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
10
---
17
block/blkreplay.c | 3 +--
11
include/system/block-backend-io.h | 7 +++++++
18
1 file changed, 1 insertion(+), 2 deletions(-)
12
block/block-backend.c | 12 ++++++++----
13
2 files changed, 15 insertions(+), 4 deletions(-)
19
14
20
diff --git a/block/blkreplay.c b/block/blkreplay.c
15
diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h
21
index XXXXXXX..XXXXXXX 100755
16
index XXXXXXX..XXXXXXX 100644
22
--- a/block/blkreplay.c
17
--- a/include/system/block-backend-io.h
23
+++ b/block/blkreplay.c
18
+++ b/include/system/block-backend-io.h
24
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
19
@@ -XXX,XX +XXX,XX @@ void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow);
25
20
void blk_set_disable_request_queuing(BlockBackend *blk, bool disable);
26
static BlockDriver bdrv_blkreplay = {
21
bool blk_iostatus_is_enabled(const BlockBackend *blk);
27
.format_name = "blkreplay",
22
28
- .protocol_name = "blkreplay",
23
+/*
29
.instance_size = 0,
24
+ * Return the qdev ID, or if no ID is assigned the QOM path,
30
25
+ * of the block device attached to the BlockBackend.
31
- .bdrv_file_open = blkreplay_open,
26
+ *
32
+ .bdrv_open = blkreplay_open,
27
+ * The caller is responsible for releasing the value returned
33
.bdrv_close = blkreplay_close,
28
+ * with g_free() after use.
34
.bdrv_child_perm = bdrv_filter_default_perms,
29
+ */
35
.bdrv_getlength = blkreplay_getlength,
30
char *blk_get_attached_dev_id(BlockBackend *blk);
31
32
BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset,
33
diff --git a/block/block-backend.c b/block/block-backend.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/block/block-backend.c
36
+++ b/block/block-backend.c
37
@@ -XXX,XX +XXX,XX @@ DeviceState *blk_get_attached_dev(BlockBackend *blk)
38
return blk->dev;
39
}
40
41
+/*
42
+ * The caller is responsible for releasing the value returned
43
+ * with g_free() after use.
44
+ */
45
static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id)
46
{
47
DeviceState *dev = blk->dev;
48
@@ -XXX,XX +XXX,XX @@ static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id)
49
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
50
}
51
52
-/*
53
- * Return the qdev ID, or if no ID is assigned the QOM path, of the block
54
- * device attached to the BlockBackend.
55
- */
56
char *blk_get_attached_dev_id(BlockBackend *blk)
57
{
58
return blk_get_attached_dev_id_or_path(blk, true);
59
}
60
61
+/*
62
+ * The caller is responsible for releasing the value returned
63
+ * with g_free() after use.
64
+ */
65
static char *blk_get_attached_dev_path(BlockBackend *blk)
66
{
67
return blk_get_attached_dev_id_or_path(blk, false);
36
--
68
--
37
2.13.6
69
2.48.1
38
70
39
71
diff view generated by jsdifflib
1
From: Fabiano Rosas <farosas@linux.vnet.ibm.com>
1
From: Fabiano Rosas <farosas@suse.de>
2
2
3
Clarify that:
3
ASAN detected a leak when running the ahci-test
4
/ahci/io/dma/lba28/retry:
4
5
5
- for protocols the brdv_file_open function is used instead
6
Direct leak of 35 byte(s) in 1 object(s) allocated from:
6
of bdrv_open;
7
#0 in malloc
8
#1 in __vasprintf_internal
9
#2 in vasprintf
10
#3 in g_vasprintf
11
#4 in g_strdup_vprintf
12
#5 in g_strdup_printf
13
#6 in object_get_canonical_path ../qom/object.c:2096:19
14
#7 in blk_get_attached_dev_id_or_path ../block/block-backend.c:1033:12
15
#8 in blk_get_attached_dev_path ../block/block-backend.c:1047:12
16
#9 in send_qmp_error_event ../block/block-backend.c:2140:36
17
#10 in blk_error_action ../block/block-backend.c:2172:9
18
#11 in ide_handle_rw_error ../hw/ide/core.c:875:5
19
#12 in ide_dma_cb ../hw/ide/core.c:894:13
20
#13 in dma_complete ../system/dma-helpers.c:107:9
21
#14 in dma_blk_cb ../system/dma-helpers.c:129:9
22
#15 in blk_aio_complete ../block/block-backend.c:1552:9
23
#16 in blk_aio_write_entry ../block/block-backend.c:1619:5
24
#17 in coroutine_trampoline ../util/coroutine-ucontext.c:175:9
7
25
8
- when protocol_name is set, a driver should expect
26
Plug the leak by freeing the device path string.
9
to be given only a filename and no other options.
10
27
11
Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
28
Signed-off-by: Fabiano Rosas <farosas@suse.de>
29
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
30
Message-ID: <20241111145214.8261-1-farosas@suse.de>
31
[PMD: Use g_autofree]
32
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
33
Message-ID: <20241111170333.43833-3-philmd@linaro.org>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
35
---
14
include/block/block_int.h | 8 ++++++++
36
block/block-backend.c | 4 ++--
15
1 file changed, 8 insertions(+)
37
1 file changed, 2 insertions(+), 2 deletions(-)
16
38
17
diff --git a/include/block/block_int.h b/include/block/block_int.h
39
diff --git a/block/block-backend.c b/block/block-backend.c
18
index XXXXXXX..XXXXXXX 100644
40
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/block_int.h
41
--- a/block/block-backend.c
20
+++ b/include/block/block_int.h
42
+++ b/block/block-backend.c
21
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
43
@@ -XXX,XX +XXX,XX @@ static void send_qmp_error_event(BlockBackend *blk,
22
44
{
23
int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags,
45
IoOperationType optype;
24
Error **errp);
46
BlockDriverState *bs = blk_bs(blk);
25
+
47
+ g_autofree char *path = blk_get_attached_dev_path(blk);
26
+ /* Protocol drivers should implement this instead of bdrv_open */
48
27
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
49
optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
28
Error **errp);
50
- qapi_event_send_block_io_error(blk_name(blk),
29
void (*bdrv_close)(BlockDriverState *bs);
51
- blk_get_attached_dev_path(blk),
30
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
52
+ qapi_event_send_block_io_error(blk_name(blk), path,
31
*/
53
bs ? bdrv_get_node_name(bs) : NULL, optype,
32
int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs);
54
action, blk_iostatus_is_enabled(blk),
33
55
error == ENOSPC, strerror(error));
34
+ /*
35
+ * Drivers setting this field must be able to work with just a plain
36
+ * filename with '<protocol_name>:' as a prefix, and no other options.
37
+ * Options may be extracted from the filename by implementing
38
+ * bdrv_parse_filename.
39
+ */
40
const char *protocol_name;
41
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset,
42
PreallocMode prealloc, Error **errp);
43
--
56
--
44
2.13.6
57
2.48.1
45
58
46
59
diff view generated by jsdifflib
New patch
1
From: Peter Xu <peterx@redhat.com>
1
2
3
It's easier for either debugging plugin errors, or issue reports.
4
5
Signed-off-by: Peter Xu <peterx@redhat.com>
6
Message-ID: <20241212204801.1420528-2-peterx@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
scripts/qemu-gdb.py | 2 ++
11
1 file changed, 2 insertions(+)
12
13
diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py
14
index XXXXXXX..XXXXXXX 100644
15
--- a/scripts/qemu-gdb.py
16
+++ b/scripts/qemu-gdb.py
17
@@ -XXX,XX +XXX,XX @@ def __init__(self):
18
# Default to silently passing through SIGUSR1, because QEMU sends it
19
# to itself a lot.
20
gdb.execute('handle SIGUSR1 pass noprint nostop')
21
+# Always print full stack for python errors, easier to debug and report issues
22
+gdb.execute('set python print-stack full')
23
--
24
2.48.1
diff view generated by jsdifflib
New patch
1
From: Peter Xu <peterx@redhat.com>
1
2
3
There're a bunch of code trying to fetch fs_base in different ways. IIUC
4
the simplest way instead is "$fs_base". It also has the benefit that it'll
5
work for both live gdb session or coredumps.
6
7
Signed-off-by: Peter Xu <peterx@redhat.com>
8
Message-ID: <20241212204801.1420528-3-peterx@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
scripts/qemugdb/coroutine.py | 23 ++---------------------
13
1 file changed, 2 insertions(+), 21 deletions(-)
14
15
diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
16
index XXXXXXX..XXXXXXX 100644
17
--- a/scripts/qemugdb/coroutine.py
18
+++ b/scripts/qemugdb/coroutine.py
19
@@ -XXX,XX +XXX,XX @@
20
21
VOID_PTR = gdb.lookup_type('void').pointer()
22
23
-def get_fs_base():
24
- '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is
25
- pthread_self().'''
26
- # %rsp - 120 is scratch space according to the SystemV ABI
27
- old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
28
- gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True)
29
- fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
30
- gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True)
31
- return fs_base
32
-
33
def pthread_self():
34
- '''Fetch pthread_self() from the glibc start_thread function.'''
35
- f = gdb.newest_frame()
36
- while f.name() != 'start_thread':
37
- f = f.older()
38
- if f is None:
39
- return get_fs_base()
40
-
41
- try:
42
- return f.read_var("arg")
43
- except ValueError:
44
- return get_fs_base()
45
+ '''Fetch the base address of TLS.'''
46
+ return gdb.parse_and_eval("$fs_base")
47
48
def get_glibc_pointer_guard():
49
'''Fetch glibc pointer guard value'''
50
--
51
2.48.1
diff view generated by jsdifflib
New patch
1
From: Peter Xu <peterx@redhat.com>
1
2
3
Dumping coroutines don't yet work with coredumps. Let's make it work.
4
5
We still kept most of the old code because they can be either more
6
flexible, or prettier. Only add the fallbacks when they stop working.
7
8
Currently the raw unwind is pretty ugly, but it works, like this:
9
10
(gdb) qemu bt
11
#0 process_incoming_migration_co (opaque=0x0) at ../migration/migration.c:788
12
#1 0x000055ae6c0dc4d9 in coroutine_trampoline (i0=-1711718576, i1=21934) at ../util/coroutine-ucontext.c:175
13
#2 0x00007f9f59d72f40 in ??? () at /lib64/libc.so.6
14
#3 0x00007ffd549214a0 in ??? ()
15
#4 0x0000000000000000 in ??? ()
16
Coroutine at 0x7f9f4c57c748:
17
#0 0x55ae6c0dc9a8 in qemu_coroutine_switch<+120> () at ../util/coroutine-ucontext.c:321
18
#1 0x55ae6c0da2f8 in qemu_aio_coroutine_enter<+356> () at ../util/qemu-coroutine.c:293
19
#2 0x55ae6c0da3f1 in qemu_coroutine_enter<+34> () at ../util/qemu-coroutine.c:316
20
#3 0x55ae6baf775e in migration_incoming_process<+43> () at ../migration/migration.c:876
21
#4 0x55ae6baf7ab4 in migration_ioc_process_incoming<+490> () at ../migration/migration.c:1008
22
#5 0x55ae6bae9ae7 in migration_channel_process_incoming<+145> () at ../migration/channel.c:45
23
#6 0x55ae6bb18e35 in socket_accept_incoming_migration<+118> () at ../migration/socket.c:132
24
#7 0x55ae6be939ef in qio_net_listener_channel_func<+131> () at ../io/net-listener.c:54
25
#8 0x55ae6be8ce1a in qio_channel_fd_source_dispatch<+78> () at ../io/channel-watch.c:84
26
#9 0x7f9f5b26728c in g_main_context_dispatch_unlocked.lto_priv<+315> ()
27
#10 0x7f9f5b267555 in g_main_context_dispatch<+36> ()
28
#11 0x55ae6c0d91a7 in glib_pollfds_poll<+90> () at ../util/main-loop.c:287
29
#12 0x55ae6c0d9235 in os_host_main_loop_wait<+128> () at ../util/main-loop.c:310
30
#13 0x55ae6c0d9364 in main_loop_wait<+203> () at ../util/main-loop.c:589
31
#14 0x55ae6bac212a in qemu_main_loop<+41> () at ../system/runstate.c:835
32
#15 0x55ae6bfdf522 in qemu_default_main<+19> () at ../system/main.c:37
33
#16 0x55ae6bfdf55f in main<+40> () at ../system/main.c:48
34
#17 0x7f9f59d42248 in __libc_start_call_main<+119> ()
35
#18 0x7f9f59d4230b in __libc_start_main_impl<+138> ()
36
37
Signed-off-by: Peter Xu <peterx@redhat.com>
38
Message-ID: <20241212204801.1420528-4-peterx@redhat.com>
39
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
40
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
41
---
42
scripts/qemugdb/coroutine.py | 79 +++++++++++++++++++++++++++++++++---
43
1 file changed, 73 insertions(+), 6 deletions(-)
44
45
diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
46
index XXXXXXX..XXXXXXX 100644
47
--- a/scripts/qemugdb/coroutine.py
48
+++ b/scripts/qemugdb/coroutine.py
49
@@ -XXX,XX +XXX,XX @@ def get_jmpbuf_regs(jmpbuf):
50
'r15': jmpbuf[JB_R15],
51
'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) }
52
53
-def bt_jmpbuf(jmpbuf):
54
- '''Backtrace a jmpbuf'''
55
- regs = get_jmpbuf_regs(jmpbuf)
56
+def symbol_lookup(addr):
57
+ # Example: "__clone3 + 44 in section .text of /lib64/libc.so.6"
58
+ result = gdb.execute(f"info symbol {hex(addr)}", to_string=True).strip()
59
+ try:
60
+ if "+" in result:
61
+ (func, result) = result.split(" + ")
62
+ (offset, result) = result.split(" in ")
63
+ else:
64
+ offset = "0"
65
+ (func, result) = result.split(" in ")
66
+ func_str = f"{func}<+{offset}> ()"
67
+ except:
68
+ return f"??? ({result})"
69
+
70
+ # Example: Line 321 of "../util/coroutine-ucontext.c" starts at address
71
+ # 0x55cf3894d993 <qemu_coroutine_switch+99> and ends at 0x55cf3894d9ab
72
+ # <qemu_coroutine_switch+123>.
73
+ result = gdb.execute(f"info line *{hex(addr)}", to_string=True).strip()
74
+ if not result.startswith("Line "):
75
+ return func_str
76
+ result = result[5:]
77
+
78
+ try:
79
+ result = result.split(" starts ")[0]
80
+ (line, path) = result.split(" of ")
81
+ path = path.replace("\"", "")
82
+ except:
83
+ return func_str
84
+
85
+ return f"{func_str} at {path}:{line}"
86
+
87
+def dump_backtrace(regs):
88
+ '''
89
+ Backtrace dump with raw registers, mimic GDB command 'bt'.
90
+ '''
91
+ # Here only rbp and rip that matter..
92
+ rbp = regs['rbp']
93
+ rip = regs['rip']
94
+ i = 0
95
+
96
+ while rbp:
97
+ # For all return addresses on stack, we want to look up symbol/line
98
+ # on the CALL command, because the return address is the next
99
+ # instruction instead of the CALL. Here -1 would work for any
100
+ # sized CALL instruction.
101
+ print(f"#{i} {hex(rip)} in {symbol_lookup(rip if i == 0 else rip-1)}")
102
+ rip = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)} + 8)")
103
+ rbp = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)})")
104
+ i += 1
105
+
106
+def dump_backtrace_live(regs):
107
+ '''
108
+ Backtrace dump with gdb's 'bt' command, only usable in a live session.
109
+ '''
110
old = dict()
111
112
# remember current stack frame and select the topmost
113
@@ -XXX,XX +XXX,XX @@ def bt_jmpbuf(jmpbuf):
114
115
selected_frame.select()
116
117
+def bt_jmpbuf(jmpbuf):
118
+ '''Backtrace a jmpbuf'''
119
+ regs = get_jmpbuf_regs(jmpbuf)
120
+ try:
121
+ # This reuses gdb's "bt" command, which can be slightly prettier
122
+ # but only works with live sessions.
123
+ dump_backtrace_live(regs)
124
+ except:
125
+ # If above doesn't work, fallback to poor man's unwind
126
+ dump_backtrace(regs)
127
+
128
def co_cast(co):
129
return co.cast(gdb.lookup_type('CoroutineUContext').pointer())
130
131
@@ -XXX,XX +XXX,XX @@ def invoke(self, arg, from_tty):
132
133
gdb.execute("bt")
134
135
- if gdb.parse_and_eval("qemu_in_coroutine()") == False:
136
- return
137
+ try:
138
+ # This only works with a live session
139
+ co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
140
+ except:
141
+ # Fallback to use hard-coded ucontext vars if it's coredump
142
+ co_ptr = gdb.parse_and_eval("co_tls_current")
143
144
- co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
145
+ if co_ptr == False:
146
+ return
147
148
while True:
149
co = co_cast(co_ptr)
150
--
151
2.48.1
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Peter Krempa <pkrempa@redhat.com>
2
2
3
Testing on ext4, most 'quick' qcow2 tests took less than 5 seconds,
3
Commit 7452162adec25c10 introduced 'qom-path' argument to BLOCK_IO_ERROR
4
but 163 took more than 20. Let's remove it from the quick set.
4
event but when the event is instantiated in 'send_qmp_error_event()' the
5
arguments for 'device' and 'qom_path' in
6
qapi_event_send_block_io_error() were reversed :
5
7
6
Signed-off-by: Eric Blake <eblake@redhat.com>
8
Generated code for sending event:
9
10
void qapi_event_send_block_io_error(const char *qom_path,
11
const char *device,
12
const char *node_name,
13
IoOperationType operation,
14
[...]
15
16
Call inside send_qmp_error_event():
17
18
qapi_event_send_block_io_error(blk_name(blk),
19
blk_get_attached_dev_path(blk),
20
bs ? bdrv_get_node_name(bs) : NULL, optype,
21
[...]
22
23
This results into reporting the QOM path as the device alias and vice
24
versa which in turn breaks libvirt, which expects the device alias being
25
either a valid alias or empty (which would make libvirt do the lookup by
26
node-name instead).
27
28
Cc: qemu-stable@nongnu.org
29
Fixes: 7452162adec2 ("qapi: add qom-path to BLOCK_IO_ERROR event")
30
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
31
Message-ID: <09728d784888b38d7a8f09ee5e9e9c542c875e1e.1737973614.git.pkrempa@redhat.com>
32
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
33
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
35
---
9
tests/qemu-iotests/group | 2 +-
36
block/block-backend.c | 2 +-
10
1 file changed, 1 insertion(+), 1 deletion(-)
37
1 file changed, 1 insertion(+), 1 deletion(-)
11
38
12
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
39
diff --git a/block/block-backend.c b/block/block-backend.c
13
index XXXXXXX..XXXXXXX 100644
40
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/qemu-iotests/group
41
--- a/block/block-backend.c
15
+++ b/tests/qemu-iotests/group
42
+++ b/block/block-backend.c
16
@@ -XXX,XX +XXX,XX @@
43
@@ -XXX,XX +XXX,XX @@ static void send_qmp_error_event(BlockBackend *blk,
17
159 rw auto quick
44
g_autofree char *path = blk_get_attached_dev_path(blk);
18
160 rw auto quick
45
19
162 auto quick
46
optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
20
-163 rw auto quick
47
- qapi_event_send_block_io_error(blk_name(blk), path,
21
+163 rw auto
48
+ qapi_event_send_block_io_error(path, blk_name(blk),
22
165 rw auto quick
49
bs ? bdrv_get_node_name(bs) : NULL, optype,
23
169 rw auto quick
50
action, blk_iostatus_is_enabled(blk),
24
170 rw auto quick
51
error == ENOSPC, strerror(error));
25
--
52
--
26
2.13.6
53
2.48.1
27
54
28
55
diff view generated by jsdifflib
New patch
1
This allows querying from QMP (and also HMP) whether an image is
2
currently active or inactive (in the sense of BDRV_O_INACTIVE).
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Acked-by: Fabiano Rosas <farosas@suse.de>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-ID: <20250204211407.381505-2-kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
qapi/block-core.json | 6 +++++-
12
include/block/block-global-state.h | 3 +++
13
block.c | 4 ++++
14
block/monitor/block-hmp-cmds.c | 5 +++--
15
block/qapi.c | 1 +
16
tests/qemu-iotests/184.out | 2 ++
17
tests/qemu-iotests/191.out | 16 ++++++++++++++++
18
tests/qemu-iotests/273.out | 5 +++++
19
8 files changed, 39 insertions(+), 3 deletions(-)
20
21
diff --git a/qapi/block-core.json b/qapi/block-core.json
22
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/block-core.json
24
+++ b/qapi/block-core.json
25
@@ -XXX,XX +XXX,XX @@
26
# @backing_file_depth: number of files in the backing file chain
27
# (since: 1.2)
28
#
29
+# @active: true if the backend is active; typical cases for inactive backends
30
+# are on the migration source instance after migration completes and on the
31
+# destination before it completes. (since: 10.0)
32
+#
33
# @encrypted: true if the backing device is encrypted
34
#
35
# @detect_zeroes: detect and optimize zero writes (Since 2.1)
36
@@ -XXX,XX +XXX,XX @@
37
{ 'struct': 'BlockDeviceInfo',
38
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
39
'*backing_file': 'str', 'backing_file_depth': 'int',
40
- 'encrypted': 'bool',
41
+ 'active': 'bool', 'encrypted': 'bool',
42
'detect_zeroes': 'BlockdevDetectZeroesOptions',
43
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
44
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
45
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
46
index XXXXXXX..XXXXXXX 100644
47
--- a/include/block/block-global-state.h
48
+++ b/include/block/block-global-state.h
49
@@ -XXX,XX +XXX,XX @@ BlockDriverState * GRAPH_RDLOCK
50
check_to_replace_node(BlockDriverState *parent_bs, const char *node_name,
51
Error **errp);
52
53
+
54
+bool GRAPH_RDLOCK bdrv_is_inactive(BlockDriverState *bs);
55
+
56
int no_coroutine_fn GRAPH_RDLOCK
57
bdrv_activate(BlockDriverState *bs, Error **errp);
58
59
diff --git a/block.c b/block.c
60
index XXXXXXX..XXXXXXX 100644
61
--- a/block.c
62
+++ b/block.c
63
@@ -XXX,XX +XXX,XX @@ void bdrv_init_with_whitelist(void)
64
bdrv_init();
65
}
66
67
+bool bdrv_is_inactive(BlockDriverState *bs) {
68
+ return bs->open_flags & BDRV_O_INACTIVE;
69
+}
70
+
71
int bdrv_activate(BlockDriverState *bs, Error **errp)
72
{
73
BdrvChild *child, *parent;
74
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/block/monitor/block-hmp-cmds.c
77
+++ b/block/monitor/block-hmp-cmds.c
78
@@ -XXX,XX +XXX,XX @@ static void print_block_info(Monitor *mon, BlockInfo *info,
79
}
80
81
if (inserted) {
82
- monitor_printf(mon, ": %s (%s%s%s)\n",
83
+ monitor_printf(mon, ": %s (%s%s%s%s)\n",
84
inserted->file,
85
inserted->drv,
86
inserted->ro ? ", read-only" : "",
87
- inserted->encrypted ? ", encrypted" : "");
88
+ inserted->encrypted ? ", encrypted" : "",
89
+ inserted->active ? "" : ", inactive");
90
} else {
91
monitor_printf(mon, ": [not inserted]\n");
92
}
93
diff --git a/block/qapi.c b/block/qapi.c
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/qapi.c
96
+++ b/block/qapi.c
97
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
98
info->file = g_strdup(bs->filename);
99
info->ro = bdrv_is_read_only(bs);
100
info->drv = g_strdup(bs->drv->format_name);
101
+ info->active = !bdrv_is_inactive(bs);
102
info->encrypted = bs->encrypted;
103
104
info->cache = g_new(BlockdevCacheInfo, 1);
105
diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out
106
index XXXXXXX..XXXXXXX 100644
107
--- a/tests/qemu-iotests/184.out
108
+++ b/tests/qemu-iotests/184.out
109
@@ -XXX,XX +XXX,XX @@ Testing:
110
{
111
"iops_rd": 0,
112
"detect_zeroes": "off",
113
+ "active": true,
114
"image": {
115
"backing-image": {
116
"virtual-size": 1073741824,
117
@@ -XXX,XX +XXX,XX @@ Testing:
118
{
119
"iops_rd": 0,
120
"detect_zeroes": "off",
121
+ "active": true,
122
"image": {
123
"virtual-size": 1073741824,
124
"filename": "null-co://",
125
diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out
126
index XXXXXXX..XXXXXXX 100644
127
--- a/tests/qemu-iotests/191.out
128
+++ b/tests/qemu-iotests/191.out
129
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
130
{
131
"iops_rd": 0,
132
"detect_zeroes": "off",
133
+ "active": true,
134
"image": {
135
"backing-image": {
136
"virtual-size": 67108864,
137
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
138
{
139
"iops_rd": 0,
140
"detect_zeroes": "off",
141
+ "active": true,
142
"image": {
143
"virtual-size": 197120,
144
"filename": "TEST_DIR/t.IMGFMT.ovl2",
145
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
146
{
147
"iops_rd": 0,
148
"detect_zeroes": "off",
149
+ "active": true,
150
"image": {
151
"backing-image": {
152
"virtual-size": 67108864,
153
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
154
{
155
"iops_rd": 0,
156
"detect_zeroes": "off",
157
+ "active": true,
158
"image": {
159
"virtual-size": 197120,
160
"filename": "TEST_DIR/t.IMGFMT",
161
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
162
{
163
"iops_rd": 0,
164
"detect_zeroes": "off",
165
+ "active": true,
166
"image": {
167
"backing-image": {
168
"virtual-size": 67108864,
169
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
170
{
171
"iops_rd": 0,
172
"detect_zeroes": "off",
173
+ "active": true,
174
"image": {
175
"virtual-size": 393216,
176
"filename": "TEST_DIR/t.IMGFMT.mid",
177
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
178
{
179
"iops_rd": 0,
180
"detect_zeroes": "off",
181
+ "active": true,
182
"image": {
183
"virtual-size": 67108864,
184
"filename": "TEST_DIR/t.IMGFMT.base",
185
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
186
{
187
"iops_rd": 0,
188
"detect_zeroes": "off",
189
+ "active": true,
190
"image": {
191
"virtual-size": 393216,
192
"filename": "TEST_DIR/t.IMGFMT.base",
193
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
194
{
195
"iops_rd": 0,
196
"detect_zeroes": "off",
197
+ "active": true,
198
"image": {
199
"backing-image": {
200
"virtual-size": 67108864,
201
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
202
{
203
"iops_rd": 0,
204
"detect_zeroes": "off",
205
+ "active": true,
206
"image": {
207
"virtual-size": 197120,
208
"filename": "TEST_DIR/t.IMGFMT.ovl2",
209
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
210
{
211
"iops_rd": 0,
212
"detect_zeroes": "off",
213
+ "active": true,
214
"image": {
215
"backing-image": {
216
"backing-image": {
217
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
218
{
219
"iops_rd": 0,
220
"detect_zeroes": "off",
221
+ "active": true,
222
"image": {
223
"virtual-size": 197120,
224
"filename": "TEST_DIR/t.IMGFMT.ovl3",
225
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
226
{
227
"iops_rd": 0,
228
"detect_zeroes": "off",
229
+ "active": true,
230
"image": {
231
"virtual-size": 67108864,
232
"filename": "TEST_DIR/t.IMGFMT.base",
233
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
234
{
235
"iops_rd": 0,
236
"detect_zeroes": "off",
237
+ "active": true,
238
"image": {
239
"virtual-size": 393216,
240
"filename": "TEST_DIR/t.IMGFMT.base",
241
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
242
{
243
"iops_rd": 0,
244
"detect_zeroes": "off",
245
+ "active": true,
246
"image": {
247
"backing-image": {
248
"virtual-size": 67108864,
249
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
250
{
251
"iops_rd": 0,
252
"detect_zeroes": "off",
253
+ "active": true,
254
"image": {
255
"virtual-size": 197120,
256
"filename": "TEST_DIR/t.IMGFMT",
257
diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out
258
index XXXXXXX..XXXXXXX 100644
259
--- a/tests/qemu-iotests/273.out
260
+++ b/tests/qemu-iotests/273.out
261
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
262
{
263
"iops_rd": 0,
264
"detect_zeroes": "off",
265
+ "active": true,
266
"image": {
267
"backing-image": {
268
"backing-image": {
269
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
270
{
271
"iops_rd": 0,
272
"detect_zeroes": "off",
273
+ "active": true,
274
"image": {
275
"virtual-size": 197120,
276
"filename": "TEST_DIR/t.IMGFMT",
277
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
278
{
279
"iops_rd": 0,
280
"detect_zeroes": "off",
281
+ "active": true,
282
"image": {
283
"backing-image": {
284
"virtual-size": 197120,
285
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
286
{
287
"iops_rd": 0,
288
"detect_zeroes": "off",
289
+ "active": true,
290
"image": {
291
"virtual-size": 197120,
292
"filename": "TEST_DIR/t.IMGFMT.mid",
293
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
294
{
295
"iops_rd": 0,
296
"detect_zeroes": "off",
297
+ "active": true,
298
"image": {
299
"virtual-size": 197120,
300
"filename": "TEST_DIR/t.IMGFMT.base",
301
--
302
2.48.1
diff view generated by jsdifflib
New patch
1
What we wanted to catch with the assertion is cases where the recursion
2
finds that a child was inactive before its parent. This should never
3
happen. But if the user tries to inactivate an image that is already
4
inactive, that's harmless and we don't want to fail the assertion.
1
5
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Acked-by: Fabiano Rosas <farosas@suse.de>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Message-ID: <20250204211407.381505-3-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block.c | 16 ++++++++++++----
14
1 file changed, 12 insertions(+), 4 deletions(-)
15
16
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
19
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ bdrv_has_bds_parent(BlockDriverState *bs, bool only_active)
21
return false;
22
}
23
24
-static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs)
25
+static int GRAPH_RDLOCK
26
+bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
27
{
28
BdrvChild *child, *parent;
29
int ret;
30
@@ -XXX,XX +XXX,XX @@ static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs)
31
return 0;
32
}
33
34
- assert(!(bs->open_flags & BDRV_O_INACTIVE));
35
+ /*
36
+ * Inactivating an already inactive node on user request is harmless, but if
37
+ * a child is already inactive before its parent, that's bad.
38
+ */
39
+ if (bs->open_flags & BDRV_O_INACTIVE) {
40
+ assert(top_level);
41
+ return 0;
42
+ }
43
44
/* Inactivate this node */
45
if (bs->drv->bdrv_inactivate) {
46
@@ -XXX,XX +XXX,XX @@ static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs)
47
48
/* Recursively inactivate children */
49
QLIST_FOREACH(child, &bs->children, next) {
50
- ret = bdrv_inactivate_recurse(child->bs);
51
+ ret = bdrv_inactivate_recurse(child->bs, false);
52
if (ret < 0) {
53
return ret;
54
}
55
@@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void)
56
if (bdrv_has_bds_parent(bs, false)) {
57
continue;
58
}
59
- ret = bdrv_inactivate_recurse(bs);
60
+ ret = bdrv_inactivate_recurse(bs, true);
61
if (ret < 0) {
62
bdrv_next_cleanup(&it);
63
break;
64
--
65
2.48.1
diff view generated by jsdifflib
1
It's unclear what the real maximum is, but we use an uint32_t to store
1
Putting an active block node on top of an inactive one is strictly
2
the log size in vhdx_co_create(), so we should check that the given
2
speaking an invalid configuration and the next patch will turn it into a
3
value fits in 32 bits.
3
hard error.
4
5
However, taking a snapshot while disk images are inactive after
6
completing migration has an important use case: After migrating to a
7
file, taking an external snapshot is what is needed to take a full VM
8
snapshot.
9
10
In order for this to keep working after the later patches, change
11
creating a snapshot such that it automatically inactivates an overlay
12
that is added on top of an already inactive node.
4
13
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Acked-by: Fabiano Rosas <farosas@suse.de>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Jeff Cody <jcody@redhat.com>
17
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
18
Message-ID: <20250204211407.381505-4-kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
20
---
9
block/vhdx.c | 4 ++++
21
blockdev.c | 16 ++++++++++++++++
10
1 file changed, 4 insertions(+)
22
1 file changed, 16 insertions(+)
11
23
12
diff --git a/block/vhdx.c b/block/vhdx.c
24
diff --git a/blockdev.c b/blockdev.c
13
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
14
--- a/block/vhdx.c
26
--- a/blockdev.c
15
+++ b/block/vhdx.c
27
+++ b/blockdev.c
16
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
28
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action,
17
if (!vhdx_opts->has_log_size) {
29
return;
18
log_size = DEFAULT_LOG_SIZE;
30
}
19
} else {
31
20
+ if (vhdx_opts->log_size > UINT32_MAX) {
32
+ /*
21
+ error_setg(errp, "Log size must be smaller than 4 GB");
33
+ * Older QEMU versions have allowed adding an active parent node to an
22
+ return -EINVAL;
34
+ * inactive child node. This is unsafe in the general case, but there is an
35
+ * important use case, which is taking a VM snapshot with migration to file
36
+ * and then adding an external snapshot while the VM is still stopped and
37
+ * images are inactive. Requiring the user to explicitly create the overlay
38
+ * as inactive would break compatibility, so just do it automatically here
39
+ * to keep this working.
40
+ */
41
+ if (bdrv_is_inactive(state->old_bs) && !bdrv_is_inactive(state->new_bs)) {
42
+ ret = bdrv_inactivate(state->new_bs, errp);
43
+ if (ret < 0) {
44
+ return;
23
+ }
45
+ }
24
log_size = vhdx_opts->log_size;
46
+ }
25
}
47
+
26
if (log_size < MiB || (log_size % MiB) != 0) {
48
ret = bdrv_append(state->new_bs, state->old_bs, errp);
49
if (ret < 0) {
50
return;
27
--
51
--
28
2.13.6
52
2.48.1
29
30
diff view generated by jsdifflib
1
error_setg_errno() is meant for cases where we got an errno from the OS
1
Block devices have an individual active state, a single global flag
2
that can add useful extra information to an error message. It's
2
can't cover this correctly. This becomes more important as we allow
3
pointless if we pass a constant errno, these cases should use plain
3
users to manually manage which nodes are active or inactive.
4
error_setg().
4
5
Now that it's allowed to call bdrv_inactivate_all() even when some
6
nodes are already inactive, we can remove the flag and just
7
unconditionally call bdrv_inactivate_all() and, more importantly,
8
bdrv_activate_all() before we make use of the nodes.
5
9
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Acked-by: Fabiano Rosas <farosas@suse.de>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Jeff Cody <jcody@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Message-ID: <20250204211407.381505-5-kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
16
---
10
block/vhdx.c | 9 ++++-----
17
migration/migration.h | 3 ---
11
1 file changed, 4 insertions(+), 5 deletions(-)
18
migration/block-active.c | 46 ----------------------------------------
19
migration/migration.c | 8 -------
20
3 files changed, 57 deletions(-)
12
21
13
diff --git a/block/vhdx.c b/block/vhdx.c
22
diff --git a/migration/migration.h b/migration/migration.h
14
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
15
--- a/block/vhdx.c
24
--- a/migration/migration.h
16
+++ b/block/vhdx.c
25
+++ b/migration/migration.h
17
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
26
@@ -XXX,XX +XXX,XX @@ void migration_bitmap_sync_precopy(bool last_stage);
18
/* Validate options and set default values */
27
void dirty_bitmap_mig_init(void);
19
image_size = vhdx_opts->size;
28
bool should_send_vmdesc(void);
20
if (image_size > VHDX_MAX_IMAGE_SIZE) {
29
21
- error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
30
-/* migration/block-active.c */
22
+ error_setg(errp, "Image size too large; max of 64TB");
31
-void migration_block_active_setup(bool active);
23
return -EINVAL;
32
-
33
#endif
34
diff --git a/migration/block-active.c b/migration/block-active.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/migration/block-active.c
37
+++ b/migration/block-active.c
38
@@ -XXX,XX +XXX,XX @@
39
#include "qemu/error-report.h"
40
#include "trace.h"
41
42
-/*
43
- * Migration-only cache to remember the block layer activation status.
44
- * Protected by BQL.
45
- *
46
- * We need this because..
47
- *
48
- * - Migration can fail after block devices are invalidated (during
49
- * switchover phase). When that happens, we need to be able to recover
50
- * the block drive status by re-activating them.
51
- *
52
- * - Currently bdrv_inactivate_all() is not safe to be invoked on top of
53
- * invalidated drives (even if bdrv_activate_all() is actually safe to be
54
- * called any time!). It means remembering this could help migration to
55
- * make sure it won't invalidate twice in a row, crashing QEMU. It can
56
- * happen when we migrate a PAUSED VM from host1 to host2, then migrate
57
- * again to host3 without starting it. TODO: a cleaner solution is to
58
- * allow safe invoke of bdrv_inactivate_all() at anytime, like
59
- * bdrv_activate_all().
60
- *
61
- * For freshly started QEMU, the flag is initialized to TRUE reflecting the
62
- * scenario where QEMU owns block device ownerships.
63
- *
64
- * For incoming QEMU taking a migration stream, the flag is initialized to
65
- * FALSE reflecting that the incoming side doesn't own the block devices,
66
- * not until switchover happens.
67
- */
68
-static bool migration_block_active;
69
-
70
-/* Setup the disk activation status */
71
-void migration_block_active_setup(bool active)
72
-{
73
- migration_block_active = active;
74
-}
75
-
76
bool migration_block_activate(Error **errp)
77
{
78
ERRP_GUARD();
79
80
assert(bql_locked());
81
82
- if (migration_block_active) {
83
- trace_migration_block_activation("active-skipped");
84
- return true;
85
- }
86
-
87
trace_migration_block_activation("active");
88
89
bdrv_activate_all(errp);
90
@@ -XXX,XX +XXX,XX @@ bool migration_block_activate(Error **errp)
91
return false;
24
}
92
}
25
93
26
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
94
- migration_block_active = true;
27
log_size = vhdx_opts->log_size;
95
return true;
96
}
97
98
@@ -XXX,XX +XXX,XX @@ bool migration_block_inactivate(void)
99
100
assert(bql_locked());
101
102
- if (!migration_block_active) {
103
- trace_migration_block_activation("inactive-skipped");
104
- return true;
105
- }
106
-
107
trace_migration_block_activation("inactive");
108
109
ret = bdrv_inactivate_all();
110
@@ -XXX,XX +XXX,XX @@ bool migration_block_inactivate(void)
111
return false;
28
}
112
}
29
if (log_size < MiB || (log_size % MiB) != 0) {
113
30
- error_setg_errno(errp, EINVAL, "Log size must be a multiple of 1 MB");
114
- migration_block_active = false;
31
+ error_setg(errp, "Log size must be a multiple of 1 MB");
115
return true;
32
return -EINVAL;
116
}
117
diff --git a/migration/migration.c b/migration/migration.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/migration/migration.c
120
+++ b/migration/migration.c
121
@@ -XXX,XX +XXX,XX @@ void qmp_migrate_incoming(const char *uri, bool has_channels,
122
return;
33
}
123
}
34
124
35
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
125
- /*
36
}
126
- * Newly setup incoming QEMU. Mark the block active state to reflect
37
127
- * that the src currently owns the disks.
38
if (block_size < MiB || (block_size % MiB) != 0) {
128
- */
39
- error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB");
129
- migration_block_active_setup(false);
40
+ error_setg(errp, "Block size must be a multiple of 1 MB");
130
-
41
return -EINVAL;
131
once = false;
42
}
132
}
43
if (!is_power_of_2(block_size)) {
133
44
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
134
@@ -XXX,XX +XXX,XX @@ static void migration_instance_init(Object *obj)
45
return -EINVAL;
135
ms->state = MIGRATION_STATUS_NONE;
46
}
136
ms->mbps = -1;
47
if (block_size > VHDX_BLOCK_SIZE_MAX) {
137
ms->pages_per_second = -1;
48
- error_setg_errno(errp, EINVAL, "Block size must not exceed %d",
138
- /* Freshly started QEMU owns all the block devices */
49
- VHDX_BLOCK_SIZE_MAX);
139
- migration_block_active_setup(true);
50
+ error_setg(errp, "Block size must not exceed %d", VHDX_BLOCK_SIZE_MAX);
140
qemu_sem_init(&ms->pause_sem, 0);
51
return -EINVAL;
141
qemu_mutex_init(&ms->error_mutex);
52
}
53
142
54
--
143
--
55
2.13.6
144
2.48.1
56
57
diff view generated by jsdifflib
1
Images with a non-power-of-two block size are invalid and cannot be
1
An active node makes unrestricted use of its children and would possibly
2
opened. Reject such block sizes when creating an image.
2
run into assertion failures when it operates on an inactive child node.
3
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Acked-by: Fabiano Rosas <farosas@suse.de>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Jeff Cody <jcody@redhat.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-ID: <20250204211407.381505-6-kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
10
---
8
block/vhdx.c | 4 ++++
11
block.c | 5 +++++
9
1 file changed, 4 insertions(+)
12
1 file changed, 5 insertions(+)
10
13
11
diff --git a/block/vhdx.c b/block/vhdx.c
14
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/block/vhdx.c
16
--- a/block.c
14
+++ b/block/vhdx.c
17
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
18
@@ -XXX,XX +XXX,XX @@ bdrv_attach_child_noperm(BlockDriverState *parent_bs,
16
error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB");
19
child_bs->node_name, child_name, parent_bs->node_name);
17
return -EINVAL;
20
return NULL;
18
}
21
}
19
+ if (!is_power_of_2(block_size)) {
22
+ if (bdrv_is_inactive(child_bs) && !bdrv_is_inactive(parent_bs)) {
20
+ error_setg(errp, "Block size must be a power of two");
23
+ error_setg(errp, "Inactive '%s' can't be a %s child of active '%s'",
21
+ return -EINVAL;
24
+ child_bs->node_name, child_name, parent_bs->node_name);
25
+ return NULL;
22
+ }
26
+ }
23
if (block_size > VHDX_BLOCK_SIZE_MAX) {
27
24
error_setg_errno(errp, EINVAL, "Block size must not exceed %d",
28
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
25
VHDX_BLOCK_SIZE_MAX);
29
bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
26
--
30
--
27
2.13.6
31
2.48.1
28
29
diff view generated by jsdifflib
1
From: Fabiano Rosas <farosas@linux.vnet.ibm.com>
1
In order for block_resize to fail gracefully on an inactive node instead
2
of crashing with an assertion failure in bdrv_co_write_req_prepare()
3
(called from bdrv_co_truncate()), we need to check for inactive nodes
4
also when they are attached as a root node and make sure that
5
BLK_PERM_RESIZE isn't among the permissions allowed for inactive nodes.
6
To this effect, don't enumerate the permissions that are incompatible
7
with inactive nodes any more, but allow only BLK_PERM_CONSISTENT_READ
8
for them.
2
9
3
The quorum driver is not a protocol so it should implement bdrv_open
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
instead of bdrv_file_open and not provide a protocol_name.
11
Acked-by: Fabiano Rosas <farosas@suse.de>
5
12
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Attempts to invoke this driver using protocol syntax
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
(i.e. quorum:<filename:options:...>) will now fail gracefully:
14
Message-ID: <20250204211407.381505-7-kwolf@redhat.com>
8
9
$ qemu-img info quorum:foo
10
qemu-img: Could not open 'quorum:foo': Unknown protocol 'quorum'
11
12
Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: Alberto Garcia <berto@igalia.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
16
---
17
block/quorum.c | 3 +--
17
block.c | 7 +++++++
18
1 file changed, 1 insertion(+), 2 deletions(-)
18
block/block-backend.c | 2 +-
19
2 files changed, 8 insertions(+), 1 deletion(-)
19
20
20
diff --git a/block/quorum.c b/block/quorum.c
21
diff --git a/block.c b/block.c
21
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
22
--- a/block/quorum.c
23
--- a/block.c
23
+++ b/block/quorum.c
24
+++ b/block.c
24
@@ -XXX,XX +XXX,XX @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
25
@@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs,
25
26
assert(child_class->get_parent_desc);
26
static BlockDriver bdrv_quorum = {
27
GLOBAL_STATE_CODE();
27
.format_name = "quorum",
28
28
- .protocol_name = "quorum",
29
+ if (bdrv_is_inactive(child_bs) && (perm & ~BLK_PERM_CONSISTENT_READ)) {
29
30
+ g_autofree char *perm_names = bdrv_perm_names(perm);
30
.instance_size = sizeof(BDRVQuorumState),
31
+ error_setg(errp, "Permission '%s' unavailable on inactive node",
31
32
+ perm_names);
32
- .bdrv_file_open = quorum_open,
33
+ return NULL;
33
+ .bdrv_open = quorum_open,
34
+ }
34
.bdrv_close = quorum_close,
35
+
35
.bdrv_refresh_filename = quorum_refresh_filename,
36
new_child = g_new(BdrvChild, 1);
37
*new_child = (BdrvChild) {
38
.bs = NULL,
39
diff --git a/block/block-backend.c b/block/block-backend.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/block/block-backend.c
42
+++ b/block/block-backend.c
43
@@ -XXX,XX +XXX,XX @@ static bool blk_can_inactivate(BlockBackend *blk)
44
* guest. For block job BBs that satisfy this, we can just allow
45
* it. This is the case for mirror job source, which is required
46
* by libvirt non-shared block migration. */
47
- if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) {
48
+ if (!(blk->perm & ~BLK_PERM_CONSISTENT_READ)) {
49
return true;
50
}
36
51
37
--
52
--
38
2.13.6
53
2.48.1
39
40
diff view generated by jsdifflib
1
What static=on really does is what we call metadata preallocation for
1
In QEMU, nodes are automatically created inactive while expecting an
2
other block drivers. While we can still change the QMP interface, make
2
incoming migration (i.e. RUN_STATE_INMIGRATE). In qemu-storage-daemon,
3
it more consistent by using 'preallocation' for VDI, too.
3
the notion of runstates doesn't exist. It also wouldn't necessarily make
4
sense to introduce it because a single daemon can serve multiple VMs
5
that can be in different states.
4
6
5
This doesn't implement any new functionality, so the only supported
7
Therefore, allow the user to explicitly open images as inactive with a
6
preallocation modes are 'off' and 'metadata' for now.
8
new option. The default is as before: Nodes are usually active, except
9
when created during RUN_STATE_INMIGRATE.
7
10
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Acked-by: Fabiano Rosas <farosas@suse.de>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
14
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Message-ID: <20250204211407.381505-8-kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
17
---
11
qapi/block-core.json | 7 +++----
18
qapi/block-core.json | 6 ++++++
12
block/vdi.c | 24 ++++++++++++++++++++++--
19
include/block/block-common.h | 1 +
13
2 files changed, 25 insertions(+), 6 deletions(-)
20
block.c | 9 +++++++++
21
3 files changed, 16 insertions(+)
14
22
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
23
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
17
--- a/qapi/block-core.json
25
--- a/qapi/block-core.json
18
+++ b/qapi/block-core.json
26
+++ b/qapi/block-core.json
19
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@
20
#
28
#
21
# @file Node to create the image format on
29
# @cache: cache-related options
22
# @size Size of the virtual disk in bytes
23
-# @static Whether to create a statically (true) or
24
-# dynamically (false) allocated image
25
-# (default: false, i.e. dynamic)
26
+# @preallocation Preallocation mode for the new image (allowed values: off,
27
+# metadata; default: off)
28
#
30
#
29
# Since: 2.12
31
+# @active: whether the block node should be activated (default: true).
30
##
32
+# Having inactive block nodes is useful primarily for migration because it
31
{ 'struct': 'BlockdevCreateOptionsVdi',
33
+# allows opening an image on the destination while the source is still
32
'data': { 'file': 'BlockdevRef',
34
+# holding locks for it. (Since 10.0)
33
'size': 'size',
35
+#
34
- '*static': 'bool' } }
36
# @read-only: whether the block device should be read-only (default:
35
+ '*preallocation': 'PreallocMode' } }
37
# false). Note that some block drivers support only read-only
36
38
# access, either generally or in certain configurations. In this
37
##
39
@@ -XXX,XX +XXX,XX @@
38
# @BlockdevVhdxSubformat:
40
'*node-name': 'str',
39
diff --git a/block/vdi.c b/block/vdi.c
41
'*discard': 'BlockdevDiscardOptions',
42
'*cache': 'BlockdevCacheOptions',
43
+ '*active': 'bool',
44
'*read-only': 'bool',
45
'*auto-read-only': 'bool',
46
'*force-share': 'bool',
47
diff --git a/include/block/block-common.h b/include/block/block-common.h
40
index XXXXXXX..XXXXXXX 100644
48
index XXXXXXX..XXXXXXX 100644
41
--- a/block/vdi.c
49
--- a/include/block/block-common.h
42
+++ b/block/vdi.c
50
+++ b/include/block/block-common.h
43
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
51
@@ -XXX,XX +XXX,XX @@ typedef enum {
44
int ret = 0;
52
#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only"
45
uint64_t bytes = 0;
53
#define BDRV_OPT_DISCARD "discard"
46
uint32_t blocks;
54
#define BDRV_OPT_FORCE_SHARE "force-share"
47
- uint32_t image_type = VDI_TYPE_DYNAMIC;
55
+#define BDRV_OPT_ACTIVE "active"
48
+ uint32_t image_type;
56
49
VdiHeader header;
57
50
size_t i;
58
#define BDRV_SECTOR_BITS 9
51
size_t bmap_size;
59
diff --git a/block.c b/block.c
52
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
60
index XXXXXXX..XXXXXXX 100644
53
61
--- a/block.c
54
/* Validate options and set default values */
62
+++ b/block.c
55
bytes = vdi_opts->size;
63
@@ -XXX,XX +XXX,XX @@ static void update_flags_from_options(int *flags, QemuOpts *opts)
56
- if (vdi_opts->q_static) {
64
if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) {
57
+
65
*flags |= BDRV_O_AUTO_RDONLY;
58
+ if (!vdi_opts->has_preallocation) {
59
+ vdi_opts->preallocation = PREALLOC_MODE_OFF;
60
+ }
61
+ switch (vdi_opts->preallocation) {
62
+ case PREALLOC_MODE_OFF:
63
+ image_type = VDI_TYPE_DYNAMIC;
64
+ break;
65
+ case PREALLOC_MODE_METADATA:
66
image_type = VDI_TYPE_STATIC;
67
+ break;
68
+ default:
69
+ error_setg(errp, "Preallocation mode not supported for vdi");
70
+ return -EINVAL;
71
}
66
}
72
+
67
+
73
#ifndef CONFIG_VDI_STATIC_IMAGE
68
+ if (!qemu_opt_get_bool_del(opts, BDRV_OPT_ACTIVE, true)) {
74
if (image_type == VDI_TYPE_STATIC) {
69
+ *flags |= BDRV_O_INACTIVE;
75
ret = -ENOTSUP;
76
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
77
BlockdevCreateOptions *create_options = NULL;
78
BlockDriverState *bs_file = NULL;
79
uint64_t block_size = DEFAULT_CLUSTER_SIZE;
80
+ bool is_static = false;
81
Visitor *v;
82
Error *local_err = NULL;
83
int ret;
84
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
85
goto done;
86
}
87
#endif
88
+ if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
89
+ is_static = true;
90
+ }
70
+ }
91
71
}
92
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
72
93
73
static void update_options_from_flags(QDict *options, int flags)
94
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
74
@@ -XXX,XX +XXX,XX @@ QemuOptsList bdrv_runtime_opts = {
95
75
.type = QEMU_OPT_BOOL,
96
qdict_put_str(qdict, "driver", "vdi");
76
.help = "Ignore flush requests",
97
qdict_put_str(qdict, "file", bs_file->node_name);
77
},
98
+ if (is_static) {
78
+ {
99
+ qdict_put_str(qdict, "preallocation", "metadata");
79
+ .name = BDRV_OPT_ACTIVE,
100
+ }
80
+ .type = QEMU_OPT_BOOL,
101
81
+ .help = "Node is activated",
102
/* Get the QAPI object */
82
+ },
103
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
83
{
84
.name = BDRV_OPT_READ_ONLY,
85
.type = QEMU_OPT_BOOL,
104
--
86
--
105
2.13.6
87
2.48.1
106
107
diff view generated by jsdifflib
1
The system emulator tries to automatically activate and inactivate block
2
nodes at the right point during migration. However, there are still
3
cases where it's necessary that the user can do this manually.
4
5
Images are only activated on the destination VM of a migration when the
6
VM is actually resumed. If the VM was paused, this doesn't happen
7
automatically. The user may want to perform some operation on a block
8
device (e.g. taking a snapshot or starting a block job) without also
9
resuming the VM yet. This is an example where a manual command is
10
necessary.
11
12
Another example is VM migration when the image files are opened by an
13
external qemu-storage-daemon instance on each side. In this case, the
14
process that needs to hand over the images isn't even part of the
15
migration and can't know when the migration completes. Management tools
16
need a way to explicitly inactivate images on the source and activate
17
them on the destination.
18
19
This adds a new blockdev-set-active QMP command that lets the user
20
change the status of individual nodes (this is necessary in
21
qemu-storage-daemon because it could be serving multiple VMs and only
22
one of them migrates at a time). For convenience, operating on all
23
devices (like QEMU does automatically during migration) is offered as an
24
option, too, and can be used in the context of single VM.
25
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
Acked-by: Fabiano Rosas <farosas@suse.de>
28
Reviewed-by: Eric Blake <eblake@redhat.com>
29
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
30
Message-ID: <20250204211407.381505-9-kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
31
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
---
32
---
3
tests/qemu-iotests/213 | 349 +++++++++++++++++++++++++++++++++++++++++++++
33
qapi/block-core.json | 32 ++++++++++++++++++++++++++++++
4
tests/qemu-iotests/213.out | 121 ++++++++++++++++
34
include/block/block-global-state.h | 3 +++
5
tests/qemu-iotests/group | 1 +
35
block.c | 21 ++++++++++++++++++++
6
3 files changed, 471 insertions(+)
36
blockdev.c | 32 ++++++++++++++++++++++++++++++
7
create mode 100755 tests/qemu-iotests/213
37
4 files changed, 88 insertions(+)
8
create mode 100644 tests/qemu-iotests/213.out
9
38
10
diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213
39
diff --git a/qapi/block-core.json b/qapi/block-core.json
11
new file mode 100755
40
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX
41
--- a/qapi/block-core.json
13
--- /dev/null
42
+++ b/qapi/block-core.json
14
+++ b/tests/qemu-iotests/213
15
@@ -XXX,XX +XXX,XX @@
43
@@ -XXX,XX +XXX,XX @@
16
+#!/bin/bash
44
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' },
45
'allow-preconfig': true }
46
47
+##
48
+# @blockdev-set-active:
17
+#
49
+#
18
+# Test vhdx and file image creation
50
+# Activate or inactivate a block device. Use this to manage the handover of
51
+# block devices on migration with qemu-storage-daemon.
19
+#
52
+#
20
+# Copyright (C) 2018 Red Hat, Inc.
53
+# Activating a node automatically activates all of its child nodes first.
54
+# Inactivating a node automatically inactivates any of its child nodes that are
55
+# not in use by a still active node.
21
+#
56
+#
22
+# This program is free software; you can redistribute it and/or modify
57
+# @node-name: Name of the graph node to activate or inactivate. By default, all
23
+# it under the terms of the GNU General Public License as published by
58
+# nodes are affected by the operation.
24
+# the Free Software Foundation; either version 2 of the License, or
25
+# (at your option) any later version.
26
+#
59
+#
27
+# This program is distributed in the hope that it will be useful,
60
+# @active: true if the nodes should be active when the command returns success,
28
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
61
+# false if they should be inactive.
29
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30
+# GNU General Public License for more details.
31
+#
62
+#
32
+# You should have received a copy of the GNU General Public License
63
+# Since: 10.0
33
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
34
+#
64
+#
65
+# .. qmp-example::
66
+#
67
+# -> { "execute": "blockdev-set-active",
68
+# "arguments": {
69
+# "node-name": "node0",
70
+# "active": false
71
+# }
72
+# }
73
+# <- { "return": {} }
74
+##
75
+{ 'command': 'blockdev-set-active',
76
+ 'data': { '*node-name': 'str', 'active': 'bool' },
77
+ 'allow-preconfig': true }
35
+
78
+
36
+# creator
79
##
37
+owner=kwolf@redhat.com
80
# @BlockdevCreateOptionsFile:
81
#
82
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
83
index XXXXXXX..XXXXXXX 100644
84
--- a/include/block/block-global-state.h
85
+++ b/include/block/block-global-state.h
86
@@ -XXX,XX +XXX,XX @@ bdrv_activate(BlockDriverState *bs, Error **errp);
87
int coroutine_fn no_co_wrapper_bdrv_rdlock
88
bdrv_co_activate(BlockDriverState *bs, Error **errp);
89
90
+int no_coroutine_fn
91
+bdrv_inactivate(BlockDriverState *bs, Error **errp);
38
+
92
+
39
+seq=`basename $0`
93
void bdrv_activate_all(Error **errp);
40
+echo "QA output created by $seq"
94
int bdrv_inactivate_all(void);
95
96
diff --git a/block.c b/block.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/block.c
99
+++ b/block.c
100
@@ -XXX,XX +XXX,XX @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
101
return 0;
102
}
103
104
+int bdrv_inactivate(BlockDriverState *bs, Error **errp)
105
+{
106
+ int ret;
41
+
107
+
42
+here=`pwd`
108
+ GLOBAL_STATE_CODE();
43
+status=1    # failure is the default!
109
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
44
+
110
+
45
+# get standard environment, filters and checks
111
+ if (bdrv_has_bds_parent(bs, true)) {
46
+. ./common.rc
112
+ error_setg(errp, "Node has active parent node");
47
+. ./common.filter
113
+ return -EPERM;
114
+ }
48
+
115
+
49
+_supported_fmt vhdx
116
+ ret = bdrv_inactivate_recurse(bs, true);
50
+_supported_proto file
117
+ if (ret < 0) {
51
+_supported_os Linux
118
+ error_setg_errno(errp, -ret, "Failed to inactivate node");
119
+ return ret;
120
+ }
52
+
121
+
53
+function do_run_qemu()
122
+ return 0;
54
+{
55
+ echo Testing: "$@"
56
+ $QEMU -nographic -qmp stdio -serial none "$@"
57
+ echo
58
+}
123
+}
59
+
124
+
60
+function run_qemu()
125
int bdrv_inactivate_all(void)
126
{
127
BlockDriverState *bs = NULL;
128
diff --git a/blockdev.c b/blockdev.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/blockdev.c
131
+++ b/blockdev.c
132
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_del(const char *node_name, Error **errp)
133
bdrv_unref(bs);
134
}
135
136
+void qmp_blockdev_set_active(const char *node_name, bool active, Error **errp)
61
+{
137
+{
62
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
138
+ int ret;
63
+ | _filter_qemu | _filter_imgfmt \
139
+
64
+ | _filter_actual_image_size
140
+ GLOBAL_STATE_CODE();
141
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
142
+
143
+ if (!node_name) {
144
+ if (active) {
145
+ bdrv_activate_all(errp);
146
+ } else {
147
+ ret = bdrv_inactivate_all();
148
+ if (ret < 0) {
149
+ error_setg_errno(errp, -ret, "Failed to inactivate all nodes");
150
+ }
151
+ }
152
+ } else {
153
+ BlockDriverState *bs = bdrv_find_node(node_name);
154
+ if (!bs) {
155
+ error_setg(errp, "Failed to find node with node-name='%s'",
156
+ node_name);
157
+ return;
158
+ }
159
+
160
+ if (active) {
161
+ bdrv_activate(bs, errp);
162
+ } else {
163
+ bdrv_inactivate(bs, errp);
164
+ }
165
+ }
65
+}
166
+}
66
+
167
+
67
+echo
168
static BdrvChild * GRAPH_RDLOCK
68
+echo "=== Successful image creation (defaults) ==="
169
bdrv_find_child(BlockDriverState *parent_bs, const char *child_name)
69
+echo
170
{
70
+
71
+size=$((128 * 1024 * 1024))
72
+
73
+run_qemu <<EOF
74
+{ "execute": "qmp_capabilities" }
75
+{ "execute": "x-blockdev-create",
76
+ "arguments": {
77
+ "driver": "file",
78
+ "filename": "$TEST_IMG",
79
+ "size": 0
80
+ }
81
+}
82
+{ "execute": "blockdev-add",
83
+ "arguments": {
84
+ "driver": "file",
85
+ "node-name": "imgfile",
86
+ "filename": "$TEST_IMG"
87
+ }
88
+}
89
+{ "execute": "x-blockdev-create",
90
+ "arguments": {
91
+ "driver": "$IMGFMT",
92
+ "file": "imgfile",
93
+ "size": $size
94
+ }
95
+}
96
+{ "execute": "quit" }
97
+EOF
98
+
99
+_img_info --format-specific | _filter_img_info --format-specific
100
+
101
+echo
102
+echo "=== Successful image creation (explicit defaults) ==="
103
+echo
104
+
105
+# Choose a different size to show that we got a new image
106
+size=$((64 * 1024 * 1024))
107
+
108
+run_qemu <<EOF
109
+{ "execute": "qmp_capabilities" }
110
+{ "execute": "x-blockdev-create",
111
+ "arguments": {
112
+ "driver": "file",
113
+ "filename": "$TEST_IMG",
114
+ "size": 0
115
+ }
116
+}
117
+{ "execute": "x-blockdev-create",
118
+ "arguments": {
119
+ "driver": "$IMGFMT",
120
+ "file": {
121
+ "driver": "file",
122
+ "filename": "$TEST_IMG"
123
+ },
124
+ "size": $size,
125
+ "log-size": 1048576,
126
+ "block-size": 8388608,
127
+ "subformat": "dynamic",
128
+ "block-state-zero": true
129
+ }
130
+}
131
+{ "execute": "quit" }
132
+EOF
133
+
134
+_img_info --format-specific | _filter_img_info --format-specific
135
+
136
+echo
137
+echo "=== Successful image creation (with non-default options) ==="
138
+echo
139
+
140
+# Choose a different size to show that we got a new image
141
+size=$((32 * 1024 * 1024))
142
+
143
+run_qemu <<EOF
144
+{ "execute": "qmp_capabilities" }
145
+{ "execute": "x-blockdev-create",
146
+ "arguments": {
147
+ "driver": "file",
148
+ "filename": "$TEST_IMG",
149
+ "size": 0
150
+ }
151
+}
152
+{ "execute": "x-blockdev-create",
153
+ "arguments": {
154
+ "driver": "$IMGFMT",
155
+ "file": {
156
+ "driver": "file",
157
+ "filename": "$TEST_IMG"
158
+ },
159
+ "size": $size,
160
+ "log-size": 8388608,
161
+ "block-size": 268435456,
162
+ "subformat": "fixed",
163
+ "block-state-zero": false
164
+ }
165
+}
166
+{ "execute": "quit" }
167
+EOF
168
+
169
+_img_info --format-specific | _filter_img_info --format-specific
170
+
171
+echo
172
+echo "=== Invalid BlockdevRef ==="
173
+echo
174
+
175
+run_qemu <<EOF
176
+{ "execute": "qmp_capabilities" }
177
+{ "execute": "x-blockdev-create",
178
+ "arguments": {
179
+ "driver": "$IMGFMT",
180
+ "file": "this doesn't exist",
181
+ "size": $size
182
+ }
183
+}
184
+{ "execute": "quit" }
185
+EOF
186
+
187
+echo
188
+echo "=== Zero size ==="
189
+echo
190
+
191
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
192
+{ "execute": "qmp_capabilities" }
193
+{ "execute": "x-blockdev-create",
194
+ "arguments": {
195
+ "driver": "$IMGFMT",
196
+ "file": "node0",
197
+ "size": 0
198
+ }
199
+}
200
+{ "execute": "quit" }
201
+EOF
202
+
203
+_img_info | _filter_img_info
204
+
205
+echo
206
+echo "=== Maximum size ==="
207
+echo
208
+
209
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
210
+{ "execute": "qmp_capabilities" }
211
+{ "execute": "x-blockdev-create",
212
+ "arguments": {
213
+ "driver": "$IMGFMT",
214
+ "file": "node0",
215
+ "size": 70368744177664
216
+ }
217
+}
218
+{ "execute": "quit" }
219
+EOF
220
+
221
+_img_info | _filter_img_info
222
+
223
+echo
224
+echo "=== Invalid sizes ==="
225
+echo
226
+
227
+# TODO Negative image sizes aren't handled correctly, but this is a problem
228
+# with QAPI's implementation of the 'size' type and affects other commands as
229
+# well. Once this is fixed, we may want to add a test case here.
230
+
231
+# 1. 2^64 - 512
232
+# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
233
+# 3. 2^63 - 512 (generally valid, but with the image header the file will
234
+# exceed 63 bits)
235
+# 4. 2^46 + 1 (one byte more than maximum image size)
236
+
237
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
238
+{ "execute": "qmp_capabilities" }
239
+{ "execute": "x-blockdev-create",
240
+ "arguments": {
241
+ "driver": "$IMGFMT",
242
+ "file": "node0",
243
+ "size": 18446744073709551104
244
+ }
245
+}
246
+{ "execute": "x-blockdev-create",
247
+ "arguments": {
248
+ "driver": "$IMGFMT",
249
+ "file": "node0",
250
+ "size": 9223372036854775808
251
+ }
252
+}
253
+{ "execute": "x-blockdev-create",
254
+ "arguments": {
255
+ "driver": "$IMGFMT",
256
+ "file": "node0",
257
+ "size": 9223372036854775296
258
+ }
259
+}
260
+{ "execute": "x-blockdev-create",
261
+ "arguments": {
262
+ "driver": "$IMGFMT",
263
+ "file": "node0",
264
+ "size": 70368744177665
265
+ }
266
+}
267
+{ "execute": "quit" }
268
+EOF
269
+
270
+echo
271
+echo "=== Invalid block size ==="
272
+echo
273
+
274
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
275
+{ "execute": "qmp_capabilities" }
276
+{ "execute": "x-blockdev-create",
277
+ "arguments": {
278
+ "driver": "$IMGFMT",
279
+ "file": "node0",
280
+ "size": 67108864,
281
+ "block-size": 1234567
282
+ }
283
+}
284
+{ "execute": "x-blockdev-create",
285
+ "arguments": {
286
+ "driver": "$IMGFMT",
287
+ "file": "node0",
288
+ "size": 67108864,
289
+ "block-size": 128
290
+ }
291
+}
292
+{ "execute": "x-blockdev-create",
293
+ "arguments": {
294
+ "driver": "$IMGFMT",
295
+ "file": "node0",
296
+ "size": 67108864,
297
+ "block-size": 3145728
298
+ }
299
+}
300
+{ "execute": "x-blockdev-create",
301
+ "arguments": {
302
+ "driver": "$IMGFMT",
303
+ "file": "node0",
304
+ "size": 67108864,
305
+ "block-size": 536870912
306
+ }
307
+}
308
+{ "execute": "x-blockdev-create",
309
+ "arguments": {
310
+ "driver": "$IMGFMT",
311
+ "file": "node0",
312
+ "size": 67108864,
313
+ "block-size": 0
314
+ }
315
+}
316
+{ "execute": "quit" }
317
+EOF
318
+
319
+echo
320
+echo "=== Invalid log size ==="
321
+echo
322
+
323
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
324
+{ "execute": "qmp_capabilities" }
325
+{ "execute": "x-blockdev-create",
326
+ "arguments": {
327
+ "driver": "$IMGFMT",
328
+ "file": "node0",
329
+ "size": 67108864,
330
+ "log-size": 1234567
331
+ }
332
+}
333
+{ "execute": "x-blockdev-create",
334
+ "arguments": {
335
+ "driver": "$IMGFMT",
336
+ "file": "node0",
337
+ "size": 67108864,
338
+ "log-size": 128
339
+ }
340
+}
341
+{ "execute": "x-blockdev-create",
342
+ "arguments": {
343
+ "driver": "$IMGFMT",
344
+ "file": "node0",
345
+ "size": 67108864,
346
+ "log-size": 4294967296
347
+ }
348
+}
349
+{ "execute": "x-blockdev-create",
350
+ "arguments": {
351
+ "driver": "$IMGFMT",
352
+ "file": "node0",
353
+ "size": 67108864,
354
+ "log-size": 0
355
+ }
356
+}
357
+{ "execute": "quit" }
358
+EOF
359
+
360
+
361
+# success, all done
362
+echo "*** done"
363
+rm -f $seq.full
364
+status=0
365
diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out
366
new file mode 100644
367
index XXXXXXX..XXXXXXX
368
--- /dev/null
369
+++ b/tests/qemu-iotests/213.out
370
@@ -XXX,XX +XXX,XX @@
371
+QA output created by 213
372
+
373
+=== Successful image creation (defaults) ===
374
+
375
+Testing:
376
+QMP_VERSION
377
+{"return": {}}
378
+{"return": {}}
379
+{"return": {}}
380
+{"return": {}}
381
+{"return": {}}
382
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
383
+
384
+image: TEST_DIR/t.IMGFMT
385
+file format: IMGFMT
386
+virtual size: 128M (134217728 bytes)
387
+
388
+=== Successful image creation (explicit defaults) ===
389
+
390
+Testing:
391
+QMP_VERSION
392
+{"return": {}}
393
+{"return": {}}
394
+{"return": {}}
395
+{"return": {}}
396
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
397
+
398
+image: TEST_DIR/t.IMGFMT
399
+file format: IMGFMT
400
+virtual size: 64M (67108864 bytes)
401
+
402
+=== Successful image creation (with non-default options) ===
403
+
404
+Testing:
405
+QMP_VERSION
406
+{"return": {}}
407
+{"return": {}}
408
+{"return": {}}
409
+{"return": {}}
410
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
411
+
412
+image: TEST_DIR/t.IMGFMT
413
+file format: IMGFMT
414
+virtual size: 32M (33554432 bytes)
415
+
416
+=== Invalid BlockdevRef ===
417
+
418
+Testing:
419
+QMP_VERSION
420
+{"return": {}}
421
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
422
+{"return": {}}
423
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
424
+
425
+
426
+=== Zero size ===
427
+
428
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
429
+QMP_VERSION
430
+{"return": {}}
431
+{"return": {}}
432
+{"return": {}}
433
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
434
+
435
+image: TEST_DIR/t.IMGFMT
436
+file format: IMGFMT
437
+virtual size: 0 (0 bytes)
438
+
439
+=== Maximum size ===
440
+
441
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
442
+QMP_VERSION
443
+{"return": {}}
444
+{"return": {}}
445
+{"return": {}}
446
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
447
+
448
+image: TEST_DIR/t.IMGFMT
449
+file format: IMGFMT
450
+virtual size: 64T (70368744177664 bytes)
451
+
452
+=== Invalid sizes ===
453
+
454
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
455
+QMP_VERSION
456
+{"return": {}}
457
+{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}}
458
+{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}}
459
+{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}}
460
+{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}}
461
+{"return": {}}
462
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
463
+
464
+
465
+=== Invalid block size ===
466
+
467
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
468
+QMP_VERSION
469
+{"return": {}}
470
+{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}}
471
+{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}}
472
+{"error": {"class": "GenericError", "desc": "Block size must be a power of two"}}
473
+{"error": {"class": "GenericError", "desc": "Block size must not exceed 268435456"}}
474
+{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}}
475
+{"return": {}}
476
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
477
+
478
+
479
+=== Invalid log size ===
480
+
481
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
482
+QMP_VERSION
483
+{"return": {}}
484
+{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}}
485
+{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}}
486
+{"error": {"class": "GenericError", "desc": "Log size must be smaller than 4 GB"}}
487
+{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}}
488
+{"return": {}}
489
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
490
+
491
+*** done
492
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
493
index XXXXXXX..XXXXXXX 100644
494
--- a/tests/qemu-iotests/group
495
+++ b/tests/qemu-iotests/group
496
@@ -XXX,XX +XXX,XX @@
497
210 rw auto
498
211 rw auto quick
499
212 rw auto quick
500
+213 rw auto quick
501
--
171
--
502
2.13.6
172
2.48.1
503
504
diff view generated by jsdifflib
1
This tests that the .bdrv_truncate implementation for luks doesn't crash
1
Device models have a relatively complex way to set up their block
2
for invalid image sizes.
2
backends, in which blk_attach_dev() sets blk->disable_perm = true.
3
We want to support inactive images in exports, too, so that
4
qemu-storage-daemon can be used with migration. Because they don't use
5
blk_attach_dev(), they need another way to set this flag. The most
6
convenient is to do this automatically when an inactive node is attached
7
to a BlockBackend that can be inactivated.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Acked-by: Fabiano Rosas <farosas@suse.de>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Message-ID: <20250204211407.381505-10-kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
15
---
7
tests/qemu-iotests/210 | 37 +++++++++++++++++++++++++++++++++++++
16
block/block-backend.c | 14 ++++++++++++--
8
tests/qemu-iotests/210.out | 16 ++++++++++++++++
17
1 file changed, 12 insertions(+), 2 deletions(-)
9
2 files changed, 53 insertions(+)
10
18
11
diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210
19
diff --git a/block/block-backend.c b/block/block-backend.c
12
index XXXXXXX..XXXXXXX 100755
20
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/210
21
--- a/block/block-backend.c
14
+++ b/tests/qemu-iotests/210
22
+++ b/block/block-backend.c
15
@@ -XXX,XX +XXX,XX @@ run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \
23
@@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk)
16
{ "execute": "quit" }
24
int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
17
EOF
25
{
18
26
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
19
+echo
27
+ uint64_t perm, shared_perm;
20
+echo "=== Resize image with invalid sizes ==="
28
21
+echo
29
GLOBAL_STATE_CODE();
30
bdrv_ref(bs);
31
bdrv_graph_wrlock();
22
+
32
+
23
+run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \
33
+ if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) {
24
+ -blockdev driver=luks,file=node0,key-secret=keysec0,node-name=node1 \
34
+ blk->disable_perm = true;
25
+ -object secret,id=keysec0,data="foo" <<EOF
35
+ perm = 0;
26
+{ "execute": "qmp_capabilities" }
36
+ shared_perm = BLK_PERM_ALL;
27
+{ "execute": "block_resize",
37
+ } else {
28
+ "arguments": {
38
+ perm = blk->perm;
29
+ "node-name": "node1",
39
+ shared_perm = blk->shared_perm;
30
+ "size": 9223372036854775296
40
+ }
31
+ }
32
+}
33
+{ "execute": "block_resize",
34
+ "arguments": {
35
+ "node-name": "node1",
36
+ "size": 9223372036854775808
37
+ }
38
+}
39
+{ "execute": "block_resize",
40
+ "arguments": {
41
+ "node-name": "node1",
42
+ "size": 18446744073709551104
43
+ }
44
+}
45
+{ "execute": "block_resize",
46
+ "arguments": {
47
+ "node-name": "node1",
48
+ "size": -9223372036854775808
49
+ }
50
+}
51
+{ "execute": "quit" }
52
+EOF
53
+
41
+
54
+_img_info | _filter_img_info
42
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
55
+
43
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
56
# success, all done
44
- blk->perm, blk->shared_perm,
57
echo "*** done"
45
- blk, errp);
58
rm -f $seq.full
46
+ perm, shared_perm, blk, errp);
59
diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out
47
bdrv_graph_wrunlock();
60
index XXXXXXX..XXXXXXX 100644
48
if (blk->root == NULL) {
61
--- a/tests/qemu-iotests/210.out
49
return -EPERM;
62
+++ b/tests/qemu-iotests/210.out
63
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
64
{"return": {}}
65
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
66
67
+
68
+=== Resize image with invalid sizes ===
69
+
70
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -blockdev driver=IMGFMT,file=node0,key-secret=keysec0,node-name=node1 -object secret,id=keysec0,data=foo
71
+QMP_VERSION
72
+{"return": {}}
73
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
74
+{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}}
75
+{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}}
76
+{"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}}
77
+{"return": {}}
78
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
79
+
80
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
81
+file format: IMGFMT
82
+virtual size: 0 (0 bytes)
83
*** done
84
--
50
--
85
2.13.6
51
2.48.1
86
87
diff view generated by jsdifflib
1
Commit e39e959e fixed an invalid assertion in the .bdrv_length
1
Currently, block exports can't handle inactive images correctly.
2
implementation, but left a similar assertion in place for
2
Incoming write requests would run into assertion failures. Make sure
3
.bdrv_truncate. Instead of crashing when the user requests a too large
3
that we return an error when creating an export can't activate the
4
image size, fail gracefully.
4
image.
5
6
A file size of exactly INT64_MAX caused failure before, but is actually
7
legal.
8
5
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Acked-by: Fabiano Rosas <farosas@suse.de>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Message-ID: <20250204211407.381505-11-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
12
---
13
block/crypto.c | 6 +++++-
13
block/export/export.c | 6 +++++-
14
1 file changed, 5 insertions(+), 1 deletion(-)
14
1 file changed, 5 insertions(+), 1 deletion(-)
15
15
16
diff --git a/block/crypto.c b/block/crypto.c
16
diff --git a/block/export/export.c b/block/export/export.c
17
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/crypto.c
18
--- a/block/export/export.c
19
+++ b/block/crypto.c
19
+++ b/block/export/export.c
20
@@ -XXX,XX +XXX,XX @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
20
@@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
21
BlockCrypto *crypto = bs->opaque;
21
* ctx was acquired in the caller.
22
uint64_t payload_offset =
22
*/
23
qcrypto_block_get_payload_offset(crypto->block);
23
bdrv_graph_rdlock_main_loop();
24
- assert(payload_offset < (INT64_MAX - offset));
24
- bdrv_activate(bs, NULL);
25
+
25
+ ret = bdrv_activate(bs, errp);
26
+ if (payload_offset > INT64_MAX - offset) {
26
+ if (ret < 0) {
27
+ error_setg(errp, "The requested file size is too large");
27
+ bdrv_graph_rdunlock_main_loop();
28
+ return -EFBIG;
28
+ goto fail;
29
+ }
29
+ }
30
30
bdrv_graph_rdunlock_main_loop();
31
offset += payload_offset;
31
32
32
perm = BLK_PERM_CONSISTENT_READ;
33
--
33
--
34
2.13.6
34
2.48.1
35
36
diff view generated by jsdifflib
1
It's unclear what the real maximum cluster size is for the Parallels
1
So far the assumption has always been that if we try to inactivate a
2
format, but let's at least make sure that we don't get integer
2
node, it is already idle. This doesn't hold true any more if we allow
3
overflows in our .bdrv_co_create implementation.
3
inactivating exported nodes because we can't know when new external
4
requests come in.
5
6
Drain the node around setting BDRV_O_INACTIVE so that requests can't
7
start operating on an active node and then in the middle it suddenly
8
becomes inactive. With this change, it's enough for exports to check
9
for new requests that they operate on an active node (or, like reads,
10
are allowed even on an inactive node).
4
11
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Acked-by: Fabiano Rosas <farosas@suse.de>
14
Message-ID: <20250204211407.381505-12-kwolf@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
18
---
8
block/parallels.c | 5 +++++
19
block.c | 2 ++
9
1 file changed, 5 insertions(+)
20
1 file changed, 2 insertions(+)
10
21
11
diff --git a/block/parallels.c b/block/parallels.c
22
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
13
--- a/block/parallels.c
24
--- a/block.c
14
+++ b/block/parallels.c
25
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
26
@@ -XXX,XX +XXX,XX @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
16
cl_size = DEFAULT_CLUSTER_SIZE;
27
return -EPERM;
17
}
28
}
18
29
19
+ /* XXX What is the real limit here? This is an insanely large maximum. */
30
+ bdrv_drained_begin(bs);
20
+ if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) {
31
bs->open_flags |= BDRV_O_INACTIVE;
21
+ error_setg(errp, "Cluster size is too large");
32
+ bdrv_drained_end(bs);
22
+ return -EINVAL;
33
23
+ }
34
/*
24
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
35
* Update permissions, they may differ for inactive nodes.
25
error_setg(errp, "Image size is too large for this cluster size");
26
return -E2BIG;
27
--
36
--
28
2.13.6
37
2.48.1
29
30
diff view generated by jsdifflib
1
Use qemu_uuid_unparse() instead of uuid_unparse() to make vdi.c compile
1
Add an option in BlockExportOptions to allow creating an export on an
2
again when CONFIG_VDI_DEBUG is set. In order to prevent future bitrot,
2
inactive node without activating the node. This mode needs to be
3
replace '#ifdef CONFIG_VDI_DEBUG' by 'if (VDI_DEBUG)' so that the
3
explicitly supported by the export type (so that it doesn't perform any
4
compiler always sees the code.
4
operations that are forbidden for inactive nodes), so this patch alone
5
doesn't allow this option to be successfully used yet.
5
6
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Acked-by: Fabiano Rosas <farosas@suse.de>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Message-ID: <20250204211407.381505-13-kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
13
---
9
block/vdi.c | 22 ++++++++++------------
14
qapi/block-export.json | 10 +++++++++-
10
1 file changed, 10 insertions(+), 12 deletions(-)
15
include/block/export.h | 3 +++
16
block/export/export.c | 31 +++++++++++++++++++++----------
17
3 files changed, 33 insertions(+), 11 deletions(-)
11
18
12
diff --git a/block/vdi.c b/block/vdi.c
19
diff --git a/qapi/block-export.json b/qapi/block-export.json
13
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
14
--- a/block/vdi.c
21
--- a/qapi/block-export.json
15
+++ b/block/vdi.c
22
+++ b/qapi/block-export.json
16
@@ -XXX,XX +XXX,XX @@ static void vdi_header_to_le(VdiHeader *header)
23
@@ -XXX,XX +XXX,XX @@
17
qemu_uuid_bswap(&header->uuid_parent);
24
# cannot be moved to the iothread. The default is false.
18
}
25
# (since: 5.2)
19
26
#
20
-#if defined(CONFIG_VDI_DEBUG)
27
+# @allow-inactive: If true, the export allows the exported node to be inactive.
21
static void vdi_header_print(VdiHeader *header)
28
+# If it is created for an inactive block node, the node remains inactive. If
29
+# the export type doesn't support running on an inactive node, an error is
30
+# returned. If false, inactive block nodes are automatically activated before
31
+# creating the export and trying to inactivate them later fails.
32
+# (since: 10.0; default: false)
33
+#
34
# Since: 4.2
35
##
36
{ 'union': 'BlockExportOptions',
37
@@ -XXX,XX +XXX,XX @@
38
'*iothread': 'str',
39
'node-name': 'str',
40
'*writable': 'bool',
41
- '*writethrough': 'bool' },
42
+ '*writethrough': 'bool',
43
+ '*allow-inactive': 'bool' },
44
'discriminator': 'type',
45
'data': {
46
'nbd': 'BlockExportOptionsNbd',
47
diff --git a/include/block/export.h b/include/block/export.h
48
index XXXXXXX..XXXXXXX 100644
49
--- a/include/block/export.h
50
+++ b/include/block/export.h
51
@@ -XXX,XX +XXX,XX @@ typedef struct BlockExportDriver {
52
*/
53
size_t instance_size;
54
55
+ /* True if the export type supports running on an inactive node */
56
+ bool supports_inactive;
57
+
58
/* Creates and starts a new block export */
59
int (*create)(BlockExport *, BlockExportOptions *, Error **);
60
61
diff --git a/block/export/export.c b/block/export/export.c
62
index XXXXXXX..XXXXXXX 100644
63
--- a/block/export/export.c
64
+++ b/block/export/export.c
65
@@ -XXX,XX +XXX,XX @@ static const BlockExportDriver *blk_exp_find_driver(BlockExportType type)
66
BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
22
{
67
{
23
char uuid[37];
68
bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread;
24
@@ -XXX,XX +XXX,XX @@ static void vdi_header_print(VdiHeader *header)
69
+ bool allow_inactive = export->has_allow_inactive && export->allow_inactive;
25
logout("block extra 0x%04x\n", header->block_extra);
70
const BlockExportDriver *drv;
26
logout("blocks tot. 0x%04x\n", header->blocks_in_image);
71
BlockExport *exp = NULL;
27
logout("blocks all. 0x%04x\n", header->blocks_allocated);
72
BlockDriverState *bs;
28
- uuid_unparse(header->uuid_image, uuid);
73
@@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
29
+ qemu_uuid_unparse(&header->uuid_image, uuid);
74
}
30
logout("uuid image %s\n", uuid);
31
- uuid_unparse(header->uuid_last_snap, uuid);
32
+ qemu_uuid_unparse(&header->uuid_last_snap, uuid);
33
logout("uuid snap %s\n", uuid);
34
- uuid_unparse(header->uuid_link, uuid);
35
+ qemu_uuid_unparse(&header->uuid_link, uuid);
36
logout("uuid link %s\n", uuid);
37
- uuid_unparse(header->uuid_parent, uuid);
38
+ qemu_uuid_unparse(&header->uuid_parent, uuid);
39
logout("uuid parent %s\n", uuid);
40
}
41
-#endif
42
43
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
44
BdrvCheckMode fix)
45
@@ -XXX,XX +XXX,XX @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
46
}
75
}
47
76
48
vdi_header_to_cpu(&header);
77
- /*
49
-#if defined(CONFIG_VDI_DEBUG)
78
- * Block exports are used for non-shared storage migration. Make sure
50
- vdi_header_print(&header);
79
- * that BDRV_O_INACTIVE is cleared and the image is ready for write
51
-#endif
80
- * access since the export could be available before migration handover.
52
+ if (VDI_DEBUG) {
81
- * ctx was acquired in the caller.
53
+ vdi_header_print(&header);
82
- */
83
bdrv_graph_rdlock_main_loop();
84
- ret = bdrv_activate(bs, errp);
85
- if (ret < 0) {
86
- bdrv_graph_rdunlock_main_loop();
87
- goto fail;
88
+ if (allow_inactive) {
89
+ if (!drv->supports_inactive) {
90
+ error_setg(errp, "Export type does not support inactive exports");
91
+ bdrv_graph_rdunlock_main_loop();
92
+ goto fail;
93
+ }
94
+ } else {
95
+ /*
96
+ * Block exports are used for non-shared storage migration. Make sure
97
+ * that BDRV_O_INACTIVE is cleared and the image is ready for write
98
+ * access since the export could be available before migration handover.
99
+ */
100
+ ret = bdrv_activate(bs, errp);
101
+ if (ret < 0) {
102
+ bdrv_graph_rdunlock_main_loop();
103
+ goto fail;
104
+ }
105
}
106
bdrv_graph_rdunlock_main_loop();
107
108
@@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
109
if (!fixed_iothread) {
110
blk_set_allow_aio_context_change(blk, true);
111
}
112
+ if (allow_inactive) {
113
+ blk_set_force_allow_inactivate(blk);
54
+ }
114
+ }
55
115
56
if (header.disk_size > VDI_DISK_SIZE_MAX) {
116
ret = blk_insert_bs(blk, bs, errp);
57
error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64
58
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
59
qemu_uuid_generate(&header.uuid_image);
60
qemu_uuid_generate(&header.uuid_last_snap);
61
/* There is no need to set header.uuid_link or header.uuid_parent here. */
62
-#if defined(CONFIG_VDI_DEBUG)
63
- vdi_header_print(&header);
64
-#endif
65
+ if (VDI_DEBUG) {
66
+ vdi_header_print(&header);
67
+ }
68
vdi_header_to_le(&header);
69
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
70
if (ret < 0) {
117
if (ret < 0) {
71
--
118
--
72
2.13.6
119
2.48.1
73
74
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
In order to support running an NBD export on inactive nodes, we must
2
make sure to return errors for any operations that aren't allowed on
3
inactive nodes. Reads are the only operation we know we need for
4
inactive images, so to err on the side of caution, return errors for
5
everything else, even if some operations could possibly be okay.
2
6
3
When we try to allocate new clusters we first look for available ones
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
starting from s->free_cluster_index and once we find them we increase
8
Acked-by: Fabiano Rosas <farosas@suse.de>
5
their reference counts. Before we get to call update_refcount() to do
9
Message-ID: <20250204211407.381505-14-kwolf@redhat.com>
6
this last step s->free_cluster_index is already pointing to the next
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
cluster after the ones we are trying to allocate.
8
9
During update_refcount() it may happen however that we also need to
10
allocate a new refcount block in order to store the refcounts of these
11
new clusters (and to complicate things further that may also require
12
us to grow the refcount table). After all this we don't know if the
13
clusters that we originally tried to allocate are still available, so
14
we return -EAGAIN to ask the caller to restart the search for free
15
clusters.
16
17
This is what can happen in a common scenario:
18
19
1) We want to allocate a new cluster and we see that cluster N is
20
free.
21
22
2) We try to increase N's refcount but all refcount blocks are full,
23
so we allocate a new one at N+1 (where s->free_cluster_index was
24
pointing at).
25
26
3) Once we're done we return -EAGAIN to look again for a free
27
cluster, but now s->free_cluster_index points at N+2, so that's
28
the one we allocate. Cluster N remains unallocated and we have a
29
hole in the qcow2 file.
30
31
This can be reproduced easily:
32
33
qemu-img create -f qcow2 -o cluster_size=512 hd.qcow2 1M
34
qemu-io -c 'write 0 124k' hd.qcow2
35
36
After this the image has 132608 bytes (256 clusters), and the refcount
37
block is full. If we write 512 more bytes it should allocate two new
38
clusters: the data cluster itself and a new refcount block.
39
40
qemu-io -c 'write 124k 512' hd.qcow2
41
42
However the image has now three new clusters (259 in total), and the
43
first one of them is empty (and unallocated):
44
45
dd if=hd.qcow2 bs=512c skip=256 count=1 | hexdump -C
46
47
If we write larger amounts of data in the last step instead of the 512
48
bytes used in this example we can create larger holes in the qcow2
49
file.
50
51
What this patch does is reset s->free_cluster_index to its previous
52
value when alloc_refcount_block() returns -EAGAIN. This way the caller
53
will try to allocate again the original clusters if they are still
54
free.
55
56
The output of iotest 026 also needs to be updated because now that
57
images have no holes some tests fail at a different point and the
58
number of leaked clusters is different.
59
60
Signed-off-by: Alberto Garcia <berto@igalia.com>
61
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
62
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
63
---
13
---
64
block/qcow2-refcount.c | 7 +++++++
14
nbd/server.c | 17 +++++++++++++++++
65
tests/qemu-iotests/026.out | 6 +++---
15
1 file changed, 17 insertions(+)
66
tests/qemu-iotests/121 | 20 ++++++++++++++++++++
67
tests/qemu-iotests/121.out | 10 ++++++++++
68
4 files changed, 40 insertions(+), 3 deletions(-)
69
16
70
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
17
diff --git a/nbd/server.c b/nbd/server.c
71
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
72
--- a/block/qcow2-refcount.c
19
--- a/nbd/server.c
73
+++ b/block/qcow2-refcount.c
20
+++ b/nbd/server.c
74
@@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
21
@@ -XXX,XX +XXX,XX @@ static void nbd_export_delete(BlockExport *blk_exp)
75
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
22
const BlockExportDriver blk_exp_nbd = {
76
}
23
.type = BLOCK_EXPORT_TYPE_NBD,
77
ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
24
.instance_size = sizeof(NBDExport),
78
+ /* If the caller needs to restart the search for free clusters,
25
+ .supports_inactive = true,
79
+ * try the same ones first to see if they're still free. */
26
.create = nbd_export_create,
80
+ if (ret == -EAGAIN) {
27
.delete = nbd_export_delete,
81
+ if (s->free_cluster_index > (start >> s->cluster_bits)) {
28
.request_shutdown = nbd_export_request_shutdown,
82
+ s->free_cluster_index = (start >> s->cluster_bits);
29
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
83
+ }
30
NBDExport *exp = client->exp;
31
char *msg;
32
size_t i;
33
+ bool inactive;
34
+
35
+ WITH_GRAPH_RDLOCK_GUARD() {
36
+ inactive = bdrv_is_inactive(blk_bs(exp->common.blk));
37
+ if (inactive) {
38
+ switch (request->type) {
39
+ case NBD_CMD_READ:
40
+ /* These commands are allowed on inactive nodes */
41
+ break;
42
+ default:
43
+ /* Return an error for the rest */
44
+ return nbd_send_generic_reply(client, request, -EPERM,
45
+ "export is inactive", errp);
84
+ }
46
+ }
85
if (ret < 0) {
47
+ }
86
goto fail;
48
+ }
87
}
49
88
diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
50
switch (request->type) {
89
index XXXXXXX..XXXXXXX 100644
51
case NBD_CMD_CACHE:
90
--- a/tests/qemu-iotests/026.out
91
+++ b/tests/qemu-iotests/026.out
92
@@ -XXX,XX +XXX,XX @@ Failed to flush the L2 table cache: No space left on device
93
Failed to flush the refcount block cache: No space left on device
94
write failed: No space left on device
95
96
-11 leaked clusters were found on the image.
97
+10 leaked clusters were found on the image.
98
This means waste of disk space, but no harm to data.
99
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
100
101
@@ -XXX,XX +XXX,XX @@ Failed to flush the L2 table cache: No space left on device
102
Failed to flush the refcount block cache: No space left on device
103
write failed: No space left on device
104
105
-11 leaked clusters were found on the image.
106
+10 leaked clusters were found on the image.
107
This means waste of disk space, but no harm to data.
108
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
109
110
@@ -XXX,XX +XXX,XX @@ Failed to flush the L2 table cache: No space left on device
111
Failed to flush the refcount block cache: No space left on device
112
write failed: No space left on device
113
114
-11 leaked clusters were found on the image.
115
+10 leaked clusters were found on the image.
116
This means waste of disk space, but no harm to data.
117
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
118
119
diff --git a/tests/qemu-iotests/121 b/tests/qemu-iotests/121
120
index XXXXXXX..XXXXXXX 100755
121
--- a/tests/qemu-iotests/121
122
+++ b/tests/qemu-iotests/121
123
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'write 63M 130K' "$TEST_IMG" | _filter_qemu_io
124
125
_check_test_img
126
127
+echo
128
+echo '=== Allocating a new refcount block must not leave holes in the image ==='
129
+echo
130
+
131
+IMGOPTS='cluster_size=512,refcount_bits=16' _make_test_img 1M
132
+
133
+# This results in an image with 256 used clusters: the qcow2 header,
134
+# the refcount table, one refcount block, the L1 table, four L2 tables
135
+# and 248 data clusters
136
+$QEMU_IO -c 'write 0 124k' "$TEST_IMG" | _filter_qemu_io
137
+
138
+# 256 clusters of 512 bytes each give us a 128K image
139
+stat -c "size=%s (expected 131072)" $TEST_IMG
140
+
141
+# All 256 entries of the refcount block are used, so writing a new
142
+# data cluster also allocates a new refcount block
143
+$QEMU_IO -c 'write 124k 512' "$TEST_IMG" | _filter_qemu_io
144
+
145
+# Two more clusters, the image size should be 129K now
146
+stat -c "size=%s (expected 132096)" $TEST_IMG
147
148
# success, all done
149
echo
150
diff --git a/tests/qemu-iotests/121.out b/tests/qemu-iotests/121.out
151
index XXXXXXX..XXXXXXX 100644
152
--- a/tests/qemu-iotests/121.out
153
+++ b/tests/qemu-iotests/121.out
154
@@ -XXX,XX +XXX,XX @@ wrote 133120/133120 bytes at offset 66060288
155
130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
156
No errors were found on the image.
157
158
+=== Allocating a new refcount block must not leave holes in the image ===
159
+
160
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
161
+wrote 126976/126976 bytes at offset 0
162
+124 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
163
+size=131072 (expected 131072)
164
+wrote 512/512 bytes at offset 126976
165
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
166
+size=132096 (expected 132096)
167
+
168
*** done
169
--
52
--
170
2.13.6
53
2.48.1
171
172
diff view generated by jsdifflib
1
We want to test resizing even for luks. The only change that is needed
1
The open-coded form of this filter has been copied into enough tests
2
is to explicitly zero out new space for luks because it's undefined.
2
that it's better to move it into iotests.py.
3
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Acked-by: Fabiano Rosas <farosas@suse.de>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
7
Message-ID: <20250204211407.381505-15-kwolf@redhat.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
10
---
8
tests/qemu-iotests/025 | 9 ++++++++-
11
tests/qemu-iotests/iotests.py | 4 ++++
9
1 file changed, 8 insertions(+), 1 deletion(-)
12
tests/qemu-iotests/041 | 4 +---
13
tests/qemu-iotests/165 | 4 +---
14
tests/qemu-iotests/tests/copy-before-write | 3 +--
15
tests/qemu-iotests/tests/migrate-bitmaps-test | 7 +++----
16
5 files changed, 10 insertions(+), 12 deletions(-)
10
17
11
diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025
18
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
19
index XXXXXXX..XXXXXXX 100644
20
--- a/tests/qemu-iotests/iotests.py
21
+++ b/tests/qemu-iotests/iotests.py
22
@@ -XXX,XX +XXX,XX @@ def _filter(_key, value):
23
def filter_nbd_exports(output: str) -> str:
24
return re.sub(r'((min|opt|max) block): [0-9]+', r'\1: XXX', output)
25
26
+def filter_qtest(output: str) -> str:
27
+ output = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', output)
28
+ output = re.sub(r'\n?\[I \+\d+\.\d+\] CLOSED\n?$', '', output)
29
+ return output
30
31
Msg = TypeVar('Msg', Dict[str, Any], List[Any], str)
32
33
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
12
index XXXXXXX..XXXXXXX 100755
34
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/025
35
--- a/tests/qemu-iotests/041
14
+++ b/tests/qemu-iotests/025
36
+++ b/tests/qemu-iotests/041
15
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
37
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
16
. ./common.filter
38
17
. ./common.pattern
39
# Check the full error message now
18
40
self.vm.shutdown()
19
-_supported_fmt raw qcow2 qed
41
- log = self.vm.get_log()
20
+_supported_fmt raw qcow2 qed luks
42
- log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
21
_supported_proto file sheepdog rbd nfs
43
+ log = iotests.filter_qtest(self.vm.get_log())
22
_supported_os Linux
44
log = re.sub(r'^Formatting.*\n', '', log)
23
45
- log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
24
@@ -XXX,XX +XXX,XX @@ length
46
log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
25
EOF
47
26
_check_test_img
48
self.assertEqual(log,
27
49
diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165
28
+# bdrv_truncate() doesn't zero the new space, so we need to do that explicitly.
50
index XXXXXXX..XXXXXXX 100755
29
+# We still want to test automatic zeroing for other formats even though
51
--- a/tests/qemu-iotests/165
30
+# bdrv_truncate() doesn't guarantee it.
52
+++ b/tests/qemu-iotests/165
31
+if [ "$IMGFMT" == "luks" ]; then
53
@@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
32
+ $QEMU_IO -c "write -z $small_size $((big_size - small_size))" "$TEST_IMG" > /dev/null
54
self.vm.shutdown()
33
+fi
55
34
+
56
#catch 'Persistent bitmaps are lost' possible error
35
echo
57
- log = self.vm.get_log()
36
echo "=== Verifying image size after reopen"
58
- log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
37
$QEMU_IO -c "length" "$TEST_IMG"
59
- log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
60
+ log = iotests.filter_qtest(self.vm.get_log())
61
if log:
62
print(log)
63
64
diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write
65
index XXXXXXX..XXXXXXX 100755
66
--- a/tests/qemu-iotests/tests/copy-before-write
67
+++ b/tests/qemu-iotests/tests/copy-before-write
68
@@ -XXX,XX +XXX,XX @@ class TestCbwError(iotests.QMPTestCase):
69
70
self.vm.shutdown()
71
log = self.vm.get_log()
72
- log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
73
- log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
74
+ log = iotests.filter_qtest(log)
75
log = iotests.filter_qemu_io(log)
76
return log
77
78
diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test
79
index XXXXXXX..XXXXXXX 100755
80
--- a/tests/qemu-iotests/tests/migrate-bitmaps-test
81
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-test
82
@@ -XXX,XX +XXX,XX @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
83
84
# catch 'Could not reopen qcow2 layer: Bitmap already exists'
85
# possible error
86
- log = self.vm_a.get_log()
87
- log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
88
- log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}',
89
+ log = iotests.filter_qtest(self.vm_a.get_log())
90
+ log = re.sub(r'^(wrote .* bytes at offset .*\n'
91
+ r'.*KiB.*ops.*sec.*\n?){3}',
92
'', log)
93
- log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
94
self.assertEqual(log, '')
95
96
# test that bitmap is still persistent
38
--
97
--
39
2.13.6
98
2.48.1
40
41
diff view generated by jsdifflib
1
Test that it's possible to migrate a VM that uses an image on shared
2
storage through qemu-storage-daemon.
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Acked-by: Fabiano Rosas <farosas@suse.de>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Message-ID: <20250204211407.381505-16-kwolf@redhat.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
---
10
---
3
tests/qemu-iotests/212 | 326 +++++++++++++++++++++++++++++++++++++++++++++
11
tests/qemu-iotests/tests/qsd-migrate | 140 +++++++++++++++++++++++
4
tests/qemu-iotests/212.out | 111 +++++++++++++++
12
tests/qemu-iotests/tests/qsd-migrate.out | 59 ++++++++++
5
tests/qemu-iotests/group | 1 +
13
2 files changed, 199 insertions(+)
6
3 files changed, 438 insertions(+)
14
create mode 100755 tests/qemu-iotests/tests/qsd-migrate
7
create mode 100755 tests/qemu-iotests/212
15
create mode 100644 tests/qemu-iotests/tests/qsd-migrate.out
8
create mode 100644 tests/qemu-iotests/212.out
9
16
10
diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212
17
diff --git a/tests/qemu-iotests/tests/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate
11
new file mode 100755
18
new file mode 100755
12
index XXXXXXX..XXXXXXX
19
index XXXXXXX..XXXXXXX
13
--- /dev/null
20
--- /dev/null
14
+++ b/tests/qemu-iotests/212
21
+++ b/tests/qemu-iotests/tests/qsd-migrate
15
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@
16
+#!/bin/bash
23
+#!/usr/bin/env python3
17
+#
24
+# group: rw quick
18
+# Test parallels and file image creation
25
+#
19
+#
26
+# Copyright (C) Red Hat, Inc.
20
+# Copyright (C) 2018 Red Hat, Inc.
21
+#
27
+#
22
+# This program is free software; you can redistribute it and/or modify
28
+# This program is free software; you can redistribute it and/or modify
23
+# it under the terms of the GNU General Public License as published by
29
+# it under the terms of the GNU General Public License as published by
24
+# the Free Software Foundation; either version 2 of the License, or
30
+# the Free Software Foundation; either version 2 of the License, or
25
+# (at your option) any later version.
31
+# (at your option) any later version.
...
...
30
+# GNU General Public License for more details.
36
+# GNU General Public License for more details.
31
+#
37
+#
32
+# You should have received a copy of the GNU General Public License
38
+# You should have received a copy of the GNU General Public License
33
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
39
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
34
+#
40
+#
35
+
41
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
36
+# creator
42
+
37
+owner=kwolf@redhat.com
43
+import iotests
38
+
44
+
39
+seq=`basename $0`
45
+from iotests import filter_qemu_io, filter_qtest
40
+echo "QA output created by $seq"
46
+
41
+
47
+iotests.script_initialize(supported_fmts=['generic'],
42
+here=`pwd`
48
+ supported_protocols=['file'],
43
+status=1    # failure is the default!
49
+ supported_platforms=['linux'])
44
+
50
+
45
+# get standard environment, filters and checks
51
+with iotests.FilePath('disk.img') as path, \
46
+. ./common.rc
52
+ iotests.FilePath('nbd-src.sock', base_dir=iotests.sock_dir) as nbd_src, \
47
+. ./common.filter
53
+ iotests.FilePath('nbd-dst.sock', base_dir=iotests.sock_dir) as nbd_dst, \
48
+
54
+ iotests.FilePath('migrate.sock', base_dir=iotests.sock_dir) as mig_sock, \
49
+_supported_fmt parallels
55
+ iotests.VM(path_suffix="-src") as vm_src, \
50
+_supported_proto file
56
+ iotests.VM(path_suffix="-dst") as vm_dst:
51
+_supported_os Linux
57
+
52
+
58
+ img_size = '10M'
53
+function do_run_qemu()
59
+
54
+{
60
+ iotests.log('Preparing disk...')
55
+ echo Testing: "$@"
61
+ iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size)
56
+ $QEMU -nographic -qmp stdio -serial none "$@"
62
+
57
+ echo
63
+ iotests.log('Launching source QSD...')
58
+}
64
+ qsd_src = iotests.QemuStorageDaemon(
59
+
65
+ '--blockdev', f'file,node-name=disk-file,filename={path}',
60
+function run_qemu()
66
+ '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt',
61
+{
67
+ '--nbd-server', f'addr.type=unix,addr.path={nbd_src}',
62
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
68
+ '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,'
63
+ | _filter_qemu | _filter_imgfmt \
69
+ 'allow-inactive=true',
64
+ | _filter_actual_image_size
70
+ qmp=True,
65
+}
71
+ )
66
+
72
+
67
+echo
73
+ iotests.log('Launching source VM...')
68
+echo "=== Successful image creation (defaults) ==="
74
+ vm_src.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,'
69
+echo
75
+ f'server.path={nbd_src},export=disk-fmt')
70
+
76
+ vm_src.add_args('-device', 'virtio-blk,drive=disk,id=virtio0')
71
+size=$((128 * 1024 * 1024))
77
+ vm_src.launch()
72
+
78
+
73
+run_qemu <<EOF
79
+ iotests.log('Launching destination QSD...')
74
+{ "execute": "qmp_capabilities" }
80
+ qsd_dst = iotests.QemuStorageDaemon(
75
+{ "execute": "x-blockdev-create",
81
+ '--blockdev', f'file,node-name=disk-file,filename={path},active=off',
76
+ "arguments": {
82
+ '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,'
77
+ "driver": "file",
83
+ f'active=off',
78
+ "filename": "$TEST_IMG",
84
+ '--nbd-server', f'addr.type=unix,addr.path={nbd_dst}',
79
+ "size": 0
85
+ '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,'
80
+ }
86
+ 'allow-inactive=true',
81
+}
87
+ qmp=True,
82
+{ "execute": "blockdev-add",
88
+ instance_id='b',
83
+ "arguments": {
89
+ )
84
+ "driver": "file",
90
+
85
+ "node-name": "imgfile",
91
+ iotests.log('Launching destination VM...')
86
+ "filename": "$TEST_IMG"
92
+ vm_dst.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,'
87
+ }
93
+ f'server.path={nbd_dst},export=disk-fmt')
88
+}
94
+ vm_dst.add_args('-device', 'virtio-blk,drive=disk,id=virtio0')
89
+{ "execute": "x-blockdev-create",
95
+ vm_dst.add_args('-incoming', f'unix:{mig_sock}')
90
+ "arguments": {
96
+ vm_dst.launch()
91
+ "driver": "$IMGFMT",
97
+
92
+ "file": "imgfile",
98
+ iotests.log('\nTest I/O on the source')
93
+ "size": $size
99
+ vm_src.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x11 0 4k',
94
+ }
100
+ use_log=True, qdev=True)
95
+}
101
+ vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k',
96
+{ "execute": "quit" }
102
+ use_log=True, qdev=True)
97
+EOF
103
+
98
+
104
+ iotests.log('\nStarting migration...')
99
+_img_info --format-specific | _filter_img_info --format-specific
105
+
100
+
106
+ mig_caps = [
101
+echo
107
+ {'capability': 'events', 'state': True},
102
+echo "=== Successful image creation (explicit defaults) ==="
108
+ {'capability': 'pause-before-switchover', 'state': True},
103
+echo
109
+ ]
104
+
110
+ vm_src.qmp_log('migrate-set-capabilities', capabilities=mig_caps)
105
+# Choose a different size to show that we got a new image
111
+ vm_dst.qmp_log('migrate-set-capabilities', capabilities=mig_caps)
106
+size=$((64 * 1024 * 1024))
112
+ vm_src.qmp_log('migrate', uri=f'unix:{mig_sock}',
107
+
113
+ filters=[iotests.filter_qmp_testfiles])
108
+run_qemu <<EOF
114
+
109
+{ "execute": "qmp_capabilities" }
115
+ vm_src.event_wait('MIGRATION',
110
+{ "execute": "x-blockdev-create",
116
+ match={'data': {'status': 'pre-switchover'}})
111
+ "arguments": {
117
+
112
+ "driver": "file",
118
+ iotests.log('\nPre-switchover: Reconfigure QSD instances')
113
+ "filename": "$TEST_IMG",
119
+
114
+ "size": 0
120
+ iotests.log(qsd_src.qmp('blockdev-set-active', {'active': False}))
115
+ }
121
+
116
+}
122
+ # Reading is okay from both sides while the image is inactive. Note that
117
+{ "execute": "x-blockdev-create",
123
+ # the destination may have stale data until it activates the image, though.
118
+ "arguments": {
124
+ vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k',
119
+ "driver": "$IMGFMT",
125
+ use_log=True, qdev=True)
120
+ "file": {
126
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read 0 4k',
121
+ "driver": "file",
127
+ use_log=True, qdev=True)
122
+ "filename": "$TEST_IMG"
128
+
123
+ },
129
+ iotests.log(qsd_dst.qmp('blockdev-set-active', {'active': True}))
124
+ "size": $size,
130
+
125
+ "cluster-size": 1048576
131
+ iotests.log('\nCompleting migration...')
126
+ }
132
+
127
+}
133
+ vm_src.qmp_log('migrate-continue', state='pre-switchover')
128
+{ "execute": "quit" }
134
+ vm_dst.event_wait('MIGRATION', match={'data': {'status': 'completed'}})
129
+EOF
135
+
130
+
136
+ iotests.log('\nTest I/O on the destination')
131
+_img_info --format-specific | _filter_img_info --format-specific
137
+
132
+
138
+ # Now the destination must see what the source wrote
133
+echo
139
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k',
134
+echo "=== Successful image creation (with non-default options) ==="
140
+ use_log=True, qdev=True)
135
+echo
141
+
136
+
142
+ # And be able to overwrite it
137
+# Choose a different size to show that we got a new image
143
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x22 0 4k',
138
+size=$((32 * 1024 * 1024))
144
+ use_log=True, qdev=True)
139
+
145
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x22 0 4k',
140
+run_qemu <<EOF
146
+ use_log=True, qdev=True)
141
+{ "execute": "qmp_capabilities" }
147
+
142
+{ "execute": "x-blockdev-create",
148
+ iotests.log('\nDone')
143
+ "arguments": {
149
+
144
+ "driver": "file",
150
+ vm_src.shutdown()
145
+ "filename": "$TEST_IMG",
151
+ iotests.log('\n--- vm_src log ---')
146
+ "size": 0
152
+ log = vm_src.get_log()
147
+ }
153
+ if log:
148
+}
154
+ iotests.log(log, [filter_qtest, filter_qemu_io])
149
+{ "execute": "x-blockdev-create",
155
+ qsd_src.stop()
150
+ "arguments": {
156
+
151
+ "driver": "$IMGFMT",
157
+ vm_dst.shutdown()
152
+ "file": {
158
+ iotests.log('\n--- vm_dst log ---')
153
+ "driver": "file",
159
+ log = vm_dst.get_log()
154
+ "filename": "$TEST_IMG"
160
+ if log:
155
+ },
161
+ iotests.log(log, [filter_qtest, filter_qemu_io])
156
+ "size": $size,
162
+ qsd_dst.stop()
157
+ "cluster-size": 65536
163
diff --git a/tests/qemu-iotests/tests/qsd-migrate.out b/tests/qemu-iotests/tests/qsd-migrate.out
158
+ }
159
+}
160
+{ "execute": "quit" }
161
+EOF
162
+
163
+_img_info --format-specific | _filter_img_info --format-specific
164
+
165
+echo
166
+echo "=== Invalid BlockdevRef ==="
167
+echo
168
+
169
+run_qemu <<EOF
170
+{ "execute": "qmp_capabilities" }
171
+{ "execute": "x-blockdev-create",
172
+ "arguments": {
173
+ "driver": "$IMGFMT",
174
+ "file": "this doesn't exist",
175
+ "size": $size
176
+ }
177
+}
178
+{ "execute": "quit" }
179
+EOF
180
+
181
+echo
182
+echo "=== Zero size ==="
183
+echo
184
+
185
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
186
+{ "execute": "qmp_capabilities" }
187
+{ "execute": "x-blockdev-create",
188
+ "arguments": {
189
+ "driver": "$IMGFMT",
190
+ "file": "node0",
191
+ "size": 0
192
+ }
193
+}
194
+{ "execute": "quit" }
195
+EOF
196
+
197
+_img_info | _filter_img_info
198
+
199
+echo
200
+echo "=== Maximum size ==="
201
+echo
202
+
203
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
204
+{ "execute": "qmp_capabilities" }
205
+{ "execute": "x-blockdev-create",
206
+ "arguments": {
207
+ "driver": "$IMGFMT",
208
+ "file": "node0",
209
+ "size": 4503599627369984
210
+ }
211
+}
212
+{ "execute": "quit" }
213
+EOF
214
+
215
+_img_info | _filter_img_info
216
+
217
+echo
218
+echo "=== Invalid sizes ==="
219
+echo
220
+
221
+# TODO Negative image sizes aren't handled correctly, but this is a problem
222
+# with QAPI's implementation of the 'size' type and affects other commands as
223
+# well. Once this is fixed, we may want to add a test case here.
224
+
225
+# 1. Misaligned image size
226
+# 2. 2^64 - 512
227
+# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
228
+# 4. 2^63 - 512 (generally valid, but with the image header the file will
229
+# exceed 63 bits)
230
+# 5. 2^52 (512 bytes more than maximum image size)
231
+
232
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
233
+{ "execute": "qmp_capabilities" }
234
+{ "execute": "x-blockdev-create",
235
+ "arguments": {
236
+ "driver": "$IMGFMT",
237
+ "file": "node0",
238
+ "size": 1234
239
+ }
240
+}
241
+{ "execute": "x-blockdev-create",
242
+ "arguments": {
243
+ "driver": "$IMGFMT",
244
+ "file": "node0",
245
+ "size": 18446744073709551104
246
+ }
247
+}
248
+{ "execute": "x-blockdev-create",
249
+ "arguments": {
250
+ "driver": "$IMGFMT",
251
+ "file": "node0",
252
+ "size": 9223372036854775808
253
+ }
254
+}
255
+{ "execute": "x-blockdev-create",
256
+ "arguments": {
257
+ "driver": "$IMGFMT",
258
+ "file": "node0",
259
+ "size": 9223372036854775296
260
+ }
261
+}
262
+{ "execute": "x-blockdev-create",
263
+ "arguments": {
264
+ "driver": "$IMGFMT",
265
+ "file": "node0",
266
+ "size": 4503599627370497
267
+ }
268
+}
269
+{ "execute": "quit" }
270
+EOF
271
+
272
+echo
273
+echo "=== Invalid cluster size ==="
274
+echo
275
+
276
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
277
+{ "execute": "qmp_capabilities" }
278
+{ "execute": "x-blockdev-create",
279
+ "arguments": {
280
+ "driver": "$IMGFMT",
281
+ "file": "node0",
282
+ "size": 67108864,
283
+ "cluster-size": 1234
284
+ }
285
+}
286
+{ "execute": "x-blockdev-create",
287
+ "arguments": {
288
+ "driver": "$IMGFMT",
289
+ "file": "node0",
290
+ "size": 67108864,
291
+ "cluster-size": 128
292
+ }
293
+}
294
+{ "execute": "x-blockdev-create",
295
+ "arguments": {
296
+ "driver": "$IMGFMT",
297
+ "file": "node0",
298
+ "size": 67108864,
299
+ "cluster-size": 4294967296
300
+ }
301
+}
302
+{ "execute": "x-blockdev-create",
303
+ "arguments": {
304
+ "driver": "$IMGFMT",
305
+ "file": "node0",
306
+ "size": 67108864,
307
+ "cluster-size": 9223372036854775808
308
+ }
309
+}
310
+{ "execute": "x-blockdev-create",
311
+ "arguments": {
312
+ "driver": "$IMGFMT",
313
+ "file": "node0",
314
+ "size": 67108864,
315
+ "cluster-size": 18446744073709551104
316
+ }
317
+}
318
+{ "execute": "x-blockdev-create",
319
+ "arguments": {
320
+ "driver": "$IMGFMT",
321
+ "file": "node0",
322
+ "size": 67108864,
323
+ "cluster-size": 0
324
+ }
325
+}
326
+{ "execute": "x-blockdev-create",
327
+ "arguments": {
328
+ "driver": "$IMGFMT",
329
+ "file": "node0",
330
+ "size": 281474976710656,
331
+ "cluster-size": 512
332
+ }
333
+}
334
+{ "execute": "quit" }
335
+EOF
336
+
337
+
338
+# success, all done
339
+echo "*** done"
340
+rm -f $seq.full
341
+status=0
342
diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out
343
new file mode 100644
164
new file mode 100644
344
index XXXXXXX..XXXXXXX
165
index XXXXXXX..XXXXXXX
345
--- /dev/null
166
--- /dev/null
346
+++ b/tests/qemu-iotests/212.out
167
+++ b/tests/qemu-iotests/tests/qsd-migrate.out
347
@@ -XXX,XX +XXX,XX @@
168
@@ -XXX,XX +XXX,XX @@
348
+QA output created by 212
169
+Preparing disk...
349
+
170
+Launching source QSD...
350
+=== Successful image creation (defaults) ===
171
+Launching source VM...
351
+
172
+Launching destination QSD...
352
+Testing:
173
+Launching destination VM...
353
+QMP_VERSION
174
+
354
+{"return": {}}
175
+Test I/O on the source
355
+{"return": {}}
176
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x11 0 4k\""}}
356
+{"return": {}}
177
+{"return": ""}
357
+{"return": {}}
178
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}}
358
+{"return": {}}
179
+{"return": ""}
359
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
180
+
360
+
181
+Starting migration...
361
+image: TEST_DIR/t.IMGFMT
182
+{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}}
362
+file format: IMGFMT
183
+{"return": {}}
363
+virtual size: 128M (134217728 bytes)
184
+{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}}
364
+
185
+{"return": {}}
365
+=== Successful image creation (explicit defaults) ===
186
+{"execute": "migrate", "arguments": {"uri": "unix:SOCK_DIR/PID-migrate.sock"}}
366
+
187
+{"return": {}}
367
+Testing:
188
+
368
+QMP_VERSION
189
+Pre-switchover: Reconfigure QSD instances
369
+{"return": {}}
190
+{"return": {}}
370
+{"return": {}}
191
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}}
371
+{"return": {}}
192
+{"return": ""}
372
+{"return": {}}
193
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read 0 4k\""}}
373
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
194
+{"return": ""}
374
+
195
+{"return": {}}
375
+image: TEST_DIR/t.IMGFMT
196
+
376
+file format: IMGFMT
197
+Completing migration...
377
+virtual size: 64M (67108864 bytes)
198
+{"execute": "migrate-continue", "arguments": {"state": "pre-switchover"}}
378
+
199
+{"return": {}}
379
+=== Successful image creation (with non-default options) ===
200
+
380
+
201
+Test I/O on the destination
381
+Testing:
202
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}}
382
+QMP_VERSION
203
+{"return": ""}
383
+{"return": {}}
204
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x22 0 4k\""}}
384
+{"return": {}}
205
+{"return": ""}
385
+{"return": {}}
206
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x22 0 4k\""}}
386
+{"return": {}}
207
+{"return": ""}
387
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
208
+
388
+
209
+Done
389
+image: TEST_DIR/t.IMGFMT
210
+
390
+file format: IMGFMT
211
+--- vm_src log ---
391
+virtual size: 32M (33554432 bytes)
212
+wrote 4096/4096 bytes at offset 0
392
+
213
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
393
+=== Invalid BlockdevRef ===
214
+read 4096/4096 bytes at offset 0
394
+
215
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
395
+Testing:
216
+read 4096/4096 bytes at offset 0
396
+QMP_VERSION
217
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
397
+{"return": {}}
218
+
398
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
219
+--- vm_dst log ---
399
+{"return": {}}
220
+read 4096/4096 bytes at offset 0
400
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
221
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
401
+
222
+read 4096/4096 bytes at offset 0
402
+
223
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
403
+=== Zero size ===
224
+wrote 4096/4096 bytes at offset 0
404
+
225
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
405
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
226
+read 4096/4096 bytes at offset 0
406
+QMP_VERSION
227
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
407
+{"return": {}}
408
+{"return": {}}
409
+{"return": {}}
410
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
411
+
412
+image: TEST_DIR/t.IMGFMT
413
+file format: IMGFMT
414
+virtual size: 0 (0 bytes)
415
+
416
+=== Maximum size ===
417
+
418
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
419
+QMP_VERSION
420
+{"return": {}}
421
+{"return": {}}
422
+{"return": {}}
423
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
424
+
425
+image: TEST_DIR/t.IMGFMT
426
+file format: IMGFMT
427
+virtual size: 4096T (4503599627369984 bytes)
428
+
429
+=== Invalid sizes ===
430
+
431
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
432
+QMP_VERSION
433
+{"return": {}}
434
+{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}}
435
+{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}}
436
+{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}}
437
+{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}}
438
+{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}}
439
+{"return": {}}
440
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
441
+
442
+
443
+=== Invalid cluster size ===
444
+
445
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
446
+QMP_VERSION
447
+{"return": {}}
448
+{"error": {"class": "GenericError", "desc": "Cluster size must be a multiple of 512 bytes"}}
449
+{"error": {"class": "GenericError", "desc": "Cluster size must be a multiple of 512 bytes"}}
450
+{"error": {"class": "GenericError", "desc": "Cluster size is too large"}}
451
+{"error": {"class": "GenericError", "desc": "Cluster size is too large"}}
452
+{"error": {"class": "GenericError", "desc": "Cluster size is too large"}}
453
+{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}}
454
+{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}}
455
+{"return": {}}
456
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
457
+
458
+*** done
459
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
460
index XXXXXXX..XXXXXXX 100644
461
--- a/tests/qemu-iotests/group
462
+++ b/tests/qemu-iotests/group
463
@@ -XXX,XX +XXX,XX @@
464
209 rw auto quick
465
210 rw auto
466
211 rw auto quick
467
+212 rw auto quick
468
--
228
--
469
2.13.6
229
2.48.1
470
471
diff view generated by jsdifflib
1
This tests different types of operations on inactive block nodes
2
(including graph changes, block jobs and NBD exports) to make sure that
3
users manually activating and inactivating nodes doesn't break things.
4
5
Support for inactive nodes in other export types will have to come with
6
separate test cases because they have different dependencies like blkio
7
or root permissions and we don't want to disable this basic test when
8
they are not fulfilled.
9
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Acked-by: Fabiano Rosas <farosas@suse.de>
12
Message-ID: <20250204211407.381505-17-kwolf@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
14
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
---
16
---
4
tests/qemu-iotests/211 | 246 +++++++++++++++++++++++++++++++++++++++++++++
17
tests/qemu-iotests/iotests.py | 4 +
5
tests/qemu-iotests/211.out | 97 ++++++++++++++++++
18
tests/qemu-iotests/tests/inactive-node-nbd | 303 ++++++++++++++++++
6
tests/qemu-iotests/group | 1 +
19
.../qemu-iotests/tests/inactive-node-nbd.out | 239 ++++++++++++++
7
3 files changed, 344 insertions(+)
20
3 files changed, 546 insertions(+)
8
create mode 100755 tests/qemu-iotests/211
21
create mode 100755 tests/qemu-iotests/tests/inactive-node-nbd
9
create mode 100644 tests/qemu-iotests/211.out
22
create mode 100644 tests/qemu-iotests/tests/inactive-node-nbd.out
10
23
11
diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211
24
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
25
index XXXXXXX..XXXXXXX 100644
26
--- a/tests/qemu-iotests/iotests.py
27
+++ b/tests/qemu-iotests/iotests.py
28
@@ -XXX,XX +XXX,XX @@ def add_incoming(self, addr):
29
self._args.append(addr)
30
return self
31
32
+ def add_paused(self):
33
+ self._args.append('-S')
34
+ return self
35
+
36
def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage:
37
cmd = 'human-monitor-command'
38
kwargs: Dict[str, Any] = {'command-line': command_line}
39
diff --git a/tests/qemu-iotests/tests/inactive-node-nbd b/tests/qemu-iotests/tests/inactive-node-nbd
12
new file mode 100755
40
new file mode 100755
13
index XXXXXXX..XXXXXXX
41
index XXXXXXX..XXXXXXX
14
--- /dev/null
42
--- /dev/null
15
+++ b/tests/qemu-iotests/211
43
+++ b/tests/qemu-iotests/tests/inactive-node-nbd
16
@@ -XXX,XX +XXX,XX @@
44
@@ -XXX,XX +XXX,XX @@
17
+#!/bin/bash
45
+#!/usr/bin/env python3
46
+# group: rw quick
18
+#
47
+#
19
+# Test VDI and file image creation
48
+# Copyright (C) Red Hat, Inc.
20
+#
21
+# Copyright (C) 2018 Red Hat, Inc.
22
+#
49
+#
23
+# This program is free software; you can redistribute it and/or modify
50
+# This program is free software; you can redistribute it and/or modify
24
+# it under the terms of the GNU General Public License as published by
51
+# it under the terms of the GNU General Public License as published by
25
+# the Free Software Foundation; either version 2 of the License, or
52
+# the Free Software Foundation; either version 2 of the License, or
26
+# (at your option) any later version.
53
+# (at your option) any later version.
...
...
31
+# GNU General Public License for more details.
58
+# GNU General Public License for more details.
32
+#
59
+#
33
+# You should have received a copy of the GNU General Public License
60
+# You should have received a copy of the GNU General Public License
34
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
61
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
35
+#
62
+#
36
+
63
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
37
+# creator
64
+
38
+owner=kwolf@redhat.com
65
+import iotests
39
+
66
+
40
+seq=`basename $0`
67
+from iotests import QemuIoInteractive
41
+echo "QA output created by $seq"
68
+from iotests import filter_qemu_io, filter_qtest, filter_qmp_testfiles
42
+
69
+
43
+here=`pwd`
70
+iotests.script_initialize(supported_fmts=['generic'],
44
+status=1    # failure is the default!
71
+ supported_protocols=['file'],
45
+
72
+ supported_platforms=['linux'])
46
+# get standard environment, filters and checks
73
+
47
+. ./common.rc
74
+def get_export(node_name='disk-fmt', allow_inactive=None):
48
+. ./common.filter
75
+ exp = {
49
+
76
+ 'id': 'exp0',
50
+_supported_fmt vdi
77
+ 'type': 'nbd',
51
+_supported_proto file
78
+ 'node-name': node_name,
52
+_supported_os Linux
79
+ 'writable': True,
53
+
80
+ }
54
+function do_run_qemu()
81
+
55
+{
82
+ if allow_inactive is not None:
56
+ echo Testing: "$@"
83
+ exp['allow-inactive'] = allow_inactive
57
+ $QEMU -nographic -qmp stdio -serial none "$@"
84
+
58
+ echo
85
+ return exp
59
+}
86
+
60
+
87
+def node_is_active(_vm, node_name):
61
+function run_qemu()
88
+ nodes = _vm.cmd('query-named-block-nodes', flat=True)
62
+{
89
+ node = next(n for n in nodes if n['node-name'] == node_name)
63
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
90
+ return node['active']
64
+ | _filter_qemu | _filter_imgfmt \
91
+
65
+ | _filter_actual_image_size
92
+with iotests.FilePath('disk.img') as path, \
66
+}
93
+ iotests.FilePath('snap.qcow2') as snap_path, \
67
+
94
+ iotests.FilePath('snap2.qcow2') as snap2_path, \
68
+echo
95
+ iotests.FilePath('target.img') as target_path, \
69
+echo "=== Successful image creation (defaults) ==="
96
+ iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \
70
+echo
97
+ iotests.VM() as vm:
71
+
98
+
72
+size=$((128 * 1024 * 1024))
99
+ img_size = '10M'
73
+
100
+
74
+run_qemu <<EOF
101
+ iotests.log('Preparing disk...')
75
+{ "execute": "qmp_capabilities" }
102
+ iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size)
76
+{ "execute": "x-blockdev-create",
103
+ iotests.qemu_img_create('-f', iotests.imgfmt, target_path, img_size)
77
+ "arguments": {
104
+
78
+ "driver": "file",
105
+ iotests.qemu_img_create('-f', 'qcow2', '-b', path, '-F', iotests.imgfmt,
79
+ "filename": "$TEST_IMG",
106
+ snap_path)
80
+ "size": 0
107
+ iotests.qemu_img_create('-f', 'qcow2', '-b', snap_path, '-F', 'qcow2',
81
+ }
108
+ snap2_path)
82
+}
109
+
83
+{ "execute": "blockdev-add",
110
+ iotests.log('Launching VM...')
84
+ "arguments": {
111
+ vm.add_blockdev(f'file,node-name=disk-file,filename={path}')
85
+ "driver": "file",
112
+ vm.add_blockdev(f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,'
86
+ "node-name": "imgfile",
113
+ 'active=off')
87
+ "filename": "$TEST_IMG"
114
+ vm.add_blockdev(f'file,node-name=target-file,filename={target_path}')
88
+ }
115
+ vm.add_blockdev(f'{iotests.imgfmt},file=target-file,node-name=target-fmt')
89
+}
116
+ vm.add_blockdev(f'file,node-name=snap-file,filename={snap_path}')
90
+{ "execute": "x-blockdev-create",
117
+ vm.add_blockdev(f'file,node-name=snap2-file,filename={snap2_path}')
91
+ "arguments": {
118
+
92
+ "driver": "$IMGFMT",
119
+ # Actually running the VM activates all images
93
+ "file": "imgfile",
120
+ vm.add_paused()
94
+ "size": $size
121
+
95
+ }
122
+ vm.launch()
96
+}
123
+ vm.qmp_log('nbd-server-start',
97
+{ "execute": "quit" }
124
+ addr={'type': 'unix', 'data':{'path': nbd_sock}},
98
+EOF
125
+ filters=[filter_qmp_testfiles])
99
+
126
+
100
+_img_info --format-specific | _filter_img_info --format-specific
127
+ iotests.log('\n=== Creating export of inactive node ===')
101
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
128
+
102
+
129
+ iotests.log('\nExports activate nodes without allow-inactive')
103
+echo
130
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
104
+echo "=== Successful image creation (explicit defaults) ==="
131
+ vm.qmp_log('block-export-add', **get_export())
105
+echo
132
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
106
+
133
+ vm.qmp_log('query-block-exports')
107
+# Choose a different size to show that we got a new image
134
+ vm.qmp_log('block-export-del', id='exp0')
108
+size=$((64 * 1024 * 1024))
135
+ vm.event_wait('BLOCK_EXPORT_DELETED')
109
+
136
+ vm.qmp_log('query-block-exports')
110
+run_qemu <<EOF
137
+
111
+{ "execute": "qmp_capabilities" }
138
+ iotests.log('\nExports activate nodes with allow-inactive=false')
112
+{ "execute": "x-blockdev-create",
139
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
113
+ "arguments": {
140
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
114
+ "driver": "file",
141
+ vm.qmp_log('block-export-add', **get_export(allow_inactive=False))
115
+ "filename": "$TEST_IMG",
142
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
116
+ "size": 0
143
+ vm.qmp_log('query-block-exports')
117
+ }
144
+ vm.qmp_log('block-export-del', id='exp0')
118
+}
145
+ vm.event_wait('BLOCK_EXPORT_DELETED')
119
+{ "execute": "x-blockdev-create",
146
+ vm.qmp_log('query-block-exports')
120
+ "arguments": {
147
+
121
+ "driver": "$IMGFMT",
148
+ iotests.log('\nExport leaves nodes inactive with allow-inactive=true')
122
+ "file": {
149
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
123
+ "driver": "file",
150
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
124
+ "filename": "$TEST_IMG"
151
+ vm.qmp_log('block-export-add', **get_export(allow_inactive=True))
125
+ },
152
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
126
+ "size": $size,
153
+ vm.qmp_log('query-block-exports')
127
+ "preallocation": "off"
154
+ vm.qmp_log('block-export-del', id='exp0')
128
+ }
155
+ vm.event_wait('BLOCK_EXPORT_DELETED')
129
+}
156
+ vm.qmp_log('query-block-exports')
130
+{ "execute": "quit" }
157
+
131
+EOF
158
+ iotests.log('\n=== Inactivating node with existing export ===')
132
+
159
+
133
+_img_info --format-specific | _filter_img_info --format-specific
160
+ iotests.log('\nInactivating nodes with an export fails without '
134
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
161
+ 'allow-inactive')
135
+
162
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
136
+echo
163
+ vm.qmp_log('block-export-add', **get_export(node_name='disk-fmt'))
137
+echo "=== Successful image creation (with non-default options) ==="
164
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
138
+echo
165
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
139
+
166
+ vm.qmp_log('query-block-exports')
140
+# Choose a different size to show that we got a new image
167
+ vm.qmp_log('block-export-del', id='exp0')
141
+size=$((32 * 1024 * 1024))
168
+ vm.event_wait('BLOCK_EXPORT_DELETED')
142
+
169
+ vm.qmp_log('query-block-exports')
143
+run_qemu <<EOF
170
+
144
+{ "execute": "qmp_capabilities" }
171
+ iotests.log('\nInactivating nodes with an export fails with '
145
+{ "execute": "x-blockdev-create",
172
+ 'allow-inactive=false')
146
+ "arguments": {
173
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
147
+ "driver": "file",
174
+ vm.qmp_log('block-export-add',
148
+ "filename": "$TEST_IMG",
175
+ **get_export(node_name='disk-fmt', allow_inactive=False))
149
+ "size": 0
176
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
150
+ }
177
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
151
+}
178
+ vm.qmp_log('query-block-exports')
152
+{ "execute": "x-blockdev-create",
179
+ vm.qmp_log('block-export-del', id='exp0')
153
+ "arguments": {
180
+ vm.event_wait('BLOCK_EXPORT_DELETED')
154
+ "driver": "$IMGFMT",
181
+ vm.qmp_log('query-block-exports')
155
+ "file": {
182
+
156
+ "driver": "file",
183
+ iotests.log('\nInactivating nodes with an export works with '
157
+ "filename": "$TEST_IMG"
184
+ 'allow-inactive=true')
158
+ },
185
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
159
+ "size": $size,
186
+ vm.qmp_log('block-export-add',
160
+ "preallocation": "metadata"
187
+ **get_export(node_name='disk-fmt', allow_inactive=True))
161
+ }
188
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
162
+}
189
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
163
+{ "execute": "quit" }
190
+ vm.qmp_log('query-block-exports')
164
+EOF
191
+ vm.qmp_log('block-export-del', id='exp0')
165
+
192
+ vm.event_wait('BLOCK_EXPORT_DELETED')
166
+_img_info --format-specific | _filter_img_info --format-specific
193
+ vm.qmp_log('query-block-exports')
167
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
194
+
168
+
195
+ iotests.log('\n=== Inactive nodes with parent ===')
169
+echo
196
+
170
+echo "=== Invalid BlockdevRef ==="
197
+ iotests.log('\nInactivating nodes with an active parent fails')
171
+echo
198
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
172
+
199
+ vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False)
173
+run_qemu <<EOF
200
+ iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file'))
174
+{ "execute": "qmp_capabilities" }
201
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
175
+{ "execute": "x-blockdev-create",
202
+
176
+ "arguments": {
203
+ iotests.log('\nInactivating nodes with an inactive parent works')
177
+ "driver": "$IMGFMT",
204
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
178
+ "file": "this doesn't exist",
205
+ vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False)
179
+ "size": $size
206
+ iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file'))
180
+ }
207
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
181
+}
208
+
182
+{ "execute": "quit" }
209
+ iotests.log('\nCreating active parent node with an inactive child fails')
183
+EOF
210
+ vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt',
184
+
211
+ node_name='disk-filter')
185
+echo
212
+ vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt',
186
+echo "=== Zero size ==="
213
+ node_name='disk-filter', active=True)
187
+echo
214
+
188
+
215
+ iotests.log('\nCreating inactive parent node with an inactive child works')
189
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
216
+ vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt',
190
+{ "execute": "qmp_capabilities" }
217
+ node_name='disk-filter', active=False)
191
+{ "execute": "x-blockdev-create",
218
+ vm.qmp_log('blockdev-del', node_name='disk-filter')
192
+ "arguments": {
219
+
193
+ "driver": "$IMGFMT",
220
+ iotests.log('\n=== Resizing an inactive node ===')
194
+ "file": "node0",
221
+ vm.qmp_log('block_resize', node_name='disk-fmt', size=16*1024*1024)
195
+ "size": 0
222
+
196
+ }
223
+ iotests.log('\n=== Taking a snapshot of an inactive node ===')
197
+}
224
+
198
+{ "execute": "quit" }
225
+ iotests.log('\nActive overlay over inactive backing file automatically '
199
+EOF
226
+ 'makes both inactive for compatibility')
200
+
227
+ vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt',
201
+_img_info | _filter_img_info
228
+ file='snap-file', backing=None)
202
+
229
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
203
+echo
230
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
204
+echo "=== Maximum size ==="
231
+ vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt')
205
+echo
232
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
206
+
233
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
207
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
234
+ vm.qmp_log('blockdev-del', node_name='snap-fmt')
208
+{ "execute": "qmp_capabilities" }
235
+
209
+{ "execute": "x-blockdev-create",
236
+ iotests.log('\nInactive overlay over inactive backing file just works')
210
+ "arguments": {
237
+ vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt',
211
+ "driver": "$IMGFMT",
238
+ file='snap-file', backing=None, active=False)
212
+ "file": "node0",
239
+ vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt')
213
+ "size": 562949819203584
240
+
214
+ }
241
+ iotests.log('\n=== Block jobs with inactive nodes ===')
215
+}
242
+
216
+{ "execute": "quit" }
243
+ iotests.log('\nStreaming into an inactive node')
217
+EOF
244
+ vm.qmp_log('block-stream', device='snap-fmt',
218
+
245
+ filters=[iotests.filter_qmp_generated_node_ids])
219
+_img_info | _filter_img_info
246
+
220
+
247
+ iotests.log('\nCommitting an inactive root node (active commit)')
221
+echo
248
+ vm.qmp_log('block-commit', job_id='job0', device='snap-fmt',
222
+echo "=== Invalid sizes ==="
249
+ filters=[iotests.filter_qmp_generated_node_ids])
223
+echo
250
+
224
+
251
+ iotests.log('\nCommitting an inactive intermediate node to inactive base')
225
+# TODO Negative image sizes aren't handled correctly, but this is a problem
252
+ vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap2-fmt',
226
+# with QAPI's implementation of the 'size' type and affects other commands as
253
+ file='snap2-file', backing='snap-fmt', active=False)
227
+# well. Once this is fixed, we may want to add a test case here.
254
+
228
+
255
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
229
+# 1. 2^64 - 512
256
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
230
+# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
257
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
231
+# 3. 0x1fffff8000001 (one byte more than maximum image size for VDI)
258
+
232
+
259
+ vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt',
233
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
260
+ top_node='snap-fmt',
234
+{ "execute": "qmp_capabilities" }
261
+ filters=[iotests.filter_qmp_generated_node_ids])
235
+{ "execute": "x-blockdev-create",
262
+
236
+ "arguments": {
263
+ iotests.log('\nCommitting an inactive intermediate node to active base')
237
+ "driver": "$IMGFMT",
264
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
238
+ "file": "node0",
265
+ vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt',
239
+ "size": 18446744073709551104
266
+ top_node='snap-fmt',
240
+ }
267
+ filters=[iotests.filter_qmp_generated_node_ids])
241
+}
268
+
242
+{ "execute": "x-blockdev-create",
269
+ iotests.log('\nMirror from inactive source to active target')
243
+ "arguments": {
270
+ vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt',
244
+ "driver": "$IMGFMT",
271
+ target='target-fmt', sync='full',
245
+ "file": "node0",
272
+ filters=[iotests.filter_qmp_generated_node_ids])
246
+ "size": 9223372036854775808
273
+
247
+ }
274
+ iotests.log('\nMirror from active source to inactive target')
248
+}
275
+
249
+{ "execute": "x-blockdev-create",
276
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
250
+ "arguments": {
277
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
251
+ "driver": "$IMGFMT",
278
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
252
+ "file": "node0",
279
+ iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
253
+ "size": 562949819203585
280
+
254
+ }
281
+ # Activating snap2-fmt recursively activates the whole backing chain
255
+}
282
+ vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=True)
256
+{ "execute": "quit" }
283
+ vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False)
257
+EOF
284
+
258
+
285
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
259
+# success, all done
286
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
260
+echo "*** done"
287
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
261
+rm -f $seq.full
288
+ iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
262
+status=0
289
+
263
diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out
290
+ vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt',
291
+ target='target-fmt', sync='full',
292
+ filters=[iotests.filter_qmp_generated_node_ids])
293
+
294
+ iotests.log('\nBackup from active source to inactive target')
295
+
296
+ vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt',
297
+ target='target-fmt', sync='full',
298
+ filters=[iotests.filter_qmp_generated_node_ids])
299
+
300
+ iotests.log('\nBackup from inactive source to active target')
301
+
302
+ # Inactivating snap2-fmt recursively inactivates the whole backing chain
303
+ vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=False)
304
+ vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=True)
305
+
306
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
307
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
308
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
309
+ iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
310
+
311
+ vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt',
312
+ target='target-fmt', sync='full',
313
+ filters=[iotests.filter_qmp_generated_node_ids])
314
+
315
+ iotests.log('\n=== Accessing export on inactive node ===')
316
+
317
+ # Use the target node because it has the right image format and isn't the
318
+ # (read-only) backing file of a qcow2 node
319
+ vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False)
320
+ vm.qmp_log('block-export-add',
321
+ **get_export(node_name='target-fmt', allow_inactive=True))
322
+
323
+ # The read should succeed, everything else should fail gracefully
324
+ qemu_io = QemuIoInteractive('-f', 'raw',
325
+ f'nbd+unix:///target-fmt?socket={nbd_sock}')
326
+ iotests.log(qemu_io.cmd('read 0 64k'), filters=[filter_qemu_io])
327
+ iotests.log(qemu_io.cmd('write 0 64k'), filters=[filter_qemu_io])
328
+ iotests.log(qemu_io.cmd('write -z 0 64k'), filters=[filter_qemu_io])
329
+ iotests.log(qemu_io.cmd('write -zu 0 64k'), filters=[filter_qemu_io])
330
+ iotests.log(qemu_io.cmd('discard 0 64k'), filters=[filter_qemu_io])
331
+ iotests.log(qemu_io.cmd('flush'), filters=[filter_qemu_io])
332
+ iotests.log(qemu_io.cmd('map'), filters=[filter_qemu_io])
333
+ qemu_io.close()
334
+
335
+ iotests.log('\n=== Resuming VM activates all images ===')
336
+ vm.qmp_log('cont')
337
+
338
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
339
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
340
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
341
+ iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
342
+
343
+ iotests.log('\nShutting down...')
344
+ vm.shutdown()
345
+ log = vm.get_log()
346
+ if log:
347
+ iotests.log(log, [filter_qtest, filter_qemu_io])
348
diff --git a/tests/qemu-iotests/tests/inactive-node-nbd.out b/tests/qemu-iotests/tests/inactive-node-nbd.out
264
new file mode 100644
349
new file mode 100644
265
index XXXXXXX..XXXXXXX
350
index XXXXXXX..XXXXXXX
266
--- /dev/null
351
--- /dev/null
267
+++ b/tests/qemu-iotests/211.out
352
+++ b/tests/qemu-iotests/tests/inactive-node-nbd.out
268
@@ -XXX,XX +XXX,XX @@
353
@@ -XXX,XX +XXX,XX @@
269
+QA output created by 211
354
+Preparing disk...
270
+
355
+Launching VM...
271
+=== Successful image creation (defaults) ===
356
+{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}}
272
+
357
+{"return": {}}
273
+Testing:
358
+
274
+QMP_VERSION
359
+=== Creating export of inactive node ===
275
+{"return": {}}
360
+
276
+{"return": {}}
361
+Exports activate nodes without allow-inactive
277
+{"return": {}}
362
+disk-fmt active: False
278
+{"return": {}}
363
+{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
279
+{"return": {}}
364
+{"return": {}}
280
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
365
+disk-fmt active: True
281
+
366
+{"execute": "query-block-exports", "arguments": {}}
282
+image: TEST_DIR/t.IMGFMT
367
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
283
+file format: IMGFMT
368
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
284
+virtual size: 128M (134217728 bytes)
369
+{"return": {}}
285
+[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false}]
370
+{"execute": "query-block-exports", "arguments": {}}
286
+
371
+{"return": []}
287
+=== Successful image creation (explicit defaults) ===
372
+
288
+
373
+Exports activate nodes with allow-inactive=false
289
+Testing:
374
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
290
+QMP_VERSION
375
+{"return": {}}
291
+{"return": {}}
376
+disk-fmt active: False
292
+{"return": {}}
377
+{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
293
+{"return": {}}
378
+{"return": {}}
294
+{"return": {}}
379
+disk-fmt active: True
295
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
380
+{"execute": "query-block-exports", "arguments": {}}
296
+
381
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
297
+image: TEST_DIR/t.IMGFMT
382
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
298
+file format: IMGFMT
383
+{"return": {}}
299
+virtual size: 64M (67108864 bytes)
384
+{"execute": "query-block-exports", "arguments": {}}
300
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
385
+{"return": []}
301
+
386
+
302
+=== Successful image creation (with non-default options) ===
387
+Export leaves nodes inactive with allow-inactive=true
303
+
388
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
304
+Testing:
389
+{"return": {}}
305
+QMP_VERSION
390
+disk-fmt active: False
306
+{"return": {}}
391
+{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
307
+{"return": {}}
392
+{"return": {}}
308
+{"return": {}}
393
+disk-fmt active: False
309
+{"return": {}}
394
+{"execute": "query-block-exports", "arguments": {}}
310
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
395
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
311
+
396
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
312
+image: TEST_DIR/t.IMGFMT
397
+{"return": {}}
313
+file format: IMGFMT
398
+{"execute": "query-block-exports", "arguments": {}}
314
+virtual size: 32M (33554432 bytes)
399
+{"return": []}
315
+[{ "start": 0, "length": 3072, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
400
+
316
+{ "start": 3072, "length": 33551360, "depth": 0, "zero": true, "data": true, "offset": OFFSET}]
401
+=== Inactivating node with existing export ===
317
+
402
+
318
+=== Invalid BlockdevRef ===
403
+Inactivating nodes with an export fails without allow-inactive
319
+
404
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
320
+Testing:
405
+{"return": {}}
321
+QMP_VERSION
406
+{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
322
+{"return": {}}
407
+{"return": {}}
323
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
408
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
324
+{"return": {}}
409
+{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}}
325
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
410
+disk-fmt active: True
326
+
411
+{"execute": "query-block-exports", "arguments": {}}
327
+
412
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
328
+=== Zero size ===
413
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
329
+
414
+{"return": {}}
330
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
415
+{"execute": "query-block-exports", "arguments": {}}
331
+QMP_VERSION
416
+{"return": []}
332
+{"return": {}}
417
+
333
+{"return": {}}
418
+Inactivating nodes with an export fails with allow-inactive=false
334
+{"return": {}}
419
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
335
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
420
+{"return": {}}
336
+
421
+{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
337
+image: TEST_DIR/t.IMGFMT
422
+{"return": {}}
338
+file format: IMGFMT
423
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
339
+virtual size: 0 (0 bytes)
424
+{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}}
340
+
425
+disk-fmt active: True
341
+=== Maximum size ===
426
+{"execute": "query-block-exports", "arguments": {}}
342
+
427
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
343
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
428
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
344
+QMP_VERSION
429
+{"return": {}}
345
+{"return": {}}
430
+{"execute": "query-block-exports", "arguments": {}}
346
+{"return": {}}
431
+{"return": []}
347
+{"return": {}}
432
+
348
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
433
+Inactivating nodes with an export works with allow-inactive=true
349
+
434
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
350
+image: TEST_DIR/t.IMGFMT
435
+{"return": {}}
351
+file format: IMGFMT
436
+{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
352
+virtual size: 512T (562949819203584 bytes)
437
+{"return": {}}
353
+
438
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
354
+=== Invalid sizes ===
439
+{"return": {}}
355
+
440
+disk-fmt active: False
356
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
441
+{"execute": "query-block-exports", "arguments": {}}
357
+QMP_VERSION
442
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
358
+{"return": {}}
443
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
359
+{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000)"}}
444
+{"return": {}}
360
+{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000)"}}
445
+{"execute": "query-block-exports", "arguments": {}}
361
+{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000)"}}
446
+{"return": []}
362
+{"return": {}}
447
+
363
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
448
+=== Inactive nodes with parent ===
364
+
449
+
365
+*** done
450
+Inactivating nodes with an active parent fails
366
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
451
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
367
index XXXXXXX..XXXXXXX 100644
452
+{"return": {}}
368
--- a/tests/qemu-iotests/group
453
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}}
369
+++ b/tests/qemu-iotests/group
454
+{"error": {"class": "GenericError", "desc": "Node has active parent node"}}
370
@@ -XXX,XX +XXX,XX @@
455
+disk-file active: True
371
208 rw auto quick
456
+disk-fmt active: True
372
209 rw auto quick
457
+
373
210 rw auto
458
+Inactivating nodes with an inactive parent works
374
+211 rw auto quick
459
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
460
+{"return": {}}
461
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}}
462
+{"return": {}}
463
+disk-file active: False
464
+disk-fmt active: False
465
+
466
+Creating active parent node with an inactive child fails
467
+{"execute": "blockdev-add", "arguments": {"driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}}
468
+{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}}
469
+{"execute": "blockdev-add", "arguments": {"active": true, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}}
470
+{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}}
471
+
472
+Creating inactive parent node with an inactive child works
473
+{"execute": "blockdev-add", "arguments": {"active": false, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}}
474
+{"return": {}}
475
+{"execute": "blockdev-del", "arguments": {"node-name": "disk-filter"}}
476
+{"return": {}}
477
+
478
+=== Resizing an inactive node ===
479
+{"execute": "block_resize", "arguments": {"node-name": "disk-fmt", "size": 16777216}}
480
+{"error": {"class": "GenericError", "desc": "Permission 'resize' unavailable on inactive node"}}
481
+
482
+=== Taking a snapshot of an inactive node ===
483
+
484
+Active overlay over inactive backing file automatically makes both inactive for compatibility
485
+{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}}
486
+{"return": {}}
487
+disk-fmt active: False
488
+snap-fmt active: True
489
+{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}}
490
+{"return": {}}
491
+disk-fmt active: False
492
+snap-fmt active: False
493
+{"execute": "blockdev-del", "arguments": {"node-name": "snap-fmt"}}
494
+{"return": {}}
495
+
496
+Inactive overlay over inactive backing file just works
497
+{"execute": "blockdev-add", "arguments": {"active": false, "backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}}
498
+{"return": {}}
499
+{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}}
500
+{"return": {}}
501
+
502
+=== Block jobs with inactive nodes ===
503
+
504
+Streaming into an inactive node
505
+{"execute": "block-stream", "arguments": {"device": "snap-fmt"}}
506
+{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap-fmt' can't be a file child of active 'NODE_NAME'"}}
507
+
508
+Committing an inactive root node (active commit)
509
+{"execute": "block-commit", "arguments": {"device": "snap-fmt", "job-id": "job0"}}
510
+{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}}
511
+
512
+Committing an inactive intermediate node to inactive base
513
+{"execute": "blockdev-add", "arguments": {"active": false, "backing": "snap-fmt", "driver": "qcow2", "file": "snap2-file", "node-name": "snap2-fmt"}}
514
+{"return": {}}
515
+disk-fmt active: False
516
+snap-fmt active: False
517
+snap2-fmt active: False
518
+{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}}
519
+{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}}
520
+
521
+Committing an inactive intermediate node to active base
522
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
523
+{"return": {}}
524
+{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}}
525
+{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}}
526
+
527
+Mirror from inactive source to active target
528
+{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}}
529
+{"error": {"class": "GenericError", "desc": "Inactive 'snap2-fmt' can't be a backing child of active 'NODE_NAME'"}}
530
+
531
+Mirror from active source to inactive target
532
+disk-fmt active: True
533
+snap-fmt active: False
534
+snap2-fmt active: False
535
+target-fmt active: True
536
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "snap2-fmt"}}
537
+{"return": {}}
538
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}}
539
+{"return": {}}
540
+disk-fmt active: True
541
+snap-fmt active: True
542
+snap2-fmt active: True
543
+target-fmt active: False
544
+{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}}
545
+{"error": {"class": "GenericError", "desc": "Permission 'write' unavailable on inactive node"}}
546
+
547
+Backup from active source to inactive target
548
+{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}}
549
+{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'target-fmt' can't be a target child of active 'NODE_NAME'"}}
550
+
551
+Backup from inactive source to active target
552
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "snap2-fmt"}}
553
+{"return": {}}
554
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "target-fmt"}}
555
+{"return": {}}
556
+disk-fmt active: False
557
+snap-fmt active: False
558
+snap2-fmt active: False
559
+target-fmt active: True
560
+{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}}
561
+{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap2-fmt' can't be a file child of active 'NODE_NAME'"}}
562
+
563
+=== Accessing export on inactive node ===
564
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}}
565
+{"return": {}}
566
+{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "target-fmt", "type": "nbd", "writable": true}}
567
+{"return": {}}
568
+read 65536/65536 bytes at offset 0
569
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
570
+
571
+write failed: Operation not permitted
572
+
573
+write failed: Operation not permitted
574
+
575
+write failed: Operation not permitted
576
+
577
+discard failed: Operation not permitted
578
+
579
+
580
+qemu-io: Failed to get allocation status: Operation not permitted
581
+
582
+
583
+=== Resuming VM activates all images ===
584
+{"execute": "cont", "arguments": {}}
585
+{"return": {}}
586
+disk-fmt active: True
587
+snap-fmt active: True
588
+snap2-fmt active: True
589
+target-fmt active: True
590
+
591
+Shutting down...
592
+
375
--
593
--
376
2.13.6
594
2.48.1
377
378
diff view generated by jsdifflib
1
From: Fabiano Rosas <farosas@linux.vnet.ibm.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
The protocol_name field is used when selecting a driver via protocol
3
BLOCK_OP_TYPE_DATAPLANE prevents BlockDriverState from being used by
4
syntax (i.e. <protocol_name>:<filename:options:...>). Drivers that are
4
virtio-blk/virtio-scsi with IOThread. Commit b112a65c52aa ("block:
5
only selected explicitly (e.g. driver=replication,mode=primary,...)
5
declare blockjobs and dataplane friends!") eliminated the main reason
6
should not have a protocol_name.
6
for this blocker in 2014.
7
7
8
This patch removes the protocol_name field from the brdv_replication
8
Nowadays the block layer supports I/O from multiple AioContexts, so
9
structure so that attempts to invoke this driver using protocol syntax
9
there is even less reason to block IOThread users. Any legitimate
10
will fail gracefully:
10
reasons related to interference would probably also apply to
11
non-IOThread users.
11
12
12
$ qemu-img info replication:foo
13
The only remaining users are bdrv_op_unblock(BLOCK_OP_TYPE_DATAPLANE)
13
qemu-img: Could not open 'replication:': Unknown protocol 'replication'
14
calls after bdrv_op_block_all(). If we remove BLOCK_OP_TYPE_DATAPLANE
15
their behavior doesn't change.
14
16
15
Buglink: https://bugs.launchpad.net/qemu/+bug/1726733
17
Existing bdrv_op_block_all() callers that don't explicitly unblock
16
Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
18
BLOCK_OP_TYPE_DATAPLANE seem to do so simply because no one bothered to
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
19
rather than because it is necessary to keep BLOCK_OP_TYPE_DATAPLANE
20
blocked.
21
22
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
23
Message-ID: <20250203182529.269066-1-stefanha@redhat.com>
24
Reviewed-by: Eric Blake <eblake@redhat.com>
25
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
27
---
20
replication.h | 1 -
28
include/block/block-common.h | 1 -
21
block/replication.c | 1 -
29
block/replication.c | 1 -
22
2 files changed, 2 deletions(-)
30
blockjob.c | 2 --
31
hw/block/virtio-blk.c | 9 ---------
32
hw/scsi/virtio-scsi.c | 3 ---
33
5 files changed, 16 deletions(-)
23
34
24
diff --git a/replication.h b/replication.h
35
diff --git a/include/block/block-common.h b/include/block/block-common.h
25
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
26
--- a/replication.h
37
--- a/include/block/block-common.h
27
+++ b/replication.h
38
+++ b/include/block/block-common.h
28
@@ -XXX,XX +XXX,XX @@ typedef struct ReplicationState ReplicationState;
39
@@ -XXX,XX +XXX,XX @@ typedef enum BlockOpType {
29
*
40
BLOCK_OP_TYPE_CHANGE,
30
* BlockDriver bdrv_replication = {
41
BLOCK_OP_TYPE_COMMIT_SOURCE,
31
* .format_name = "replication",
42
BLOCK_OP_TYPE_COMMIT_TARGET,
32
- * .protocol_name = "replication",
43
- BLOCK_OP_TYPE_DATAPLANE,
33
* .instance_size = sizeof(BDRVReplicationState),
44
BLOCK_OP_TYPE_DRIVE_DEL,
34
*
45
BLOCK_OP_TYPE_EJECT,
35
* .bdrv_open = replication_open,
46
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
36
diff --git a/block/replication.c b/block/replication.c
47
diff --git a/block/replication.c b/block/replication.c
37
index XXXXXXX..XXXXXXX 100644
48
index XXXXXXX..XXXXXXX 100644
38
--- a/block/replication.c
49
--- a/block/replication.c
39
+++ b/block/replication.c
50
+++ b/block/replication.c
40
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
51
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
41
52
return;
42
BlockDriver bdrv_replication = {
53
}
43
.format_name = "replication",
54
bdrv_op_block_all(top_bs, s->blocker);
44
- .protocol_name = "replication",
55
- bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
45
.instance_size = sizeof(BDRVReplicationState),
56
46
57
bdrv_graph_wrunlock();
47
.bdrv_open = replication_open,
58
59
diff --git a/blockjob.c b/blockjob.c
60
index XXXXXXX..XXXXXXX 100644
61
--- a/blockjob.c
62
+++ b/blockjob.c
63
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
64
goto fail;
65
}
66
67
- bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
68
-
69
if (!block_job_set_speed(job, speed, errp)) {
70
goto fail;
71
}
72
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
73
index XXXXXXX..XXXXXXX 100644
74
--- a/hw/block/virtio-blk.c
75
+++ b/hw/block/virtio-blk.c
76
@@ -XXX,XX +XXX,XX @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
77
error_setg(errp, "ioeventfd is required for iothread");
78
return false;
79
}
80
-
81
- /*
82
- * If ioeventfd is (re-)enabled while the guest is running there could
83
- * be block jobs that can conflict.
84
- */
85
- if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
86
- error_prepend(errp, "cannot start virtio-blk ioeventfd: ");
87
- return false;
88
- }
89
}
90
91
s->vq_aio_context = g_new(AioContext *, conf->num_queues);
92
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/hw/scsi/virtio-scsi.c
95
+++ b/hw/scsi/virtio-scsi.c
96
@@ -XXX,XX +XXX,XX @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
97
int ret;
98
99
if (s->ctx && !s->dataplane_fenced) {
100
- if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
101
- return;
102
- }
103
ret = blk_set_aio_context(sd->conf.blk, s->ctx, errp);
104
if (ret < 0) {
105
return;
48
--
106
--
49
2.13.6
107
2.48.1
50
51
diff view generated by jsdifflib