xref: /openbmc/qemu/util/throttle.c (revision 70173138)
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