net_sched: sfq: extend limits
[deliverable/linux.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
LT
21#include <net/pkt_sched.h>
22#include <net/inet_ecn.h>
6b31b28a 23#include <net/red.h>
1da177e4
LT
24
25
6b31b28a 26/* Parameters, settable by user:
1da177e4
LT
27 -----------------------------
28
29 limit - bytes (must be > qth_max + burst)
30
31 Hard limit on queue length, should be chosen >qth_max
32 to allow packet bursts. This parameter does not
33 affect the algorithms behaviour and can be chosen
34 arbitrarily high (well, less than ram size)
35 Really, this limit will never be reached
36 if RED works correctly.
1da177e4
LT
37 */
38
cc7ec456 39struct red_sched_data {
6b31b28a
TG
40 u32 limit; /* HARD maximal queue length */
41 unsigned char flags;
8af2a218 42 struct timer_list adapt_timer;
6b31b28a
TG
43 struct red_parms parms;
44 struct red_stats stats;
f38c39d6 45 struct Qdisc *qdisc;
1da177e4
LT
46};
47
6b31b28a 48static inline int red_use_ecn(struct red_sched_data *q)
1da177e4 49{
6b31b28a 50 return q->flags & TC_RED_ECN;
1da177e4
LT
51}
52
bdc450a0
TG
53static inline int red_use_harddrop(struct red_sched_data *q)
54{
55 return q->flags & TC_RED_HARDDROP;
56}
57
cc7ec456 58static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch)
1da177e4
LT
59{
60 struct red_sched_data *q = qdisc_priv(sch);
f38c39d6
PM
61 struct Qdisc *child = q->qdisc;
62 int ret;
1da177e4 63
f38c39d6 64 q->parms.qavg = red_calc_qavg(&q->parms, child->qstats.backlog);
1da177e4 65
6b31b28a
TG
66 if (red_is_idling(&q->parms))
67 red_end_of_idle_period(&q->parms);
1da177e4 68
6b31b28a 69 switch (red_action(&q->parms, q->parms.qavg)) {
cc7ec456
ED
70 case RED_DONT_MARK:
71 break;
72
73 case RED_PROB_MARK:
74 sch->qstats.overlimits++;
75 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
76 q->stats.prob_drop++;
77 goto congestion_drop;
78 }
79
80 q->stats.prob_mark++;
81 break;
82
83 case RED_HARD_MARK:
84 sch->qstats.overlimits++;
85 if (red_use_harddrop(q) || !red_use_ecn(q) ||
86 !INET_ECN_set_ce(skb)) {
87 q->stats.forced_drop++;
88 goto congestion_drop;
89 }
90
91 q->stats.forced_mark++;
92 break;
1da177e4
LT
93 }
94
5f86173b 95 ret = qdisc_enqueue(skb, child);
f38c39d6 96 if (likely(ret == NET_XMIT_SUCCESS)) {
f38c39d6 97 sch->q.qlen++;
378a2f09 98 } else if (net_xmit_drop_count(ret)) {
f38c39d6
PM
99 q->stats.pdrop++;
100 sch->qstats.drops++;
101 }
102 return ret;
6b31b28a
TG
103
104congestion_drop:
9e178ff2 105 qdisc_drop(skb, sch);
1da177e4
LT
106 return NET_XMIT_CN;
107}
108
cc7ec456 109static struct sk_buff *red_dequeue(struct Qdisc *sch)
1da177e4
LT
110{
111 struct sk_buff *skb;
112 struct red_sched_data *q = qdisc_priv(sch);
f38c39d6 113 struct Qdisc *child = q->qdisc;
1da177e4 114
f38c39d6 115 skb = child->dequeue(child);
9190b3b3
ED
116 if (skb) {
117 qdisc_bstats_update(sch, skb);
f38c39d6 118 sch->q.qlen--;
9190b3b3
ED
119 } else {
120 if (!red_is_idling(&q->parms))
121 red_start_of_idle_period(&q->parms);
122 }
9e178ff2 123 return skb;
1da177e4
LT
124}
125
cc7ec456 126static struct sk_buff *red_peek(struct Qdisc *sch)
8e3af978
JP
127{
128 struct red_sched_data *q = qdisc_priv(sch);
129 struct Qdisc *child = q->qdisc;
130
131 return child->ops->peek(child);
132}
133
cc7ec456 134static unsigned int red_drop(struct Qdisc *sch)
1da177e4 135{
1da177e4 136 struct red_sched_data *q = qdisc_priv(sch);
f38c39d6
PM
137 struct Qdisc *child = q->qdisc;
138 unsigned int len;
1da177e4 139
f38c39d6 140 if (child->ops->drop && (len = child->ops->drop(child)) > 0) {
6b31b28a 141 q->stats.other++;
f38c39d6
PM
142 sch->qstats.drops++;
143 sch->q.qlen--;
1da177e4
LT
144 return len;
145 }
6b31b28a 146
6a1b63d4
TG
147 if (!red_is_idling(&q->parms))
148 red_start_of_idle_period(&q->parms);
149
1da177e4
LT
150 return 0;
151}
152
cc7ec456 153static void red_reset(struct Qdisc *sch)
1da177e4
LT
154{
155 struct red_sched_data *q = qdisc_priv(sch);
156
f38c39d6
PM
157 qdisc_reset(q->qdisc);
158 sch->q.qlen = 0;
6b31b28a 159 red_restart(&q->parms);
1da177e4
LT
160}
161
f38c39d6
PM
162static void red_destroy(struct Qdisc *sch)
163{
164 struct red_sched_data *q = qdisc_priv(sch);
8af2a218
ED
165
166 del_timer_sync(&q->adapt_timer);
f38c39d6
PM
167 qdisc_destroy(q->qdisc);
168}
169
27a3421e
PM
170static const struct nla_policy red_policy[TCA_RED_MAX + 1] = {
171 [TCA_RED_PARMS] = { .len = sizeof(struct tc_red_qopt) },
172 [TCA_RED_STAB] = { .len = RED_STAB_SIZE },
a73ed26b 173 [TCA_RED_MAX_P] = { .type = NLA_U32 },
27a3421e
PM
174};
175
1e90474c 176static int red_change(struct Qdisc *sch, struct nlattr *opt)
1da177e4
LT
177{
178 struct red_sched_data *q = qdisc_priv(sch);
1e90474c 179 struct nlattr *tb[TCA_RED_MAX + 1];
1da177e4 180 struct tc_red_qopt *ctl;
f38c39d6 181 struct Qdisc *child = NULL;
cee63723 182 int err;
a73ed26b 183 u32 max_P;
1da177e4 184
cee63723 185 if (opt == NULL)
dba051f3
TG
186 return -EINVAL;
187
27a3421e 188 err = nla_parse_nested(tb, TCA_RED_MAX, opt, red_policy);
cee63723
PM
189 if (err < 0)
190 return err;
191
1e90474c 192 if (tb[TCA_RED_PARMS] == NULL ||
27a3421e 193 tb[TCA_RED_STAB] == NULL)
1da177e4
LT
194 return -EINVAL;
195
a73ed26b
ED
196 max_P = tb[TCA_RED_MAX_P] ? nla_get_u32(tb[TCA_RED_MAX_P]) : 0;
197
1e90474c 198 ctl = nla_data(tb[TCA_RED_PARMS]);
1da177e4 199
f38c39d6 200 if (ctl->limit > 0) {
fb0305ce
PM
201 child = fifo_create_dflt(sch, &bfifo_qdisc_ops, ctl->limit);
202 if (IS_ERR(child))
203 return PTR_ERR(child);
f38c39d6
PM
204 }
205
1da177e4
LT
206 sch_tree_lock(sch);
207 q->flags = ctl->flags;
1da177e4 208 q->limit = ctl->limit;
5e50da01
PM
209 if (child) {
210 qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
b94c8afc
PM
211 qdisc_destroy(q->qdisc);
212 q->qdisc = child;
5e50da01 213 }
1da177e4 214
6b31b28a 215 red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
a73ed26b
ED
216 ctl->Plog, ctl->Scell_log,
217 nla_data(tb[TCA_RED_STAB]),
218 max_P);
6b31b28a 219
8af2a218
ED
220 del_timer(&q->adapt_timer);
221 if (ctl->flags & TC_RED_ADAPTATIVE)
222 mod_timer(&q->adapt_timer, jiffies + HZ/2);
223
1ee5fa1e
ED
224 if (!q->qdisc->q.qlen)
225 red_start_of_idle_period(&q->parms);
dba051f3 226
1da177e4
LT
227 sch_tree_unlock(sch);
228 return 0;
229}
230
8af2a218
ED
231static inline void red_adaptative_timer(unsigned long arg)
232{
233 struct Qdisc *sch = (struct Qdisc *)arg;
234 struct red_sched_data *q = qdisc_priv(sch);
235 spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
236
237 spin_lock(root_lock);
238 red_adaptative_algo(&q->parms);
239 mod_timer(&q->adapt_timer, jiffies + HZ/2);
240 spin_unlock(root_lock);
241}
242
cc7ec456 243static int red_init(struct Qdisc *sch, struct nlattr *opt)
1da177e4 244{
f38c39d6
PM
245 struct red_sched_data *q = qdisc_priv(sch);
246
247 q->qdisc = &noop_qdisc;
8af2a218 248 setup_timer(&q->adapt_timer, red_adaptative_timer, (unsigned long)sch);
1da177e4
LT
249 return red_change(sch, opt);
250}
251
252static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
253{
254 struct red_sched_data *q = qdisc_priv(sch);
1e90474c 255 struct nlattr *opts = NULL;
6b31b28a
TG
256 struct tc_red_qopt opt = {
257 .limit = q->limit,
258 .flags = q->flags,
259 .qth_min = q->parms.qth_min >> q->parms.Wlog,
260 .qth_max = q->parms.qth_max >> q->parms.Wlog,
261 .Wlog = q->parms.Wlog,
262 .Plog = q->parms.Plog,
263 .Scell_log = q->parms.Scell_log,
264 };
1da177e4 265
0dfb33a0 266 sch->qstats.backlog = q->qdisc->qstats.backlog;
1e90474c
PM
267 opts = nla_nest_start(skb, TCA_OPTIONS);
268 if (opts == NULL)
269 goto nla_put_failure;
270 NLA_PUT(skb, TCA_RED_PARMS, sizeof(opt), &opt);
8af2a218 271 NLA_PUT_U32(skb, TCA_RED_MAX_P, q->parms.max_P);
1e90474c 272 return nla_nest_end(skb, opts);
1da177e4 273
1e90474c 274nla_put_failure:
bc3ed28c
TG
275 nla_nest_cancel(skb, opts);
276 return -EMSGSIZE;
1da177e4
LT
277}
278
279static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
280{
281 struct red_sched_data *q = qdisc_priv(sch);
6b31b28a
TG
282 struct tc_red_xstats st = {
283 .early = q->stats.prob_drop + q->stats.forced_drop,
284 .pdrop = q->stats.pdrop,
285 .other = q->stats.other,
286 .marked = q->stats.prob_mark + q->stats.forced_mark,
287 };
288
289 return gnet_stats_copy_app(d, &st, sizeof(st));
1da177e4
LT
290}
291
f38c39d6
PM
292static int red_dump_class(struct Qdisc *sch, unsigned long cl,
293 struct sk_buff *skb, struct tcmsg *tcm)
294{
295 struct red_sched_data *q = qdisc_priv(sch);
296
f38c39d6
PM
297 tcm->tcm_handle |= TC_H_MIN(1);
298 tcm->tcm_info = q->qdisc->handle;
299 return 0;
300}
301
302static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
303 struct Qdisc **old)
304{
305 struct red_sched_data *q = qdisc_priv(sch);
306
307 if (new == NULL)
308 new = &noop_qdisc;
309
310 sch_tree_lock(sch);
b94c8afc
PM
311 *old = q->qdisc;
312 q->qdisc = new;
5e50da01 313 qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
f38c39d6 314 qdisc_reset(*old);
f38c39d6
PM
315 sch_tree_unlock(sch);
316 return 0;
317}
318
319static struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg)
320{
321 struct red_sched_data *q = qdisc_priv(sch);
322 return q->qdisc;
323}
324
325static unsigned long red_get(struct Qdisc *sch, u32 classid)
326{
327 return 1;
328}
329
330static void red_put(struct Qdisc *sch, unsigned long arg)
331{
f38c39d6
PM
332}
333
f38c39d6
PM
334static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker)
335{
336 if (!walker->stop) {
337 if (walker->count >= walker->skip)
338 if (walker->fn(sch, 1, walker) < 0) {
339 walker->stop = 1;
340 return;
341 }
342 walker->count++;
343 }
344}
345
20fea08b 346static const struct Qdisc_class_ops red_class_ops = {
f38c39d6
PM
347 .graft = red_graft,
348 .leaf = red_leaf,
349 .get = red_get,
350 .put = red_put,
f38c39d6 351 .walk = red_walk,
f38c39d6
PM
352 .dump = red_dump_class,
353};
354
20fea08b 355static struct Qdisc_ops red_qdisc_ops __read_mostly = {
1da177e4
LT
356 .id = "red",
357 .priv_size = sizeof(struct red_sched_data),
f38c39d6 358 .cl_ops = &red_class_ops,
1da177e4
LT
359 .enqueue = red_enqueue,
360 .dequeue = red_dequeue,
8e3af978 361 .peek = red_peek,
1da177e4
LT
362 .drop = red_drop,
363 .init = red_init,
364 .reset = red_reset,
f38c39d6 365 .destroy = red_destroy,
1da177e4
LT
366 .change = red_change,
367 .dump = red_dump,
368 .dump_stats = red_dump_stats,
369 .owner = THIS_MODULE,
370};
371
372static int __init red_module_init(void)
373{
374 return register_qdisc(&red_qdisc_ops);
375}
dba051f3
TG
376
377static void __exit red_module_exit(void)
1da177e4
LT
378{
379 unregister_qdisc(&red_qdisc_ops);
380}
dba051f3 381
1da177e4
LT
382module_init(red_module_init)
383module_exit(red_module_exit)
dba051f3 384
1da177e4 385MODULE_LICENSE("GPL");
This page took 1.015154 seconds and 5 git commands to generate.