blob: 76e8df8447d90671c714b9038ea35436cc6dac79 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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:
12 * J Hadi Salim <hadi@nortel.com> 980914: computation fixes
13 * Alexey Makarenko <makar@phoenix.kharkov.ua> 990814: qave on idle link was calculated incorrectly.
14 * J Hadi Salim <hadi@nortelnetworks.com> 980816: ECN support
15 */
16
17#include <linux/config.h>
18#include <linux/module.h>
19#include <asm/uaccess.h>
20#include <asm/system.h>
21#include <linux/bitops.h>
22#include <linux/types.h>
23#include <linux/kernel.h>
24#include <linux/sched.h>
25#include <linux/string.h>
26#include <linux/mm.h>
27#include <linux/socket.h>
28#include <linux/sockios.h>
29#include <linux/in.h>
30#include <linux/errno.h>
31#include <linux/interrupt.h>
32#include <linux/if_ether.h>
33#include <linux/inet.h>
34#include <linux/netdevice.h>
35#include <linux/etherdevice.h>
36#include <linux/notifier.h>
37#include <net/ip.h>
38#include <net/route.h>
39#include <linux/skbuff.h>
40#include <net/sock.h>
41#include <net/pkt_sched.h>
42#include <net/inet_ecn.h>
43#include <net/dsfield.h>
Thomas Graf6b31b282005-11-05 21:14:05 +010044#include <net/red.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46
Thomas Graf6b31b282005-11-05 21:14:05 +010047/* Parameters, settable by user:
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 -----------------------------
49
50 limit - bytes (must be > qth_max + burst)
51
52 Hard limit on queue length, should be chosen >qth_max
53 to allow packet bursts. This parameter does not
54 affect the algorithms behaviour and can be chosen
55 arbitrarily high (well, less than ram size)
56 Really, this limit will never be reached
57 if RED works correctly.
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 */
59
60struct red_sched_data
61{
Thomas Graf6b31b282005-11-05 21:14:05 +010062 u32 limit; /* HARD maximal queue length */
63 unsigned char flags;
64 struct red_parms parms;
65 struct red_stats stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066};
67
Thomas Graf6b31b282005-11-05 21:14:05 +010068static inline int red_use_ecn(struct red_sched_data *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -070069{
Thomas Graf6b31b282005-11-05 21:14:05 +010070 return q->flags & TC_RED_ECN;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071}
72
73static int
74red_enqueue(struct sk_buff *skb, struct Qdisc* sch)
75{
76 struct red_sched_data *q = qdisc_priv(sch);
77
Thomas Graf6b31b282005-11-05 21:14:05 +010078 q->parms.qavg = red_calc_qavg(&q->parms, sch->qstats.backlog);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Thomas Graf6b31b282005-11-05 21:14:05 +010080 if (red_is_idling(&q->parms))
81 red_end_of_idle_period(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
Thomas Graf6b31b282005-11-05 21:14:05 +010083 switch (red_action(&q->parms, q->parms.qavg)) {
84 case RED_DONT_MARK:
85 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Thomas Graf6b31b282005-11-05 21:14:05 +010087 case RED_PROB_MARK:
88 sch->qstats.overlimits++;
89 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
90 q->stats.prob_drop++;
91 goto congestion_drop;
92 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Thomas Graf6b31b282005-11-05 21:14:05 +010094 q->stats.prob_mark++;
95 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
Thomas Graf6b31b282005-11-05 21:14:05 +010097 case RED_HARD_MARK:
98 sch->qstats.overlimits++;
99 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
100 q->stats.forced_drop++;
101 goto congestion_drop;
102 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
Thomas Graf6b31b282005-11-05 21:14:05 +0100104 q->stats.forced_mark++;
105 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 }
107
Thomas Graf9e178ff2005-11-05 21:14:06 +0100108 if (sch->qstats.backlog + skb->len <= q->limit)
109 return qdisc_enqueue_tail(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Thomas Graf6b31b282005-11-05 21:14:05 +0100111 q->stats.pdrop++;
Thomas Graf9e178ff2005-11-05 21:14:06 +0100112 return qdisc_drop(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Thomas Graf6b31b282005-11-05 21:14:05 +0100114congestion_drop:
Thomas Graf9e178ff2005-11-05 21:14:06 +0100115 qdisc_drop(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 return NET_XMIT_CN;
117}
118
119static int
120red_requeue(struct sk_buff *skb, struct Qdisc* sch)
121{
122 struct red_sched_data *q = qdisc_priv(sch);
123
Thomas Graf6b31b282005-11-05 21:14:05 +0100124 if (red_is_idling(&q->parms))
125 red_end_of_idle_period(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Thomas Graf9e178ff2005-11-05 21:14:06 +0100127 return qdisc_requeue(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128}
129
130static struct sk_buff *
131red_dequeue(struct Qdisc* sch)
132{
133 struct sk_buff *skb;
134 struct red_sched_data *q = qdisc_priv(sch);
135
Thomas Graf9e178ff2005-11-05 21:14:06 +0100136 skb = qdisc_dequeue_head(sch);
Thomas Graf6b31b282005-11-05 21:14:05 +0100137
Thomas Graf6a1b63d2005-11-05 21:14:07 +0100138 if (skb == NULL && !red_is_idling(&q->parms))
Thomas Graf9e178ff2005-11-05 21:14:06 +0100139 red_start_of_idle_period(&q->parms);
140
141 return skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142}
143
144static unsigned int red_drop(struct Qdisc* sch)
145{
146 struct sk_buff *skb;
147 struct red_sched_data *q = qdisc_priv(sch);
148
Thomas Graf9e178ff2005-11-05 21:14:06 +0100149 skb = qdisc_dequeue_tail(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 if (skb) {
151 unsigned int len = skb->len;
Thomas Graf6b31b282005-11-05 21:14:05 +0100152 q->stats.other++;
Thomas Graf9e178ff2005-11-05 21:14:06 +0100153 qdisc_drop(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 return len;
155 }
Thomas Graf6b31b282005-11-05 21:14:05 +0100156
Thomas Graf6a1b63d2005-11-05 21:14:07 +0100157 if (!red_is_idling(&q->parms))
158 red_start_of_idle_period(&q->parms);
159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 return 0;
161}
162
163static void red_reset(struct Qdisc* sch)
164{
165 struct red_sched_data *q = qdisc_priv(sch);
166
Thomas Graf9e178ff2005-11-05 21:14:06 +0100167 qdisc_reset_queue(sch);
Thomas Graf6b31b282005-11-05 21:14:05 +0100168 red_restart(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169}
170
171static int red_change(struct Qdisc *sch, struct rtattr *opt)
172{
173 struct red_sched_data *q = qdisc_priv(sch);
174 struct rtattr *tb[TCA_RED_STAB];
175 struct tc_red_qopt *ctl;
176
177 if (opt == NULL ||
178 rtattr_parse_nested(tb, TCA_RED_STAB, opt) ||
179 tb[TCA_RED_PARMS-1] == 0 || tb[TCA_RED_STAB-1] == 0 ||
180 RTA_PAYLOAD(tb[TCA_RED_PARMS-1]) < sizeof(*ctl) ||
181 RTA_PAYLOAD(tb[TCA_RED_STAB-1]) < 256)
182 return -EINVAL;
183
184 ctl = RTA_DATA(tb[TCA_RED_PARMS-1]);
185
186 sch_tree_lock(sch);
187 q->flags = ctl->flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 q->limit = ctl->limit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Thomas Graf6b31b282005-11-05 21:14:05 +0100190 red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
191 ctl->Plog, ctl->Scell_log,
192 RTA_DATA(tb[TCA_RED_STAB-1]));
193
David S. Millerb03efcf2005-07-08 14:57:23 -0700194 if (skb_queue_empty(&sch->q))
Thomas Graf6b31b282005-11-05 21:14:05 +0100195 red_end_of_idle_period(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 sch_tree_unlock(sch);
197 return 0;
198}
199
200static int red_init(struct Qdisc* sch, struct rtattr *opt)
201{
202 return red_change(sch, opt);
203}
204
205static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
206{
207 struct red_sched_data *q = qdisc_priv(sch);
208 unsigned char *b = skb->tail;
209 struct rtattr *rta;
Thomas Graf6b31b282005-11-05 21:14:05 +0100210 struct tc_red_qopt opt = {
211 .limit = q->limit,
212 .flags = q->flags,
213 .qth_min = q->parms.qth_min >> q->parms.Wlog,
214 .qth_max = q->parms.qth_max >> q->parms.Wlog,
215 .Wlog = q->parms.Wlog,
216 .Plog = q->parms.Plog,
217 .Scell_log = q->parms.Scell_log,
218 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
220 rta = (struct rtattr*)b;
221 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 RTA_PUT(skb, TCA_RED_PARMS, sizeof(opt), &opt);
223 rta->rta_len = skb->tail - b;
224
225 return skb->len;
226
227rtattr_failure:
228 skb_trim(skb, b - skb->data);
229 return -1;
230}
231
232static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
233{
234 struct red_sched_data *q = qdisc_priv(sch);
Thomas Graf6b31b282005-11-05 21:14:05 +0100235 struct tc_red_xstats st = {
236 .early = q->stats.prob_drop + q->stats.forced_drop,
237 .pdrop = q->stats.pdrop,
238 .other = q->stats.other,
239 .marked = q->stats.prob_mark + q->stats.forced_mark,
240 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Thomas Graf6b31b282005-11-05 21:14:05 +0100242 return gnet_stats_copy_app(d, &st, sizeof(st));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243}
244
245static struct Qdisc_ops red_qdisc_ops = {
246 .next = NULL,
247 .cl_ops = NULL,
248 .id = "red",
249 .priv_size = sizeof(struct red_sched_data),
250 .enqueue = red_enqueue,
251 .dequeue = red_dequeue,
252 .requeue = red_requeue,
253 .drop = red_drop,
254 .init = red_init,
255 .reset = red_reset,
256 .change = red_change,
257 .dump = red_dump,
258 .dump_stats = red_dump_stats,
259 .owner = THIS_MODULE,
260};
261
262static int __init red_module_init(void)
263{
264 return register_qdisc(&red_qdisc_ops);
265}
266static void __exit red_module_exit(void)
267{
268 unregister_qdisc(&red_qdisc_ops);
269}
270module_init(red_module_init)
271module_exit(red_module_exit)
272MODULE_LICENSE("GPL");