blob: 4799c4840c1a2e89fd22885a9e6212ae1682b819 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/sched/sch_api.c Packet scheduler API.
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 * Fixes:
12 *
13 * Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired.
14 * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
15 * Jamal Hadi Salim <hadi@nortelnetworks.com>: 990601: ingress support
16 */
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/module.h>
19#include <linux/types.h>
20#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/init.h>
25#include <linux/proc_fs.h>
26#include <linux/seq_file.h>
27#include <linux/kmod.h>
28#include <linux/list.h>
Patrick McHardy41794772007-03-16 01:19:15 -070029#include <linux/hrtimer.h>
Jarek Poplawski25bfcd52008-08-18 20:53:34 -070030#include <linux/lockdep.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090031#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020033#include <net/net_namespace.h>
Denis V. Lunevb8542722007-12-01 00:21:31 +110034#include <net/sock.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070035#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <net/pkt_sched.h>
37
Tom Goff7316ae82010-03-19 15:40:13 +000038static int qdisc_notify(struct net *net, struct sk_buff *oskb,
39 struct nlmsghdr *n, u32 clid,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 struct Qdisc *old, struct Qdisc *new);
Tom Goff7316ae82010-03-19 15:40:13 +000041static int tclass_notify(struct net *net, struct sk_buff *oskb,
42 struct nlmsghdr *n, struct Qdisc *q,
43 unsigned long cl, int event);
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45/*
46
47 Short review.
48 -------------
49
50 This file consists of two interrelated parts:
51
52 1. queueing disciplines manager frontend.
53 2. traffic classes manager frontend.
54
55 Generally, queueing discipline ("qdisc") is a black box,
56 which is able to enqueue packets and to dequeue them (when
57 device is ready to send something) in order and at times
58 determined by algorithm hidden in it.
59
60 qdisc's are divided to two categories:
61 - "queues", which have no internal structure visible from outside.
62 - "schedulers", which split all the packets to "traffic classes",
63 using "packet classifiers" (look at cls_api.c)
64
65 In turn, classes may have child qdiscs (as rule, queues)
66 attached to them etc. etc. etc.
67
68 The goal of the routines in this file is to translate
69 information supplied by user in the form of handles
70 to more intelligible for kernel form, to make some sanity
71 checks and part of work, which is common to all qdiscs
72 and to provide rtnetlink notifications.
73
74 All real intelligent work is done inside qdisc modules.
75
76
77
78 Every discipline has two major routines: enqueue and dequeue.
79
80 ---dequeue
81
82 dequeue usually returns a skb to send. It is allowed to return NULL,
83 but it does not mean that queue is empty, it just means that
84 discipline does not want to send anything this time.
85 Queue is really empty if q->q.qlen == 0.
86 For complicated disciplines with multiple queues q->q is not
87 real packet queue, but however q->q.qlen must be valid.
88
89 ---enqueue
90
91 enqueue returns 0, if packet was enqueued successfully.
92 If packet (this one or another one) was dropped, it returns
93 not zero error code.
94 NET_XMIT_DROP - this packet dropped
95 Expected action: do not backoff, but wait until queue will clear.
96 NET_XMIT_CN - probably this packet enqueued, but another one dropped.
97 Expected action: backoff or ignore
98 NET_XMIT_POLICED - dropped by police.
99 Expected action: backoff or error to real-time apps.
100
101 Auxiliary routines:
102
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700103 ---peek
104
105 like dequeue but without removing a packet from the queue
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 ---reset
108
109 returns qdisc to initial state: purge all buffers, clear all
110 timers, counters (except for statistics) etc.
111
112 ---init
113
114 initializes newly created qdisc.
115
116 ---destroy
117
118 destroys resources allocated by init and during lifetime of qdisc.
119
120 ---change
121
122 changes qdisc parameters.
123 */
124
125/* Protects list of registered TC modules. It is pure SMP lock. */
126static DEFINE_RWLOCK(qdisc_mod_lock);
127
128
129/************************************************
130 * Queueing disciplines manipulation. *
131 ************************************************/
132
133
134/* The list of all installed queueing disciplines. */
135
136static struct Qdisc_ops *qdisc_base;
137
138/* Register/uregister queueing discipline */
139
140int register_qdisc(struct Qdisc_ops *qops)
141{
142 struct Qdisc_ops *q, **qp;
143 int rc = -EEXIST;
144
145 write_lock(&qdisc_mod_lock);
146 for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
147 if (!strcmp(qops->id, q->id))
148 goto out;
149
150 if (qops->enqueue == NULL)
151 qops->enqueue = noop_qdisc_ops.enqueue;
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700152 if (qops->peek == NULL) {
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000153 if (qops->dequeue == NULL)
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700154 qops->peek = noop_qdisc_ops.peek;
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000155 else
156 goto out_einval;
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700157 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 if (qops->dequeue == NULL)
159 qops->dequeue = noop_qdisc_ops.dequeue;
160
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000161 if (qops->cl_ops) {
162 const struct Qdisc_class_ops *cops = qops->cl_ops;
163
Jarek Poplawski3e9e5a52010-08-10 22:31:20 +0000164 if (!(cops->get && cops->put && cops->walk && cops->leaf))
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000165 goto out_einval;
166
167 if (cops->tcf_chain && !(cops->bind_tcf && cops->unbind_tcf))
168 goto out_einval;
169 }
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 qops->next = NULL;
172 *qp = qops;
173 rc = 0;
174out:
175 write_unlock(&qdisc_mod_lock);
176 return rc;
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000177
178out_einval:
179 rc = -EINVAL;
180 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800182EXPORT_SYMBOL(register_qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
184int unregister_qdisc(struct Qdisc_ops *qops)
185{
186 struct Qdisc_ops *q, **qp;
187 int err = -ENOENT;
188
189 write_lock(&qdisc_mod_lock);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000190 for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 if (q == qops)
192 break;
193 if (q) {
194 *qp = q->next;
195 q->next = NULL;
196 err = 0;
197 }
198 write_unlock(&qdisc_mod_lock);
199 return err;
200}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800201EXPORT_SYMBOL(unregister_qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203/* We know handle. Find qdisc among all qdisc's attached to device
204 (root qdisc, all its children, children of children etc.)
205 */
206
Hannes Eder6113b742008-11-28 03:06:46 -0800207static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
David S. Miller8123b422008-08-08 23:23:39 -0700208{
209 struct Qdisc *q;
210
211 if (!(root->flags & TCQ_F_BUILTIN) &&
212 root->handle == handle)
213 return root;
214
215 list_for_each_entry(q, &root->list, list) {
216 if (q->handle == handle)
217 return q;
218 }
219 return NULL;
220}
221
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700222static void qdisc_list_add(struct Qdisc *q)
223{
Jarek Poplawskif6486d42008-11-25 13:56:06 -0800224 if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
Patrick McHardyaf356af2009-09-04 06:41:18 +0000225 list_add_tail(&q->list, &qdisc_dev(q)->qdisc->list);
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700226}
227
228void qdisc_list_del(struct Qdisc *q)
229{
Jarek Poplawskif6486d42008-11-25 13:56:06 -0800230 if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700231 list_del(&q->list);
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700232}
233EXPORT_SYMBOL(qdisc_list_del);
234
David S. Milleread81cc2008-07-17 00:50:32 -0700235struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
Patrick McHardy43effa12006-11-29 17:35:48 -0800236{
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700237 struct Qdisc *q;
238
Patrick McHardyaf356af2009-09-04 06:41:18 +0000239 q = qdisc_match_from_root(dev->qdisc, handle);
240 if (q)
241 goto out;
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700242
Eric Dumazet24824a02010-10-02 06:11:55 +0000243 if (dev_ingress_queue(dev))
244 q = qdisc_match_from_root(
245 dev_ingress_queue(dev)->qdisc_sleeping,
246 handle);
Jarek Poplawskif6486d42008-11-25 13:56:06 -0800247out:
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700248 return q;
Patrick McHardy43effa12006-11-29 17:35:48 -0800249}
250
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
252{
253 unsigned long cl;
254 struct Qdisc *leaf;
Eric Dumazet20fea082007-11-14 01:44:41 -0800255 const struct Qdisc_class_ops *cops = p->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257 if (cops == NULL)
258 return NULL;
259 cl = cops->get(p, classid);
260
261 if (cl == 0)
262 return NULL;
263 leaf = cops->leaf(p, cl);
264 cops->put(p, cl);
265 return leaf;
266}
267
268/* Find queueing discipline by name */
269
Patrick McHardy1e904742008-01-22 22:11:17 -0800270static struct Qdisc_ops *qdisc_lookup_ops(struct nlattr *kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
272 struct Qdisc_ops *q = NULL;
273
274 if (kind) {
275 read_lock(&qdisc_mod_lock);
276 for (q = qdisc_base; q; q = q->next) {
Patrick McHardy1e904742008-01-22 22:11:17 -0800277 if (nla_strcmp(kind, q->id) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 if (!try_module_get(q->owner))
279 q = NULL;
280 break;
281 }
282 }
283 read_unlock(&qdisc_mod_lock);
284 }
285 return q;
286}
287
288static struct qdisc_rate_table *qdisc_rtab_list;
289
Patrick McHardy1e904742008-01-22 22:11:17 -0800290struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291{
292 struct qdisc_rate_table *rtab;
293
294 for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
295 if (memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) == 0) {
296 rtab->refcnt++;
297 return rtab;
298 }
299 }
300
Patrick McHardy5feb5e12008-01-23 20:35:19 -0800301 if (tab == NULL || r->rate == 0 || r->cell_log == 0 ||
302 nla_len(tab) != TC_RTAB_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 return NULL;
304
305 rtab = kmalloc(sizeof(*rtab), GFP_KERNEL);
306 if (rtab) {
307 rtab->rate = *r;
308 rtab->refcnt = 1;
Patrick McHardy1e904742008-01-22 22:11:17 -0800309 memcpy(rtab->data, nla_data(tab), 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 rtab->next = qdisc_rtab_list;
311 qdisc_rtab_list = rtab;
312 }
313 return rtab;
314}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800315EXPORT_SYMBOL(qdisc_get_rtab);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317void qdisc_put_rtab(struct qdisc_rate_table *tab)
318{
319 struct qdisc_rate_table *rtab, **rtabp;
320
321 if (!tab || --tab->refcnt)
322 return;
323
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000324 for (rtabp = &qdisc_rtab_list;
325 (rtab = *rtabp) != NULL;
326 rtabp = &rtab->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 if (rtab == tab) {
328 *rtabp = rtab->next;
329 kfree(rtab);
330 return;
331 }
332 }
333}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800334EXPORT_SYMBOL(qdisc_put_rtab);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700336static LIST_HEAD(qdisc_stab_list);
337static DEFINE_SPINLOCK(qdisc_stab_lock);
338
339static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = {
340 [TCA_STAB_BASE] = { .len = sizeof(struct tc_sizespec) },
341 [TCA_STAB_DATA] = { .type = NLA_BINARY },
342};
343
344static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
345{
346 struct nlattr *tb[TCA_STAB_MAX + 1];
347 struct qdisc_size_table *stab;
348 struct tc_sizespec *s;
349 unsigned int tsize = 0;
350 u16 *tab = NULL;
351 int err;
352
353 err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy);
354 if (err < 0)
355 return ERR_PTR(err);
356 if (!tb[TCA_STAB_BASE])
357 return ERR_PTR(-EINVAL);
358
359 s = nla_data(tb[TCA_STAB_BASE]);
360
361 if (s->tsize > 0) {
362 if (!tb[TCA_STAB_DATA])
363 return ERR_PTR(-EINVAL);
364 tab = nla_data(tb[TCA_STAB_DATA]);
365 tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16);
366 }
367
Dan Carpenter00093fa2010-08-14 11:09:49 +0000368 if (tsize != s->tsize || (!tab && tsize > 0))
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700369 return ERR_PTR(-EINVAL);
370
David S. Millerf3b96052008-08-18 22:33:05 -0700371 spin_lock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700372
373 list_for_each_entry(stab, &qdisc_stab_list, list) {
374 if (memcmp(&stab->szopts, s, sizeof(*s)))
375 continue;
376 if (tsize > 0 && memcmp(stab->data, tab, tsize * sizeof(u16)))
377 continue;
378 stab->refcnt++;
David S. Millerf3b96052008-08-18 22:33:05 -0700379 spin_unlock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700380 return stab;
381 }
382
David S. Millerf3b96052008-08-18 22:33:05 -0700383 spin_unlock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700384
385 stab = kmalloc(sizeof(*stab) + tsize * sizeof(u16), GFP_KERNEL);
386 if (!stab)
387 return ERR_PTR(-ENOMEM);
388
389 stab->refcnt = 1;
390 stab->szopts = *s;
391 if (tsize > 0)
392 memcpy(stab->data, tab, tsize * sizeof(u16));
393
David S. Millerf3b96052008-08-18 22:33:05 -0700394 spin_lock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700395 list_add_tail(&stab->list, &qdisc_stab_list);
David S. Millerf3b96052008-08-18 22:33:05 -0700396 spin_unlock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700397
398 return stab;
399}
400
Eric Dumazeta2da5702011-01-20 03:48:19 +0000401static void stab_kfree_rcu(struct rcu_head *head)
402{
403 kfree(container_of(head, struct qdisc_size_table, rcu));
404}
405
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700406void qdisc_put_stab(struct qdisc_size_table *tab)
407{
408 if (!tab)
409 return;
410
David S. Millerf3b96052008-08-18 22:33:05 -0700411 spin_lock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700412
413 if (--tab->refcnt == 0) {
414 list_del(&tab->list);
Eric Dumazeta2da5702011-01-20 03:48:19 +0000415 call_rcu_bh(&tab->rcu, stab_kfree_rcu);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700416 }
417
David S. Millerf3b96052008-08-18 22:33:05 -0700418 spin_unlock(&qdisc_stab_lock);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700419}
420EXPORT_SYMBOL(qdisc_put_stab);
421
422static int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab)
423{
424 struct nlattr *nest;
425
426 nest = nla_nest_start(skb, TCA_STAB);
Patrick McHardy3aa46142008-11-20 04:07:14 -0800427 if (nest == NULL)
428 goto nla_put_failure;
David S. Miller1b34ec42012-03-29 05:11:39 -0400429 if (nla_put(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts))
430 goto nla_put_failure;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700431 nla_nest_end(skb, nest);
432
433 return skb->len;
434
435nla_put_failure:
436 return -1;
437}
438
Eric Dumazeta2da5702011-01-20 03:48:19 +0000439void __qdisc_calculate_pkt_len(struct sk_buff *skb, const struct qdisc_size_table *stab)
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700440{
441 int pkt_len, slot;
442
443 pkt_len = skb->len + stab->szopts.overhead;
444 if (unlikely(!stab->szopts.tsize))
445 goto out;
446
447 slot = pkt_len + stab->szopts.cell_align;
448 if (unlikely(slot < 0))
449 slot = 0;
450
451 slot >>= stab->szopts.cell_log;
452 if (likely(slot < stab->szopts.tsize))
453 pkt_len = stab->data[slot];
454 else
455 pkt_len = stab->data[stab->szopts.tsize - 1] *
456 (slot / stab->szopts.tsize) +
457 stab->data[slot % stab->szopts.tsize];
458
459 pkt_len <<= stab->szopts.size_log;
460out:
461 if (unlikely(pkt_len < 1))
462 pkt_len = 1;
463 qdisc_skb_cb(skb)->pkt_len = pkt_len;
464}
Eric Dumazeta2da5702011-01-20 03:48:19 +0000465EXPORT_SYMBOL(__qdisc_calculate_pkt_len);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700466
Jarek Poplawskib00355d2009-02-01 01:12:42 -0800467void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc)
468{
469 if (!(qdisc->flags & TCQ_F_WARN_NONWC)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000470 pr_warn("%s: %s qdisc %X: is non-work-conserving?\n",
471 txt, qdisc->ops->id, qdisc->handle >> 16);
Jarek Poplawskib00355d2009-02-01 01:12:42 -0800472 qdisc->flags |= TCQ_F_WARN_NONWC;
473 }
474}
475EXPORT_SYMBOL(qdisc_warn_nonwc);
476
Patrick McHardy41794772007-03-16 01:19:15 -0700477static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
478{
479 struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog,
David S. Miller2fbd3da2009-09-01 17:59:25 -0700480 timer);
Patrick McHardy41794772007-03-16 01:19:15 -0700481
Eric Dumazetfd245a42011-01-20 05:27:16 +0000482 qdisc_unthrottled(wd->qdisc);
David S. Miller8608db02008-08-18 20:51:18 -0700483 __netif_schedule(qdisc_root(wd->qdisc));
Stephen Hemminger19365022007-03-22 12:18:35 -0700484
Patrick McHardy41794772007-03-16 01:19:15 -0700485 return HRTIMER_NORESTART;
486}
487
488void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc)
489{
David S. Miller2fbd3da2009-09-01 17:59:25 -0700490 hrtimer_init(&wd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
491 wd->timer.function = qdisc_watchdog;
Patrick McHardy41794772007-03-16 01:19:15 -0700492 wd->qdisc = qdisc;
493}
494EXPORT_SYMBOL(qdisc_watchdog_init);
495
496void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires)
497{
Jarek Poplawski2540e052008-08-21 05:11:14 -0700498 if (test_bit(__QDISC_STATE_DEACTIVATED,
499 &qdisc_root_sleeping(wd->qdisc)->state))
500 return;
501
Eric Dumazetfd245a42011-01-20 05:27:16 +0000502 qdisc_throttled(wd->qdisc);
Eric Dumazet46baac32012-10-20 00:40:51 +0000503
504 hrtimer_start(&wd->timer,
505 ns_to_ktime(PSCHED_TICKS2NS(expires)),
506 HRTIMER_MODE_ABS);
Patrick McHardy41794772007-03-16 01:19:15 -0700507}
508EXPORT_SYMBOL(qdisc_watchdog_schedule);
509
510void qdisc_watchdog_cancel(struct qdisc_watchdog *wd)
511{
David S. Miller2fbd3da2009-09-01 17:59:25 -0700512 hrtimer_cancel(&wd->timer);
Eric Dumazetfd245a42011-01-20 05:27:16 +0000513 qdisc_unthrottled(wd->qdisc);
Patrick McHardy41794772007-03-16 01:19:15 -0700514}
515EXPORT_SYMBOL(qdisc_watchdog_cancel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Adrian Bunka94f7792008-07-22 14:20:11 -0700517static struct hlist_head *qdisc_class_hash_alloc(unsigned int n)
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700518{
519 unsigned int size = n * sizeof(struct hlist_head), i;
520 struct hlist_head *h;
521
522 if (size <= PAGE_SIZE)
523 h = kmalloc(size, GFP_KERNEL);
524 else
525 h = (struct hlist_head *)
526 __get_free_pages(GFP_KERNEL, get_order(size));
527
528 if (h != NULL) {
529 for (i = 0; i < n; i++)
530 INIT_HLIST_HEAD(&h[i]);
531 }
532 return h;
533}
534
535static void qdisc_class_hash_free(struct hlist_head *h, unsigned int n)
536{
537 unsigned int size = n * sizeof(struct hlist_head);
538
539 if (size <= PAGE_SIZE)
540 kfree(h);
541 else
542 free_pages((unsigned long)h, get_order(size));
543}
544
545void qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash)
546{
547 struct Qdisc_class_common *cl;
548 struct hlist_node *n, *next;
549 struct hlist_head *nhash, *ohash;
550 unsigned int nsize, nmask, osize;
551 unsigned int i, h;
552
553 /* Rehash when load factor exceeds 0.75 */
554 if (clhash->hashelems * 4 <= clhash->hashsize * 3)
555 return;
556 nsize = clhash->hashsize * 2;
557 nmask = nsize - 1;
558 nhash = qdisc_class_hash_alloc(nsize);
559 if (nhash == NULL)
560 return;
561
562 ohash = clhash->hash;
563 osize = clhash->hashsize;
564
565 sch_tree_lock(sch);
566 for (i = 0; i < osize; i++) {
567 hlist_for_each_entry_safe(cl, n, next, &ohash[i], hnode) {
568 h = qdisc_class_hash(cl->classid, nmask);
569 hlist_add_head(&cl->hnode, &nhash[h]);
570 }
571 }
572 clhash->hash = nhash;
573 clhash->hashsize = nsize;
574 clhash->hashmask = nmask;
575 sch_tree_unlock(sch);
576
577 qdisc_class_hash_free(ohash, osize);
578}
579EXPORT_SYMBOL(qdisc_class_hash_grow);
580
581int qdisc_class_hash_init(struct Qdisc_class_hash *clhash)
582{
583 unsigned int size = 4;
584
585 clhash->hash = qdisc_class_hash_alloc(size);
586 if (clhash->hash == NULL)
587 return -ENOMEM;
588 clhash->hashsize = size;
589 clhash->hashmask = size - 1;
590 clhash->hashelems = 0;
591 return 0;
592}
593EXPORT_SYMBOL(qdisc_class_hash_init);
594
595void qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash)
596{
597 qdisc_class_hash_free(clhash->hash, clhash->hashsize);
598}
599EXPORT_SYMBOL(qdisc_class_hash_destroy);
600
601void qdisc_class_hash_insert(struct Qdisc_class_hash *clhash,
602 struct Qdisc_class_common *cl)
603{
604 unsigned int h;
605
606 INIT_HLIST_NODE(&cl->hnode);
607 h = qdisc_class_hash(cl->classid, clhash->hashmask);
608 hlist_add_head(&cl->hnode, &clhash->hash[h]);
609 clhash->hashelems++;
610}
611EXPORT_SYMBOL(qdisc_class_hash_insert);
612
613void qdisc_class_hash_remove(struct Qdisc_class_hash *clhash,
614 struct Qdisc_class_common *cl)
615{
616 hlist_del(&cl->hnode);
617 clhash->hashelems--;
618}
619EXPORT_SYMBOL(qdisc_class_hash_remove);
620
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000621/* Allocate an unique handle from space managed by kernel
622 * Possible range is [8000-FFFF]:0000 (0x8000 values)
623 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624static u32 qdisc_alloc_handle(struct net_device *dev)
625{
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000626 int i = 0x8000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 static u32 autohandle = TC_H_MAKE(0x80000000U, 0);
628
629 do {
630 autohandle += TC_H_MAKE(0x10000U, 0);
631 if (autohandle == TC_H_MAKE(TC_H_ROOT, 0))
632 autohandle = TC_H_MAKE(0x80000000U, 0);
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000633 if (!qdisc_lookup(dev, autohandle))
634 return autohandle;
635 cond_resched();
636 } while (--i > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000638 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639}
640
Patrick McHardy43effa12006-11-29 17:35:48 -0800641void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
642{
Eric Dumazet20fea082007-11-14 01:44:41 -0800643 const struct Qdisc_class_ops *cops;
Patrick McHardy43effa12006-11-29 17:35:48 -0800644 unsigned long cl;
645 u32 parentid;
646
647 if (n == 0)
648 return;
649 while ((parentid = sch->parent)) {
Jarek Poplawski066a3b52008-04-14 15:10:42 -0700650 if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS))
651 return;
652
David S. Miller5ce2d482008-07-08 17:06:30 -0700653 sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid));
Patrick McHardyffc8fef2007-07-30 17:11:50 -0700654 if (sch == NULL) {
655 WARN_ON(parentid != TC_H_ROOT);
656 return;
657 }
Patrick McHardy43effa12006-11-29 17:35:48 -0800658 cops = sch->ops->cl_ops;
659 if (cops->qlen_notify) {
660 cl = cops->get(sch, parentid);
661 cops->qlen_notify(sch, cl);
662 cops->put(sch, cl);
663 }
664 sch->q.qlen -= n;
665 }
666}
667EXPORT_SYMBOL(qdisc_tree_decrease_qlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Tom Goff7316ae82010-03-19 15:40:13 +0000669static void notify_and_destroy(struct net *net, struct sk_buff *skb,
670 struct nlmsghdr *n, u32 clid,
David S. Miller99194cf2008-07-17 04:54:10 -0700671 struct Qdisc *old, struct Qdisc *new)
672{
673 if (new || old)
Tom Goff7316ae82010-03-19 15:40:13 +0000674 qdisc_notify(net, skb, n, clid, old, new);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
David S. Miller4d8863a2008-08-18 21:03:15 -0700676 if (old)
David S. Miller99194cf2008-07-17 04:54:10 -0700677 qdisc_destroy(old);
David S. Miller99194cf2008-07-17 04:54:10 -0700678}
679
680/* Graft qdisc "new" to class "classid" of qdisc "parent" or
681 * to device "dev".
682 *
683 * When appropriate send a netlink notification using 'skb'
684 * and "n".
685 *
686 * On success, destroy old qdisc.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 */
688
689static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
David S. Miller99194cf2008-07-17 04:54:10 -0700690 struct sk_buff *skb, struct nlmsghdr *n, u32 classid,
691 struct Qdisc *new, struct Qdisc *old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
David S. Miller99194cf2008-07-17 04:54:10 -0700693 struct Qdisc *q = old;
Tom Goff7316ae82010-03-19 15:40:13 +0000694 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900697 if (parent == NULL) {
David S. Miller99194cf2008-07-17 04:54:10 -0700698 unsigned int i, num_q, ingress;
699
700 ingress = 0;
701 num_q = dev->num_tx_queues;
David S. Miller8d50b532008-07-30 02:37:46 -0700702 if ((q && q->flags & TCQ_F_INGRESS) ||
703 (new && new->flags & TCQ_F_INGRESS)) {
David S. Miller99194cf2008-07-17 04:54:10 -0700704 num_q = 1;
705 ingress = 1;
Eric Dumazet24824a02010-10-02 06:11:55 +0000706 if (!dev_ingress_queue(dev))
707 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
David S. Miller99194cf2008-07-17 04:54:10 -0700709
710 if (dev->flags & IFF_UP)
711 dev_deactivate(dev);
712
David S. Miller6ec1c692009-09-06 01:58:51 -0700713 if (new && new->ops->attach) {
714 new->ops->attach(new);
715 num_q = 0;
716 }
717
David S. Miller99194cf2008-07-17 04:54:10 -0700718 for (i = 0; i < num_q; i++) {
Eric Dumazet24824a02010-10-02 06:11:55 +0000719 struct netdev_queue *dev_queue = dev_ingress_queue(dev);
David S. Miller99194cf2008-07-17 04:54:10 -0700720
721 if (!ingress)
722 dev_queue = netdev_get_tx_queue(dev, i);
723
David S. Miller8d50b532008-07-30 02:37:46 -0700724 old = dev_graft_qdisc(dev_queue, new);
725 if (new && i > 0)
726 atomic_inc(&new->refcnt);
727
Jarek Poplawski036d6a62009-09-13 22:35:44 +0000728 if (!ingress)
729 qdisc_destroy(old);
David S. Miller99194cf2008-07-17 04:54:10 -0700730 }
731
Jarek Poplawski036d6a62009-09-13 22:35:44 +0000732 if (!ingress) {
Tom Goff7316ae82010-03-19 15:40:13 +0000733 notify_and_destroy(net, skb, n, classid,
734 dev->qdisc, new);
Jarek Poplawski036d6a62009-09-13 22:35:44 +0000735 if (new && !new->ops->attach)
736 atomic_inc(&new->refcnt);
737 dev->qdisc = new ? : &noop_qdisc;
738 } else {
Tom Goff7316ae82010-03-19 15:40:13 +0000739 notify_and_destroy(net, skb, n, classid, old, new);
Jarek Poplawski036d6a62009-09-13 22:35:44 +0000740 }
Patrick McHardyaf356af2009-09-04 06:41:18 +0000741
David S. Miller99194cf2008-07-17 04:54:10 -0700742 if (dev->flags & IFF_UP)
743 dev_activate(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 } else {
Eric Dumazet20fea082007-11-14 01:44:41 -0800745 const struct Qdisc_class_ops *cops = parent->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Patrick McHardyc9f1d032009-09-04 06:41:13 +0000747 err = -EOPNOTSUPP;
748 if (cops && cops->graft) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 unsigned long cl = cops->get(parent, classid);
750 if (cl) {
David S. Miller99194cf2008-07-17 04:54:10 -0700751 err = cops->graft(parent, cl, new, &old);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 cops->put(parent, cl);
Patrick McHardyc9f1d032009-09-04 06:41:13 +0000753 } else
754 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 }
David S. Miller99194cf2008-07-17 04:54:10 -0700756 if (!err)
Tom Goff7316ae82010-03-19 15:40:13 +0000757 notify_and_destroy(net, skb, n, classid, old, new);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 }
759 return err;
760}
761
Jarek Poplawski25bfcd52008-08-18 20:53:34 -0700762/* lockdep annotation is needed for ingress; egress gets it only for name */
763static struct lock_class_key qdisc_tx_lock;
764static struct lock_class_key qdisc_rx_lock;
765
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766/*
767 Allocate and initialize new qdisc.
768
769 Parameters are passed via opt.
770 */
771
772static struct Qdisc *
David S. Millerbb949fb2008-07-08 16:55:56 -0700773qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
Patrick McHardy23bcf632009-09-09 18:11:23 -0700774 struct Qdisc *p, u32 parent, u32 handle,
775 struct nlattr **tca, int *errp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776{
777 int err;
Patrick McHardy1e904742008-01-22 22:11:17 -0800778 struct nlattr *kind = tca[TCA_KIND];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 struct Qdisc *sch;
780 struct Qdisc_ops *ops;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700781 struct qdisc_size_table *stab;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 ops = qdisc_lookup_ops(kind);
Johannes Berg95a5afc2008-10-16 15:24:51 -0700784#ifdef CONFIG_MODULES
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if (ops == NULL && kind != NULL) {
786 char name[IFNAMSIZ];
Patrick McHardy1e904742008-01-22 22:11:17 -0800787 if (nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 /* We dropped the RTNL semaphore in order to
789 * perform the module load. So, even if we
790 * succeeded in loading the module we have to
791 * tell the caller to replay the request. We
792 * indicate this using -EAGAIN.
793 * We replay the request because the device may
794 * go away in the mean time.
795 */
796 rtnl_unlock();
797 request_module("sch_%s", name);
798 rtnl_lock();
799 ops = qdisc_lookup_ops(kind);
800 if (ops != NULL) {
801 /* We will try again qdisc_lookup_ops,
802 * so don't keep a reference.
803 */
804 module_put(ops->owner);
805 err = -EAGAIN;
806 goto err_out;
807 }
808 }
809 }
810#endif
811
Jamal Hadi Salimb9e2cc02006-08-03 16:36:51 -0700812 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 if (ops == NULL)
814 goto err_out;
815
David S. Miller5ce2d482008-07-08 17:06:30 -0700816 sch = qdisc_alloc(dev_queue, ops);
Thomas Graf3d54b822005-07-05 14:15:09 -0700817 if (IS_ERR(sch)) {
818 err = PTR_ERR(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 goto err_out2;
Thomas Graf3d54b822005-07-05 14:15:09 -0700820 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Patrick McHardyffc8fef2007-07-30 17:11:50 -0700822 sch->parent = parent;
823
Thomas Graf3d54b822005-07-05 14:15:09 -0700824 if (handle == TC_H_INGRESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 sch->flags |= TCQ_F_INGRESS;
Thomas Graf3d54b822005-07-05 14:15:09 -0700826 handle = TC_H_MAKE(TC_H_INGRESS, 0);
Jarek Poplawski25bfcd52008-08-18 20:53:34 -0700827 lockdep_set_class(qdisc_lock(sch), &qdisc_rx_lock);
Patrick McHardyfd44de72007-04-16 17:07:08 -0700828 } else {
Patrick McHardyfd44de72007-04-16 17:07:08 -0700829 if (handle == 0) {
830 handle = qdisc_alloc_handle(dev);
831 err = -ENOMEM;
832 if (handle == 0)
833 goto err_out3;
834 }
Jarek Poplawski25bfcd52008-08-18 20:53:34 -0700835 lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 }
837
Thomas Graf3d54b822005-07-05 14:15:09 -0700838 sch->handle = handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Patrick McHardy1e904742008-01-22 22:11:17 -0800840 if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700841 if (tca[TCA_STAB]) {
842 stab = qdisc_get_stab(tca[TCA_STAB]);
843 if (IS_ERR(stab)) {
844 err = PTR_ERR(stab);
Jarek Poplawski7c64b9f2009-09-15 23:42:05 -0700845 goto err_out4;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700846 }
Eric Dumazeta2da5702011-01-20 03:48:19 +0000847 rcu_assign_pointer(sch->stab, stab);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700848 }
Patrick McHardy1e904742008-01-22 22:11:17 -0800849 if (tca[TCA_RATE]) {
Jarek Poplawskif6f9b932008-08-27 02:25:17 -0700850 spinlock_t *root_lock;
851
Patrick McHardy23bcf632009-09-09 18:11:23 -0700852 err = -EOPNOTSUPP;
853 if (sch->flags & TCQ_F_MQROOT)
854 goto err_out4;
855
Jarek Poplawskif6f9b932008-08-27 02:25:17 -0700856 if ((sch->parent != TC_H_ROOT) &&
Patrick McHardy23bcf632009-09-09 18:11:23 -0700857 !(sch->flags & TCQ_F_INGRESS) &&
858 (!p || !(p->flags & TCQ_F_MQROOT)))
Jarek Poplawskif6f9b932008-08-27 02:25:17 -0700859 root_lock = qdisc_root_sleeping_lock(sch);
860 else
861 root_lock = qdisc_lock(sch);
862
Thomas Graf023e09a2005-07-05 14:15:53 -0700863 err = gen_new_estimator(&sch->bstats, &sch->rate_est,
Jarek Poplawskif6f9b932008-08-27 02:25:17 -0700864 root_lock, tca[TCA_RATE]);
Patrick McHardy23bcf632009-09-09 18:11:23 -0700865 if (err)
866 goto err_out4;
Thomas Graf023e09a2005-07-05 14:15:53 -0700867 }
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700868
869 qdisc_list_add(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 return sch;
872 }
873err_out3:
874 dev_put(dev);
Thomas Graf3d54b822005-07-05 14:15:09 -0700875 kfree((char *) sch - sch->padded);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876err_out2:
877 module_put(ops->owner);
878err_out:
879 *errp = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 return NULL;
Patrick McHardy23bcf632009-09-09 18:11:23 -0700881
882err_out4:
883 /*
884 * Any broken qdiscs that would require a ops->reset() here?
885 * The qdisc was never in action so it shouldn't be necessary.
886 */
Eric Dumazeta2da5702011-01-20 03:48:19 +0000887 qdisc_put_stab(rtnl_dereference(sch->stab));
Patrick McHardy23bcf632009-09-09 18:11:23 -0700888 if (ops->destroy)
889 ops->destroy(sch);
890 goto err_out3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891}
892
Patrick McHardy1e904742008-01-22 22:11:17 -0800893static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894{
Eric Dumazeta2da5702011-01-20 03:48:19 +0000895 struct qdisc_size_table *ostab, *stab = NULL;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700896 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700898 if (tca[TCA_OPTIONS]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 if (sch->ops->change == NULL)
900 return -EINVAL;
Patrick McHardy1e904742008-01-22 22:11:17 -0800901 err = sch->ops->change(sch, tca[TCA_OPTIONS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 if (err)
903 return err;
904 }
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700905
906 if (tca[TCA_STAB]) {
907 stab = qdisc_get_stab(tca[TCA_STAB]);
908 if (IS_ERR(stab))
909 return PTR_ERR(stab);
910 }
911
Eric Dumazeta2da5702011-01-20 03:48:19 +0000912 ostab = rtnl_dereference(sch->stab);
913 rcu_assign_pointer(sch->stab, stab);
914 qdisc_put_stab(ostab);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700915
Patrick McHardy23bcf632009-09-09 18:11:23 -0700916 if (tca[TCA_RATE]) {
Stephen Hemminger71bcb092008-11-25 21:13:31 -0800917 /* NB: ignores errors from replace_estimator
918 because change can't be undone. */
Patrick McHardy23bcf632009-09-09 18:11:23 -0700919 if (sch->flags & TCQ_F_MQROOT)
920 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 gen_replace_estimator(&sch->bstats, &sch->rate_est,
Stephen Hemminger71bcb092008-11-25 21:13:31 -0800922 qdisc_root_sleeping_lock(sch),
923 tca[TCA_RATE]);
Patrick McHardy23bcf632009-09-09 18:11:23 -0700924 }
925out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 return 0;
927}
928
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000929struct check_loop_arg {
930 struct qdisc_walker w;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 struct Qdisc *p;
932 int depth;
933};
934
935static int check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w);
936
937static int check_loop(struct Qdisc *q, struct Qdisc *p, int depth)
938{
939 struct check_loop_arg arg;
940
941 if (q->ops->cl_ops == NULL)
942 return 0;
943
944 arg.w.stop = arg.w.skip = arg.w.count = 0;
945 arg.w.fn = check_loop_fn;
946 arg.depth = depth;
947 arg.p = p;
948 q->ops->cl_ops->walk(q, &arg.w);
949 return arg.w.stop ? -ELOOP : 0;
950}
951
952static int
953check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w)
954{
955 struct Qdisc *leaf;
Eric Dumazet20fea082007-11-14 01:44:41 -0800956 const struct Qdisc_class_ops *cops = q->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 struct check_loop_arg *arg = (struct check_loop_arg *)w;
958
959 leaf = cops->leaf(q, cl);
960 if (leaf) {
961 if (leaf == arg->p || arg->depth > 7)
962 return -ELOOP;
963 return check_loop(leaf, arg->p, arg->depth + 1);
964 }
965 return 0;
966}
967
968/*
969 * Delete/get qdisc.
970 */
971
972static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
973{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900974 struct net *net = sock_net(skb->sk);
David S. Miller02ef22c2012-06-26 21:50:05 -0700975 struct tcmsg *tcm = nlmsg_data(n);
Patrick McHardy1e904742008-01-22 22:11:17 -0800976 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 struct net_device *dev;
978 u32 clid = tcm->tcm_parent;
979 struct Qdisc *q = NULL;
980 struct Qdisc *p = NULL;
981 int err;
982
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +0000983 if ((n->nlmsg_type != RTM_GETQDISC) && !capable(CAP_NET_ADMIN))
984 return -EPERM;
985
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000986 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
987 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 return -ENODEV;
989
Patrick McHardy1e904742008-01-22 22:11:17 -0800990 err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
991 if (err < 0)
992 return err;
993
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 if (clid) {
995 if (clid != TC_H_ROOT) {
996 if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000997 p = qdisc_lookup(dev, TC_H_MAJ(clid));
998 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 return -ENOENT;
1000 q = qdisc_leaf(p, clid);
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001001 } else if (dev_ingress_queue(dev)) {
1002 q = dev_ingress_queue(dev)->qdisc_sleeping;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001003 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 } else {
Patrick McHardyaf356af2009-09-04 06:41:18 +00001005 q = dev->qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 }
1007 if (!q)
1008 return -ENOENT;
1009
1010 if (tcm->tcm_handle && q->handle != tcm->tcm_handle)
1011 return -EINVAL;
1012 } else {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001013 q = qdisc_lookup(dev, tcm->tcm_handle);
1014 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 return -ENOENT;
1016 }
1017
Patrick McHardy1e904742008-01-22 22:11:17 -08001018 if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 return -EINVAL;
1020
1021 if (n->nlmsg_type == RTM_DELQDISC) {
1022 if (!clid)
1023 return -EINVAL;
1024 if (q->handle == 0)
1025 return -ENOENT;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001026 err = qdisc_graft(dev, p, skb, n, clid, NULL, q);
1027 if (err != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 } else {
Tom Goff7316ae82010-03-19 15:40:13 +00001030 qdisc_notify(net, skb, n, clid, NULL, q);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 }
1032 return 0;
1033}
1034
1035/*
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001036 * Create/change qdisc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 */
1038
1039static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
1040{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001041 struct net *net = sock_net(skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 struct tcmsg *tcm;
Patrick McHardy1e904742008-01-22 22:11:17 -08001043 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 struct net_device *dev;
1045 u32 clid;
1046 struct Qdisc *q, *p;
1047 int err;
1048
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +00001049 if (!capable(CAP_NET_ADMIN))
1050 return -EPERM;
1051
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052replay:
1053 /* Reinit, just in case something touches this. */
David S. Miller02ef22c2012-06-26 21:50:05 -07001054 tcm = nlmsg_data(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 clid = tcm->tcm_parent;
1056 q = p = NULL;
1057
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001058 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
1059 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 return -ENODEV;
1061
Patrick McHardy1e904742008-01-22 22:11:17 -08001062 err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
1063 if (err < 0)
1064 return err;
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (clid) {
1067 if (clid != TC_H_ROOT) {
1068 if (clid != TC_H_INGRESS) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001069 p = qdisc_lookup(dev, TC_H_MAJ(clid));
1070 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 return -ENOENT;
1072 q = qdisc_leaf(p, clid);
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001073 } else if (dev_ingress_queue_create(dev)) {
1074 q = dev_ingress_queue(dev)->qdisc_sleeping;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 }
1076 } else {
Patrick McHardyaf356af2009-09-04 06:41:18 +00001077 q = dev->qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 }
1079
1080 /* It may be default qdisc, ignore it */
1081 if (q && q->handle == 0)
1082 q = NULL;
1083
1084 if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
1085 if (tcm->tcm_handle) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001086 if (q && !(n->nlmsg_flags & NLM_F_REPLACE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 return -EEXIST;
1088 if (TC_H_MIN(tcm->tcm_handle))
1089 return -EINVAL;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001090 q = qdisc_lookup(dev, tcm->tcm_handle);
1091 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 goto create_n_graft;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001093 if (n->nlmsg_flags & NLM_F_EXCL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 return -EEXIST;
Patrick McHardy1e904742008-01-22 22:11:17 -08001095 if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 return -EINVAL;
1097 if (q == p ||
1098 (p && check_loop(q, p, 0)))
1099 return -ELOOP;
1100 atomic_inc(&q->refcnt);
1101 goto graft;
1102 } else {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001103 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 goto create_n_graft;
1105
1106 /* This magic test requires explanation.
1107 *
1108 * We know, that some child q is already
1109 * attached to this parent and have choice:
1110 * either to change it or to create/graft new one.
1111 *
1112 * 1. We are allowed to create/graft only
1113 * if CREATE and REPLACE flags are set.
1114 *
1115 * 2. If EXCL is set, requestor wanted to say,
1116 * that qdisc tcm_handle is not expected
1117 * to exist, so that we choose create/graft too.
1118 *
1119 * 3. The last case is when no flags are set.
1120 * Alas, it is sort of hole in API, we
1121 * cannot decide what to do unambiguously.
1122 * For now we select create/graft, if
1123 * user gave KIND, which does not match existing.
1124 */
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001125 if ((n->nlmsg_flags & NLM_F_CREATE) &&
1126 (n->nlmsg_flags & NLM_F_REPLACE) &&
1127 ((n->nlmsg_flags & NLM_F_EXCL) ||
Patrick McHardy1e904742008-01-22 22:11:17 -08001128 (tca[TCA_KIND] &&
1129 nla_strcmp(tca[TCA_KIND], q->ops->id))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 goto create_n_graft;
1131 }
1132 }
1133 } else {
1134 if (!tcm->tcm_handle)
1135 return -EINVAL;
1136 q = qdisc_lookup(dev, tcm->tcm_handle);
1137 }
1138
1139 /* Change qdisc parameters */
1140 if (q == NULL)
1141 return -ENOENT;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001142 if (n->nlmsg_flags & NLM_F_EXCL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 return -EEXIST;
Patrick McHardy1e904742008-01-22 22:11:17 -08001144 if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 return -EINVAL;
1146 err = qdisc_change(q, tca);
1147 if (err == 0)
Tom Goff7316ae82010-03-19 15:40:13 +00001148 qdisc_notify(net, skb, n, clid, NULL, q);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 return err;
1150
1151create_n_graft:
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001152 if (!(n->nlmsg_flags & NLM_F_CREATE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 return -ENOENT;
Eric Dumazet24824a02010-10-02 06:11:55 +00001154 if (clid == TC_H_INGRESS) {
1155 if (dev_ingress_queue(dev))
1156 q = qdisc_create(dev, dev_ingress_queue(dev), p,
1157 tcm->tcm_parent, tcm->tcm_parent,
1158 tca, &err);
1159 else
1160 err = -ENOENT;
1161 } else {
Jarek Poplawski926e61b2009-09-15 02:53:07 -07001162 struct netdev_queue *dev_queue;
David S. Miller6ec1c692009-09-06 01:58:51 -07001163
1164 if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue)
Jarek Poplawski926e61b2009-09-15 02:53:07 -07001165 dev_queue = p->ops->cl_ops->select_queue(p, tcm);
1166 else if (p)
1167 dev_queue = p->dev_queue;
1168 else
1169 dev_queue = netdev_get_tx_queue(dev, 0);
David S. Miller6ec1c692009-09-06 01:58:51 -07001170
Jarek Poplawski926e61b2009-09-15 02:53:07 -07001171 q = qdisc_create(dev, dev_queue, p,
David S. Millerbb949fb2008-07-08 16:55:56 -07001172 tcm->tcm_parent, tcm->tcm_handle,
Patrick McHardyffc8fef2007-07-30 17:11:50 -07001173 tca, &err);
David S. Miller6ec1c692009-09-06 01:58:51 -07001174 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 if (q == NULL) {
1176 if (err == -EAGAIN)
1177 goto replay;
1178 return err;
1179 }
1180
1181graft:
Ilpo Järvinene5befbd2008-08-18 22:30:01 -07001182 err = qdisc_graft(dev, p, skb, n, clid, q, NULL);
1183 if (err) {
1184 if (q)
1185 qdisc_destroy(q);
1186 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
Ilpo Järvinene5befbd2008-08-18 22:30:01 -07001188
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 return 0;
1190}
1191
1192static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
Eric W. Biederman15e47302012-09-07 20:12:54 +00001193 u32 portid, u32 seq, u16 flags, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194{
1195 struct tcmsg *tcm;
1196 struct nlmsghdr *nlh;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001197 unsigned char *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 struct gnet_dump d;
Eric Dumazeta2da5702011-01-20 03:48:19 +00001199 struct qdisc_size_table *stab;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
Eric W. Biederman15e47302012-09-07 20:12:54 +00001201 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
David S. Miller02ef22c2012-06-26 21:50:05 -07001202 if (!nlh)
1203 goto out_nlmsg_trim;
1204 tcm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 tcm->tcm_family = AF_UNSPEC;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -07001206 tcm->tcm__pad1 = 0;
1207 tcm->tcm__pad2 = 0;
David S. Miller5ce2d482008-07-08 17:06:30 -07001208 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 tcm->tcm_parent = clid;
1210 tcm->tcm_handle = q->handle;
1211 tcm->tcm_info = atomic_read(&q->refcnt);
David S. Miller1b34ec42012-03-29 05:11:39 -04001212 if (nla_put_string(skb, TCA_KIND, q->ops->id))
1213 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 if (q->ops->dump && q->ops->dump(q, skb) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001215 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 q->qstats.qlen = q->q.qlen;
1217
Eric Dumazeta2da5702011-01-20 03:48:19 +00001218 stab = rtnl_dereference(q->stab);
1219 if (stab && qdisc_dump_stab(skb, stab) < 0)
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001220 goto nla_put_failure;
1221
Jarek Poplawski102396a2008-08-29 14:21:52 -07001222 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
1223 qdisc_root_sleeping_lock(q), &d) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001224 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
1226 if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001227 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
1229 if (gnet_stats_copy_basic(&d, &q->bstats) < 0 ||
Eric Dumazetd250a5f2009-10-02 10:32:18 +00001230 gnet_stats_copy_rate_est(&d, &q->bstats, &q->rate_est) < 0 ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 gnet_stats_copy_queue(&d, &q->qstats) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001232 goto nla_put_failure;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001233
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 if (gnet_stats_finish_copy(&d) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001235 goto nla_put_failure;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001236
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001237 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 return skb->len;
1239
David S. Miller02ef22c2012-06-26 21:50:05 -07001240out_nlmsg_trim:
Patrick McHardy1e904742008-01-22 22:11:17 -08001241nla_put_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001242 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 return -1;
1244}
1245
Eric Dumazet53b0f082010-05-22 20:37:44 +00001246static bool tc_qdisc_dump_ignore(struct Qdisc *q)
1247{
1248 return (q->flags & TCQ_F_BUILTIN) ? true : false;
1249}
1250
Tom Goff7316ae82010-03-19 15:40:13 +00001251static int qdisc_notify(struct net *net, struct sk_buff *oskb,
1252 struct nlmsghdr *n, u32 clid,
1253 struct Qdisc *old, struct Qdisc *new)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254{
1255 struct sk_buff *skb;
Eric W. Biederman15e47302012-09-07 20:12:54 +00001256 u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
1258 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1259 if (!skb)
1260 return -ENOBUFS;
1261
Eric Dumazet53b0f082010-05-22 20:37:44 +00001262 if (old && !tc_qdisc_dump_ignore(old)) {
Eric W. Biederman15e47302012-09-07 20:12:54 +00001263 if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq,
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001264 0, RTM_DELQDISC) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 goto err_out;
1266 }
Eric Dumazet53b0f082010-05-22 20:37:44 +00001267 if (new && !tc_qdisc_dump_ignore(new)) {
Eric W. Biederman15e47302012-09-07 20:12:54 +00001268 if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq,
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001269 old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 goto err_out;
1271 }
1272
1273 if (skb->len)
Eric W. Biederman15e47302012-09-07 20:12:54 +00001274 return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001275 n->nlmsg_flags & NLM_F_ECHO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276
1277err_out:
1278 kfree_skb(skb);
1279 return -EINVAL;
1280}
1281
David S. Miller30723672008-07-18 22:50:15 -07001282static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
1283 struct netlink_callback *cb,
1284 int *q_idx_p, int s_q_idx)
1285{
1286 int ret = 0, q_idx = *q_idx_p;
1287 struct Qdisc *q;
1288
1289 if (!root)
1290 return 0;
1291
1292 q = root;
1293 if (q_idx < s_q_idx) {
1294 q_idx++;
1295 } else {
1296 if (!tc_qdisc_dump_ignore(q) &&
Eric W. Biederman15e47302012-09-07 20:12:54 +00001297 tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
David S. Miller30723672008-07-18 22:50:15 -07001298 cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)
1299 goto done;
1300 q_idx++;
1301 }
1302 list_for_each_entry(q, &root->list, list) {
1303 if (q_idx < s_q_idx) {
1304 q_idx++;
1305 continue;
1306 }
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001307 if (!tc_qdisc_dump_ignore(q) &&
Eric W. Biederman15e47302012-09-07 20:12:54 +00001308 tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
David S. Miller30723672008-07-18 22:50:15 -07001309 cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)
1310 goto done;
1311 q_idx++;
1312 }
1313
1314out:
1315 *q_idx_p = q_idx;
1316 return ret;
1317done:
1318 ret = -1;
1319 goto out;
1320}
1321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
1323{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001324 struct net *net = sock_net(skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 int idx, q_idx;
1326 int s_idx, s_q_idx;
1327 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 s_idx = cb->args[0];
1330 s_q_idx = q_idx = cb->args[1];
stephen hemmingerf1e90162009-11-10 07:54:49 +00001331
1332 rcu_read_lock();
Pavel Emelianov7562f872007-05-03 15:13:45 -07001333 idx = 0;
Tom Goff7316ae82010-03-19 15:40:13 +00001334 for_each_netdev_rcu(net, dev) {
David S. Miller30723672008-07-18 22:50:15 -07001335 struct netdev_queue *dev_queue;
1336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 if (idx < s_idx)
Pavel Emelianov7562f872007-05-03 15:13:45 -07001338 goto cont;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 if (idx > s_idx)
1340 s_q_idx = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 q_idx = 0;
David S. Miller30723672008-07-18 22:50:15 -07001342
Patrick McHardyaf356af2009-09-04 06:41:18 +00001343 if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx) < 0)
David S. Miller30723672008-07-18 22:50:15 -07001344 goto done;
1345
Eric Dumazet24824a02010-10-02 06:11:55 +00001346 dev_queue = dev_ingress_queue(dev);
1347 if (dev_queue &&
1348 tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb,
1349 &q_idx, s_q_idx) < 0)
David S. Miller30723672008-07-18 22:50:15 -07001350 goto done;
1351
Pavel Emelianov7562f872007-05-03 15:13:45 -07001352cont:
1353 idx++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 }
1355
1356done:
stephen hemmingerf1e90162009-11-10 07:54:49 +00001357 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
1359 cb->args[0] = idx;
1360 cb->args[1] = q_idx;
1361
1362 return skb->len;
1363}
1364
1365
1366
1367/************************************************
1368 * Traffic classes manipulation. *
1369 ************************************************/
1370
1371
1372
1373static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
1374{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001375 struct net *net = sock_net(skb->sk);
David S. Miller02ef22c2012-06-26 21:50:05 -07001376 struct tcmsg *tcm = nlmsg_data(n);
Patrick McHardy1e904742008-01-22 22:11:17 -08001377 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 struct net_device *dev;
1379 struct Qdisc *q = NULL;
Eric Dumazet20fea082007-11-14 01:44:41 -08001380 const struct Qdisc_class_ops *cops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 unsigned long cl = 0;
1382 unsigned long new_cl;
Eric W. Biederman15e47302012-09-07 20:12:54 +00001383 u32 portid = tcm->tcm_parent;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 u32 clid = tcm->tcm_handle;
1385 u32 qid = TC_H_MAJ(clid);
1386 int err;
1387
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +00001388 if ((n->nlmsg_type != RTM_GETTCLASS) && !capable(CAP_NET_ADMIN))
1389 return -EPERM;
1390
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001391 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
1392 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return -ENODEV;
1394
Patrick McHardy1e904742008-01-22 22:11:17 -08001395 err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
1396 if (err < 0)
1397 return err;
1398
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 /*
1400 parent == TC_H_UNSPEC - unspecified parent.
1401 parent == TC_H_ROOT - class is root, which has no parent.
1402 parent == X:0 - parent is root class.
1403 parent == X:Y - parent is a node in hierarchy.
1404 parent == 0:Y - parent is X:Y, where X:0 is qdisc.
1405
1406 handle == 0:0 - generate handle from kernel pool.
1407 handle == 0:Y - class is X:Y, where X:0 is qdisc.
1408 handle == X:Y - clear.
1409 handle == X:0 - root class.
1410 */
1411
1412 /* Step 1. Determine qdisc handle X:0 */
1413
Eric W. Biederman15e47302012-09-07 20:12:54 +00001414 if (portid != TC_H_ROOT) {
1415 u32 qid1 = TC_H_MAJ(portid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
1417 if (qid && qid1) {
1418 /* If both majors are known, they must be identical. */
1419 if (qid != qid1)
1420 return -EINVAL;
1421 } else if (qid1) {
1422 qid = qid1;
1423 } else if (qid == 0)
Patrick McHardyaf356af2009-09-04 06:41:18 +00001424 qid = dev->qdisc->handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425
1426 /* Now qid is genuine qdisc handle consistent
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001427 * both with parent and child.
1428 *
Eric W. Biederman15e47302012-09-07 20:12:54 +00001429 * TC_H_MAJ(portid) still may be unspecified, complete it now.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 */
Eric W. Biederman15e47302012-09-07 20:12:54 +00001431 if (portid)
1432 portid = TC_H_MAKE(qid, portid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 } else {
1434 if (qid == 0)
Patrick McHardyaf356af2009-09-04 06:41:18 +00001435 qid = dev->qdisc->handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 }
1437
1438 /* OK. Locate qdisc */
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001439 q = qdisc_lookup(dev, qid);
1440 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 return -ENOENT;
1442
1443 /* An check that it supports classes */
1444 cops = q->ops->cl_ops;
1445 if (cops == NULL)
1446 return -EINVAL;
1447
1448 /* Now try to get class */
1449 if (clid == 0) {
Eric W. Biederman15e47302012-09-07 20:12:54 +00001450 if (portid == TC_H_ROOT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 clid = qid;
1452 } else
1453 clid = TC_H_MAKE(qid, clid);
1454
1455 if (clid)
1456 cl = cops->get(q, clid);
1457
1458 if (cl == 0) {
1459 err = -ENOENT;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001460 if (n->nlmsg_type != RTM_NEWTCLASS ||
1461 !(n->nlmsg_flags & NLM_F_CREATE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 goto out;
1463 } else {
1464 switch (n->nlmsg_type) {
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001465 case RTM_NEWTCLASS:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 err = -EEXIST;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001467 if (n->nlmsg_flags & NLM_F_EXCL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 goto out;
1469 break;
1470 case RTM_DELTCLASS:
Patrick McHardyde6d5cd2009-09-04 06:41:16 +00001471 err = -EOPNOTSUPP;
1472 if (cops->delete)
1473 err = cops->delete(q, cl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 if (err == 0)
Tom Goff7316ae82010-03-19 15:40:13 +00001475 tclass_notify(net, skb, n, q, cl, RTM_DELTCLASS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 goto out;
1477 case RTM_GETTCLASS:
Tom Goff7316ae82010-03-19 15:40:13 +00001478 err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 goto out;
1480 default:
1481 err = -EINVAL;
1482 goto out;
1483 }
1484 }
1485
1486 new_cl = cl;
Patrick McHardyde6d5cd2009-09-04 06:41:16 +00001487 err = -EOPNOTSUPP;
1488 if (cops->change)
Eric W. Biederman15e47302012-09-07 20:12:54 +00001489 err = cops->change(q, clid, portid, tca, &new_cl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 if (err == 0)
Tom Goff7316ae82010-03-19 15:40:13 +00001491 tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
1493out:
1494 if (cl)
1495 cops->put(q, cl);
1496
1497 return err;
1498}
1499
1500
1501static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
1502 unsigned long cl,
Eric W. Biederman15e47302012-09-07 20:12:54 +00001503 u32 portid, u32 seq, u16 flags, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504{
1505 struct tcmsg *tcm;
1506 struct nlmsghdr *nlh;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001507 unsigned char *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 struct gnet_dump d;
Eric Dumazet20fea082007-11-14 01:44:41 -08001509 const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510
Eric W. Biederman15e47302012-09-07 20:12:54 +00001511 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
David S. Miller02ef22c2012-06-26 21:50:05 -07001512 if (!nlh)
1513 goto out_nlmsg_trim;
1514 tcm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 tcm->tcm_family = AF_UNSPEC;
Eric Dumazet16ebb5e2009-09-02 02:40:09 +00001516 tcm->tcm__pad1 = 0;
1517 tcm->tcm__pad2 = 0;
David S. Miller5ce2d482008-07-08 17:06:30 -07001518 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 tcm->tcm_parent = q->handle;
1520 tcm->tcm_handle = q->handle;
1521 tcm->tcm_info = 0;
David S. Miller1b34ec42012-03-29 05:11:39 -04001522 if (nla_put_string(skb, TCA_KIND, q->ops->id))
1523 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001525 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526
Jarek Poplawski102396a2008-08-29 14:21:52 -07001527 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
1528 qdisc_root_sleeping_lock(q), &d) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001529 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
1531 if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001532 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 if (gnet_stats_finish_copy(&d) < 0)
Patrick McHardy1e904742008-01-22 22:11:17 -08001535 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001537 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 return skb->len;
1539
David S. Miller02ef22c2012-06-26 21:50:05 -07001540out_nlmsg_trim:
Patrick McHardy1e904742008-01-22 22:11:17 -08001541nla_put_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001542 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 return -1;
1544}
1545
Tom Goff7316ae82010-03-19 15:40:13 +00001546static int tclass_notify(struct net *net, struct sk_buff *oskb,
1547 struct nlmsghdr *n, struct Qdisc *q,
1548 unsigned long cl, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549{
1550 struct sk_buff *skb;
Eric W. Biederman15e47302012-09-07 20:12:54 +00001551 u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
1553 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1554 if (!skb)
1555 return -ENOBUFS;
1556
Eric W. Biederman15e47302012-09-07 20:12:54 +00001557 if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 kfree_skb(skb);
1559 return -EINVAL;
1560 }
1561
Eric W. Biederman15e47302012-09-07 20:12:54 +00001562 return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001563 n->nlmsg_flags & NLM_F_ECHO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564}
1565
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001566struct qdisc_dump_args {
1567 struct qdisc_walker w;
1568 struct sk_buff *skb;
1569 struct netlink_callback *cb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570};
1571
1572static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walker *arg)
1573{
1574 struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg;
1575
Eric W. Biederman15e47302012-09-07 20:12:54 +00001576 return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS);
1578}
1579
David S. Miller30723672008-07-18 22:50:15 -07001580static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb,
1581 struct tcmsg *tcm, struct netlink_callback *cb,
1582 int *t_p, int s_t)
1583{
1584 struct qdisc_dump_args arg;
1585
1586 if (tc_qdisc_dump_ignore(q) ||
1587 *t_p < s_t || !q->ops->cl_ops ||
1588 (tcm->tcm_parent &&
1589 TC_H_MAJ(tcm->tcm_parent) != q->handle)) {
1590 (*t_p)++;
1591 return 0;
1592 }
1593 if (*t_p > s_t)
1594 memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
1595 arg.w.fn = qdisc_class_dump;
1596 arg.skb = skb;
1597 arg.cb = cb;
1598 arg.w.stop = 0;
1599 arg.w.skip = cb->args[1];
1600 arg.w.count = 0;
1601 q->ops->cl_ops->walk(q, &arg.w);
1602 cb->args[1] = arg.w.count;
1603 if (arg.w.stop)
1604 return -1;
1605 (*t_p)++;
1606 return 0;
1607}
1608
1609static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb,
1610 struct tcmsg *tcm, struct netlink_callback *cb,
1611 int *t_p, int s_t)
1612{
1613 struct Qdisc *q;
1614
1615 if (!root)
1616 return 0;
1617
1618 if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0)
1619 return -1;
1620
1621 list_for_each_entry(q, &root->list, list) {
1622 if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0)
1623 return -1;
1624 }
1625
1626 return 0;
1627}
1628
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
1630{
David S. Miller02ef22c2012-06-26 21:50:05 -07001631 struct tcmsg *tcm = nlmsg_data(cb->nlh);
David S. Miller30723672008-07-18 22:50:15 -07001632 struct net *net = sock_net(skb->sk);
1633 struct netdev_queue *dev_queue;
1634 struct net_device *dev;
1635 int t, s_t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636
1637 if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
1638 return 0;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001639 dev = dev_get_by_index(net, tcm->tcm_ifindex);
1640 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 return 0;
1642
1643 s_t = cb->args[0];
1644 t = 0;
1645
Patrick McHardyaf356af2009-09-04 06:41:18 +00001646 if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0)
David S. Miller30723672008-07-18 22:50:15 -07001647 goto done;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
Eric Dumazet24824a02010-10-02 06:11:55 +00001649 dev_queue = dev_ingress_queue(dev);
1650 if (dev_queue &&
1651 tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb,
1652 &t, s_t) < 0)
David S. Miller30723672008-07-18 22:50:15 -07001653 goto done;
1654
1655done:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 cb->args[0] = t;
1657
1658 dev_put(dev);
1659 return skb->len;
1660}
1661
1662/* Main classifier routine: scans classifier chain attached
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001663 * to this qdisc, (optionally) tests for protocol and asks
1664 * specific classifiers.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 */
Eric Dumazetdc7f9f62011-07-05 23:25:42 +00001666int tc_classify_compat(struct sk_buff *skb, const struct tcf_proto *tp,
Patrick McHardy73ca4912007-07-15 00:02:31 -07001667 struct tcf_result *res)
1668{
1669 __be16 protocol = skb->protocol;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001670 int err;
Patrick McHardy73ca4912007-07-15 00:02:31 -07001671
1672 for (; tp; tp = tp->next) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001673 if (tp->protocol != protocol &&
1674 tp->protocol != htons(ETH_P_ALL))
1675 continue;
1676 err = tp->classify(skb, tp, res);
1677
1678 if (err >= 0) {
Patrick McHardy73ca4912007-07-15 00:02:31 -07001679#ifdef CONFIG_NET_CLS_ACT
1680 if (err != TC_ACT_RECLASSIFY && skb->tc_verd)
1681 skb->tc_verd = SET_TC_VERD(skb->tc_verd, 0);
1682#endif
1683 return err;
1684 }
1685 }
1686 return -1;
1687}
1688EXPORT_SYMBOL(tc_classify_compat);
1689
Eric Dumazetdc7f9f62011-07-05 23:25:42 +00001690int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp,
Patrick McHardy73ca4912007-07-15 00:02:31 -07001691 struct tcf_result *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692{
1693 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694#ifdef CONFIG_NET_CLS_ACT
Eric Dumazetdc7f9f62011-07-05 23:25:42 +00001695 const struct tcf_proto *otp = tp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696reclassify:
Hagen Paul Pfeifer52bc9742011-02-25 05:45:21 +00001697#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
Patrick McHardy73ca4912007-07-15 00:02:31 -07001699 err = tc_classify_compat(skb, tp, res);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700#ifdef CONFIG_NET_CLS_ACT
Patrick McHardy73ca4912007-07-15 00:02:31 -07001701 if (err == TC_ACT_RECLASSIFY) {
1702 u32 verd = G_TC_VERD(skb->tc_verd);
1703 tp = otp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704
Patrick McHardy73ca4912007-07-15 00:02:31 -07001705 if (verd++ >= MAX_REC_LOOP) {
Joe Perchese87cc472012-05-13 21:56:26 +00001706 net_notice_ratelimited("%s: packet reclassify loop rule prio %u protocol %02x\n",
1707 tp->q->ops->id,
1708 tp->prio & 0xffff,
1709 ntohs(tp->protocol));
Patrick McHardy73ca4912007-07-15 00:02:31 -07001710 return TC_ACT_SHOT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 }
Patrick McHardy73ca4912007-07-15 00:02:31 -07001712 skb->tc_verd = SET_TC_VERD(skb->tc_verd, verd);
1713 goto reclassify;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 }
Patrick McHardy73ca4912007-07-15 00:02:31 -07001715#endif
1716 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717}
Patrick McHardy73ca4912007-07-15 00:02:31 -07001718EXPORT_SYMBOL(tc_classify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719
Patrick McHardya48b5a62007-03-23 11:29:43 -07001720void tcf_destroy(struct tcf_proto *tp)
1721{
1722 tp->ops->destroy(tp);
1723 module_put(tp->ops->owner);
1724 kfree(tp);
1725}
1726
Patrick McHardyff31ab52008-07-01 19:52:38 -07001727void tcf_destroy_chain(struct tcf_proto **fl)
Patrick McHardya48b5a62007-03-23 11:29:43 -07001728{
1729 struct tcf_proto *tp;
1730
Patrick McHardyff31ab52008-07-01 19:52:38 -07001731 while ((tp = *fl) != NULL) {
1732 *fl = tp->next;
Patrick McHardya48b5a62007-03-23 11:29:43 -07001733 tcf_destroy(tp);
1734 }
1735}
1736EXPORT_SYMBOL(tcf_destroy_chain);
1737
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738#ifdef CONFIG_PROC_FS
1739static int psched_show(struct seq_file *seq, void *v)
1740{
Patrick McHardy3c0cfc12007-10-10 16:32:41 -07001741 struct timespec ts;
1742
1743 hrtimer_get_res(CLOCK_MONOTONIC, &ts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 seq_printf(seq, "%08x %08x %08x %08x\n",
Jarek Poplawskica44d6e2009-06-15 02:31:47 -07001745 (u32)NSEC_PER_USEC, (u32)PSCHED_TICKS2NS(1),
Patrick McHardy514bca32007-03-16 12:34:52 -07001746 1000000,
Patrick McHardy3c0cfc12007-10-10 16:32:41 -07001747 (u32)NSEC_PER_SEC/(u32)ktime_to_ns(timespec_to_ktime(ts)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748
1749 return 0;
1750}
1751
1752static int psched_open(struct inode *inode, struct file *file)
1753{
Tom Goff7e5ab152010-03-30 19:44:56 -07001754 return single_open(file, psched_show, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755}
1756
Arjan van de Venda7071d2007-02-12 00:55:36 -08001757static const struct file_operations psched_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 .owner = THIS_MODULE,
1759 .open = psched_open,
1760 .read = seq_read,
1761 .llseek = seq_lseek,
1762 .release = single_release,
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001763};
Tom Goff7316ae82010-03-19 15:40:13 +00001764
1765static int __net_init psched_net_init(struct net *net)
1766{
1767 struct proc_dir_entry *e;
1768
1769 e = proc_net_fops_create(net, "psched", 0, &psched_fops);
1770 if (e == NULL)
1771 return -ENOMEM;
1772
1773 return 0;
1774}
1775
1776static void __net_exit psched_net_exit(struct net *net)
1777{
1778 proc_net_remove(net, "psched");
Tom Goff7316ae82010-03-19 15:40:13 +00001779}
1780#else
1781static int __net_init psched_net_init(struct net *net)
1782{
1783 return 0;
1784}
1785
1786static void __net_exit psched_net_exit(struct net *net)
1787{
1788}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789#endif
1790
Tom Goff7316ae82010-03-19 15:40:13 +00001791static struct pernet_operations psched_net_ops = {
1792 .init = psched_net_init,
1793 .exit = psched_net_exit,
1794};
1795
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796static int __init pktsched_init(void)
1797{
Tom Goff7316ae82010-03-19 15:40:13 +00001798 int err;
1799
1800 err = register_pernet_subsys(&psched_net_ops);
1801 if (err) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001802 pr_err("pktsched_init: "
Tom Goff7316ae82010-03-19 15:40:13 +00001803 "cannot initialize per netns operations\n");
1804 return err;
1805 }
1806
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 register_qdisc(&pfifo_qdisc_ops);
1808 register_qdisc(&bfifo_qdisc_ops);
Hagen Paul Pfeifer57dbb2d2010-01-24 12:30:59 +00001809 register_qdisc(&pfifo_head_drop_qdisc_ops);
David S. Miller6ec1c692009-09-06 01:58:51 -07001810 register_qdisc(&mq_qdisc_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
Greg Rosec7ac8672011-06-10 01:27:09 +00001812 rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, NULL);
1813 rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, NULL);
1814 rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc, NULL);
1815 rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, NULL);
1816 rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, NULL);
1817 rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass, NULL);
Thomas Grafbe577dd2007-03-22 11:55:50 -07001818
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 return 0;
1820}
1821
1822subsys_initcall(pktsched_init);