Merge branch 'net-tls-small-code-cleanup'
[linux-block.git] / net / sched / sch_red.c
CommitLineData
1da177e4
LT
1/*
2 * net/sched/sch_red.c Random Early Detection queue.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 * Changes:
dba051f3 12 * J Hadi Salim 980914: computation fixes
1da177e4 13 * Alexey Makarenko <makar@phoenix.kharkov.ua> 990814: qave on idle link was calculated incorrectly.
dba051f3 14 * J Hadi Salim 980816: ECN support
1da177e4
LT
15 */
16
1da177e4 17#include <linux/module.h>
1da177e4
LT
18#include <linux/types.h>
19#include <linux/kernel.h>
1da177e4 20#include <linux/skbuff.h>
1da177e4 21#include <net/pkt_sched.h>
602f3baf 22#include <net/pkt_cls.h>
1da177e4 23#include <net/inet_ecn.h>
6b31b28a 24#include <net/red.h>
1da177e4
LT
25
26
6b31b28a 27/* Parameters, settable by user:
1da177e4
LT
28 -----------------------------
29
30 limit - bytes (must be > qth_max + burst)
31
32 Hard limit on queue length, should be chosen >qth_max
33 to allow packet bursts. This parameter does not
34 affect the algorithms behaviour and can be chosen
35 arbitrarily high (well, less than ram size)
36 Really, this limit will never be reached
37 if RED works correctly.
1da177e4
LT
38 */
39
cc7ec456 40struct red_sched_data {
6b31b28a
TG
41 u32 limit; /* HARD maximal queue length */
42 unsigned char flags;
8af2a218 43 struct timer_list adapt_timer;
cdeabbb8 44 struct Qdisc *sch;
6b31b28a 45 struct red_parms parms;
eeca6688 46 struct red_vars vars;
6b31b28a 47 struct red_stats stats;
f38c39d6 48 struct Qdisc *qdisc;
1da177e4
LT
49};
50
6b31b28a 51static inline int red_use_ecn(struct red_sched_data *q)
1da177e4 52{
6b31b28a 53 return q->flags & TC_RED_ECN;
1da177e4
LT
54}
55
bdc450a0
TG
56static inline int red_use_harddrop(struct red_sched_data *q)
57{
58 return q->flags & TC_RED_HARDDROP;
59}
60
520ac30f
ED
61static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch,
62 struct sk_buff **to_free)
1da177e4
LT
63{
64 struct red_sched_data *q = qdisc_priv(sch);
f38c39d6
PM
65 struct Qdisc *child = q->qdisc;
66 int ret;
1da177e4 67
eeca6688
ED
68 q->vars.qavg = red_calc_qavg(&q->parms,
69 &q->vars,
70 child->qstats.backlog);
1da177e4 71
eeca6688
ED
72 if (red_is_idling(&q->vars))
73 red_end_of_idle_period(&q->vars);
1da177e4 74
eeca6688 75 switch (red_action(&q->parms, &q->vars, q->vars.qavg)) {
cc7ec456
ED
76 case RED_DONT_MARK:
77 break;
78
79 case RED_PROB_MARK:
25331d6c 80 qdisc_qstats_overlimit(sch);
cc7ec456
ED
81 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
82 q->stats.prob_drop++;
83 goto congestion_drop;
84 }
85
86 q->stats.prob_mark++;
87 break;
88
89 case RED_HARD_MARK:
25331d6c 90 qdisc_qstats_overlimit(sch);
cc7ec456
ED
91 if (red_use_harddrop(q) || !red_use_ecn(q) ||
92 !INET_ECN_set_ce(skb)) {
93 q->stats.forced_drop++;
94 goto congestion_drop;
95 }
96
97 q->stats.forced_mark++;
98 break;
1da177e4
LT
99 }
100
520ac30f 101 ret = qdisc_enqueue(skb, child, to_free);
f38c39d6 102 if (likely(ret == NET_XMIT_SUCCESS)) {
d7f4f332 103 qdisc_qstats_backlog_inc(sch, skb);
f38c39d6 104 sch->q.qlen++;
378a2f09 105 } else if (net_xmit_drop_count(ret)) {
f38c39d6 106 q->stats.pdrop++;
25331d6c 107 qdisc_qstats_drop(sch);
f38c39d6
PM
108 }
109 return ret;
6b31b28a
TG
110
111congestion_drop:
520ac30f 112 qdisc_drop(skb, sch, to_free);
1da177e4
LT
113 return NET_XMIT_CN;
114}
115
cc7ec456 116static struct sk_buff *red_dequeue(struct Qdisc *sch)
1da177e4
LT
117{
118 struct sk_buff *skb;
119 struct red_sched_data *q = qdisc_priv(sch);
f38c39d6 120 struct Qdisc *child = q->qdisc;
1da177e4 121
f38c39d6 122 skb = child->dequeue(child);
9190b3b3
ED
123 if (skb) {
124 qdisc_bstats_update(sch, skb);
d7f4f332 125 qdisc_qstats_backlog_dec(sch, skb);
f38c39d6 126 sch->q.qlen--;
9190b3b3 127 } else {
eeca6688
ED
128 if (!red_is_idling(&q->vars))
129 red_start_of_idle_period(&q->vars);
9190b3b3 130 }
9e178ff2 131 return skb;
1da177e4
LT
132}
133
cc7ec456 134static struct sk_buff *red_peek(struct Qdisc *sch)
8e3af978
JP
135{
136 struct red_sched_data *q = qdisc_priv(sch);
137 struct Qdisc *child = q->qdisc;
138
139 return child->ops->peek(child);
140}
141
cc7ec456 142static void red_reset(struct Qdisc *sch)
1da177e4
LT
143{
144 struct red_sched_data *q = qdisc_priv(sch);
145
f38c39d6 146 qdisc_reset(q->qdisc);
d7f4f332 147 sch->qstats.backlog = 0;
f38c39d6 148 sch->q.qlen = 0;
eeca6688 149 red_restart(&q->vars);
1da177e4
LT
150}
151
602f3baf
NF
152static int red_offload(struct Qdisc *sch, bool enable)
153{
154 struct red_sched_data *q = qdisc_priv(sch);
155 struct net_device *dev = qdisc_dev(sch);
156 struct tc_red_qopt_offload opt = {
157 .handle = sch->handle,
158 .parent = sch->parent,
159 };
160
161 if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
162 return -EOPNOTSUPP;
163
164 if (enable) {
165 opt.command = TC_RED_REPLACE;
166 opt.set.min = q->parms.qth_min >> q->parms.Wlog;
167 opt.set.max = q->parms.qth_max >> q->parms.Wlog;
168 opt.set.probability = q->parms.max_P;
c0b7490b 169 opt.set.limit = q->limit;
602f3baf 170 opt.set.is_ecn = red_use_ecn(q);
190852a5 171 opt.set.is_harddrop = red_use_harddrop(q);
416ef9b1 172 opt.set.qstats = &sch->qstats;
602f3baf
NF
173 } else {
174 opt.command = TC_RED_DESTROY;
175 }
176
8234af2d 177 return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &opt);
602f3baf
NF
178}
179
f38c39d6
PM
180static void red_destroy(struct Qdisc *sch)
181{
182 struct red_sched_data *q = qdisc_priv(sch);
8af2a218
ED
183
184 del_timer_sync(&q->adapt_timer);
602f3baf 185 red_offload(sch, false);
86bd446b 186 qdisc_put(q->qdisc);
f38c39d6
PM
187}
188
27a3421e
PM
189static const struct nla_policy red_policy[TCA_RED_MAX + 1] = {
190 [TCA_RED_PARMS] = { .len = sizeof(struct tc_red_qopt) },
191 [TCA_RED_STAB] = { .len = RED_STAB_SIZE },
a73ed26b 192 [TCA_RED_MAX_P] = { .type = NLA_U32 },
27a3421e
PM
193};
194
2030721c
AA
195static int red_change(struct Qdisc *sch, struct nlattr *opt,
196 struct netlink_ext_ack *extack)
1da177e4 197{
0c8d13ac 198 struct Qdisc *old_child = NULL, *child = NULL;
1da177e4 199 struct red_sched_data *q = qdisc_priv(sch);
1e90474c 200 struct nlattr *tb[TCA_RED_MAX + 1];
1da177e4 201 struct tc_red_qopt *ctl;
cee63723 202 int err;
a73ed26b 203 u32 max_P;
1da177e4 204
cee63723 205 if (opt == NULL)
dba051f3
TG
206 return -EINVAL;
207
fceb6435 208 err = nla_parse_nested(tb, TCA_RED_MAX, opt, red_policy, NULL);
cee63723
PM
209 if (err < 0)
210 return err;
211
1e90474c 212 if (tb[TCA_RED_PARMS] == NULL ||
27a3421e 213 tb[TCA_RED_STAB] == NULL)
1da177e4
LT
214 return -EINVAL;
215
a73ed26b
ED
216 max_P = tb[TCA_RED_MAX_P] ? nla_get_u32(tb[TCA_RED_MAX_P]) : 0;
217
1e90474c 218 ctl = nla_data(tb[TCA_RED_PARMS]);
8afa10cb
NF
219 if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog))
220 return -EINVAL;
1da177e4 221
f38c39d6 222 if (ctl->limit > 0) {
a38a9882
AA
223 child = fifo_create_dflt(sch, &bfifo_qdisc_ops, ctl->limit,
224 extack);
fb0305ce
PM
225 if (IS_ERR(child))
226 return PTR_ERR(child);
f38c39d6 227
44a63b13 228 /* child is fifo, no need to check for noop_qdisc */
49b49971 229 qdisc_hash_add(child, true);
44a63b13
PA
230 }
231
1da177e4
LT
232 sch_tree_lock(sch);
233 q->flags = ctl->flags;
1da177e4 234 q->limit = ctl->limit;
5e50da01 235 if (child) {
e5f0e8f8 236 qdisc_tree_flush_backlog(q->qdisc);
0c8d13ac 237 old_child = q->qdisc;
b94c8afc 238 q->qdisc = child;
5e50da01 239 }
1da177e4 240
eeca6688
ED
241 red_set_parms(&q->parms,
242 ctl->qth_min, ctl->qth_max, ctl->Wlog,
a73ed26b
ED
243 ctl->Plog, ctl->Scell_log,
244 nla_data(tb[TCA_RED_STAB]),
245 max_P);
eeca6688 246 red_set_vars(&q->vars);
6b31b28a 247
8af2a218
ED
248 del_timer(&q->adapt_timer);
249 if (ctl->flags & TC_RED_ADAPTATIVE)
250 mod_timer(&q->adapt_timer, jiffies + HZ/2);
251
1ee5fa1e 252 if (!q->qdisc->q.qlen)
eeca6688 253 red_start_of_idle_period(&q->vars);
dba051f3 254
1da177e4 255 sch_tree_unlock(sch);
0c8d13ac 256
602f3baf 257 red_offload(sch, true);
0c8d13ac
JK
258
259 if (old_child)
260 qdisc_put(old_child);
1da177e4
LT
261 return 0;
262}
263
cdeabbb8 264static inline void red_adaptative_timer(struct timer_list *t)
8af2a218 265{
cdeabbb8
KC
266 struct red_sched_data *q = from_timer(q, t, adapt_timer);
267 struct Qdisc *sch = q->sch;
8af2a218
ED
268 spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
269
270 spin_lock(root_lock);
eeca6688 271 red_adaptative_algo(&q->parms, &q->vars);
8af2a218
ED
272 mod_timer(&q->adapt_timer, jiffies + HZ/2);
273 spin_unlock(root_lock);
274}
275
e63d7dfd
AA
276static int red_init(struct Qdisc *sch, struct nlattr *opt,
277 struct netlink_ext_ack *extack)
1da177e4 278{
f38c39d6
PM
279 struct red_sched_data *q = qdisc_priv(sch);
280
281 q->qdisc = &noop_qdisc;
cdeabbb8
KC
282 q->sch = sch;
283 timer_setup(&q->adapt_timer, red_adaptative_timer, 0);
2030721c 284 return red_change(sch, opt, extack);
1da177e4
LT
285}
286
dad54c0f 287static int red_dump_offload_stats(struct Qdisc *sch)
602f3baf 288{
602f3baf 289 struct tc_red_qopt_offload hw_stats = {
ee9d3429 290 .command = TC_RED_STATS,
602f3baf
NF
291 .handle = sch->handle,
292 .parent = sch->parent,
ee9d3429
AM
293 {
294 .stats.bstats = &sch->bstats,
295 .stats.qstats = &sch->qstats,
296 },
602f3baf 297 };
8234af2d 298
b592843c 299 return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_RED, &hw_stats);
602f3baf
NF
300}
301
1da177e4
LT
302static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
303{
304 struct red_sched_data *q = qdisc_priv(sch);
1e90474c 305 struct nlattr *opts = NULL;
6b31b28a
TG
306 struct tc_red_qopt opt = {
307 .limit = q->limit,
308 .flags = q->flags,
309 .qth_min = q->parms.qth_min >> q->parms.Wlog,
310 .qth_max = q->parms.qth_max >> q->parms.Wlog,
311 .Wlog = q->parms.Wlog,
312 .Plog = q->parms.Plog,
313 .Scell_log = q->parms.Scell_log,
314 };
602f3baf 315 int err;
1da177e4 316
dad54c0f 317 err = red_dump_offload_stats(sch);
602f3baf
NF
318 if (err)
319 goto nla_put_failure;
320
1e90474c
PM
321 opts = nla_nest_start(skb, TCA_OPTIONS);
322 if (opts == NULL)
323 goto nla_put_failure;
1b34ec43
DM
324 if (nla_put(skb, TCA_RED_PARMS, sizeof(opt), &opt) ||
325 nla_put_u32(skb, TCA_RED_MAX_P, q->parms.max_P))
326 goto nla_put_failure;
1e90474c 327 return nla_nest_end(skb, opts);
1da177e4 328
1e90474c 329nla_put_failure:
bc3ed28c
TG
330 nla_nest_cancel(skb, opts);
331 return -EMSGSIZE;
1da177e4
LT
332}
333
334static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
335{
336 struct red_sched_data *q = qdisc_priv(sch);
602f3baf 337 struct net_device *dev = qdisc_dev(sch);
f8253df5 338 struct tc_red_xstats st = {0};
6b31b28a 339
428a68af 340 if (sch->flags & TCQ_F_OFFLOADED) {
602f3baf 341 struct tc_red_qopt_offload hw_stats_request = {
ee9d3429 342 .command = TC_RED_XSTATS,
602f3baf
NF
343 .handle = sch->handle,
344 .parent = sch->parent,
ee9d3429 345 {
f8253df5 346 .xstats = &q->stats,
ee9d3429 347 },
602f3baf 348 };
f8253df5
NF
349 dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED,
350 &hw_stats_request);
602f3baf 351 }
f8253df5
NF
352 st.early = q->stats.prob_drop + q->stats.forced_drop;
353 st.pdrop = q->stats.pdrop;
354 st.other = q->stats.other;
355 st.marked = q->stats.prob_mark + q->stats.forced_mark;
602f3baf 356
6b31b28a 357 return gnet_stats_copy_app(d, &st, sizeof(st));
1da177e4
LT
358}
359
f38c39d6
PM
360static int red_dump_class(struct Qdisc *sch, unsigned long cl,
361 struct sk_buff *skb, struct tcmsg *tcm)
362{
363 struct red_sched_data *q = qdisc_priv(sch);
364
f38c39d6
PM
365 tcm->tcm_handle |= TC_H_MIN(1);
366 tcm->tcm_info = q->qdisc->handle;
367 return 0;
368}
369
bf2a752b
JK
370static void red_graft_offload(struct Qdisc *sch,
371 struct Qdisc *new, struct Qdisc *old,
372 struct netlink_ext_ack *extack)
373{
374 struct tc_red_qopt_offload graft_offload = {
375 .handle = sch->handle,
376 .parent = sch->parent,
377 .child_handle = new->handle,
378 .command = TC_RED_GRAFT,
379 };
380
381 qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old,
382 TC_SETUP_QDISC_RED, &graft_offload, extack);
383}
384
f38c39d6 385static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
653d6fd6 386 struct Qdisc **old, struct netlink_ext_ack *extack)
f38c39d6
PM
387{
388 struct red_sched_data *q = qdisc_priv(sch);
389
390 if (new == NULL)
391 new = &noop_qdisc;
392
86a7996c 393 *old = qdisc_replace(sch, new, &q->qdisc);
bf2a752b
JK
394
395 red_graft_offload(sch, new, *old, extack);
f38c39d6
PM
396 return 0;
397}
398
399static struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg)
400{
401 struct red_sched_data *q = qdisc_priv(sch);
402 return q->qdisc;
403}
404
143976ce 405static unsigned long red_find(struct Qdisc *sch, u32 classid)
f38c39d6
PM
406{
407 return 1;
408}
409
f38c39d6
PM
410static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker)
411{
412 if (!walker->stop) {
413 if (walker->count >= walker->skip)
414 if (walker->fn(sch, 1, walker) < 0) {
415 walker->stop = 1;
416 return;
417 }
418 walker->count++;
419 }
420}
421
20fea08b 422static const struct Qdisc_class_ops red_class_ops = {
f38c39d6
PM
423 .graft = red_graft,
424 .leaf = red_leaf,
143976ce 425 .find = red_find,
f38c39d6 426 .walk = red_walk,
f38c39d6
PM
427 .dump = red_dump_class,
428};
429
20fea08b 430static struct Qdisc_ops red_qdisc_ops __read_mostly = {
1da177e4
LT
431 .id = "red",
432 .priv_size = sizeof(struct red_sched_data),
f38c39d6 433 .cl_ops = &red_class_ops,
1da177e4
LT
434 .enqueue = red_enqueue,
435 .dequeue = red_dequeue,
8e3af978 436 .peek = red_peek,
1da177e4
LT
437 .init = red_init,
438 .reset = red_reset,
f38c39d6 439 .destroy = red_destroy,
1da177e4
LT
440 .change = red_change,
441 .dump = red_dump,
442 .dump_stats = red_dump_stats,
443 .owner = THIS_MODULE,
444};
445
446static int __init red_module_init(void)
447{
448 return register_qdisc(&red_qdisc_ops);
449}
dba051f3
TG
450
451static void __exit red_module_exit(void)
1da177e4
LT
452{
453 unregister_qdisc(&red_qdisc_ops);
454}
dba051f3 455
1da177e4
LT
456module_init(red_module_init)
457module_exit(red_module_exit)
dba051f3 458
1da177e4 459MODULE_LICENSE("GPL");