2 * Dynamic byte queue limits. See include/linux/dynamic_queue_limits.h
4 * Copyright (c) 2011, Tom Herbert <therbert@google.com>
6 #include <linux/module.h>
7 #include <linux/types.h>
8 #include <linux/ctype.h>
9 #include <linux/kernel.h>
10 #include <linux/jiffies.h>
11 #include <linux/dynamic_queue_limits.h>
13 #define POSDIFF(A, B) ((A) > (B) ? (A) - (B) : 0)
15 /* Records completed count and recalculates the queue limit */
16 void dql_completed(struct dql
*dql
, unsigned int count
)
18 unsigned int inprogress
, prev_inprogress
, limit
;
19 unsigned int ovlimit
, all_prev_completed
, completed
;
21 /* Can't complete more than what's in queue */
22 BUG_ON(count
> dql
->num_queued
- dql
->num_completed
);
24 completed
= dql
->num_completed
+ count
;
26 ovlimit
= POSDIFF(dql
->num_queued
- dql
->num_completed
, limit
);
27 inprogress
= dql
->num_queued
- completed
;
28 prev_inprogress
= dql
->prev_num_queued
- dql
->num_completed
;
29 all_prev_completed
= POSDIFF(completed
, dql
->prev_num_queued
);
31 if ((ovlimit
&& !inprogress
) ||
32 (dql
->prev_ovlimit
&& all_prev_completed
)) {
34 * Queue considered starved if:
35 * - The queue was over-limit in the last interval,
36 * and there is no more data in the queue.
38 * - The queue was over-limit in the previous interval and
39 * when enqueuing it was possible that all queued data
40 * had been consumed. This covers the case when queue
41 * may have becomes starved between completion processing
42 * running and next time enqueue was scheduled.
44 * When queue is starved increase the limit by the amount
45 * of bytes both sent and completed in the last interval,
46 * plus any previous over-limit.
48 limit
+= POSDIFF(completed
, dql
->prev_num_queued
) +
50 dql
->slack_start_time
= jiffies
;
51 dql
->lowest_slack
= UINT_MAX
;
52 } else if (inprogress
&& prev_inprogress
&& !all_prev_completed
) {
54 * Queue was not starved, check if the limit can be decreased.
55 * A decrease is only considered if the queue has been busy in
56 * the whole interval (the check above).
58 * If there is slack, the amount of execess data queued above
59 * the the amount needed to prevent starvation, the queue limit
60 * can be decreased. To avoid hysteresis we consider the
61 * minimum amount of slack found over several iterations of the
64 unsigned int slack
, slack_last_objs
;
67 * Slack is the maximum of
68 * - The queue limit plus previous over-limit minus twice
69 * the number of objects completed. Note that two times
70 * number of completed bytes is a basis for an upper bound
72 * - Portion of objects in the last queuing operation that
73 * was not part of non-zero previous over-limit. That is
74 * "round down" by non-overlimit portion of the last
77 slack
= POSDIFF(limit
+ dql
->prev_ovlimit
,
78 2 * (completed
- dql
->num_completed
));
79 slack_last_objs
= dql
->prev_ovlimit
?
80 POSDIFF(dql
->prev_last_obj_cnt
, dql
->prev_ovlimit
) : 0;
82 slack
= max(slack
, slack_last_objs
);
84 if (slack
< dql
->lowest_slack
)
85 dql
->lowest_slack
= slack
;
87 if (time_after(jiffies
,
88 dql
->slack_start_time
+ dql
->slack_hold_time
)) {
89 limit
= POSDIFF(limit
, dql
->lowest_slack
);
90 dql
->slack_start_time
= jiffies
;
91 dql
->lowest_slack
= UINT_MAX
;
95 /* Enforce bounds on limit */
96 limit
= clamp(limit
, dql
->min_limit
, dql
->max_limit
);
98 if (limit
!= dql
->limit
) {
103 dql
->adj_limit
= limit
+ completed
;
104 dql
->prev_ovlimit
= ovlimit
;
105 dql
->prev_last_obj_cnt
= dql
->last_obj_cnt
;
106 dql
->num_completed
= completed
;
107 dql
->prev_num_queued
= dql
->num_queued
;
109 EXPORT_SYMBOL(dql_completed
);
111 void dql_reset(struct dql
*dql
)
113 /* Reset all dynamic values */
116 dql
->num_completed
= 0;
117 dql
->last_obj_cnt
= 0;
118 dql
->prev_num_queued
= 0;
119 dql
->prev_last_obj_cnt
= 0;
120 dql
->prev_ovlimit
= 0;
121 dql
->lowest_slack
= UINT_MAX
;
122 dql
->slack_start_time
= jiffies
;
124 EXPORT_SYMBOL(dql_reset
);
126 int dql_init(struct dql
*dql
, unsigned hold_time
)
128 dql
->max_limit
= DQL_MAX_LIMIT
;
130 dql
->slack_hold_time
= hold_time
;
134 EXPORT_SYMBOL(dql_init
);