| /* |
| * Fsdev Throttle |
| * |
| * Copyright (C) 2016 Huawei Technologies Duesseldorf GmbH |
| * |
| * Author: Pradeep Jagadeesh <pradeep.jagadeesh@huawei.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or |
| * (at your option) any later version. |
| * |
| * See the COPYING file in the top-level directory for details. |
| * |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/error-report.h" |
| #include "qemu-fsdev-throttle.h" |
| #include "qemu/iov.h" |
| #include "qemu/main-loop.h" |
| #include "qemu/option.h" |
| |
| static void fsdev_throttle_read_timer_cb(void *opaque) |
| { |
| FsThrottle *fst = opaque; |
| qemu_co_enter_next(&fst->throttled_reqs[false], NULL); |
| } |
| |
| static void fsdev_throttle_write_timer_cb(void *opaque) |
| { |
| FsThrottle *fst = opaque; |
| qemu_co_enter_next(&fst->throttled_reqs[true], NULL); |
| } |
| |
| int fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp) |
| { |
| throttle_config_init(&fst->cfg); |
| fst->cfg.buckets[THROTTLE_BPS_TOTAL].avg = |
| qemu_opt_get_number(opts, "throttling.bps-total", 0); |
| fst->cfg.buckets[THROTTLE_BPS_READ].avg = |
| qemu_opt_get_number(opts, "throttling.bps-read", 0); |
| fst->cfg.buckets[THROTTLE_BPS_WRITE].avg = |
| qemu_opt_get_number(opts, "throttling.bps-write", 0); |
| fst->cfg.buckets[THROTTLE_OPS_TOTAL].avg = |
| qemu_opt_get_number(opts, "throttling.iops-total", 0); |
| fst->cfg.buckets[THROTTLE_OPS_READ].avg = |
| qemu_opt_get_number(opts, "throttling.iops-read", 0); |
| fst->cfg.buckets[THROTTLE_OPS_WRITE].avg = |
| qemu_opt_get_number(opts, "throttling.iops-write", 0); |
| |
| fst->cfg.buckets[THROTTLE_BPS_TOTAL].max = |
| qemu_opt_get_number(opts, "throttling.bps-total-max", 0); |
| fst->cfg.buckets[THROTTLE_BPS_READ].max = |
| qemu_opt_get_number(opts, "throttling.bps-read-max", 0); |
| fst->cfg.buckets[THROTTLE_BPS_WRITE].max = |
| qemu_opt_get_number(opts, "throttling.bps-write-max", 0); |
| fst->cfg.buckets[THROTTLE_OPS_TOTAL].max = |
| qemu_opt_get_number(opts, "throttling.iops-total-max", 0); |
| fst->cfg.buckets[THROTTLE_OPS_READ].max = |
| qemu_opt_get_number(opts, "throttling.iops-read-max", 0); |
| fst->cfg.buckets[THROTTLE_OPS_WRITE].max = |
| qemu_opt_get_number(opts, "throttling.iops-write-max", 0); |
| |
| fst->cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = |
| qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1); |
| fst->cfg.buckets[THROTTLE_BPS_READ].burst_length = |
| qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1); |
| fst->cfg.buckets[THROTTLE_BPS_WRITE].burst_length = |
| qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1); |
| fst->cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = |
| qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1); |
| fst->cfg.buckets[THROTTLE_OPS_READ].burst_length = |
| qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1); |
| fst->cfg.buckets[THROTTLE_OPS_WRITE].burst_length = |
| qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1); |
| fst->cfg.op_size = |
| qemu_opt_get_number(opts, "throttling.iops-size", 0); |
| |
| return throttle_is_valid(&fst->cfg, errp) ? 0 : -1; |
| } |
| |
| void fsdev_throttle_init(FsThrottle *fst) |
| { |
| if (throttle_enabled(&fst->cfg)) { |
| throttle_init(&fst->ts); |
| throttle_timers_init(&fst->tt, |
| qemu_get_aio_context(), |
| QEMU_CLOCK_REALTIME, |
| fsdev_throttle_read_timer_cb, |
| fsdev_throttle_write_timer_cb, |
| fst); |
| throttle_config(&fst->ts, QEMU_CLOCK_REALTIME, &fst->cfg); |
| qemu_co_queue_init(&fst->throttled_reqs[0]); |
| qemu_co_queue_init(&fst->throttled_reqs[1]); |
| } |
| } |
| |
| void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst, |
| ThrottleDirection direction, |
| struct iovec *iov, int iovcnt) |
| { |
| assert(direction < THROTTLE_MAX); |
| if (throttle_enabled(&fst->cfg)) { |
| if (throttle_schedule_timer(&fst->ts, &fst->tt, direction) || |
| !qemu_co_queue_empty(&fst->throttled_reqs[direction])) { |
| qemu_co_queue_wait(&fst->throttled_reqs[direction], NULL); |
| } |
| |
| throttle_account(&fst->ts, direction, iov_size(iov, iovcnt)); |
| |
| if (!qemu_co_queue_empty(&fst->throttled_reqs[direction]) && |
| !throttle_schedule_timer(&fst->ts, &fst->tt, direction)) { |
| qemu_co_queue_next(&fst->throttled_reqs[direction]); |
| } |
| } |
| } |
| |
| void fsdev_throttle_cleanup(FsThrottle *fst) |
| { |
| if (throttle_enabled(&fst->cfg)) { |
| throttle_timers_destroy(&fst->tt); |
| } |
| } |