From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57062) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aXtQM-0006ib-CG for qemu-devel@nongnu.org; Mon, 22 Feb 2016 11:33:31 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aXtQK-0004j2-Mu for qemu-devel@nongnu.org; Mon, 22 Feb 2016 11:33:26 -0500 From: Kevin Wolf Date: Mon, 22 Feb 2016 17:32:39 +0100 Message-Id: <1456158772-9344-22-git-send-email-kwolf@redhat.com> In-Reply-To: <1456158772-9344-1-git-send-email-kwolf@redhat.com> References: <1456158772-9344-1-git-send-email-kwolf@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PULL 21/34] throttle: Add support for burst periods List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-block@nongnu.org Cc: kwolf@redhat.com, qemu-devel@nongnu.org From: Alberto Garcia This patch adds support for burst periods to the throttling code. With this feature the user can keep performing bursts as defined by the LeakyBucket.max rate for a configurable period of time. Signed-off-by: Alberto Garcia Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- include/qemu/throttle.h | 41 +++++++++++++++++++++++---- util/throttle.c | 73 ++++++++++++++++++++++++++++++++++++++++---= ------ 2 files changed, 96 insertions(+), 18 deletions(-) diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h index 8ec8951..63df690 100644 --- a/include/qemu/throttle.h +++ b/include/qemu/throttle.h @@ -2,7 +2,7 @@ * QEMU throttling infrastructure * * Copyright (C) Nodalink, EURL. 2013-2014 - * Copyright (C) Igalia, S.L. 2015 + * Copyright (C) Igalia, S.L. 2015-2016 * * Authors: * Beno=C3=AEt Canet @@ -42,16 +42,47 @@ typedef enum { } BucketType; =20 /* - * The max parameter of the leaky bucket throttling algorithm can be use= d to - * allow the guest to do bursts. - * The max value is a pool of I/O that the guest can use without being t= hrottled - * at all. Throttling is triggered once this pool is empty. + * This module implements I/O limits using the leaky bucket + * algorithm. The code is independent of the I/O units, but it is + * currently used for bytes per second and operations per second. + * + * Three parameters can be set by the user: + * + * - avg: the desired I/O limits in units per second. + * - max: the limit during bursts, also in units per second. + * - burst_length: the maximum length of the burst period, in seconds. + * + * Here's how it works: + * + * - The bucket level (number of performed I/O units) is kept in + * bkt.level and leaks at a rate of bkt.avg units per second. + * + * - The size of the bucket is bkt.max * bkt.burst_length. Once the + * bucket is full no more I/O is performed until the bucket leaks + * again. This is what makes the I/O rate bkt.avg. + * + * - The bkt.avg rate does not apply until the bucket is full, + * allowing the user to do bursts until then. The I/O limit during + * bursts is bkt.max. To enforce this limit we keep an additional + * bucket in bkt.burst_length that leaks at a rate of bkt.max units + * per second. + * + * - Because of all of the above, the user can perform I/O at a + * maximum of bkt.max units per second for at most bkt.burst_length + * seconds in a row. After that the bucket will be full and the I/O + * rate will go down to bkt.avg. + * + * - Since the bucket always leaks at a rate of bkt.avg, this also + * determines how much the user needs to wait before being able to + * do bursts again. */ =20 typedef struct LeakyBucket { double avg; /* average goal in units per second */ double max; /* leaky bucket max burst in units */ double level; /* bucket level in units */ + double burst_level; /* bucket level in units (for computing bu= rsts) */ + unsigned burst_length; /* max length of the burst period, in seco= nds */ } LeakyBucket; =20 /* The following structure is used to configure a ThrottleState diff --git a/util/throttle.c b/util/throttle.c index 6a01cee..371c769 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -41,6 +41,14 @@ void throttle_leak_bucket(LeakyBucket *bkt, int64_t de= lta_ns) =20 /* make the bucket leak */ bkt->level =3D MAX(bkt->level - leak, 0); + + /* if we allow bursts for more than one second we also need to + * keep track of bkt->burst_level so the bkt->max goal per second + * is attained */ + if (bkt->burst_length > 1) { + leak =3D (bkt->max * (double) delta_ns) / NANOSECONDS_PER_SECOND= ; + bkt->burst_level =3D MAX(bkt->burst_level - leak, 0); + } } =20 /* Calculate the time delta since last leak and make proportionals leaks @@ -91,13 +99,24 @@ int64_t throttle_compute_wait(LeakyBucket *bkt) return 0; } =20 - extra =3D bkt->level - bkt->max; + /* If the bucket is full then we have to wait */ + extra =3D bkt->level - bkt->max * bkt->burst_length; + if (extra > 0) { + return throttle_do_compute_wait(bkt->avg, extra); + } =20 - if (extra <=3D 0) { - return 0; + /* If the bucket is not full yet we have to make sure that we + * fulfill the goal of bkt->max units per second. */ + if (bkt->burst_length > 1) { + /* We use 1/10 of the max value to smooth the throttling. + * See throttle_fix_bucket() for more details. */ + extra =3D bkt->burst_level - bkt->max / 10; + if (extra > 0) { + return throttle_do_compute_wait(bkt->max, extra); + } } =20 - return throttle_do_compute_wait(bkt->avg, extra); + return 0; } =20 /* This function compute the time that must be waited while this IO @@ -177,7 +196,11 @@ void throttle_timers_attach_aio_context(ThrottleTime= rs *tt, */ void throttle_config_init(ThrottleConfig *cfg) { + unsigned i; memset(cfg, 0, sizeof(*cfg)); + for (i =3D 0; i < BUCKETS_COUNT; i++) { + cfg->buckets[i].burst_length =3D 1; + } } =20 /* To be called first on the ThrottleState */ @@ -301,6 +324,16 @@ bool throttle_is_valid(ThrottleConfig *cfg, Error **= errp) return false; } =20 + if (!cfg->buckets[i].burst_length) { + error_setg(errp, "the burst length cannot be 0"); + return false; + } + + if (cfg->buckets[i].burst_length > 1 && !cfg->buckets[i].max) { + error_setg(errp, "burst length set without burst rate"); + return false; + } + if (cfg->buckets[i].max && !cfg->buckets[i].avg) { error_setg(errp, "bps_max/iops_max require corresponding" " bps/iops values"); @@ -317,7 +350,7 @@ static void throttle_fix_bucket(LeakyBucket *bkt) double min; =20 /* zero bucket level */ - bkt->level =3D 0; + bkt->level =3D bkt->burst_level =3D 0; =20 /* The following is done to cope with the Linux CFQ block scheduler * which regroup reads and writes by block of 100ms in the guest. @@ -420,22 +453,36 @@ bool throttle_schedule_timer(ThrottleState *ts, */ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size) { + const BucketType bucket_types_size[2][2] =3D { + { THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ }, + { THROTTLE_BPS_TOTAL, THROTTLE_BPS_WRITE } + }; + const BucketType bucket_types_units[2][2] =3D { + { THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ }, + { THROTTLE_OPS_TOTAL, THROTTLE_OPS_WRITE } + }; double units =3D 1.0; + unsigned i; =20 /* if cfg.op_size is defined and smaller than size we compute unit c= ount */ if (ts->cfg.op_size && size > ts->cfg.op_size) { units =3D (double) size / ts->cfg.op_size; } =20 - ts->cfg.buckets[THROTTLE_BPS_TOTAL].level +=3D size; - ts->cfg.buckets[THROTTLE_OPS_TOTAL].level +=3D units; + for (i =3D 0; i < 2; i++) { + LeakyBucket *bkt; + + bkt =3D &ts->cfg.buckets[bucket_types_size[is_write][i]]; + bkt->level +=3D size; + if (bkt->burst_length > 1) { + bkt->burst_level +=3D size; + } =20 - if (is_write) { - ts->cfg.buckets[THROTTLE_BPS_WRITE].level +=3D size; - ts->cfg.buckets[THROTTLE_OPS_WRITE].level +=3D units; - } else { - ts->cfg.buckets[THROTTLE_BPS_READ].level +=3D size; - ts->cfg.buckets[THROTTLE_OPS_READ].level +=3D units; + bkt =3D &ts->cfg.buckets[bucket_types_units[is_write][i]]; + bkt->level +=3D units; + if (bkt->burst_length > 1) { + bkt->burst_level +=3D units; + } } } =20 --=20 1.8.3.1