162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Dynamic byte queue limits. See include/linux/dynamic_queue_limits.h 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011, Tom Herbert <therbert@google.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/types.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/jiffies.h> 1062306a36Sopenharmony_ci#include <linux/dynamic_queue_limits.h> 1162306a36Sopenharmony_ci#include <linux/compiler.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define POSDIFF(A, B) ((int)((A) - (B)) > 0 ? (A) - (B) : 0) 1562306a36Sopenharmony_ci#define AFTER_EQ(A, B) ((int)((A) - (B)) >= 0) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Records completed count and recalculates the queue limit */ 1862306a36Sopenharmony_civoid dql_completed(struct dql *dql, unsigned int count) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci unsigned int inprogress, prev_inprogress, limit; 2162306a36Sopenharmony_ci unsigned int ovlimit, completed, num_queued; 2262306a36Sopenharmony_ci bool all_prev_completed; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci num_queued = READ_ONCE(dql->num_queued); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* Can't complete more than what's in queue */ 2762306a36Sopenharmony_ci BUG_ON(count > num_queued - dql->num_completed); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci completed = dql->num_completed + count; 3062306a36Sopenharmony_ci limit = dql->limit; 3162306a36Sopenharmony_ci ovlimit = POSDIFF(num_queued - dql->num_completed, limit); 3262306a36Sopenharmony_ci inprogress = num_queued - completed; 3362306a36Sopenharmony_ci prev_inprogress = dql->prev_num_queued - dql->num_completed; 3462306a36Sopenharmony_ci all_prev_completed = AFTER_EQ(completed, dql->prev_num_queued); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if ((ovlimit && !inprogress) || 3762306a36Sopenharmony_ci (dql->prev_ovlimit && all_prev_completed)) { 3862306a36Sopenharmony_ci /* 3962306a36Sopenharmony_ci * Queue considered starved if: 4062306a36Sopenharmony_ci * - The queue was over-limit in the last interval, 4162306a36Sopenharmony_ci * and there is no more data in the queue. 4262306a36Sopenharmony_ci * OR 4362306a36Sopenharmony_ci * - The queue was over-limit in the previous interval and 4462306a36Sopenharmony_ci * when enqueuing it was possible that all queued data 4562306a36Sopenharmony_ci * had been consumed. This covers the case when queue 4662306a36Sopenharmony_ci * may have becomes starved between completion processing 4762306a36Sopenharmony_ci * running and next time enqueue was scheduled. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * When queue is starved increase the limit by the amount 5062306a36Sopenharmony_ci * of bytes both sent and completed in the last interval, 5162306a36Sopenharmony_ci * plus any previous over-limit. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci limit += POSDIFF(completed, dql->prev_num_queued) + 5462306a36Sopenharmony_ci dql->prev_ovlimit; 5562306a36Sopenharmony_ci dql->slack_start_time = jiffies; 5662306a36Sopenharmony_ci dql->lowest_slack = UINT_MAX; 5762306a36Sopenharmony_ci } else if (inprogress && prev_inprogress && !all_prev_completed) { 5862306a36Sopenharmony_ci /* 5962306a36Sopenharmony_ci * Queue was not starved, check if the limit can be decreased. 6062306a36Sopenharmony_ci * A decrease is only considered if the queue has been busy in 6162306a36Sopenharmony_ci * the whole interval (the check above). 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * If there is slack, the amount of excess data queued above 6462306a36Sopenharmony_ci * the amount needed to prevent starvation, the queue limit 6562306a36Sopenharmony_ci * can be decreased. To avoid hysteresis we consider the 6662306a36Sopenharmony_ci * minimum amount of slack found over several iterations of the 6762306a36Sopenharmony_ci * completion routine. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci unsigned int slack, slack_last_objs; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * Slack is the maximum of 7362306a36Sopenharmony_ci * - The queue limit plus previous over-limit minus twice 7462306a36Sopenharmony_ci * the number of objects completed. Note that two times 7562306a36Sopenharmony_ci * number of completed bytes is a basis for an upper bound 7662306a36Sopenharmony_ci * of the limit. 7762306a36Sopenharmony_ci * - Portion of objects in the last queuing operation that 7862306a36Sopenharmony_ci * was not part of non-zero previous over-limit. That is 7962306a36Sopenharmony_ci * "round down" by non-overlimit portion of the last 8062306a36Sopenharmony_ci * queueing operation. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci slack = POSDIFF(limit + dql->prev_ovlimit, 8362306a36Sopenharmony_ci 2 * (completed - dql->num_completed)); 8462306a36Sopenharmony_ci slack_last_objs = dql->prev_ovlimit ? 8562306a36Sopenharmony_ci POSDIFF(dql->prev_last_obj_cnt, dql->prev_ovlimit) : 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci slack = max(slack, slack_last_objs); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (slack < dql->lowest_slack) 9062306a36Sopenharmony_ci dql->lowest_slack = slack; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (time_after(jiffies, 9362306a36Sopenharmony_ci dql->slack_start_time + dql->slack_hold_time)) { 9462306a36Sopenharmony_ci limit = POSDIFF(limit, dql->lowest_slack); 9562306a36Sopenharmony_ci dql->slack_start_time = jiffies; 9662306a36Sopenharmony_ci dql->lowest_slack = UINT_MAX; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Enforce bounds on limit */ 10162306a36Sopenharmony_ci limit = clamp(limit, dql->min_limit, dql->max_limit); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (limit != dql->limit) { 10462306a36Sopenharmony_ci dql->limit = limit; 10562306a36Sopenharmony_ci ovlimit = 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci dql->adj_limit = limit + completed; 10962306a36Sopenharmony_ci dql->prev_ovlimit = ovlimit; 11062306a36Sopenharmony_ci dql->prev_last_obj_cnt = dql->last_obj_cnt; 11162306a36Sopenharmony_ci dql->num_completed = completed; 11262306a36Sopenharmony_ci dql->prev_num_queued = num_queued; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ciEXPORT_SYMBOL(dql_completed); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid dql_reset(struct dql *dql) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci /* Reset all dynamic values */ 11962306a36Sopenharmony_ci dql->limit = 0; 12062306a36Sopenharmony_ci dql->num_queued = 0; 12162306a36Sopenharmony_ci dql->num_completed = 0; 12262306a36Sopenharmony_ci dql->last_obj_cnt = 0; 12362306a36Sopenharmony_ci dql->prev_num_queued = 0; 12462306a36Sopenharmony_ci dql->prev_last_obj_cnt = 0; 12562306a36Sopenharmony_ci dql->prev_ovlimit = 0; 12662306a36Sopenharmony_ci dql->lowest_slack = UINT_MAX; 12762306a36Sopenharmony_ci dql->slack_start_time = jiffies; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ciEXPORT_SYMBOL(dql_reset); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid dql_init(struct dql *dql, unsigned int hold_time) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci dql->max_limit = DQL_MAX_LIMIT; 13462306a36Sopenharmony_ci dql->min_limit = 0; 13562306a36Sopenharmony_ci dql->slack_hold_time = hold_time; 13662306a36Sopenharmony_ci dql_reset(dql); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ciEXPORT_SYMBOL(dql_init); 139