Blame view

net/sched/sch_api.c 47.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * net/sched/sch_api.c	Packet scheduler API.
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   * Fixes:
   *
   * Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired.
   * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
   * Jamal Hadi Salim <hadi@nortelnetworks.com>: 990601: ingress support
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/skbuff.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  #include <linux/init.h>
  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
  #include <linux/kmod.h>
  #include <linux/list.h>
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
28
  #include <linux/hrtimer.h>
25bfcd5a7   Jarek Poplawski   pkt_sched: Add lo...
29
  #include <linux/lockdep.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
30
  #include <linux/slab.h>
59cc1f61f   Jiri Kosina   net: sched: conve...
31
  #include <linux/hashtable.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
33
  #include <net/net_namespace.h>
b854272b3   Denis V. Lunev   [NET]: Modify all...
34
  #include <net/sock.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
35
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #include <net/pkt_sched.h>
07d79fc7d   Cong Wang   net_sched: add re...
37
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /*
  
     Short review.
     -------------
  
     This file consists of two interrelated parts:
  
     1. queueing disciplines manager frontend.
     2. traffic classes manager frontend.
  
     Generally, queueing discipline ("qdisc") is a black box,
     which is able to enqueue packets and to dequeue them (when
     device is ready to send something) in order and at times
     determined by algorithm hidden in it.
  
     qdisc's are divided to two categories:
     - "queues", which have no internal structure visible from outside.
     - "schedulers", which split all the packets to "traffic classes",
       using "packet classifiers" (look at cls_api.c)
  
     In turn, classes may have child qdiscs (as rule, queues)
     attached to them etc. etc. etc.
  
     The goal of the routines in this file is to translate
     information supplied by user in the form of handles
     to more intelligible for kernel form, to make some sanity
     checks and part of work, which is common to all qdiscs
     and to provide rtnetlink notifications.
  
     All real intelligent work is done inside qdisc modules.
  
  
  
     Every discipline has two major routines: enqueue and dequeue.
  
     ---dequeue
  
     dequeue usually returns a skb to send. It is allowed to return NULL,
     but it does not mean that queue is empty, it just means that
     discipline does not want to send anything this time.
     Queue is really empty if q->q.qlen == 0.
     For complicated disciplines with multiple queues q->q is not
     real packet queue, but however q->q.qlen must be valid.
  
     ---enqueue
  
     enqueue returns 0, if packet was enqueued successfully.
     If packet (this one or another one) was dropped, it returns
     not zero error code.
     NET_XMIT_DROP 	- this packet dropped
       Expected action: do not backoff, but wait until queue will clear.
     NET_XMIT_CN	 	- probably this packet enqueued, but another one dropped.
       Expected action: backoff or ignore
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
  
     Auxiliary routines:
99c0db267   Jarek Poplawski   pkt_sched: sch_ge...
94
95
96
     ---peek
  
     like dequeue but without removing a packet from the queue
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
     ---reset
  
     returns qdisc to initial state: purge all buffers, clear all
     timers, counters (except for statistics) etc.
  
     ---init
  
     initializes newly created qdisc.
  
     ---destroy
  
     destroys resources allocated by init and during lifetime of qdisc.
  
     ---change
  
     changes qdisc parameters.
   */
  
  /* Protects list of registered TC modules. It is pure SMP lock. */
  static DEFINE_RWLOCK(qdisc_mod_lock);
  
  
  /************************************************
   *	Queueing disciplines manipulation.	*
   ************************************************/
  
  
  /* The list of all installed queueing disciplines. */
  
  static struct Qdisc_ops *qdisc_base;
21eb21898   Zhi Yong Wu   net, sch: fix the...
127
  /* Register/unregister queueing discipline */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
