xref: /openbmc/qemu/block/throttle.c (revision 3e4d0e72b77b70578d5530af588d6f0484e18325)
1d8e7d87eSManos Pitsidianakis /*
2d8e7d87eSManos Pitsidianakis  * QEMU block throttling filter driver infrastructure
3d8e7d87eSManos Pitsidianakis  *
4d8e7d87eSManos Pitsidianakis  * Copyright (c) 2017 Manos Pitsidianakis
5d8e7d87eSManos Pitsidianakis  *
6d8e7d87eSManos Pitsidianakis  * This program is free software; you can redistribute it and/or
7d8e7d87eSManos Pitsidianakis  * modify it under the terms of the GNU General Public License as
8d8e7d87eSManos Pitsidianakis  * published by the Free Software Foundation; either version 2 or
9d8e7d87eSManos Pitsidianakis  * (at your option) version 3 of the License.
10d8e7d87eSManos Pitsidianakis  *
11d8e7d87eSManos Pitsidianakis  * This program is distributed in the hope that it will be useful,
12d8e7d87eSManos Pitsidianakis  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13d8e7d87eSManos Pitsidianakis  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14d8e7d87eSManos Pitsidianakis  * GNU General Public License for more details.
15d8e7d87eSManos Pitsidianakis  *
16d8e7d87eSManos Pitsidianakis  * You should have received a copy of the GNU General Public License
17d8e7d87eSManos Pitsidianakis  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18d8e7d87eSManos Pitsidianakis  */
19d8e7d87eSManos Pitsidianakis 
20d8e7d87eSManos Pitsidianakis #include "qemu/osdep.h"
21d8e7d87eSManos Pitsidianakis #include "block/throttle-groups.h"
22922a01a0SMarkus Armbruster #include "qemu/option.h"
23d8e7d87eSManos Pitsidianakis #include "qemu/throttle-options.h"
24d8e7d87eSManos Pitsidianakis #include "qapi/error.h"
25d8e7d87eSManos Pitsidianakis 
26d8e7d87eSManos Pitsidianakis static QemuOptsList throttle_opts = {
27d8e7d87eSManos Pitsidianakis     .name = "throttle",
28d8e7d87eSManos Pitsidianakis     .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head),
29d8e7d87eSManos Pitsidianakis     .desc = {
30d8e7d87eSManos Pitsidianakis         {
31d8e7d87eSManos Pitsidianakis             .name = QEMU_OPT_THROTTLE_GROUP_NAME,
32d8e7d87eSManos Pitsidianakis             .type = QEMU_OPT_STRING,
33d8e7d87eSManos Pitsidianakis             .help = "Name of the throttle group",
34d8e7d87eSManos Pitsidianakis         },
35d8e7d87eSManos Pitsidianakis         { /* end of list */ }
36d8e7d87eSManos Pitsidianakis     },
37d8e7d87eSManos Pitsidianakis };
38d8e7d87eSManos Pitsidianakis 
39d8e7d87eSManos Pitsidianakis static int throttle_configure_tgm(BlockDriverState *bs,
40d8e7d87eSManos Pitsidianakis                                   ThrottleGroupMember *tgm,
41d8e7d87eSManos Pitsidianakis                                   QDict *options, Error **errp)
42d8e7d87eSManos Pitsidianakis {
43d8e7d87eSManos Pitsidianakis     int ret;
44d8e7d87eSManos Pitsidianakis     const char *group_name;
45d8e7d87eSManos Pitsidianakis     Error *local_err = NULL;
46d8e7d87eSManos Pitsidianakis     QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort);
47d8e7d87eSManos Pitsidianakis 
48d8e7d87eSManos Pitsidianakis     qemu_opts_absorb_qdict(opts, options, &local_err);
49d8e7d87eSManos Pitsidianakis     if (local_err) {
50d8e7d87eSManos Pitsidianakis         error_propagate(errp, local_err);
51d8e7d87eSManos Pitsidianakis         ret = -EINVAL;
52d8e7d87eSManos Pitsidianakis         goto fin;
53d8e7d87eSManos Pitsidianakis     }
54d8e7d87eSManos Pitsidianakis 
55d8e7d87eSManos Pitsidianakis     group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME);
56d8e7d87eSManos Pitsidianakis     if (!group_name) {
57d8e7d87eSManos Pitsidianakis         error_setg(errp, "Please specify a throttle group");
58d8e7d87eSManos Pitsidianakis         ret = -EINVAL;
59d8e7d87eSManos Pitsidianakis         goto fin;
60d8e7d87eSManos Pitsidianakis     } else if (!throttle_group_exists(group_name)) {
61d8e7d87eSManos Pitsidianakis         error_setg(errp, "Throttle group '%s' does not exist", group_name);
62d8e7d87eSManos Pitsidianakis         ret = -EINVAL;
63d8e7d87eSManos Pitsidianakis         goto fin;
64d8e7d87eSManos Pitsidianakis     }
65d8e7d87eSManos Pitsidianakis 
66d8e7d87eSManos Pitsidianakis     /* Register membership to group with name group_name */
67d8e7d87eSManos Pitsidianakis     throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
68d8e7d87eSManos Pitsidianakis     ret = 0;
69d8e7d87eSManos Pitsidianakis fin:
70d8e7d87eSManos Pitsidianakis     qemu_opts_del(opts);
71d8e7d87eSManos Pitsidianakis     return ret;
72d8e7d87eSManos Pitsidianakis }
73d8e7d87eSManos Pitsidianakis 
74d8e7d87eSManos Pitsidianakis static int throttle_open(BlockDriverState *bs, QDict *options,
75d8e7d87eSManos Pitsidianakis                          int flags, Error **errp)
76d8e7d87eSManos Pitsidianakis {
77d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
78d8e7d87eSManos Pitsidianakis 
79d8e7d87eSManos Pitsidianakis     bs->file = bdrv_open_child(NULL, options, "file", bs,
80d8e7d87eSManos Pitsidianakis                                &child_file, false, errp);
81d8e7d87eSManos Pitsidianakis     if (!bs->file) {
82d8e7d87eSManos Pitsidianakis         return -EINVAL;
83d8e7d87eSManos Pitsidianakis     }
84d8e7d87eSManos Pitsidianakis     bs->supported_write_flags = bs->file->bs->supported_write_flags;
85d8e7d87eSManos Pitsidianakis     bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
86d8e7d87eSManos Pitsidianakis 
87d8e7d87eSManos Pitsidianakis     return throttle_configure_tgm(bs, tgm, options, errp);
88d8e7d87eSManos Pitsidianakis }
89d8e7d87eSManos Pitsidianakis 
90d8e7d87eSManos Pitsidianakis static void throttle_close(BlockDriverState *bs)
91d8e7d87eSManos Pitsidianakis {
92d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
93d8e7d87eSManos Pitsidianakis     throttle_group_unregister_tgm(tgm);
94d8e7d87eSManos Pitsidianakis }
95d8e7d87eSManos Pitsidianakis 
96d8e7d87eSManos Pitsidianakis 
97d8e7d87eSManos Pitsidianakis static int64_t throttle_getlength(BlockDriverState *bs)
98d8e7d87eSManos Pitsidianakis {
99d8e7d87eSManos Pitsidianakis     return bdrv_getlength(bs->file->bs);
100d8e7d87eSManos Pitsidianakis }
101d8e7d87eSManos Pitsidianakis 
102d8e7d87eSManos Pitsidianakis static int coroutine_fn throttle_co_preadv(BlockDriverState *bs,
103d8e7d87eSManos Pitsidianakis                                            uint64_t offset, uint64_t bytes,
104d8e7d87eSManos Pitsidianakis                                            QEMUIOVector *qiov, int flags)
105d8e7d87eSManos Pitsidianakis {
106d8e7d87eSManos Pitsidianakis 
107d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
108d8e7d87eSManos Pitsidianakis     throttle_group_co_io_limits_intercept(tgm, bytes, false);
109d8e7d87eSManos Pitsidianakis 
110d8e7d87eSManos Pitsidianakis     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
111d8e7d87eSManos Pitsidianakis }
112d8e7d87eSManos Pitsidianakis 
113d8e7d87eSManos Pitsidianakis static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs,
114d8e7d87eSManos Pitsidianakis                                             uint64_t offset, uint64_t bytes,
115d8e7d87eSManos Pitsidianakis                                             QEMUIOVector *qiov, int flags)
116d8e7d87eSManos Pitsidianakis {
117d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
118d8e7d87eSManos Pitsidianakis     throttle_group_co_io_limits_intercept(tgm, bytes, true);
119d8e7d87eSManos Pitsidianakis 
120d8e7d87eSManos Pitsidianakis     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
121d8e7d87eSManos Pitsidianakis }
122d8e7d87eSManos Pitsidianakis 
123d8e7d87eSManos Pitsidianakis static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs,
124d8e7d87eSManos Pitsidianakis                                                   int64_t offset, int bytes,
125d8e7d87eSManos Pitsidianakis                                                   BdrvRequestFlags flags)
126d8e7d87eSManos Pitsidianakis {
127d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
128d8e7d87eSManos Pitsidianakis     throttle_group_co_io_limits_intercept(tgm, bytes, true);
129d8e7d87eSManos Pitsidianakis 
130d8e7d87eSManos Pitsidianakis     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
131d8e7d87eSManos Pitsidianakis }
132d8e7d87eSManos Pitsidianakis 
133d8e7d87eSManos Pitsidianakis static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
134d8e7d87eSManos Pitsidianakis                                              int64_t offset, int bytes)
135d8e7d87eSManos Pitsidianakis {
136d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
137d8e7d87eSManos Pitsidianakis     throttle_group_co_io_limits_intercept(tgm, bytes, true);
138d8e7d87eSManos Pitsidianakis 
139d8e7d87eSManos Pitsidianakis     return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
140d8e7d87eSManos Pitsidianakis }
141d8e7d87eSManos Pitsidianakis 
142d8e7d87eSManos Pitsidianakis static int throttle_co_flush(BlockDriverState *bs)
143d8e7d87eSManos Pitsidianakis {
144d8e7d87eSManos Pitsidianakis     return bdrv_co_flush(bs->file->bs);
145d8e7d87eSManos Pitsidianakis }
146d8e7d87eSManos Pitsidianakis 
147d8e7d87eSManos Pitsidianakis static void throttle_detach_aio_context(BlockDriverState *bs)
148d8e7d87eSManos Pitsidianakis {
149d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
150d8e7d87eSManos Pitsidianakis     throttle_group_detach_aio_context(tgm);
151d8e7d87eSManos Pitsidianakis }
152d8e7d87eSManos Pitsidianakis 
153d8e7d87eSManos Pitsidianakis static void throttle_attach_aio_context(BlockDriverState *bs,
154d8e7d87eSManos Pitsidianakis                                         AioContext *new_context)
155d8e7d87eSManos Pitsidianakis {
156d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
157d8e7d87eSManos Pitsidianakis     throttle_group_attach_aio_context(tgm, new_context);
158d8e7d87eSManos Pitsidianakis }
159d8e7d87eSManos Pitsidianakis 
160d8e7d87eSManos Pitsidianakis static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
161d8e7d87eSManos Pitsidianakis                                    BlockReopenQueue *queue, Error **errp)
162d8e7d87eSManos Pitsidianakis {
163d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm;
164d8e7d87eSManos Pitsidianakis 
165d8e7d87eSManos Pitsidianakis     assert(reopen_state != NULL);
166d8e7d87eSManos Pitsidianakis     assert(reopen_state->bs != NULL);
167d8e7d87eSManos Pitsidianakis 
168d8e7d87eSManos Pitsidianakis     reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
169d8e7d87eSManos Pitsidianakis     tgm = reopen_state->opaque;
170d8e7d87eSManos Pitsidianakis 
171d8e7d87eSManos Pitsidianakis     return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
172d8e7d87eSManos Pitsidianakis             errp);
173d8e7d87eSManos Pitsidianakis }
174d8e7d87eSManos Pitsidianakis 
175d8e7d87eSManos Pitsidianakis static void throttle_reopen_commit(BDRVReopenState *reopen_state)
176d8e7d87eSManos Pitsidianakis {
177d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
178d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *new_tgm = reopen_state->opaque;
179d8e7d87eSManos Pitsidianakis 
180d8e7d87eSManos Pitsidianakis     throttle_group_unregister_tgm(old_tgm);
181d8e7d87eSManos Pitsidianakis     g_free(old_tgm);
182d8e7d87eSManos Pitsidianakis     reopen_state->bs->opaque = new_tgm;
183d8e7d87eSManos Pitsidianakis     reopen_state->opaque = NULL;
184d8e7d87eSManos Pitsidianakis }
185d8e7d87eSManos Pitsidianakis 
186d8e7d87eSManos Pitsidianakis static void throttle_reopen_abort(BDRVReopenState *reopen_state)
187d8e7d87eSManos Pitsidianakis {
188d8e7d87eSManos Pitsidianakis     ThrottleGroupMember *tgm = reopen_state->opaque;
189d8e7d87eSManos Pitsidianakis 
190d8e7d87eSManos Pitsidianakis     throttle_group_unregister_tgm(tgm);
191d8e7d87eSManos Pitsidianakis     g_free(tgm);
192d8e7d87eSManos Pitsidianakis     reopen_state->opaque = NULL;
193d8e7d87eSManos Pitsidianakis }
194d8e7d87eSManos Pitsidianakis 
195d8e7d87eSManos Pitsidianakis static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
196d8e7d87eSManos Pitsidianakis                                                  BlockDriverState *candidate)
197d8e7d87eSManos Pitsidianakis {
198d8e7d87eSManos Pitsidianakis     return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
199d8e7d87eSManos Pitsidianakis }
200d8e7d87eSManos Pitsidianakis 
201b867eaa1SManos Pitsidianakis static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
202b867eaa1SManos Pitsidianakis {
203b867eaa1SManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
204b867eaa1SManos Pitsidianakis     if (atomic_fetch_inc(&tgm->io_limits_disabled) == 0) {
205b867eaa1SManos Pitsidianakis         throttle_group_restart_tgm(tgm);
206b867eaa1SManos Pitsidianakis     }
207b867eaa1SManos Pitsidianakis }
208b867eaa1SManos Pitsidianakis 
209b867eaa1SManos Pitsidianakis static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
210b867eaa1SManos Pitsidianakis {
211b867eaa1SManos Pitsidianakis     ThrottleGroupMember *tgm = bs->opaque;
212b867eaa1SManos Pitsidianakis     assert(tgm->io_limits_disabled);
213b867eaa1SManos Pitsidianakis     atomic_dec(&tgm->io_limits_disabled);
214b867eaa1SManos Pitsidianakis }
215b867eaa1SManos Pitsidianakis 
216d8e7d87eSManos Pitsidianakis static BlockDriver bdrv_throttle = {
217d8e7d87eSManos Pitsidianakis     .format_name                        =   "throttle",
218d8e7d87eSManos Pitsidianakis     .protocol_name                      =   "throttle",
219d8e7d87eSManos Pitsidianakis     .instance_size                      =   sizeof(ThrottleGroupMember),
220d8e7d87eSManos Pitsidianakis 
221d8e7d87eSManos Pitsidianakis     .bdrv_file_open                     =   throttle_open,
222d8e7d87eSManos Pitsidianakis     .bdrv_close                         =   throttle_close,
223d8e7d87eSManos Pitsidianakis     .bdrv_co_flush                      =   throttle_co_flush,
224d8e7d87eSManos Pitsidianakis 
225d8e7d87eSManos Pitsidianakis     .bdrv_child_perm                    =   bdrv_filter_default_perms,
226d8e7d87eSManos Pitsidianakis 
227d8e7d87eSManos Pitsidianakis     .bdrv_getlength                     =   throttle_getlength,
228d8e7d87eSManos Pitsidianakis 
229d8e7d87eSManos Pitsidianakis     .bdrv_co_preadv                     =   throttle_co_preadv,
230d8e7d87eSManos Pitsidianakis     .bdrv_co_pwritev                    =   throttle_co_pwritev,
231d8e7d87eSManos Pitsidianakis 
232d8e7d87eSManos Pitsidianakis     .bdrv_co_pwrite_zeroes              =   throttle_co_pwrite_zeroes,
233d8e7d87eSManos Pitsidianakis     .bdrv_co_pdiscard                   =   throttle_co_pdiscard,
234d8e7d87eSManos Pitsidianakis 
235d8e7d87eSManos Pitsidianakis     .bdrv_recurse_is_first_non_filter   =   throttle_recurse_is_first_non_filter,
236d8e7d87eSManos Pitsidianakis 
237d8e7d87eSManos Pitsidianakis     .bdrv_attach_aio_context            =   throttle_attach_aio_context,
238d8e7d87eSManos Pitsidianakis     .bdrv_detach_aio_context            =   throttle_detach_aio_context,
239d8e7d87eSManos Pitsidianakis 
240d8e7d87eSManos Pitsidianakis     .bdrv_reopen_prepare                =   throttle_reopen_prepare,
241d8e7d87eSManos Pitsidianakis     .bdrv_reopen_commit                 =   throttle_reopen_commit,
242d8e7d87eSManos Pitsidianakis     .bdrv_reopen_abort                  =   throttle_reopen_abort,
243*3e4d0e72SEric Blake     .bdrv_co_block_status               =   bdrv_co_block_status_from_file,
244d8e7d87eSManos Pitsidianakis 
245b867eaa1SManos Pitsidianakis     .bdrv_co_drain_begin                =   throttle_co_drain_begin,
246b867eaa1SManos Pitsidianakis     .bdrv_co_drain_end                  =   throttle_co_drain_end,
247b867eaa1SManos Pitsidianakis 
248d8e7d87eSManos Pitsidianakis     .is_filter                          =   true,
249d8e7d87eSManos Pitsidianakis };
250d8e7d87eSManos Pitsidianakis 
251d8e7d87eSManos Pitsidianakis static void bdrv_throttle_init(void)
252d8e7d87eSManos Pitsidianakis {
253d8e7d87eSManos Pitsidianakis     bdrv_register(&bdrv_throttle);
254d8e7d87eSManos Pitsidianakis }
255d8e7d87eSManos Pitsidianakis 
256d8e7d87eSManos Pitsidianakis block_init(bdrv_throttle_init);
257