X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/24790aefe03381e47679c9459a3cd0b5e5af751a..ef1b1de5b3bd0fed11998014dceaee32861f8122:/util/throttle.c?ds=sidebyside diff --git a/util/throttle.c b/util/throttle.c index 71246b2343..b38e742da5 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -95,23 +95,37 @@ static int64_t throttle_do_compute_wait(double limit, double extra) int64_t throttle_compute_wait(LeakyBucket *bkt) { double extra; /* the number of extra units blocking the io */ + double bucket_size; /* I/O before throttling to bkt->avg */ + double burst_bucket_size; /* Before throttling to bkt->max */ if (!bkt->avg) { return 0; } - /* If the bucket is full then we have to wait */ - extra = bkt->level - bkt->max * bkt->burst_length; + if (!bkt->max) { + /* If bkt->max is 0 we still want to allow short bursts of I/O + * from the guest, otherwise every other request will be throttled + * and performance will suffer considerably. */ + bucket_size = (double) bkt->avg / 10; + burst_bucket_size = 0; + } else { + /* If we have a burst limit then we have to wait until all I/O + * at burst rate has finished before throttling to bkt->avg */ + bucket_size = bkt->max * bkt->burst_length; + burst_bucket_size = (double) bkt->max / 10; + } + + /* If the main bucket is full then we have to wait */ + extra = bkt->level - bucket_size; if (extra > 0) { return throttle_do_compute_wait(bkt->avg, extra); } - /* If the bucket is not full yet we have to make sure that we - * fulfill the goal of bkt->max units per second. */ + /* If the main bucket is not full yet we still have to check the + * burst bucket in order to enforce the burst limit */ 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 = bkt->burst_level - bkt->max / 10; + assert(bkt->max > 0); /* see throttle_is_valid() */ + extra = bkt->burst_level - burst_bucket_size; if (extra > 0) { return throttle_do_compute_wait(bkt->max, extra); } @@ -315,89 +329,73 @@ bool throttle_is_valid(ThrottleConfig *cfg, Error **errp) return false; } + if (cfg->op_size && + !cfg->buckets[THROTTLE_OPS_TOTAL].avg && + !cfg->buckets[THROTTLE_OPS_READ].avg && + !cfg->buckets[THROTTLE_OPS_WRITE].avg) { + error_setg(errp, "iops size requires an iops value to be set"); + return false; + } + for (i = 0; i < BUCKETS_COUNT; i++) { - if (cfg->buckets[i].avg < 0 || - cfg->buckets[i].max < 0 || - cfg->buckets[i].avg > THROTTLE_VALUE_MAX || - cfg->buckets[i].max > THROTTLE_VALUE_MAX) { + LeakyBucket *bkt = &cfg->buckets[i]; + if (bkt->avg > THROTTLE_VALUE_MAX || bkt->max > THROTTLE_VALUE_MAX) { error_setg(errp, "bps/iops/max values must be within [0, %lld]", THROTTLE_VALUE_MAX); return false; } - if (!cfg->buckets[i].burst_length) { + if (!bkt->burst_length) { error_setg(errp, "the burst length cannot be 0"); return false; } - if (cfg->buckets[i].burst_length > 1 && !cfg->buckets[i].max) { + if (bkt->burst_length > 1 && !bkt->max) { error_setg(errp, "burst length set without burst rate"); return false; } - if (cfg->buckets[i].max && !cfg->buckets[i].avg) { + if (bkt->max && bkt->burst_length > THROTTLE_VALUE_MAX / bkt->max) { + error_setg(errp, "burst length too high for this burst rate"); + return false; + } + + if (bkt->max && !bkt->avg) { error_setg(errp, "bps_max/iops_max require corresponding" " bps/iops values"); return false; } - } - - return true; -} -/* fix bucket parameters */ -static void throttle_fix_bucket(LeakyBucket *bkt) -{ - double min; - - /* zero bucket level */ - bkt->level = bkt->burst_level = 0; - - /* The following is done to cope with the Linux CFQ block scheduler - * which regroup reads and writes by block of 100ms in the guest. - * When they are two process one making reads and one making writes cfq - * make a pattern looking like the following: - * WWWWWWWWWWWRRRRRRRRRRRRRRWWWWWWWWWWWWWwRRRRRRRRRRRRRRRRR - * Having a max burst value of 100ms of the average will help smooth the - * throttling - */ - min = bkt->avg / 10; - if (bkt->avg && !bkt->max) { - bkt->max = min; + if (bkt->max && bkt->max < bkt->avg) { + error_setg(errp, "bps_max/iops_max cannot be lower than bps/iops"); + return false; + } } -} -/* take care of canceling a timer */ -static void throttle_cancel_timer(QEMUTimer *timer) -{ - assert(timer != NULL); - - timer_del(timer); + return true; } /* Used to configure the throttle * * @ts: the throttle state we are working on - * @tt: the throttle timers we use in this aio context + * @clock_type: the group's clock_type * @cfg: the config to set */ void throttle_config(ThrottleState *ts, - ThrottleTimers *tt, + QEMUClockType clock_type, ThrottleConfig *cfg) { int i; ts->cfg = *cfg; + /* Zero bucket level */ for (i = 0; i < BUCKETS_COUNT; i++) { - throttle_fix_bucket(&ts->cfg.buckets[i]); + ts->cfg.buckets[i].level = 0; + ts->cfg.buckets[i].burst_level = 0; } - ts->previous_leak = qemu_clock_get_ns(tt->clock_type); - - for (i = 0; i < 2; i++) { - throttle_cancel_timer(tt->timers[i]); - } + ts->previous_leak = qemu_clock_get_ns(clock_type); } /* used to get config @@ -487,3 +485,154 @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size) } } +/* return a ThrottleConfig based on the options in a ThrottleLimits + * + * @arg: the ThrottleLimits object to read from + * @cfg: the ThrottleConfig to edit + * @errp: error object + */ +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg, + Error **errp) +{ + if (arg->has_bps_total) { + cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total; + } + if (arg->has_bps_read) { + cfg->buckets[THROTTLE_BPS_READ].avg = arg->bps_read; + } + if (arg->has_bps_write) { + cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write; + } + + if (arg->has_iops_total) { + cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total; + } + if (arg->has_iops_read) { + cfg->buckets[THROTTLE_OPS_READ].avg = arg->iops_read; + } + if (arg->has_iops_write) { + cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write; + } + + if (arg->has_bps_total_max) { + cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max; + } + if (arg->has_bps_read_max) { + cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max; + } + if (arg->has_bps_write_max) { + cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max; + } + if (arg->has_iops_total_max) { + cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max; + } + if (arg->has_iops_read_max) { + cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max; + } + if (arg->has_iops_write_max) { + cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max; + } + + if (arg->has_bps_total_max_length) { + if (arg->bps_total_max_length > UINT_MAX) { + error_setg(errp, "bps-total-max-length value must be in" + " the range [0, %u]", UINT_MAX); + return; + } + cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length; + } + if (arg->has_bps_read_max_length) { + if (arg->bps_read_max_length > UINT_MAX) { + error_setg(errp, "bps-read-max-length value must be in" + " the range [0, %u]", UINT_MAX); + return; + } + cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length; + } + if (arg->has_bps_write_max_length) { + if (arg->bps_write_max_length > UINT_MAX) { + error_setg(errp, "bps-write-max-length value must be in" + " the range [0, %u]", UINT_MAX); + return; + } + cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length; + } + if (arg->has_iops_total_max_length) { + if (arg->iops_total_max_length > UINT_MAX) { + error_setg(errp, "iops-total-max-length value must be in" + " the range [0, %u]", UINT_MAX); + return; + } + cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length; + } + if (arg->has_iops_read_max_length) { + if (arg->iops_read_max_length > UINT_MAX) { + error_setg(errp, "iops-read-max-length value must be in" + " the range [0, %u]", UINT_MAX); + return; + } + cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length; + } + if (arg->has_iops_write_max_length) { + if (arg->iops_write_max_length > UINT_MAX) { + error_setg(errp, "iops-write-max-length value must be in" + " the range [0, %u]", UINT_MAX); + return; + } + cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length; + } + + if (arg->has_iops_size) { + cfg->op_size = arg->iops_size; + } + + throttle_is_valid(cfg, errp); +} + +/* write the options of a ThrottleConfig to a ThrottleLimits + * + * @cfg: the ThrottleConfig to read from + * @var: the ThrottleLimits to write to + */ +void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var) +{ + var->bps_total = cfg->buckets[THROTTLE_BPS_TOTAL].avg; + var->bps_read = cfg->buckets[THROTTLE_BPS_READ].avg; + var->bps_write = cfg->buckets[THROTTLE_BPS_WRITE].avg; + var->iops_total = cfg->buckets[THROTTLE_OPS_TOTAL].avg; + var->iops_read = cfg->buckets[THROTTLE_OPS_READ].avg; + var->iops_write = cfg->buckets[THROTTLE_OPS_WRITE].avg; + var->bps_total_max = cfg->buckets[THROTTLE_BPS_TOTAL].max; + var->bps_read_max = cfg->buckets[THROTTLE_BPS_READ].max; + var->bps_write_max = cfg->buckets[THROTTLE_BPS_WRITE].max; + var->iops_total_max = cfg->buckets[THROTTLE_OPS_TOTAL].max; + var->iops_read_max = cfg->buckets[THROTTLE_OPS_READ].max; + var->iops_write_max = cfg->buckets[THROTTLE_OPS_WRITE].max; + var->bps_total_max_length = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length; + var->bps_read_max_length = cfg->buckets[THROTTLE_BPS_READ].burst_length; + var->bps_write_max_length = cfg->buckets[THROTTLE_BPS_WRITE].burst_length; + var->iops_total_max_length = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length; + var->iops_read_max_length = cfg->buckets[THROTTLE_OPS_READ].burst_length; + var->iops_write_max_length = cfg->buckets[THROTTLE_OPS_WRITE].burst_length; + var->iops_size = cfg->op_size; + + var->has_bps_total = true; + var->has_bps_read = true; + var->has_bps_write = true; + var->has_iops_total = true; + var->has_iops_read = true; + var->has_iops_write = true; + var->has_bps_total_max = true; + var->has_bps_read_max = true; + var->has_bps_write_max = true; + var->has_iops_total_max = true; + var->has_iops_read_max = true; + var->has_iops_write_max = true; + var->has_bps_read_max_length = true; + var->has_bps_total_max_length = true; + var->has_bps_write_max_length = true; + var->has_iops_total_max_length = true; + var->has_iops_read_max_length = true; + var->has_iops_write_max_length = true; + var->has_iops_size = true; +}