134
135
136
137
138
139
140
  
  int register_qdisc(struct Qdisc_ops *qops)
  {
  	struct Qdisc_ops *q, **qp;
  	int rc = -EEXIST;
  
  	write_lock(&qdisc_mod_lock);
  	for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
  		if (!strcmp(qops->id, q->id))
  			goto out;
  
  	if (qops->enqueue == NULL)
  		qops->enqueue = noop_qdisc_ops.enqueue;
99c0db267   Jarek Poplawski   pkt_sched: sch_ge...
141
  	if (qops->peek == NULL) {
68fd26b59   Jarek Poplawski   pkt_sched: Add so...
142
  		if (qops->dequeue == NULL)
99c0db267   Jarek Poplawski   pkt_sched: sch_ge...
143
  			qops->peek = noop_qdisc_ops.peek;
68fd26b59   Jarek Poplawski   pkt_sched: Add so...
144
145
  		else
  			goto out_einval;
99c0db267   Jarek Poplawski   pkt_sched: sch_ge...
146
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
  	if (qops->dequeue == NULL)
  		qops->dequeue = noop_qdisc_ops.dequeue;
68fd26b59   Jarek Poplawski   pkt_sched: Add so...
149
150
  	if (qops->cl_ops) {
  		const struct Qdisc_class_ops *cops = qops->cl_ops;
143976ce9   WANG Cong   net_sched: remove...
151
  		if (!(cops->find && cops->walk && cops->leaf))
68fd26b59   Jarek Poplawski   pkt_sched: Add so...
152
  			goto out_einval;
6529eaba3   Jiri Pirko   net: sched: intro...
153
  		if (cops->tcf_block && !(cops->bind_tcf && cops->unbind_tcf))
68fd26b59   Jarek Poplawski   pkt_sched: Add so...
154
155
  			goto out_einval;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
  	qops->next = NULL;
  	*qp = qops;
  	rc = 0;
  out:
  	write_unlock(&qdisc_mod_lock);
  	return rc;
68fd26b59   Jarek Poplawski   pkt_sched: Add so...
162
163
164
165
  
  out_einval:
  	rc = -EINVAL;
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
167
  EXPORT_SYMBOL(register_qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
174
  
  int unregister_qdisc(struct Qdisc_ops *qops)
  {
  	struct Qdisc_ops *q, **qp;
  	int err = -ENOENT;
  
  	write_lock(&qdisc_mod_lock);
cc7ec456f   Eric Dumazet   net_sched: cleanups
175
  	for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
182
183
184
185
  		if (q == qops)
  			break;
  	if (q) {
  		*qp = q->next;
  		q->next = NULL;
  		err = 0;
  	}
  	write_unlock(&qdisc_mod_lock);
  	return err;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
186
  EXPORT_SYMBOL(unregister_qdisc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187

6da7c8fcb   stephen hemminger   qdisc: allow sett...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  /* Get default qdisc if not otherwise specified */
  void qdisc_get_default(char *name, size_t len)
  {
  	read_lock(&qdisc_mod_lock);
  	strlcpy(name, default_qdisc_ops->id, len);
  	read_unlock(&qdisc_mod_lock);
  }
  
  static struct Qdisc_ops *qdisc_lookup_default(const char *name)
  {
  	struct Qdisc_ops *q = NULL;
  
  	for (q = qdisc_base; q; q = q->next) {
  		if (!strcmp(name, q->id)) {
  			if (!try_module_get(q->owner))
  				q = NULL;
  			break;
  		}
  	}
  
  	return q;
  }
  
  /* Set new default qdisc to use */
  int qdisc_set_default(const char *name)
  {
  	const struct Qdisc_ops *ops;
  
  	if (!capable(CAP_NET_ADMIN))
  		return -EPERM;
  
  	write_lock(&qdisc_mod_lock);
  	ops = qdisc_lookup_default(name);
  	if (!ops) {
  		/* Not found, drop lock and try to load module */
  		write_unlock(&qdisc_mod_lock);
  		request_module("sch_%s", name);
  		write_lock(&qdisc_mod_lock);
  
  		ops = qdisc_lookup_default(name);
  	}
  
  	if (ops) {
  		/* Set new default */
  		module_put(default_qdisc_ops->owner);
  		default_qdisc_ops = ops;
  	}
  	write_unlock(&qdisc_mod_lock);
  
  	return ops ? 0 : -ENOENT;
  }
8ea3e4391   stephen hemminger   Subject: net: all...
239
240
241
242
243
244
245
246
  #ifdef CONFIG_NET_SCH_DEFAULT
  /* Set default value from kernel config */
  static int __init sch_default_qdisc(void)
  {
  	return qdisc_set_default(CONFIG_DEFAULT_NET_SCH);
  }
  late_initcall(sch_default_qdisc);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  /* We know handle. Find qdisc among all qdisc's attached to device
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
248
249
   * (root qdisc, all its children, children of children etc.)
   * Note: caller either uses rtnl or rcu_read_lock()
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
   */
6113b748f   Hannes Eder   pkt_sched: fix sp...
251
  static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
8123b421e   David S. Miller   pkt_sched: Fix in...
252
253
  {
  	struct Qdisc *q;
69012ae42   Jiri Kosina   net: sched: fix h...
254
255
  	if (!qdisc_dev(root))
  		return (root->handle == handle ? root : NULL);
8123b421e   David S. Miller   pkt_sched: Fix in...
256
257
258
  	if (!(root->flags & TCQ_F_BUILTIN) &&
  	    root->handle == handle)
  		return root;
59cc1f61f   Jiri Kosina   net: sched: conve...
259
  	hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle) {
8123b421e   David S. Miller   pkt_sched: Fix in...
260
261
262
263
264
  		if (q->handle == handle)
  			return q;
  	}
  	return NULL;
  }
49b499718   Jiri Kosina   net: sched: make ...
265
  void qdisc_hash_add(struct Qdisc *q, bool invisible)
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
266
  {
37314363c   Eric Dumazet   pkt_sched: move t...
267
  	if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
268
  		ASSERT_RTNL();
59cc1f61f   Jiri Kosina   net: sched: conve...
269
  		hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle);
49b499718   Jiri Kosina   net: sched: make ...
270
271
  		if (invisible)
  			q->flags |= TCQ_F_INVISIBLE;
37314363c   Eric Dumazet   pkt_sched: move t...
272
  	}
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
273
  }
59cc1f61f   Jiri Kosina   net: sched: conve...
274
  EXPORT_SYMBOL(qdisc_hash_add);
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
275

59cc1f61f   Jiri Kosina   net: sched: conve...
276
  void qdisc_hash_del(struct Qdisc *q)
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
277
  {
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
278
279
  	if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
  		ASSERT_RTNL();
59cc1f61f   Jiri Kosina   net: sched: conve...
280
  		hash_del_rcu(&q->hash);
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
281
  	}
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
282
  }
59cc1f61f   Jiri Kosina   net: sched: conve...
283
  EXPORT_SYMBOL(qdisc_hash_del);
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
284

ead81cc5f   David S. Miller   netdevice: Move q...
285
  struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  {
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
287
  	struct Qdisc *q;
50317fce2   Cong Wang   net_sched: avoid ...
288
289
  	if (!handle)
  		return NULL;
af356afa0   Patrick McHardy   net_sched: reintr...
290
291
292
  	q = qdisc_match_from_root(dev->qdisc, handle);
  	if (q)
  		goto out;
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
293

24824a09e   Eric Dumazet   net: dynamic ingr...
294
295
296
297
  	if (dev_ingress_queue(dev))
  		q = qdisc_match_from_root(
  			dev_ingress_queue(dev)->qdisc_sleeping,
  			handle);
f6486d40b   Jarek Poplawski   pkt_sched: sch_ap...
298
  out:
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
299
  	return q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
305
  }
  
  static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
  {
  	unsigned long cl;
  	struct Qdisc *leaf;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
306
  	const struct Qdisc_class_ops *cops = p->ops->cl_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
  
  	if (cops == NULL)
  		return NULL;
143976ce9   WANG Cong   net_sched: remove...
310
  	cl = cops->find(p, classid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
  
  	if (cl == 0)
  		return NULL;
  	leaf = cops->leaf(p, cl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
  	return leaf;
  }
  
  /* Find queueing discipline by name */
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
319
  static struct Qdisc_ops *qdisc_lookup_ops(struct nlattr *kind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
325
  {
  	struct Qdisc_ops *q = NULL;
  
  	if (kind) {
  		read_lock(&qdisc_mod_lock);
  		for (q = qdisc_base; q; q = q->next) {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
326
  			if (nla_strcmp(kind, q->id) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
333
334
335
  				if (!try_module_get(q->owner))
  					q = NULL;
  				break;
  			}
  		}
  		read_unlock(&qdisc_mod_lock);
  	}
  	return q;
  }
8a8e3d84b   Jesper Dangaard Brouer   net_sched: restor...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  /* The linklayer setting were not transferred from iproute2, in older
   * versions, and the rate tables lookup systems have been dropped in
   * the kernel. To keep backward compatible with older iproute2 tc
   * utils, we detect the linklayer setting by detecting if the rate
   * table were modified.
   *
   * For linklayer ATM table entries, the rate table will be aligned to
   * 48 bytes, thus some table entries will contain the same value.  The
   * mpu (min packet unit) is also encoded into the old rate table, thus
   * starting from the mpu, we find low and high table entries for
   * mapping this cell.  If these entries contain the same value, when
   * the rate tables have been modified for linklayer ATM.
   *
   * This is done by rounding mpu to the nearest 48 bytes cell/entry,
   * and then roundup to the next cell, calc the table entry one below,
   * and compare.
   */
  static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab)
  {
  	int low       = roundup(r->mpu, 48);
  	int high      = roundup(low+1, 48);
  	int cell_low  = low >> r->cell_log;
  	int cell_high = (high >> r->cell_log) - 1;
  
  	/* rtab is too inaccurate at rates > 100Mbit/s */
  	if ((r->rate > (100000000/8)) || (rtab[0] == 0)) {
  		pr_debug("TC linklayer: Giving up ATM detection
  ");
  		return TC_LINKLAYER_ETHERNET;
  	}
  
  	if ((cell_high > cell_low) && (cell_high < 256)
  	    && (rtab[cell_low] == rtab[cell_high])) {
  		pr_debug("TC linklayer: Detected ATM, low(%d)=high(%d)=%u
  ",
  			 cell_low, cell_high, rtab[cell_high]);
  		return TC_LINKLAYER_ATM;
  	}
  	return TC_LINKLAYER_ETHERNET;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  static struct qdisc_rate_table *qdisc_rtab_list;
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
377
378
  struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
  					struct nlattr *tab)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
  {
  	struct qdisc_rate_table *rtab;
40edeff6e   Eric Dumazet   net_sched: qdisc_...
381
382
383
  	if (tab == NULL || r->rate == 0 || r->cell_log == 0 ||
  	    nla_len(tab) != TC_RTAB_SIZE)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  	for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
40edeff6e   Eric Dumazet   net_sched: qdisc_...
385
386
  		if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) &&
  		    !memcmp(&rtab->data, nla_data(tab), 1024)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
  			rtab->refcnt++;
  			return rtab;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
  	rtab = kmalloc(sizeof(*rtab), GFP_KERNEL);
  	if (rtab) {
  		rtab->rate = *r;
  		rtab->refcnt = 1;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
395
  		memcpy(rtab->data, nla_data(tab), 1024);
8a8e3d84b   Jesper Dangaard Brouer   net_sched: restor...
396
397
  		if (r->linklayer == TC_LINKLAYER_UNAWARE)
  			r->linklayer = __detect_linklayer(r, rtab->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
401
402
  		rtab->next = qdisc_rtab_list;
  		qdisc_rtab_list = rtab;
  	}
  	return rtab;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
403
  EXPORT_SYMBOL(qdisc_get_rtab);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
407
408
409
410
  
  void qdisc_put_rtab(struct qdisc_rate_table *tab)
  {
  	struct qdisc_rate_table *rtab, **rtabp;
  
  	if (!tab || --tab->refcnt)
  		return;
cc7ec456f   Eric Dumazet   net_sched: cleanups
411
412
413
  	for (rtabp = &qdisc_rtab_list;
  	     (rtab = *rtabp) != NULL;
  	     rtabp = &rtab->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
420
  		if (rtab == tab) {
  			*rtabp = rtab->next;
  			kfree(rtab);
  			return;
  		}
  	}
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
421
  EXPORT_SYMBOL(qdisc_put_rtab);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422

175f9c1bb   Jussi Kivilinna   net_sched: Add si...
423
  static LIST_HEAD(qdisc_stab_list);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
  
  static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = {
  	[TCA_STAB_BASE]	= { .len = sizeof(struct tc_sizespec) },
  	[TCA_STAB_DATA] = { .type = NLA_BINARY },
  };
  
  static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
  {
  	struct nlattr *tb[TCA_STAB_MAX + 1];
  	struct qdisc_size_table *stab;
  	struct tc_sizespec *s;
  	unsigned int tsize = 0;
  	u16 *tab = NULL;
  	int err;
fceb6435e   Johannes Berg   netlink: pass ext...
438
  	err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy, NULL);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
439
440
441
442
443
444
445
446
447
448
449
450
451
  	if (err < 0)
  		return ERR_PTR(err);
  	if (!tb[TCA_STAB_BASE])
  		return ERR_PTR(-EINVAL);
  
  	s = nla_data(tb[TCA_STAB_BASE]);
  
  	if (s->tsize > 0) {
  		if (!tb[TCA_STAB_DATA])
  			return ERR_PTR(-EINVAL);
  		tab = nla_data(tb[TCA_STAB_DATA]);
  		tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16);
  	}
00093fab9   Dan Carpenter   net/sched: remove...
452
  	if (tsize != s->tsize || (!tab && tsize > 0))
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
453
  		return ERR_PTR(-EINVAL);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
454
455
456
457
458
459
  	list_for_each_entry(stab, &qdisc_stab_list, list) {
  		if (memcmp(&stab->szopts, s, sizeof(*s)))
  			continue;
  		if (tsize > 0 && memcmp(stab->data, tab, tsize * sizeof(u16)))
  			continue;
  		stab->refcnt++;
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
460
461
  		return stab;
  	}
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
462
463
464
465
466
467
468
469
  	stab = kmalloc(sizeof(*stab) + tsize * sizeof(u16), GFP_KERNEL);
  	if (!stab)
  		return ERR_PTR(-ENOMEM);
  
  	stab->refcnt = 1;
  	stab->szopts = *s;
  	if (tsize > 0)
  		memcpy(stab->data, tab, tsize * sizeof(u16));
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
470
  	list_add_tail(&stab->list, &qdisc_stab_list);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
471
472
473
  
  	return stab;
  }
a2da570d6   Eric Dumazet   net_sched: RCU co...
474
475
476
477
  static void stab_kfree_rcu(struct rcu_head *head)
  {
  	kfree(container_of(head, struct qdisc_size_table, rcu));
  }
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
478
479
480
481
  void qdisc_put_stab(struct qdisc_size_table *tab)
  {
  	if (!tab)
  		return;
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
482
483
  	if (--tab->refcnt == 0) {
  		list_del(&tab->list);
a2da570d6   Eric Dumazet   net_sched: RCU co...
484
  		call_rcu_bh(&tab->rcu, stab_kfree_rcu);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
485
  	}
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
486
487
488
489
490
491
492
493
  }
  EXPORT_SYMBOL(qdisc_put_stab);
  
  static int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab)
  {
  	struct nlattr *nest;
  
  	nest = nla_nest_start(skb, TCA_STAB);
3aa4614da   Patrick McHardy   pkt_sched: fix mi...
494
495
  	if (nest == NULL)
  		goto nla_put_failure;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
496
497
  	if (nla_put(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts))
  		goto nla_put_failure;
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
498
499
500
501
502
503
504
  	nla_nest_end(skb, nest);
  
  	return skb->len;
  
  nla_put_failure:
  	return -1;
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
505
506
  void __qdisc_calculate_pkt_len(struct sk_buff *skb,
  			       const struct qdisc_size_table *stab)
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  {
  	int pkt_len, slot;
  
  	pkt_len = skb->len + stab->szopts.overhead;
  	if (unlikely(!stab->szopts.tsize))
  		goto out;
  
  	slot = pkt_len + stab->szopts.cell_align;
  	if (unlikely(slot < 0))
  		slot = 0;
  
  	slot >>= stab->szopts.cell_log;
  	if (likely(slot < stab->szopts.tsize))
  		pkt_len = stab->data[slot];
  	else
  		pkt_len = stab->data[stab->szopts.tsize - 1] *
  				(slot / stab->szopts.tsize) +
  				stab->data[slot % stab->szopts.tsize];
  
  	pkt_len <<= stab->szopts.size_log;
  out:
  	if (unlikely(pkt_len < 1))
  		pkt_len = 1;
  	qdisc_skb_cb(skb)->pkt_len = pkt_len;
  }
a2da570d6   Eric Dumazet   net_sched: RCU co...
532
  EXPORT_SYMBOL(__qdisc_calculate_pkt_len);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
533

6e765a009   Florian Westphal   net_sched: drr: w...
534
  void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc)
b00355db3   Jarek Poplawski   pkt_sched: sch_hf...
535
536
  {
  	if (!(qdisc->flags & TCQ_F_WARN_NONWC)) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
537
538
539
  		pr_warn("%s: %s qdisc %X: is non-work-conserving?
  ",
  			txt, qdisc->ops->id, qdisc->handle >> 16);
b00355db3   Jarek Poplawski   pkt_sched: sch_hf...
540
541
542
543
  		qdisc->flags |= TCQ_F_WARN_NONWC;
  	}
  }
  EXPORT_SYMBOL(qdisc_warn_nonwc);
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
544
545
546
  static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
  {
  	struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog,
2fbd3da38   David S. Miller   pkt_sched: Revert...
547
  						 timer);
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
548

1e203c1a2   John Fastabend   net: sched: suspi...
549
  	rcu_read_lock();
8608db031   David S. Miller   pkt_sched: Never ...
550
  	__netif_schedule(qdisc_root(wd->qdisc));
1e203c1a2   John Fastabend   net: sched: suspi...
551
  	rcu_read_unlock();
1936502d0   Stephen Hemminger   [NET_SCHED] qdisc...
552

4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
553
554
555
556
557
  	return HRTIMER_NORESTART;
  }
  
  void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc)
  {
4a8e320c9   Eric Dumazet   net: sched: use p...
558
  	hrtimer_init(&wd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
2fbd3da38   David S. Miller   pkt_sched: Revert...
559
  	wd->timer.function = qdisc_watchdog;
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
560
561
562
  	wd->qdisc = qdisc;
  }
  EXPORT_SYMBOL(qdisc_watchdog_init);
45f50bed1   Eric Dumazet   net_sched: remove...
563
  void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires)
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
564
  {
2540e0511   Jarek Poplawski   pkt_sched: Fix qd...
565
566
567
  	if (test_bit(__QDISC_STATE_DEACTIVATED,
  		     &qdisc_root_sleeping(wd->qdisc)->state))
  		return;
a9efad8b2   Eric Dumazet   net_sched: avoid ...
568
569
570
571
  	if (wd->last_expires == expires)
  		return;
  
  	wd->last_expires = expires;
46baac38e   Eric Dumazet   pkt_sched: use ns...
572
  	hrtimer_start(&wd->timer,
34c5d292c   Jiri Pirko   sch_api: introduc...
573
  		      ns_to_ktime(expires),
4a8e320c9   Eric Dumazet   net: sched: use p...
574
  		      HRTIMER_MODE_ABS_PINNED);
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
575
  }
34c5d292c   Jiri Pirko   sch_api: introduc...
576
  EXPORT_SYMBOL(qdisc_watchdog_schedule_ns);
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
577
578
579
  
  void qdisc_watchdog_cancel(struct qdisc_watchdog *wd)
  {
2fbd3da38   David S. Miller   pkt_sched: Revert...
580
  	hrtimer_cancel(&wd->timer);
4179477f6   Patrick McHardy   [NET_SCHED]: Add ...
581
582
  }
  EXPORT_SYMBOL(qdisc_watchdog_cancel);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583

a94f779f9   Adrian Bunk   pkt_sched: make q...
584
  static struct hlist_head *qdisc_class_hash_alloc(unsigned int n)
6fe1c7a55   Patrick McHardy   net-sched: add dy...
585
  {
6fe1c7a55   Patrick McHardy   net-sched: add dy...
586
  	struct hlist_head *h;
9695fe6f2   Eric Dumazet   net: sched: use k...
587
  	unsigned int i;
6fe1c7a55   Patrick McHardy   net-sched: add dy...
588

9695fe6f2   Eric Dumazet   net: sched: use k...
589
  	h = kvmalloc_array(n, sizeof(struct hlist_head), GFP_KERNEL);
6fe1c7a55   Patrick McHardy   net-sched: add dy...
590
591
592
593
594
595
596
  
  	if (h != NULL) {
  		for (i = 0; i < n; i++)
  			INIT_HLIST_HEAD(&h[i]);
  	}
  	return h;
  }
6fe1c7a55   Patrick McHardy   net-sched: add dy...
597
598
599
  void qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash)
  {
  	struct Qdisc_class_common *cl;
b67bfe0d4   Sasha Levin   hlist: drop the n...
600
  	struct hlist_node *next;
6fe1c7a55   Patrick McHardy   net-sched: add dy...
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  	struct hlist_head *nhash, *ohash;
  	unsigned int nsize, nmask, osize;
  	unsigned int i, h;
  
  	/* Rehash when load factor exceeds 0.75 */
  	if (clhash->hashelems * 4 <= clhash->hashsize * 3)
  		return;
  	nsize = clhash->hashsize * 2;
  	nmask = nsize - 1;
  	nhash = qdisc_class_hash_alloc(nsize);
  	if (nhash == NULL)
  		return;
  
  	ohash = clhash->hash;
  	osize = clhash->hashsize;
  
  	sch_tree_lock(sch);
  	for (i = 0; i < osize; i++) {
b67bfe0d4   Sasha Levin   hlist: drop the n...
619
  		hlist_for_each_entry_safe(cl, next, &ohash[i], hnode) {
6fe1c7a55   Patrick McHardy   net-sched: add dy...
620
621
622
623
624
625
626
627
  			h = qdisc_class_hash(cl->classid, nmask);
  			hlist_add_head(&cl->hnode, &nhash[h]);
  		}
  	}
  	clhash->hash     = nhash;
  	clhash->hashsize = nsize;
  	clhash->hashmask = nmask;
  	sch_tree_unlock(sch);
9695fe6f2   Eric Dumazet   net: sched: use k...
628
  	kvfree(ohash);
6fe1c7a55   Patrick McHardy   net-sched: add dy...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
  }
  EXPORT_SYMBOL(qdisc_class_hash_grow);
  
  int qdisc_class_hash_init(struct Qdisc_class_hash *clhash)
  {
  	unsigned int size = 4;
  
  	clhash->hash = qdisc_class_hash_alloc(size);
  	if (clhash->hash == NULL)
  		return -ENOMEM;
  	clhash->hashsize  = size;
  	clhash->hashmask  = size - 1;
  	clhash->hashelems = 0;
  	return 0;
  }
  EXPORT_SYMBOL(qdisc_class_hash_init);
  
  void qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash)
  {
9695fe6f2   Eric Dumazet   net: sched: use k...
648
  	kvfree(clhash->hash);
6fe1c7a55   Patrick McHardy   net-sched: add dy...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
  }
  EXPORT_SYMBOL(qdisc_class_hash_destroy);
  
  void qdisc_class_hash_insert(struct Qdisc_class_hash *clhash,
  			     struct Qdisc_class_common *cl)
  {
  	unsigned int h;
  
  	INIT_HLIST_NODE(&cl->hnode);
  	h = qdisc_class_hash(cl->classid, clhash->hashmask);
  	hlist_add_head(&cl->hnode, &clhash->hash[h]);
  	clhash->hashelems++;
  }
  EXPORT_SYMBOL(qdisc_class_hash_insert);
  
  void qdisc_class_hash_remove(struct Qdisc_class_hash *clhash,
  			     struct Qdisc_class_common *cl)
  {
  	hlist_del(&cl->hnode);
  	clhash->hashelems--;
  }
  EXPORT_SYMBOL(qdisc_class_hash_remove);
