1
The following changes since commit 98bfaac788be0ca63d7d010c8d4ba100ff1d8278:
1
The following changes since commit ccdf06c1db192152ac70a1dd974c624f566cb7d4:
2
2
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-09-01-v3' into staging (2017-09-04 13:28:09 +0100)
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 83a8c775a8bf134eb18a719322939b74a818d750:
9
for you to fetch changes up to 68bf7336533faa6aa90fdd4558edddbf5d8ef814:
10
10
11
qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing (2017-09-06 14:40:18 +0200)
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
- Fix permission update order problems with block graph changes
17
- qemu-img convert: Unshare write permission for source
18
- vhost-user-blk: Fail gracefully on too large queue size
19
16
----------------------------------------------------------------
20
----------------------------------------------------------------
17
Daniel P. Berrange (1):
21
Kevin Wolf (3):
18
block: document semantics of bdrv_co_preadv|pwritev
22
block: Add BDRV_O_NO_SHARE for blk_new_open()
23
qemu-img convert: Unshare write permission for source
24
vhost-user-blk: Fail gracefully on too large queue size
19
25
20
Eric Blake (2):
26
Vladimir Sementsov-Ogievskiy (36):
21
qcow: Change signature of get_cluster_offset()
27
tests/test-bdrv-graph-mod: add test_parallel_exclusive_write
22
qcow: Check failure of bdrv_getlength() and bdrv_truncate()
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()
23
63
24
Manos Pitsidianakis (10):
64
include/block/block.h | 14 +-
25
block: pass bdrv_* methods to bs->file by default in block filters
65
include/block/block_int.h | 8 +-
26
block: remove unused bdrv_media_changed
66
include/qemu/transactions.h | 63 ++
27
block: remove bdrv_truncate callback in blkdebug
67
block.c | 1329 ++++++++++++++++++++-------------
28
block: add default implementations for bdrv_co_get_block_status()
68
block/backup-top.c | 48 +-
29
block: move ThrottleGroup membership to ThrottleGroupMember
69
block/block-backend.c | 30 +-
30
block: add aio_context field in ThrottleGroupMember
70
block/commit.c | 1 +
31
block: tidy ThrottleGroupMember initializations
71
block/file-posix.c | 91 +--
32
block: convert ThrottleGroup to object with QOM
72
block/io.c | 31 +-
33
block: add throttle block filter driver
73
block/mirror.c | 3 -
34
qemu-iotests: add 184 for throttle filter driver
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
35
89
36
Pavel Butsykin (1):
37
qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing
38
90
39
qapi/block-core.json | 66 +++-
40
include/block/block.h | 1 -
41
include/block/block_int.h | 56 ++-
42
include/block/throttle-groups.h | 52 ++-
43
include/qemu/throttle-options.h | 60 +++-
44
include/qemu/throttle.h | 3 +
45
include/sysemu/block-backend.h | 20 +-
46
block.c | 35 +-
47
block/blkdebug.c | 20 +-
48
block/block-backend.c | 62 ++--
49
block/commit.c | 12 +-
50
block/io.c | 26 ++
51
block/mirror.c | 12 +-
52
block/qapi.c | 8 +-
53
block/qcow.c | 153 ++++----
54
block/qcow2.c | 16 +-
55
block/raw-format.c | 6 -
56
block/throttle-groups.c | 750 ++++++++++++++++++++++++++++++----------
57
block/throttle.c | 237 +++++++++++++
58
blockdev.c | 4 +-
59
tests/test-throttle.c | 111 +++---
60
util/throttle.c | 151 ++++++++
61
block/Makefile.objs | 1 +
62
tests/qemu-iotests/184 | 205 +++++++++++
63
tests/qemu-iotests/184.out | 302 ++++++++++++++++
64
tests/qemu-iotests/group | 1 +
65
26 files changed, 1917 insertions(+), 453 deletions(-)
66
create mode 100644 block/throttle.c
67
create mode 100755 tests/qemu-iotests/184
68
create mode 100644 tests/qemu-iotests/184.out
69
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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>
50
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
51
---
52
tests/unit/test-bdrv-graph-mod.c | 116 +++++++++++++++++++++++++++++++
53
1 file changed, 116 insertions(+)
54
55
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
56
index XXXXXXX..XXXXXXX 100644
57
--- a/tests/unit/test-bdrv-graph-mod.c
58
+++ b/tests/unit/test-bdrv-graph-mod.c
59
@@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void)
60
bdrv_unref(top);
61
}
62
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)
68
+{
69
+ if (bs->file && c == bs->file) {
70
+ *nperm = BLK_PERM_WRITE;
71
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
72
+ } else {
73
+ *nperm = 0;
74
+ *nshared = BLK_PERM_ALL;
75
+ }
76
+}
77
+
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[])
178
{
179
int i;
180
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
181
if (debug) {
182
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
183
test_parallel_exclusive_write);
184
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
185
+ test_parallel_perm_update);
186
}
187
188
return g_test_run();
189
--
190
2.30.2
191
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: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
bdrv_co_get_block_status_from_file() and
3
We have too much comments for this feature. It seems better just don't
4
bdrv_co_get_block_status_from_backing() set *file to bs->file and
4
do it. Most of real users (tests don't count) have to create additional
5
bs->backing respectively, so that bdrv_co_get_block_status() can recurse
5
reference.
6
to them. Future block drivers won't have to duplicate code to implement
7
this.
8
6
9
Reviewed-by: Fam Zheng <famz@redhat.com>
7
Drop also comment in external_snapshot_prepare:
10
Reviewed-by: Eric Blake <eblake@redhat.com>
8
- bdrv_append doesn't "remove" old bs in common sense, it sounds
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
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>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
16
Message-Id: <20210428151804.439460-5-vsementsov@virtuozzo.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
18
---
16
include/block/block_int.h | 18 ++++++++++++++++++
19
block.c | 25 +++----------------------
17
block/blkdebug.c | 12 +-----------
20
block/backup-top.c | 1 -
18
block/commit.c | 12 +-----------
21
block/commit.c | 1 +
19
block/io.c | 26 ++++++++++++++++++++++++++
22
block/mirror.c | 3 ---
20
block/mirror.c | 12 +-----------
23
blockdev.c | 4 ----
21
5 files changed, 47 insertions(+), 33 deletions(-)
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(-)
22
27
23
diff --git a/include/block/block_int.h b/include/block/block_int.h
28
diff --git a/block.c b/block.c
24
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/block_int.h
30
--- a/block.c
26
+++ b/include/block/block_int.h
31
+++ b/block.c
27
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
32
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
28
uint64_t perm, uint64_t shared,
33
goto out;
29
uint64_t *nperm, uint64_t *nshared);
34
}
30
35
31
+/*
36
- /* bdrv_append() consumes a strong reference to bs_snapshot
32
+ * Default implementation for drivers to pass bdrv_co_get_block_status() to
37
- * (i.e. it will call bdrv_unref() on it) even on error, so in
33
+ * their file.
38
- * order to be able to return one, we have to increase
34
+ */
39
- * bs_snapshot's refcount here */
35
+int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
40
- bdrv_ref(bs_snapshot);
36
+ int64_t sector_num,
41
ret = bdrv_append(bs_snapshot, bs, errp);
37
+ int nb_sectors,
42
if (ret < 0) {
38
+ int *pnum,
43
bs_snapshot = NULL;
39
+ BlockDriverState **file);
44
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
40
+/*
45
* bs_new must not be attached to a BlockBackend.
41
+ * Default implementation for drivers to pass bdrv_co_get_block_status() to
46
*
42
+ * their backing file.
47
* This function does not create any image files.
43
+ */
48
- *
44
+int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
49
- * bdrv_append() takes ownership of a bs_new reference and unrefs it because
45
+ int64_t sector_num,
50
- * that's what the callers commonly need. bs_new will be referenced by the old
46
+ int nb_sectors,
51
- * parents of bs_top after bdrv_append() returns. If the caller needs to keep a
47
+ int *pnum,
52
- * reference of its own, it must call bdrv_ref().
48
+ BlockDriverState **file);
53
*/
49
const char *bdrv_get_parent_name(const BlockDriverState *bs);
54
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
50
void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
55
Error **errp)
51
bool blk_dev_has_removable_media(BlockBackend *blk);
56
{
52
diff --git a/block/blkdebug.c b/block/blkdebug.c
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
53
index XXXXXXX..XXXXXXX 100644
85
index XXXXXXX..XXXXXXX 100644
54
--- a/block/blkdebug.c
86
--- a/block/backup-top.c
55
+++ b/block/blkdebug.c
87
+++ b/block/backup-top.c
56
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
88
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
57
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
89
58
}
90
bdrv_drained_begin(source);
59
91
60
-static int64_t coroutine_fn blkdebug_co_get_block_status(
92
- bdrv_ref(top);
61
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
93
ret = bdrv_append(top, source, errp);
62
- BlockDriverState **file)
94
if (ret < 0) {
63
-{
95
error_prepend(errp, "Cannot append backup-top filter: ");
64
- *pnum = nb_sectors;
65
- *file = bs->file->bs;
66
- return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
67
- (sector_num << BDRV_SECTOR_BITS);
68
-}
69
-
70
static void blkdebug_close(BlockDriverState *bs)
71
{
72
BDRVBlkdebugState *s = bs->opaque;
73
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
74
.bdrv_co_flush_to_disk = blkdebug_co_flush,
75
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
76
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
77
- .bdrv_co_get_block_status = blkdebug_co_get_block_status,
78
+ .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file,
79
80
.bdrv_debug_event = blkdebug_debug_event,
81
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
82
diff --git a/block/commit.c b/block/commit.c
96
diff --git a/block/commit.c b/block/commit.c
83
index XXXXXXX..XXXXXXX 100644
97
index XXXXXXX..XXXXXXX 100644
84
--- a/block/commit.c
98
--- a/block/commit.c
85
+++ b/block/commit.c
99
+++ b/block/commit.c
86
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
100
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
87
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
101
commit_top_bs->total_sectors = top->total_sectors;
88
}
102
89
103
ret = bdrv_append(commit_top_bs, top, errp);
90
-static int64_t coroutine_fn bdrv_commit_top_get_block_status(
104
+ bdrv_unref(commit_top_bs); /* referenced by new parents or failed */
91
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
105
if (ret < 0) {
92
- BlockDriverState **file)
106
commit_top_bs = NULL;
93
-{
107
goto fail;
94
- *pnum = nb_sectors;
95
- *file = bs->backing->bs;
96
- return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
97
- (sector_num << BDRV_SECTOR_BITS);
98
-}
99
-
100
static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
101
{
102
bdrv_refresh_filename(bs->backing->bs);
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
104
static BlockDriver bdrv_commit_top = {
105
.format_name = "commit_top",
106
.bdrv_co_preadv = bdrv_commit_top_preadv,
107
- .bdrv_co_get_block_status = bdrv_commit_top_get_block_status,
108
+ .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing,
109
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
110
.bdrv_close = bdrv_commit_top_close,
111
.bdrv_child_perm = bdrv_commit_top_child_perm,
112
diff --git a/block/io.c b/block/io.c
113
index XXXXXXX..XXXXXXX 100644
114
--- a/block/io.c
115
+++ b/block/io.c
116
@@ -XXX,XX +XXX,XX @@ typedef struct BdrvCoGetBlockStatusData {
117
bool done;
118
} BdrvCoGetBlockStatusData;
119
120
+int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
121
+ int64_t sector_num,
122
+ int nb_sectors,
123
+ int *pnum,
124
+ BlockDriverState **file)
125
+{
126
+ assert(bs->file && bs->file->bs);
127
+ *pnum = nb_sectors;
128
+ *file = bs->file->bs;
129
+ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
130
+ (sector_num << BDRV_SECTOR_BITS);
131
+}
132
+
133
+int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
134
+ int64_t sector_num,
135
+ int nb_sectors,
136
+ int *pnum,
137
+ BlockDriverState **file)
138
+{
139
+ assert(bs->backing && bs->backing->bs);
140
+ *pnum = nb_sectors;
141
+ *file = bs->backing->bs;
142
+ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
143
+ (sector_num << BDRV_SECTOR_BITS);
144
+}
145
+
146
/*
147
* Returns the allocation status of the specified sectors.
148
* Drivers not implementing the functionality are assumed to not support
149
diff --git a/block/mirror.c b/block/mirror.c
108
diff --git a/block/mirror.c b/block/mirror.c
150
index XXXXXXX..XXXXXXX 100644
109
index XXXXXXX..XXXXXXX 100644
151
--- a/block/mirror.c
110
--- a/block/mirror.c
152
+++ b/block/mirror.c
111
+++ b/block/mirror.c
153
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs)
112
@@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job(
154
return bdrv_co_flush(bs->backing->bs);
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;
128
}
129
130
- /* This removes our old bs and adds the new bs. This is an operation that
131
- * can fail, so we need to do it in .prepare; undoing it for abort is
132
- * always possible. */
133
- bdrv_ref(state->new_bs);
134
ret = bdrv_append(state->new_bs, state->old_bs, errp);
135
if (ret < 0) {
136
goto out;
137
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
138
index XXXXXXX..XXXXXXX 100644
139
--- a/tests/unit/test-bdrv-drain.c
140
+++ b/tests/unit/test-bdrv-drain.c
141
@@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void)
142
g_assert_cmpint(base_s->drain_count, ==, 1);
143
g_assert_cmpint(base->in_flight, ==, 0);
144
145
- /* Takes ownership of overlay, so we don't have to unref it later */
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);
155
}
156
}
156
157
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
157
-static int64_t coroutine_fn bdrv_mirror_top_get_block_status(
158
index XXXXXXX..XXXXXXX 100644
158
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
159
--- a/tests/unit/test-bdrv-graph-mod.c
159
- BlockDriverState **file)
160
+++ b/tests/unit/test-bdrv-graph-mod.c
160
-{
161
@@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void)
161
- *pnum = nb_sectors;
162
ret = bdrv_append(filter, bs, NULL);
162
- *file = bs->backing->bs;
163
g_assert_cmpint(ret, <, 0);
163
- return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
164
164
- (sector_num << BDRV_SECTOR_BITS);
165
+ bdrv_unref(filter);
165
-}
166
blk_unref(root);
166
-
167
}
167
static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs,
168
168
int64_t offset, int bytes, BdrvRequestFlags flags)
169
@@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void)
169
{
170
bdrv_append(filter, bs, &error_abort);
170
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_mirror_top = {
171
g_assert(target->backing->bs == bs);
171
.bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes,
172
172
.bdrv_co_pdiscard = bdrv_mirror_top_pdiscard,
173
+ bdrv_unref(filter);
173
.bdrv_co_flush = bdrv_mirror_top_flush,
174
bdrv_unref(bs);
174
- .bdrv_co_get_block_status = bdrv_mirror_top_get_block_status,
175
blk_unref(root);
175
+ .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing,
176
}
176
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
177
@@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void)
177
.bdrv_close = bdrv_mirror_top_close,
178
&error_abort);
178
.bdrv_child_perm = bdrv_mirror_top_child_perm,
179
180
bdrv_append(fl, base, &error_abort);
181
+ bdrv_unref(fl);
182
bdrv_unref(top);
183
}
184
179
--
185
--
180
2.13.5
186
2.30.2
181
187
182
188
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This function is not used anywhere, so remove it.
3
Add new handler to get aio context and implement it in all child
4
classes. Add corresponding public interface to be used soon.
4
5
5
Markus Armbruster adds:
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
The i82078 floppy device model used to call bdrv_media_changed() to
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
implement its media change bit when backed by a host floppy. This
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
went away in 21fcf36 "fdc: simplify media change handling".
9
Message-Id: <20210428151804.439460-6-vsementsov@virtuozzo.com>
9
Probably broke host floppy media change. Host floppy pass-through
10
was dropped in commit f709623. bdrv_media_changed() has never been
11
used for anything else. Remove it.
12
(Source is Message-ID: <87y3ruaypm.fsf@dusky.pond.sub.org>)
13
14
Reviewed-by: Eric Blake <eblake@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
11
---
19
include/block/block.h | 1 -
12
include/block/block.h | 2 ++
20
include/block/block_int.h | 1 -
13
include/block/block_int.h | 2 ++
21
block.c | 14 --------------
14
block.c | 13 +++++++++++++
22
block/raw-format.c | 6 ------
15
block/block-backend.c | 9 +++++++++
23
4 files changed, 22 deletions(-)
16
blockjob.c | 8 ++++++++
17
5 files changed, 34 insertions(+)
24
18
25
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
26
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/block.h
21
--- a/include/block/block.h
28
+++ b/include/block/block.h
22
+++ b/include/block/block.h
29
@@ -XXX,XX +XXX,XX @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
23
@@ -XXX,XX +XXX,XX @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
30
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
24
GSList **ignore, Error **errp);
31
bool bdrv_is_sg(BlockDriverState *bs);
25
bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
32
bool bdrv_is_inserted(BlockDriverState *bs);
26
GSList **ignore, Error **errp);
33
-int bdrv_media_changed(BlockDriverState *bs);
27
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
34
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
28
+
35
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
29
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
36
const char *bdrv_get_format_name(BlockDriverState *bs);
30
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
31
37
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
38
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
39
--- a/include/block/block_int.h
34
--- a/include/block/block_int.h
40
+++ b/include/block/block_int.h
35
+++ b/include/block/block_int.h
41
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
36
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
42
37
bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx,
43
/* removable device specific */
38
GSList **ignore, Error **errp);
44
bool (*bdrv_is_inserted)(BlockDriverState *bs);
39
void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore);
45
- int (*bdrv_media_changed)(BlockDriverState *bs);
40
+
46
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
41
+ AioContext *(*get_parent_aio_context)(BdrvChild *child);
47
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
42
};
48
43
44
extern const BdrvChildClass child_of_bds;
49
diff --git a/block.c b/block.c
45
diff --git a/block.c b/block.c
50
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
51
--- a/block.c
47
--- a/block.c
52
+++ b/block.c
48
+++ b/block.c
53
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_inserted(BlockDriverState *bs)
49
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
50
return 0;
54
}
51
}
55
52
56
/**
53
+static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c)
57
- * Return whether the media changed since the last call to this
54
+{
58
- * function, or -ENOTSUP if we don't know. Most drivers don't know.
55
+ BlockDriverState *bs = c->opaque;
59
- */
56
+
60
-int bdrv_media_changed(BlockDriverState *bs)
57
+ return bdrv_get_aio_context(bs);
61
-{
58
+}
62
- BlockDriver *drv = bs->drv;
59
+
63
-
60
const BdrvChildClass child_of_bds = {
64
- if (drv && drv->bdrv_media_changed) {
61
.parent_is_bds = true,
65
- return drv->bdrv_media_changed(bs);
62
.get_parent_desc = bdrv_child_get_parent_desc,
66
- }
63
@@ -XXX,XX +XXX,XX @@ const BdrvChildClass child_of_bds = {
67
- return -ENOTSUP;
64
.can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
68
-}
65
.set_aio_ctx = bdrv_child_cb_set_aio_ctx,
69
-
66
.update_filename = bdrv_child_cb_update_filename,
70
-/**
67
+ .get_parent_aio_context = bdrv_child_cb_get_parent_aio_context,
71
* If eject_flag is TRUE, eject the media. Otherwise, close the tray
68
};
72
*/
69
73
void bdrv_eject(BlockDriverState *bs, bool eject_flag)
70
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
74
diff --git a/block/raw-format.c b/block/raw-format.c
71
+{
72
+ return c->klass->get_parent_aio_context(c);
73
+}
74
+
75
static int bdrv_open_flags(BlockDriverState *bs, int flags)
76
{
77
int open_flags = flags;
78
diff --git a/block/block-backend.c b/block/block-backend.c
75
index XXXXXXX..XXXXXXX 100644
79
index XXXXXXX..XXXXXXX 100644
76
--- a/block/raw-format.c
80
--- a/block/block-backend.c
77
+++ b/block/raw-format.c
81
+++ b/block/block-backend.c
78
@@ -XXX,XX +XXX,XX @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
82
@@ -XXX,XX +XXX,XX @@ static void blk_root_detach(BdrvChild *child)
79
return bdrv_truncate(bs->file, offset, prealloc, errp);
83
}
80
}
84
}
81
85
82
-static int raw_media_changed(BlockDriverState *bs)
86
+static AioContext *blk_root_get_parent_aio_context(BdrvChild *c)
83
-{
87
+{
84
- return bdrv_media_changed(bs->file->bs);
88
+ BlockBackend *blk = c->opaque;
85
-}
89
+
86
-
90
+ return blk_get_aio_context(blk);
87
static void raw_eject(BlockDriverState *bs, bool eject_flag)
91
+}
88
{
92
+
89
bdrv_eject(bs->file->bs, eject_flag);
93
static const BdrvChildClass child_root = {
90
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
94
.inherit_options = blk_root_inherit_options,
91
.bdrv_refresh_limits = &raw_refresh_limits,
95
92
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
96
@@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_root = {
93
.bdrv_probe_geometry = &raw_probe_geometry,
97
94
- .bdrv_media_changed = &raw_media_changed,
98
.can_set_aio_ctx = blk_root_can_set_aio_ctx,
95
.bdrv_eject = &raw_eject,
99
.set_aio_ctx = blk_root_set_aio_ctx,
96
.bdrv_lock_medium = &raw_lock_medium,
100
+
97
.bdrv_co_ioctl = &raw_co_ioctl,
101
+ .get_parent_aio_context = blk_root_get_parent_aio_context,
102
};
103
104
/*
105
diff --git a/blockjob.c b/blockjob.c
106
index XXXXXXX..XXXXXXX 100644
107
--- a/blockjob.c
108
+++ b/blockjob.c
109
@@ -XXX,XX +XXX,XX @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx,
110
job->job.aio_context = ctx;
111
}
112
113
+static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
114
+{
115
+ BlockJob *job = c->opaque;
116
+
117
+ return job->job.aio_context;
118
+}
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)
98
--
131
--
99
2.13.5
132
2.30.2
100
133
101
134
diff view generated by jsdifflib
1
From: "Daniel P. Berrange" <berrange@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
3
Passing parent aio context is redundant, as child_class and parent
4
Reviewed-by: Eric Blake <eblake@redhat.com>
4
opaque pointer are enough to retrieve it. Drop the argument and use new
5
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
12
---
8
include/block/block_int.h | 31 +++++++++++++++++++++++++++++++
13
include/block/block_int.h | 1 -
9
1 file changed, 31 insertions(+)
14
block.c | 8 +++++---
15
block/block-backend.c | 4 ++--
16
blockjob.c | 3 +--
17
4 files changed, 8 insertions(+), 8 deletions(-)
10
18
11
diff --git a/include/block/block_int.h b/include/block/block_int.h
19
diff --git a/include/block/block_int.h b/include/block/block_int.h
12
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
13
--- a/include/block/block_int.h
21
--- a/include/block/block_int.h
14
+++ b/include/block/block_int.h
22
+++ b/include/block/block_int.h
15
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
23
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
16
24
const char *child_name,
17
int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs,
25
const BdrvChildClass *child_class,
18
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
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);
19
+
55
+
20
+ /**
56
/* If the AioContexts don't match, first try to move the subtree of
21
+ * @offset: position in bytes to read at
57
* child_bs into the AioContext of the new parent. If this doesn't work,
22
+ * @bytes: number of bytes to read
58
* try moving the parent into the AioContext of child_bs instead. */
23
+ * @qiov: the buffers to fill with read data
59
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
24
+ * @flags: currently unused, always 0
60
perm, shared_perm, &perm, &shared_perm);
25
+ *
61
26
+ * @offset and @bytes will be a multiple of 'request_alignment',
62
child = bdrv_root_attach_child(child_bs, child_name, child_class,
27
+ * but the length of individual @qiov elements does not have to
63
- child_role, bdrv_get_aio_context(parent_bs),
28
+ * be a multiple.
64
- perm, shared_perm, parent_bs, errp);
29
+ *
65
+ child_role, perm, shared_perm, parent_bs,
30
+ * @bytes will always equal the total size of @qiov, and will be
66
+ errp);
31
+ * no larger than 'max_transfer'.
67
if (child == NULL) {
32
+ *
68
return NULL;
33
+ * The buffer in @qiov may point directly to guest memory.
69
}
34
+ */
70
diff --git a/block/block-backend.c b/block/block-backend.c
35
int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs,
71
index XXXXXXX..XXXXXXX 100644
36
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
72
--- a/block/block-backend.c
37
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
73
+++ b/block/block-backend.c
38
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
74
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
39
int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs,
75
40
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags);
76
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
41
+ /**
77
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
42
+ * @offset: position in bytes to write at
78
- blk->ctx, perm, BLK_PERM_ALL, blk, errp);
43
+ * @bytes: number of bytes to write
79
+ perm, BLK_PERM_ALL, blk, errp);
44
+ * @qiov: the buffers containing data to write
80
if (!blk->root) {
45
+ * @flags: zero or more bits allowed by 'supported_write_flags'
81
blk_unref(blk);
46
+ *
82
return NULL;
47
+ * @offset and @bytes will be a multiple of 'request_alignment',
83
@@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
48
+ * but the length of individual @qiov elements does not have to
84
bdrv_ref(bs);
49
+ * be a multiple.
85
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
50
+ *
86
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
51
+ * @bytes will always equal the total size of @qiov, and will be
87
- blk->ctx, blk->perm, blk->shared_perm,
52
+ * no larger than 'max_transfer'.
88
+ blk->perm, blk->shared_perm,
53
+ *
89
blk, errp);
54
+ * The buffer in @qiov may point directly to guest memory.
90
if (blk->root == NULL) {
55
+ */
91
return -EPERM;
56
int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs,
92
diff --git a/blockjob.c b/blockjob.c
57
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
93
index XXXXXXX..XXXXXXX 100644
58
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);
59
--
106
--
60
2.13.5
107
2.30.2
61
108
62
109
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
These functions are called only from bdrv_reopen_multiple() in block.c.
4
No reason to publish them.
5
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>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
include/block/block.h | 4 ----
13
block.c | 13 +++++++++----
14
2 files changed, 9 insertions(+), 8 deletions(-)
15
16
diff --git a/include/block/block.h b/include/block/block.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block.h
19
+++ b/include/block/block.h
20
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
21
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
22
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
23
Error **errp);
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);
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 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
36
BdrvChildRole child_role,
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
*
50
*/
51
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
52
- Error **errp)
53
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
54
+ BlockReopenQueue *queue, Error **errp)
55
{
56
int ret = -1;
57
int old_flags;
58
@@ -XXX,XX +XXX,XX @@ error:
59
* makes them final by swapping the staging BlockDriverState contents into
60
* the active BlockDriverState contents.
61
*/
62
-void bdrv_reopen_commit(BDRVReopenState *reopen_state)
63
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
64
{
65
BlockDriver *drv;
66
BlockDriverState *bs;
67
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
68
* Abort the reopen, and delete and free the staged changes in
69
* reopen_state
70
*/
71
-void bdrv_reopen_abort(BDRVReopenState *reopen_state)
72
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state)
73
{
74
BlockDriver *drv;
75
76
--
77
2.30.2
78
79
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
block/throttle.c uses existing I/O throttle infrastructure inside a
3
Add simple transaction API to use in further update of block graph
4
block filter driver. I/O operations are intercepted in the filter's
4
operations.
5
read/write coroutines, and referred to block/throttle-groups.c
5
6
6
Supposed usage is:
7
The driver can be used with the syntax
7
8
-drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar
8
- "prepare" is main function of the action and it should make the main
9
9
effect of the action to be visible for the following actions, keeping
10
which registers the throttle filter node with the ThrottleGroup 'bar'. The
10
possibility of roll-back, saving necessary things in action state,
11
given group must be created beforehand with object-add or -object.
11
which is prepended to the action list (to do that, prepare func
12
12
should call tran_add()). So, driver struct doesn't include "prepare"
13
Reviewed-by: Alberto Garcia <berto@igalia.com>
13
field, as it is supposed to be called directly.
14
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
14
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
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>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
26
---
18
qapi/block-core.json | 18 ++-
27
include/qemu/transactions.h | 63 ++++++++++++++++++++++++
19
include/block/throttle-groups.h | 5 +
28
util/transactions.c | 96 +++++++++++++++++++++++++++++++++++++
20
include/qemu/throttle-options.h | 1 +
29
MAINTAINERS | 6 +++
21
block/throttle-groups.c | 15 ++-
30
util/meson.build | 1 +
22
block/throttle.c | 237 ++++++++++++++++++++++++++++++++++++++++
31
4 files changed, 166 insertions(+)
23
block/Makefile.objs | 1 +
32
create mode 100644 include/qemu/transactions.h
24
6 files changed, 275 insertions(+), 2 deletions(-)
33
create mode 100644 util/transactions.c
25
create mode 100644 block/throttle.c
34
26
35
diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h
27
diff --git a/qapi/block-core.json b/qapi/block-core.json
28
index XXXXXXX..XXXXXXX 100644
29
--- a/qapi/block-core.json
30
+++ b/qapi/block-core.json
31
@@ -XXX,XX +XXX,XX @@
32
# Drivers that are supported in block device operations.
33
#
34
# @vxhs: Since 2.10
35
+# @throttle: Since 2.11
36
#
37
# Since: 2.9
38
##
39
@@ -XXX,XX +XXX,XX @@
40
'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
41
'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
42
'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
43
- 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
44
+ 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
45
46
##
47
# @BlockdevOptionsFile:
48
@@ -XXX,XX +XXX,XX @@
49
'*tls-creds': 'str' } }
50
51
##
52
+# @BlockdevOptionsThrottle:
53
+#
54
+# Driver specific block device options for the throttle driver
55
+#
56
+# @throttle-group: the name of the throttle-group object to use. It
57
+# must already exist.
58
+# @file: reference to or definition of the data source block device
59
+# Since: 2.11
60
+##
61
+{ 'struct': 'BlockdevOptionsThrottle',
62
+ 'data': { 'throttle-group': 'str',
63
+ 'file' : 'BlockdevRef'
64
+ } }
65
+##
66
# @BlockdevOptions:
67
#
68
# Options for creating a block device. Many options are available for all
69
@@ -XXX,XX +XXX,XX @@
70
'replication':'BlockdevOptionsReplication',
71
'sheepdog': 'BlockdevOptionsSheepdog',
72
'ssh': 'BlockdevOptionsSsh',
73
+ 'throttle': 'BlockdevOptionsThrottle',
74
'vdi': 'BlockdevOptionsGenericFormat',
75
'vhdx': 'BlockdevOptionsGenericFormat',
76
'vmdk': 'BlockdevOptionsGenericCOWFormat',
77
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
78
index XXXXXXX..XXXXXXX 100644
79
--- a/include/block/throttle-groups.h
80
+++ b/include/block/throttle-groups.h
81
@@ -XXX,XX +XXX,XX @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm
82
void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
83
AioContext *new_context);
84
void throttle_group_detach_aio_context(ThrottleGroupMember *tgm);
85
+/*
86
+ * throttle_group_exists() must be called under the global
87
+ * mutex.
88
+ */
89
+bool throttle_group_exists(const char *name);
90
91
#endif
92
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
93
index XXXXXXX..XXXXXXX 100644
94
--- a/include/qemu/throttle-options.h
95
+++ b/include/qemu/throttle-options.h
96
@@ -XXX,XX +XXX,XX @@
97
#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max"
98
#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length"
99
#define QEMU_OPT_IOPS_SIZE "iops-size"
100
+#define QEMU_OPT_THROTTLE_GROUP_NAME "throttle-group"
101
102
#define THROTTLE_OPT_PREFIX "throttling."
103
#define THROTTLE_OPTS \
104
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
105
index XXXXXXX..XXXXXXX 100644
106
--- a/block/throttle-groups.c
107
+++ b/block/throttle-groups.c
108
@@ -XXX,XX +XXX,XX @@ static ThrottleGroup *throttle_group_by_name(const char *name)
109
return NULL;
110
}
111
112
+/* This function reads throttle_groups and must be called under the global
113
+ * mutex.
114
+ */
115
+bool throttle_group_exists(const char *name)
116
+{
117
+ return throttle_group_by_name(name) != NULL;
118
+}
119
+
120
/* Increments the reference count of a ThrottleGroup given its name.
121
*
122
* If no ThrottleGroup is found with the given name a new one is
123
@@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
124
ThrottleGroupMember *token;
125
int i;
126
127
+ if (!ts) {
128
+ /* Discard already unregistered tgm */
129
+ return;
130
+ }
131
+
132
assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
133
assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
134
assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
135
@@ -XXX,XX +XXX,XX @@ static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
136
assert(tg->name);
137
138
/* error if name is duplicate */
139
- if (throttle_group_by_name(tg->name) != NULL) {
140
+ if (throttle_group_exists(tg->name)) {
141
error_setg(errp, "A group with this name already exists");
142
return;
143
}
144
diff --git a/block/throttle.c b/block/throttle.c
145
new file mode 100644
36
new file mode 100644
146
index XXXXXXX..XXXXXXX
37
index XXXXXXX..XXXXXXX
147
--- /dev/null
38
--- /dev/null
148
+++ b/block/throttle.c
39
+++ b/include/qemu/transactions.h
149
@@ -XXX,XX +XXX,XX @@
40
@@ -XXX,XX +XXX,XX @@
150
+/*
41
+/*
151
+ * QEMU block throttling filter driver infrastructure
42
+ * Simple transactions API
152
+ *
43
+ *
153
+ * Copyright (c) 2017 Manos Pitsidianakis
44
+ * Copyright (c) 2021 Virtuozzo International GmbH.
154
+ *
45
+ *
155
+ * This program is free software; you can redistribute it and/or
46
+ * Author:
156
+ * modify it under the terms of the GNU General Public License as
47
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
157
+ * published by the Free Software Foundation; either version 2 or
48
+ *
158
+ * (at your option) version 3 of the License.
49
+ * This program is free software; you can redistribute it and/or modify
50
+ * it under the terms of the GNU General Public License as published by
51
+ * the Free Software Foundation; either version 2 of the License, or
52
+ * (at your option) any later version.
159
+ *
53
+ *
160
+ * This program is distributed in the hope that it will be useful,
54
+ * This program is distributed in the hope that it will be useful,
161
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
55
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
162
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
163
+ * GNU General Public License for more details.
57
+ * GNU General Public License for more details.
164
+ *
58
+ *
165
+ * You should have received a copy of the GNU General Public License
59
+ * You should have received a copy of the GNU General Public License
166
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
60
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
61
+ *
62
+ *
63
+ * = Generic transaction API =
64
+ *
65
+ * The intended usage is the following: you create "prepare" functions, which
66
+ * represents the actions. They will usually have Transaction* argument, and
67
+ * call tran_add() to register finalization callbacks. For finalization
68
+ * callbacks, prepare corresponding TransactionActionDrv structures.
69
+ *
70
+ * Then, when you need to make a transaction, create an empty Transaction by
71
+ * tran_create(), call your "prepare" functions on it, and finally call
72
+ * tran_abort() or tran_commit() to finalize the transaction by corresponding
73
+ * finalization actions in reverse order.
167
+ */
74
+ */
168
+
75
+
76
+#ifndef QEMU_TRANSACTIONS_H
77
+#define QEMU_TRANSACTIONS_H
78
+
79
+#include <gmodule.h>
80
+
81
+typedef struct TransactionActionDrv {
82
+ void (*abort)(void *opaque);
83
+ void (*commit)(void *opaque);
84
+ void (*clean)(void *opaque);
85
+} TransactionActionDrv;
86
+
87
+typedef struct Transaction Transaction;
88
+
89
+Transaction *tran_new(void);
90
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque);
91
+void tran_abort(Transaction *tran);
92
+void tran_commit(Transaction *tran);
93
+
94
+static inline void tran_finalize(Transaction *tran, int ret)
95
+{
96
+ if (ret < 0) {
97
+ tran_abort(tran);
98
+ } else {
99
+ tran_commit(tran);
100
+ }
101
+}
102
+
103
+#endif /* QEMU_TRANSACTIONS_H */
104
diff --git a/util/transactions.c b/util/transactions.c
105
new file mode 100644
106
index XXXXXXX..XXXXXXX
107
--- /dev/null
108
+++ b/util/transactions.c
109
@@ -XXX,XX +XXX,XX @@
110
+/*
111
+ * Simple transactions API
112
+ *
113
+ * Copyright (c) 2021 Virtuozzo International GmbH.
114
+ *
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
+
169
+#include "qemu/osdep.h"
132
+#include "qemu/osdep.h"
170
+#include "block/throttle-groups.h"
133
+
171
+#include "qemu/throttle-options.h"
134
+#include "qemu/transactions.h"
172
+#include "qapi/error.h"
135
+#include "qemu/queue.h"
173
+
136
+
174
+static QemuOptsList throttle_opts = {
137
+typedef struct TransactionAction {
175
+ .name = "throttle",
138
+ TransactionActionDrv *drv;
176
+ .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
139
+ void *opaque;
177
+ .desc = {
140
+ QSLIST_ENTRY(TransactionAction) entry;
178
+ {
141
+} TransactionAction;
179
+ .name = QEMU_OPT_THROTTLE_GROUP_NAME,
142
+
180
+ .type = QEMU_OPT_STRING,
143
+struct Transaction {
181
+ .help = "Name of the throttle group",
144
+ QSLIST_HEAD(, TransactionAction) actions;
182
+ },
183
+ { /* end of list */ }
184
+ },
185
+};
145
+};
186
+
146
+
187
+static int throttle_configure_tgm(BlockDriverState *bs,
147
+Transaction *tran_new(void)
188
+ ThrottleGroupMember *tgm,
148
+{
189
+ QDict *options, Error **errp)
149
+ Transaction *tran = g_new(Transaction, 1);
190
+{
150
+
191
+ int ret;
151
+ QSLIST_INIT(&tran->actions);
192
+ const char *group_name;
152
+
193
+ Error *local_err = NULL;
153
+ return tran;
194
+ QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort);
154
+}
195
+
155
+
196
+ qemu_opts_absorb_qdict(opts, options, &local_err);
156
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque)
197
+ if (local_err) {
157
+{
198
+ error_propagate(errp, local_err);
158
+ TransactionAction *act;
199
+ ret = -EINVAL;
159
+
200
+ goto fin;
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);
201
+ }
183
+ }
202
+
184
+
203
+ group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
185
+ g_free(tran);
204
+ if (!group_name) {
186
+}
205
+ error_setg(errp, "Please specify a throttle group");
187
+
206
+ ret = -EINVAL;
188
+void tran_commit(Transaction *tran)
207
+ goto fin;
189
+{
208
+ } else if (!throttle_group_exists(group_name)) {
190
+ TransactionAction *act, *next;
209
+ error_setg(errp, "Throttle group '%s' does not exist", group_name);
191
+
210
+ ret = -EINVAL;
192
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
211
+ goto fin;
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);
212
+ }
202
+ }
213
+
203
+
214
+ /* Register membership to group with name group_name */
204
+ g_free(tran);
215
+ throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
205
+}
216
+ ret = 0;
206
diff --git a/MAINTAINERS b/MAINTAINERS
217
+fin:
218
+ qemu_opts_del(opts);
219
+ return ret;
220
+}
221
+
222
+static int throttle_open(BlockDriverState *bs, QDict *options,
223
+ int flags, Error **errp)
224
+{
225
+ ThrottleGroupMember *tgm = bs->opaque;
226
+
227
+ bs->file = bdrv_open_child(NULL, options, "file", bs,
228
+ &child_file, false, errp);
229
+ if (!bs->file) {
230
+ return -EINVAL;
231
+ }
232
+ bs->supported_write_flags = bs->file->bs->supported_write_flags;
233
+ bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
234
+
235
+ return throttle_configure_tgm(bs, tgm, options, errp);
236
+}
237
+
238
+static void throttle_close(BlockDriverState *bs)
239
+{
240
+ ThrottleGroupMember *tgm = bs->opaque;
241
+ throttle_group_unregister_tgm(tgm);
242
+}
243
+
244
+
245
+static int64_t throttle_getlength(BlockDriverState *bs)
246
+{
247
+ return bdrv_getlength(bs->file->bs);
248
+}
249
+
250
+static int coroutine_fn throttle_co_preadv(BlockDriverState *bs,
251
+ uint64_t offset, uint64_t bytes,
252
+ QEMUIOVector *qiov, int flags)
253
+{
254
+
255
+ ThrottleGroupMember *tgm = bs->opaque;
256
+ throttle_group_co_io_limits_intercept(tgm, bytes, false);
257
+
258
+ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
259
+}
260
+
261
+static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs,
262
+ uint64_t offset, uint64_t bytes,
263
+ QEMUIOVector *qiov, int flags)
264
+{
265
+ ThrottleGroupMember *tgm = bs->opaque;
266
+ throttle_group_co_io_limits_intercept(tgm, bytes, true);
267
+
268
+ return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
269
+}
270
+
271
+static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs,
272
+ int64_t offset, int bytes,
273
+ BdrvRequestFlags flags)
274
+{
275
+ ThrottleGroupMember *tgm = bs->opaque;
276
+ throttle_group_co_io_limits_intercept(tgm, bytes, true);
277
+
278
+ return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
279
+}
280
+
281
+static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
282
+ int64_t offset, int bytes)
283
+{
284
+ ThrottleGroupMember *tgm = bs->opaque;
285
+ throttle_group_co_io_limits_intercept(tgm, bytes, true);
286
+
287
+ return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
288
+}
289
+
290
+static int throttle_co_flush(BlockDriverState *bs)
291
+{
292
+ return bdrv_co_flush(bs->file->bs);
293
+}
294
+
295
+static void throttle_detach_aio_context(BlockDriverState *bs)
296
+{
297
+ ThrottleGroupMember *tgm = bs->opaque;
298
+ throttle_group_detach_aio_context(tgm);
299
+}
300
+
301
+static void throttle_attach_aio_context(BlockDriverState *bs,
302
+ AioContext *new_context)
303
+{
304
+ ThrottleGroupMember *tgm = bs->opaque;
305
+ throttle_group_attach_aio_context(tgm, new_context);
306
+}
307
+
308
+static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
309
+ BlockReopenQueue *queue, Error **errp)
310
+{
311
+ ThrottleGroupMember *tgm;
312
+
313
+ assert(reopen_state != NULL);
314
+ assert(reopen_state->bs != NULL);
315
+
316
+ reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
317
+ tgm = reopen_state->opaque;
318
+
319
+ return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
320
+ errp);
321
+}
322
+
323
+static void throttle_reopen_commit(BDRVReopenState *reopen_state)
324
+{
325
+ ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
326
+ ThrottleGroupMember *new_tgm = reopen_state->opaque;
327
+
328
+ throttle_group_unregister_tgm(old_tgm);
329
+ g_free(old_tgm);
330
+ reopen_state->bs->opaque = new_tgm;
331
+ reopen_state->opaque = NULL;
332
+}
333
+
334
+static void throttle_reopen_abort(BDRVReopenState *reopen_state)
335
+{
336
+ ThrottleGroupMember *tgm = reopen_state->opaque;
337
+
338
+ throttle_group_unregister_tgm(tgm);
339
+ g_free(tgm);
340
+ reopen_state->opaque = NULL;
341
+}
342
+
343
+static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
344
+ BlockDriverState *candidate)
345
+{
346
+ return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
347
+}
348
+
349
+static BlockDriver bdrv_throttle = {
350
+ .format_name = "throttle",
351
+ .protocol_name = "throttle",
352
+ .instance_size = sizeof(ThrottleGroupMember),
353
+
354
+ .bdrv_file_open = throttle_open,
355
+ .bdrv_close = throttle_close,
356
+ .bdrv_co_flush = throttle_co_flush,
357
+
358
+ .bdrv_child_perm = bdrv_filter_default_perms,
359
+
360
+ .bdrv_getlength = throttle_getlength,
361
+
362
+ .bdrv_co_preadv = throttle_co_preadv,
363
+ .bdrv_co_pwritev = throttle_co_pwritev,
364
+
365
+ .bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes,
366
+ .bdrv_co_pdiscard = throttle_co_pdiscard,
367
+
368
+ .bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter,
369
+
370
+ .bdrv_attach_aio_context = throttle_attach_aio_context,
371
+ .bdrv_detach_aio_context = throttle_detach_aio_context,
372
+
373
+ .bdrv_reopen_prepare = throttle_reopen_prepare,
374
+ .bdrv_reopen_commit = throttle_reopen_commit,
375
+ .bdrv_reopen_abort = throttle_reopen_abort,
376
+ .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file,
377
+
378
+ .is_filter = true,
379
+};
380
+
381
+static void bdrv_throttle_init(void)
382
+{
383
+ bdrv_register(&bdrv_throttle);
384
+}
385
+
386
+block_init(bdrv_throttle_init);
387
diff --git a/block/Makefile.objs b/block/Makefile.objs
388
index XXXXXXX..XXXXXXX 100644
207
index XXXXXXX..XXXXXXX 100644
389
--- a/block/Makefile.objs
208
--- a/MAINTAINERS
390
+++ b/block/Makefile.objs
209
+++ b/MAINTAINERS
391
@@ -XXX,XX +XXX,XX @@ block-obj-y += accounting.o dirty-bitmap.o
210
@@ -XXX,XX +XXX,XX @@ M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
392
block-obj-y += write-threshold.o
211
S: Maintained
393
block-obj-y += backup.o
212
F: scripts/simplebench/
394
block-obj-$(CONFIG_REPLICATION) += replication.o
213
395
+block-obj-y += throttle.o
214
+Transactions helper
396
215
+M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
397
block-obj-y += crypto.o
216
+S: Maintained
398
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'))
399
--
235
--
400
2.13.5
236
2.30.2
401
237
402
238
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
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>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
block.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++---------
19
1 file changed, 54 insertions(+), 9 deletions(-)
20
21
diff --git a/block.c b/block.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block.c
24
+++ b/block.c
25
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_writable(BlockDriverState *bs)
26
return bdrv_is_writable_after_reopen(bs, NULL);
27
}
28
29
+static char *bdrv_child_user_desc(BdrvChild *c)
30
+{
31
+ if (c->klass->get_parent_desc) {
32
+ return c->klass->get_parent_desc(c);
33
+ }
34
+
35
+ return g_strdup("another user");
36
+}
37
+
38
+static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
39
+{
40
+ g_autofree char *user = NULL;
41
+ g_autofree char *perm_names = NULL;
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.
64
+ */
65
+ QLIST_FOREACH(a, &bs->parents, next_parent) {
66
+ QLIST_FOREACH(b, &bs->parents, next_parent) {
67
+ if (a == b) {
68
+ continue;
69
+ }
70
+
71
+ if (!bdrv_a_allow_b(a, b, errp)) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+
77
+ return false;
78
+}
79
+
80
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
81
BdrvChild *c, BdrvChildRole role,
82
BlockReopenQueue *reopen_queue,
83
@@ -XXX,XX +XXX,XX @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
84
*shared_perm = cumulative_shared_perms;
85
}
86
87
-static char *bdrv_child_user_desc(BdrvChild *c)
88
-{
89
- if (c->klass->get_parent_desc) {
90
- return c->klass->get_parent_desc(c);
91
- }
92
-
93
- return g_strdup("another user");
94
-}
95
-
96
char *bdrv_perm_names(uint64_t perm)
97
{
98
struct perm_name {
99
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
100
int ret;
101
uint64_t perm, shared_perm;
102
103
+ if (bdrv_parent_perms_conflict(bs, errp)) {
104
+ return -EPERM;
105
+ }
106
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
107
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
108
if (ret < 0) {
109
--
110
2.30.2
111
112
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Reviewed-by: Alberto Garcia <berto@igalia.com>
3
Split out non-recursive parts, and refactor as block graph transaction
4
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
4
action.
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
10
---
8
tests/qemu-iotests/184 | 205 ++++++++++++++++++++++++++++++
11
block.c | 79 ++++++++++++++++++++++++++++++++++++++++++---------------
9
tests/qemu-iotests/184.out | 302 +++++++++++++++++++++++++++++++++++++++++++++
12
1 file changed, 59 insertions(+), 20 deletions(-)
10
tests/qemu-iotests/group | 1 +
11
3 files changed, 508 insertions(+)
12
create mode 100755 tests/qemu-iotests/184
13
create mode 100644 tests/qemu-iotests/184.out
14
13
15
diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184
14
diff --git a/block.c b/block.c
16
new file mode 100755
15
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX
16
--- a/block.c
18
--- /dev/null
17
+++ b/block.c
19
+++ b/tests/qemu-iotests/184
20
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@
21
+#!/bin/bash
19
#include "qemu/timer.h"
22
+#
20
#include "qemu/cutils.h"
23
+# Test I/O throttle block filter driver interface
21
#include "qemu/id.h"
24
+#
22
+#include "qemu/transactions.h"
25
+# Copyright (C) 2017 Manos Pitsidianakis
23
#include "block/coroutines.h"
26
+#
24
27
+# This program is free software; you can redistribute it and/or modify
25
#ifdef CONFIG_BSD
28
+# it under the terms of the GNU General Public License as published by
26
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
29
+# the Free Software Foundation; either version 2 of the License, or
27
}
30
+# (at your option) any later version.
28
}
31
+#
29
32
+# This program is distributed in the hope that it will be useful,
30
+static void bdrv_child_set_perm_commit(void *opaque)
33
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
31
+{
34
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
+ BdrvChild *c = opaque;
35
+# GNU General Public License for more details.
36
+#
37
+# You should have received a copy of the GNU General Public License
38
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
39
+#
40
+
33
+
41
+# creator
34
+ c->has_backup_perm = false;
42
+owner="Manos Pitsidianakis"
43
+
44
+seq=`basename $0`
45
+echo "QA output created by $seq"
46
+
47
+here=`pwd`
48
+status=1    # failure is the default!
49
+
50
+_cleanup()
51
+{
52
+ _cleanup_test_img
53
+}
54
+trap "_cleanup; exit \$status" 0 1 2 3 15
55
+
56
+# get standard environment, filters and checks
57
+. ./common.rc
58
+. ./common.filter
59
+
60
+_supported_fmt qcow2
61
+_supported_proto file
62
+_supported_os Linux
63
+
64
+function do_run_qemu()
65
+{
66
+ echo Testing: "$@" | _filter_imgfmt
67
+ $QEMU -nographic -qmp-pretty stdio -serial none "$@"
68
+ echo
69
+}
35
+}
70
+
36
+
71
+function run_qemu()
37
+static void bdrv_child_set_perm_abort(void *opaque)
72
+{
38
+{
73
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\
39
+ BdrvChild *c = opaque;
74
+ | _filter_qemu_io | _filter_generated_node_ids
40
+ /*
75
+}
41
+ * We may have child->has_backup_perm unset at this point, as in case of
76
+
42
+ * _check_ stage of permission update failure we may _check_ not the whole
77
+_make_test_img 64M
43
+ * subtree. Still, _abort_ is called on the whole subtree anyway.
78
+test_throttle=$($QEMU_IMG --help|grep throttle)
44
+ */
79
+[ "$test_throttle" = "" ] && _supported_fmt throttle
45
+ if (c->has_backup_perm) {
80
+
46
+ c->perm = c->backup_perm;
81
+echo
47
+ c->shared_perm = c->backup_shared_perm;
82
+echo "== checking interface =="
48
+ c->has_backup_perm = false;
83
+
84
+run_qemu <<EOF
85
+{ "execute": "qmp_capabilities" }
86
+{ "execute": "blockdev-add",
87
+ "arguments": {
88
+ "driver": "$IMGFMT",
89
+ "node-name": "disk0",
90
+ "file": {
91
+ "driver": "file",
92
+ "filename": "$TEST_IMG"
93
+ }
94
+ }
95
+}
96
+{ "execute": "object-add",
97
+ "arguments": {
98
+ "qom-type": "throttle-group",
99
+ "id": "group0",
100
+ "props": {
101
+ "limits" : {
102
+ "iops-total": 1000
103
+ }
104
+ }
105
+ }
106
+}
107
+{ "execute": "blockdev-add",
108
+ "arguments": {
109
+ "driver": "throttle",
110
+ "node-name": "throttle0",
111
+ "throttle-group": "group0",
112
+ "file": "disk0"
113
+ }
114
+}
115
+{ "execute": "query-named-block-nodes" }
116
+{ "execute": "query-block" }
117
+{ "execute": "quit" }
118
+EOF
119
+
120
+echo
121
+echo "== property changes in ThrottleGroup =="
122
+
123
+run_qemu <<EOF
124
+{ "execute": "qmp_capabilities" }
125
+{ "execute": "object-add",
126
+ "arguments": {
127
+ "qom-type": "throttle-group",
128
+ "id": "group0",
129
+ "props" : {
130
+ "limits": {
131
+ "iops-total": 1000
132
+ }
133
+ }
134
+ }
135
+}
136
+{ "execute" : "qom-get",
137
+ "arguments" : {
138
+ "path" : "group0",
139
+ "property" : "limits"
140
+ }
141
+}
142
+{ "execute" : "qom-set",
143
+ "arguments" : {
144
+ "path" : "group0",
145
+ "property" : "limits",
146
+ "value" : {
147
+ "iops-total" : 0
148
+ }
149
+ }
150
+}
151
+{ "execute" : "qom-get",
152
+ "arguments" : {
153
+ "path" : "group0",
154
+ "property" : "limits"
155
+ }
156
+}
157
+{ "execute": "quit" }
158
+EOF
159
+
160
+echo
161
+echo "== object creation/set errors =="
162
+
163
+run_qemu <<EOF
164
+{ "execute": "qmp_capabilities" }
165
+{ "execute": "object-add",
166
+ "arguments": {
167
+ "qom-type": "throttle-group",
168
+ "id": "group0",
169
+ "props" : {
170
+ "limits": {
171
+ "iops-total": 1000
172
+ }
173
+ }
174
+ }
175
+}
176
+{ "execute" : "qom-set",
177
+ "arguments" : {
178
+ "path" : "group0",
179
+ "property" : "x-iops-total",
180
+ "value" : 0
181
+ }
182
+}
183
+{ "execute" : "qom-set",
184
+ "arguments" : {
185
+ "path" : "group0",
186
+ "property" : "limits",
187
+ "value" : {
188
+ "iops-total" : 10,
189
+ "iops-read" : 10
190
+ }
191
+ }
192
+}
193
+{ "execute": "quit" }
194
+EOF
195
+
196
+echo
197
+echo "== don't specify group =="
198
+
199
+run_qemu <<EOF
200
+{ "execute": "qmp_capabilities" }
201
+{ "execute": "blockdev-add",
202
+ "arguments": {
203
+ "driver": "$IMGFMT",
204
+ "node-name": "disk0",
205
+ "file": {
206
+ "driver": "file",
207
+ "filename": "$TEST_IMG"
208
+ }
209
+ }
210
+}
211
+{ "execute": "blockdev-add",
212
+ "arguments": {
213
+ "driver": "throttle",
214
+ "node-name": "throttle0",
215
+ "file": "disk0"
216
+ }
217
+}
218
+{ "execute": "quit" }
219
+EOF
220
+
221
+echo
222
+# success, all done
223
+echo "*** done"
224
+rm -f $seq.full
225
+status=0
226
diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out
227
new file mode 100644
228
index XXXXXXX..XXXXXXX
229
--- /dev/null
230
+++ b/tests/qemu-iotests/184.out
231
@@ -XXX,XX +XXX,XX @@
232
+QA output created by 184
233
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
234
+
235
+== checking interface ==
236
+Testing:
237
+{
238
+ QMP_VERSION
239
+}
240
+{
241
+ "return": {
242
+ }
243
+}
244
+{
245
+ "return": {
246
+ }
247
+}
248
+{
249
+ "return": {
250
+ }
251
+}
252
+{
253
+ "return": {
254
+ }
255
+}
256
+{
257
+ "return": [
258
+ {
259
+ "iops_rd": 0,
260
+ "detect_zeroes": "off",
261
+ "image": {
262
+ "virtual-size": 67108864,
263
+ "filename": "json:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}",
264
+ "cluster-size": 65536,
265
+ "format": "throttle",
266
+ "actual-size": 200704,
267
+ "dirty-flag": false
268
+ },
269
+ "iops_wr": 0,
270
+ "ro": false,
271
+ "node-name": "throttle0",
272
+ "backing_file_depth": 0,
273
+ "drv": "throttle",
274
+ "iops": 0,
275
+ "bps_wr": 0,
276
+ "write_threshold": 0,
277
+ "encrypted": false,
278
+ "bps": 0,
279
+ "bps_rd": 0,
280
+ "cache": {
281
+ "no-flush": false,
282
+ "direct": false,
283
+ "writeback": true
284
+ },
285
+ "file": "json:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}",
286
+ "encryption_key_missing": false
287
+ },
288
+ {
289
+ "iops_rd": 0,
290
+ "detect_zeroes": "off",
291
+ "image": {
292
+ "virtual-size": 67108864,
293
+ "filename": "TEST_DIR/t.qcow2",
294
+ "cluster-size": 65536,
295
+ "format": "qcow2",
296
+ "actual-size": 200704,
297
+ "format-specific": {
298
+ "type": "qcow2",
299
+ "data": {
300
+ "compat": "1.1",
301
+ "lazy-refcounts": false,
302
+ "refcount-bits": 16,
303
+ "corrupt": false
304
+ }
305
+ },
306
+ "dirty-flag": false
307
+ },
308
+ "iops_wr": 0,
309
+ "ro": false,
310
+ "node-name": "disk0",
311
+ "backing_file_depth": 0,
312
+ "drv": "qcow2",
313
+ "iops": 0,
314
+ "bps_wr": 0,
315
+ "write_threshold": 0,
316
+ "encrypted": false,
317
+ "bps": 0,
318
+ "bps_rd": 0,
319
+ "cache": {
320
+ "no-flush": false,
321
+ "direct": false,
322
+ "writeback": true
323
+ },
324
+ "file": "TEST_DIR/t.qcow2",
325
+ "encryption_key_missing": false
326
+ },
327
+ {
328
+ "iops_rd": 0,
329
+ "detect_zeroes": "off",
330
+ "image": {
331
+ "virtual-size": 197120,
332
+ "filename": "TEST_DIR/t.qcow2",
333
+ "format": "file",
334
+ "actual-size": 200704,
335
+ "dirty-flag": false
336
+ },
337
+ "iops_wr": 0,
338
+ "ro": false,
339
+ "node-name": "NODE_NAME",
340
+ "backing_file_depth": 0,
341
+ "drv": "file",
342
+ "iops": 0,
343
+ "bps_wr": 0,
344
+ "write_threshold": 0,
345
+ "encrypted": false,
346
+ "bps": 0,
347
+ "bps_rd": 0,
348
+ "cache": {
349
+ "no-flush": false,
350
+ "direct": false,
351
+ "writeback": true
352
+ },
353
+ "file": "TEST_DIR/t.qcow2",
354
+ "encryption_key_missing": false
355
+ }
356
+ ]
357
+}
358
+{
359
+ "return": [
360
+ ]
361
+}
362
+{
363
+ "return": {
364
+ }
365
+}
366
+{
367
+ "timestamp": {
368
+ "seconds": TIMESTAMP,
369
+ "microseconds": TIMESTAMP
370
+ },
371
+ "event": "SHUTDOWN",
372
+ "data": {
373
+ "guest": false
374
+ }
49
+ }
375
+}
50
+}
376
+
51
+
52
+static TransactionActionDrv bdrv_child_set_pem_drv = {
53
+ .abort = bdrv_child_set_perm_abort,
54
+ .commit = bdrv_child_set_perm_commit,
55
+};
377
+
56
+
378
+== property changes in ThrottleGroup ==
57
+/*
379
+Testing:
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)
380
+{
66
+{
381
+ QMP_VERSION
67
+ if (!c->has_backup_perm) {
382
+}
68
+ c->has_backup_perm = true;
383
+{
69
+ c->backup_perm = c->perm;
384
+ "return": {
70
+ c->backup_shared_perm = c->shared_perm;
385
+ }
71
+ }
386
+}
72
+ /*
387
+{
73
+ * Note: it's OK if c->has_backup_perm was already set, as we can find the
388
+ "return": {
74
+ * same c twice during check_perm procedure
389
+ }
75
+ */
390
+}
76
+
391
+{
77
+ c->perm = perm;
392
+ "return": {
78
+ c->shared_perm = shared;
393
+ "bps-read-max-length": 1,
79
+
394
+ "iops-read-max-length": 1,
80
+ if (tran) {
395
+ "bps-read-max": 0,
81
+ tran_add(tran, &bdrv_child_set_pem_drv, c);
396
+ "bps-total": 0,
397
+ "iops-total-max-length": 1,
398
+ "iops-total": 1000,
399
+ "iops-write-max": 0,
400
+ "bps-write": 0,
401
+ "bps-total-max": 0,
402
+ "bps-write-max": 0,
403
+ "iops-size": 0,
404
+ "iops-read": 0,
405
+ "iops-write-max-length": 1,
406
+ "iops-write": 0,
407
+ "bps-total-max-length": 1,
408
+ "iops-read-max": 0,
409
+ "bps-read": 0,
410
+ "bps-write-max-length": 1,
411
+ "iops-total-max": 0
412
+ }
413
+}
414
+{
415
+ "return": {
416
+ }
417
+}
418
+{
419
+ "return": {
420
+ "bps-read-max-length": 1,
421
+ "iops-read-max-length": 1,
422
+ "bps-read-max": 0,
423
+ "bps-total": 0,
424
+ "iops-total-max-length": 1,
425
+ "iops-total": 0,
426
+ "iops-write-max": 0,
427
+ "bps-write": 0,
428
+ "bps-total-max": 0,
429
+ "bps-write-max": 0,
430
+ "iops-size": 0,
431
+ "iops-read": 0,
432
+ "iops-write-max-length": 1,
433
+ "iops-write": 0,
434
+ "bps-total-max-length": 1,
435
+ "iops-read-max": 0,
436
+ "bps-read": 0,
437
+ "bps-write-max-length": 1,
438
+ "iops-total-max": 0
439
+ }
440
+}
441
+{
442
+ "return": {
443
+ }
444
+}
445
+{
446
+ "timestamp": {
447
+ "seconds": TIMESTAMP,
448
+ "microseconds": TIMESTAMP
449
+ },
450
+ "event": "SHUTDOWN",
451
+ "data": {
452
+ "guest": false
453
+ }
82
+ }
454
+}
83
+}
455
+
84
+
456
+
85
/*
457
+== object creation/set errors ==
86
* Check whether permissions on this node can be changed in a way that
458
+Testing:
87
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
459
+{
88
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
460
+ QMP_VERSION
89
return ret;
461
+}
90
}
462
+{
91
463
+ "return": {
92
- if (!c->has_backup_perm) {
464
+ }
93
- c->has_backup_perm = true;
465
+}
94
- c->backup_perm = c->perm;
466
+{
95
- c->backup_shared_perm = c->shared_perm;
467
+ "return": {
96
- }
468
+ }
97
- /*
469
+}
98
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
470
+{
99
- * same child twice during check_perm procedure
471
+ "error": {
100
- */
472
+ "class": "GenericError",
101
-
473
+ "desc": "Property cannot be set after initialization"
102
- c->perm = perm;
474
+ }
103
- c->shared_perm = shared;
475
+}
104
+ bdrv_child_set_perm_safe(c, perm, shared, NULL);
476
+{
105
477
+ "error": {
106
return 0;
478
+ "class": "GenericError",
107
}
479
+ "desc": "bps/iops/max total values and read/write values cannot be used at the same time"
108
480
+ }
109
static void bdrv_child_set_perm(BdrvChild *c)
481
+}
110
{
482
+{
111
- c->has_backup_perm = false;
483
+ "return": {
112
-
484
+ }
113
+ bdrv_child_set_perm_commit(c);
485
+}
114
bdrv_set_perm(c->bs);
486
+{
115
}
487
+ "timestamp": {
116
488
+ "seconds": TIMESTAMP,
117
static void bdrv_child_abort_perm_update(BdrvChild *c)
489
+ "microseconds": TIMESTAMP
118
{
490
+ },
119
- if (c->has_backup_perm) {
491
+ "event": "SHUTDOWN",
120
- c->perm = c->backup_perm;
492
+ "data": {
121
- c->shared_perm = c->backup_shared_perm;
493
+ "guest": false
122
- c->has_backup_perm = false;
494
+ }
123
- }
495
+}
124
-
496
+
125
+ bdrv_child_set_perm_abort(c);
497
+
126
bdrv_abort_perm_update(c->bs);
498
+== don't specify group ==
127
}
499
+Testing:
128
500
+{
501
+ QMP_VERSION
502
+}
503
+{
504
+ "return": {
505
+ }
506
+}
507
+{
508
+ "return": {
509
+ }
510
+}
511
+{
512
+ "error": {
513
+ "class": "GenericError",
514
+ "desc": "Parameter 'throttle-group' is missing"
515
+ }
516
+}
517
+{
518
+ "return": {
519
+ }
520
+}
521
+{
522
+ "timestamp": {
523
+ "seconds": TIMESTAMP,
524
+ "microseconds": TIMESTAMP
525
+ },
526
+ "event": "SHUTDOWN",
527
+ "data": {
528
+ "guest": false
529
+ }
530
+}
531
+
532
+
533
+*** done
534
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
535
index XXXXXXX..XXXXXXX 100644
536
--- a/tests/qemu-iotests/group
537
+++ b/tests/qemu-iotests/group
538
@@ -XXX,XX +XXX,XX @@
539
181 rw auto migration
540
182 rw auto quick
541
183 rw auto migration
542
+184 rw auto quick
543
185 rw auto
544
186 rw auto
545
187 rw auto
546
--
129
--
547
2.13.5
130
2.30.2
548
131
549
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
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This commit eliminates the 1:1 relationship between BlockBackend and
3
Each of them has only one caller. Open-coding simplifies further
4
throttle group state. Users will be able to create multiple throttle
4
pemission-update system changes.
5
nodes, each with its own throttle group state, in the future. The
6
throttle group state cannot be per-BlockBackend anymore, it must be
7
per-throttle node. This is done by gathering ThrottleGroup membership
8
details from BlockBackendPublic into ThrottleGroupMember and refactoring
9
existing code to use the structure.
10
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
9
Message-Id: <20210428151804.439460-13-vsementsov@virtuozzo.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
11
---
16
include/block/throttle-groups.h | 39 +++++-
12
block.c | 59 +++++++++++++++++----------------------------------------
17
include/sysemu/block-backend.h | 20 +--
13
1 file changed, 17 insertions(+), 42 deletions(-)
18
block/block-backend.c | 66 +++++----
19
block/qapi.c | 8 +-
20
block/throttle-groups.c | 288 ++++++++++++++++++++--------------------
21
blockdev.c | 4 +-
22
tests/test-throttle.c | 53 ++++----
23
7 files changed, 252 insertions(+), 226 deletions(-)
24
14
25
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
15
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/throttle-groups.h
17
--- a/block.c
28
+++ b/include/block/throttle-groups.h
18
+++ b/block.c
29
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
30
#include "qemu/throttle.h"
20
return 0;
31
#include "block/block_int.h"
21
}
32
22
33
-const char *throttle_group_get_name(BlockBackend *blk);
23
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
34
+/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup
24
- uint64_t perm, uint64_t shared,
35
+ * and holds related data.
25
- GSList *ignore_children, Error **errp);
36
+ */
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);
33
34
typedef struct BlockReopenQueueEntry {
35
bool prepared;
36
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
37
/* Check all children */
38
QLIST_FOREACH(c, &bs->children, next) {
39
uint64_t cur_perm, cur_shared;
40
+ GSList *cur_ignore_children;
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);
37
+
47
+
38
+typedef struct ThrottleGroupMember {
48
+ cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
39
+ /* throttled_reqs_lock protects the CoQueues for throttled requests. */
49
+ ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared,
40
+ CoMutex throttled_reqs_lock;
50
+ cur_ignore_children, errp);
41
+ CoQueue throttled_reqs[2];
51
+ g_slist_free(cur_ignore_children);
52
if (ret < 0) {
53
return ret;
54
}
42
+
55
+
43
+ /* Nonzero if the I/O limits are currently being ignored; generally
56
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
44
+ * it is zero. Accessed with atomic operations.
45
+ */
46
+ unsigned int io_limits_disabled;
47
+
48
+ /* The following fields are protected by the ThrottleGroup lock.
49
+ * See the ThrottleGroup documentation for details.
50
+ * throttle_state tells us if I/O limits are configured. */
51
+ ThrottleState *throttle_state;
52
+ ThrottleTimers throttle_timers;
53
+ unsigned pending_reqs[2];
54
+ QLIST_ENTRY(ThrottleGroupMember) round_robin;
55
+
56
+} ThrottleGroupMember;
57
+
58
+const char *throttle_group_get_name(ThrottleGroupMember *tgm);
59
60
ThrottleState *throttle_group_incref(const char *name);
61
void throttle_group_unref(ThrottleState *ts);
62
63
-void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg);
64
-void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg);
65
+void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
66
+void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
67
68
-void throttle_group_register_blk(BlockBackend *blk, const char *groupname);
69
-void throttle_group_unregister_blk(BlockBackend *blk);
70
-void throttle_group_restart_blk(BlockBackend *blk);
71
+void throttle_group_register_tgm(ThrottleGroupMember *tgm,
72
+ const char *groupname);
73
+void throttle_group_unregister_tgm(ThrottleGroupMember *tgm);
74
+void throttle_group_restart_tgm(ThrottleGroupMember *tgm);
75
76
-void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
77
+void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
78
unsigned int bytes,
79
bool is_write);
80
81
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/sysemu/block-backend.h
84
+++ b/include/sysemu/block-backend.h
85
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDevOps {
86
87
/* This struct is embedded in (the private) BlockBackend struct and contains
88
* fields that must be public. This is in particular for QLIST_ENTRY() and
89
- * friends so that BlockBackends can be kept in lists outside block-backend.c */
90
+ * friends so that BlockBackends can be kept in lists outside block-backend.c
91
+ * */
92
typedef struct BlockBackendPublic {
93
- /* throttled_reqs_lock protects the CoQueues for throttled requests. */
94
- CoMutex throttled_reqs_lock;
95
- CoQueue throttled_reqs[2];
96
-
97
- /* Nonzero if the I/O limits are currently being ignored; generally
98
- * it is zero. Accessed with atomic operations.
99
- */
100
- unsigned int io_limits_disabled;
101
-
102
- /* The following fields are protected by the ThrottleGroup lock.
103
- * See the ThrottleGroup documentation for details.
104
- * throttle_state tells us if I/O limits are configured. */
105
- ThrottleState *throttle_state;
106
- ThrottleTimers throttle_timers;
107
- unsigned pending_reqs[2];
108
- QLIST_ENTRY(BlockBackendPublic) round_robin;
109
+ ThrottleGroupMember throttle_group_member;
110
} BlockBackendPublic;
111
112
BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);
113
diff --git a/block/block-backend.c b/block/block-backend.c
114
index XXXXXXX..XXXXXXX 100644
115
--- a/block/block-backend.c
116
+++ b/block/block-backend.c
117
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
118
blk->shared_perm = shared_perm;
119
blk_set_enable_write_cache(blk, true);
120
121
- qemu_co_mutex_init(&blk->public.throttled_reqs_lock);
122
- qemu_co_queue_init(&blk->public.throttled_reqs[0]);
123
- qemu_co_queue_init(&blk->public.throttled_reqs[1]);
124
+ qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
125
+ qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
126
+ qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
127
block_acct_init(&blk->stats);
128
129
notifier_list_init(&blk->remove_bs_notifiers);
130
@@ -XXX,XX +XXX,XX @@ static void blk_delete(BlockBackend *blk)
131
assert(!blk->refcnt);
132
assert(!blk->name);
133
assert(!blk->dev);
134
- if (blk->public.throttle_state) {
135
+ if (blk->public.throttle_group_member.throttle_state) {
136
blk_io_limits_disable(blk);
137
}
57
}
138
if (blk->root) {
58
139
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
59
return 0;
140
*/
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
141
void blk_remove_bs(BlockBackend *blk)
142
{
143
+ ThrottleTimers *tt;
144
+
145
notifier_list_notify(&blk->remove_bs_notifiers, blk);
146
- if (blk->public.throttle_state) {
147
- throttle_timers_detach_aio_context(&blk->public.throttle_timers);
148
+ if (blk->public.throttle_group_member.throttle_state) {
149
+ tt = &blk->public.throttle_group_member.throttle_timers;
150
+ throttle_timers_detach_aio_context(tt);
151
}
61
}
152
62
153
blk_update_root_state(blk);
63
QLIST_FOREACH(c, &bs->children, next) {
154
@@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
64
- bdrv_child_abort_perm_update(c);
155
bdrv_ref(bs);
65
+ bdrv_child_set_perm_abort(c);
156
66
+ bdrv_abort_perm_update(c->bs);
157
notifier_list_notify(&blk->insert_bs_notifiers, blk);
158
- if (blk->public.throttle_state) {
159
+ if (blk->public.throttle_group_member.throttle_state) {
160
throttle_timers_attach_aio_context(
161
- &blk->public.throttle_timers, bdrv_get_aio_context(bs));
162
+ &blk->public.throttle_group_member.throttle_timers,
163
+ bdrv_get_aio_context(bs));
164
}
165
166
return 0;
167
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
168
bdrv_inc_in_flight(bs);
169
170
/* throttling disk I/O */
171
- if (blk->public.throttle_state) {
172
- throttle_group_co_io_limits_intercept(blk, bytes, false);
173
+ if (blk->public.throttle_group_member.throttle_state) {
174
+ throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
175
+ bytes, false);
176
}
177
178
ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
179
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
180
}
181
182
bdrv_inc_in_flight(bs);
183
-
184
/* throttling disk I/O */
185
- if (blk->public.throttle_state) {
186
- throttle_group_co_io_limits_intercept(blk, bytes, true);
187
+ if (blk->public.throttle_group_member.throttle_state) {
188
+ throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
189
+ bytes, true);
190
}
191
192
if (!blk->enable_write_cache) {
193
@@ -XXX,XX +XXX,XX @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
194
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
195
{
196
BlockDriverState *bs = blk_bs(blk);
197
+ ThrottleTimers *tt;
198
199
if (bs) {
200
- if (blk->public.throttle_state) {
201
- throttle_timers_detach_aio_context(&blk->public.throttle_timers);
202
+ if (blk->public.throttle_group_member.throttle_state) {
203
+ tt = &blk->public.throttle_group_member.throttle_timers;
204
+ throttle_timers_detach_aio_context(tt);
205
}
206
bdrv_set_aio_context(bs, new_context);
207
- if (blk->public.throttle_state) {
208
- throttle_timers_attach_aio_context(&blk->public.throttle_timers,
209
- new_context);
210
+ if (blk->public.throttle_group_member.throttle_state) {
211
+ tt = &blk->public.throttle_group_member.throttle_timers;
212
+ throttle_timers_attach_aio_context(tt, new_context);
213
}
214
}
67
}
215
}
68
}
216
@@ -XXX,XX +XXX,XX @@ int blk_commit_all(void)
69
217
/* throttling disk I/O limits */
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs)
218
void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
71
219
{
72
/* Update all children */
220
- throttle_group_config(blk, cfg);
73
QLIST_FOREACH(c, &bs->children, next) {
221
+ throttle_group_config(&blk->public.throttle_group_member, cfg);
74
- bdrv_child_set_perm(c);
222
}
75
+ bdrv_child_set_perm_commit(c);
223
76
+ bdrv_set_perm(c->bs);
224
void blk_io_limits_disable(BlockBackend *blk)
225
{
226
- assert(blk->public.throttle_state);
227
+ assert(blk->public.throttle_group_member.throttle_state);
228
bdrv_drained_begin(blk_bs(blk));
229
- throttle_group_unregister_blk(blk);
230
+ throttle_group_unregister_tgm(&blk->public.throttle_group_member);
231
bdrv_drained_end(blk_bs(blk));
232
}
233
234
/* should be called before blk_set_io_limits if a limit is set */
235
void blk_io_limits_enable(BlockBackend *blk, const char *group)
236
{
237
- assert(!blk->public.throttle_state);
238
- throttle_group_register_blk(blk, group);
239
+ assert(!blk->public.throttle_group_member.throttle_state);
240
+ throttle_group_register_tgm(&blk->public.throttle_group_member, group);
241
}
242
243
void blk_io_limits_update_group(BlockBackend *blk, const char *group)
244
{
245
/* this BB is not part of any group */
246
- if (!blk->public.throttle_state) {
247
+ if (!blk->public.throttle_group_member.throttle_state) {
248
return;
249
}
250
251
/* this BB is a part of the same group than the one we want */
252
- if (!g_strcmp0(throttle_group_get_name(blk), group)) {
253
+ if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member),
254
+ group)) {
255
return;
256
}
257
258
@@ -XXX,XX +XXX,XX @@ static void blk_root_drained_begin(BdrvChild *child)
259
/* Note that blk->root may not be accessible here yet if we are just
260
* attaching to a BlockDriverState that is drained. Use child instead. */
261
262
- if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) {
263
- throttle_group_restart_blk(blk);
264
+ if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) {
265
+ throttle_group_restart_tgm(&blk->public.throttle_group_member);
266
}
77
}
267
}
78
}
268
79
269
@@ -XXX,XX +XXX,XX @@ static void blk_root_drained_end(BdrvChild *child)
80
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
270
BlockBackend *blk = child->opaque;
81
ignore_children, errp);
271
assert(blk->quiesce_counter);
272
273
- assert(blk->public.io_limits_disabled);
274
- atomic_dec(&blk->public.io_limits_disabled);
275
+ assert(blk->public.throttle_group_member.io_limits_disabled);
276
+ atomic_dec(&blk->public.throttle_group_member.io_limits_disabled);
277
278
if (--blk->quiesce_counter == 0) {
279
if (blk->dev_ops && blk->dev_ops->drained_end) {
280
diff --git a/block/qapi.c b/block/qapi.c
281
index XXXXXXX..XXXXXXX 100644
282
--- a/block/qapi.c
283
+++ b/block/qapi.c
284
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
285
286
info->detect_zeroes = bs->detect_zeroes;
287
288
- if (blk && blk_get_public(blk)->throttle_state) {
289
+ if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) {
290
ThrottleConfig cfg;
291
+ BlockBackendPublic *blkp = blk_get_public(blk);
292
293
- throttle_group_get_config(blk, &cfg);
294
+ throttle_group_get_config(&blkp->throttle_group_member, &cfg);
295
296
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
297
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
298
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
299
info->iops_size = cfg.op_size;
300
301
info->has_group = true;
302
- info->group = g_strdup(throttle_group_get_name(blk));
303
+ info->group =
304
+ g_strdup(throttle_group_get_name(&blkp->throttle_group_member));
305
}
306
307
info->write_threshold = bdrv_write_threshold_get(bs);
308
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
309
index XXXXXXX..XXXXXXX 100644
310
--- a/block/throttle-groups.c
311
+++ b/block/throttle-groups.c
312
@@ -XXX,XX +XXX,XX @@
313
#include "sysemu/qtest.h"
314
315
/* The ThrottleGroup structure (with its ThrottleState) is shared
316
- * among different BlockBackends and it's independent from
317
+ * among different ThrottleGroupMembers and it's independent from
318
* AioContext, so in order to use it from different threads it needs
319
* its own locking.
320
*
321
@@ -XXX,XX +XXX,XX @@
322
* The whole ThrottleGroup structure is private and invisible to
323
* outside users, that only use it through its ThrottleState.
324
*
325
- * In addition to the ThrottleGroup structure, BlockBackendPublic has
326
+ * In addition to the ThrottleGroup structure, ThrottleGroupMember has
327
* fields that need to be accessed by other members of the group and
328
* therefore also need to be protected by this lock. Once a
329
- * BlockBackend is registered in a group those fields can be accessed
330
+ * ThrottleGroupMember is registered in a group those fields can be accessed
331
* by other threads any time.
332
*
333
* Again, all this is handled internally and is mostly transparent to
334
* the outside. The 'throttle_timers' field however has an additional
335
* constraint because it may be temporarily invalid (see for example
336
* blk_set_aio_context()). Therefore in this file a thread will
337
- * access some other BlockBackend's timers only after verifying that
338
- * that BlockBackend has throttled requests in the queue.
339
+ * access some other ThrottleGroupMember's timers only after verifying that
340
+ * that ThrottleGroupMember has throttled requests in the queue.
341
*/
342
typedef struct ThrottleGroup {
343
char *name; /* This is constant during the lifetime of the group */
344
345
QemuMutex lock; /* This lock protects the following four fields */
346
ThrottleState ts;
347
- QLIST_HEAD(, BlockBackendPublic) head;
348
- BlockBackend *tokens[2];
349
+ QLIST_HEAD(, ThrottleGroupMember) head;
350
+ ThrottleGroupMember *tokens[2];
351
bool any_timer_armed[2];
352
QEMUClockType clock_type;
353
354
@@ -XXX,XX +XXX,XX @@ void throttle_group_unref(ThrottleState *ts)
355
qemu_mutex_unlock(&throttle_groups_lock);
356
}
82
}
357
83
358
-/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer)
84
-/* Needs to be followed by a call to either bdrv_child_set_perm() or
359
+/* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
85
- * bdrv_child_abort_perm_update(). */
360
* is guaranteed to remain constant during the lifetime of the group.
86
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
361
*
87
- uint64_t perm, uint64_t shared,
362
- * @blk: a BlockBackend that is member of a throttling group
88
- GSList *ignore_children, Error **errp)
363
+ * @tgm: a ThrottleGroupMember
89
-{
364
* @ret: the name of the group.
90
- int ret;
365
*/
91
-
366
-const char *throttle_group_get_name(BlockBackend *blk)
92
- ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
367
+const char *throttle_group_get_name(ThrottleGroupMember *tgm)
93
- ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
94
- g_slist_free(ignore_children);
95
-
96
- if (ret < 0) {
97
- return ret;
98
- }
99
-
100
- bdrv_child_set_perm_safe(c, perm, shared, NULL);
101
-
102
- return 0;
103
-}
104
-
105
-static void bdrv_child_set_perm(BdrvChild *c)
106
-{
107
- bdrv_child_set_perm_commit(c);
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)
368
{
118
{
369
- BlockBackendPublic *blkp = blk_get_public(blk);
119
int ret;
370
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
371
+ ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
372
return tg->name;
373
}
374
375
-/* Return the next BlockBackend in the round-robin sequence, simulating a
376
- * circular list.
377
+/* Return the next ThrottleGroupMember in the round-robin sequence, simulating
378
+ * a circular list.
379
*
380
* This assumes that tg->lock is held.
381
*
382
- * @blk: the current BlockBackend
383
- * @ret: the next BlockBackend in the sequence
384
+ * @tgm: the current ThrottleGroupMember
385
+ * @ret: the next ThrottleGroupMember in the sequence
386
*/
387
-static BlockBackend *throttle_group_next_blk(BlockBackend *blk)
388
+static ThrottleGroupMember *throttle_group_next_tgm(ThrottleGroupMember *tgm)
389
{
390
- BlockBackendPublic *blkp = blk_get_public(blk);
391
- ThrottleState *ts = blkp->throttle_state;
392
+ ThrottleState *ts = tgm->throttle_state;
393
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
394
- BlockBackendPublic *next = QLIST_NEXT(blkp, round_robin);
395
+ ThrottleGroupMember *next = QLIST_NEXT(tgm, round_robin);
396
397
if (!next) {
398
next = QLIST_FIRST(&tg->head);
399
}
400
401
- return blk_by_public(next);
402
+ return next;
403
}
404
405
/*
406
- * Return whether a BlockBackend has pending requests.
407
+ * Return whether a ThrottleGroupMember has pending requests.
408
*
409
* This assumes that tg->lock is held.
410
*
411
- * @blk: the BlockBackend
412
- * @is_write: the type of operation (read/write)
413
- * @ret: whether the BlockBackend has pending requests.
414
+ * @tgm: the ThrottleGroupMember
415
+ * @is_write: the type of operation (read/write)
416
+ * @ret: whether the ThrottleGroupMember has pending requests.
417
*/
418
-static inline bool blk_has_pending_reqs(BlockBackend *blk,
419
+static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm,
420
bool is_write)
421
{
422
- const BlockBackendPublic *blkp = blk_get_public(blk);
423
- return blkp->pending_reqs[is_write];
424
+ return tgm->pending_reqs[is_write];
425
}
426
427
-/* Return the next BlockBackend in the round-robin sequence with pending I/O
428
- * requests.
429
+/* Return the next ThrottleGroupMember in the round-robin sequence with pending
430
+ * I/O requests.
431
*
432
* This assumes that tg->lock is held.
433
*
434
- * @blk: the current BlockBackend
435
+ * @tgm: the current ThrottleGroupMember
436
* @is_write: the type of operation (read/write)
437
- * @ret: the next BlockBackend with pending requests, or blk if there is
438
- * none.
439
+ * @ret: the next ThrottleGroupMember with pending requests, or tgm if
440
+ * there is none.
441
*/
442
-static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write)
443
+static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
444
+ bool is_write)
445
{
446
- BlockBackendPublic *blkp = blk_get_public(blk);
447
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
448
- BlockBackend *token, *start;
449
+ ThrottleState *ts = tgm->throttle_state;
450
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
451
+ ThrottleGroupMember *token, *start;
452
453
start = token = tg->tokens[is_write];
454
455
/* get next bs round in round robin style */
456
- token = throttle_group_next_blk(token);
457
- while (token != start && !blk_has_pending_reqs(token, is_write)) {
458
- token = throttle_group_next_blk(token);
459
+ token = throttle_group_next_tgm(token);
460
+ while (token != start && !tgm_has_pending_reqs(token, is_write)) {
461
+ token = throttle_group_next_tgm(token);
462
}
463
464
/* If no IO are queued for scheduling on the next round robin token
465
- * then decide the token is the current bs because chances are
466
- * the current bs get the current request queued.
467
+ * then decide the token is the current tgm because chances are
468
+ * the current tgm got the current request queued.
469
*/
470
- if (token == start && !blk_has_pending_reqs(token, is_write)) {
471
- token = blk;
472
+ if (token == start && !tgm_has_pending_reqs(token, is_write)) {
473
+ token = tgm;
474
}
475
476
- /* Either we return the original BB, or one with pending requests */
477
- assert(token == blk || blk_has_pending_reqs(token, is_write));
478
+ /* Either we return the original TGM, or one with pending requests */
479
+ assert(token == tgm || tgm_has_pending_reqs(token, is_write));
480
481
return token;
482
}
483
484
-/* Check if the next I/O request for a BlockBackend needs to be throttled or
485
- * not. If there's no timer set in this group, set one and update the token
486
- * accordingly.
487
+/* Check if the next I/O request for a ThrottleGroupMember needs to be
488
+ * throttled or not. If there's no timer set in this group, set one and update
489
+ * the token accordingly.
490
*
491
* This assumes that tg->lock is held.
492
*
493
- * @blk: the current BlockBackend
494
+ * @tgm: the current ThrottleGroupMember
495
* @is_write: the type of operation (read/write)
496
* @ret: whether the I/O request needs to be throttled or not
497
*/
498
-static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
499
+static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
500
+ bool is_write)
501
{
502
- BlockBackendPublic *blkp = blk_get_public(blk);
503
- ThrottleState *ts = blkp->throttle_state;
504
- ThrottleTimers *tt = &blkp->throttle_timers;
505
+ ThrottleState *ts = tgm->throttle_state;
506
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
507
+ ThrottleTimers *tt = &tgm->throttle_timers;
508
bool must_wait;
509
510
- if (atomic_read(&blkp->io_limits_disabled)) {
511
+ if (atomic_read(&tgm->io_limits_disabled)) {
512
return false;
513
}
514
515
@@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write)
516
517
must_wait = throttle_schedule_timer(ts, tt, is_write);
518
519
- /* If a timer just got armed, set blk as the current token */
520
+ /* If a timer just got armed, set tgm as the current token */
521
if (must_wait) {
522
- tg->tokens[is_write] = blk;
523
+ tg->tokens[is_write] = tgm;
524
tg->any_timer_armed[is_write] = true;
525
}
526
527
return must_wait;
528
}
529
530
-/* Start the next pending I/O request for a BlockBackend. Return whether
531
+/* Start the next pending I/O request for a ThrottleGroupMember. Return whether
532
* any request was actually pending.
533
*
534
- * @blk: the current BlockBackend
535
+ * @tgm: the current ThrottleGroupMember
536
* @is_write: the type of operation (read/write)
537
*/
538
-static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
539
+static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm,
540
bool is_write)
541
{
542
- BlockBackendPublic *blkp = blk_get_public(blk);
543
bool ret;
544
545
- qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
546
- ret = qemu_co_queue_next(&blkp->throttled_reqs[is_write]);
547
- qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
548
+ qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
549
+ ret = qemu_co_queue_next(&tgm->throttled_reqs[is_write]);
550
+ qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
551
552
return ret;
553
}
554
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk,
555
*
556
* This assumes that tg->lock is held.
557
*
558
- * @blk: the current BlockBackend
559
+ * @tgm: the current ThrottleGroupMember
560
* @is_write: the type of operation (read/write)
561
*/
562
-static void schedule_next_request(BlockBackend *blk, bool is_write)
563
+static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
564
{
565
- BlockBackendPublic *blkp = blk_get_public(blk);
566
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
567
+ ThrottleState *ts = tgm->throttle_state;
568
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
569
bool must_wait;
570
- BlockBackend *token;
571
+ ThrottleGroupMember *token;
572
573
/* Check if there's any pending request to schedule next */
574
- token = next_throttle_token(blk, is_write);
575
- if (!blk_has_pending_reqs(token, is_write)) {
576
+ token = next_throttle_token(tgm, is_write);
577
+ if (!tgm_has_pending_reqs(token, is_write)) {
578
return;
579
}
580
581
@@ -XXX,XX +XXX,XX @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
582
583
/* If it doesn't have to wait, queue it for immediate execution */
584
if (!must_wait) {
585
- /* Give preference to requests from the current blk */
586
+ /* Give preference to requests from the current tgm */
587
if (qemu_in_coroutine() &&
588
- throttle_group_co_restart_queue(blk, is_write)) {
589
- token = blk;
590
+ throttle_group_co_restart_queue(tgm, is_write)) {
591
+ token = tgm;
592
} else {
593
- ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
594
+ ThrottleTimers *tt = &token->throttle_timers;
595
int64_t now = qemu_clock_get_ns(tg->clock_type);
596
timer_mod(tt->timers[is_write], now);
597
tg->any_timer_armed[is_write] = true;
598
@@ -XXX,XX +XXX,XX @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
599
* if necessary, and schedule the next request using a round robin
600
* algorithm.
601
*
602
- * @blk: the current BlockBackend
603
+ * @tgm: the current ThrottleGroupMember
604
* @bytes: the number of bytes for this I/O
605
* @is_write: the type of operation (read/write)
606
*/
607
-void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
608
+void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
609
unsigned int bytes,
610
bool is_write)
611
{
612
bool must_wait;
613
- BlockBackend *token;
614
-
615
- BlockBackendPublic *blkp = blk_get_public(blk);
616
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
617
+ ThrottleGroupMember *token;
618
+ ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
619
qemu_mutex_lock(&tg->lock);
620
621
/* First we check if this I/O has to be throttled. */
622
- token = next_throttle_token(blk, is_write);
623
+ token = next_throttle_token(tgm, is_write);
624
must_wait = throttle_group_schedule_timer(token, is_write);
625
626
/* Wait if there's a timer set or queued requests of this type */
627
- if (must_wait || blkp->pending_reqs[is_write]) {
628
- blkp->pending_reqs[is_write]++;
629
+ if (must_wait || tgm->pending_reqs[is_write]) {
630
+ tgm->pending_reqs[is_write]++;
631
qemu_mutex_unlock(&tg->lock);
632
- qemu_co_mutex_lock(&blkp->throttled_reqs_lock);
633
- qemu_co_queue_wait(&blkp->throttled_reqs[is_write],
634
- &blkp->throttled_reqs_lock);
635
- qemu_co_mutex_unlock(&blkp->throttled_reqs_lock);
636
+ qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
637
+ qemu_co_queue_wait(&tgm->throttled_reqs[is_write],
638
+ &tgm->throttled_reqs_lock);
639
+ qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
640
qemu_mutex_lock(&tg->lock);
641
- blkp->pending_reqs[is_write]--;
642
+ tgm->pending_reqs[is_write]--;
643
}
644
645
/* The I/O will be executed, so do the accounting */
646
- throttle_account(blkp->throttle_state, is_write, bytes);
647
+ throttle_account(tgm->throttle_state, is_write, bytes);
648
649
/* Schedule the next request */
650
- schedule_next_request(blk, is_write);
651
+ schedule_next_request(tgm, is_write);
652
653
qemu_mutex_unlock(&tg->lock);
654
}
655
656
typedef struct {
657
- BlockBackend *blk;
658
+ ThrottleGroupMember *tgm;
659
bool is_write;
660
} RestartData;
661
662
static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
663
{
664
RestartData *data = opaque;
665
- BlockBackend *blk = data->blk;
666
+ ThrottleGroupMember *tgm = data->tgm;
667
+ ThrottleState *ts = tgm->throttle_state;
668
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
669
bool is_write = data->is_write;
670
- BlockBackendPublic *blkp = blk_get_public(blk);
671
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
672
bool empty_queue;
673
674
- empty_queue = !throttle_group_co_restart_queue(blk, is_write);
675
+ empty_queue = !throttle_group_co_restart_queue(tgm, is_write);
676
677
/* If the request queue was empty then we have to take care of
678
* scheduling the next one */
679
if (empty_queue) {
680
qemu_mutex_lock(&tg->lock);
681
- schedule_next_request(blk, is_write);
682
+ schedule_next_request(tgm, is_write);
683
qemu_mutex_unlock(&tg->lock);
684
}
685
}
686
687
-static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
688
+static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
689
{
690
+ BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
691
+ throttle_group_member);
692
+ BlockBackend *blk = blk_by_public(blkp);
693
Coroutine *co;
694
RestartData rd = {
695
- .blk = blk,
696
+ .tgm = tgm,
697
.is_write = is_write
698
};
699
700
@@ -XXX,XX +XXX,XX @@ static void throttle_group_restart_queue(BlockBackend *blk, bool is_write)
701
aio_co_enter(blk_get_aio_context(blk), co);
702
}
703
704
-void throttle_group_restart_blk(BlockBackend *blk)
705
+void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
706
{
707
- BlockBackendPublic *blkp = blk_get_public(blk);
708
-
709
- if (blkp->throttle_state) {
710
- throttle_group_restart_queue(blk, 0);
711
- throttle_group_restart_queue(blk, 1);
712
+ if (tgm->throttle_state) {
713
+ throttle_group_restart_queue(tgm, 0);
714
+ throttle_group_restart_queue(tgm, 1);
715
}
716
}
717
718
@@ -XXX,XX +XXX,XX @@ void throttle_group_restart_blk(BlockBackend *blk)
719
* to throttle_config(), but guarantees atomicity within the
720
* throttling group.
721
*
722
- * @blk: a BlockBackend that is a member of the group
723
+ * @tgm: a ThrottleGroupMember that is a member of the group
724
* @cfg: the configuration to set
725
*/
726
-void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg)
727
+void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
728
{
729
- BlockBackendPublic *blkp = blk_get_public(blk);
730
- ThrottleState *ts = blkp->throttle_state;
731
+ ThrottleState *ts = tgm->throttle_state;
732
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
733
qemu_mutex_lock(&tg->lock);
734
throttle_config(ts, tg->clock_type, cfg);
735
qemu_mutex_unlock(&tg->lock);
736
737
- throttle_group_restart_blk(blk);
738
+ throttle_group_restart_tgm(tgm);
739
}
740
741
/* Get the throttle configuration from a particular group. Similar to
742
* throttle_get_config(), but guarantees atomicity within the
743
* throttling group.
744
*
745
- * @blk: a BlockBackend that is a member of the group
746
+ * @tgm: a ThrottleGroupMember that is a member of the group
747
* @cfg: the configuration will be written here
748
*/
749
-void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
750
+void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
751
{
752
- BlockBackendPublic *blkp = blk_get_public(blk);
753
- ThrottleState *ts = blkp->throttle_state;
754
+ ThrottleState *ts = tgm->throttle_state;
755
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
756
qemu_mutex_lock(&tg->lock);
757
throttle_get_config(ts, cfg);
758
@@ -XXX,XX +XXX,XX @@ void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg)
759
static void timer_cb(BlockBackend *blk, bool is_write)
760
{
761
BlockBackendPublic *blkp = blk_get_public(blk);
762
- ThrottleState *ts = blkp->throttle_state;
763
+ ThrottleGroupMember *tgm = &blkp->throttle_group_member;
764
+ ThrottleState *ts = tgm->throttle_state;
765
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
766
767
/* The timer has just been fired, so we can update the flag */
768
@@ -XXX,XX +XXX,XX @@ static void timer_cb(BlockBackend *blk, bool is_write)
769
qemu_mutex_unlock(&tg->lock);
770
771
/* Run the request that was waiting for this timer */
772
- throttle_group_restart_queue(blk, is_write);
773
+ throttle_group_restart_queue(tgm, is_write);
774
}
775
776
static void read_timer_cb(void *opaque)
777
@@ -XXX,XX +XXX,XX @@ static void write_timer_cb(void *opaque)
778
timer_cb(opaque, true);
779
}
780
781
-/* Register a BlockBackend in the throttling group, also initializing its
782
- * timers and updating its throttle_state pointer to point to it. If a
783
+/* Register a ThrottleGroupMember from the throttling group, also initializing
784
+ * its timers and updating its throttle_state pointer to point to it. If a
785
* throttling group with that name does not exist yet, it will be created.
786
*
787
- * @blk: the BlockBackend to insert
788
+ * @tgm: the ThrottleGroupMember to insert
789
* @groupname: the name of the group
790
*/
791
-void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
792
+void throttle_group_register_tgm(ThrottleGroupMember *tgm,
793
+ const char *groupname)
794
{
795
int i;
796
- BlockBackendPublic *blkp = blk_get_public(blk);
797
+ BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
798
+ throttle_group_member);
799
+ BlockBackend *blk = blk_by_public(blkp);
800
ThrottleState *ts = throttle_group_incref(groupname);
801
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
802
- blkp->throttle_state = ts;
803
+
804
+ tgm->throttle_state = ts;
805
806
qemu_mutex_lock(&tg->lock);
807
- /* If the ThrottleGroup is new set this BlockBackend as the token */
808
+ /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
809
for (i = 0; i < 2; i++) {
810
if (!tg->tokens[i]) {
811
- tg->tokens[i] = blk;
812
+ tg->tokens[i] = tgm;
813
}
814
}
815
816
- QLIST_INSERT_HEAD(&tg->head, blkp, round_robin);
817
+ QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
818
819
- throttle_timers_init(&blkp->throttle_timers,
820
+ throttle_timers_init(&tgm->throttle_timers,
821
blk_get_aio_context(blk),
822
tg->clock_type,
823
read_timer_cb,
824
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname)
825
qemu_mutex_unlock(&tg->lock);
826
}
827
828
-/* Unregister a BlockBackend from its group, removing it from the list,
829
+/* Unregister a ThrottleGroupMember from its group, removing it from the list,
830
* destroying the timers and setting the throttle_state pointer to NULL.
831
*
832
- * The BlockBackend must not have pending throttled requests, so the caller has
833
- * to drain them first.
834
+ * The ThrottleGroupMember must not have pending throttled requests, so the
835
+ * caller has to drain them first.
836
*
837
* The group will be destroyed if it's empty after this operation.
838
*
839
- * @blk: the BlockBackend to remove
840
+ * @tgm the ThrottleGroupMember to remove
841
*/
842
-void throttle_group_unregister_blk(BlockBackend *blk)
843
+void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
844
{
845
- BlockBackendPublic *blkp = blk_get_public(blk);
846
- ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts);
847
+ ThrottleState *ts = tgm->throttle_state;
848
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
849
+ ThrottleGroupMember *token;
850
int i;
851
852
- assert(blkp->pending_reqs[0] == 0 && blkp->pending_reqs[1] == 0);
853
- assert(qemu_co_queue_empty(&blkp->throttled_reqs[0]));
854
- assert(qemu_co_queue_empty(&blkp->throttled_reqs[1]));
855
+ assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
856
+ assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
857
+ assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
858
859
qemu_mutex_lock(&tg->lock);
860
for (i = 0; i < 2; i++) {
861
- if (tg->tokens[i] == blk) {
862
- BlockBackend *token = throttle_group_next_blk(blk);
863
- /* Take care of the case where this is the last blk in the group */
864
- if (token == blk) {
865
+ if (tg->tokens[i] == tgm) {
866
+ token = throttle_group_next_tgm(tgm);
867
+ /* Take care of the case where this is the last tgm in the group */
868
+ if (token == tgm) {
869
token = NULL;
870
}
871
tg->tokens[i] = token;
872
}
873
}
874
875
- /* remove the current blk from the list */
876
- QLIST_REMOVE(blkp, round_robin);
877
- throttle_timers_destroy(&blkp->throttle_timers);
878
+ /* remove the current tgm from the list */
879
+ QLIST_REMOVE(tgm, round_robin);
880
+ throttle_timers_destroy(&tgm->throttle_timers);
881
qemu_mutex_unlock(&tg->lock);
882
883
throttle_group_unref(&tg->ts);
884
- blkp->throttle_state = NULL;
885
+ tgm->throttle_state = NULL;
886
}
887
888
static void throttle_groups_init(void)
889
diff --git a/blockdev.c b/blockdev.c
890
index XXXXXXX..XXXXXXX 100644
891
--- a/blockdev.c
892
+++ b/blockdev.c
893
@@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
894
if (throttle_enabled(&cfg)) {
895
/* Enable I/O limits if they're not enabled yet, otherwise
896
* just update the throttling group. */
897
- if (!blk_get_public(blk)->throttle_state) {
898
+ if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
899
blk_io_limits_enable(blk,
900
arg->has_group ? arg->group :
901
arg->has_device ? arg->device :
902
@@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
903
}
904
/* Set the new throttling configuration */
905
blk_set_io_limits(blk, &cfg);
906
- } else if (blk_get_public(blk)->throttle_state) {
907
+ } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
908
/* If all throttling settings are set to 0, disable I/O limits */
909
blk_io_limits_disable(blk);
910
}
911
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
912
index XXXXXXX..XXXXXXX 100644
913
--- a/tests/test-throttle.c
914
+++ b/tests/test-throttle.c
915
@@ -XXX,XX +XXX,XX @@ static void test_groups(void)
916
ThrottleConfig cfg1, cfg2;
917
BlockBackend *blk1, *blk2, *blk3;
918
BlockBackendPublic *blkp1, *blkp2, *blkp3;
919
+ ThrottleGroupMember *tgm1, *tgm2, *tgm3;
920
921
/* No actual I/O is performed on these devices */
922
blk1 = blk_new(0, BLK_PERM_ALL);
923
@@ -XXX,XX +XXX,XX @@ static void test_groups(void)
924
blkp2 = blk_get_public(blk2);
925
blkp3 = blk_get_public(blk3);
926
927
- g_assert(blkp1->throttle_state == NULL);
928
- g_assert(blkp2->throttle_state == NULL);
929
- g_assert(blkp3->throttle_state == NULL);
930
+ tgm1 = &blkp1->throttle_group_member;
931
+ tgm2 = &blkp2->throttle_group_member;
932
+ tgm3 = &blkp3->throttle_group_member;
933
934
- throttle_group_register_blk(blk1, "bar");
935
- throttle_group_register_blk(blk2, "foo");
936
- throttle_group_register_blk(blk3, "bar");
937
+ g_assert(tgm1->throttle_state == NULL);
938
+ g_assert(tgm2->throttle_state == NULL);
939
+ g_assert(tgm3->throttle_state == NULL);
940
941
- g_assert(blkp1->throttle_state != NULL);
942
- g_assert(blkp2->throttle_state != NULL);
943
- g_assert(blkp3->throttle_state != NULL);
944
+ throttle_group_register_tgm(tgm1, "bar");
945
+ throttle_group_register_tgm(tgm2, "foo");
946
+ throttle_group_register_tgm(tgm3, "bar");
947
948
- g_assert(!strcmp(throttle_group_get_name(blk1), "bar"));
949
- g_assert(!strcmp(throttle_group_get_name(blk2), "foo"));
950
- g_assert(blkp1->throttle_state == blkp3->throttle_state);
951
+ g_assert(tgm1->throttle_state != NULL);
952
+ g_assert(tgm2->throttle_state != NULL);
953
+ g_assert(tgm3->throttle_state != NULL);
954
+
955
+ g_assert(!strcmp(throttle_group_get_name(tgm1), "bar"));
956
+ g_assert(!strcmp(throttle_group_get_name(tgm2), "foo"));
957
+ g_assert(tgm1->throttle_state == tgm3->throttle_state);
958
959
/* Setting the config of a group member affects the whole group */
960
throttle_config_init(&cfg1);
961
@@ -XXX,XX +XXX,XX @@ static void test_groups(void)
962
cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
963
cfg1.buckets[THROTTLE_OPS_READ].avg = 20000;
964
cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
965
- throttle_group_config(blk1, &cfg1);
966
+ throttle_group_config(tgm1, &cfg1);
967
968
- throttle_group_get_config(blk1, &cfg1);
969
- throttle_group_get_config(blk3, &cfg2);
970
+ throttle_group_get_config(tgm1, &cfg1);
971
+ throttle_group_get_config(tgm3, &cfg2);
972
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
973
974
cfg2.buckets[THROTTLE_BPS_READ].avg = 4547;
975
cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
976
cfg2.buckets[THROTTLE_OPS_READ].avg = 123;
977
cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
978
- throttle_group_config(blk3, &cfg1);
979
+ throttle_group_config(tgm3, &cfg1);
980
981
- throttle_group_get_config(blk1, &cfg1);
982
- throttle_group_get_config(blk3, &cfg2);
983
+ throttle_group_get_config(tgm1, &cfg1);
984
+ throttle_group_get_config(tgm3, &cfg2);
985
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
986
987
- throttle_group_unregister_blk(blk1);
988
- throttle_group_unregister_blk(blk2);
989
- throttle_group_unregister_blk(blk3);
990
+ throttle_group_unregister_tgm(tgm1);
991
+ throttle_group_unregister_tgm(tgm2);
992
+ throttle_group_unregister_tgm(tgm3);
993
994
- g_assert(blkp1->throttle_state == NULL);
995
- g_assert(blkp2->throttle_state == NULL);
996
- g_assert(blkp3->throttle_state == NULL);
997
+ g_assert(tgm1->throttle_state == NULL);
998
+ g_assert(tgm2->throttle_state == NULL);
999
+ g_assert(tgm3->throttle_state == NULL);
1000
}
1001
1002
int main(int argc, char **argv)
1003
--
120
--
1004
2.13.5
121
2.30.2
1005
122
1006
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: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
timer_cb() needs to know about the current Aio context of the throttle
3
Refactor calling driver callbacks to a separate transaction action to
4
request that is woken up. In order to make ThrottleGroupMember backend
4
be used later.
5
agnostic, this information is stored in an aio_context field instead of
6
accessing it from BlockBackend.
7
5
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
8
Message-Id: <20210428151804.439460-15-vsementsov@virtuozzo.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
10
---
13
include/block/throttle-groups.h | 7 ++++-
11
block.c | 70 ++++++++++++++++++++++++++++++++++++++++++++-------------
14
block/block-backend.c | 15 ++++------
12
1 file changed, 54 insertions(+), 16 deletions(-)
15
block/throttle-groups.c | 38 ++++++++++++++++---------
16
tests/test-throttle.c | 63 +++++++++++++++++++++--------------------
17
4 files changed, 69 insertions(+), 54 deletions(-)
18
13
19
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
14
diff --git a/block.c b/block.c
20
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/throttle-groups.h
16
--- a/block.c
22
+++ b/include/block/throttle-groups.h
17
+++ b/block.c
23
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
24
*/
25
26
typedef struct ThrottleGroupMember {
27
+ AioContext *aio_context;
28
/* throttled_reqs_lock protects the CoQueues for throttled requests. */
29
CoMutex throttled_reqs_lock;
30
CoQueue throttled_reqs[2];
31
@@ -XXX,XX +XXX,XX @@ void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
32
void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg);
33
34
void throttle_group_register_tgm(ThrottleGroupMember *tgm,
35
- const char *groupname);
36
+ const char *groupname,
37
+ AioContext *ctx);
38
void throttle_group_unregister_tgm(ThrottleGroupMember *tgm);
39
void throttle_group_restart_tgm(ThrottleGroupMember *tgm);
40
41
void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
42
unsigned int bytes,
43
bool is_write);
44
+void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
45
+ AioContext *new_context);
46
+void throttle_group_detach_aio_context(ThrottleGroupMember *tgm);
47
48
#endif
49
diff --git a/block/block-backend.c b/block/block-backend.c
50
index XXXXXXX..XXXXXXX 100644
51
--- a/block/block-backend.c
52
+++ b/block/block-backend.c
53
@@ -XXX,XX +XXX,XX @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
54
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
55
{
56
BlockDriverState *bs = blk_bs(blk);
57
- ThrottleTimers *tt;
58
+ ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
59
60
if (bs) {
61
- if (blk->public.throttle_group_member.throttle_state) {
62
- tt = &blk->public.throttle_group_member.throttle_timers;
63
- throttle_timers_detach_aio_context(tt);
64
+ if (tgm->throttle_state) {
65
+ throttle_group_detach_aio_context(tgm);
66
+ throttle_group_attach_aio_context(tgm, new_context);
67
}
68
bdrv_set_aio_context(bs, new_context);
69
- if (blk->public.throttle_group_member.throttle_state) {
70
- tt = &blk->public.throttle_group_member.throttle_timers;
71
- throttle_timers_attach_aio_context(tt, new_context);
72
- }
73
}
19
}
74
}
20
}
75
21
76
@@ -XXX,XX +XXX,XX @@ void blk_io_limits_disable(BlockBackend *blk)
22
+static void bdrv_drv_set_perm_commit(void *opaque)
77
void blk_io_limits_enable(BlockBackend *blk, const char *group)
78
{
79
assert(!blk->public.throttle_group_member.throttle_state);
80
- throttle_group_register_tgm(&blk->public.throttle_group_member, group);
81
+ throttle_group_register_tgm(&blk->public.throttle_group_member,
82
+ group, blk_get_aio_context(blk));
83
}
84
85
void blk_io_limits_update_group(BlockBackend *blk, const char *group)
86
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/throttle-groups.c
89
+++ b/block/throttle-groups.c
90
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
91
92
static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
93
{
94
- BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
95
- throttle_group_member);
96
- BlockBackend *blk = blk_by_public(blkp);
97
Coroutine *co;
98
RestartData rd = {
99
.tgm = tgm,
100
@@ -XXX,XX +XXX,XX @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
101
};
102
103
co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd);
104
- aio_co_enter(blk_get_aio_context(blk), co);
105
+ aio_co_enter(tgm->aio_context, co);
106
}
107
108
void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
109
@@ -XXX,XX +XXX,XX @@ void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
110
/* ThrottleTimers callback. This wakes up a request that was waiting
111
* because it had been throttled.
112
*
113
- * @blk: the BlockBackend whose request had been throttled
114
+ * @tgm: the ThrottleGroupMember whose request had been throttled
115
* @is_write: the type of operation (read/write)
116
*/
117
-static void timer_cb(BlockBackend *blk, bool is_write)
118
+static void timer_cb(ThrottleGroupMember *tgm, bool is_write)
119
{
120
- BlockBackendPublic *blkp = blk_get_public(blk);
121
- ThrottleGroupMember *tgm = &blkp->throttle_group_member;
122
ThrottleState *ts = tgm->throttle_state;
123
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
124
125
@@ -XXX,XX +XXX,XX @@ static void write_timer_cb(void *opaque)
126
*
127
* @tgm: the ThrottleGroupMember to insert
128
* @groupname: the name of the group
129
+ * @ctx: the AioContext to use
130
*/
131
void throttle_group_register_tgm(ThrottleGroupMember *tgm,
132
- const char *groupname)
133
+ const char *groupname,
134
+ AioContext *ctx)
135
{
136
int i;
137
- BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic,
138
- throttle_group_member);
139
- BlockBackend *blk = blk_by_public(blkp);
140
ThrottleState *ts = throttle_group_incref(groupname);
141
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
142
143
tgm->throttle_state = ts;
144
+ tgm->aio_context = ctx;
145
146
qemu_mutex_lock(&tg->lock);
147
/* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
148
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
149
QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
150
151
throttle_timers_init(&tgm->throttle_timers,
152
- blk_get_aio_context(blk),
153
+ tgm->aio_context,
154
tg->clock_type,
155
read_timer_cb,
156
write_timer_cb,
157
- blk);
158
+ tgm);
159
160
qemu_mutex_unlock(&tg->lock);
161
}
162
@@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
163
tgm->throttle_state = NULL;
164
}
165
166
+void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
167
+ AioContext *new_context)
168
+{
23
+{
169
+ ThrottleTimers *tt = &tgm->throttle_timers;
24
+ BlockDriverState *bs = opaque;
170
+ throttle_timers_attach_aio_context(tt, new_context);
25
+ uint64_t cumulative_perms, cumulative_shared_perms;
171
+ tgm->aio_context = new_context;
26
+
27
+ if (bs->drv->bdrv_set_perm) {
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
+ }
172
+}
32
+}
173
+
33
+
174
+void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
34
+static void bdrv_drv_set_perm_abort(void *opaque)
175
+{
35
+{
176
+ ThrottleTimers *tt = &tgm->throttle_timers;
36
+ BlockDriverState *bs = opaque;
177
+ throttle_timers_detach_aio_context(tt);
37
+
178
+ tgm->aio_context = NULL;
38
+ if (bs->drv->bdrv_abort_perm_update) {
39
+ bs->drv->bdrv_abort_perm_update(bs);
40
+ }
179
+}
41
+}
180
+
42
+
181
static void throttle_groups_init(void)
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;
54
+ }
55
+
56
+ if (bs->drv->bdrv_check_perm) {
57
+ int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp);
58
+ if (ret < 0) {
59
+ return ret;
60
+ }
61
+ }
62
+
63
+ if (tran) {
64
+ tran_add(tran, &bdrv_drv_set_perm_drv, bs);
65
+ }
66
+
67
+ return 0;
68
+}
69
+
70
/*
71
* Check whether permissions on this node can be changed in a way that
72
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
73
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
74
return 0;
75
}
76
77
- if (drv->bdrv_check_perm) {
78
- ret = drv->bdrv_check_perm(bs, cumulative_perms,
79
- cumulative_shared_perms, errp);
80
- if (ret < 0) {
81
- return ret;
82
- }
83
+ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL,
84
+ errp);
85
+ if (ret < 0) {
86
+ return ret;
87
}
88
89
/* Drivers that never have children can omit .bdrv_child_perm() */
90
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs)
91
return;
92
}
93
94
- if (drv->bdrv_abort_perm_update) {
95
- drv->bdrv_abort_perm_update(bs);
96
- }
97
+ bdrv_drv_set_perm_abort(bs);
98
99
QLIST_FOREACH(c, &bs->children, next) {
100
bdrv_child_set_perm_abort(c);
101
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
102
103
static void bdrv_node_set_perm(BlockDriverState *bs)
182
{
104
{
183
qemu_mutex_init(&throttle_groups_lock);
105
- uint64_t cumulative_perms, cumulative_shared_perms;
184
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
106
BlockDriver *drv = bs->drv;
185
index XXXXXXX..XXXXXXX 100644
107
BdrvChild *c;
186
--- a/tests/test-throttle.c
108
187
+++ b/tests/test-throttle.c
109
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs)
188
@@ -XXX,XX +XXX,XX @@
110
return;
189
static AioContext *ctx;
190
static LeakyBucket bkt;
191
static ThrottleConfig cfg;
192
+static ThrottleGroupMember tgm;
193
static ThrottleState ts;
194
-static ThrottleTimers tt;
195
+static ThrottleTimers *tt;
196
197
/* useful function */
198
static bool double_cmp(double x, double y)
199
@@ -XXX,XX +XXX,XX @@ static void test_init(void)
200
{
201
int i;
202
203
+ tt = &tgm.throttle_timers;
204
+
205
/* fill the structures with crap */
206
memset(&ts, 1, sizeof(ts));
207
- memset(&tt, 1, sizeof(tt));
208
+ memset(tt, 1, sizeof(*tt));
209
210
/* init structures */
211
throttle_init(&ts);
212
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
213
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
214
read_timer_cb, write_timer_cb, &ts);
215
216
/* check initialized fields */
217
- g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
218
- g_assert(tt.timers[0]);
219
- g_assert(tt.timers[1]);
220
+ g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
221
+ g_assert(tt->timers[0]);
222
+ g_assert(tt->timers[1]);
223
224
/* check other fields where cleared */
225
g_assert(!ts.previous_leak);
226
@@ -XXX,XX +XXX,XX @@ static void test_init(void)
227
g_assert(!ts.cfg.buckets[i].level);
228
}
111
}
229
112
230
- throttle_timers_destroy(&tt);
113
- bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
231
+ throttle_timers_destroy(tt);
114
-
232
}
115
- /* Update this node */
233
116
- if (drv->bdrv_set_perm) {
234
static void test_destroy(void)
117
- drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
235
{
118
- }
236
int i;
119
+ bdrv_drv_set_perm_commit(bs);
237
throttle_init(&ts);
120
238
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
121
/* Drivers that never have children can omit .bdrv_child_perm() */
239
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
122
if (!drv->bdrv_child_perm) {
240
read_timer_cb, write_timer_cb, &ts);
241
- throttle_timers_destroy(&tt);
242
+ throttle_timers_destroy(tt);
243
for (i = 0; i < 2; i++) {
244
- g_assert(!tt.timers[i]);
245
+ g_assert(!tt->timers[i]);
246
}
247
}
248
249
@@ -XXX,XX +XXX,XX @@ static void test_config_functions(void)
250
orig_cfg.op_size = 1;
251
252
throttle_init(&ts);
253
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
254
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
255
read_timer_cb, write_timer_cb, &ts);
256
/* structure reset by throttle_init previous_leak should be null */
257
g_assert(!ts.previous_leak);
258
@@ -XXX,XX +XXX,XX @@ static void test_config_functions(void)
259
/* get back the fixed configuration */
260
throttle_get_config(&ts, &final_cfg);
261
262
- throttle_timers_destroy(&tt);
263
+ throttle_timers_destroy(tt);
264
265
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
266
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
267
@@ -XXX,XX +XXX,XX @@ static void test_have_timer(void)
268
{
269
/* zero structures */
270
memset(&ts, 0, sizeof(ts));
271
- memset(&tt, 0, sizeof(tt));
272
+ memset(tt, 0, sizeof(*tt));
273
274
/* no timer set should return false */
275
- g_assert(!throttle_timers_are_initialized(&tt));
276
+ g_assert(!throttle_timers_are_initialized(tt));
277
278
/* init structures */
279
throttle_init(&ts);
280
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
281
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
282
read_timer_cb, write_timer_cb, &ts);
283
284
/* timer set by init should return true */
285
- g_assert(throttle_timers_are_initialized(&tt));
286
+ g_assert(throttle_timers_are_initialized(tt));
287
288
- throttle_timers_destroy(&tt);
289
+ throttle_timers_destroy(tt);
290
}
291
292
static void test_detach_attach(void)
293
{
294
/* zero structures */
295
memset(&ts, 0, sizeof(ts));
296
- memset(&tt, 0, sizeof(tt));
297
+ memset(tt, 0, sizeof(*tt));
298
299
/* init the structure */
300
throttle_init(&ts);
301
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
302
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
303
read_timer_cb, write_timer_cb, &ts);
304
305
/* timer set by init should return true */
306
- g_assert(throttle_timers_are_initialized(&tt));
307
+ g_assert(throttle_timers_are_initialized(tt));
308
309
/* timer should no longer exist after detaching */
310
- throttle_timers_detach_aio_context(&tt);
311
- g_assert(!throttle_timers_are_initialized(&tt));
312
+ throttle_timers_detach_aio_context(tt);
313
+ g_assert(!throttle_timers_are_initialized(tt));
314
315
/* timer should exist again after attaching */
316
- throttle_timers_attach_aio_context(&tt, ctx);
317
- g_assert(throttle_timers_are_initialized(&tt));
318
+ throttle_timers_attach_aio_context(tt, ctx);
319
+ g_assert(throttle_timers_are_initialized(tt));
320
321
- throttle_timers_destroy(&tt);
322
+ throttle_timers_destroy(tt);
323
}
324
325
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
326
@@ -XXX,XX +XXX,XX @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
327
cfg.op_size = op_size;
328
329
throttle_init(&ts);
330
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
331
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
332
read_timer_cb, write_timer_cb, &ts);
333
throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg);
334
335
@@ -XXX,XX +XXX,XX @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
336
return false;
337
}
338
339
- throttle_timers_destroy(&tt);
340
+ throttle_timers_destroy(tt);
341
342
return true;
343
}
344
@@ -XXX,XX +XXX,XX @@ static void test_groups(void)
345
g_assert(tgm2->throttle_state == NULL);
346
g_assert(tgm3->throttle_state == NULL);
347
348
- throttle_group_register_tgm(tgm1, "bar");
349
- throttle_group_register_tgm(tgm2, "foo");
350
- throttle_group_register_tgm(tgm3, "bar");
351
+ throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1));
352
+ throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2));
353
+ throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3));
354
355
g_assert(tgm1->throttle_state != NULL);
356
g_assert(tgm2->throttle_state != NULL);
357
--
123
--
358
2.13.5
124
2.30.2
359
125
360
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: Eric Blake <eblake@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The old signature has an ambiguous meaning for a return of 0:
3
Split no-perm part of bdrv_attach_child as separate transaction action.
4
either no allocation was requested or necessary, or an error
4
It will be used in later commits.
5
occurred (but any errno associated with the error is lost to
6
the caller, which then has to assume EIO).
7
5
8
Better is to follow the example of qcow2, by changing the
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
signature to have a separate return value that cleanly
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
distinguishes between failure and success, along with a
8
Message-Id: <20210428151804.439460-20-vsementsov@virtuozzo.com>
11
parameter that cleanly holds a 64-bit value. Then update all
12
callers.
13
14
While auditing that all return paths return a negative errno
15
(rather than -1), I also simplified places where we can pass
16
NULL rather than a local Error that just gets thrown away.
17
18
Suggested-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Eric Blake <eblake@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
---
10
---
22
block/qcow.c | 123 +++++++++++++++++++++++++++++++++++------------------------
11
block.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++-----------
23
1 file changed, 73 insertions(+), 50 deletions(-)
12
1 file changed, 58 insertions(+), 13 deletions(-)
24
13
25
diff --git a/block/qcow.c b/block/qcow.c
14
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
27
--- a/block/qcow.c
16
--- a/block.c
28
+++ b/block/qcow.c
17
+++ b/block.c
29
@@ -XXX,XX +XXX,XX @@ static int qcow_reopen_prepare(BDRVReopenState *state,
18
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
30
* 'compressed_size'. 'compressed_size' must be > 0 and <
19
31
* cluster_size
20
static void bdrv_replace_child_noperm(BdrvChild *child,
32
*
21
BlockDriverState *new_bs);
33
- * return 0 if not allocated.
22
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
34
+ * return 0 if not allocated, 1 if *result is assigned, and negative
23
+ BlockDriverState *child_bs,
35
+ * errno on failure.
24
+ const char *child_name,
36
*/
25
+ const BdrvChildClass *child_class,
37
-static uint64_t get_cluster_offset(BlockDriverState *bs,
26
+ BdrvChildRole child_role,
38
- uint64_t offset, int allocate,
27
+ BdrvChild **child,
39
- int compressed_size,
28
+ Transaction *tran,
40
- int n_start, int n_end)
29
+ Error **errp);
41
+static int get_cluster_offset(BlockDriverState *bs,
30
42
+ uint64_t offset, int allocate,
31
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
43
+ int compressed_size,
32
*queue, Error **errp);
44
+ int n_start, int n_end, uint64_t *result)
33
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
45
{
34
return 0;
46
BDRVQcowState *s = bs->opaque;
47
- int min_index, i, j, l1_index, l2_index;
48
+ int min_index, i, j, l1_index, l2_index, ret;
49
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
50
uint32_t min_count;
51
int new_l2_table;
52
53
+ *result = 0;
54
l1_index = offset >> (s->l2_bits + s->cluster_bits);
55
l2_offset = s->l1_table[l1_index];
56
new_l2_table = 0;
57
@@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
58
/* update the L1 entry */
59
s->l1_table[l1_index] = l2_offset;
60
tmp = cpu_to_be64(l2_offset);
61
- if (bdrv_pwrite_sync(bs->file,
62
- s->l1_table_offset + l1_index * sizeof(tmp),
63
- &tmp, sizeof(tmp)) < 0)
64
- return 0;
65
+ ret = bdrv_pwrite_sync(bs->file,
66
+ s->l1_table_offset + l1_index * sizeof(tmp),
67
+ &tmp, sizeof(tmp));
68
+ if (ret < 0) {
69
+ return ret;
70
+ }
71
new_l2_table = 1;
72
}
73
for(i = 0; i < L2_CACHE_SIZE; i++) {
74
@@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
75
l2_table = s->l2_cache + (min_index << s->l2_bits);
76
if (new_l2_table) {
77
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
78
- if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
79
- s->l2_size * sizeof(uint64_t)) < 0)
80
- return 0;
81
+ ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
82
+ s->l2_size * sizeof(uint64_t));
83
+ if (ret < 0) {
84
+ return ret;
85
+ }
86
} else {
87
- if (bdrv_pread(bs->file, l2_offset, l2_table,
88
- s->l2_size * sizeof(uint64_t)) !=
89
- s->l2_size * sizeof(uint64_t))
90
- return 0;
91
+ ret = bdrv_pread(bs->file, l2_offset, l2_table,
92
+ s->l2_size * sizeof(uint64_t));
93
+ if (ret < 0) {
94
+ return ret;
95
+ }
96
}
97
s->l2_cache_offsets[min_index] = l2_offset;
98
s->l2_cache_counts[min_index] = 1;
99
@@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
100
/* if the cluster is already compressed, we must
101
decompress it in the case it is not completely
102
overwritten */
103
- if (decompress_cluster(bs, cluster_offset) < 0)
104
- return 0;
105
+ if (decompress_cluster(bs, cluster_offset) < 0) {
106
+ return -EIO;
107
+ }
108
cluster_offset = bdrv_getlength(bs->file->bs);
109
cluster_offset = (cluster_offset + s->cluster_size - 1) &
110
~(s->cluster_size - 1);
111
/* write the cluster content */
112
- if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
113
- s->cluster_size) !=
114
- s->cluster_size)
115
- return -1;
116
+ ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
117
+ s->cluster_size);
118
+ if (ret < 0) {
119
+ return ret;
120
+ }
121
} else {
122
cluster_offset = bdrv_getlength(bs->file->bs);
123
if (allocate == 1) {
124
@@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
125
s->cluster_data,
126
BDRV_SECTOR_SIZE,
127
NULL) < 0) {
128
- errno = EIO;
129
- return -1;
130
+ return -EIO;
131
+ }
132
+ ret = bdrv_pwrite(bs->file,
133
+ cluster_offset + i * 512,
134
+ s->cluster_data, 512);
135
+ if (ret < 0) {
136
+ return ret;
137
}
138
- if (bdrv_pwrite(bs->file,
139
- cluster_offset + i * 512,
140
- s->cluster_data, 512) != 512)
141
- return -1;
142
}
143
}
144
}
145
@@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
146
/* update L2 table */
147
tmp = cpu_to_be64(cluster_offset);
148
l2_table[l2_index] = tmp;
149
- if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
150
- &tmp, sizeof(tmp)) < 0)
151
- return 0;
152
+ ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
153
+ &tmp, sizeof(tmp));
154
+ if (ret < 0) {
155
+ return ret;
156
+ }
157
}
158
- return cluster_offset;
159
+ *result = cluster_offset;
160
+ return 1;
161
}
35
}
162
36
163
static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
37
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
164
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
38
+ BlockDriverState *child_bs,
165
{
39
+ const char *child_name,
166
BDRVQcowState *s = bs->opaque;
40
+ const BdrvChildClass *child_class,
167
- int index_in_cluster, n;
41
+ BdrvChildRole child_role,
168
+ int index_in_cluster, n, ret;
42
+ BdrvChild **child,
169
uint64_t cluster_offset;
43
+ Transaction *tran,
170
44
+ Error **errp)
171
qemu_co_mutex_lock(&s->lock);
45
+{
172
- cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
46
+ int ret;
173
+ ret = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0, &cluster_offset);
47
+ uint64_t perm, shared_perm;
174
qemu_co_mutex_unlock(&s->lock);
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);
175
+ if (ret < 0) {
58
+ if (ret < 0) {
176
+ return ret;
59
+ return ret;
177
+ }
60
+ }
178
index_in_cluster = sector_num & (s->cluster_sectors - 1);
61
+
179
n = s->cluster_sectors - index_in_cluster;
62
+ QLIST_INSERT_HEAD(&parent_bs->children, *child, next);
180
if (n > nb_sectors)
63
+ /*
181
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
64
+ * child is removed in bdrv_attach_child_common_abort(), so don't care to
182
65
+ * abort this change separately.
183
while (nb_sectors != 0) {
66
+ */
184
/* prepare next request */
67
+
185
- cluster_offset = get_cluster_offset(bs, sector_num << 9,
68
+ return 0;
186
- 0, 0, 0, 0);
69
+}
187
+ ret = get_cluster_offset(bs, sector_num << 9,
70
+
188
+ 0, 0, 0, 0, &cluster_offset);
71
static void bdrv_detach_child(BdrvChild *child)
189
+ if (ret < 0) {
72
{
190
+ break;
73
bdrv_replace_child(child, NULL);
191
+ }
74
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
192
index_in_cluster = sector_num & (s->cluster_sectors - 1);
75
BdrvChildRole child_role,
193
n = s->cluster_sectors - index_in_cluster;
76
Error **errp)
194
if (n > nb_sectors) {
77
{
195
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
78
- BdrvChild *child;
196
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
79
- uint64_t perm, shared_perm;
197
qemu_co_mutex_lock(&s->lock);
80
-
198
if (ret < 0) {
81
- bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
199
- goto fail;
82
+ int ret;
200
+ break;
83
+ BdrvChild *child = NULL;
201
}
84
+ Transaction *tran = tran_new();
202
} else {
85
203
/* Note: in this case, no need to wait */
86
- assert(parent_bs->drv);
204
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
87
- bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
205
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
88
- perm, shared_perm, &perm, &shared_perm);
206
/* add AIO support for compressed blocks ? */
89
+ ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class,
207
if (decompress_cluster(bs, cluster_offset) < 0) {
90
+ child_role, &child, tran, errp);
208
- goto fail;
91
+ if (ret < 0) {
209
+ ret = -EIO;
92
+ goto out;
210
+ break;
93
+ }
211
}
94
212
memcpy(buf,
95
- child = bdrv_root_attach_child(child_bs, child_name, child_class,
213
s->cluster_cache + index_in_cluster * 512, 512 * n);
96
- child_role, perm, shared_perm, parent_bs,
214
} else {
97
- errp);
215
if ((cluster_offset & 511) != 0) {
98
- if (child == NULL) {
216
- goto fail;
99
- return NULL;
217
+ ret = -EIO;
100
+ ret = bdrv_refresh_perms(parent_bs, errp);
218
+ break;
101
+ if (ret < 0) {
219
}
102
+ goto out;
220
hd_iov.iov_base = (void *)buf;
221
hd_iov.iov_len = n * 512;
222
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
223
assert(s->crypto);
224
if (qcrypto_block_decrypt(s->crypto, sector_num, buf,
225
n * BDRV_SECTOR_SIZE, NULL) < 0) {
226
- goto fail;
227
+ ret = -EIO;
228
+ break;
229
}
230
}
231
}
232
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
233
buf += n * 512;
234
}
103
}
235
104
236
-done:
105
- QLIST_INSERT_HEAD(&parent_bs->children, child, next);
237
qemu_co_mutex_unlock(&s->lock);
106
+out:
238
107
+ tran_finalize(tran, ret);
239
if (qiov->niov > 1) {
108
+
240
@@ -XXX,XX +XXX,XX @@ done:
109
+ bdrv_unref(child_bs);
241
}
110
+
242
111
return child;
243
return ret;
244
-
245
-fail:
246
- ret = -EIO;
247
- goto done;
248
}
112
}
249
113
250
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
251
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
252
if (n > nb_sectors) {
253
n = nb_sectors;
254
}
255
- cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0,
256
- index_in_cluster,
257
- index_in_cluster + n);
258
+ ret = get_cluster_offset(bs, sector_num << 9, 1, 0,
259
+ index_in_cluster,
260
+ index_in_cluster + n, &cluster_offset);
261
+ if (ret < 0) {
262
+ break;
263
+ }
264
if (!cluster_offset || (cluster_offset & 511) != 0) {
265
ret = -EIO;
266
break;
267
@@ -XXX,XX +XXX,XX @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
268
goto success;
269
}
270
qemu_co_mutex_lock(&s->lock);
271
- cluster_offset = get_cluster_offset(bs, offset, 2, out_len, 0, 0);
272
+ ret = get_cluster_offset(bs, offset, 2, out_len, 0, 0, &cluster_offset);
273
qemu_co_mutex_unlock(&s->lock);
274
+ if (ret < 0) {
275
+ goto fail;
276
+ }
277
if (cluster_offset == 0) {
278
ret = -EIO;
279
goto fail;
280
--
114
--
281
2.13.5
115
2.30.2
282
116
283
117
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Split part of bdrv_replace_node_common() to be used separately.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-21-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block.c | 50 +++++++++++++++++++++++++++++++-------------------
11
1 file changed, 31 insertions(+), 19 deletions(-)
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 bool should_update_child(BdrvChild *c, BlockDriverState *to)
18
return ret;
19
}
20
21
+static int bdrv_replace_node_noperm(BlockDriverState *from,
22
+ BlockDriverState *to,
23
+ bool auto_skip, Transaction *tran,
24
+ Error **errp)
25
+{
26
+ BdrvChild *c, *next;
27
+
28
+ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
29
+ assert(c->bs == from);
30
+ if (!should_update_child(c, to)) {
31
+ if (auto_skip) {
32
+ continue;
33
+ }
34
+ error_setg(errp, "Should not change '%s' link to '%s'",
35
+ c->name, from->node_name);
36
+ return -EINVAL;
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);
44
+ }
45
+
46
+ return 0;
47
+}
48
+
49
/*
50
* With auto_skip=true bdrv_replace_node_common skips updating from parents
51
* if it creates a parent-child relation loop or if parent is block-job.
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;
85
}
86
87
found = g_hash_table_new(NULL, NULL);
88
--
89
2.30.2
90
91
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
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: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
ThrottleGroup is converted to an object. This will allow the future
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
throttle block filter drive easy creation and configuration of throttle
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
groups in QMP and cli.
5
Message-Id: <20210428151804.439460-23-vsementsov@virtuozzo.com>
6
7
A new QAPI struct, ThrottleLimits, is introduced to provide a shared
8
struct for all throttle configuration needs in QMP.
9
10
ThrottleGroups can be created via CLI as
11
-object throttle-group,id=foo,x-iops-total=100,x-..
12
where x-* are individual limit properties. Since we can't add non-scalar
13
properties in -object this interface must be used instead. However,
14
setting these properties must be disabled after initialization because
15
certain combinations of limits are forbidden and thus configuration
16
changes should be done in one transaction. The individual properties
17
will go away when support for non-scalar values in CLI is implemented
18
and thus are marked as experimental.
19
20
ThrottleGroup also has a `limits` property that uses the ThrottleLimits
21
struct. It can be used to create ThrottleGroups or set the
22
configuration in existing groups as follows:
23
24
{ "execute": "object-add",
25
"arguments": {
26
"qom-type": "throttle-group",
27
"id": "foo",
28
"props" : {
29
"limits": {
30
"iops-total": 100
31
}
32
}
33
}
34
}
35
{ "execute" : "qom-set",
36
"arguments" : {
37
"path" : "foo",
38
"property" : "limits",
39
"value" : {
40
"iops-total" : 99
41
}
42
}
43
}
44
45
This also means a group's configuration can be fetched with qom-get.
46
47
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
48
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
49
Reviewed-by: Alberto Garcia <berto@igalia.com>
50
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
51
---
7
---
52
qapi/block-core.json | 48 +++++
8
block.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
53
include/block/throttle-groups.h | 3 +
9
1 file changed, 82 insertions(+), 2 deletions(-)
54
include/qemu/throttle-options.h | 59 ++++--
55
include/qemu/throttle.h | 3 +
56
block/throttle-groups.c | 424 ++++++++++++++++++++++++++++++++++++----
57
tests/test-throttle.c | 1 +
58
util/throttle.c | 151 ++++++++++++++
59
7 files changed, 628 insertions(+), 61 deletions(-)
60
10
61
diff --git a/qapi/block-core.json b/qapi/block-core.json
11
diff --git a/block.c b/block.c
62
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
63
--- a/qapi/block-core.json
13
--- a/block.c
64
+++ b/qapi/block-core.json
14
+++ b/block.c
65
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
66
'*iops_size': 'int', '*group': 'str' } }
16
}
67
17
}
68
##
18
69
+# @ThrottleLimits:
19
+static void bdrv_child_free(void *opaque)
70
+#
20
+{
71
+# Limit parameters for throttling.
21
+ BdrvChild *c = opaque;
72
+# Since some limit combinations are illegal, limits should always be set in one
73
+# transaction. All fields are optional. When setting limits, if a field is
74
+# missing the current value is not changed.
75
+#
76
+# @iops-total: limit total I/O operations per second
77
+# @iops-total-max: I/O operations burst
78
+# @iops-total-max-length: length of the iops-total-max burst period, in seconds
79
+# It must only be set if @iops-total-max is set as well.
80
+# @iops-read: limit read operations per second
81
+# @iops-read-max: I/O operations read burst
82
+# @iops-read-max-length: length of the iops-read-max burst period, in seconds
83
+# It must only be set if @iops-read-max is set as well.
84
+# @iops-write: limit write operations per second
85
+# @iops-write-max: I/O operations write burst
86
+# @iops-write-max-length: length of the iops-write-max burst period, in seconds
87
+# It must only be set if @iops-write-max is set as well.
88
+# @bps-total: limit total bytes per second
89
+# @bps-total-max: total bytes burst
90
+# @bps-total-max-length: length of the bps-total-max burst period, in seconds.
91
+# It must only be set if @bps-total-max is set as well.
92
+# @bps-read: limit read bytes per second
93
+# @bps-read-max: total bytes read burst
94
+# @bps-read-max-length: length of the bps-read-max burst period, in seconds
95
+# It must only be set if @bps-read-max is set as well.
96
+# @bps-write: limit write bytes per second
97
+# @bps-write-max: total bytes write burst
98
+# @bps-write-max-length: length of the bps-write-max burst period, in seconds
99
+# It must only be set if @bps-write-max is set as well.
100
+# @iops-size: when limiting by iops max size of an I/O in bytes
101
+#
102
+# Since: 2.11
103
+##
104
+{ 'struct': 'ThrottleLimits',
105
+ 'data': { '*iops-total' : 'int', '*iops-total-max' : 'int',
106
+ '*iops-total-max-length' : 'int', '*iops-read' : 'int',
107
+ '*iops-read-max' : 'int', '*iops-read-max-length' : 'int',
108
+ '*iops-write' : 'int', '*iops-write-max' : 'int',
109
+ '*iops-write-max-length' : 'int', '*bps-total' : 'int',
110
+ '*bps-total-max' : 'int', '*bps-total-max-length' : 'int',
111
+ '*bps-read' : 'int', '*bps-read-max' : 'int',
112
+ '*bps-read-max-length' : 'int', '*bps-write' : 'int',
113
+ '*bps-write-max' : 'int', '*bps-write-max-length' : 'int',
114
+ '*iops-size' : 'int' } }
115
+
22
+
116
+##
23
+ g_free(c->name);
117
# @block-stream:
24
+ g_free(c);
118
#
25
+}
119
# Copy data from a backing file into a block device.
120
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
121
index XXXXXXX..XXXXXXX 100644
122
--- a/include/block/throttle-groups.h
123
+++ b/include/block/throttle-groups.h
124
@@ -XXX,XX +XXX,XX @@ typedef struct ThrottleGroupMember {
125
126
} ThrottleGroupMember;
127
128
+#define TYPE_THROTTLE_GROUP "throttle-group"
129
+#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP)
130
+
26
+
131
const char *throttle_group_get_name(ThrottleGroupMember *tgm);
27
static void bdrv_remove_empty_child(BdrvChild *child)
132
28
{
133
ThrottleState *throttle_group_incref(const char *name);
29
assert(!child->bs);
134
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
30
QLIST_SAFE_REMOVE(child, next);
135
index XXXXXXX..XXXXXXX 100644
31
- g_free(child->name);
136
--- a/include/qemu/throttle-options.h
32
- g_free(child);
137
+++ b/include/qemu/throttle-options.h
33
+ bdrv_child_free(child);
138
@@ -XXX,XX +XXX,XX @@
34
}
139
#ifndef THROTTLE_OPTIONS_H
35
140
#define THROTTLE_OPTIONS_H
36
typedef struct BdrvAttachChildCommonState {
141
37
@@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
142
+#define QEMU_OPT_IOPS_TOTAL "iops-total"
38
return ret;
143
+#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max"
39
}
144
+#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length"
40
145
+#define QEMU_OPT_IOPS_READ "iops-read"
41
+typedef struct BdrvRemoveFilterOrCowChild {
146
+#define QEMU_OPT_IOPS_READ_MAX "iops-read-max"
42
+ BdrvChild *child;
147
+#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length"
43
+ bool is_backing;
148
+#define QEMU_OPT_IOPS_WRITE "iops-write"
44
+} BdrvRemoveFilterOrCowChild;
149
+#define QEMU_OPT_IOPS_WRITE_MAX "iops-write-max"
150
+#define QEMU_OPT_IOPS_WRITE_MAX_LENGTH "iops-write-max-length"
151
+#define QEMU_OPT_BPS_TOTAL "bps-total"
152
+#define QEMU_OPT_BPS_TOTAL_MAX "bps-total-max"
153
+#define QEMU_OPT_BPS_TOTAL_MAX_LENGTH "bps-total-max-length"
154
+#define QEMU_OPT_BPS_READ "bps-read"
155
+#define QEMU_OPT_BPS_READ_MAX "bps-read-max"
156
+#define QEMU_OPT_BPS_READ_MAX_LENGTH "bps-read-max-length"
157
+#define QEMU_OPT_BPS_WRITE "bps-write"
158
+#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max"
159
+#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length"
160
+#define QEMU_OPT_IOPS_SIZE "iops-size"
161
+
45
+
162
+#define THROTTLE_OPT_PREFIX "throttling."
46
+static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
163
#define THROTTLE_OPTS \
47
+{
164
{ \
48
+ BdrvRemoveFilterOrCowChild *s = opaque;
165
- .name = "throttling.iops-total",\
49
+ BlockDriverState *parent_bs = s->child->opaque;
166
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\
167
.type = QEMU_OPT_NUMBER,\
168
.help = "limit total I/O operations per second",\
169
},{ \
170
- .name = "throttling.iops-read",\
171
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\
172
.type = QEMU_OPT_NUMBER,\
173
.help = "limit read operations per second",\
174
},{ \
175
- .name = "throttling.iops-write",\
176
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\
177
.type = QEMU_OPT_NUMBER,\
178
.help = "limit write operations per second",\
179
},{ \
180
- .name = "throttling.bps-total",\
181
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\
182
.type = QEMU_OPT_NUMBER,\
183
.help = "limit total bytes per second",\
184
},{ \
185
- .name = "throttling.bps-read",\
186
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\
187
.type = QEMU_OPT_NUMBER,\
188
.help = "limit read bytes per second",\
189
},{ \
190
- .name = "throttling.bps-write",\
191
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\
192
.type = QEMU_OPT_NUMBER,\
193
.help = "limit write bytes per second",\
194
},{ \
195
- .name = "throttling.iops-total-max",\
196
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\
197
.type = QEMU_OPT_NUMBER,\
198
.help = "I/O operations burst",\
199
},{ \
200
- .name = "throttling.iops-read-max",\
201
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\
202
.type = QEMU_OPT_NUMBER,\
203
.help = "I/O operations read burst",\
204
},{ \
205
- .name = "throttling.iops-write-max",\
206
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\
207
.type = QEMU_OPT_NUMBER,\
208
.help = "I/O operations write burst",\
209
},{ \
210
- .name = "throttling.bps-total-max",\
211
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\
212
.type = QEMU_OPT_NUMBER,\
213
.help = "total bytes burst",\
214
},{ \
215
- .name = "throttling.bps-read-max",\
216
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\
217
.type = QEMU_OPT_NUMBER,\
218
.help = "total bytes read burst",\
219
},{ \
220
- .name = "throttling.bps-write-max",\
221
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\
222
.type = QEMU_OPT_NUMBER,\
223
.help = "total bytes write burst",\
224
},{ \
225
- .name = "throttling.iops-total-max-length",\
226
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,\
227
.type = QEMU_OPT_NUMBER,\
228
.help = "length of the iops-total-max burst period, in seconds",\
229
},{ \
230
- .name = "throttling.iops-read-max-length",\
231
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,\
232
.type = QEMU_OPT_NUMBER,\
233
.help = "length of the iops-read-max burst period, in seconds",\
234
},{ \
235
- .name = "throttling.iops-write-max-length",\
236
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,\
237
.type = QEMU_OPT_NUMBER,\
238
.help = "length of the iops-write-max burst period, in seconds",\
239
},{ \
240
- .name = "throttling.bps-total-max-length",\
241
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,\
242
.type = QEMU_OPT_NUMBER,\
243
.help = "length of the bps-total-max burst period, in seconds",\
244
},{ \
245
- .name = "throttling.bps-read-max-length",\
246
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,\
247
.type = QEMU_OPT_NUMBER,\
248
.help = "length of the bps-read-max burst period, in seconds",\
249
},{ \
250
- .name = "throttling.bps-write-max-length",\
251
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,\
252
.type = QEMU_OPT_NUMBER,\
253
.help = "length of the bps-write-max burst period, in seconds",\
254
},{ \
255
- .name = "throttling.iops-size",\
256
+ .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,\
257
.type = QEMU_OPT_NUMBER,\
258
.help = "when limiting by iops max size of an I/O in bytes",\
259
}
260
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
261
index XXXXXXX..XXXXXXX 100644
262
--- a/include/qemu/throttle.h
263
+++ b/include/qemu/throttle.h
264
@@ -XXX,XX +XXX,XX @@ bool throttle_schedule_timer(ThrottleState *ts,
265
bool is_write);
266
267
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
268
+void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
269
+ Error **errp);
270
+void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var);
271
272
#endif
273
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
274
index XXXXXXX..XXXXXXX 100644
275
--- a/block/throttle-groups.c
276
+++ b/block/throttle-groups.c
277
@@ -XXX,XX +XXX,XX @@
278
#include "qemu/osdep.h"
279
#include "sysemu/block-backend.h"
280
#include "block/throttle-groups.h"
281
+#include "qemu/throttle-options.h"
282
#include "qemu/queue.h"
283
#include "qemu/thread.h"
284
#include "sysemu/qtest.h"
285
+#include "qapi/error.h"
286
+#include "qapi-visit.h"
287
+#include "qom/object.h"
288
+#include "qom/object_interfaces.h"
289
+
50
+
290
+static void throttle_group_obj_init(Object *obj);
51
+ QLIST_INSERT_HEAD(&parent_bs->children, s->child, next);
291
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
52
+ if (s->is_backing) {
292
53
+ parent_bs->backing = s->child;
293
/* The ThrottleGroup structure (with its ThrottleState) is shared
54
+ } else {
294
* among different ThrottleGroupMembers and it's independent from
55
+ parent_bs->file = s->child;
295
@@ -XXX,XX +XXX,XX @@
296
* that ThrottleGroupMember has throttled requests in the queue.
297
*/
298
typedef struct ThrottleGroup {
299
+ Object parent_obj;
300
+
301
+ /* refuse individual property change if initialization is complete */
302
+ bool is_initialized;
303
char *name; /* This is constant during the lifetime of the group */
304
305
QemuMutex lock; /* This lock protects the following four fields */
306
@@ -XXX,XX +XXX,XX @@ typedef struct ThrottleGroup {
307
bool any_timer_armed[2];
308
QEMUClockType clock_type;
309
310
- /* These two are protected by the global throttle_groups_lock */
311
- unsigned refcount;
312
+ /* This field is protected by the global QEMU mutex */
313
QTAILQ_ENTRY(ThrottleGroup) list;
314
} ThrottleGroup;
315
316
-static QemuMutex throttle_groups_lock;
317
+/* This is protected by the global QEMU mutex */
318
static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
319
QTAILQ_HEAD_INITIALIZER(throttle_groups);
320
321
+
322
+/* This function reads throttle_groups and must be called under the global
323
+ * mutex.
324
+ */
325
+static ThrottleGroup *throttle_group_by_name(const char *name)
326
+{
327
+ ThrottleGroup *iter;
328
+
329
+ /* Look for an existing group with that name */
330
+ QTAILQ_FOREACH(iter, &throttle_groups, list) {
331
+ if (!g_strcmp0(name, iter->name)) {
332
+ return iter;
333
+ }
334
+ }
56
+ }
335
+
57
+
336
+ return NULL;
58
+ /*
59
+ * We don't have to restore child->bs here to undo bdrv_replace_child()
60
+ * because that function is transactionable and it registered own completion
61
+ * entries in @tran, so .abort() for bdrv_replace_child_safe() will be
62
+ * called automatically.
63
+ */
337
+}
64
+}
338
+
65
+
339
/* Increments the reference count of a ThrottleGroup given its name.
66
+static void bdrv_remove_filter_or_cow_child_commit(void *opaque)
340
*
67
+{
341
* If no ThrottleGroup is found with the given name a new one is
68
+ BdrvRemoveFilterOrCowChild *s = opaque;
342
* created.
343
*
344
+ * This function edits throttle_groups and must be called under the global
345
+ * mutex.
346
+ *
347
* @name: the name of the ThrottleGroup
348
* @ret: the ThrottleState member of the ThrottleGroup
349
*/
350
ThrottleState *throttle_group_incref(const char *name)
351
{
352
ThrottleGroup *tg = NULL;
353
- ThrottleGroup *iter;
354
-
355
- qemu_mutex_lock(&throttle_groups_lock);
356
357
/* Look for an existing group with that name */
358
- QTAILQ_FOREACH(iter, &throttle_groups, list) {
359
- if (!strcmp(name, iter->name)) {
360
- tg = iter;
361
- break;
362
- }
363
- }
364
-
365
- /* Create a new one if not found */
366
- if (!tg) {
367
- tg = g_new0(ThrottleGroup, 1);
368
+ tg = throttle_group_by_name(name);
369
+
69
+
370
+ if (tg) {
70
+ bdrv_child_free(s->child);
371
+ object_ref(OBJECT(tg));
71
+}
372
+ } else {
373
+ /* Create a new one if not found */
374
+ /* new ThrottleGroup obj will have a refcnt = 1 */
375
+ tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
376
tg->name = g_strdup(name);
377
- tg->clock_type = QEMU_CLOCK_REALTIME;
378
-
379
- if (qtest_enabled()) {
380
- /* For testing block IO throttling only */
381
- tg->clock_type = QEMU_CLOCK_VIRTUAL;
382
- }
383
- qemu_mutex_init(&tg->lock);
384
- throttle_init(&tg->ts);
385
- QLIST_INIT(&tg->head);
386
-
387
- QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
388
+ throttle_group_obj_complete(USER_CREATABLE(tg), &error_abort);
389
}
390
391
- tg->refcount++;
392
-
393
- qemu_mutex_unlock(&throttle_groups_lock);
394
-
395
return &tg->ts;
396
}
397
398
@@ -XXX,XX +XXX,XX @@ ThrottleState *throttle_group_incref(const char *name)
399
* When the reference count reaches zero the ThrottleGroup is
400
* destroyed.
401
*
402
+ * This function edits throttle_groups and must be called under the global
403
+ * mutex.
404
+ *
405
* @ts: The ThrottleGroup to unref, given by its ThrottleState member
406
*/
407
void throttle_group_unref(ThrottleState *ts)
408
{
409
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
410
-
411
- qemu_mutex_lock(&throttle_groups_lock);
412
- if (--tg->refcount == 0) {
413
- QTAILQ_REMOVE(&throttle_groups, tg, list);
414
- qemu_mutex_destroy(&tg->lock);
415
- g_free(tg->name);
416
- g_free(tg);
417
- }
418
- qemu_mutex_unlock(&throttle_groups_lock);
419
+ object_unref(OBJECT(tg));
420
}
421
422
/* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
423
@@ -XXX,XX +XXX,XX @@ static void write_timer_cb(void *opaque)
424
* its timers and updating its throttle_state pointer to point to it. If a
425
* throttling group with that name does not exist yet, it will be created.
426
*
427
+ * This function edits throttle_groups and must be called under the global
428
+ * mutex.
429
+ *
430
* @tgm: the ThrottleGroupMember to insert
431
* @groupname: the name of the group
432
* @ctx: the AioContext to use
433
@@ -XXX,XX +XXX,XX @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
434
tgm->aio_context = NULL;
435
}
436
437
+#undef THROTTLE_OPT_PREFIX
438
+#define THROTTLE_OPT_PREFIX "x-"
439
+
72
+
440
+/* Helper struct and array for QOM property setter/getter */
73
+static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
441
+typedef struct {
74
+ .abort = bdrv_remove_filter_or_cow_child_abort,
442
+ const char *name;
75
+ .commit = bdrv_remove_filter_or_cow_child_commit,
443
+ BucketType type;
76
+ .clean = g_free,
444
+ enum {
445
+ AVG,
446
+ MAX,
447
+ BURST_LENGTH,
448
+ IOPS_SIZE,
449
+ } category;
450
+} ThrottleParamInfo;
451
+
452
+static ThrottleParamInfo properties[] = {
453
+ {
454
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
455
+ THROTTLE_OPS_TOTAL, AVG,
456
+ },
457
+ {
458
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,
459
+ THROTTLE_OPS_TOTAL, MAX,
460
+ },
461
+ {
462
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
463
+ THROTTLE_OPS_TOTAL, BURST_LENGTH,
464
+ },
465
+ {
466
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
467
+ THROTTLE_OPS_READ, AVG,
468
+ },
469
+ {
470
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,
471
+ THROTTLE_OPS_READ, MAX,
472
+ },
473
+ {
474
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
475
+ THROTTLE_OPS_READ, BURST_LENGTH,
476
+ },
477
+ {
478
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
479
+ THROTTLE_OPS_WRITE, AVG,
480
+ },
481
+ {
482
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,
483
+ THROTTLE_OPS_WRITE, MAX,
484
+ },
485
+ {
486
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
487
+ THROTTLE_OPS_WRITE, BURST_LENGTH,
488
+ },
489
+ {
490
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
491
+ THROTTLE_BPS_TOTAL, AVG,
492
+ },
493
+ {
494
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,
495
+ THROTTLE_BPS_TOTAL, MAX,
496
+ },
497
+ {
498
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
499
+ THROTTLE_BPS_TOTAL, BURST_LENGTH,
500
+ },
501
+ {
502
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
503
+ THROTTLE_BPS_READ, AVG,
504
+ },
505
+ {
506
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,
507
+ THROTTLE_BPS_READ, MAX,
508
+ },
509
+ {
510
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
511
+ THROTTLE_BPS_READ, BURST_LENGTH,
512
+ },
513
+ {
514
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
515
+ THROTTLE_BPS_WRITE, AVG,
516
+ },
517
+ {
518
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,
519
+ THROTTLE_BPS_WRITE, MAX,
520
+ },
521
+ {
522
+ THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
523
+ THROTTLE_BPS_WRITE, BURST_LENGTH,
524
+ },
525
+ {
526
+ THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
527
+ 0, IOPS_SIZE,
528
+ }
529
+};
77
+};
530
+
78
+
531
+/* This function edits throttle_groups and must be called under the global
79
+/*
532
+ * mutex */
80
+ * A function to remove backing-chain child of @bs if exists: cow child for
533
+static void throttle_group_obj_init(Object *obj)
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)
534
+{
87
+{
535
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
88
+ BdrvRemoveFilterOrCowChild *s;
89
+ BdrvChild *child = bdrv_filter_or_cow_child(bs);
536
+
90
+
537
+ tg->clock_type = QEMU_CLOCK_REALTIME;
91
+ if (!child) {
538
+ if (qtest_enabled()) {
539
+ /* For testing block IO throttling only */
540
+ tg->clock_type = QEMU_CLOCK_VIRTUAL;
541
+ }
542
+ tg->is_initialized = false;
543
+ qemu_mutex_init(&tg->lock);
544
+ throttle_init(&tg->ts);
545
+ QLIST_INIT(&tg->head);
546
+}
547
+
548
+/* This function edits throttle_groups and must be called under the global
549
+ * mutex */
550
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
551
+{
552
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
553
+ ThrottleConfig cfg;
554
+
555
+ /* set group name to object id if it exists */
556
+ if (!tg->name && tg->parent_obj.parent) {
557
+ tg->name = object_get_canonical_path_component(OBJECT(obj));
558
+ }
559
+ /* We must have a group name at this point */
560
+ assert(tg->name);
561
+
562
+ /* error if name is duplicate */
563
+ if (throttle_group_by_name(tg->name) != NULL) {
564
+ error_setg(errp, "A group with this name already exists");
565
+ return;
92
+ return;
566
+ }
93
+ }
567
+
94
+
568
+ /* check validity */
95
+ if (child->bs) {
569
+ throttle_get_config(&tg->ts, &cfg);
96
+ bdrv_replace_child_safe(child, NULL, tran);
570
+ if (!throttle_is_valid(&cfg, errp)) {
571
+ return;
572
+ }
97
+ }
573
+ throttle_config(&tg->ts, tg->clock_type, &cfg);
98
+
574
+ QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
99
+ s = g_new(BdrvRemoveFilterOrCowChild, 1);
575
+ tg->is_initialized = true;
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);
105
+
106
+ QLIST_SAFE_REMOVE(child, next);
107
+ if (s->is_backing) {
108
+ bs->backing = NULL;
109
+ } else {
110
+ bs->file = NULL;
111
+ }
576
+}
112
+}
577
+
113
+
578
+/* This function edits throttle_groups and must be called under the global
114
static int bdrv_replace_node_noperm(BlockDriverState *from,
579
+ * mutex */
115
BlockDriverState *to,
580
+static void throttle_group_obj_finalize(Object *obj)
116
bool auto_skip, Transaction *tran,
581
+{
582
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
583
+ if (tg->is_initialized) {
584
+ QTAILQ_REMOVE(&throttle_groups, tg, list);
585
+ }
586
+ qemu_mutex_destroy(&tg->lock);
587
+ g_free(tg->name);
588
+}
589
+
590
+static void throttle_group_set(Object *obj, Visitor *v, const char * name,
591
+ void *opaque, Error **errp)
592
+
593
+{
594
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
595
+ ThrottleConfig *cfg;
596
+ ThrottleParamInfo *info = opaque;
597
+ Error *local_err = NULL;
598
+ int64_t value;
599
+
600
+ /* If we have finished initialization, don't accept individual property
601
+ * changes through QOM. Throttle configuration limits must be set in one
602
+ * transaction, as certain combinations are invalid.
603
+ */
604
+ if (tg->is_initialized) {
605
+ error_setg(&local_err, "Property cannot be set after initialization");
606
+ goto ret;
607
+ }
608
+
609
+ visit_type_int64(v, name, &value, &local_err);
610
+ if (local_err) {
611
+ goto ret;
612
+ }
613
+ if (value < 0) {
614
+ error_setg(&local_err, "Property values cannot be negative");
615
+ goto ret;
616
+ }
617
+
618
+ cfg = &tg->ts.cfg;
619
+ switch (info->category) {
620
+ case AVG:
621
+ cfg->buckets[info->type].avg = value;
622
+ break;
623
+ case MAX:
624
+ cfg->buckets[info->type].max = value;
625
+ break;
626
+ case BURST_LENGTH:
627
+ if (value > UINT_MAX) {
628
+ error_setg(&local_err, "%s value must be in the"
629
+ "range [0, %u]", info->name, UINT_MAX);
630
+ goto ret;
631
+ }
632
+ cfg->buckets[info->type].burst_length = value;
633
+ break;
634
+ case IOPS_SIZE:
635
+ cfg->op_size = value;
636
+ break;
637
+ }
638
+
639
+ret:
640
+ error_propagate(errp, local_err);
641
+ return;
642
+
643
+}
644
+
645
+static void throttle_group_get(Object *obj, Visitor *v, const char *name,
646
+ void *opaque, Error **errp)
647
+{
648
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
649
+ ThrottleConfig cfg;
650
+ ThrottleParamInfo *info = opaque;
651
+ int64_t value;
652
+
653
+ throttle_get_config(&tg->ts, &cfg);
654
+ switch (info->category) {
655
+ case AVG:
656
+ value = cfg.buckets[info->type].avg;
657
+ break;
658
+ case MAX:
659
+ value = cfg.buckets[info->type].max;
660
+ break;
661
+ case BURST_LENGTH:
662
+ value = cfg.buckets[info->type].burst_length;
663
+ break;
664
+ case IOPS_SIZE:
665
+ value = cfg.op_size;
666
+ break;
667
+ }
668
+
669
+ visit_type_int64(v, name, &value, errp);
670
+}
671
+
672
+static void throttle_group_set_limits(Object *obj, Visitor *v,
673
+ const char *name, void *opaque,
674
+ Error **errp)
675
+
676
+{
677
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
678
+ ThrottleConfig cfg;
679
+ ThrottleLimits arg = { 0 };
680
+ ThrottleLimits *argp = &arg;
681
+ Error *local_err = NULL;
682
+
683
+ visit_type_ThrottleLimits(v, name, &argp, &local_err);
684
+ if (local_err) {
685
+ goto ret;
686
+ }
687
+ qemu_mutex_lock(&tg->lock);
688
+ throttle_get_config(&tg->ts, &cfg);
689
+ throttle_limits_to_config(argp, &cfg, &local_err);
690
+ if (local_err) {
691
+ goto unlock;
692
+ }
693
+ throttle_config(&tg->ts, tg->clock_type, &cfg);
694
+
695
+unlock:
696
+ qemu_mutex_unlock(&tg->lock);
697
+ret:
698
+ error_propagate(errp, local_err);
699
+ return;
700
+}
701
+
702
+static void throttle_group_get_limits(Object *obj, Visitor *v,
703
+ const char *name, void *opaque,
704
+ Error **errp)
705
+{
706
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
707
+ ThrottleConfig cfg;
708
+ ThrottleLimits arg = { 0 };
709
+ ThrottleLimits *argp = &arg;
710
+
711
+ qemu_mutex_lock(&tg->lock);
712
+ throttle_get_config(&tg->ts, &cfg);
713
+ qemu_mutex_unlock(&tg->lock);
714
+
715
+ throttle_config_to_limits(&cfg, argp);
716
+
717
+ visit_type_ThrottleLimits(v, name, &argp, errp);
718
+}
719
+
720
+static bool throttle_group_can_be_deleted(UserCreatable *uc)
721
+{
722
+ return OBJECT(uc)->ref == 1;
723
+}
724
+
725
+static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data)
726
+{
727
+ size_t i = 0;
728
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
729
+
730
+ ucc->complete = throttle_group_obj_complete;
731
+ ucc->can_be_deleted = throttle_group_can_be_deleted;
732
+
733
+ /* individual properties */
734
+ for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
735
+ object_class_property_add(klass,
736
+ properties[i].name,
737
+ "int",
738
+ throttle_group_get,
739
+ throttle_group_set,
740
+ NULL, &properties[i],
741
+ &error_abort);
742
+ }
743
+
744
+ /* ThrottleLimits */
745
+ object_class_property_add(klass,
746
+ "limits", "ThrottleLimits",
747
+ throttle_group_get_limits,
748
+ throttle_group_set_limits,
749
+ NULL, NULL,
750
+ &error_abort);
751
+}
752
+
753
+static const TypeInfo throttle_group_info = {
754
+ .name = TYPE_THROTTLE_GROUP,
755
+ .parent = TYPE_OBJECT,
756
+ .class_init = throttle_group_obj_class_init,
757
+ .instance_size = sizeof(ThrottleGroup),
758
+ .instance_init = throttle_group_obj_init,
759
+ .instance_finalize = throttle_group_obj_finalize,
760
+ .interfaces = (InterfaceInfo[]) {
761
+ { TYPE_USER_CREATABLE },
762
+ { }
763
+ },
764
+};
765
+
766
static void throttle_groups_init(void)
767
{
768
- qemu_mutex_init(&throttle_groups_lock);
769
+ type_register_static(&throttle_group_info);
770
}
771
772
-block_init(throttle_groups_init);
773
+type_init(throttle_groups_init);
774
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
775
index XXXXXXX..XXXXXXX 100644
776
--- a/tests/test-throttle.c
777
+++ b/tests/test-throttle.c
778
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
779
qemu_init_main_loop(&error_fatal);
780
ctx = qemu_get_aio_context();
781
bdrv_init();
782
+ module_call_init(MODULE_INIT_QOM);
783
784
do {} while (g_main_context_iteration(NULL, false));
785
786
diff --git a/util/throttle.c b/util/throttle.c
787
index XXXXXXX..XXXXXXX 100644
788
--- a/util/throttle.c
789
+++ b/util/throttle.c
790
@@ -XXX,XX +XXX,XX @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
791
}
792
}
793
794
+/* return a ThrottleConfig based on the options in a ThrottleLimits
795
+ *
796
+ * @arg: the ThrottleLimits object to read from
797
+ * @cfg: the ThrottleConfig to edit
798
+ * @errp: error object
799
+ */
800
+void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
801
+ Error **errp)
802
+{
803
+ if (arg->has_bps_total) {
804
+ cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
805
+ }
806
+ if (arg->has_bps_read) {
807
+ cfg->buckets[THROTTLE_BPS_READ].avg = arg->bps_read;
808
+ }
809
+ if (arg->has_bps_write) {
810
+ cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
811
+ }
812
+
813
+ if (arg->has_iops_total) {
814
+ cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
815
+ }
816
+ if (arg->has_iops_read) {
817
+ cfg->buckets[THROTTLE_OPS_READ].avg = arg->iops_read;
818
+ }
819
+ if (arg->has_iops_write) {
820
+ cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
821
+ }
822
+
823
+ if (arg->has_bps_total_max) {
824
+ cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
825
+ }
826
+ if (arg->has_bps_read_max) {
827
+ cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
828
+ }
829
+ if (arg->has_bps_write_max) {
830
+ cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
831
+ }
832
+ if (arg->has_iops_total_max) {
833
+ cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
834
+ }
835
+ if (arg->has_iops_read_max) {
836
+ cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
837
+ }
838
+ if (arg->has_iops_write_max) {
839
+ cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
840
+ }
841
+
842
+ if (arg->has_bps_total_max_length) {
843
+ if (arg->bps_total_max_length > UINT_MAX) {
844
+ error_setg(errp, "bps-total-max-length value must be in"
845
+ " the range [0, %u]", UINT_MAX);
846
+ return;
847
+ }
848
+ cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length;
849
+ }
850
+ if (arg->has_bps_read_max_length) {
851
+ if (arg->bps_read_max_length > UINT_MAX) {
852
+ error_setg(errp, "bps-read-max-length value must be in"
853
+ " the range [0, %u]", UINT_MAX);
854
+ return;
855
+ }
856
+ cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length;
857
+ }
858
+ if (arg->has_bps_write_max_length) {
859
+ if (arg->bps_write_max_length > UINT_MAX) {
860
+ error_setg(errp, "bps-write-max-length value must be in"
861
+ " the range [0, %u]", UINT_MAX);
862
+ return;
863
+ }
864
+ cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length;
865
+ }
866
+ if (arg->has_iops_total_max_length) {
867
+ if (arg->iops_total_max_length > UINT_MAX) {
868
+ error_setg(errp, "iops-total-max-length value must be in"
869
+ " the range [0, %u]", UINT_MAX);
870
+ return;
871
+ }
872
+ cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length;
873
+ }
874
+ if (arg->has_iops_read_max_length) {
875
+ if (arg->iops_read_max_length > UINT_MAX) {
876
+ error_setg(errp, "iops-read-max-length value must be in"
877
+ " the range [0, %u]", UINT_MAX);
878
+ return;
879
+ }
880
+ cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length;
881
+ }
882
+ if (arg->has_iops_write_max_length) {
883
+ if (arg->iops_write_max_length > UINT_MAX) {
884
+ error_setg(errp, "iops-write-max-length value must be in"
885
+ " the range [0, %u]", UINT_MAX);
886
+ return;
887
+ }
888
+ cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length;
889
+ }
890
+
891
+ if (arg->has_iops_size) {
892
+ cfg->op_size = arg->iops_size;
893
+ }
894
+
895
+ throttle_is_valid(cfg, errp);
896
+}
897
+
898
+/* write the options of a ThrottleConfig to a ThrottleLimits
899
+ *
900
+ * @cfg: the ThrottleConfig to read from
901
+ * @var: the ThrottleLimits to write to
902
+ */
903
+void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var)
904
+{
905
+ var->bps_total = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
906
+ var->bps_read = cfg->buckets[THROTTLE_BPS_READ].avg;
907
+ var->bps_write = cfg->buckets[THROTTLE_BPS_WRITE].avg;
908
+ var->iops_total = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
909
+ var->iops_read = cfg->buckets[THROTTLE_OPS_READ].avg;
910
+ var->iops_write = cfg->buckets[THROTTLE_OPS_WRITE].avg;
911
+ var->bps_total_max = cfg->buckets[THROTTLE_BPS_TOTAL].max;
912
+ var->bps_read_max = cfg->buckets[THROTTLE_BPS_READ].max;
913
+ var->bps_write_max = cfg->buckets[THROTTLE_BPS_WRITE].max;
914
+ var->iops_total_max = cfg->buckets[THROTTLE_OPS_TOTAL].max;
915
+ var->iops_read_max = cfg->buckets[THROTTLE_OPS_READ].max;
916
+ var->iops_write_max = cfg->buckets[THROTTLE_OPS_WRITE].max;
917
+ var->bps_total_max_length = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
918
+ var->bps_read_max_length = cfg->buckets[THROTTLE_BPS_READ].burst_length;
919
+ var->bps_write_max_length = cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
920
+ var->iops_total_max_length = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
921
+ var->iops_read_max_length = cfg->buckets[THROTTLE_OPS_READ].burst_length;
922
+ var->iops_write_max_length = cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
923
+ var->iops_size = cfg->op_size;
924
+
925
+ var->has_bps_total = true;
926
+ var->has_bps_read = true;
927
+ var->has_bps_write = true;
928
+ var->has_iops_total = true;
929
+ var->has_iops_read = true;
930
+ var->has_iops_write = true;
931
+ var->has_bps_total_max = true;
932
+ var->has_bps_read_max = true;
933
+ var->has_bps_write_max = true;
934
+ var->has_iops_total_max = true;
935
+ var->has_iops_read_max = true;
936
+ var->has_iops_write_max = true;
937
+ var->has_bps_read_max_length = true;
938
+ var->has_bps_total_max_length = true;
939
+ var->has_bps_write_max_length = true;
940
+ var->has_iops_total_max_length = true;
941
+ var->has_iops_read_max_length = true;
942
+ var->has_iops_write_max_length = true;
943
+ var->has_iops_size = true;
944
+}
945
--
117
--
946
2.13.5
118
2.30.2
947
119
948
120
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Using bdrv_replace_node() for removing filter is not good enough: it
4
keeps child reference of the filter, which may conflict with original
5
top node during permission update.
6
7
Instead let's create new interface, which will do all graph
8
modifications first and then update permissions.
9
10
Let's modify bdrv_replace_node_common(), allowing it additionally drop
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.
14
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
16
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
17
Message-Id: <20210428151804.439460-24-vsementsov@virtuozzo.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
20
include/block/block.h | 1 +
21
block.c | 43 +++++++++++++++++++++++++++++++++++++++----
22
2 files changed, 40 insertions(+), 4 deletions(-)
23
24
diff --git a/include/block/block.h b/include/block/block.h
25
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block.h
27
+++ b/include/block/block.h
28
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
29
Error **errp);
30
BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
31
int flags, Error **errp);
32
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp);
33
34
int bdrv_parse_aio(const char *mode, int *flags);
35
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
36
diff --git a/block.c b/block.c
37
index XXXXXXX..XXXXXXX 100644
38
--- a/block.c
39
+++ b/block.c
40
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
41
* format nodes (always .backing) and filter child for filters (may be .file or
42
* .backing)
43
*/
44
-__attribute__((unused))
45
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
46
Transaction *tran)
47
{
48
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
49
*
50
* With auto_skip=false the error is returned if from has a parent which should
51
* not be updated.
52
+ *
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
+ }
77
+ }
78
+
79
/* Make sure that @from doesn't go away until we have successfully attached
80
* all of its parents to @to. */
81
bdrv_ref(from);
82
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
83
goto out;
84
}
85
86
+ if (detach_subchain) {
87
+ bdrv_remove_filter_or_cow_child(to_cow_parent, tran);
88
+ }
89
+
90
found = g_hash_table_new(NULL, NULL);
91
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;
127
--
128
2.30.2
129
130
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
We don't need this workaround anymore: bdrv_append is already smart
4
enough and we can use new bdrv_drop_filter().
5
6
This commit efficiently reverts also recent 705dde27c6c53b73, which
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.
9
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>
14
---
15
block/backup-top.c | 47 +-------------------------------------
16
tests/qemu-iotests/283.out | 2 +-
17
2 files changed, 2 insertions(+), 47 deletions(-)
18
19
diff --git a/block/backup-top.c b/block/backup-top.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/backup-top.c
22
+++ b/block/backup-top.c
23
@@ -XXX,XX +XXX,XX @@
24
typedef struct BDRVBackupTopState {
25
BlockCopyState *bcs;
26
BdrvChild *target;
27
- bool active;
28
int64_t cluster_size;
29
} BDRVBackupTopState;
30
31
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_co_preadv(
32
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
33
QEMUIOVector *qiov, int flags)
34
{
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 ===
133
134
--
135
2.30.2
136
137
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
This argument is always NULL. Drop it.
4
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>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block.c | 38 +++++++++++---------------------------
11
1 file changed, 11 insertions(+), 27 deletions(-)
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 int bdrv_fill_options(QDict **options, const char *filename,
18
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
19
uint64_t new_used_perm,
20
uint64_t new_shared_perm,
21
- GSList *ignore_children,
22
Error **errp);
23
24
typedef struct BlockReopenQueueEntry {
25
@@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
26
return false;
27
}
28
29
-static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
30
- GSList *ignore_children,
31
- Error **errp)
32
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
33
{
34
BdrvChild *a, *b;
35
36
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
37
* directions.
38
*/
39
QLIST_FOREACH(a, &bs->parents, next_parent) {
40
- if (g_slist_find(ignore_children, a)) {
41
- continue;
42
- }
43
-
44
QLIST_FOREACH(b, &bs->parents, next_parent) {
45
- if (a == b || g_slist_find(ignore_children, b)) {
46
+ if (a == b) {
47
continue;
48
}
49
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
51
static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
52
uint64_t cumulative_perms,
53
uint64_t cumulative_shared_perms,
54
- GSList *ignore_children,
55
Transaction *tran, Error **errp)
56
{
57
BlockDriver *drv = bs->drv;
58
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
59
bool use_cumulative_perms,
60
uint64_t cumulative_perms,
61
uint64_t cumulative_shared_perms,
62
- GSList *ignore_children,
63
Transaction *tran, Error **errp)
64
{
65
int ret;
66
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
67
68
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
69
cumulative_shared_perms,
70
- ignore_children, tran, errp);
71
+ tran, errp);
72
if (ret < 0) {
73
return ret;
74
}
75
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
76
for ( ; list; list = list->next) {
77
bs = list->data;
78
79
- if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
80
+ if (bdrv_parent_perms_conflict(bs, errp)) {
81
return -EINVAL;
82
}
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,
136
}
137
138
return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
139
- ignore_children, errp);
140
+ errp);
141
}
142
143
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
144
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
145
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
146
BDRVReopenState *state = &bs_entry->state;
147
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
148
- state->shared_perm, NULL, errp);
149
+ state->shared_perm, errp);
150
if (ret < 0) {
151
goto cleanup_perm;
152
}
153
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
154
bs_queue, state->perm, state->shared_perm,
155
&nperm, &nshared);
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
}
162
--
163
2.30.2
164
165
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
To be used in the further 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-27-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block.c | 46 ++++++++++++++++++++++++++++++++++++++++++----
11
1 file changed, 42 insertions(+), 4 deletions(-)
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 @@ void bdrv_root_unref_child(BdrvChild *child)
18
bdrv_unref(child_bs);
19
}
20
21
+typedef struct BdrvSetInheritsFrom {
22
+ BlockDriverState *bs;
23
+ BlockDriverState *old_inherits_from;
24
+} BdrvSetInheritsFrom;
25
+
26
+static void bdrv_set_inherits_from_abort(void *opaque)
27
+{
28
+ BdrvSetInheritsFrom *s = opaque;
29
+
30
+ s->bs->inherits_from = s->old_inherits_from;
31
+}
32
+
33
+static TransactionActionDrv bdrv_set_inherits_from_drv = {
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);
52
+ }
53
+
54
+ bs->inherits_from = new_inherits_from;
55
+}
56
+
57
/**
58
* Clear all inherits_from pointers from children and grandchildren of
59
* @root that point to @root, where necessary.
60
+ * @tran is allowed to be NULL. In this case no rollback is possible
61
*/
62
-static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
63
+static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
64
+ Transaction *tran)
65
{
66
BdrvChild *c;
67
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
69
}
70
}
71
if (c == NULL) {
72
- child->bs->inherits_from = NULL;
73
+ bdrv_set_inherits_from(child->bs, NULL, tran);
74
}
75
}
76
77
QLIST_FOREACH(c, &child->bs->children, next) {
78
- bdrv_unset_inherits_from(root, c);
79
+ bdrv_unset_inherits_from(root, c, tran);
80
}
81
}
82
83
@@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
84
return;
85
}
86
87
- bdrv_unset_inherits_from(parent, child);
88
+ bdrv_unset_inherits_from(parent, child, NULL);
89
bdrv_root_unref_child(child);
90
}
91
92
--
93
2.30.2
94
95
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
To be used in further 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-28-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
include/block/block.h | 3 ++-
11
block.c | 9 ++++-----
12
block/io.c | 31 +++++++++++++++++++++++++++++--
13
3 files changed, 35 insertions(+), 8 deletions(-)
14
15
diff --git a/include/block/block.h b/include/block/block.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/block.h
18
+++ b/include/block/block.h
19
@@ -XXX,XX +XXX,XX @@
20
#include "block/dirty-bitmap.h"
21
#include "block/blockjob.h"
22
#include "qemu/hbitmap.h"
23
+#include "qemu/transactions.h"
24
25
/*
26
* generated_co_wrapper
27
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
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,
36
diff --git a/block.c b/block.c
37
index XXXXXXX..XXXXXXX 100644
38
--- a/block.c
39
+++ b/block.c
40
@@ -XXX,XX +XXX,XX @@
41
#include "qemu/timer.h"
42
#include "qemu/cutils.h"
43
#include "qemu/id.h"
44
-#include "qemu/transactions.h"
45
#include "block/coroutines.h"
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)
112
{
113
ERRP_GUARD();
114
BlockDriver *drv = bs->drv;
115
BdrvChild *c;
116
bool have_limits;
117
118
+ if (tran) {
119
+ BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1);
120
+ *s = (BdrvRefreshLimitsState) {
121
+ .bs = bs,
122
+ .old_bl = bs->bl,
123
+ };
124
+ tran_add(tran, &bdrv_refresh_limits_drv, s);
125
+ }
126
+
127
memset(&bs->bl, 0, sizeof(bs->bl));
128
129
if (!drv) {
130
@@ -XXX,XX +XXX,XX @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
131
QLIST_FOREACH(c, &bs->children, next) {
132
if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW))
133
{
134
- bdrv_refresh_limits(c->bs, errp);
135
+ bdrv_refresh_limits(c->bs, tran, errp);
136
if (*errp) {
137
return;
138
}
139
--
140
2.30.2
141
142
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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.
6
7
We don't need to create extra reference to backing_hd as we don't lose
8
it in bdrv_attach_child().
9
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>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
block.c | 54 +++++++++++++++++++++++++++++++++++++-----------------
16
1 file changed, 37 insertions(+), 17 deletions(-)
17
18
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block.c
21
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
23
BdrvChild **child,
24
Transaction *tran,
25
Error **errp);
26
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
27
+ Transaction *tran);
28
29
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
30
*queue, Error **errp);
31
@@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
32
* Sets the bs->backing link of a BDS. A new reference is created; callers
33
* which don't need their own reference any more must call bdrv_unref().
34
*/
35
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
36
- Error **errp)
37
+static int bdrv_set_backing_noperm(BlockDriverState *bs,
38
+ BlockDriverState *backing_hd,
39
+ Transaction *tran, Error **errp)
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;
111
}
112
--
113
2.30.2
114
115
diff view generated by jsdifflib
1
From: Pavel Butsykin <pbutsykin@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
After calling qcow2_inactivate(), all qcow2 caches must be flushed, but this
3
During reopen we may add backing bs from other aio context, which may
4
may not happen, because the last call qcow2_store_persistent_dirty_bitmaps()
4
lead to changing original context of top bs.
5
can lead to marking l2/refcont cache as dirty.
6
5
7
Let's move qcow2_store_persistent_dirty_bitmaps() before the caсhe flushing
6
We are going to move graph modification to prepare stage. So, it will
8
to fix it.
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
9
10
Cc: qemu-stable@nongnu.org
10
To avoid this problem move bdrv_flush() to be a separate reopen stage
11
Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com>
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>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
27
---
14
block/qcow2.c | 16 ++++++++--------
28
block.c | 14 ++++++++------
15
1 file changed, 8 insertions(+), 8 deletions(-)
29
1 file changed, 8 insertions(+), 6 deletions(-)
16
30
17
diff --git a/block/qcow2.c b/block/qcow2.c
31
diff --git a/block.c b/block.c
18
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
19
--- a/block/qcow2.c
33
--- a/block.c
20
+++ b/block/qcow2.c
34
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs)
35
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
22
int ret, result = 0;
36
23
Error *local_err = NULL;
37
assert(bs_queue != NULL);
24
38
25
+ qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
39
+ QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
26
+ if (local_err != NULL) {
40
+ ret = bdrv_flush(bs_entry->state.bs);
27
+ result = -EINVAL;
41
+ if (ret < 0) {
28
+ error_report_err(local_err);
42
+ error_setg_errno(errp, -ret, "Error flushing drive");
29
+ error_report("Persistent bitmaps are lost for node '%s'",
43
+ goto cleanup;
30
+ bdrv_get_device_or_node_name(bs));
44
+ }
31
+ }
45
+ }
32
+
46
+
33
ret = qcow2_cache_flush(bs, s->l2_table_cache);
47
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
34
if (ret) {
48
assert(bs_entry->state.bs->quiesce_counter > 0);
35
result = ret;
49
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
36
@@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs)
50
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
37
strerror(-ret));
51
bdrv_reopen_perm(queue, reopen_state->bs,
38
}
52
&reopen_state->perm, &reopen_state->shared_perm);
39
53
40
- qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
54
- ret = bdrv_flush(reopen_state->bs);
41
- if (local_err != NULL) {
55
- if (ret) {
42
- result = -EINVAL;
56
- error_setg_errno(errp, -ret, "Error flushing drive");
43
- error_report_err(local_err);
57
- goto error;
44
- error_report("Persistent bitmaps are lost for node '%s'",
45
- bdrv_get_device_or_node_name(bs));
46
- }
58
- }
47
-
59
-
48
if (result == 0) {
60
if (drv->bdrv_reopen_prepare) {
49
qcow2_mark_clean(bs);
61
/*
50
}
62
* If a driver-specific option is missing, it means that we
51
--
63
--
52
2.13.5
64
2.30.2
53
65
54
66
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
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>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
include/block/block.h | 3 +-
17
block.c | 187 ++++++++++++------------------------------
18
block/file-posix.c | 91 +++++++-------------
19
3 files changed, 84 insertions(+), 197 deletions(-)
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
348
diff --git a/block/file-posix.c b/block/file-posix.c
349
index XXXXXXX..XXXXXXX 100644
350
--- a/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;
360
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
361
BDRVRawReopenState *rs;
362
QemuOpts *opts;
363
int ret;
364
- Error *local_err = NULL;
365
366
assert(state != NULL);
367
assert(state->bs != NULL);
368
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
369
* bdrv_reopen_prepare() will detect changes and complain. */
370
qemu_opts_to_qdict(opts, state->options);
371
372
- rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
373
- state->perm, true, &local_err);
374
- if (local_err) {
375
- error_propagate(errp, local_err);
376
- ret = -1;
377
- goto out;
378
- }
379
-
380
- /* Fail already reopen_prepare() if we can't get a working O_DIRECT
381
- * alignment with the new fd. */
382
- if (rs->fd != -1) {
383
- raw_probe_alignment(state->bs, rs->fd, &local_err);
384
- if (local_err) {
385
- error_propagate(errp, local_err);
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
+ */
398
399
s->reopen_state = state;
400
ret = 0;
401
-out_fd:
402
- if (ret < 0) {
403
- qemu_close(rs->fd);
404
- rs->fd = -1;
405
- }
406
+
407
out:
408
qemu_opts_del(opts);
409
return ret;
410
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
411
s->drop_cache = rs->drop_cache;
412
s->check_cache_dropped = rs->check_cache_dropped;
413
s->open_flags = rs->open_flags;
414
-
415
- qemu_close(s->fd);
416
- s->fd = rs->fd;
417
-
418
g_free(state->opaque);
419
state->opaque = NULL;
420
421
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_abort(BDRVReopenState *state)
422
return;
423
}
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;
508
--
509
2.30.2
510
511
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
block.c | 103 --------------------------------------------------------
9
1 file changed, 103 deletions(-)
10
11
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block.c
14
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
16
return 0;
17
}
18
19
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
20
- uint64_t new_used_perm,
21
- uint64_t new_shared_perm,
22
- Error **errp);
23
-
24
typedef struct BlockReopenQueueEntry {
25
bool prepared;
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)
42
{
43
return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
44
}
45
46
-/*
47
- * Notifies drivers that after a previous bdrv_check_perm() call, the
48
- * permission update is not performed and any preparations made for it (e.g.
49
- * taken file locks) need to be undone.
50
- */
51
-static void bdrv_node_abort_perm_update(BlockDriverState *bs)
52
-{
53
- BlockDriver *drv = bs->drv;
54
- BdrvChild *c;
55
-
56
- if (!drv) {
57
- return;
58
- }
59
-
60
- bdrv_drv_set_perm_abort(bs);
61
-
62
- QLIST_FOREACH(c, &bs->children, next) {
63
- bdrv_child_set_perm_abort(c);
64
- }
65
-}
66
-
67
-static void bdrv_list_abort_perm_update(GSList *list)
68
-{
69
- for ( ; list; list = list->next) {
70
- bdrv_node_abort_perm_update((BlockDriverState *)list->data);
71
- }
72
-}
73
-
74
-__attribute__((unused))
75
-static void bdrv_abort_perm_update(BlockDriverState *bs)
76
-{
77
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
78
- return bdrv_list_abort_perm_update(list);
79
-}
80
-
81
static void bdrv_node_set_perm(BlockDriverState *bs)
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
{
145
--
146
2.30.2
147
148
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
bdrv_check_perm_common() has only one caller, so no more sense in
4
"common".
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-33-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block.c | 32 +++-----------------------------
12
1 file changed, 3 insertions(+), 29 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_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
19
return 0;
20
}
21
22
-/*
23
- * If use_cumulative_perms is true, use cumulative_perms and
24
- * cumulative_shared_perms for first element of the list. Otherwise just refresh
25
- * all permissions.
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;
50
- }
51
-
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;
57
}
58
59
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
60
- Transaction *tran, Error **errp)
61
-{
62
- return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
63
-}
64
-
65
static void bdrv_node_set_perm(BlockDriverState *bs)
66
{
67
BlockDriver *drv = bs->drv;
68
--
69
2.30.2
70
71
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Now that bdrv_truncate is passed to bs->file by default, remove the
3
bdrv_replace_child() has only one caller, the second argument is
4
callback from block/blkdebug.c and set is_filter to true. is_filter also gives
4
unused. Inline it now. This triggers deletion of some more unused
5
access to other callbacks that are forwarded automatically to bs->file for
5
interfaces.
6
filters.
7
6
8
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
9
Message-Id: <20210428151804.439460-34-vsementsov@virtuozzo.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
11
---
13
block/blkdebug.c | 8 +-------
12
block.c | 101 ++++++++++----------------------------------------------
14
1 file changed, 1 insertion(+), 7 deletions(-)
13
1 file changed, 18 insertions(+), 83 deletions(-)
15
14
16
diff --git a/block/blkdebug.c b/block/blkdebug.c
15
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
18
--- a/block/blkdebug.c
17
--- a/block.c
19
+++ b/block/blkdebug.c
18
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
19
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
21
return bdrv_getlength(bs->file->bs);
20
return 0;
22
}
21
}
23
22
24
-static int blkdebug_truncate(BlockDriverState *bs, int64_t offset,
23
-static void bdrv_node_set_perm(BlockDriverState *bs)
25
- PreallocMode prealloc, Error **errp)
26
-{
24
-{
27
- return bdrv_truncate(bs->file, offset, prealloc, errp);
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
- }
28
-}
44
-}
29
-
45
-
30
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
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)
31
{
61
{
32
BDRVBlkdebugState *s = bs->opaque;
62
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
33
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
63
}
34
.format_name = "blkdebug",
64
}
35
.protocol_name = "blkdebug",
65
36
.instance_size = sizeof(BDRVBlkdebugState),
66
-/*
37
+ .is_filter = true,
67
- * Updates @child to change its reference to point to @new_bs, including
38
68
- * checking and applying the necessary permission updates both to the old node
39
.bdrv_parse_filename = blkdebug_parse_filename,
69
- * and to @new_bs.
40
.bdrv_file_open = blkdebug_open,
70
- *
41
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
71
- * NULL is passed as @new_bs for removing the reference before freeing @child.
42
.bdrv_child_perm = bdrv_filter_default_perms,
72
- *
43
73
- * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this
44
.bdrv_getlength = blkdebug_getlength,
74
- * function uses bdrv_set_perm() to update the permissions according to the new
45
- .bdrv_truncate = blkdebug_truncate,
75
- * reference that @new_bs gets.
46
.bdrv_refresh_filename = blkdebug_refresh_filename,
76
- *
47
.bdrv_refresh_limits = blkdebug_refresh_limits,
77
- * Callers must ensure that child->frozen is false.
48
78
- */
79
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
80
-{
81
- BlockDriverState *old_bs = child->bs;
82
-
83
- /* Asserts that child->frozen == false */
84
- bdrv_replace_child_noperm(child, new_bs);
85
-
86
- /*
87
- * Start with the new node's permissions. If @new_bs is a (direct
88
- * or indirect) child of @old_bs, we must complete the permission
89
- * update on @new_bs before we loosen the restrictions on @old_bs.
90
- * Otherwise, bdrv_check_perm() on @old_bs would re-initiate
91
- * updating the permissions of @new_bs, and thus not purely loosen
92
- * restrictions.
93
- */
94
- if (new_bs) {
95
- bdrv_set_perm(new_bs);
96
- }
97
-
98
- if (old_bs) {
99
- /*
100
- * Update permissions for old node. We're just taking a parent away, so
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
+ }
139
}
140
141
/*
49
--
142
--
50
2.13.5
143
2.30.2
51
144
52
145
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The following functions fail if bs->drv is a filter and does not
3
Old interfaces dropped, nobody directly calls
4
implement them:
4
bdrv_child_set_perm_abort() and bdrv_child_set_perm_commit(), so we can
5
use personal state structure for the action and stop exploiting
6
BdrvChild structure. Also, drop "_safe" suffix which is redundant now.
5
7
6
bdrv_probe_blocksizes
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
bdrv_probe_geometry
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
bdrv_truncate
10
Message-Id: <20210428151804.439460-35-vsementsov@virtuozzo.com>
9
bdrv_has_zero_init
10
bdrv_get_info
11
12
Instead, the call should be passed to bs->file if it exists, to allow
13
filter drivers to support those methods without implementing them. This
14
commit makes `drv->is_filter = true` imply that these callbacks will be
15
forwarded to bs->file by default, so disabling support for these
16
functions must be done explicitly.
17
18
Reviewed-by: Eric Blake <eblake@redhat.com>
19
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
20
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
12
---
23
include/block/block_int.h | 6 +++++-
13
include/block/block_int.h | 5 ----
24
block.c | 21 +++++++++++++++++++--
14
block.c | 63 ++++++++++++++-------------------------
25
2 files changed, 24 insertions(+), 3 deletions(-)
15
2 files changed, 22 insertions(+), 46 deletions(-)
26
16
27
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
28
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/block_int.h
19
--- a/include/block/block_int.h
30
+++ b/include/block/block_int.h
20
+++ b/include/block/block_int.h
31
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
21
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
32
const char *format_name;
22
*/
33
int instance_size;
23
uint64_t shared_perm;
34
24
35
- /* set to true if the BlockDriver is a block filter */
25
- /* backup of permissions during permission update procedure */
36
+ /* set to true if the BlockDriver is a block filter. Block filters pass
26
- bool has_backup_perm;
37
+ * certain callbacks that refer to data (see block.c) to their bs->file if
27
- uint64_t backup_perm;
38
+ * the driver doesn't implement them. Drivers that do not wish to forward
28
- uint64_t backup_shared_perm;
39
+ * must implement them and return -ENOTSUP.
29
-
40
+ */
30
/*
41
bool is_filter;
31
* This link is frozen: the child can neither be replaced nor
42
/* for snapshots block filter like Quorum can implement the
32
* detached from the parent.
43
* following recursive callback.
44
diff --git a/block.c b/block.c
33
diff --git a/block.c b/block.c
45
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
46
--- a/block.c
35
--- a/block.c
47
+++ b/block.c
36
+++ b/block.c
48
@@ -XXX,XX +XXX,XX @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
37
@@ -XXX,XX +XXX,XX @@ static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
49
38
return g_slist_prepend(list, bs);
50
if (drv && drv->bdrv_probe_blocksizes) {
39
}
51
return drv->bdrv_probe_blocksizes(bs, bsz);
40
52
+ } else if (drv && drv->is_filter && bs->file) {
41
-static void bdrv_child_set_perm_commit(void *opaque)
53
+ return bdrv_probe_blocksizes(bs->file->bs, bsz);
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,
76
};
77
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);
100
+
101
+ *s = (BdrvChildSetPermState) {
102
+ .child = c,
103
+ .old_perm = c->perm,
104
+ .old_shared_perm = c->shared_perm,
105
+ };
106
107
c->perm = perm;
108
c->shared_perm = shared;
109
110
- if (tran) {
111
- tran_add(tran, &bdrv_child_set_pem_drv, c);
112
- }
113
+ tran_add(tran, &bdrv_child_set_pem_drv, s);
114
}
115
116
static void bdrv_drv_set_perm_commit(void *opaque)
117
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
118
bdrv_child_perm(bs, c->bs, c, c->role, q,
119
cumulative_perms, cumulative_shared_perms,
120
&cur_perm, &cur_shared);
121
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran);
122
+ bdrv_child_set_perm(c, cur_perm, cur_shared, tran);
54
}
123
}
55
124
56
return -ENOTSUP;
57
@@ -XXX,XX +XXX,XX @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
58
59
if (drv && drv->bdrv_probe_geometry) {
60
return drv->bdrv_probe_geometry(bs, geo);
61
+ } else if (drv && drv->is_filter && bs->file) {
62
+ return bdrv_probe_geometry(bs->file->bs, geo);
63
}
64
65
return -ENOTSUP;
66
@@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
67
68
assert(child->perm & BLK_PERM_RESIZE);
69
70
+ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */
71
if (!drv) {
72
error_setg(errp, "No medium inserted");
73
return -ENOMEDIUM;
74
}
75
if (!drv->bdrv_truncate) {
76
+ if (bs->file && drv->is_filter) {
77
+ return bdrv_truncate(bs->file, offset, prealloc, errp);
78
+ }
79
error_setg(errp, "Image format driver does not support resize");
80
return -ENOTSUP;
81
}
82
@@ -XXX,XX +XXX,XX @@ int bdrv_has_zero_init(BlockDriverState *bs)
83
if (bs->drv->bdrv_has_zero_init) {
84
return bs->drv->bdrv_has_zero_init(bs);
85
}
86
+ if (bs->file && bs->drv->is_filter) {
87
+ return bdrv_has_zero_init(bs->file->bs);
88
+ }
89
90
/* safe default */
91
return 0;
125
return 0;
92
@@ -XXX,XX +XXX,XX @@ void bdrv_get_backing_filename(BlockDriverState *bs,
126
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
93
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
127
Transaction *tran = tran_new();
94
{
128
int ret;
95
BlockDriver *drv = bs->drv;
129
96
- if (!drv)
130
- bdrv_child_set_perm_safe(c, perm, shared, tran);
97
+ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */
131
+ bdrv_child_set_perm(c, perm, shared, tran);
98
+ if (!drv) {
132
99
return -ENOMEDIUM;
133
ret = bdrv_refresh_perms(c->bs, &local_err);
100
- if (!drv->bdrv_get_info)
134
101
+ }
102
+ if (!drv->bdrv_get_info) {
103
+ if (bs->file && drv->is_filter) {
104
+ return bdrv_get_info(bs->file->bs, bdi);
105
+ }
106
return -ENOTSUP;
107
+ }
108
memset(bdi, 0, sizeof(*bdi));
109
return drv->bdrv_get_info(bs, bdi);
110
}
111
--
135
--
112
2.13.5
136
2.30.2
113
137
114
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Now, bdrv_node_check_perm() is called only with fresh cumulative
4
permissions, so its actually "refresh_perm".
5
6
Move permission calculation to the function. Also, drop unreachable
7
error message and rewrite the remaining one to be more generic (as now
8
we don't know which node is added and which was already here).
9
10
Add also Virtuozzo copyright, as big work is done at this point.
11
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Message-Id: <20210428151804.439460-37-vsementsov@virtuozzo.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
17
block.c | 38 +++++++++++---------------------------
18
tests/qemu-iotests/245 | 2 +-
19
2 files changed, 12 insertions(+), 28 deletions(-)
20
21
diff --git a/block.c b/block.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block.c
24
+++ b/block.c
25
@@ -XXX,XX +XXX,XX @@
26
* QEMU System Emulator block driver
27
*
28
* Copyright (c) 2003 Fabrice Bellard
29
+ * Copyright (c) 2020 Virtuozzo International GmbH.
30
*
31
* Permission is hereby granted, free of charge, to any person obtaining a copy
32
* of this software and associated documentation files (the "Software"), to deal
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
34
}
35
36
/*
37
- * Check whether permissions on this node can be changed in a way that
38
- * @cumulative_perms and @cumulative_shared_perms are the new cumulative
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
116
--
117
2.30.2
118
119
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
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.
2
6
3
Move the CoMutex and CoQueue inits inside throttle_group_register_tgm()
7
Add a new BDRV_O_NO_SHARE flag that is used in blk_new_open() to unshare
4
which is called whenever a ThrottleGroupMember is initialized. There's
8
any permissions that can be unshared.
5
no need for them to be separate.
6
9
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Message-Id: <20210422164344.283389-2-kwolf@redhat.com>
9
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
15
---
12
block/block-backend.c | 3 ---
16
include/block/block.h | 1 +
13
block/throttle-groups.c | 3 +++
17
block/block-backend.c | 19 +++++++++++++------
14
2 files changed, 3 insertions(+), 3 deletions(-)
18
2 files changed, 14 insertions(+), 6 deletions(-)
15
19
20
diff --git a/include/block/block.h b/include/block/block.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/block.h
23
+++ b/include/block/block.h
24
@@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry {
25
uint32_t cylinders;
26
} HDGeometry;
27
28
+#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */
29
#define BDRV_O_RDWR 0x0002
30
#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */
31
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
16
diff --git a/block/block-backend.c b/block/block-backend.c
32
diff --git a/block/block-backend.c b/block/block-backend.c
17
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
18
--- a/block/block-backend.c
34
--- a/block/block-backend.c
19
+++ b/block/block-backend.c
35
+++ b/block/block-backend.c
20
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
36
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
21
blk->shared_perm = shared_perm;
37
BlockBackend *blk;
22
blk_set_enable_write_cache(blk, true);
38
BlockDriverState *bs;
23
39
uint64_t perm = 0;
24
- qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
40
+ uint64_t shared = BLK_PERM_ALL;
25
- qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
41
26
- qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
42
- /* blk_new_open() is mainly used in .bdrv_create implementations and the
27
block_acct_init(&blk->stats);
43
- * tools where sharing isn't a concern because the BDS stays private, so we
28
44
- * just request permission according to the flags.
29
notifier_list_init(&blk->remove_bs_notifiers);
45
+ /*
30
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
46
+ * blk_new_open() is mainly used in .bdrv_create implementations and the
31
index XXXXXXX..XXXXXXX 100644
47
+ * tools where sharing isn't a major concern because the BDS stays private
32
--- a/block/throttle-groups.c
48
+ * and the file is generally not supposed to be used by a second process,
33
+++ b/block/throttle-groups.c
49
+ * so we just request permission according to the flags.
34
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
50
*
35
read_timer_cb,
51
* The exceptions are xen_disk and blockdev_init(); in these cases, the
36
write_timer_cb,
52
* caller of blk_new_open() doesn't make use of the permissions, but they
37
tgm);
53
* shouldn't hurt either. We can still share everything here because the
38
+ qemu_co_mutex_init(&tgm->throttled_reqs_lock);
54
- * guest devices will add their own blockers if they can't share. */
39
+ qemu_co_queue_init(&tgm->throttled_reqs[0]);
55
+ * guest devices will add their own blockers if they can't share.
40
+ qemu_co_queue_init(&tgm->throttled_reqs[1]);
56
+ */
41
57
if ((flags & BDRV_O_NO_IO) == 0) {
42
qemu_mutex_unlock(&tg->lock);
58
perm |= BLK_PERM_CONSISTENT_READ;
43
}
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;
44
--
82
--
45
2.13.5
83
2.30.2
46
84
47
85
diff view generated by jsdifflib
New patch
1
For a successful conversion of an image, we must make sure that its
2
content doesn't change during the conversion.
1
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>
20
Signed-off-by: Kevin Wolf <kwolf@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>
25
---
26
qemu-img.c | 2 +-
27
1 file changed, 1 insertion(+), 1 deletion(-)
28
29
diff --git a/qemu-img.c b/qemu-img.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/qemu-img.c
32
+++ b/qemu-img.c
33
@@ -XXX,XX +XXX,XX @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit)
34
35
static int img_convert(int argc, char **argv)
36
{
37
- int c, bs_i, flags, src_flags = 0;
38
+ int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE;
39
const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe",
40
*src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL,
41
*out_filename, *out_baseimg_param, *snapshot_name = NULL;
42
--
43
2.30.2
44
45
diff view generated by jsdifflib
1
From: Eric Blake <eblake@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
Omitting the check for whether bdrv_getlength() and bdrv_truncate()
4
Simple reproducer:
4
failed meant that it was theoretically possible to return an
5
incorrect offset to the caller. More likely, conditions for either
6
of these functions to fail would also cause one of our other calls
7
(such as bdrv_pread() or bdrv_pwrite_sync()) to also fail, but
8
auditing that we are safe is difficult compared to just patching
9
things to always forward on the error rather than ignoring it.
10
5
11
Use osdep.h macros instead of open-coded rounding while in the
6
qemu-system-x86_64 \
12
area.
7
-chardev null,id=foo \
8
-device vhost-user-blk-pci,queue-size=4096,chardev=foo
13
9
14
Reported-by: Markus Armbruster <armbru@redhat.com>
10
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1935014
15
Signed-off-by: Eric Blake <eblake@redhat.com>
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>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
---
18
block/qcow.c | 30 ++++++++++++++++++++++--------
19
hw/block/vhost-user-blk.c | 5 +++++
19
1 file changed, 22 insertions(+), 8 deletions(-)
20
1 file changed, 5 insertions(+)
20
21
21
diff --git a/block/qcow.c b/block/qcow.c
22
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
22
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
23
--- a/block/qcow.c
24
--- a/hw/block/vhost-user-blk.c
24
+++ b/block/qcow.c
25
+++ b/hw/block/vhost-user-blk.c
25
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
26
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
26
{
27
error_setg(errp, "vhost-user-blk: queue size must be non-zero");
27
BDRVQcowState *s = bs->opaque;
28
return;
28
int min_index, i, j, l1_index, l2_index, ret;
29
}
29
- uint64_t l2_offset, *l2_table, cluster_offset, tmp;
30
+ if (s->queue_size > VIRTQUEUE_MAX_SIZE) {
30
+ int64_t l2_offset;
31
+ error_setg(errp, "vhost-user-blk: queue size must not exceed %d",
31
+ uint64_t *l2_table, cluster_offset, tmp;
32
+ VIRTQUEUE_MAX_SIZE);
32
uint32_t min_count;
33
+ return;
33
int new_l2_table;
34
+ }
34
35
35
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
36
if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
36
return 0;
37
return;
37
/* allocate a new l2 entry */
38
l2_offset = bdrv_getlength(bs->file->bs);
39
+ if (l2_offset < 0) {
40
+ return l2_offset;
41
+ }
42
/* round to cluster size */
43
- l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
44
+ l2_offset = QEMU_ALIGN_UP(l2_offset, s->cluster_size);
45
/* update the L1 entry */
46
s->l1_table[l1_index] = l2_offset;
47
tmp = cpu_to_be64(l2_offset);
48
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
49
return -EIO;
50
}
51
cluster_offset = bdrv_getlength(bs->file->bs);
52
- cluster_offset = (cluster_offset + s->cluster_size - 1) &
53
- ~(s->cluster_size - 1);
54
+ if ((int64_t) cluster_offset < 0) {
55
+ return cluster_offset;
56
+ }
57
+ cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
58
/* write the cluster content */
59
ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache,
60
s->cluster_size);
61
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
62
}
63
} else {
64
cluster_offset = bdrv_getlength(bs->file->bs);
65
+ if ((int64_t) cluster_offset < 0) {
66
+ return cluster_offset;
67
+ }
68
if (allocate == 1) {
69
/* round to cluster size */
70
- cluster_offset = (cluster_offset + s->cluster_size - 1) &
71
- ~(s->cluster_size - 1);
72
- bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
73
- PREALLOC_MODE_OFF, NULL);
74
+ cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
75
+ if (cluster_offset + s->cluster_size > INT64_MAX) {
76
+ return -E2BIG;
77
+ }
78
+ ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
79
+ PREALLOC_MODE_OFF, NULL);
80
+ if (ret < 0) {
81
+ return ret;
82
+ }
83
/* if encrypted, we must initialize the cluster
84
content which won't be written */
85
if (bs->encrypted &&
86
--
38
--
87
2.13.5
39
2.30.2
88
40
89
41
diff view generated by jsdifflib