2013-09-02 16:14:38 +04:00
|
|
|
/*
|
|
|
|
* Throttle infrastructure tests
|
|
|
|
*
|
2015-06-08 19:17:43 +03:00
|
|
|
* Copyright Nodalink, EURL. 2013-2014
|
|
|
|
* Copyright Igalia, S.L. 2015
|
2013-09-02 16:14:38 +04:00
|
|
|
*
|
|
|
|
* Authors:
|
2015-06-08 19:17:43 +03:00
|
|
|
* Benoît Canet <benoit.canet@nodalink.com>
|
|
|
|
* Alberto Garcia <berto@igalia.com>
|
2013-09-02 16:14:38 +04:00
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
|
|
|
* See the COPYING.LIB file in the top-level directory.
|
|
|
|
*/
|
|
|
|
|
2016-02-08 21:08:51 +03:00
|
|
|
#include "qemu/osdep.h"
|
2013-09-02 16:14:38 +04:00
|
|
|
#include <math.h>
|
2014-05-14 18:22:45 +04:00
|
|
|
#include "block/aio.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 11:01:28 +03:00
|
|
|
#include "qapi/error.h"
|
2013-09-02 16:14:38 +04:00
|
|
|
#include "qemu/throttle.h"
|
2014-09-18 15:30:49 +04:00
|
|
|
#include "qemu/error-report.h"
|
Include qemu/main-loop.h less
In my "build everything" tree, changing qemu/main-loop.h triggers a
recompile of some 5600 out of 6600 objects (not counting tests and
objects that don't depend on qemu/osdep.h). It includes block/aio.h,
which in turn includes qemu/event_notifier.h, qemu/notify.h,
qemu/processor.h, qemu/qsp.h, qemu/queue.h, qemu/thread-posix.h,
qemu/thread.h, qemu/timer.h, and a few more.
Include qemu/main-loop.h only where it's needed. Touching it now
recompiles only some 1700 objects. For block/aio.h and
qemu/event_notifier.h, these numbers drop from 5600 to 2800. For the
others, they shrink only slightly.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20190812052359.30071-21-armbru@redhat.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
2019-08-12 08:23:50 +03:00
|
|
|
#include "qemu/main-loop.h"
|
2019-05-23 17:35:07 +03:00
|
|
|
#include "qemu/module.h"
|
2015-06-08 19:17:43 +03:00
|
|
|
#include "block/throttle-groups.h"
|
2016-03-21 13:30:57 +03:00
|
|
|
#include "sysemu/block-backend.h"
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2014-07-07 23:03:38 +04:00
|
|
|
static AioContext *ctx;
|
|
|
|
static LeakyBucket bkt;
|
|
|
|
static ThrottleConfig cfg;
|
2017-08-25 16:20:24 +03:00
|
|
|
static ThrottleGroupMember tgm;
|
2014-07-07 23:03:38 +04:00
|
|
|
static ThrottleState ts;
|
2017-08-25 16:20:24 +03:00
|
|
|
static ThrottleTimers *tt;
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2013-10-10 22:53:40 +04:00
|
|
|
/* useful function */
|
2013-09-02 16:14:38 +04:00
|
|
|
static bool double_cmp(double x, double y)
|
|
|
|
{
|
|
|
|
return fabsl(x - y) < 1e-6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* tests for single bucket operations */
|
|
|
|
static void test_leak_bucket(void)
|
|
|
|
{
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
|
|
|
bkt = cfg.buckets[THROTTLE_BPS_TOTAL];
|
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
/* set initial value */
|
|
|
|
bkt.avg = 150;
|
|
|
|
bkt.max = 15;
|
|
|
|
bkt.level = 1.5;
|
|
|
|
|
|
|
|
/* leak an op work of time */
|
2015-07-08 17:10:09 +03:00
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(bkt.avg == 150);
|
|
|
|
g_assert(bkt.max == 15);
|
|
|
|
g_assert(double_cmp(bkt.level, 0.5));
|
|
|
|
|
|
|
|
/* leak again emptying the bucket */
|
2015-07-08 17:10:09 +03:00
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(bkt.avg == 150);
|
|
|
|
g_assert(bkt.max == 15);
|
|
|
|
g_assert(double_cmp(bkt.level, 0));
|
|
|
|
|
|
|
|
/* check that the bucket level won't go lower */
|
2015-07-08 17:10:09 +03:00
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(bkt.avg == 150);
|
|
|
|
g_assert(bkt.max == 15);
|
|
|
|
g_assert(double_cmp(bkt.level, 0));
|
2016-02-18 13:27:05 +03:00
|
|
|
|
|
|
|
/* check that burst_level leaks correctly */
|
|
|
|
bkt.burst_level = 6;
|
|
|
|
bkt.max = 250;
|
|
|
|
bkt.burst_length = 2; /* otherwise burst_level will not leak */
|
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
|
|
|
|
g_assert(double_cmp(bkt.burst_level, 3.5));
|
|
|
|
|
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
|
|
|
|
g_assert(double_cmp(bkt.burst_level, 1));
|
|
|
|
|
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
|
|
|
|
g_assert(double_cmp(bkt.burst_level, 0));
|
|
|
|
|
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
|
|
|
|
g_assert(double_cmp(bkt.burst_level, 0));
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_compute_wait(void)
|
|
|
|
{
|
2016-02-18 13:27:06 +03:00
|
|
|
unsigned i;
|
2013-09-02 16:14:38 +04:00
|
|
|
int64_t wait;
|
|
|
|
int64_t result;
|
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
|
|
|
bkt = cfg.buckets[THROTTLE_BPS_TOTAL];
|
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
/* no operation limit set */
|
|
|
|
bkt.avg = 0;
|
|
|
|
bkt.max = 15;
|
|
|
|
bkt.level = 1.5;
|
|
|
|
wait = throttle_compute_wait(&bkt);
|
|
|
|
g_assert(!wait);
|
|
|
|
|
|
|
|
/* zero delta */
|
|
|
|
bkt.avg = 150;
|
|
|
|
bkt.max = 15;
|
|
|
|
bkt.level = 15;
|
|
|
|
wait = throttle_compute_wait(&bkt);
|
|
|
|
g_assert(!wait);
|
|
|
|
|
|
|
|
/* below zero delta */
|
|
|
|
bkt.avg = 150;
|
|
|
|
bkt.max = 15;
|
|
|
|
bkt.level = 9;
|
|
|
|
wait = throttle_compute_wait(&bkt);
|
|
|
|
g_assert(!wait);
|
|
|
|
|
|
|
|
/* half an operation above max */
|
|
|
|
bkt.avg = 150;
|
|
|
|
bkt.max = 15;
|
|
|
|
bkt.level = 15.5;
|
|
|
|
wait = throttle_compute_wait(&bkt);
|
|
|
|
/* time required to do half an operation */
|
2015-07-08 17:10:09 +03:00
|
|
|
result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2;
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(wait == result);
|
2016-02-18 13:27:06 +03:00
|
|
|
|
|
|
|
/* Perform I/O for 2.2 seconds at a rate of bkt.max */
|
|
|
|
bkt.burst_length = 2;
|
|
|
|
bkt.level = 0;
|
|
|
|
bkt.avg = 10;
|
|
|
|
bkt.max = 200;
|
|
|
|
for (i = 0; i < 22; i++) {
|
|
|
|
double units = bkt.max / 10;
|
|
|
|
bkt.level += units;
|
|
|
|
bkt.burst_level += units;
|
|
|
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 10);
|
|
|
|
wait = throttle_compute_wait(&bkt);
|
|
|
|
g_assert(double_cmp(bkt.burst_level, 0));
|
|
|
|
g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10));
|
|
|
|
/* We can do bursts for the 2 seconds we have configured in
|
2023-07-14 14:33:27 +03:00
|
|
|
* burst_length. We have 100 extra milliseconds of burst
|
2016-02-18 13:27:06 +03:00
|
|
|
* because bkt.level has been leaking during this time.
|
|
|
|
* After that, we have to wait. */
|
|
|
|
result = i < 21 ? 0 : 1.8 * NANOSECONDS_PER_SECOND;
|
|
|
|
g_assert(wait == result);
|
|
|
|
}
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* functions to test ThrottleState initialization/destroy methods */
|
|
|
|
static void read_timer_cb(void *opaque)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_timer_cb(void *opaque)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
tt = &tgm.throttle_timers;
|
|
|
|
|
2015-06-08 19:17:41 +03:00
|
|
|
/* fill the structures with crap */
|
2013-09-02 16:14:38 +04:00
|
|
|
memset(&ts, 1, sizeof(ts));
|
2017-08-25 16:20:24 +03:00
|
|
|
memset(tt, 1, sizeof(*tt));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2015-06-08 19:17:41 +03:00
|
|
|
/* init structures */
|
|
|
|
throttle_init(&ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
2015-06-08 19:17:41 +03:00
|
|
|
read_timer_cb, write_timer_cb, &ts);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* check initialized fields */
|
2017-08-25 16:20:24 +03:00
|
|
|
g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
|
2023-07-28 05:19:59 +03:00
|
|
|
g_assert(tt->timers[THROTTLE_READ]);
|
|
|
|
g_assert(tt->timers[THROTTLE_WRITE]);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* check other fields where cleared */
|
|
|
|
g_assert(!ts.previous_leak);
|
|
|
|
g_assert(!ts.cfg.op_size);
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
|
|
|
g_assert(!ts.cfg.buckets[i].avg);
|
|
|
|
g_assert(!ts.cfg.buckets[i].max);
|
|
|
|
g_assert(!ts.cfg.buckets[i].level);
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
|
2023-07-28 05:20:01 +03:00
|
|
|
static void test_init_readonly(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tt = &tgm.throttle_timers;
|
|
|
|
|
|
|
|
/* fill the structures with crap */
|
|
|
|
memset(&ts, 1, sizeof(ts));
|
|
|
|
memset(tt, 1, sizeof(*tt));
|
|
|
|
|
|
|
|
/* init structures */
|
|
|
|
throttle_init(&ts);
|
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
|
|
|
read_timer_cb, NULL, &ts);
|
|
|
|
|
|
|
|
/* check initialized fields */
|
|
|
|
g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
|
|
|
|
g_assert(tt->timers[THROTTLE_READ]);
|
|
|
|
g_assert(!tt->timers[THROTTLE_WRITE]);
|
|
|
|
|
|
|
|
/* check other fields where cleared */
|
|
|
|
g_assert(!ts.previous_leak);
|
|
|
|
g_assert(!ts.cfg.op_size);
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
|
|
|
g_assert(!ts.cfg.buckets[i].avg);
|
|
|
|
g_assert(!ts.cfg.buckets[i].max);
|
|
|
|
g_assert(!ts.cfg.buckets[i].level);
|
|
|
|
}
|
|
|
|
|
|
|
|
throttle_timers_destroy(tt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_init_writeonly(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tt = &tgm.throttle_timers;
|
|
|
|
|
|
|
|
/* fill the structures with crap */
|
|
|
|
memset(&ts, 1, sizeof(ts));
|
|
|
|
memset(tt, 1, sizeof(*tt));
|
|
|
|
|
|
|
|
/* init structures */
|
|
|
|
throttle_init(&ts);
|
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
|
|
|
NULL, write_timer_cb, &ts);
|
|
|
|
|
|
|
|
/* check initialized fields */
|
|
|
|
g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
|
|
|
|
g_assert(!tt->timers[THROTTLE_READ]);
|
|
|
|
g_assert(tt->timers[THROTTLE_WRITE]);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* check other fields where cleared */
|
|
|
|
g_assert(!ts.previous_leak);
|
|
|
|
g_assert(!ts.cfg.op_size);
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
|
|
|
g_assert(!ts.cfg.buckets[i].avg);
|
|
|
|
g_assert(!ts.cfg.buckets[i].max);
|
|
|
|
g_assert(!ts.cfg.buckets[i].level);
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_destroy(void)
|
|
|
|
{
|
|
|
|
int i;
|
2015-06-08 19:17:41 +03:00
|
|
|
throttle_init(&ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
2015-06-08 19:17:41 +03:00
|
|
|
read_timer_cb, write_timer_cb, &ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2023-07-28 05:19:59 +03:00
|
|
|
for (i = 0; i < THROTTLE_MAX; i++) {
|
2017-08-25 16:20:24 +03:00
|
|
|
g_assert(!tt->timers[i]);
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* function to test throttle_config and throttle_get_config */
|
|
|
|
static void test_config_functions(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
ThrottleConfig orig_cfg, final_cfg;
|
|
|
|
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153;
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56;
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1;
|
|
|
|
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150;
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69;
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23;
|
|
|
|
|
2017-03-01 14:50:26 +03:00
|
|
|
orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0;
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_READ].max = 56;
|
2013-09-02 16:14:38 +04:00
|
|
|
orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120;
|
|
|
|
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150;
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_READ].max = 400;
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500;
|
|
|
|
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45;
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_READ].level = 65;
|
|
|
|
orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23;
|
|
|
|
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1;
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_READ].level = 90;
|
|
|
|
orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75;
|
|
|
|
|
|
|
|
orig_cfg.op_size = 1;
|
|
|
|
|
2015-06-08 19:17:41 +03:00
|
|
|
throttle_init(&ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
2015-06-08 19:17:41 +03:00
|
|
|
read_timer_cb, write_timer_cb, &ts);
|
2013-09-02 16:14:38 +04:00
|
|
|
/* structure reset by throttle_init previous_leak should be null */
|
|
|
|
g_assert(!ts.previous_leak);
|
2017-07-02 13:06:46 +03:00
|
|
|
throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &orig_cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* has previous leak been initialized by throttle_config ? */
|
|
|
|
g_assert(ts.previous_leak);
|
|
|
|
|
|
|
|
/* get back the fixed configuration */
|
|
|
|
throttle_get_config(&ts, &final_cfg);
|
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1);
|
|
|
|
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23);
|
|
|
|
|
2017-03-01 14:50:26 +03:00
|
|
|
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 0);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 56);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120);
|
|
|
|
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400);
|
|
|
|
g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500);
|
|
|
|
|
|
|
|
g_assert(final_cfg.op_size == 1);
|
|
|
|
|
|
|
|
/* check bucket have been cleared */
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
|
|
|
g_assert(!final_cfg.buckets[i].level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* functions to test is throttle is enabled by a config */
|
|
|
|
static void set_cfg_value(bool is_max, int index, int value)
|
|
|
|
{
|
|
|
|
if (is_max) {
|
|
|
|
cfg.buckets[index].max = value;
|
2016-02-18 13:26:58 +03:00
|
|
|
/* If max is set, avg should never be 0 */
|
|
|
|
cfg.buckets[index].avg = MAX(cfg.buckets[index].avg, 1);
|
2013-09-02 16:14:38 +04:00
|
|
|
} else {
|
|
|
|
cfg.buckets[index].avg = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_enabled(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(!throttle_enabled(&cfg));
|
|
|
|
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(false, i, 150);
|
2017-08-24 16:24:47 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
g_assert(throttle_enabled(&cfg));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(false, i, -150);
|
2017-08-24 16:24:47 +03:00
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* tests functions for throttle_conflicting */
|
|
|
|
|
|
|
|
static void test_conflicts_for_one_set(bool is_max,
|
|
|
|
int total,
|
|
|
|
int read,
|
|
|
|
int write)
|
|
|
|
{
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
set_cfg_value(is_max, total, 1);
|
|
|
|
set_cfg_value(is_max, read, 1);
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(is_max, total, 1);
|
|
|
|
set_cfg_value(is_max, write, 1);
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(is_max, total, 1);
|
|
|
|
set_cfg_value(is_max, read, 1);
|
|
|
|
set_cfg_value(is_max, write, 1);
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(is_max, total, 1);
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(is_max, read, 1);
|
|
|
|
set_cfg_value(is_max, write, 1);
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_conflicting_config(void)
|
|
|
|
{
|
|
|
|
/* bps average conflicts */
|
|
|
|
test_conflicts_for_one_set(false,
|
|
|
|
THROTTLE_BPS_TOTAL,
|
|
|
|
THROTTLE_BPS_READ,
|
|
|
|
THROTTLE_BPS_WRITE);
|
|
|
|
|
|
|
|
/* ops average conflicts */
|
|
|
|
test_conflicts_for_one_set(false,
|
|
|
|
THROTTLE_OPS_TOTAL,
|
|
|
|
THROTTLE_OPS_READ,
|
|
|
|
THROTTLE_OPS_WRITE);
|
|
|
|
|
|
|
|
/* bps average conflicts */
|
|
|
|
test_conflicts_for_one_set(true,
|
|
|
|
THROTTLE_BPS_TOTAL,
|
|
|
|
THROTTLE_BPS_READ,
|
|
|
|
THROTTLE_BPS_WRITE);
|
|
|
|
/* ops average conflicts */
|
|
|
|
test_conflicts_for_one_set(true,
|
|
|
|
THROTTLE_OPS_TOTAL,
|
|
|
|
THROTTLE_OPS_READ,
|
|
|
|
THROTTLE_OPS_WRITE);
|
|
|
|
}
|
|
|
|
/* functions to test the throttle_is_valid function */
|
|
|
|
static void test_is_valid_for_value(int value, bool should_be_valid)
|
|
|
|
{
|
|
|
|
int is_max, index;
|
|
|
|
for (is_max = 0; is_max < 2; is_max++) {
|
|
|
|
for (index = 0; index < BUCKETS_COUNT; index++) {
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
set_cfg_value(is_max, index, value);
|
2016-02-18 13:26:57 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL) == should_be_valid);
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_is_valid(void)
|
|
|
|
{
|
2023-07-14 14:33:27 +03:00
|
|
|
/* negative numbesr are invalid */
|
2013-09-02 16:14:38 +04:00
|
|
|
test_is_valid_for_value(-1, false);
|
2023-07-14 14:33:27 +03:00
|
|
|
/* zero is valid */
|
2013-09-02 16:14:38 +04:00
|
|
|
test_is_valid_for_value(0, true);
|
2023-07-14 14:33:27 +03:00
|
|
|
/* positives numbers are valid */
|
2013-09-02 16:14:38 +04:00
|
|
|
test_is_valid_for_value(1, true);
|
|
|
|
}
|
|
|
|
|
2017-08-24 16:24:49 +03:00
|
|
|
static void test_ranges(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
|
|
|
LeakyBucket *b = &cfg.buckets[i];
|
|
|
|
throttle_config_init(&cfg);
|
|
|
|
|
|
|
|
/* avg = 0 means throttling is disabled, but the config is valid */
|
|
|
|
b->avg = 0;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
g_assert(!throttle_enabled(&cfg));
|
|
|
|
|
|
|
|
/* These are valid configurations (values <= THROTTLE_VALUE_MAX) */
|
|
|
|
b->avg = 1;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = THROTTLE_VALUE_MAX;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = THROTTLE_VALUE_MAX;
|
|
|
|
b->max = THROTTLE_VALUE_MAX;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
/* Values over THROTTLE_VALUE_MAX are not allowed */
|
|
|
|
b->avg = THROTTLE_VALUE_MAX + 1;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = THROTTLE_VALUE_MAX;
|
|
|
|
b->max = THROTTLE_VALUE_MAX + 1;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
/* burst_length must be between 1 and THROTTLE_VALUE_MAX */
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = 1;
|
|
|
|
b->burst_length = 0;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = 1;
|
|
|
|
b->burst_length = 1;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = 1;
|
|
|
|
b->burst_length = THROTTLE_VALUE_MAX;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = 1;
|
|
|
|
b->burst_length = THROTTLE_VALUE_MAX + 1;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
/* burst_length * max cannot exceed THROTTLE_VALUE_MAX */
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = 2;
|
|
|
|
b->burst_length = THROTTLE_VALUE_MAX / 2;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = 3;
|
|
|
|
b->burst_length = THROTTLE_VALUE_MAX / 2;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = THROTTLE_VALUE_MAX;
|
|
|
|
b->burst_length = 1;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
b->avg = 1;
|
|
|
|
b->max = THROTTLE_VALUE_MAX;
|
|
|
|
b->burst_length = 2;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-04 13:22:13 +03:00
|
|
|
static void test_max_is_missing_limit(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg);
|
2015-08-04 13:22:13 +03:00
|
|
|
cfg.buckets[i].max = 100;
|
|
|
|
cfg.buckets[i].avg = 0;
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
2015-08-04 13:22:13 +03:00
|
|
|
|
|
|
|
cfg.buckets[i].max = 0;
|
|
|
|
cfg.buckets[i].avg = 0;
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2015-08-04 13:22:13 +03:00
|
|
|
|
|
|
|
cfg.buckets[i].max = 0;
|
|
|
|
cfg.buckets[i].avg = 100;
|
2016-02-18 13:26:59 +03:00
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2016-07-28 11:08:13 +03:00
|
|
|
|
|
|
|
cfg.buckets[i].max = 30;
|
|
|
|
cfg.buckets[i].avg = 100;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
|
|
|
|
cfg.buckets[i].max = 100;
|
|
|
|
cfg.buckets[i].avg = 100;
|
|
|
|
g_assert(throttle_is_valid(&cfg, NULL));
|
2015-08-04 13:22:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-02 03:40:31 +03:00
|
|
|
static void test_iops_size_is_missing_limit(void)
|
|
|
|
{
|
|
|
|
/* A total/read/write iops limit is required */
|
|
|
|
throttle_config_init(&cfg);
|
|
|
|
cfg.op_size = 4096;
|
|
|
|
g_assert(!throttle_is_valid(&cfg, NULL));
|
|
|
|
}
|
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
static void test_have_timer(void)
|
|
|
|
{
|
2015-06-08 19:17:41 +03:00
|
|
|
/* zero structures */
|
2013-09-02 16:14:38 +04:00
|
|
|
memset(&ts, 0, sizeof(ts));
|
2017-08-25 16:20:24 +03:00
|
|
|
memset(tt, 0, sizeof(*tt));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2013-10-10 22:53:40 +04:00
|
|
|
/* no timer set should return false */
|
2017-08-25 16:20:24 +03:00
|
|
|
g_assert(!throttle_timers_are_initialized(tt));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2015-06-08 19:17:41 +03:00
|
|
|
/* init structures */
|
|
|
|
throttle_init(&ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
2015-06-08 19:17:41 +03:00
|
|
|
read_timer_cb, write_timer_cb, &ts);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* timer set by init should return true */
|
2017-08-25 16:20:24 +03:00
|
|
|
g_assert(throttle_timers_are_initialized(tt));
|
2013-09-02 16:14:38 +04:00
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2013-09-02 16:14:38 +04:00
|
|
|
}
|
|
|
|
|
2014-05-14 18:22:46 +04:00
|
|
|
static void test_detach_attach(void)
|
|
|
|
{
|
2015-06-08 19:17:41 +03:00
|
|
|
/* zero structures */
|
2014-05-14 18:22:46 +04:00
|
|
|
memset(&ts, 0, sizeof(ts));
|
2017-08-25 16:20:24 +03:00
|
|
|
memset(tt, 0, sizeof(*tt));
|
2014-05-14 18:22:46 +04:00
|
|
|
|
|
|
|
/* init the structure */
|
2015-06-08 19:17:41 +03:00
|
|
|
throttle_init(&ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
2015-06-08 19:17:41 +03:00
|
|
|
read_timer_cb, write_timer_cb, &ts);
|
2014-05-14 18:22:46 +04:00
|
|
|
|
|
|
|
/* timer set by init should return true */
|
2017-08-25 16:20:24 +03:00
|
|
|
g_assert(throttle_timers_are_initialized(tt));
|
2014-05-14 18:22:46 +04:00
|
|
|
|
|
|
|
/* timer should no longer exist after detaching */
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_detach_aio_context(tt);
|
|
|
|
g_assert(!throttle_timers_are_initialized(tt));
|
2014-05-14 18:22:46 +04:00
|
|
|
|
|
|
|
/* timer should exist again after attaching */
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_attach_aio_context(tt, ctx);
|
|
|
|
g_assert(throttle_timers_are_initialized(tt));
|
2014-05-14 18:22:46 +04:00
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2014-05-14 18:22:46 +04:00
|
|
|
}
|
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
|
|
|
|
int size, /* size of the operation to do */
|
|
|
|
double avg, /* io limit */
|
|
|
|
uint64_t op_size, /* ideal size of an io */
|
|
|
|
double total_result,
|
|
|
|
double read_result,
|
|
|
|
double write_result)
|
|
|
|
{
|
|
|
|
BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL,
|
|
|
|
THROTTLE_BPS_READ,
|
|
|
|
THROTTLE_BPS_WRITE, },
|
|
|
|
{ THROTTLE_OPS_TOTAL,
|
|
|
|
THROTTLE_OPS_READ,
|
|
|
|
THROTTLE_OPS_WRITE, } };
|
|
|
|
BucketType index;
|
|
|
|
int i;
|
|
|
|
|
2019-07-30 19:01:36 +03:00
|
|
|
throttle_config_init(&cfg);
|
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
for (i = 0; i < 3; i++) {
|
2023-09-22 13:57:42 +03:00
|
|
|
index = to_test[is_ops][i];
|
2013-09-02 16:14:38 +04:00
|
|
|
cfg.buckets[index].avg = avg;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.op_size = op_size;
|
|
|
|
|
2015-06-08 19:17:41 +03:00
|
|
|
throttle_init(&ts);
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
|
2015-06-08 19:17:41 +03:00
|
|
|
read_timer_cb, write_timer_cb, &ts);
|
2017-07-02 13:06:46 +03:00
|
|
|
throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* account a read */
|
2023-07-28 05:20:03 +03:00
|
|
|
throttle_account(&ts, THROTTLE_READ, size);
|
2013-09-02 16:14:38 +04:00
|
|
|
/* account a write */
|
2023-07-28 05:20:03 +03:00
|
|
|
throttle_account(&ts, THROTTLE_WRITE, size);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
/* check total result */
|
|
|
|
index = to_test[is_ops][0];
|
|
|
|
if (!double_cmp(ts.cfg.buckets[index].level, total_result)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check read result */
|
|
|
|
index = to_test[is_ops][1];
|
|
|
|
if (!double_cmp(ts.cfg.buckets[index].level, read_result)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check write result */
|
|
|
|
index = to_test[is_ops][2];
|
|
|
|
if (!double_cmp(ts.cfg.buckets[index].level, write_result)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_timers_destroy(tt);
|
2013-09-02 16:14:38 +04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_accounting(void)
|
|
|
|
{
|
|
|
|
/* tests for bps */
|
|
|
|
|
|
|
|
/* op of size 1 */
|
|
|
|
g_assert(do_test_accounting(false,
|
|
|
|
1 * 512,
|
|
|
|
150,
|
|
|
|
0,
|
|
|
|
1024,
|
|
|
|
512,
|
|
|
|
512));
|
|
|
|
|
|
|
|
/* op of size 2 */
|
|
|
|
g_assert(do_test_accounting(false,
|
|
|
|
2 * 512,
|
|
|
|
150,
|
|
|
|
0,
|
|
|
|
2048,
|
|
|
|
1024,
|
|
|
|
1024));
|
|
|
|
|
|
|
|
/* op of size 2 and orthogonal parameter change */
|
|
|
|
g_assert(do_test_accounting(false,
|
|
|
|
2 * 512,
|
|
|
|
150,
|
|
|
|
17,
|
|
|
|
2048,
|
|
|
|
1024,
|
|
|
|
1024));
|
|
|
|
|
|
|
|
|
|
|
|
/* tests for ops */
|
|
|
|
|
|
|
|
/* op of size 1 */
|
|
|
|
g_assert(do_test_accounting(true,
|
|
|
|
1 * 512,
|
|
|
|
150,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
1,
|
|
|
|
1));
|
|
|
|
|
|
|
|
/* op of size 2 */
|
|
|
|
g_assert(do_test_accounting(true,
|
|
|
|
2 * 512,
|
|
|
|
150,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
1,
|
|
|
|
1));
|
|
|
|
|
|
|
|
/* jumbo op accounting fragmentation : size 64 with op size of 13 units */
|
|
|
|
g_assert(do_test_accounting(true,
|
|
|
|
64 * 512,
|
|
|
|
150,
|
|
|
|
13 * 512,
|
|
|
|
(64.0 * 2) / 13,
|
|
|
|
(64.0 / 13),
|
|
|
|
(64.0 / 13)));
|
|
|
|
|
|
|
|
/* same with orthogonal parameters changes */
|
|
|
|
g_assert(do_test_accounting(true,
|
|
|
|
64 * 512,
|
|
|
|
300,
|
|
|
|
13 * 512,
|
|
|
|
(64.0 * 2) / 13,
|
|
|
|
(64.0 / 13),
|
|
|
|
(64.0 / 13)));
|
|
|
|
}
|
|
|
|
|
2015-06-08 19:17:43 +03:00
|
|
|
static void test_groups(void)
|
|
|
|
{
|
|
|
|
ThrottleConfig cfg1, cfg2;
|
2016-03-21 12:49:51 +03:00
|
|
|
BlockBackend *blk1, *blk2, *blk3;
|
2016-03-21 14:56:44 +03:00
|
|
|
BlockBackendPublic *blkp1, *blkp2, *blkp3;
|
2017-08-25 16:20:23 +03:00
|
|
|
ThrottleGroupMember *tgm1, *tgm2, *tgm3;
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-02-09 17:48:04 +03:00
|
|
|
/* No actual I/O is performed on these devices */
|
2019-04-25 15:25:10 +03:00
|
|
|
blk1 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
|
|
|
|
blk2 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
|
|
|
|
blk3 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
|
2016-03-21 12:49:51 +03:00
|
|
|
|
2016-03-21 14:56:44 +03:00
|
|
|
blkp1 = blk_get_public(blk1);
|
|
|
|
blkp2 = blk_get_public(blk2);
|
|
|
|
blkp3 = blk_get_public(blk3);
|
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
tgm1 = &blkp1->throttle_group_member;
|
|
|
|
tgm2 = &blkp2->throttle_group_member;
|
|
|
|
tgm3 = &blkp3->throttle_group_member;
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
g_assert(tgm1->throttle_state == NULL);
|
|
|
|
g_assert(tgm2->throttle_state == NULL);
|
|
|
|
g_assert(tgm3->throttle_state == NULL);
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-08-25 16:20:24 +03:00
|
|
|
throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1));
|
|
|
|
throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2));
|
|
|
|
throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3));
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
g_assert(tgm1->throttle_state != NULL);
|
|
|
|
g_assert(tgm2->throttle_state != NULL);
|
|
|
|
g_assert(tgm3->throttle_state != NULL);
|
|
|
|
|
|
|
|
g_assert(!strcmp(throttle_group_get_name(tgm1), "bar"));
|
|
|
|
g_assert(!strcmp(throttle_group_get_name(tgm2), "foo"));
|
|
|
|
g_assert(tgm1->throttle_state == tgm3->throttle_state);
|
2015-06-08 19:17:43 +03:00
|
|
|
|
|
|
|
/* Setting the config of a group member affects the whole group */
|
2016-02-18 13:27:00 +03:00
|
|
|
throttle_config_init(&cfg1);
|
2015-06-08 19:17:43 +03:00
|
|
|
cfg1.buckets[THROTTLE_BPS_READ].avg = 500000;
|
|
|
|
cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
|
|
|
|
cfg1.buckets[THROTTLE_OPS_READ].avg = 20000;
|
|
|
|
cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
|
2017-08-25 16:20:23 +03:00
|
|
|
throttle_group_config(tgm1, &cfg1);
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
throttle_group_get_config(tgm1, &cfg1);
|
|
|
|
throttle_group_get_config(tgm3, &cfg2);
|
2015-06-08 19:17:43 +03:00
|
|
|
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
|
|
|
|
|
|
|
|
cfg2.buckets[THROTTLE_BPS_READ].avg = 4547;
|
|
|
|
cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
|
|
|
|
cfg2.buckets[THROTTLE_OPS_READ].avg = 123;
|
|
|
|
cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
|
2017-08-25 16:20:23 +03:00
|
|
|
throttle_group_config(tgm3, &cfg1);
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
throttle_group_get_config(tgm1, &cfg1);
|
|
|
|
throttle_group_get_config(tgm3, &cfg2);
|
2015-06-08 19:17:43 +03:00
|
|
|
g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
|
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
throttle_group_unregister_tgm(tgm1);
|
|
|
|
throttle_group_unregister_tgm(tgm2);
|
|
|
|
throttle_group_unregister_tgm(tgm3);
|
2015-06-08 19:17:43 +03:00
|
|
|
|
2017-08-25 16:20:23 +03:00
|
|
|
g_assert(tgm1->throttle_state == NULL);
|
|
|
|
g_assert(tgm2->throttle_state == NULL);
|
|
|
|
g_assert(tgm3->throttle_state == NULL);
|
2015-06-08 19:17:43 +03:00
|
|
|
}
|
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2015-12-18 18:35:11 +03:00
|
|
|
qemu_init_main_loop(&error_fatal);
|
2015-06-08 19:17:43 +03:00
|
|
|
ctx = qemu_get_aio_context();
|
|
|
|
bdrv_init();
|
block: convert ThrottleGroup to object with QOM
ThrottleGroup is converted to an object. This will allow the future
throttle block filter drive easy creation and configuration of throttle
groups in QMP and cli.
A new QAPI struct, ThrottleLimits, is introduced to provide a shared
struct for all throttle configuration needs in QMP.
ThrottleGroups can be created via CLI as
-object throttle-group,id=foo,x-iops-total=100,x-..
where x-* are individual limit properties. Since we can't add non-scalar
properties in -object this interface must be used instead. However,
setting these properties must be disabled after initialization because
certain combinations of limits are forbidden and thus configuration
changes should be done in one transaction. The individual properties
will go away when support for non-scalar values in CLI is implemented
and thus are marked as experimental.
ThrottleGroup also has a `limits` property that uses the ThrottleLimits
struct. It can be used to create ThrottleGroups or set the
configuration in existing groups as follows:
{ "execute": "object-add",
"arguments": {
"qom-type": "throttle-group",
"id": "foo",
"props" : {
"limits": {
"iops-total": 100
}
}
}
}
{ "execute" : "qom-set",
"arguments" : {
"path" : "foo",
"property" : "limits",
"value" : {
"iops-total" : 99
}
}
}
This also means a group's configuration can be fetched with qom-get.
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2017-08-25 16:20:26 +03:00
|
|
|
module_call_init(MODULE_INIT_QOM);
|
2014-05-14 18:22:45 +04:00
|
|
|
|
2013-09-02 16:14:38 +04:00
|
|
|
do {} while (g_main_context_iteration(NULL, false));
|
|
|
|
|
|
|
|
/* tests in the same order as the header function declarations */
|
|
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
g_test_add_func("/throttle/leak_bucket", test_leak_bucket);
|
|
|
|
g_test_add_func("/throttle/compute_wait", test_compute_wait);
|
|
|
|
g_test_add_func("/throttle/init", test_init);
|
2023-07-28 05:20:01 +03:00
|
|
|
g_test_add_func("/throttle/init_readonly", test_init_readonly);
|
|
|
|
g_test_add_func("/throttle/init_writeonly", test_init_writeonly);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_test_add_func("/throttle/destroy", test_destroy);
|
|
|
|
g_test_add_func("/throttle/have_timer", test_have_timer);
|
2014-05-14 18:22:46 +04:00
|
|
|
g_test_add_func("/throttle/detach_attach", test_detach_attach);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_test_add_func("/throttle/config/enabled", test_enabled);
|
|
|
|
g_test_add_func("/throttle/config/conflicting", test_conflicting_config);
|
|
|
|
g_test_add_func("/throttle/config/is_valid", test_is_valid);
|
2017-08-24 16:24:49 +03:00
|
|
|
g_test_add_func("/throttle/config/ranges", test_ranges);
|
2015-08-04 13:22:13 +03:00
|
|
|
g_test_add_func("/throttle/config/max", test_max_is_missing_limit);
|
2016-06-02 03:40:31 +03:00
|
|
|
g_test_add_func("/throttle/config/iops_size",
|
|
|
|
test_iops_size_is_missing_limit);
|
2013-09-02 16:14:38 +04:00
|
|
|
g_test_add_func("/throttle/config_functions", test_config_functions);
|
|
|
|
g_test_add_func("/throttle/accounting", test_accounting);
|
2015-06-08 19:17:43 +03:00
|
|
|
g_test_add_func("/throttle/groups", test_groups);
|
2013-09-02 16:14:38 +04:00
|
|
|
return g_test_run();
|
|
|
|
}
|
|
|
|
|