fa0f5aa74   Eric Dumazet   net_sched: qdisc_...
671
672
673
  /* Allocate an unique handle from space managed by kernel
   * Possible range is [8000-FFFF]:0000 (0x8000 values)
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
  static u32 qdisc_alloc_handle(struct net_device *dev)
  {
fa0f5aa74   Eric Dumazet   net_sched: qdisc_...
676
  	int i = 0x8000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
681
682
  	static u32 autohandle = TC_H_MAKE(0x80000000U, 0);
  
  	do {
  		autohandle += TC_H_MAKE(0x10000U, 0);
  		if (autohandle == TC_H_MAKE(TC_H_ROOT, 0))
  			autohandle = TC_H_MAKE(0x80000000U, 0);
fa0f5aa74   Eric Dumazet   net_sched: qdisc_...
683
684
685
686
  		if (!qdisc_lookup(dev, autohandle))
  			return autohandle;
  		cond_resched();
  	} while	(--i > 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687

fa0f5aa74   Eric Dumazet   net_sched: qdisc_...
688
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
  }
2ccccf5fb   WANG Cong   net_sched: update...
690
691
  void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n,
  			       unsigned int len)
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
692
  {
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
693
  	const struct Qdisc_class_ops *cops;
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
694
695
  	unsigned long cl;
  	u32 parentid;
959466588   Konstantin Khlebnikov   net_sched: call q...
696
  	bool notify;
2c8c8e6f9   Eric Dumazet   net_sched: increm...
697
  	int drops;
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
698

2ccccf5fb   WANG Cong   net_sched: update...
699
  	if (n == 0 && len == 0)
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
700
  		return;
2c8c8e6f9   Eric Dumazet   net_sched: increm...
701
  	drops = max_t(int, n, 0);
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
702
  	rcu_read_lock();
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
703
  	while ((parentid = sch->parent)) {
066a3b5b2   Jarek Poplawski   [NET_SCHED] sch_a...
704
  		if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS))
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
705
  			break;
066a3b5b2   Jarek Poplawski   [NET_SCHED] sch_a...
706

4eaf3b84f   Eric Dumazet   net_sched: fix qd...
707
708
  		if (sch->flags & TCQ_F_NOPARENT)
  			break;
959466588   Konstantin Khlebnikov   net_sched: call q...
709
710
711
712
713
714
715
  		/* Notify parent qdisc only if child qdisc becomes empty.
  		 *
  		 * If child was empty even before update then backlog
  		 * counter is screwed and we skip notification because
  		 * parent class is already passive.
  		 */
  		notify = !sch->q.qlen && !WARN_ON_ONCE(!n);
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
716
  		/* TODO: perform the search on a per txq basis */
5ce2d488f   David S. Miller   pkt_sched: Remove...
717
  		sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid));
ffc8fefaf   Patrick McHardy   [NET]: Fix sch_ap...
718
  		if (sch == NULL) {
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
719
720
  			WARN_ON_ONCE(parentid != TC_H_ROOT);
  			break;
ffc8fefaf   Patrick McHardy   [NET]: Fix sch_ap...
721
  		}
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
722
  		cops = sch->ops->cl_ops;
959466588   Konstantin Khlebnikov   net_sched: call q...
723
  		if (notify && cops->qlen_notify) {
143976ce9   WANG Cong   net_sched: remove...
724
  			cl = cops->find(sch, parentid);
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
725
  			cops->qlen_notify(sch, cl);
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
726
727
  		}
  		sch->q.qlen -= n;
2ccccf5fb   WANG Cong   net_sched: update...
728
  		sch->qstats.backlog -= len;
25331d6ce   John Fastabend   net: sched: imple...
729
  		__qdisc_qstats_drop(sch, drops);
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
730
  	}
4eaf3b84f   Eric Dumazet   net_sched: fix qd...
731
  	rcu_read_unlock();
43effa1e5   Patrick McHardy   [NET_SCHED]: Fix ...
732
  }
