qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

throttle.c (8826B)


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