1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "qemu/osdep.h"
21#include "block/throttle-groups.h"
22#include "qemu/throttle-options.h"
23#include "qapi/error.h"
24
25static QemuOptsList throttle_opts = {
26 .name = "throttle",
27 .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
28 .desc = {
29 {
30 .name = QEMU_OPT_THROTTLE_GROUP_NAME,
31 .type = QEMU_OPT_STRING,
32 .help = "Name of the throttle group",
33 },
34 { }
35 },
36};
37
38
39
40
41
42
43static int throttle_parse_options(QDict *options, char **group, Error **errp)
44{
45 int ret;
46 const char *group_name;
47 Error *local_err = NULL;
48 QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort);
49
50 qemu_opts_absorb_qdict(opts, options, &local_err);
51 if (local_err) {
52 error_propagate(errp, local_err);
53 ret = -EINVAL;
54 goto fin;
55 }
56
57 group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
58 if (!group_name) {
59 error_setg(errp, "Please specify a throttle group");
60 ret = -EINVAL;
61 goto fin;
62 } else if (!throttle_group_exists(group_name)) {
63 error_setg(errp, "Throttle group '%s' does not exist", group_name);
64 ret = -EINVAL;
65 goto fin;
66 }
67
68 *group = g_strdup(group_name);
69 ret = 0;
70fin:
71 qemu_opts_del(opts);
72 return ret;
73}
74
75static int throttle_open(BlockDriverState *bs, QDict *options,
76 int flags, Error **errp)
77{
78 ThrottleGroupMember *tgm = bs->opaque;
79 char *group;
80 int ret;
81
82 bs->file = bdrv_open_child(NULL, options, "file", bs,
83 &child_file, false, errp);
84 if (!bs->file) {
85 return -EINVAL;
86 }
87 bs->supported_write_flags = bs->file->bs->supported_write_flags;
88 bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
89
90 ret = throttle_parse_options(options, &group, errp);
91 if (ret == 0) {
92
93 throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
94 g_free(group);
95 }
96
97 return ret;
98}
99
100static void throttle_close(BlockDriverState *bs)
101{
102 ThrottleGroupMember *tgm = bs->opaque;
103 throttle_group_unregister_tgm(tgm);
104}
105
106
107static int64_t throttle_getlength(BlockDriverState *bs)
108{
109 return bdrv_getlength(bs->file->bs);
110}
111
112static int coroutine_fn throttle_co_preadv(BlockDriverState *bs,
113 uint64_t offset, uint64_t bytes,
114 QEMUIOVector *qiov, int flags)
115{
116
117 ThrottleGroupMember *tgm = bs->opaque;
118 throttle_group_co_io_limits_intercept(tgm, bytes, false);
119
120 return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
121}
122
123static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs,
124 uint64_t offset, uint64_t bytes,
125 QEMUIOVector *qiov, int flags)
126{
127 ThrottleGroupMember *tgm = bs->opaque;
128 throttle_group_co_io_limits_intercept(tgm, bytes, true);
129
130 return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
131}
132
133static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs,
134 int64_t offset, int bytes,
135 BdrvRequestFlags flags)
136{
137 ThrottleGroupMember *tgm = bs->opaque;
138 throttle_group_co_io_limits_intercept(tgm, bytes, true);
139
140 return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
141}
142
143static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
144 int64_t offset, int bytes)
145{
146 ThrottleGroupMember *tgm = bs->opaque;
147 throttle_group_co_io_limits_intercept(tgm, bytes, true);
148
149 return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
150}
151
152static int throttle_co_flush(BlockDriverState *bs)
153{
154 return bdrv_co_flush(bs->file->bs);
155}
156
157static void throttle_detach_aio_context(BlockDriverState *bs)
158{
159 ThrottleGroupMember *tgm = bs->opaque;
160 throttle_group_detach_aio_context(tgm);
161}
162
163static void throttle_attach_aio_context(BlockDriverState *bs,
164 AioContext *new_context)
165{
166 ThrottleGroupMember *tgm = bs->opaque;
167 throttle_group_attach_aio_context(tgm, new_context);
168}
169
170static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
171 BlockReopenQueue *queue, Error **errp)
172{
173 int ret;
174 char *group = NULL;
175
176 assert(reopen_state != NULL);
177 assert(reopen_state->bs != NULL);
178
179 ret = throttle_parse_options(reopen_state->options, &group, errp);
180 reopen_state->opaque = group;
181 return ret;
182}
183
184static void throttle_reopen_commit(BDRVReopenState *reopen_state)
185{
186 BlockDriverState *bs = reopen_state->bs;
187 ThrottleGroupMember *tgm = bs->opaque;
188 char *group = reopen_state->opaque;
189
190 assert(group);
191
192 if (strcmp(group, throttle_group_get_name(tgm))) {
193 throttle_group_unregister_tgm(tgm);
194 throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
195 }
196 g_free(reopen_state->opaque);
197 reopen_state->opaque = NULL;
198}
199
200static void throttle_reopen_abort(BDRVReopenState *reopen_state)
201{
202 g_free(reopen_state->opaque);
203 reopen_state->opaque = NULL;
204}
205
206static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
207 BlockDriverState *candidate)
208{
209 return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
210}
211
212static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
213{
214 ThrottleGroupMember *tgm = bs->opaque;
215 if (atomic_fetch_inc(&tgm->io_limits_disabled) == 0) {
216 throttle_group_restart_tgm(tgm);
217 }
218}
219
220static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
221{
222 ThrottleGroupMember *tgm = bs->opaque;
223 assert(tgm->io_limits_disabled);
224 atomic_dec(&tgm->io_limits_disabled);
225}
226
227static BlockDriver bdrv_throttle = {
228 .format_name = "throttle",
229 .protocol_name = "throttle",
230 .instance_size = sizeof(ThrottleGroupMember),
231
232 .bdrv_file_open = throttle_open,
233 .bdrv_close = throttle_close,
234 .bdrv_co_flush = throttle_co_flush,
235
236 .bdrv_child_perm = bdrv_filter_default_perms,
237
238 .bdrv_getlength = throttle_getlength,
239
240 .bdrv_co_preadv = throttle_co_preadv,
241 .bdrv_co_pwritev = throttle_co_pwritev,
242
243 .bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes,
244 .bdrv_co_pdiscard = throttle_co_pdiscard,
245
246 .bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter,
247
248 .bdrv_attach_aio_context = throttle_attach_aio_context,
249 .bdrv_detach_aio_context = throttle_detach_aio_context,
250
251 .bdrv_reopen_prepare = throttle_reopen_prepare,
252 .bdrv_reopen_commit = throttle_reopen_commit,
253 .bdrv_reopen_abort = throttle_reopen_abort,
254 .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file,
255
256 .bdrv_co_drain_begin = throttle_co_drain_begin,
257 .bdrv_co_drain_end = throttle_co_drain_end,
258
259 .is_filter = true,
260};
261
262static void bdrv_throttle_init(void)
263{
264 bdrv_register(&bdrv_throttle);
265}
266
267block_init(bdrv_throttle_init);
268