iocost: factor out ioc_forgive_debts()

Debt reduction logic is going to be improved and expanded. Factor it out
into ioc_forgive_debts() and generalize the comment a bit. No functional
change.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Tejun Heo 2020-09-17 20:44:52 -04:00 committed by Jens Axboe
parent 6abc49468e
commit ab8df828b5

View File

@ -1979,6 +1979,40 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
list_del_init(&iocg->walk_list);
}
/*
* A low weight iocg can amass a large amount of debt, for example, when
* anonymous memory gets reclaimed aggressively. If the system has a lot of
* memory paired with a slow IO device, the debt can span multiple seconds or
* more. If there are no other subsequent IO issuers, the in-debt iocg may end
* up blocked paying its debt while the IO device is idle.
*
* The following protects against such cases. If the device has been
* sufficiently idle for a while, the debts are halved.
*/
static void ioc_forgive_debts(struct ioc *ioc, u64 usage_us_sum, int nr_debtors,
int nr_shortages, struct ioc_now *now)
{
if (nr_shortages ||
div64_u64(100 * usage_us_sum, now->now - ioc->period_at) >=
DEBT_BUSY_USAGE_PCT)
ioc->debt_busy_at = now->now;
if (nr_debtors &&
now->now - ioc->debt_busy_at >= DEBT_REDUCTION_IDLE_DUR) {
struct ioc_gq *iocg;
list_for_each_entry(iocg, &ioc->active_iocgs, active_list) {
if (iocg->abs_vdebt) {
spin_lock(&iocg->waitq.lock);
iocg->abs_vdebt /= 2;
iocg_kick_waitq(iocg, true, now);
spin_unlock(&iocg->waitq.lock);
}
}
ioc->debt_busy_at = now->now;
}
}
static void ioc_timer_fn(struct timer_list *timer)
{
struct ioc *ioc = container_of(timer, struct ioc, timer);
@ -2171,37 +2205,7 @@ static void ioc_timer_fn(struct timer_list *timer)
list_for_each_entry_safe(iocg, tiocg, &surpluses, surplus_list)
list_del_init(&iocg->surplus_list);
/*
* A low weight iocg can amass a large amount of debt, for example, when
* anonymous memory gets reclaimed aggressively. If the system has a lot
* of memory paired with a slow IO device, the debt can span multiple
* seconds or more. If there are no other subsequent IO issuers, the
* in-debt iocg may end up blocked paying its debt while the IO device
* is idle.
*
* The following protects against such pathological cases. If the device
* has been sufficiently idle for a substantial amount of time, the
* debts are halved. The criteria are on the conservative side as we
* want to resolve the rare extreme cases without impacting regular
* operation by forgiving debts too readily.
*/
if (nr_shortages ||
div64_u64(100 * usage_us_sum, now.now - ioc->period_at) >=
DEBT_BUSY_USAGE_PCT)
ioc->debt_busy_at = now.now;
if (nr_debtors &&
now.now - ioc->debt_busy_at >= DEBT_REDUCTION_IDLE_DUR) {
list_for_each_entry(iocg, &ioc->active_iocgs, active_list) {
if (iocg->abs_vdebt) {
spin_lock(&iocg->waitq.lock);
iocg->abs_vdebt /= 2;
iocg_kick_waitq(iocg, true, &now);
spin_unlock(&iocg->waitq.lock);
}
}
ioc->debt_busy_at = now.now;
}
ioc_forgive_debts(ioc, usage_us_sum, nr_debtors, nr_shortages, &now);
/*
* If q is getting clogged or we're missing too much, we're issuing