2ccccf5fb   WANG Cong   net_sched: update...
733
  EXPORT_SYMBOL(qdisc_tree_reduce_backlog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734

27d7f07c4   WANG Cong   net_sched: get ri...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
  static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
  			 u32 portid, u32 seq, u16 flags, int event)
  {
  	struct gnet_stats_basic_cpu __percpu *cpu_bstats = NULL;
  	struct gnet_stats_queue __percpu *cpu_qstats = NULL;
  	struct tcmsg *tcm;
  	struct nlmsghdr  *nlh;
  	unsigned char *b = skb_tail_pointer(skb);
  	struct gnet_dump d;
  	struct qdisc_size_table *stab;
  	__u32 qlen;
  
  	cond_resched();
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
  	if (!nlh)
  		goto out_nlmsg_trim;
  	tcm = nlmsg_data(nlh);
  	tcm->tcm_family = AF_UNSPEC;
  	tcm->tcm__pad1 = 0;
  	tcm->tcm__pad2 = 0;
  	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
  	tcm->tcm_parent = clid;
  	tcm->tcm_handle = q->handle;
  	tcm->tcm_info = refcount_read(&q->refcnt);
  	if (nla_put_string(skb, TCA_KIND, q->ops->id))
  		goto nla_put_failure;
  	if (q->ops->dump && q->ops->dump(q, skb) < 0)
  		goto nla_put_failure;
  	qlen = q->q.qlen;
  
  	stab = rtnl_dereference(q->stab);
  	if (stab && qdisc_dump_stab(skb, stab) < 0)
  		goto nla_put_failure;
  
  	if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
  					 NULL, &d, TCA_PAD) < 0)
  		goto nla_put_failure;
  
  	if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)
  		goto nla_put_failure;
  
  	if (qdisc_is_percpu_stats(q)) {
  		cpu_bstats = q->cpu_bstats;
  		cpu_qstats = q->cpu_qstats;
  	}
  
  	if (gnet_stats_copy_basic(qdisc_root_sleeping_running(q),
  				  &d, cpu_bstats, &q->bstats) < 0 ||
  	    gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 ||
  	    gnet_stats_copy_queue(&d, cpu_qstats, &q->qstats, qlen) < 0)
  		goto nla_put_failure;
  
  	if (gnet_stats_finish_copy(&d) < 0)
  		goto nla_put_failure;
  
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
  	return skb->len;
  
  out_nlmsg_trim:
  nla_put_failure:
  	nlmsg_trim(skb, b);
  	return -1;
  }
  
  static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible)
  {
  	if (q->flags & TCQ_F_BUILTIN)
  		return true;
  	if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible)
  		return true;
  
  	return false;
  }
  
  static int qdisc_notify(struct net *net, struct sk_buff *oskb,
  			struct nlmsghdr *n, u32 clid,
  			struct Qdisc *old, struct Qdisc *new)
  {
  	struct sk_buff *skb;
  	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
  
  	if (old && !tc_qdisc_dump_ignore(old, false)) {
  		if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq,
  				  0, RTM_DELQDISC) < 0)
  			goto err_out;
  	}
  	if (new && !tc_qdisc_dump_ignore(new, false)) {
  		if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq,
  				  old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
  			goto err_out;
  	}
  
  	if (skb->len)
  		return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
  				      n->nlmsg_flags & NLM_F_ECHO);
  
  err_out:
  	kfree_skb(skb);
  	return -EINVAL;
  }
