15ddfffbdSBenoît Canet /*
25ddfffbdSBenoît Canet * QEMU throttling infrastructure
35ddfffbdSBenoît Canet *
4a291d5d9SAlberto Garcia * Copyright (C) Nodalink, EURL. 2013-2014
5a291d5d9SAlberto Garcia * Copyright (C) Igalia, S.L. 2015
65ddfffbdSBenoît Canet *
7a291d5d9SAlberto Garcia * Authors:
8a291d5d9SAlberto Garcia * Benoît Canet <benoit.canet@nodalink.com>
9a291d5d9SAlberto Garcia * Alberto Garcia <berto@igalia.com>
105ddfffbdSBenoît Canet *
115ddfffbdSBenoît Canet * This program is free software; you can redistribute it and/or
125ddfffbdSBenoît Canet * modify it under the terms of the GNU General Public License as
135ddfffbdSBenoît Canet * published by the Free Software Foundation; either version 2 or
145ddfffbdSBenoît Canet * (at your option) version 3 of the License.
155ddfffbdSBenoît Canet *
165ddfffbdSBenoît Canet * This program is distributed in the hope that it will be useful,
175ddfffbdSBenoît Canet * but WITHOUT ANY WARRANTY; without even the implied warranty of
185ddfffbdSBenoît Canet * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
195ddfffbdSBenoît Canet * GNU General Public License for more details.
205ddfffbdSBenoît Canet *
215ddfffbdSBenoît Canet * You should have received a copy of the GNU General Public License
225ddfffbdSBenoît Canet * along with this program; if not, see <http://www.gnu.org/licenses/>.
235ddfffbdSBenoît Canet */
245ddfffbdSBenoît Canet
25aafd7584SPeter Maydell #include "qemu/osdep.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
275ddfffbdSBenoît Canet #include "qemu/throttle.h"
285ddfffbdSBenoît Canet #include "qemu/timer.h"
2913af91ebSStefan Hajnoczi #include "block/aio.h"
305ddfffbdSBenoît Canet
315ddfffbdSBenoît Canet /* This function make a bucket leak
325ddfffbdSBenoît Canet *
335ddfffbdSBenoît Canet * @bkt: the bucket to make leak
345ddfffbdSBenoît Canet * @delta_ns: the time delta
355ddfffbdSBenoît Canet */
throttle_leak_bucket(LeakyBucket * bkt,int64_t delta_ns)365ddfffbdSBenoît Canet void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta_ns)
375ddfffbdSBenoît Canet {
385ddfffbdSBenoît Canet double leak;
395ddfffbdSBenoît Canet
405ddfffbdSBenoît Canet /* compute how much to leak */
4113566fe3SStefan Hajnoczi leak = (bkt->avg * (double) delta_ns) / NANOSECONDS_PER_SECOND;
425ddfffbdSBenoît Canet
435ddfffbdSBenoît Canet /* make the bucket leak */
445ddfffbdSBenoît Canet bkt->level = MAX(bkt->level - leak, 0);
45100f8f26SAlberto Garcia
46100f8f26SAlberto Garcia /* if we allow bursts for more than one second we also need to
47100f8f26SAlberto Garcia * keep track of bkt->burst_level so the bkt->max goal per second
48100f8f26SAlberto Garcia * is attained */
49100f8f26SAlberto Garcia if (bkt->burst_length > 1) {
50100f8f26SAlberto Garcia leak = (bkt->max * (double) delta_ns) / NANOSECONDS_PER_SECOND;
51100f8f26SAlberto Garcia bkt->burst_level = MAX(bkt->burst_level - leak, 0);
52100f8f26SAlberto Garcia }
535ddfffbdSBenoît Canet }
545ddfffbdSBenoît Canet
555ddfffbdSBenoît Canet /* Calculate the time delta since last leak and make proportionals leaks
565ddfffbdSBenoît Canet *
575ddfffbdSBenoît Canet * @now: the current timestamp in ns
585ddfffbdSBenoît Canet */
throttle_do_leak(ThrottleState * ts,int64_t now)595ddfffbdSBenoît Canet static void throttle_do_leak(ThrottleState *ts, int64_t now)
605ddfffbdSBenoît Canet {
615ddfffbdSBenoît Canet /* compute the time elapsed since the last leak */
625ddfffbdSBenoît Canet int64_t delta_ns = now - ts->previous_leak;
635ddfffbdSBenoît Canet int i;
645ddfffbdSBenoît Canet
655ddfffbdSBenoît Canet ts->previous_leak = now;
665ddfffbdSBenoît Canet
675ddfffbdSBenoît Canet if (delta_ns <= 0) {
685ddfffbdSBenoît Canet return;
695ddfffbdSBenoît Canet }
705ddfffbdSBenoît Canet
715ddfffbdSBenoît Canet /* make each bucket leak */
725ddfffbdSBenoît Canet for (i = 0; i < BUCKETS_COUNT; i++) {
735ddfffbdSBenoît Canet throttle_leak_bucket(&ts->cfg.buckets[i], delta_ns);
745ddfffbdSBenoît Canet }
755ddfffbdSBenoît Canet }
765ddfffbdSBenoît Canet
775ddfffbdSBenoît Canet /* do the real job of computing the time to wait
785ddfffbdSBenoît Canet *
795ddfffbdSBenoît Canet * @limit: the throttling limit
805ddfffbdSBenoît Canet * @extra: the number of operation to delay
815ddfffbdSBenoît Canet * @ret: the time to wait in ns
825ddfffbdSBenoît Canet */
throttle_do_compute_wait(double limit,double extra)835ddfffbdSBenoît Canet static int64_t throttle_do_compute_wait(double limit, double extra)
845ddfffbdSBenoît Canet {
8513566fe3SStefan Hajnoczi double wait = extra * NANOSECONDS_PER_SECOND;
865ddfffbdSBenoît Canet wait /= limit;
875ddfffbdSBenoît Canet return wait;
885ddfffbdSBenoît Canet }
895ddfffbdSBenoît Canet
905ddfffbdSBenoît Canet /* This function compute the wait time in ns that a leaky bucket should trigger
915ddfffbdSBenoît Canet *
925ddfffbdSBenoît Canet * @bkt: the leaky bucket we operate on
935ddfffbdSBenoît Canet * @ret: the resulting wait time in ns or 0 if the operation can go through
945ddfffbdSBenoît Canet */
throttle_compute_wait(LeakyBucket * bkt)955ddfffbdSBenoît Canet int64_t throttle_compute_wait(LeakyBucket *bkt)
965ddfffbdSBenoît Canet {
975ddfffbdSBenoît Canet double extra; /* the number of extra units blocking the io */
982a8be39eSAlberto Garcia double bucket_size; /* I/O before throttling to bkt->avg */
992a8be39eSAlberto Garcia double burst_bucket_size; /* Before throttling to bkt->max */
1005ddfffbdSBenoît Canet
1015ddfffbdSBenoît Canet if (!bkt->avg) {
1025ddfffbdSBenoît Canet return 0;
1035ddfffbdSBenoît Canet }
1045ddfffbdSBenoît Canet
1052a8be39eSAlberto Garcia if (!bkt->max) {
1062a8be39eSAlberto Garcia /* If bkt->max is 0 we still want to allow short bursts of I/O
1072a8be39eSAlberto Garcia * from the guest, otherwise every other request will be throttled
1082a8be39eSAlberto Garcia * and performance will suffer considerably. */
109d00e6923SAlberto Garcia bucket_size = (double) bkt->avg / 10;
1102a8be39eSAlberto Garcia burst_bucket_size = 0;
1112a8be39eSAlberto Garcia } else {
1122a8be39eSAlberto Garcia /* If we have a burst limit then we have to wait until all I/O
1132a8be39eSAlberto Garcia * at burst rate has finished before throttling to bkt->avg */
1142a8be39eSAlberto Garcia bucket_size = bkt->max * bkt->burst_length;
115d00e6923SAlberto Garcia burst_bucket_size = (double) bkt->max / 10;
1162a8be39eSAlberto Garcia }
1172a8be39eSAlberto Garcia
1182a8be39eSAlberto Garcia /* If the main bucket is full then we have to wait */
1192a8be39eSAlberto Garcia extra = bkt->level - bucket_size;
120100f8f26SAlberto Garcia if (extra > 0) {
121100f8f26SAlberto Garcia return throttle_do_compute_wait(bkt->avg, extra);
1225ddfffbdSBenoît Canet }
1235ddfffbdSBenoît Canet
1242a8be39eSAlberto Garcia /* If the main bucket is not full yet we still have to check the
1252a8be39eSAlberto Garcia * burst bucket in order to enforce the burst limit */
126100f8f26SAlberto Garcia if (bkt->burst_length > 1) {
127b5806108SAlberto Garcia assert(bkt->max > 0); /* see throttle_is_valid() */
1282a8be39eSAlberto Garcia extra = bkt->burst_level - burst_bucket_size;
129100f8f26SAlberto Garcia if (extra > 0) {
130100f8f26SAlberto Garcia return throttle_do_compute_wait(bkt->max, extra);
131100f8f26SAlberto Garcia }
132100f8f26SAlberto Garcia }
133100f8f26SAlberto Garcia
134100f8f26SAlberto Garcia return 0;
1355ddfffbdSBenoît Canet }
1365ddfffbdSBenoît Canet
1375ddfffbdSBenoît Canet /* This function compute the time that must be waited while this IO
1385ddfffbdSBenoît Canet *
139e76f201fSzhenwei pi * @direction: throttle direction
1405ddfffbdSBenoît Canet * @ret: time to wait
1415ddfffbdSBenoît Canet */
throttle_compute_wait_for(ThrottleState * ts,ThrottleDirection direction)1425ddfffbdSBenoît Canet static int64_t throttle_compute_wait_for(ThrottleState *ts,
143e76f201fSzhenwei pi ThrottleDirection direction)
1445ddfffbdSBenoît Canet {
145*70173138Szhenwei pi static const BucketType to_check[THROTTLE_MAX][4] = {
146*70173138Szhenwei pi {THROTTLE_BPS_TOTAL,
1475ddfffbdSBenoît Canet THROTTLE_OPS_TOTAL,
1485ddfffbdSBenoît Canet THROTTLE_BPS_READ,
1495ddfffbdSBenoît Canet THROTTLE_OPS_READ},
1505ddfffbdSBenoît Canet {THROTTLE_BPS_TOTAL,
1515ddfffbdSBenoît Canet THROTTLE_OPS_TOTAL,
1525ddfffbdSBenoît Canet THROTTLE_BPS_WRITE,
1535ddfffbdSBenoît Canet THROTTLE_OPS_WRITE}, };
1545ddfffbdSBenoît Canet int64_t wait, max_wait = 0;
1555ddfffbdSBenoît Canet int i;
1565ddfffbdSBenoît Canet
157*70173138Szhenwei pi for (i = 0; i < ARRAY_SIZE(to_check[THROTTLE_READ]); i++) {
158e76f201fSzhenwei pi BucketType index = to_check[direction][i];
1595ddfffbdSBenoît Canet wait = throttle_compute_wait(&ts->cfg.buckets[index]);
1605ddfffbdSBenoît Canet if (wait > max_wait) {
1615ddfffbdSBenoît Canet max_wait = wait;
1625ddfffbdSBenoît Canet }
1635ddfffbdSBenoît Canet }
1645ddfffbdSBenoît Canet
1655ddfffbdSBenoît Canet return max_wait;
1665ddfffbdSBenoît Canet }
1675ddfffbdSBenoît Canet
1685ddfffbdSBenoît Canet /* compute the timer for this type of operation
1695ddfffbdSBenoît Canet *
170e76f201fSzhenwei pi * @direction: throttle direction
1715ddfffbdSBenoît Canet * @now: the current clock timestamp
1725ddfffbdSBenoît Canet * @next_timestamp: the resulting timer
1735ddfffbdSBenoît Canet * @ret: true if a timer must be set
1745ddfffbdSBenoît Canet */
throttle_compute_timer(ThrottleState * ts,ThrottleDirection direction,int64_t now,int64_t * next_timestamp)1753c9242f5SAlberto Garcia static bool throttle_compute_timer(ThrottleState *ts,
176e76f201fSzhenwei pi ThrottleDirection direction,
1775ddfffbdSBenoît Canet int64_t now,
1785ddfffbdSBenoît Canet int64_t *next_timestamp)
1795ddfffbdSBenoît Canet {
1805ddfffbdSBenoît Canet int64_t wait;
1815ddfffbdSBenoît Canet
1825ddfffbdSBenoît Canet /* leak proportionally to the time elapsed */
1835ddfffbdSBenoît Canet throttle_do_leak(ts, now);
1845ddfffbdSBenoît Canet
1855ddfffbdSBenoît Canet /* compute the wait time if any */
186e76f201fSzhenwei pi wait = throttle_compute_wait_for(ts, direction);
1875ddfffbdSBenoît Canet
1885ddfffbdSBenoît Canet /* if the code must wait compute when the next timer should fire */
1895ddfffbdSBenoît Canet if (wait) {
1905ddfffbdSBenoît Canet *next_timestamp = now + wait;
1915ddfffbdSBenoît Canet return true;
1925ddfffbdSBenoît Canet }
1935ddfffbdSBenoît Canet
1945ddfffbdSBenoît Canet /* else no need to wait at all */
1955ddfffbdSBenoît Canet *next_timestamp = now;
1965ddfffbdSBenoît Canet return false;
1975ddfffbdSBenoît Canet }
1985ddfffbdSBenoît Canet
19913af91ebSStefan Hajnoczi /* Add timers to event loop */
throttle_timers_attach_aio_context(ThrottleTimers * tt,AioContext * new_context)2000e5b0a2dSBenoît Canet void throttle_timers_attach_aio_context(ThrottleTimers *tt,
2010e5b0a2dSBenoît Canet AioContext *new_context)
20213af91ebSStefan Hajnoczi {
203d85b08c6Szhenwei pi ThrottleDirection dir;
204d85b08c6Szhenwei pi
205d85b08c6Szhenwei pi for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
206d85b08c6Szhenwei pi if (tt->timer_cb[dir]) {
207d85b08c6Szhenwei pi tt->timers[dir] =
2088ba02c24Szhenwei pi aio_timer_new(new_context, tt->clock_type, SCALE_NS,
209d85b08c6Szhenwei pi tt->timer_cb[dir], tt->timer_opaque);
210d85b08c6Szhenwei pi }
211d85b08c6Szhenwei pi }
21213af91ebSStefan Hajnoczi }
21313af91ebSStefan Hajnoczi
2141588ab5dSAlberto Garcia /*
2151588ab5dSAlberto Garcia * Initialize the ThrottleConfig structure to a valid state
2161588ab5dSAlberto Garcia * @cfg: the config to initialize
2171588ab5dSAlberto Garcia */
throttle_config_init(ThrottleConfig * cfg)2181588ab5dSAlberto Garcia void throttle_config_init(ThrottleConfig *cfg)
2191588ab5dSAlberto Garcia {
220100f8f26SAlberto Garcia unsigned i;
2211588ab5dSAlberto Garcia memset(cfg, 0, sizeof(*cfg));
222100f8f26SAlberto Garcia for (i = 0; i < BUCKETS_COUNT; i++) {
223100f8f26SAlberto Garcia cfg->buckets[i].burst_length = 1;
224100f8f26SAlberto Garcia }
2251588ab5dSAlberto Garcia }
2261588ab5dSAlberto Garcia
2275ddfffbdSBenoît Canet /* To be called first on the ThrottleState */
throttle_init(ThrottleState * ts)2280e5b0a2dSBenoît Canet void throttle_init(ThrottleState *ts)
2290e5b0a2dSBenoît Canet {
2300e5b0a2dSBenoît Canet memset(ts, 0, sizeof(ThrottleState));
2311588ab5dSAlberto Garcia throttle_config_init(&ts->cfg);
2320e5b0a2dSBenoît Canet }
2330e5b0a2dSBenoît Canet
2340e5b0a2dSBenoît Canet /* To be called first on the ThrottleTimers */
throttle_timers_init(ThrottleTimers * tt,AioContext * aio_context,QEMUClockType clock_type,QEMUTimerCB * read_timer_cb,QEMUTimerCB * write_timer_cb,void * timer_opaque)2350e5b0a2dSBenoît Canet void throttle_timers_init(ThrottleTimers *tt,
23613af91ebSStefan Hajnoczi AioContext *aio_context,
2375ddfffbdSBenoît Canet QEMUClockType clock_type,
2385ddfffbdSBenoît Canet QEMUTimerCB *read_timer_cb,
2395ddfffbdSBenoît Canet QEMUTimerCB *write_timer_cb,
2405ddfffbdSBenoît Canet void *timer_opaque)
2415ddfffbdSBenoît Canet {
242d85b08c6Szhenwei pi assert(read_timer_cb || write_timer_cb);
2430e5b0a2dSBenoît Canet memset(tt, 0, sizeof(ThrottleTimers));
2445ddfffbdSBenoît Canet
2450e5b0a2dSBenoît Canet tt->clock_type = clock_type;
2468ba02c24Szhenwei pi tt->timer_cb[THROTTLE_READ] = read_timer_cb;
2478ba02c24Szhenwei pi tt->timer_cb[THROTTLE_WRITE] = write_timer_cb;
2480e5b0a2dSBenoît Canet tt->timer_opaque = timer_opaque;
2490e5b0a2dSBenoît Canet throttle_timers_attach_aio_context(tt, aio_context);
2505ddfffbdSBenoît Canet }
2515ddfffbdSBenoît Canet
2525ddfffbdSBenoît Canet /* destroy a timer */
throttle_timer_destroy(QEMUTimer ** timer)2535ddfffbdSBenoît Canet static void throttle_timer_destroy(QEMUTimer **timer)
2545ddfffbdSBenoît Canet {
255d85b08c6Szhenwei pi if (*timer == NULL) {
256d85b08c6Szhenwei pi return;
257d85b08c6Szhenwei pi }
2585ddfffbdSBenoît Canet
2595ddfffbdSBenoît Canet timer_free(*timer);
2605ddfffbdSBenoît Canet *timer = NULL;
2615ddfffbdSBenoît Canet }
2625ddfffbdSBenoît Canet
26313af91ebSStefan Hajnoczi /* Remove timers from event loop */
throttle_timers_detach_aio_context(ThrottleTimers * tt)2640e5b0a2dSBenoît Canet void throttle_timers_detach_aio_context(ThrottleTimers *tt)
2655ddfffbdSBenoît Canet {
266d85b08c6Szhenwei pi ThrottleDirection dir;
2675ddfffbdSBenoît Canet
268d85b08c6Szhenwei pi for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
269d85b08c6Szhenwei pi throttle_timer_destroy(&tt->timers[dir]);
2705ddfffbdSBenoît Canet }
2715ddfffbdSBenoît Canet }
2725ddfffbdSBenoît Canet
2730e5b0a2dSBenoît Canet /* To be called last on the ThrottleTimers */
throttle_timers_destroy(ThrottleTimers * tt)2740e5b0a2dSBenoît Canet void throttle_timers_destroy(ThrottleTimers *tt)
27513af91ebSStefan Hajnoczi {
2760e5b0a2dSBenoît Canet throttle_timers_detach_aio_context(tt);
27713af91ebSStefan Hajnoczi }
27813af91ebSStefan Hajnoczi
2795ddfffbdSBenoît Canet /* is any throttling timer configured */
throttle_timers_are_initialized(ThrottleTimers * tt)2800e5b0a2dSBenoît Canet bool throttle_timers_are_initialized(ThrottleTimers *tt)
2815ddfffbdSBenoît Canet {
282d85b08c6Szhenwei pi ThrottleDirection dir;
283d85b08c6Szhenwei pi
284d85b08c6Szhenwei pi for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
285d85b08c6Szhenwei pi if (tt->timers[dir]) {
2865ddfffbdSBenoît Canet return true;
2875ddfffbdSBenoît Canet }
288d85b08c6Szhenwei pi }
2895ddfffbdSBenoît Canet
2905ddfffbdSBenoît Canet return false;
2915ddfffbdSBenoît Canet }
2925ddfffbdSBenoît Canet
2935ddfffbdSBenoît Canet /* Does any throttling must be done
2945ddfffbdSBenoît Canet *
2955ddfffbdSBenoît Canet * @cfg: the throttling configuration to inspect
2965ddfffbdSBenoît Canet * @ret: true if throttling must be done else false
2975ddfffbdSBenoît Canet */
throttle_enabled(ThrottleConfig * cfg)2985ddfffbdSBenoît Canet bool throttle_enabled(ThrottleConfig *cfg)
2995ddfffbdSBenoît Canet {
3005ddfffbdSBenoît Canet int i;
3015ddfffbdSBenoît Canet
3025ddfffbdSBenoît Canet for (i = 0; i < BUCKETS_COUNT; i++) {
3035ddfffbdSBenoît Canet if (cfg->buckets[i].avg > 0) {
3045ddfffbdSBenoît Canet return true;
3055ddfffbdSBenoît Canet }
3065ddfffbdSBenoît Canet }
3075ddfffbdSBenoît Canet
3085ddfffbdSBenoît Canet return false;
3095ddfffbdSBenoît Canet }
3105ddfffbdSBenoît Canet
311d5851089SAlberto Garcia /* check if a throttling configuration is valid
3125ddfffbdSBenoît Canet * @cfg: the throttling configuration to inspect
313d5851089SAlberto Garcia * @ret: true if valid else false
3146921b180SAlberto Garcia * @errp: error object
3155ddfffbdSBenoît Canet */
throttle_is_valid(ThrottleConfig * cfg,Error ** errp)316d5851089SAlberto Garcia bool throttle_is_valid(ThrottleConfig *cfg, Error **errp)
3175ddfffbdSBenoît Canet {
318d5851089SAlberto Garcia int i;
3195ddfffbdSBenoît Canet bool bps_flag, ops_flag;
3205ddfffbdSBenoît Canet bool bps_max_flag, ops_max_flag;
3215ddfffbdSBenoît Canet
3225ddfffbdSBenoît Canet bps_flag = cfg->buckets[THROTTLE_BPS_TOTAL].avg &&
3235ddfffbdSBenoît Canet (cfg->buckets[THROTTLE_BPS_READ].avg ||
3245ddfffbdSBenoît Canet cfg->buckets[THROTTLE_BPS_WRITE].avg);
3255ddfffbdSBenoît Canet
3265ddfffbdSBenoît Canet ops_flag = cfg->buckets[THROTTLE_OPS_TOTAL].avg &&
3275ddfffbdSBenoît Canet (cfg->buckets[THROTTLE_OPS_READ].avg ||
3285ddfffbdSBenoît Canet cfg->buckets[THROTTLE_OPS_WRITE].avg);
3295ddfffbdSBenoît Canet
3305ddfffbdSBenoît Canet bps_max_flag = cfg->buckets[THROTTLE_BPS_TOTAL].max &&
3315ddfffbdSBenoît Canet (cfg->buckets[THROTTLE_BPS_READ].max ||
3325ddfffbdSBenoît Canet cfg->buckets[THROTTLE_BPS_WRITE].max);
3335ddfffbdSBenoît Canet
3345ddfffbdSBenoît Canet ops_max_flag = cfg->buckets[THROTTLE_OPS_TOTAL].max &&
3355ddfffbdSBenoît Canet (cfg->buckets[THROTTLE_OPS_READ].max ||
3365ddfffbdSBenoît Canet cfg->buckets[THROTTLE_OPS_WRITE].max);
3375ddfffbdSBenoît Canet
3386921b180SAlberto Garcia if (bps_flag || ops_flag || bps_max_flag || ops_max_flag) {
3396921b180SAlberto Garcia error_setg(errp, "bps/iops/max total values and read/write values"
3406921b180SAlberto Garcia " cannot be used at the same time");
3416921b180SAlberto Garcia return false;
3425ddfffbdSBenoît Canet }
3435ddfffbdSBenoît Canet
3448860eabdSStefan Hajnoczi if (cfg->op_size &&
3458860eabdSStefan Hajnoczi !cfg->buckets[THROTTLE_OPS_TOTAL].avg &&
3468860eabdSStefan Hajnoczi !cfg->buckets[THROTTLE_OPS_READ].avg &&
3478860eabdSStefan Hajnoczi !cfg->buckets[THROTTLE_OPS_WRITE].avg) {
3488860eabdSStefan Hajnoczi error_setg(errp, "iops size requires an iops value to be set");
3498860eabdSStefan Hajnoczi return false;
3508860eabdSStefan Hajnoczi }
3518860eabdSStefan Hajnoczi
3525ddfffbdSBenoît Canet for (i = 0; i < BUCKETS_COUNT; i++) {
353fa36f1b2SAlberto Garcia LeakyBucket *bkt = &cfg->buckets[i];
354d00e6923SAlberto Garcia if (bkt->avg > THROTTLE_VALUE_MAX || bkt->max > THROTTLE_VALUE_MAX) {
35503ba36c8SAlberto Garcia error_setg(errp, "bps/iops/max values must be within [0, %lld]",
35603ba36c8SAlberto Garcia THROTTLE_VALUE_MAX);
357972606c4SFam Zheng return false;
3585ddfffbdSBenoît Canet }
3595ddfffbdSBenoît Canet
360fa36f1b2SAlberto Garcia if (!bkt->burst_length) {
361100f8f26SAlberto Garcia error_setg(errp, "the burst length cannot be 0");
362100f8f26SAlberto Garcia return false;
363100f8f26SAlberto Garcia }
364100f8f26SAlberto Garcia
365fa36f1b2SAlberto Garcia if (bkt->burst_length > 1 && !bkt->max) {
366100f8f26SAlberto Garcia error_setg(errp, "burst length set without burst rate");
367100f8f26SAlberto Garcia return false;
368100f8f26SAlberto Garcia }
369100f8f26SAlberto Garcia
37067335a45SAlberto Garcia if (bkt->max && bkt->burst_length > THROTTLE_VALUE_MAX / bkt->max) {
37167335a45SAlberto Garcia error_setg(errp, "burst length too high for this burst rate");
37267335a45SAlberto Garcia return false;
37367335a45SAlberto Garcia }
37467335a45SAlberto Garcia
375fa36f1b2SAlberto Garcia if (bkt->max && !bkt->avg) {
37645b2d418SAlberto Garcia error_setg(errp, "bps_max/iops_max require corresponding"
37745b2d418SAlberto Garcia " bps/iops values");
378ee2bdc33SStefan Hajnoczi return false;
379ee2bdc33SStefan Hajnoczi }
380aaa1e77fSAlberto Garcia
381fa36f1b2SAlberto Garcia if (bkt->max && bkt->max < bkt->avg) {
382aaa1e77fSAlberto Garcia error_setg(errp, "bps_max/iops_max cannot be lower than bps/iops");
383aaa1e77fSAlberto Garcia return false;
384aaa1e77fSAlberto Garcia }
385d5851089SAlberto Garcia }
386d5851089SAlberto Garcia
387d5851089SAlberto Garcia return true;
388d5851089SAlberto Garcia }
389ee2bdc33SStefan Hajnoczi
3905ddfffbdSBenoît Canet /* Used to configure the throttle
3915ddfffbdSBenoît Canet *
3925ddfffbdSBenoît Canet * @ts: the throttle state we are working on
393dbe824ccSManos Pitsidianakis * @clock_type: the group's clock_type
3945ddfffbdSBenoît Canet * @cfg: the config to set
3955ddfffbdSBenoît Canet */
throttle_config(ThrottleState * ts,QEMUClockType clock_type,ThrottleConfig * cfg)3960e5b0a2dSBenoît Canet void throttle_config(ThrottleState *ts,
397dbe824ccSManos Pitsidianakis QEMUClockType clock_type,
3980e5b0a2dSBenoît Canet ThrottleConfig *cfg)
3995ddfffbdSBenoît Canet {
4005ddfffbdSBenoît Canet int i;
4015ddfffbdSBenoît Canet
4025ddfffbdSBenoît Canet ts->cfg = *cfg;
4035ddfffbdSBenoît Canet
4042a8be39eSAlberto Garcia /* Zero bucket level */
4055ddfffbdSBenoît Canet for (i = 0; i < BUCKETS_COUNT; i++) {
4062a8be39eSAlberto Garcia ts->cfg.buckets[i].level = 0;
4072a8be39eSAlberto Garcia ts->cfg.buckets[i].burst_level = 0;
4085ddfffbdSBenoît Canet }
4095ddfffbdSBenoît Canet
410dbe824ccSManos Pitsidianakis ts->previous_leak = qemu_clock_get_ns(clock_type);
4115ddfffbdSBenoît Canet }
4125ddfffbdSBenoît Canet
4135ddfffbdSBenoît Canet /* used to get config
4145ddfffbdSBenoît Canet *
4155ddfffbdSBenoît Canet * @ts: the throttle state we are working on
4165ddfffbdSBenoît Canet * @cfg: the config to write
4175ddfffbdSBenoît Canet */
throttle_get_config(ThrottleState * ts,ThrottleConfig * cfg)4185ddfffbdSBenoît Canet void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
4195ddfffbdSBenoît Canet {
4205ddfffbdSBenoît Canet *cfg = ts->cfg;
4215ddfffbdSBenoît Canet }
4225ddfffbdSBenoît Canet
4235ddfffbdSBenoît Canet
4245ddfffbdSBenoît Canet /* Schedule the read or write timer if needed
4255ddfffbdSBenoît Canet *
4265ddfffbdSBenoît Canet * NOTE: this function is not unit tested due to it's usage of timer_mod
4275ddfffbdSBenoît Canet *
4280e5b0a2dSBenoît Canet * @tt: the timers structure
429e76f201fSzhenwei pi * @direction: throttle direction
4305ddfffbdSBenoît Canet * @ret: true if the timer has been scheduled else false
4315ddfffbdSBenoît Canet */
throttle_schedule_timer(ThrottleState * ts,ThrottleTimers * tt,ThrottleDirection direction)4320e5b0a2dSBenoît Canet bool throttle_schedule_timer(ThrottleState *ts,
4330e5b0a2dSBenoît Canet ThrottleTimers *tt,
434e76f201fSzhenwei pi ThrottleDirection direction)
4355ddfffbdSBenoît Canet {
4360e5b0a2dSBenoît Canet int64_t now = qemu_clock_get_ns(tt->clock_type);
4375ddfffbdSBenoît Canet int64_t next_timestamp;
438d85b08c6Szhenwei pi QEMUTimer *timer;
4395ddfffbdSBenoît Canet bool must_wait;
4405ddfffbdSBenoît Canet
441e76f201fSzhenwei pi assert(direction < THROTTLE_MAX);
442e76f201fSzhenwei pi timer = tt->timers[direction];
443d85b08c6Szhenwei pi assert(timer);
444d85b08c6Szhenwei pi
4455ddfffbdSBenoît Canet must_wait = throttle_compute_timer(ts,
446e76f201fSzhenwei pi direction,
4475ddfffbdSBenoît Canet now,
4485ddfffbdSBenoît Canet &next_timestamp);
4495ddfffbdSBenoît Canet
4505ddfffbdSBenoît Canet /* request not throttled */
4515ddfffbdSBenoît Canet if (!must_wait) {
4525ddfffbdSBenoît Canet return false;
4535ddfffbdSBenoît Canet }
4545ddfffbdSBenoît Canet
4555ddfffbdSBenoît Canet /* request throttled and timer pending -> do nothing */
456d85b08c6Szhenwei pi if (timer_pending(timer)) {
4575ddfffbdSBenoît Canet return true;
4585ddfffbdSBenoît Canet }
4595ddfffbdSBenoît Canet
4605ddfffbdSBenoît Canet /* request throttled and timer not pending -> arm timer */
461d85b08c6Szhenwei pi timer_mod(timer, next_timestamp);
4625ddfffbdSBenoît Canet return true;
4635ddfffbdSBenoît Canet }
4645ddfffbdSBenoît Canet
4655ddfffbdSBenoît Canet /* do the accounting for this operation
4665ddfffbdSBenoît Canet *
467e76f201fSzhenwei pi * @direction: throttle direction
4685ddfffbdSBenoît Canet * @size: the size of the operation
4695ddfffbdSBenoît Canet */
throttle_account(ThrottleState * ts,ThrottleDirection direction,uint64_t size)470e76f201fSzhenwei pi void throttle_account(ThrottleState *ts, ThrottleDirection direction,
471e76f201fSzhenwei pi uint64_t size)
4725ddfffbdSBenoît Canet {
473*70173138Szhenwei pi static const BucketType bucket_types_size[THROTTLE_MAX][2] = {
474100f8f26SAlberto Garcia { THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ },
475100f8f26SAlberto Garcia { THROTTLE_BPS_TOTAL, THROTTLE_BPS_WRITE }
476100f8f26SAlberto Garcia };
477*70173138Szhenwei pi static const BucketType bucket_types_units[THROTTLE_MAX][2] = {
478100f8f26SAlberto Garcia { THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ },
479100f8f26SAlberto Garcia { THROTTLE_OPS_TOTAL, THROTTLE_OPS_WRITE }
480100f8f26SAlberto Garcia };
4815ddfffbdSBenoît Canet double units = 1.0;
482100f8f26SAlberto Garcia unsigned i;
4835ddfffbdSBenoît Canet
484e76f201fSzhenwei pi assert(direction < THROTTLE_MAX);
4855ddfffbdSBenoît Canet /* if cfg.op_size is defined and smaller than size we compute unit count */
4865ddfffbdSBenoît Canet if (ts->cfg.op_size && size > ts->cfg.op_size) {
4875ddfffbdSBenoît Canet units = (double) size / ts->cfg.op_size;
4885ddfffbdSBenoît Canet }
4895ddfffbdSBenoît Canet
490*70173138Szhenwei pi for (i = 0; i < ARRAY_SIZE(bucket_types_size[THROTTLE_READ]); i++) {
491100f8f26SAlberto Garcia LeakyBucket *bkt;
4925ddfffbdSBenoît Canet
493e76f201fSzhenwei pi bkt = &ts->cfg.buckets[bucket_types_size[direction][i]];
494100f8f26SAlberto Garcia bkt->level += size;
495100f8f26SAlberto Garcia if (bkt->burst_length > 1) {
496100f8f26SAlberto Garcia bkt->burst_level += size;
497100f8f26SAlberto Garcia }
498100f8f26SAlberto Garcia
499e76f201fSzhenwei pi bkt = &ts->cfg.buckets[bucket_types_units[direction][i]];
500100f8f26SAlberto Garcia bkt->level += units;
501100f8f26SAlberto Garcia if (bkt->burst_length > 1) {
502100f8f26SAlberto Garcia bkt->burst_level += units;
503100f8f26SAlberto Garcia }
5045ddfffbdSBenoît Canet }
5055ddfffbdSBenoît Canet }
5065ddfffbdSBenoît Canet
507432d889eSManos Pitsidianakis /* return a ThrottleConfig based on the options in a ThrottleLimits
508432d889eSManos Pitsidianakis *
509432d889eSManos Pitsidianakis * @arg: the ThrottleLimits object to read from
510432d889eSManos Pitsidianakis * @cfg: the ThrottleConfig to edit
511432d889eSManos Pitsidianakis * @errp: error object
512432d889eSManos Pitsidianakis */
throttle_limits_to_config(ThrottleLimits * arg,ThrottleConfig * cfg,Error ** errp)513432d889eSManos Pitsidianakis void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
514432d889eSManos Pitsidianakis Error **errp)
515432d889eSManos Pitsidianakis {
516432d889eSManos Pitsidianakis if (arg->has_bps_total) {
517432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
518432d889eSManos Pitsidianakis }
519432d889eSManos Pitsidianakis if (arg->has_bps_read) {
520432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_READ].avg = arg->bps_read;
521432d889eSManos Pitsidianakis }
522432d889eSManos Pitsidianakis if (arg->has_bps_write) {
523432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
524432d889eSManos Pitsidianakis }
525432d889eSManos Pitsidianakis
526432d889eSManos Pitsidianakis if (arg->has_iops_total) {
527432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
528432d889eSManos Pitsidianakis }
529432d889eSManos Pitsidianakis if (arg->has_iops_read) {
530432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_READ].avg = arg->iops_read;
531432d889eSManos Pitsidianakis }
532432d889eSManos Pitsidianakis if (arg->has_iops_write) {
533432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
534432d889eSManos Pitsidianakis }
535432d889eSManos Pitsidianakis
536432d889eSManos Pitsidianakis if (arg->has_bps_total_max) {
537432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
538432d889eSManos Pitsidianakis }
539432d889eSManos Pitsidianakis if (arg->has_bps_read_max) {
540432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
541432d889eSManos Pitsidianakis }
542432d889eSManos Pitsidianakis if (arg->has_bps_write_max) {
543432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
544432d889eSManos Pitsidianakis }
545432d889eSManos Pitsidianakis if (arg->has_iops_total_max) {
546432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
547432d889eSManos Pitsidianakis }
548432d889eSManos Pitsidianakis if (arg->has_iops_read_max) {
549432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
550432d889eSManos Pitsidianakis }
551432d889eSManos Pitsidianakis if (arg->has_iops_write_max) {
552432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
553432d889eSManos Pitsidianakis }
554432d889eSManos Pitsidianakis
555432d889eSManos Pitsidianakis if (arg->has_bps_total_max_length) {
556432d889eSManos Pitsidianakis if (arg->bps_total_max_length > UINT_MAX) {
557432d889eSManos Pitsidianakis error_setg(errp, "bps-total-max-length value must be in"
558432d889eSManos Pitsidianakis " the range [0, %u]", UINT_MAX);
559432d889eSManos Pitsidianakis return;
560432d889eSManos Pitsidianakis }
561432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length;
562432d889eSManos Pitsidianakis }
563432d889eSManos Pitsidianakis if (arg->has_bps_read_max_length) {
564432d889eSManos Pitsidianakis if (arg->bps_read_max_length > UINT_MAX) {
565432d889eSManos Pitsidianakis error_setg(errp, "bps-read-max-length value must be in"
566432d889eSManos Pitsidianakis " the range [0, %u]", UINT_MAX);
567432d889eSManos Pitsidianakis return;
568432d889eSManos Pitsidianakis }
569432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length;
570432d889eSManos Pitsidianakis }
571432d889eSManos Pitsidianakis if (arg->has_bps_write_max_length) {
572432d889eSManos Pitsidianakis if (arg->bps_write_max_length > UINT_MAX) {
573432d889eSManos Pitsidianakis error_setg(errp, "bps-write-max-length value must be in"
574432d889eSManos Pitsidianakis " the range [0, %u]", UINT_MAX);
575432d889eSManos Pitsidianakis return;
576432d889eSManos Pitsidianakis }
577432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length;
578432d889eSManos Pitsidianakis }
579432d889eSManos Pitsidianakis if (arg->has_iops_total_max_length) {
580432d889eSManos Pitsidianakis if (arg->iops_total_max_length > UINT_MAX) {
581432d889eSManos Pitsidianakis error_setg(errp, "iops-total-max-length value must be in"
582432d889eSManos Pitsidianakis " the range [0, %u]", UINT_MAX);
583432d889eSManos Pitsidianakis return;
584432d889eSManos Pitsidianakis }
585432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length;
586432d889eSManos Pitsidianakis }
587432d889eSManos Pitsidianakis if (arg->has_iops_read_max_length) {
588432d889eSManos Pitsidianakis if (arg->iops_read_max_length > UINT_MAX) {
589432d889eSManos Pitsidianakis error_setg(errp, "iops-read-max-length value must be in"
590432d889eSManos Pitsidianakis " the range [0, %u]", UINT_MAX);
591432d889eSManos Pitsidianakis return;
592432d889eSManos Pitsidianakis }
593432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length;
594432d889eSManos Pitsidianakis }
595432d889eSManos Pitsidianakis if (arg->has_iops_write_max_length) {
596432d889eSManos Pitsidianakis if (arg->iops_write_max_length > UINT_MAX) {
597432d889eSManos Pitsidianakis error_setg(errp, "iops-write-max-length value must be in"
598432d889eSManos Pitsidianakis " the range [0, %u]", UINT_MAX);
599432d889eSManos Pitsidianakis return;
600432d889eSManos Pitsidianakis }
601432d889eSManos Pitsidianakis cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length;
602432d889eSManos Pitsidianakis }
603432d889eSManos Pitsidianakis
604432d889eSManos Pitsidianakis if (arg->has_iops_size) {
605432d889eSManos Pitsidianakis cfg->op_size = arg->iops_size;
606432d889eSManos Pitsidianakis }
607432d889eSManos Pitsidianakis
608432d889eSManos Pitsidianakis throttle_is_valid(cfg, errp);
609432d889eSManos Pitsidianakis }
610432d889eSManos Pitsidianakis
611432d889eSManos Pitsidianakis /* write the options of a ThrottleConfig to a ThrottleLimits
612432d889eSManos Pitsidianakis *
613432d889eSManos Pitsidianakis * @cfg: the ThrottleConfig to read from
614432d889eSManos Pitsidianakis * @var: the ThrottleLimits to write to
615432d889eSManos Pitsidianakis */
throttle_config_to_limits(ThrottleConfig * cfg,ThrottleLimits * var)616432d889eSManos Pitsidianakis void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var)
617432d889eSManos Pitsidianakis {
618432d889eSManos Pitsidianakis var->bps_total = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
619432d889eSManos Pitsidianakis var->bps_read = cfg->buckets[THROTTLE_BPS_READ].avg;
620432d889eSManos Pitsidianakis var->bps_write = cfg->buckets[THROTTLE_BPS_WRITE].avg;
621432d889eSManos Pitsidianakis var->iops_total = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
622432d889eSManos Pitsidianakis var->iops_read = cfg->buckets[THROTTLE_OPS_READ].avg;
623432d889eSManos Pitsidianakis var->iops_write = cfg->buckets[THROTTLE_OPS_WRITE].avg;
624432d889eSManos Pitsidianakis var->bps_total_max = cfg->buckets[THROTTLE_BPS_TOTAL].max;
625432d889eSManos Pitsidianakis var->bps_read_max = cfg->buckets[THROTTLE_BPS_READ].max;
626432d889eSManos Pitsidianakis var->bps_write_max = cfg->buckets[THROTTLE_BPS_WRITE].max;
627432d889eSManos Pitsidianakis var->iops_total_max = cfg->buckets[THROTTLE_OPS_TOTAL].max;
628432d889eSManos Pitsidianakis var->iops_read_max = cfg->buckets[THROTTLE_OPS_READ].max;
629432d889eSManos Pitsidianakis var->iops_write_max = cfg->buckets[THROTTLE_OPS_WRITE].max;
630432d889eSManos Pitsidianakis var->bps_total_max_length = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
631432d889eSManos Pitsidianakis var->bps_read_max_length = cfg->buckets[THROTTLE_BPS_READ].burst_length;
632432d889eSManos Pitsidianakis var->bps_write_max_length = cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
633432d889eSManos Pitsidianakis var->iops_total_max_length = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
634432d889eSManos Pitsidianakis var->iops_read_max_length = cfg->buckets[THROTTLE_OPS_READ].burst_length;
635432d889eSManos Pitsidianakis var->iops_write_max_length = cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
636432d889eSManos Pitsidianakis var->iops_size = cfg->op_size;
637432d889eSManos Pitsidianakis
638432d889eSManos Pitsidianakis var->has_bps_total = true;
639432d889eSManos Pitsidianakis var->has_bps_read = true;
640432d889eSManos Pitsidianakis var->has_bps_write = true;
641432d889eSManos Pitsidianakis var->has_iops_total = true;
642432d889eSManos Pitsidianakis var->has_iops_read = true;
643432d889eSManos Pitsidianakis var->has_iops_write = true;
644432d889eSManos Pitsidianakis var->has_bps_total_max = true;
645432d889eSManos Pitsidianakis var->has_bps_read_max = true;
646432d889eSManos Pitsidianakis var->has_bps_write_max = true;
647432d889eSManos Pitsidianakis var->has_iops_total_max = true;
648432d889eSManos Pitsidianakis var->has_iops_read_max = true;
649432d889eSManos Pitsidianakis var->has_iops_write_max = true;
650432d889eSManos Pitsidianakis var->has_bps_read_max_length = true;
651432d889eSManos Pitsidianakis var->has_bps_total_max_length = true;
652432d889eSManos Pitsidianakis var->has_bps_write_max_length = true;
653432d889eSManos Pitsidianakis var->has_iops_total_max_length = true;
654432d889eSManos Pitsidianakis var->has_iops_read_max_length = true;
655432d889eSManos Pitsidianakis var->has_iops_write_max_length = true;
656432d889eSManos Pitsidianakis var->has_iops_size = true;
657432d889eSManos Pitsidianakis }
658