18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Dynamic byte queue limits.  See include/linux/dynamic_queue_limits.h
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2011, Tom Herbert <therbert@google.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
108c2ecf20Sopenharmony_ci#include <linux/dynamic_queue_limits.h>
118c2ecf20Sopenharmony_ci#include <linux/compiler.h>
128c2ecf20Sopenharmony_ci#include <linux/export.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define POSDIFF(A, B) ((int)((A) - (B)) > 0 ? (A) - (B) : 0)
158c2ecf20Sopenharmony_ci#define AFTER_EQ(A, B) ((int)((A) - (B)) >= 0)
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Records completed count and recalculates the queue limit */
188c2ecf20Sopenharmony_civoid dql_completed(struct dql *dql, unsigned int count)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	unsigned int inprogress, prev_inprogress, limit;
218c2ecf20Sopenharmony_ci	unsigned int ovlimit, completed, num_queued;
228c2ecf20Sopenharmony_ci	bool all_prev_completed;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	num_queued = READ_ONCE(dql->num_queued);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	/* Can't complete more than what's in queue */
278c2ecf20Sopenharmony_ci	BUG_ON(count > num_queued - dql->num_completed);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	completed = dql->num_completed + count;
308c2ecf20Sopenharmony_ci	limit = dql->limit;
318c2ecf20Sopenharmony_ci	ovlimit = POSDIFF(num_queued - dql->num_completed, limit);
328c2ecf20Sopenharmony_ci	inprogress = num_queued - completed;
338c2ecf20Sopenharmony_ci	prev_inprogress = dql->prev_num_queued - dql->num_completed;
348c2ecf20Sopenharmony_ci	all_prev_completed = AFTER_EQ(completed, dql->prev_num_queued);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if ((ovlimit && !inprogress) ||
378c2ecf20Sopenharmony_ci	    (dql->prev_ovlimit && all_prev_completed)) {
388c2ecf20Sopenharmony_ci		/*
398c2ecf20Sopenharmony_ci		 * Queue considered starved if:
408c2ecf20Sopenharmony_ci		 *   - The queue was over-limit in the last interval,
418c2ecf20Sopenharmony_ci		 *     and there is no more data in the queue.
428c2ecf20Sopenharmony_ci		 *  OR
438c2ecf20Sopenharmony_ci		 *   - The queue was over-limit in the previous interval and
448c2ecf20Sopenharmony_ci		 *     when enqueuing it was possible that all queued data
458c2ecf20Sopenharmony_ci		 *     had been consumed.  This covers the case when queue
468c2ecf20Sopenharmony_ci		 *     may have becomes starved between completion processing
478c2ecf20Sopenharmony_ci		 *     running and next time enqueue was scheduled.
488c2ecf20Sopenharmony_ci		 *
498c2ecf20Sopenharmony_ci		 *     When queue is starved increase the limit by the amount
508c2ecf20Sopenharmony_ci		 *     of bytes both sent and completed in the last interval,
518c2ecf20Sopenharmony_ci		 *     plus any previous over-limit.
528c2ecf20Sopenharmony_ci		 */
538c2ecf20Sopenharmony_ci		limit += POSDIFF(completed, dql->prev_num_queued) +
548c2ecf20Sopenharmony_ci		     dql->prev_ovlimit;
558c2ecf20Sopenharmony_ci		dql->slack_start_time = jiffies;
568c2ecf20Sopenharmony_ci		dql->lowest_slack = UINT_MAX;
578c2ecf20Sopenharmony_ci	} else if (inprogress && prev_inprogress && !all_prev_completed) {
588c2ecf20Sopenharmony_ci		/*
598c2ecf20Sopenharmony_ci		 * Queue was not starved, check if the limit can be decreased.
608c2ecf20Sopenharmony_ci		 * A decrease is only considered if the queue has been busy in
618c2ecf20Sopenharmony_ci		 * the whole interval (the check above).
628c2ecf20Sopenharmony_ci		 *
638c2ecf20Sopenharmony_ci		 * If there is slack, the amount of excess data queued above
648c2ecf20Sopenharmony_ci		 * the amount needed to prevent starvation, the queue limit
658c2ecf20Sopenharmony_ci		 * can be decreased.  To avoid hysteresis we consider the
668c2ecf20Sopenharmony_ci		 * minimum amount of slack found over several iterations of the
678c2ecf20Sopenharmony_ci		 * completion routine.
688c2ecf20Sopenharmony_ci		 */
698c2ecf20Sopenharmony_ci		unsigned int slack, slack_last_objs;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		/*
728c2ecf20Sopenharmony_ci		 * Slack is the maximum of
738c2ecf20Sopenharmony_ci		 *   - The queue limit plus previous over-limit minus twice
748c2ecf20Sopenharmony_ci		 *     the number of objects completed.  Note that two times
758c2ecf20Sopenharmony_ci		 *     number of completed bytes is a basis for an upper bound
768c2ecf20Sopenharmony_ci		 *     of the limit.
778c2ecf20Sopenharmony_ci		 *   - Portion of objects in the last queuing operation that
788c2ecf20Sopenharmony_ci		 *     was not part of non-zero previous over-limit.  That is
798c2ecf20Sopenharmony_ci		 *     "round down" by non-overlimit portion of the last
808c2ecf20Sopenharmony_ci		 *     queueing operation.
818c2ecf20Sopenharmony_ci		 */
828c2ecf20Sopenharmony_ci		slack = POSDIFF(limit + dql->prev_ovlimit,
838c2ecf20Sopenharmony_ci		    2 * (completed - dql->num_completed));
848c2ecf20Sopenharmony_ci		slack_last_objs = dql->prev_ovlimit ?
858c2ecf20Sopenharmony_ci		    POSDIFF(dql->prev_last_obj_cnt, dql->prev_ovlimit) : 0;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		slack = max(slack, slack_last_objs);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		if (slack < dql->lowest_slack)
908c2ecf20Sopenharmony_ci			dql->lowest_slack = slack;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		if (time_after(jiffies,
938c2ecf20Sopenharmony_ci			       dql->slack_start_time + dql->slack_hold_time)) {
948c2ecf20Sopenharmony_ci			limit = POSDIFF(limit, dql->lowest_slack);
958c2ecf20Sopenharmony_ci			dql->slack_start_time = jiffies;
968c2ecf20Sopenharmony_ci			dql->lowest_slack = UINT_MAX;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* Enforce bounds on limit */
1018c2ecf20Sopenharmony_ci	limit = clamp(limit, dql->min_limit, dql->max_limit);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (limit != dql->limit) {
1048c2ecf20Sopenharmony_ci		dql->limit = limit;
1058c2ecf20Sopenharmony_ci		ovlimit = 0;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	dql->adj_limit = limit + completed;
1098c2ecf20Sopenharmony_ci	dql->prev_ovlimit = ovlimit;
1108c2ecf20Sopenharmony_ci	dql->prev_last_obj_cnt = dql->last_obj_cnt;
1118c2ecf20Sopenharmony_ci	dql->num_completed = completed;
1128c2ecf20Sopenharmony_ci	dql->prev_num_queued = num_queued;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dql_completed);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_civoid dql_reset(struct dql *dql)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	/* Reset all dynamic values */
1198c2ecf20Sopenharmony_ci	dql->limit = 0;
1208c2ecf20Sopenharmony_ci	dql->num_queued = 0;
1218c2ecf20Sopenharmony_ci	dql->num_completed = 0;
1228c2ecf20Sopenharmony_ci	dql->last_obj_cnt = 0;
1238c2ecf20Sopenharmony_ci	dql->prev_num_queued = 0;
1248c2ecf20Sopenharmony_ci	dql->prev_last_obj_cnt = 0;
1258c2ecf20Sopenharmony_ci	dql->prev_ovlimit = 0;
1268c2ecf20Sopenharmony_ci	dql->lowest_slack = UINT_MAX;
1278c2ecf20Sopenharmony_ci	dql->slack_start_time = jiffies;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dql_reset);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_civoid dql_init(struct dql *dql, unsigned int hold_time)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	dql->max_limit = DQL_MAX_LIMIT;
1348c2ecf20Sopenharmony_ci	dql->min_limit = 0;
1358c2ecf20Sopenharmony_ci	dql->slack_hold_time = hold_time;
1368c2ecf20Sopenharmony_ci	dql_reset(dql);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dql_init);
139