aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/sched/sch_red.c179
1 files changed, 163 insertions, 16 deletions
diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c
index dccfa44c2d71..2be563cba72b 100644
--- a/net/sched/sch_red.c
+++ b/net/sched/sch_red.c
@@ -44,6 +44,7 @@ struct red_sched_data
44 unsigned char flags; 44 unsigned char flags;
45 struct red_parms parms; 45 struct red_parms parms;
46 struct red_stats stats; 46 struct red_stats stats;
47 struct Qdisc *qdisc;
47}; 48};
48 49
49static inline int red_use_ecn(struct red_sched_data *q) 50static inline int red_use_ecn(struct red_sched_data *q)
@@ -59,8 +60,10 @@ static inline int red_use_harddrop(struct red_sched_data *q)
59static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch) 60static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch)
60{ 61{
61 struct red_sched_data *q = qdisc_priv(sch); 62 struct red_sched_data *q = qdisc_priv(sch);
63 struct Qdisc *child = q->qdisc;
64 int ret;
62 65
63 q->parms.qavg = red_calc_qavg(&q->parms, sch->qstats.backlog); 66 q->parms.qavg = red_calc_qavg(&q->parms, child->qstats.backlog);
64 67
65 if (red_is_idling(&q->parms)) 68 if (red_is_idling(&q->parms))
66 red_end_of_idle_period(&q->parms); 69 red_end_of_idle_period(&q->parms);
@@ -91,11 +94,16 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch)
91 break; 94 break;
92 } 95 }
93 96
94 if (sch->qstats.backlog + skb->len <= q->limit) 97 ret = child->enqueue(skb, child);
95 return qdisc_enqueue_tail(skb, sch); 98 if (likely(ret == NET_XMIT_SUCCESS)) {
96 99 sch->bstats.bytes += skb->len;
97 q->stats.pdrop++; 100 sch->bstats.packets++;
98 return qdisc_drop(skb, sch); 101 sch->q.qlen++;
102 } else {
103 q->stats.pdrop++;
104 sch->qstats.drops++;
105 }
106 return ret;
99 107
100congestion_drop: 108congestion_drop:
101 qdisc_drop(skb, sch); 109 qdisc_drop(skb, sch);
@@ -105,21 +113,30 @@ congestion_drop:
105static int red_requeue(struct sk_buff *skb, struct Qdisc* sch) 113static int red_requeue(struct sk_buff *skb, struct Qdisc* sch)
106{ 114{
107 struct red_sched_data *q = qdisc_priv(sch); 115 struct red_sched_data *q = qdisc_priv(sch);
116 struct Qdisc *child = q->qdisc;
117 int ret;
108 118
109 if (red_is_idling(&q->parms)) 119 if (red_is_idling(&q->parms))
110 red_end_of_idle_period(&q->parms); 120 red_end_of_idle_period(&q->parms);
111 121
112 return qdisc_requeue(skb, sch); 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;
113} 128}
114 129
115static struct sk_buff * red_dequeue(struct Qdisc* sch) 130static struct sk_buff * red_dequeue(struct Qdisc* sch)
116{ 131{
117 struct sk_buff *skb; 132 struct sk_buff *skb;
118 struct red_sched_data *q = qdisc_priv(sch); 133 struct red_sched_data *q = qdisc_priv(sch);
134 struct Qdisc *child = q->qdisc;
119 135
120 skb = qdisc_dequeue_head(sch); 136 skb = child->dequeue(child);
121 137 if (skb)
122 if (skb == NULL && !red_is_idling(&q->parms)) 138 sch->q.qlen--;
139 else if (!red_is_idling(&q->parms))
123 red_start_of_idle_period(&q->parms); 140 red_start_of_idle_period(&q->parms);
124 141
125 return skb; 142 return skb;
@@ -127,14 +144,14 @@ static struct sk_buff * red_dequeue(struct Qdisc* sch)
127 144
128static unsigned int red_drop(struct Qdisc* sch) 145static unsigned int red_drop(struct Qdisc* sch)
129{ 146{
130 struct sk_buff *skb;
131 struct red_sched_data *q = qdisc_priv(sch); 147 struct red_sched_data *q = qdisc_priv(sch);
148 struct Qdisc *child = q->qdisc;
149 unsigned int len;
132 150
133 skb = qdisc_dequeue_tail(sch); 151 if (child->ops->drop && (len = child->ops->drop(child)) > 0) {
134 if (skb) {
135 unsigned int len = skb->len;
136 q->stats.other++; 152 q->stats.other++;
137 qdisc_drop(skb, sch); 153 sch->qstats.drops++;
154 sch->q.qlen--;
138 return len; 155 return len;
139 } 156 }
140 157
@@ -148,15 +165,48 @@ static void red_reset(struct Qdisc* sch)
148{ 165{
149 struct red_sched_data *q = qdisc_priv(sch); 166 struct red_sched_data *q = qdisc_priv(sch);
150 167
151 qdisc_reset_queue(sch); 168 qdisc_reset(q->qdisc);
169 sch->q.qlen = 0;
152 red_restart(&q->parms); 170 red_restart(&q->parms);
153} 171}
154 172
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
155static int red_change(struct Qdisc *sch, struct rtattr *opt) 204static int red_change(struct Qdisc *sch, struct rtattr *opt)
156{ 205{
157 struct red_sched_data *q = qdisc_priv(sch); 206 struct red_sched_data *q = qdisc_priv(sch);
158 struct rtattr *tb[TCA_RED_MAX]; 207 struct rtattr *tb[TCA_RED_MAX];
159 struct tc_red_qopt *ctl; 208 struct tc_red_qopt *ctl;
209 struct Qdisc *child = NULL;
160 210
161 if (opt == NULL || rtattr_parse_nested(tb, TCA_RED_MAX, opt)) 211 if (opt == NULL || rtattr_parse_nested(tb, TCA_RED_MAX, opt))
162 return -EINVAL; 212 return -EINVAL;
@@ -169,9 +219,17 @@ static int red_change(struct Qdisc *sch, struct rtattr *opt)
169 219
170 ctl = RTA_DATA(tb[TCA_RED_PARMS-1]); 220 ctl = RTA_DATA(tb[TCA_RED_PARMS-1]);
171 221
222 if (ctl->limit > 0) {
223 child = red_create_dflt(sch->dev, ctl->limit);
224 if (child == NULL)
225 return -ENOMEM;
226 }
227
172 sch_tree_lock(sch); 228 sch_tree_lock(sch);
173 q->flags = ctl->flags; 229 q->flags = ctl->flags;
174 q->limit = ctl->limit; 230 q->limit = ctl->limit;
231 if (child)
232 qdisc_destroy(xchg(&q->qdisc, child));
175 233
176 red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog, 234 red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
177 ctl->Plog, ctl->Scell_log, 235 ctl->Plog, ctl->Scell_log,
@@ -186,6 +244,9 @@ static int red_change(struct Qdisc *sch, struct rtattr *opt)
186 244
187static int red_init(struct Qdisc* sch, struct rtattr *opt) 245static int red_init(struct Qdisc* sch, struct rtattr *opt)
188{ 246{
247 struct red_sched_data *q = qdisc_priv(sch);
248
249 q->qdisc = &noop_qdisc;
189 return red_change(sch, opt); 250 return red_change(sch, opt);
190} 251}
191 252
@@ -224,15 +285,101 @@ static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
224 return gnet_stats_copy_app(d, &st, sizeof(st)); 285 return gnet_stats_copy_app(d, &st, sizeof(st));
225} 286}
226 287
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
227static struct Qdisc_ops red_qdisc_ops = { 372static struct Qdisc_ops red_qdisc_ops = {
228 .id = "red", 373 .id = "red",
229 .priv_size = sizeof(struct red_sched_data), 374 .priv_size = sizeof(struct red_sched_data),
375 .cl_ops = &red_class_ops,
230 .enqueue = red_enqueue, 376 .enqueue = red_enqueue,
231 .dequeue = red_dequeue, 377 .dequeue = red_dequeue,
232 .requeue = red_requeue, 378 .requeue = red_requeue,
233 .drop = red_drop, 379 .drop = red_drop,
234 .init = red_init, 380 .init = red_init,
235 .reset = red_reset, 381 .reset = red_reset,
382 .destroy = red_destroy,
236 .change = red_change, 383 .change = red_change,
237 .dump = red_dump, 384 .dump = red_dump,
238 .dump_stats = red_dump_stats, 385 .dump_stats = red_dump_stats,