diff --git a/blockdev.c b/blockdev.c index 2b2f6ceef0..8682bd81d8 100644 --- a/blockdev.c +++ b/blockdev.c @@ -52,6 +52,7 @@ #include "sysemu/arch_init.h" #include "qemu/cutils.h" #include "qemu/help_option.h" +#include "qemu/throttle-options.h" static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); @@ -4007,83 +4008,11 @@ QemuOptsList qemu_common_drive_opts = { .name = BDRV_OPT_READ_ONLY, .type = QEMU_OPT_BOOL, .help = "open drive file as read-only", - },{ - .name = "throttling.iops-total", - .type = QEMU_OPT_NUMBER, - .help = "limit total I/O operations per second", - },{ - .name = "throttling.iops-read", - .type = QEMU_OPT_NUMBER, - .help = "limit read operations per second", - },{ - .name = "throttling.iops-write", - .type = QEMU_OPT_NUMBER, - .help = "limit write operations per second", - },{ - .name = "throttling.bps-total", - .type = QEMU_OPT_NUMBER, - .help = "limit total bytes per second", - },{ - .name = "throttling.bps-read", - .type = QEMU_OPT_NUMBER, - .help = "limit read bytes per second", - },{ - .name = "throttling.bps-write", - .type = QEMU_OPT_NUMBER, - .help = "limit write bytes per second", - },{ - .name = "throttling.iops-total-max", - .type = QEMU_OPT_NUMBER, - .help = "I/O operations burst", - },{ - .name = "throttling.iops-read-max", - .type = QEMU_OPT_NUMBER, - .help = "I/O operations read burst", - },{ - .name = "throttling.iops-write-max", - .type = QEMU_OPT_NUMBER, - .help = "I/O operations write burst", - },{ - .name = "throttling.bps-total-max", - .type = QEMU_OPT_NUMBER, - .help = "total bytes burst", - },{ - .name = "throttling.bps-read-max", - .type = QEMU_OPT_NUMBER, - .help = "total bytes read burst", - },{ - .name = "throttling.bps-write-max", - .type = QEMU_OPT_NUMBER, - .help = "total bytes write burst", - },{ - .name = "throttling.iops-total-max-length", - .type = QEMU_OPT_NUMBER, - .help = "length of the iops-total-max burst period, in seconds", - },{ - .name = "throttling.iops-read-max-length", - .type = QEMU_OPT_NUMBER, - .help = "length of the iops-read-max burst period, in seconds", - },{ - .name = "throttling.iops-write-max-length", - .type = QEMU_OPT_NUMBER, - .help = "length of the iops-write-max burst period, in seconds", - },{ - .name = "throttling.bps-total-max-length", - .type = QEMU_OPT_NUMBER, - .help = "length of the bps-total-max burst period, in seconds", - },{ - .name = "throttling.bps-read-max-length", - .type = QEMU_OPT_NUMBER, - .help = "length of the bps-read-max burst period, in seconds", - },{ - .name = "throttling.bps-write-max-length", - .type = QEMU_OPT_NUMBER, - .help = "length of the bps-write-max burst period, in seconds", - },{ - .name = "throttling.iops-size", - .type = QEMU_OPT_NUMBER, - .help = "when limiting by iops max size of an I/O in bytes", - },{ + }, + + THROTTLE_OPTS, + + { .name = "throttling.group", .type = QEMU_OPT_STRING, .help = "name of the block throttling group", diff --git a/fsdev/Makefile.objs b/fsdev/Makefile.objs index 1b120a4a7d..659df6e187 100644 --- a/fsdev/Makefile.objs +++ b/fsdev/Makefile.objs @@ -5,7 +5,7 @@ common-obj-y = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o else common-obj-y = qemu-fsdev-dummy.o endif -common-obj-y += qemu-fsdev-opts.o +common-obj-y += qemu-fsdev-opts.o qemu-fsdev-throttle.o # Toplevel always builds this; targets without virtio will put it in # common-obj-y diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index a56dc8488d..0844a403dc 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -17,6 +17,7 @@ #include #include #include +#include "qemu-fsdev-throttle.h" #define SM_LOCAL_MODE_BITS 0600 #define SM_LOCAL_DIR_MODE_BITS 0700 @@ -74,6 +75,7 @@ typedef struct FsDriverEntry { char *path; int export_flags; FileOperations *ops; + FsThrottle fst; } FsDriverEntry; typedef struct FsContext @@ -83,6 +85,7 @@ typedef struct FsContext int export_flags; struct xattr_operations **xops; struct extended_ops exops; + FsThrottle *fst; /* fs driver specific data */ void *private; } FsContext; diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c index 1dd8c7a24c..bf5713008a 100644 --- a/fsdev/qemu-fsdev-opts.c +++ b/fsdev/qemu-fsdev-opts.c @@ -9,6 +9,7 @@ #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/module.h" +#include "qemu/throttle-options.h" static QemuOptsList qemu_fsdev_opts = { .name = "fsdev", @@ -39,6 +40,8 @@ static QemuOptsList qemu_fsdev_opts = { .type = QEMU_OPT_NUMBER, }, + THROTTLE_OPTS, + { /*End of list */ } }, }; diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c new file mode 100644 index 0000000000..7ae4e86646 --- /dev/null +++ b/fsdev/qemu-fsdev-throttle.c @@ -0,0 +1,118 @@ +/* + * Fsdev Throttle + * + * Copyright (C) 2016 Huawei Technologies Duesseldorf GmbH + * + * Author: Pradeep Jagadeesh + * + * 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" + +static void fsdev_throttle_read_timer_cb(void *opaque) +{ + FsThrottle *fst = opaque; + qemu_co_enter_next(&fst->throttled_reqs[false]); +} + +static void fsdev_throttle_write_timer_cb(void *opaque) +{ + FsThrottle *fst = opaque; + qemu_co_enter_next(&fst->throttled_reqs[true]); +} + +void 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); + + throttle_is_valid(&fst->cfg, errp); +} + +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, &fst->tt, &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, bool is_write, + struct iovec *iov, int iovcnt) +{ + if (throttle_enabled(&fst->cfg)) { + if (throttle_schedule_timer(&fst->ts, &fst->tt, is_write) || + !qemu_co_queue_empty(&fst->throttled_reqs[is_write])) { + qemu_co_queue_wait(&fst->throttled_reqs[is_write], NULL); + } + + throttle_account(&fst->ts, is_write, iov_size(iov, iovcnt)); + + if (!qemu_co_queue_empty(&fst->throttled_reqs[is_write]) && + !throttle_schedule_timer(&fst->ts, &fst->tt, is_write)) { + qemu_co_queue_next(&fst->throttled_reqs[is_write]); + } + } +} + +void fsdev_throttle_cleanup(FsThrottle *fst) +{ + if (throttle_enabled(&fst->cfg)) { + throttle_timers_destroy(&fst->tt); + } +} diff --git a/fsdev/qemu-fsdev-throttle.h b/fsdev/qemu-fsdev-throttle.h new file mode 100644 index 0000000000..e418643ccb --- /dev/null +++ b/fsdev/qemu-fsdev-throttle.h @@ -0,0 +1,39 @@ +/* + * Fsdev Throttle + * + * Copyright (C) 2016 Huawei Technologies Duesseldorf GmbH + * + * Author: Pradeep Jagadeesh + * + * 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. + * + */ + +#ifndef _FSDEV_THROTTLE_H +#define _FSDEV_THROTTLE_H + +#include "block/aio.h" +#include "qemu/main-loop.h" +#include "qemu/coroutine.h" +#include "qapi/error.h" +#include "qemu/throttle.h" + +typedef struct FsThrottle { + ThrottleState ts; + ThrottleTimers tt; + ThrottleConfig cfg; + CoQueue throttled_reqs[2]; +} FsThrottle; + +void fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **); + +void fsdev_throttle_init(FsThrottle *); + +void coroutine_fn fsdev_co_throttle_request(FsThrottle *, bool , + struct iovec *, int); + +void fsdev_throttle_cleanup(FsThrottle *); +#endif /* _FSDEV_THROTTLE_H */ diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 7de07e1ba6..2369b918aa 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1208,6 +1208,7 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) { const char *sec_model = qemu_opt_get(opts, "security_model"); const char *path = qemu_opt_get(opts, "path"); + Error *err = NULL; if (!sec_model) { error_report("Security model not specified, local fs needs security model"); @@ -1236,6 +1237,13 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) error_report("fsdev: No path specified"); return -1; } + + fsdev_throttle_parse_opts(opts, &fse->fst, &err); + if (err) { + error_reportf_err(err, "Throttle configuration is not valid: "); + return -1; + } + fse->path = g_strdup(path); return 0; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 3af1c93dc8..76c9247c77 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -3010,7 +3010,6 @@ out_nofid: */ static void coroutine_fn v9fs_lock(void *opaque) { - int8_t status; V9fsFlock flock; size_t offset = 7; struct stat stbuf; @@ -3018,7 +3017,6 @@ static void coroutine_fn v9fs_lock(void *opaque) int32_t fid, err = 0; V9fsPDU *pdu = opaque; - status = P9_LOCK_ERROR; v9fs_string_init(&flock.client_id); err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type, &flock.flags, &flock.start, &flock.length, @@ -3044,15 +3042,15 @@ static void coroutine_fn v9fs_lock(void *opaque) if (err < 0) { goto out; } - status = P9_LOCK_SUCCESS; + err = pdu_marshal(pdu, offset, "b", P9_LOCK_SUCCESS); + if (err < 0) { + goto out; + } + err += offset; + trace_v9fs_lock_return(pdu->tag, pdu->id, P9_LOCK_SUCCESS); out: put_fid(pdu, fidp); out_nofid: - err = pdu_marshal(pdu, offset, "b", status); - if (err > 0) { - err += offset; - } - trace_v9fs_lock_return(pdu->tag, pdu->id, status); pdu_complete(pdu, err); v9fs_string_free(&flock.client_id); } @@ -3531,6 +3529,10 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp) error_setg(errp, "share path %s is not a directory", fse->path); goto out; } + + s->ctx.fst = &fse->fst; + fsdev_throttle_init(s->ctx.fst); + v9fs_path_free(&path); rc = 0; @@ -3551,6 +3553,7 @@ void v9fs_device_unrealize_common(V9fsState *s, Error **errp) if (s->ops->cleanup) { s->ops->cleanup(&s->ctx); } + fsdev_throttle_cleanup(s->ctx.fst); g_free(s->tag); g_free(s->ctx.fs_root); } diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 120e267108..88791bc327 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -247,6 +247,7 @@ int coroutine_fn v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp, if (v9fs_request_cancelled(pdu)) { return -EINTR; } + fsdev_co_throttle_request(s->ctx.fst, true, iov, iovcnt); v9fs_co_run_in_worker( { err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset); @@ -266,6 +267,7 @@ int coroutine_fn v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp, if (v9fs_request_cancelled(pdu)) { return -EINTR; } + fsdev_co_throttle_request(s->ctx.fst, false, iov, iovcnt); v9fs_co_run_in_worker( { err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset); diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h new file mode 100644 index 0000000000..3133d1ca40 --- /dev/null +++ b/include/qemu/throttle-options.h @@ -0,0 +1,92 @@ +/* + * QEMU throttling command line options + * + * 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. + * + */ +#ifndef THROTTLE_OPTIONS_H +#define THROTTLE_OPTIONS_H + +#define THROTTLE_OPTS \ + { \ + .name = "throttling.iops-total",\ + .type = QEMU_OPT_NUMBER,\ + .help = "limit total I/O operations per second",\ + },{ \ + .name = "throttling.iops-read",\ + .type = QEMU_OPT_NUMBER,\ + .help = "limit read operations per second",\ + },{ \ + .name = "throttling.iops-write",\ + .type = QEMU_OPT_NUMBER,\ + .help = "limit write operations per second",\ + },{ \ + .name = "throttling.bps-total",\ + .type = QEMU_OPT_NUMBER,\ + .help = "limit total bytes per second",\ + },{ \ + .name = "throttling.bps-read",\ + .type = QEMU_OPT_NUMBER,\ + .help = "limit read bytes per second",\ + },{ \ + .name = "throttling.bps-write",\ + .type = QEMU_OPT_NUMBER,\ + .help = "limit write bytes per second",\ + },{ \ + .name = "throttling.iops-total-max",\ + .type = QEMU_OPT_NUMBER,\ + .help = "I/O operations burst",\ + },{ \ + .name = "throttling.iops-read-max",\ + .type = QEMU_OPT_NUMBER,\ + .help = "I/O operations read burst",\ + },{ \ + .name = "throttling.iops-write-max",\ + .type = QEMU_OPT_NUMBER,\ + .help = "I/O operations write burst",\ + },{ \ + .name = "throttling.bps-total-max",\ + .type = QEMU_OPT_NUMBER,\ + .help = "total bytes burst",\ + },{ \ + .name = "throttling.bps-read-max",\ + .type = QEMU_OPT_NUMBER,\ + .help = "total bytes read burst",\ + },{ \ + .name = "throttling.bps-write-max",\ + .type = QEMU_OPT_NUMBER,\ + .help = "total bytes write burst",\ + },{ \ + .name = "throttling.iops-total-max-length",\ + .type = QEMU_OPT_NUMBER,\ + .help = "length of the iops-total-max burst period, in seconds",\ + },{ \ + .name = "throttling.iops-read-max-length",\ + .type = QEMU_OPT_NUMBER,\ + .help = "length of the iops-read-max burst period, in seconds",\ + },{ \ + .name = "throttling.iops-write-max-length",\ + .type = QEMU_OPT_NUMBER,\ + .help = "length of the iops-write-max burst period, in seconds",\ + },{ \ + .name = "throttling.bps-total-max-length",\ + .type = QEMU_OPT_NUMBER,\ + .help = "length of the bps-total-max burst period, in seconds",\ + },{ \ + .name = "throttling.bps-read-max-length",\ + .type = QEMU_OPT_NUMBER,\ + .help = "length of the bps-read-max burst period, in seconds",\ + },{ \ + .name = "throttling.bps-write-max-length",\ + .type = QEMU_OPT_NUMBER,\ + .help = "length of the bps-write-max burst period, in seconds",\ + },{ \ + .name = "throttling.iops-size",\ + .type = QEMU_OPT_NUMBER,\ + .help = "when limiting by iops max size of an I/O in bytes",\ + } + +#endif diff --git a/qemu-options.hx b/qemu-options.hx index faf5cf8d45..c85f77d1d8 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -744,7 +744,12 @@ ETEXI DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n" - " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n", + " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n" + " [[,throttling.bps-total=b]|[[,throttling.bps-read=r][,throttling.bps-write=w]]]\n" + " [[,throttling.iops-total=i]|[[,throttling.iops-read=r][,throttling.iops-write=w]]]\n" + " [[,throttling.bps-total-max=bm]|[[,throttling.bps-read-max=rm][,throttling.bps-write-max=wm]]]\n" + " [[,throttling.iops-total-max=im]|[[,throttling.iops-read-max=irm][,throttling.iops-write-max=iwm]]]\n" + " [[,throttling.iops-size=is]]\n", QEMU_ARCH_ALL) STEXI