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