1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/module.h>
16#include <linux/types.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/skbuff.h>
21#include <net/netlink.h>
22#include <net/pkt_sched.h>
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100struct tbf_sched_data
101{
102
103 u32 limit;
104 u32 buffer;
105 u32 mtu;
106 u32 max_size;
107 struct qdisc_rate_table *R_tab;
108 struct qdisc_rate_table *P_tab;
109
110
111 long tokens;
112 long ptokens;
113 psched_time_t t_c;
114 struct Qdisc *qdisc;
115 struct qdisc_watchdog watchdog;
116};
117
118#define L2T(q,L) qdisc_l2t((q)->R_tab,L)
119#define L2T_P(q,L) qdisc_l2t((q)->P_tab,L)
120
121static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
122{
123 struct tbf_sched_data *q = qdisc_priv(sch);
124 int ret;
125
126 if (qdisc_pkt_len(skb) > q->max_size)
127 return qdisc_reshape_fail(skb, sch);
128
129 ret = qdisc_enqueue(skb, q->qdisc);
130 if (ret != NET_XMIT_SUCCESS) {
131 if (net_xmit_drop_count(ret))
132 sch->qstats.drops++;
133 return ret;
134 }
135
136 sch->q.qlen++;
137 sch->bstats.bytes += qdisc_pkt_len(skb);
138 sch->bstats.packets++;
139 return NET_XMIT_SUCCESS;
140}
141
142static unsigned int tbf_drop(struct Qdisc* sch)
143{
144 struct tbf_sched_data *q = qdisc_priv(sch);
145 unsigned int len = 0;
146
147 if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
148 sch->q.qlen--;
149 sch->qstats.drops++;
150 }
151 return len;
152}
153
154static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
155{
156 struct tbf_sched_data *q = qdisc_priv(sch);
157 struct sk_buff *skb;
158
159 skb = q->qdisc->ops->peek(q->qdisc);
160
161 if (skb) {
162 psched_time_t now;
163 long toks;
164 long ptoks = 0;
165 unsigned int len = qdisc_pkt_len(skb);
166
167 now = psched_get_time();
168 toks = psched_tdiff_bounded(now, q->t_c, q->buffer);
169
170 if (q->P_tab) {
171 ptoks = toks + q->ptokens;
172 if (ptoks > (long)q->mtu)
173 ptoks = q->mtu;
174 ptoks -= L2T_P(q, len);
175 }
176 toks += q->tokens;
177 if (toks > (long)q->buffer)
178 toks = q->buffer;
179 toks -= L2T(q, len);
180
181 if ((toks|ptoks) >= 0) {
182 skb = qdisc_dequeue_peeked(q->qdisc);
183 if (unlikely(!skb))
184 return NULL;
185
186 q->t_c = now;
187 q->tokens = toks;
188 q->ptokens = ptoks;
189 sch->q.qlen--;
190 sch->flags &= ~TCQ_F_THROTTLED;
191 return skb;
192 }
193
194 qdisc_watchdog_schedule(&q->watchdog,
195 now + max_t(long, -toks, -ptoks));
196
197
198
199
200
201
202
203
204
205
206
207
208 sch->qstats.overlimits++;
209 }
210 return NULL;
211}
212
213static void tbf_reset(struct Qdisc* sch)
214{
215 struct tbf_sched_data *q = qdisc_priv(sch);
216
217 qdisc_reset(q->qdisc);
218 sch->q.qlen = 0;
219 q->t_c = psched_get_time();
220 q->tokens = q->buffer;
221 q->ptokens = q->mtu;
222 qdisc_watchdog_cancel(&q->watchdog);
223}
224
225static const struct nla_policy tbf_policy[TCA_TBF_MAX + 1] = {
226 [TCA_TBF_PARMS] = { .len = sizeof(struct tc_tbf_qopt) },
227 [TCA_TBF_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
228 [TCA_TBF_PTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
229};
230
231static int tbf_change(struct Qdisc* sch, struct nlattr *opt)
232{
233 int err;
234 struct tbf_sched_data *q = qdisc_priv(sch);
235 struct nlattr *tb[TCA_TBF_PTAB + 1];
236 struct tc_tbf_qopt *qopt;
237 struct qdisc_rate_table *rtab = NULL;
238 struct qdisc_rate_table *ptab = NULL;
239 struct Qdisc *child = NULL;
240 int max_size,n;
241
242 err = nla_parse_nested(tb, TCA_TBF_PTAB, opt, tbf_policy);
243 if (err < 0)
244 return err;
245
246 err = -EINVAL;
247 if (tb[TCA_TBF_PARMS] == NULL)
248 goto done;
249
250 qopt = nla_data(tb[TCA_TBF_PARMS]);
251 rtab = qdisc_get_rtab(&qopt->rate, tb[TCA_TBF_RTAB]);
252 if (rtab == NULL)
253 goto done;
254
255 if (qopt->peakrate.rate) {
256 if (qopt->peakrate.rate > qopt->rate.rate)
257 ptab = qdisc_get_rtab(&qopt->peakrate, tb[TCA_TBF_PTAB]);
258 if (ptab == NULL)
259 goto done;
260 }
261
262 for (n = 0; n < 256; n++)
263 if (rtab->data[n] > qopt->buffer) break;
264 max_size = (n << qopt->rate.cell_log)-1;
265 if (ptab) {
266 int size;
267
268 for (n = 0; n < 256; n++)
269 if (ptab->data[n] > qopt->mtu) break;
270 size = (n << qopt->peakrate.cell_log)-1;
271 if (size < max_size) max_size = size;
272 }
273 if (max_size < 0)
274 goto done;
275
276 if (q->qdisc != &noop_qdisc) {
277 err = fifo_set_limit(q->qdisc, qopt->limit);
278 if (err)
279 goto done;
280 } else if (qopt->limit > 0) {
281 child = fifo_create_dflt(sch, &bfifo_qdisc_ops, qopt->limit);
282 if (IS_ERR(child)) {
283 err = PTR_ERR(child);
284 goto done;
285 }
286 }
287
288 sch_tree_lock(sch);
289 if (child) {
290 qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
291 qdisc_destroy(q->qdisc);
292 q->qdisc = child;
293 }
294 q->limit = qopt->limit;
295 q->mtu = qopt->mtu;
296 q->max_size = max_size;
297 q->buffer = qopt->buffer;
298 q->tokens = q->buffer;
299 q->ptokens = q->mtu;
300
301 swap(q->R_tab, rtab);
302 swap(q->P_tab, ptab);
303
304 sch_tree_unlock(sch);
305 err = 0;
306done:
307 if (rtab)
308 qdisc_put_rtab(rtab);
309 if (ptab)
310 qdisc_put_rtab(ptab);
311 return err;
312}
313
314static int tbf_init(struct Qdisc* sch, struct nlattr *opt)
315{
316 struct tbf_sched_data *q = qdisc_priv(sch);
317
318 if (opt == NULL)
319 return -EINVAL;
320
321 q->t_c = psched_get_time();
322 qdisc_watchdog_init(&q->watchdog, sch);
323 q->qdisc = &noop_qdisc;
324
325 return tbf_change(sch, opt);
326}
327
328static void tbf_destroy(struct Qdisc *sch)
329{
330 struct tbf_sched_data *q = qdisc_priv(sch);
331
332 qdisc_watchdog_cancel(&q->watchdog);
333
334 if (q->P_tab)
335 qdisc_put_rtab(q->P_tab);
336 if (q->R_tab)
337 qdisc_put_rtab(q->R_tab);
338
339 qdisc_destroy(q->qdisc);
340}
341
342static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
343{
344 struct tbf_sched_data *q = qdisc_priv(sch);
345 struct nlattr *nest;
346 struct tc_tbf_qopt opt;
347
348 nest = nla_nest_start(skb, TCA_OPTIONS);
349 if (nest == NULL)
350 goto nla_put_failure;
351
352 opt.limit = q->limit;
353 opt.rate = q->R_tab->rate;
354 if (q->P_tab)
355 opt.peakrate = q->P_tab->rate;
356 else
357 memset(&opt.peakrate, 0, sizeof(opt.peakrate));
358 opt.mtu = q->mtu;
359 opt.buffer = q->buffer;
360 NLA_PUT(skb, TCA_TBF_PARMS, sizeof(opt), &opt);
361
362 nla_nest_end(skb, nest);
363 return skb->len;
364
365nla_put_failure:
366 nla_nest_cancel(skb, nest);
367 return -1;
368}
369
370static int tbf_dump_class(struct Qdisc *sch, unsigned long cl,
371 struct sk_buff *skb, struct tcmsg *tcm)
372{
373 struct tbf_sched_data *q = qdisc_priv(sch);
374
375 tcm->tcm_handle |= TC_H_MIN(1);
376 tcm->tcm_info = q->qdisc->handle;
377
378 return 0;
379}
380
381static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
382 struct Qdisc **old)
383{
384 struct tbf_sched_data *q = qdisc_priv(sch);
385
386 if (new == NULL)
387 new = &noop_qdisc;
388
389 sch_tree_lock(sch);
390 *old = q->qdisc;
391 q->qdisc = new;
392 qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
393 qdisc_reset(*old);
394 sch_tree_unlock(sch);
395
396 return 0;
397}
398
399static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg)
400{
401 struct tbf_sched_data *q = qdisc_priv(sch);
402 return q->qdisc;
403}
404
405static unsigned long tbf_get(struct Qdisc *sch, u32 classid)
406{
407 return 1;
408}
409
410static void tbf_put(struct Qdisc *sch, unsigned long arg)
411{
412}
413
414static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker)
415{
416 if (!walker->stop) {
417 if (walker->count >= walker->skip)
418 if (walker->fn(sch, 1, walker) < 0) {
419 walker->stop = 1;
420 return;
421 }
422 walker->count++;
423 }
424}
425
426static const struct Qdisc_class_ops tbf_class_ops =
427{
428 .graft = tbf_graft,
429 .leaf = tbf_leaf,
430 .get = tbf_get,
431 .put = tbf_put,
432 .walk = tbf_walk,
433 .dump = tbf_dump_class,
434};
435
436static struct Qdisc_ops tbf_qdisc_ops __read_mostly = {
437 .next = NULL,
438 .cl_ops = &tbf_class_ops,
439 .id = "tbf",
440 .priv_size = sizeof(struct tbf_sched_data),
441 .enqueue = tbf_enqueue,
442 .dequeue = tbf_dequeue,
443 .peek = qdisc_peek_dequeued,
444 .drop = tbf_drop,
445 .init = tbf_init,
446 .reset = tbf_reset,
447 .destroy = tbf_destroy,
448 .change = tbf_change,
449 .dump = tbf_dump,
450 .owner = THIS_MODULE,
451};
452
453static int __init tbf_module_init(void)
454{
455 return register_qdisc(&tbf_qdisc_ops);
456}
457
458static void __exit tbf_module_exit(void)
459{
460 unregister_qdisc(&tbf_qdisc_ops);
461}
462module_init(tbf_module_init)
463module_exit(tbf_module_exit)
464MODULE_LICENSE("GPL");
465