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