blob: 0d89dee751a945bd33c9b3a3f2aee28eb111bfa7 [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:
Thomas Grafdba051f2005-11-05 21:14:08 +010012 * J Hadi Salim 980914: computation fixes
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 * Alexey Makarenko <makar@phoenix.kharkov.ua> 990814: qave on idle link was calculated incorrectly.
Thomas Grafdba051f2005-11-05 21:14:08 +010014 * J Hadi Salim 980816: ECN support
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 */
16
17#include <linux/config.h>
18#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/types.h>
20#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/netdevice.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <net/pkt_sched.h>
24#include <net/inet_ecn.h>
Thomas Graf6b31b282005-11-05 21:14:05 +010025#include <net/red.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27
Thomas Graf6b31b282005-11-05 21:14:05 +010028/* Parameters, settable by user:
Linus Torvalds1da177e2005-04-16 15:20:36 -070029 -----------------------------
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.
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 */
40
41struct red_sched_data
42{
Thomas Graf6b31b282005-11-05 21:14:05 +010043 u32 limit; /* HARD maximal queue length */
44 unsigned char flags;
45 struct red_parms parms;
46 struct red_stats stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047};
48
Thomas Graf6b31b282005-11-05 21:14:05 +010049static inline int red_use_ecn(struct red_sched_data *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
Thomas Graf6b31b282005-11-05 21:14:05 +010051 return q->flags & TC_RED_ECN;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052}
53
Thomas Grafdba051f2005-11-05 21:14:08 +010054static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -070055{
56 struct red_sched_data *q = qdisc_priv(sch);
57
Thomas Graf6b31b282005-11-05 21:14:05 +010058 q->parms.qavg = red_calc_qavg(&q->parms, sch->qstats.backlog);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Thomas Graf6b31b282005-11-05 21:14:05 +010060 if (red_is_idling(&q->parms))
61 red_end_of_idle_period(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
Thomas Graf6b31b282005-11-05 21:14:05 +010063 switch (red_action(&q->parms, q->parms.qavg)) {
64 case RED_DONT_MARK:
65 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Thomas Graf6b31b282005-11-05 21:14:05 +010067 case RED_PROB_MARK:
68 sch->qstats.overlimits++;
69 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
70 q->stats.prob_drop++;
71 goto congestion_drop;
72 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Thomas Graf6b31b282005-11-05 21:14:05 +010074 q->stats.prob_mark++;
75 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
Thomas Graf6b31b282005-11-05 21:14:05 +010077 case RED_HARD_MARK:
78 sch->qstats.overlimits++;
79 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
80 q->stats.forced_drop++;
81 goto congestion_drop;
82 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Thomas Graf6b31b282005-11-05 21:14:05 +010084 q->stats.forced_mark++;
85 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 }
87
Thomas Graf9e178ff2005-11-05 21:14:06 +010088 if (sch->qstats.backlog + skb->len <= q->limit)
89 return qdisc_enqueue_tail(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Thomas Graf6b31b282005-11-05 21:14:05 +010091 q->stats.pdrop++;
Thomas Graf9e178ff2005-11-05 21:14:06 +010092 return qdisc_drop(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Thomas Graf6b31b282005-11-05 21:14:05 +010094congestion_drop:
Thomas Graf9e178ff2005-11-05 21:14:06 +010095 qdisc_drop(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 return NET_XMIT_CN;
97}
98
Thomas Grafdba051f2005-11-05 21:14:08 +010099static int red_requeue(struct sk_buff *skb, struct Qdisc* sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
101 struct red_sched_data *q = qdisc_priv(sch);
102
Thomas Graf6b31b282005-11-05 21:14:05 +0100103 if (red_is_idling(&q->parms))
104 red_end_of_idle_period(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
Thomas Graf9e178ff2005-11-05 21:14:06 +0100106 return qdisc_requeue(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
Thomas Grafdba051f2005-11-05 21:14:08 +0100109static struct sk_buff * red_dequeue(struct Qdisc* sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
111 struct sk_buff *skb;
112 struct red_sched_data *q = qdisc_priv(sch);
113
Thomas Graf9e178ff2005-11-05 21:14:06 +0100114 skb = qdisc_dequeue_head(sch);
Thomas Graf6b31b282005-11-05 21:14:05 +0100115
Thomas Graf6a1b63d2005-11-05 21:14:07 +0100116 if (skb == NULL && !red_is_idling(&q->parms))
Thomas Graf9e178ff2005-11-05 21:14:06 +0100117 red_start_of_idle_period(&q->parms);
118
119 return skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120}
121
122static unsigned int red_drop(struct Qdisc* sch)
123{
124 struct sk_buff *skb;
125 struct red_sched_data *q = qdisc_priv(sch);
126
Thomas Graf9e178ff2005-11-05 21:14:06 +0100127 skb = qdisc_dequeue_tail(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 if (skb) {
129 unsigned int len = skb->len;
Thomas Graf6b31b282005-11-05 21:14:05 +0100130 q->stats.other++;
Thomas Graf9e178ff2005-11-05 21:14:06 +0100131 qdisc_drop(skb, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 return len;
133 }
Thomas Graf6b31b282005-11-05 21:14:05 +0100134
Thomas Graf6a1b63d2005-11-05 21:14:07 +0100135 if (!red_is_idling(&q->parms))
136 red_start_of_idle_period(&q->parms);
137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 return 0;
139}
140
141static void red_reset(struct Qdisc* sch)
142{
143 struct red_sched_data *q = qdisc_priv(sch);
144
Thomas Graf9e178ff2005-11-05 21:14:06 +0100145 qdisc_reset_queue(sch);
Thomas Graf6b31b282005-11-05 21:14:05 +0100146 red_restart(&q->parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
149static int red_change(struct Qdisc *sch, struct rtattr *opt)
150{
151 struct red_sched_data *q = qdisc_priv(sch);
Thomas Grafdba051f2005-11-05 21:14:08 +0100152 struct rtattr *tb[TCA_RED_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 struct tc_red_qopt *ctl;
154
Thomas Grafdba051f2005-11-05 21:14:08 +0100155 if (opt == NULL || rtattr_parse_nested(tb, TCA_RED_MAX, opt))
156 return -EINVAL;
157
158 if (tb[TCA_RED_PARMS-1] == NULL ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 RTA_PAYLOAD(tb[TCA_RED_PARMS-1]) < sizeof(*ctl) ||
Thomas Grafdba051f2005-11-05 21:14:08 +0100160 tb[TCA_RED_STAB-1] == NULL ||
161 RTA_PAYLOAD(tb[TCA_RED_STAB-1]) < RED_STAB_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 return -EINVAL;
163
164 ctl = RTA_DATA(tb[TCA_RED_PARMS-1]);
165
166 sch_tree_lock(sch);
167 q->flags = ctl->flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 q->limit = ctl->limit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
Thomas Graf6b31b282005-11-05 21:14:05 +0100170 red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
171 ctl->Plog, ctl->Scell_log,
172 RTA_DATA(tb[TCA_RED_STAB-1]));
173
David S. Millerb03efcf2005-07-08 14:57:23 -0700174 if (skb_queue_empty(&sch->q))
Thomas Graf6b31b282005-11-05 21:14:05 +0100175 red_end_of_idle_period(&q->parms);
Thomas Grafdba051f2005-11-05 21:14:08 +0100176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 sch_tree_unlock(sch);
178 return 0;
179}
180
181static int red_init(struct Qdisc* sch, struct rtattr *opt)
182{
183 return red_change(sch, opt);
184}
185
186static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
187{
188 struct red_sched_data *q = qdisc_priv(sch);
Thomas Grafdba051f2005-11-05 21:14:08 +0100189 struct rtattr *opts = NULL;
Thomas Graf6b31b282005-11-05 21:14:05 +0100190 struct tc_red_qopt opt = {
191 .limit = q->limit,
192 .flags = q->flags,
193 .qth_min = q->parms.qth_min >> q->parms.Wlog,
194 .qth_max = q->parms.qth_max >> q->parms.Wlog,
195 .Wlog = q->parms.Wlog,
196 .Plog = q->parms.Plog,
197 .Scell_log = q->parms.Scell_log,
198 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Thomas Grafdba051f2005-11-05 21:14:08 +0100200 opts = RTA_NEST(skb, TCA_OPTIONS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 RTA_PUT(skb, TCA_RED_PARMS, sizeof(opt), &opt);
Thomas Grafdba051f2005-11-05 21:14:08 +0100202 return RTA_NEST_END(skb, opts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
204rtattr_failure:
Thomas Grafdba051f2005-11-05 21:14:08 +0100205 return RTA_NEST_CANCEL(skb, opts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206}
207
208static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
209{
210 struct red_sched_data *q = qdisc_priv(sch);
Thomas Graf6b31b282005-11-05 21:14:05 +0100211 struct tc_red_xstats st = {
212 .early = q->stats.prob_drop + q->stats.forced_drop,
213 .pdrop = q->stats.pdrop,
214 .other = q->stats.other,
215 .marked = q->stats.prob_mark + q->stats.forced_mark,
216 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
Thomas Graf6b31b282005-11-05 21:14:05 +0100218 return gnet_stats_copy_app(d, &st, sizeof(st));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219}
220
221static struct Qdisc_ops red_qdisc_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 .id = "red",
223 .priv_size = sizeof(struct red_sched_data),
224 .enqueue = red_enqueue,
225 .dequeue = red_dequeue,
226 .requeue = red_requeue,
227 .drop = red_drop,
228 .init = red_init,
229 .reset = red_reset,
230 .change = red_change,
231 .dump = red_dump,
232 .dump_stats = red_dump_stats,
233 .owner = THIS_MODULE,
234};
235
236static int __init red_module_init(void)
237{
238 return register_qdisc(&red_qdisc_ops);
239}
Thomas Grafdba051f2005-11-05 21:14:08 +0100240
241static void __exit red_module_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 unregister_qdisc(&red_qdisc_ops);
244}
Thomas Grafdba051f2005-11-05 21:14:08 +0100245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246module_init(red_module_init)
247module_exit(red_module_exit)
Thomas Grafdba051f2005-11-05 21:14:08 +0100248
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249MODULE_LICENSE("GPL");