1#ifndef __NET_SCHED_CODEL_H
2#define __NET_SCHED_CODEL_H
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44#include <linux/types.h>
45#include <linux/ktime.h>
46#include <linux/skbuff.h>
47#include <net/pkt_sched.h>
48#include <net/inet_ecn.h>
49
50
51
52
53
54
55
56
57
58
59
60
61
62typedef u32 codel_time_t;
63typedef s32 codel_tdiff_t;
64#define CODEL_SHIFT 10
65#define MS2TIME(a) ((a * NSEC_PER_MSEC) >> CODEL_SHIFT)
66
67static inline codel_time_t codel_get_time(void)
68{
69 u64 ns = ktime_get_ns();
70
71 return ns >> CODEL_SHIFT;
72}
73
74
75
76
77
78#define codel_time_after(a, b) \
79 (typecheck(codel_time_t, a) && \
80 typecheck(codel_time_t, b) && \
81 ((s32)((a) - (b)) > 0))
82#define codel_time_before(a, b) codel_time_after(b, a)
83
84#define codel_time_after_eq(a, b) \
85 (typecheck(codel_time_t, a) && \
86 typecheck(codel_time_t, b) && \
87 ((s32)((a) - (b)) >= 0))
88#define codel_time_before_eq(a, b) codel_time_after_eq(b, a)
89
90
91struct codel_skb_cb {
92 codel_time_t enqueue_time;
93};
94
95static struct codel_skb_cb *get_codel_cb(const struct sk_buff *skb)
96{
97 qdisc_cb_private_validate(skb, sizeof(struct codel_skb_cb));
98 return (struct codel_skb_cb *)qdisc_skb_cb(skb)->data;
99}
100
101static codel_time_t codel_get_enqueue_time(const struct sk_buff *skb)
102{
103 return get_codel_cb(skb)->enqueue_time;
104}
105
106static void codel_set_enqueue_time(struct sk_buff *skb)
107{
108 get_codel_cb(skb)->enqueue_time = codel_get_time();
109}
110
111static inline u32 codel_time_to_us(codel_time_t val)
112{
113 u64 valns = ((u64)val << CODEL_SHIFT);
114
115 do_div(valns, NSEC_PER_USEC);
116 return (u32)valns;
117}
118
119
120
121
122
123
124
125
126
127struct codel_params {
128 codel_time_t target;
129 codel_time_t ce_threshold;
130 codel_time_t interval;
131 u32 mtu;
132 bool ecn;
133};
134
135
136
137
138
139
140
141
142
143
144
145
146
147struct codel_vars {
148 u32 count;
149 u32 lastcount;
150 bool dropping;
151 u16 rec_inv_sqrt;
152 codel_time_t first_above_time;
153 codel_time_t drop_next;
154 codel_time_t ldelay;
155};
156
157#define REC_INV_SQRT_BITS (8 * sizeof(u16))
158
159#define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS)
160
161
162
163
164
165
166
167
168struct codel_stats {
169 u32 maxpacket;
170 u32 drop_count;
171 u32 ecn_mark;
172 u32 ce_mark;
173};
174
175#define CODEL_DISABLED_THRESHOLD INT_MAX
176
177static void codel_params_init(struct codel_params *params,
178 const struct Qdisc *sch)
179{
180 params->interval = MS2TIME(100);
181 params->target = MS2TIME(5);
182 params->mtu = psched_mtu(qdisc_dev(sch));
183 params->ce_threshold = CODEL_DISABLED_THRESHOLD;
184 params->ecn = false;
185}
186
187static void codel_vars_init(struct codel_vars *vars)
188{
189 memset(vars, 0, sizeof(*vars));
190}
191
192static void codel_stats_init(struct codel_stats *stats)
193{
194 stats->maxpacket = 0;
195}
196
197
198
199
200
201
202
203static void codel_Newton_step(struct codel_vars *vars)
204{
205 u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
206 u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32;
207 u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2);
208
209 val >>= 2;
210 val = (val * invsqrt) >> (32 - 2 + 1);
211
212 vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
213}
214
215
216
217
218
219
220static codel_time_t codel_control_law(codel_time_t t,
221 codel_time_t interval,
222 u32 rec_inv_sqrt)
223{
224 return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT);
225}
226
227static bool codel_should_drop(const struct sk_buff *skb,
228 struct Qdisc *sch,
229 struct codel_vars *vars,
230 struct codel_params *params,
231 struct codel_stats *stats,
232 codel_time_t now)
233{
234 bool ok_to_drop;
235
236 if (!skb) {
237 vars->first_above_time = 0;
238 return false;
239 }
240
241 vars->ldelay = now - codel_get_enqueue_time(skb);
242 sch->qstats.backlog -= qdisc_pkt_len(skb);
243
244 if (unlikely(qdisc_pkt_len(skb) > stats->maxpacket))
245 stats->maxpacket = qdisc_pkt_len(skb);
246
247 if (codel_time_before(vars->ldelay, params->target) ||
248 sch->qstats.backlog <= params->mtu) {
249
250 vars->first_above_time = 0;
251 return false;
252 }
253 ok_to_drop = false;
254 if (vars->first_above_time == 0) {
255
256
257
258 vars->first_above_time = now + params->interval;
259 } else if (codel_time_after(now, vars->first_above_time)) {
260 ok_to_drop = true;
261 }
262 return ok_to_drop;
263}
264
265typedef struct sk_buff * (*codel_skb_dequeue_t)(struct codel_vars *vars,
266 struct Qdisc *sch);
267
268static struct sk_buff *codel_dequeue(struct Qdisc *sch,
269 struct codel_params *params,
270 struct codel_vars *vars,
271 struct codel_stats *stats,
272 codel_skb_dequeue_t dequeue_func)
273{
274 struct sk_buff *skb = dequeue_func(vars, sch);
275 codel_time_t now;
276 bool drop;
277
278 if (!skb) {
279 vars->dropping = false;
280 return skb;
281 }
282 now = codel_get_time();
283 drop = codel_should_drop(skb, sch, vars, params, stats, now);
284 if (vars->dropping) {
285 if (!drop) {
286
287 vars->dropping = false;
288 } else if (codel_time_after_eq(now, vars->drop_next)) {
289
290
291
292
293
294
295
296
297 while (vars->dropping &&
298 codel_time_after_eq(now, vars->drop_next)) {
299 vars->count++;
300
301
302 codel_Newton_step(vars);
303 if (params->ecn && INET_ECN_set_ce(skb)) {
304 stats->ecn_mark++;
305 vars->drop_next =
306 codel_control_law(vars->drop_next,
307 params->interval,
308 vars->rec_inv_sqrt);
309 goto end;
310 }
311 qdisc_drop(skb, sch);
312 stats->drop_count++;
313 skb = dequeue_func(vars, sch);
314 if (!codel_should_drop(skb, sch,
315 vars, params, stats, now)) {
316
317 vars->dropping = false;
318 } else {
319
320 vars->drop_next =
321 codel_control_law(vars->drop_next,
322 params->interval,
323 vars->rec_inv_sqrt);
324 }
325 }
326 }
327 } else if (drop) {
328 u32 delta;
329
330 if (params->ecn && INET_ECN_set_ce(skb)) {
331 stats->ecn_mark++;
332 } else {
333 qdisc_drop(skb, sch);
334 stats->drop_count++;
335
336 skb = dequeue_func(vars, sch);
337 drop = codel_should_drop(skb, sch, vars, params,
338 stats, now);
339 }
340 vars->dropping = true;
341
342
343
344
345 delta = vars->count - vars->lastcount;
346 if (delta > 1 &&
347 codel_time_before(now - vars->drop_next,
348 16 * params->interval)) {
349 vars->count = delta;
350
351
352
353
354 codel_Newton_step(vars);
355 } else {
356 vars->count = 1;
357 vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
358 }
359 vars->lastcount = vars->count;
360 vars->drop_next = codel_control_law(now, params->interval,
361 vars->rec_inv_sqrt);
362 }
363end:
364 if (skb && codel_time_after(vars->ldelay, params->ce_threshold) &&
365 INET_ECN_set_ce(skb))
366 stats->ce_mark++;
367 return skb;
368}
369#endif
370