7316ae88c   Tom Goff   net_sched: make t...
839
840
  static void notify_and_destroy(struct net *net, struct sk_buff *skb,
  			       struct nlmsghdr *n, u32 clid,
99194cff3   David S. Miller   pkt_sched: Add mu...
841
842
843
  			       struct Qdisc *old, struct Qdisc *new)
  {
  	if (new || old)
7316ae88c   Tom Goff   net_sched: make t...
844
  		qdisc_notify(net, skb, n, clid, old, new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845

4d8863a29   David S. Miller   pkt_sched: Don't ...
846
  	if (old)
99194cff3   David S. Miller   pkt_sched: Add mu...
847
  		qdisc_destroy(old);
99194cff3   David S. Miller   pkt_sched: Add mu...
848
849
850
851
852
853
854
855
856
  }
  
  /* Graft qdisc "new" to class "classid" of qdisc "parent" or
   * to device "dev".
   *
   * When appropriate send a netlink notification using 'skb'
   * and "n".
   *
   * On success, destroy old qdisc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
857
858
859
   */
  
  static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
99194cff3   David S. Miller   pkt_sched: Add mu...
860
861
  		       struct sk_buff *skb, struct nlmsghdr *n, u32 classid,
  		       struct Qdisc *new, struct Qdisc *old)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  {
99194cff3   David S. Miller   pkt_sched: Add mu...
863
  	struct Qdisc *q = old;
7316ae88c   Tom Goff   net_sched: make t...
864
  	struct net *net = dev_net(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866

10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
867
  	if (parent == NULL) {
99194cff3   David S. Miller   pkt_sched: Add mu...
868
869
870
871
  		unsigned int i, num_q, ingress;
  
  		ingress = 0;
  		num_q = dev->num_tx_queues;
8d50b53d6   David S. Miller   pkt_sched: Fix OO...
872
873
  		if ((q && q->flags & TCQ_F_INGRESS) ||
  		    (new && new->flags & TCQ_F_INGRESS)) {
99194cff3   David S. Miller   pkt_sched: Add mu...
874
875
  			num_q = 1;
  			ingress = 1;
24824a09e   Eric Dumazet   net: dynamic ingr...
876
877
  			if (!dev_ingress_queue(dev))
  				return -ENOENT;
99194cff3   David S. Miller   pkt_sched: Add mu...
878
879
880
881
  		}
  
  		if (dev->flags & IFF_UP)
  			dev_deactivate(dev);
86e363dc3   WANG Cong   net_sched: invoke...
882
883
  		if (new && new->ops->attach)
  			goto skip;
6ec1c69a8   David S. Miller   net_sched: add cl...
884

99194cff3   David S. Miller   pkt_sched: Add mu...
885
  		for (i = 0; i < num_q; i++) {
24824a09e   Eric Dumazet   net: dynamic ingr...
886
  			struct netdev_queue *dev_queue = dev_ingress_queue(dev);
99194cff3   David S. Miller   pkt_sched: Add mu...
887
888
889
  
  			if (!ingress)
  				dev_queue = netdev_get_tx_queue(dev, i);
8d50b53d6   David S. Miller   pkt_sched: Fix OO...
890
891
  			old = dev_graft_qdisc(dev_queue, new);
  			if (new && i > 0)
551143d8d   Eric Dumazet   net_sched: fix a ...
892
  				qdisc_refcount_inc(new);
8d50b53d6   David S. Miller   pkt_sched: Fix OO...
893

036d6a673   Jarek Poplawski   pkt_sched: Fix qd...
894
895
  			if (!ingress)
  				qdisc_destroy(old);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
  		}
99194cff3   David S. Miller   pkt_sched: Add mu...
897

86e363dc3   WANG Cong   net_sched: invoke...
898
  skip:
036d6a673   Jarek Poplawski   pkt_sched: Fix qd...
899
  		if (!ingress) {
7316ae88c   Tom Goff   net_sched: make t...
900
901
  			notify_and_destroy(net, skb, n, classid,
  					   dev->qdisc, new);
036d6a673   Jarek Poplawski   pkt_sched: Fix qd...
902
  			if (new && !new->ops->attach)
551143d8d   Eric Dumazet   net_sched: fix a ...
903
  				qdisc_refcount_inc(new);
036d6a673   Jarek Poplawski   pkt_sched: Fix qd...
904
  			dev->qdisc = new ? : &noop_qdisc;
86e363dc3   WANG Cong   net_sched: invoke...
905
906
907
  
  			if (new && new->ops->attach)
  				new->ops->attach(new);
036d6a673   Jarek Poplawski   pkt_sched: Fix qd...
908
  		} else {
7316ae88c   Tom Goff   net_sched: make t...
909
  			notify_and_destroy(net, skb, n, classid, old, new);
036d6a673   Jarek Poplawski   pkt_sched: Fix qd...
910
  		}
af356afa0   Patrick McHardy   net_sched: reintr...
911

99194cff3   David S. Miller   pkt_sched: Add mu...
912
913
  		if (dev->flags & IFF_UP)
  			dev_activate(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
  	} else {
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
915
  		const struct Qdisc_class_ops *cops = parent->ops->cl_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916

c9f1d0389   Patrick McHardy   net_sched: fix cl...
917
918
  		err = -EOPNOTSUPP;
  		if (cops && cops->graft) {
143976ce9   WANG Cong   net_sched: remove...
919
920
921
  			unsigned long cl = cops->find(parent, classid);
  
  			if (cl)
99194cff3   David S. Miller   pkt_sched: Add mu...
922
  				err = cops->graft(parent, cl, new, &old);
143976ce9   WANG Cong   net_sched: remove...
923
  			else
c9f1d0389   Patrick McHardy   net_sched: fix cl...
924
  				err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
  		}
99194cff3   David S. Miller   pkt_sched: Add mu...
926
  		if (!err)
7316ae88c   Tom Goff   net_sched: make t...
927
  			notify_and_destroy(net, skb, n, classid, old, new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
  	}
  	return err;
  }
25bfcd5a7   Jarek Poplawski   pkt_sched: Add lo...
931
932
933
  /* lockdep annotation is needed for ingress; egress gets it only for name */
  static struct lock_class_key qdisc_tx_lock;
  static struct lock_class_key qdisc_rx_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
935
936
937
938
  /*
     Allocate and initialize new qdisc.
  
     Parameters are passed via opt.
   */
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
939
940
941
942
  static struct Qdisc *qdisc_create(struct net_device *dev,
  				  struct netdev_queue *dev_queue,
  				  struct Qdisc *p, u32 parent, u32 handle,
  				  struct nlattr **tca, int *errp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
944
  {
  	int err;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
945
  	struct nlattr *kind = tca[TCA_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
  	struct Qdisc *sch;
  	struct Qdisc_ops *ops;
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
948
  	struct qdisc_size_table *stab;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
  
  	ops = qdisc_lookup_ops(kind);
95a5afca4   Johannes Berg   net: Remove CONFI...
951
  #ifdef CONFIG_MODULES
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
953
  	if (ops == NULL && kind != NULL) {
  		char name[IFNAMSIZ];
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
954
  		if (nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
  			/* We dropped the RTNL semaphore in order to
  			 * perform the module load.  So, even if we
  			 * succeeded in loading the module we have to
  			 * tell the caller to replay the request.  We
  			 * indicate this using -EAGAIN.
  			 * We replay the request because the device may
  			 * go away in the mean time.
  			 */
  			rtnl_unlock();
  			request_module("sch_%s", name);
  			rtnl_lock();
  			ops = qdisc_lookup_ops(kind);
  			if (ops != NULL) {
  				/* We will try again qdisc_lookup_ops,
  				 * so don't keep a reference.
  				 */
  				module_put(ops->owner);
  				err = -EAGAIN;
  				goto err_out;
  			}
  		}
  	}
  #endif
b9e2cc0f0   Jamal Hadi Salim   [PKT_SCHED]: Retu...
978
  	err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
980
  	if (ops == NULL)
  		goto err_out;
5ce2d488f   David S. Miller   pkt_sched: Remove...
981
  	sch = qdisc_alloc(dev_queue, ops);
3d54b82fd   Thomas Graf   [PKT_SCHED]: Clea...
982
983
  	if (IS_ERR(sch)) {
  		err = PTR_ERR(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
  		goto err_out2;
3d54b82fd   Thomas Graf   [PKT_SCHED]: Clea...
985
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986

ffc8fefaf   Patrick McHardy   [NET]: Fix sch_ap...
987
  	sch->parent = parent;
3d54b82fd   Thomas Graf   [PKT_SCHED]: Clea...
988
  	if (handle == TC_H_INGRESS) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  		sch->flags |= TCQ_F_INGRESS;
3d54b82fd   Thomas Graf   [PKT_SCHED]: Clea...
990
  		handle = TC_H_MAKE(TC_H_INGRESS, 0);
25bfcd5a7   Jarek Poplawski   pkt_sched: Add lo...
991
  		lockdep_set_class(qdisc_lock(sch), &qdisc_rx_lock);
fd44de7cc   Patrick McHardy   [NET_SCHED]: ingr...
992
  	} else {
fd44de7cc   Patrick McHardy   [NET_SCHED]: ingr...
993
994
995
996
997
998
  		if (handle == 0) {
  			handle = qdisc_alloc_handle(dev);
  			err = -ENOMEM;
  			if (handle == 0)
  				goto err_out3;
  		}
25bfcd5a7   Jarek Poplawski   pkt_sched: Add lo...
999
  		lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock);
1abbe1394   Eric Dumazet   pkt_sched: avoid ...
1000
  		if (!netif_is_multiqueue(dev))
225734de7   Eric Dumazet   net_sched: make q...
1001
  			sch->flags |= TCQ_F_ONETXQUEUE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
  	}
3d54b82fd   Thomas Graf   [PKT_SCHED]: Clea...
1003
  	sch->handle = handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004

84c46dd86   Jesper Dangaard Brouer   qdisc: catch misc...
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
  	/* This exist to keep backward compatible with a userspace
  	 * loophole, what allowed userspace to get IFF_NO_QUEUE
  	 * facility on older kernels by setting tx_queue_len=0 (prior
  	 * to qdisc init), and then forgot to reinit tx_queue_len
  	 * before again attaching a qdisc.
  	 */
  	if ((dev->priv_flags & IFF_NO_QUEUE) && (dev->tx_queue_len == 0)) {
  		dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
  		netdev_info(dev, "Caught tx_queue_len zero misconfig
  ");
  	}
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1016
  	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {
22e0f8b93   John Fastabend   net: sched: make ...
1017
1018
  		if (qdisc_is_percpu_stats(sch)) {
  			sch->cpu_bstats =
7c1c97d54   Sabrina Dubroca   net: sched: initi...
1019
  				netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu);
22e0f8b93   John Fastabend   net: sched: make ...
1020
1021
  			if (!sch->cpu_bstats)
  				goto err_out4;
b0ab6f927   John Fastabend   net: sched: enabl...
1022
1023
1024
1025
  
  			sch->cpu_qstats = alloc_percpu(struct gnet_stats_queue);
  			if (!sch->cpu_qstats)
  				goto err_out4;
22e0f8b93   John Fastabend   net: sched: make ...
1026
  		}
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1027
1028
1029
1030
  		if (tca[TCA_STAB]) {
  			stab = qdisc_get_stab(tca[TCA_STAB]);
  			if (IS_ERR(stab)) {
  				err = PTR_ERR(stab);
7c64b9f3f   Jarek Poplawski   pkt_sched: Fix qd...
1031
  				goto err_out4;
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1032
  			}
a2da570d6   Eric Dumazet   net_sched: RCU co...
1033
  			rcu_assign_pointer(sch->stab, stab);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1034
  		}
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1035
  		if (tca[TCA_RATE]) {
edb09eb17   Eric Dumazet   net: sched: do no...
1036
  			seqcount_t *running;
f6f9b93f1   Jarek Poplawski   pkt_sched: Fix ge...
1037

23bcf634c   Patrick McHardy   net_sched: fix es...
1038
1039
1040
  			err = -EOPNOTSUPP;
  			if (sch->flags & TCQ_F_MQROOT)
  				goto err_out4;
f6f9b93f1   Jarek Poplawski   pkt_sched: Fix ge...
1041
  			if ((sch->parent != TC_H_ROOT) &&
23bcf634c   Patrick McHardy   net_sched: fix es...
1042
1043
  			    !(sch->flags & TCQ_F_INGRESS) &&
  			    (!p || !(p->flags & TCQ_F_MQROOT)))
edb09eb17   Eric Dumazet   net: sched: do no...
1044
  				running = qdisc_root_sleeping_running(sch);
f6f9b93f1   Jarek Poplawski   pkt_sched: Fix ge...
1045
  			else
edb09eb17   Eric Dumazet   net: sched: do no...
1046
  				running = &sch->running;
f6f9b93f1   Jarek Poplawski   pkt_sched: Fix ge...
1047

22e0f8b93   John Fastabend   net: sched: make ...
1048
1049
1050
  			err = gen_new_estimator(&sch->bstats,
  						sch->cpu_bstats,
  						&sch->rate_est,
edb09eb17   Eric Dumazet   net: sched: do no...
1051
1052
  						NULL,
  						running,
22e0f8b93   John Fastabend   net: sched: make ...
1053
  						tca[TCA_RATE]);
23bcf634c   Patrick McHardy   net_sched: fix es...
1054
1055
  			if (err)
  				goto err_out4;
023e09a76   Thomas Graf   [PKT_SCHED]: Repo...
1056
  		}
f6e0b239a   Jarek Poplawski   pkt_sched: Fix qd...
1057

49b499718   Jiri Kosina   net: sched: make ...
1058
  		qdisc_hash_add(sch, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
  		return sch;
  	}
87b60cfac   Eric Dumazet   net_sched: fix er...
1062
  	/* ops->init() failed, we call ->destroy() like qdisc_create_dflt() */
c1a4872eb   Gao Feng   net: sched: Fix o...
1063
1064
  	if (ops->destroy)
  		ops->destroy(sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
  err_out3:
  	dev_put(dev);
3d54b82fd   Thomas Graf   [PKT_SCHED]: Clea...
1067
  	kfree((char *) sch - sch->padded);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1068
1069
1070
1071
  err_out2:
  	module_put(ops->owner);
  err_out:
  	*errp = err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072
  	return NULL;
23bcf634c   Patrick McHardy   net_sched: fix es...
1073
1074
  
  err_out4:
22e0f8b93   John Fastabend   net: sched: make ...
1075
  	free_percpu(sch->cpu_bstats);
b0ab6f927   John Fastabend   net: sched: enabl...
1076
  	free_percpu(sch->cpu_qstats);
23bcf634c   Patrick McHardy   net_sched: fix es...
1077
1078
1079
1080
  	/*
  	 * Any broken qdiscs that would require a ops->reset() here?
  	 * The qdisc was never in action so it shouldn't be necessary.
  	 */
a2da570d6   Eric Dumazet   net_sched: RCU co...
1081
  	qdisc_put_stab(rtnl_dereference(sch->stab));
23bcf634c   Patrick McHardy   net_sched: fix es...
1082
1083
1084
  	if (ops->destroy)
  		ops->destroy(sch);
  	goto err_out3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
  }
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1086
  static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
  {
a2da570d6   Eric Dumazet   net_sched: RCU co...
1088
  	struct qdisc_size_table *ostab, *stab = NULL;
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1089
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1090

175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1091
  	if (tca[TCA_OPTIONS]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1092
1093
  		if (sch->ops->change == NULL)
  			return -EINVAL;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1094
  		err = sch->ops->change(sch, tca[TCA_OPTIONS]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
  		if (err)
  			return err;
  	}
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1098
1099
1100
1101
1102
1103
  
  	if (tca[TCA_STAB]) {
  		stab = qdisc_get_stab(tca[TCA_STAB]);
  		if (IS_ERR(stab))
  			return PTR_ERR(stab);
  	}
a2da570d6   Eric Dumazet   net_sched: RCU co...
1104
1105
1106
  	ostab = rtnl_dereference(sch->stab);
  	rcu_assign_pointer(sch->stab, stab);
  	qdisc_put_stab(ostab);
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
1107

23bcf634c   Patrick McHardy   net_sched: fix es...
1108
  	if (tca[TCA_RATE]) {
71bcb09a5   Stephen Hemminger   tc: check for err...
1109
1110
  		/* NB: ignores errors from replace_estimator
  		   because change can't be undone. */
23bcf634c   Patrick McHardy   net_sched: fix es...
1111
1112
  		if (sch->flags & TCQ_F_MQROOT)
  			goto out;
22e0f8b93   John Fastabend   net: sched: make ...
1113
1114
1115
  		gen_replace_estimator(&sch->bstats,
  				      sch->cpu_bstats,
  				      &sch->rate_est,
edb09eb17   Eric Dumazet   net: sched: do no...
1116
1117
  				      NULL,
  				      qdisc_root_sleeping_running(sch),
22e0f8b93   John Fastabend   net: sched: make ...
1118
  				      tca[TCA_RATE]);
23bcf634c   Patrick McHardy   net_sched: fix es...
1119
1120
  	}
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
1122
  	return 0;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
1123
1124
  struct check_loop_arg {
  	struct qdisc_walker	w;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1125
1126
1127
  	struct Qdisc		*p;
  	int			depth;
  };
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1128
1129
  static int check_loop_fn(struct Qdisc *q, unsigned long cl,
  			 struct qdisc_walker *w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
  
  static int check_loop(struct Qdisc *q, struct Qdisc *p, int depth)
  {
  	struct check_loop_arg	arg;
  
  	if (q->ops->cl_ops == NULL)
  		return 0;
  
  	arg.w.stop = arg.w.skip = arg.w.count = 0;
  	arg.w.fn = check_loop_fn;
  	arg.depth = depth;
  	arg.p = p;
  	q->ops->cl_ops->walk(q, &arg.w);
  	return arg.w.stop ? -ELOOP : 0;
  }
  
  static int
  check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w)
  {
  	struct Qdisc *leaf;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
1150
  	const struct Qdisc_class_ops *cops = q->ops->cl_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
  	struct check_loop_arg *arg = (struct check_loop_arg *)w;
  
  	leaf = cops->leaf(q, cl);
  	if (leaf) {
  		if (leaf == arg->p || arg->depth > 7)
  			return -ELOOP;
  		return check_loop(leaf, arg->p, arg->depth + 1);
  	}
  	return 0;
  }
  
  /*
   * Delete/get qdisc.
   */
d90574233   David Ahern   net: sched: Add p...
1165
1166
  const struct nla_policy rtm_tca_policy[TCA_MAX + 1] = {
  	[TCA_KIND]		= { .type = NLA_STRING },
d90574233   David Ahern   net: sched: Add p...
1167
1168
1169
1170
1171
1172
  	[TCA_RATE]		= { .type = NLA_BINARY,
  				    .len = sizeof(struct tc_estimator) },
  	[TCA_STAB]		= { .type = NLA_NESTED },
  	[TCA_DUMP_INVISIBLE]	= { .type = NLA_FLAG },
  	[TCA_CHAIN]		= { .type = NLA_U32 },
  };
c21ef3e34   David Ahern   net: rtnetlink: p...
1173
1174
  static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
  			struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1175
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1176
  	struct net *net = sock_net(skb->sk);
02ef22ca4   David S. Miller   pkt_sched: sch_ap...
1177
  	struct tcmsg *tcm = nlmsg_data(n);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1178
  	struct nlattr *tca[TCA_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1179
  	struct net_device *dev;
de179c8c1   Hong zhi guo   netlink: have len...
1180
  	u32 clid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
1183
  	struct Qdisc *q = NULL;
  	struct Qdisc *p = NULL;
  	int err;
4e8bbb819   Stéphane Graber   net: Allow tc cha...
1184
  	if ((n->nlmsg_type != RTM_GETQDISC) &&
5f013c9bc   David S. Miller   Merge git://git.k...
1185
  	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
1186
  		return -EPERM;
d90574233   David Ahern   net: sched: Add p...
1187
1188
  	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy,
  			  extack);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1189
1190
  	if (err < 0)
  		return err;
de179c8c1   Hong zhi guo   netlink: have len...
1191
1192
1193
1194
1195
  	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
  	if (!dev)
  		return -ENODEV;
  
  	clid = tcm->tcm_parent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1196
1197
1198
  	if (clid) {
  		if (clid != TC_H_ROOT) {
  			if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1199
1200
  				p = qdisc_lookup(dev, TC_H_MAJ(clid));
  				if (!p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1201
1202
  					return -ENOENT;
  				q = qdisc_leaf(p, clid);
cc7ec456f   Eric Dumazet   net_sched: cleanups
1203
1204
  			} else if (dev_ingress_queue(dev)) {
  				q = dev_ingress_queue(dev)->qdisc_sleeping;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1205
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1206
  		} else {
af356afa0   Patrick McHardy   net_sched: reintr...
1207
  			q = dev->qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1208
1209
1210
1211
1212
1213
1214
  		}
  		if (!q)
  			return -ENOENT;
  
  		if (tcm->tcm_handle && q->handle != tcm->tcm_handle)
  			return -EINVAL;
  	} else {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1215
1216
  		q = qdisc_lookup(dev, tcm->tcm_handle);
  		if (!q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
1218
  			return -ENOENT;
  	}
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1219
  	if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1220
1221
1222
1223
1224
1225
1226
  		return -EINVAL;
  
  	if (n->nlmsg_type == RTM_DELQDISC) {
  		if (!clid)
  			return -EINVAL;
  		if (q->handle == 0)
  			return -ENOENT;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1227
1228
  		err = qdisc_graft(dev, p, skb, n, clid, NULL, q);
  		if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1229
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1230
  	} else {
7316ae88c   Tom Goff   net_sched: make t...
1231
  		qdisc_notify(net, skb, n, clid, NULL, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1232
1233
1234
1235
1236
  	}
  	return 0;
  }
  
  /*
cc7ec456f   Eric Dumazet   net_sched: cleanups
1237
   * Create/change qdisc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1238
   */
c21ef3e34   David Ahern   net: rtnetlink: p...
1239
1240
  static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
  			   struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1241
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1242
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
  	struct tcmsg *tcm;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1244
  	struct nlattr *tca[TCA_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1245
1246
1247
1248
  	struct net_device *dev;
  	u32 clid;
  	struct Qdisc *q, *p;
  	int err;
5f013c9bc   David S. Miller   Merge git://git.k...
1249
  	if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
1250
  		return -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1251
1252
  replay:
  	/* Reinit, just in case something touches this. */
d90574233   David Ahern   net: sched: Add p...
1253
1254
  	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy,
  			  extack);
de179c8c1   Hong zhi guo   netlink: have len...
1255
1256
  	if (err < 0)
  		return err;
02ef22ca4   David S. Miller   pkt_sched: sch_ap...
1257
  	tcm = nlmsg_data(n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1258
1259
  	clid = tcm->tcm_parent;
  	q = p = NULL;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1260
1261
  	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
  	if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1262
  		return -ENODEV;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1263

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1264
1265
1266
  	if (clid) {
  		if (clid != TC_H_ROOT) {
  			if (clid != TC_H_INGRESS) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1267
1268
  				p = qdisc_lookup(dev, TC_H_MAJ(clid));
  				if (!p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1269
1270
  					return -ENOENT;
  				q = qdisc_leaf(p, clid);
cc7ec456f   Eric Dumazet   net_sched: cleanups
1271
1272
  			} else if (dev_ingress_queue_create(dev)) {
  				q = dev_ingress_queue(dev)->qdisc_sleeping;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
1274
  			}
  		} else {
af356afa0   Patrick McHardy   net_sched: reintr...
1275
  			q = dev->qdisc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1276
1277
1278
1279
1280
1281
1282
1283
  		}
  
  		/* It may be default qdisc, ignore it */
  		if (q && q->handle == 0)
  			q = NULL;
  
  		if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
  			if (tcm->tcm_handle) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1284
  				if (q && !(n->nlmsg_flags & NLM_F_REPLACE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
1286
1287
  					return -EEXIST;
  				if (TC_H_MIN(tcm->tcm_handle))
  					return -EINVAL;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1288
1289
  				q = qdisc_lookup(dev, tcm->tcm_handle);
  				if (!q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
  					goto create_n_graft;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1291
  				if (n->nlmsg_flags & NLM_F_EXCL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1292
  					return -EEXIST;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1293
  				if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
1295
1296
1297
  					return -EINVAL;
  				if (q == p ||
  				    (p && check_loop(q, p, 0)))
  					return -ELOOP;
551143d8d   Eric Dumazet   net_sched: fix a ...
1298
  				qdisc_refcount_inc(q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1299
1300
  				goto graft;
  			} else {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1301
  				if (!q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
  					goto create_n_graft;
  
  				/* This magic test requires explanation.
  				 *
  				 *   We know, that some child q is already
  				 *   attached to this parent and have choice:
  				 *   either to change it or to create/graft new one.
  				 *
  				 *   1. We are allowed to create/graft only
  				 *   if CREATE and REPLACE flags are set.
  				 *
  				 *   2. If EXCL is set, requestor wanted to say,
  				 *   that qdisc tcm_handle is not expected
  				 *   to exist, so that we choose create/graft too.
  				 *
  				 *   3. The last case is when no flags are set.
  				 *   Alas, it is sort of hole in API, we
  				 *   cannot decide what to do unambiguously.
  				 *   For now we select create/graft, if
  				 *   user gave KIND, which does not match existing.
  				 */
cc7ec456f   Eric Dumazet   net_sched: cleanups
1323
1324
1325
  				if ((n->nlmsg_flags & NLM_F_CREATE) &&
  				    (n->nlmsg_flags & NLM_F_REPLACE) &&
  				    ((n->nlmsg_flags & NLM_F_EXCL) ||
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1326
1327
  				     (tca[TCA_KIND] &&
  				      nla_strcmp(tca[TCA_KIND], q->ops->id))))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
  					goto create_n_graft;
  			}
  		}
  	} else {
  		if (!tcm->tcm_handle)
  			return -EINVAL;
  		q = qdisc_lookup(dev, tcm->tcm_handle);
  	}
  
  	/* Change qdisc parameters */
  	if (q == NULL)
  		return -ENOENT;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1340
  	if (n->nlmsg_flags & NLM_F_EXCL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1341
  		return -EEXIST;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1342
  	if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1343
1344
1345
  		return -EINVAL;
  	err = qdisc_change(q, tca);
  	if (err == 0)
7316ae88c   Tom Goff   net_sched: make t...
1346
  		qdisc_notify(net, skb, n, clid, NULL, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1347
1348
1349
  	return err;
  
  create_n_graft:
cc7ec456f   Eric Dumazet   net_sched: cleanups
1350
  	if (!(n->nlmsg_flags & NLM_F_CREATE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
  		return -ENOENT;
24824a09e   Eric Dumazet   net: dynamic ingr...
1352
1353
1354
1355
1356
1357
1358
1359
  	if (clid == TC_H_INGRESS) {
  		if (dev_ingress_queue(dev))
  			q = qdisc_create(dev, dev_ingress_queue(dev), p,
  					 tcm->tcm_parent, tcm->tcm_parent,
  					 tca, &err);
  		else
  			err = -ENOENT;
  	} else {
926e61b7c   Jarek Poplawski   pkt_sched: Fix tx...
1360
  		struct netdev_queue *dev_queue;
6ec1c69a8   David S. Miller   net_sched: add cl...
1361
1362
  
  		if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue)
926e61b7c   Jarek Poplawski   pkt_sched: Fix tx...
1363
1364
1365
1366
1367
  			dev_queue = p->ops->cl_ops->select_queue(p, tcm);
  		else if (p)
  			dev_queue = p->dev_queue;
  		else
  			dev_queue = netdev_get_tx_queue(dev, 0);
6ec1c69a8   David S. Miller   net_sched: add cl...
1368

926e61b7c   Jarek Poplawski   pkt_sched: Fix tx...
1369
  		q = qdisc_create(dev, dev_queue, p,
bb949fbd1   David S. Miller   netdev: Create ne...
1370
  				 tcm->tcm_parent, tcm->tcm_handle,
ffc8fefaf   Patrick McHardy   [NET]: Fix sch_ap...
1371
  				 tca, &err);
6ec1c69a8   David S. Miller   net_sched: add cl...
1372
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
1374
1375
1376
1377
1378
1379
  	if (q == NULL) {
  		if (err == -EAGAIN)
  			goto replay;
  		return err;
  	}
  
  graft:
e5befbd95   Ilpo Järvinen   pkt_sched: remove...
1380
1381
1382
1383
1384
  	err = qdisc_graft(dev, p, skb, n, clid, q, NULL);
  	if (err) {
  		if (q)
  			qdisc_destroy(q);
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1385
  	}
e5befbd95   Ilpo Järvinen   pkt_sched: remove...
1386

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1387
1388
  	return 0;
  }
307236730   David S. Miller   pkt_sched: Manage...
1389
1390
  static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
  			      struct netlink_callback *cb,
49b499718   Jiri Kosina   net: sched: make ...
1391
1392
  			      int *q_idx_p, int s_q_idx, bool recur,
  			      bool dump_invisible)
307236730   David S. Miller   pkt_sched: Manage...
1393
1394
1395
  {
  	int ret = 0, q_idx = *q_idx_p;
  	struct Qdisc *q;
59cc1f61f   Jiri Kosina   net: sched: conve...
1396
  	int b;
307236730   David S. Miller   pkt_sched: Manage...
1397
1398
1399
1400
1401
1402
1403
1404
  
  	if (!root)
  		return 0;
  
  	q = root;
  	if (q_idx < s_q_idx) {
  		q_idx++;
  	} else {
49b499718   Jiri Kosina   net: sched: make ...
1405
  		if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
15e473046   Eric W. Biederman   netlink: Rename p...
1406
  		    tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1407
1408
  				  cb->nlh->nlmsg_seq, NLM_F_MULTI,
  				  RTM_NEWQDISC) <= 0)
307236730   David S. Miller   pkt_sched: Manage...
1409
1410
1411
  			goto done;
  		q_idx++;
  	}
69012ae42   Jiri Kosina   net: sched: fix h...
1412

ea3274695   Jiri Kosina   net: sched: avoid...
1413
1414
1415
1416
1417
1418
1419
  	/* If dumping singletons, there is no qdisc_dev(root) and the singleton
  	 * itself has already been dumped.
  	 *
  	 * If we've already dumped the top-level (ingress) qdisc above and the global
  	 * qdisc hashtable, we don't want to hit it again
  	 */
  	if (!qdisc_dev(root) || !recur)
69012ae42   Jiri Kosina   net: sched: fix h...
1420
  		goto out;
59cc1f61f   Jiri Kosina   net: sched: conve...
1421
  	hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) {
307236730   David S. Miller   pkt_sched: Manage...
1422
1423
1424
1425
  		if (q_idx < s_q_idx) {
  			q_idx++;
  			continue;
  		}
49b499718   Jiri Kosina   net: sched: make ...
1426
  		if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
15e473046   Eric W. Biederman   netlink: Rename p...
1427
  		    tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1428
1429
  				  cb->nlh->nlmsg_seq, NLM_F_MULTI,
  				  RTM_NEWQDISC) <= 0)
307236730   David S. Miller   pkt_sched: Manage...
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
  			goto done;
  		q_idx++;
  	}
  
  out:
  	*q_idx_p = q_idx;
  	return ret;
  done:
  	ret = -1;
  	goto out;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1441
1442
  static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1443
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1444
1445
1446
  	int idx, q_idx;
  	int s_idx, s_q_idx;
  	struct net_device *dev;
49b499718   Jiri Kosina   net: sched: make ...
1447
1448
1449
1450
  	const struct nlmsghdr *nlh = cb->nlh;
  	struct tcmsg *tcm = nlmsg_data(nlh);
  	struct nlattr *tca[TCA_MAX + 1];
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1451
1452
1453
  
  	s_idx = cb->args[0];
  	s_q_idx = q_idx = cb->args[1];
f1e9016da   stephen hemminger   net: use rcu for ...
1454

7562f876c   Pavel Emelianov   [NET]: Rework dev...
1455
  	idx = 0;
15dc36ebb   Eric Dumazet   pkt_sched: do not...
1456
  	ASSERT_RTNL();
49b499718   Jiri Kosina   net: sched: make ...
1457

d90574233   David Ahern   net: sched: Add p...
1458
1459
  	err = nlmsg_parse(nlh, sizeof(*tcm), tca, TCA_MAX,
  			  rtm_tca_policy, NULL);
49b499718   Jiri Kosina   net: sched: make ...
1460
1461
  	if (err < 0)
  		return err;
15dc36ebb   Eric Dumazet   pkt_sched: do not...
1462
  	for_each_netdev(net, dev) {
307236730   David S. Miller   pkt_sched: Manage...
1463
  		struct netdev_queue *dev_queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1464
  		if (idx < s_idx)
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1465
  			goto cont;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1466
1467
  		if (idx > s_idx)
  			s_q_idx = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1468
  		q_idx = 0;
307236730   David S. Miller   pkt_sched: Manage...
1469

5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1470
  		if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx,
49b499718   Jiri Kosina   net: sched: make ...
1471
  				       true, tca[TCA_DUMP_INVISIBLE]) < 0)
307236730   David S. Miller   pkt_sched: Manage...
1472
  			goto done;
24824a09e   Eric Dumazet   net: dynamic ingr...
1473
1474
1475
  		dev_queue = dev_ingress_queue(dev);
  		if (dev_queue &&
  		    tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb,
49b499718   Jiri Kosina   net: sched: make ...
1476
1477
  				       &q_idx, s_q_idx, false,
  				       tca[TCA_DUMP_INVISIBLE]) < 0)
307236730   David S. Miller   pkt_sched: Manage...
1478
  			goto done;
7562f876c   Pavel Emelianov   [NET]: Rework dev...
1479
1480
  cont:
  		idx++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1481
1482
1483
  	}
  
  done:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
  	cb->args[0] = idx;
  	cb->args[1] = q_idx;
  
  	return skb->len;
  }
  
  
  
  /************************************************
   *	Traffic classes manipulation.		*
   ************************************************/
27d7f07c4   WANG Cong   net_sched: get ri...
1495
1496
1497
1498
1499
1500
1501
1502
1503
  static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
  			  unsigned long cl,
  			  u32 portid, u32 seq, u16 flags, int event)
  {
  	struct tcmsg *tcm;
  	struct nlmsghdr  *nlh;
  	unsigned char *b = skb_tail_pointer(skb);
  	struct gnet_dump d;
  	const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1504

27d7f07c4   WANG Cong   net_sched: get ri...
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
  	cond_resched();
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
  	if (!nlh)
  		goto out_nlmsg_trim;
  	tcm = nlmsg_data(nlh);
  	tcm->tcm_family = AF_UNSPEC;
  	tcm->tcm__pad1 = 0;
  	tcm->tcm__pad2 = 0;
  	tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
  	tcm->tcm_parent = q->handle;
  	tcm->tcm_handle = q->handle;
  	tcm->tcm_info = 0;
  	if (nla_put_string(skb, TCA_KIND, q->ops->id))
  		goto nla_put_failure;
  	if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0)
  		goto nla_put_failure;
  
  	if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
  					 NULL, &d, TCA_PAD) < 0)
  		goto nla_put_failure;
  
  	if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0)
  		goto nla_put_failure;
  
  	if (gnet_stats_finish_copy(&d) < 0)
  		goto nla_put_failure;
  
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
  	return skb->len;
  
  out_nlmsg_trim:
  nla_put_failure:
  	nlmsg_trim(skb, b);
  	return -1;
  }
  
  static int tclass_notify(struct net *net, struct sk_buff *oskb,
  			 struct nlmsghdr *n, struct Qdisc *q,
  			 unsigned long cl, int event)
  {
  	struct sk_buff *skb;
  	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
  
  	if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) {
  		kfree_skb(skb);
  		return -EINVAL;
  	}
  
  	return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
  			      n->nlmsg_flags & NLM_F_ECHO);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1560

14546ba1e   WANG Cong   net_sched: introd...
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
  static int tclass_del_notify(struct net *net,
  			     const struct Qdisc_class_ops *cops,
  			     struct sk_buff *oskb, struct nlmsghdr *n,
  			     struct Qdisc *q, unsigned long cl)
  {
  	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
  	struct sk_buff *skb;
  	int err = 0;
  
  	if (!cops->delete)
  		return -EOPNOTSUPP;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
  
  	if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
  			   RTM_DELTCLASS) < 0) {
  		kfree_skb(skb);
  		return -EINVAL;
  	}
  
  	err = cops->delete(q, cl);
  	if (err) {
  		kfree_skb(skb);
  		return err;
  	}
  
  	return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
  			      n->nlmsg_flags & NLM_F_ECHO);
  }
07d79fc7d   Cong Wang   net_sched: add re...
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
  #ifdef CONFIG_NET_CLS
  
  struct tcf_bind_args {
  	struct tcf_walker w;
  	u32 classid;
  	unsigned long cl;
  };
  
  static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
  {
  	struct tcf_bind_args *a = (void *)arg;
  
  	if (tp->ops->bind_class) {
  		tcf_tree_lock(tp);
  		tp->ops->bind_class(n, a->classid, a->cl);
  		tcf_tree_unlock(tp);
  	}
  	return 0;
  }
  
  static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
  			   unsigned long new_cl)
  {
  	const struct Qdisc_class_ops *cops = q->ops->cl_ops;
  	struct tcf_block *block;
  	struct tcf_chain *chain;
  	unsigned long cl;
  
  	cl = cops->find(q, portid);
  	if (!cl)
  		return;
  	block = cops->tcf_block(q, cl);
  	if (!block)
  		return;
  	list_for_each_entry(chain, &block->chain_list, list) {
  		struct tcf_proto *tp;
  
  		for (tp = rtnl_dereference(chain->filter_chain);
  		     tp; tp = rtnl_dereference(tp->next)) {
  			struct tcf_bind_args arg = {};
  
  			arg.w.fn = tcf_node_bind;
  			arg.classid = clid;
  			arg.cl = new_cl;
  			tp->ops->walk(tp, &arg.w);
  		}
  	}
  }
  
  #else
  
  static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
  			   unsigned long new_cl)
  {
  }
  
  #endif
