1
The following changes since commit 98bfaac788be0ca63d7d010c8d4ba100ff1d8278:
1
The following changes since commit a395717cbd26e7593d3c3fe81faca121ec6d13e8:
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
Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging (2018-07-03 11:49:51 +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 59738025a1674bb7e07713c3c93ff4fb9c5079f5:
10
10
11
qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing (2017-09-06 14:40:18 +0200)
11
block: Add blklogwrites (2018-07-03 16:09:48 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches
14
Block layer patches:
15
16
- qcow2: Use worker threads for compression to improve performance of
17
'qemu-img convert -W' and compressed backup jobs
18
- blklogwrites: New filter driver to log write requests to an image in
19
the dm-log-writes format
15
20
16
----------------------------------------------------------------
21
----------------------------------------------------------------
17
Daniel P. Berrange (1):
22
Aapo Vienamo (1):
18
block: document semantics of bdrv_co_preadv|pwritev
23
block: Add blklogwrites
19
24
20
Eric Blake (2):
25
Ari Sundholm (1):
21
qcow: Change signature of get_cluster_offset()
26
block: Move two block permission constants to the relevant enum
22
qcow: Check failure of bdrv_getlength() and bdrv_truncate()
23
27
24
Manos Pitsidianakis (10):
28
Vladimir Sementsov-Ogievskiy (3):
25
block: pass bdrv_* methods to bs->file by default in block filters
29
qemu-img: allow compressed not-in-order writes
26
block: remove unused bdrv_media_changed
30
qcow2: refactor data compression
27
block: remove bdrv_truncate callback in blkdebug
31
qcow2: add compress threads
28
block: add default implementations for bdrv_co_get_block_status()
29
block: move ThrottleGroup membership to ThrottleGroupMember
30
block: add aio_context field in ThrottleGroupMember
31
block: tidy ThrottleGroupMember initializations
32
block: convert ThrottleGroup to object with QOM
33
block: add throttle block filter driver
34
qemu-iotests: add 184 for throttle filter driver
35
32
36
Pavel Butsykin (1):
33
qapi/block-core.json | 33 ++++-
37
qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing
34
block/qcow2.h | 3 +
35
include/block/block.h | 7 +
36
block.c | 6 -
37
block/blklogwrites.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++
38
block/qcow2.c | 136 ++++++++++++++----
39
qemu-img.c | 5 -
40
MAINTAINERS | 6 +
41
block/Makefile.objs | 1 +
42
9 files changed, 545 insertions(+), 44 deletions(-)
43
create mode 100644 block/blklogwrites.c
38
44
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
Deleted patch
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
2
1
3
The following functions fail if bs->drv is a filter and does not
4
implement them:
5
6
bdrv_probe_blocksizes
7
bdrv_probe_geometry
8
bdrv_truncate
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>
22
---
23
include/block/block_int.h | 6 +++++-
24
block.c | 21 +++++++++++++++++++--
25
2 files changed, 24 insertions(+), 3 deletions(-)
26
27
diff --git a/include/block/block_int.h b/include/block/block_int.h
28
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/block_int.h
30
+++ b/include/block/block_int.h
31
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
32
const char *format_name;
33
int instance_size;
34
35
- /* set to true if the BlockDriver is a block filter */
36
+ /* set to true if the BlockDriver is a block filter. Block filters pass
37
+ * certain callbacks that refer to data (see block.c) to their bs->file if
38
+ * the driver doesn't implement them. Drivers that do not wish to forward
39
+ * must implement them and return -ENOTSUP.
40
+ */
41
bool is_filter;
42
/* for snapshots block filter like Quorum can implement the
43
* following recursive callback.
44
diff --git a/block.c b/block.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block.c
47
+++ b/block.c
48
@@ -XXX,XX +XXX,XX @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
49
50
if (drv && drv->bdrv_probe_blocksizes) {
51
return drv->bdrv_probe_blocksizes(bs, bsz);
52
+ } else if (drv && drv->is_filter && bs->file) {
53
+ return bdrv_probe_blocksizes(bs->file->bs, bsz);
54
}
55
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;
92
@@ -XXX,XX +XXX,XX @@ void bdrv_get_backing_filename(BlockDriverState *bs,
93
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
94
{
95
BlockDriver *drv = bs->drv;
96
- if (!drv)
97
+ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */
98
+ if (!drv) {
99
return -ENOMEDIUM;
100
- if (!drv->bdrv_get_info)
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
--
112
2.13.5
113
114
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
No reason to forbid them, and they are needed to improve performance
4
throttle block filter drive easy creation and configuration of throttle
4
with compress-threads in further patches.
5
groups in QMP and cli.
6
5
7
A new QAPI struct, ThrottleLimits, is introduced to provide a shared
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
51
---
8
---
52
qapi/block-core.json | 48 +++++
9
qemu-img.c | 5 -----
53
include/block/throttle-groups.h | 3 +
10
1 file changed, 5 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
11
61
diff --git a/qapi/block-core.json b/qapi/block-core.json
12
diff --git a/qemu-img.c b/qemu-img.c
62
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
63
--- a/qapi/block-core.json
14
--- a/qemu-img.c
64
+++ b/qapi/block-core.json
15
+++ b/qemu-img.c
65
@@ -XXX,XX +XXX,XX @@
16
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
66
'*iops_size': 'int', '*group': 'str' } }
17
goto fail_getopt;
67
18
}
68
##
19
69
+# @ThrottleLimits:
20
- if (!s.wr_in_order && s.compressed) {
70
+#
21
- error_report("Out of order write and compress are mutually exclusive");
71
+# Limit parameters for throttling.
22
- goto fail_getopt;
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
+
116
+##
117
# @block-stream:
118
#
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
+
131
const char *throttle_group_get_name(ThrottleGroupMember *tgm);
132
133
ThrottleState *throttle_group_incref(const char *name);
134
diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
135
index XXXXXXX..XXXXXXX 100644
136
--- a/include/qemu/throttle-options.h
137
+++ b/include/qemu/throttle-options.h
138
@@ -XXX,XX +XXX,XX @@
139
#ifndef THROTTLE_OPTIONS_H
140
#define THROTTLE_OPTIONS_H
141
142
+#define QEMU_OPT_IOPS_TOTAL "iops-total"
143
+#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max"
144
+#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length"
145
+#define QEMU_OPT_IOPS_READ "iops-read"
146
+#define QEMU_OPT_IOPS_READ_MAX "iops-read-max"
147
+#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length"
148
+#define QEMU_OPT_IOPS_WRITE "iops-write"
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
+
162
+#define THROTTLE_OPT_PREFIX "throttling."
163
#define THROTTLE_OPTS \
164
{ \
165
- .name = "throttling.iops-total",\
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
+
290
+static void throttle_group_obj_init(Object *obj);
291
+static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
292
293
/* The ThrottleGroup structure (with its ThrottleState) is shared
294
* among different ThrottleGroupMembers and it's independent from
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
+ }
335
+
336
+ return NULL;
337
+}
338
+
339
/* Increments the reference count of a ThrottleGroup given its name.
340
*
341
* If no ThrottleGroup is found with the given name a new one is
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
- }
23
- }
364
-
24
-
365
- /* Create a new one if not found */
25
if (tgt_image_opts && !skip_create) {
366
- if (!tg) {
26
error_report("--target-image-opts requires use of -n flag");
367
- tg = g_new0(ThrottleGroup, 1);
27
goto fail_getopt;
368
+ tg = throttle_group_by_name(name);
369
+
370
+ if (tg) {
371
+ object_ref(OBJECT(tg));
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
+
440
+/* Helper struct and array for QOM property setter/getter */
441
+typedef struct {
442
+ const char *name;
443
+ BucketType type;
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
+};
530
+
531
+/* This function edits throttle_groups and must be called under the global
532
+ * mutex */
533
+static void throttle_group_obj_init(Object *obj)
534
+{
535
+ ThrottleGroup *tg = THROTTLE_GROUP(obj);
536
+
537
+ tg->clock_type = QEMU_CLOCK_REALTIME;
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;
566
+ }
567
+
568
+ /* check validity */
569
+ throttle_get_config(&tg->ts, &cfg);
570
+ if (!throttle_is_valid(&cfg, errp)) {
571
+ return;
572
+ }
573
+ throttle_config(&tg->ts, tg->clock_type, &cfg);
574
+ QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
575
+ tg->is_initialized = true;
576
+}
577
+
578
+/* This function edits throttle_groups and must be called under the global
579
+ * mutex */
580
+static void throttle_group_obj_finalize(Object *obj)
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
--
28
--
946
2.13.5
29
2.13.6
947
30
948
31
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
Make a separate function for compression to be parallelized later.
4
may not happen, because the last call qcow2_store_persistent_dirty_bitmaps()
4
- use .avail_out field instead of .next_out to calculate size of
5
can lead to marking l2/refcont cache as dirty.
5
compressed data. It looks more natural and it allows to keep dest to
6
be void pointer
7
- set avail_out to be at least one byte less than input, to be sure
8
avoid inefficient compression earlier
6
9
7
Let's move qcow2_store_persistent_dirty_bitmaps() before the caсhe flushing
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
to fix it.
9
10
Cc: qemu-stable@nongnu.org
11
Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
12
---
14
block/qcow2.c | 16 ++++++++--------
13
block/qcow2.c | 76 ++++++++++++++++++++++++++++++++++++++---------------------
15
1 file changed, 8 insertions(+), 8 deletions(-)
14
1 file changed, 49 insertions(+), 27 deletions(-)
16
15
17
diff --git a/block/qcow2.c b/block/qcow2.c
16
diff --git a/block/qcow2.c b/block/qcow2.c
18
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
19
--- a/block/qcow2.c
18
--- a/block/qcow2.c
20
+++ b/block/qcow2.c
19
+++ b/block/qcow2.c
21
@@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs)
20
@@ -XXX,XX +XXX,XX @@
22
int ret, result = 0;
21
*/
23
Error *local_err = NULL;
22
24
23
#include "qemu/osdep.h"
25
+ qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
24
+
26
+ if (local_err != NULL) {
25
+#define ZLIB_CONST
27
+ result = -EINVAL;
26
+#include <zlib.h>
28
+ error_report_err(local_err);
27
+
29
+ error_report("Persistent bitmaps are lost for node '%s'",
28
#include "block/block_int.h"
30
+ bdrv_get_device_or_node_name(bs));
29
#include "block/qdict.h"
30
#include "sysemu/block-backend.h"
31
#include "qemu/module.h"
32
-#include <zlib.h>
33
#include "qcow2.h"
34
#include "qemu/error-report.h"
35
#include "qapi/error.h"
36
@@ -XXX,XX +XXX,XX @@ fail:
37
return ret;
38
}
39
40
+/*
41
+ * qcow2_compress()
42
+ *
43
+ * @dest - destination buffer, at least of @size-1 bytes
44
+ * @src - source buffer, @size bytes
45
+ *
46
+ * Returns: compressed size on success
47
+ * -1 if compression is inefficient
48
+ * -2 on any other error
49
+ */
50
+static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
51
+{
52
+ ssize_t ret;
53
+ z_stream strm;
54
+
55
+ /* best compression, small window, no zlib header */
56
+ memset(&strm, 0, sizeof(strm));
57
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
58
+ -12, 9, Z_DEFAULT_STRATEGY);
59
+ if (ret != 0) {
60
+ return -2;
31
+ }
61
+ }
32
+
62
+
33
ret = qcow2_cache_flush(bs, s->l2_table_cache);
63
+ strm.avail_in = size;
34
if (ret) {
64
+ strm.next_in = src;
35
result = ret;
65
+ strm.avail_out = size - 1;
36
@@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs)
66
+ strm.next_out = dest;
37
strerror(-ret));
67
+
38
}
68
+ ret = deflate(&strm, Z_FINISH);
39
69
+ if (ret == Z_STREAM_END) {
40
- qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
70
+ ret = size - 1 - strm.avail_out;
41
- if (local_err != NULL) {
71
+ } else {
42
- result = -EINVAL;
72
+ ret = (ret == Z_OK ? -1 : -2);
43
- error_report_err(local_err);
73
+ }
44
- error_report("Persistent bitmaps are lost for node '%s'",
74
+
45
- bdrv_get_device_or_node_name(bs));
75
+ deflateEnd(&strm);
76
+
77
+ return ret;
78
+}
79
+
80
/* XXX: put compressed sectors first, then all the cluster aligned
81
tables to avoid losing bytes in alignment */
82
static coroutine_fn int
83
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
84
BDRVQcow2State *s = bs->opaque;
85
QEMUIOVector hd_qiov;
86
struct iovec iov;
87
- z_stream strm;
88
- int ret, out_len;
89
+ int ret;
90
+ size_t out_len;
91
uint8_t *buf, *out_buf;
92
int64_t cluster_offset;
93
94
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
95
96
out_buf = g_malloc(s->cluster_size);
97
98
- /* best compression, small window, no zlib header */
99
- memset(&strm, 0, sizeof(strm));
100
- ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
101
- Z_DEFLATED, -12,
102
- 9, Z_DEFAULT_STRATEGY);
103
- if (ret != 0) {
104
+ out_len = qcow2_compress(out_buf, buf, s->cluster_size);
105
+ if (out_len == -2) {
106
ret = -EINVAL;
107
goto fail;
46
- }
108
- }
47
-
109
-
48
if (result == 0) {
110
- strm.avail_in = s->cluster_size;
49
qcow2_mark_clean(bs);
111
- strm.next_in = (uint8_t *)buf;
50
}
112
- strm.avail_out = s->cluster_size;
113
- strm.next_out = out_buf;
114
-
115
- ret = deflate(&strm, Z_FINISH);
116
- if (ret != Z_STREAM_END && ret != Z_OK) {
117
- deflateEnd(&strm);
118
- ret = -EINVAL;
119
- goto fail;
120
- }
121
- out_len = strm.next_out - out_buf;
122
-
123
- deflateEnd(&strm);
124
-
125
- if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
126
+ } else if (out_len == -1) {
127
/* could not compress: write normal cluster */
128
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
129
if (ret < 0) {
51
--
130
--
52
2.13.5
131
2.13.6
53
132
54
133
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
Do data compression in separate threads. This significantly improve
4
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
4
performance for qemu-img convert with -W (allow async writes) and -c
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
(compressed) options.
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
9
---
8
tests/qemu-iotests/184 | 205 ++++++++++++++++++++++++++++++
10
block/qcow2.h | 3 +++
9
tests/qemu-iotests/184.out | 302 +++++++++++++++++++++++++++++++++++++++++++++
11
block/qcow2.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
10
tests/qemu-iotests/group | 1 +
12
2 files changed, 64 insertions(+), 1 deletion(-)
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/qcow2.h b/block/qcow2.h
16
new file mode 100755
15
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX
16
--- a/block/qcow2.h
18
--- /dev/null
17
+++ b/block/qcow2.h
19
+++ b/tests/qemu-iotests/184
18
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVQcow2State {
19
* override) */
20
char *image_backing_file;
21
char *image_backing_format;
22
+
23
+ CoQueue compress_wait_queue;
24
+ int nb_compress_threads;
25
} BDRVQcow2State;
26
27
typedef struct Qcow2COWRegion {
28
diff --git a/block/qcow2.c b/block/qcow2.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/block/qcow2.c
31
+++ b/block/qcow2.c
20
@@ -XXX,XX +XXX,XX @@
32
@@ -XXX,XX +XXX,XX @@
21
+#!/bin/bash
33
#include "qapi/qobject-input-visitor.h"
22
+#
34
#include "qapi/qapi-visit-block-core.h"
23
+# Test I/O throttle block filter driver interface
35
#include "crypto.h"
24
+#
36
+#include "block/thread-pool.h"
25
+# Copyright (C) 2017 Manos Pitsidianakis
37
26
+#
38
/*
27
+# This program is free software; you can redistribute it and/or modify
39
Differences with QCOW:
28
+# it under the terms of the GNU General Public License as published by
40
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
29
+# the Free Software Foundation; either version 2 of the License, or
41
qcow2_check_refcounts(bs, &result, 0);
30
+# (at your option) any later version.
42
}
31
+#
43
#endif
32
+# This program is distributed in the hope that it will be useful,
33
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
34
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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
+
44
+
41
+# creator
45
+ qemu_co_queue_init(&s->compress_wait_queue);
42
+owner="Manos Pitsidianakis"
43
+
46
+
44
+seq=`basename $0`
47
return ret;
45
+echo "QA output created by $seq"
48
49
fail:
50
@@ -XXX,XX +XXX,XX @@ static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
51
return ret;
52
}
53
54
+#define MAX_COMPRESS_THREADS 4
46
+
55
+
47
+here=`pwd`
56
+typedef struct Qcow2CompressData {
48
+status=1    # failure is the default!
57
+ void *dest;
58
+ const void *src;
59
+ size_t size;
60
+ ssize_t ret;
61
+} Qcow2CompressData;
49
+
62
+
50
+_cleanup()
63
+static int qcow2_compress_pool_func(void *opaque)
51
+{
64
+{
52
+ _cleanup_test_img
65
+ Qcow2CompressData *data = opaque;
53
+}
54
+trap "_cleanup; exit \$status" 0 1 2 3 15
55
+
66
+
56
+# get standard environment, filters and checks
67
+ data->ret = qcow2_compress(data->dest, data->src, data->size);
57
+. ./common.rc
58
+. ./common.filter
59
+
68
+
60
+_supported_fmt qcow2
69
+ return 0;
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
+}
70
+}
70
+
71
+
71
+function run_qemu()
72
+static void qcow2_compress_complete(void *opaque, int ret)
72
+{
73
+{
73
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\
74
+ qemu_coroutine_enter(opaque);
74
+ | _filter_qemu_io | _filter_generated_node_ids
75
+}
75
+}
76
+
76
+
77
+_make_test_img 64M
77
+/* See qcow2_compress definition for parameters description */
78
+test_throttle=$($QEMU_IMG --help|grep throttle)
78
+static ssize_t qcow2_co_compress(BlockDriverState *bs,
79
+[ "$test_throttle" = "" ] && _supported_fmt throttle
79
+ void *dest, const void *src, size_t size)
80
+{
81
+ BDRVQcow2State *s = bs->opaque;
82
+ BlockAIOCB *acb;
83
+ ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
84
+ Qcow2CompressData arg = {
85
+ .dest = dest,
86
+ .src = src,
87
+ .size = size,
88
+ };
80
+
89
+
81
+echo
90
+ while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) {
82
+echo "== checking interface =="
91
+ qemu_co_queue_wait(&s->compress_wait_queue, NULL);
92
+ }
83
+
93
+
84
+run_qemu <<EOF
94
+ s->nb_compress_threads++;
85
+{ "execute": "qmp_capabilities" }
95
+ acb = thread_pool_submit_aio(pool, qcow2_compress_pool_func, &arg,
86
+{ "execute": "blockdev-add",
96
+ qcow2_compress_complete,
87
+ "arguments": {
97
+ qemu_coroutine_self());
88
+ "driver": "$IMGFMT",
98
+
89
+ "node-name": "disk0",
99
+ if (!acb) {
90
+ "file": {
100
+ s->nb_compress_threads--;
91
+ "driver": "file",
101
+ return -EINVAL;
92
+ "filename": "$TEST_IMG"
93
+ }
102
+ }
94
+ }
103
+ qemu_coroutine_yield();
95
+}
104
+ s->nb_compress_threads--;
96
+{ "execute": "object-add",
105
+ qemu_co_queue_next(&s->compress_wait_queue);
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
+
106
+
120
+echo
107
+ return arg.ret;
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
+ }
375
+}
108
+}
376
+
109
+
377
+
110
/* XXX: put compressed sectors first, then all the cluster aligned
378
+== property changes in ThrottleGroup ==
111
tables to avoid losing bytes in alignment */
379
+Testing:
112
static coroutine_fn int
380
+{
113
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
381
+ QMP_VERSION
114
382
+}
115
out_buf = g_malloc(s->cluster_size);
383
+{
116
384
+ "return": {
117
- out_len = qcow2_compress(out_buf, buf, s->cluster_size);
385
+ }
118
+ out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size);
386
+}
119
if (out_len == -2) {
387
+{
120
ret = -EINVAL;
388
+ "return": {
121
goto fail;
389
+ }
390
+}
391
+{
392
+ "return": {
393
+ "bps-read-max-length": 1,
394
+ "iops-read-max-length": 1,
395
+ "bps-read-max": 0,
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
+ }
454
+}
455
+
456
+
457
+== object creation/set errors ==
458
+Testing:
459
+{
460
+ QMP_VERSION
461
+}
462
+{
463
+ "return": {
464
+ }
465
+}
466
+{
467
+ "return": {
468
+ }
469
+}
470
+{
471
+ "error": {
472
+ "class": "GenericError",
473
+ "desc": "Property cannot be set after initialization"
474
+ }
475
+}
476
+{
477
+ "error": {
478
+ "class": "GenericError",
479
+ "desc": "bps/iops/max total values and read/write values cannot be used at the same time"
480
+ }
481
+}
482
+{
483
+ "return": {
484
+ }
485
+}
486
+{
487
+ "timestamp": {
488
+ "seconds": TIMESTAMP,
489
+ "microseconds": TIMESTAMP
490
+ },
491
+ "event": "SHUTDOWN",
492
+ "data": {
493
+ "guest": false
494
+ }
495
+}
496
+
497
+
498
+== don't specify group ==
499
+Testing:
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
--
122
--
547
2.13.5
123
2.13.6
548
124
549
125
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Ari Sundholm <ari@tuxera.com>
2
2
3
This function is not used anywhere, so remove it.
3
This allows using the two constants outside of block.c, which will
4
happen in a subsequent patch.
4
5
5
Markus Armbruster adds:
6
Signed-off-by: Ari Sundholm <ari@tuxera.com>
6
The i82078 floppy device model used to call bdrv_media_changed() to
7
implement its media change bit when backed by a host floppy. This
8
went away in 21fcf36 "fdc: simplify media change handling".
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>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
8
---
19
include/block/block.h | 1 -
9
include/block/block.h | 7 +++++++
20
include/block/block_int.h | 1 -
10
block.c | 6 ------
21
block.c | 14 --------------
11
2 files changed, 7 insertions(+), 6 deletions(-)
22
block/raw-format.c | 6 ------
23
4 files changed, 22 deletions(-)
24
12
25
diff --git a/include/block/block.h b/include/block/block.h
13
diff --git a/include/block/block.h b/include/block/block.h
26
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/block.h
15
--- a/include/block/block.h
28
+++ b/include/block/block.h
16
+++ b/include/block/block.h
29
@@ -XXX,XX +XXX,XX @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
17
@@ -XXX,XX +XXX,XX @@ enum {
30
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
18
BLK_PERM_GRAPH_MOD = 0x10,
31
bool bdrv_is_sg(BlockDriverState *bs);
19
32
bool bdrv_is_inserted(BlockDriverState *bs);
20
BLK_PERM_ALL = 0x1f,
33
-int bdrv_media_changed(BlockDriverState *bs);
21
+
34
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
22
+ DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ
35
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
23
+ | BLK_PERM_WRITE
36
const char *bdrv_get_format_name(BlockDriverState *bs);
24
+ | BLK_PERM_WRITE_UNCHANGED
37
diff --git a/include/block/block_int.h b/include/block/block_int.h
25
+ | BLK_PERM_RESIZE,
38
index XXXXXXX..XXXXXXX 100644
26
+
39
--- a/include/block/block_int.h
27
+ DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH,
40
+++ b/include/block/block_int.h
28
};
41
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
29
42
30
char *bdrv_perm_names(uint64_t perm);
43
/* removable device specific */
44
bool (*bdrv_is_inserted)(BlockDriverState *bs);
45
- int (*bdrv_media_changed)(BlockDriverState *bs);
46
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
47
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
48
49
diff --git a/block.c b/block.c
31
diff --git a/block.c b/block.c
50
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
51
--- a/block.c
33
--- a/block.c
52
+++ b/block.c
34
+++ b/block.c
53
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_inserted(BlockDriverState *bs)
35
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
36
return 0;
54
}
37
}
55
38
56
/**
39
-#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
57
- * Return whether the media changed since the last call to this
40
- | BLK_PERM_WRITE \
58
- * function, or -ENOTSUP if we don't know. Most drivers don't know.
41
- | BLK_PERM_WRITE_UNCHANGED \
59
- */
42
- | BLK_PERM_RESIZE)
60
-int bdrv_media_changed(BlockDriverState *bs)
43
-#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
61
-{
62
- BlockDriver *drv = bs->drv;
63
-
44
-
64
- if (drv && drv->bdrv_media_changed) {
45
void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
65
- return drv->bdrv_media_changed(bs);
46
const BdrvChildRole *role,
66
- }
47
BlockReopenQueue *reopen_queue,
67
- return -ENOTSUP;
68
-}
69
-
70
-/**
71
* If eject_flag is TRUE, eject the media. Otherwise, close the tray
72
*/
73
void bdrv_eject(BlockDriverState *bs, bool eject_flag)
74
diff --git a/block/raw-format.c b/block/raw-format.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/block/raw-format.c
77
+++ b/block/raw-format.c
78
@@ -XXX,XX +XXX,XX @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
79
return bdrv_truncate(bs->file, offset, prealloc, errp);
80
}
81
82
-static int raw_media_changed(BlockDriverState *bs)
83
-{
84
- return bdrv_media_changed(bs->file->bs);
85
-}
86
-
87
static void raw_eject(BlockDriverState *bs, bool eject_flag)
88
{
89
bdrv_eject(bs->file->bs, eject_flag);
90
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
91
.bdrv_refresh_limits = &raw_refresh_limits,
92
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
93
.bdrv_probe_geometry = &raw_probe_geometry,
94
- .bdrv_media_changed = &raw_media_changed,
95
.bdrv_eject = &raw_eject,
96
.bdrv_lock_medium = &raw_lock_medium,
97
.bdrv_co_ioctl = &raw_co_ioctl,
98
--
48
--
99
2.13.5
49
2.13.6
100
50
101
51
diff view generated by jsdifflib
Deleted patch
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
2
1
3
Now that bdrv_truncate is passed to bs->file by default, remove the
4
callback from block/blkdebug.c and set is_filter to true. is_filter also gives
5
access to other callbacks that are forwarded automatically to bs->file for
6
filters.
7
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block/blkdebug.c | 8 +-------
14
1 file changed, 1 insertion(+), 7 deletions(-)
15
16
diff --git a/block/blkdebug.c b/block/blkdebug.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/blkdebug.c
19
+++ b/block/blkdebug.c
20
@@ -XXX,XX +XXX,XX @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
21
return bdrv_getlength(bs->file->bs);
22
}
23
24
-static int blkdebug_truncate(BlockDriverState *bs, int64_t offset,
25
- PreallocMode prealloc, Error **errp)
26
-{
27
- return bdrv_truncate(bs->file, offset, prealloc, errp);
28
-}
29
-
30
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
31
{
32
BDRVBlkdebugState *s = bs->opaque;
33
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
34
.format_name = "blkdebug",
35
.protocol_name = "blkdebug",
36
.instance_size = sizeof(BDRVBlkdebugState),
37
+ .is_filter = true,
38
39
.bdrv_parse_filename = blkdebug_parse_filename,
40
.bdrv_file_open = blkdebug_open,
41
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
42
.bdrv_child_perm = bdrv_filter_default_perms,
43
44
.bdrv_getlength = blkdebug_getlength,
45
- .bdrv_truncate = blkdebug_truncate,
46
.bdrv_refresh_filename = blkdebug_refresh_filename,
47
.bdrv_refresh_limits = blkdebug_refresh_limits,
48
49
--
50
2.13.5
51
52
diff view generated by jsdifflib
Deleted patch
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
2
1
3
bdrv_co_get_block_status_from_file() and
4
bdrv_co_get_block_status_from_backing() set *file to bs->file and
5
bs->backing respectively, so that bdrv_co_get_block_status() can recurse
6
to them. Future block drivers won't have to duplicate code to implement
7
this.
8
9
Reviewed-by: Fam Zheng <famz@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
include/block/block_int.h | 18 ++++++++++++++++++
17
block/blkdebug.c | 12 +-----------
18
block/commit.c | 12 +-----------
19
block/io.c | 26 ++++++++++++++++++++++++++
20
block/mirror.c | 12 +-----------
21
5 files changed, 47 insertions(+), 33 deletions(-)
22
23
diff --git a/include/block/block_int.h b/include/block/block_int.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/block_int.h
26
+++ b/include/block/block_int.h
27
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
28
uint64_t perm, uint64_t shared,
29
uint64_t *nperm, uint64_t *nshared);
30
31
+/*
32
+ * Default implementation for drivers to pass bdrv_co_get_block_status() to
33
+ * their file.
34
+ */
35
+int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
36
+ int64_t sector_num,
37
+ int nb_sectors,
38
+ int *pnum,
39
+ BlockDriverState **file);
40
+/*
41
+ * Default implementation for drivers to pass bdrv_co_get_block_status() to
42
+ * their backing file.
43
+ */
44
+int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
45
+ int64_t sector_num,
46
+ int nb_sectors,
47
+ int *pnum,
48
+ BlockDriverState **file);
49
const char *bdrv_get_parent_name(const BlockDriverState *bs);
50
void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
51
bool blk_dev_has_removable_media(BlockBackend *blk);
52
diff --git a/block/blkdebug.c b/block/blkdebug.c
53
index XXXXXXX..XXXXXXX 100644
54
--- a/block/blkdebug.c
55
+++ b/block/blkdebug.c
56
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
57
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
58
}
59
60
-static int64_t coroutine_fn blkdebug_co_get_block_status(
61
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
62
- BlockDriverState **file)
63
-{
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
83
index XXXXXXX..XXXXXXX 100644
84
--- a/block/commit.c
85
+++ b/block/commit.c
86
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
87
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
88
}
89
90
-static int64_t coroutine_fn bdrv_commit_top_get_block_status(
91
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
92
- BlockDriverState **file)
93
-{
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
150
index XXXXXXX..XXXXXXX 100644
151
--- a/block/mirror.c
152
+++ b/block/mirror.c
153
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs)
154
return bdrv_co_flush(bs->backing->bs);
155
}
156
157
-static int64_t coroutine_fn bdrv_mirror_top_get_block_status(
158
- BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
159
- BlockDriverState **file)
160
-{
161
- *pnum = nb_sectors;
162
- *file = bs->backing->bs;
163
- return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
164
- (sector_num << BDRV_SECTOR_BITS);
165
-}
166
-
167
static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs,
168
int64_t offset, int bytes, BdrvRequestFlags flags)
169
{
170
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_mirror_top = {
171
.bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes,
172
.bdrv_co_pdiscard = bdrv_mirror_top_pdiscard,
173
.bdrv_co_flush = bdrv_mirror_top_flush,
174
- .bdrv_co_get_block_status = bdrv_mirror_top_get_block_status,
175
+ .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing,
176
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
177
.bdrv_close = bdrv_mirror_top_close,
178
.bdrv_child_perm = bdrv_mirror_top_child_perm,
179
--
180
2.13.5
181
182
diff view generated by jsdifflib
Deleted patch
1
From: Eric Blake <eblake@redhat.com>
2
1
3
The old signature has an ambiguous meaning for a return of 0:
4
either no allocation was requested or necessary, or an error
5
occurred (but any errno associated with the error is lost to
6
the caller, which then has to assume EIO).
7
8
Better is to follow the example of qcow2, by changing the
9
signature to have a separate return value that cleanly
10
distinguishes between failure and success, along with a
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>
21
---
22
block/qcow.c | 123 +++++++++++++++++++++++++++++++++++------------------------
23
1 file changed, 73 insertions(+), 50 deletions(-)
24
25
diff --git a/block/qcow.c b/block/qcow.c
26
index XXXXXXX..XXXXXXX 100644
27
--- a/block/qcow.c
28
+++ b/block/qcow.c
29
@@ -XXX,XX +XXX,XX @@ static int qcow_reopen_prepare(BDRVReopenState *state,
30
* 'compressed_size'. 'compressed_size' must be > 0 and <
31
* cluster_size
32
*
33
- * return 0 if not allocated.
34
+ * return 0 if not allocated, 1 if *result is assigned, and negative
35
+ * errno on failure.
36
*/
37
-static uint64_t get_cluster_offset(BlockDriverState *bs,
38
- uint64_t offset, int allocate,
39
- int compressed_size,
40
- int n_start, int n_end)
41
+static int get_cluster_offset(BlockDriverState *bs,
42
+ uint64_t offset, int allocate,
43
+ int compressed_size,
44
+ int n_start, int n_end, uint64_t *result)
45
{
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
}
162
163
static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
164
int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
165
{
166
BDRVQcowState *s = bs->opaque;
167
- int index_in_cluster, n;
168
+ int index_in_cluster, n, ret;
169
uint64_t cluster_offset;
170
171
qemu_co_mutex_lock(&s->lock);
172
- cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
173
+ ret = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0, &cluster_offset);
174
qemu_co_mutex_unlock(&s->lock);
175
+ if (ret < 0) {
176
+ return ret;
177
+ }
178
index_in_cluster = sector_num & (s->cluster_sectors - 1);
179
n = s->cluster_sectors - index_in_cluster;
180
if (n > nb_sectors)
181
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
182
183
while (nb_sectors != 0) {
184
/* prepare next request */
185
- cluster_offset = get_cluster_offset(bs, sector_num << 9,
186
- 0, 0, 0, 0);
187
+ ret = get_cluster_offset(bs, sector_num << 9,
188
+ 0, 0, 0, 0, &cluster_offset);
189
+ if (ret < 0) {
190
+ break;
191
+ }
192
index_in_cluster = sector_num & (s->cluster_sectors - 1);
193
n = s->cluster_sectors - index_in_cluster;
194
if (n > nb_sectors) {
195
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
196
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
197
qemu_co_mutex_lock(&s->lock);
198
if (ret < 0) {
199
- goto fail;
200
+ break;
201
}
202
} else {
203
/* Note: in this case, no need to wait */
204
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
205
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
206
/* add AIO support for compressed blocks ? */
207
if (decompress_cluster(bs, cluster_offset) < 0) {
208
- goto fail;
209
+ ret = -EIO;
210
+ break;
211
}
212
memcpy(buf,
213
s->cluster_cache + index_in_cluster * 512, 512 * n);
214
} else {
215
if ((cluster_offset & 511) != 0) {
216
- goto fail;
217
+ ret = -EIO;
218
+ break;
219
}
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
}
235
236
-done:
237
qemu_co_mutex_unlock(&s->lock);
238
239
if (qiov->niov > 1) {
240
@@ -XXX,XX +XXX,XX @@ done:
241
}
242
243
return ret;
244
-
245
-fail:
246
- ret = -EIO;
247
- goto done;
248
}
249
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
--
281
2.13.5
282
283
diff view generated by jsdifflib
Deleted patch
1
From: Eric Blake <eblake@redhat.com>
2
1
3
Omitting the check for whether bdrv_getlength() and bdrv_truncate()
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
11
Use osdep.h macros instead of open-coded rounding while in the
12
area.
13
14
Reported-by: Markus Armbruster <armbru@redhat.com>
15
Signed-off-by: Eric Blake <eblake@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
block/qcow.c | 30 ++++++++++++++++++++++--------
19
1 file changed, 22 insertions(+), 8 deletions(-)
20
21
diff --git a/block/qcow.c b/block/qcow.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block/qcow.c
24
+++ b/block/qcow.c
25
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
26
{
27
BDRVQcowState *s = bs->opaque;
28
int min_index, i, j, l1_index, l2_index, ret;
29
- uint64_t l2_offset, *l2_table, cluster_offset, tmp;
30
+ int64_t l2_offset;
31
+ uint64_t *l2_table, cluster_offset, tmp;
32
uint32_t min_count;
33
int new_l2_table;
34
35
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
36
return 0;
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
--
87
2.13.5
88
89
diff view generated by jsdifflib
Deleted patch
1
From: "Daniel P. Berrange" <berrange@redhat.com>
2
1
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Reviewed-by: Eric Blake <eblake@redhat.com>
5
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
include/block/block_int.h | 31 +++++++++++++++++++++++++++++++
9
1 file changed, 31 insertions(+)
10
11
diff --git a/include/block/block_int.h b/include/block/block_int.h
12
index XXXXXXX..XXXXXXX 100644
13
--- a/include/block/block_int.h
14
+++ b/include/block/block_int.h
15
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
16
17
int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs,
18
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
19
+
20
+ /**
21
+ * @offset: position in bytes to read at
22
+ * @bytes: number of bytes to read
23
+ * @qiov: the buffers to fill with read data
24
+ * @flags: currently unused, always 0
25
+ *
26
+ * @offset and @bytes will be a multiple of 'request_alignment',
27
+ * but the length of individual @qiov elements does not have to
28
+ * be a multiple.
29
+ *
30
+ * @bytes will always equal the total size of @qiov, and will be
31
+ * no larger than 'max_transfer'.
32
+ *
33
+ * The buffer in @qiov may point directly to guest memory.
34
+ */
35
int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs,
36
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
37
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
38
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
39
int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs,
40
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags);
41
+ /**
42
+ * @offset: position in bytes to write at
43
+ * @bytes: number of bytes to write
44
+ * @qiov: the buffers containing data to write
45
+ * @flags: zero or more bits allowed by 'supported_write_flags'
46
+ *
47
+ * @offset and @bytes will be a multiple of 'request_alignment',
48
+ * but the length of individual @qiov elements does not have to
49
+ * be a multiple.
50
+ *
51
+ * @bytes will always equal the total size of @qiov, and will be
52
+ * no larger than 'max_transfer'.
53
+ *
54
+ * The buffer in @qiov may point directly to guest memory.
55
+ */
56
int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs,
57
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
58
59
--
60
2.13.5
61
62
diff view generated by jsdifflib
Deleted patch
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
2
1
3
This commit eliminates the 1:1 relationship between BlockBackend and
4
throttle group state. Users will be able to create multiple throttle
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
11
Reviewed-by: Alberto Garcia <berto@igalia.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
include/block/throttle-groups.h | 39 +++++-
17
include/sysemu/block-backend.h | 20 +--
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
25
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
26
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/throttle-groups.h
28
+++ b/include/block/throttle-groups.h
29
@@ -XXX,XX +XXX,XX @@
30
#include "qemu/throttle.h"
31
#include "block/block_int.h"
32
33
-const char *throttle_group_get_name(BlockBackend *blk);
34
+/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup
35
+ * and holds related data.
36
+ */
37
+
38
+typedef struct ThrottleGroupMember {
39
+ /* throttled_reqs_lock protects the CoQueues for throttled requests. */
40
+ CoMutex throttled_reqs_lock;
41
+ CoQueue throttled_reqs[2];
42
+
43
+ /* Nonzero if the I/O limits are currently being ignored; generally
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
}
138
if (blk->root) {
139
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
140
*/
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
}
152
153
blk_update_root_state(blk);
154
@@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
155
bdrv_ref(bs);
156
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
}
215
}
216
@@ -XXX,XX +XXX,XX @@ int blk_commit_all(void)
217
/* throttling disk I/O limits */
218
void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg)
219
{
220
- throttle_group_config(blk, cfg);
221
+ throttle_group_config(&blk->public.throttle_group_member, cfg);
222
}
223
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
}
267
}
268
269
@@ -XXX,XX +XXX,XX @@ static void blk_root_drained_end(BdrvChild *child)
270
BlockBackend *blk = child->opaque;
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
}
357
358
-/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer)
359
+/* Get the name from a ThrottleGroupMember's group. The name (and the pointer)
360
* is guaranteed to remain constant during the lifetime of the group.
361
*
362
- * @blk: a BlockBackend that is member of a throttling group
363
+ * @tgm: a ThrottleGroupMember
364
* @ret: the name of the group.
365
*/
366
-const char *throttle_group_get_name(BlockBackend *blk)
367
+const char *throttle_group_get_name(ThrottleGroupMember *tgm)
368
{
369
- BlockBackendPublic *blkp = blk_get_public(blk);
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
--
1004
2.13.5
1005
1006
diff view generated by jsdifflib
Deleted patch
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
2
1
3
timer_cb() needs to know about the current Aio context of the throttle
4
request that is woken up. In order to make ThrottleGroupMember backend
5
agnostic, this information is stored in an aio_context field instead of
6
accessing it from BlockBackend.
7
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
include/block/throttle-groups.h | 7 ++++-
14
block/block-backend.c | 15 ++++------
15
block/throttle-groups.c | 38 ++++++++++++++++---------
16
tests/test-throttle.c | 63 +++++++++++++++++++++--------------------
17
4 files changed, 69 insertions(+), 54 deletions(-)
18
19
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/throttle-groups.h
22
+++ b/include/block/throttle-groups.h
23
@@ -XXX,XX +XXX,XX @@
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
}
74
}
75
76
@@ -XXX,XX +XXX,XX @@ void blk_io_limits_disable(BlockBackend *blk)
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
+{
169
+ ThrottleTimers *tt = &tgm->throttle_timers;
170
+ throttle_timers_attach_aio_context(tt, new_context);
171
+ tgm->aio_context = new_context;
172
+}
173
+
174
+void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
175
+{
176
+ ThrottleTimers *tt = &tgm->throttle_timers;
177
+ throttle_timers_detach_aio_context(tt);
178
+ tgm->aio_context = NULL;
179
+}
180
+
181
static void throttle_groups_init(void)
182
{
183
qemu_mutex_init(&throttle_groups_lock);
184
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
185
index XXXXXXX..XXXXXXX 100644
186
--- a/tests/test-throttle.c
187
+++ b/tests/test-throttle.c
188
@@ -XXX,XX +XXX,XX @@
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
}
229
230
- throttle_timers_destroy(&tt);
231
+ throttle_timers_destroy(tt);
232
}
233
234
static void test_destroy(void)
235
{
236
int i;
237
throttle_init(&ts);
238
- throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
239
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
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
--
358
2.13.5
359
360
diff view generated by jsdifflib
Deleted patch
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
2
1
3
Move the CoMutex and CoQueue inits inside throttle_group_register_tgm()
4
which is called whenever a ThrottleGroupMember is initialized. There's
5
no need for them to be separate.
6
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/block-backend.c | 3 ---
13
block/throttle-groups.c | 3 +++
14
2 files changed, 3 insertions(+), 3 deletions(-)
15
16
diff --git a/block/block-backend.c b/block/block-backend.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/block-backend.c
19
+++ b/block/block-backend.c
20
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
21
blk->shared_perm = shared_perm;
22
blk_set_enable_write_cache(blk, true);
23
24
- qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock);
25
- qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]);
26
- qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]);
27
block_acct_init(&blk->stats);
28
29
notifier_list_init(&blk->remove_bs_notifiers);
30
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/block/throttle-groups.c
33
+++ b/block/throttle-groups.c
34
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
35
read_timer_cb,
36
write_timer_cb,
37
tgm);
38
+ qemu_co_mutex_init(&tgm->throttled_reqs_lock);
39
+ qemu_co_queue_init(&tgm->throttled_reqs[0]);
40
+ qemu_co_queue_init(&tgm->throttled_reqs[1]);
41
42
qemu_mutex_unlock(&tg->lock);
43
}
44
--
45
2.13.5
46
47
diff view generated by jsdifflib
1
From: Manos Pitsidianakis <el13635@mail.ntua.gr>
1
From: Aapo Vienamo <aapo@tuxera.com>
2
2
3
block/throttle.c uses existing I/O throttle infrastructure inside a
3
Implements a block device write logging system, similar to Linux kernel
4
block filter driver. I/O operations are intercepted in the filter's
4
device mapper dm-log-writes. The write operations that are performed
5
read/write coroutines, and referred to block/throttle-groups.c
5
on a block device are logged to a file or another block device. The
6
6
write log format is identical to the dm-log-writes format. Currently,
7
The driver can be used with the syntax
7
log markers are not supported.
8
-drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar
8
9
9
This functionality can be used for crash consistency and fs consistency
10
which registers the throttle filter node with the ThrottleGroup 'bar'. The
10
testing. By implementing it in qemu, tests utilizing write logs can be
11
given group must be created beforehand with object-add or -object.
11
be used to test non-Linux drivers and older kernels.
12
12
13
Reviewed-by: Alberto Garcia <berto@igalia.com>
13
The driver accepts an optional parameter to set the sector size used
14
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
14
for logging. This makes the driver require all requests to be aligned
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
to this sector size and also makes offsets and sizes of writes in the
16
log metadata to be expressed in terms of this value (the log format has
17
a granularity of one sector for offsets and sizes). This allows
18
accurate logging of writes to guest block devices that have unusual
19
sector sizes.
20
21
The implementation is based on the blkverify and blkdebug block
22
drivers.
23
24
Signed-off-by: Aapo Vienamo <aapo@tuxera.com>
25
Signed-off-by: Ari Sundholm <ari@tuxera.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
27
---
18
qapi/block-core.json | 18 ++-
28
qapi/block-core.json | 33 ++++-
19
include/block/throttle-groups.h | 5 +
29
block/blklogwrites.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++
20
include/qemu/throttle-options.h | 1 +
30
MAINTAINERS | 6 +
21
block/throttle-groups.c | 15 ++-
31
block/Makefile.objs | 1 +
22
block/throttle.c | 237 ++++++++++++++++++++++++++++++++++++++++
32
4 files changed, 426 insertions(+), 6 deletions(-)
23
block/Makefile.objs | 1 +
33
create mode 100644 block/blklogwrites.c
24
6 files changed, 275 insertions(+), 2 deletions(-)
25
create mode 100644 block/throttle.c
26
34
27
diff --git a/qapi/block-core.json b/qapi/block-core.json
35
diff --git a/qapi/block-core.json b/qapi/block-core.json
28
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
29
--- a/qapi/block-core.json
37
--- a/qapi/block-core.json
30
+++ b/qapi/block-core.json
38
+++ b/qapi/block-core.json
31
@@ -XXX,XX +XXX,XX @@
39
@@ -XXX,XX +XXX,XX @@
32
# Drivers that are supported in block device operations.
40
# @throttle: Since 2.11
33
#
41
# @nvme: Since 2.12
34
# @vxhs: Since 2.10
42
# @copy-on-read: Since 3.0
35
+# @throttle: Since 2.11
43
+# @blklogwrites: Since 3.0
36
#
44
#
37
# Since: 2.9
45
# Since: 2.9
38
##
46
##
39
@@ -XXX,XX +XXX,XX @@
47
{ 'enum': 'BlockdevDriver',
40
'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
48
- 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', 'copy-on-read',
41
'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
49
- 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
42
'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
50
- 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
43
- 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
51
- 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed',
44
+ 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
52
- 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
53
- 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
54
+ 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop',
55
+ 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
56
+ 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
57
+ 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
58
+ 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
59
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
45
60
46
##
61
##
47
# @BlockdevOptionsFile:
62
# @BlockdevOptionsFile:
48
@@ -XXX,XX +XXX,XX @@
63
@@ -XXX,XX +XXX,XX @@
49
'*tls-creds': 'str' } }
64
'*set-state': ['BlkdebugSetStateOptions'] } }
50
65
51
##
66
##
52
+# @BlockdevOptionsThrottle:
67
+# @BlockdevOptionsBlklogwrites:
53
+#
68
+#
54
+# Driver specific block device options for the throttle driver
69
+# Driver specific block device options for blklogwrites.
55
+#
70
+#
56
+# @throttle-group: the name of the throttle-group object to use. It
71
+# @file: block device
57
+# must already exist.
72
+#
58
+# @file: reference to or definition of the data source block device
73
+# @log: block device used to log writes to @file
59
+# Since: 2.11
74
+#
75
+# @log-sector-size: sector size used in logging writes to @file, determines
76
+# granularity of offsets and sizes of writes (default: 512)
77
+#
78
+# Since: 3.0
60
+##
79
+##
61
+{ 'struct': 'BlockdevOptionsThrottle',
80
+{ 'struct': 'BlockdevOptionsBlklogwrites',
62
+ 'data': { 'throttle-group': 'str',
81
+ 'data': { 'file': 'BlockdevRef',
63
+ 'file' : 'BlockdevRef'
82
+ 'log': 'BlockdevRef',
64
+ } }
83
+ '*log-sector-size': 'uint32' } }
84
+
65
+##
85
+##
66
# @BlockdevOptions:
86
# @BlockdevOptionsBlkverify:
67
#
87
#
68
# Options for creating a block device. Many options are available for all
88
# Driver specific block device options for blkverify.
69
@@ -XXX,XX +XXX,XX @@
89
@@ -XXX,XX +XXX,XX @@
70
'replication':'BlockdevOptionsReplication',
90
'discriminator': 'driver',
71
'sheepdog': 'BlockdevOptionsSheepdog',
91
'data': {
72
'ssh': 'BlockdevOptionsSsh',
92
'blkdebug': 'BlockdevOptionsBlkdebug',
73
+ 'throttle': 'BlockdevOptionsThrottle',
93
+ 'blklogwrites':'BlockdevOptionsBlklogwrites',
74
'vdi': 'BlockdevOptionsGenericFormat',
94
'blkverify': 'BlockdevOptionsBlkverify',
75
'vhdx': 'BlockdevOptionsGenericFormat',
95
'bochs': 'BlockdevOptionsGenericFormat',
76
'vmdk': 'BlockdevOptionsGenericCOWFormat',
96
'cloop': 'BlockdevOptionsGenericFormat',
77
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
97
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
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
98
new file mode 100644
146
index XXXXXXX..XXXXXXX
99
index XXXXXXX..XXXXXXX
147
--- /dev/null
100
--- /dev/null
148
+++ b/block/throttle.c
101
+++ b/block/blklogwrites.c
149
@@ -XXX,XX +XXX,XX @@
102
@@ -XXX,XX +XXX,XX @@
150
+/*
103
+/*
151
+ * QEMU block throttling filter driver infrastructure
104
+ * Write logging blk driver based on blkverify and blkdebug.
152
+ *
105
+ *
153
+ * Copyright (c) 2017 Manos Pitsidianakis
106
+ * Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
107
+ * Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
108
+ * Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
154
+ *
109
+ *
155
+ * This program is free software; you can redistribute it and/or
110
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
156
+ * modify it under the terms of the GNU General Public License as
111
+ * See the COPYING file in the top-level directory.
157
+ * published by the Free Software Foundation; either version 2 or
158
+ * (at your option) version 3 of the License.
159
+ *
160
+ * This program is distributed in the hope that it will be useful,
161
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
162
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
163
+ * GNU General Public License for more details.
164
+ *
165
+ * 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/>.
167
+ */
112
+ */
168
+
113
+
169
+#include "qemu/osdep.h"
114
+#include "qemu/osdep.h"
170
+#include "block/throttle-groups.h"
171
+#include "qemu/throttle-options.h"
172
+#include "qapi/error.h"
115
+#include "qapi/error.h"
173
+
116
+#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
174
+static QemuOptsList throttle_opts = {
117
+#include "block/block_int.h"
175
+ .name = "throttle",
118
+#include "qapi/qmp/qdict.h"
176
+ .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
119
+#include "qapi/qmp/qstring.h"
177
+ .desc = {
120
+#include "qemu/cutils.h"
178
+ {
121
+#include "qemu/option.h"
179
+ .name = QEMU_OPT_THROTTLE_GROUP_NAME,
122
+
180
+ .type = QEMU_OPT_STRING,
123
+/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
181
+ .help = "Name of the throttle group",
124
+
125
+#define LOG_FLUSH_FLAG (1 << 0)
126
+#define LOG_FUA_FLAG (1 << 1)
127
+#define LOG_DISCARD_FLAG (1 << 2)
128
+#define LOG_MARK_FLAG (1 << 3)
129
+
130
+#define WRITE_LOG_VERSION 1ULL
131
+#define WRITE_LOG_MAGIC 0x6a736677736872ULL
132
+
133
+/* All fields are little-endian. */
134
+struct log_write_super {
135
+ uint64_t magic;
136
+ uint64_t version;
137
+ uint64_t nr_entries;
138
+ uint32_t sectorsize;
139
+} QEMU_PACKED;
140
+
141
+struct log_write_entry {
142
+ uint64_t sector;
143
+ uint64_t nr_sectors;
144
+ uint64_t flags;
145
+ uint64_t data_len;
146
+} QEMU_PACKED;
147
+
148
+/* End of disk format structures. */
149
+
150
+typedef struct {
151
+ BdrvChild *log_file;
152
+ uint32_t sectorsize;
153
+ uint32_t sectorbits;
154
+ uint64_t cur_log_sector;
155
+ uint64_t nr_entries;
156
+} BDRVBlkLogWritesState;
157
+
158
+static inline uint32_t blk_log_writes_log2(uint32_t value)
159
+{
160
+ assert(value > 0);
161
+ return 31 - clz32(value);
162
+}
163
+
164
+static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
165
+ Error **errp)
166
+{
167
+ BDRVBlkLogWritesState *s = bs->opaque;
168
+ Error *local_err = NULL;
169
+ int ret;
170
+ int64_t log_sector_size = BDRV_SECTOR_SIZE;
171
+
172
+ /* Open the file */
173
+ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
174
+ &local_err);
175
+ if (local_err) {
176
+ ret = -EINVAL;
177
+ error_propagate(errp, local_err);
178
+ goto fail;
179
+ }
180
+
181
+ if (qdict_haskey(options, "log-sector-size")) {
182
+ log_sector_size = qdict_get_int(options, "log-sector-size");
183
+ qdict_del(options, "log-sector-size");
184
+ }
185
+
186
+ if (log_sector_size < 0 || log_sector_size >= (1ull << 32) ||
187
+ !is_power_of_2(log_sector_size))
188
+ {
189
+ ret = -EINVAL;
190
+ error_setg(errp, "Invalid log sector size %"PRId64, log_sector_size);
191
+ goto fail;
192
+ }
193
+
194
+ s->sectorsize = log_sector_size;
195
+ s->sectorbits = blk_log_writes_log2(log_sector_size);
196
+ s->cur_log_sector = 1;
197
+ s->nr_entries = 0;
198
+
199
+ /* Open the log file */
200
+ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
201
+ &local_err);
202
+ if (local_err) {
203
+ ret = -EINVAL;
204
+ error_propagate(errp, local_err);
205
+ goto fail;
206
+ }
207
+
208
+ ret = 0;
209
+fail:
210
+ if (ret < 0) {
211
+ bdrv_unref_child(bs, bs->file);
212
+ bs->file = NULL;
213
+ }
214
+ return ret;
215
+}
216
+
217
+static void blk_log_writes_close(BlockDriverState *bs)
218
+{
219
+ BDRVBlkLogWritesState *s = bs->opaque;
220
+
221
+ bdrv_unref_child(bs, s->log_file);
222
+ s->log_file = NULL;
223
+}
224
+
225
+static int64_t blk_log_writes_getlength(BlockDriverState *bs)
226
+{
227
+ return bdrv_getlength(bs->file->bs);
228
+}
229
+
230
+static void blk_log_writes_refresh_filename(BlockDriverState *bs,
231
+ QDict *options)
232
+{
233
+ BDRVBlkLogWritesState *s = bs->opaque;
234
+
235
+ /* bs->file->bs has already been refreshed */
236
+ bdrv_refresh_filename(s->log_file->bs);
237
+
238
+ if (bs->file->bs->full_open_options
239
+ && s->log_file->bs->full_open_options)
240
+ {
241
+ QDict *opts = qdict_new();
242
+ qdict_put_str(opts, "driver", "blklogwrites");
243
+
244
+ qobject_ref(bs->file->bs->full_open_options);
245
+ qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
246
+ qobject_ref(s->log_file->bs->full_open_options);
247
+ qdict_put_obj(opts, "log",
248
+ QOBJECT(s->log_file->bs->full_open_options));
249
+
250
+ bs->full_open_options = opts;
251
+ }
252
+}
253
+
254
+static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
255
+ const BdrvChildRole *role,
256
+ BlockReopenQueue *ro_q,
257
+ uint64_t perm, uint64_t shrd,
258
+ uint64_t *nperm, uint64_t *nshrd)
259
+{
260
+ if (!c) {
261
+ *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
262
+ *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
263
+ return;
264
+ }
265
+
266
+ if (!strcmp(c->name, "log")) {
267
+ bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
268
+ } else {
269
+ bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
270
+ }
271
+}
272
+
273
+static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
274
+{
275
+ BDRVBlkLogWritesState *s = bs->opaque;
276
+ bs->bl.request_alignment = s->sectorsize;
277
+}
278
+
279
+static int coroutine_fn
280
+blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
281
+ QEMUIOVector *qiov, int flags)
282
+{
283
+ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
284
+}
285
+
286
+typedef struct BlkLogWritesFileReq {
287
+ BlockDriverState *bs;
288
+ uint64_t offset;
289
+ uint64_t bytes;
290
+ int file_flags;
291
+ QEMUIOVector *qiov;
292
+ int (*func)(struct BlkLogWritesFileReq *r);
293
+ int file_ret;
294
+} BlkLogWritesFileReq;
295
+
296
+typedef struct {
297
+ BlockDriverState *bs;
298
+ QEMUIOVector *qiov;
299
+ struct log_write_entry entry;
300
+ uint64_t zero_size;
301
+ int log_ret;
302
+} BlkLogWritesLogReq;
303
+
304
+static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
305
+{
306
+ BDRVBlkLogWritesState *s = lr->bs->opaque;
307
+ uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
308
+
309
+ s->nr_entries++;
310
+ s->cur_log_sector +=
311
+ ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
312
+
313
+ lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
314
+ lr->qiov, 0);
315
+
316
+ /* Logging for the "write zeroes" operation */
317
+ if (lr->log_ret == 0 && lr->zero_size) {
318
+ cur_log_offset = s->cur_log_sector << s->sectorbits;
319
+ s->cur_log_sector +=
320
+ ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
321
+
322
+ lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
323
+ lr->zero_size, 0);
324
+ }
325
+
326
+ /* Update super block on flush */
327
+ if (lr->log_ret == 0 && lr->entry.flags & LOG_FLUSH_FLAG) {
328
+ struct log_write_super super = {
329
+ .magic = cpu_to_le64(WRITE_LOG_MAGIC),
330
+ .version = cpu_to_le64(WRITE_LOG_VERSION),
331
+ .nr_entries = cpu_to_le64(s->nr_entries),
332
+ .sectorsize = cpu_to_le32(s->sectorsize),
333
+ };
334
+ void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
335
+ QEMUIOVector qiov;
336
+
337
+ qemu_iovec_init(&qiov, 2);
338
+ qemu_iovec_add(&qiov, &super, sizeof(super));
339
+ qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
340
+
341
+ lr->log_ret =
342
+ bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
343
+ if (lr->log_ret == 0) {
344
+ lr->log_ret = bdrv_co_flush(s->log_file->bs);
345
+ }
346
+ qemu_iovec_destroy(&qiov);
347
+ g_free(zeroes);
348
+ }
349
+}
350
+
351
+static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
352
+{
353
+ fr->file_ret = fr->func(fr);
354
+}
355
+
356
+static int coroutine_fn
357
+blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
358
+ QEMUIOVector *qiov, int flags,
359
+ int (*file_func)(BlkLogWritesFileReq *r),
360
+ uint64_t entry_flags, bool is_zero_write)
361
+{
362
+ QEMUIOVector log_qiov;
363
+ size_t niov = qiov ? qiov->niov : 0;
364
+ BDRVBlkLogWritesState *s = bs->opaque;
365
+ BlkLogWritesFileReq fr = {
366
+ .bs = bs,
367
+ .offset = offset,
368
+ .bytes = bytes,
369
+ .file_flags = flags,
370
+ .qiov = qiov,
371
+ .func = file_func,
372
+ };
373
+ BlkLogWritesLogReq lr = {
374
+ .bs = bs,
375
+ .qiov = &log_qiov,
376
+ .entry = {
377
+ .sector = cpu_to_le64(offset >> s->sectorbits),
378
+ .nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
379
+ .flags = cpu_to_le64(entry_flags),
380
+ .data_len = 0,
182
+ },
381
+ },
183
+ { /* end of list */ }
382
+ .zero_size = is_zero_write ? bytes : 0,
184
+ },
383
+ };
384
+ void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
385
+
386
+ assert((1 << s->sectorbits) == s->sectorsize);
387
+ assert(bs->bl.request_alignment == s->sectorsize);
388
+ assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
389
+ assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
390
+
391
+ qemu_iovec_init(&log_qiov, niov + 2);
392
+ qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
393
+ qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
394
+ if (qiov) {
395
+ qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
396
+ }
397
+
398
+ blk_log_writes_co_do_file(&fr);
399
+ blk_log_writes_co_do_log(&lr);
400
+
401
+ qemu_iovec_destroy(&log_qiov);
402
+ g_free(zeroes);
403
+
404
+ if (lr.log_ret < 0) {
405
+ return lr.log_ret;
406
+ }
407
+
408
+ return fr.file_ret;
409
+}
410
+
411
+static int coroutine_fn
412
+blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
413
+{
414
+ return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
415
+ fr->qiov, fr->file_flags);
416
+}
417
+
418
+static int coroutine_fn
419
+blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
420
+{
421
+ return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
422
+ fr->file_flags);
423
+}
424
+
425
+static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
426
+{
427
+ return bdrv_co_flush(fr->bs->file->bs);
428
+}
429
+
430
+static int coroutine_fn
431
+blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
432
+{
433
+ return bdrv_co_pdiscard(fr->bs->file->bs, fr->offset, fr->bytes);
434
+}
435
+
436
+static int coroutine_fn
437
+blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
438
+ QEMUIOVector *qiov, int flags)
439
+{
440
+ return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
441
+ blk_log_writes_co_do_file_pwritev, 0, false);
442
+}
443
+
444
+static int coroutine_fn
445
+blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
446
+ BdrvRequestFlags flags)
447
+{
448
+ return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
449
+ blk_log_writes_co_do_file_pwrite_zeroes, 0,
450
+ true);
451
+}
452
+
453
+static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
454
+{
455
+ return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
456
+ blk_log_writes_co_do_file_flush,
457
+ LOG_FLUSH_FLAG, false);
458
+}
459
+
460
+static int coroutine_fn
461
+blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
462
+{
463
+ return blk_log_writes_co_log(bs, offset, count, NULL, 0,
464
+ blk_log_writes_co_do_file_pdiscard,
465
+ LOG_DISCARD_FLAG, false);
466
+}
467
+
468
+static BlockDriver bdrv_blk_log_writes = {
469
+ .format_name = "blklogwrites",
470
+ .instance_size = sizeof(BDRVBlkLogWritesState),
471
+
472
+ .bdrv_open = blk_log_writes_open,
473
+ .bdrv_close = blk_log_writes_close,
474
+ .bdrv_getlength = blk_log_writes_getlength,
475
+ .bdrv_refresh_filename = blk_log_writes_refresh_filename,
476
+ .bdrv_child_perm = blk_log_writes_child_perm,
477
+ .bdrv_refresh_limits = blk_log_writes_refresh_limits,
478
+
479
+ .bdrv_co_preadv = blk_log_writes_co_preadv,
480
+ .bdrv_co_pwritev = blk_log_writes_co_pwritev,
481
+ .bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
482
+ .bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
483
+ .bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
484
+ .bdrv_co_block_status = bdrv_co_block_status_from_file,
485
+
486
+ .is_filter = true,
185
+};
487
+};
186
+
488
+
187
+static int throttle_configure_tgm(BlockDriverState *bs,
489
+static void bdrv_blk_log_writes_init(void)
188
+ ThrottleGroupMember *tgm,
490
+{
189
+ QDict *options, Error **errp)
491
+ bdrv_register(&bdrv_blk_log_writes);
190
+{
492
+}
191
+ int ret;
493
+
192
+ const char *group_name;
494
+block_init(bdrv_blk_log_writes_init);
193
+ Error *local_err = NULL;
495
diff --git a/MAINTAINERS b/MAINTAINERS
194
+ QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort);
496
index XXXXXXX..XXXXXXX 100644
195
+
497
--- a/MAINTAINERS
196
+ qemu_opts_absorb_qdict(opts, options, &local_err);
498
+++ b/MAINTAINERS
197
+ if (local_err) {
499
@@ -XXX,XX +XXX,XX @@ S: Supported
198
+ error_propagate(errp, local_err);
500
F: block/quorum.c
199
+ ret = -EINVAL;
501
L: qemu-block@nongnu.org
200
+ goto fin;
502
201
+ }
503
+blklogwrites
202
+
504
+M: Ari Sundholm <ari@tuxera.com>
203
+ group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
505
+L: qemu-block@nongnu.org
204
+ if (!group_name) {
506
+S: Supported
205
+ error_setg(errp, "Please specify a throttle group");
507
+F: block/blklogwrites.c
206
+ ret = -EINVAL;
508
+
207
+ goto fin;
509
blkverify
208
+ } else if (!throttle_group_exists(group_name)) {
510
M: Stefan Hajnoczi <stefanha@redhat.com>
209
+ error_setg(errp, "Throttle group '%s' does not exist", group_name);
511
L: qemu-block@nongnu.org
210
+ ret = -EINVAL;
211
+ goto fin;
212
+ }
213
+
214
+ /* Register membership to group with name group_name */
215
+ throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
216
+ ret = 0;
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
512
diff --git a/block/Makefile.objs b/block/Makefile.objs
388
index XXXXXXX..XXXXXXX 100644
513
index XXXXXXX..XXXXXXX 100644
389
--- a/block/Makefile.objs
514
--- a/block/Makefile.objs
390
+++ b/block/Makefile.objs
515
+++ b/block/Makefile.objs
391
@@ -XXX,XX +XXX,XX @@ block-obj-y += accounting.o dirty-bitmap.o
516
@@ -XXX,XX +XXX,XX @@ block-obj-y += qed-check.o
392
block-obj-y += write-threshold.o
517
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
393
block-obj-y += backup.o
518
block-obj-y += quorum.o
394
block-obj-$(CONFIG_REPLICATION) += replication.o
519
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
395
+block-obj-y += throttle.o
520
+block-obj-y += blklogwrites.o
396
521
block-obj-y += block-backend.o snapshot.o qapi.o
397
block-obj-y += crypto.o
522
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
398
523
block-obj-$(CONFIG_POSIX) += file-posix.o
399
--
524
--
400
2.13.5
525
2.13.6
401
526
402
527
diff view generated by jsdifflib