1d068ca2aSMichal Kazior #ifndef __NET_SCHED_CODEL_IMPL_H
2d068ca2aSMichal Kazior #define __NET_SCHED_CODEL_IMPL_H
3d068ca2aSMichal Kazior
4d068ca2aSMichal Kazior /*
5d068ca2aSMichal Kazior * Codel - The Controlled-Delay Active Queue Management algorithm
6d068ca2aSMichal Kazior *
7d068ca2aSMichal Kazior * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
8d068ca2aSMichal Kazior * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
9d068ca2aSMichal Kazior * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
10d068ca2aSMichal Kazior * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com>
11d068ca2aSMichal Kazior *
12d068ca2aSMichal Kazior * Redistribution and use in source and binary forms, with or without
13d068ca2aSMichal Kazior * modification, are permitted provided that the following conditions
14d068ca2aSMichal Kazior * are met:
15d068ca2aSMichal Kazior * 1. Redistributions of source code must retain the above copyright
16d068ca2aSMichal Kazior * notice, this list of conditions, and the following disclaimer,
17d068ca2aSMichal Kazior * without modification.
18d068ca2aSMichal Kazior * 2. Redistributions in binary form must reproduce the above copyright
19d068ca2aSMichal Kazior * notice, this list of conditions and the following disclaimer in the
20d068ca2aSMichal Kazior * documentation and/or other materials provided with the distribution.
21d068ca2aSMichal Kazior * 3. The names of the authors may not be used to endorse or promote products
22d068ca2aSMichal Kazior * derived from this software without specific prior written permission.
23d068ca2aSMichal Kazior *
24d068ca2aSMichal Kazior * Alternatively, provided that this notice is retained in full, this
25d068ca2aSMichal Kazior * software may be distributed under the terms of the GNU General
26d068ca2aSMichal Kazior * Public License ("GPL") version 2, in which case the provisions of the
27d068ca2aSMichal Kazior * GPL apply INSTEAD OF those given above.
28d068ca2aSMichal Kazior *
29d068ca2aSMichal Kazior * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30d068ca2aSMichal Kazior * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31d068ca2aSMichal Kazior * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32d068ca2aSMichal Kazior * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33d068ca2aSMichal Kazior * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34d068ca2aSMichal Kazior * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35d068ca2aSMichal Kazior * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36d068ca2aSMichal Kazior * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37d068ca2aSMichal Kazior * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38d068ca2aSMichal Kazior * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39d068ca2aSMichal Kazior * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
40d068ca2aSMichal Kazior * DAMAGE.
41d068ca2aSMichal Kazior *
42d068ca2aSMichal Kazior */
43d068ca2aSMichal Kazior
44d068ca2aSMichal Kazior /* Controlling Queue Delay (CoDel) algorithm
45d068ca2aSMichal Kazior * =========================================
46d068ca2aSMichal Kazior * Source : Kathleen Nichols and Van Jacobson
47d068ca2aSMichal Kazior * http://queue.acm.org/detail.cfm?id=2209336
48d068ca2aSMichal Kazior *
49d068ca2aSMichal Kazior * Implemented on linux by Dave Taht and Eric Dumazet
50d068ca2aSMichal Kazior */
51d068ca2aSMichal Kazior
52*15fcb103SJakub Kicinski #include <net/inet_ecn.h>
53*15fcb103SJakub Kicinski
codel_params_init(struct codel_params * params)54d068ca2aSMichal Kazior static void codel_params_init(struct codel_params *params)
55d068ca2aSMichal Kazior {
56d068ca2aSMichal Kazior params->interval = MS2TIME(100);
57d068ca2aSMichal Kazior params->target = MS2TIME(5);
58d068ca2aSMichal Kazior params->ce_threshold = CODEL_DISABLED_THRESHOLD;
59dfcb63ceSToke Høiland-Jørgensen params->ce_threshold_mask = 0;
60dfcb63ceSToke Høiland-Jørgensen params->ce_threshold_selector = 0;
61d068ca2aSMichal Kazior params->ecn = false;
62d068ca2aSMichal Kazior }
63d068ca2aSMichal Kazior
codel_vars_init(struct codel_vars * vars)64d068ca2aSMichal Kazior static void codel_vars_init(struct codel_vars *vars)
65d068ca2aSMichal Kazior {
66d068ca2aSMichal Kazior memset(vars, 0, sizeof(*vars));
67d068ca2aSMichal Kazior }
68d068ca2aSMichal Kazior
codel_stats_init(struct codel_stats * stats)69d068ca2aSMichal Kazior static void codel_stats_init(struct codel_stats *stats)
70d068ca2aSMichal Kazior {
71d068ca2aSMichal Kazior stats->maxpacket = 0;
72d068ca2aSMichal Kazior }
73d068ca2aSMichal Kazior
74d068ca2aSMichal Kazior /*
75d068ca2aSMichal Kazior * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots
76d068ca2aSMichal Kazior * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2)
77d068ca2aSMichal Kazior *
78d068ca2aSMichal Kazior * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32
79d068ca2aSMichal Kazior */
codel_Newton_step(struct codel_vars * vars)80d068ca2aSMichal Kazior static void codel_Newton_step(struct codel_vars *vars)
81d068ca2aSMichal Kazior {
82d068ca2aSMichal Kazior u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
83d068ca2aSMichal Kazior u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32;
84d068ca2aSMichal Kazior u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2);
85d068ca2aSMichal Kazior
86d068ca2aSMichal Kazior val >>= 2; /* avoid overflow in following multiply */
87d068ca2aSMichal Kazior val = (val * invsqrt) >> (32 - 2 + 1);
88d068ca2aSMichal Kazior
89d068ca2aSMichal Kazior vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
90d068ca2aSMichal Kazior }
91d068ca2aSMichal Kazior
92d068ca2aSMichal Kazior /*
93d068ca2aSMichal Kazior * CoDel control_law is t + interval/sqrt(count)
94d068ca2aSMichal Kazior * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid
95d068ca2aSMichal Kazior * both sqrt() and divide operation.
96d068ca2aSMichal Kazior */
codel_control_law(codel_time_t t,codel_time_t interval,u32 rec_inv_sqrt)97d068ca2aSMichal Kazior static codel_time_t codel_control_law(codel_time_t t,
98d068ca2aSMichal Kazior codel_time_t interval,
99d068ca2aSMichal Kazior u32 rec_inv_sqrt)
100d068ca2aSMichal Kazior {
101d068ca2aSMichal Kazior return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT);
102d068ca2aSMichal Kazior }
103d068ca2aSMichal Kazior
codel_should_drop(const struct sk_buff * skb,void * ctx,struct codel_vars * vars,struct codel_params * params,struct codel_stats * stats,codel_skb_len_t skb_len_func,codel_skb_time_t skb_time_func,u32 * backlog,codel_time_t now)104d068ca2aSMichal Kazior static bool codel_should_drop(const struct sk_buff *skb,
105d068ca2aSMichal Kazior void *ctx,
106d068ca2aSMichal Kazior struct codel_vars *vars,
107d068ca2aSMichal Kazior struct codel_params *params,
108d068ca2aSMichal Kazior struct codel_stats *stats,
109d068ca2aSMichal Kazior codel_skb_len_t skb_len_func,
110d068ca2aSMichal Kazior codel_skb_time_t skb_time_func,
111d068ca2aSMichal Kazior u32 *backlog,
112d068ca2aSMichal Kazior codel_time_t now)
113d068ca2aSMichal Kazior {
114d068ca2aSMichal Kazior bool ok_to_drop;
115d068ca2aSMichal Kazior u32 skb_len;
116d068ca2aSMichal Kazior
117d068ca2aSMichal Kazior if (!skb) {
118d068ca2aSMichal Kazior vars->first_above_time = 0;
119d068ca2aSMichal Kazior return false;
120d068ca2aSMichal Kazior }
121d068ca2aSMichal Kazior
122d068ca2aSMichal Kazior skb_len = skb_len_func(skb);
123d068ca2aSMichal Kazior vars->ldelay = now - skb_time_func(skb);
124d068ca2aSMichal Kazior
125d068ca2aSMichal Kazior if (unlikely(skb_len > stats->maxpacket))
126d068ca2aSMichal Kazior stats->maxpacket = skb_len;
127d068ca2aSMichal Kazior
128d068ca2aSMichal Kazior if (codel_time_before(vars->ldelay, params->target) ||
129d068ca2aSMichal Kazior *backlog <= params->mtu) {
130d068ca2aSMichal Kazior /* went below - stay below for at least interval */
131d068ca2aSMichal Kazior vars->first_above_time = 0;
132d068ca2aSMichal Kazior return false;
133d068ca2aSMichal Kazior }
134d068ca2aSMichal Kazior ok_to_drop = false;
135d068ca2aSMichal Kazior if (vars->first_above_time == 0) {
136d068ca2aSMichal Kazior /* just went above from below. If we stay above
137d068ca2aSMichal Kazior * for at least interval we'll say it's ok to drop
138d068ca2aSMichal Kazior */
139d068ca2aSMichal Kazior vars->first_above_time = now + params->interval;
140d068ca2aSMichal Kazior } else if (codel_time_after(now, vars->first_above_time)) {
141d068ca2aSMichal Kazior ok_to_drop = true;
142d068ca2aSMichal Kazior }
143d068ca2aSMichal Kazior return ok_to_drop;
144d068ca2aSMichal Kazior }
145d068ca2aSMichal Kazior
codel_dequeue(void * ctx,u32 * backlog,struct codel_params * params,struct codel_vars * vars,struct codel_stats * stats,codel_skb_len_t skb_len_func,codel_skb_time_t skb_time_func,codel_skb_drop_t drop_func,codel_skb_dequeue_t dequeue_func)146d068ca2aSMichal Kazior static struct sk_buff *codel_dequeue(void *ctx,
147d068ca2aSMichal Kazior u32 *backlog,
148d068ca2aSMichal Kazior struct codel_params *params,
149d068ca2aSMichal Kazior struct codel_vars *vars,
150d068ca2aSMichal Kazior struct codel_stats *stats,
151d068ca2aSMichal Kazior codel_skb_len_t skb_len_func,
152d068ca2aSMichal Kazior codel_skb_time_t skb_time_func,
153d068ca2aSMichal Kazior codel_skb_drop_t drop_func,
154d068ca2aSMichal Kazior codel_skb_dequeue_t dequeue_func)
155d068ca2aSMichal Kazior {
156d068ca2aSMichal Kazior struct sk_buff *skb = dequeue_func(vars, ctx);
157d068ca2aSMichal Kazior codel_time_t now;
158d068ca2aSMichal Kazior bool drop;
159d068ca2aSMichal Kazior
160d068ca2aSMichal Kazior if (!skb) {
161d068ca2aSMichal Kazior vars->dropping = false;
162d068ca2aSMichal Kazior return skb;
163d068ca2aSMichal Kazior }
164d068ca2aSMichal Kazior now = codel_get_time();
165d068ca2aSMichal Kazior drop = codel_should_drop(skb, ctx, vars, params, stats,
166d068ca2aSMichal Kazior skb_len_func, skb_time_func, backlog, now);
167d068ca2aSMichal Kazior if (vars->dropping) {
168d068ca2aSMichal Kazior if (!drop) {
169d068ca2aSMichal Kazior /* sojourn time below target - leave dropping state */
170d068ca2aSMichal Kazior vars->dropping = false;
171d068ca2aSMichal Kazior } else if (codel_time_after_eq(now, vars->drop_next)) {
172d068ca2aSMichal Kazior /* It's time for the next drop. Drop the current
173d068ca2aSMichal Kazior * packet and dequeue the next. The dequeue might
174d068ca2aSMichal Kazior * take us out of dropping state.
175d068ca2aSMichal Kazior * If not, schedule the next drop.
176d068ca2aSMichal Kazior * A large backlog might result in drop rates so high
177d068ca2aSMichal Kazior * that the next drop should happen now,
178d068ca2aSMichal Kazior * hence the while loop.
179d068ca2aSMichal Kazior */
180d068ca2aSMichal Kazior while (vars->dropping &&
181d068ca2aSMichal Kazior codel_time_after_eq(now, vars->drop_next)) {
182d068ca2aSMichal Kazior vars->count++; /* dont care of possible wrap
183d068ca2aSMichal Kazior * since there is no more divide
184d068ca2aSMichal Kazior */
185d068ca2aSMichal Kazior codel_Newton_step(vars);
186d068ca2aSMichal Kazior if (params->ecn && INET_ECN_set_ce(skb)) {
187d068ca2aSMichal Kazior stats->ecn_mark++;
188d068ca2aSMichal Kazior vars->drop_next =
189d068ca2aSMichal Kazior codel_control_law(vars->drop_next,
190d068ca2aSMichal Kazior params->interval,
191d068ca2aSMichal Kazior vars->rec_inv_sqrt);
192d068ca2aSMichal Kazior goto end;
193d068ca2aSMichal Kazior }
194d068ca2aSMichal Kazior stats->drop_len += skb_len_func(skb);
195d068ca2aSMichal Kazior drop_func(skb, ctx);
196d068ca2aSMichal Kazior stats->drop_count++;
197d068ca2aSMichal Kazior skb = dequeue_func(vars, ctx);
198d068ca2aSMichal Kazior if (!codel_should_drop(skb, ctx,
199d068ca2aSMichal Kazior vars, params, stats,
200d068ca2aSMichal Kazior skb_len_func,
201d068ca2aSMichal Kazior skb_time_func,
202d068ca2aSMichal Kazior backlog, now)) {
203d068ca2aSMichal Kazior /* leave dropping state */
204d068ca2aSMichal Kazior vars->dropping = false;
205d068ca2aSMichal Kazior } else {
206d068ca2aSMichal Kazior /* and schedule the next drop */
207d068ca2aSMichal Kazior vars->drop_next =
208d068ca2aSMichal Kazior codel_control_law(vars->drop_next,
209d068ca2aSMichal Kazior params->interval,
210d068ca2aSMichal Kazior vars->rec_inv_sqrt);
211d068ca2aSMichal Kazior }
212d068ca2aSMichal Kazior }
213d068ca2aSMichal Kazior }
214d068ca2aSMichal Kazior } else if (drop) {
215d068ca2aSMichal Kazior u32 delta;
216d068ca2aSMichal Kazior
217d068ca2aSMichal Kazior if (params->ecn && INET_ECN_set_ce(skb)) {
218d068ca2aSMichal Kazior stats->ecn_mark++;
219d068ca2aSMichal Kazior } else {
220d068ca2aSMichal Kazior stats->drop_len += skb_len_func(skb);
221d068ca2aSMichal Kazior drop_func(skb, ctx);
222d068ca2aSMichal Kazior stats->drop_count++;
223d068ca2aSMichal Kazior
224d068ca2aSMichal Kazior skb = dequeue_func(vars, ctx);
225d068ca2aSMichal Kazior drop = codel_should_drop(skb, ctx, vars, params,
226d068ca2aSMichal Kazior stats, skb_len_func,
227d068ca2aSMichal Kazior skb_time_func, backlog, now);
228d068ca2aSMichal Kazior }
229d068ca2aSMichal Kazior vars->dropping = true;
230d068ca2aSMichal Kazior /* if min went above target close to when we last went below it
231d068ca2aSMichal Kazior * assume that the drop rate that controlled the queue on the
232d068ca2aSMichal Kazior * last cycle is a good starting point to control it now.
233d068ca2aSMichal Kazior */
234d068ca2aSMichal Kazior delta = vars->count - vars->lastcount;
235d068ca2aSMichal Kazior if (delta > 1 &&
236d068ca2aSMichal Kazior codel_time_before(now - vars->drop_next,
237d068ca2aSMichal Kazior 16 * params->interval)) {
238d068ca2aSMichal Kazior vars->count = delta;
239d068ca2aSMichal Kazior /* we dont care if rec_inv_sqrt approximation
240d068ca2aSMichal Kazior * is not very precise :
241d068ca2aSMichal Kazior * Next Newton steps will correct it quadratically.
242d068ca2aSMichal Kazior */
243d068ca2aSMichal Kazior codel_Newton_step(vars);
244d068ca2aSMichal Kazior } else {
245d068ca2aSMichal Kazior vars->count = 1;
246d068ca2aSMichal Kazior vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
247d068ca2aSMichal Kazior }
248d068ca2aSMichal Kazior vars->lastcount = vars->count;
249d068ca2aSMichal Kazior vars->drop_next = codel_control_law(now, params->interval,
250d068ca2aSMichal Kazior vars->rec_inv_sqrt);
251d068ca2aSMichal Kazior }
252d068ca2aSMichal Kazior end:
253e72aeb9eSEric Dumazet if (skb && codel_time_after(vars->ldelay, params->ce_threshold)) {
254e72aeb9eSEric Dumazet bool set_ce = true;
255e72aeb9eSEric Dumazet
256dfcb63ceSToke Høiland-Jørgensen if (params->ce_threshold_mask) {
257dfcb63ceSToke Høiland-Jørgensen int dsfield = skb_get_dsfield(skb);
258e72aeb9eSEric Dumazet
259dfcb63ceSToke Høiland-Jørgensen set_ce = (dsfield >= 0 &&
260dfcb63ceSToke Høiland-Jørgensen (((u8)dsfield & params->ce_threshold_mask) ==
261dfcb63ceSToke Høiland-Jørgensen params->ce_threshold_selector));
262e72aeb9eSEric Dumazet }
263e72aeb9eSEric Dumazet if (set_ce && INET_ECN_set_ce(skb))
264d068ca2aSMichal Kazior stats->ce_mark++;
265e72aeb9eSEric Dumazet }
266d068ca2aSMichal Kazior return skb;
267d068ca2aSMichal Kazior }
268d068ca2aSMichal Kazior
269d068ca2aSMichal Kazior #endif
270