c21ef3e34   David Ahern   net: rtnetlink: p...
1649
1650
  static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
  			 struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1651
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1652
  	struct net *net = sock_net(skb->sk);
02ef22ca4   David S. Miller   pkt_sched: sch_ap...
1653
  	struct tcmsg *tcm = nlmsg_data(n);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1654
  	struct nlattr *tca[TCA_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1655
1656
  	struct net_device *dev;
  	struct Qdisc *q = NULL;
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
1657
  	const struct Qdisc_class_ops *cops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1658
1659
  	unsigned long cl = 0;
  	unsigned long new_cl;
de179c8c1   Hong zhi guo   netlink: have len...
1660
1661
1662
  	u32 portid;
  	u32 clid;
  	u32 qid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663
  	int err;
4e8bbb819   Stéphane Graber   net: Allow tc cha...
1664
  	if ((n->nlmsg_type != RTM_GETTCLASS) &&
5f013c9bc   David S. Miller   Merge git://git.k...
1665
  	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
1666
  		return -EPERM;
d90574233   David Ahern   net: sched: Add p...
1667
1668
  	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy,
  			  extack);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1669
1670
  	if (err < 0)
  		return err;
de179c8c1   Hong zhi guo   netlink: have len...
1671
1672
1673
  	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
  	if (!dev)
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
  	/*
  	   parent == TC_H_UNSPEC - unspecified parent.
  	   parent == TC_H_ROOT   - class is root, which has no parent.
  	   parent == X:0	 - parent is root class.
  	   parent == X:Y	 - parent is a node in hierarchy.
  	   parent == 0:Y	 - parent is X:Y, where X:0 is qdisc.
  
  	   handle == 0:0	 - generate handle from kernel pool.
  	   handle == 0:Y	 - class is X:Y, where X:0 is qdisc.
  	   handle == X:Y	 - clear.
  	   handle == X:0	 - root class.
  	 */
  
  	/* Step 1. Determine qdisc handle X:0 */
de179c8c1   Hong zhi guo   netlink: have len...
1688
1689
1690
  	portid = tcm->tcm_parent;
  	clid = tcm->tcm_handle;
  	qid = TC_H_MAJ(clid);
15e473046   Eric W. Biederman   netlink: Rename p...
1691
1692
  	if (portid != TC_H_ROOT) {
  		u32 qid1 = TC_H_MAJ(portid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1693
1694
1695
1696
1697
1698
1699
1700
  
  		if (qid && qid1) {
  			/* If both majors are known, they must be identical. */
  			if (qid != qid1)
  				return -EINVAL;
  		} else if (qid1) {
  			qid = qid1;
  		} else if (qid == 0)
af356afa0   Patrick McHardy   net_sched: reintr...
1701
  			qid = dev->qdisc->handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1702
1703
  
  		/* Now qid is genuine qdisc handle consistent
cc7ec456f   Eric Dumazet   net_sched: cleanups
1704
1705
  		 * both with parent and child.
  		 *
15e473046   Eric W. Biederman   netlink: Rename p...
1706
  		 * TC_H_MAJ(portid) still may be unspecified, complete it now.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1707
  		 */
15e473046   Eric W. Biederman   netlink: Rename p...
1708
1709
  		if (portid)
  			portid = TC_H_MAKE(qid, portid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1710
1711
  	} else {
  		if (qid == 0)
af356afa0   Patrick McHardy   net_sched: reintr...
1712
  			qid = dev->qdisc->handle;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1713
1714
1715
  	}
  
  	/* OK. Locate qdisc */
cc7ec456f   Eric Dumazet   net_sched: cleanups
1716
1717
  	q = qdisc_lookup(dev, qid);
  	if (!q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1718
1719
1720
1721
1722
1723
1724
1725
1726
  		return -ENOENT;
  
  	/* An check that it supports classes */
  	cops = q->ops->cl_ops;
  	if (cops == NULL)
  		return -EINVAL;
  
  	/* Now try to get class */
  	if (clid == 0) {
15e473046   Eric W. Biederman   netlink: Rename p...
1727
  		if (portid == TC_H_ROOT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1728
1729
1730
1731
1732
  			clid = qid;
  	} else
  		clid = TC_H_MAKE(qid, clid);
  
  	if (clid)
143976ce9   WANG Cong   net_sched: remove...
1733
  		cl = cops->find(q, clid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1734
1735
1736
  
  	if (cl == 0) {
  		err = -ENOENT;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1737
1738
  		if (n->nlmsg_type != RTM_NEWTCLASS ||
  		    !(n->nlmsg_flags & NLM_F_CREATE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1739
1740
1741
  			goto out;
  	} else {
  		switch (n->nlmsg_type) {
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1742
  		case RTM_NEWTCLASS:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1743
  			err = -EEXIST;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1744
  			if (n->nlmsg_flags & NLM_F_EXCL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1745
1746
1747
  				goto out;
  			break;
  		case RTM_DELTCLASS:
14546ba1e   WANG Cong   net_sched: introd...
1748
  			err = tclass_del_notify(net, cops, skb, n, q, cl);
07d79fc7d   Cong Wang   net_sched: add re...
1749
1750
  			/* Unbind the class with flilters with 0 */
  			tc_bind_tclass(q, portid, clid, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1751
1752
  			goto out;
  		case RTM_GETTCLASS:
7316ae88c   Tom Goff   net_sched: make t...
1753
  			err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1754
1755
1756
1757
1758
1759
1760
1761
  			goto out;
  		default:
  			err = -EINVAL;
  			goto out;
  		}
  	}
  
  	new_cl = cl;
de6d5cdf8   Patrick McHardy   net_sched: make c...
1762
1763
  	err = -EOPNOTSUPP;
  	if (cops->change)
15e473046   Eric W. Biederman   netlink: Rename p...
1764
  		err = cops->change(q, clid, portid, tca, &new_cl);
07d79fc7d   Cong Wang   net_sched: add re...
1765
  	if (err == 0) {
7316ae88c   Tom Goff   net_sched: make t...
1766
  		tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS);
07d79fc7d   Cong Wang   net_sched: add re...
1767
1768
1769
1770
  		/* We just create a new class, need to do reverse binding. */
  		if (cl != new_cl)
  			tc_bind_tclass(q, portid, clid, new_cl);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1771
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1772
1773
  	return err;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
1774
1775
1776
1777
  struct qdisc_dump_args {
  	struct qdisc_walker	w;
  	struct sk_buff		*skb;
  	struct netlink_callback	*cb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1778
  };
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1779
1780
  static int qdisc_class_dump(struct Qdisc *q, unsigned long cl,
  			    struct qdisc_walker *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1781
1782
  {
  	struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg;
15e473046   Eric W. Biederman   netlink: Rename p...
1783
  	return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid,
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1784
1785
  			      a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
  			      RTM_NEWTCLASS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1786
  }
307236730   David S. Miller   pkt_sched: Manage...
1787
1788
1789
1790
1791
  static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb,
  				struct tcmsg *tcm, struct netlink_callback *cb,
  				int *t_p, int s_t)
  {
  	struct qdisc_dump_args arg;
49b499718   Jiri Kosina   net: sched: make ...
1792
  	if (tc_qdisc_dump_ignore(q, false) ||
307236730   David S. Miller   pkt_sched: Manage...
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
  	    *t_p < s_t || !q->ops->cl_ops ||
  	    (tcm->tcm_parent &&
  	     TC_H_MAJ(tcm->tcm_parent) != q->handle)) {
  		(*t_p)++;
  		return 0;
  	}
  	if (*t_p > s_t)
  		memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
  	arg.w.fn = qdisc_class_dump;
  	arg.skb = skb;
  	arg.cb = cb;
  	arg.w.stop  = 0;
  	arg.w.skip = cb->args[1];
  	arg.w.count = 0;
  	q->ops->cl_ops->walk(q, &arg.w);
  	cb->args[1] = arg.w.count;
  	if (arg.w.stop)
  		return -1;
  	(*t_p)++;
  	return 0;
  }
  
  static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb,
  			       struct tcmsg *tcm, struct netlink_callback *cb,
  			       int *t_p, int s_t)
  {
  	struct Qdisc *q;
59cc1f61f   Jiri Kosina   net: sched: conve...
1820
  	int b;
307236730   David S. Miller   pkt_sched: Manage...
1821
1822
1823
1824
1825
1826
  
  	if (!root)
  		return 0;
  
  	if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0)
  		return -1;
69012ae42   Jiri Kosina   net: sched: fix h...
1827
1828
  	if (!qdisc_dev(root))
  		return 0;
cb395b201   Eric Dumazet   net: sched: optim...
1829
1830
  	if (tcm->tcm_parent) {
  		q = qdisc_match_from_root(root, TC_H_MAJ(tcm->tcm_parent));
fd090ba39   Phil Sutter   net: sched: Fix f...
1831
1832
  		if (q && q != root &&
  		    tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0)
cb395b201   Eric Dumazet   net: sched: optim...
1833
1834
1835
  			return -1;
  		return 0;
  	}
59cc1f61f   Jiri Kosina   net: sched: conve...
1836
  	hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) {
307236730   David S. Miller   pkt_sched: Manage...
1837
1838
1839
1840
1841
1842
  		if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0)
  			return -1;
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1843
1844
  static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
  {
02ef22ca4   David S. Miller   pkt_sched: sch_ap...
1845
  	struct tcmsg *tcm = nlmsg_data(cb->nlh);
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
1846
  	struct net *net = sock_net(skb->sk);
307236730   David S. Miller   pkt_sched: Manage...
1847
  	struct netdev_queue *dev_queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1848
  	struct net_device *dev;
307236730   David S. Miller   pkt_sched: Manage...
1849
  	int t, s_t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1850

573ce260b   Hong zhi guo   net-next: replace...
1851
  	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1852
  		return 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
1853
1854
  	dev = dev_get_by_index(net, tcm->tcm_ifindex);
  	if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1855
1856
1857
1858
  		return 0;
  
  	s_t = cb->args[0];
  	t = 0;
af356afa0   Patrick McHardy   net_sched: reintr...
1859
  	if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0)
307236730   David S. Miller   pkt_sched: Manage...
1860
  		goto done;
24824a09e   Eric Dumazet   net: dynamic ingr...
1861
1862
1863
1864
  	dev_queue = dev_ingress_queue(dev);
  	if (dev_queue &&
  	    tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb,
  				&t, s_t) < 0)
307236730   David S. Miller   pkt_sched: Manage...
1865
  		goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1866

307236730   David S. Miller   pkt_sched: Manage...
1867
  done:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1868
1869
1870
1871
1872
  	cb->args[0] = t;
  
  	dev_put(dev);
  	return skb->len;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1873
1874
1875
1876
1877
  #ifdef CONFIG_PROC_FS
  static int psched_show(struct seq_file *seq, void *v)
  {
  	seq_printf(seq, "%08x %08x %08x %08x
  ",
ca44d6e60   Jarek Poplawski   pkt_sched: Rename...
1878
  		   (u32)NSEC_PER_USEC, (u32)PSCHED_TICKS2NS(1),
514bca322   Patrick McHardy   [NET_SCHED]: Fix ...
1879
  		   1000000,
1e3176885   Thomas Gleixner   net: sched: Use h...
1880
  		   (u32)NSEC_PER_SEC / hrtimer_resolution);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1881
1882
1883
1884
1885
1886
  
  	return 0;
  }
  
  static int psched_open(struct inode *inode, struct file *file)
  {
7e5ab1578   Tom Goff   net_sched: minor ...
1887
  	return single_open(file, psched_show, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1888
  }
da7071d7e   Arjan van de Ven   [PATCH] mark stru...
1889
  static const struct file_operations psched_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1890
1891
1892
1893
1894
  	.owner = THIS_MODULE,
  	.open = psched_open,
  	.read  = seq_read,
  	.llseek = seq_lseek,
  	.release = single_release,
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
1895
  };
7316ae88c   Tom Goff   net_sched: make t...
1896
1897
1898
1899
  
  static int __net_init psched_net_init(struct net *net)
  {
  	struct proc_dir_entry *e;
d4beaa66a   Gao feng   net: proc: change...
1900
  	e = proc_create("psched", 0, net->proc_net, &psched_fops);
7316ae88c   Tom Goff   net_sched: make t...
1901
1902
1903
1904
1905
1906
1907
1908
  	if (e == NULL)
  		return -ENOMEM;
  
  	return 0;
  }
  
  static void __net_exit psched_net_exit(struct net *net)
  {
ece31ffd5   Gao feng   net: proc: change...
1909
  	remove_proc_entry("psched", net->proc_net);
7316ae88c   Tom Goff   net_sched: make t...
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
  }
  #else
  static int __net_init psched_net_init(struct net *net)
  {
  	return 0;
  }
  
  static void __net_exit psched_net_exit(struct net *net)
  {
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1920
  #endif
7316ae88c   Tom Goff   net_sched: make t...
1921
1922
1923
1924
  static struct pernet_operations psched_net_ops = {
  	.init = psched_net_init,
  	.exit = psched_net_exit,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1925
1926
  static int __init pktsched_init(void)
  {
7316ae88c   Tom Goff   net_sched: make t...
1927
1928
1929
1930
  	int err;
  
  	err = register_pernet_subsys(&psched_net_ops);
  	if (err) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
1931
  		pr_err("pktsched_init: "
7316ae88c   Tom Goff   net_sched: make t...
1932
1933
1934
1935
  		       "cannot initialize per netns operations
  ");
  		return err;
  	}
6da7c8fcb   stephen hemminger   qdisc: allow sett...
1936
  	register_qdisc(&pfifo_fast_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1937
1938
  	register_qdisc(&pfifo_qdisc_ops);
  	register_qdisc(&bfifo_qdisc_ops);
57dbb2d83   Hagen Paul Pfeifer   sched: add head d...
1939
  	register_qdisc(&pfifo_head_drop_qdisc_ops);
6ec1c69a8   David S. Miller   net_sched: add cl...
1940
  	register_qdisc(&mq_qdisc_ops);
d66d6c315   Phil Sutter   net: sched: regis...
1941
  	register_qdisc(&noqueue_qdisc_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1942

b97bac64a   Florian Westphal   rtnetlink: make r...
1943
1944
  	rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, 0);
  	rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, 0);
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1945
  	rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc,
b97bac64a   Florian Westphal   rtnetlink: make r...
1946
1947
1948
  		      0);
  	rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, 0);
  	rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, 0);
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
1949
  	rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass,
b97bac64a   Florian Westphal   rtnetlink: make r...
1950
  		      0);
be577ddc2   Thomas Graf   [PKT_SCHED] qdisc...
1951

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1952
1953
1954
1955
  	return 0;
  }
  
  subsys_initcall(pktsched_init);