blob: 2283924fb56d57622d06691cb7f5dd13ec1e6783 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/sched/sch_api.c Packet scheduler API.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 * Fixes:
12 *
13 * Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired.
14 * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
15 * Jamal Hadi Salim <hadi@nortelnetworks.com>: 990601: ingress support
16 */
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/module.h>
19#include <linux/types.h>
20#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/init.h>
25#include <linux/proc_fs.h>
26#include <linux/seq_file.h>
27#include <linux/kmod.h>
28#include <linux/list.h>
Patrick McHardy41794772007-03-16 01:19:15 -070029#include <linux/hrtimer.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Jiri Kosina59cc1f62016-08-10 11:05:15 +020031#include <linux/hashtable.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020033#include <net/net_namespace.h>
Denis V. Lunevb8542722007-12-01 00:21:31 +110034#include <net/sock.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070035#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <net/pkt_sched.h>
Cong Wang07d79fc2017-08-30 14:30:36 -070037#include <net/pkt_cls.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Linus Torvalds1da177e2005-04-16 15:20:36 -070039/*
40
41 Short review.
42 -------------
43
44 This file consists of two interrelated parts:
45
46 1. queueing disciplines manager frontend.
47 2. traffic classes manager frontend.
48
49 Generally, queueing discipline ("qdisc") is a black box,
50 which is able to enqueue packets and to dequeue them (when
51 device is ready to send something) in order and at times
52 determined by algorithm hidden in it.
53
54 qdisc's are divided to two categories:
55 - "queues", which have no internal structure visible from outside.
56 - "schedulers", which split all the packets to "traffic classes",
57 using "packet classifiers" (look at cls_api.c)
58
59 In turn, classes may have child qdiscs (as rule, queues)
60 attached to them etc. etc. etc.
61
62 The goal of the routines in this file is to translate
63 information supplied by user in the form of handles
64 to more intelligible for kernel form, to make some sanity
65 checks and part of work, which is common to all qdiscs
66 and to provide rtnetlink notifications.
67
68 All real intelligent work is done inside qdisc modules.
69
70
71
72 Every discipline has two major routines: enqueue and dequeue.
73
74 ---dequeue
75
76 dequeue usually returns a skb to send. It is allowed to return NULL,
77 but it does not mean that queue is empty, it just means that
78 discipline does not want to send anything this time.
79 Queue is really empty if q->q.qlen == 0.
80 For complicated disciplines with multiple queues q->q is not
81 real packet queue, but however q->q.qlen must be valid.
82
83 ---enqueue
84
85 enqueue returns 0, if packet was enqueued successfully.
86 If packet (this one or another one) was dropped, it returns
87 not zero error code.
88 NET_XMIT_DROP - this packet dropped
89 Expected action: do not backoff, but wait until queue will clear.
90 NET_XMIT_CN - probably this packet enqueued, but another one dropped.
91 Expected action: backoff or ignore
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93 Auxiliary routines:
94
Jarek Poplawski99c0db22008-10-31 00:45:27 -070095 ---peek
96
97 like dequeue but without removing a packet from the queue
98
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 ---reset
100
101 returns qdisc to initial state: purge all buffers, clear all
102 timers, counters (except for statistics) etc.
103
104 ---init
105
106 initializes newly created qdisc.
107
108 ---destroy
109
110 destroys resources allocated by init and during lifetime of qdisc.
111
112 ---change
113
114 changes qdisc parameters.
115 */
116
117/* Protects list of registered TC modules. It is pure SMP lock. */
118static DEFINE_RWLOCK(qdisc_mod_lock);
119
120
121/************************************************
122 * Queueing disciplines manipulation. *
123 ************************************************/
124
125
126/* The list of all installed queueing disciplines. */
127
128static struct Qdisc_ops *qdisc_base;
129
Zhi Yong Wu21eb2182014-01-01 04:34:51 +0800130/* Register/unregister queueing discipline */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132int register_qdisc(struct Qdisc_ops *qops)
133{
134 struct Qdisc_ops *q, **qp;
135 int rc = -EEXIST;
136
137 write_lock(&qdisc_mod_lock);
138 for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
139 if (!strcmp(qops->id, q->id))
140 goto out;
141
142 if (qops->enqueue == NULL)
143 qops->enqueue = noop_qdisc_ops.enqueue;
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700144 if (qops->peek == NULL) {
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000145 if (qops->dequeue == NULL)
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700146 qops->peek = noop_qdisc_ops.peek;
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000147 else
148 goto out_einval;
Jarek Poplawski99c0db22008-10-31 00:45:27 -0700149 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 if (qops->dequeue == NULL)
151 qops->dequeue = noop_qdisc_ops.dequeue;
152
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000153 if (qops->cl_ops) {
154 const struct Qdisc_class_ops *cops = qops->cl_ops;
155
WANG Cong143976c2017-08-24 16:51:29 -0700156 if (!(cops->find && cops->walk && cops->leaf))
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000157 goto out_einval;
158
Jiri Pirko6529eab2017-05-17 11:07:55 +0200159 if (cops->tcf_block && !(cops->bind_tcf && cops->unbind_tcf))
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000160 goto out_einval;
161 }
162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 qops->next = NULL;
164 *qp = qops;
165 rc = 0;
166out:
167 write_unlock(&qdisc_mod_lock);
168 return rc;
Jarek Poplawski68fd26b2010-08-09 12:18:48 +0000169
170out_einval:
171 rc = -EINVAL;
172 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800174EXPORT_SYMBOL(register_qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176int unregister_qdisc(struct Qdisc_ops *qops)
177{
178 struct Qdisc_ops *q, **qp;
179 int err = -ENOENT;
180
181 write_lock(&qdisc_mod_lock);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000182 for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 if (q == qops)
184 break;
185 if (q) {
186 *qp = q->next;
187 q->next = NULL;
188 err = 0;
189 }
190 write_unlock(&qdisc_mod_lock);
191 return err;
192}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800193EXPORT_SYMBOL(unregister_qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
stephen hemminger6da7c8f2013-08-27 16:19:08 -0700195/* Get default qdisc if not otherwise specified */
196void qdisc_get_default(char *name, size_t len)
197{
198 read_lock(&qdisc_mod_lock);
199 strlcpy(name, default_qdisc_ops->id, len);
200 read_unlock(&qdisc_mod_lock);
201}
202
203static struct Qdisc_ops *qdisc_lookup_default(const char *name)
204{
205 struct Qdisc_ops *q = NULL;
206
207 for (q = qdisc_base; q; q = q->next) {
208 if (!strcmp(name, q->id)) {
209 if (!try_module_get(q->owner))
210 q = NULL;
211 break;
212 }
213 }
214
215 return q;
216}
217
218/* Set new default qdisc to use */
219int qdisc_set_default(const char *name)
220{
221 const struct Qdisc_ops *ops;
222
223 if (!capable(CAP_NET_ADMIN))
224 return -EPERM;
225
226 write_lock(&qdisc_mod_lock);
227 ops = qdisc_lookup_default(name);
228 if (!ops) {
229 /* Not found, drop lock and try to load module */
230 write_unlock(&qdisc_mod_lock);
231 request_module("sch_%s", name);
232 write_lock(&qdisc_mod_lock);
233
234 ops = qdisc_lookup_default(name);
235 }
236
237 if (ops) {
238 /* Set new default */
239 module_put(default_qdisc_ops->owner);
240 default_qdisc_ops = ops;
241 }
242 write_unlock(&qdisc_mod_lock);
243
244 return ops ? 0 : -ENOENT;
245}
246
stephen hemminger8ea3e432017-04-13 08:40:53 -0700247#ifdef CONFIG_NET_SCH_DEFAULT
248/* Set default value from kernel config */
249static int __init sch_default_qdisc(void)
250{
251 return qdisc_set_default(CONFIG_DEFAULT_NET_SCH);
252}
253late_initcall(sch_default_qdisc);
254#endif
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256/* We know handle. Find qdisc among all qdisc's attached to device
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800257 * (root qdisc, all its children, children of children etc.)
258 * Note: caller either uses rtnl or rcu_read_lock()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 */
260
Hannes Eder6113b742008-11-28 03:06:46 -0800261static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
David S. Miller8123b422008-08-08 23:23:39 -0700262{
263 struct Qdisc *q;
264
Jiri Kosina69012ae2016-08-16 23:52:58 +0200265 if (!qdisc_dev(root))
266 return (root->handle == handle ? root : NULL);
267
David S. Miller8123b422008-08-08 23:23:39 -0700268 if (!(root->flags & TCQ_F_BUILTIN) &&
269 root->handle == handle)
270 return root;
271
Jiri Kosina59cc1f62016-08-10 11:05:15 +0200272 hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle) {
David S. Miller8123b422008-08-08 23:23:39 -0700273 if (q->handle == handle)
274 return q;
275 }
276 return NULL;
277}
278
Jiri Kosina49b49972017-03-08 16:03:32 +0100279void qdisc_hash_add(struct Qdisc *q, bool invisible)
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700280{
Eric Dumazet37314362014-03-08 08:01:19 -0800281 if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800282 ASSERT_RTNL();
Jiri Kosina59cc1f62016-08-10 11:05:15 +0200283 hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle);
Jiri Kosina49b49972017-03-08 16:03:32 +0100284 if (invisible)
285 q->flags |= TCQ_F_INVISIBLE;
Eric Dumazet37314362014-03-08 08:01:19 -0800286 }
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700287}
Jiri Kosina59cc1f62016-08-10 11:05:15 +0200288EXPORT_SYMBOL(qdisc_hash_add);
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700289
Jiri Kosina59cc1f62016-08-10 11:05:15 +0200290void qdisc_hash_del(struct Qdisc *q)
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700291{
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800292 if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
293 ASSERT_RTNL();
Jiri Kosina59cc1f62016-08-10 11:05:15 +0200294 hash_del_rcu(&q->hash);
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800295 }
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700296}
Jiri Kosina59cc1f62016-08-10 11:05:15 +0200297EXPORT_SYMBOL(qdisc_hash_del);
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700298
David S. Milleread81cc2008-07-17 00:50:32 -0700299struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
Patrick McHardy43effa12006-11-29 17:35:48 -0800300{
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700301 struct Qdisc *q;
302
Cong Wang50317fc2017-10-27 22:08:56 -0700303 if (!handle)
304 return NULL;
Patrick McHardyaf356af2009-09-04 06:41:18 +0000305 q = qdisc_match_from_root(dev->qdisc, handle);
306 if (q)
307 goto out;
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700308
Eric Dumazet24824a02010-10-02 06:11:55 +0000309 if (dev_ingress_queue(dev))
310 q = qdisc_match_from_root(
311 dev_ingress_queue(dev)->qdisc_sleeping,
312 handle);
Jarek Poplawskif6486d42008-11-25 13:56:06 -0800313out:
Jarek Poplawskif6e0b232008-08-22 03:24:05 -0700314 return q;
Patrick McHardy43effa12006-11-29 17:35:48 -0800315}
316
Vlad Buslov3a7d0d02018-09-24 19:22:51 +0300317struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle)
318{
319 struct netdev_queue *nq;
320 struct Qdisc *q;
321
322 if (!handle)
323 return NULL;
324 q = qdisc_match_from_root(dev->qdisc, handle);
325 if (q)
326 goto out;
327
328 nq = dev_ingress_queue_rcu(dev);
329 if (nq)
330 q = qdisc_match_from_root(nq->qdisc_sleeping, handle);
331out:
332 return q;
333}
334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
336{
337 unsigned long cl;
Eric Dumazet20fea082007-11-14 01:44:41 -0800338 const struct Qdisc_class_ops *cops = p->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 if (cops == NULL)
341 return NULL;
WANG Cong143976c2017-08-24 16:51:29 -0700342 cl = cops->find(p, classid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344 if (cl == 0)
345 return NULL;
Tonghao Zhang2561f972018-12-13 00:43:23 -0800346 return cops->leaf(p, cl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347}
348
349/* Find queueing discipline by name */
350
Patrick McHardy1e904742008-01-22 22:11:17 -0800351static struct Qdisc_ops *qdisc_lookup_ops(struct nlattr *kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352{
353 struct Qdisc_ops *q = NULL;
354
355 if (kind) {
356 read_lock(&qdisc_mod_lock);
357 for (q = qdisc_base; q; q = q->next) {
Patrick McHardy1e904742008-01-22 22:11:17 -0800358 if (nla_strcmp(kind, q->id) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 if (!try_module_get(q->owner))
360 q = NULL;
361 break;
362 }
363 }
364 read_unlock(&qdisc_mod_lock);
365 }
366 return q;
367}
368
Jesper Dangaard Brouer8a8e3d82013-08-14 23:47:11 +0200369/* The linklayer setting were not transferred from iproute2, in older
370 * versions, and the rate tables lookup systems have been dropped in
371 * the kernel. To keep backward compatible with older iproute2 tc
372 * utils, we detect the linklayer setting by detecting if the rate
373 * table were modified.
374 *
375 * For linklayer ATM table entries, the rate table will be aligned to
376 * 48 bytes, thus some table entries will contain the same value. The
377 * mpu (min packet unit) is also encoded into the old rate table, thus
378 * starting from the mpu, we find low and high table entries for
379 * mapping this cell. If these entries contain the same value, when
380 * the rate tables have been modified for linklayer ATM.
381 *
382 * This is done by rounding mpu to the nearest 48 bytes cell/entry,
383 * and then roundup to the next cell, calc the table entry one below,
384 * and compare.
385 */
386static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab)
387{
388 int low = roundup(r->mpu, 48);
389 int high = roundup(low+1, 48);
390 int cell_low = low >> r->cell_log;
391 int cell_high = (high >> r->cell_log) - 1;
392
393 /* rtab is too inaccurate at rates > 100Mbit/s */
394 if ((r->rate > (100000000/8)) || (rtab[0] == 0)) {
395 pr_debug("TC linklayer: Giving up ATM detection\n");
396 return TC_LINKLAYER_ETHERNET;
397 }
398
399 if ((cell_high > cell_low) && (cell_high < 256)
400 && (rtab[cell_low] == rtab[cell_high])) {
401 pr_debug("TC linklayer: Detected ATM, low(%d)=high(%d)=%u\n",
402 cell_low, cell_high, rtab[cell_high]);
403 return TC_LINKLAYER_ATM;
404 }
405 return TC_LINKLAYER_ETHERNET;
406}
407
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408static struct qdisc_rate_table *qdisc_rtab_list;
409
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400410struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
Alexander Aringe9bc3fa2017-12-20 12:35:18 -0500411 struct nlattr *tab,
412 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
414 struct qdisc_rate_table *rtab;
415
Eric Dumazet40edeff2013-06-02 11:15:55 +0000416 if (tab == NULL || r->rate == 0 || r->cell_log == 0 ||
Alexander Aringe9bc3fa2017-12-20 12:35:18 -0500417 nla_len(tab) != TC_RTAB_SIZE) {
418 NL_SET_ERR_MSG(extack, "Invalid rate table parameters for searching");
Eric Dumazet40edeff2013-06-02 11:15:55 +0000419 return NULL;
Alexander Aringe9bc3fa2017-12-20 12:35:18 -0500420 }
Eric Dumazet40edeff2013-06-02 11:15:55 +0000421
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
Eric Dumazet40edeff2013-06-02 11:15:55 +0000423 if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) &&
424 !memcmp(&rtab->data, nla_data(tab), 1024)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 rtab->refcnt++;
426 return rtab;
427 }
428 }
429
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 rtab = kmalloc(sizeof(*rtab), GFP_KERNEL);
431 if (rtab) {
432 rtab->rate = *r;
433 rtab->refcnt = 1;
Patrick McHardy1e904742008-01-22 22:11:17 -0800434 memcpy(rtab->data, nla_data(tab), 1024);
Jesper Dangaard Brouer8a8e3d82013-08-14 23:47:11 +0200435 if (r->linklayer == TC_LINKLAYER_UNAWARE)
436 r->linklayer = __detect_linklayer(r, rtab->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 rtab->next = qdisc_rtab_list;
438 qdisc_rtab_list = rtab;
Alexander Aringe9bc3fa2017-12-20 12:35:18 -0500439 } else {
440 NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 }
442 return rtab;
443}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800444EXPORT_SYMBOL(qdisc_get_rtab);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
446void qdisc_put_rtab(struct qdisc_rate_table *tab)
447{
448 struct qdisc_rate_table *rtab, **rtabp;
449
450 if (!tab || --tab->refcnt)
451 return;
452
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000453 for (rtabp = &qdisc_rtab_list;
454 (rtab = *rtabp) != NULL;
455 rtabp = &rtab->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 if (rtab == tab) {
457 *rtabp = rtab->next;
458 kfree(rtab);
459 return;
460 }
461 }
462}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800463EXPORT_SYMBOL(qdisc_put_rtab);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700465static LIST_HEAD(qdisc_stab_list);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700466
467static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = {
468 [TCA_STAB_BASE] = { .len = sizeof(struct tc_sizespec) },
469 [TCA_STAB_DATA] = { .type = NLA_BINARY },
470};
471
Alexander Aring09215592017-12-20 12:35:12 -0500472static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt,
473 struct netlink_ext_ack *extack)
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700474{
475 struct nlattr *tb[TCA_STAB_MAX + 1];
476 struct qdisc_size_table *stab;
477 struct tc_sizespec *s;
478 unsigned int tsize = 0;
479 u16 *tab = NULL;
480 int err;
481
Alexander Aring09215592017-12-20 12:35:12 -0500482 err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy, extack);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700483 if (err < 0)
484 return ERR_PTR(err);
Alexander Aring09215592017-12-20 12:35:12 -0500485 if (!tb[TCA_STAB_BASE]) {
486 NL_SET_ERR_MSG(extack, "Size table base attribute is missing");
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700487 return ERR_PTR(-EINVAL);
Alexander Aring09215592017-12-20 12:35:12 -0500488 }
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700489
490 s = nla_data(tb[TCA_STAB_BASE]);
491
492 if (s->tsize > 0) {
Alexander Aring09215592017-12-20 12:35:12 -0500493 if (!tb[TCA_STAB_DATA]) {
494 NL_SET_ERR_MSG(extack, "Size table data attribute is missing");
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700495 return ERR_PTR(-EINVAL);
Alexander Aring09215592017-12-20 12:35:12 -0500496 }
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700497 tab = nla_data(tb[TCA_STAB_DATA]);
498 tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16);
499 }
500
Alexander Aring09215592017-12-20 12:35:12 -0500501 if (tsize != s->tsize || (!tab && tsize > 0)) {
502 NL_SET_ERR_MSG(extack, "Invalid size of size table");
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700503 return ERR_PTR(-EINVAL);
Alexander Aring09215592017-12-20 12:35:12 -0500504 }
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700505
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700506 list_for_each_entry(stab, &qdisc_stab_list, list) {
507 if (memcmp(&stab->szopts, s, sizeof(*s)))
508 continue;
509 if (tsize > 0 && memcmp(stab->data, tab, tsize * sizeof(u16)))
510 continue;
511 stab->refcnt++;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700512 return stab;
513 }
514
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700515 stab = kmalloc(sizeof(*stab) + tsize * sizeof(u16), GFP_KERNEL);
516 if (!stab)
517 return ERR_PTR(-ENOMEM);
518
519 stab->refcnt = 1;
520 stab->szopts = *s;
521 if (tsize > 0)
522 memcpy(stab->data, tab, tsize * sizeof(u16));
523
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700524 list_add_tail(&stab->list, &qdisc_stab_list);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700525
526 return stab;
527}
528
Eric Dumazeta2da5702011-01-20 03:48:19 +0000529static void stab_kfree_rcu(struct rcu_head *head)
530{
531 kfree(container_of(head, struct qdisc_size_table, rcu));
532}
533
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700534void qdisc_put_stab(struct qdisc_size_table *tab)
535{
536 if (!tab)
537 return;
538
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700539 if (--tab->refcnt == 0) {
540 list_del(&tab->list);
Paul E. McKenneyae0e3342018-11-06 19:40:39 -0800541 call_rcu(&tab->rcu, stab_kfree_rcu);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700542 }
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700543}
544EXPORT_SYMBOL(qdisc_put_stab);
545
546static int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab)
547{
548 struct nlattr *nest;
549
550 nest = nla_nest_start(skb, TCA_STAB);
Patrick McHardy3aa46142008-11-20 04:07:14 -0800551 if (nest == NULL)
552 goto nla_put_failure;
David S. Miller1b34ec42012-03-29 05:11:39 -0400553 if (nla_put(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts))
554 goto nla_put_failure;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700555 nla_nest_end(skb, nest);
556
557 return skb->len;
558
559nla_put_failure:
560 return -1;
561}
562
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400563void __qdisc_calculate_pkt_len(struct sk_buff *skb,
564 const struct qdisc_size_table *stab)
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700565{
566 int pkt_len, slot;
567
568 pkt_len = skb->len + stab->szopts.overhead;
569 if (unlikely(!stab->szopts.tsize))
570 goto out;
571
572 slot = pkt_len + stab->szopts.cell_align;
573 if (unlikely(slot < 0))
574 slot = 0;
575
576 slot >>= stab->szopts.cell_log;
577 if (likely(slot < stab->szopts.tsize))
578 pkt_len = stab->data[slot];
579 else
580 pkt_len = stab->data[stab->szopts.tsize - 1] *
581 (slot / stab->szopts.tsize) +
582 stab->data[slot % stab->szopts.tsize];
583
584 pkt_len <<= stab->szopts.size_log;
585out:
586 if (unlikely(pkt_len < 1))
587 pkt_len = 1;
588 qdisc_skb_cb(skb)->pkt_len = pkt_len;
589}
Eric Dumazeta2da5702011-01-20 03:48:19 +0000590EXPORT_SYMBOL(__qdisc_calculate_pkt_len);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -0700591
Florian Westphal6e765a02014-06-11 20:35:18 +0200592void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc)
Jarek Poplawskib00355d2009-02-01 01:12:42 -0800593{
594 if (!(qdisc->flags & TCQ_F_WARN_NONWC)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000595 pr_warn("%s: %s qdisc %X: is non-work-conserving?\n",
596 txt, qdisc->ops->id, qdisc->handle >> 16);
Jarek Poplawskib00355d2009-02-01 01:12:42 -0800597 qdisc->flags |= TCQ_F_WARN_NONWC;
598 }
599}
600EXPORT_SYMBOL(qdisc_warn_nonwc);
601
Patrick McHardy41794772007-03-16 01:19:15 -0700602static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
603{
604 struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog,
David S. Miller2fbd3da2009-09-01 17:59:25 -0700605 timer);
Patrick McHardy41794772007-03-16 01:19:15 -0700606
John Fastabend1e203c12014-10-02 22:43:09 -0700607 rcu_read_lock();
David S. Miller8608db02008-08-18 20:51:18 -0700608 __netif_schedule(qdisc_root(wd->qdisc));
John Fastabend1e203c12014-10-02 22:43:09 -0700609 rcu_read_unlock();
Stephen Hemminger19365022007-03-22 12:18:35 -0700610
Patrick McHardy41794772007-03-16 01:19:15 -0700611 return HRTIMER_NORESTART;
612}
613
Vinicius Costa Gomes860b6422018-07-03 15:42:52 -0700614void qdisc_watchdog_init_clockid(struct qdisc_watchdog *wd, struct Qdisc *qdisc,
615 clockid_t clockid)
Patrick McHardy41794772007-03-16 01:19:15 -0700616{
Vinicius Costa Gomes860b6422018-07-03 15:42:52 -0700617 hrtimer_init(&wd->timer, clockid, HRTIMER_MODE_ABS_PINNED);
David S. Miller2fbd3da2009-09-01 17:59:25 -0700618 wd->timer.function = qdisc_watchdog;
Patrick McHardy41794772007-03-16 01:19:15 -0700619 wd->qdisc = qdisc;
620}
Vinicius Costa Gomes860b6422018-07-03 15:42:52 -0700621EXPORT_SYMBOL(qdisc_watchdog_init_clockid);
622
623void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc)
624{
625 qdisc_watchdog_init_clockid(wd, qdisc, CLOCK_MONOTONIC);
626}
Patrick McHardy41794772007-03-16 01:19:15 -0700627EXPORT_SYMBOL(qdisc_watchdog_init);
628
Eric Dumazet45f50be2016-06-10 16:41:39 -0700629void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires)
Patrick McHardy41794772007-03-16 01:19:15 -0700630{
Jarek Poplawski2540e052008-08-21 05:11:14 -0700631 if (test_bit(__QDISC_STATE_DEACTIVATED,
632 &qdisc_root_sleeping(wd->qdisc)->state))
633 return;
634
Eric Dumazeta9efad82016-05-23 14:24:56 -0700635 if (wd->last_expires == expires)
636 return;
637
638 wd->last_expires = expires;
Eric Dumazet46baac32012-10-20 00:40:51 +0000639 hrtimer_start(&wd->timer,
Jiri Pirko34c5d292013-02-12 00:12:04 +0000640 ns_to_ktime(expires),
Eric Dumazet4a8e3202014-09-20 18:01:30 -0700641 HRTIMER_MODE_ABS_PINNED);
Patrick McHardy41794772007-03-16 01:19:15 -0700642}
Jiri Pirko34c5d292013-02-12 00:12:04 +0000643EXPORT_SYMBOL(qdisc_watchdog_schedule_ns);
Patrick McHardy41794772007-03-16 01:19:15 -0700644
645void qdisc_watchdog_cancel(struct qdisc_watchdog *wd)
646{
David S. Miller2fbd3da2009-09-01 17:59:25 -0700647 hrtimer_cancel(&wd->timer);
Patrick McHardy41794772007-03-16 01:19:15 -0700648}
649EXPORT_SYMBOL(qdisc_watchdog_cancel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Adrian Bunka94f7792008-07-22 14:20:11 -0700651static struct hlist_head *qdisc_class_hash_alloc(unsigned int n)
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700652{
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700653 struct hlist_head *h;
Eric Dumazet9695fe62017-08-22 12:26:46 -0700654 unsigned int i;
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700655
Eric Dumazet9695fe62017-08-22 12:26:46 -0700656 h = kvmalloc_array(n, sizeof(struct hlist_head), GFP_KERNEL);
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700657
658 if (h != NULL) {
659 for (i = 0; i < n; i++)
660 INIT_HLIST_HEAD(&h[i]);
661 }
662 return h;
663}
664
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700665void qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash)
666{
667 struct Qdisc_class_common *cl;
Sasha Levinb67bfe02013-02-27 17:06:00 -0800668 struct hlist_node *next;
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700669 struct hlist_head *nhash, *ohash;
670 unsigned int nsize, nmask, osize;
671 unsigned int i, h;
672
673 /* Rehash when load factor exceeds 0.75 */
674 if (clhash->hashelems * 4 <= clhash->hashsize * 3)
675 return;
676 nsize = clhash->hashsize * 2;
677 nmask = nsize - 1;
678 nhash = qdisc_class_hash_alloc(nsize);
679 if (nhash == NULL)
680 return;
681
682 ohash = clhash->hash;
683 osize = clhash->hashsize;
684
685 sch_tree_lock(sch);
686 for (i = 0; i < osize; i++) {
Sasha Levinb67bfe02013-02-27 17:06:00 -0800687 hlist_for_each_entry_safe(cl, next, &ohash[i], hnode) {
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700688 h = qdisc_class_hash(cl->classid, nmask);
689 hlist_add_head(&cl->hnode, &nhash[h]);
690 }
691 }
692 clhash->hash = nhash;
693 clhash->hashsize = nsize;
694 clhash->hashmask = nmask;
695 sch_tree_unlock(sch);
696
Eric Dumazet9695fe62017-08-22 12:26:46 -0700697 kvfree(ohash);
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700698}
699EXPORT_SYMBOL(qdisc_class_hash_grow);
700
701int qdisc_class_hash_init(struct Qdisc_class_hash *clhash)
702{
703 unsigned int size = 4;
704
705 clhash->hash = qdisc_class_hash_alloc(size);
Alexander Aringac8ef4a2017-12-20 12:35:11 -0500706 if (!clhash->hash)
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700707 return -ENOMEM;
708 clhash->hashsize = size;
709 clhash->hashmask = size - 1;
710 clhash->hashelems = 0;
711 return 0;
712}
713EXPORT_SYMBOL(qdisc_class_hash_init);
714
715void qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash)
716{
Eric Dumazet9695fe62017-08-22 12:26:46 -0700717 kvfree(clhash->hash);
Patrick McHardy6fe1c7a2008-07-05 23:21:31 -0700718}
719EXPORT_SYMBOL(qdisc_class_hash_destroy);
720
721void qdisc_class_hash_insert(struct Qdisc_class_hash *clhash,
722 struct Qdisc_class_common *cl)
723{
724 unsigned int h;
725
726 INIT_HLIST_NODE(&cl->hnode);
727 h = qdisc_class_hash(cl->classid, clhash->hashmask);
728 hlist_add_head(&cl->hnode, &clhash->hash[h]);
729 clhash->hashelems++;
730}
731EXPORT_SYMBOL(qdisc_class_hash_insert);
732
733void qdisc_class_hash_remove(struct Qdisc_class_hash *clhash,
734 struct Qdisc_class_common *cl)
735{
736 hlist_del(&cl->hnode);
737 clhash->hashelems--;
738}
739EXPORT_SYMBOL(qdisc_class_hash_remove);
740
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000741/* Allocate an unique handle from space managed by kernel
742 * Possible range is [8000-FFFF]:0000 (0x8000 values)
743 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744static u32 qdisc_alloc_handle(struct net_device *dev)
745{
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000746 int i = 0x8000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 static u32 autohandle = TC_H_MAKE(0x80000000U, 0);
748
749 do {
750 autohandle += TC_H_MAKE(0x10000U, 0);
751 if (autohandle == TC_H_MAKE(TC_H_ROOT, 0))
752 autohandle = TC_H_MAKE(0x80000000U, 0);
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000753 if (!qdisc_lookup(dev, autohandle))
754 return autohandle;
755 cond_resched();
756 } while (--i > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Eric Dumazetfa0f5aa2012-01-03 00:00:11 +0000758 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759}
760
Toke Høiland-Jørgensen5f2939d2019-01-09 17:10:57 +0100761void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len)
Patrick McHardy43effa12006-11-29 17:35:48 -0800762{
Nogah Frankelfd5ac142018-02-28 10:45:03 +0100763 bool qdisc_is_offloaded = sch->flags & TCQ_F_OFFLOADED;
Eric Dumazet20fea082007-11-14 01:44:41 -0800764 const struct Qdisc_class_ops *cops;
Patrick McHardy43effa12006-11-29 17:35:48 -0800765 unsigned long cl;
766 u32 parentid;
Konstantin Khlebnikov95946652017-08-15 16:39:59 +0300767 bool notify;
Eric Dumazet2c8c8e62013-10-07 08:32:32 -0700768 int drops;
Patrick McHardy43effa12006-11-29 17:35:48 -0800769
WANG Cong2ccccf52016-02-25 14:55:01 -0800770 if (n == 0 && len == 0)
Patrick McHardy43effa12006-11-29 17:35:48 -0800771 return;
Eric Dumazet2c8c8e62013-10-07 08:32:32 -0700772 drops = max_t(int, n, 0);
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800773 rcu_read_lock();
Patrick McHardy43effa12006-11-29 17:35:48 -0800774 while ((parentid = sch->parent)) {
Jarek Poplawski066a3b52008-04-14 15:10:42 -0700775 if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS))
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800776 break;
Jarek Poplawski066a3b52008-04-14 15:10:42 -0700777
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800778 if (sch->flags & TCQ_F_NOPARENT)
779 break;
Konstantin Khlebnikov95946652017-08-15 16:39:59 +0300780 /* Notify parent qdisc only if child qdisc becomes empty.
781 *
782 * If child was empty even before update then backlog
783 * counter is screwed and we skip notification because
784 * parent class is already passive.
Nogah Frankelfd5ac142018-02-28 10:45:03 +0100785 *
786 * If the original child was offloaded then it is allowed
787 * to be seem as empty, so the parent is notified anyway.
Konstantin Khlebnikov95946652017-08-15 16:39:59 +0300788 */
Nogah Frankelfd5ac142018-02-28 10:45:03 +0100789 notify = !sch->q.qlen && !WARN_ON_ONCE(!n &&
790 !qdisc_is_offloaded);
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800791 /* TODO: perform the search on a per txq basis */
David S. Miller5ce2d482008-07-08 17:06:30 -0700792 sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid));
Patrick McHardyffc8fef2007-07-30 17:11:50 -0700793 if (sch == NULL) {
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800794 WARN_ON_ONCE(parentid != TC_H_ROOT);
795 break;
Patrick McHardyffc8fef2007-07-30 17:11:50 -0700796 }
Patrick McHardy43effa12006-11-29 17:35:48 -0800797 cops = sch->ops->cl_ops;
Konstantin Khlebnikov95946652017-08-15 16:39:59 +0300798 if (notify && cops->qlen_notify) {
WANG Cong143976c2017-08-24 16:51:29 -0700799 cl = cops->find(sch, parentid);
Patrick McHardy43effa12006-11-29 17:35:48 -0800800 cops->qlen_notify(sch, cl);
Patrick McHardy43effa12006-11-29 17:35:48 -0800801 }
802 sch->q.qlen -= n;
WANG Cong2ccccf52016-02-25 14:55:01 -0800803 sch->qstats.backlog -= len;
John Fastabend25331d62014-09-28 11:53:29 -0700804 __qdisc_qstats_drop(sch, drops);
Patrick McHardy43effa12006-11-29 17:35:48 -0800805 }
Eric Dumazet4eaf3b82015-12-01 20:08:51 -0800806 rcu_read_unlock();
Patrick McHardy43effa12006-11-29 17:35:48 -0800807}
WANG Cong2ccccf52016-02-25 14:55:01 -0800808EXPORT_SYMBOL(qdisc_tree_reduce_backlog);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Jakub Kicinskib5928432018-11-07 17:33:34 -0800810int qdisc_offload_dump_helper(struct Qdisc *sch, enum tc_setup_type type,
811 void *type_data)
812{
813 struct net_device *dev = qdisc_dev(sch);
814 int err;
815
816 sch->flags &= ~TCQ_F_OFFLOADED;
817 if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
818 return 0;
819
820 err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data);
821 if (err == -EOPNOTSUPP)
822 return 0;
823
824 if (!err)
825 sch->flags |= TCQ_F_OFFLOADED;
826
827 return err;
828}
829EXPORT_SYMBOL(qdisc_offload_dump_helper);
830
Jakub Kicinskibfaee912018-11-07 17:33:37 -0800831void qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch,
832 struct Qdisc *new, struct Qdisc *old,
833 enum tc_setup_type type, void *type_data,
834 struct netlink_ext_ack *extack)
835{
836 bool any_qdisc_is_offloaded;
837 int err;
838
839 if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
840 return;
841
842 err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data);
843
844 /* Don't report error if the graft is part of destroy operation. */
845 if (!err || !new || new == &noop_qdisc)
846 return;
847
848 /* Don't report error if the parent, the old child and the new
849 * one are not offloaded.
850 */
851 any_qdisc_is_offloaded = new->flags & TCQ_F_OFFLOADED;
852 any_qdisc_is_offloaded |= sch && sch->flags & TCQ_F_OFFLOADED;
853 any_qdisc_is_offloaded |= old && old->flags & TCQ_F_OFFLOADED;
854
855 if (any_qdisc_is_offloaded)
856 NL_SET_ERR_MSG(extack, "Offloading graft operation failed.");
857}
858EXPORT_SYMBOL(qdisc_offload_graft_helper);
859
Jakub Kicinski98b0e5f2018-11-12 14:58:10 -0800860static void qdisc_offload_graft_root(struct net_device *dev,
861 struct Qdisc *new, struct Qdisc *old,
862 struct netlink_ext_ack *extack)
863{
864 struct tc_root_qopt_offload graft_offload = {
865 .command = TC_ROOT_GRAFT,
866 .handle = new ? new->handle : 0,
867 .ingress = (new && new->flags & TCQ_F_INGRESS) ||
868 (old && old->flags & TCQ_F_INGRESS),
869 };
870
871 qdisc_offload_graft_helper(dev, NULL, new, old,
872 TC_SETUP_ROOT_QDISC, &graft_offload, extack);
873}
874
WANG Cong27d7f072017-08-24 16:51:27 -0700875static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
876 u32 portid, u32 seq, u16 flags, int event)
877{
878 struct gnet_stats_basic_cpu __percpu *cpu_bstats = NULL;
879 struct gnet_stats_queue __percpu *cpu_qstats = NULL;
880 struct tcmsg *tcm;
881 struct nlmsghdr *nlh;
882 unsigned char *b = skb_tail_pointer(skb);
883 struct gnet_dump d;
884 struct qdisc_size_table *stab;
Jiri Pirkod47a6b02018-01-17 11:46:52 +0100885 u32 block_index;
WANG Cong27d7f072017-08-24 16:51:27 -0700886 __u32 qlen;
887
888 cond_resched();
889 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
890 if (!nlh)
891 goto out_nlmsg_trim;
892 tcm = nlmsg_data(nlh);
893 tcm->tcm_family = AF_UNSPEC;
894 tcm->tcm__pad1 = 0;
895 tcm->tcm__pad2 = 0;
896 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
897 tcm->tcm_parent = clid;
898 tcm->tcm_handle = q->handle;
899 tcm->tcm_info = refcount_read(&q->refcnt);
900 if (nla_put_string(skb, TCA_KIND, q->ops->id))
901 goto nla_put_failure;
Jiri Pirkod47a6b02018-01-17 11:46:52 +0100902 if (q->ops->ingress_block_get) {
903 block_index = q->ops->ingress_block_get(q);
904 if (block_index &&
905 nla_put_u32(skb, TCA_INGRESS_BLOCK, block_index))
906 goto nla_put_failure;
907 }
908 if (q->ops->egress_block_get) {
909 block_index = q->ops->egress_block_get(q);
910 if (block_index &&
911 nla_put_u32(skb, TCA_EGRESS_BLOCK, block_index))
912 goto nla_put_failure;
913 }
WANG Cong27d7f072017-08-24 16:51:27 -0700914 if (q->ops->dump && q->ops->dump(q, skb) < 0)
915 goto nla_put_failure;
Nogah Frankel44edf2f2017-12-25 10:51:42 +0200916 if (nla_put_u8(skb, TCA_HW_OFFLOAD, !!(q->flags & TCQ_F_OFFLOADED)))
917 goto nla_put_failure;
John Fastabend7e660162017-12-07 09:57:00 -0800918 qlen = qdisc_qlen_sum(q);
WANG Cong27d7f072017-08-24 16:51:27 -0700919
920 stab = rtnl_dereference(q->stab);
921 if (stab && qdisc_dump_stab(skb, stab) < 0)
922 goto nla_put_failure;
923
924 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
925 NULL, &d, TCA_PAD) < 0)
926 goto nla_put_failure;
927
928 if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)
929 goto nla_put_failure;
930
931 if (qdisc_is_percpu_stats(q)) {
932 cpu_bstats = q->cpu_bstats;
933 cpu_qstats = q->cpu_qstats;
934 }
935
936 if (gnet_stats_copy_basic(qdisc_root_sleeping_running(q),
937 &d, cpu_bstats, &q->bstats) < 0 ||
938 gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 ||
939 gnet_stats_copy_queue(&d, cpu_qstats, &q->qstats, qlen) < 0)
940 goto nla_put_failure;
941
942 if (gnet_stats_finish_copy(&d) < 0)
943 goto nla_put_failure;
944
945 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
946 return skb->len;
947
948out_nlmsg_trim:
949nla_put_failure:
950 nlmsg_trim(skb, b);
951 return -1;
952}
953
954static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible)
955{
956 if (q->flags & TCQ_F_BUILTIN)
957 return true;
958 if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible)
959 return true;
960
961 return false;
962}
963
964static int qdisc_notify(struct net *net, struct sk_buff *oskb,
965 struct nlmsghdr *n, u32 clid,
966 struct Qdisc *old, struct Qdisc *new)
967{
968 struct sk_buff *skb;
969 u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
970
971 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
972 if (!skb)
973 return -ENOBUFS;
974
975 if (old && !tc_qdisc_dump_ignore(old, false)) {
976 if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq,
977 0, RTM_DELQDISC) < 0)
978 goto err_out;
979 }
980 if (new && !tc_qdisc_dump_ignore(new, false)) {
981 if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq,
982 old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
983 goto err_out;
984 }
985
986 if (skb->len)
987 return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
988 n->nlmsg_flags & NLM_F_ECHO);
989
990err_out:
991 kfree_skb(skb);
992 return -EINVAL;
993}
994
Tom Goff7316ae82010-03-19 15:40:13 +0000995static void notify_and_destroy(struct net *net, struct sk_buff *skb,
996 struct nlmsghdr *n, u32 clid,
David S. Miller99194cf2008-07-17 04:54:10 -0700997 struct Qdisc *old, struct Qdisc *new)
998{
999 if (new || old)
Tom Goff7316ae82010-03-19 15:40:13 +00001000 qdisc_notify(net, skb, n, clid, old, new);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
David S. Miller4d8863a2008-08-18 21:03:15 -07001002 if (old)
Vlad Buslov86bd4462018-09-24 19:22:50 +03001003 qdisc_put(old);
David S. Miller99194cf2008-07-17 04:54:10 -07001004}
1005
1006/* Graft qdisc "new" to class "classid" of qdisc "parent" or
1007 * to device "dev".
1008 *
1009 * When appropriate send a netlink notification using 'skb'
1010 * and "n".
1011 *
1012 * On success, destroy old qdisc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 */
1014
1015static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
David S. Miller99194cf2008-07-17 04:54:10 -07001016 struct sk_buff *skb, struct nlmsghdr *n, u32 classid,
Alexander Aring09215592017-12-20 12:35:12 -05001017 struct Qdisc *new, struct Qdisc *old,
1018 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019{
David S. Miller99194cf2008-07-17 04:54:10 -07001020 struct Qdisc *q = old;
Tom Goff7316ae82010-03-19 15:40:13 +00001021 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001023 if (parent == NULL) {
David S. Miller99194cf2008-07-17 04:54:10 -07001024 unsigned int i, num_q, ingress;
1025
1026 ingress = 0;
1027 num_q = dev->num_tx_queues;
David S. Miller8d50b532008-07-30 02:37:46 -07001028 if ((q && q->flags & TCQ_F_INGRESS) ||
1029 (new && new->flags & TCQ_F_INGRESS)) {
David S. Miller99194cf2008-07-17 04:54:10 -07001030 num_q = 1;
1031 ingress = 1;
Alexander Aring09215592017-12-20 12:35:12 -05001032 if (!dev_ingress_queue(dev)) {
1033 NL_SET_ERR_MSG(extack, "Device does not have an ingress queue");
Eric Dumazet24824a02010-10-02 06:11:55 +00001034 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001035 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 }
David S. Miller99194cf2008-07-17 04:54:10 -07001037
1038 if (dev->flags & IFF_UP)
1039 dev_deactivate(dev);
1040
Jakub Kicinski98b0e5f2018-11-12 14:58:10 -08001041 qdisc_offload_graft_root(dev, new, old, extack);
1042
WANG Cong86e363d2015-05-26 16:08:48 -07001043 if (new && new->ops->attach)
1044 goto skip;
David S. Miller6ec1c692009-09-06 01:58:51 -07001045
David S. Miller99194cf2008-07-17 04:54:10 -07001046 for (i = 0; i < num_q; i++) {
Eric Dumazet24824a02010-10-02 06:11:55 +00001047 struct netdev_queue *dev_queue = dev_ingress_queue(dev);
David S. Miller99194cf2008-07-17 04:54:10 -07001048
1049 if (!ingress)
1050 dev_queue = netdev_get_tx_queue(dev, i);
1051
David S. Miller8d50b532008-07-30 02:37:46 -07001052 old = dev_graft_qdisc(dev_queue, new);
1053 if (new && i > 0)
Eric Dumazet551143d2017-08-24 21:12:28 -07001054 qdisc_refcount_inc(new);
David S. Miller8d50b532008-07-30 02:37:46 -07001055
Jarek Poplawski036d6a62009-09-13 22:35:44 +00001056 if (!ingress)
Vlad Buslov86bd4462018-09-24 19:22:50 +03001057 qdisc_put(old);
David S. Miller99194cf2008-07-17 04:54:10 -07001058 }
1059
WANG Cong86e363d2015-05-26 16:08:48 -07001060skip:
Jarek Poplawski036d6a62009-09-13 22:35:44 +00001061 if (!ingress) {
Tom Goff7316ae82010-03-19 15:40:13 +00001062 notify_and_destroy(net, skb, n, classid,
1063 dev->qdisc, new);
Jarek Poplawski036d6a62009-09-13 22:35:44 +00001064 if (new && !new->ops->attach)
Eric Dumazet551143d2017-08-24 21:12:28 -07001065 qdisc_refcount_inc(new);
Jarek Poplawski036d6a62009-09-13 22:35:44 +00001066 dev->qdisc = new ? : &noop_qdisc;
WANG Cong86e363d2015-05-26 16:08:48 -07001067
1068 if (new && new->ops->attach)
1069 new->ops->attach(new);
Jarek Poplawski036d6a62009-09-13 22:35:44 +00001070 } else {
Tom Goff7316ae82010-03-19 15:40:13 +00001071 notify_and_destroy(net, skb, n, classid, old, new);
Jarek Poplawski036d6a62009-09-13 22:35:44 +00001072 }
Patrick McHardyaf356af2009-09-04 06:41:18 +00001073
David S. Miller99194cf2008-07-17 04:54:10 -07001074 if (dev->flags & IFF_UP)
1075 dev_activate(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 } else {
Eric Dumazet20fea082007-11-14 01:44:41 -08001077 const struct Qdisc_class_ops *cops = parent->ops->cl_ops;
Jakub Kicinski9da93ec2018-11-07 17:33:38 -08001078 unsigned long cl;
1079 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
John Fastabendc5ad1192017-12-07 09:58:19 -08001081 /* Only support running class lockless if parent is lockless */
1082 if (new && (new->flags & TCQ_F_NOLOCK) &&
1083 parent && !(parent->flags & TCQ_F_NOLOCK))
1084 new->flags &= ~TCQ_F_NOLOCK;
1085
Jakub Kicinski9da93ec2018-11-07 17:33:38 -08001086 if (!cops || !cops->graft)
1087 return -EOPNOTSUPP;
WANG Cong143976c2017-08-24 16:51:29 -07001088
Jakub Kicinski9da93ec2018-11-07 17:33:38 -08001089 cl = cops->find(parent, classid);
1090 if (!cl) {
1091 NL_SET_ERR_MSG(extack, "Specified class not found");
1092 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 }
Jakub Kicinski9da93ec2018-11-07 17:33:38 -08001094
1095 err = cops->graft(parent, cl, new, &old, extack);
1096 if (err)
1097 return err;
1098 notify_and_destroy(net, skb, n, classid, old, new);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 }
Jakub Kicinski9da93ec2018-11-07 17:33:38 -08001100 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101}
1102
Jiri Pirkod47a6b02018-01-17 11:46:52 +01001103static int qdisc_block_indexes_set(struct Qdisc *sch, struct nlattr **tca,
1104 struct netlink_ext_ack *extack)
1105{
1106 u32 block_index;
1107
1108 if (tca[TCA_INGRESS_BLOCK]) {
1109 block_index = nla_get_u32(tca[TCA_INGRESS_BLOCK]);
1110
1111 if (!block_index) {
1112 NL_SET_ERR_MSG(extack, "Ingress block index cannot be 0");
1113 return -EINVAL;
1114 }
1115 if (!sch->ops->ingress_block_set) {
1116 NL_SET_ERR_MSG(extack, "Ingress block sharing is not supported");
1117 return -EOPNOTSUPP;
1118 }
1119 sch->ops->ingress_block_set(sch, block_index);
1120 }
1121 if (tca[TCA_EGRESS_BLOCK]) {
1122 block_index = nla_get_u32(tca[TCA_EGRESS_BLOCK]);
1123
1124 if (!block_index) {
1125 NL_SET_ERR_MSG(extack, "Egress block index cannot be 0");
1126 return -EINVAL;
1127 }
1128 if (!sch->ops->egress_block_set) {
1129 NL_SET_ERR_MSG(extack, "Egress block sharing is not supported");
1130 return -EOPNOTSUPP;
1131 }
1132 sch->ops->egress_block_set(sch, block_index);
1133 }
1134 return 0;
1135}
1136
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137/*
1138 Allocate and initialize new qdisc.
1139
1140 Parameters are passed via opt.
1141 */
1142
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001143static struct Qdisc *qdisc_create(struct net_device *dev,
1144 struct netdev_queue *dev_queue,
1145 struct Qdisc *p, u32 parent, u32 handle,
Alexander Aring09215592017-12-20 12:35:12 -05001146 struct nlattr **tca, int *errp,
1147 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148{
1149 int err;
Patrick McHardy1e904742008-01-22 22:11:17 -08001150 struct nlattr *kind = tca[TCA_KIND];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 struct Qdisc *sch;
1152 struct Qdisc_ops *ops;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001153 struct qdisc_size_table *stab;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
1155 ops = qdisc_lookup_ops(kind);
Johannes Berg95a5afc2008-10-16 15:24:51 -07001156#ifdef CONFIG_MODULES
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 if (ops == NULL && kind != NULL) {
1158 char name[IFNAMSIZ];
Patrick McHardy1e904742008-01-22 22:11:17 -08001159 if (nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 /* We dropped the RTNL semaphore in order to
1161 * perform the module load. So, even if we
1162 * succeeded in loading the module we have to
1163 * tell the caller to replay the request. We
1164 * indicate this using -EAGAIN.
1165 * We replay the request because the device may
1166 * go away in the mean time.
1167 */
1168 rtnl_unlock();
1169 request_module("sch_%s", name);
1170 rtnl_lock();
1171 ops = qdisc_lookup_ops(kind);
1172 if (ops != NULL) {
1173 /* We will try again qdisc_lookup_ops,
1174 * so don't keep a reference.
1175 */
1176 module_put(ops->owner);
1177 err = -EAGAIN;
1178 goto err_out;
1179 }
1180 }
1181 }
1182#endif
1183
Jamal Hadi Salimb9e2cc02006-08-03 16:36:51 -07001184 err = -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001185 if (!ops) {
1186 NL_SET_ERR_MSG(extack, "Specified qdisc not found");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 goto err_out;
Alexander Aring09215592017-12-20 12:35:12 -05001188 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
Alexander Aringd0bd6842017-12-20 12:35:20 -05001190 sch = qdisc_alloc(dev_queue, ops, extack);
Thomas Graf3d54b822005-07-05 14:15:09 -07001191 if (IS_ERR(sch)) {
1192 err = PTR_ERR(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 goto err_out2;
Thomas Graf3d54b822005-07-05 14:15:09 -07001194 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
Patrick McHardyffc8fef2007-07-30 17:11:50 -07001196 sch->parent = parent;
1197
Thomas Graf3d54b822005-07-05 14:15:09 -07001198 if (handle == TC_H_INGRESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 sch->flags |= TCQ_F_INGRESS;
Thomas Graf3d54b822005-07-05 14:15:09 -07001200 handle = TC_H_MAKE(TC_H_INGRESS, 0);
Patrick McHardyfd44de72007-04-16 17:07:08 -07001201 } else {
Patrick McHardyfd44de72007-04-16 17:07:08 -07001202 if (handle == 0) {
1203 handle = qdisc_alloc_handle(dev);
1204 err = -ENOMEM;
1205 if (handle == 0)
1206 goto err_out3;
1207 }
Eric Dumazet1abbe132012-12-11 15:54:33 +00001208 if (!netif_is_multiqueue(dev))
Eric Dumazet225734d2015-12-15 09:43:12 -08001209 sch->flags |= TCQ_F_ONETXQUEUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 }
1211
Thomas Graf3d54b822005-07-05 14:15:09 -07001212 sch->handle = handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
Jesper Dangaard Brouer84c46dd2016-11-03 14:56:11 +01001214 /* This exist to keep backward compatible with a userspace
1215 * loophole, what allowed userspace to get IFF_NO_QUEUE
1216 * facility on older kernels by setting tx_queue_len=0 (prior
1217 * to qdisc init), and then forgot to reinit tx_queue_len
1218 * before again attaching a qdisc.
1219 */
1220 if ((dev->priv_flags & IFF_NO_QUEUE) && (dev->tx_queue_len == 0)) {
1221 dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
1222 netdev_info(dev, "Caught tx_queue_len zero misconfig\n");
1223 }
1224
Jiri Pirkod47a6b02018-01-17 11:46:52 +01001225 err = qdisc_block_indexes_set(sch, tca, extack);
1226 if (err)
1227 goto err_out3;
1228
Alexander Aring54160ef2017-12-04 18:40:00 -05001229 if (ops->init) {
Alexander Aringe63d7df2017-12-20 12:35:13 -05001230 err = ops->init(sch, tca[TCA_OPTIONS], extack);
Alexander Aring54160ef2017-12-04 18:40:00 -05001231 if (err != 0)
1232 goto err_out5;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 }
Alexander Aring54160ef2017-12-04 18:40:00 -05001234
Alexander Aring54160ef2017-12-04 18:40:00 -05001235 if (tca[TCA_STAB]) {
Alexander Aring09215592017-12-20 12:35:12 -05001236 stab = qdisc_get_stab(tca[TCA_STAB], extack);
Alexander Aring54160ef2017-12-04 18:40:00 -05001237 if (IS_ERR(stab)) {
1238 err = PTR_ERR(stab);
1239 goto err_out4;
1240 }
1241 rcu_assign_pointer(sch->stab, stab);
1242 }
1243 if (tca[TCA_RATE]) {
1244 seqcount_t *running;
1245
1246 err = -EOPNOTSUPP;
Alexander Aring09215592017-12-20 12:35:12 -05001247 if (sch->flags & TCQ_F_MQROOT) {
1248 NL_SET_ERR_MSG(extack, "Cannot attach rate estimator to a multi-queue root qdisc");
Alexander Aring54160ef2017-12-04 18:40:00 -05001249 goto err_out4;
Alexander Aring09215592017-12-20 12:35:12 -05001250 }
Alexander Aring54160ef2017-12-04 18:40:00 -05001251
1252 if (sch->parent != TC_H_ROOT &&
1253 !(sch->flags & TCQ_F_INGRESS) &&
1254 (!p || !(p->flags & TCQ_F_MQROOT)))
1255 running = qdisc_root_sleeping_running(sch);
1256 else
1257 running = &sch->running;
1258
1259 err = gen_new_estimator(&sch->bstats,
1260 sch->cpu_bstats,
1261 &sch->rate_est,
1262 NULL,
1263 running,
1264 tca[TCA_RATE]);
Alexander Aring09215592017-12-20 12:35:12 -05001265 if (err) {
1266 NL_SET_ERR_MSG(extack, "Failed to generate new estimator");
Alexander Aring54160ef2017-12-04 18:40:00 -05001267 goto err_out4;
Alexander Aring09215592017-12-20 12:35:12 -05001268 }
Alexander Aring54160ef2017-12-04 18:40:00 -05001269 }
1270
1271 qdisc_hash_add(sch, false);
1272
1273 return sch;
1274
1275err_out5:
Eric Dumazet87b60cf2017-02-10 10:31:49 -08001276 /* ops->init() failed, we call ->destroy() like qdisc_create_dflt() */
Gao Fengc1a48722017-06-28 12:53:54 +08001277 if (ops->destroy)
1278 ops->destroy(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279err_out3:
1280 dev_put(dev);
Daniel Borkmann81d947e2018-01-15 23:12:09 +01001281 qdisc_free(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282err_out2:
1283 module_put(ops->owner);
1284err_out:
1285 *errp = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 return NULL;
Patrick McHardy23bcf632009-09-09 18:11:23 -07001287
1288err_out4:
1289 /*
1290 * Any broken qdiscs that would require a ops->reset() here?
1291 * The qdisc was never in action so it shouldn't be necessary.
1292 */
Eric Dumazeta2da5702011-01-20 03:48:19 +00001293 qdisc_put_stab(rtnl_dereference(sch->stab));
Patrick McHardy23bcf632009-09-09 18:11:23 -07001294 if (ops->destroy)
1295 ops->destroy(sch);
1296 goto err_out3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297}
1298
Alexander Aring09215592017-12-20 12:35:12 -05001299static int qdisc_change(struct Qdisc *sch, struct nlattr **tca,
1300 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301{
Eric Dumazeta2da5702011-01-20 03:48:19 +00001302 struct qdisc_size_table *ostab, *stab = NULL;
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001303 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001305 if (tca[TCA_OPTIONS]) {
Alexander Aring09215592017-12-20 12:35:12 -05001306 if (!sch->ops->change) {
1307 NL_SET_ERR_MSG(extack, "Change operation not supported by specified qdisc");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001309 }
Jiri Pirkod47a6b02018-01-17 11:46:52 +01001310 if (tca[TCA_INGRESS_BLOCK] || tca[TCA_EGRESS_BLOCK]) {
1311 NL_SET_ERR_MSG(extack, "Change of blocks is not supported");
1312 return -EOPNOTSUPP;
1313 }
Alexander Aring20307212017-12-20 12:35:14 -05001314 err = sch->ops->change(sch, tca[TCA_OPTIONS], extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 if (err)
1316 return err;
1317 }
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001318
1319 if (tca[TCA_STAB]) {
Alexander Aring09215592017-12-20 12:35:12 -05001320 stab = qdisc_get_stab(tca[TCA_STAB], extack);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001321 if (IS_ERR(stab))
1322 return PTR_ERR(stab);
1323 }
1324
Eric Dumazeta2da5702011-01-20 03:48:19 +00001325 ostab = rtnl_dereference(sch->stab);
1326 rcu_assign_pointer(sch->stab, stab);
1327 qdisc_put_stab(ostab);
Jussi Kivilinna175f9c12008-07-20 00:08:47 -07001328
Patrick McHardy23bcf632009-09-09 18:11:23 -07001329 if (tca[TCA_RATE]) {
Stephen Hemminger71bcb092008-11-25 21:13:31 -08001330 /* NB: ignores errors from replace_estimator
1331 because change can't be undone. */
Patrick McHardy23bcf632009-09-09 18:11:23 -07001332 if (sch->flags & TCQ_F_MQROOT)
1333 goto out;
John Fastabend22e0f8b2014-09-28 11:52:56 -07001334 gen_replace_estimator(&sch->bstats,
1335 sch->cpu_bstats,
1336 &sch->rate_est,
Eric Dumazetedb09eb2016-06-06 09:37:16 -07001337 NULL,
1338 qdisc_root_sleeping_running(sch),
John Fastabend22e0f8b2014-09-28 11:52:56 -07001339 tca[TCA_RATE]);
Patrick McHardy23bcf632009-09-09 18:11:23 -07001340 }
1341out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 return 0;
1343}
1344
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001345struct check_loop_arg {
1346 struct qdisc_walker w;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 struct Qdisc *p;
1348 int depth;
1349};
1350
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001351static int check_loop_fn(struct Qdisc *q, unsigned long cl,
1352 struct qdisc_walker *w);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
1354static int check_loop(struct Qdisc *q, struct Qdisc *p, int depth)
1355{
1356 struct check_loop_arg arg;
1357
1358 if (q->ops->cl_ops == NULL)
1359 return 0;
1360
1361 arg.w.stop = arg.w.skip = arg.w.count = 0;
1362 arg.w.fn = check_loop_fn;
1363 arg.depth = depth;
1364 arg.p = p;
1365 q->ops->cl_ops->walk(q, &arg.w);
1366 return arg.w.stop ? -ELOOP : 0;
1367}
1368
1369static int
1370check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w)
1371{
1372 struct Qdisc *leaf;
Eric Dumazet20fea082007-11-14 01:44:41 -08001373 const struct Qdisc_class_ops *cops = q->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 struct check_loop_arg *arg = (struct check_loop_arg *)w;
1375
1376 leaf = cops->leaf(q, cl);
1377 if (leaf) {
1378 if (leaf == arg->p || arg->depth > 7)
1379 return -ELOOP;
1380 return check_loop(leaf, arg->p, arg->depth + 1);
1381 }
1382 return 0;
1383}
1384
David Ahern8b4c3cd2018-10-03 15:05:36 -07001385const struct nla_policy rtm_tca_policy[TCA_MAX + 1] = {
1386 [TCA_KIND] = { .type = NLA_STRING },
David Ahern8b4c3cd2018-10-03 15:05:36 -07001387 [TCA_RATE] = { .type = NLA_BINARY,
1388 .len = sizeof(struct tc_estimator) },
1389 [TCA_STAB] = { .type = NLA_NESTED },
1390 [TCA_DUMP_INVISIBLE] = { .type = NLA_FLAG },
1391 [TCA_CHAIN] = { .type = NLA_U32 },
1392 [TCA_INGRESS_BLOCK] = { .type = NLA_U32 },
1393 [TCA_EGRESS_BLOCK] = { .type = NLA_U32 },
1394};
1395
Davide Carattie3314732018-10-10 22:00:58 +02001396/*
1397 * Delete/get qdisc.
1398 */
1399
David Ahernc21ef3e2017-04-16 09:48:24 -07001400static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
1401 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001403 struct net *net = sock_net(skb->sk);
David S. Miller02ef22c2012-06-26 21:50:05 -07001404 struct tcmsg *tcm = nlmsg_data(n);
Patrick McHardy1e904742008-01-22 22:11:17 -08001405 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 struct net_device *dev;
Hong zhi guode179c82013-03-25 17:36:33 +00001407 u32 clid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 struct Qdisc *q = NULL;
1409 struct Qdisc *p = NULL;
1410 int err;
1411
Stéphane Graber4e8bbb82014-04-30 11:25:43 -04001412 if ((n->nlmsg_type != RTM_GETQDISC) &&
David S. Miller5f013c9b2014-05-12 13:19:14 -04001413 !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +00001414 return -EPERM;
1415
David Ahern8b4c3cd2018-10-03 15:05:36 -07001416 err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy,
1417 extack);
Patrick McHardy1e904742008-01-22 22:11:17 -08001418 if (err < 0)
1419 return err;
1420
Hong zhi guode179c82013-03-25 17:36:33 +00001421 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
1422 if (!dev)
1423 return -ENODEV;
1424
1425 clid = tcm->tcm_parent;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 if (clid) {
1427 if (clid != TC_H_ROOT) {
1428 if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001429 p = qdisc_lookup(dev, TC_H_MAJ(clid));
Alexander Aring09215592017-12-20 12:35:12 -05001430 if (!p) {
1431 NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified classid");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 q = qdisc_leaf(p, clid);
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001435 } else if (dev_ingress_queue(dev)) {
1436 q = dev_ingress_queue(dev)->qdisc_sleeping;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09001437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 } else {
Patrick McHardyaf356af2009-09-04 06:41:18 +00001439 q = dev->qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 }
Alexander Aring09215592017-12-20 12:35:12 -05001441 if (!q) {
1442 NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001444 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445
Alexander Aring09215592017-12-20 12:35:12 -05001446 if (tcm->tcm_handle && q->handle != tcm->tcm_handle) {
1447 NL_SET_ERR_MSG(extack, "Invalid handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001449 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 } else {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001451 q = qdisc_lookup(dev, tcm->tcm_handle);
Alexander Aring09215592017-12-20 12:35:12 -05001452 if (!q) {
1453 NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001455 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 }
1457
Alexander Aring09215592017-12-20 12:35:12 -05001458 if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id)) {
1459 NL_SET_ERR_MSG(extack, "Invalid qdisc name");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001461 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 if (n->nlmsg_type == RTM_DELQDISC) {
Alexander Aring09215592017-12-20 12:35:12 -05001464 if (!clid) {
1465 NL_SET_ERR_MSG(extack, "Classid cannot be zero");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001467 }
1468 if (q->handle == 0) {
1469 NL_SET_ERR_MSG(extack, "Cannot delete qdisc with handle of zero");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001471 }
1472 err = qdisc_graft(dev, p, skb, n, clid, NULL, q, extack);
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001473 if (err != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 } else {
Tom Goff7316ae82010-03-19 15:40:13 +00001476 qdisc_notify(net, skb, n, clid, NULL, q);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 }
1478 return 0;
1479}
1480
1481/*
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001482 * Create/change qdisc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 */
1484
David Ahernc21ef3e2017-04-16 09:48:24 -07001485static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
1486 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001488 struct net *net = sock_net(skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 struct tcmsg *tcm;
Patrick McHardy1e904742008-01-22 22:11:17 -08001490 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 struct net_device *dev;
1492 u32 clid;
1493 struct Qdisc *q, *p;
1494 int err;
1495
David S. Miller5f013c9b2014-05-12 13:19:14 -04001496 if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +00001497 return -EPERM;
1498
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499replay:
1500 /* Reinit, just in case something touches this. */
David Ahern8b4c3cd2018-10-03 15:05:36 -07001501 err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy,
1502 extack);
Hong zhi guode179c82013-03-25 17:36:33 +00001503 if (err < 0)
1504 return err;
1505
David S. Miller02ef22c2012-06-26 21:50:05 -07001506 tcm = nlmsg_data(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 clid = tcm->tcm_parent;
1508 q = p = NULL;
1509
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001510 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
1511 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 return -ENODEV;
1513
Patrick McHardy1e904742008-01-22 22:11:17 -08001514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 if (clid) {
1516 if (clid != TC_H_ROOT) {
1517 if (clid != TC_H_INGRESS) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001518 p = qdisc_lookup(dev, TC_H_MAJ(clid));
Alexander Aring09215592017-12-20 12:35:12 -05001519 if (!p) {
1520 NL_SET_ERR_MSG(extack, "Failed to find specified qdisc");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001522 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 q = qdisc_leaf(p, clid);
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001524 } else if (dev_ingress_queue_create(dev)) {
1525 q = dev_ingress_queue(dev)->qdisc_sleeping;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 }
1527 } else {
Patrick McHardyaf356af2009-09-04 06:41:18 +00001528 q = dev->qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 }
1530
1531 /* It may be default qdisc, ignore it */
1532 if (q && q->handle == 0)
1533 q = NULL;
1534
1535 if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
1536 if (tcm->tcm_handle) {
Alexander Aring09215592017-12-20 12:35:12 -05001537 if (q && !(n->nlmsg_flags & NLM_F_REPLACE)) {
1538 NL_SET_ERR_MSG(extack, "NLM_F_REPLACE needed to override");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 return -EEXIST;
Alexander Aring09215592017-12-20 12:35:12 -05001540 }
1541 if (TC_H_MIN(tcm->tcm_handle)) {
1542 NL_SET_ERR_MSG(extack, "Invalid minor handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001544 }
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001545 q = qdisc_lookup(dev, tcm->tcm_handle);
Jiri Pirko8ec69572017-12-28 16:52:10 +01001546 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 goto create_n_graft;
Alexander Aring09215592017-12-20 12:35:12 -05001548 if (n->nlmsg_flags & NLM_F_EXCL) {
1549 NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot override");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 return -EEXIST;
Alexander Aring09215592017-12-20 12:35:12 -05001551 }
Alexander Aring0ac4bd62017-12-04 18:39:59 -05001552 if (tca[TCA_KIND] &&
Alexander Aring09215592017-12-20 12:35:12 -05001553 nla_strcmp(tca[TCA_KIND], q->ops->id)) {
1554 NL_SET_ERR_MSG(extack, "Invalid qdisc name");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001556 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 if (q == p ||
Alexander Aring09215592017-12-20 12:35:12 -05001558 (p && check_loop(q, p, 0))) {
1559 NL_SET_ERR_MSG(extack, "Qdisc parent/child loop detected");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 return -ELOOP;
Alexander Aring09215592017-12-20 12:35:12 -05001561 }
Eric Dumazet551143d2017-08-24 21:12:28 -07001562 qdisc_refcount_inc(q);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 goto graft;
1564 } else {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001565 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 goto create_n_graft;
1567
1568 /* This magic test requires explanation.
1569 *
1570 * We know, that some child q is already
1571 * attached to this parent and have choice:
1572 * either to change it or to create/graft new one.
1573 *
1574 * 1. We are allowed to create/graft only
1575 * if CREATE and REPLACE flags are set.
1576 *
1577 * 2. If EXCL is set, requestor wanted to say,
1578 * that qdisc tcm_handle is not expected
1579 * to exist, so that we choose create/graft too.
1580 *
1581 * 3. The last case is when no flags are set.
1582 * Alas, it is sort of hole in API, we
1583 * cannot decide what to do unambiguously.
1584 * For now we select create/graft, if
1585 * user gave KIND, which does not match existing.
1586 */
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001587 if ((n->nlmsg_flags & NLM_F_CREATE) &&
1588 (n->nlmsg_flags & NLM_F_REPLACE) &&
1589 ((n->nlmsg_flags & NLM_F_EXCL) ||
Patrick McHardy1e904742008-01-22 22:11:17 -08001590 (tca[TCA_KIND] &&
1591 nla_strcmp(tca[TCA_KIND], q->ops->id))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 goto create_n_graft;
1593 }
1594 }
1595 } else {
Alexander Aring09215592017-12-20 12:35:12 -05001596 if (!tcm->tcm_handle) {
1597 NL_SET_ERR_MSG(extack, "Handle cannot be zero");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001599 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 q = qdisc_lookup(dev, tcm->tcm_handle);
1601 }
1602
1603 /* Change qdisc parameters */
Alexander Aring09215592017-12-20 12:35:12 -05001604 if (!q) {
1605 NL_SET_ERR_MSG(extack, "Specified qdisc not found");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001607 }
1608 if (n->nlmsg_flags & NLM_F_EXCL) {
1609 NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot modify");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 return -EEXIST;
Alexander Aring09215592017-12-20 12:35:12 -05001611 }
1612 if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id)) {
1613 NL_SET_ERR_MSG(extack, "Invalid qdisc name");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 return -EINVAL;
Alexander Aring09215592017-12-20 12:35:12 -05001615 }
1616 err = qdisc_change(q, tca, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 if (err == 0)
Tom Goff7316ae82010-03-19 15:40:13 +00001618 qdisc_notify(net, skb, n, clid, NULL, q);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 return err;
1620
1621create_n_graft:
Alexander Aring09215592017-12-20 12:35:12 -05001622 if (!(n->nlmsg_flags & NLM_F_CREATE)) {
1623 NL_SET_ERR_MSG(extack, "Qdisc not found. To create specify NLM_F_CREATE flag");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 return -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001625 }
Eric Dumazet24824a02010-10-02 06:11:55 +00001626 if (clid == TC_H_INGRESS) {
Alexander Aring09215592017-12-20 12:35:12 -05001627 if (dev_ingress_queue(dev)) {
Eric Dumazet24824a02010-10-02 06:11:55 +00001628 q = qdisc_create(dev, dev_ingress_queue(dev), p,
1629 tcm->tcm_parent, tcm->tcm_parent,
Alexander Aring09215592017-12-20 12:35:12 -05001630 tca, &err, extack);
1631 } else {
1632 NL_SET_ERR_MSG(extack, "Cannot find ingress queue for specified device");
Eric Dumazet24824a02010-10-02 06:11:55 +00001633 err = -ENOENT;
Alexander Aring09215592017-12-20 12:35:12 -05001634 }
Eric Dumazet24824a02010-10-02 06:11:55 +00001635 } else {
Jarek Poplawski926e61b2009-09-15 02:53:07 -07001636 struct netdev_queue *dev_queue;
David S. Miller6ec1c692009-09-06 01:58:51 -07001637
1638 if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue)
Jarek Poplawski926e61b2009-09-15 02:53:07 -07001639 dev_queue = p->ops->cl_ops->select_queue(p, tcm);
1640 else if (p)
1641 dev_queue = p->dev_queue;
1642 else
1643 dev_queue = netdev_get_tx_queue(dev, 0);
David S. Miller6ec1c692009-09-06 01:58:51 -07001644
Jarek Poplawski926e61b2009-09-15 02:53:07 -07001645 q = qdisc_create(dev, dev_queue, p,
David S. Millerbb949fb2008-07-08 16:55:56 -07001646 tcm->tcm_parent, tcm->tcm_handle,
Alexander Aring09215592017-12-20 12:35:12 -05001647 tca, &err, extack);
David S. Miller6ec1c692009-09-06 01:58:51 -07001648 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 if (q == NULL) {
1650 if (err == -EAGAIN)
1651 goto replay;
1652 return err;
1653 }
1654
1655graft:
Alexander Aring09215592017-12-20 12:35:12 -05001656 err = qdisc_graft(dev, p, skb, n, clid, q, NULL, extack);
Ilpo Järvinene5befbd2008-08-18 22:30:01 -07001657 if (err) {
1658 if (q)
Vlad Buslov86bd4462018-09-24 19:22:50 +03001659 qdisc_put(q);
Ilpo Järvinene5befbd2008-08-18 22:30:01 -07001660 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 }
Ilpo Järvinene5befbd2008-08-18 22:30:01 -07001662
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 return 0;
1664}
1665
David S. Miller30723672008-07-18 22:50:15 -07001666static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
1667 struct netlink_callback *cb,
Jiri Kosina49b49972017-03-08 16:03:32 +01001668 int *q_idx_p, int s_q_idx, bool recur,
1669 bool dump_invisible)
David S. Miller30723672008-07-18 22:50:15 -07001670{
1671 int ret = 0, q_idx = *q_idx_p;
1672 struct Qdisc *q;
Jiri Kosina59cc1f62016-08-10 11:05:15 +02001673 int b;
David S. Miller30723672008-07-18 22:50:15 -07001674
1675 if (!root)
1676 return 0;
1677
1678 q = root;
1679 if (q_idx < s_q_idx) {
1680 q_idx++;
1681 } else {
Jiri Kosina49b49972017-03-08 16:03:32 +01001682 if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
Eric W. Biederman15e47302012-09-07 20:12:54 +00001683 tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001684 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1685 RTM_NEWQDISC) <= 0)
David S. Miller30723672008-07-18 22:50:15 -07001686 goto done;
1687 q_idx++;
1688 }
Jiri Kosina69012ae2016-08-16 23:52:58 +02001689
Jiri Kosinaea327462016-08-16 23:53:46 +02001690 /* If dumping singletons, there is no qdisc_dev(root) and the singleton
1691 * itself has already been dumped.
1692 *
1693 * If we've already dumped the top-level (ingress) qdisc above and the global
1694 * qdisc hashtable, we don't want to hit it again
1695 */
1696 if (!qdisc_dev(root) || !recur)
Jiri Kosina69012ae2016-08-16 23:52:58 +02001697 goto out;
1698
Jiri Kosina59cc1f62016-08-10 11:05:15 +02001699 hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) {
David S. Miller30723672008-07-18 22:50:15 -07001700 if (q_idx < s_q_idx) {
1701 q_idx++;
1702 continue;
1703 }
Jiri Kosina49b49972017-03-08 16:03:32 +01001704 if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
Eric W. Biederman15e47302012-09-07 20:12:54 +00001705 tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001706 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1707 RTM_NEWQDISC) <= 0)
David S. Miller30723672008-07-18 22:50:15 -07001708 goto done;
1709 q_idx++;
1710 }
1711
1712out:
1713 *q_idx_p = q_idx;
1714 return ret;
1715done:
1716 ret = -1;
1717 goto out;
1718}
1719
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
1721{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001722 struct net *net = sock_net(skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 int idx, q_idx;
1724 int s_idx, s_q_idx;
1725 struct net_device *dev;
Jiri Kosina49b49972017-03-08 16:03:32 +01001726 const struct nlmsghdr *nlh = cb->nlh;
Jiri Kosina49b49972017-03-08 16:03:32 +01001727 struct nlattr *tca[TCA_MAX + 1];
1728 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
1730 s_idx = cb->args[0];
1731 s_q_idx = q_idx = cb->args[1];
stephen hemmingerf1e90162009-11-10 07:54:49 +00001732
Pavel Emelianov7562f872007-05-03 15:13:45 -07001733 idx = 0;
Eric Dumazet15dc36e2014-03-10 17:11:42 -07001734 ASSERT_RTNL();
Jiri Kosina49b49972017-03-08 16:03:32 +01001735
David Ahern8b4c3cd2018-10-03 15:05:36 -07001736 err = nlmsg_parse(nlh, sizeof(struct tcmsg), tca, TCA_MAX,
David Aherndac9c972018-10-07 20:16:24 -07001737 rtm_tca_policy, cb->extack);
Jiri Kosina49b49972017-03-08 16:03:32 +01001738 if (err < 0)
1739 return err;
1740
Eric Dumazet15dc36e2014-03-10 17:11:42 -07001741 for_each_netdev(net, dev) {
David S. Miller30723672008-07-18 22:50:15 -07001742 struct netdev_queue *dev_queue;
1743
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 if (idx < s_idx)
Pavel Emelianov7562f872007-05-03 15:13:45 -07001745 goto cont;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 if (idx > s_idx)
1747 s_q_idx = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 q_idx = 0;
David S. Miller30723672008-07-18 22:50:15 -07001749
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001750 if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx,
Jiri Kosina49b49972017-03-08 16:03:32 +01001751 true, tca[TCA_DUMP_INVISIBLE]) < 0)
David S. Miller30723672008-07-18 22:50:15 -07001752 goto done;
1753
Eric Dumazet24824a02010-10-02 06:11:55 +00001754 dev_queue = dev_ingress_queue(dev);
1755 if (dev_queue &&
1756 tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb,
Jiri Kosina49b49972017-03-08 16:03:32 +01001757 &q_idx, s_q_idx, false,
1758 tca[TCA_DUMP_INVISIBLE]) < 0)
David S. Miller30723672008-07-18 22:50:15 -07001759 goto done;
1760
Pavel Emelianov7562f872007-05-03 15:13:45 -07001761cont:
1762 idx++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 }
1764
1765done:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 cb->args[0] = idx;
1767 cb->args[1] = q_idx;
1768
1769 return skb->len;
1770}
1771
1772
1773
1774/************************************************
1775 * Traffic classes manipulation. *
1776 ************************************************/
1777
WANG Cong27d7f072017-08-24 16:51:27 -07001778static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
1779 unsigned long cl,
1780 u32 portid, u32 seq, u16 flags, int event)
1781{
1782 struct tcmsg *tcm;
1783 struct nlmsghdr *nlh;
1784 unsigned char *b = skb_tail_pointer(skb);
1785 struct gnet_dump d;
1786 const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
WANG Cong27d7f072017-08-24 16:51:27 -07001788 cond_resched();
1789 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
1790 if (!nlh)
1791 goto out_nlmsg_trim;
1792 tcm = nlmsg_data(nlh);
1793 tcm->tcm_family = AF_UNSPEC;
1794 tcm->tcm__pad1 = 0;
1795 tcm->tcm__pad2 = 0;
1796 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
1797 tcm->tcm_parent = q->handle;
1798 tcm->tcm_handle = q->handle;
1799 tcm->tcm_info = 0;
1800 if (nla_put_string(skb, TCA_KIND, q->ops->id))
1801 goto nla_put_failure;
1802 if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0)
1803 goto nla_put_failure;
1804
1805 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
1806 NULL, &d, TCA_PAD) < 0)
1807 goto nla_put_failure;
1808
1809 if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0)
1810 goto nla_put_failure;
1811
1812 if (gnet_stats_finish_copy(&d) < 0)
1813 goto nla_put_failure;
1814
1815 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1816 return skb->len;
1817
1818out_nlmsg_trim:
1819nla_put_failure:
1820 nlmsg_trim(skb, b);
1821 return -1;
1822}
1823
1824static int tclass_notify(struct net *net, struct sk_buff *oskb,
1825 struct nlmsghdr *n, struct Qdisc *q,
1826 unsigned long cl, int event)
1827{
1828 struct sk_buff *skb;
1829 u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
1830
1831 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1832 if (!skb)
1833 return -ENOBUFS;
1834
1835 if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) {
1836 kfree_skb(skb);
1837 return -EINVAL;
1838 }
1839
1840 return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1841 n->nlmsg_flags & NLM_F_ECHO);
1842}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
WANG Cong14546ba2017-08-24 16:51:28 -07001844static int tclass_del_notify(struct net *net,
1845 const struct Qdisc_class_ops *cops,
1846 struct sk_buff *oskb, struct nlmsghdr *n,
1847 struct Qdisc *q, unsigned long cl)
1848{
1849 u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
1850 struct sk_buff *skb;
1851 int err = 0;
1852
1853 if (!cops->delete)
1854 return -EOPNOTSUPP;
1855
1856 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1857 if (!skb)
1858 return -ENOBUFS;
1859
1860 if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
1861 RTM_DELTCLASS) < 0) {
1862 kfree_skb(skb);
1863 return -EINVAL;
1864 }
1865
1866 err = cops->delete(q, cl);
1867 if (err) {
1868 kfree_skb(skb);
1869 return err;
1870 }
1871
1872 return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1873 n->nlmsg_flags & NLM_F_ECHO);
1874}
1875
Cong Wang07d79fc2017-08-30 14:30:36 -07001876#ifdef CONFIG_NET_CLS
1877
1878struct tcf_bind_args {
1879 struct tcf_walker w;
1880 u32 classid;
1881 unsigned long cl;
1882};
1883
1884static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
1885{
1886 struct tcf_bind_args *a = (void *)arg;
1887
1888 if (tp->ops->bind_class) {
Jiri Pirko74e3be62017-10-13 14:01:04 +02001889 struct Qdisc *q = tcf_block_q(tp->chain->block);
1890
1891 sch_tree_lock(q);
Cong Wang07d79fc2017-08-30 14:30:36 -07001892 tp->ops->bind_class(n, a->classid, a->cl);
Jiri Pirko74e3be62017-10-13 14:01:04 +02001893 sch_tree_unlock(q);
Cong Wang07d79fc2017-08-30 14:30:36 -07001894 }
1895 return 0;
1896}
1897
1898static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
1899 unsigned long new_cl)
1900{
1901 const struct Qdisc_class_ops *cops = q->ops->cl_ops;
1902 struct tcf_block *block;
1903 struct tcf_chain *chain;
1904 unsigned long cl;
1905
1906 cl = cops->find(q, portid);
1907 if (!cl)
1908 return;
Alexander Aringcbaacc42017-12-20 12:35:16 -05001909 block = cops->tcf_block(q, cl, NULL);
Cong Wang07d79fc2017-08-30 14:30:36 -07001910 if (!block)
1911 return;
Vlad Buslovbbf73832019-02-11 10:55:36 +02001912 for (chain = tcf_get_next_chain(block, NULL);
1913 chain;
1914 chain = tcf_get_next_chain(block, chain)) {
Cong Wang07d79fc2017-08-30 14:30:36 -07001915 struct tcf_proto *tp;
1916
Vlad Buslov12db03b2019-02-11 10:55:45 +02001917 for (tp = tcf_get_next_proto(chain, NULL, true);
1918 tp; tp = tcf_get_next_proto(chain, tp, true)) {
Cong Wang07d79fc2017-08-30 14:30:36 -07001919 struct tcf_bind_args arg = {};
1920
1921 arg.w.fn = tcf_node_bind;
1922 arg.classid = clid;
1923 arg.cl = new_cl;
Vlad Buslov12db03b2019-02-11 10:55:45 +02001924 tp->ops->walk(tp, &arg.w, true);
Cong Wang07d79fc2017-08-30 14:30:36 -07001925 }
1926 }
1927}
1928
1929#else
1930
1931static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
1932 unsigned long new_cl)
1933{
1934}
1935
1936#endif
1937
David Ahernc21ef3e2017-04-16 09:48:24 -07001938static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
1939 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001941 struct net *net = sock_net(skb->sk);
David S. Miller02ef22c2012-06-26 21:50:05 -07001942 struct tcmsg *tcm = nlmsg_data(n);
Patrick McHardy1e904742008-01-22 22:11:17 -08001943 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 struct net_device *dev;
1945 struct Qdisc *q = NULL;
Eric Dumazet20fea082007-11-14 01:44:41 -08001946 const struct Qdisc_class_ops *cops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 unsigned long cl = 0;
1948 unsigned long new_cl;
Hong zhi guode179c82013-03-25 17:36:33 +00001949 u32 portid;
1950 u32 clid;
1951 u32 qid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 int err;
1953
Stéphane Graber4e8bbb82014-04-30 11:25:43 -04001954 if ((n->nlmsg_type != RTM_GETTCLASS) &&
David S. Miller5f013c9b2014-05-12 13:19:14 -04001955 !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +00001956 return -EPERM;
1957
David Ahern8b4c3cd2018-10-03 15:05:36 -07001958 err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy,
1959 extack);
Patrick McHardy1e904742008-01-22 22:11:17 -08001960 if (err < 0)
1961 return err;
1962
Hong zhi guode179c82013-03-25 17:36:33 +00001963 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
1964 if (!dev)
1965 return -ENODEV;
1966
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 /*
1968 parent == TC_H_UNSPEC - unspecified parent.
1969 parent == TC_H_ROOT - class is root, which has no parent.
1970 parent == X:0 - parent is root class.
1971 parent == X:Y - parent is a node in hierarchy.
1972 parent == 0:Y - parent is X:Y, where X:0 is qdisc.
1973
1974 handle == 0:0 - generate handle from kernel pool.
1975 handle == 0:Y - class is X:Y, where X:0 is qdisc.
1976 handle == X:Y - clear.
1977 handle == X:0 - root class.
1978 */
1979
1980 /* Step 1. Determine qdisc handle X:0 */
1981
Hong zhi guode179c82013-03-25 17:36:33 +00001982 portid = tcm->tcm_parent;
1983 clid = tcm->tcm_handle;
1984 qid = TC_H_MAJ(clid);
1985
Eric W. Biederman15e47302012-09-07 20:12:54 +00001986 if (portid != TC_H_ROOT) {
1987 u32 qid1 = TC_H_MAJ(portid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988
1989 if (qid && qid1) {
1990 /* If both majors are known, they must be identical. */
1991 if (qid != qid1)
1992 return -EINVAL;
1993 } else if (qid1) {
1994 qid = qid1;
1995 } else if (qid == 0)
Patrick McHardyaf356af2009-09-04 06:41:18 +00001996 qid = dev->qdisc->handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
1998 /* Now qid is genuine qdisc handle consistent
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001999 * both with parent and child.
2000 *
Eric W. Biederman15e47302012-09-07 20:12:54 +00002001 * TC_H_MAJ(portid) still may be unspecified, complete it now.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 */
Eric W. Biederman15e47302012-09-07 20:12:54 +00002003 if (portid)
2004 portid = TC_H_MAKE(qid, portid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 } else {
2006 if (qid == 0)
Patrick McHardyaf356af2009-09-04 06:41:18 +00002007 qid = dev->qdisc->handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 }
2009
2010 /* OK. Locate qdisc */
Eric Dumazetcc7ec452011-01-19 19:26:56 +00002011 q = qdisc_lookup(dev, qid);
2012 if (!q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 return -ENOENT;
2014
2015 /* An check that it supports classes */
2016 cops = q->ops->cl_ops;
2017 if (cops == NULL)
2018 return -EINVAL;
2019
2020 /* Now try to get class */
2021 if (clid == 0) {
Eric W. Biederman15e47302012-09-07 20:12:54 +00002022 if (portid == TC_H_ROOT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 clid = qid;
2024 } else
2025 clid = TC_H_MAKE(qid, clid);
2026
2027 if (clid)
WANG Cong143976c2017-08-24 16:51:29 -07002028 cl = cops->find(q, clid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
2030 if (cl == 0) {
2031 err = -ENOENT;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00002032 if (n->nlmsg_type != RTM_NEWTCLASS ||
2033 !(n->nlmsg_flags & NLM_F_CREATE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 goto out;
2035 } else {
2036 switch (n->nlmsg_type) {
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +09002037 case RTM_NEWTCLASS:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038 err = -EEXIST;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00002039 if (n->nlmsg_flags & NLM_F_EXCL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 goto out;
2041 break;
2042 case RTM_DELTCLASS:
WANG Cong14546ba2017-08-24 16:51:28 -07002043 err = tclass_del_notify(net, cops, skb, n, q, cl);
Cong Wang07d79fc2017-08-30 14:30:36 -07002044 /* Unbind the class with flilters with 0 */
2045 tc_bind_tclass(q, portid, clid, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 goto out;
2047 case RTM_GETTCLASS:
Tom Goff7316ae82010-03-19 15:40:13 +00002048 err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 goto out;
2050 default:
2051 err = -EINVAL;
2052 goto out;
2053 }
2054 }
2055
Jiri Pirkod47a6b02018-01-17 11:46:52 +01002056 if (tca[TCA_INGRESS_BLOCK] || tca[TCA_EGRESS_BLOCK]) {
2057 NL_SET_ERR_MSG(extack, "Shared blocks are not supported for classes");
2058 return -EOPNOTSUPP;
2059 }
2060
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 new_cl = cl;
Patrick McHardyde6d5cd2009-09-04 06:41:16 +00002062 err = -EOPNOTSUPP;
2063 if (cops->change)
Alexander Aring793d81d2017-12-20 12:35:15 -05002064 err = cops->change(q, clid, portid, tca, &new_cl, extack);
Cong Wang07d79fc2017-08-30 14:30:36 -07002065 if (err == 0) {
Tom Goff7316ae82010-03-19 15:40:13 +00002066 tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS);
Cong Wang07d79fc2017-08-30 14:30:36 -07002067 /* We just create a new class, need to do reverse binding. */
2068 if (cl != new_cl)
2069 tc_bind_tclass(q, portid, clid, new_cl);
2070 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 return err;
2073}
2074
Eric Dumazetcc7ec452011-01-19 19:26:56 +00002075struct qdisc_dump_args {
2076 struct qdisc_walker w;
2077 struct sk_buff *skb;
2078 struct netlink_callback *cb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079};
2080
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04002081static int qdisc_class_dump(struct Qdisc *q, unsigned long cl,
2082 struct qdisc_walker *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083{
2084 struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg;
2085
Eric W. Biederman15e47302012-09-07 20:12:54 +00002086 return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04002087 a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
2088 RTM_NEWTCLASS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089}
2090
David S. Miller30723672008-07-18 22:50:15 -07002091static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb,
2092 struct tcmsg *tcm, struct netlink_callback *cb,
2093 int *t_p, int s_t)
2094{
2095 struct qdisc_dump_args arg;
2096
Jiri Kosina49b49972017-03-08 16:03:32 +01002097 if (tc_qdisc_dump_ignore(q, false) ||
David S. Miller30723672008-07-18 22:50:15 -07002098 *t_p < s_t || !q->ops->cl_ops ||
2099 (tcm->tcm_parent &&
2100 TC_H_MAJ(tcm->tcm_parent) != q->handle)) {
2101 (*t_p)++;
2102 return 0;
2103 }
2104 if (*t_p > s_t)
2105 memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
2106 arg.w.fn = qdisc_class_dump;
2107 arg.skb = skb;
2108 arg.cb = cb;
2109 arg.w.stop = 0;
2110 arg.w.skip = cb->args[1];
2111 arg.w.count = 0;
2112 q->ops->cl_ops->walk(q, &arg.w);
2113 cb->args[1] = arg.w.count;
2114 if (arg.w.stop)
2115 return -1;
2116 (*t_p)++;
2117 return 0;
2118}
2119
2120static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb,
2121 struct tcmsg *tcm, struct netlink_callback *cb,
2122 int *t_p, int s_t)
2123{
2124 struct Qdisc *q;
Jiri Kosina59cc1f62016-08-10 11:05:15 +02002125 int b;
David S. Miller30723672008-07-18 22:50:15 -07002126
2127 if (!root)
2128 return 0;
2129
2130 if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0)
2131 return -1;
2132
Jiri Kosina69012ae2016-08-16 23:52:58 +02002133 if (!qdisc_dev(root))
2134 return 0;
2135
Eric Dumazetcb395b22017-05-10 21:59:28 -07002136 if (tcm->tcm_parent) {
2137 q = qdisc_match_from_root(root, TC_H_MAJ(tcm->tcm_parent));
Phil Sutter3c53ed82018-10-18 10:34:26 +02002138 if (q && q != root &&
2139 tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0)
Eric Dumazetcb395b22017-05-10 21:59:28 -07002140 return -1;
2141 return 0;
2142 }
Jiri Kosina59cc1f62016-08-10 11:05:15 +02002143 hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) {
David S. Miller30723672008-07-18 22:50:15 -07002144 if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0)
2145 return -1;
2146 }
2147
2148 return 0;
2149}
2150
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
2152{
David S. Miller02ef22c2012-06-26 21:50:05 -07002153 struct tcmsg *tcm = nlmsg_data(cb->nlh);
David S. Miller30723672008-07-18 22:50:15 -07002154 struct net *net = sock_net(skb->sk);
2155 struct netdev_queue *dev_queue;
2156 struct net_device *dev;
2157 int t, s_t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158
Hong zhi guo573ce262013-03-27 06:47:04 +00002159 if (nlmsg_len(cb->nlh) < sizeof(*tcm))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 return 0;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00002161 dev = dev_get_by_index(net, tcm->tcm_ifindex);
2162 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 return 0;
2164
2165 s_t = cb->args[0];
2166 t = 0;
2167
Patrick McHardyaf356af2009-09-04 06:41:18 +00002168 if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0)
David S. Miller30723672008-07-18 22:50:15 -07002169 goto done;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
Eric Dumazet24824a02010-10-02 06:11:55 +00002171 dev_queue = dev_ingress_queue(dev);
2172 if (dev_queue &&
2173 tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb,
2174 &t, s_t) < 0)
David S. Miller30723672008-07-18 22:50:15 -07002175 goto done;
2176
2177done:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 cb->args[0] = t;
2179
2180 dev_put(dev);
2181 return skb->len;
2182}
2183
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184#ifdef CONFIG_PROC_FS
2185static int psched_show(struct seq_file *seq, void *v)
2186{
2187 seq_printf(seq, "%08x %08x %08x %08x\n",
Jarek Poplawskica44d6e2009-06-15 02:31:47 -07002188 (u32)NSEC_PER_USEC, (u32)PSCHED_TICKS2NS(1),
Patrick McHardy514bca32007-03-16 12:34:52 -07002189 1000000,
Thomas Gleixner1e317682015-04-14 21:08:28 +00002190 (u32)NSEC_PER_SEC / hrtimer_resolution);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191
2192 return 0;
2193}
2194
Tom Goff7316ae82010-03-19 15:40:13 +00002195static int __net_init psched_net_init(struct net *net)
2196{
2197 struct proc_dir_entry *e;
2198
Christoph Hellwig3f3942a2018-05-15 15:57:23 +02002199 e = proc_create_single("psched", 0, net->proc_net, psched_show);
Tom Goff7316ae82010-03-19 15:40:13 +00002200 if (e == NULL)
2201 return -ENOMEM;
2202
2203 return 0;
2204}
2205
2206static void __net_exit psched_net_exit(struct net *net)
2207{
Gao fengece31ff2013-02-18 01:34:56 +00002208 remove_proc_entry("psched", net->proc_net);
Tom Goff7316ae82010-03-19 15:40:13 +00002209}
2210#else
2211static int __net_init psched_net_init(struct net *net)
2212{
2213 return 0;
2214}
2215
2216static void __net_exit psched_net_exit(struct net *net)
2217{
2218}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219#endif
2220
Tom Goff7316ae82010-03-19 15:40:13 +00002221static struct pernet_operations psched_net_ops = {
2222 .init = psched_net_init,
2223 .exit = psched_net_exit,
2224};
2225
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226static int __init pktsched_init(void)
2227{
Tom Goff7316ae82010-03-19 15:40:13 +00002228 int err;
2229
2230 err = register_pernet_subsys(&psched_net_ops);
2231 if (err) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +00002232 pr_err("pktsched_init: "
Tom Goff7316ae82010-03-19 15:40:13 +00002233 "cannot initialize per netns operations\n");
2234 return err;
2235 }
2236
stephen hemminger6da7c8f2013-08-27 16:19:08 -07002237 register_qdisc(&pfifo_fast_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 register_qdisc(&pfifo_qdisc_ops);
2239 register_qdisc(&bfifo_qdisc_ops);
Hagen Paul Pfeifer57dbb2d2010-01-24 12:30:59 +00002240 register_qdisc(&pfifo_head_drop_qdisc_ops);
David S. Miller6ec1c692009-09-06 01:58:51 -07002241 register_qdisc(&mq_qdisc_ops);
Phil Sutterd66d6c32015-08-27 21:21:38 +02002242 register_qdisc(&noqueue_qdisc_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243
Florian Westphalb97bac62017-08-09 20:41:48 +02002244 rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, 0);
2245 rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, 0);
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04002246 rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc,
Florian Westphalb97bac62017-08-09 20:41:48 +02002247 0);
2248 rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, 0);
2249 rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, 0);
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04002250 rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass,
Florian Westphalb97bac62017-08-09 20:41:48 +02002251 0);
Thomas Grafbe577dd2007-03-22 11:55:50 -07002252
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 return 0;
2254}
2255
2256subsys_initcall(pktsched_init);