1
The following changes since commit eda1df0345f5a1e337e30367124dcb0e802bdfde:
1
The following changes since commit ccdf06c1db192152ac70a1dd974c624f566cb7d4:
2
2
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-pflash-2019-03-11' into staging (2019-03-12 11:12:36 +0000)
3
Open 6.1 development tree (2021-04-30 11:15:40 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to c31dfeb02a1d155bdb961edeb61a137a589c174b:
9
for you to fetch changes up to 68bf7336533faa6aa90fdd4558edddbf5d8ef814:
10
10
11
qemu-iotests: Test the x-blockdev-reopen QMP command (2019-03-12 17:58:37 +0100)
11
vhost-user-blk: Fail gracefully on too large queue size (2021-04-30 12:27:48 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
15
16
- file-posix: Make auto-read-only dynamic
16
- Fix permission update order problems with block graph changes
17
- Add x-blockdev-reopen QMP command
17
- qemu-img convert: Unshare write permission for source
18
- Finalize block-latency-histogram QMP command
18
- vhost-user-blk: Fail gracefully on too large queue size
19
- gluster: Build fixes for newer lib version
20
19
21
----------------------------------------------------------------
20
----------------------------------------------------------------
22
Alberto Garcia (13):
21
Kevin Wolf (3):
23
block: Allow freezing BdrvChild links
22
block: Add BDRV_O_NO_SHARE for blk_new_open()
24
block: Freeze the backing chain for the duration of the commit job
23
qemu-img convert: Unshare write permission for source
25
block: Freeze the backing chain for the duration of the mirror job
24
vhost-user-blk: Fail gracefully on too large queue size
26
block: Freeze the backing chain for the duration of the stream job
27
block: Add 'keep_old_opts' parameter to bdrv_reopen_queue()
28
block: Handle child references in bdrv_reopen_queue()
29
block: Allow omitting the 'backing' option in certain cases
30
block: Allow changing the backing file on reopen
31
block: Add a 'mutable_opts' field to BlockDriver
32
block: Add bdrv_reset_options_allowed()
33
block: Remove the AioContext parameter from bdrv_reopen_multiple()
34
block: Add an 'x-blockdev-reopen' QMP command
35
qemu-iotests: Test the x-blockdev-reopen QMP command
36
25
37
Keith Busch (1):
26
Vladimir Sementsov-Ogievskiy (36):
38
nvme: fix write zeroes offset and count
27
tests/test-bdrv-graph-mod: add test_parallel_exclusive_write
28
tests/test-bdrv-graph-mod: add test_parallel_perm_update
29
tests/test-bdrv-graph-mod: add test_append_greedy_filter
30
block: bdrv_append(): don't consume reference
31
block: BdrvChildClass: add .get_parent_aio_context handler
32
block: drop ctx argument from bdrv_root_attach_child
33
block: make bdrv_reopen_{prepare,commit,abort} private
34
util: add transactions.c
35
block: bdrv_refresh_perms: check for parents permissions conflict
36
block: refactor bdrv_child* permission functions
37
block: rewrite bdrv_child_try_set_perm() using bdrv_refresh_perms()
38
block: inline bdrv_child_*() permission functions calls
39
block: use topological sort for permission update
40
block: add bdrv_drv_set_perm transaction action
41
block: add bdrv_list_* permission update functions
42
block: add bdrv_replace_child_safe() transaction action
43
block: fix bdrv_replace_node_common
44
block: add bdrv_attach_child_common() transaction action
45
block: add bdrv_attach_child_noperm() transaction action
46
block: split out bdrv_replace_node_noperm()
47
block: adapt bdrv_append() for inserting filters
48
block: add bdrv_remove_filter_or_cow transaction action
49
block: introduce bdrv_drop_filter()
50
block/backup-top: drop .active
51
block: drop ignore_children for permission update functions
52
block: make bdrv_unset_inherits_from to be a transaction action
53
block: make bdrv_refresh_limits() to be a transaction action
54
block: add bdrv_set_backing_noperm() transaction action
55
block: bdrv_reopen_multiple(): move bdrv_flush to separate pre-prepare
56
block: bdrv_reopen_multiple: refresh permissions on updated graph
57
block: drop unused permission update functions
58
block: inline bdrv_check_perm_common()
59
block: inline bdrv_replace_child()
60
block: refactor bdrv_child_set_perm_safe() transaction action
61
block: rename bdrv_replace_child_safe() to bdrv_replace_child()
62
block: refactor bdrv_node_check_perm()
39
63
40
Kevin Wolf (10):
64
include/block/block.h | 14 +-
41
tests/virtio-blk-test: Disable auto-read-only
65
include/block/block_int.h | 8 +-
42
qemu-iotests: commit to backing file with auto-read-only
66
include/qemu/transactions.h | 63 ++
43
block: Avoid useless local_err
67
block.c | 1329 ++++++++++++++++++++-------------
44
block: Make permission changes in reopen less wrong
68
block/backup-top.c | 48 +-
45
file-posix: Fix bdrv_open_flags() for snapshot=on
69
block/block-backend.c | 30 +-
46
file-posix: Factor out raw_reconfigure_getfd()
70
block/commit.c | 1 +
47
file-posix: Store BDRVRawState.reopen_state during reopen
71
block/file-posix.c | 91 +--
48
file-posix: Lock new fd in raw_reopen_prepare()
72
block/io.c | 31 +-
49
file-posix: Prepare permission code for fd switching
73
block/mirror.c | 3 -
50
file-posix: Make auto-read-only dynamic
74
blockdev.c | 4 -
75
blockjob.c | 11 +-
76
hw/block/vhost-user-blk.c | 5 +
77
qemu-img.c | 2 +-
78
tests/unit/test-bdrv-drain.c | 2 +-
79
tests/unit/test-bdrv-graph-mod.c | 209 +++++-
80
util/transactions.c | 96 +++
81
MAINTAINERS | 6 +
82
tests/qemu-iotests/245 | 2 +-
83
tests/qemu-iotests/283.out | 2 +-
84
tests/qemu-iotests/tests/qsd-jobs.out | 2 +-
85
util/meson.build | 1 +
86
22 files changed, 1280 insertions(+), 680 deletions(-)
87
create mode 100644 include/qemu/transactions.h
88
create mode 100644 util/transactions.c
51
89
52
Niels de Vos (1):
53
gluster: the glfs_io_cbk callback function pointer adds pre/post stat args
54
90
55
Prasanna Kumar Kalever (1):
56
gluster: Handle changed glfs_ftruncate signature
57
58
Vladimir Sementsov-Ogievskiy (2):
59
qapi: move to QOM path for x-block-latency-histogram-set
60
qapi: drop x- from x-block-latency-histogram-set
61
62
qapi/block-core.json | 66 ++-
63
configure | 42 ++
64
include/block/block.h | 13 +-
65
include/block/block_int.h | 14 +
66
block.c | 440 +++++++++++++++++--
67
block/commit.c | 16 +
68
block/file-posix.c | 254 ++++++++---
69
block/gluster.c | 10 +-
70
block/mirror.c | 8 +
71
block/qapi.c | 12 +-
72
block/qcow2.c | 25 ++
73
block/raw-format.c | 3 +
74
block/replication.c | 7 +-
75
block/stream.c | 21 +
76
blockdev.c | 61 ++-
77
hw/block/nvme.c | 6 +-
78
qemu-io-cmds.c | 4 +-
79
tests/virtio-blk-test.c | 2 +-
80
tests/qemu-iotests/051 | 7 +
81
tests/qemu-iotests/051.out | 9 +
82
tests/qemu-iotests/051.pc.out | 9 +
83
tests/qemu-iotests/232 | 31 ++
84
tests/qemu-iotests/232.out | 32 +-
85
tests/qemu-iotests/245 | 991 ++++++++++++++++++++++++++++++++++++++++++
86
tests/qemu-iotests/245.out | 5 +
87
tests/qemu-iotests/group | 1 +
88
26 files changed, 1929 insertions(+), 160 deletions(-)
89
create mode 100644 tests/qemu-iotests/245
90
create mode 100644 tests/qemu-iotests/245.out
91
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Add the test that shows that concept of ignore_children is incomplete.
4
Actually, when we want to update something, ignoring permission of some
5
existing BdrvChild, we should ignore also the propagated effect of this
6
child to the other children. But that's not done. Better approach
7
(update permissions on already updated graph) will be implemented
8
later.
9
10
Now the test fails, so it's added with -d argument to not break make
11
check.
12
13
Test fails with
14
15
"Conflicts with use by fl1 as 'backing', which does not allow 'write' on base"
16
17
because when updating permissions we can ignore original top->fl1
18
BdrvChild. But we don't ignore exclusive write permission in fl1->base
19
BdrvChild, which is propagated. Correct thing to do is make graph
20
change first and then do permission update from the top node.
21
22
To run test do
23
24
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-exclusive-write
25
26
from <build-directory>/tests.
27
28
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
29
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
30
Message-Id: <20210428151804.439460-2-vsementsov@virtuozzo.com>
31
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
32
---
33
tests/unit/test-bdrv-graph-mod.c | 70 +++++++++++++++++++++++++++++++-
34
1 file changed, 69 insertions(+), 1 deletion(-)
35
36
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
37
index XXXXXXX..XXXXXXX 100644
38
--- a/tests/unit/test-bdrv-graph-mod.c
39
+++ b/tests/unit/test-bdrv-graph-mod.c
40
@@ -XXX,XX +XXX,XX @@
41
/*
42
* Block node graph modifications tests
43
*
44
- * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved.
45
+ * Copyright (c) 2019-2021 Virtuozzo International GmbH. All rights reserved.
46
*
47
* This program is free software; you can redistribute it and/or modify
48
* it under the terms of the GNU General Public License as published by
49
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_no_perm = {
50
.bdrv_child_perm = no_perm_default_perms,
51
};
52
53
+static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c,
54
+ BdrvChildRole role,
55
+ BlockReopenQueue *reopen_queue,
56
+ uint64_t perm, uint64_t shared,
57
+ uint64_t *nperm, uint64_t *nshared)
58
+{
59
+ *nperm = BLK_PERM_WRITE;
60
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
61
+}
62
+
63
+static BlockDriver bdrv_exclusive_writer = {
64
+ .format_name = "exclusive-writer",
65
+ .bdrv_child_perm = exclusive_write_perms,
66
+};
67
+
68
static BlockDriverState *no_perm_node(const char *name)
69
{
70
return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
71
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *pass_through_node(const char *name)
72
BDRV_O_RDWR, &error_abort);
73
}
74
75
+static BlockDriverState *exclusive_writer_node(const char *name)
76
+{
77
+ return bdrv_new_open_driver(&bdrv_exclusive_writer, name,
78
+ BDRV_O_RDWR, &error_abort);
79
+}
80
+
81
/*
82
* test_update_perm_tree
83
*
84
@@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void)
85
blk_unref(root);
86
}
87
88
+/*
89
+ * test_parallel_exclusive_write
90
+ *
91
+ * Check that when we replace node, old permissions of the node being removed
92
+ * doesn't break the replacement.
93
+ */
94
+static void test_parallel_exclusive_write(void)
95
+{
96
+ BlockDriverState *top = exclusive_writer_node("top");
97
+ BlockDriverState *base = no_perm_node("base");
98
+ BlockDriverState *fl1 = pass_through_node("fl1");
99
+ BlockDriverState *fl2 = pass_through_node("fl2");
100
+
101
+ /*
102
+ * bdrv_attach_child() eats child bs reference, so we need two @base
103
+ * references for two filters:
104
+ */
105
+ bdrv_ref(base);
106
+
107
+ bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_DATA,
108
+ &error_abort);
109
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
110
+ &error_abort);
111
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
112
+ &error_abort);
113
+
114
+ bdrv_replace_node(fl1, fl2, &error_abort);
115
+
116
+ bdrv_unref(fl2);
117
+ bdrv_unref(top);
118
+}
119
+
120
int main(int argc, char *argv[])
121
{
122
+ int i;
123
+ bool debug = false;
124
+
125
+ for (i = 1; i < argc; i++) {
126
+ if (!strcmp(argv[i], "-d")) {
127
+ debug = true;
128
+ break;
129
+ }
130
+ }
131
+
132
bdrv_init();
133
qemu_init_main_loop(&error_abort);
134
135
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
136
g_test_add_func("/bdrv-graph-mod/should-update-child",
137
test_should_update_child);
138
139
+ if (debug) {
140
+ g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
141
+ test_parallel_exclusive_write);
142
+ }
143
+
144
return g_test_run();
145
}
146
--
147
2.30.2
148
149
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
3
Add test to show that simple DFS recursion order is not correct for
4
permission update. Correct order is topological-sort order, which will
5
be introduced later.
6
7
Consider the block driver which has two filter children: one active
8
with exclusive write access and one inactive with no specific
9
permissions.
10
11
And, these two children has a common base child, like this:
12
13
┌─────┐ ┌──────┐
14
│ fl2 │ ◀── │ top │
15
└─────┘ └──────┘
16
│ │
17
│ │ w
18
│ ▼
19
│ ┌──────┐
20
│ │ fl1 │
21
│ └──────┘
22
│ │
23
│ │ w
24
│ ▼
25
│ ┌──────┐
26
└───────▶ │ base │
27
└──────┘
28
29
So, exclusive write is propagated.
30
31
Assume, we want to make fl2 active instead of fl1.
32
So, we set some option for top driver and do permission update.
33
34
If permission update (remember, it's DFS) goes first through
35
top->fl1->base branch it will succeed: it firstly drop exclusive write
36
permissions and than apply them for another BdrvChildren.
37
But if permission update goes first through top->fl2->base branch it
38
will fail, as when we try to update fl2->base child, old not yet
39
updated fl1->base child will be in conflict.
40
41
Now test fails, so it runs only with -d flag. To run do
42
43
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-perm-update
44
45
from <build-directory>/tests.
46
47
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
48
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
49
Message-Id: <20210428151804.439460-3-vsementsov@virtuozzo.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
51
---
6
block/stream.c | 21 +++++++++++++++++++++
52
tests/unit/test-bdrv-graph-mod.c | 116 +++++++++++++++++++++++++++++++
7
1 file changed, 21 insertions(+)
53
1 file changed, 116 insertions(+)
8
54
9
diff --git a/block/stream.c b/block/stream.c
55
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
10
index XXXXXXX..XXXXXXX 100644
56
index XXXXXXX..XXXXXXX 100644
11
--- a/block/stream.c
57
--- a/tests/unit/test-bdrv-graph-mod.c
12
+++ b/block/stream.c
58
+++ b/tests/unit/test-bdrv-graph-mod.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct StreamBlockJob {
59
@@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void)
14
BlockdevOnError on_error;
60
bdrv_unref(top);
15
char *backing_file_str;
16
bool bs_read_only;
17
+ bool chain_frozen;
18
} StreamBlockJob;
19
20
static int coroutine_fn stream_populate(BlockBackend *blk,
21
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_populate(BlockBackend *blk,
22
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
23
}
61
}
24
62
25
+static void stream_abort(Job *job)
63
+static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
64
+ BdrvChildRole role,
65
+ BlockReopenQueue *reopen_queue,
66
+ uint64_t perm, uint64_t shared,
67
+ uint64_t *nperm, uint64_t *nshared)
26
+{
68
+{
27
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
69
+ if (bs->file && c == bs->file) {
28
+
70
+ *nperm = BLK_PERM_WRITE;
29
+ if (s->chain_frozen) {
71
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
30
+ BlockJob *bjob = &s->common;
72
+ } else {
31
+ bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base);
73
+ *nperm = 0;
74
+ *nshared = BLK_PERM_ALL;
32
+ }
75
+ }
33
+}
76
+}
34
+
77
+
35
static int stream_prepare(Job *job)
78
+static BlockDriver bdrv_write_to_file = {
79
+ .format_name = "tricky-perm",
80
+ .bdrv_child_perm = write_to_file_perms,
81
+};
82
+
83
+
84
+/*
85
+ * The following test shows that topological-sort order is required for
86
+ * permission update, simple DFS is not enough.
87
+ *
88
+ * Consider the block driver which has two filter children: one active
89
+ * with exclusive write access and one inactive with no specific
90
+ * permissions.
91
+ *
92
+ * And, these two children has a common base child, like this:
93
+ *
94
+ * ┌─────┐ ┌──────┐
95
+ * │ fl2 │ ◀── │ top │
96
+ * └─────┘ └──────┘
97
+ * │ │
98
+ * │ │ w
99
+ * │ ▼
100
+ * │ ┌──────┐
101
+ * │ │ fl1 │
102
+ * │ └──────┘
103
+ * │ │
104
+ * │ │ w
105
+ * │ ▼
106
+ * │ ┌──────┐
107
+ * └───────▶ │ base │
108
+ * └──────┘
109
+ *
110
+ * So, exclusive write is propagated.
111
+ *
112
+ * Assume, we want to make fl2 active instead of fl1.
113
+ * So, we set some option for top driver and do permission update.
114
+ *
115
+ * With simple DFS, if permission update goes first through
116
+ * top->fl1->base branch it will succeed: it firstly drop exclusive write
117
+ * permissions and than apply them for another BdrvChildren.
118
+ * But if permission update goes first through top->fl2->base branch it
119
+ * will fail, as when we try to update fl2->base child, old not yet
120
+ * updated fl1->base child will be in conflict.
121
+ *
122
+ * With topological-sort order we always update parents before children, so fl1
123
+ * and fl2 are both updated when we update base and there is no conflict.
124
+ */
125
+static void test_parallel_perm_update(void)
126
+{
127
+ BlockDriverState *top = no_perm_node("top");
128
+ BlockDriverState *tricky =
129
+ bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR,
130
+ &error_abort);
131
+ BlockDriverState *base = no_perm_node("base");
132
+ BlockDriverState *fl1 = pass_through_node("fl1");
133
+ BlockDriverState *fl2 = pass_through_node("fl2");
134
+ BdrvChild *c_fl1, *c_fl2;
135
+
136
+ /*
137
+ * bdrv_attach_child() eats child bs reference, so we need two @base
138
+ * references for two filters:
139
+ */
140
+ bdrv_ref(base);
141
+
142
+ bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA,
143
+ &error_abort);
144
+ c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds,
145
+ BDRV_CHILD_FILTERED, &error_abort);
146
+ c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds,
147
+ BDRV_CHILD_FILTERED, &error_abort);
148
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
149
+ &error_abort);
150
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
151
+ &error_abort);
152
+
153
+ /* Select fl1 as first child to be active */
154
+ tricky->file = c_fl1;
155
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
156
+
157
+ assert(c_fl1->perm & BLK_PERM_WRITE);
158
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
159
+
160
+ /* Now, try to switch active child and update permissions */
161
+ tricky->file = c_fl2;
162
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
163
+
164
+ assert(c_fl2->perm & BLK_PERM_WRITE);
165
+ assert(!(c_fl1->perm & BLK_PERM_WRITE));
166
+
167
+ /* Switch once more, to not care about real child order in the list */
168
+ tricky->file = c_fl1;
169
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
170
+
171
+ assert(c_fl1->perm & BLK_PERM_WRITE);
172
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
173
+
174
+ bdrv_unref(top);
175
+}
176
+
177
int main(int argc, char *argv[])
36
{
178
{
37
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
179
int i;
38
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
180
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
39
Error *local_err = NULL;
181
if (debug) {
40
int ret = 0;
182
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
41
183
test_parallel_exclusive_write);
42
+ bdrv_unfreeze_backing_chain(bs, base);
184
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
43
+ s->chain_frozen = false;
185
+ test_parallel_perm_update);
44
+
45
if (bs->backing) {
46
const char *base_id = NULL, *base_fmt = NULL;
47
if (base) {
48
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
49
.free = block_job_free,
50
.run = stream_run,
51
.prepare = stream_prepare,
52
+ .abort = stream_abort,
53
.clean = stream_clean,
54
.user_resume = block_job_user_resume,
55
.drain = block_job_drain,
56
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
57
&error_abort);
58
}
186
}
59
187
60
+ if (bdrv_freeze_backing_chain(bs, base, errp) < 0) {
188
return g_test_run();
61
+ job_early_fail(&s->common.job);
62
+ goto fail;
63
+ }
64
+
65
s->base = base;
66
s->backing_file_str = g_strdup(backing_file_str);
67
s->bs_read_only = bs_read_only;
68
+ s->chain_frozen = true;
69
70
s->on_error = on_error;
71
trace_stream_start(bs, base, s);
72
--
189
--
73
2.20.1
190
2.30.2
74
191
75
192
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
bdrv_append() is not quite good for inserting filters: it does extra
4
permission update in intermediate state, where filter get it filtered
5
child but is not yet replace it in a backing chain.
6
7
Some filters (for example backup-top) may want permissions even when
8
have no parents. And described intermediate state becomes invalid.
9
10
That's (half a) reason, why we need "inactive" state for backup-top
11
filter.
12
13
bdrv_append() will be improved later, now let's add a unit test.
14
15
Now test fails, so it runs only with -d flag. To run do
16
17
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/append-greedy-filter
18
19
from <build-directory>/tests.
20
21
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
23
Message-Id: <20210428151804.439460-4-vsementsov@virtuozzo.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
26
tests/unit/test-bdrv-graph-mod.c | 33 ++++++++++++++++++++++++++++++++
27
1 file changed, 33 insertions(+)
28
29
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/tests/unit/test-bdrv-graph-mod.c
32
+++ b/tests/unit/test-bdrv-graph-mod.c
33
@@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void)
34
bdrv_unref(top);
35
}
36
37
+/*
38
+ * It's possible that filter required permissions allows to insert it to backing
39
+ * chain, like:
40
+ *
41
+ * 1. [top] -> [filter] -> [base]
42
+ *
43
+ * but doesn't allow to add it as a branch:
44
+ *
45
+ * 2. [filter] --\
46
+ * v
47
+ * [top] -> [base]
48
+ *
49
+ * So, inserting such filter should do all graph modifications and only then
50
+ * update permissions. If we try to go through intermediate state [2] and update
51
+ * permissions on it we'll fail.
52
+ *
53
+ * Let's check that bdrv_append() can append such a filter.
54
+ */
55
+static void test_append_greedy_filter(void)
56
+{
57
+ BlockDriverState *top = exclusive_writer_node("top");
58
+ BlockDriverState *base = no_perm_node("base");
59
+ BlockDriverState *fl = exclusive_writer_node("fl1");
60
+
61
+ bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_COW,
62
+ &error_abort);
63
+
64
+ bdrv_append(fl, base, &error_abort);
65
+ bdrv_unref(top);
66
+}
67
+
68
int main(int argc, char *argv[])
69
{
70
int i;
71
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
72
test_parallel_exclusive_write);
73
g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
74
test_parallel_perm_update);
75
+ g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
76
+ test_append_greedy_filter);
77
}
78
79
return g_test_run();
80
--
81
2.30.2
82
83
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
3
We have too much comments for this feature. It seems better just don't
4
do it. Most of real users (tests don't count) have to create additional
5
reference.
6
7
Drop also comment in external_snapshot_prepare:
8
- bdrv_append doesn't "remove" old bs in common sense, it sounds
9
strange
10
- the fact that bdrv_append can fail is obvious from the context
11
- the fact that we must rollback all changes in transaction abort is
12
known (it's the direct role of abort)
13
14
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
15
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
16
Message-Id: <20210428151804.439460-5-vsementsov@virtuozzo.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
18
---
6
block/commit.c | 16 ++++++++++++++++
19
block.c | 25 +++----------------------
7
1 file changed, 16 insertions(+)
20
block/backup-top.c | 1 -
21
block/commit.c | 1 +
22
block/mirror.c | 3 ---
23
blockdev.c | 4 ----
24
tests/unit/test-bdrv-drain.c | 2 +-
25
tests/unit/test-bdrv-graph-mod.c | 3 +++
26
7 files changed, 8 insertions(+), 31 deletions(-)
8
27
28
diff --git a/block.c b/block.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/block.c
31
+++ b/block.c
32
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
33
goto out;
34
}
35
36
- /* bdrv_append() consumes a strong reference to bs_snapshot
37
- * (i.e. it will call bdrv_unref() on it) even on error, so in
38
- * order to be able to return one, we have to increase
39
- * bs_snapshot's refcount here */
40
- bdrv_ref(bs_snapshot);
41
ret = bdrv_append(bs_snapshot, bs, errp);
42
if (ret < 0) {
43
bs_snapshot = NULL;
44
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
45
* bs_new must not be attached to a BlockBackend.
46
*
47
* This function does not create any image files.
48
- *
49
- * bdrv_append() takes ownership of a bs_new reference and unrefs it because
50
- * that's what the callers commonly need. bs_new will be referenced by the old
51
- * parents of bs_top after bdrv_append() returns. If the caller needs to keep a
52
- * reference of its own, it must call bdrv_ref().
53
*/
54
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
55
Error **errp)
56
{
57
int ret = bdrv_set_backing_hd(bs_new, bs_top, errp);
58
if (ret < 0) {
59
- goto out;
60
+ return ret;
61
}
62
63
ret = bdrv_replace_node(bs_top, bs_new, errp);
64
if (ret < 0) {
65
bdrv_set_backing_hd(bs_new, NULL, &error_abort);
66
- goto out;
67
+ return ret;
68
}
69
70
- ret = 0;
71
-
72
-out:
73
- /*
74
- * bs_new is now referenced by its new parents, we don't need the
75
- * additional reference any more.
76
- */
77
- bdrv_unref(bs_new);
78
-
79
- return ret;
80
+ return 0;
81
}
82
83
static void bdrv_delete(BlockDriverState *bs)
84
diff --git a/block/backup-top.c b/block/backup-top.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/block/backup-top.c
87
+++ b/block/backup-top.c
88
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
89
90
bdrv_drained_begin(source);
91
92
- bdrv_ref(top);
93
ret = bdrv_append(top, source, errp);
94
if (ret < 0) {
95
error_prepend(errp, "Cannot append backup-top filter: ");
9
diff --git a/block/commit.c b/block/commit.c
96
diff --git a/block/commit.c b/block/commit.c
10
index XXXXXXX..XXXXXXX 100644
97
index XXXXXXX..XXXXXXX 100644
11
--- a/block/commit.c
98
--- a/block/commit.c
12
+++ b/block/commit.c
99
+++ b/block/commit.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct CommitBlockJob {
14
BlockDriverState *base_bs;
15
BlockdevOnError on_error;
16
bool base_read_only;
17
+ bool chain_frozen;
18
char *backing_file_str;
19
} CommitBlockJob;
20
21
@@ -XXX,XX +XXX,XX @@ static int commit_prepare(Job *job)
22
{
23
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
24
25
+ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
26
+ s->chain_frozen = false;
27
+
28
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
29
* the normal backing chain can be restored. */
30
blk_unref(s->base);
31
@@ -XXX,XX +XXX,XX @@ static void commit_abort(Job *job)
32
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
33
BlockDriverState *top_bs = blk_bs(s->top);
34
35
+ if (s->chain_frozen) {
36
+ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
37
+ }
38
+
39
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
40
bdrv_ref(top_bs);
41
bdrv_ref(s->commit_top_bs);
42
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
100
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
43
}
101
commit_top_bs->total_sectors = top->total_sectors;
102
103
ret = bdrv_append(commit_top_bs, top, errp);
104
+ bdrv_unref(commit_top_bs); /* referenced by new parents or failed */
105
if (ret < 0) {
106
commit_top_bs = NULL;
107
goto fail;
108
diff --git a/block/mirror.c b/block/mirror.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/mirror.c
111
+++ b/block/mirror.c
112
@@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job(
113
114
bs_opaque->is_commit = target_is_backing;
115
116
- /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
117
- * it alive until block_job_create() succeeds even if bs has no parent. */
118
- bdrv_ref(mirror_top_bs);
119
bdrv_drained_begin(bs);
120
ret = bdrv_append(mirror_top_bs, bs, errp);
121
bdrv_drained_end(bs);
122
diff --git a/blockdev.c b/blockdev.c
123
index XXXXXXX..XXXXXXX 100644
124
--- a/blockdev.c
125
+++ b/blockdev.c
126
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
127
goto out;
44
}
128
}
45
129
46
+ if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
130
- /* This removes our old bs and adds the new bs. This is an operation that
47
+ goto fail;
131
- * can fail, so we need to do it in .prepare; undoing it for abort is
48
+ }
132
- * always possible. */
49
+ s->chain_frozen = true;
133
- bdrv_ref(state->new_bs);
50
+
134
ret = bdrv_append(state->new_bs, state->old_bs, errp);
51
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
52
if (ret < 0) {
135
if (ret < 0) {
53
goto fail;
136
goto out;
54
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
137
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
55
return;
138
index XXXXXXX..XXXXXXX 100644
56
139
--- a/tests/unit/test-bdrv-drain.c
57
fail:
140
+++ b/tests/unit/test-bdrv-drain.c
58
+ if (s->chain_frozen) {
141
@@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void)
59
+ bdrv_unfreeze_backing_chain(commit_top_bs, base);
142
g_assert_cmpint(base_s->drain_count, ==, 1);
60
+ }
143
g_assert_cmpint(base->in_flight, ==, 0);
61
if (s->base) {
144
62
blk_unref(s->base);
145
- /* Takes ownership of overlay, so we don't have to unref it later */
63
}
146
bdrv_append(overlay, base, &error_abort);
147
g_assert_cmpint(base->in_flight, ==, 0);
148
g_assert_cmpint(overlay->in_flight, ==, 0);
149
@@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void)
150
g_assert_cmpint(overlay->quiesce_counter, ==, 0);
151
g_assert_cmpint(overlay_s->drain_count, ==, 0);
152
153
+ bdrv_unref(overlay);
154
bdrv_unref(base);
155
blk_unref(blk);
156
}
157
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
158
index XXXXXXX..XXXXXXX 100644
159
--- a/tests/unit/test-bdrv-graph-mod.c
160
+++ b/tests/unit/test-bdrv-graph-mod.c
161
@@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void)
162
ret = bdrv_append(filter, bs, NULL);
163
g_assert_cmpint(ret, <, 0);
164
165
+ bdrv_unref(filter);
166
blk_unref(root);
167
}
168
169
@@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void)
170
bdrv_append(filter, bs, &error_abort);
171
g_assert(target->backing->bs == bs);
172
173
+ bdrv_unref(filter);
174
bdrv_unref(bs);
175
blk_unref(root);
176
}
177
@@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void)
178
&error_abort);
179
180
bdrv_append(fl, base, &error_abort);
181
+ bdrv_unref(fl);
182
bdrv_unref(top);
183
}
184
64
--
185
--
65
2.20.1
186
2.30.2
66
187
67
188
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Our permission system is useful to define what operations are allowed
3
Add new handler to get aio context and implement it in all child
4
on a certain block node and includes things like BLK_PERM_WRITE or
4
classes. Add corresponding public interface to be used soon.
5
BLK_PERM_RESIZE among others.
6
5
7
One of the permissions is BLK_PERM_GRAPH_MOD which allows "changing
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
the node that this BdrvChild points to". The exact meaning of this has
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
never been very clear, but it can be understood as "change any of the
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
links connected to the node". This can be used to prevent changing a
9
Message-Id: <20210428151804.439460-6-vsementsov@virtuozzo.com>
11
backing link, but it's too coarse.
12
13
This patch adds a new 'frozen' attribute to BdrvChild, which forbids
14
detaching the link from the node it points to, and new API to freeze
15
and unfreeze a backing chain.
16
17
After this change a few functions can fail, so they need additional
18
checks.
19
20
Signed-off-by: Alberto Garcia <berto@igalia.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
11
---
23
include/block/block.h | 5 +++
12
include/block/block.h | 2 ++
24
include/block/block_int.h | 6 ++++
13
include/block/block_int.h | 2 ++
25
block.c | 76 +++++++++++++++++++++++++++++++++++++++
14
block.c | 13 +++++++++++++
26
3 files changed, 87 insertions(+)
15
block/block-backend.c | 9 +++++++++
16
blockjob.c | 8 ++++++++
17
5 files changed, 34 insertions(+)
27
18
28
diff --git a/include/block/block.h b/include/block/block.h
19
diff --git a/include/block/block.h b/include/block/block.h
29
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
30
--- a/include/block/block.h
21
--- a/include/block/block.h
31
+++ b/include/block/block.h
22
+++ b/include/block/block.h
32
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
23
@@ -XXX,XX +XXX,XX @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
33
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
24
GSList **ignore, Error **errp);
34
BlockDriverState *bs);
25
bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
35
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
26
GSList **ignore, Error **errp);
36
+bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
27
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
37
+ Error **errp);
28
+
38
+int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
29
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
39
+ Error **errp);
30
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
40
+void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base);
31
41
42
43
typedef struct BdrvCheckResult {
44
diff --git a/include/block/block_int.h b/include/block/block_int.h
32
diff --git a/include/block/block_int.h b/include/block/block_int.h
45
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
46
--- a/include/block/block_int.h
34
--- a/include/block/block_int.h
47
+++ b/include/block/block_int.h
35
+++ b/include/block/block_int.h
48
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
36
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
49
uint64_t backup_perm;
37
bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx,
50
uint64_t backup_shared_perm;
38
GSList **ignore, Error **errp);
51
39
void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore);
52
+ /*
53
+ * This link is frozen: the child can neither be replaced nor
54
+ * detached from the parent.
55
+ */
56
+ bool frozen;
57
+
40
+
58
QLIST_ENTRY(BdrvChild) next;
41
+ AioContext *(*get_parent_aio_context)(BdrvChild *child);
59
QLIST_ENTRY(BdrvChild) next_parent;
60
};
42
};
43
44
extern const BdrvChildClass child_of_bds;
61
diff --git a/block.c b/block.c
45
diff --git a/block.c b/block.c
62
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
63
--- a/block.c
47
--- a/block.c
64
+++ b/block.c
48
+++ b/block.c
65
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
49
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
66
BlockDriverState *old_bs = child->bs;
50
return 0;
67
int i;
51
}
68
52
69
+ assert(!child->frozen);
53
+static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c)
54
+{
55
+ BlockDriverState *bs = c->opaque;
70
+
56
+
71
if (old_bs && new_bs) {
57
+ return bdrv_get_aio_context(bs);
72
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
73
}
74
@@ -XXX,XX +XXX,XX @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
75
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
76
bdrv_inherits_from_recursive(backing_hd, bs);
77
78
+ if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) {
79
+ return;
80
+ }
81
+
82
if (backing_hd) {
83
bdrv_ref(backing_hd);
84
}
85
@@ -XXX,XX +XXX,XX @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
86
if (!should_update_child(c, to)) {
87
continue;
88
}
89
+ if (c->frozen) {
90
+ error_setg(errp, "Cannot change '%s' link to '%s'",
91
+ c->name, from->node_name);
92
+ goto out;
93
+ }
94
list = g_slist_prepend(list, c);
95
perm |= c->perm;
96
shared &= c->shared_perm;
97
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
98
return bdrv_find_overlay(bs, NULL);
99
}
100
101
+/*
102
+ * Return true if at least one of the backing links between @bs and
103
+ * @base is frozen. @errp is set if that's the case.
104
+ */
105
+bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
106
+ Error **errp)
107
+{
108
+ BlockDriverState *i;
109
+
110
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
111
+ if (i->backing->frozen) {
112
+ error_setg(errp, "Cannot change '%s' link from '%s' to '%s'",
113
+ i->backing->name, i->node_name,
114
+ backing_bs(i)->node_name);
115
+ return true;
116
+ }
117
+ }
118
+
119
+ return false;
120
+}
58
+}
121
+
59
+
122
+/*
60
const BdrvChildClass child_of_bds = {
123
+ * Freeze all backing links between @bs and @base.
61
.parent_is_bds = true,
124
+ * If any of the links is already frozen the operation is aborted and
62
.get_parent_desc = bdrv_child_get_parent_desc,
125
+ * none of the links are modified.
63
@@ -XXX,XX +XXX,XX @@ const BdrvChildClass child_of_bds = {
126
+ * Returns 0 on success. On failure returns < 0 and sets @errp.
64
.can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
127
+ */
65
.set_aio_ctx = bdrv_child_cb_set_aio_ctx,
128
+int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
66
.update_filename = bdrv_child_cb_update_filename,
129
+ Error **errp)
67
+ .get_parent_aio_context = bdrv_child_cb_get_parent_aio_context,
68
};
69
70
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
130
+{
71
+{
131
+ BlockDriverState *i;
72
+ return c->klass->get_parent_aio_context(c);
132
+
133
+ if (bdrv_is_backing_chain_frozen(bs, base, errp)) {
134
+ return -EPERM;
135
+ }
136
+
137
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
138
+ i->backing->frozen = true;
139
+ }
140
+
141
+ return 0;
142
+}
73
+}
143
+
74
+
144
+/*
75
static int bdrv_open_flags(BlockDriverState *bs, int flags)
145
+ * Unfreeze all backing links between @bs and @base. The caller must
76
{
146
+ * ensure that all links are frozen before using this function.
77
int open_flags = flags;
147
+ */
78
diff --git a/block/block-backend.c b/block/block-backend.c
148
+void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base)
79
index XXXXXXX..XXXXXXX 100644
80
--- a/block/block-backend.c
81
+++ b/block/block-backend.c
82
@@ -XXX,XX +XXX,XX @@ static void blk_root_detach(BdrvChild *child)
83
}
84
}
85
86
+static AioContext *blk_root_get_parent_aio_context(BdrvChild *c)
149
+{
87
+{
150
+ BlockDriverState *i;
88
+ BlockBackend *blk = c->opaque;
151
+
89
+
152
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
90
+ return blk_get_aio_context(blk);
153
+ assert(i->backing->frozen);
154
+ i->backing->frozen = false;
155
+ }
156
+}
91
+}
157
+
92
+
93
static const BdrvChildClass child_root = {
94
.inherit_options = blk_root_inherit_options,
95
96
@@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_root = {
97
98
.can_set_aio_ctx = blk_root_can_set_aio_ctx,
99
.set_aio_ctx = blk_root_set_aio_ctx,
100
+
101
+ .get_parent_aio_context = blk_root_get_parent_aio_context,
102
};
103
158
/*
104
/*
159
* Drops images above 'base' up to and including 'top', and sets the image
105
diff --git a/blockjob.c b/blockjob.c
160
* above 'top' to have base as its backing file.
106
index XXXXXXX..XXXXXXX 100644
161
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
107
--- a/blockjob.c
162
goto exit;
108
+++ b/blockjob.c
163
}
109
@@ -XXX,XX +XXX,XX @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx,
164
110
job->job.aio_context = ctx;
165
+ /* This function changes all links that point to top and makes
111
}
166
+ * them point to base. Check that none of them is frozen. */
112
167
+ QLIST_FOREACH(c, &top->parents, next_parent) {
113
+static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
168
+ if (c->frozen) {
114
+{
169
+ goto exit;
115
+ BlockJob *job = c->opaque;
170
+ }
171
+ }
172
+
116
+
173
/* If 'base' recursively inherits from 'top' then we should set
117
+ return job->job.aio_context;
174
* base->inherits_from to top->inherits_from after 'top' and all
118
+}
175
* other intermediate nodes have been dropped.
119
+
120
static const BdrvChildClass child_job = {
121
.get_parent_desc = child_job_get_parent_desc,
122
.drained_begin = child_job_drained_begin,
123
@@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_job = {
124
.can_set_aio_ctx = child_job_can_set_aio_ctx,
125
.set_aio_ctx = child_job_set_aio_ctx,
126
.stay_at_node = true,
127
+ .get_parent_aio_context = child_job_get_parent_aio_context,
128
};
129
130
void block_job_remove_all_bdrv(BlockJob *job)
176
--
131
--
177
2.20.1
132
2.30.2
178
133
179
134
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Passing parent aio context is redundant, as child_class and parent
4
opaque pointer are enough to retrieve it. Drop the argument and use new
5
bdrv_child_get_parent_aio_context() interface.
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20210428151804.439460-7-vsementsov@virtuozzo.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
include/block/block_int.h | 1 -
14
block.c | 8 +++++---
15
block/block-backend.c | 4 ++--
16
blockjob.c | 3 +--
17
4 files changed, 8 insertions(+), 8 deletions(-)
18
19
diff --git a/include/block/block_int.h b/include/block/block_int.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/block_int.h
22
+++ b/include/block/block_int.h
23
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
24
const char *child_name,
25
const BdrvChildClass *child_class,
26
BdrvChildRole child_role,
27
- AioContext *ctx,
28
uint64_t perm, uint64_t shared_perm,
29
void *opaque, Error **errp);
30
void bdrv_root_unref_child(BdrvChild *child);
31
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
33
--- a/block.c
34
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
36
const char *child_name,
37
const BdrvChildClass *child_class,
38
BdrvChildRole child_role,
39
- AioContext *ctx,
40
uint64_t perm, uint64_t shared_perm,
41
void *opaque, Error **errp)
42
{
43
BdrvChild *child;
44
Error *local_err = NULL;
45
int ret;
46
+ AioContext *ctx;
47
48
ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
49
if (ret < 0) {
50
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
51
.opaque = opaque,
52
};
53
54
+ ctx = bdrv_child_get_parent_aio_context(child);
55
+
56
/* If the AioContexts don't match, first try to move the subtree of
57
* child_bs into the AioContext of the new parent. If this doesn't work,
58
* try moving the parent into the AioContext of child_bs instead. */
59
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
60
perm, shared_perm, &perm, &shared_perm);
61
62
child = bdrv_root_attach_child(child_bs, child_name, child_class,
63
- child_role, bdrv_get_aio_context(parent_bs),
64
- perm, shared_perm, parent_bs, errp);
65
+ child_role, perm, shared_perm, parent_bs,
66
+ errp);
67
if (child == NULL) {
68
return NULL;
69
}
70
diff --git a/block/block-backend.c b/block/block-backend.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/block/block-backend.c
73
+++ b/block/block-backend.c
74
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
75
76
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
77
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
78
- blk->ctx, perm, BLK_PERM_ALL, blk, errp);
79
+ perm, BLK_PERM_ALL, blk, errp);
80
if (!blk->root) {
81
blk_unref(blk);
82
return NULL;
83
@@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
84
bdrv_ref(bs);
85
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
86
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
87
- blk->ctx, blk->perm, blk->shared_perm,
88
+ blk->perm, blk->shared_perm,
89
blk, errp);
90
if (blk->root == NULL) {
91
return -EPERM;
92
diff --git a/blockjob.c b/blockjob.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/blockjob.c
95
+++ b/blockjob.c
96
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
97
if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
98
aio_context_release(job->job.aio_context);
99
}
100
- c = bdrv_root_attach_child(bs, name, &child_job, 0,
101
- job->job.aio_context, perm, shared_perm, job,
102
+ c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job,
103
errp);
104
if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
105
aio_context_acquire(job->job.aio_context);
106
--
107
2.30.2
108
109
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This parameter has been unused since 1a63a907507fbbcfaee3f622907ec244b
3
These functions are called only from bdrv_reopen_multiple() in block.c.
4
No reason to publish them.
4
5
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20210428151804.439460-8-vsementsov@virtuozzo.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
11
---
8
include/block/block.h | 2 +-
12
include/block/block.h | 4 ----
9
block.c | 4 ++--
13
block.c | 13 +++++++++----
10
block/replication.c | 3 +--
14
2 files changed, 9 insertions(+), 8 deletions(-)
11
qemu-io-cmds.c | 2 +-
12
4 files changed, 5 insertions(+), 6 deletions(-)
13
15
14
diff --git a/include/block/block.h b/include/block/block.h
16
diff --git a/include/block/block.h b/include/block/block.h
15
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/block.h
18
--- a/include/block/block.h
17
+++ b/include/block/block.h
19
+++ b/include/block/block.h
18
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
20
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
19
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
21
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
20
BlockDriverState *bs, QDict *options,
21
bool keep_old_opts);
22
-int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp);
23
+int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
24
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
22
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
25
Error **errp);
23
Error **errp);
26
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
24
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
25
- BlockReopenQueue *queue, Error **errp);
26
-void bdrv_reopen_commit(BDRVReopenState *reopen_state);
27
-void bdrv_reopen_abort(BDRVReopenState *reopen_state);
28
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
29
int64_t bytes, BdrvRequestFlags flags);
30
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);
27
diff --git a/block.c b/block.c
31
diff --git a/block.c b/block.c
28
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
29
--- a/block.c
33
--- a/block.c
30
+++ b/block.c
34
+++ b/block.c
31
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
35
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
32
* All affected nodes must be drained between bdrv_reopen_queue() and
36
BdrvChildRole child_role,
33
* bdrv_reopen_multiple().
37
Error **errp);
38
39
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
40
+ *queue, Error **errp);
41
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
42
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
43
+
44
/* If non-zero, use only whitelisted block drivers */
45
static int use_bdrv_whitelist;
46
47
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
48
* commit() for any other BDS that have been left in a prepare() state
49
*
34
*/
50
*/
35
-int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
51
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
36
+int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
52
- Error **errp)
53
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
54
+ BlockReopenQueue *queue, Error **errp)
37
{
55
{
38
int ret = -1;
56
int ret = -1;
39
BlockReopenQueueEntry *bs_entry, *next;
57
int old_flags;
40
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
58
@@ -XXX,XX +XXX,XX @@ error:
41
59
* makes them final by swapping the staging BlockDriverState contents into
42
bdrv_subtree_drained_begin(bs);
60
* the active BlockDriverState contents.
43
queue = bdrv_reopen_queue(NULL, bs, opts, true);
61
*/
44
- ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp);
62
-void bdrv_reopen_commit(BDRVReopenState *reopen_state)
45
+ ret = bdrv_reopen_multiple(queue, errp);
63
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
46
bdrv_subtree_drained_end(bs);
64
{
47
65
BlockDriver *drv;
48
return ret;
66
BlockDriverState *bs;
49
diff --git a/block/replication.c b/block/replication.c
67
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
50
index XXXXXXX..XXXXXXX 100644
68
* Abort the reopen, and delete and free the staged changes in
51
--- a/block/replication.c
69
* reopen_state
52
+++ b/block/replication.c
70
*/
53
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
71
-void bdrv_reopen_abort(BDRVReopenState *reopen_state)
54
}
72
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state)
55
73
{
56
if (reopen_queue) {
74
BlockDriver *drv;
57
- bdrv_reopen_multiple(bdrv_get_aio_context(bs),
75
58
- reopen_queue, &local_err);
59
+ bdrv_reopen_multiple(reopen_queue, &local_err);
60
error_propagate(errp, local_err);
61
}
62
63
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
64
index XXXXXXX..XXXXXXX 100644
65
--- a/qemu-io-cmds.c
66
+++ b/qemu-io-cmds.c
67
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
68
69
bdrv_subtree_drained_begin(bs);
70
brq = bdrv_reopen_queue(NULL, bs, opts, true);
71
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
72
+ bdrv_reopen_multiple(brq, &local_err);
73
bdrv_subtree_drained_end(bs);
74
75
if (local_err) {
76
--
76
--
77
2.20.1
77
2.30.2
78
78
79
79
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This patch adds several tests for the x-blockdev-reopen QMP command.
3
Add simple transaction API to use in further update of block graph
4
4
operations.
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
5
6
Supposed usage is:
7
8
- "prepare" is main function of the action and it should make the main
9
effect of the action to be visible for the following actions, keeping
10
possibility of roll-back, saving necessary things in action state,
11
which is prepended to the action list (to do that, prepare func
12
should call tran_add()). So, driver struct doesn't include "prepare"
13
field, as it is supposed to be called directly.
14
15
- commit/rollback is supposed to be called for the list of action
16
states, to commit/rollback all the actions in reverse order
17
18
- When possible "commit" should not make visible effect for other
19
actions, which make possible transparent logical interaction between
20
actions.
21
22
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
23
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
24
Message-Id: <20210428151804.439460-9-vsementsov@virtuozzo.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
26
---
8
tests/qemu-iotests/245 | 991 +++++++++++++++++++++++++++++++++++++
27
include/qemu/transactions.h | 63 ++++++++++++++++++++++++
9
tests/qemu-iotests/245.out | 5 +
28
util/transactions.c | 96 +++++++++++++++++++++++++++++++++++++
10
tests/qemu-iotests/group | 1 +
29
MAINTAINERS | 6 +++
11
3 files changed, 997 insertions(+)
30
util/meson.build | 1 +
12
create mode 100644 tests/qemu-iotests/245
31
4 files changed, 166 insertions(+)
13
create mode 100644 tests/qemu-iotests/245.out
32
create mode 100644 include/qemu/transactions.h
14
33
create mode 100644 util/transactions.c
15
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
34
35
diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h
16
new file mode 100644
36
new file mode 100644
17
index XXXXXXX..XXXXXXX
37
index XXXXXXX..XXXXXXX
18
--- /dev/null
38
--- /dev/null
19
+++ b/tests/qemu-iotests/245
39
+++ b/include/qemu/transactions.h
20
@@ -XXX,XX +XXX,XX @@
40
@@ -XXX,XX +XXX,XX @@
21
+#!/usr/bin/env python
41
+/*
22
+#
42
+ * Simple transactions API
23
+# Test cases for the QMP 'x-blockdev-reopen' command
43
+ *
24
+#
44
+ * Copyright (c) 2021 Virtuozzo International GmbH.
25
+# Copyright (C) 2018-2019 Igalia, S.L.
45
+ *
26
+# Author: Alberto Garcia <berto@igalia.com>
46
+ * Author:
27
+#
47
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
28
+# This program is free software; you can redistribute it and/or modify
48
+ *
29
+# it under the terms of the GNU General Public License as published by
49
+ * This program is free software; you can redistribute it and/or modify
30
+# the Free Software Foundation; either version 2 of the License, or
50
+ * it under the terms of the GNU General Public License as published by
31
+# (at your option) any later version.
51
+ * the Free Software Foundation; either version 2 of the License, or
32
+#
52
+ * (at your option) any later version.
33
+# This program is distributed in the hope that it will be useful,
53
+ *
34
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
54
+ * This program is distributed in the hope that it will be useful,
35
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
55
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
36
+# GNU General Public License for more details.
56
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37
+#
57
+ * GNU General Public License for more details.
38
+# You should have received a copy of the GNU General Public License
58
+ *
39
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
59
+ * You should have received a copy of the GNU General Public License
40
+#
60
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
41
+
61
+ *
42
+import os
62
+ *
43
+import re
63
+ * = Generic transaction API =
44
+import iotests
64
+ *
45
+import copy
65
+ * The intended usage is the following: you create "prepare" functions, which
46
+import json
66
+ * represents the actions. They will usually have Transaction* argument, and
47
+from iotests import qemu_img, qemu_io
67
+ * call tran_add() to register finalization callbacks. For finalization
48
+
68
+ * callbacks, prepare corresponding TransactionActionDrv structures.
49
+hd_path = [
69
+ *
50
+ os.path.join(iotests.test_dir, 'hd0.img'),
70
+ * Then, when you need to make a transaction, create an empty Transaction by
51
+ os.path.join(iotests.test_dir, 'hd1.img'),
71
+ * tran_create(), call your "prepare" functions on it, and finally call
52
+ os.path.join(iotests.test_dir, 'hd2.img')
72
+ * tran_abort() or tran_commit() to finalize the transaction by corresponding
53
+]
73
+ * finalization actions in reverse order.
54
+
74
+ */
55
+def hd_opts(idx):
75
+
56
+ return {'driver': iotests.imgfmt,
76
+#ifndef QEMU_TRANSACTIONS_H
57
+ 'node-name': 'hd%d' % idx,
77
+#define QEMU_TRANSACTIONS_H
58
+ 'file': {'driver': 'file',
78
+
59
+ 'node-name': 'hd%d-file' % idx,
79
+#include <gmodule.h>
60
+ 'filename': hd_path[idx] } }
80
+
61
+
81
+typedef struct TransactionActionDrv {
62
+class TestBlockdevReopen(iotests.QMPTestCase):
82
+ void (*abort)(void *opaque);
63
+ total_io_cmds = 0
83
+ void (*commit)(void *opaque);
64
+
84
+ void (*clean)(void *opaque);
65
+ def setUp(self):
85
+} TransactionActionDrv;
66
+ qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M')
86
+
67
+ qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1])
87
+typedef struct Transaction Transaction;
68
+ qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M')
88
+
69
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0])
89
+Transaction *tran_new(void);
70
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1])
90
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque);
71
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa2 2M 1M', hd_path[2])
91
+void tran_abort(Transaction *tran);
72
+ self.vm = iotests.VM()
92
+void tran_commit(Transaction *tran);
73
+ self.vm.launch()
93
+
74
+
94
+static inline void tran_finalize(Transaction *tran, int ret)
75
+ def tearDown(self):
95
+{
76
+ self.vm.shutdown()
96
+ if (ret < 0) {
77
+ self.check_qemu_io_errors()
97
+ tran_abort(tran);
78
+ os.remove(hd_path[0])
98
+ } else {
79
+ os.remove(hd_path[1])
99
+ tran_commit(tran);
80
+ os.remove(hd_path[2])
100
+ }
81
+
101
+}
82
+ # The output of qemu-io is not returned by vm.hmp_qemu_io() but
102
+
83
+ # it's stored in the log and can only be read when the VM has been
103
+#endif /* QEMU_TRANSACTIONS_H */
84
+ # shut down. This function runs qemu-io and keeps track of the
104
diff --git a/util/transactions.c b/util/transactions.c
85
+ # number of times it's been called.
86
+ def run_qemu_io(self, img, cmd):
87
+ result = self.vm.hmp_qemu_io(img, cmd)
88
+ self.assert_qmp(result, 'return', '')
89
+ self.total_io_cmds += 1
90
+
91
+ # Once the VM is shut down we can parse the log and see if qemu-io
92
+ # ran without errors.
93
+ def check_qemu_io_errors(self):
94
+ self.assertFalse(self.vm.is_running())
95
+ found = 0
96
+ log = self.vm.get_log()
97
+ for line in log.split("\n"):
98
+ if line.startswith("Pattern verification failed"):
99
+ raise Exception("%s (command #%d)" % (line, found))
100
+ if re.match("read .*/.* bytes at offset", line):
101
+ found += 1
102
+ self.assertEqual(found, self.total_io_cmds,
103
+ "Expected output of %d qemu-io commands, found %d" %
104
+ (found, self.total_io_cmds))
105
+
106
+ # Run x-blockdev-reopen with 'opts' but applying 'newopts'
107
+ # on top of it. The original 'opts' dict is unmodified
108
+ def reopen(self, opts, newopts = {}, errmsg = None):
109
+ opts = copy.deepcopy(opts)
110
+
111
+ # Apply the changes from 'newopts' on top of 'opts'
112
+ for key in newopts:
113
+ value = newopts[key]
114
+ # If key has the form "foo.bar" then we need to do
115
+ # opts["foo"]["bar"] = value, not opts["foo.bar"] = value
116
+ subdict = opts
117
+ while key.find('.') != -1:
118
+ [prefix, key] = key.split('.', 1)
119
+ subdict = opts[prefix]
120
+ subdict[key] = value
121
+
122
+ result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts)
123
+ if errmsg:
124
+ self.assert_qmp(result, 'error/class', 'GenericError')
125
+ self.assert_qmp(result, 'error/desc', errmsg)
126
+ else:
127
+ self.assert_qmp(result, 'return', {})
128
+
129
+
130
+ # Run query-named-block-nodes and return the specified entry
131
+ def get_node(self, node_name):
132
+ result = self.vm.qmp('query-named-block-nodes')
133
+ for node in result['return']:
134
+ if node['node-name'] == node_name:
135
+ return node
136
+ return None
137
+
138
+ # Run 'query-named-block-nodes' and compare its output with the
139
+ # value passed by the user in 'graph'
140
+ def check_node_graph(self, graph):
141
+ result = self.vm.qmp('query-named-block-nodes')
142
+ self.assertEqual(json.dumps(graph, sort_keys=True),
143
+ json.dumps(result, sort_keys=True))
144
+
145
+ # This test opens one single disk image (without backing files)
146
+ # and tries to reopen it with illegal / incorrect parameters.
147
+ def test_incorrect_parameters_single_file(self):
148
+ # Open 'hd0' only (no backing files)
149
+ opts = hd_opts(0)
150
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
151
+ self.assert_qmp(result, 'return', {})
152
+ original_graph = self.vm.qmp('query-named-block-nodes')
153
+
154
+ # We can reopen the image passing the same options
155
+ self.reopen(opts)
156
+
157
+ # We can also reopen passing a child reference in 'file'
158
+ self.reopen(opts, {'file': 'hd0-file'})
159
+
160
+ # We cannot change any of these
161
+ self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'")
162
+ self.reopen(opts, {'node-name': ''}, "Cannot find node named ''")
163
+ self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string")
164
+ self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
165
+ self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
166
+ self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string")
167
+ self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'")
168
+ self.reopen(opts, {'file': ''}, "Cannot change the option 'file'")
169
+ self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef")
170
+ self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'")
171
+ self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'")
172
+ self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'")
173
+ self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'")
174
+ self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
175
+ self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string")
176
+
177
+ # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it
178
+ del opts['node-name']
179
+ self.reopen(opts, {}, "Node name not specified")
180
+
181
+ # Check that nothing has changed
182
+ self.check_node_graph(original_graph)
183
+
184
+ # Remove the node
185
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
186
+ self.assert_qmp(result, 'return', {})
187
+
188
+ # This test opens an image with a backing file and tries to reopen
189
+ # it with illegal / incorrect parameters.
190
+ def test_incorrect_parameters_backing_file(self):
191
+ # Open hd1 omitting the backing options (hd0 will be opened
192
+ # with the default options)
193
+ opts = hd_opts(1)
194
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
195
+ self.assert_qmp(result, 'return', {})
196
+ original_graph = self.vm.qmp('query-named-block-nodes')
197
+
198
+ # We can't reopen the image passing the same options, 'backing' is mandatory
199
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
200
+
201
+ # Everything works if we pass 'backing' using the existing node name
202
+ for node in original_graph['return']:
203
+ if node['drv'] == iotests.imgfmt and node['file'] == hd_path[0]:
204
+ backing_node_name = node['node-name']
205
+ self.reopen(opts, {'backing': backing_node_name})
206
+
207
+ # We can't use a non-existing or empty (non-NULL) node as the backing image
208
+ self.reopen(opts, {'backing': 'not-found'}, "Cannot find device= nor node_name=not-found")
209
+ self.reopen(opts, {'backing': ''}, "Cannot find device= nor node_name=")
210
+
211
+ # We can reopen the image just fine if we specify the backing options
212
+ opts['backing'] = {'driver': iotests.imgfmt,
213
+ 'file': {'driver': 'file',
214
+ 'filename': hd_path[0]}}
215
+ self.reopen(opts)
216
+
217
+ # We cannot change any of these options
218
+ self.reopen(opts, {'backing.node-name': 'newname'}, "Cannot change the option 'node-name'")
219
+ self.reopen(opts, {'backing.driver': 'raw'}, "Cannot change the option 'driver'")
220
+ self.reopen(opts, {'backing.file.node-name': 'newname'}, "Cannot change the option 'node-name'")
221
+ self.reopen(opts, {'backing.file.driver': 'host_device'}, "Cannot change the option 'driver'")
222
+
223
+ # Check that nothing has changed since the beginning
224
+ self.check_node_graph(original_graph)
225
+
226
+ # Remove the node
227
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
228
+ self.assert_qmp(result, 'return', {})
229
+
230
+ # Reopen an image several times changing some of its options
231
+ def test_reopen(self):
232
+ # Open the hd1 image passing all backing options
233
+ opts = hd_opts(1)
234
+ opts['backing'] = hd_opts(0)
235
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
236
+ self.assert_qmp(result, 'return', {})
237
+ original_graph = self.vm.qmp('query-named-block-nodes')
238
+
239
+ # We can reopen the image passing the same options
240
+ self.reopen(opts)
241
+
242
+ # Reopen in read-only mode
243
+ self.assert_qmp(self.get_node('hd1'), 'ro', False)
244
+
245
+ self.reopen(opts, {'read-only': True})
246
+ self.assert_qmp(self.get_node('hd1'), 'ro', True)
247
+ self.reopen(opts)
248
+ self.assert_qmp(self.get_node('hd1'), 'ro', False)
249
+
250
+ # Change the cache options
251
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
252
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', False)
253
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False)
254
+ self.reopen(opts, {'cache': { 'direct': True, 'no-flush': True }})
255
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
256
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', True)
257
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', True)
258
+
259
+ # Reopen again with the original options
260
+ self.reopen(opts)
261
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
262
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', False)
263
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False)
264
+
265
+ # Change 'detect-zeroes' and 'discard'
266
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off')
267
+ self.reopen(opts, {'detect-zeroes': 'on'})
268
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
269
+ self.reopen(opts, {'detect-zeroes': 'unmap'},
270
+ "setting detect-zeroes to unmap is not allowed " +
271
+ "without setting discard operation to unmap")
272
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
273
+ self.reopen(opts, {'detect-zeroes': 'unmap', 'discard': 'unmap'})
274
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'unmap')
275
+ self.reopen(opts)
276
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off')
277
+
278
+ # Changing 'force-share' is currently not supported
279
+ self.reopen(opts, {'force-share': True}, "Cannot change the option 'force-share'")
280
+
281
+ # Change some qcow2-specific options
282
+ # No way to test for success other than checking the return message
283
+ if iotests.imgfmt == 'qcow2':
284
+ self.reopen(opts, {'l2-cache-entry-size': 128 * 1024},
285
+ "L2 cache entry size must be a power of two "+
286
+ "between 512 and the cluster size (65536)")
287
+ self.reopen(opts, {'l2-cache-size': 1024 * 1024,
288
+ 'cache-size': 512 * 1024},
289
+ "l2-cache-size may not exceed cache-size")
290
+ self.reopen(opts, {'l2-cache-size': 4 * 1024 * 1024,
291
+ 'refcount-cache-size': 4 * 1024 * 1024,
292
+ 'l2-cache-entry-size': 32 * 1024})
293
+ self.reopen(opts, {'pass-discard-request': True})
294
+
295
+ # Check that nothing has changed since the beginning
296
+ # (from the parameters that we can check)
297
+ self.check_node_graph(original_graph)
298
+
299
+ # Check that the node names (other than the top-level one) are optional
300
+ del opts['file']['node-name']
301
+ del opts['backing']['node-name']
302
+ del opts['backing']['file']['node-name']
303
+ self.reopen(opts)
304
+ self.check_node_graph(original_graph)
305
+
306
+ # Reopen setting backing = null, this removes the backing image from the chain
307
+ self.reopen(opts, {'backing': None})
308
+ self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image')
309
+
310
+ # Open the 'hd0' image
311
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0))
312
+ self.assert_qmp(result, 'return', {})
313
+
314
+ # Reopen the hd1 image setting 'hd0' as its backing image
315
+ self.reopen(opts, {'backing': 'hd0'})
316
+ self.assert_qmp(self.get_node('hd1'), 'image/backing-image/filename', hd_path[0])
317
+
318
+ # Check that nothing has changed since the beginning
319
+ self.reopen(hd_opts(0), {'read-only': True})
320
+ self.check_node_graph(original_graph)
321
+
322
+ # The backing file (hd0) is now a reference, we cannot change backing.* anymore
323
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
324
+
325
+ # We can't remove 'hd0' while it's a backing image of 'hd1'
326
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
327
+ self.assert_qmp(result, 'error/class', 'GenericError')
328
+ self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'")
329
+
330
+ # But we can remove both nodes if done in the proper order
331
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
332
+ self.assert_qmp(result, 'return', {})
333
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
334
+ self.assert_qmp(result, 'return', {})
335
+
336
+ # Reopen a raw image and see the effect of changing the 'offset' option
337
+ def test_reopen_raw(self):
338
+ opts = {'driver': 'raw', 'node-name': 'hd0',
339
+ 'file': { 'driver': 'file',
340
+ 'filename': hd_path[0],
341
+ 'node-name': 'hd0-file' } }
342
+
343
+ # First we create a 2MB raw file, and fill each half with a
344
+ # different value
345
+ qemu_img('create', '-f', 'raw', hd_path[0], '2M')
346
+ qemu_io('-f', 'raw', '-c', 'write -P 0xa0 0 1M', hd_path[0])
347
+ qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0])
348
+
349
+ # Open the raw file with QEMU
350
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
351
+ self.assert_qmp(result, 'return', {})
352
+
353
+ # Read 1MB from offset 0
354
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
355
+
356
+ # Reopen the image with a 1MB offset.
357
+ # Now the results are different
358
+ self.reopen(opts, {'offset': 1024*1024})
359
+ self.run_qemu_io("hd0", "read -P 0xa1 0 1M")
360
+
361
+ # Reopen again with the original options.
362
+ # We get the original results again
363
+ self.reopen(opts)
364
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
365
+
366
+ # Remove the block device
367
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
368
+ self.assert_qmp(result, 'return', {})
369
+
370
+ # Omitting an option should reset it to the default value, but if
371
+ # an option cannot be changed it shouldn't be possible to reset it
372
+ # to its default value either
373
+ def test_reset_default_values(self):
374
+ opts = {'driver': 'qcow2', 'node-name': 'hd0',
375
+ 'file': { 'driver': 'file',
376
+ 'filename': hd_path[0],
377
+ 'x-check-cache-dropped': True, # This one can be changed
378
+ 'locking': 'off', # This one can NOT be changed
379
+ 'node-name': 'hd0-file' } }
380
+
381
+ # Open the file with QEMU
382
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
383
+ self.assert_qmp(result, 'return', {})
384
+
385
+ # file.x-check-cache-dropped can be changed...
386
+ self.reopen(opts, { 'file.x-check-cache-dropped': False })
387
+ # ...and dropped completely (resetting to the default value)
388
+ del opts['file']['x-check-cache-dropped']
389
+ self.reopen(opts)
390
+
391
+ # file.locking cannot be changed nor reset to the default value
392
+ self.reopen(opts, { 'file.locking': 'on' }, "Cannot change the option 'locking'")
393
+ del opts['file']['locking']
394
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
395
+ # But we can reopen it if we maintain its previous value
396
+ self.reopen(opts, { 'file.locking': 'off' })
397
+
398
+ # Remove the block device
399
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
400
+ self.assert_qmp(result, 'return', {})
401
+
402
+ # This test modifies the node graph a few times by changing the
403
+ # 'backing' option on reopen and verifies that the guest data that
404
+ # is read afterwards is consistent with the graph changes.
405
+ def test_io_with_graph_changes(self):
406
+ opts = []
407
+
408
+ # Open hd0, hd1 and hd2 without any backing image
409
+ for i in range(3):
410
+ opts.append(hd_opts(i))
411
+ opts[i]['backing'] = None
412
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i])
413
+ self.assert_qmp(result, 'return', {})
414
+
415
+ # hd0
416
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
417
+ self.run_qemu_io("hd0", "read -P 0 1M 1M")
418
+ self.run_qemu_io("hd0", "read -P 0 2M 1M")
419
+
420
+ # hd1 <- hd0
421
+ self.reopen(opts[0], {'backing': 'hd1'})
422
+
423
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
424
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
425
+ self.run_qemu_io("hd0", "read -P 0 2M 1M")
426
+
427
+ # hd1 <- hd0 , hd1 <- hd2
428
+ self.reopen(opts[2], {'backing': 'hd1'})
429
+
430
+ self.run_qemu_io("hd2", "read -P 0 0 1M")
431
+ self.run_qemu_io("hd2", "read -P 0xa1 1M 1M")
432
+ self.run_qemu_io("hd2", "read -P 0xa2 2M 1M")
433
+
434
+ # hd1 <- hd2 <- hd0
435
+ self.reopen(opts[0], {'backing': 'hd2'})
436
+
437
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
438
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
439
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
440
+
441
+ # hd2 <- hd0
442
+ self.reopen(opts[2], {'backing': None})
443
+
444
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
445
+ self.run_qemu_io("hd0", "read -P 0 1M 1M")
446
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
447
+
448
+ # hd2 <- hd1 <- hd0
449
+ self.reopen(opts[1], {'backing': 'hd2'})
450
+ self.reopen(opts[0], {'backing': 'hd1'})
451
+
452
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
453
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
454
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
455
+
456
+ # Illegal operation: hd2 is a child of hd1
457
+ self.reopen(opts[2], {'backing': 'hd1'},
458
+ "Making 'hd1' a backing file of 'hd2' would create a cycle")
459
+
460
+ # hd2 <- hd0, hd2 <- hd1
461
+ self.reopen(opts[0], {'backing': 'hd2'})
462
+
463
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
464
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
465
+ self.run_qemu_io("hd1", "read -P 0xa2 2M 1M")
466
+
467
+ # More illegal operations
468
+ self.reopen(opts[2], {'backing': 'hd1'},
469
+ "Making 'hd1' a backing file of 'hd2' would create a cycle")
470
+ self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'")
471
+
472
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
473
+ self.assert_qmp(result, 'error/class', 'GenericError')
474
+ self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'")
475
+
476
+ # hd1 doesn't have a backing file now
477
+ self.reopen(opts[1], {'backing': None})
478
+
479
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
480
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
481
+ self.run_qemu_io("hd1", "read -P 0 2M 1M")
482
+
483
+ # We can't remove the 'backing' option if the image has a
484
+ # default backing file
485
+ del opts[1]['backing']
486
+ self.reopen(opts[1], {}, "backing is missing for 'hd1'")
487
+
488
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
489
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
490
+ self.run_qemu_io("hd1", "read -P 0 2M 1M")
491
+
492
+ # This test verifies that we can't change the children of a block
493
+ # device during a reopen operation in a way that would create
494
+ # cycles in the node graph
495
+ def test_graph_cycles(self):
496
+ opts = []
497
+
498
+ # Open all three images without backing file
499
+ for i in range(3):
500
+ opts.append(hd_opts(i))
501
+ opts[i]['backing'] = None
502
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i])
503
+ self.assert_qmp(result, 'return', {})
504
+
505
+ # hd1 <- hd0, hd1 <- hd2
506
+ self.reopen(opts[0], {'backing': 'hd1'})
507
+ self.reopen(opts[2], {'backing': 'hd1'})
508
+
509
+ # Illegal: hd2 is backed by hd1
510
+ self.reopen(opts[1], {'backing': 'hd2'},
511
+ "Making 'hd2' a backing file of 'hd1' would create a cycle")
512
+
513
+ # hd1 <- hd0 <- hd2
514
+ self.reopen(opts[2], {'backing': 'hd0'})
515
+
516
+ # Illegal: hd2 is backed by hd0, which is backed by hd1
517
+ self.reopen(opts[1], {'backing': 'hd2'},
518
+ "Making 'hd2' a backing file of 'hd1' would create a cycle")
519
+
520
+ # Illegal: hd1 cannot point to itself
521
+ self.reopen(opts[1], {'backing': 'hd1'},
522
+ "Making 'hd1' a backing file of 'hd1' would create a cycle")
523
+
524
+ # Remove all backing files
525
+ self.reopen(opts[0])
526
+ self.reopen(opts[2])
527
+
528
+ ##########################################
529
+ # Add a blkverify node using hd0 and hd1 #
530
+ ##########################################
531
+ bvopts = {'driver': 'blkverify',
532
+ 'node-name': 'bv',
533
+ 'test': 'hd0',
534
+ 'raw': 'hd1'}
535
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts)
536
+ self.assert_qmp(result, 'return', {})
537
+
538
+ # blkverify doesn't currently allow reopening. TODO: implement this
539
+ self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" +
540
+ " does not support reopening files")
541
+
542
+ # Illegal: hd0 is a child of the blkverify node
543
+ self.reopen(opts[0], {'backing': 'bv'},
544
+ "Making 'bv' a backing file of 'hd0' would create a cycle")
545
+
546
+ # Delete the blkverify node
547
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv')
548
+ self.assert_qmp(result, 'return', {})
549
+
550
+ # Misc reopen tests with different block drivers
551
+ def test_misc_drivers(self):
552
+ ####################
553
+ ###### quorum ######
554
+ ####################
555
+ for i in range(3):
556
+ opts = hd_opts(i)
557
+ # Open all three images without backing file
558
+ opts['backing'] = None
559
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
560
+ self.assert_qmp(result, 'return', {})
561
+
562
+ opts = {'driver': 'quorum',
563
+ 'node-name': 'quorum0',
564
+ 'children': ['hd0', 'hd1', 'hd2'],
565
+ 'vote-threshold': 2}
566
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
567
+ self.assert_qmp(result, 'return', {})
568
+
569
+ # Quorum doesn't currently allow reopening. TODO: implement this
570
+ self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" +
571
+ " does not support reopening files")
572
+
573
+ # You can't make quorum0 a backing file of hd0:
574
+ # hd0 is already a child of quorum0.
575
+ self.reopen(hd_opts(0), {'backing': 'quorum0'},
576
+ "Making 'quorum0' a backing file of 'hd0' would create a cycle")
577
+
578
+ # Delete quorum0
579
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0')
580
+ self.assert_qmp(result, 'return', {})
581
+
582
+ # Delete hd0, hd1 and hd2
583
+ for i in range(3):
584
+ result = self.vm.qmp('blockdev-del', conv_keys = True,
585
+ node_name = 'hd%d' % i)
586
+ self.assert_qmp(result, 'return', {})
587
+
588
+ ######################
589
+ ###### blkdebug ######
590
+ ######################
591
+ opts = {'driver': 'blkdebug',
592
+ 'node-name': 'bd',
593
+ 'config': '/dev/null',
594
+ 'image': hd_opts(0)}
595
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
596
+ self.assert_qmp(result, 'return', {})
597
+
598
+ # blkdebug allows reopening if we keep the same options
599
+ self.reopen(opts)
600
+
601
+ # but it currently does not allow changes
602
+ self.reopen(opts, {'image': 'hd1'}, "Cannot change the option 'image'")
603
+ self.reopen(opts, {'align': 33554432}, "Cannot change the option 'align'")
604
+ self.reopen(opts, {'config': '/non/existent'}, "Cannot change the option 'config'")
605
+ del opts['config']
606
+ self.reopen(opts, {}, "Option 'config' cannot be reset to its default value")
607
+
608
+ # Delete the blkdebug node
609
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd')
610
+ self.assert_qmp(result, 'return', {})
611
+
612
+ ##################
613
+ ###### null ######
614
+ ##################
615
+ opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024}
616
+
617
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
618
+ self.assert_qmp(result, 'return', {})
619
+
620
+ # 1 << 30 is the default value, but we cannot change it explicitly
621
+ self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'")
622
+
623
+ # We cannot change 'size' back to its default value either
624
+ del opts['size']
625
+ self.reopen(opts, {}, "Option 'size' cannot be reset to its default value")
626
+
627
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root')
628
+ self.assert_qmp(result, 'return', {})
629
+
630
+ ##################
631
+ ###### file ######
632
+ ##################
633
+ opts = hd_opts(0)
634
+ opts['file']['locking'] = 'on'
635
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
636
+ self.assert_qmp(result, 'return', {})
637
+
638
+ # 'locking' cannot be changed
639
+ del opts['file']['locking']
640
+ self.reopen(opts, {'file.locking': 'on'})
641
+ self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
642
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
643
+
644
+ # Trying to reopen the 'file' node directly does not make a difference
645
+ opts = opts['file']
646
+ self.reopen(opts, {'locking': 'on'})
647
+ self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'")
648
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
649
+
650
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
651
+ self.assert_qmp(result, 'return', {})
652
+
653
+ ######################
654
+ ###### throttle ######
655
+ ######################
656
+ opts = { 'qom-type': 'throttle-group', 'id': 'group0',
657
+ 'props': { 'limits': { 'iops-total': 1000 } } }
658
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
659
+ self.assert_qmp(result, 'return', {})
660
+
661
+ opts = { 'qom-type': 'throttle-group', 'id': 'group1',
662
+ 'props': { 'limits': { 'iops-total': 2000 } } }
663
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
664
+ self.assert_qmp(result, 'return', {})
665
+
666
+ # Add a throttle filter with group = group0
667
+ opts = { 'driver': 'throttle', 'node-name': 'throttle0',
668
+ 'throttle-group': 'group0', 'file': hd_opts(0) }
669
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
670
+ self.assert_qmp(result, 'return', {})
671
+
672
+ # We can reopen it if we keep the same options
673
+ self.reopen(opts)
674
+
675
+ # We can also reopen if 'file' is a reference to the child
676
+ self.reopen(opts, {'file': 'hd0'})
677
+
678
+ # This is illegal
679
+ self.reopen(opts, {'throttle-group': 'notfound'}, "Throttle group 'notfound' does not exist")
680
+
681
+ # But it's possible to change the group to group1
682
+ self.reopen(opts, {'throttle-group': 'group1'})
683
+
684
+ # Now group1 is in use, it cannot be deleted
685
+ result = self.vm.qmp('object-del', id = 'group1')
686
+ self.assert_qmp(result, 'error/class', 'GenericError')
687
+ self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted")
688
+
689
+ # Default options, this switches the group back to group0
690
+ self.reopen(opts)
691
+
692
+ # So now we cannot delete group0
693
+ result = self.vm.qmp('object-del', id = 'group0')
694
+ self.assert_qmp(result, 'error/class', 'GenericError')
695
+ self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted")
696
+
697
+ # But group1 is free this time, and it can be deleted
698
+ result = self.vm.qmp('object-del', id = 'group1')
699
+ self.assert_qmp(result, 'return', {})
700
+
701
+ # Let's delete the filter node
702
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0')
703
+ self.assert_qmp(result, 'return', {})
704
+
705
+ # And we can finally get rid of group0
706
+ result = self.vm.qmp('object-del', id = 'group0')
707
+ self.assert_qmp(result, 'return', {})
708
+
709
+ # If an image has a backing file then the 'backing' option must be
710
+ # passed on reopen. We don't allow leaving the option out in this
711
+ # case because it's unclear what the correct semantics would be.
712
+ def test_missing_backing_options_1(self):
713
+ # hd2
714
+ opts = hd_opts(2)
715
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
716
+ self.assert_qmp(result, 'return', {})
717
+
718
+ # hd0
719
+ opts = hd_opts(0)
720
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
721
+ self.assert_qmp(result, 'return', {})
722
+
723
+ # hd0 has no backing file: we can omit the 'backing' option
724
+ self.reopen(opts)
725
+
726
+ # hd2 <- hd0
727
+ self.reopen(opts, {'backing': 'hd2'})
728
+
729
+ # hd0 has a backing file: we must set the 'backing' option
730
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
731
+
732
+ # hd2 can't be removed because it's the backing file of hd0
733
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
734
+ self.assert_qmp(result, 'error/class', 'GenericError')
735
+ self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'")
736
+
737
+ # Detach hd2 from hd0.
738
+ self.reopen(opts, {'backing': None})
739
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
740
+
741
+ # Remove both hd0 and hd2
742
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
743
+ self.assert_qmp(result, 'return', {})
744
+
745
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
746
+ self.assert_qmp(result, 'return', {})
747
+
748
+ # If an image has default backing file (as part of its metadata)
749
+ # then the 'backing' option must be passed on reopen. We don't
750
+ # allow leaving the option out in this case because it's unclear
751
+ # what the correct semantics would be.
752
+ def test_missing_backing_options_2(self):
753
+ # hd0 <- hd1
754
+ # (hd0 is hd1's default backing file)
755
+ opts = hd_opts(1)
756
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
757
+ self.assert_qmp(result, 'return', {})
758
+
759
+ # hd1 has a backing file: we can't omit the 'backing' option
760
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
761
+
762
+ # Let's detach the backing file
763
+ self.reopen(opts, {'backing': None})
764
+
765
+ # No backing file attached to hd1 now, but we still can't omit the 'backing' option
766
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
767
+
768
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
769
+ self.assert_qmp(result, 'return', {})
770
+
771
+ # Test that making 'backing' a reference to an existing child
772
+ # keeps its current options
773
+ def test_backing_reference(self):
774
+ # hd2 <- hd1 <- hd0
775
+ opts = hd_opts(0)
776
+ opts['backing'] = hd_opts(1)
777
+ opts['backing']['backing'] = hd_opts(2)
778
+ # Enable 'detect-zeroes' on all three nodes
779
+ opts['detect-zeroes'] = 'on'
780
+ opts['backing']['detect-zeroes'] = 'on'
781
+ opts['backing']['backing']['detect-zeroes'] = 'on'
782
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
783
+ self.assert_qmp(result, 'return', {})
784
+
785
+ # Reopen the chain passing the minimum amount of required options.
786
+ # By making 'backing' a reference to hd1 (instead of a sub-dict)
787
+ # we tell QEMU to keep its current set of options.
788
+ opts = {'driver': iotests.imgfmt,
789
+ 'node-name': 'hd0',
790
+ 'file': 'hd0-file',
791
+ 'backing': 'hd1' }
792
+ self.reopen(opts)
793
+
794
+ # This has reset 'detect-zeroes' on hd0, but not on hd1 and hd2.
795
+ self.assert_qmp(self.get_node('hd0'), 'detect_zeroes', 'off')
796
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
797
+ self.assert_qmp(self.get_node('hd2'), 'detect_zeroes', 'on')
798
+
799
+ # Test what happens if the graph changes due to other operations
800
+ # such as block-stream
801
+ def test_block_stream_1(self):
802
+ # hd1 <- hd0
803
+ opts = hd_opts(0)
804
+ opts['backing'] = hd_opts(1)
805
+ opts['backing']['backing'] = None
806
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
807
+ self.assert_qmp(result, 'return', {})
808
+
809
+ # Stream hd1 into hd0 and wait until it's done
810
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0')
811
+ self.assert_qmp(result, 'return', {})
812
+ self.wait_until_completed(drive = 'stream0')
813
+
814
+ # Now we have only hd0
815
+ self.assertEqual(self.get_node('hd1'), None)
816
+
817
+ # We have backing.* options but there's no backing file anymore
818
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
819
+
820
+ # If we remove the 'backing' option then we can reopen hd0 just fine
821
+ del opts['backing']
822
+ self.reopen(opts)
823
+
824
+ # We can also reopen hd0 if we set 'backing' to null
825
+ self.reopen(opts, {'backing': None})
826
+
827
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
828
+ self.assert_qmp(result, 'return', {})
829
+
830
+ # Another block_stream test
831
+ def test_block_stream_2(self):
832
+ # hd2 <- hd1 <- hd0
833
+ opts = hd_opts(0)
834
+ opts['backing'] = hd_opts(1)
835
+ opts['backing']['backing'] = hd_opts(2)
836
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
837
+ self.assert_qmp(result, 'return', {})
838
+
839
+ # Stream hd1 into hd0 and wait until it's done
840
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
841
+ device = 'hd0', base_node = 'hd2')
842
+ self.assert_qmp(result, 'return', {})
843
+ self.wait_until_completed(drive = 'stream0')
844
+
845
+ # The chain is hd2 <- hd0 now. hd1 is missing
846
+ self.assertEqual(self.get_node('hd1'), None)
847
+
848
+ # The backing options in the dict were meant for hd1, but we cannot
849
+ # use them with hd2 because hd1 had a backing file while hd2 does not.
850
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
851
+
852
+ # If we remove hd1's options from the dict then things work fine
853
+ opts['backing'] = opts['backing']['backing']
854
+ self.reopen(opts)
855
+
856
+ # We can also reopen hd0 if we use a reference to the backing file
857
+ self.reopen(opts, {'backing': 'hd2'})
858
+
859
+ # But we cannot leave the option out
860
+ del opts['backing']
861
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
862
+
863
+ # Now we can delete hd0 (and hd2)
864
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
865
+ self.assert_qmp(result, 'return', {})
866
+ self.assertEqual(self.get_node('hd2'), None)
867
+
868
+ # Reopen the chain during a block-stream job (from hd1 to hd0)
869
+ def test_block_stream_3(self):
870
+ # hd2 <- hd1 <- hd0
871
+ opts = hd_opts(0)
872
+ opts['backing'] = hd_opts(1)
873
+ opts['backing']['backing'] = hd_opts(2)
874
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
875
+ self.assert_qmp(result, 'return', {})
876
+
877
+ # hd2 <- hd0
878
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
879
+ device = 'hd0', base_node = 'hd2', speed = 512 * 1024)
880
+ self.assert_qmp(result, 'return', {})
881
+
882
+ # We can't remove hd2 while the stream job is ongoing
883
+ opts['backing']['backing'] = None
884
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
885
+
886
+ # We can't remove hd1 while the stream job is ongoing
887
+ opts['backing'] = None
888
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
889
+
890
+ self.wait_until_completed(drive = 'stream0')
891
+
892
+ # Reopen the chain during a block-stream job (from hd2 to hd1)
893
+ def test_block_stream_4(self):
894
+ # hd2 <- hd1 <- hd0
895
+ opts = hd_opts(0)
896
+ opts['backing'] = hd_opts(1)
897
+ opts['backing']['backing'] = hd_opts(2)
898
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
899
+ self.assert_qmp(result, 'return', {})
900
+
901
+ # hd1 <- hd0
902
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
903
+ device = 'hd1', speed = 512 * 1024)
904
+ self.assert_qmp(result, 'return', {})
905
+
906
+ # We can't reopen with the original options because that would
907
+ # make hd1 read-only and block-stream requires it to be read-write
908
+ self.reopen(opts, {}, "Can't set node 'hd1' to r/o with copy-on-read enabled")
909
+
910
+ # We can't remove hd2 while the stream job is ongoing
911
+ opts['backing']['backing'] = None
912
+ self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
913
+
914
+ # We can detach hd1 from hd0 because it doesn't affect the stream job
915
+ opts['backing'] = None
916
+ self.reopen(opts)
917
+
918
+ self.wait_until_completed(drive = 'stream0')
919
+
920
+ # Reopen the chain during a block-commit job (from hd0 to hd2)
921
+ def test_block_commit_1(self):
922
+ # hd2 <- hd1 <- hd0
923
+ opts = hd_opts(0)
924
+ opts['backing'] = hd_opts(1)
925
+ opts['backing']['backing'] = hd_opts(2)
926
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
927
+ self.assert_qmp(result, 'return', {})
928
+
929
+ result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0',
930
+ device = 'hd0', speed = 1024 * 1024)
931
+ self.assert_qmp(result, 'return', {})
932
+
933
+ # We can't remove hd2 while the commit job is ongoing
934
+ opts['backing']['backing'] = None
935
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
936
+
937
+ # We can't remove hd1 while the commit job is ongoing
938
+ opts['backing'] = None
939
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
940
+
941
+ event = self.vm.event_wait(name='BLOCK_JOB_READY')
942
+ self.assert_qmp(event, 'data/device', 'commit0')
943
+ self.assert_qmp(event, 'data/type', 'commit')
944
+ self.assert_qmp_absent(event, 'data/error')
945
+
946
+ result = self.vm.qmp('block-job-complete', device='commit0')
947
+ self.assert_qmp(result, 'return', {})
948
+
949
+ self.wait_until_completed(drive = 'commit0')
950
+
951
+ # Reopen the chain during a block-commit job (from hd1 to hd2)
952
+ def test_block_commit_2(self):
953
+ # hd2 <- hd1 <- hd0
954
+ opts = hd_opts(0)
955
+ opts['backing'] = hd_opts(1)
956
+ opts['backing']['backing'] = hd_opts(2)
957
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
958
+ self.assert_qmp(result, 'return', {})
959
+
960
+ result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0',
961
+ device = 'hd0', top_node = 'hd1', speed = 1024 * 1024)
962
+ self.assert_qmp(result, 'return', {})
963
+
964
+ # We can't remove hd2 while the commit job is ongoing
965
+ opts['backing']['backing'] = None
966
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
967
+
968
+ # We can't remove hd1 while the commit job is ongoing
969
+ opts['backing'] = None
970
+ self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an implicit backing file")
971
+
972
+ # hd2 <- hd0
973
+ self.wait_until_completed(drive = 'commit0')
974
+
975
+ self.assert_qmp(self.get_node('hd0'), 'ro', False)
976
+ self.assertEqual(self.get_node('hd1'), None)
977
+ self.assert_qmp(self.get_node('hd2'), 'ro', True)
978
+
979
+ # We don't allow setting a backing file that uses a different AioContext
980
+ def test_iothreads(self):
981
+ opts = hd_opts(0)
982
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
983
+ self.assert_qmp(result, 'return', {})
984
+
985
+ opts2 = hd_opts(2)
986
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2)
987
+ self.assert_qmp(result, 'return', {})
988
+
989
+ result = self.vm.qmp('object-add', qom_type='iothread', id='iothread0')
990
+ self.assert_qmp(result, 'return', {})
991
+
992
+ result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1')
993
+ self.assert_qmp(result, 'return', {})
994
+
995
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0')
996
+ self.assert_qmp(result, 'return', {})
997
+
998
+ self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
999
+
1000
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1')
1001
+ self.assert_qmp(result, 'return', {})
1002
+
1003
+ self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
1004
+
1005
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0')
1006
+ self.assert_qmp(result, 'return', {})
1007
+
1008
+ self.reopen(opts, {'backing': 'hd2'})
1009
+
1010
+if __name__ == '__main__':
1011
+ iotests.main(supported_fmts=["qcow2"])
1012
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
1013
new file mode 100644
105
new file mode 100644
1014
index XXXXXXX..XXXXXXX
106
index XXXXXXX..XXXXXXX
1015
--- /dev/null
107
--- /dev/null
1016
+++ b/tests/qemu-iotests/245.out
108
+++ b/util/transactions.c
1017
@@ -XXX,XX +XXX,XX @@
109
@@ -XXX,XX +XXX,XX @@
1018
+..................
110
+/*
1019
+----------------------------------------------------------------------
111
+ * Simple transactions API
1020
+Ran 18 tests
112
+ *
1021
+
113
+ * Copyright (c) 2021 Virtuozzo International GmbH.
1022
+OK
114
+ *
1023
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
115
+ * Author:
116
+ * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
117
+ *
118
+ * This program is free software; you can redistribute it and/or modify
119
+ * it under the terms of the GNU General Public License as published by
120
+ * the Free Software Foundation; either version 2 of the License, or
121
+ * (at your option) any later version.
122
+ *
123
+ * This program is distributed in the hope that it will be useful,
124
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
125
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
126
+ * GNU General Public License for more details.
127
+ *
128
+ * You should have received a copy of the GNU General Public License
129
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
130
+ */
131
+
132
+#include "qemu/osdep.h"
133
+
134
+#include "qemu/transactions.h"
135
+#include "qemu/queue.h"
136
+
137
+typedef struct TransactionAction {
138
+ TransactionActionDrv *drv;
139
+ void *opaque;
140
+ QSLIST_ENTRY(TransactionAction) entry;
141
+} TransactionAction;
142
+
143
+struct Transaction {
144
+ QSLIST_HEAD(, TransactionAction) actions;
145
+};
146
+
147
+Transaction *tran_new(void)
148
+{
149
+ Transaction *tran = g_new(Transaction, 1);
150
+
151
+ QSLIST_INIT(&tran->actions);
152
+
153
+ return tran;
154
+}
155
+
156
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque)
157
+{
158
+ TransactionAction *act;
159
+
160
+ act = g_new(TransactionAction, 1);
161
+ *act = (TransactionAction) {
162
+ .drv = drv,
163
+ .opaque = opaque
164
+ };
165
+
166
+ QSLIST_INSERT_HEAD(&tran->actions, act, entry);
167
+}
168
+
169
+void tran_abort(Transaction *tran)
170
+{
171
+ TransactionAction *act, *next;
172
+
173
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
174
+ if (act->drv->abort) {
175
+ act->drv->abort(act->opaque);
176
+ }
177
+
178
+ if (act->drv->clean) {
179
+ act->drv->clean(act->opaque);
180
+ }
181
+
182
+ g_free(act);
183
+ }
184
+
185
+ g_free(tran);
186
+}
187
+
188
+void tran_commit(Transaction *tran)
189
+{
190
+ TransactionAction *act, *next;
191
+
192
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
193
+ if (act->drv->commit) {
194
+ act->drv->commit(act->opaque);
195
+ }
196
+
197
+ if (act->drv->clean) {
198
+ act->drv->clean(act->opaque);
199
+ }
200
+
201
+ g_free(act);
202
+ }
203
+
204
+ g_free(tran);
205
+}
206
diff --git a/MAINTAINERS b/MAINTAINERS
1024
index XXXXXXX..XXXXXXX 100644
207
index XXXXXXX..XXXXXXX 100644
1025
--- a/tests/qemu-iotests/group
208
--- a/MAINTAINERS
1026
+++ b/tests/qemu-iotests/group
209
+++ b/MAINTAINERS
1027
@@ -XXX,XX +XXX,XX @@
210
@@ -XXX,XX +XXX,XX @@ M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1028
242 rw auto quick
211
S: Maintained
1029
243 rw auto quick
212
F: scripts/simplebench/
1030
244 rw auto quick
213
1031
+245 rw auto
214
+Transactions helper
215
+M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
216
+S: Maintained
217
+F: include/qemu/transactions.h
218
+F: util/transactions.c
219
+
220
QAPI
221
M: Markus Armbruster <armbru@redhat.com>
222
M: Michael Roth <michael.roth@amd.com>
223
diff --git a/util/meson.build b/util/meson.build
224
index XXXXXXX..XXXXXXX 100644
225
--- a/util/meson.build
226
+++ b/util/meson.build
227
@@ -XXX,XX +XXX,XX @@ util_ss.add(files('qsp.c'))
228
util_ss.add(files('range.c'))
229
util_ss.add(files('stats64.c'))
230
util_ss.add(files('systemd.c'))
231
+util_ss.add(files('transactions.c'))
232
util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c'))
233
util_ss.add(files('guest-random.c'))
234
util_ss.add(files('yank.c'))
1032
--
235
--
1033
2.20.1
236
2.30.2
1034
237
1035
238
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
3
Add additional check that node parents do not interfere with each
4
other. This should not hurt existing callers and allows in further
5
patch use bdrv_refresh_perms() to update a subtree of changed
6
BdrvChild (check that change is correct).
7
8
New check will substitute bdrv_check_update_perm() in following
9
permissions refactoring, so keep error messages the same to avoid
10
unit test result changes.
11
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Reviewed-by: Alberto Garcia <berto@igalia.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Message-Id: <20210428151804.439460-10-vsementsov@virtuozzo.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
---
17
---
3
block/file-posix.c | 107 ++++++++++++++++++++++++++-------------------
18
block.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++---------
4
1 file changed, 62 insertions(+), 45 deletions(-)
19
1 file changed, 54 insertions(+), 9 deletions(-)
5
20
6
diff --git a/block/file-posix.c b/block/file-posix.c
21
diff --git a/block.c b/block.c
7
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
8
--- a/block/file-posix.c
23
--- a/block.c
9
+++ b/block/file-posix.c
24
+++ b/block.c
10
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
25
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_writable(BlockDriverState *bs)
11
return ret;
26
return bdrv_is_writable_after_reopen(bs, NULL);
12
}
27
}
13
28
14
+static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
29
+static char *bdrv_child_user_desc(BdrvChild *c)
15
+ int *open_flags, Error **errp)
16
+{
30
+{
17
+ BDRVRawState *s = bs->opaque;
31
+ if (c->klass->get_parent_desc) {
18
+ int fd = -1;
32
+ return c->klass->get_parent_desc(c);
19
+ int ret;
20
+ int fcntl_flags = O_APPEND | O_NONBLOCK;
21
+#ifdef O_NOATIME
22
+ fcntl_flags |= O_NOATIME;
23
+#endif
24
+
25
+ *open_flags = 0;
26
+ if (s->type == FTYPE_CD) {
27
+ *open_flags |= O_NONBLOCK;
28
+ }
33
+ }
29
+
34
+
30
+ raw_parse_flags(flags, open_flags);
35
+ return g_strdup("another user");
36
+}
31
+
37
+
32
+#ifdef O_ASYNC
38
+static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
33
+ /* Not all operating systems have O_ASYNC, and those that don't
39
+{
34
+ * will not let us track the state into rs->open_flags (typically
40
+ g_autofree char *user = NULL;
35
+ * you achieve the same effect with an ioctl, for example I_SETSIG
41
+ g_autofree char *perm_names = NULL;
36
+ * on Solaris). But we do not use O_ASYNC, so that's fine.
42
+
43
+ if ((b->perm & a->shared_perm) == b->perm) {
44
+ return true;
45
+ }
46
+
47
+ perm_names = bdrv_perm_names(b->perm & ~a->shared_perm);
48
+ user = bdrv_child_user_desc(a);
49
+ error_setg(errp, "Conflicts with use by %s as '%s', which does not "
50
+ "allow '%s' on %s",
51
+ user, a->name, perm_names, bdrv_get_node_name(b->bs));
52
+
53
+ return false;
54
+}
55
+
56
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
57
+{
58
+ BdrvChild *a, *b;
59
+
60
+ /*
61
+ * During the loop we'll look at each pair twice. That's correct because
62
+ * bdrv_a_allow_b() is asymmetric and we should check each pair in both
63
+ * directions.
37
+ */
64
+ */
38
+ assert((s->open_flags & O_ASYNC) == 0);
65
+ QLIST_FOREACH(a, &bs->parents, next_parent) {
39
+#endif
66
+ QLIST_FOREACH(b, &bs->parents, next_parent) {
67
+ if (a == b) {
68
+ continue;
69
+ }
40
+
70
+
41
+ if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
71
+ if (!bdrv_a_allow_b(a, b, errp)) {
42
+ /* dup the original fd */
72
+ return true;
43
+ fd = qemu_dup(s->fd);
44
+ if (fd >= 0) {
45
+ ret = fcntl_setfl(fd, *open_flags);
46
+ if (ret) {
47
+ qemu_close(fd);
48
+ fd = -1;
49
+ }
73
+ }
50
+ }
74
+ }
51
+ }
75
+ }
52
+
76
+
53
+ /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
77
+ return false;
54
+ if (fd == -1) {
55
+ const char *normalized_filename = bs->filename;
56
+ ret = raw_normalize_devicepath(&normalized_filename, errp);
57
+ if (ret >= 0) {
58
+ assert(!(*open_flags & O_CREAT));
59
+ fd = qemu_open(normalized_filename, *open_flags);
60
+ if (fd == -1) {
61
+ error_setg_errno(errp, errno, "Could not reopen file");
62
+ return -1;
63
+ }
64
+ }
65
+ }
66
+
67
+ return fd;
68
+}
78
+}
69
+
79
+
70
static int raw_reopen_prepare(BDRVReopenState *state,
80
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
71
BlockReopenQueue *queue, Error **errp)
81
BdrvChild *c, BdrvChildRole role,
72
{
82
BlockReopenQueue *reopen_queue,
73
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
83
@@ -XXX,XX +XXX,XX @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
74
84
*shared_perm = cumulative_shared_perms;
75
state->opaque = g_new0(BDRVRawReopenState, 1);
85
}
76
rs = state->opaque;
86
77
- rs->fd = -1;
87
-static char *bdrv_child_user_desc(BdrvChild *c)
78
88
-{
79
/* Handle options changes */
89
- if (c->klass->get_parent_desc) {
80
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
90
- return c->klass->get_parent_desc(c);
81
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
82
* bdrv_reopen_prepare() will detect changes and complain. */
83
qemu_opts_to_qdict(opts, state->options);
84
85
- if (s->type == FTYPE_CD) {
86
- rs->open_flags |= O_NONBLOCK;
87
- }
91
- }
88
-
92
-
89
- raw_parse_flags(state->flags, &rs->open_flags);
93
- return g_strdup("another user");
94
-}
90
-
95
-
91
- int fcntl_flags = O_APPEND | O_NONBLOCK;
96
char *bdrv_perm_names(uint64_t perm)
92
-#ifdef O_NOATIME
97
{
93
- fcntl_flags |= O_NOATIME;
98
struct perm_name {
94
-#endif
99
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
95
-
100
int ret;
96
-#ifdef O_ASYNC
101
uint64_t perm, shared_perm;
97
- /* Not all operating systems have O_ASYNC, and those that don't
102
98
- * will not let us track the state into rs->open_flags (typically
103
+ if (bdrv_parent_perms_conflict(bs, errp)) {
99
- * you achieve the same effect with an ioctl, for example I_SETSIG
104
+ return -EPERM;
100
- * on Solaris). But we do not use O_ASYNC, so that's fine.
105
+ }
101
- */
106
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
102
- assert((s->open_flags & O_ASYNC) == 0);
107
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
103
-#endif
108
if (ret < 0) {
104
-
105
- if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
106
- /* dup the original fd */
107
- rs->fd = qemu_dup(s->fd);
108
- if (rs->fd >= 0) {
109
- ret = fcntl_setfl(rs->fd, rs->open_flags);
110
- if (ret) {
111
- qemu_close(rs->fd);
112
- rs->fd = -1;
113
- }
114
- }
115
- }
116
-
117
- /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
118
- if (rs->fd == -1) {
119
- const char *normalized_filename = state->bs->filename;
120
- ret = raw_normalize_devicepath(&normalized_filename, errp);
121
- if (ret >= 0) {
122
- assert(!(rs->open_flags & O_CREAT));
123
- rs->fd = qemu_open(normalized_filename, rs->open_flags);
124
- if (rs->fd == -1) {
125
- error_setg_errno(errp, errno, "Could not reopen file");
126
- ret = -1;
127
- }
128
- }
129
+ rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
130
+ &local_err);
131
+ if (local_err) {
132
+ error_propagate(errp, local_err);
133
+ ret = -1;
134
+ goto out;
135
}
136
137
/* Fail already reopen_prepare() if we can't get a working O_DIRECT
138
--
109
--
139
2.20.1
110
2.30.2
140
111
141
112
diff view generated by jsdifflib
1
We'll want to access the file descriptor in the reopen_state while
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
processing permission changes in the context of the repoen.
3
2
3
Split out non-recursive parts, and refactor as block graph transaction
4
action.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-11-vsementsov@virtuozzo.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
block/file-posix.c | 10 ++++++++++
11
block.c | 79 ++++++++++++++++++++++++++++++++++++++++++---------------
7
1 file changed, 10 insertions(+)
12
1 file changed, 59 insertions(+), 20 deletions(-)
8
13
9
diff --git a/block/file-posix.c b/block/file-posix.c
14
diff --git a/block.c b/block.c
10
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
11
--- a/block/file-posix.c
16
--- a/block.c
12
+++ b/block/file-posix.c
17
+++ b/block.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
18
@@ -XXX,XX +XXX,XX @@
14
uint64_t locked_perm;
19
#include "qemu/timer.h"
15
uint64_t locked_shared_perm;
20
#include "qemu/cutils.h"
16
21
#include "qemu/id.h"
17
+ BDRVReopenState *reopen_state;
22
+#include "qemu/transactions.h"
23
#include "block/coroutines.h"
24
25
#ifdef CONFIG_BSD
26
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
27
}
28
}
29
30
+static void bdrv_child_set_perm_commit(void *opaque)
31
+{
32
+ BdrvChild *c = opaque;
18
+
33
+
19
#ifdef CONFIG_XFS
34
+ c->has_backup_perm = false;
20
bool is_xfs:1;
35
+}
21
#endif
36
+
22
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
37
+static void bdrv_child_set_perm_abort(void *opaque)
23
}
38
+{
39
+ BdrvChild *c = opaque;
40
+ /*
41
+ * We may have child->has_backup_perm unset at this point, as in case of
42
+ * _check_ stage of permission update failure we may _check_ not the whole
43
+ * subtree. Still, _abort_ is called on the whole subtree anyway.
44
+ */
45
+ if (c->has_backup_perm) {
46
+ c->perm = c->backup_perm;
47
+ c->shared_perm = c->backup_shared_perm;
48
+ c->has_backup_perm = false;
49
+ }
50
+}
51
+
52
+static TransactionActionDrv bdrv_child_set_pem_drv = {
53
+ .abort = bdrv_child_set_perm_abort,
54
+ .commit = bdrv_child_set_perm_commit,
55
+};
56
+
57
+/*
58
+ * With tran=NULL needs to be followed by direct call to either
59
+ * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort().
60
+ *
61
+ * With non-NULL tran needs to be followed by tran_abort() or tran_commit()
62
+ * instead.
63
+ */
64
+static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
65
+ uint64_t shared, Transaction *tran)
66
+{
67
+ if (!c->has_backup_perm) {
68
+ c->has_backup_perm = true;
69
+ c->backup_perm = c->perm;
70
+ c->backup_shared_perm = c->shared_perm;
71
+ }
72
+ /*
73
+ * Note: it's OK if c->has_backup_perm was already set, as we can find the
74
+ * same c twice during check_perm procedure
75
+ */
76
+
77
+ c->perm = perm;
78
+ c->shared_perm = shared;
79
+
80
+ if (tran) {
81
+ tran_add(tran, &bdrv_child_set_pem_drv, c);
82
+ }
83
+}
84
+
85
/*
86
* Check whether permissions on this node can be changed in a way that
87
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
88
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
89
return ret;
24
}
90
}
25
91
26
+ s->reopen_state = state;
92
- if (!c->has_backup_perm) {
27
out:
93
- c->has_backup_perm = true;
28
qemu_opts_del(opts);
94
- c->backup_perm = c->perm;
29
return ret;
95
- c->backup_shared_perm = c->shared_perm;
30
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
96
- }
31
97
- /*
32
g_free(state->opaque);
98
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
33
state->opaque = NULL;
99
- * same child twice during check_perm procedure
34
+
100
- */
35
+ assert(s->reopen_state == state);
101
-
36
+ s->reopen_state = NULL;
102
- c->perm = perm;
103
- c->shared_perm = shared;
104
+ bdrv_child_set_perm_safe(c, perm, shared, NULL);
105
106
return 0;
37
}
107
}
38
108
39
109
static void bdrv_child_set_perm(BdrvChild *c)
40
static void raw_reopen_abort(BDRVReopenState *state)
41
{
110
{
42
BDRVRawReopenState *rs = state->opaque;
111
- c->has_backup_perm = false;
43
+ BDRVRawState *s = state->bs->opaque;
112
-
44
113
+ bdrv_child_set_perm_commit(c);
45
/* nothing to do if NULL, we didn't get far enough */
114
bdrv_set_perm(c->bs);
46
if (rs == NULL) {
47
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_abort(BDRVReopenState *state)
48
}
49
g_free(state->opaque);
50
state->opaque = NULL;
51
+
52
+ assert(s->reopen_state == state);
53
+ s->reopen_state = NULL;
54
}
115
}
55
116
56
static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
117
static void bdrv_child_abort_perm_update(BdrvChild *c)
118
{
119
- if (c->has_backup_perm) {
120
- c->perm = c->backup_perm;
121
- c->shared_perm = c->backup_shared_perm;
122
- c->has_backup_perm = false;
123
- }
124
-
125
+ bdrv_child_set_perm_abort(c);
126
bdrv_abort_perm_update(c->bs);
127
}
128
57
--
129
--
58
2.20.1
130
2.30.2
59
131
60
132
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
We are going to drop recursive bdrv_child_* functions, so stop use them
4
in bdrv_child_try_set_perm() as a first step.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-12-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block.c | 14 ++++++++------
12
1 file changed, 8 insertions(+), 6 deletions(-)
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 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
19
Error **errp)
20
{
21
Error *local_err = NULL;
22
+ Transaction *tran = tran_new();
23
int ret;
24
25
- ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, &local_err);
26
+ bdrv_child_set_perm_safe(c, perm, shared, tran);
27
+
28
+ ret = bdrv_refresh_perms(c->bs, &local_err);
29
+
30
+ tran_finalize(tran, ret);
31
+
32
if (ret < 0) {
33
- bdrv_child_abort_perm_update(c);
34
if ((perm & ~c->perm) || (c->shared_perm & ~shared)) {
35
/* tighten permissions */
36
error_propagate(errp, local_err);
37
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
38
error_free(local_err);
39
ret = 0;
40
}
41
- return ret;
42
}
43
44
- bdrv_child_set_perm(c);
45
-
46
- return 0;
47
+ return ret;
48
}
49
50
int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp)
51
--
52
2.30.2
53
54
diff view generated by jsdifflib
1
The way that reopen interacts with permission changes has one big
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
problem: Both operations are recursive, and the permissions are changes
3
for each node in the reopen queue.
4
2
5
For a simple graph that consists just of parent and child,
3
Each of them has only one caller. Open-coding simplifies further
6
.bdrv_check_perm will be called twice for the child, once recursively
4
pemission-update system changes.
7
when adjusting the permissions of parent, and once again when the child
8
itself is reopened.
9
5
10
Even worse, the first .bdrv_check_perm call happens before
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
.bdrv_reopen_prepare was called for the child and the second one is
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
12
called afterwards.
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
9
Message-Id: <20210428151804.439460-13-vsementsov@virtuozzo.com>
14
Making sure that .bdrv_check_perm (and the other permission callbacks)
15
are called only once is hard. We can cope with multiple calls right now,
16
but as soon as file-posix gets a dynamic auto-read-only that may need to
17
open a new file descriptor, we get the additional requirement that all
18
of them are after the .bdrv_reopen_prepare call.
19
20
So reorder things in bdrv_reopen_multiple() to first call
21
.bdrv_reopen_prepare for all involved nodes and only then adjust
22
permissions.
23
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
11
---
26
block.c | 35 ++++++++++++++++++++++++-----------
12
block.c | 59 +++++++++++++++++----------------------------------------
27
1 file changed, 24 insertions(+), 11 deletions(-)
13
1 file changed, 17 insertions(+), 42 deletions(-)
28
14
29
diff --git a/block.c b/block.c
15
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
17
--- a/block.c
32
+++ b/block.c
18
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
19
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
20
return 0;
21
}
22
23
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
24
- uint64_t perm, uint64_t shared,
25
- GSList *ignore_children, Error **errp);
26
-static void bdrv_child_abort_perm_update(BdrvChild *c);
27
-static void bdrv_child_set_perm(BdrvChild *c);
28
+static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
29
+ uint64_t new_used_perm,
30
+ uint64_t new_shared_perm,
31
+ GSList *ignore_children,
32
+ Error **errp);
34
33
35
typedef struct BlockReopenQueueEntry {
34
typedef struct BlockReopenQueueEntry {
36
bool prepared;
35
bool prepared;
37
+ bool perms_checked;
36
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
38
BDRVReopenState state;
37
/* Check all children */
39
QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
38
QLIST_FOREACH(c, &bs->children, next) {
40
} BlockReopenQueueEntry;
39
uint64_t cur_perm, cur_shared;
41
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
40
+ GSList *cur_ignore_children;
42
bs_entry->prepared = true;
41
42
bdrv_child_perm(bs, c->bs, c, c->role, q,
43
cumulative_perms, cumulative_shared_perms,
44
&cur_perm, &cur_shared);
45
- ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children,
46
- errp);
47
+
48
+ cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
49
+ ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared,
50
+ cur_ignore_children, errp);
51
+ g_slist_free(cur_ignore_children);
52
if (ret < 0) {
53
return ret;
54
}
55
+
56
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
43
}
57
}
44
58
45
+ QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
59
return 0;
46
+ BDRVReopenState *state = &bs_entry->state;
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
47
+ ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
48
+ state->shared_perm, NULL, errp);
49
+ if (ret < 0) {
50
+ goto cleanup_perm;
51
+ }
52
+ bs_entry->perms_checked = true;
53
+ }
54
+
55
/* If we reach this point, we have success and just need to apply the
56
* changes
57
*/
58
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
59
}
61
}
60
62
61
ret = 0;
63
QLIST_FOREACH(c, &bs->children, next) {
62
+cleanup_perm:
64
- bdrv_child_abort_perm_update(c);
63
+ QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
65
+ bdrv_child_set_perm_abort(c);
64
+ BDRVReopenState *state = &bs_entry->state;
66
+ bdrv_abort_perm_update(c->bs);
65
+
66
+ if (!bs_entry->perms_checked) {
67
+ continue;
68
+ }
69
70
+ if (ret == 0) {
71
+ bdrv_set_perm(state->bs, state->perm, state->shared_perm);
72
+ } else {
73
+ bdrv_abort_perm_update(state->bs);
74
+ }
75
+ }
76
cleanup:
77
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
78
if (ret) {
79
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
80
} while ((entry = qdict_next(reopen_state->options, entry)));
81
}
67
}
82
68
}
83
- ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm,
69
84
- reopen_state->shared_perm, NULL, errp);
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs)
71
72
/* Update all children */
73
QLIST_FOREACH(c, &bs->children, next) {
74
- bdrv_child_set_perm(c);
75
+ bdrv_child_set_perm_commit(c);
76
+ bdrv_set_perm(c->bs);
77
}
78
}
79
80
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
81
ignore_children, errp);
82
}
83
84
-/* Needs to be followed by a call to either bdrv_child_set_perm() or
85
- * bdrv_child_abort_perm_update(). */
86
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
87
- uint64_t perm, uint64_t shared,
88
- GSList *ignore_children, Error **errp)
89
-{
90
- int ret;
91
-
92
- ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
93
- ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
94
- g_slist_free(ignore_children);
95
-
85
- if (ret < 0) {
96
- if (ret < 0) {
86
- goto error;
97
- return ret;
87
- }
98
- }
88
-
99
-
89
ret = 0;
100
- bdrv_child_set_perm_safe(c, perm, shared, NULL);
90
91
/* Restore the original reopen_state->options QDict */
92
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
93
94
bdrv_refresh_limits(bs, NULL);
95
96
- bdrv_set_perm(reopen_state->bs, reopen_state->perm,
97
- reopen_state->shared_perm);
98
-
101
-
99
new_can_write =
102
- return 0;
100
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
103
-}
101
if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) {
102
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
103
if (drv->bdrv_reopen_abort) {
104
drv->bdrv_reopen_abort(reopen_state);
105
}
106
-
104
-
107
- bdrv_abort_perm_update(reopen_state->bs);
105
-static void bdrv_child_set_perm(BdrvChild *c)
108
}
106
-{
109
107
- bdrv_child_set_perm_commit(c);
110
108
- bdrv_set_perm(c->bs);
109
-}
110
-
111
-static void bdrv_child_abort_perm_update(BdrvChild *c)
112
-{
113
- bdrv_child_set_perm_abort(c);
114
- bdrv_abort_perm_update(c->bs);
115
-}
116
-
117
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
118
{
119
int ret;
111
--
120
--
112
2.20.1
121
2.30.2
113
122
114
123
diff view generated by jsdifflib
New patch
1
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
3
Rewrite bdrv_check_perm(), bdrv_abort_perm_update() and bdrv_set_perm()
4
to update nodes in topological sort order instead of simple DFS. With
5
topologically sorted nodes, we update a node only when all its parents
6
already updated. With DFS it's not so.
7
8
Consider the following example:
9
10
A -+
11
| |
12
| v
13
| B
14
| |
15
v |
16
C<-+
17
18
A is parent for B and C, B is parent for C.
19
20
Obviously, to update permissions, we should go in order A B C, so, when
21
we update C, all parent permissions already updated. But with current
22
approach (simple recursion) we can update in sequence A C B C (C is
23
updated twice). On first update of C, we consider old B permissions, so
24
doing wrong thing. If it succeed, all is OK, on second C update we will
25
finish with correct graph. But if the wrong thing failed, we break the
26
whole process for no reason (it's possible that updated B permission
27
will be less strict, but we will never check it).
28
29
Also new approach gives a way to simultaneously and correctly update
30
several nodes, we just need to run bdrv_topological_dfs() several times
31
to add all nodes and their subtrees into one topologically sorted list
32
(next patch will update bdrv_replace_node() in this manner).
33
34
Test test_parallel_perm_update() is now passing, so move it out of
35
debugging "if".
36
37
We also need to support ignore_children in
38
bdrv_parent_perms_conflict()
39
40
For test 283 order of conflicting parents check is changed.
41
42
Note also that in bdrv_check_perm() we don't check for parents conflict
43
at root bs, as we may be in the middle of permission update in
44
bdrv_reopen_multiple(). bdrv_reopen_multiple() will be updated soon.
45
46
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
47
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
48
Message-Id: <20210428151804.439460-14-vsementsov@virtuozzo.com>
49
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
---
51
block.c | 116 +++++++++++++++++++++++++------
52
tests/unit/test-bdrv-graph-mod.c | 4 +-
53
tests/qemu-iotests/283.out | 2 +-
54
3 files changed, 99 insertions(+), 23 deletions(-)
55
56
diff --git a/block.c b/block.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/block.c
59
+++ b/block.c
60
@@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
61
return false;
62
}
63
64
-static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
65
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
66
+ GSList *ignore_children,
67
+ Error **errp)
68
{
69
BdrvChild *a, *b;
70
71
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
72
* directions.
73
*/
74
QLIST_FOREACH(a, &bs->parents, next_parent) {
75
+ if (g_slist_find(ignore_children, a)) {
76
+ continue;
77
+ }
78
+
79
QLIST_FOREACH(b, &bs->parents, next_parent) {
80
- if (a == b) {
81
+ if (a == b || g_slist_find(ignore_children, b)) {
82
continue;
83
}
84
85
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
86
}
87
}
88
89
+/*
90
+ * Adds the whole subtree of @bs (including @bs itself) to the @list (except for
91
+ * nodes that are already in the @list, of course) so that final list is
92
+ * topologically sorted. Return the result (GSList @list object is updated, so
93
+ * don't use old reference after function call).
94
+ *
95
+ * On function start @list must be already topologically sorted and for any node
96
+ * in the @list the whole subtree of the node must be in the @list as well. The
97
+ * simplest way to satisfy this criteria: use only result of
98
+ * bdrv_topological_dfs() or NULL as @list parameter.
99
+ */
100
+static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
101
+ BlockDriverState *bs)
102
+{
103
+ BdrvChild *child;
104
+ g_autoptr(GHashTable) local_found = NULL;
105
+
106
+ if (!found) {
107
+ assert(!list);
108
+ found = local_found = g_hash_table_new(NULL, NULL);
109
+ }
110
+
111
+ if (g_hash_table_contains(found, bs)) {
112
+ return list;
113
+ }
114
+ g_hash_table_add(found, bs);
115
+
116
+ QLIST_FOREACH(child, &bs->children, next) {
117
+ list = bdrv_topological_dfs(list, found, child->bs);
118
+ }
119
+
120
+ return g_slist_prepend(list, bs);
121
+}
122
+
123
static void bdrv_child_set_perm_commit(void *opaque)
124
{
125
BdrvChild *c = opaque;
126
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
127
* A call to this function must always be followed by a call to bdrv_set_perm()
128
* or bdrv_abort_perm_update().
129
*/
130
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
131
- uint64_t cumulative_perms,
132
- uint64_t cumulative_shared_perms,
133
- GSList *ignore_children, Error **errp)
134
+static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
135
+ uint64_t cumulative_perms,
136
+ uint64_t cumulative_shared_perms,
137
+ GSList *ignore_children, Error **errp)
138
{
139
BlockDriver *drv = bs->drv;
140
BdrvChild *c;
141
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
142
/* Check all children */
143
QLIST_FOREACH(c, &bs->children, next) {
144
uint64_t cur_perm, cur_shared;
145
- GSList *cur_ignore_children;
146
147
bdrv_child_perm(bs, c->bs, c, c->role, q,
148
cumulative_perms, cumulative_shared_perms,
149
&cur_perm, &cur_shared);
150
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
151
+ }
152
+
153
+ return 0;
154
+}
155
+
156
+static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
157
+ uint64_t cumulative_perms,
158
+ uint64_t cumulative_shared_perms,
159
+ GSList *ignore_children, Error **errp)
160
+{
161
+ int ret;
162
+ BlockDriverState *root = bs;
163
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root);
164
165
- cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
166
- ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared,
167
- cur_ignore_children, errp);
168
- g_slist_free(cur_ignore_children);
169
+ for ( ; list; list = list->next) {
170
+ bs = list->data;
171
+
172
+ if (bs != root) {
173
+ if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
174
+ return -EINVAL;
175
+ }
176
+
177
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
178
+ &cumulative_shared_perms);
179
+ }
180
+
181
+ ret = bdrv_node_check_perm(bs, q, cumulative_perms,
182
+ cumulative_shared_perms,
183
+ ignore_children, errp);
184
if (ret < 0) {
185
return ret;
186
}
187
-
188
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
189
}
190
191
return 0;
192
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
193
* Notifies drivers that after a previous bdrv_check_perm() call, the
194
* permission update is not performed and any preparations made for it (e.g.
195
* taken file locks) need to be undone.
196
- *
197
- * This function recursively notifies all child nodes.
198
*/
199
-static void bdrv_abort_perm_update(BlockDriverState *bs)
200
+static void bdrv_node_abort_perm_update(BlockDriverState *bs)
201
{
202
BlockDriver *drv = bs->drv;
203
BdrvChild *c;
204
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
205
206
QLIST_FOREACH(c, &bs->children, next) {
207
bdrv_child_set_perm_abort(c);
208
- bdrv_abort_perm_update(c->bs);
209
}
210
}
211
212
-static void bdrv_set_perm(BlockDriverState *bs)
213
+static void bdrv_abort_perm_update(BlockDriverState *bs)
214
+{
215
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
216
+
217
+ for ( ; list; list = list->next) {
218
+ bdrv_node_abort_perm_update((BlockDriverState *)list->data);
219
+ }
220
+}
221
+
222
+static void bdrv_node_set_perm(BlockDriverState *bs)
223
{
224
uint64_t cumulative_perms, cumulative_shared_perms;
225
BlockDriver *drv = bs->drv;
226
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs)
227
/* Update all children */
228
QLIST_FOREACH(c, &bs->children, next) {
229
bdrv_child_set_perm_commit(c);
230
- bdrv_set_perm(c->bs);
231
+ }
232
+}
233
+
234
+static void bdrv_set_perm(BlockDriverState *bs)
235
+{
236
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
237
+
238
+ for ( ; list; list = list->next) {
239
+ bdrv_node_set_perm((BlockDriverState *)list->data);
240
}
241
}
242
243
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
244
int ret;
245
uint64_t perm, shared_perm;
246
247
- if (bdrv_parent_perms_conflict(bs, errp)) {
248
+ if (bdrv_parent_perms_conflict(bs, NULL, errp)) {
249
return -EPERM;
250
}
251
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
252
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
253
index XXXXXXX..XXXXXXX 100644
254
--- a/tests/unit/test-bdrv-graph-mod.c
255
+++ b/tests/unit/test-bdrv-graph-mod.c
256
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
257
g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
258
g_test_add_func("/bdrv-graph-mod/should-update-child",
259
test_should_update_child);
260
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
261
+ test_parallel_perm_update);
262
263
if (debug) {
264
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
265
test_parallel_exclusive_write);
266
- g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
267
- test_parallel_perm_update);
268
g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
269
test_append_greedy_filter);
270
}
271
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
272
index XXXXXXX..XXXXXXX 100644
273
--- a/tests/qemu-iotests/283.out
274
+++ b/tests/qemu-iotests/283.out
275
@@ -XXX,XX +XXX,XX @@
276
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
277
{"return": {}}
278
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
279
-{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}}
280
+{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
281
282
=== backup-top should be gone after job-finalize ===
283
284
--
285
2.30.2
286
287
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This patch allows the user to change the backing file of an image that
3
Refactor calling driver callbacks to a separate transaction action to
4
is being reopened. Here's what it does:
4
be used later.
5
5
6
- In bdrv_reopen_prepare(): check that the value of 'backing' points
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
to an existing node or is null. If it points to an existing node it
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
also needs to make sure that replacing the backing file will not
8
Message-Id: <20210428151804.439460-15-vsementsov@virtuozzo.com>
9
create a cycle in the node graph (i.e. you cannot reach the parent
10
from the new backing file).
11
12
- In bdrv_reopen_commit(): perform the actual node replacement by
13
calling bdrv_set_backing_hd().
14
15
There may be temporary implicit nodes between a BDS and its backing
16
file (e.g. a commit filter node). In these cases bdrv_reopen_prepare()
17
looks for the real (non-implicit) backing file and requires that the
18
'backing' option points to it. Replacing or detaching a backing file
19
is forbidden if there are implicit nodes in the middle.
20
21
Although x-blockdev-reopen is meant to be used like blockdev-add,
22
there's an important thing that must be taken into account: the only
23
way to set a new backing file is by using a reference to an existing
24
node (previously added with e.g. blockdev-add). If 'backing' contains
25
a dictionary with a new set of options ({"driver": "qcow2", "file": {
26
... }}) then it is interpreted that the _existing_ backing file must
27
be reopened with those options.
28
29
Signed-off-by: Alberto Garcia <berto@igalia.com>
30
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
31
---
10
---
32
include/block/block.h | 2 +
11
block.c | 70 ++++++++++++++++++++++++++++++++++++++++++++-------------
33
block.c | 166 ++++++++++++++++++++++++++++++++++++++++++
12
1 file changed, 54 insertions(+), 16 deletions(-)
34
2 files changed, 168 insertions(+)
35
13
36
diff --git a/include/block/block.h b/include/block/block.h
37
index XXXXXXX..XXXXXXX 100644
38
--- a/include/block/block.h
39
+++ b/include/block/block.h
40
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
41
int flags;
42
BlockdevDetectZeroesOptions detect_zeroes;
43
bool backing_missing;
44
+ bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
45
+ BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */
46
uint64_t perm, shared_perm;
47
QDict *options;
48
QDict *explicit_options;
49
diff --git a/block.c b/block.c
14
diff --git a/block.c b/block.c
50
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
51
--- a/block.c
16
--- a/block.c
52
+++ b/block.c
17
+++ b/block.c
53
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
18
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
54
NULL, errp);
19
}
55
}
20
}
56
21
57
+/*
22
+static void bdrv_drv_set_perm_commit(void *opaque)
58
+ * Returns true if @child can be reached recursively from @bs
59
+ */
60
+static bool bdrv_recurse_has_child(BlockDriverState *bs,
61
+ BlockDriverState *child)
62
+{
23
+{
63
+ BdrvChild *c;
24
+ BlockDriverState *bs = opaque;
25
+ uint64_t cumulative_perms, cumulative_shared_perms;
64
+
26
+
65
+ if (bs == child) {
27
+ if (bs->drv->bdrv_set_perm) {
66
+ return true;
28
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
29
+ &cumulative_shared_perms);
30
+ bs->drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
31
+ }
32
+}
33
+
34
+static void bdrv_drv_set_perm_abort(void *opaque)
35
+{
36
+ BlockDriverState *bs = opaque;
37
+
38
+ if (bs->drv->bdrv_abort_perm_update) {
39
+ bs->drv->bdrv_abort_perm_update(bs);
40
+ }
41
+}
42
+
43
+TransactionActionDrv bdrv_drv_set_perm_drv = {
44
+ .abort = bdrv_drv_set_perm_abort,
45
+ .commit = bdrv_drv_set_perm_commit,
46
+};
47
+
48
+static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
49
+ uint64_t shared_perm, Transaction *tran,
50
+ Error **errp)
51
+{
52
+ if (!bs->drv) {
53
+ return 0;
67
+ }
54
+ }
68
+
55
+
69
+ QLIST_FOREACH(c, &bs->children, next) {
56
+ if (bs->drv->bdrv_check_perm) {
70
+ if (bdrv_recurse_has_child(c->bs, child)) {
57
+ int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp);
71
+ return true;
58
+ if (ret < 0) {
59
+ return ret;
72
+ }
60
+ }
73
+ }
61
+ }
74
+
62
+
75
+ return false;
63
+ if (tran) {
76
+}
64
+ tran_add(tran, &bdrv_drv_set_perm_drv, bs);
77
+
78
/*
79
* Adds a BlockDriverState to a simple queue for an atomic, transactional
80
* reopen of multiple devices.
81
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
82
if (ret < 0) {
83
goto cleanup_perm;
84
}
85
+ /* Check if new_backing_bs would accept the new permissions */
86
+ if (state->replace_backing_bs && state->new_backing_bs) {
87
+ uint64_t nperm, nshared;
88
+ bdrv_child_perm(state->bs, state->new_backing_bs,
89
+ NULL, &child_backing, bs_queue,
90
+ state->perm, state->shared_perm,
91
+ &nperm, &nshared);
92
+ ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
93
+ nperm, nshared, NULL, errp);
94
+ if (ret < 0) {
95
+ goto cleanup_perm;
96
+ }
97
+ }
98
bs_entry->perms_checked = true;
99
}
100
101
@@ -XXX,XX +XXX,XX @@ cleanup_perm:
102
bdrv_set_perm(state->bs, state->perm, state->shared_perm);
103
} else {
104
bdrv_abort_perm_update(state->bs);
105
+ if (state->replace_backing_bs && state->new_backing_bs) {
106
+ bdrv_abort_perm_update(state->new_backing_bs);
107
+ }
108
}
109
}
110
cleanup:
111
@@ -XXX,XX +XXX,XX @@ cleanup:
112
qobject_unref(bs_entry->state.explicit_options);
113
qobject_unref(bs_entry->state.options);
114
}
115
+ if (bs_entry->state.new_backing_bs) {
116
+ bdrv_unref(bs_entry->state.new_backing_bs);
117
+ }
118
g_free(bs_entry);
119
}
120
g_free(bs_queue);
121
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
122
*shared = cumulative_shared_perms;
123
}
124
125
+/*
126
+ * Take a BDRVReopenState and check if the value of 'backing' in the
127
+ * reopen_state->options QDict is valid or not.
128
+ *
129
+ * If 'backing' is missing from the QDict then return 0.
130
+ *
131
+ * If 'backing' contains the node name of the backing file of
132
+ * reopen_state->bs then return 0.
133
+ *
134
+ * If 'backing' contains a different node name (or is null) then check
135
+ * whether the current backing file can be replaced with the new one.
136
+ * If that's the case then reopen_state->replace_backing_bs is set to
137
+ * true and reopen_state->new_backing_bs contains a pointer to the new
138
+ * backing BlockDriverState (or NULL).
139
+ *
140
+ * Return 0 on success, otherwise return < 0 and set @errp.
141
+ */
142
+static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
143
+ Error **errp)
144
+{
145
+ BlockDriverState *bs = reopen_state->bs;
146
+ BlockDriverState *overlay_bs, *new_backing_bs;
147
+ QObject *value;
148
+ const char *str;
149
+
150
+ value = qdict_get(reopen_state->options, "backing");
151
+ if (value == NULL) {
152
+ return 0;
153
+ }
154
+
155
+ switch (qobject_type(value)) {
156
+ case QTYPE_QNULL:
157
+ new_backing_bs = NULL;
158
+ break;
159
+ case QTYPE_QSTRING:
160
+ str = qobject_get_try_str(value);
161
+ new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
162
+ if (new_backing_bs == NULL) {
163
+ return -EINVAL;
164
+ } else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
165
+ error_setg(errp, "Making '%s' a backing file of '%s' "
166
+ "would create a cycle", str, bs->node_name);
167
+ return -EINVAL;
168
+ }
169
+ break;
170
+ default:
171
+ /* 'backing' does not allow any other data type */
172
+ g_assert_not_reached();
173
+ }
174
+
175
+ /*
176
+ * TODO: before removing the x- prefix from x-blockdev-reopen we
177
+ * should move the new backing file into the right AioContext
178
+ * instead of returning an error.
179
+ */
180
+ if (new_backing_bs) {
181
+ if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
182
+ error_setg(errp, "Cannot use a new backing file "
183
+ "with a different AioContext");
184
+ return -EINVAL;
185
+ }
186
+ }
187
+
188
+ /*
189
+ * Find the "actual" backing file by skipping all links that point
190
+ * to an implicit node, if any (e.g. a commit filter node).
191
+ */
192
+ overlay_bs = bs;
193
+ while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) {
194
+ overlay_bs = backing_bs(overlay_bs);
195
+ }
196
+
197
+ /* If we want to replace the backing file we need some extra checks */
198
+ if (new_backing_bs != backing_bs(overlay_bs)) {
199
+ /* Check for implicit nodes between bs and its backing file */
200
+ if (bs != overlay_bs) {
201
+ error_setg(errp, "Cannot change backing link if '%s' has "
202
+ "an implicit backing file", bs->node_name);
203
+ return -EPERM;
204
+ }
205
+ /* Check if the backing link that we want to replace is frozen */
206
+ if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs),
207
+ errp)) {
208
+ return -EPERM;
209
+ }
210
+ reopen_state->replace_backing_bs = true;
211
+ if (new_backing_bs) {
212
+ bdrv_ref(new_backing_bs);
213
+ reopen_state->new_backing_bs = new_backing_bs;
214
+ }
215
+ }
65
+ }
216
+
66
+
217
+ return 0;
67
+ return 0;
218
+}
68
+}
219
+
69
+
220
/*
70
/*
221
* Prepares a BlockDriverState for reopen. All changes are staged in the
71
* Check whether permissions on this node can be changed in a way that
222
* 'opaque' field of the BDRVReopenState, which is used and allocated by
72
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
223
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
73
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
224
goto error;
74
return 0;
225
}
75
}
226
76
227
+ /*
77
- if (drv->bdrv_check_perm) {
228
+ * Allow changing the 'backing' option. The new value can be
78
- ret = drv->bdrv_check_perm(bs, cumulative_perms,
229
+ * either a reference to an existing node (using its node name)
79
- cumulative_shared_perms, errp);
230
+ * or NULL to simply detach the current backing file.
80
- if (ret < 0) {
231
+ */
81
- return ret;
232
+ ret = bdrv_reopen_parse_backing(reopen_state, errp);
82
- }
83
+ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL,
84
+ errp);
233
+ if (ret < 0) {
85
+ if (ret < 0) {
234
+ goto error;
86
+ return ret;
235
+ }
236
+ qdict_del(reopen_state->options, "backing");
237
+
238
/* Options that are not handled are only okay if they are unchanged
239
* compared to the old state. It is expected that some options are only
240
* used for the initial open, but not reopen (e.g. filename) */
241
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
242
bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
243
bs->detect_zeroes = reopen_state->detect_zeroes;
244
245
+ if (reopen_state->replace_backing_bs) {
246
+ qdict_del(bs->explicit_options, "backing");
247
+ qdict_del(bs->options, "backing");
248
+ }
249
+
250
/* Remove child references from bs->options and bs->explicit_options.
251
* Child options were already removed in bdrv_reopen_queue_child() */
252
QLIST_FOREACH(child, &bs->children, next) {
253
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
254
qdict_del(bs->options, child->name);
255
}
87
}
256
88
257
+ /*
89
/* Drivers that never have children can omit .bdrv_child_perm() */
258
+ * Change the backing file if a new one was specified. We do this
90
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs)
259
+ * after updating bs->options, so bdrv_refresh_filename() (called
91
return;
260
+ * from bdrv_set_backing_hd()) has the new values.
92
}
261
+ */
93
262
+ if (reopen_state->replace_backing_bs) {
94
- if (drv->bdrv_abort_perm_update) {
263
+ BlockDriverState *old_backing_bs = backing_bs(bs);
95
- drv->bdrv_abort_perm_update(bs);
264
+ assert(!old_backing_bs || !old_backing_bs->implicit);
96
- }
265
+ /* Abort the permission update on the backing bs we're detaching */
97
+ bdrv_drv_set_perm_abort(bs);
266
+ if (old_backing_bs) {
98
267
+ bdrv_abort_perm_update(old_backing_bs);
99
QLIST_FOREACH(c, &bs->children, next) {
268
+ }
100
bdrv_child_set_perm_abort(c);
269
+ bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
101
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
270
+ }
102
271
+
103
static void bdrv_node_set_perm(BlockDriverState *bs)
272
bdrv_refresh_limits(bs, NULL);
104
{
273
105
- uint64_t cumulative_perms, cumulative_shared_perms;
274
new_can_write =
106
BlockDriver *drv = bs->drv;
107
BdrvChild *c;
108
109
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs)
110
return;
111
}
112
113
- bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
114
-
115
- /* Update this node */
116
- if (drv->bdrv_set_perm) {
117
- drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
118
- }
119
+ bdrv_drv_set_perm_commit(bs);
120
121
/* Drivers that never have children can omit .bdrv_child_perm() */
122
if (!drv->bdrv_child_perm) {
275
--
123
--
276
2.20.1
124
2.30.2
277
125
278
126
diff view generated by jsdifflib
New patch
1
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
3
Add new interface, allowing use of existing node list. It will be used
4
to fix bdrv_replace_node() in the further commit.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-16-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block.c | 106 +++++++++++++++++++++++++++++++++++++-------------------
12
1 file changed, 71 insertions(+), 35 deletions(-)
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 @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
19
static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
20
uint64_t cumulative_perms,
21
uint64_t cumulative_shared_perms,
22
- GSList *ignore_children, Error **errp)
23
+ GSList *ignore_children,
24
+ Transaction *tran, Error **errp)
25
{
26
BlockDriver *drv = bs->drv;
27
BdrvChild *c;
28
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
29
return 0;
30
}
31
32
- ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL,
33
+ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, tran,
34
errp);
35
if (ret < 0) {
36
return ret;
37
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
38
bdrv_child_perm(bs, c->bs, c, c->role, q,
39
cumulative_perms, cumulative_shared_perms,
40
&cur_perm, &cur_shared);
41
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
42
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran);
43
}
44
45
return 0;
46
}
47
48
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
49
- uint64_t cumulative_perms,
50
- uint64_t cumulative_shared_perms,
51
- GSList *ignore_children, Error **errp)
52
+/*
53
+ * If use_cumulative_perms is true, use cumulative_perms and
54
+ * cumulative_shared_perms for first element of the list. Otherwise just refresh
55
+ * all permissions.
56
+ */
57
+static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
58
+ bool use_cumulative_perms,
59
+ uint64_t cumulative_perms,
60
+ uint64_t cumulative_shared_perms,
61
+ GSList *ignore_children,
62
+ Transaction *tran, Error **errp)
63
{
64
int ret;
65
- BlockDriverState *root = bs;
66
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root);
67
+ BlockDriverState *bs;
68
69
- for ( ; list; list = list->next) {
70
+ if (use_cumulative_perms) {
71
bs = list->data;
72
73
- if (bs != root) {
74
- if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
75
- return -EINVAL;
76
- }
77
+ ret = bdrv_node_check_perm(bs, q, cumulative_perms,
78
+ cumulative_shared_perms,
79
+ ignore_children, tran, errp);
80
+ if (ret < 0) {
81
+ return ret;
82
+ }
83
84
- bdrv_get_cumulative_perm(bs, &cumulative_perms,
85
- &cumulative_shared_perms);
86
+ list = list->next;
87
+ }
88
+
89
+ for ( ; list; list = list->next) {
90
+ bs = list->data;
91
+
92
+ if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
93
+ return -EINVAL;
94
}
95
96
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
97
+ &cumulative_shared_perms);
98
+
99
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
100
cumulative_shared_perms,
101
- ignore_children, errp);
102
+ ignore_children, tran, errp);
103
if (ret < 0) {
104
return ret;
105
}
106
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
107
return 0;
108
}
109
110
+static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
111
+ uint64_t cumulative_perms,
112
+ uint64_t cumulative_shared_perms,
113
+ GSList *ignore_children, Error **errp)
114
+{
115
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
116
+ return bdrv_check_perm_common(list, q, true, cumulative_perms,
117
+ cumulative_shared_perms, ignore_children,
118
+ NULL, errp);
119
+}
120
+
121
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
122
+ Transaction *tran, Error **errp)
123
+{
124
+ return bdrv_check_perm_common(list, q, false, 0, 0, NULL, tran, errp);
125
+}
126
+
127
/*
128
* Notifies drivers that after a previous bdrv_check_perm() call, the
129
* permission update is not performed and any preparations made for it (e.g.
130
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs)
131
}
132
}
133
134
-static void bdrv_abort_perm_update(BlockDriverState *bs)
135
+static void bdrv_list_abort_perm_update(GSList *list)
136
{
137
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
138
-
139
for ( ; list; list = list->next) {
140
bdrv_node_abort_perm_update((BlockDriverState *)list->data);
141
}
142
}
143
144
+static void bdrv_abort_perm_update(BlockDriverState *bs)
145
+{
146
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
147
+ return bdrv_list_abort_perm_update(list);
148
+}
149
+
150
static void bdrv_node_set_perm(BlockDriverState *bs)
151
{
152
BlockDriver *drv = bs->drv;
153
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs)
154
}
155
}
156
157
-static void bdrv_set_perm(BlockDriverState *bs)
158
+static void bdrv_list_set_perm(GSList *list)
159
{
160
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
161
-
162
for ( ; list; list = list->next) {
163
bdrv_node_set_perm((BlockDriverState *)list->data);
164
}
165
}
166
167
+static void bdrv_set_perm(BlockDriverState *bs)
168
+{
169
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
170
+ return bdrv_list_set_perm(list);
171
+}
172
+
173
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
174
uint64_t *shared_perm)
175
{
176
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
177
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
178
{
179
int ret;
180
- uint64_t perm, shared_perm;
181
+ Transaction *tran = tran_new();
182
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
183
184
- if (bdrv_parent_perms_conflict(bs, NULL, errp)) {
185
- return -EPERM;
186
- }
187
- bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
188
- ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
189
- if (ret < 0) {
190
- bdrv_abort_perm_update(bs);
191
- return ret;
192
- }
193
- bdrv_set_perm(bs);
194
+ ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
195
+ tran_finalize(tran, ret);
196
197
- return 0;
198
+ return ret;
199
}
200
201
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
202
--
203
2.30.2
204
205
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
To be used in the following commit.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-17-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
11
1 file changed, 54 insertions(+)
12
13
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
16
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
18
BdrvChildRole child_role,
19
Error **errp);
20
21
+static void bdrv_replace_child_noperm(BdrvChild *child,
22
+ BlockDriverState *new_bs);
23
+
24
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
25
*queue, Error **errp);
26
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
27
@@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
28
return 0;
29
}
30
31
+typedef struct BdrvReplaceChildState {
32
+ BdrvChild *child;
33
+ BlockDriverState *old_bs;
34
+} BdrvReplaceChildState;
35
+
36
+static void bdrv_replace_child_commit(void *opaque)
37
+{
38
+ BdrvReplaceChildState *s = opaque;
39
+
40
+ bdrv_unref(s->old_bs);
41
+}
42
+
43
+static void bdrv_replace_child_abort(void *opaque)
44
+{
45
+ BdrvReplaceChildState *s = opaque;
46
+ BlockDriverState *new_bs = s->child->bs;
47
+
48
+ /* old_bs reference is transparently moved from @s to @s->child */
49
+ bdrv_replace_child_noperm(s->child, s->old_bs);
50
+ bdrv_unref(new_bs);
51
+}
52
+
53
+static TransactionActionDrv bdrv_replace_child_drv = {
54
+ .commit = bdrv_replace_child_commit,
55
+ .abort = bdrv_replace_child_abort,
56
+ .clean = g_free,
57
+};
58
+
59
+/*
60
+ * bdrv_replace_child_safe
61
+ *
62
+ * Note: real unref of old_bs is done only on commit.
63
+ */
64
+__attribute__((unused))
65
+static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
66
+ Transaction *tran)
67
+{
68
+ BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
69
+ *s = (BdrvReplaceChildState) {
70
+ .child = child,
71
+ .old_bs = child->bs,
72
+ };
73
+ tran_add(tran, &bdrv_replace_child_drv, s);
74
+
75
+ if (new_bs) {
76
+ bdrv_ref(new_bs);
77
+ }
78
+ bdrv_replace_child_noperm(child, new_bs);
79
+ /* old_bs reference is transparently moved from @child to @s */
80
+}
81
+
82
/*
83
* Check whether permissions on this node can be changed in a way that
84
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
85
--
86
2.30.2
87
88
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
inore_children thing doesn't help to track all propagated permissions
4
of children we want to ignore. The simplest way to correctly update
5
permissions is update graph first and then do permission update. In
6
this case we just referesh permissions for the whole subgraph (in
7
topological-sort defined order) and everything is correctly calculated
8
automatically without any ignore_children.
9
10
So, refactor bdrv_replace_node_common to first do graph update and then
11
refresh the permissions.
12
13
Test test_parallel_exclusive_write() now pass, so move it out of
14
debugging "if".
15
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
18
Message-Id: <20210428151804.439460-18-vsementsov@virtuozzo.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
21
block.c | 43 +++++++++++++-------------------
22
tests/unit/test-bdrv-graph-mod.c | 4 +--
23
2 files changed, 20 insertions(+), 27 deletions(-)
24
25
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
28
+++ b/block.c
29
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
30
*
31
* Note: real unref of old_bs is done only on commit.
32
*/
33
-__attribute__((unused))
34
static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
35
Transaction *tran)
36
{
37
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
38
bool auto_skip, Error **errp)
39
{
40
BdrvChild *c, *next;
41
- GSList *list = NULL, *p;
42
- uint64_t perm = 0, shared = BLK_PERM_ALL;
43
+ Transaction *tran = tran_new();
44
+ g_autoptr(GHashTable) found = NULL;
45
+ g_autoptr(GSList) refresh_list = NULL;
46
int ret;
47
48
/* Make sure that @from doesn't go away until we have successfully attached
49
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
50
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
51
bdrv_drained_begin(from);
52
53
- /* Put all parents into @list and calculate their cumulative permissions */
54
+ /*
55
+ * Do the replacement without permission update.
56
+ * Replacement may influence the permissions, we should calculate new
57
+ * permissions based on new graph. If we fail, we'll roll-back the
58
+ * replacement.
59
+ */
60
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
61
assert(c->bs == from);
62
if (!should_update_child(c, to)) {
63
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
64
c->name, from->node_name);
65
goto out;
66
}
67
- list = g_slist_prepend(list, c);
68
- perm |= c->perm;
69
- shared &= c->shared_perm;
70
+ bdrv_replace_child_safe(c, to, tran);
71
}
72
73
- /* Check whether the required permissions can be granted on @to, ignoring
74
- * all BdrvChild in @list so that they can't block themselves. */
75
- ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp);
76
- if (ret < 0) {
77
- bdrv_abort_perm_update(to);
78
- goto out;
79
- }
80
+ found = g_hash_table_new(NULL, NULL);
81
82
- /* Now actually perform the change. We performed the permission check for
83
- * all elements of @list at once, so set the permissions all at once at the
84
- * very end. */
85
- for (p = list; p != NULL; p = p->next) {
86
- c = p->data;
87
+ refresh_list = bdrv_topological_dfs(refresh_list, found, to);
88
+ refresh_list = bdrv_topological_dfs(refresh_list, found, from);
89
90
- bdrv_ref(to);
91
- bdrv_replace_child_noperm(c, to);
92
- bdrv_unref(from);
93
+ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
94
+ if (ret < 0) {
95
+ goto out;
96
}
97
98
- bdrv_set_perm(to);
99
-
100
ret = 0;
101
102
out:
103
- g_slist_free(list);
104
+ tran_finalize(tran, ret);
105
+
106
bdrv_drained_end(from);
107
bdrv_unref(from);
108
109
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
110
index XXXXXXX..XXXXXXX 100644
111
--- a/tests/unit/test-bdrv-graph-mod.c
112
+++ b/tests/unit/test-bdrv-graph-mod.c
113
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
114
test_should_update_child);
115
g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
116
test_parallel_perm_update);
117
+ g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
118
+ test_parallel_exclusive_write);
119
120
if (debug) {
121
- g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
122
- test_parallel_exclusive_write);
123
g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
124
test_append_greedy_filter);
125
}
126
--
127
2.30.2
128
129
diff view generated by jsdifflib
New patch
1
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
3
Split out no-perm part of bdrv_root_attach_child() into separate
4
transaction action. bdrv_root_attach_child() now moves to new
5
permission update paradigm: first update graph relations then update
6
permissions.
7
8
qsd-jobs test output updated. Seems now permission update goes in
9
another order. Still, the test comment say that we only want to check
10
that command doesn't crash, and it's still so.
11
12
Error message is a bit misleading as it looks like job was added first.
13
But actually in new paradigm of graph update we can't distinguish such
14
things. We should update the error message, but let's not do it now.
15
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Message-Id: <20210428151804.439460-19-vsementsov@virtuozzo.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
20
block.c | 190 ++++++++++++++++++--------
21
tests/qemu-iotests/tests/qsd-jobs.out | 2 +-
22
2 files changed, 137 insertions(+), 55 deletions(-)
23
24
diff --git a/block.c b/block.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/block.c
27
+++ b/block.c
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
29
}
30
}
31
32
-/*
33
- * This function steals the reference to child_bs from the caller.
34
- * That reference is later dropped by bdrv_root_unref_child().
35
- *
36
- * On failure NULL is returned, errp is set and the reference to
37
- * child_bs is also dropped.
38
- *
39
- * The caller must hold the AioContext lock @child_bs, but not that of @ctx
40
- * (unless @child_bs is already in @ctx).
41
- */
42
-BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
43
- const char *child_name,
44
- const BdrvChildClass *child_class,
45
- BdrvChildRole child_role,
46
- uint64_t perm, uint64_t shared_perm,
47
- void *opaque, Error **errp)
48
+static void bdrv_remove_empty_child(BdrvChild *child)
49
{
50
- BdrvChild *child;
51
- Error *local_err = NULL;
52
- int ret;
53
- AioContext *ctx;
54
+ assert(!child->bs);
55
+ QLIST_SAFE_REMOVE(child, next);
56
+ g_free(child->name);
57
+ g_free(child);
58
+}
59
60
- ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
61
- if (ret < 0) {
62
- bdrv_abort_perm_update(child_bs);
63
- bdrv_unref(child_bs);
64
- return NULL;
65
+typedef struct BdrvAttachChildCommonState {
66
+ BdrvChild **child;
67
+ AioContext *old_parent_ctx;
68
+ AioContext *old_child_ctx;
69
+} BdrvAttachChildCommonState;
70
+
71
+static void bdrv_attach_child_common_abort(void *opaque)
72
+{
73
+ BdrvAttachChildCommonState *s = opaque;
74
+ BdrvChild *child = *s->child;
75
+ BlockDriverState *bs = child->bs;
76
+
77
+ bdrv_replace_child_noperm(child, NULL);
78
+
79
+ if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
80
+ bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
81
}
82
83
- child = g_new(BdrvChild, 1);
84
- *child = (BdrvChild) {
85
+ if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) {
86
+ GSList *ignore = g_slist_prepend(NULL, child);
87
+
88
+ child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore,
89
+ &error_abort);
90
+ g_slist_free(ignore);
91
+ ignore = g_slist_prepend(NULL, child);
92
+ child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore);
93
+
94
+ g_slist_free(ignore);
95
+ }
96
+
97
+ bdrv_unref(bs);
98
+ bdrv_remove_empty_child(child);
99
+ *s->child = NULL;
100
+}
101
+
102
+static TransactionActionDrv bdrv_attach_child_common_drv = {
103
+ .abort = bdrv_attach_child_common_abort,
104
+ .clean = g_free,
105
+};
106
+
107
+/*
108
+ * Common part of attaching bdrv child to bs or to blk or to job
109
+ */
110
+static int bdrv_attach_child_common(BlockDriverState *child_bs,
111
+ const char *child_name,
112
+ const BdrvChildClass *child_class,
113
+ BdrvChildRole child_role,
114
+ uint64_t perm, uint64_t shared_perm,
115
+ void *opaque, BdrvChild **child,
116
+ Transaction *tran, Error **errp)
117
+{
118
+ BdrvChild *new_child;
119
+ AioContext *parent_ctx;
120
+ AioContext *child_ctx = bdrv_get_aio_context(child_bs);
121
+
122
+ assert(child);
123
+ assert(*child == NULL);
124
+
125
+ new_child = g_new(BdrvChild, 1);
126
+ *new_child = (BdrvChild) {
127
.bs = NULL,
128
.name = g_strdup(child_name),
129
.klass = child_class,
130
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
131
.opaque = opaque,
132
};
133
134
- ctx = bdrv_child_get_parent_aio_context(child);
135
-
136
- /* If the AioContexts don't match, first try to move the subtree of
137
+ /*
138
+ * If the AioContexts don't match, first try to move the subtree of
139
* child_bs into the AioContext of the new parent. If this doesn't work,
140
- * try moving the parent into the AioContext of child_bs instead. */
141
- if (bdrv_get_aio_context(child_bs) != ctx) {
142
- ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
143
+ * try moving the parent into the AioContext of child_bs instead.
144
+ */
145
+ parent_ctx = bdrv_child_get_parent_aio_context(new_child);
146
+ if (child_ctx != parent_ctx) {
147
+ Error *local_err = NULL;
148
+ int ret = bdrv_try_set_aio_context(child_bs, parent_ctx, &local_err);
149
+
150
if (ret < 0 && child_class->can_set_aio_ctx) {
151
- GSList *ignore = g_slist_prepend(NULL, child);
152
- ctx = bdrv_get_aio_context(child_bs);
153
- if (child_class->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
154
+ GSList *ignore = g_slist_prepend(NULL, new_child);
155
+ if (child_class->can_set_aio_ctx(new_child, child_ctx, &ignore,
156
+ NULL))
157
+ {
158
error_free(local_err);
159
ret = 0;
160
g_slist_free(ignore);
161
- ignore = g_slist_prepend(NULL, child);
162
- child_class->set_aio_ctx(child, ctx, &ignore);
163
+ ignore = g_slist_prepend(NULL, new_child);
164
+ child_class->set_aio_ctx(new_child, child_ctx, &ignore);
165
}
166
g_slist_free(ignore);
167
}
168
+
169
if (ret < 0) {
170
error_propagate(errp, local_err);
171
- g_free(child);
172
- bdrv_abort_perm_update(child_bs);
173
- bdrv_unref(child_bs);
174
- return NULL;
175
+ bdrv_remove_empty_child(new_child);
176
+ return ret;
177
}
178
}
179
180
- /* This performs the matching bdrv_set_perm() for the above check. */
181
- bdrv_replace_child(child, child_bs);
182
+ bdrv_ref(child_bs);
183
+ bdrv_replace_child_noperm(new_child, child_bs);
184
185
+ *child = new_child;
186
+
187
+ BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
188
+ *s = (BdrvAttachChildCommonState) {
189
+ .child = child,
190
+ .old_parent_ctx = parent_ctx,
191
+ .old_child_ctx = child_ctx,
192
+ };
193
+ tran_add(tran, &bdrv_attach_child_common_drv, s);
194
+
195
+ return 0;
196
+}
197
+
198
+static void bdrv_detach_child(BdrvChild *child)
199
+{
200
+ bdrv_replace_child(child, NULL);
201
+ bdrv_remove_empty_child(child);
202
+}
203
+
204
+/*
205
+ * This function steals the reference to child_bs from the caller.
206
+ * That reference is later dropped by bdrv_root_unref_child().
207
+ *
208
+ * On failure NULL is returned, errp is set and the reference to
209
+ * child_bs is also dropped.
210
+ *
211
+ * The caller must hold the AioContext lock @child_bs, but not that of @ctx
212
+ * (unless @child_bs is already in @ctx).
213
+ */
214
+BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
215
+ const char *child_name,
216
+ const BdrvChildClass *child_class,
217
+ BdrvChildRole child_role,
218
+ uint64_t perm, uint64_t shared_perm,
219
+ void *opaque, Error **errp)
220
+{
221
+ int ret;
222
+ BdrvChild *child = NULL;
223
+ Transaction *tran = tran_new();
224
+
225
+ ret = bdrv_attach_child_common(child_bs, child_name, child_class,
226
+ child_role, perm, shared_perm, opaque,
227
+ &child, tran, errp);
228
+ if (ret < 0) {
229
+ bdrv_unref(child_bs);
230
+ return NULL;
231
+ }
232
+
233
+ ret = bdrv_refresh_perms(child_bs, errp);
234
+ tran_finalize(tran, ret);
235
+
236
+ bdrv_unref(child_bs);
237
return child;
238
}
239
240
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
241
return child;
242
}
243
244
-static void bdrv_detach_child(BdrvChild *child)
245
-{
246
- QLIST_SAFE_REMOVE(child, next);
247
-
248
- bdrv_replace_child(child, NULL);
249
-
250
- g_free(child->name);
251
- g_free(child);
252
-}
253
-
254
/* Callers must ensure that child->frozen is false. */
255
void bdrv_root_unref_child(BdrvChild *child)
256
{
257
diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out
258
index XXXXXXX..XXXXXXX 100644
259
--- a/tests/qemu-iotests/tests/qsd-jobs.out
260
+++ b/tests/qemu-iotests/tests/qsd-jobs.out
261
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
262
{"return": {}}
263
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
264
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
265
-{"error": {"class": "GenericError", "desc": "Conflicts with use by a block device as 'root', which uses 'write' on fmt_base"}}
266
+{"error": {"class": "GenericError", "desc": "Conflicts with use by stream job 'job0' as 'intermediate node', which does not allow 'write' on fmt_base"}}
267
{"return": {}}
268
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}}
269
*** done
270
--
271
2.30.2
272
273
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
3
Split no-perm part of bdrv_attach_child as separate transaction action.
4
It will be used in later commits.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-20-vsementsov@virtuozzo.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
block/mirror.c | 8 ++++++++
11
block.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++-----------
7
1 file changed, 8 insertions(+)
12
1 file changed, 58 insertions(+), 13 deletions(-)
8
13
9
diff --git a/block/mirror.c b/block/mirror.c
14
diff --git a/block.c b/block.c
10
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
11
--- a/block/mirror.c
16
--- a/block.c
12
+++ b/block/mirror.c
17
+++ b/block.c
13
@@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job)
18
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
14
}
19
15
s->prepared = true;
20
static void bdrv_replace_child_noperm(BdrvChild *child,
16
21
BlockDriverState *new_bs);
17
+ if (bdrv_chain_contains(src, target_bs)) {
22
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
18
+ bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
23
+ BlockDriverState *child_bs,
24
+ const char *child_name,
25
+ const BdrvChildClass *child_class,
26
+ BdrvChildRole child_role,
27
+ BdrvChild **child,
28
+ Transaction *tran,
29
+ Error **errp);
30
31
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
32
*queue, Error **errp);
33
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
34
return 0;
35
}
36
37
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
38
+ BlockDriverState *child_bs,
39
+ const char *child_name,
40
+ const BdrvChildClass *child_class,
41
+ BdrvChildRole child_role,
42
+ BdrvChild **child,
43
+ Transaction *tran,
44
+ Error **errp)
45
+{
46
+ int ret;
47
+ uint64_t perm, shared_perm;
48
+
49
+ assert(parent_bs->drv);
50
+
51
+ bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
52
+ bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
53
+ perm, shared_perm, &perm, &shared_perm);
54
+
55
+ ret = bdrv_attach_child_common(child_bs, child_name, child_class,
56
+ child_role, perm, shared_perm, parent_bs,
57
+ child, tran, errp);
58
+ if (ret < 0) {
59
+ return ret;
19
+ }
60
+ }
20
+
61
+
21
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
62
+ QLIST_INSERT_HEAD(&parent_bs->children, *child, next);
22
63
+ /*
23
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
64
+ * child is removed in bdrv_attach_child_common_abort(), so don't care to
24
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
65
+ * abort this change separately.
25
goto fail;
66
+ */
26
}
27
}
28
+
67
+
29
+ if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
68
+ return 0;
30
+ goto fail;
69
+}
31
+ }
70
+
71
static void bdrv_detach_child(BdrvChild *child)
72
{
73
bdrv_replace_child(child, NULL);
74
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
75
BdrvChildRole child_role,
76
Error **errp)
77
{
78
- BdrvChild *child;
79
- uint64_t perm, shared_perm;
80
-
81
- bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
82
+ int ret;
83
+ BdrvChild *child = NULL;
84
+ Transaction *tran = tran_new();
85
86
- assert(parent_bs->drv);
87
- bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
88
- perm, shared_perm, &perm, &shared_perm);
89
+ ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class,
90
+ child_role, &child, tran, errp);
91
+ if (ret < 0) {
92
+ goto out;
93
+ }
94
95
- child = bdrv_root_attach_child(child_bs, child_name, child_class,
96
- child_role, perm, shared_perm, parent_bs,
97
- errp);
98
- if (child == NULL) {
99
- return NULL;
100
+ ret = bdrv_refresh_perms(parent_bs, errp);
101
+ if (ret < 0) {
102
+ goto out;
32
}
103
}
33
104
34
QTAILQ_INIT(&s->ops_in_flight);
105
- QLIST_INSERT_HEAD(&parent_bs->children, child, next);
106
+out:
107
+ tran_finalize(tran, ret);
108
+
109
+ bdrv_unref(child_bs);
110
+
111
return child;
112
}
113
35
--
114
--
36
2.20.1
115
2.30.2
37
116
38
117
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
bdrv_reopen_prepare() receives a BDRVReopenState with (among other
3
Split part of bdrv_replace_node_common() to be used separately.
4
things) a new set of options to be applied to that BlockDriverState.
5
4
6
If an option is missing then it means that we want to reset it to its
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
default value rather than keep the previous one. This way the state
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
of the block device after being reopened is comparable to that of a
7
Message-Id: <20210428151804.439460-21-vsementsov@virtuozzo.com>
9
device added with "blockdev-add" using the same set of options.
10
11
Not all options from all drivers can be changed this way, however.
12
If the user attempts to reset an immutable option to its default value
13
using this method then we must forbid it.
14
15
This new function takes a BlockDriverState and a new set of options
16
and checks if there's any option that was previously set but is
17
missing from the new set of options.
18
19
If the option is present in both sets we don't need to check that they
20
have the same value. The loop at the end of bdrv_reopen_prepare()
21
already takes care of that.
22
23
Signed-off-by: Alberto Garcia <berto@igalia.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
9
---
26
block.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10
block.c | 50 +++++++++++++++++++++++++++++++-------------------
27
1 file changed, 58 insertions(+)
11
1 file changed, 31 insertions(+), 19 deletions(-)
28
12
29
diff --git a/block.c b/block.c
13
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
15
--- a/block.c
32
+++ b/block.c
16
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
17
@@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
34
NULL, errp);
18
return ret;
35
}
19
}
36
20
37
+/* Return true if the NULL-terminated @list contains @str */
21
+static int bdrv_replace_node_noperm(BlockDriverState *from,
38
+static bool is_str_in_list(const char *str, const char *const *list)
22
+ BlockDriverState *to,
23
+ bool auto_skip, Transaction *tran,
24
+ Error **errp)
39
+{
25
+{
40
+ if (str && list) {
26
+ BdrvChild *c, *next;
41
+ int i;
27
+
42
+ for (i = 0; list[i] != NULL; i++) {
28
+ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
43
+ if (!strcmp(str, list[i])) {
29
+ assert(c->bs == from);
44
+ return true;
30
+ if (!should_update_child(c, to)) {
31
+ if (auto_skip) {
32
+ continue;
45
+ }
33
+ }
46
+ }
34
+ error_setg(errp, "Should not change '%s' link to '%s'",
47
+ }
35
+ c->name, from->node_name);
48
+ return false;
49
+}
50
+
51
+/*
52
+ * Check that every option set in @bs->options is also set in
53
+ * @new_opts.
54
+ *
55
+ * Options listed in the common_options list and in
56
+ * @bs->drv->mutable_opts are skipped.
57
+ *
58
+ * Return 0 on success, otherwise return -EINVAL and set @errp.
59
+ */
60
+static int bdrv_reset_options_allowed(BlockDriverState *bs,
61
+ const QDict *new_opts, Error **errp)
62
+{
63
+ const QDictEntry *e;
64
+ /* These options are common to all block drivers and are handled
65
+ * in bdrv_reopen_prepare() so they can be left out of @new_opts */
66
+ const char *const common_options[] = {
67
+ "node-name", "discard", "cache.direct", "cache.no-flush",
68
+ "read-only", "auto-read-only", "detect-zeroes", NULL
69
+ };
70
+
71
+ for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
72
+ if (!qdict_haskey(new_opts, e->key) &&
73
+ !is_str_in_list(e->key, common_options) &&
74
+ !is_str_in_list(e->key, bs->drv->mutable_opts)) {
75
+ error_setg(errp, "Option '%s' cannot be reset "
76
+ "to its default value", e->key);
77
+ return -EINVAL;
36
+ return -EINVAL;
78
+ }
37
+ }
38
+ if (c->frozen) {
39
+ error_setg(errp, "Cannot change '%s' link to '%s'",
40
+ c->name, from->node_name);
41
+ return -EPERM;
42
+ }
43
+ bdrv_replace_child_safe(c, to, tran);
79
+ }
44
+ }
80
+
45
+
81
+ return 0;
46
+ return 0;
82
+}
47
+}
83
+
48
+
84
/*
49
/*
85
* Returns true if @child can be reached recursively from @bs
50
* With auto_skip=true bdrv_replace_node_common skips updating from parents
86
*/
51
* if it creates a parent-child relation loop or if parent is block-job.
87
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
52
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
53
BlockDriverState *to,
54
bool auto_skip, Error **errp)
55
{
56
- BdrvChild *c, *next;
57
Transaction *tran = tran_new();
58
g_autoptr(GHashTable) found = NULL;
59
g_autoptr(GSList) refresh_list = NULL;
60
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
61
* permissions based on new graph. If we fail, we'll roll-back the
62
* replacement.
63
*/
64
- QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
65
- assert(c->bs == from);
66
- if (!should_update_child(c, to)) {
67
- if (auto_skip) {
68
- continue;
69
- }
70
- ret = -EINVAL;
71
- error_setg(errp, "Should not change '%s' link to '%s'",
72
- c->name, from->node_name);
73
- goto out;
74
- }
75
- if (c->frozen) {
76
- ret = -EPERM;
77
- error_setg(errp, "Cannot change '%s' link to '%s'",
78
- c->name, from->node_name);
79
- goto out;
80
- }
81
- bdrv_replace_child_safe(c, to, tran);
82
+ ret = bdrv_replace_node_noperm(from, to, auto_skip, tran, errp);
83
+ if (ret < 0) {
84
+ goto out;
88
}
85
}
89
86
90
if (drv->bdrv_reopen_prepare) {
87
found = g_hash_table_new(NULL, NULL);
91
+ /*
92
+ * If a driver-specific option is missing, it means that we
93
+ * should reset it to its default value.
94
+ * But not all options allow that, so we need to check it first.
95
+ */
96
+ ret = bdrv_reset_options_allowed(reopen_state->bs,
97
+ reopen_state->options, errp);
98
+ if (ret) {
99
+ goto error;
100
+ }
101
+
102
ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err);
103
if (ret) {
104
if (local_err != NULL) {
105
--
88
--
106
2.20.1
89
2.30.2
107
90
108
91
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
bdrv_append is not very good for inserting filters: it does extra
4
permission update as part of bdrv_set_backing_hd(). During this update
5
filter may conflict with other parents of top_bs.
6
7
Instead, let's first do all graph modifications and after it update
8
permissions.
9
10
append-greedy-filter test-case in test-bdrv-graph-mod is now works, so
11
move it out of debug option.
12
13
Note: bdrv_append() is still only works for backing-child based
14
filters. It's something to improve later.
15
16
Note2: we use the fact that bdrv_append() is used to append new nodes,
17
without backing child, so we don't need frozen check and inherits_from
18
logic from bdrv_set_backing_hd().
19
20
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
21
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
22
Message-Id: <20210428151804.439460-22-vsementsov@virtuozzo.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
---
25
block.c | 27 ++++++++++++++++++++-------
26
tests/unit/test-bdrv-graph-mod.c | 17 ++---------------
27
2 files changed, 22 insertions(+), 22 deletions(-)
28
29
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
32
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
34
* This will modify the BlockDriverState fields, and swap contents
35
* between bs_new and bs_top. Both bs_new and bs_top are modified.
36
*
37
- * bs_new must not be attached to a BlockBackend.
38
+ * bs_new must not be attached to a BlockBackend and must not have backing
39
+ * child.
40
*
41
* This function does not create any image files.
42
*/
43
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
44
Error **errp)
45
{
46
- int ret = bdrv_set_backing_hd(bs_new, bs_top, errp);
47
+ int ret;
48
+ Transaction *tran = tran_new();
49
+
50
+ assert(!bs_new->backing);
51
+
52
+ ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing",
53
+ &child_of_bds, bdrv_backing_role(bs_new),
54
+ &bs_new->backing, tran, errp);
55
if (ret < 0) {
56
- return ret;
57
+ goto out;
58
}
59
60
- ret = bdrv_replace_node(bs_top, bs_new, errp);
61
+ ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp);
62
if (ret < 0) {
63
- bdrv_set_backing_hd(bs_new, NULL, &error_abort);
64
- return ret;
65
+ goto out;
66
}
67
68
- return 0;
69
+ ret = bdrv_refresh_perms(bs_new, errp);
70
+out:
71
+ tran_finalize(tran, ret);
72
+
73
+ bdrv_refresh_limits(bs_top, NULL);
74
+
75
+ return ret;
76
}
77
78
static void bdrv_delete(BlockDriverState *bs)
79
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/tests/unit/test-bdrv-graph-mod.c
82
+++ b/tests/unit/test-bdrv-graph-mod.c
83
@@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void)
84
85
int main(int argc, char *argv[])
86
{
87
- int i;
88
- bool debug = false;
89
-
90
- for (i = 1; i < argc; i++) {
91
- if (!strcmp(argv[i], "-d")) {
92
- debug = true;
93
- break;
94
- }
95
- }
96
-
97
bdrv_init();
98
qemu_init_main_loop(&error_abort);
99
100
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
101
test_parallel_perm_update);
102
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
103
test_parallel_exclusive_write);
104
-
105
- if (debug) {
106
- g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
107
- test_append_greedy_filter);
108
- }
109
+ g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
110
+ test_append_greedy_filter);
111
112
return g_test_run();
113
}
114
--
115
2.30.2
116
117
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This command allows reopening an arbitrary BlockDriverState with a
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
new set of options. Some options (e.g node-name) cannot be changed
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
and some block drivers don't allow reopening, but otherwise this
5
Message-Id: <20210428151804.439460-23-vsementsov@virtuozzo.com>
6
command is modelled after 'blockdev-add' and the state of the reopened
7
BlockDriverState should generally be the same as if it had just been
8
added by 'blockdev-add' with the same set of options.
9
10
One notable exception is the 'backing' option: 'x-blockdev-reopen'
11
requires that it is always present unless the BlockDriverState in
12
question doesn't have a current or default backing file.
13
14
This command allows reconfiguring the graph by using the appropriate
15
options to change the children of a node. At the moment it's possible
16
to change a backing file by setting the 'backing' option to the name
17
of the new node, but it should also be possible to add a similar
18
functionality to other block drivers (e.g. Quorum, blkverify).
19
20
Although the API is unlikely to change, this command is marked
21
experimental for the time being so there's room to see if the
22
semantics need changes.
23
24
Signed-off-by: Alberto Garcia <berto@igalia.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
---
7
---
27
qapi/block-core.json | 42 +++++++++++++++++++++++++++++++++++++++
8
block.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
28
blockdev.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
9
1 file changed, 82 insertions(+), 2 deletions(-)
29
2 files changed, 89 insertions(+)
30
10
31
diff --git a/qapi/block-core.json b/qapi/block-core.json
11
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
33
--- a/qapi/block-core.json
13
--- a/block.c
34
+++ b/qapi/block-core.json
14
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
36
##
16
}
37
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
17
}
38
18
39
+##
19
+static void bdrv_child_free(void *opaque)
40
+# @x-blockdev-reopen:
20
+{
41
+#
21
+ BdrvChild *c = opaque;
42
+# Reopens a block device using the given set of options. Any option
43
+# not specified will be reset to its default value regardless of its
44
+# previous status. If an option cannot be changed or a particular
45
+# driver does not support reopening then the command will return an
46
+# error.
47
+#
48
+# The top-level @node-name option (from BlockdevOptions) must be
49
+# specified and is used to select the block device to be reopened.
50
+# Other @node-name options must be either omitted or set to the
51
+# current name of the appropriate node. This command won't change any
52
+# node name and any attempt to do it will result in an error.
53
+#
54
+# In the case of options that refer to child nodes, the behavior of
55
+# this command depends on the value:
56
+#
57
+# 1) A set of options (BlockdevOptions): the child is reopened with
58
+# the specified set of options.
59
+#
60
+# 2) A reference to the current child: the child is reopened using
61
+# its existing set of options.
62
+#
63
+# 3) A reference to a different node: the current child is replaced
64
+# with the specified one.
65
+#
66
+# 4) NULL: the current child (if any) is detached.
67
+#
68
+# Options (1) and (2) are supported in all cases, but at the moment
69
+# only @backing allows replacing or detaching an existing child.
70
+#
71
+# Unlike with blockdev-add, the @backing option must always be present
72
+# unless the node being reopened does not have a backing file and its
73
+# image does not have a default backing file name as part of its
74
+# metadata.
75
+#
76
+# Since: 4.0
77
+##
78
+{ 'command': 'x-blockdev-reopen',
79
+ 'data': 'BlockdevOptions', 'boxed': true }
80
+
22
+
81
##
23
+ g_free(c->name);
82
# @blockdev-del:
24
+ g_free(c);
83
#
25
+}
84
diff --git a/blockdev.c b/blockdev.c
26
+
85
index XXXXXXX..XXXXXXX 100644
27
static void bdrv_remove_empty_child(BdrvChild *child)
86
--- a/blockdev.c
28
{
87
+++ b/blockdev.c
29
assert(!child->bs);
88
@@ -XXX,XX +XXX,XX @@ fail:
30
QLIST_SAFE_REMOVE(child, next);
89
visit_free(v);
31
- g_free(child->name);
32
- g_free(child);
33
+ bdrv_child_free(child);
90
}
34
}
91
35
92
+void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
36
typedef struct BdrvAttachChildCommonState {
37
@@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
38
return ret;
39
}
40
41
+typedef struct BdrvRemoveFilterOrCowChild {
42
+ BdrvChild *child;
43
+ bool is_backing;
44
+} BdrvRemoveFilterOrCowChild;
45
+
46
+static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
93
+{
47
+{
94
+ BlockDriverState *bs;
48
+ BdrvRemoveFilterOrCowChild *s = opaque;
95
+ AioContext *ctx;
49
+ BlockDriverState *parent_bs = s->child->opaque;
96
+ QObject *obj;
97
+ Visitor *v = qobject_output_visitor_new(&obj);
98
+ Error *local_err = NULL;
99
+ BlockReopenQueue *queue;
100
+ QDict *qdict;
101
+
50
+
102
+ /* Check for the selected node name */
51
+ QLIST_INSERT_HEAD(&parent_bs->children, s->child, next);
103
+ if (!options->has_node_name) {
52
+ if (s->is_backing) {
104
+ error_setg(errp, "Node name not specified");
53
+ parent_bs->backing = s->child;
105
+ goto fail;
54
+ } else {
55
+ parent_bs->file = s->child;
106
+ }
56
+ }
107
+
57
+
108
+ bs = bdrv_find_node(options->node_name);
58
+ /*
109
+ if (!bs) {
59
+ * We don't have to restore child->bs here to undo bdrv_replace_child()
110
+ error_setg(errp, "Cannot find node named '%s'", options->node_name);
60
+ * because that function is transactionable and it registered own completion
111
+ goto fail;
61
+ * entries in @tran, so .abort() for bdrv_replace_child_safe() will be
62
+ * called automatically.
63
+ */
64
+}
65
+
66
+static void bdrv_remove_filter_or_cow_child_commit(void *opaque)
67
+{
68
+ BdrvRemoveFilterOrCowChild *s = opaque;
69
+
70
+ bdrv_child_free(s->child);
71
+}
72
+
73
+static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
74
+ .abort = bdrv_remove_filter_or_cow_child_abort,
75
+ .commit = bdrv_remove_filter_or_cow_child_commit,
76
+ .clean = g_free,
77
+};
78
+
79
+/*
80
+ * A function to remove backing-chain child of @bs if exists: cow child for
81
+ * format nodes (always .backing) and filter child for filters (may be .file or
82
+ * .backing)
83
+ */
84
+__attribute__((unused))
85
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
86
+ Transaction *tran)
87
+{
88
+ BdrvRemoveFilterOrCowChild *s;
89
+ BdrvChild *child = bdrv_filter_or_cow_child(bs);
90
+
91
+ if (!child) {
92
+ return;
112
+ }
93
+ }
113
+
94
+
114
+ /* Put all options in a QDict and flatten it */
95
+ if (child->bs) {
115
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
96
+ bdrv_replace_child_safe(child, NULL, tran);
116
+ if (local_err) {
117
+ error_propagate(errp, local_err);
118
+ goto fail;
119
+ }
97
+ }
120
+
98
+
121
+ visit_complete(v, &obj);
99
+ s = g_new(BdrvRemoveFilterOrCowChild, 1);
122
+ qdict = qobject_to(QDict, obj);
100
+ *s = (BdrvRemoveFilterOrCowChild) {
101
+ .child = child,
102
+ .is_backing = (child == bs->backing),
103
+ };
104
+ tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
123
+
105
+
124
+ qdict_flatten(qdict);
106
+ QLIST_SAFE_REMOVE(child, next);
125
+
107
+ if (s->is_backing) {
126
+ /* Perform the reopen operation */
108
+ bs->backing = NULL;
127
+ ctx = bdrv_get_aio_context(bs);
109
+ } else {
128
+ aio_context_acquire(ctx);
110
+ bs->file = NULL;
129
+ bdrv_subtree_drained_begin(bs);
111
+ }
130
+ queue = bdrv_reopen_queue(NULL, bs, qdict, false);
131
+ bdrv_reopen_multiple(queue, errp);
132
+ bdrv_subtree_drained_end(bs);
133
+ aio_context_release(ctx);
134
+
135
+fail:
136
+ visit_free(v);
137
+}
112
+}
138
+
113
+
139
void qmp_blockdev_del(const char *node_name, Error **errp)
114
static int bdrv_replace_node_noperm(BlockDriverState *from,
140
{
115
BlockDriverState *to,
141
AioContext *aio_context;
116
bool auto_skip, Transaction *tran,
142
--
117
--
143
2.20.1
118
2.30.2
144
119
145
120
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Children in QMP are specified with BlockdevRef / BlockdevRefOrNull,
3
Using bdrv_replace_node() for removing filter is not good enough: it
4
which can contain a set of child options, a child reference, or
4
keeps child reference of the filter, which may conflict with original
5
NULL. In optional attributes like "backing" it can also be missing.
5
top node during permission update.
6
6
7
Only the first case (set of child options) is being handled properly
7
Instead let's create new interface, which will do all graph
8
by bdrv_reopen_queue(). This patch deals with all the others.
8
modifications first and then update permissions.
9
9
10
Here's how these cases should be handled when bdrv_reopen_queue() is
10
Let's modify bdrv_replace_node_common(), allowing it additionally drop
11
deciding what to do with each child of a BlockDriverState:
11
backing chain child link pointing to new node. This is quite
12
appropriate for bdrv_drop_intermediate() and makes possible to add
13
new bdrv_drop_filter() as a simple wrapper.
12
14
13
1) Set of child options: if the child was implicitly created (i.e
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
inherits_from points to the parent) then the options are removed
16
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
from the parent's options QDict and are passed to the child with
17
Message-Id: <20210428151804.439460-24-vsementsov@virtuozzo.com>
16
a recursive bdrv_reopen_queue() call. This case was already
17
working fine.
18
19
2) Child reference: there's two possibilites here.
20
21
2a) Reference to the current child: if the child was implicitly
22
created then it is put in the reopen queue, keeping its
23
current set of options (since this was a child reference
24
there was no way to specify a different set of options).
25
If the child is not implicit then it keeps its current set
26
of options but it is not reopened (and therefore does not
27
inherit any new option from the parent).
28
29
2b) Reference to a different BDS: the current child is not put
30
in the reopen queue at all. Passing a reference to a
31
different BDS can be used to replace a child, although at
32
the moment no driver implements this, so it results in an
33
error. In any case, the current child is not going to be
34
reopened (and might in fact disappear if it's replaced)
35
36
3) NULL: This is similar to (2b). Although no driver allows this
37
yet it can be used to detach the current child so it should not
38
be put in the reopen queue.
39
40
4) Missing option: at the moment "backing" is the only case where
41
this can happen. With "blockdev-add", leaving "backing" out
42
means that the default backing file is opened. We don't want to
43
open a new image during reopen, so we require that "backing" is
44
always present. We'll relax this requirement a bit in the next
45
patch. If keep_old_opts is true and "backing" is missing then
46
this behaves like 2a (the current child is reopened).
47
48
Signed-off-by: Alberto Garcia <berto@igalia.com>
49
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
---
19
---
51
include/block/block.h | 1 +
20
include/block/block.h | 1 +
52
block.c | 52 +++++++++++++++++++++++++++++++++++++------
21
block.c | 43 +++++++++++++++++++++++++++++++++++++++----
53
2 files changed, 46 insertions(+), 7 deletions(-)
22
2 files changed, 40 insertions(+), 4 deletions(-)
54
23
55
diff --git a/include/block/block.h b/include/block/block.h
24
diff --git a/include/block/block.h b/include/block/block.h
56
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
57
--- a/include/block/block.h
26
--- a/include/block/block.h
58
+++ b/include/block/block.h
27
+++ b/include/block/block.h
59
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
28
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
60
BlockDriverState *bs;
29
Error **errp);
61
int flags;
30
BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
62
BlockdevDetectZeroesOptions detect_zeroes;
31
int flags, Error **errp);
63
+ bool backing_missing;
32
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp);
64
uint64_t perm, shared_perm;
33
65
QDict *options;
34
int bdrv_parse_aio(const char *mode, int *flags);
66
QDict *explicit_options;
35
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
67
diff --git a/block.c b/block.c
36
diff --git a/block.c b/block.c
68
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100644
69
--- a/block.c
38
--- a/block.c
70
+++ b/block.c
39
+++ b/block.c
71
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
40
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
72
bs_entry->state.perm = UINT64_MAX;
41
* format nodes (always .backing) and filter child for filters (may be .file or
73
bs_entry->state.shared_perm = 0;
42
* .backing)
74
43
*/
75
+ /*
44
-__attribute__((unused))
76
+ * If keep_old_opts is false then it means that unspecified
45
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
77
+ * options must be reset to their original value. We don't allow
46
Transaction *tran)
78
+ * resetting 'backing' but we need to know if the option is
47
{
79
+ * missing in order to decide if we have to return an error.
48
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
80
+ */
49
*
81
+ if (!keep_old_opts) {
50
* With auto_skip=false the error is returned if from has a parent which should
82
+ bs_entry->state.backing_missing =
51
* not be updated.
83
+ !qdict_haskey(options, "backing") &&
52
+ *
84
+ !qdict_haskey(options, "backing.driver");
53
+ * With @detach_subchain=true @to must be in a backing chain of @from. In this
54
+ * case backing link of the cow-parent of @to is removed.
55
*/
56
static int bdrv_replace_node_common(BlockDriverState *from,
57
BlockDriverState *to,
58
- bool auto_skip, Error **errp)
59
+ bool auto_skip, bool detach_subchain,
60
+ Error **errp)
61
{
62
Transaction *tran = tran_new();
63
g_autoptr(GHashTable) found = NULL;
64
g_autoptr(GSList) refresh_list = NULL;
65
+ BlockDriverState *to_cow_parent;
66
int ret;
67
68
+ if (detach_subchain) {
69
+ assert(bdrv_chain_contains(from, to));
70
+ assert(from != to);
71
+ for (to_cow_parent = from;
72
+ bdrv_filter_or_cow_bs(to_cow_parent) != to;
73
+ to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent))
74
+ {
75
+ ;
76
+ }
85
+ }
77
+ }
86
+
78
+
87
QLIST_FOREACH(child, &bs->children, next) {
79
/* Make sure that @from doesn't go away until we have successfully attached
88
- QDict *new_child_options;
80
* all of its parents to @to. */
89
- char *child_key_dot;
81
bdrv_ref(from);
90
+ QDict *new_child_options = NULL;
82
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
91
+ bool child_keep_old = keep_old_opts;
83
goto out;
92
93
/* reopen can only change the options of block devices that were
94
* implicitly created and inherited options. For other (referenced)
95
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
96
continue;
97
}
98
99
- child_key_dot = g_strdup_printf("%s.", child->name);
100
- qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
101
- qdict_extract_subqdict(options, &new_child_options, child_key_dot);
102
- g_free(child_key_dot);
103
+ /* Check if the options contain a child reference */
104
+ if (qdict_haskey(options, child->name)) {
105
+ const char *childref = qdict_get_try_str(options, child->name);
106
+ /*
107
+ * The current child must not be reopened if the child
108
+ * reference is null or points to a different node.
109
+ */
110
+ if (g_strcmp0(childref, child->bs->node_name)) {
111
+ continue;
112
+ }
113
+ /*
114
+ * If the child reference points to the current child then
115
+ * reopen it with its existing set of options (note that
116
+ * it can still inherit new options from the parent).
117
+ */
118
+ child_keep_old = true;
119
+ } else {
120
+ /* Extract child options ("child-name.*") */
121
+ char *child_key_dot = g_strdup_printf("%s.", child->name);
122
+ qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
123
+ qdict_extract_subqdict(options, &new_child_options, child_key_dot);
124
+ g_free(child_key_dot);
125
+ }
126
127
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options,
128
- child->role, options, flags, keep_old_opts);
129
+ child->role, options, flags, child_keep_old);
130
}
84
}
131
85
132
return bs_queue;
86
+ if (detach_subchain) {
133
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
87
+ bdrv_remove_filter_or_cow_child(to_cow_parent, tran);
134
135
drv_prepared = true;
136
137
+ if (drv->supports_backing && reopen_state->backing_missing) {
138
+ error_setg(errp, "backing is missing for '%s'",
139
+ reopen_state->bs->node_name);
140
+ ret = -EINVAL;
141
+ goto error;
142
+ }
88
+ }
143
+
89
+
144
/* Options that are not handled are only okay if they are unchanged
90
found = g_hash_table_new(NULL, NULL);
145
* compared to the old state. It is expected that some options are only
91
146
* used for the initial open, but not reopen (e.g. filename) */
92
refresh_list = bdrv_topological_dfs(refresh_list, found, to);
93
@@ -XXX,XX +XXX,XX @@ out:
94
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
95
Error **errp)
96
{
97
- return bdrv_replace_node_common(from, to, true, errp);
98
+ return bdrv_replace_node_common(from, to, true, false, errp);
99
+}
100
+
101
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
102
+{
103
+ return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true,
104
+ errp);
105
}
106
107
/*
108
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
109
updated_children = g_slist_prepend(updated_children, c);
110
}
111
112
- bdrv_replace_node_common(top, base, false, &local_err);
113
+ /*
114
+ * It seems correct to pass detach_subchain=true here, but it triggers
115
+ * one more yet not fixed bug, when due to nested aio_poll loop we switch to
116
+ * another drained section, which modify the graph (for example, removing
117
+ * the child, which we keep in updated_children list). So, it's a TODO.
118
+ *
119
+ * Note, bug triggered if pass detach_subchain=true here and run
120
+ * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash.
121
+ * That's a FIXME.
122
+ */
123
+ bdrv_replace_node_common(top, base, false, false, &local_err);
124
if (local_err) {
125
error_report_err(local_err);
126
goto exit;
147
--
127
--
148
2.20.1
128
2.30.2
149
129
150
130
diff view generated by jsdifflib
1
From: Niels de Vos <ndevos@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The glfs_*_async() functions do a callback once finished. This callback
3
We don't need this workaround anymore: bdrv_append is already smart
4
has changed its arguments, pre- and post-stat structures have been
4
enough and we can use new bdrv_drop_filter().
5
added. This makes it possible to improve caching, which is useful for
6
Samba and NFS-Ganesha, but not so much for QEMU. Gluster 6 is the first
7
release that includes these new arguments.
8
5
9
With an additional detection in ./configure, the new arguments can
6
This commit efficiently reverts also recent 705dde27c6c53b73, which
10
conditionally get included in the glfs_io_cbk handler.
7
checked .active on io path. Still it said that the problem should be
8
theoretical. And the logic of filter removement is changed anyway.
11
9
12
Signed-off-by: Niels de Vos <ndevos@redhat.com>
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20210428151804.439460-25-vsementsov@virtuozzo.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
14
---
15
configure | 24 ++++++++++++++++++++++++
15
block/backup-top.c | 47 +-------------------------------------
16
block/gluster.c | 6 +++++-
16
tests/qemu-iotests/283.out | 2 +-
17
2 files changed, 29 insertions(+), 1 deletion(-)
17
2 files changed, 2 insertions(+), 47 deletions(-)
18
18
19
diff --git a/configure b/configure
19
diff --git a/block/backup-top.c b/block/backup-top.c
20
index XXXXXXX..XXXXXXX 100755
21
--- a/configure
22
+++ b/configure
23
@@ -XXX,XX +XXX,XX @@ glusterfs_discard="no"
24
glusterfs_fallocate="no"
25
glusterfs_zerofill="no"
26
glusterfs_ftruncate_has_stat="no"
27
+glusterfs_iocb_has_stat="no"
28
gtk=""
29
gtk_gl="no"
30
tls_priority="NORMAL"
31
@@ -XXX,XX +XXX,XX @@ EOF
32
if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
33
glusterfs_ftruncate_has_stat="yes"
34
fi
35
+ cat > $TMPC << EOF
36
+#include <glusterfs/api/glfs.h>
37
+
38
+/* new glfs_io_cbk() passes two additional glfs_stat structs */
39
+static void
40
+glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data)
41
+{}
42
+
43
+int
44
+main(void)
45
+{
46
+    glfs_io_cbk iocb = &glusterfs_iocb;
47
+    iocb(NULL, 0 , NULL, NULL, NULL);
48
+    return 0;
49
+}
50
+EOF
51
+ if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
52
+ glusterfs_iocb_has_stat="yes"
53
+ fi
54
else
55
if test "$glusterfs" = "yes" ; then
56
feature_not_found "GlusterFS backend support" \
57
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs_ftruncate_has_stat" = "yes" ; then
58
echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak
59
fi
60
61
+if test "$glusterfs_iocb_has_stat" = "yes" ; then
62
+ echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
63
+fi
64
+
65
if test "$libssh2" = "yes" ; then
66
echo "CONFIG_LIBSSH2=m" >> $config_host_mak
67
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
68
diff --git a/block/gluster.c b/block/gluster.c
69
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
70
--- a/block/gluster.c
21
--- a/block/backup-top.c
71
+++ b/block/gluster.c
22
+++ b/block/backup-top.c
72
@@ -XXX,XX +XXX,XX @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
23
@@ -XXX,XX +XXX,XX @@
73
/*
24
typedef struct BDRVBackupTopState {
74
* AIO callback routine called from GlusterFS thread.
25
BlockCopyState *bcs;
75
*/
26
BdrvChild *target;
76
-static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
27
- bool active;
77
+static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
28
int64_t cluster_size;
78
+#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
29
} BDRVBackupTopState;
79
+ struct glfs_stat *pre, struct glfs_stat *post,
30
80
+#endif
31
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_co_preadv(
81
+ void *arg)
32
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
33
QEMUIOVector *qiov, int flags)
82
{
34
{
83
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
35
- BDRVBackupTopState *s = bs->opaque;
36
-
37
- if (!s->active) {
38
- return -EIO;
39
- }
40
-
41
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
42
}
43
44
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
45
BDRVBackupTopState *s = bs->opaque;
46
uint64_t off, end;
47
48
- if (!s->active) {
49
- return -EIO;
50
- }
51
-
52
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
53
return 0;
54
}
55
@@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
56
uint64_t perm, uint64_t shared,
57
uint64_t *nperm, uint64_t *nshared)
58
{
59
- BDRVBackupTopState *s = bs->opaque;
60
-
61
- if (!s->active) {
62
- /*
63
- * The filter node may be in process of bdrv_append(), which firstly do
64
- * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
65
- * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
66
- * let's require nothing during bdrv_append() and refresh permissions
67
- * after it (see bdrv_backup_top_append()).
68
- */
69
- *nperm = 0;
70
- *nshared = BLK_PERM_ALL;
71
- return;
72
- }
73
-
74
if (!(role & BDRV_CHILD_FILTERED)) {
75
/*
76
* Target child
77
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
78
}
79
appended = true;
80
81
- /*
82
- * bdrv_append() finished successfully, now we can require permissions
83
- * we want.
84
- */
85
- state->active = true;
86
- ret = bdrv_child_refresh_perms(top, top->backing, errp);
87
- if (ret < 0) {
88
- error_prepend(errp, "Cannot set permissions for backup-top filter: ");
89
- goto fail;
90
- }
91
-
92
state->cluster_size = cluster_size;
93
state->bcs = block_copy_state_new(top->backing, state->target,
94
cluster_size, perf->use_copy_range,
95
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
96
97
fail:
98
if (appended) {
99
- state->active = false;
100
bdrv_backup_top_drop(top);
101
} else {
102
bdrv_unref(top);
103
@@ -XXX,XX +XXX,XX @@ void bdrv_backup_top_drop(BlockDriverState *bs)
104
{
105
BDRVBackupTopState *s = bs->opaque;
106
107
- bdrv_drained_begin(bs);
108
+ bdrv_drop_filter(bs, &error_abort);
109
110
block_copy_state_free(s->bcs);
111
112
- s->active = false;
113
- bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
114
- bdrv_replace_node(bs, bs->backing->bs, &error_abort);
115
- bdrv_set_backing_hd(bs, NULL, &error_abort);
116
-
117
- bdrv_drained_end(bs);
118
-
119
bdrv_unref(bs);
120
}
121
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
122
index XXXXXXX..XXXXXXX 100644
123
--- a/tests/qemu-iotests/283.out
124
+++ b/tests/qemu-iotests/283.out
125
@@ -XXX,XX +XXX,XX @@
126
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
127
{"return": {}}
128
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
129
-{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
130
+{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
131
132
=== backup-top should be gone after job-finalize ===
84
133
85
--
134
--
86
2.20.1
135
2.30.2
87
136
88
137
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Drop x- and x_ prefixes for latency histograms and update version to
3
This argument is always NULL. Drop it.
4
3.1
5
4
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-26-vsementsov@virtuozzo.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
---
9
qapi/block-core.json | 20 ++++++++++----------
10
block.c | 38 +++++++++++---------------------------
10
block/qapi.c | 12 ++++++------
11
1 file changed, 11 insertions(+), 27 deletions(-)
11
blockdev.c | 2 +-
12
3 files changed, 17 insertions(+), 17 deletions(-)
13
12
14
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/block-core.json
15
--- a/block.c
17
+++ b/qapi/block-core.json
16
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@
17
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
19
# +------------------
18
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
20
# 10 50 100
19
uint64_t new_used_perm,
21
#
20
uint64_t new_shared_perm,
22
-# Since: 2.12
21
- GSList *ignore_children,
23
+# Since: 4.0
22
Error **errp);
24
##
23
25
{ 'struct': 'BlockLatencyHistogramInfo',
24
typedef struct BlockReopenQueueEntry {
26
'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
25
@@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
27
26
return false;
28
##
27
}
29
-# @x-block-latency-histogram-set:
28
30
+# @block-latency-histogram-set:
29
-static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
31
#
30
- GSList *ignore_children,
32
# Manage read, write and flush latency histograms for the device.
31
- Error **errp)
33
#
32
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
34
@@ -XXX,XX +XXX,XX @@
33
{
35
#
34
BdrvChild *a, *b;
36
# Returns: error if device is not found or any boundary arrays are invalid.
35
37
#
36
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
38
-# Since: 2.12
37
* directions.
39
+# Since: 4.0
38
*/
40
#
39
QLIST_FOREACH(a, &bs->parents, next_parent) {
41
# Example: set new histograms for all io types with intervals
40
- if (g_slist_find(ignore_children, a)) {
42
# [0, 10), [10, 50), [50, 100), [100, +inf):
41
- continue;
43
@@ -XXX,XX +XXX,XX @@
42
- }
44
# "arguments": { "device": "drive0" } }
43
-
45
# <- { "return": {} }
44
QLIST_FOREACH(b, &bs->parents, next_parent) {
46
##
45
- if (a == b || g_slist_find(ignore_children, b)) {
47
-{ 'command': 'x-block-latency-histogram-set',
46
+ if (a == b) {
48
+{ 'command': 'block-latency-histogram-set',
47
continue;
49
'data': {'id': 'str',
48
}
50
'*boundaries': ['uint64'],
49
51
'*boundaries-read': ['uint64'],
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
52
@@ -XXX,XX +XXX,XX @@
51
static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
53
# @timed_stats: Statistics specific to the set of previously defined
52
uint64_t cumulative_perms,
54
# intervals of time (Since 2.5)
53
uint64_t cumulative_shared_perms,
55
#
54
- GSList *ignore_children,
56
-# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
55
Transaction *tran, Error **errp)
57
+# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
56
{
58
#
57
BlockDriver *drv = bs->drv;
59
-# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
58
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
60
+# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
59
bool use_cumulative_perms,
61
#
60
uint64_t cumulative_perms,
62
-# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
61
uint64_t cumulative_shared_perms,
63
+# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
62
- GSList *ignore_children,
64
#
63
Transaction *tran, Error **errp)
65
# Since: 0.14.0
64
{
66
##
65
int ret;
67
@@ -XXX,XX +XXX,XX @@
66
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
68
'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
67
69
'account_invalid': 'bool', 'account_failed': 'bool',
68
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
70
'timed_stats': ['BlockDeviceTimedStats'],
69
cumulative_shared_perms,
71
- '*x_rd_latency_histogram': 'BlockLatencyHistogramInfo',
70
- ignore_children, tran, errp);
72
- '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
71
+ tran, errp);
73
- '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
72
if (ret < 0) {
74
+ '*rd_latency_histogram': 'BlockLatencyHistogramInfo',
73
return ret;
75
+ '*wr_latency_histogram': 'BlockLatencyHistogramInfo',
74
}
76
+ '*flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
75
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
77
76
for ( ; list; list = list->next) {
78
##
77
bs = list->data;
79
# @BlockStats:
78
80
diff --git a/block/qapi.c b/block/qapi.c
79
- if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
81
index XXXXXXX..XXXXXXX 100644
80
+ if (bdrv_parent_perms_conflict(bs, errp)) {
82
--- a/block/qapi.c
81
return -EINVAL;
83
+++ b/block/qapi.c
82
}
84
@@ -XXX,XX +XXX,XX @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
83
84
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
85
86
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
87
cumulative_shared_perms,
88
- ignore_children, tran, errp);
89
+ tran, errp);
90
if (ret < 0) {
91
return ret;
92
}
93
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
94
95
static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
96
uint64_t cumulative_perms,
97
- uint64_t cumulative_shared_perms,
98
- GSList *ignore_children, Error **errp)
99
+ uint64_t cumulative_shared_perms, Error **errp)
100
{
101
g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
102
return bdrv_check_perm_common(list, q, true, cumulative_perms,
103
- cumulative_shared_perms, ignore_children,
104
- NULL, errp);
105
+ cumulative_shared_perms, NULL, errp);
106
}
107
108
static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
109
Transaction *tran, Error **errp)
110
{
111
- return bdrv_check_perm_common(list, q, false, 0, 0, NULL, tran, errp);
112
+ return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
113
}
114
115
/*
116
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
117
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
118
uint64_t new_used_perm,
119
uint64_t new_shared_perm,
120
- GSList *ignore_children,
121
Error **errp)
122
{
123
BdrvChild *c;
124
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
125
assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
126
127
QLIST_FOREACH(c, &bs->parents, next_parent) {
128
- if (g_slist_find(ignore_children, c)) {
129
- continue;
130
- }
131
-
132
if ((new_used_perm & c->shared_perm) != new_used_perm) {
133
char *user = bdrv_child_user_desc(c);
134
char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
135
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
85
}
136
}
86
137
87
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
138
return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
88
- &ds->has_x_rd_latency_histogram,
139
- ignore_children, errp);
89
- &ds->x_rd_latency_histogram);
140
+ errp);
90
+ &ds->has_rd_latency_histogram,
91
+ &ds->rd_latency_histogram);
92
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
93
- &ds->has_x_wr_latency_histogram,
94
- &ds->x_wr_latency_histogram);
95
+ &ds->has_wr_latency_histogram,
96
+ &ds->wr_latency_histogram);
97
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
98
- &ds->has_x_flush_latency_histogram,
99
- &ds->x_flush_latency_histogram);
100
+ &ds->has_flush_latency_histogram,
101
+ &ds->flush_latency_histogram);
102
}
141
}
103
142
104
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
143
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
105
diff --git a/blockdev.c b/blockdev.c
144
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
106
index XXXXXXX..XXXXXXX 100644
145
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
107
--- a/blockdev.c
146
BDRVReopenState *state = &bs_entry->state;
108
+++ b/blockdev.c
147
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
109
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
148
- state->shared_perm, NULL, errp);
110
aio_context_release(old_context);
149
+ state->shared_perm, errp);
111
}
150
if (ret < 0) {
112
151
goto cleanup_perm;
113
-void qmp_x_block_latency_histogram_set(
152
}
114
+void qmp_block_latency_histogram_set(
153
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
115
const char *id,
154
bs_queue, state->perm, state->shared_perm,
116
bool has_boundaries, uint64List *boundaries,
155
&nperm, &nshared);
117
bool has_boundaries_read, uint64List *boundaries_read,
156
ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
157
- nperm, nshared, NULL, errp);
158
+ nperm, nshared, errp);
159
if (ret < 0) {
160
goto cleanup_perm;
161
}
118
--
162
--
119
2.20.1
163
2.30.2
120
164
121
165
diff view generated by jsdifflib
1
In order to be able to dynamically reopen the file read-only or
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
read-write, depending on the users that are attached, we need to be able
3
to switch to a different file descriptor during the permission change.
4
2
5
This interacts with reopen, which also creates a new file descriptor and
3
To be used in the further commit.
6
performs permission changes internally. In this case, the permission
7
change code must reuse the reopen file descriptor instead of creating a
8
third one.
9
4
10
In turn, reopen can drop its code to copy file locks to the new file
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
descriptor because that is now done when applying the new permissions.
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
7
Message-Id: <20210428151804.439460-27-vsementsov@virtuozzo.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
9
---
15
block/file-posix.c | 96 ++++++++++++++++++++++++++++++++++++++++------
10
block.c | 46 ++++++++++++++++++++++++++++++++++++++++++----
16
1 file changed, 85 insertions(+), 11 deletions(-)
11
1 file changed, 42 insertions(+), 4 deletions(-)
17
12
18
diff --git a/block/file-posix.c b/block/file-posix.c
13
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
20
--- a/block/file-posix.c
15
--- a/block.c
21
+++ b/block/file-posix.c
16
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
17
@@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child)
23
uint64_t locked_perm;
18
bdrv_unref(child_bs);
24
uint64_t locked_shared_perm;
25
26
+ int perm_change_fd;
27
BDRVReopenState *reopen_state;
28
29
#ifdef CONFIG_XFS
30
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
31
}
19
}
32
20
33
static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
21
+typedef struct BdrvSetInheritsFrom {
34
- int *open_flags, Error **errp)
22
+ BlockDriverState *bs;
35
+ int *open_flags, bool force_dup,
23
+ BlockDriverState *old_inherits_from;
36
+ Error **errp)
24
+} BdrvSetInheritsFrom;
37
{
25
+
38
BDRVRawState *s = bs->opaque;
26
+static void bdrv_set_inherits_from_abort(void *opaque)
39
int fd = -1;
27
+{
40
@@ -XXX,XX +XXX,XX @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
28
+ BdrvSetInheritsFrom *s = opaque;
41
assert((s->open_flags & O_ASYNC) == 0);
29
+
42
#endif
30
+ s->bs->inherits_from = s->old_inherits_from;
43
31
+}
44
+ if (!force_dup && *open_flags == s->open_flags) {
32
+
45
+ /* We're lucky, the existing fd is fine */
33
+static TransactionActionDrv bdrv_set_inherits_from_drv = {
46
+ return s->fd;
34
+ .abort = bdrv_set_inherits_from_abort,
35
+ .clean = g_free,
36
+};
37
+
38
+/* @tran is allowed to be NULL. In this case no rollback is possible */
39
+static void bdrv_set_inherits_from(BlockDriverState *bs,
40
+ BlockDriverState *new_inherits_from,
41
+ Transaction *tran)
42
+{
43
+ if (tran) {
44
+ BdrvSetInheritsFrom *s = g_new(BdrvSetInheritsFrom, 1);
45
+
46
+ *s = (BdrvSetInheritsFrom) {
47
+ .bs = bs,
48
+ .old_inherits_from = bs->inherits_from,
49
+ };
50
+
51
+ tran_add(tran, &bdrv_set_inherits_from_drv, s);
47
+ }
52
+ }
48
+
53
+
49
if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
54
+ bs->inherits_from = new_inherits_from;
50
/* dup the original fd */
55
+}
51
fd = qemu_dup(s->fd);
56
+
52
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
57
/**
53
qemu_opts_to_qdict(opts, state->options);
58
* Clear all inherits_from pointers from children and grandchildren of
54
59
* @root that point to @root, where necessary.
55
rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
60
+ * @tran is allowed to be NULL. In this case no rollback is possible
56
- &local_err);
61
*/
57
+ true, &local_err);
62
-static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
58
if (local_err) {
63
+static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
59
error_propagate(errp, local_err);
64
+ Transaction *tran)
60
ret = -1;
65
{
61
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
66
BdrvChild *c;
62
ret = -EINVAL;
67
63
goto out_fd;
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
69
}
64
}
70
}
65
-
71
if (c == NULL) {
66
- /* Copy locks to the new fd */
72
- child->bs->inherits_from = NULL;
67
- ret = raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
73
+ bdrv_set_inherits_from(child->bs, NULL, tran);
68
- s->locked_shared_perm, false, errp);
74
}
69
- if (ret < 0) {
70
- ret = -EINVAL;
71
- goto out_fd;
72
- }
73
}
75
}
74
76
75
s->reopen_state = state;
77
QLIST_FOREACH(c, &child->bs->children, next) {
76
@@ -XXX,XX +XXX,XX @@ static QemuOptsList raw_create_opts = {
78
- bdrv_unset_inherits_from(root, c);
77
static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
79
+ bdrv_unset_inherits_from(root, c, tran);
78
Error **errp)
80
}
79
{
80
- return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
81
+ BDRVRawState *s = bs->opaque;
82
+ BDRVRawReopenState *rs = NULL;
83
+ int open_flags;
84
+ int ret;
85
+
86
+ if (s->perm_change_fd) {
87
+ /*
88
+ * In the context of reopen, this function may be called several times
89
+ * (directly and recursively while change permissions of the parent).
90
+ * This is even true for children that don't inherit from the original
91
+ * reopen node, so s->reopen_state is not set.
92
+ *
93
+ * Ignore all but the first call.
94
+ */
95
+ return 0;
96
+ }
97
+
98
+ if (s->reopen_state) {
99
+ /* We already have a new file descriptor to set permissions for */
100
+ assert(s->reopen_state->perm == perm);
101
+ assert(s->reopen_state->shared_perm == shared);
102
+ rs = s->reopen_state->opaque;
103
+ s->perm_change_fd = rs->fd;
104
+ } else {
105
+ /* We may need a new fd if auto-read-only switches the mode */
106
+ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags,
107
+ false, errp);
108
+ if (ret < 0) {
109
+ return ret;
110
+ } else if (ret != s->fd) {
111
+ s->perm_change_fd = ret;
112
+ }
113
+ }
114
+
115
+ /* Prepare permissions on old fd to avoid conflicts between old and new,
116
+ * but keep everything locked that new will need. */
117
+ ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
118
+ if (ret < 0) {
119
+ goto fail;
120
+ }
121
+
122
+ /* Copy locks to the new fd */
123
+ if (s->perm_change_fd) {
124
+ ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared,
125
+ false, errp);
126
+ if (ret < 0) {
127
+ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
128
+ goto fail;
129
+ }
130
+ }
131
+ return 0;
132
+
133
+fail:
134
+ if (s->perm_change_fd && !s->reopen_state) {
135
+ qemu_close(s->perm_change_fd);
136
+ }
137
+ s->perm_change_fd = 0;
138
+ return ret;
139
}
81
}
140
82
141
static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
83
@@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
142
{
84
return;
143
BDRVRawState *s = bs->opaque;
85
}
144
+
86
145
+ /* For reopen, we have already switched to the new fd (.bdrv_set_perm is
87
- bdrv_unset_inherits_from(parent, child);
146
+ * called after .bdrv_reopen_commit) */
88
+ bdrv_unset_inherits_from(parent, child, NULL);
147
+ if (s->perm_change_fd && s->fd != s->perm_change_fd) {
89
bdrv_root_unref_child(child);
148
+ qemu_close(s->fd);
149
+ s->fd = s->perm_change_fd;
150
+ }
151
+ s->perm_change_fd = 0;
152
+
153
raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
154
s->perm = perm;
155
s->shared_perm = shared;
156
@@ -XXX,XX +XXX,XX @@ static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
157
158
static void raw_abort_perm_update(BlockDriverState *bs)
159
{
160
+ BDRVRawState *s = bs->opaque;
161
+
162
+ /* For reopen, .bdrv_reopen_abort is called afterwards and will close
163
+ * the file descriptor. */
164
+ if (s->perm_change_fd && !s->reopen_state) {
165
+ qemu_close(s->perm_change_fd);
166
+ }
167
+ s->perm_change_fd = 0;
168
+
169
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
170
}
90
}
171
91
172
--
92
--
173
2.20.1
93
2.30.2
174
94
175
95
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The bdrv_reopen_queue() function is used to create a queue with
3
To be used in further commit.
4
the BDSs that are going to be reopened and their new options. Once
5
the queue is ready bdrv_reopen_multiple() is called to perform the
6
operation.
7
4
8
The original options from each one of the BDSs are kept, with the new
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
options passed to bdrv_reopen_queue() applied on top of them.
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
7
Message-Id: <20210428151804.439460-28-vsementsov@virtuozzo.com>
11
For "x-blockdev-reopen" we want a function that behaves much like
12
"blockdev-add". We want to ignore the previous set of options so that
13
only the ones actually specified by the user are applied, with the
14
rest having their default values.
15
16
One of the things that we need is a way to tell bdrv_reopen_queue()
17
whether we want to keep the old set of options or not, and that's what
18
this patch does. All current callers are setting this new parameter to
19
true and x-blockdev-reopen will set it to false.
20
21
Signed-off-by: Alberto Garcia <berto@igalia.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
9
---
24
include/block/block.h | 3 ++-
10
include/block/block.h | 3 ++-
25
block.c | 34 +++++++++++++++++++---------------
11
block.c | 9 ++++-----
26
block/replication.c | 4 ++--
12
block/io.c | 31 +++++++++++++++++++++++++++++--
27
qemu-io-cmds.c | 2 +-
13
3 files changed, 35 insertions(+), 8 deletions(-)
28
4 files changed, 24 insertions(+), 19 deletions(-)
29
14
30
diff --git a/include/block/block.h b/include/block/block.h
15
diff --git a/include/block/block.h b/include/block/block.h
31
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
32
--- a/include/block/block.h
17
--- a/include/block/block.h
33
+++ b/include/block/block.h
18
+++ b/include/block/block.h
34
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
19
@@ -XXX,XX +XXX,XX @@
35
BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
20
#include "block/dirty-bitmap.h"
36
int flags, Error **errp);
21
#include "block/blockjob.h"
37
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
22
#include "qemu/hbitmap.h"
38
- BlockDriverState *bs, QDict *options);
23
+#include "qemu/transactions.h"
39
+ BlockDriverState *bs, QDict *options,
24
40
+ bool keep_old_opts);
25
/*
41
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp);
26
* generated_co_wrapper
42
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
27
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
43
Error **errp);
28
BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
29
BlockDriverState *in_bs, Error **errp);
30
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
31
-void bdrv_refresh_limits(BlockDriverState *bs, Error **errp);
32
+void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp);
33
int bdrv_commit(BlockDriverState *bs);
34
int bdrv_make_empty(BdrvChild *c, Error **errp);
35
int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
44
diff --git a/block.c b/block.c
36
diff --git a/block.c b/block.c
45
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100644
46
--- a/block.c
38
--- a/block.c
47
+++ b/block.c
39
+++ b/block.c
48
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
40
@@ -XXX,XX +XXX,XX @@
49
QDict *options,
41
#include "qemu/timer.h"
50
const BdrvChildRole *role,
42
#include "qemu/cutils.h"
51
QDict *parent_options,
43
#include "qemu/id.h"
52
- int parent_flags)
44
-#include "qemu/transactions.h"
53
+ int parent_flags,
45
#include "block/coroutines.h"
54
+ bool keep_old_opts)
46
47
#ifdef CONFIG_BSD
48
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
49
return ret;
50
}
51
52
- bdrv_refresh_limits(bs, &local_err);
53
+ bdrv_refresh_limits(bs, NULL, &local_err);
54
if (local_err) {
55
error_propagate(errp, local_err);
56
return -EINVAL;
57
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
58
}
59
60
out:
61
- bdrv_refresh_limits(bs, NULL);
62
+ bdrv_refresh_limits(bs, NULL, NULL);
63
64
return ret;
65
}
66
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
67
bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
68
}
69
70
- bdrv_refresh_limits(bs, NULL);
71
+ bdrv_refresh_limits(bs, NULL, NULL);
72
}
73
74
/*
75
@@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
76
out:
77
tran_finalize(tran, ret);
78
79
- bdrv_refresh_limits(bs_top, NULL);
80
+ bdrv_refresh_limits(bs_top, NULL, NULL);
81
82
return ret;
83
}
84
diff --git a/block/io.c b/block/io.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/block/io.c
87
+++ b/block/io.c
88
@@ -XXX,XX +XXX,XX @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
89
dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov);
90
}
91
92
-void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
93
+typedef struct BdrvRefreshLimitsState {
94
+ BlockDriverState *bs;
95
+ BlockLimits old_bl;
96
+} BdrvRefreshLimitsState;
97
+
98
+static void bdrv_refresh_limits_abort(void *opaque)
99
+{
100
+ BdrvRefreshLimitsState *s = opaque;
101
+
102
+ s->bs->bl = s->old_bl;
103
+}
104
+
105
+static TransactionActionDrv bdrv_refresh_limits_drv = {
106
+ .abort = bdrv_refresh_limits_abort,
107
+ .clean = g_free,
108
+};
109
+
110
+/* @tran is allowed to be NULL, in this case no rollback is possible. */
111
+void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp)
55
{
112
{
56
assert(bs != NULL);
113
ERRP_GUARD();
57
114
BlockDriver *drv = bs->drv;
58
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
115
BdrvChild *c;
59
*/
116
bool have_limits;
60
117
61
/* Old explicitly set values (don't overwrite by inherited value) */
118
+ if (tran) {
62
- if (bs_entry) {
119
+ BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1);
63
- old_options = qdict_clone_shallow(bs_entry->state.explicit_options);
120
+ *s = (BdrvRefreshLimitsState) {
64
- } else {
121
+ .bs = bs,
65
- old_options = qdict_clone_shallow(bs->explicit_options);
122
+ .old_bl = bs->bl,
66
+ if (bs_entry || keep_old_opts) {
123
+ };
67
+ old_options = qdict_clone_shallow(bs_entry ?
124
+ tran_add(tran, &bdrv_refresh_limits_drv, s);
68
+ bs_entry->state.explicit_options :
69
+ bs->explicit_options);
70
+ bdrv_join_options(bs, options, old_options);
71
+ qobject_unref(old_options);
72
}
73
- bdrv_join_options(bs, options, old_options);
74
- qobject_unref(old_options);
75
76
explicit_options = qdict_clone_shallow(options);
77
78
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
79
flags = bdrv_get_flags(bs);
80
}
81
82
- /* Old values are used for options that aren't set yet */
83
- old_options = qdict_clone_shallow(bs->options);
84
- bdrv_join_options(bs, options, old_options);
85
- qobject_unref(old_options);
86
+ if (keep_old_opts) {
87
+ /* Old values are used for options that aren't set yet */
88
+ old_options = qdict_clone_shallow(bs->options);
89
+ bdrv_join_options(bs, options, old_options);
90
+ qobject_unref(old_options);
91
+ }
125
+ }
92
126
+
93
/* We have the final set of options so let's update the flags */
127
memset(&bs->bl, 0, sizeof(bs->bl));
94
options_copy = qdict_clone_shallow(options);
128
95
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
129
if (!drv) {
96
g_free(child_key_dot);
130
@@ -XXX,XX +XXX,XX @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
97
131
QLIST_FOREACH(c, &bs->children, next) {
98
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options,
132
if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW))
99
- child->role, options, flags);
133
{
100
+ child->role, options, flags, keep_old_opts);
134
- bdrv_refresh_limits(c->bs, errp);
101
}
135
+ bdrv_refresh_limits(c->bs, tran, errp);
102
136
if (*errp) {
103
return bs_queue;
137
return;
104
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
138
}
105
106
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
107
BlockDriverState *bs,
108
- QDict *options)
109
+ QDict *options, bool keep_old_opts)
110
{
111
- return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0);
112
+ return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0,
113
+ keep_old_opts);
114
}
115
116
/*
117
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
118
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
119
120
bdrv_subtree_drained_begin(bs);
121
- queue = bdrv_reopen_queue(NULL, bs, opts);
122
+ queue = bdrv_reopen_queue(NULL, bs, opts, true);
123
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp);
124
bdrv_subtree_drained_end(bs);
125
126
diff --git a/block/replication.c b/block/replication.c
127
index XXXXXXX..XXXXXXX 100644
128
--- a/block/replication.c
129
+++ b/block/replication.c
130
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
131
QDict *opts = qdict_new();
132
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
133
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
134
- opts);
135
+ opts, true);
136
}
137
138
if (s->orig_secondary_read_only) {
139
QDict *opts = qdict_new();
140
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
141
reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
142
- opts);
143
+ opts, true);
144
}
145
146
if (reopen_queue) {
147
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
148
index XXXXXXX..XXXXXXX 100644
149
--- a/qemu-io-cmds.c
150
+++ b/qemu-io-cmds.c
151
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
152
}
153
154
bdrv_subtree_drained_begin(bs);
155
- brq = bdrv_reopen_queue(NULL, bs, opts);
156
+ brq = bdrv_reopen_queue(NULL, bs, opts, true);
157
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
158
bdrv_subtree_drained_end(bs);
159
160
--
139
--
161
2.20.1
140
2.30.2
162
141
163
142
diff view generated by jsdifflib
1
From: Keith Busch <keith.busch@intel.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The implementation used blocks units rather than the expected bytes.
3
Split out no-perm part of bdrv_set_backing_hd() as a separate
4
transaction action. Note the in case of existing BdrvChild we reuse it,
5
not recreate, just to do less actions.
4
6
5
Fixes: c03e7ef12a9 ("nvme: Implement Write Zeroes")
7
We don't need to create extra reference to backing_hd as we don't lose
6
Reported-by: Ming Lei <ming.lei@redhat.com>
8
it in bdrv_attach_child().
7
Signed-off-by: Keith Busch <keith.busch@intel.com>
9
8
Reviewed-by: Christoph Hellwig <hch@lst.de>
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20210428151804.439460-29-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
14
---
11
hw/block/nvme.c | 6 +++---
15
block.c | 54 +++++++++++++++++++++++++++++++++++++-----------------
12
1 file changed, 3 insertions(+), 3 deletions(-)
16
1 file changed, 37 insertions(+), 17 deletions(-)
13
17
14
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
18
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/block/nvme.c
20
--- a/block.c
17
+++ b/hw/block/nvme.c
21
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
22
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
19
const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
23
BdrvChild **child,
20
uint64_t slba = le64_to_cpu(rw->slba);
24
Transaction *tran,
21
uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
25
Error **errp);
22
- uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
26
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
23
- uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
27
+ Transaction *tran);
24
+ uint64_t offset = slba << data_shift;
28
25
+ uint32_t count = nlb << data_shift;
29
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
26
30
*queue, Error **errp);
27
if (unlikely(slba + nlb > ns->id_ns.nsze)) {
31
@@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
28
trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
32
* Sets the bs->backing link of a BDS. A new reference is created; callers
29
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
33
* which don't need their own reference any more must call bdrv_unref().
30
req->has_sg = false;
34
*/
31
block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0,
35
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
32
BLOCK_ACCT_WRITE);
36
- Error **errp)
33
- req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, aio_slba, aio_nlb,
37
+static int bdrv_set_backing_noperm(BlockDriverState *bs,
34
+ req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, offset, count,
38
+ BlockDriverState *backing_hd,
35
BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req);
39
+ Transaction *tran, Error **errp)
36
return NVME_NO_COMPLETE;
40
{
41
int ret = 0;
42
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
43
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
44
return -EPERM;
45
}
46
47
- if (backing_hd) {
48
- bdrv_ref(backing_hd);
49
- }
50
-
51
if (bs->backing) {
52
/* Cannot be frozen, we checked that above */
53
- bdrv_unref_child(bs, bs->backing);
54
- bs->backing = NULL;
55
+ bdrv_unset_inherits_from(bs, bs->backing, tran);
56
+ bdrv_remove_filter_or_cow_child(bs, tran);
57
}
58
59
if (!backing_hd) {
60
goto out;
61
}
62
63
- bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds,
64
- bdrv_backing_role(bs), errp);
65
- if (!bs->backing) {
66
- ret = -EPERM;
67
- goto out;
68
+ ret = bdrv_attach_child_noperm(bs, backing_hd, "backing",
69
+ &child_of_bds, bdrv_backing_role(bs),
70
+ &bs->backing, tran, errp);
71
+ if (ret < 0) {
72
+ return ret;
73
}
74
75
- /* If backing_hd was already part of bs's backing chain, and
76
+
77
+ /*
78
+ * If backing_hd was already part of bs's backing chain, and
79
* inherits_from pointed recursively to bs then let's update it to
80
- * point directly to bs (else it will become NULL). */
81
+ * point directly to bs (else it will become NULL).
82
+ */
83
if (update_inherits_from) {
84
- backing_hd->inherits_from = bs;
85
+ bdrv_set_inherits_from(backing_hd, bs, tran);
86
}
87
88
out:
89
- bdrv_refresh_limits(bs, NULL, NULL);
90
+ bdrv_refresh_limits(bs, tran, NULL);
91
+
92
+ return 0;
93
+}
94
+
95
+int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
96
+ Error **errp)
97
+{
98
+ int ret;
99
+ Transaction *tran = tran_new();
100
+
101
+ ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
102
+ if (ret < 0) {
103
+ goto out;
104
+ }
105
+
106
+ ret = bdrv_refresh_perms(bs, errp);
107
+out:
108
+ tran_finalize(tran, ret);
109
110
return ret;
37
}
111
}
38
--
112
--
39
2.20.1
113
2.30.2
40
114
41
115
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
3
During reopen we may add backing bs from other aio context, which may
4
lead to changing original context of top bs.
5
6
We are going to move graph modification to prepare stage. So, it will
7
be possible that bdrv_flush() in bdrv_reopen_prepare called on bs in
8
non-original aio context, which we didn't aquire which leads to crash.
9
10
To avoid this problem move bdrv_flush() to be a separate reopen stage
11
before bdrv_reopen_prepare().
12
13
This doesn't seem correct to acquire only one aio context and not all
14
contexts participating in reopen. But it's not obvious how to do it
15
correctly, keeping in mind:
16
17
1. rules of bdrv_set_aio_context_ignore() that requires new_context
18
lock not being held
19
20
2. possible deadlocks because of holding all (or several?) AioContext
21
locks
22
23
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
24
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
25
Message-Id: <20210428151804.439460-30-vsementsov@virtuozzo.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Alberto Garcia <berto@igalia.com>
3
Reviewed-by: Eric Blake <eblake@redhat.com>
4
---
27
---
5
block.c | 4 +---
28
block.c | 14 ++++++++------
6
1 file changed, 1 insertion(+), 3 deletions(-)
29
1 file changed, 8 insertions(+), 6 deletions(-)
7
30
8
diff --git a/block.c b/block.c
31
diff --git a/block.c b/block.c
9
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
10
--- a/block.c
33
--- a/block.c
11
+++ b/block.c
34
+++ b/block.c
12
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
35
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
13
{
14
int ret = -1;
15
BlockReopenQueueEntry *bs_entry, *next;
16
- Error *local_err = NULL;
17
36
18
assert(bs_queue != NULL);
37
assert(bs_queue != NULL);
19
38
20
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
39
+ QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
40
+ ret = bdrv_flush(bs_entry->state.bs);
41
+ if (ret < 0) {
42
+ error_setg_errno(errp, -ret, "Error flushing drive");
43
+ goto cleanup;
44
+ }
45
+ }
46
+
47
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
21
assert(bs_entry->state.bs->quiesce_counter > 0);
48
assert(bs_entry->state.bs->quiesce_counter > 0);
22
- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
49
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
23
- error_propagate(errp, local_err);
50
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
24
+ if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
51
bdrv_reopen_perm(queue, reopen_state->bs,
25
goto cleanup;
52
&reopen_state->perm, &reopen_state->shared_perm);
26
}
53
27
bs_entry->prepared = true;
54
- ret = bdrv_flush(reopen_state->bs);
55
- if (ret) {
56
- error_setg_errno(errp, -ret, "Error flushing drive");
57
- goto error;
58
- }
59
-
60
if (drv->bdrv_reopen_prepare) {
61
/*
62
* If a driver-specific option is missing, it means that we
28
--
63
--
29
2.20.1
64
2.30.2
30
65
31
66
diff view generated by jsdifflib
1
There is no reason why we can take locks on the new file descriptor only
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
in raw_reopen_commit() where error handling isn't possible any more.
3
Instead, we can already do this in raw_reopen_prepare().
4
2
3
Move bdrv_reopen_multiple to new paradigm of permission update:
4
first update graph relations, then do refresh the permissions.
5
6
We have to modify reopen process in file-posix driver: with new scheme
7
we don't have prepared permissions in raw_reopen_prepare(), so we
8
should reconfigure fd in raw_check_perm(). Still this seems more native
9
and simple anyway.
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20210428151804.439460-31-vsementsov@virtuozzo.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
15
---
7
block/file-posix.c | 27 ++++++++++++++++-----------
16
include/block/block.h | 3 +-
8
1 file changed, 16 insertions(+), 11 deletions(-)
17
block.c | 187 ++++++++++++------------------------------
18
block/file-posix.c | 91 +++++++-------------
19
3 files changed, 84 insertions(+), 197 deletions(-)
9
20
21
diff --git a/include/block/block.h b/include/block/block.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block.h
24
+++ b/include/block/block.h
25
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
26
BlockdevDetectZeroesOptions detect_zeroes;
27
bool backing_missing;
28
bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
29
- BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */
30
- uint64_t perm, shared_perm;
31
+ BlockDriverState *old_backing_bs; /* keep pointer for permissions update */
32
QDict *options;
33
QDict *explicit_options;
34
void *opaque;
35
diff --git a/block.c b/block.c
36
index XXXXXXX..XXXXXXX 100644
37
--- a/block.c
38
+++ b/block.c
39
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
40
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
41
Transaction *tran);
42
43
-static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
44
- *queue, Error **errp);
45
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
46
+ BlockReopenQueue *queue,
47
+ Transaction *set_backings_tran, Error **errp);
48
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
49
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
50
51
@@ -XXX,XX +XXX,XX @@ static void bdrv_list_abort_perm_update(GSList *list)
52
}
53
}
54
55
+__attribute__((unused))
56
static void bdrv_abort_perm_update(BlockDriverState *bs)
57
{
58
g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
59
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
60
*
61
* Needs to be followed by a call to either bdrv_set_perm() or
62
* bdrv_abort_perm_update(). */
63
+__attribute__((unused))
64
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
65
uint64_t new_used_perm,
66
uint64_t new_shared_perm,
67
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
68
bs_entry->state.explicit_options = explicit_options;
69
bs_entry->state.flags = flags;
70
71
- /* This needs to be overwritten in bdrv_reopen_prepare() */
72
- bs_entry->state.perm = UINT64_MAX;
73
- bs_entry->state.shared_perm = 0;
74
-
75
/*
76
* If keep_old_opts is false then it means that unspecified
77
* options must be reset to their original value. We don't allow
78
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
79
{
80
int ret = -1;
81
BlockReopenQueueEntry *bs_entry, *next;
82
+ Transaction *tran = tran_new();
83
+ g_autoptr(GHashTable) found = NULL;
84
+ g_autoptr(GSList) refresh_list = NULL;
85
86
assert(bs_queue != NULL);
87
88
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
89
90
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
91
assert(bs_entry->state.bs->quiesce_counter > 0);
92
- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
93
- goto cleanup;
94
+ ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp);
95
+ if (ret < 0) {
96
+ goto abort;
97
}
98
bs_entry->prepared = true;
99
}
100
101
+ found = g_hash_table_new(NULL, NULL);
102
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
103
BDRVReopenState *state = &bs_entry->state;
104
- ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
105
- state->shared_perm, errp);
106
- if (ret < 0) {
107
- goto cleanup_perm;
108
- }
109
- /* Check if new_backing_bs would accept the new permissions */
110
- if (state->replace_backing_bs && state->new_backing_bs) {
111
- uint64_t nperm, nshared;
112
- bdrv_child_perm(state->bs, state->new_backing_bs,
113
- NULL, bdrv_backing_role(state->bs),
114
- bs_queue, state->perm, state->shared_perm,
115
- &nperm, &nshared);
116
- ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
117
- nperm, nshared, errp);
118
- if (ret < 0) {
119
- goto cleanup_perm;
120
- }
121
+
122
+ refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs);
123
+ if (state->old_backing_bs) {
124
+ refresh_list = bdrv_topological_dfs(refresh_list, found,
125
+ state->old_backing_bs);
126
}
127
- bs_entry->perms_checked = true;
128
+ }
129
+
130
+ /*
131
+ * Note that file-posix driver rely on permission update done during reopen
132
+ * (even if no permission changed), because it wants "new" permissions for
133
+ * reconfiguring the fd and that's why it does it in raw_check_perm(), not
134
+ * in raw_reopen_prepare() which is called with "old" permissions.
135
+ */
136
+ ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp);
137
+ if (ret < 0) {
138
+ goto abort;
139
}
140
141
/*
142
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
143
bdrv_reopen_commit(&bs_entry->state);
144
}
145
146
- ret = 0;
147
-cleanup_perm:
148
- QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
149
- BDRVReopenState *state = &bs_entry->state;
150
-
151
- if (!bs_entry->perms_checked) {
152
- continue;
153
- }
154
-
155
- if (ret == 0) {
156
- uint64_t perm, shared;
157
+ tran_commit(tran);
158
159
- bdrv_get_cumulative_perm(state->bs, &perm, &shared);
160
- assert(perm == state->perm);
161
- assert(shared == state->shared_perm);
162
+ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
163
+ BlockDriverState *bs = bs_entry->state.bs;
164
165
- bdrv_set_perm(state->bs);
166
- } else {
167
- bdrv_abort_perm_update(state->bs);
168
- if (state->replace_backing_bs && state->new_backing_bs) {
169
- bdrv_abort_perm_update(state->new_backing_bs);
170
- }
171
+ if (bs->drv->bdrv_reopen_commit_post) {
172
+ bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
173
}
174
}
175
176
- if (ret == 0) {
177
- QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
178
- BlockDriverState *bs = bs_entry->state.bs;
179
+ ret = 0;
180
+ goto cleanup;
181
182
- if (bs->drv->bdrv_reopen_commit_post)
183
- bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
184
+abort:
185
+ tran_abort(tran);
186
+ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
187
+ if (bs_entry->prepared) {
188
+ bdrv_reopen_abort(&bs_entry->state);
189
}
190
+ qobject_unref(bs_entry->state.explicit_options);
191
+ qobject_unref(bs_entry->state.options);
192
}
193
+
194
cleanup:
195
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
196
- if (ret) {
197
- if (bs_entry->prepared) {
198
- bdrv_reopen_abort(&bs_entry->state);
199
- }
200
- qobject_unref(bs_entry->state.explicit_options);
201
- qobject_unref(bs_entry->state.options);
202
- }
203
- if (bs_entry->state.new_backing_bs) {
204
- bdrv_unref(bs_entry->state.new_backing_bs);
205
- }
206
g_free(bs_entry);
207
}
208
g_free(bs_queue);
209
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
210
return ret;
211
}
212
213
-static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q,
214
- BdrvChild *c)
215
-{
216
- BlockReopenQueueEntry *entry;
217
-
218
- QTAILQ_FOREACH(entry, q, entry) {
219
- BlockDriverState *bs = entry->state.bs;
220
- BdrvChild *child;
221
-
222
- QLIST_FOREACH(child, &bs->children, next) {
223
- if (child == c) {
224
- return entry;
225
- }
226
- }
227
- }
228
-
229
- return NULL;
230
-}
231
-
232
-static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
233
- uint64_t *perm, uint64_t *shared)
234
-{
235
- BdrvChild *c;
236
- BlockReopenQueueEntry *parent;
237
- uint64_t cumulative_perms = 0;
238
- uint64_t cumulative_shared_perms = BLK_PERM_ALL;
239
-
240
- QLIST_FOREACH(c, &bs->parents, next_parent) {
241
- parent = find_parent_in_reopen_queue(q, c);
242
- if (!parent) {
243
- cumulative_perms |= c->perm;
244
- cumulative_shared_perms &= c->shared_perm;
245
- } else {
246
- uint64_t nperm, nshared;
247
-
248
- bdrv_child_perm(parent->state.bs, bs, c, c->role, q,
249
- parent->state.perm, parent->state.shared_perm,
250
- &nperm, &nshared);
251
-
252
- cumulative_perms |= nperm;
253
- cumulative_shared_perms &= nshared;
254
- }
255
- }
256
- *perm = cumulative_perms;
257
- *shared = cumulative_shared_perms;
258
-}
259
-
260
static bool bdrv_reopen_can_attach(BlockDriverState *parent,
261
BdrvChild *child,
262
BlockDriverState *new_child,
263
@@ -XXX,XX +XXX,XX @@ static bool bdrv_reopen_can_attach(BlockDriverState *parent,
264
* Return 0 on success, otherwise return < 0 and set @errp.
265
*/
266
static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
267
+ Transaction *set_backings_tran,
268
Error **errp)
269
{
270
BlockDriverState *bs = reopen_state->bs;
271
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
272
273
/* If we want to replace the backing file we need some extra checks */
274
if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
275
+ int ret;
276
+
277
/* Check for implicit nodes between bs and its backing file */
278
if (bs != overlay_bs) {
279
error_setg(errp, "Cannot change backing link if '%s' has "
280
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
281
return -EPERM;
282
}
283
reopen_state->replace_backing_bs = true;
284
- if (new_backing_bs) {
285
- bdrv_ref(new_backing_bs);
286
- reopen_state->new_backing_bs = new_backing_bs;
287
+ reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
288
+ ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
289
+ errp);
290
+ if (ret < 0) {
291
+ return ret;
292
}
293
}
294
295
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
296
*
297
*/
298
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
299
- BlockReopenQueue *queue, Error **errp)
300
+ BlockReopenQueue *queue,
301
+ Transaction *set_backings_tran, Error **errp)
302
{
303
int ret = -1;
304
int old_flags;
305
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
306
goto error;
307
}
308
309
- /* Calculate required permissions after reopening */
310
- bdrv_reopen_perm(queue, reopen_state->bs,
311
- &reopen_state->perm, &reopen_state->shared_perm);
312
-
313
if (drv->bdrv_reopen_prepare) {
314
/*
315
* If a driver-specific option is missing, it means that we
316
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
317
* either a reference to an existing node (using its node name)
318
* or NULL to simply detach the current backing file.
319
*/
320
- ret = bdrv_reopen_parse_backing(reopen_state, errp);
321
+ ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp);
322
if (ret < 0) {
323
goto error;
324
}
325
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
326
qdict_del(bs->explicit_options, child->name);
327
qdict_del(bs->options, child->name);
328
}
329
-
330
- /*
331
- * Change the backing file if a new one was specified. We do this
332
- * after updating bs->options, so bdrv_refresh_filename() (called
333
- * from bdrv_set_backing_hd()) has the new values.
334
- */
335
- if (reopen_state->replace_backing_bs) {
336
- BlockDriverState *old_backing_bs = child_bs(bs->backing);
337
- assert(!old_backing_bs || !old_backing_bs->implicit);
338
- /* Abort the permission update on the backing bs we're detaching */
339
- if (old_backing_bs) {
340
- bdrv_abort_perm_update(old_backing_bs);
341
- }
342
- bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
343
- }
344
-
345
bdrv_refresh_limits(bs, NULL, NULL);
346
}
347
10
diff --git a/block/file-posix.c b/block/file-posix.c
348
diff --git a/block/file-posix.c b/block/file-posix.c
11
index XXXXXXX..XXXXXXX 100644
349
index XXXXXXX..XXXXXXX 100644
12
--- a/block/file-posix.c
350
--- a/block/file-posix.c
13
+++ b/block/file-posix.c
351
+++ b/block/file-posix.c
352
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
353
} BDRVRawState;
354
355
typedef struct BDRVRawReopenState {
356
- int fd;
357
int open_flags;
358
bool drop_cache;
359
bool check_cache_dropped;
14
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
360
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
15
BDRVRawState *s;
16
BDRVRawReopenState *rs;
361
BDRVRawReopenState *rs;
17
QemuOpts *opts;
362
QemuOpts *opts;
18
- int ret = 0;
363
int ret;
19
+ int ret;
364
- Error *local_err = NULL;
20
Error *local_err = NULL;
21
365
22
assert(state != NULL);
366
assert(state != NULL);
367
assert(state->bs != NULL);
23
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
368
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
24
if (rs->fd != -1) {
369
* bdrv_reopen_prepare() will detect changes and complain. */
25
raw_probe_alignment(state->bs, rs->fd, &local_err);
370
qemu_opts_to_qdict(opts, state->options);
26
if (local_err) {
371
27
- qemu_close(rs->fd);
372
- rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
28
- rs->fd = -1;
373
- state->perm, true, &local_err);
29
error_propagate(errp, local_err);
374
- if (local_err) {
30
ret = -EINVAL;
375
- error_propagate(errp, local_err);
31
+ goto out_fd;
376
- ret = -1;
32
+ }
377
- goto out;
33
+
378
- }
34
+ /* Copy locks to the new fd */
379
-
35
+ ret = raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
380
- /* Fail already reopen_prepare() if we can't get a working O_DIRECT
36
+ s->locked_shared_perm, false, errp);
381
- * alignment with the new fd. */
37
+ if (ret < 0) {
382
- if (rs->fd != -1) {
38
+ ret = -EINVAL;
383
- raw_probe_alignment(state->bs, rs->fd, &local_err);
39
+ goto out_fd;
384
- if (local_err) {
40
}
385
- error_propagate(errp, local_err);
41
}
386
- ret = -EINVAL;
387
- goto out_fd;
388
- }
389
- }
390
+ /*
391
+ * As part of reopen prepare we also want to create new fd by
392
+ * raw_reconfigure_getfd(). But it wants updated "perm", when in
393
+ * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to
394
+ * permission update. Happily, permission update is always a part (a seprate
395
+ * stage) of bdrv_reopen_multiple() so we can rely on this fact and
396
+ * reconfigure fd in raw_check_perm().
397
+ */
42
398
43
s->reopen_state = state;
399
s->reopen_state = state;
44
+ ret = 0;
400
ret = 0;
45
+out_fd:
401
-out_fd:
46
+ if (ret < 0) {
402
- if (ret < 0) {
47
+ qemu_close(rs->fd);
403
- qemu_close(rs->fd);
48
+ rs->fd = -1;
404
- rs->fd = -1;
49
+ }
405
- }
406
+
50
out:
407
out:
51
qemu_opts_del(opts);
408
qemu_opts_del(opts);
52
return ret;
409
return ret;
53
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
410
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
54
{
411
s->drop_cache = rs->drop_cache;
55
BDRVRawReopenState *rs = state->opaque;
56
BDRVRawState *s = state->bs->opaque;
57
- Error *local_err = NULL;
58
59
s->check_cache_dropped = rs->check_cache_dropped;
412
s->check_cache_dropped = rs->check_cache_dropped;
60
s->open_flags = rs->open_flags;
413
s->open_flags = rs->open_flags;
61
414
-
62
- /* Copy locks to the new fd before closing the old one. */
415
- qemu_close(s->fd);
63
- raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
416
- s->fd = rs->fd;
64
- s->locked_shared_perm, false, &local_err);
417
-
65
- if (local_err) {
418
g_free(state->opaque);
66
- /* shouldn't fail in a sane host, but report it just in case. */
419
state->opaque = NULL;
67
- error_report_err(local_err);
420
68
- }
421
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_abort(BDRVReopenState *state)
69
qemu_close(s->fd);
422
return;
70
s->fd = rs->fd;
423
}
71
424
425
- if (rs->fd >= 0) {
426
- qemu_close(rs->fd);
427
- rs->fd = -1;
428
- }
429
g_free(state->opaque);
430
state->opaque = NULL;
431
432
@@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
433
Error **errp)
434
{
435
BDRVRawState *s = bs->opaque;
436
- BDRVRawReopenState *rs = NULL;
437
+ int input_flags = s->reopen_state ? s->reopen_state->flags : bs->open_flags;
438
int open_flags;
439
int ret;
440
441
- if (s->perm_change_fd) {
442
+ /* We may need a new fd if auto-read-only switches the mode */
443
+ ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm,
444
+ false, errp);
445
+ if (ret < 0) {
446
+ return ret;
447
+ } else if (ret != s->fd) {
448
+ Error *local_err = NULL;
449
+
450
/*
451
- * In the context of reopen, this function may be called several times
452
- * (directly and recursively while change permissions of the parent).
453
- * This is even true for children that don't inherit from the original
454
- * reopen node, so s->reopen_state is not set.
455
- *
456
- * Ignore all but the first call.
457
+ * Fail already check_perm() if we can't get a working O_DIRECT
458
+ * alignment with the new fd.
459
*/
460
- return 0;
461
- }
462
-
463
- if (s->reopen_state) {
464
- /* We already have a new file descriptor to set permissions for */
465
- assert(s->reopen_state->perm == perm);
466
- assert(s->reopen_state->shared_perm == shared);
467
- rs = s->reopen_state->opaque;
468
- s->perm_change_fd = rs->fd;
469
- s->perm_change_flags = rs->open_flags;
470
- } else {
471
- /* We may need a new fd if auto-read-only switches the mode */
472
- ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
473
- false, errp);
474
- if (ret < 0) {
475
- return ret;
476
- } else if (ret != s->fd) {
477
- s->perm_change_fd = ret;
478
- s->perm_change_flags = open_flags;
479
+ raw_probe_alignment(bs, ret, &local_err);
480
+ if (local_err) {
481
+ error_propagate(errp, local_err);
482
+ return -EINVAL;
483
}
484
+
485
+ s->perm_change_fd = ret;
486
+ s->perm_change_flags = open_flags;
487
}
488
489
/* Prepare permissions on old fd to avoid conflicts between old and new,
490
@@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
491
return 0;
492
493
fail:
494
- if (s->perm_change_fd && !s->reopen_state) {
495
+ if (s->perm_change_fd) {
496
qemu_close(s->perm_change_fd);
497
}
498
s->perm_change_fd = 0;
499
@@ -XXX,XX +XXX,XX @@ static void raw_abort_perm_update(BlockDriverState *bs)
500
501
/* For reopen, .bdrv_reopen_abort is called afterwards and will close
502
* the file descriptor. */
503
- if (s->perm_change_fd && !s->reopen_state) {
504
+ if (s->perm_change_fd) {
505
qemu_close(s->perm_change_fd);
506
}
507
s->perm_change_fd = 0;
72
--
508
--
73
2.20.1
509
2.30.2
74
510
75
511
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Message-Id: <20210428151804.439460-32-vsementsov@virtuozzo.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
7
---
6
qapi/block-core.json | 4 ++--
8
block.c | 103 --------------------------------------------------------
7
blockdev.c | 12 ++++++------
9
1 file changed, 103 deletions(-)
8
2 files changed, 8 insertions(+), 8 deletions(-)
9
10
10
diff --git a/qapi/block-core.json b/qapi/block-core.json
11
diff --git a/block.c b/block.c
11
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
12
--- a/qapi/block-core.json
13
--- a/block.c
13
+++ b/qapi/block-core.json
14
+++ b/block.c
14
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
15
# If only @device parameter is specified, remove all present latency histograms
16
return 0;
16
# for the device. Otherwise, add/reset some of (or all) latency histograms.
17
#
18
-# @device: device name to set latency histogram for.
19
+# @id: The name or QOM path of the guest device.
20
#
21
# @boundaries: list of interval boundary values (see description in
22
# BlockLatencyHistogramInfo definition). If specified, all
23
@@ -XXX,XX +XXX,XX @@
24
# <- { "return": {} }
25
##
26
{ 'command': 'x-block-latency-histogram-set',
27
- 'data': {'device': 'str',
28
+ 'data': {'id': 'str',
29
'*boundaries': ['uint64'],
30
'*boundaries-read': ['uint64'],
31
'*boundaries-write': ['uint64'],
32
diff --git a/blockdev.c b/blockdev.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/blockdev.c
35
+++ b/blockdev.c
36
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
37
}
17
}
38
18
39
void qmp_x_block_latency_histogram_set(
19
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
40
- const char *device,
20
- uint64_t new_used_perm,
41
+ const char *id,
21
- uint64_t new_shared_perm,
42
bool has_boundaries, uint64List *boundaries,
22
- Error **errp);
43
bool has_boundaries_read, uint64List *boundaries_read,
23
-
44
bool has_boundaries_write, uint64List *boundaries_write,
24
typedef struct BlockReopenQueueEntry {
45
bool has_boundaries_flush, uint64List *boundaries_flush,
25
bool prepared;
46
Error **errp)
26
bool perms_checked;
27
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
28
return 0;
29
}
30
31
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
32
- uint64_t cumulative_perms,
33
- uint64_t cumulative_shared_perms, Error **errp)
34
-{
35
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
36
- return bdrv_check_perm_common(list, q, true, cumulative_perms,
37
- cumulative_shared_perms, NULL, errp);
38
-}
39
-
40
static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
41
Transaction *tran, Error **errp)
47
{
42
{
48
- BlockBackend *blk = blk_by_name(device);
43
return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
49
+ BlockBackend *blk = qmp_get_blk(NULL, id, errp);
44
}
50
BlockAcctStats *stats;
45
51
int ret;
46
-/*
52
47
- * Notifies drivers that after a previous bdrv_check_perm() call, the
53
if (!blk) {
48
- * permission update is not performed and any preparations made for it (e.g.
54
- error_setg(errp, "Device '%s' not found", device);
49
- * taken file locks) need to be undone.
55
return;
50
- */
56
}
51
-static void bdrv_node_abort_perm_update(BlockDriverState *bs)
57
+
52
-{
58
stats = blk_get_stats(blk);
53
- BlockDriver *drv = bs->drv;
59
54
- BdrvChild *c;
60
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
55
-
61
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
56
- if (!drv) {
62
stats, BLOCK_ACCT_READ,
57
- return;
63
has_boundaries_read ? boundaries_read : boundaries);
58
- }
64
if (ret) {
59
-
65
- error_setg(errp, "Device '%s' set read boundaries fail", device);
60
- bdrv_drv_set_perm_abort(bs);
66
+ error_setg(errp, "Device '%s' set read boundaries fail", id);
61
-
67
return;
62
- QLIST_FOREACH(c, &bs->children, next) {
68
}
63
- bdrv_child_set_perm_abort(c);
69
}
64
- }
70
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
65
-}
71
stats, BLOCK_ACCT_WRITE,
66
-
72
has_boundaries_write ? boundaries_write : boundaries);
67
-static void bdrv_list_abort_perm_update(GSList *list)
73
if (ret) {
68
-{
74
- error_setg(errp, "Device '%s' set write boundaries fail", device);
69
- for ( ; list; list = list->next) {
75
+ error_setg(errp, "Device '%s' set write boundaries fail", id);
70
- bdrv_node_abort_perm_update((BlockDriverState *)list->data);
76
return;
71
- }
77
}
72
-}
78
}
73
-
79
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
74
-__attribute__((unused))
80
stats, BLOCK_ACCT_FLUSH,
75
-static void bdrv_abort_perm_update(BlockDriverState *bs)
81
has_boundaries_flush ? boundaries_flush : boundaries);
76
-{
82
if (ret) {
77
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
83
- error_setg(errp, "Device '%s' set flush boundaries fail", device);
78
- return bdrv_list_abort_perm_update(list);
84
+ error_setg(errp, "Device '%s' set flush boundaries fail", id);
79
-}
85
return;
80
-
86
}
81
static void bdrv_node_set_perm(BlockDriverState *bs)
87
}
82
{
83
BlockDriver *drv = bs->drv;
84
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
85
return g_string_free(result, FALSE);
86
}
87
88
-/*
89
- * Checks whether a new reference to @bs can be added if the new user requires
90
- * @new_used_perm/@new_shared_perm as its permissions. If @ignore_children is
91
- * set, the BdrvChild objects in this list are ignored in the calculations;
92
- * this allows checking permission updates for an existing reference.
93
- *
94
- * Needs to be followed by a call to either bdrv_set_perm() or
95
- * bdrv_abort_perm_update(). */
96
-__attribute__((unused))
97
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
98
- uint64_t new_used_perm,
99
- uint64_t new_shared_perm,
100
- Error **errp)
101
-{
102
- BdrvChild *c;
103
- uint64_t cumulative_perms = new_used_perm;
104
- uint64_t cumulative_shared_perms = new_shared_perm;
105
-
106
-
107
- /* There is no reason why anyone couldn't tolerate write_unchanged */
108
- assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
109
-
110
- QLIST_FOREACH(c, &bs->parents, next_parent) {
111
- if ((new_used_perm & c->shared_perm) != new_used_perm) {
112
- char *user = bdrv_child_user_desc(c);
113
- char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
114
-
115
- error_setg(errp, "Conflicts with use by %s as '%s', which does not "
116
- "allow '%s' on %s",
117
- user, c->name, perm_names, bdrv_get_node_name(c->bs));
118
- g_free(user);
119
- g_free(perm_names);
120
- return -EPERM;
121
- }
122
-
123
- if ((c->perm & new_shared_perm) != c->perm) {
124
- char *user = bdrv_child_user_desc(c);
125
- char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
126
-
127
- error_setg(errp, "Conflicts with use by %s as '%s', which uses "
128
- "'%s' on %s",
129
- user, c->name, perm_names, bdrv_get_node_name(c->bs));
130
- g_free(user);
131
- g_free(perm_names);
132
- return -EPERM;
133
- }
134
-
135
- cumulative_perms |= c->perm;
136
- cumulative_shared_perms &= c->shared_perm;
137
- }
138
-
139
- return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
140
- errp);
141
-}
142
143
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
144
{
88
--
145
--
89
2.20.1
146
2.30.2
90
147
91
148
diff view generated by jsdifflib
1
Using a different read-only setting for bs->open_flags than for the
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
flags to the driver's open function is just inconsistent and a bad idea.
3
After this patch, the temporary snapshot keeps being opened read-only if
4
read-only=on,snapshot=on is passed.
5
2
6
If we wanted to change this behaviour to make only the orginal image
3
bdrv_check_perm_common() has only one caller, so no more sense in
7
file read-only, but the temporary overlay read-write (as the comment in
4
"common".
8
the removed code suggests), that change would have to be made in
9
bdrv_temp_snapshot_options() (where the comment suggests otherwise).
10
5
11
Addressing this inconsistency before introducing dynamic auto-read-only
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
is important because otherwise we would immediately try to reopen the
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
temporary overlay even though the file is already unlinked.
8
Message-Id: <20210428151804.439460-33-vsementsov@virtuozzo.com>
14
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
10
---
17
block.c | 7 -------
11
block.c | 32 +++-----------------------------
18
tests/qemu-iotests/051 | 7 +++++++
12
1 file changed, 3 insertions(+), 29 deletions(-)
19
tests/qemu-iotests/051.out | 9 +++++++++
20
tests/qemu-iotests/051.pc.out | 9 +++++++++
21
4 files changed, 25 insertions(+), 7 deletions(-)
22
13
23
diff --git a/block.c b/block.c
14
diff --git a/block.c b/block.c
24
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
25
--- a/block.c
16
--- a/block.c
26
+++ b/block.c
17
+++ b/block.c
27
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
18
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
28
*/
19
return 0;
29
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL);
20
}
30
21
31
- /*
22
-/*
32
- * Snapshots should be writable.
23
- * If use_cumulative_perms is true, use cumulative_perms and
33
- */
24
- * cumulative_shared_perms for first element of the list. Otherwise just refresh
34
- if (flags & BDRV_O_TEMPORARY) {
25
- * all permissions.
35
- open_flags |= BDRV_O_RDWR;
26
- */
27
-static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
28
- bool use_cumulative_perms,
29
- uint64_t cumulative_perms,
30
- uint64_t cumulative_shared_perms,
31
- Transaction *tran, Error **errp)
32
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
33
+ Transaction *tran, Error **errp)
34
{
35
int ret;
36
+ uint64_t cumulative_perms, cumulative_shared_perms;
37
BlockDriverState *bs;
38
39
- if (use_cumulative_perms) {
40
- bs = list->data;
41
-
42
- ret = bdrv_node_check_perm(bs, q, cumulative_perms,
43
- cumulative_shared_perms,
44
- tran, errp);
45
- if (ret < 0) {
46
- return ret;
47
- }
48
-
49
- list = list->next;
36
- }
50
- }
37
-
51
-
38
return open_flags;
52
for ( ; list; list = list->next) {
53
bs = list->data;
54
55
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
56
return 0;
39
}
57
}
40
58
41
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
59
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
42
index XXXXXXX..XXXXXXX 100755
60
- Transaction *tran, Error **errp)
43
--- a/tests/qemu-iotests/051
61
-{
44
+++ b/tests/qemu-iotests/051
62
- return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
45
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io
63
-}
46
# Using snapshot=on with a non-existent TMPDIR
64
-
47
TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on
65
static void bdrv_node_set_perm(BlockDriverState *bs)
48
66
{
49
+# Using snapshot=on together with read-only=on
67
BlockDriver *drv = bs->drv;
50
+echo "info block" |
51
+ run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id |
52
+ _filter_qemu_io |
53
+ sed -e 's#"/[^"]*/vl\.[A-Za-z]\{6\}"#SNAPSHOT_PATH#g'
54
+
55
+
56
# success, all done
57
echo "*** done"
58
rm -f $seq.full
59
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
60
index XXXXXXX..XXXXXXX 100644
61
--- a/tests/qemu-iotests/051.out
62
+++ b/tests/qemu-iotests/051.out
63
@@ -XXX,XX +XXX,XX @@ read 4096/4096 bytes at offset 0
64
Testing: -drive driver=null-co,snapshot=on
65
QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
66
67
+Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
68
+QEMU X.Y.Z monitor - type 'help' for more information
69
+(qemu) info block
70
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only)
71
+ Removable device: not locked, tray closed
72
+ Cache mode: writeback, ignore flushes
73
+ Backing file: TEST_DIR/t.qcow2 (chain depth: 1)
74
+(qemu) quit
75
+
76
*** done
77
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
78
index XXXXXXX..XXXXXXX 100644
79
--- a/tests/qemu-iotests/051.pc.out
80
+++ b/tests/qemu-iotests/051.pc.out
81
@@ -XXX,XX +XXX,XX @@ read 4096/4096 bytes at offset 0
82
Testing: -drive driver=null-co,snapshot=on
83
QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
84
85
+Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
86
+QEMU X.Y.Z monitor - type 'help' for more information
87
+(qemu) info block
88
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only)
89
+ Removable device: not locked, tray closed
90
+ Cache mode: writeback, ignore flushes
91
+ Backing file: TEST_DIR/t.qcow2 (chain depth: 1)
92
+(qemu) quit
93
+
94
*** done
95
--
68
--
96
2.20.1
69
2.30.2
97
70
98
71
diff view generated by jsdifflib
1
Until now, with auto-read-only=on we tried to open the file read-write
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
first and if that failed, read-only was tried. This is actually not good
3
enough for libvirt, which gives QEMU SELinux permissions for read-write
4
only as soon as it actually intends to write to the image. So we need to
5
be able to switch between read-only and read-write at runtime.
6
2
7
This patch makes auto-read-only dynamic, i.e. the file is opened
3
bdrv_replace_child() has only one caller, the second argument is
8
read-only as long as no user of the node has requested write
4
unused. Inline it now. This triggers deletion of some more unused
9
permissions, but it is automatically reopened read-write as soon as the
5
interfaces.
10
first writer is attached. Conversely, if the last writer goes away, the
11
file is reopened read-only again.
12
6
13
bs->read_only is no longer set for auto-read-only=on files even if the
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
file descriptor is opened read-only because it will be transparently
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
upgraded as soon as a writer is attached. This changes the output of
9
Message-Id: <20210428151804.439460-34-vsementsov@virtuozzo.com>
16
qemu-iotests 232.
17
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
11
---
20
block/file-posix.c | 36 +++++++++++++++++-------------------
12
block.c | 101 ++++++++++----------------------------------------------
21
tests/qemu-iotests/232.out | 12 ++++++------
13
1 file changed, 18 insertions(+), 83 deletions(-)
22
2 files changed, 23 insertions(+), 25 deletions(-)
23
14
24
diff --git a/block/file-posix.c b/block/file-posix.c
15
diff --git a/block.c b/block.c
25
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
26
--- a/block/file-posix.c
17
--- a/block.c
27
+++ b/block/file-posix.c
18
+++ b/block.c
28
@@ -XXX,XX +XXX,XX @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
19
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
20
return 0;
21
}
22
23
-static void bdrv_node_set_perm(BlockDriverState *bs)
24
-{
25
- BlockDriver *drv = bs->drv;
26
- BdrvChild *c;
27
-
28
- if (!drv) {
29
- return;
30
- }
31
-
32
- bdrv_drv_set_perm_commit(bs);
33
-
34
- /* Drivers that never have children can omit .bdrv_child_perm() */
35
- if (!drv->bdrv_child_perm) {
36
- assert(QLIST_EMPTY(&bs->children));
37
- return;
38
- }
39
-
40
- /* Update all children */
41
- QLIST_FOREACH(c, &bs->children, next) {
42
- bdrv_child_set_perm_commit(c);
43
- }
44
-}
45
-
46
-static void bdrv_list_set_perm(GSList *list)
47
-{
48
- for ( ; list; list = list->next) {
49
- bdrv_node_set_perm((BlockDriverState *)list->data);
50
- }
51
-}
52
-
53
-static void bdrv_set_perm(BlockDriverState *bs)
54
-{
55
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
56
- return bdrv_list_set_perm(list);
57
-}
58
-
59
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
60
uint64_t *shared_perm)
61
{
62
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
29
}
63
}
30
}
64
}
31
65
32
-static void raw_parse_flags(int bdrv_flags, int *open_flags)
66
-/*
33
+static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers)
67
- * Updates @child to change its reference to point to @new_bs, including
34
{
68
- * checking and applying the necessary permission updates both to the old node
35
+ bool read_write = false;
69
- * and to @new_bs.
36
assert(open_flags != NULL);
70
- *
37
71
- * NULL is passed as @new_bs for removing the reference before freeing @child.
38
*open_flags |= O_BINARY;
72
- *
39
*open_flags &= ~O_ACCMODE;
73
- * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this
40
- if (bdrv_flags & BDRV_O_RDWR) {
74
- * function uses bdrv_set_perm() to update the permissions according to the new
41
+
75
- * reference that @new_bs gets.
42
+ if (bdrv_flags & BDRV_O_AUTO_RDONLY) {
76
- *
43
+ read_write = has_writers;
77
- * Callers must ensure that child->frozen is false.
44
+ } else if (bdrv_flags & BDRV_O_RDWR) {
78
- */
45
+ read_write = true;
79
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
46
+ }
80
-{
47
+
81
- BlockDriverState *old_bs = child->bs;
48
+ if (read_write) {
82
-
49
*open_flags |= O_RDWR;
83
- /* Asserts that child->frozen == false */
50
} else {
84
- bdrv_replace_child_noperm(child, new_bs);
51
*open_flags |= O_RDONLY;
85
-
52
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
86
- /*
53
false);
87
- * Start with the new node's permissions. If @new_bs is a (direct
54
88
- * or indirect) child of @old_bs, we must complete the permission
55
s->open_flags = open_flags;
89
- * update on @new_bs before we loosen the restrictions on @old_bs.
56
- raw_parse_flags(bdrv_flags, &s->open_flags);
90
- * Otherwise, bdrv_check_perm() on @old_bs would re-initiate
57
+ raw_parse_flags(bdrv_flags, &s->open_flags, false);
91
- * updating the permissions of @new_bs, and thus not purely loosen
58
92
- * restrictions.
59
s->fd = -1;
93
- */
60
fd = qemu_open(filename, s->open_flags, 0644);
94
- if (new_bs) {
61
ret = fd < 0 ? -errno : 0;
95
- bdrv_set_perm(new_bs);
62
63
- if (ret == -EACCES || ret == -EROFS) {
64
- /* Try to degrade to read-only, but if it doesn't work, still use the
65
- * normal error message. */
66
- if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
67
- bdrv_flags &= ~BDRV_O_RDWR;
68
- raw_parse_flags(bdrv_flags, &s->open_flags);
69
- assert(!(s->open_flags & O_CREAT));
70
- fd = qemu_open(filename, s->open_flags);
71
- ret = fd < 0 ? -errno : 0;
72
- }
73
- }
96
- }
74
-
97
-
75
if (ret < 0) {
98
- if (old_bs) {
76
error_setg_errno(errp, -ret, "Could not open '%s'", filename);
99
- /*
77
if (ret == -EROFS) {
100
- * Update permissions for old node. We're just taking a parent away, so
78
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
101
- * we're loosening restrictions. Errors of permission update are not
102
- * fatal in this case, ignore them.
103
- */
104
- bdrv_refresh_perms(old_bs, NULL);
105
-
106
- /* When the parent requiring a non-default AioContext is removed, the
107
- * node moves back to the main AioContext */
108
- bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
109
- }
110
-}
111
-
112
static void bdrv_child_free(void *opaque)
113
{
114
BdrvChild *c = opaque;
115
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
116
117
static void bdrv_detach_child(BdrvChild *child)
118
{
119
- bdrv_replace_child(child, NULL);
120
+ BlockDriverState *old_bs = child->bs;
121
+
122
+ bdrv_replace_child_noperm(child, NULL);
123
bdrv_remove_empty_child(child);
124
+
125
+ if (old_bs) {
126
+ /*
127
+ * Update permissions for old node. We're just taking a parent away, so
128
+ * we're loosening restrictions. Errors of permission update are not
129
+ * fatal in this case, ignore them.
130
+ */
131
+ bdrv_refresh_perms(old_bs, NULL);
132
+
133
+ /*
134
+ * When the parent requiring a non-default AioContext is removed, the
135
+ * node moves back to the main AioContext
136
+ */
137
+ bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
138
+ }
79
}
139
}
80
140
81
static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
141
/*
82
- int *open_flags, bool force_dup,
83
+ int *open_flags, uint64_t perm, bool force_dup,
84
Error **errp)
85
{
86
BDRVRawState *s = bs->opaque;
87
int fd = -1;
88
int ret;
89
+ bool has_writers = perm &
90
+ (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE);
91
int fcntl_flags = O_APPEND | O_NONBLOCK;
92
#ifdef O_NOATIME
93
fcntl_flags |= O_NOATIME;
94
@@ -XXX,XX +XXX,XX @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
95
*open_flags |= O_NONBLOCK;
96
}
97
98
- raw_parse_flags(flags, open_flags);
99
+ raw_parse_flags(flags, open_flags, has_writers);
100
101
#ifdef O_ASYNC
102
/* Not all operating systems have O_ASYNC, and those that don't
103
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
104
qemu_opts_to_qdict(opts, state->options);
105
106
rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
107
- true, &local_err);
108
+ state->perm, true, &local_err);
109
if (local_err) {
110
error_propagate(errp, local_err);
111
ret = -1;
112
@@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
113
s->perm_change_fd = rs->fd;
114
} else {
115
/* We may need a new fd if auto-read-only switches the mode */
116
- ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags,
117
+ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
118
false, errp);
119
if (ret < 0) {
120
return ret;
121
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
122
index XXXXXXX..XXXXXXX 100644
123
--- a/tests/qemu-iotests/232.out
124
+++ b/tests/qemu-iotests/232.out
125
@@ -XXX,XX +XXX,XX @@ NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
126
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
127
128
QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
129
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
130
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
131
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
132
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
133
134
QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
135
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
136
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
137
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
138
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
139
140
=== -blockdev with read-write image: read-only/auto-read-only combinations ===
141
142
@@ -XXX,XX +XXX,XX @@ node0: TEST_DIR/t.IMGFMT (file, read-only)
143
node0: TEST_DIR/t.IMGFMT (file, read-only)
144
145
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
146
-node0: TEST_DIR/t.IMGFMT (file, read-only)
147
+node0: TEST_DIR/t.IMGFMT (file)
148
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
149
150
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
151
-node0: TEST_DIR/t.IMGFMT (file, read-only)
152
+node0: TEST_DIR/t.IMGFMT (file)
153
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
154
155
=== Try commit to backing file with auto-read-only ===
156
--
142
--
157
2.20.1
143
2.30.2
158
144
159
145
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
If we reopen a BlockDriverState and there is an option that is present
3
Old interfaces dropped, nobody directly calls
4
in bs->options but missing from the new set of options then we have to
4
bdrv_child_set_perm_abort() and bdrv_child_set_perm_commit(), so we can
5
return an error unless the driver is able to reset it to its default
5
use personal state structure for the action and stop exploiting
6
value.
6
BdrvChild structure. Also, drop "_safe" suffix which is redundant now.
7
7
8
This patch adds a new 'mutable_opts' field to BlockDriver. This is
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
a list of runtime options that can be modified during reopen. If an
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
option in this list is unspecified on reopen then it must be reset (or
10
Message-Id: <20210428151804.439460-35-vsementsov@virtuozzo.com>
11
return an error).
12
13
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
12
---
16
include/block/block_int.h | 8 ++++++++
13
include/block/block_int.h | 5 ----
17
block/file-posix.c | 6 ++++++
14
block.c | 63 ++++++++++++++-------------------------
18
block/qcow2.c | 25 +++++++++++++++++++++++++
15
2 files changed, 22 insertions(+), 46 deletions(-)
19
block/raw-format.c | 3 +++
20
4 files changed, 42 insertions(+)
21
16
22
diff --git a/include/block/block_int.h b/include/block/block_int.h
17
diff --git a/include/block/block_int.h b/include/block/block_int.h
23
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/block_int.h
19
--- a/include/block/block_int.h
25
+++ b/include/block/block_int.h
20
+++ b/include/block/block_int.h
26
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
21
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
27
22
*/
28
/* List of options for creating images, terminated by name == NULL */
23
uint64_t shared_perm;
29
QemuOptsList *create_opts;
24
30
+ /*
25
- /* backup of permissions during permission update procedure */
31
+ * If this driver supports reopening images this contains a
26
- bool has_backup_perm;
32
+ * NULL-terminated list of the runtime options that can be
27
- uint64_t backup_perm;
33
+ * modified. If an option in this list is unspecified during
28
- uint64_t backup_shared_perm;
34
+ * reopen then it _must_ be reset to its default value or return
29
-
35
+ * an error.
36
+ */
37
+ const char *const *mutable_opts;
38
39
/*
30
/*
40
* Returns 0 for completed check, -errno for internal errors.
31
* This link is frozen: the child can neither be replaced nor
41
diff --git a/block/file-posix.c b/block/file-posix.c
32
* detached from the parent.
33
diff --git a/block.c b/block.c
42
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
43
--- a/block/file-posix.c
35
--- a/block.c
44
+++ b/block/file-posix.c
36
+++ b/block.c
45
@@ -XXX,XX +XXX,XX @@ static QemuOptsList raw_runtime_opts = {
37
@@ -XXX,XX +XXX,XX @@ static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
46
},
38
return g_slist_prepend(list, bs);
39
}
40
41
-static void bdrv_child_set_perm_commit(void *opaque)
42
-{
43
- BdrvChild *c = opaque;
44
-
45
- c->has_backup_perm = false;
46
-}
47
+typedef struct BdrvChildSetPermState {
48
+ BdrvChild *child;
49
+ uint64_t old_perm;
50
+ uint64_t old_shared_perm;
51
+} BdrvChildSetPermState;
52
53
static void bdrv_child_set_perm_abort(void *opaque)
54
{
55
- BdrvChild *c = opaque;
56
- /*
57
- * We may have child->has_backup_perm unset at this point, as in case of
58
- * _check_ stage of permission update failure we may _check_ not the whole
59
- * subtree. Still, _abort_ is called on the whole subtree anyway.
60
- */
61
- if (c->has_backup_perm) {
62
- c->perm = c->backup_perm;
63
- c->shared_perm = c->backup_shared_perm;
64
- c->has_backup_perm = false;
65
- }
66
+ BdrvChildSetPermState *s = opaque;
67
+
68
+ s->child->perm = s->old_perm;
69
+ s->child->shared_perm = s->old_shared_perm;
70
}
71
72
static TransactionActionDrv bdrv_child_set_pem_drv = {
73
.abort = bdrv_child_set_perm_abort,
74
- .commit = bdrv_child_set_perm_commit,
75
+ .clean = g_free,
47
};
76
};
48
77
49
+static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL };
78
-/*
79
- * With tran=NULL needs to be followed by direct call to either
80
- * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort().
81
- *
82
- * With non-NULL tran needs to be followed by tran_abort() or tran_commit()
83
- * instead.
84
- */
85
-static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
86
- uint64_t shared, Transaction *tran)
87
+static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm,
88
+ uint64_t shared, Transaction *tran)
89
{
90
- if (!c->has_backup_perm) {
91
- c->has_backup_perm = true;
92
- c->backup_perm = c->perm;
93
- c->backup_shared_perm = c->shared_perm;
94
- }
95
- /*
96
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
97
- * same c twice during check_perm procedure
98
- */
99
+ BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1);
50
+
100
+
51
static int raw_open_common(BlockDriverState *bs, QDict *options,
101
+ *s = (BdrvChildSetPermState) {
52
int bdrv_flags, int open_flags,
102
+ .child = c,
53
bool device, Error **errp)
103
+ .old_perm = c->perm,
54
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
104
+ .old_shared_perm = c->shared_perm,
55
.bdrv_set_perm = raw_set_perm,
105
+ };
56
.bdrv_abort_perm_update = raw_abort_perm_update,
106
57
.create_opts = &raw_create_opts,
107
c->perm = perm;
58
+ .mutable_opts = mutable_opts,
108
c->shared_perm = shared;
59
};
109
60
110
- if (tran) {
61
/***********************************************/
111
- tran_add(tran, &bdrv_child_set_pem_drv, c);
62
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
112
- }
63
.bdrv_reopen_abort = raw_reopen_abort,
113
+ tran_add(tran, &bdrv_child_set_pem_drv, s);
64
.bdrv_co_create_opts = hdev_co_create_opts,
114
}
65
.create_opts = &raw_create_opts,
115
66
+ .mutable_opts = mutable_opts,
116
static void bdrv_drv_set_perm_commit(void *opaque)
67
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
117
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
68
.bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
118
bdrv_child_perm(bs, c->bs, c, c->role, q,
69
119
cumulative_perms, cumulative_shared_perms,
70
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
120
&cur_perm, &cur_shared);
71
.bdrv_reopen_abort = raw_reopen_abort,
121
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran);
72
.bdrv_co_create_opts = hdev_co_create_opts,
122
+ bdrv_child_set_perm(c, cur_perm, cur_shared, tran);
73
.create_opts = &raw_create_opts,
123
}
74
+ .mutable_opts = mutable_opts,
124
75
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
76
77
78
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
79
.bdrv_reopen_abort = raw_reopen_abort,
80
.bdrv_co_create_opts = hdev_co_create_opts,
81
.create_opts = &raw_create_opts,
82
+ .mutable_opts = mutable_opts,
83
84
.bdrv_co_preadv = raw_co_preadv,
85
.bdrv_co_pwritev = raw_co_pwritev,
86
diff --git a/block/qcow2.c b/block/qcow2.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/qcow2.c
89
+++ b/block/qcow2.c
90
@@ -XXX,XX +XXX,XX @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
91
return 0;
125
return 0;
92
}
126
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
93
127
Transaction *tran = tran_new();
94
+static const char *const mutable_opts[] = {
128
int ret;
95
+ QCOW2_OPT_LAZY_REFCOUNTS,
129
96
+ QCOW2_OPT_DISCARD_REQUEST,
130
- bdrv_child_set_perm_safe(c, perm, shared, tran);
97
+ QCOW2_OPT_DISCARD_SNAPSHOT,
131
+ bdrv_child_set_perm(c, perm, shared, tran);
98
+ QCOW2_OPT_DISCARD_OTHER,
132
99
+ QCOW2_OPT_OVERLAP,
133
ret = bdrv_refresh_perms(c->bs, &local_err);
100
+ QCOW2_OPT_OVERLAP_TEMPLATE,
134
101
+ QCOW2_OPT_OVERLAP_MAIN_HEADER,
102
+ QCOW2_OPT_OVERLAP_ACTIVE_L1,
103
+ QCOW2_OPT_OVERLAP_ACTIVE_L2,
104
+ QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
105
+ QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
106
+ QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
107
+ QCOW2_OPT_OVERLAP_INACTIVE_L1,
108
+ QCOW2_OPT_OVERLAP_INACTIVE_L2,
109
+ QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
110
+ QCOW2_OPT_CACHE_SIZE,
111
+ QCOW2_OPT_L2_CACHE_SIZE,
112
+ QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
113
+ QCOW2_OPT_REFCOUNT_CACHE_SIZE,
114
+ QCOW2_OPT_CACHE_CLEAN_INTERVAL,
115
+ NULL
116
+};
117
+
118
static QemuOptsList qcow2_runtime_opts = {
119
.name = "qcow2",
120
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
121
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
122
123
.create_opts = &qcow2_create_opts,
124
.strong_runtime_opts = qcow2_strong_runtime_opts,
125
+ .mutable_opts = mutable_opts,
126
.bdrv_co_check = qcow2_co_check,
127
.bdrv_amend_options = qcow2_amend_options,
128
129
diff --git a/block/raw-format.c b/block/raw-format.c
130
index XXXXXXX..XXXXXXX 100644
131
--- a/block/raw-format.c
132
+++ b/block/raw-format.c
133
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
134
bool has_size;
135
} BDRVRawState;
136
137
+static const char *const mutable_opts[] = { "offset", "size", NULL };
138
+
139
static QemuOptsList raw_runtime_opts = {
140
.name = "raw",
141
.head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
142
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
143
.create_opts = &raw_create_opts,
144
.bdrv_has_zero_init = &raw_has_zero_init,
145
.strong_runtime_opts = raw_strong_runtime_opts,
146
+ .mutable_opts = mutable_opts,
147
};
148
149
static void bdrv_raw_init(void)
150
--
135
--
151
2.20.1
136
2.30.2
152
137
153
138
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
We don't have bdrv_replace_child(), so it's time for
4
bdrv_replace_child_safe() to take its place.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-36-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block.c | 10 +++++-----
12
1 file changed, 5 insertions(+), 5 deletions(-)
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 @@ static TransactionActionDrv bdrv_replace_child_drv = {
19
};
20
21
/*
22
- * bdrv_replace_child_safe
23
+ * bdrv_replace_child
24
*
25
* Note: real unref of old_bs is done only on commit.
26
*/
27
-static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
28
- Transaction *tran)
29
+static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
30
+ Transaction *tran)
31
{
32
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
33
*s = (BdrvReplaceChildState) {
34
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
35
}
36
37
if (child->bs) {
38
- bdrv_replace_child_safe(child, NULL, tran);
39
+ bdrv_replace_child(child, NULL, tran);
40
}
41
42
s = g_new(BdrvRemoveFilterOrCowChild, 1);
43
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
44
c->name, from->node_name);
45
return -EPERM;
46
}
47
- bdrv_replace_child_safe(c, to, tran);
48
+ bdrv_replace_child(c, to, tran);
49
}
50
51
return 0;
52
--
53
2.30.2
54
55
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Of all options of type BlockdevRef used to specify children in
3
Now, bdrv_node_check_perm() is called only with fresh cumulative
4
BlockdevOptions, 'backing' is the only one that is optional.
4
permissions, so its actually "refresh_perm".
5
5
6
For "x-blockdev-reopen" we want that if an option is omitted then it
6
Move permission calculation to the function. Also, drop unreachable
7
must be reset to its default value. The default value of 'backing'
7
error message and rewrite the remaining one to be more generic (as now
8
means that QEMU opens the backing file specified in the image
8
we don't know which node is added and which was already here).
9
metadata, but this is not something that we want to support for the
10
reopen operation.
11
9
12
Because of this the 'backing' option has to be specified during
10
Add also Virtuozzo copyright, as big work is done at this point.
13
reopen, pointing to the existing backing file if we want to keep it,
14
or pointing to a different one (or NULL) if we want to replace it (to
15
be implemented in a subsequent patch).
16
11
17
In order to simplify things a bit and not to require that the user
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
passes the 'backing' option to every single block device even when
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
19
it's clearly not necessary, this patch allows omitting this option if
14
Message-Id: <20210428151804.439460-37-vsementsov@virtuozzo.com>
20
the block device being reopened doesn't have a backing file attached
21
_and_ no default backing file is specified in the image metadata.
22
23
Signed-off-by: Alberto Garcia <berto@igalia.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
16
---
26
block.c | 8 +++++++-
17
block.c | 38 +++++++++++---------------------------
27
1 file changed, 7 insertions(+), 1 deletion(-)
18
tests/qemu-iotests/245 | 2 +-
19
2 files changed, 12 insertions(+), 28 deletions(-)
28
20
29
diff --git a/block.c b/block.c
21
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
23
--- a/block.c
32
+++ b/block.c
24
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
25
@@ -XXX,XX +XXX,XX @@
34
26
* QEMU System Emulator block driver
35
drv_prepared = true;
27
*
36
28
* Copyright (c) 2003 Fabrice Bellard
37
- if (drv->supports_backing && reopen_state->backing_missing) {
29
+ * Copyright (c) 2020 Virtuozzo International GmbH.
38
+ /*
30
*
39
+ * We must provide the 'backing' option if the BDS has a backing
31
* Permission is hereby granted, free of charge, to any person obtaining a copy
40
+ * file or if the image file has a backing file name as part of
32
* of this software and associated documentation files (the "Software"), to deal
41
+ * its metadata. Otherwise the 'backing' option can be omitted.
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
42
+ */
34
}
43
+ if (drv->supports_backing && reopen_state->backing_missing &&
35
44
+ (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) {
36
/*
45
error_setg(errp, "backing is missing for '%s'",
37
- * Check whether permissions on this node can be changed in a way that
46
reopen_state->bs->node_name);
38
- * @cumulative_perms and @cumulative_shared_perms are the new cumulative
47
ret = -EINVAL;
39
- * permissions of all its parents. This involves checking whether all necessary
40
- * permission changes to child nodes can be performed.
41
- *
42
- * A call to this function must always be followed by a call to bdrv_set_perm()
43
- * or bdrv_abort_perm_update().
44
+ * Refresh permissions in @bs subtree. The function is intended to be called
45
+ * after some graph modification that was done without permission update.
46
*/
47
-static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
48
- uint64_t cumulative_perms,
49
- uint64_t cumulative_shared_perms,
50
- Transaction *tran, Error **errp)
51
+static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
52
+ Transaction *tran, Error **errp)
53
{
54
BlockDriver *drv = bs->drv;
55
BdrvChild *c;
56
int ret;
57
+ uint64_t cumulative_perms, cumulative_shared_perms;
58
+
59
+ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
60
61
/* Write permissions never work with read-only images */
62
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
63
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
64
if (!bdrv_is_writable_after_reopen(bs, NULL)) {
65
error_setg(errp, "Block node is read-only");
66
} else {
67
- uint64_t current_perms, current_shared;
68
- bdrv_get_cumulative_perm(bs, &current_perms, &current_shared);
69
- if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
70
- error_setg(errp, "Cannot make block node read-only, there is "
71
- "a writer on it");
72
- } else {
73
- error_setg(errp, "Cannot make block node read-only and create "
74
- "a writer on it");
75
- }
76
+ error_setg(errp, "Read-only block node '%s' cannot support "
77
+ "read-write users", bdrv_get_node_name(bs));
78
}
79
80
return -EPERM;
81
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
82
Transaction *tran, Error **errp)
83
{
84
int ret;
85
- uint64_t cumulative_perms, cumulative_shared_perms;
86
BlockDriverState *bs;
87
88
for ( ; list; list = list->next) {
89
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
90
return -EINVAL;
91
}
92
93
- bdrv_get_cumulative_perm(bs, &cumulative_perms,
94
- &cumulative_shared_perms);
95
-
96
- ret = bdrv_node_check_perm(bs, q, cumulative_perms,
97
- cumulative_shared_perms,
98
- tran, errp);
99
+ ret = bdrv_node_refresh_perm(bs, q, tran, errp);
100
if (ret < 0) {
101
return ret;
102
}
103
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
104
index XXXXXXX..XXXXXXX 100755
105
--- a/tests/qemu-iotests/245
106
+++ b/tests/qemu-iotests/245
107
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
108
# We can't reopen hd1 to read-only, as block-stream requires it to be
109
# read-write
110
self.reopen(opts['backing'], {'read-only': True},
111
- "Cannot make block node read-only, there is a writer on it")
112
+ "Read-only block node 'hd1' cannot support read-write users")
113
114
# We can't remove hd2 while the stream job is ongoing
115
opts['backing']['backing'] = None
48
--
116
--
49
2.20.1
117
2.30.2
50
118
51
119
diff view generated by jsdifflib
1
Normally, blk_new_open() just shares all permissions. This was fine
2
originally when permissions only protected against uses in the same
3
process because no other part of the code would actually get to access
4
the block nodes opened with blk_new_open(). However, since we use it for
5
file locking now, unsharing permissions becomes desirable.
6
7
Add a new BDRV_O_NO_SHARE flag that is used in blk_new_open() to unshare
8
any permissions that can be unshared.
9
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20210422164344.283389-2-kwolf@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
---
15
---
4
tests/qemu-iotests/232 | 31 +++++++++++++++++++++++++++++++
16
include/block/block.h | 1 +
5
tests/qemu-iotests/232.out | 20 ++++++++++++++++++++
17
block/block-backend.c | 19 +++++++++++++------
6
2 files changed, 51 insertions(+)
18
2 files changed, 14 insertions(+), 6 deletions(-)
7
19
8
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
20
diff --git a/include/block/block.h b/include/block/block.h
9
index XXXXXXX..XXXXXXX 100755
10
--- a/tests/qemu-iotests/232
11
+++ b/tests/qemu-iotests/232
12
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
13
_cleanup()
14
{
15
_cleanup_test_img
16
+ rm -f $TEST_IMG.[01234]
17
}
18
trap "_cleanup; exit \$status" 0 1 2 3 15
19
20
@@ -XXX,XX +XXX,XX @@ run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,a
21
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on
22
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0
23
24
+echo
25
+echo "=== Try commit to backing file with auto-read-only ==="
26
+echo
27
+
28
+TEST_IMG="$TEST_IMG.0" _make_test_img $size
29
+TEST_IMG="$TEST_IMG.1" _make_test_img $size
30
+TEST_IMG="$TEST_IMG.2" _make_test_img $size
31
+TEST_IMG="$TEST_IMG.3" _make_test_img $size
32
+TEST_IMG="$TEST_IMG.4" _make_test_img $size
33
+
34
+(cat <<EOF
35
+{"execute":"qmp_capabilities"}
36
+{"execute":"block-commit",
37
+ "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}}
38
+EOF
39
+sleep 1
40
+echo '{"execute":"quit"}'
41
+) | $QEMU -qmp stdio -nographic -nodefaults \
42
+ -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \
43
+ -blockdev qcow2,node-name=format-0,file=file-0,read-only=on \
44
+ -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \
45
+ -blockdev qcow2,node-name=format-1,file=file-1,read-only=on,backing=format-0 \
46
+ -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \
47
+ -blockdev qcow2,node-name=format-2,file=file-2,read-only=on,backing=format-1 \
48
+ -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \
49
+ -blockdev qcow2,node-name=format-3,file=file-3,read-only=on,backing=format-2 \
50
+ -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \
51
+ -blockdev qcow2,node-name=format-4,file=file-4,read-only=on,backing=format-3 |
52
+ _filter_qmp
53
+
54
# success, all done
55
echo "*** done"
56
rm -f $seq.full
57
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
58
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
59
--- a/tests/qemu-iotests/232.out
22
--- a/include/block/block.h
60
+++ b/tests/qemu-iotests/232.out
23
+++ b/include/block/block.h
61
@@ -XXX,XX +XXX,XX @@ QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read
24
@@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry {
62
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
25
uint32_t cylinders;
63
node0: TEST_DIR/t.IMGFMT (file, read-only)
26
} HDGeometry;
64
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
27
65
+
28
+#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */
66
+=== Try commit to backing file with auto-read-only ===
29
#define BDRV_O_RDWR 0x0002
67
+
30
#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */
68
+Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728
31
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
69
+Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728
32
diff --git a/block/block-backend.c b/block/block-backend.c
70
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728
33
index XXXXXXX..XXXXXXX 100644
71
+Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728
34
--- a/block/block-backend.c
72
+Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728
35
+++ b/block/block-backend.c
73
+QMP_VERSION
36
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
74
+{"return": {}}
37
BlockBackend *blk;
75
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
38
BlockDriverState *bs;
76
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
39
uint64_t perm = 0;
77
+{"return": {}}
40
+ uint64_t shared = BLK_PERM_ALL;
78
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
41
79
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
42
- /* blk_new_open() is mainly used in .bdrv_create implementations and the
80
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
43
- * tools where sharing isn't a concern because the BDS stays private, so we
81
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
44
- * just request permission according to the flags.
82
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
45
+ /*
83
+{"return": {}}
46
+ * blk_new_open() is mainly used in .bdrv_create implementations and the
84
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
47
+ * tools where sharing isn't a major concern because the BDS stays private
85
*** done
48
+ * and the file is generally not supposed to be used by a second process,
49
+ * so we just request permission according to the flags.
50
*
51
* The exceptions are xen_disk and blockdev_init(); in these cases, the
52
* caller of blk_new_open() doesn't make use of the permissions, but they
53
* shouldn't hurt either. We can still share everything here because the
54
- * guest devices will add their own blockers if they can't share. */
55
+ * guest devices will add their own blockers if they can't share.
56
+ */
57
if ((flags & BDRV_O_NO_IO) == 0) {
58
perm |= BLK_PERM_CONSISTENT_READ;
59
if (flags & BDRV_O_RDWR) {
60
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
61
if (flags & BDRV_O_RESIZE) {
62
perm |= BLK_PERM_RESIZE;
63
}
64
+ if (flags & BDRV_O_NO_SHARE) {
65
+ shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
66
+ }
67
68
- blk = blk_new(qemu_get_aio_context(), perm, BLK_PERM_ALL);
69
+ blk = blk_new(qemu_get_aio_context(), perm, shared);
70
bs = bdrv_open(filename, reference, options, flags, errp);
71
if (!bs) {
72
blk_unref(blk);
73
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
74
75
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
76
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
77
- perm, BLK_PERM_ALL, blk, errp);
78
+ perm, shared, blk, errp);
79
if (!blk->root) {
80
blk_unref(blk);
81
return NULL;
86
--
82
--
87
2.20.1
83
2.30.2
88
84
89
85
diff view generated by jsdifflib
1
tests/virtio-blk-test uses a temporary image file that it deletes while
1
For a successful conversion of an image, we must make sure that its
2
QEMU is still running, so it can't be reopened when writers are
2
content doesn't change during the conversion.
3
attached or detached. Disable auto-read-only to keep it always writable.
4
3
4
A special case of this is using the same image file both as the source
5
and as the destination. If both input and output format are raw, the
6
operation would just be useless work, with other formats it is a sure
7
way to destroy the image. This will now fail because the image file
8
can't be opened a second time for the output when opening it for the
9
input has already acquired file locks to unshare BLK_PERM_WRITE.
10
11
Nevertheless, if there is some reason in a special case why it is
12
actually okay to allow writes to the image while it is being converted,
13
-U can still be used to force sharing all permissions.
14
15
Note that for most image formats, BLK_PERM_WRITE would already be
16
unshared by the format driver, so this only really makes a difference
17
for raw source images (but any output format).
18
19
Reported-by: Xueqiang Wei <xuwei@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
22
Message-Id: <20210422164344.283389-3-kwolf@redhat.com>
23
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
25
---
8
tests/virtio-blk-test.c | 2 +-
26
qemu-img.c | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
27
1 file changed, 1 insertion(+), 1 deletion(-)
10
28
11
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
29
diff --git a/qemu-img.c b/qemu-img.c
12
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/virtio-blk-test.c
31
--- a/qemu-img.c
14
+++ b/tests/virtio-blk-test.c
32
+++ b/qemu-img.c
15
@@ -XXX,XX +XXX,XX @@ static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
33
@@ -XXX,XX +XXX,XX @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit)
16
char *tmp_path = drive_create();
34
17
35
static int img_convert(int argc, char **argv)
18
g_string_append_printf(cmd_line,
36
{
19
- " -drive if=none,id=drive0,file=%s,format=raw "
37
- int c, bs_i, flags, src_flags = 0;
20
+ " -drive if=none,id=drive0,file=%s,format=raw,auto-read-only=off "
38
+ int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE;
21
"-drive if=none,id=drive1,file=null-co://,format=raw ",
39
const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe",
22
tmp_path);
40
*src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL,
23
41
*out_filename, *out_baseimg_param, *snapshot_name = NULL;
24
--
42
--
25
2.20.1
43
2.30.2
26
44
27
45
diff view generated by jsdifflib
1
From: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
1
virtio_add_queue() aborts when queue_size > VIRTQUEUE_MAX_SIZE, so
2
vhost_user_blk_device_realize() should check this before calling it.
2
3
3
New versions of Glusters libgfapi.so have an updated glfs_ftruncate()
4
Simple reproducer:
4
function that returns additional 'struct stat' structures to enable
5
advanced caching of attributes. This is useful for file servers, not so
6
much for QEMU. Nevertheless, the API has changed and needs to be
7
adopted.
8
5
9
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
6
qemu-system-x86_64 \
10
Signed-off-by: Niels de Vos <ndevos@redhat.com>
7
-chardev null,id=foo \
8
-device vhost-user-blk-pci,queue-size=4096,chardev=foo
9
10
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1935014
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20210413165654.50810-1-kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
15
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
16
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
18
---
13
configure | 18 ++++++++++++++++++
19
hw/block/vhost-user-blk.c | 5 +++++
14
block/gluster.c | 4 ++++
20
1 file changed, 5 insertions(+)
15
2 files changed, 22 insertions(+)
16
21
17
diff --git a/configure b/configure
22
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
18
index XXXXXXX..XXXXXXX 100755
19
--- a/configure
20
+++ b/configure
21
@@ -XXX,XX +XXX,XX @@ glusterfs_xlator_opt="no"
22
glusterfs_discard="no"
23
glusterfs_fallocate="no"
24
glusterfs_zerofill="no"
25
+glusterfs_ftruncate_has_stat="no"
26
gtk=""
27
gtk_gl="no"
28
tls_priority="NORMAL"
29
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs" != "no" ; then
30
glusterfs_fallocate="yes"
31
glusterfs_zerofill="yes"
32
fi
33
+ cat > $TMPC << EOF
34
+#include <glusterfs/api/glfs.h>
35
+
36
+int
37
+main(void)
38
+{
39
+    /* new glfs_ftruncate() passes two additional args */
40
+    return glfs_ftruncate(NULL, 0, NULL, NULL);
41
+}
42
+EOF
43
+ if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
44
+ glusterfs_ftruncate_has_stat="yes"
45
+ fi
46
else
47
if test "$glusterfs" = "yes" ; then
48
feature_not_found "GlusterFS backend support" \
49
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs_zerofill" = "yes" ; then
50
echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
51
fi
52
53
+if test "$glusterfs_ftruncate_has_stat" = "yes" ; then
54
+ echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak
55
+fi
56
+
57
if test "$libssh2" = "yes" ; then
58
echo "CONFIG_LIBSSH2=m" >> $config_host_mak
59
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
60
diff --git a/block/gluster.c b/block/gluster.c
61
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
62
--- a/block/gluster.c
24
--- a/hw/block/vhost-user-blk.c
63
+++ b/block/gluster.c
25
+++ b/hw/block/vhost-user-blk.c
64
@@ -XXX,XX +XXX,XX @@
26
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
65
#include "qemu/option.h"
27
error_setg(errp, "vhost-user-blk: queue size must be non-zero");
66
#include "qemu/cutils.h"
28
return;
67
29
}
68
+#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
30
+ if (s->queue_size > VIRTQUEUE_MAX_SIZE) {
69
+# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
31
+ error_setg(errp, "vhost-user-blk: queue size must not exceed %d",
70
+#endif
32
+ VIRTQUEUE_MAX_SIZE);
71
+
33
+ return;
72
#define GLUSTER_OPT_FILENAME "filename"
34
+ }
73
#define GLUSTER_OPT_VOLUME "volume"
35
74
#define GLUSTER_OPT_PATH "path"
36
if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
37
return;
75
--
38
--
76
2.20.1
39
2.30.2
77
40
78
41
diff view generated by jsdifflib