1
The following changes since commit d922088eb4ba6bc31a99f17b32cf75e59dd306cd:
1
The following changes since commit 3f2a357b95845ea0bf7463eff6661e43b97d1afc:
2
2
3
Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging (2025-02-03 13:42:02 -0500)
3
Merge tag 'hw-cpus-20240119' of https://github.com/philmd/qemu into staging (2024-01-19 11:39:38 +0000)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://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 fc4e394b2887e15d5f83994e4fc7b26c895c627a:
9
for you to fetch changes up to c14962c3ea6f0998d028142ed14affcb9dfccf28:
10
10
11
block: remove unused BLOCK_OP_TYPE_DATAPLANE (2025-02-06 14:51:10 +0100)
11
block/blklogwrites: Protect mutable driver state with a mutex. (2024-01-22 12:16:26 +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)
16
- virtio-blk: Multiqueue fixes and cleanups
17
- Fix swapped values for BLOCK_IO_ERROR 'device' and 'qom-path'
17
- blklogwrites: Fixes for write_zeroes and superblock update races
18
- vpc: Read images exported from Azure correctly
18
- commit/stream: Allow users to request only format driver names in
19
- scripts/qemu-gdb: Support coroutine dumps in coredumps
19
backing file format
20
- Minor cleanups
20
- monitor: only run coroutine commands in qemu_aio_context
21
21
22
----------------------------------------------------------------
22
----------------------------------------------------------------
23
Fabiano Rosas (1):
23
Ari Sundholm (2):
24
block: Fix leak in send_qmp_error_event
24
block/blklogwrites: Fix a bug when logging "write zeroes" operations.
25
block/blklogwrites: Protect mutable driver state with a mutex.
25
26
26
Kevin Wolf (16):
27
Kevin Wolf (1):
27
block: Add 'active' field to BlockDeviceInfo
28
string-output-visitor: Fix (pseudo) struct handling
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
43
29
44
Peter Krempa (1):
30
Peter Krempa (2):
45
block-backend: Fix argument order when calling 'qapi_event_send_block_io_error()'
31
commit: Allow users to request only format driver names in backing file format
32
stream: Allow users to request only format driver names in backing file format
46
33
47
Peter Xu (3):
34
Stefan Hajnoczi (9):
48
scripts/qemu-gdb: Always do full stack dump for python errors
35
iotests: add filter_qmp_generated_node_ids()
49
scripts/qemu-gdb: Simplify fs_base fetching for coroutines
36
iotests: port 141 to Python for reliable QMP testing
50
scripts/qemu-gdb: Support coroutine dumps in coredumps
37
monitor: only run coroutine commands in qemu_aio_context
38
virtio-blk: move dataplane code into virtio-blk.c
39
virtio-blk: rename dataplane create/destroy functions
40
virtio-blk: rename dataplane to ioeventfd
41
virtio-blk: restart s->rq reqs in vq AioContexts
42
virtio-blk: tolerate failure to set BlockBackend AioContext
43
virtio-blk: always set ioeventfd during startup
51
44
52
Philippe Mathieu-Daudé (1):
45
qapi/block-core.json | 17 +-
53
block: Improve blk_get_attached_dev_id() docstring
46
hw/block/dataplane/trace.h | 1 -
54
47
hw/block/dataplane/virtio-blk.h | 34 ---
55
Stefan Hajnoczi (1):
48
include/block/block-global-state.h | 3 +-
56
block: remove unused BLOCK_OP_TYPE_DATAPLANE
49
include/block/block_int-common.h | 4 +-
57
50
include/block/block_int-global-state.h | 6 +
58
Vitaly Kuznetsov (2):
51
include/hw/virtio/virtio-blk.h | 16 +-
59
vpc: Split off vpc_ignore_current_size() helper
52
block.c | 37 ++-
60
vpc: Read images exported from Azure correctly
53
block/blklogwrites.c | 120 ++++++--
61
54
block/commit.c | 6 +-
62
qapi/block-core.json | 44 +++-
55
block/monitor/block-hmp-cmds.c | 2 +-
63
qapi/block-export.json | 10 +-
56
block/stream.c | 10 +-
64
include/block/block-common.h | 2 +-
57
blockdev.c | 13 +
65
include/block/block-global-state.h | 6 +
58
hw/block/dataplane/virtio-blk.c | 404 -------------------------
66
include/block/export.h | 3 +
59
hw/block/virtio-blk.c | 412 ++++++++++++++++++++++++--
67
include/system/block-backend-io.h | 7 +
60
monitor/qmp.c | 17 --
68
migration/migration.h | 3 -
61
qapi/qmp-dispatch.c | 24 +-
69
block.c | 64 +++++-
62
qapi/string-output-visitor.c | 46 +++
70
block/block-backend.c | 32 ++-
63
tests/unit/test-bdrv-drain.c | 3 +-
71
block/export/export.c | 29 ++-
64
tests/qemu-iotests/iotests.py | 7 +
72
block/monitor/block-hmp-cmds.c | 5 +-
65
hw/block/dataplane/meson.build | 1 -
73
block/qapi.c | 1 +
66
hw/block/dataplane/trace-events | 5 -
74
block/replication.c | 1 -
67
meson.build | 1 -
75
block/vpc.c | 65 +++---
68
tests/qemu-iotests/060.out | 4 +-
76
blockdev.c | 48 ++++
69
tests/qemu-iotests/071.out | 4 +-
77
blockjob.c | 2 -
70
tests/qemu-iotests/081.out | 16 +-
78
hw/block/virtio-blk.c | 9 -
71
tests/qemu-iotests/087.out | 12 +-
79
hw/scsi/virtio-scsi.c | 3 -
72
tests/qemu-iotests/108.out | 2 +-
80
migration/block-active.c | 46 ----
73
tests/qemu-iotests/109 | 4 +-
81
migration/migration.c | 8 -
74
tests/qemu-iotests/109.out | 78 +++--
82
nbd/server.c | 17 ++
75
tests/qemu-iotests/117.out | 2 +-
83
scripts/qemu-gdb.py | 2 +
76
tests/qemu-iotests/120.out | 2 +-
84
scripts/qemugdb/coroutine.py | 102 ++++++---
77
tests/qemu-iotests/127.out | 2 +-
85
tests/qemu-iotests/iotests.py | 8 +
78
tests/qemu-iotests/140.out | 2 +-
86
tests/qemu-iotests/041 | 4 +-
79
tests/qemu-iotests/141 | 307 ++++++++-----------
87
tests/qemu-iotests/165 | 4 +-
80
tests/qemu-iotests/141.out | 200 +++----------
88
tests/qemu-iotests/184.out | 2 +
81
tests/qemu-iotests/143.out | 2 +-
89
tests/qemu-iotests/191.out | 16 ++
82
tests/qemu-iotests/156.out | 2 +-
90
tests/qemu-iotests/273.out | 5 +
83
tests/qemu-iotests/176.out | 16 +-
91
tests/qemu-iotests/tests/copy-before-write | 3 +-
84
tests/qemu-iotests/182.out | 2 +-
92
tests/qemu-iotests/tests/inactive-node-nbd | 303 +++++++++++++++++++++++++
85
tests/qemu-iotests/183.out | 4 +-
93
tests/qemu-iotests/tests/inactive-node-nbd.out | 239 +++++++++++++++++++
86
tests/qemu-iotests/184.out | 32 +-
94
tests/qemu-iotests/tests/migrate-bitmaps-test | 7 +-
87
tests/qemu-iotests/185 | 6 +-
95
tests/qemu-iotests/tests/qsd-migrate | 140 ++++++++++++
88
tests/qemu-iotests/185.out | 45 ++-
96
tests/qemu-iotests/tests/qsd-migrate.out | 59 +++++
89
tests/qemu-iotests/191.out | 16 +-
97
35 files changed, 1133 insertions(+), 166 deletions(-)
90
tests/qemu-iotests/195.out | 16 +-
98
create mode 100755 tests/qemu-iotests/tests/inactive-node-nbd
91
tests/qemu-iotests/223.out | 12 +-
99
create mode 100644 tests/qemu-iotests/tests/inactive-node-nbd.out
92
tests/qemu-iotests/227.out | 32 +-
100
create mode 100755 tests/qemu-iotests/tests/qsd-migrate
93
tests/qemu-iotests/247.out | 2 +-
101
create mode 100644 tests/qemu-iotests/tests/qsd-migrate.out
94
tests/qemu-iotests/273.out | 8 +-
102
95
tests/qemu-iotests/308 | 4 +-
103
96
tests/qemu-iotests/308.out | 4 +-
97
tests/qemu-iotests/tests/file-io-error | 5 +-
98
tests/qemu-iotests/tests/iothreads-resize.out | 2 +-
99
tests/qemu-iotests/tests/qsd-jobs.out | 4 +-
100
55 files changed, 1014 insertions(+), 1024 deletions(-)
101
delete mode 100644 hw/block/dataplane/trace.h
102
delete mode 100644 hw/block/dataplane/virtio-blk.h
103
delete mode 100644 hw/block/dataplane/virtio-blk.c
104
delete mode 100644 hw/block/dataplane/trace-events
diff view generated by jsdifflib
Deleted patch
1
From: Vitaly Kuznetsov <vkuznets@redhat.com>
2
1
3
In preparation to making changes to the logic deciding whether CHS or
4
'current_size' need to be used in determining the image size, split off
5
vpc_ignore_current_size() helper.
6
7
No functional change intended.
8
9
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
10
Message-ID: <20241212134504.1983757-2-vkuznets@redhat.com>
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
block/vpc.c | 67 +++++++++++++++++++++++++++++------------------------
16
1 file changed, 37 insertions(+), 30 deletions(-)
17
18
diff --git a/block/vpc.c b/block/vpc.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/vpc.c
21
+++ b/block/vpc.c
22
@@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
23
}
24
}
25
26
+/*
27
+ * Microsoft Virtual PC and Microsoft Hyper-V produce and read
28
+ * VHD image sizes differently. VPC will rely on CHS geometry,
29
+ * while Hyper-V and disk2vhd use the size specified in the footer.
30
+ *
31
+ * We use a couple of approaches to try and determine the correct method:
32
+ * look at the Creator App field, and look for images that have CHS
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) /
103
--
104
2.48.1
105
106
diff view generated by jsdifflib
Deleted patch
1
From: Vitaly Kuznetsov <vkuznets@redhat.com>
2
1
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
Deleted patch
1
From: Philippe Mathieu-Daudé <philmd@linaro.org>
2
1
3
Expose the method docstring in the header, and mention
4
returned value must be free'd by caller.
5
6
Reported-by: Fabiano Rosas <farosas@suse.de>
7
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
8
Message-ID: <20241111170333.43833-2-philmd@linaro.org>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
include/system/block-backend-io.h | 7 +++++++
12
block/block-backend.c | 12 ++++++++----
13
2 files changed, 15 insertions(+), 4 deletions(-)
14
15
diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/system/block-backend-io.h
18
+++ b/include/system/block-backend-io.h
19
@@ -XXX,XX +XXX,XX @@ void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow);
20
void blk_set_disable_request_queuing(BlockBackend *blk, bool disable);
21
bool blk_iostatus_is_enabled(const BlockBackend *blk);
22
23
+/*
24
+ * Return the qdev ID, or if no ID is assigned the QOM path,
25
+ * of the block device attached to the BlockBackend.
26
+ *
27
+ * The caller is responsible for releasing the value returned
28
+ * with g_free() after use.
29
+ */
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);
68
--
69
2.48.1
70
71
diff view generated by jsdifflib
Deleted patch
1
From: Fabiano Rosas <farosas@suse.de>
2
1
3
ASAN detected a leak when running the ahci-test
4
/ahci/io/dma/lba28/retry:
5
6
Direct leak of 35 byte(s) in 1 object(s) allocated from:
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
25
26
Plug the leak by freeing the device path string.
27
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>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
35
---
36
block/block-backend.c | 4 ++--
37
1 file changed, 2 insertions(+), 2 deletions(-)
38
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 void send_qmp_error_event(BlockBackend *blk,
44
{
45
IoOperationType optype;
46
BlockDriverState *bs = blk_bs(blk);
47
+ g_autofree char *path = blk_get_attached_dev_path(blk);
48
49
optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
50
- qapi_event_send_block_io_error(blk_name(blk),
51
- blk_get_attached_dev_path(blk),
52
+ qapi_event_send_block_io_error(blk_name(blk), path,
53
bs ? bdrv_get_node_name(bs) : NULL, optype,
54
action, blk_iostatus_is_enabled(blk),
55
error == ENOSPC, strerror(error));
56
--
57
2.48.1
58
59
diff view generated by jsdifflib
Deleted patch
1
From: Peter Xu <peterx@redhat.com>
2
1
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
Deleted patch
1
From: Peter Xu <peterx@redhat.com>
2
1
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
Deleted patch
1
From: Peter Xu <peterx@redhat.com>
2
1
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
Deleted patch
1
From: Peter Krempa <pkrempa@redhat.com>
2
1
3
Commit 7452162adec25c10 introduced 'qom-path' argument to BLOCK_IO_ERROR
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 :
7
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>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
35
---
36
block/block-backend.c | 2 +-
37
1 file changed, 1 insertion(+), 1 deletion(-)
38
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 void send_qmp_error_event(BlockBackend *blk,
44
g_autofree char *path = blk_get_attached_dev_path(blk);
45
46
optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
47
- qapi_event_send_block_io_error(blk_name(blk), path,
48
+ qapi_event_send_block_io_error(path, blk_name(blk),
49
bs ? bdrv_get_node_name(bs) : NULL, optype,
50
action, blk_iostatus_is_enabled(blk),
51
error == ENOSPC, strerror(error));
52
--
53
2.48.1
54
55
diff view generated by jsdifflib
Deleted 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).
3
1
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
Deleted 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.
5
1
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
Deleted patch
1
Putting an active block node on top of an inactive one is strictly
2
speaking an invalid configuration and the next patch will turn it into a
3
hard error.
4
1
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.
13
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Acked-by: Fabiano Rosas <farosas@suse.de>
16
Reviewed-by: Eric Blake <eblake@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>
20
---
21
blockdev.c | 16 ++++++++++++++++
22
1 file changed, 16 insertions(+)
23
24
diff --git a/blockdev.c b/blockdev.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/blockdev.c
27
+++ b/blockdev.c
28
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action,
29
return;
30
}
31
32
+ /*
33
+ * Older QEMU versions have allowed adding an active parent node to an
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;
45
+ }
46
+ }
47
+
48
ret = bdrv_append(state->new_bs, state->old_bs, errp);
49
if (ret < 0) {
50
return;
51
--
52
2.48.1
diff view generated by jsdifflib
Deleted patch
1
Block devices have an individual active state, a single global flag
2
can't cover this correctly. This becomes more important as we allow
3
users to manually manage which nodes are active or inactive.
4
1
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.
9
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Acked-by: Fabiano Rosas <farosas@suse.de>
12
Reviewed-by: Eric Blake <eblake@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>
16
---
17
migration/migration.h | 3 ---
18
migration/block-active.c | 46 ----------------------------------------
19
migration/migration.c | 8 -------
20
3 files changed, 57 deletions(-)
21
22
diff --git a/migration/migration.h b/migration/migration.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/migration/migration.h
25
+++ b/migration/migration.h
26
@@ -XXX,XX +XXX,XX @@ void migration_bitmap_sync_precopy(bool last_stage);
27
void dirty_bitmap_mig_init(void);
28
bool should_send_vmdesc(void);
29
30
-/* migration/block-active.c */
31
-void migration_block_active_setup(bool active);
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;
92
}
93
94
- migration_block_active = true;
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;
112
}
113
114
- migration_block_active = false;
115
return true;
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;
123
}
124
125
- /*
126
- * Newly setup incoming QEMU. Mark the block active state to reflect
127
- * that the src currently owns the disks.
128
- */
129
- migration_block_active_setup(false);
130
-
131
once = false;
132
}
133
134
@@ -XXX,XX +XXX,XX @@ static void migration_instance_init(Object *obj)
135
ms->state = MIGRATION_STATUS_NONE;
136
ms->mbps = -1;
137
ms->pages_per_second = -1;
138
- /* Freshly started QEMU owns all the block devices */
139
- migration_block_active_setup(true);
140
qemu_sem_init(&ms->pause_sem, 0);
141
qemu_mutex_init(&ms->error_mutex);
142
143
--
144
2.48.1
diff view generated by jsdifflib
Deleted patch
1
An active node makes unrestricted use of its children and would possibly
2
run into assertion failures when it operates on an inactive child node.
3
1
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-6-kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block.c | 5 +++++
12
1 file changed, 5 insertions(+)
13
14
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
17
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ bdrv_attach_child_noperm(BlockDriverState *parent_bs,
19
child_bs->node_name, child_name, parent_bs->node_name);
20
return NULL;
21
}
22
+ if (bdrv_is_inactive(child_bs) && !bdrv_is_inactive(parent_bs)) {
23
+ error_setg(errp, "Inactive '%s' can't be a %s child of active '%s'",
24
+ child_bs->node_name, child_name, parent_bs->node_name);
25
+ return NULL;
26
+ }
27
28
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
29
bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
30
--
31
2.48.1
diff view generated by jsdifflib
Deleted patch
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.
9
1
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Acked-by: Fabiano Rosas <farosas@suse.de>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Message-ID: <20250204211407.381505-7-kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
17
block.c | 7 +++++++
18
block/block-backend.c | 2 +-
19
2 files changed, 8 insertions(+), 1 deletion(-)
20
21
diff --git a/block.c b/block.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block.c
24
+++ b/block.c
25
@@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs,
26
assert(child_class->get_parent_desc);
27
GLOBAL_STATE_CODE();
28
29
+ if (bdrv_is_inactive(child_bs) && (perm & ~BLK_PERM_CONSISTENT_READ)) {
30
+ g_autofree char *perm_names = bdrv_perm_names(perm);
31
+ error_setg(errp, "Permission '%s' unavailable on inactive node",
32
+ perm_names);
33
+ return NULL;
34
+ }
35
+
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
}
51
52
--
53
2.48.1
diff view generated by jsdifflib
Deleted patch
1
In QEMU, nodes are automatically created inactive while expecting an
2
incoming migration (i.e. RUN_STATE_INMIGRATE). In qemu-storage-daemon,
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.
6
1
7
Therefore, allow the user to explicitly open images as inactive with a
8
new option. The default is as before: Nodes are usually active, except
9
when created during RUN_STATE_INMIGRATE.
10
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Acked-by: Fabiano Rosas <farosas@suse.de>
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>
17
---
18
qapi/block-core.json | 6 ++++++
19
include/block/block-common.h | 1 +
20
block.c | 9 +++++++++
21
3 files changed, 16 insertions(+)
22
23
diff --git a/qapi/block-core.json b/qapi/block-core.json
24
index XXXXXXX..XXXXXXX 100644
25
--- a/qapi/block-core.json
26
+++ b/qapi/block-core.json
27
@@ -XXX,XX +XXX,XX @@
28
#
29
# @cache: cache-related options
30
#
31
+# @active: whether the block node should be activated (default: true).
32
+# Having inactive block nodes is useful primarily for migration because it
33
+# allows opening an image on the destination while the source is still
34
+# holding locks for it. (Since 10.0)
35
+#
36
# @read-only: whether the block device should be read-only (default:
37
# false). Note that some block drivers support only read-only
38
# access, either generally or in certain configurations. In this
39
@@ -XXX,XX +XXX,XX @@
40
'*node-name': 'str',
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
48
index XXXXXXX..XXXXXXX 100644
49
--- a/include/block/block-common.h
50
+++ b/include/block/block-common.h
51
@@ -XXX,XX +XXX,XX @@ typedef enum {
52
#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only"
53
#define BDRV_OPT_DISCARD "discard"
54
#define BDRV_OPT_FORCE_SHARE "force-share"
55
+#define BDRV_OPT_ACTIVE "active"
56
57
58
#define BDRV_SECTOR_BITS 9
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 @@ static void update_flags_from_options(int *flags, QemuOpts *opts)
64
if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) {
65
*flags |= BDRV_O_AUTO_RDONLY;
66
}
67
+
68
+ if (!qemu_opt_get_bool_del(opts, BDRV_OPT_ACTIVE, true)) {
69
+ *flags |= BDRV_O_INACTIVE;
70
+ }
71
}
72
73
static void update_options_from_flags(QDict *options, int flags)
74
@@ -XXX,XX +XXX,XX @@ QemuOptsList bdrv_runtime_opts = {
75
.type = QEMU_OPT_BOOL,
76
.help = "Ignore flush requests",
77
},
78
+ {
79
+ .name = BDRV_OPT_ACTIVE,
80
+ .type = QEMU_OPT_BOOL,
81
+ .help = "Node is activated",
82
+ },
83
{
84
.name = BDRV_OPT_READ_ONLY,
85
.type = QEMU_OPT_BOOL,
86
--
87
2.48.1
diff view generated by jsdifflib
Deleted patch
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
1
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>
31
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
32
---
33
qapi/block-core.json | 32 ++++++++++++++++++++++++++++++
34
include/block/block-global-state.h | 3 +++
35
block.c | 21 ++++++++++++++++++++
36
blockdev.c | 32 ++++++++++++++++++++++++++++++
37
4 files changed, 88 insertions(+)
38
39
diff --git a/qapi/block-core.json b/qapi/block-core.json
40
index XXXXXXX..XXXXXXX 100644
41
--- a/qapi/block-core.json
42
+++ b/qapi/block-core.json
43
@@ -XXX,XX +XXX,XX @@
44
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' },
45
'allow-preconfig': true }
46
47
+##
48
+# @blockdev-set-active:
49
+#
50
+# Activate or inactivate a block device. Use this to manage the handover of
51
+# block devices on migration with qemu-storage-daemon.
52
+#
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.
56
+#
57
+# @node-name: Name of the graph node to activate or inactivate. By default, all
58
+# nodes are affected by the operation.
59
+#
60
+# @active: true if the nodes should be active when the command returns success,
61
+# false if they should be inactive.
62
+#
63
+# Since: 10.0
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 }
78
+
79
##
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);
92
+
93
void bdrv_activate_all(Error **errp);
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;
107
+
108
+ GLOBAL_STATE_CODE();
109
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
110
+
111
+ if (bdrv_has_bds_parent(bs, true)) {
112
+ error_setg(errp, "Node has active parent node");
113
+ return -EPERM;
114
+ }
115
+
116
+ ret = bdrv_inactivate_recurse(bs, true);
117
+ if (ret < 0) {
118
+ error_setg_errno(errp, -ret, "Failed to inactivate node");
119
+ return ret;
120
+ }
121
+
122
+ return 0;
123
+}
124
+
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)
137
+{
138
+ int ret;
139
+
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
+ }
166
+}
167
+
168
static BdrvChild * GRAPH_RDLOCK
169
bdrv_find_child(BlockDriverState *parent_bs, const char *child_name)
170
{
171
--
172
2.48.1
diff view generated by jsdifflib
Deleted patch
1
Device models have a relatively complex way to set up their block
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.
8
1
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Acked-by: Fabiano Rosas <farosas@suse.de>
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>
15
---
16
block/block-backend.c | 14 ++++++++++++--
17
1 file changed, 12 insertions(+), 2 deletions(-)
18
19
diff --git a/block/block-backend.c b/block/block-backend.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/block-backend.c
22
+++ b/block/block-backend.c
23
@@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk)
24
int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
25
{
26
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
27
+ uint64_t perm, shared_perm;
28
29
GLOBAL_STATE_CODE();
30
bdrv_ref(bs);
31
bdrv_graph_wrlock();
32
+
33
+ if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) {
34
+ blk->disable_perm = true;
35
+ perm = 0;
36
+ shared_perm = BLK_PERM_ALL;
37
+ } else {
38
+ perm = blk->perm;
39
+ shared_perm = blk->shared_perm;
40
+ }
41
+
42
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
43
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
44
- blk->perm, blk->shared_perm,
45
- blk, errp);
46
+ perm, shared_perm, blk, errp);
47
bdrv_graph_wrunlock();
48
if (blk->root == NULL) {
49
return -EPERM;
50
--
51
2.48.1
diff view generated by jsdifflib
Deleted patch
1
Currently, block exports can't handle inactive images correctly.
2
Incoming write requests would run into assertion failures. Make sure
3
that we return an error when creating an export can't activate the
4
image.
5
1
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-11-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block/export/export.c | 6 +++++-
14
1 file changed, 5 insertions(+), 1 deletion(-)
15
16
diff --git a/block/export/export.c b/block/export/export.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/export/export.c
19
+++ b/block/export/export.c
20
@@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
21
* ctx was acquired in the caller.
22
*/
23
bdrv_graph_rdlock_main_loop();
24
- bdrv_activate(bs, NULL);
25
+ ret = bdrv_activate(bs, errp);
26
+ if (ret < 0) {
27
+ bdrv_graph_rdunlock_main_loop();
28
+ goto fail;
29
+ }
30
bdrv_graph_rdunlock_main_loop();
31
32
perm = BLK_PERM_CONSISTENT_READ;
33
--
34
2.48.1
diff view generated by jsdifflib
Deleted patch
1
So far the assumption has always been that if we try to inactivate a
2
node, it is already idle. This doesn't hold true any more if we allow
3
inactivating exported nodes because we can't know when new external
4
requests come in.
5
1
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).
11
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>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
block.c | 2 ++
20
1 file changed, 2 insertions(+)
21
22
diff --git a/block.c b/block.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block.c
25
+++ b/block.c
26
@@ -XXX,XX +XXX,XX @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
27
return -EPERM;
28
}
29
30
+ bdrv_drained_begin(bs);
31
bs->open_flags |= BDRV_O_INACTIVE;
32
+ bdrv_drained_end(bs);
33
34
/*
35
* Update permissions, they may differ for inactive nodes.
36
--
37
2.48.1
diff view generated by jsdifflib
Deleted patch
1
Add an option in BlockExportOptions to allow creating an export on an
2
inactive node without activating the node. This mode needs to be
3
explicitly supported by the export type (so that it doesn't perform any
4
operations that are forbidden for inactive nodes), so this patch alone
5
doesn't allow this option to be successfully used yet.
6
1
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Acked-by: Fabiano Rosas <farosas@suse.de>
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>
13
---
14
qapi/block-export.json | 10 +++++++++-
15
include/block/export.h | 3 +++
16
block/export/export.c | 31 +++++++++++++++++++++----------
17
3 files changed, 33 insertions(+), 11 deletions(-)
18
19
diff --git a/qapi/block-export.json b/qapi/block-export.json
20
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-export.json
22
+++ b/qapi/block-export.json
23
@@ -XXX,XX +XXX,XX @@
24
# cannot be moved to the iothread. The default is false.
25
# (since: 5.2)
26
#
27
+# @allow-inactive: If true, the export allows the exported node to be inactive.
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)
67
{
68
bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread;
69
+ bool allow_inactive = export->has_allow_inactive && export->allow_inactive;
70
const BlockExportDriver *drv;
71
BlockExport *exp = NULL;
72
BlockDriverState *bs;
73
@@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
74
}
75
}
76
77
- /*
78
- * Block exports are used for non-shared storage migration. Make sure
79
- * that BDRV_O_INACTIVE is cleared and the image is ready for write
80
- * access since the export could be available before migration handover.
81
- * ctx was acquired in the caller.
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);
114
+ }
115
116
ret = blk_insert_bs(blk, bs, errp);
117
if (ret < 0) {
118
--
119
2.48.1
diff view generated by jsdifflib
Deleted patch
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.
6
1
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Acked-by: Fabiano Rosas <farosas@suse.de>
9
Message-ID: <20250204211407.381505-14-kwolf@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
nbd/server.c | 17 +++++++++++++++++
15
1 file changed, 17 insertions(+)
16
17
diff --git a/nbd/server.c b/nbd/server.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/nbd/server.c
20
+++ b/nbd/server.c
21
@@ -XXX,XX +XXX,XX @@ static void nbd_export_delete(BlockExport *blk_exp)
22
const BlockExportDriver blk_exp_nbd = {
23
.type = BLOCK_EXPORT_TYPE_NBD,
24
.instance_size = sizeof(NBDExport),
25
+ .supports_inactive = true,
26
.create = nbd_export_create,
27
.delete = nbd_export_delete,
28
.request_shutdown = nbd_export_request_shutdown,
29
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
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);
46
+ }
47
+ }
48
+ }
49
50
switch (request->type) {
51
case NBD_CMD_CACHE:
52
--
53
2.48.1
diff view generated by jsdifflib
Deleted patch
1
The open-coded form of this filter has been copied into enough tests
2
that it's better to move it into iotests.py.
3
1
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-15-kwolf@redhat.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
tests/qemu-iotests/iotests.py | 4 ++++
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(-)
17
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
34
index XXXXXXX..XXXXXXX 100755
35
--- a/tests/qemu-iotests/041
36
+++ b/tests/qemu-iotests/041
37
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
38
39
# Check the full error message now
40
self.vm.shutdown()
41
- log = self.vm.get_log()
42
- log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
43
+ log = iotests.filter_qtest(self.vm.get_log())
44
log = re.sub(r'^Formatting.*\n', '', log)
45
- log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
46
log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
47
48
self.assertEqual(log,
49
diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165
50
index XXXXXXX..XXXXXXX 100755
51
--- a/tests/qemu-iotests/165
52
+++ b/tests/qemu-iotests/165
53
@@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
54
self.vm.shutdown()
55
56
#catch 'Persistent bitmaps are lost' possible error
57
- log = self.vm.get_log()
58
- log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
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
97
--
98
2.48.1
diff view generated by jsdifflib
Deleted patch
1
Test that it's possible to migrate a VM that uses an image on shared
2
storage through qemu-storage-daemon.
3
1
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>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
tests/qemu-iotests/tests/qsd-migrate | 140 +++++++++++++++++++++++
12
tests/qemu-iotests/tests/qsd-migrate.out | 59 ++++++++++
13
2 files changed, 199 insertions(+)
14
create mode 100755 tests/qemu-iotests/tests/qsd-migrate
15
create mode 100644 tests/qemu-iotests/tests/qsd-migrate.out
16
17
diff --git a/tests/qemu-iotests/tests/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate
18
new file mode 100755
19
index XXXXXXX..XXXXXXX
20
--- /dev/null
21
+++ b/tests/qemu-iotests/tests/qsd-migrate
22
@@ -XXX,XX +XXX,XX @@
23
+#!/usr/bin/env python3
24
+# group: rw quick
25
+#
26
+# Copyright (C) Red Hat, Inc.
27
+#
28
+# This program is free software; you can redistribute it and/or modify
29
+# it under the terms of the GNU General Public License as published by
30
+# the Free Software Foundation; either version 2 of the License, or
31
+# (at your option) any later version.
32
+#
33
+# This program is distributed in the hope that it will be useful,
34
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
35
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36
+# GNU General Public License for more details.
37
+#
38
+# You should have received a copy of the GNU General Public License
39
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
40
+#
41
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
42
+
43
+import iotests
44
+
45
+from iotests import filter_qemu_io, filter_qtest
46
+
47
+iotests.script_initialize(supported_fmts=['generic'],
48
+ supported_protocols=['file'],
49
+ supported_platforms=['linux'])
50
+
51
+with iotests.FilePath('disk.img') as path, \
52
+ iotests.FilePath('nbd-src.sock', base_dir=iotests.sock_dir) as nbd_src, \
53
+ iotests.FilePath('nbd-dst.sock', base_dir=iotests.sock_dir) as nbd_dst, \
54
+ iotests.FilePath('migrate.sock', base_dir=iotests.sock_dir) as mig_sock, \
55
+ iotests.VM(path_suffix="-src") as vm_src, \
56
+ iotests.VM(path_suffix="-dst") as vm_dst:
57
+
58
+ img_size = '10M'
59
+
60
+ iotests.log('Preparing disk...')
61
+ iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size)
62
+
63
+ iotests.log('Launching source QSD...')
64
+ qsd_src = iotests.QemuStorageDaemon(
65
+ '--blockdev', f'file,node-name=disk-file,filename={path}',
66
+ '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt',
67
+ '--nbd-server', f'addr.type=unix,addr.path={nbd_src}',
68
+ '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,'
69
+ 'allow-inactive=true',
70
+ qmp=True,
71
+ )
72
+
73
+ iotests.log('Launching source VM...')
74
+ vm_src.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,'
75
+ f'server.path={nbd_src},export=disk-fmt')
76
+ vm_src.add_args('-device', 'virtio-blk,drive=disk,id=virtio0')
77
+ vm_src.launch()
78
+
79
+ iotests.log('Launching destination QSD...')
80
+ qsd_dst = iotests.QemuStorageDaemon(
81
+ '--blockdev', f'file,node-name=disk-file,filename={path},active=off',
82
+ '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,'
83
+ f'active=off',
84
+ '--nbd-server', f'addr.type=unix,addr.path={nbd_dst}',
85
+ '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,'
86
+ 'allow-inactive=true',
87
+ qmp=True,
88
+ instance_id='b',
89
+ )
90
+
91
+ iotests.log('Launching destination VM...')
92
+ vm_dst.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,'
93
+ f'server.path={nbd_dst},export=disk-fmt')
94
+ vm_dst.add_args('-device', 'virtio-blk,drive=disk,id=virtio0')
95
+ vm_dst.add_args('-incoming', f'unix:{mig_sock}')
96
+ vm_dst.launch()
97
+
98
+ iotests.log('\nTest I/O on the source')
99
+ vm_src.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x11 0 4k',
100
+ use_log=True, qdev=True)
101
+ vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k',
102
+ use_log=True, qdev=True)
103
+
104
+ iotests.log('\nStarting migration...')
105
+
106
+ mig_caps = [
107
+ {'capability': 'events', 'state': True},
108
+ {'capability': 'pause-before-switchover', 'state': True},
109
+ ]
110
+ vm_src.qmp_log('migrate-set-capabilities', capabilities=mig_caps)
111
+ vm_dst.qmp_log('migrate-set-capabilities', capabilities=mig_caps)
112
+ vm_src.qmp_log('migrate', uri=f'unix:{mig_sock}',
113
+ filters=[iotests.filter_qmp_testfiles])
114
+
115
+ vm_src.event_wait('MIGRATION',
116
+ match={'data': {'status': 'pre-switchover'}})
117
+
118
+ iotests.log('\nPre-switchover: Reconfigure QSD instances')
119
+
120
+ iotests.log(qsd_src.qmp('blockdev-set-active', {'active': False}))
121
+
122
+ # Reading is okay from both sides while the image is inactive. Note that
123
+ # the destination may have stale data until it activates the image, though.
124
+ vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k',
125
+ use_log=True, qdev=True)
126
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read 0 4k',
127
+ use_log=True, qdev=True)
128
+
129
+ iotests.log(qsd_dst.qmp('blockdev-set-active', {'active': True}))
130
+
131
+ iotests.log('\nCompleting migration...')
132
+
133
+ vm_src.qmp_log('migrate-continue', state='pre-switchover')
134
+ vm_dst.event_wait('MIGRATION', match={'data': {'status': 'completed'}})
135
+
136
+ iotests.log('\nTest I/O on the destination')
137
+
138
+ # Now the destination must see what the source wrote
139
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k',
140
+ use_log=True, qdev=True)
141
+
142
+ # And be able to overwrite it
143
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x22 0 4k',
144
+ use_log=True, qdev=True)
145
+ vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x22 0 4k',
146
+ use_log=True, qdev=True)
147
+
148
+ iotests.log('\nDone')
149
+
150
+ vm_src.shutdown()
151
+ iotests.log('\n--- vm_src log ---')
152
+ log = vm_src.get_log()
153
+ if log:
154
+ iotests.log(log, [filter_qtest, filter_qemu_io])
155
+ qsd_src.stop()
156
+
157
+ vm_dst.shutdown()
158
+ iotests.log('\n--- vm_dst log ---')
159
+ log = vm_dst.get_log()
160
+ if log:
161
+ iotests.log(log, [filter_qtest, filter_qemu_io])
162
+ qsd_dst.stop()
163
diff --git a/tests/qemu-iotests/tests/qsd-migrate.out b/tests/qemu-iotests/tests/qsd-migrate.out
164
new file mode 100644
165
index XXXXXXX..XXXXXXX
166
--- /dev/null
167
+++ b/tests/qemu-iotests/tests/qsd-migrate.out
168
@@ -XXX,XX +XXX,XX @@
169
+Preparing disk...
170
+Launching source QSD...
171
+Launching source VM...
172
+Launching destination QSD...
173
+Launching destination VM...
174
+
175
+Test I/O on the source
176
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x11 0 4k\""}}
177
+{"return": ""}
178
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}}
179
+{"return": ""}
180
+
181
+Starting migration...
182
+{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}}
183
+{"return": {}}
184
+{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}}
185
+{"return": {}}
186
+{"execute": "migrate", "arguments": {"uri": "unix:SOCK_DIR/PID-migrate.sock"}}
187
+{"return": {}}
188
+
189
+Pre-switchover: Reconfigure QSD instances
190
+{"return": {}}
191
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}}
192
+{"return": ""}
193
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read 0 4k\""}}
194
+{"return": ""}
195
+{"return": {}}
196
+
197
+Completing migration...
198
+{"execute": "migrate-continue", "arguments": {"state": "pre-switchover"}}
199
+{"return": {}}
200
+
201
+Test I/O on the destination
202
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}}
203
+{"return": ""}
204
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x22 0 4k\""}}
205
+{"return": ""}
206
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x22 0 4k\""}}
207
+{"return": ""}
208
+
209
+Done
210
+
211
+--- vm_src log ---
212
+wrote 4096/4096 bytes at offset 0
213
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
214
+read 4096/4096 bytes at offset 0
215
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
216
+read 4096/4096 bytes at offset 0
217
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
218
+
219
+--- vm_dst log ---
220
+read 4096/4096 bytes at offset 0
221
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
222
+read 4096/4096 bytes at offset 0
223
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
224
+wrote 4096/4096 bytes at offset 0
225
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
226
+read 4096/4096 bytes at offset 0
227
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
228
--
229
2.48.1
diff view generated by jsdifflib
Deleted patch
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
1
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
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>
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>
16
---
17
tests/qemu-iotests/iotests.py | 4 +
18
tests/qemu-iotests/tests/inactive-node-nbd | 303 ++++++++++++++++++
19
.../qemu-iotests/tests/inactive-node-nbd.out | 239 ++++++++++++++
20
3 files changed, 546 insertions(+)
21
create mode 100755 tests/qemu-iotests/tests/inactive-node-nbd
22
create mode 100644 tests/qemu-iotests/tests/inactive-node-nbd.out
23
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
40
new file mode 100755
41
index XXXXXXX..XXXXXXX
42
--- /dev/null
43
+++ b/tests/qemu-iotests/tests/inactive-node-nbd
44
@@ -XXX,XX +XXX,XX @@
45
+#!/usr/bin/env python3
46
+# group: rw quick
47
+#
48
+# Copyright (C) Red Hat, Inc.
49
+#
50
+# This program is free software; you can redistribute it and/or modify
51
+# it under the terms of the GNU General Public License as published by
52
+# the Free Software Foundation; either version 2 of the License, or
53
+# (at your option) any later version.
54
+#
55
+# This program is distributed in the hope that it will be useful,
56
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
57
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
58
+# GNU General Public License for more details.
59
+#
60
+# You should have received a copy of the GNU General Public License
61
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
62
+#
63
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
64
+
65
+import iotests
66
+
67
+from iotests import QemuIoInteractive
68
+from iotests import filter_qemu_io, filter_qtest, filter_qmp_testfiles
69
+
70
+iotests.script_initialize(supported_fmts=['generic'],
71
+ supported_protocols=['file'],
72
+ supported_platforms=['linux'])
73
+
74
+def get_export(node_name='disk-fmt', allow_inactive=None):
75
+ exp = {
76
+ 'id': 'exp0',
77
+ 'type': 'nbd',
78
+ 'node-name': node_name,
79
+ 'writable': True,
80
+ }
81
+
82
+ if allow_inactive is not None:
83
+ exp['allow-inactive'] = allow_inactive
84
+
85
+ return exp
86
+
87
+def node_is_active(_vm, node_name):
88
+ nodes = _vm.cmd('query-named-block-nodes', flat=True)
89
+ node = next(n for n in nodes if n['node-name'] == node_name)
90
+ return node['active']
91
+
92
+with iotests.FilePath('disk.img') as path, \
93
+ iotests.FilePath('snap.qcow2') as snap_path, \
94
+ iotests.FilePath('snap2.qcow2') as snap2_path, \
95
+ iotests.FilePath('target.img') as target_path, \
96
+ iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \
97
+ iotests.VM() as vm:
98
+
99
+ img_size = '10M'
100
+
101
+ iotests.log('Preparing disk...')
102
+ iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size)
103
+ iotests.qemu_img_create('-f', iotests.imgfmt, target_path, img_size)
104
+
105
+ iotests.qemu_img_create('-f', 'qcow2', '-b', path, '-F', iotests.imgfmt,
106
+ snap_path)
107
+ iotests.qemu_img_create('-f', 'qcow2', '-b', snap_path, '-F', 'qcow2',
108
+ snap2_path)
109
+
110
+ iotests.log('Launching VM...')
111
+ vm.add_blockdev(f'file,node-name=disk-file,filename={path}')
112
+ vm.add_blockdev(f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,'
113
+ 'active=off')
114
+ vm.add_blockdev(f'file,node-name=target-file,filename={target_path}')
115
+ vm.add_blockdev(f'{iotests.imgfmt},file=target-file,node-name=target-fmt')
116
+ vm.add_blockdev(f'file,node-name=snap-file,filename={snap_path}')
117
+ vm.add_blockdev(f'file,node-name=snap2-file,filename={snap2_path}')
118
+
119
+ # Actually running the VM activates all images
120
+ vm.add_paused()
121
+
122
+ vm.launch()
123
+ vm.qmp_log('nbd-server-start',
124
+ addr={'type': 'unix', 'data':{'path': nbd_sock}},
125
+ filters=[filter_qmp_testfiles])
126
+
127
+ iotests.log('\n=== Creating export of inactive node ===')
128
+
129
+ iotests.log('\nExports activate nodes without allow-inactive')
130
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
131
+ vm.qmp_log('block-export-add', **get_export())
132
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
133
+ vm.qmp_log('query-block-exports')
134
+ vm.qmp_log('block-export-del', id='exp0')
135
+ vm.event_wait('BLOCK_EXPORT_DELETED')
136
+ vm.qmp_log('query-block-exports')
137
+
138
+ iotests.log('\nExports activate nodes with allow-inactive=false')
139
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
140
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
141
+ vm.qmp_log('block-export-add', **get_export(allow_inactive=False))
142
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
143
+ vm.qmp_log('query-block-exports')
144
+ vm.qmp_log('block-export-del', id='exp0')
145
+ vm.event_wait('BLOCK_EXPORT_DELETED')
146
+ vm.qmp_log('query-block-exports')
147
+
148
+ iotests.log('\nExport leaves nodes inactive with allow-inactive=true')
149
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
150
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
151
+ vm.qmp_log('block-export-add', **get_export(allow_inactive=True))
152
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
153
+ vm.qmp_log('query-block-exports')
154
+ vm.qmp_log('block-export-del', id='exp0')
155
+ vm.event_wait('BLOCK_EXPORT_DELETED')
156
+ vm.qmp_log('query-block-exports')
157
+
158
+ iotests.log('\n=== Inactivating node with existing export ===')
159
+
160
+ iotests.log('\nInactivating nodes with an export fails without '
161
+ 'allow-inactive')
162
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
163
+ vm.qmp_log('block-export-add', **get_export(node_name='disk-fmt'))
164
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
165
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
166
+ vm.qmp_log('query-block-exports')
167
+ vm.qmp_log('block-export-del', id='exp0')
168
+ vm.event_wait('BLOCK_EXPORT_DELETED')
169
+ vm.qmp_log('query-block-exports')
170
+
171
+ iotests.log('\nInactivating nodes with an export fails with '
172
+ 'allow-inactive=false')
173
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
174
+ vm.qmp_log('block-export-add',
175
+ **get_export(node_name='disk-fmt', allow_inactive=False))
176
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
177
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
178
+ vm.qmp_log('query-block-exports')
179
+ vm.qmp_log('block-export-del', id='exp0')
180
+ vm.event_wait('BLOCK_EXPORT_DELETED')
181
+ vm.qmp_log('query-block-exports')
182
+
183
+ iotests.log('\nInactivating nodes with an export works with '
184
+ 'allow-inactive=true')
185
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
186
+ vm.qmp_log('block-export-add',
187
+ **get_export(node_name='disk-fmt', allow_inactive=True))
188
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
189
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
190
+ vm.qmp_log('query-block-exports')
191
+ vm.qmp_log('block-export-del', id='exp0')
192
+ vm.event_wait('BLOCK_EXPORT_DELETED')
193
+ vm.qmp_log('query-block-exports')
194
+
195
+ iotests.log('\n=== Inactive nodes with parent ===')
196
+
197
+ iotests.log('\nInactivating nodes with an active parent fails')
198
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
199
+ vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False)
200
+ iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file'))
201
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
202
+
203
+ iotests.log('\nInactivating nodes with an inactive parent works')
204
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False)
205
+ vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False)
206
+ iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file'))
207
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
208
+
209
+ iotests.log('\nCreating active parent node with an inactive child fails')
210
+ vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt',
211
+ node_name='disk-filter')
212
+ vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt',
213
+ node_name='disk-filter', active=True)
214
+
215
+ iotests.log('\nCreating inactive parent node with an inactive child works')
216
+ vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt',
217
+ node_name='disk-filter', active=False)
218
+ vm.qmp_log('blockdev-del', node_name='disk-filter')
219
+
220
+ iotests.log('\n=== Resizing an inactive node ===')
221
+ vm.qmp_log('block_resize', node_name='disk-fmt', size=16*1024*1024)
222
+
223
+ iotests.log('\n=== Taking a snapshot of an inactive node ===')
224
+
225
+ iotests.log('\nActive overlay over inactive backing file automatically '
226
+ 'makes both inactive for compatibility')
227
+ vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt',
228
+ file='snap-file', backing=None)
229
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
230
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
231
+ vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt')
232
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
233
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
234
+ vm.qmp_log('blockdev-del', node_name='snap-fmt')
235
+
236
+ iotests.log('\nInactive overlay over inactive backing file just works')
237
+ vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt',
238
+ file='snap-file', backing=None, active=False)
239
+ vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt')
240
+
241
+ iotests.log('\n=== Block jobs with inactive nodes ===')
242
+
243
+ iotests.log('\nStreaming into an inactive node')
244
+ vm.qmp_log('block-stream', device='snap-fmt',
245
+ filters=[iotests.filter_qmp_generated_node_ids])
246
+
247
+ iotests.log('\nCommitting an inactive root node (active commit)')
248
+ vm.qmp_log('block-commit', job_id='job0', device='snap-fmt',
249
+ filters=[iotests.filter_qmp_generated_node_ids])
250
+
251
+ iotests.log('\nCommitting an inactive intermediate node to inactive base')
252
+ vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap2-fmt',
253
+ file='snap2-file', backing='snap-fmt', active=False)
254
+
255
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
256
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
257
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
258
+
259
+ vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt',
260
+ top_node='snap-fmt',
261
+ filters=[iotests.filter_qmp_generated_node_ids])
262
+
263
+ iotests.log('\nCommitting an inactive intermediate node to active base')
264
+ vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True)
265
+ vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt',
266
+ top_node='snap-fmt',
267
+ filters=[iotests.filter_qmp_generated_node_ids])
268
+
269
+ iotests.log('\nMirror from inactive source to active target')
270
+ vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt',
271
+ target='target-fmt', sync='full',
272
+ filters=[iotests.filter_qmp_generated_node_ids])
273
+
274
+ iotests.log('\nMirror from active source to inactive target')
275
+
276
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
277
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
278
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
279
+ iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
280
+
281
+ # Activating snap2-fmt recursively activates the whole backing chain
282
+ vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=True)
283
+ vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False)
284
+
285
+ iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt'))
286
+ iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt'))
287
+ iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt'))
288
+ iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt'))
289
+
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
349
new file mode 100644
350
index XXXXXXX..XXXXXXX
351
--- /dev/null
352
+++ b/tests/qemu-iotests/tests/inactive-node-nbd.out
353
@@ -XXX,XX +XXX,XX @@
354
+Preparing disk...
355
+Launching VM...
356
+{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}}
357
+{"return": {}}
358
+
359
+=== Creating export of inactive node ===
360
+
361
+Exports activate nodes without allow-inactive
362
+disk-fmt active: False
363
+{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
364
+{"return": {}}
365
+disk-fmt active: True
366
+{"execute": "query-block-exports", "arguments": {}}
367
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
368
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
369
+{"return": {}}
370
+{"execute": "query-block-exports", "arguments": {}}
371
+{"return": []}
372
+
373
+Exports activate nodes with allow-inactive=false
374
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
375
+{"return": {}}
376
+disk-fmt active: False
377
+{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
378
+{"return": {}}
379
+disk-fmt active: True
380
+{"execute": "query-block-exports", "arguments": {}}
381
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
382
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
383
+{"return": {}}
384
+{"execute": "query-block-exports", "arguments": {}}
385
+{"return": []}
386
+
387
+Export leaves nodes inactive with allow-inactive=true
388
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
389
+{"return": {}}
390
+disk-fmt active: False
391
+{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
392
+{"return": {}}
393
+disk-fmt active: False
394
+{"execute": "query-block-exports", "arguments": {}}
395
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
396
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
397
+{"return": {}}
398
+{"execute": "query-block-exports", "arguments": {}}
399
+{"return": []}
400
+
401
+=== Inactivating node with existing export ===
402
+
403
+Inactivating nodes with an export fails without allow-inactive
404
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
405
+{"return": {}}
406
+{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
407
+{"return": {}}
408
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
409
+{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}}
410
+disk-fmt active: True
411
+{"execute": "query-block-exports", "arguments": {}}
412
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
413
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
414
+{"return": {}}
415
+{"execute": "query-block-exports", "arguments": {}}
416
+{"return": []}
417
+
418
+Inactivating nodes with an export fails with allow-inactive=false
419
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
420
+{"return": {}}
421
+{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
422
+{"return": {}}
423
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
424
+{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}}
425
+disk-fmt active: True
426
+{"execute": "query-block-exports", "arguments": {}}
427
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
428
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
429
+{"return": {}}
430
+{"execute": "query-block-exports", "arguments": {}}
431
+{"return": []}
432
+
433
+Inactivating nodes with an export works with allow-inactive=true
434
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
435
+{"return": {}}
436
+{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}}
437
+{"return": {}}
438
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}}
439
+{"return": {}}
440
+disk-fmt active: False
441
+{"execute": "query-block-exports", "arguments": {}}
442
+{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]}
443
+{"execute": "block-export-del", "arguments": {"id": "exp0"}}
444
+{"return": {}}
445
+{"execute": "query-block-exports", "arguments": {}}
446
+{"return": []}
447
+
448
+=== Inactive nodes with parent ===
449
+
450
+Inactivating nodes with an active parent fails
451
+{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}}
452
+{"return": {}}
453
+{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}}
454
+{"error": {"class": "GenericError", "desc": "Node has active parent node"}}
455
+disk-file active: True
456
+disk-fmt active: True
457
+
458
+Inactivating nodes with an inactive parent works
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
+
593
--
594
2.48.1
diff view generated by jsdifflib
Deleted patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
1
3
BLOCK_OP_TYPE_DATAPLANE prevents BlockDriverState from being used by
4
virtio-blk/virtio-scsi with IOThread. Commit b112a65c52aa ("block:
5
declare blockjobs and dataplane friends!") eliminated the main reason
6
for this blocker in 2014.
7
8
Nowadays the block layer supports I/O from multiple AioContexts, so
9
there is even less reason to block IOThread users. Any legitimate
10
reasons related to interference would probably also apply to
11
non-IOThread users.
12
13
The only remaining users are bdrv_op_unblock(BLOCK_OP_TYPE_DATAPLANE)
14
calls after bdrv_op_block_all(). If we remove BLOCK_OP_TYPE_DATAPLANE
15
their behavior doesn't change.
16
17
Existing bdrv_op_block_all() callers that don't explicitly unblock
18
BLOCK_OP_TYPE_DATAPLANE seem to do so simply because no one bothered to
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>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
---
28
include/block/block-common.h | 1 -
29
block/replication.c | 1 -
30
blockjob.c | 2 --
31
hw/block/virtio-blk.c | 9 ---------
32
hw/scsi/virtio-scsi.c | 3 ---
33
5 files changed, 16 deletions(-)
34
35
diff --git a/include/block/block-common.h b/include/block/block-common.h
36
index XXXXXXX..XXXXXXX 100644
37
--- a/include/block/block-common.h
38
+++ b/include/block/block-common.h
39
@@ -XXX,XX +XXX,XX @@ typedef enum BlockOpType {
40
BLOCK_OP_TYPE_CHANGE,
41
BLOCK_OP_TYPE_COMMIT_SOURCE,
42
BLOCK_OP_TYPE_COMMIT_TARGET,
43
- BLOCK_OP_TYPE_DATAPLANE,
44
BLOCK_OP_TYPE_DRIVE_DEL,
45
BLOCK_OP_TYPE_EJECT,
46
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
47
diff --git a/block/replication.c b/block/replication.c
48
index XXXXXXX..XXXXXXX 100644
49
--- a/block/replication.c
50
+++ b/block/replication.c
51
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
52
return;
53
}
54
bdrv_op_block_all(top_bs, s->blocker);
55
- bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
56
57
bdrv_graph_wrunlock();
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;
106
--
107
2.48.1
diff view generated by jsdifflib