162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/sch_api.c Packet scheduler API. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Fixes: 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired. 1062306a36Sopenharmony_ci * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support 1162306a36Sopenharmony_ci * Jamal Hadi Salim <hadi@nortelnetworks.com>: 990601: ingress support 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/skbuff.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/proc_fs.h> 2262306a36Sopenharmony_ci#include <linux/seq_file.h> 2362306a36Sopenharmony_ci#include <linux/kmod.h> 2462306a36Sopenharmony_ci#include <linux/list.h> 2562306a36Sopenharmony_ci#include <linux/hrtimer.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/hashtable.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <net/net_namespace.h> 3062306a36Sopenharmony_ci#include <net/sock.h> 3162306a36Sopenharmony_ci#include <net/netlink.h> 3262306a36Sopenharmony_ci#include <net/pkt_sched.h> 3362306a36Sopenharmony_ci#include <net/pkt_cls.h> 3462306a36Sopenharmony_ci#include <net/tc_wrapper.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <trace/events/qdisc.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci Short review. 4162306a36Sopenharmony_ci ------------- 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci This file consists of two interrelated parts: 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 1. queueing disciplines manager frontend. 4662306a36Sopenharmony_ci 2. traffic classes manager frontend. 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci Generally, queueing discipline ("qdisc") is a black box, 4962306a36Sopenharmony_ci which is able to enqueue packets and to dequeue them (when 5062306a36Sopenharmony_ci device is ready to send something) in order and at times 5162306a36Sopenharmony_ci determined by algorithm hidden in it. 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci qdisc's are divided to two categories: 5462306a36Sopenharmony_ci - "queues", which have no internal structure visible from outside. 5562306a36Sopenharmony_ci - "schedulers", which split all the packets to "traffic classes", 5662306a36Sopenharmony_ci using "packet classifiers" (look at cls_api.c) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci In turn, classes may have child qdiscs (as rule, queues) 5962306a36Sopenharmony_ci attached to them etc. etc. etc. 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci The goal of the routines in this file is to translate 6262306a36Sopenharmony_ci information supplied by user in the form of handles 6362306a36Sopenharmony_ci to more intelligible for kernel form, to make some sanity 6462306a36Sopenharmony_ci checks and part of work, which is common to all qdiscs 6562306a36Sopenharmony_ci and to provide rtnetlink notifications. 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci All real intelligent work is done inside qdisc modules. 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci Every discipline has two major routines: enqueue and dequeue. 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ---dequeue 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci dequeue usually returns a skb to send. It is allowed to return NULL, 7662306a36Sopenharmony_ci but it does not mean that queue is empty, it just means that 7762306a36Sopenharmony_ci discipline does not want to send anything this time. 7862306a36Sopenharmony_ci Queue is really empty if q->q.qlen == 0. 7962306a36Sopenharmony_ci For complicated disciplines with multiple queues q->q is not 8062306a36Sopenharmony_ci real packet queue, but however q->q.qlen must be valid. 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ---enqueue 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci enqueue returns 0, if packet was enqueued successfully. 8562306a36Sopenharmony_ci If packet (this one or another one) was dropped, it returns 8662306a36Sopenharmony_ci not zero error code. 8762306a36Sopenharmony_ci NET_XMIT_DROP - this packet dropped 8862306a36Sopenharmony_ci Expected action: do not backoff, but wait until queue will clear. 8962306a36Sopenharmony_ci NET_XMIT_CN - probably this packet enqueued, but another one dropped. 9062306a36Sopenharmony_ci Expected action: backoff or ignore 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci Auxiliary routines: 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ---peek 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci like dequeue but without removing a packet from the queue 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ---reset 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci returns qdisc to initial state: purge all buffers, clear all 10162306a36Sopenharmony_ci timers, counters (except for statistics) etc. 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ---init 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci initializes newly created qdisc. 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ---destroy 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci destroys resources allocated by init and during lifetime of qdisc. 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ---change 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci changes qdisc parameters. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Protects list of registered TC modules. It is pure SMP lock. */ 11762306a36Sopenharmony_cistatic DEFINE_RWLOCK(qdisc_mod_lock); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/************************************************ 12162306a36Sopenharmony_ci * Queueing disciplines manipulation. * 12262306a36Sopenharmony_ci ************************************************/ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* The list of all installed queueing disciplines. */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct Qdisc_ops *qdisc_base; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Register/unregister queueing discipline */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciint register_qdisc(struct Qdisc_ops *qops) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct Qdisc_ops *q, **qp; 13462306a36Sopenharmony_ci int rc = -EEXIST; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci write_lock(&qdisc_mod_lock); 13762306a36Sopenharmony_ci for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next) 13862306a36Sopenharmony_ci if (!strcmp(qops->id, q->id)) 13962306a36Sopenharmony_ci goto out; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (qops->enqueue == NULL) 14262306a36Sopenharmony_ci qops->enqueue = noop_qdisc_ops.enqueue; 14362306a36Sopenharmony_ci if (qops->peek == NULL) { 14462306a36Sopenharmony_ci if (qops->dequeue == NULL) 14562306a36Sopenharmony_ci qops->peek = noop_qdisc_ops.peek; 14662306a36Sopenharmony_ci else 14762306a36Sopenharmony_ci goto out_einval; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci if (qops->dequeue == NULL) 15062306a36Sopenharmony_ci qops->dequeue = noop_qdisc_ops.dequeue; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (qops->cl_ops) { 15362306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = qops->cl_ops; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!(cops->find && cops->walk && cops->leaf)) 15662306a36Sopenharmony_ci goto out_einval; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (cops->tcf_block && !(cops->bind_tcf && cops->unbind_tcf)) 15962306a36Sopenharmony_ci goto out_einval; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci qops->next = NULL; 16362306a36Sopenharmony_ci *qp = qops; 16462306a36Sopenharmony_ci rc = 0; 16562306a36Sopenharmony_ciout: 16662306a36Sopenharmony_ci write_unlock(&qdisc_mod_lock); 16762306a36Sopenharmony_ci return rc; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciout_einval: 17062306a36Sopenharmony_ci rc = -EINVAL; 17162306a36Sopenharmony_ci goto out; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL(register_qdisc); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_civoid unregister_qdisc(struct Qdisc_ops *qops) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct Qdisc_ops *q, **qp; 17862306a36Sopenharmony_ci int err = -ENOENT; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci write_lock(&qdisc_mod_lock); 18162306a36Sopenharmony_ci for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next) 18262306a36Sopenharmony_ci if (q == qops) 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci if (q) { 18562306a36Sopenharmony_ci *qp = q->next; 18662306a36Sopenharmony_ci q->next = NULL; 18762306a36Sopenharmony_ci err = 0; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci write_unlock(&qdisc_mod_lock); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci WARN(err, "unregister qdisc(%s) failed\n", qops->id); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_qdisc); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* Get default qdisc if not otherwise specified */ 19662306a36Sopenharmony_civoid qdisc_get_default(char *name, size_t len) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci read_lock(&qdisc_mod_lock); 19962306a36Sopenharmony_ci strscpy(name, default_qdisc_ops->id, len); 20062306a36Sopenharmony_ci read_unlock(&qdisc_mod_lock); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct Qdisc_ops *qdisc_lookup_default(const char *name) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct Qdisc_ops *q = NULL; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (q = qdisc_base; q; q = q->next) { 20862306a36Sopenharmony_ci if (!strcmp(name, q->id)) { 20962306a36Sopenharmony_ci if (!try_module_get(q->owner)) 21062306a36Sopenharmony_ci q = NULL; 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return q; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* Set new default qdisc to use */ 21962306a36Sopenharmony_ciint qdisc_set_default(const char *name) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci const struct Qdisc_ops *ops; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 22462306a36Sopenharmony_ci return -EPERM; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci write_lock(&qdisc_mod_lock); 22762306a36Sopenharmony_ci ops = qdisc_lookup_default(name); 22862306a36Sopenharmony_ci if (!ops) { 22962306a36Sopenharmony_ci /* Not found, drop lock and try to load module */ 23062306a36Sopenharmony_ci write_unlock(&qdisc_mod_lock); 23162306a36Sopenharmony_ci request_module("sch_%s", name); 23262306a36Sopenharmony_ci write_lock(&qdisc_mod_lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ops = qdisc_lookup_default(name); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (ops) { 23862306a36Sopenharmony_ci /* Set new default */ 23962306a36Sopenharmony_ci module_put(default_qdisc_ops->owner); 24062306a36Sopenharmony_ci default_qdisc_ops = ops; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci write_unlock(&qdisc_mod_lock); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return ops ? 0 : -ENOENT; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#ifdef CONFIG_NET_SCH_DEFAULT 24862306a36Sopenharmony_ci/* Set default value from kernel config */ 24962306a36Sopenharmony_cistatic int __init sch_default_qdisc(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci return qdisc_set_default(CONFIG_DEFAULT_NET_SCH); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_cilate_initcall(sch_default_qdisc); 25462306a36Sopenharmony_ci#endif 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* We know handle. Find qdisc among all qdisc's attached to device 25762306a36Sopenharmony_ci * (root qdisc, all its children, children of children etc.) 25862306a36Sopenharmony_ci * Note: caller either uses rtnl or rcu_read_lock() 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct Qdisc *q; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (!qdisc_dev(root)) 26662306a36Sopenharmony_ci return (root->handle == handle ? root : NULL); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!(root->flags & TCQ_F_BUILTIN) && 26962306a36Sopenharmony_ci root->handle == handle) 27062306a36Sopenharmony_ci return root; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle, 27362306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 27462306a36Sopenharmony_ci if (q->handle == handle) 27562306a36Sopenharmony_ci return q; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci return NULL; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_civoid qdisc_hash_add(struct Qdisc *q, bool invisible) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { 28362306a36Sopenharmony_ci ASSERT_RTNL(); 28462306a36Sopenharmony_ci hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle); 28562306a36Sopenharmony_ci if (invisible) 28662306a36Sopenharmony_ci q->flags |= TCQ_F_INVISIBLE; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_hash_add); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_civoid qdisc_hash_del(struct Qdisc *q) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { 29462306a36Sopenharmony_ci ASSERT_RTNL(); 29562306a36Sopenharmony_ci hash_del_rcu(&q->hash); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_hash_del); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistruct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct Qdisc *q; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!handle) 30562306a36Sopenharmony_ci return NULL; 30662306a36Sopenharmony_ci q = qdisc_match_from_root(rtnl_dereference(dev->qdisc), handle); 30762306a36Sopenharmony_ci if (q) 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (dev_ingress_queue(dev)) 31162306a36Sopenharmony_ci q = qdisc_match_from_root( 31262306a36Sopenharmony_ci rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping), 31362306a36Sopenharmony_ci handle); 31462306a36Sopenharmony_ciout: 31562306a36Sopenharmony_ci return q; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistruct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct netdev_queue *nq; 32162306a36Sopenharmony_ci struct Qdisc *q; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!handle) 32462306a36Sopenharmony_ci return NULL; 32562306a36Sopenharmony_ci q = qdisc_match_from_root(rcu_dereference(dev->qdisc), handle); 32662306a36Sopenharmony_ci if (q) 32762306a36Sopenharmony_ci goto out; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci nq = dev_ingress_queue_rcu(dev); 33062306a36Sopenharmony_ci if (nq) 33162306a36Sopenharmony_ci q = qdisc_match_from_root(rcu_dereference(nq->qdisc_sleeping), 33262306a36Sopenharmony_ci handle); 33362306a36Sopenharmony_ciout: 33462306a36Sopenharmony_ci return q; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci unsigned long cl; 34062306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = p->ops->cl_ops; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (cops == NULL) 34362306a36Sopenharmony_ci return NULL; 34462306a36Sopenharmony_ci cl = cops->find(p, classid); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (cl == 0) 34762306a36Sopenharmony_ci return NULL; 34862306a36Sopenharmony_ci return cops->leaf(p, cl); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* Find queueing discipline by name */ 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic struct Qdisc_ops *qdisc_lookup_ops(struct nlattr *kind) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct Qdisc_ops *q = NULL; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (kind) { 35862306a36Sopenharmony_ci read_lock(&qdisc_mod_lock); 35962306a36Sopenharmony_ci for (q = qdisc_base; q; q = q->next) { 36062306a36Sopenharmony_ci if (nla_strcmp(kind, q->id) == 0) { 36162306a36Sopenharmony_ci if (!try_module_get(q->owner)) 36262306a36Sopenharmony_ci q = NULL; 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci read_unlock(&qdisc_mod_lock); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci return q; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* The linklayer setting were not transferred from iproute2, in older 37262306a36Sopenharmony_ci * versions, and the rate tables lookup systems have been dropped in 37362306a36Sopenharmony_ci * the kernel. To keep backward compatible with older iproute2 tc 37462306a36Sopenharmony_ci * utils, we detect the linklayer setting by detecting if the rate 37562306a36Sopenharmony_ci * table were modified. 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * For linklayer ATM table entries, the rate table will be aligned to 37862306a36Sopenharmony_ci * 48 bytes, thus some table entries will contain the same value. The 37962306a36Sopenharmony_ci * mpu (min packet unit) is also encoded into the old rate table, thus 38062306a36Sopenharmony_ci * starting from the mpu, we find low and high table entries for 38162306a36Sopenharmony_ci * mapping this cell. If these entries contain the same value, when 38262306a36Sopenharmony_ci * the rate tables have been modified for linklayer ATM. 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * This is done by rounding mpu to the nearest 48 bytes cell/entry, 38562306a36Sopenharmony_ci * and then roundup to the next cell, calc the table entry one below, 38662306a36Sopenharmony_ci * and compare. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cistatic __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int low = roundup(r->mpu, 48); 39162306a36Sopenharmony_ci int high = roundup(low+1, 48); 39262306a36Sopenharmony_ci int cell_low = low >> r->cell_log; 39362306a36Sopenharmony_ci int cell_high = (high >> r->cell_log) - 1; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* rtab is too inaccurate at rates > 100Mbit/s */ 39662306a36Sopenharmony_ci if ((r->rate > (100000000/8)) || (rtab[0] == 0)) { 39762306a36Sopenharmony_ci pr_debug("TC linklayer: Giving up ATM detection\n"); 39862306a36Sopenharmony_ci return TC_LINKLAYER_ETHERNET; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if ((cell_high > cell_low) && (cell_high < 256) 40262306a36Sopenharmony_ci && (rtab[cell_low] == rtab[cell_high])) { 40362306a36Sopenharmony_ci pr_debug("TC linklayer: Detected ATM, low(%d)=high(%d)=%u\n", 40462306a36Sopenharmony_ci cell_low, cell_high, rtab[cell_high]); 40562306a36Sopenharmony_ci return TC_LINKLAYER_ATM; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci return TC_LINKLAYER_ETHERNET; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic struct qdisc_rate_table *qdisc_rtab_list; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistruct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, 41362306a36Sopenharmony_ci struct nlattr *tab, 41462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct qdisc_rate_table *rtab; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (tab == NULL || r->rate == 0 || 41962306a36Sopenharmony_ci r->cell_log == 0 || r->cell_log >= 32 || 42062306a36Sopenharmony_ci nla_len(tab) != TC_RTAB_SIZE) { 42162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid rate table parameters for searching"); 42262306a36Sopenharmony_ci return NULL; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) { 42662306a36Sopenharmony_ci if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) && 42762306a36Sopenharmony_ci !memcmp(&rtab->data, nla_data(tab), 1024)) { 42862306a36Sopenharmony_ci rtab->refcnt++; 42962306a36Sopenharmony_ci return rtab; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci rtab = kmalloc(sizeof(*rtab), GFP_KERNEL); 43462306a36Sopenharmony_ci if (rtab) { 43562306a36Sopenharmony_ci rtab->rate = *r; 43662306a36Sopenharmony_ci rtab->refcnt = 1; 43762306a36Sopenharmony_ci memcpy(rtab->data, nla_data(tab), 1024); 43862306a36Sopenharmony_ci if (r->linklayer == TC_LINKLAYER_UNAWARE) 43962306a36Sopenharmony_ci r->linklayer = __detect_linklayer(r, rtab->data); 44062306a36Sopenharmony_ci rtab->next = qdisc_rtab_list; 44162306a36Sopenharmony_ci qdisc_rtab_list = rtab; 44262306a36Sopenharmony_ci } else { 44362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table"); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci return rtab; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_get_rtab); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_civoid qdisc_put_rtab(struct qdisc_rate_table *tab) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct qdisc_rate_table *rtab, **rtabp; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!tab || --tab->refcnt) 45462306a36Sopenharmony_ci return; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (rtabp = &qdisc_rtab_list; 45762306a36Sopenharmony_ci (rtab = *rtabp) != NULL; 45862306a36Sopenharmony_ci rtabp = &rtab->next) { 45962306a36Sopenharmony_ci if (rtab == tab) { 46062306a36Sopenharmony_ci *rtabp = rtab->next; 46162306a36Sopenharmony_ci kfree(rtab); 46262306a36Sopenharmony_ci return; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_put_rtab); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic LIST_HEAD(qdisc_stab_list); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = { 47162306a36Sopenharmony_ci [TCA_STAB_BASE] = { .len = sizeof(struct tc_sizespec) }, 47262306a36Sopenharmony_ci [TCA_STAB_DATA] = { .type = NLA_BINARY }, 47362306a36Sopenharmony_ci}; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt, 47662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct nlattr *tb[TCA_STAB_MAX + 1]; 47962306a36Sopenharmony_ci struct qdisc_size_table *stab; 48062306a36Sopenharmony_ci struct tc_sizespec *s; 48162306a36Sopenharmony_ci unsigned int tsize = 0; 48262306a36Sopenharmony_ci u16 *tab = NULL; 48362306a36Sopenharmony_ci int err; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_STAB_MAX, opt, stab_policy, 48662306a36Sopenharmony_ci extack); 48762306a36Sopenharmony_ci if (err < 0) 48862306a36Sopenharmony_ci return ERR_PTR(err); 48962306a36Sopenharmony_ci if (!tb[TCA_STAB_BASE]) { 49062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Size table base attribute is missing"); 49162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci s = nla_data(tb[TCA_STAB_BASE]); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (s->tsize > 0) { 49762306a36Sopenharmony_ci if (!tb[TCA_STAB_DATA]) { 49862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Size table data attribute is missing"); 49962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci tab = nla_data(tb[TCA_STAB_DATA]); 50262306a36Sopenharmony_ci tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (tsize != s->tsize || (!tab && tsize > 0)) { 50662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid size of size table"); 50762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci list_for_each_entry(stab, &qdisc_stab_list, list) { 51162306a36Sopenharmony_ci if (memcmp(&stab->szopts, s, sizeof(*s))) 51262306a36Sopenharmony_ci continue; 51362306a36Sopenharmony_ci if (tsize > 0 && 51462306a36Sopenharmony_ci memcmp(stab->data, tab, flex_array_size(stab, data, tsize))) 51562306a36Sopenharmony_ci continue; 51662306a36Sopenharmony_ci stab->refcnt++; 51762306a36Sopenharmony_ci return stab; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (s->size_log > STAB_SIZE_LOG_MAX || 52162306a36Sopenharmony_ci s->cell_log > STAB_SIZE_LOG_MAX) { 52262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid logarithmic size of size table"); 52362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci stab = kmalloc(struct_size(stab, data, tsize), GFP_KERNEL); 52762306a36Sopenharmony_ci if (!stab) 52862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci stab->refcnt = 1; 53162306a36Sopenharmony_ci stab->szopts = *s; 53262306a36Sopenharmony_ci if (tsize > 0) 53362306a36Sopenharmony_ci memcpy(stab->data, tab, flex_array_size(stab, data, tsize)); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci list_add_tail(&stab->list, &qdisc_stab_list); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return stab; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid qdisc_put_stab(struct qdisc_size_table *tab) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci if (!tab) 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (--tab->refcnt == 0) { 54662306a36Sopenharmony_ci list_del(&tab->list); 54762306a36Sopenharmony_ci kfree_rcu(tab, rcu); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_put_stab); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct nlattr *nest; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_STAB); 55762306a36Sopenharmony_ci if (nest == NULL) 55862306a36Sopenharmony_ci goto nla_put_failure; 55962306a36Sopenharmony_ci if (nla_put(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts)) 56062306a36Sopenharmony_ci goto nla_put_failure; 56162306a36Sopenharmony_ci nla_nest_end(skb, nest); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return skb->len; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cinla_put_failure: 56662306a36Sopenharmony_ci return -1; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_civoid __qdisc_calculate_pkt_len(struct sk_buff *skb, 57062306a36Sopenharmony_ci const struct qdisc_size_table *stab) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci int pkt_len, slot; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci pkt_len = skb->len + stab->szopts.overhead; 57562306a36Sopenharmony_ci if (unlikely(!stab->szopts.tsize)) 57662306a36Sopenharmony_ci goto out; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci slot = pkt_len + stab->szopts.cell_align; 57962306a36Sopenharmony_ci if (unlikely(slot < 0)) 58062306a36Sopenharmony_ci slot = 0; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci slot >>= stab->szopts.cell_log; 58362306a36Sopenharmony_ci if (likely(slot < stab->szopts.tsize)) 58462306a36Sopenharmony_ci pkt_len = stab->data[slot]; 58562306a36Sopenharmony_ci else 58662306a36Sopenharmony_ci pkt_len = stab->data[stab->szopts.tsize - 1] * 58762306a36Sopenharmony_ci (slot / stab->szopts.tsize) + 58862306a36Sopenharmony_ci stab->data[slot % stab->szopts.tsize]; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci pkt_len <<= stab->szopts.size_log; 59162306a36Sopenharmony_ciout: 59262306a36Sopenharmony_ci if (unlikely(pkt_len < 1)) 59362306a36Sopenharmony_ci pkt_len = 1; 59462306a36Sopenharmony_ci qdisc_skb_cb(skb)->pkt_len = pkt_len; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ciEXPORT_SYMBOL(__qdisc_calculate_pkt_len); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_civoid qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci if (!(qdisc->flags & TCQ_F_WARN_NONWC)) { 60162306a36Sopenharmony_ci pr_warn("%s: %s qdisc %X: is non-work-conserving?\n", 60262306a36Sopenharmony_ci txt, qdisc->ops->id, qdisc->handle >> 16); 60362306a36Sopenharmony_ci qdisc->flags |= TCQ_F_WARN_NONWC; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_warn_nonwc); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, 61162306a36Sopenharmony_ci timer); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci rcu_read_lock(); 61462306a36Sopenharmony_ci __netif_schedule(qdisc_root(wd->qdisc)); 61562306a36Sopenharmony_ci rcu_read_unlock(); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return HRTIMER_NORESTART; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_civoid qdisc_watchdog_init_clockid(struct qdisc_watchdog *wd, struct Qdisc *qdisc, 62162306a36Sopenharmony_ci clockid_t clockid) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci hrtimer_init(&wd->timer, clockid, HRTIMER_MODE_ABS_PINNED); 62462306a36Sopenharmony_ci wd->timer.function = qdisc_watchdog; 62562306a36Sopenharmony_ci wd->qdisc = qdisc; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_watchdog_init_clockid); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_civoid qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci qdisc_watchdog_init_clockid(wd, qdisc, CLOCK_MONOTONIC); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_watchdog_init); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_civoid qdisc_watchdog_schedule_range_ns(struct qdisc_watchdog *wd, u64 expires, 63662306a36Sopenharmony_ci u64 delta_ns) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci bool deactivated; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci rcu_read_lock(); 64162306a36Sopenharmony_ci deactivated = test_bit(__QDISC_STATE_DEACTIVATED, 64262306a36Sopenharmony_ci &qdisc_root_sleeping(wd->qdisc)->state); 64362306a36Sopenharmony_ci rcu_read_unlock(); 64462306a36Sopenharmony_ci if (deactivated) 64562306a36Sopenharmony_ci return; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (hrtimer_is_queued(&wd->timer)) { 64862306a36Sopenharmony_ci u64 softexpires; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci softexpires = ktime_to_ns(hrtimer_get_softexpires(&wd->timer)); 65162306a36Sopenharmony_ci /* If timer is already set in [expires, expires + delta_ns], 65262306a36Sopenharmony_ci * do not reprogram it. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci if (softexpires - expires <= delta_ns) 65562306a36Sopenharmony_ci return; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci hrtimer_start_range_ns(&wd->timer, 65962306a36Sopenharmony_ci ns_to_ktime(expires), 66062306a36Sopenharmony_ci delta_ns, 66162306a36Sopenharmony_ci HRTIMER_MODE_ABS_PINNED); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_watchdog_schedule_range_ns); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_civoid qdisc_watchdog_cancel(struct qdisc_watchdog *wd) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci hrtimer_cancel(&wd->timer); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_watchdog_cancel); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic struct hlist_head *qdisc_class_hash_alloc(unsigned int n) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct hlist_head *h; 67462306a36Sopenharmony_ci unsigned int i; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci h = kvmalloc_array(n, sizeof(struct hlist_head), GFP_KERNEL); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (h != NULL) { 67962306a36Sopenharmony_ci for (i = 0; i < n; i++) 68062306a36Sopenharmony_ci INIT_HLIST_HEAD(&h[i]); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci return h; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_civoid qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct Qdisc_class_common *cl; 68862306a36Sopenharmony_ci struct hlist_node *next; 68962306a36Sopenharmony_ci struct hlist_head *nhash, *ohash; 69062306a36Sopenharmony_ci unsigned int nsize, nmask, osize; 69162306a36Sopenharmony_ci unsigned int i, h; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Rehash when load factor exceeds 0.75 */ 69462306a36Sopenharmony_ci if (clhash->hashelems * 4 <= clhash->hashsize * 3) 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci nsize = clhash->hashsize * 2; 69762306a36Sopenharmony_ci nmask = nsize - 1; 69862306a36Sopenharmony_ci nhash = qdisc_class_hash_alloc(nsize); 69962306a36Sopenharmony_ci if (nhash == NULL) 70062306a36Sopenharmony_ci return; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci ohash = clhash->hash; 70362306a36Sopenharmony_ci osize = clhash->hashsize; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci sch_tree_lock(sch); 70662306a36Sopenharmony_ci for (i = 0; i < osize; i++) { 70762306a36Sopenharmony_ci hlist_for_each_entry_safe(cl, next, &ohash[i], hnode) { 70862306a36Sopenharmony_ci h = qdisc_class_hash(cl->classid, nmask); 70962306a36Sopenharmony_ci hlist_add_head(&cl->hnode, &nhash[h]); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci clhash->hash = nhash; 71362306a36Sopenharmony_ci clhash->hashsize = nsize; 71462306a36Sopenharmony_ci clhash->hashmask = nmask; 71562306a36Sopenharmony_ci sch_tree_unlock(sch); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci kvfree(ohash); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_class_hash_grow); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ciint qdisc_class_hash_init(struct Qdisc_class_hash *clhash) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci unsigned int size = 4; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci clhash->hash = qdisc_class_hash_alloc(size); 72662306a36Sopenharmony_ci if (!clhash->hash) 72762306a36Sopenharmony_ci return -ENOMEM; 72862306a36Sopenharmony_ci clhash->hashsize = size; 72962306a36Sopenharmony_ci clhash->hashmask = size - 1; 73062306a36Sopenharmony_ci clhash->hashelems = 0; 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_class_hash_init); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_civoid qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci kvfree(clhash->hash); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_class_hash_destroy); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_civoid qdisc_class_hash_insert(struct Qdisc_class_hash *clhash, 74262306a36Sopenharmony_ci struct Qdisc_class_common *cl) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci unsigned int h; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci INIT_HLIST_NODE(&cl->hnode); 74762306a36Sopenharmony_ci h = qdisc_class_hash(cl->classid, clhash->hashmask); 74862306a36Sopenharmony_ci hlist_add_head(&cl->hnode, &clhash->hash[h]); 74962306a36Sopenharmony_ci clhash->hashelems++; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_class_hash_insert); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_civoid qdisc_class_hash_remove(struct Qdisc_class_hash *clhash, 75462306a36Sopenharmony_ci struct Qdisc_class_common *cl) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci hlist_del(&cl->hnode); 75762306a36Sopenharmony_ci clhash->hashelems--; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_class_hash_remove); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci/* Allocate an unique handle from space managed by kernel 76262306a36Sopenharmony_ci * Possible range is [8000-FFFF]:0000 (0x8000 values) 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_cistatic u32 qdisc_alloc_handle(struct net_device *dev) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci int i = 0x8000; 76762306a36Sopenharmony_ci static u32 autohandle = TC_H_MAKE(0x80000000U, 0); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci do { 77062306a36Sopenharmony_ci autohandle += TC_H_MAKE(0x10000U, 0); 77162306a36Sopenharmony_ci if (autohandle == TC_H_MAKE(TC_H_ROOT, 0)) 77262306a36Sopenharmony_ci autohandle = TC_H_MAKE(0x80000000U, 0); 77362306a36Sopenharmony_ci if (!qdisc_lookup(dev, autohandle)) 77462306a36Sopenharmony_ci return autohandle; 77562306a36Sopenharmony_ci cond_resched(); 77662306a36Sopenharmony_ci } while (--i > 0); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_civoid qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci bool qdisc_is_offloaded = sch->flags & TCQ_F_OFFLOADED; 78462306a36Sopenharmony_ci const struct Qdisc_class_ops *cops; 78562306a36Sopenharmony_ci unsigned long cl; 78662306a36Sopenharmony_ci u32 parentid; 78762306a36Sopenharmony_ci bool notify; 78862306a36Sopenharmony_ci int drops; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (n == 0 && len == 0) 79162306a36Sopenharmony_ci return; 79262306a36Sopenharmony_ci drops = max_t(int, n, 0); 79362306a36Sopenharmony_ci rcu_read_lock(); 79462306a36Sopenharmony_ci while ((parentid = sch->parent)) { 79562306a36Sopenharmony_ci if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS)) 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (sch->flags & TCQ_F_NOPARENT) 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci /* Notify parent qdisc only if child qdisc becomes empty. 80162306a36Sopenharmony_ci * 80262306a36Sopenharmony_ci * If child was empty even before update then backlog 80362306a36Sopenharmony_ci * counter is screwed and we skip notification because 80462306a36Sopenharmony_ci * parent class is already passive. 80562306a36Sopenharmony_ci * 80662306a36Sopenharmony_ci * If the original child was offloaded then it is allowed 80762306a36Sopenharmony_ci * to be seem as empty, so the parent is notified anyway. 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_ci notify = !sch->q.qlen && !WARN_ON_ONCE(!n && 81062306a36Sopenharmony_ci !qdisc_is_offloaded); 81162306a36Sopenharmony_ci /* TODO: perform the search on a per txq basis */ 81262306a36Sopenharmony_ci sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid)); 81362306a36Sopenharmony_ci if (sch == NULL) { 81462306a36Sopenharmony_ci WARN_ON_ONCE(parentid != TC_H_ROOT); 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci cops = sch->ops->cl_ops; 81862306a36Sopenharmony_ci if (notify && cops->qlen_notify) { 81962306a36Sopenharmony_ci cl = cops->find(sch, parentid); 82062306a36Sopenharmony_ci cops->qlen_notify(sch, cl); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci sch->q.qlen -= n; 82362306a36Sopenharmony_ci sch->qstats.backlog -= len; 82462306a36Sopenharmony_ci __qdisc_qstats_drop(sch, drops); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci rcu_read_unlock(); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_tree_reduce_backlog); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ciint qdisc_offload_dump_helper(struct Qdisc *sch, enum tc_setup_type type, 83162306a36Sopenharmony_ci void *type_data) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 83462306a36Sopenharmony_ci int err; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci sch->flags &= ~TCQ_F_OFFLOADED; 83762306a36Sopenharmony_ci if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data); 84162306a36Sopenharmony_ci if (err == -EOPNOTSUPP) 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (!err) 84562306a36Sopenharmony_ci sch->flags |= TCQ_F_OFFLOADED; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return err; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_offload_dump_helper); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_civoid qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch, 85262306a36Sopenharmony_ci struct Qdisc *new, struct Qdisc *old, 85362306a36Sopenharmony_ci enum tc_setup_type type, void *type_data, 85462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci bool any_qdisc_is_offloaded; 85762306a36Sopenharmony_ci int err; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) 86062306a36Sopenharmony_ci return; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* Don't report error if the graft is part of destroy operation. */ 86562306a36Sopenharmony_ci if (!err || !new || new == &noop_qdisc) 86662306a36Sopenharmony_ci return; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Don't report error if the parent, the old child and the new 86962306a36Sopenharmony_ci * one are not offloaded. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ci any_qdisc_is_offloaded = new->flags & TCQ_F_OFFLOADED; 87262306a36Sopenharmony_ci any_qdisc_is_offloaded |= sch && sch->flags & TCQ_F_OFFLOADED; 87362306a36Sopenharmony_ci any_qdisc_is_offloaded |= old && old->flags & TCQ_F_OFFLOADED; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (any_qdisc_is_offloaded) 87662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Offloading graft operation failed."); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_offload_graft_helper); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_civoid qdisc_offload_query_caps(struct net_device *dev, 88162306a36Sopenharmony_ci enum tc_setup_type type, 88262306a36Sopenharmony_ci void *caps, size_t caps_len) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci const struct net_device_ops *ops = dev->netdev_ops; 88562306a36Sopenharmony_ci struct tc_query_caps_base base = { 88662306a36Sopenharmony_ci .type = type, 88762306a36Sopenharmony_ci .caps = caps, 88862306a36Sopenharmony_ci }; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci memset(caps, 0, caps_len); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (ops->ndo_setup_tc) 89362306a36Sopenharmony_ci ops->ndo_setup_tc(dev, TC_QUERY_CAPS, &base); 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ciEXPORT_SYMBOL(qdisc_offload_query_caps); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic void qdisc_offload_graft_root(struct net_device *dev, 89862306a36Sopenharmony_ci struct Qdisc *new, struct Qdisc *old, 89962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct tc_root_qopt_offload graft_offload = { 90262306a36Sopenharmony_ci .command = TC_ROOT_GRAFT, 90362306a36Sopenharmony_ci .handle = new ? new->handle : 0, 90462306a36Sopenharmony_ci .ingress = (new && new->flags & TCQ_F_INGRESS) || 90562306a36Sopenharmony_ci (old && old->flags & TCQ_F_INGRESS), 90662306a36Sopenharmony_ci }; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci qdisc_offload_graft_helper(dev, NULL, new, old, 90962306a36Sopenharmony_ci TC_SETUP_ROOT_QDISC, &graft_offload, extack); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, 91362306a36Sopenharmony_ci u32 portid, u32 seq, u16 flags, int event, 91462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu_bstats = NULL; 91762306a36Sopenharmony_ci struct gnet_stats_queue __percpu *cpu_qstats = NULL; 91862306a36Sopenharmony_ci struct tcmsg *tcm; 91962306a36Sopenharmony_ci struct nlmsghdr *nlh; 92062306a36Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 92162306a36Sopenharmony_ci struct gnet_dump d; 92262306a36Sopenharmony_ci struct qdisc_size_table *stab; 92362306a36Sopenharmony_ci u32 block_index; 92462306a36Sopenharmony_ci __u32 qlen; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci cond_resched(); 92762306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 92862306a36Sopenharmony_ci if (!nlh) 92962306a36Sopenharmony_ci goto out_nlmsg_trim; 93062306a36Sopenharmony_ci tcm = nlmsg_data(nlh); 93162306a36Sopenharmony_ci tcm->tcm_family = AF_UNSPEC; 93262306a36Sopenharmony_ci tcm->tcm__pad1 = 0; 93362306a36Sopenharmony_ci tcm->tcm__pad2 = 0; 93462306a36Sopenharmony_ci tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 93562306a36Sopenharmony_ci tcm->tcm_parent = clid; 93662306a36Sopenharmony_ci tcm->tcm_handle = q->handle; 93762306a36Sopenharmony_ci tcm->tcm_info = refcount_read(&q->refcnt); 93862306a36Sopenharmony_ci if (nla_put_string(skb, TCA_KIND, q->ops->id)) 93962306a36Sopenharmony_ci goto nla_put_failure; 94062306a36Sopenharmony_ci if (q->ops->ingress_block_get) { 94162306a36Sopenharmony_ci block_index = q->ops->ingress_block_get(q); 94262306a36Sopenharmony_ci if (block_index && 94362306a36Sopenharmony_ci nla_put_u32(skb, TCA_INGRESS_BLOCK, block_index)) 94462306a36Sopenharmony_ci goto nla_put_failure; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci if (q->ops->egress_block_get) { 94762306a36Sopenharmony_ci block_index = q->ops->egress_block_get(q); 94862306a36Sopenharmony_ci if (block_index && 94962306a36Sopenharmony_ci nla_put_u32(skb, TCA_EGRESS_BLOCK, block_index)) 95062306a36Sopenharmony_ci goto nla_put_failure; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci if (q->ops->dump && q->ops->dump(q, skb) < 0) 95362306a36Sopenharmony_ci goto nla_put_failure; 95462306a36Sopenharmony_ci if (nla_put_u8(skb, TCA_HW_OFFLOAD, !!(q->flags & TCQ_F_OFFLOADED))) 95562306a36Sopenharmony_ci goto nla_put_failure; 95662306a36Sopenharmony_ci qlen = qdisc_qlen_sum(q); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci stab = rtnl_dereference(q->stab); 95962306a36Sopenharmony_ci if (stab && qdisc_dump_stab(skb, stab) < 0) 96062306a36Sopenharmony_ci goto nla_put_failure; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, 96362306a36Sopenharmony_ci NULL, &d, TCA_PAD) < 0) 96462306a36Sopenharmony_ci goto nla_put_failure; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0) 96762306a36Sopenharmony_ci goto nla_put_failure; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (qdisc_is_percpu_stats(q)) { 97062306a36Sopenharmony_ci cpu_bstats = q->cpu_bstats; 97162306a36Sopenharmony_ci cpu_qstats = q->cpu_qstats; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (gnet_stats_copy_basic(&d, cpu_bstats, &q->bstats, true) < 0 || 97562306a36Sopenharmony_ci gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 || 97662306a36Sopenharmony_ci gnet_stats_copy_queue(&d, cpu_qstats, &q->qstats, qlen) < 0) 97762306a36Sopenharmony_ci goto nla_put_failure; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (gnet_stats_finish_copy(&d) < 0) 98062306a36Sopenharmony_ci goto nla_put_failure; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (extack && extack->_msg && 98362306a36Sopenharmony_ci nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) 98462306a36Sopenharmony_ci goto out_nlmsg_trim; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return skb->len; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ciout_nlmsg_trim: 99162306a36Sopenharmony_cinla_put_failure: 99262306a36Sopenharmony_ci nlmsg_trim(skb, b); 99362306a36Sopenharmony_ci return -1; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci if (q->flags & TCQ_F_BUILTIN) 99962306a36Sopenharmony_ci return true; 100062306a36Sopenharmony_ci if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible) 100162306a36Sopenharmony_ci return true; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return false; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int qdisc_notify(struct net *net, struct sk_buff *oskb, 100762306a36Sopenharmony_ci struct nlmsghdr *n, u32 clid, 100862306a36Sopenharmony_ci struct Qdisc *old, struct Qdisc *new, 100962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct sk_buff *skb; 101262306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 101562306a36Sopenharmony_ci if (!skb) 101662306a36Sopenharmony_ci return -ENOBUFS; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (old && !tc_qdisc_dump_ignore(old, false)) { 101962306a36Sopenharmony_ci if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, 102062306a36Sopenharmony_ci 0, RTM_DELQDISC, extack) < 0) 102162306a36Sopenharmony_ci goto err_out; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci if (new && !tc_qdisc_dump_ignore(new, false)) { 102462306a36Sopenharmony_ci if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, 102562306a36Sopenharmony_ci old ? NLM_F_REPLACE : 0, RTM_NEWQDISC, extack) < 0) 102662306a36Sopenharmony_ci goto err_out; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (skb->len) 103062306a36Sopenharmony_ci return rtnetlink_send(skb, net, portid, RTNLGRP_TC, 103162306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cierr_out: 103462306a36Sopenharmony_ci kfree_skb(skb); 103562306a36Sopenharmony_ci return -EINVAL; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic void notify_and_destroy(struct net *net, struct sk_buff *skb, 103962306a36Sopenharmony_ci struct nlmsghdr *n, u32 clid, 104062306a36Sopenharmony_ci struct Qdisc *old, struct Qdisc *new, 104162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci if (new || old) 104462306a36Sopenharmony_ci qdisc_notify(net, skb, n, clid, old, new, extack); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (old) 104762306a36Sopenharmony_ci qdisc_put(old); 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic void qdisc_clear_nolock(struct Qdisc *sch) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci sch->flags &= ~TCQ_F_NOLOCK; 105362306a36Sopenharmony_ci if (!(sch->flags & TCQ_F_CPUSTATS)) 105462306a36Sopenharmony_ci return; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci free_percpu(sch->cpu_bstats); 105762306a36Sopenharmony_ci free_percpu(sch->cpu_qstats); 105862306a36Sopenharmony_ci sch->cpu_bstats = NULL; 105962306a36Sopenharmony_ci sch->cpu_qstats = NULL; 106062306a36Sopenharmony_ci sch->flags &= ~TCQ_F_CPUSTATS; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci/* Graft qdisc "new" to class "classid" of qdisc "parent" or 106462306a36Sopenharmony_ci * to device "dev". 106562306a36Sopenharmony_ci * 106662306a36Sopenharmony_ci * When appropriate send a netlink notification using 'skb' 106762306a36Sopenharmony_ci * and "n". 106862306a36Sopenharmony_ci * 106962306a36Sopenharmony_ci * On success, destroy old qdisc. 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic int qdisc_graft(struct net_device *dev, struct Qdisc *parent, 107362306a36Sopenharmony_ci struct sk_buff *skb, struct nlmsghdr *n, u32 classid, 107462306a36Sopenharmony_ci struct Qdisc *new, struct Qdisc *old, 107562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct Qdisc *q = old; 107862306a36Sopenharmony_ci struct net *net = dev_net(dev); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (parent == NULL) { 108162306a36Sopenharmony_ci unsigned int i, num_q, ingress; 108262306a36Sopenharmony_ci struct netdev_queue *dev_queue; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci ingress = 0; 108562306a36Sopenharmony_ci num_q = dev->num_tx_queues; 108662306a36Sopenharmony_ci if ((q && q->flags & TCQ_F_INGRESS) || 108762306a36Sopenharmony_ci (new && new->flags & TCQ_F_INGRESS)) { 108862306a36Sopenharmony_ci ingress = 1; 108962306a36Sopenharmony_ci dev_queue = dev_ingress_queue(dev); 109062306a36Sopenharmony_ci if (!dev_queue) { 109162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Device does not have an ingress queue"); 109262306a36Sopenharmony_ci return -ENOENT; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci q = rtnl_dereference(dev_queue->qdisc_sleeping); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* This is the counterpart of that qdisc_refcount_inc_nz() call in 109862306a36Sopenharmony_ci * __tcf_qdisc_find() for filter requests. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci if (!qdisc_refcount_dec_if_one(q)) { 110162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 110262306a36Sopenharmony_ci "Current ingress or clsact Qdisc has ongoing filter requests"); 110362306a36Sopenharmony_ci return -EBUSY; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (dev->flags & IFF_UP) 110862306a36Sopenharmony_ci dev_deactivate(dev); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci qdisc_offload_graft_root(dev, new, old, extack); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (new && new->ops->attach && !ingress) 111362306a36Sopenharmony_ci goto skip; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (!ingress) { 111662306a36Sopenharmony_ci for (i = 0; i < num_q; i++) { 111762306a36Sopenharmony_ci dev_queue = netdev_get_tx_queue(dev, i); 111862306a36Sopenharmony_ci old = dev_graft_qdisc(dev_queue, new); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (new && i > 0) 112162306a36Sopenharmony_ci qdisc_refcount_inc(new); 112262306a36Sopenharmony_ci qdisc_put(old); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci } else { 112562306a36Sopenharmony_ci old = dev_graft_qdisc(dev_queue, NULL); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* {ingress,clsact}_destroy() @old before grafting @new to avoid 112862306a36Sopenharmony_ci * unprotected concurrent accesses to net_device::miniq_{in,e}gress 112962306a36Sopenharmony_ci * pointer(s) in mini_qdisc_pair_swap(). 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci qdisc_notify(net, skb, n, classid, old, new, extack); 113262306a36Sopenharmony_ci qdisc_destroy(old); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci dev_graft_qdisc(dev_queue, new); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ciskip: 113862306a36Sopenharmony_ci if (!ingress) { 113962306a36Sopenharmony_ci old = rtnl_dereference(dev->qdisc); 114062306a36Sopenharmony_ci if (new && !new->ops->attach) 114162306a36Sopenharmony_ci qdisc_refcount_inc(new); 114262306a36Sopenharmony_ci rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci notify_and_destroy(net, skb, n, classid, old, new, extack); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (new && new->ops->attach) 114762306a36Sopenharmony_ci new->ops->attach(new); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (dev->flags & IFF_UP) 115162306a36Sopenharmony_ci dev_activate(dev); 115262306a36Sopenharmony_ci } else { 115362306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = parent->ops->cl_ops; 115462306a36Sopenharmony_ci unsigned long cl; 115562306a36Sopenharmony_ci int err; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* Only support running class lockless if parent is lockless */ 115862306a36Sopenharmony_ci if (new && (new->flags & TCQ_F_NOLOCK) && !(parent->flags & TCQ_F_NOLOCK)) 115962306a36Sopenharmony_ci qdisc_clear_nolock(new); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (!cops || !cops->graft) 116262306a36Sopenharmony_ci return -EOPNOTSUPP; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci cl = cops->find(parent, classid); 116562306a36Sopenharmony_ci if (!cl) { 116662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified class not found"); 116762306a36Sopenharmony_ci return -ENOENT; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (new && new->ops == &noqueue_qdisc_ops) { 117162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot assign noqueue to a class"); 117262306a36Sopenharmony_ci return -EINVAL; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci err = cops->graft(parent, cl, new, &old, extack); 117662306a36Sopenharmony_ci if (err) 117762306a36Sopenharmony_ci return err; 117862306a36Sopenharmony_ci notify_and_destroy(net, skb, n, classid, old, new, extack); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci return 0; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int qdisc_block_indexes_set(struct Qdisc *sch, struct nlattr **tca, 118462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci u32 block_index; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (tca[TCA_INGRESS_BLOCK]) { 118962306a36Sopenharmony_ci block_index = nla_get_u32(tca[TCA_INGRESS_BLOCK]); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (!block_index) { 119262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Ingress block index cannot be 0"); 119362306a36Sopenharmony_ci return -EINVAL; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci if (!sch->ops->ingress_block_set) { 119662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Ingress block sharing is not supported"); 119762306a36Sopenharmony_ci return -EOPNOTSUPP; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci sch->ops->ingress_block_set(sch, block_index); 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci if (tca[TCA_EGRESS_BLOCK]) { 120262306a36Sopenharmony_ci block_index = nla_get_u32(tca[TCA_EGRESS_BLOCK]); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (!block_index) { 120562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Egress block index cannot be 0"); 120662306a36Sopenharmony_ci return -EINVAL; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci if (!sch->ops->egress_block_set) { 120962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Egress block sharing is not supported"); 121062306a36Sopenharmony_ci return -EOPNOTSUPP; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci sch->ops->egress_block_set(sch, block_index); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/* 121862306a36Sopenharmony_ci Allocate and initialize new qdisc. 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci Parameters are passed via opt. 122162306a36Sopenharmony_ci */ 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic struct Qdisc *qdisc_create(struct net_device *dev, 122462306a36Sopenharmony_ci struct netdev_queue *dev_queue, 122562306a36Sopenharmony_ci u32 parent, u32 handle, 122662306a36Sopenharmony_ci struct nlattr **tca, int *errp, 122762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci int err; 123062306a36Sopenharmony_ci struct nlattr *kind = tca[TCA_KIND]; 123162306a36Sopenharmony_ci struct Qdisc *sch; 123262306a36Sopenharmony_ci struct Qdisc_ops *ops; 123362306a36Sopenharmony_ci struct qdisc_size_table *stab; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci ops = qdisc_lookup_ops(kind); 123662306a36Sopenharmony_ci#ifdef CONFIG_MODULES 123762306a36Sopenharmony_ci if (ops == NULL && kind != NULL) { 123862306a36Sopenharmony_ci char name[IFNAMSIZ]; 123962306a36Sopenharmony_ci if (nla_strscpy(name, kind, IFNAMSIZ) >= 0) { 124062306a36Sopenharmony_ci /* We dropped the RTNL semaphore in order to 124162306a36Sopenharmony_ci * perform the module load. So, even if we 124262306a36Sopenharmony_ci * succeeded in loading the module we have to 124362306a36Sopenharmony_ci * tell the caller to replay the request. We 124462306a36Sopenharmony_ci * indicate this using -EAGAIN. 124562306a36Sopenharmony_ci * We replay the request because the device may 124662306a36Sopenharmony_ci * go away in the mean time. 124762306a36Sopenharmony_ci */ 124862306a36Sopenharmony_ci rtnl_unlock(); 124962306a36Sopenharmony_ci request_module("sch_%s", name); 125062306a36Sopenharmony_ci rtnl_lock(); 125162306a36Sopenharmony_ci ops = qdisc_lookup_ops(kind); 125262306a36Sopenharmony_ci if (ops != NULL) { 125362306a36Sopenharmony_ci /* We will try again qdisc_lookup_ops, 125462306a36Sopenharmony_ci * so don't keep a reference. 125562306a36Sopenharmony_ci */ 125662306a36Sopenharmony_ci module_put(ops->owner); 125762306a36Sopenharmony_ci err = -EAGAIN; 125862306a36Sopenharmony_ci goto err_out; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci#endif 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci err = -ENOENT; 126562306a36Sopenharmony_ci if (!ops) { 126662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified qdisc kind is unknown"); 126762306a36Sopenharmony_ci goto err_out; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci sch = qdisc_alloc(dev_queue, ops, extack); 127162306a36Sopenharmony_ci if (IS_ERR(sch)) { 127262306a36Sopenharmony_ci err = PTR_ERR(sch); 127362306a36Sopenharmony_ci goto err_out2; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci sch->parent = parent; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (handle == TC_H_INGRESS) { 127962306a36Sopenharmony_ci if (!(sch->flags & TCQ_F_INGRESS)) { 128062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 128162306a36Sopenharmony_ci "Specified parent ID is reserved for ingress and clsact Qdiscs"); 128262306a36Sopenharmony_ci err = -EINVAL; 128362306a36Sopenharmony_ci goto err_out3; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci handle = TC_H_MAKE(TC_H_INGRESS, 0); 128662306a36Sopenharmony_ci } else { 128762306a36Sopenharmony_ci if (handle == 0) { 128862306a36Sopenharmony_ci handle = qdisc_alloc_handle(dev); 128962306a36Sopenharmony_ci if (handle == 0) { 129062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Maximum number of qdisc handles was exceeded"); 129162306a36Sopenharmony_ci err = -ENOSPC; 129262306a36Sopenharmony_ci goto err_out3; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci if (!netif_is_multiqueue(dev)) 129662306a36Sopenharmony_ci sch->flags |= TCQ_F_ONETXQUEUE; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci sch->handle = handle; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* This exist to keep backward compatible with a userspace 130262306a36Sopenharmony_ci * loophole, what allowed userspace to get IFF_NO_QUEUE 130362306a36Sopenharmony_ci * facility on older kernels by setting tx_queue_len=0 (prior 130462306a36Sopenharmony_ci * to qdisc init), and then forgot to reinit tx_queue_len 130562306a36Sopenharmony_ci * before again attaching a qdisc. 130662306a36Sopenharmony_ci */ 130762306a36Sopenharmony_ci if ((dev->priv_flags & IFF_NO_QUEUE) && (dev->tx_queue_len == 0)) { 130862306a36Sopenharmony_ci dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; 130962306a36Sopenharmony_ci netdev_info(dev, "Caught tx_queue_len zero misconfig\n"); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci err = qdisc_block_indexes_set(sch, tca, extack); 131362306a36Sopenharmony_ci if (err) 131462306a36Sopenharmony_ci goto err_out3; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (tca[TCA_STAB]) { 131762306a36Sopenharmony_ci stab = qdisc_get_stab(tca[TCA_STAB], extack); 131862306a36Sopenharmony_ci if (IS_ERR(stab)) { 131962306a36Sopenharmony_ci err = PTR_ERR(stab); 132062306a36Sopenharmony_ci goto err_out3; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci rcu_assign_pointer(sch->stab, stab); 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (ops->init) { 132662306a36Sopenharmony_ci err = ops->init(sch, tca[TCA_OPTIONS], extack); 132762306a36Sopenharmony_ci if (err != 0) 132862306a36Sopenharmony_ci goto err_out4; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (tca[TCA_RATE]) { 133262306a36Sopenharmony_ci err = -EOPNOTSUPP; 133362306a36Sopenharmony_ci if (sch->flags & TCQ_F_MQROOT) { 133462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot attach rate estimator to a multi-queue root qdisc"); 133562306a36Sopenharmony_ci goto err_out4; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci err = gen_new_estimator(&sch->bstats, 133962306a36Sopenharmony_ci sch->cpu_bstats, 134062306a36Sopenharmony_ci &sch->rate_est, 134162306a36Sopenharmony_ci NULL, 134262306a36Sopenharmony_ci true, 134362306a36Sopenharmony_ci tca[TCA_RATE]); 134462306a36Sopenharmony_ci if (err) { 134562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to generate new estimator"); 134662306a36Sopenharmony_ci goto err_out4; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci qdisc_hash_add(sch, false); 135162306a36Sopenharmony_ci trace_qdisc_create(ops, dev, parent); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return sch; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cierr_out4: 135662306a36Sopenharmony_ci /* Even if ops->init() failed, we call ops->destroy() 135762306a36Sopenharmony_ci * like qdisc_create_dflt(). 135862306a36Sopenharmony_ci */ 135962306a36Sopenharmony_ci if (ops->destroy) 136062306a36Sopenharmony_ci ops->destroy(sch); 136162306a36Sopenharmony_ci qdisc_put_stab(rtnl_dereference(sch->stab)); 136262306a36Sopenharmony_cierr_out3: 136362306a36Sopenharmony_ci netdev_put(dev, &sch->dev_tracker); 136462306a36Sopenharmony_ci qdisc_free(sch); 136562306a36Sopenharmony_cierr_out2: 136662306a36Sopenharmony_ci module_put(ops->owner); 136762306a36Sopenharmony_cierr_out: 136862306a36Sopenharmony_ci *errp = err; 136962306a36Sopenharmony_ci return NULL; 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic int qdisc_change(struct Qdisc *sch, struct nlattr **tca, 137362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci struct qdisc_size_table *ostab, *stab = NULL; 137662306a36Sopenharmony_ci int err = 0; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (tca[TCA_OPTIONS]) { 137962306a36Sopenharmony_ci if (!sch->ops->change) { 138062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Change operation not supported by specified qdisc"); 138162306a36Sopenharmony_ci return -EINVAL; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci if (tca[TCA_INGRESS_BLOCK] || tca[TCA_EGRESS_BLOCK]) { 138462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Change of blocks is not supported"); 138562306a36Sopenharmony_ci return -EOPNOTSUPP; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci err = sch->ops->change(sch, tca[TCA_OPTIONS], extack); 138862306a36Sopenharmony_ci if (err) 138962306a36Sopenharmony_ci return err; 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (tca[TCA_STAB]) { 139362306a36Sopenharmony_ci stab = qdisc_get_stab(tca[TCA_STAB], extack); 139462306a36Sopenharmony_ci if (IS_ERR(stab)) 139562306a36Sopenharmony_ci return PTR_ERR(stab); 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci ostab = rtnl_dereference(sch->stab); 139962306a36Sopenharmony_ci rcu_assign_pointer(sch->stab, stab); 140062306a36Sopenharmony_ci qdisc_put_stab(ostab); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (tca[TCA_RATE]) { 140362306a36Sopenharmony_ci /* NB: ignores errors from replace_estimator 140462306a36Sopenharmony_ci because change can't be undone. */ 140562306a36Sopenharmony_ci if (sch->flags & TCQ_F_MQROOT) 140662306a36Sopenharmony_ci goto out; 140762306a36Sopenharmony_ci gen_replace_estimator(&sch->bstats, 140862306a36Sopenharmony_ci sch->cpu_bstats, 140962306a36Sopenharmony_ci &sch->rate_est, 141062306a36Sopenharmony_ci NULL, 141162306a36Sopenharmony_ci true, 141262306a36Sopenharmony_ci tca[TCA_RATE]); 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ciout: 141562306a36Sopenharmony_ci return 0; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistruct check_loop_arg { 141962306a36Sopenharmony_ci struct qdisc_walker w; 142062306a36Sopenharmony_ci struct Qdisc *p; 142162306a36Sopenharmony_ci int depth; 142262306a36Sopenharmony_ci}; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic int check_loop_fn(struct Qdisc *q, unsigned long cl, 142562306a36Sopenharmony_ci struct qdisc_walker *w); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic int check_loop(struct Qdisc *q, struct Qdisc *p, int depth) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci struct check_loop_arg arg; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (q->ops->cl_ops == NULL) 143262306a36Sopenharmony_ci return 0; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci arg.w.stop = arg.w.skip = arg.w.count = 0; 143562306a36Sopenharmony_ci arg.w.fn = check_loop_fn; 143662306a36Sopenharmony_ci arg.depth = depth; 143762306a36Sopenharmony_ci arg.p = p; 143862306a36Sopenharmony_ci q->ops->cl_ops->walk(q, &arg.w); 143962306a36Sopenharmony_ci return arg.w.stop ? -ELOOP : 0; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic int 144362306a36Sopenharmony_cicheck_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct Qdisc *leaf; 144662306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = q->ops->cl_ops; 144762306a36Sopenharmony_ci struct check_loop_arg *arg = (struct check_loop_arg *)w; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci leaf = cops->leaf(q, cl); 145062306a36Sopenharmony_ci if (leaf) { 145162306a36Sopenharmony_ci if (leaf == arg->p || arg->depth > 7) 145262306a36Sopenharmony_ci return -ELOOP; 145362306a36Sopenharmony_ci return check_loop(leaf, arg->p, arg->depth + 1); 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci return 0; 145662306a36Sopenharmony_ci} 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ciconst struct nla_policy rtm_tca_policy[TCA_MAX + 1] = { 145962306a36Sopenharmony_ci [TCA_KIND] = { .type = NLA_STRING }, 146062306a36Sopenharmony_ci [TCA_RATE] = { .type = NLA_BINARY, 146162306a36Sopenharmony_ci .len = sizeof(struct tc_estimator) }, 146262306a36Sopenharmony_ci [TCA_STAB] = { .type = NLA_NESTED }, 146362306a36Sopenharmony_ci [TCA_DUMP_INVISIBLE] = { .type = NLA_FLAG }, 146462306a36Sopenharmony_ci [TCA_CHAIN] = { .type = NLA_U32 }, 146562306a36Sopenharmony_ci [TCA_INGRESS_BLOCK] = { .type = NLA_U32 }, 146662306a36Sopenharmony_ci [TCA_EGRESS_BLOCK] = { .type = NLA_U32 }, 146762306a36Sopenharmony_ci}; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci/* 147062306a36Sopenharmony_ci * Delete/get qdisc. 147162306a36Sopenharmony_ci */ 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, 147462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 147762306a36Sopenharmony_ci struct tcmsg *tcm = nlmsg_data(n); 147862306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 147962306a36Sopenharmony_ci struct net_device *dev; 148062306a36Sopenharmony_ci u32 clid; 148162306a36Sopenharmony_ci struct Qdisc *q = NULL; 148262306a36Sopenharmony_ci struct Qdisc *p = NULL; 148362306a36Sopenharmony_ci int err; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, 148662306a36Sopenharmony_ci rtm_tca_policy, extack); 148762306a36Sopenharmony_ci if (err < 0) 148862306a36Sopenharmony_ci return err; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci dev = __dev_get_by_index(net, tcm->tcm_ifindex); 149162306a36Sopenharmony_ci if (!dev) 149262306a36Sopenharmony_ci return -ENODEV; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci clid = tcm->tcm_parent; 149562306a36Sopenharmony_ci if (clid) { 149662306a36Sopenharmony_ci if (clid != TC_H_ROOT) { 149762306a36Sopenharmony_ci if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) { 149862306a36Sopenharmony_ci p = qdisc_lookup(dev, TC_H_MAJ(clid)); 149962306a36Sopenharmony_ci if (!p) { 150062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified classid"); 150162306a36Sopenharmony_ci return -ENOENT; 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci q = qdisc_leaf(p, clid); 150462306a36Sopenharmony_ci } else if (dev_ingress_queue(dev)) { 150562306a36Sopenharmony_ci q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping); 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci } else { 150862306a36Sopenharmony_ci q = rtnl_dereference(dev->qdisc); 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci if (!q) { 151162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device"); 151262306a36Sopenharmony_ci return -ENOENT; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (tcm->tcm_handle && q->handle != tcm->tcm_handle) { 151662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid handle"); 151762306a36Sopenharmony_ci return -EINVAL; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci } else { 152062306a36Sopenharmony_ci q = qdisc_lookup(dev, tcm->tcm_handle); 152162306a36Sopenharmony_ci if (!q) { 152262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified handle"); 152362306a36Sopenharmony_ci return -ENOENT; 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id)) { 152862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid qdisc name"); 152962306a36Sopenharmony_ci return -EINVAL; 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (n->nlmsg_type == RTM_DELQDISC) { 153362306a36Sopenharmony_ci if (!clid) { 153462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Classid cannot be zero"); 153562306a36Sopenharmony_ci return -EINVAL; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci if (q->handle == 0) { 153862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot delete qdisc with handle of zero"); 153962306a36Sopenharmony_ci return -ENOENT; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci err = qdisc_graft(dev, p, skb, n, clid, NULL, q, extack); 154262306a36Sopenharmony_ci if (err != 0) 154362306a36Sopenharmony_ci return err; 154462306a36Sopenharmony_ci } else { 154562306a36Sopenharmony_ci qdisc_notify(net, skb, n, clid, NULL, q, NULL); 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci return 0; 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_cistatic bool req_create_or_replace(struct nlmsghdr *n) 155162306a36Sopenharmony_ci{ 155262306a36Sopenharmony_ci return (n->nlmsg_flags & NLM_F_CREATE && 155362306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_REPLACE); 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic bool req_create_exclusive(struct nlmsghdr *n) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci return (n->nlmsg_flags & NLM_F_CREATE && 155962306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_EXCL); 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic bool req_change(struct nlmsghdr *n) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci return (!(n->nlmsg_flags & NLM_F_CREATE) && 156562306a36Sopenharmony_ci !(n->nlmsg_flags & NLM_F_REPLACE) && 156662306a36Sopenharmony_ci !(n->nlmsg_flags & NLM_F_EXCL)); 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci/* 157062306a36Sopenharmony_ci * Create/change qdisc. 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_cistatic int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, 157362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 157662306a36Sopenharmony_ci struct tcmsg *tcm; 157762306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 157862306a36Sopenharmony_ci struct net_device *dev; 157962306a36Sopenharmony_ci u32 clid; 158062306a36Sopenharmony_ci struct Qdisc *q, *p; 158162306a36Sopenharmony_ci int err; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_cireplay: 158462306a36Sopenharmony_ci /* Reinit, just in case something touches this. */ 158562306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, 158662306a36Sopenharmony_ci rtm_tca_policy, extack); 158762306a36Sopenharmony_ci if (err < 0) 158862306a36Sopenharmony_ci return err; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci tcm = nlmsg_data(n); 159162306a36Sopenharmony_ci clid = tcm->tcm_parent; 159262306a36Sopenharmony_ci q = p = NULL; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci dev = __dev_get_by_index(net, tcm->tcm_ifindex); 159562306a36Sopenharmony_ci if (!dev) 159662306a36Sopenharmony_ci return -ENODEV; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (clid) { 160062306a36Sopenharmony_ci if (clid != TC_H_ROOT) { 160162306a36Sopenharmony_ci if (clid != TC_H_INGRESS) { 160262306a36Sopenharmony_ci p = qdisc_lookup(dev, TC_H_MAJ(clid)); 160362306a36Sopenharmony_ci if (!p) { 160462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to find specified qdisc"); 160562306a36Sopenharmony_ci return -ENOENT; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci q = qdisc_leaf(p, clid); 160862306a36Sopenharmony_ci } else if (dev_ingress_queue_create(dev)) { 160962306a36Sopenharmony_ci q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping); 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci } else { 161262306a36Sopenharmony_ci q = rtnl_dereference(dev->qdisc); 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* It may be default qdisc, ignore it */ 161662306a36Sopenharmony_ci if (q && q->handle == 0) 161762306a36Sopenharmony_ci q = NULL; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) { 162062306a36Sopenharmony_ci if (tcm->tcm_handle) { 162162306a36Sopenharmony_ci if (q && !(n->nlmsg_flags & NLM_F_REPLACE)) { 162262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "NLM_F_REPLACE needed to override"); 162362306a36Sopenharmony_ci return -EEXIST; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci if (TC_H_MIN(tcm->tcm_handle)) { 162662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid minor handle"); 162762306a36Sopenharmony_ci return -EINVAL; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci q = qdisc_lookup(dev, tcm->tcm_handle); 163062306a36Sopenharmony_ci if (!q) 163162306a36Sopenharmony_ci goto create_n_graft; 163262306a36Sopenharmony_ci if (n->nlmsg_flags & NLM_F_EXCL) { 163362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot override"); 163462306a36Sopenharmony_ci return -EEXIST; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci if (tca[TCA_KIND] && 163762306a36Sopenharmony_ci nla_strcmp(tca[TCA_KIND], q->ops->id)) { 163862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid qdisc name"); 163962306a36Sopenharmony_ci return -EINVAL; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci if (q->flags & TCQ_F_INGRESS) { 164262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 164362306a36Sopenharmony_ci "Cannot regraft ingress or clsact Qdiscs"); 164462306a36Sopenharmony_ci return -EINVAL; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci if (q == p || 164762306a36Sopenharmony_ci (p && check_loop(q, p, 0))) { 164862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Qdisc parent/child loop detected"); 164962306a36Sopenharmony_ci return -ELOOP; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci if (clid == TC_H_INGRESS) { 165262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Ingress cannot graft directly"); 165362306a36Sopenharmony_ci return -EINVAL; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci qdisc_refcount_inc(q); 165662306a36Sopenharmony_ci goto graft; 165762306a36Sopenharmony_ci } else { 165862306a36Sopenharmony_ci if (!q) 165962306a36Sopenharmony_ci goto create_n_graft; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci /* This magic test requires explanation. 166262306a36Sopenharmony_ci * 166362306a36Sopenharmony_ci * We know, that some child q is already 166462306a36Sopenharmony_ci * attached to this parent and have choice: 166562306a36Sopenharmony_ci * 1) change it or 2) create/graft new one. 166662306a36Sopenharmony_ci * If the requested qdisc kind is different 166762306a36Sopenharmony_ci * than the existing one, then we choose graft. 166862306a36Sopenharmony_ci * If they are the same then this is "change" 166962306a36Sopenharmony_ci * operation - just let it fallthrough.. 167062306a36Sopenharmony_ci * 167162306a36Sopenharmony_ci * 1. We are allowed to create/graft only 167262306a36Sopenharmony_ci * if the request is explicitly stating 167362306a36Sopenharmony_ci * "please create if it doesn't exist". 167462306a36Sopenharmony_ci * 167562306a36Sopenharmony_ci * 2. If the request is to exclusive create 167662306a36Sopenharmony_ci * then the qdisc tcm_handle is not expected 167762306a36Sopenharmony_ci * to exist, so that we choose create/graft too. 167862306a36Sopenharmony_ci * 167962306a36Sopenharmony_ci * 3. The last case is when no flags are set. 168062306a36Sopenharmony_ci * This will happen when for example tc 168162306a36Sopenharmony_ci * utility issues a "change" command. 168262306a36Sopenharmony_ci * Alas, it is sort of hole in API, we 168362306a36Sopenharmony_ci * cannot decide what to do unambiguously. 168462306a36Sopenharmony_ci * For now we select create/graft. 168562306a36Sopenharmony_ci */ 168662306a36Sopenharmony_ci if (tca[TCA_KIND] && 168762306a36Sopenharmony_ci nla_strcmp(tca[TCA_KIND], q->ops->id)) { 168862306a36Sopenharmony_ci if (req_create_or_replace(n) || 168962306a36Sopenharmony_ci req_create_exclusive(n)) 169062306a36Sopenharmony_ci goto create_n_graft; 169162306a36Sopenharmony_ci else if (req_change(n)) 169262306a36Sopenharmony_ci goto create_n_graft2; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci } else { 169762306a36Sopenharmony_ci if (!tcm->tcm_handle) { 169862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Handle cannot be zero"); 169962306a36Sopenharmony_ci return -EINVAL; 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci q = qdisc_lookup(dev, tcm->tcm_handle); 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci /* Change qdisc parameters */ 170562306a36Sopenharmony_ci if (!q) { 170662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified qdisc not found"); 170762306a36Sopenharmony_ci return -ENOENT; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci if (n->nlmsg_flags & NLM_F_EXCL) { 171062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot modify"); 171162306a36Sopenharmony_ci return -EEXIST; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id)) { 171462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid qdisc name"); 171562306a36Sopenharmony_ci return -EINVAL; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci err = qdisc_change(q, tca, extack); 171862306a36Sopenharmony_ci if (err == 0) 171962306a36Sopenharmony_ci qdisc_notify(net, skb, n, clid, NULL, q, extack); 172062306a36Sopenharmony_ci return err; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_cicreate_n_graft: 172362306a36Sopenharmony_ci if (!(n->nlmsg_flags & NLM_F_CREATE)) { 172462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Qdisc not found. To create specify NLM_F_CREATE flag"); 172562306a36Sopenharmony_ci return -ENOENT; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_cicreate_n_graft2: 172862306a36Sopenharmony_ci if (clid == TC_H_INGRESS) { 172962306a36Sopenharmony_ci if (dev_ingress_queue(dev)) { 173062306a36Sopenharmony_ci q = qdisc_create(dev, dev_ingress_queue(dev), 173162306a36Sopenharmony_ci tcm->tcm_parent, tcm->tcm_parent, 173262306a36Sopenharmony_ci tca, &err, extack); 173362306a36Sopenharmony_ci } else { 173462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Cannot find ingress queue for specified device"); 173562306a36Sopenharmony_ci err = -ENOENT; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci } else { 173862306a36Sopenharmony_ci struct netdev_queue *dev_queue; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue) 174162306a36Sopenharmony_ci dev_queue = p->ops->cl_ops->select_queue(p, tcm); 174262306a36Sopenharmony_ci else if (p) 174362306a36Sopenharmony_ci dev_queue = p->dev_queue; 174462306a36Sopenharmony_ci else 174562306a36Sopenharmony_ci dev_queue = netdev_get_tx_queue(dev, 0); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci q = qdisc_create(dev, dev_queue, 174862306a36Sopenharmony_ci tcm->tcm_parent, tcm->tcm_handle, 174962306a36Sopenharmony_ci tca, &err, extack); 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci if (q == NULL) { 175262306a36Sopenharmony_ci if (err == -EAGAIN) 175362306a36Sopenharmony_ci goto replay; 175462306a36Sopenharmony_ci return err; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_cigraft: 175862306a36Sopenharmony_ci err = qdisc_graft(dev, p, skb, n, clid, q, NULL, extack); 175962306a36Sopenharmony_ci if (err) { 176062306a36Sopenharmony_ci if (q) 176162306a36Sopenharmony_ci qdisc_put(q); 176262306a36Sopenharmony_ci return err; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci return 0; 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, 176962306a36Sopenharmony_ci struct netlink_callback *cb, 177062306a36Sopenharmony_ci int *q_idx_p, int s_q_idx, bool recur, 177162306a36Sopenharmony_ci bool dump_invisible) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci int ret = 0, q_idx = *q_idx_p; 177462306a36Sopenharmony_ci struct Qdisc *q; 177562306a36Sopenharmony_ci int b; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (!root) 177862306a36Sopenharmony_ci return 0; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci q = root; 178162306a36Sopenharmony_ci if (q_idx < s_q_idx) { 178262306a36Sopenharmony_ci q_idx++; 178362306a36Sopenharmony_ci } else { 178462306a36Sopenharmony_ci if (!tc_qdisc_dump_ignore(q, dump_invisible) && 178562306a36Sopenharmony_ci tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, 178662306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 178762306a36Sopenharmony_ci RTM_NEWQDISC, NULL) <= 0) 178862306a36Sopenharmony_ci goto done; 178962306a36Sopenharmony_ci q_idx++; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci /* If dumping singletons, there is no qdisc_dev(root) and the singleton 179362306a36Sopenharmony_ci * itself has already been dumped. 179462306a36Sopenharmony_ci * 179562306a36Sopenharmony_ci * If we've already dumped the top-level (ingress) qdisc above and the global 179662306a36Sopenharmony_ci * qdisc hashtable, we don't want to hit it again 179762306a36Sopenharmony_ci */ 179862306a36Sopenharmony_ci if (!qdisc_dev(root) || !recur) 179962306a36Sopenharmony_ci goto out; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) { 180262306a36Sopenharmony_ci if (q_idx < s_q_idx) { 180362306a36Sopenharmony_ci q_idx++; 180462306a36Sopenharmony_ci continue; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci if (!tc_qdisc_dump_ignore(q, dump_invisible) && 180762306a36Sopenharmony_ci tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, 180862306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 180962306a36Sopenharmony_ci RTM_NEWQDISC, NULL) <= 0) 181062306a36Sopenharmony_ci goto done; 181162306a36Sopenharmony_ci q_idx++; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ciout: 181562306a36Sopenharmony_ci *q_idx_p = q_idx; 181662306a36Sopenharmony_ci return ret; 181762306a36Sopenharmony_cidone: 181862306a36Sopenharmony_ci ret = -1; 181962306a36Sopenharmony_ci goto out; 182062306a36Sopenharmony_ci} 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cistatic int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) 182362306a36Sopenharmony_ci{ 182462306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 182562306a36Sopenharmony_ci int idx, q_idx; 182662306a36Sopenharmony_ci int s_idx, s_q_idx; 182762306a36Sopenharmony_ci struct net_device *dev; 182862306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 182962306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 183062306a36Sopenharmony_ci int err; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci s_idx = cb->args[0]; 183362306a36Sopenharmony_ci s_q_idx = q_idx = cb->args[1]; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci idx = 0; 183662306a36Sopenharmony_ci ASSERT_RTNL(); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(struct tcmsg), tca, TCA_MAX, 183962306a36Sopenharmony_ci rtm_tca_policy, cb->extack); 184062306a36Sopenharmony_ci if (err < 0) 184162306a36Sopenharmony_ci return err; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci for_each_netdev(net, dev) { 184462306a36Sopenharmony_ci struct netdev_queue *dev_queue; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci if (idx < s_idx) 184762306a36Sopenharmony_ci goto cont; 184862306a36Sopenharmony_ci if (idx > s_idx) 184962306a36Sopenharmony_ci s_q_idx = 0; 185062306a36Sopenharmony_ci q_idx = 0; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc), 185362306a36Sopenharmony_ci skb, cb, &q_idx, s_q_idx, 185462306a36Sopenharmony_ci true, tca[TCA_DUMP_INVISIBLE]) < 0) 185562306a36Sopenharmony_ci goto done; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci dev_queue = dev_ingress_queue(dev); 185862306a36Sopenharmony_ci if (dev_queue && 185962306a36Sopenharmony_ci tc_dump_qdisc_root(rtnl_dereference(dev_queue->qdisc_sleeping), 186062306a36Sopenharmony_ci skb, cb, &q_idx, s_q_idx, false, 186162306a36Sopenharmony_ci tca[TCA_DUMP_INVISIBLE]) < 0) 186262306a36Sopenharmony_ci goto done; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_cicont: 186562306a36Sopenharmony_ci idx++; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_cidone: 186962306a36Sopenharmony_ci cb->args[0] = idx; 187062306a36Sopenharmony_ci cb->args[1] = q_idx; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci return skb->len; 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci/************************************************ 187862306a36Sopenharmony_ci * Traffic classes manipulation. * 187962306a36Sopenharmony_ci ************************************************/ 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_cistatic int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, 188262306a36Sopenharmony_ci unsigned long cl, u32 portid, u32 seq, u16 flags, 188362306a36Sopenharmony_ci int event, struct netlink_ext_ack *extack) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci struct tcmsg *tcm; 188662306a36Sopenharmony_ci struct nlmsghdr *nlh; 188762306a36Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 188862306a36Sopenharmony_ci struct gnet_dump d; 188962306a36Sopenharmony_ci const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci cond_resched(); 189262306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); 189362306a36Sopenharmony_ci if (!nlh) 189462306a36Sopenharmony_ci goto out_nlmsg_trim; 189562306a36Sopenharmony_ci tcm = nlmsg_data(nlh); 189662306a36Sopenharmony_ci tcm->tcm_family = AF_UNSPEC; 189762306a36Sopenharmony_ci tcm->tcm__pad1 = 0; 189862306a36Sopenharmony_ci tcm->tcm__pad2 = 0; 189962306a36Sopenharmony_ci tcm->tcm_ifindex = qdisc_dev(q)->ifindex; 190062306a36Sopenharmony_ci tcm->tcm_parent = q->handle; 190162306a36Sopenharmony_ci tcm->tcm_handle = q->handle; 190262306a36Sopenharmony_ci tcm->tcm_info = 0; 190362306a36Sopenharmony_ci if (nla_put_string(skb, TCA_KIND, q->ops->id)) 190462306a36Sopenharmony_ci goto nla_put_failure; 190562306a36Sopenharmony_ci if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0) 190662306a36Sopenharmony_ci goto nla_put_failure; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, 190962306a36Sopenharmony_ci NULL, &d, TCA_PAD) < 0) 191062306a36Sopenharmony_ci goto nla_put_failure; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0) 191362306a36Sopenharmony_ci goto nla_put_failure; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci if (gnet_stats_finish_copy(&d) < 0) 191662306a36Sopenharmony_ci goto nla_put_failure; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (extack && extack->_msg && 191962306a36Sopenharmony_ci nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg)) 192062306a36Sopenharmony_ci goto out_nlmsg_trim; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - b; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci return skb->len; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ciout_nlmsg_trim: 192762306a36Sopenharmony_cinla_put_failure: 192862306a36Sopenharmony_ci nlmsg_trim(skb, b); 192962306a36Sopenharmony_ci return -1; 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic int tclass_notify(struct net *net, struct sk_buff *oskb, 193362306a36Sopenharmony_ci struct nlmsghdr *n, struct Qdisc *q, 193462306a36Sopenharmony_ci unsigned long cl, int event, struct netlink_ext_ack *extack) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci struct sk_buff *skb; 193762306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 194062306a36Sopenharmony_ci if (!skb) 194162306a36Sopenharmony_ci return -ENOBUFS; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack) < 0) { 194462306a36Sopenharmony_ci kfree_skb(skb); 194562306a36Sopenharmony_ci return -EINVAL; 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci return rtnetlink_send(skb, net, portid, RTNLGRP_TC, 194962306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 195062306a36Sopenharmony_ci} 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic int tclass_del_notify(struct net *net, 195362306a36Sopenharmony_ci const struct Qdisc_class_ops *cops, 195462306a36Sopenharmony_ci struct sk_buff *oskb, struct nlmsghdr *n, 195562306a36Sopenharmony_ci struct Qdisc *q, unsigned long cl, 195662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 195762306a36Sopenharmony_ci{ 195862306a36Sopenharmony_ci u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; 195962306a36Sopenharmony_ci struct sk_buff *skb; 196062306a36Sopenharmony_ci int err = 0; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (!cops->delete) 196362306a36Sopenharmony_ci return -EOPNOTSUPP; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 196662306a36Sopenharmony_ci if (!skb) 196762306a36Sopenharmony_ci return -ENOBUFS; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, 197062306a36Sopenharmony_ci RTM_DELTCLASS, extack) < 0) { 197162306a36Sopenharmony_ci kfree_skb(skb); 197262306a36Sopenharmony_ci return -EINVAL; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci err = cops->delete(q, cl, extack); 197662306a36Sopenharmony_ci if (err) { 197762306a36Sopenharmony_ci kfree_skb(skb); 197862306a36Sopenharmony_ci return err; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, 198262306a36Sopenharmony_ci n->nlmsg_flags & NLM_F_ECHO); 198362306a36Sopenharmony_ci return err; 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_cistruct tcf_bind_args { 198962306a36Sopenharmony_ci struct tcf_walker w; 199062306a36Sopenharmony_ci unsigned long base; 199162306a36Sopenharmony_ci unsigned long cl; 199262306a36Sopenharmony_ci u32 classid; 199362306a36Sopenharmony_ci}; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_cistatic int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci struct tcf_bind_args *a = (void *)arg; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if (n && tp->ops->bind_class) { 200062306a36Sopenharmony_ci struct Qdisc *q = tcf_block_q(tp->chain->block); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci sch_tree_lock(q); 200362306a36Sopenharmony_ci tp->ops->bind_class(n, a->classid, a->cl, q, a->base); 200462306a36Sopenharmony_ci sch_tree_unlock(q); 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci return 0; 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistruct tc_bind_class_args { 201062306a36Sopenharmony_ci struct qdisc_walker w; 201162306a36Sopenharmony_ci unsigned long new_cl; 201262306a36Sopenharmony_ci u32 portid; 201362306a36Sopenharmony_ci u32 clid; 201462306a36Sopenharmony_ci}; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_cistatic int tc_bind_class_walker(struct Qdisc *q, unsigned long cl, 201762306a36Sopenharmony_ci struct qdisc_walker *w) 201862306a36Sopenharmony_ci{ 201962306a36Sopenharmony_ci struct tc_bind_class_args *a = (struct tc_bind_class_args *)w; 202062306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = q->ops->cl_ops; 202162306a36Sopenharmony_ci struct tcf_block *block; 202262306a36Sopenharmony_ci struct tcf_chain *chain; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci block = cops->tcf_block(q, cl, NULL); 202562306a36Sopenharmony_ci if (!block) 202662306a36Sopenharmony_ci return 0; 202762306a36Sopenharmony_ci for (chain = tcf_get_next_chain(block, NULL); 202862306a36Sopenharmony_ci chain; 202962306a36Sopenharmony_ci chain = tcf_get_next_chain(block, chain)) { 203062306a36Sopenharmony_ci struct tcf_proto *tp; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci for (tp = tcf_get_next_proto(chain, NULL); 203362306a36Sopenharmony_ci tp; tp = tcf_get_next_proto(chain, tp)) { 203462306a36Sopenharmony_ci struct tcf_bind_args arg = {}; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci arg.w.fn = tcf_node_bind; 203762306a36Sopenharmony_ci arg.classid = a->clid; 203862306a36Sopenharmony_ci arg.base = cl; 203962306a36Sopenharmony_ci arg.cl = a->new_cl; 204062306a36Sopenharmony_ci tp->ops->walk(tp, &arg.w, true); 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci } 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci return 0; 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_cistatic void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, 204862306a36Sopenharmony_ci unsigned long new_cl) 204962306a36Sopenharmony_ci{ 205062306a36Sopenharmony_ci const struct Qdisc_class_ops *cops = q->ops->cl_ops; 205162306a36Sopenharmony_ci struct tc_bind_class_args args = {}; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci if (!cops->tcf_block) 205462306a36Sopenharmony_ci return; 205562306a36Sopenharmony_ci args.portid = portid; 205662306a36Sopenharmony_ci args.clid = clid; 205762306a36Sopenharmony_ci args.new_cl = new_cl; 205862306a36Sopenharmony_ci args.w.fn = tc_bind_class_walker; 205962306a36Sopenharmony_ci q->ops->cl_ops->walk(q, &args.w); 206062306a36Sopenharmony_ci} 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci#else 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_cistatic void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, 206562306a36Sopenharmony_ci unsigned long new_cl) 206662306a36Sopenharmony_ci{ 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci#endif 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_cistatic int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, 207262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 207362306a36Sopenharmony_ci{ 207462306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 207562306a36Sopenharmony_ci struct tcmsg *tcm = nlmsg_data(n); 207662306a36Sopenharmony_ci struct nlattr *tca[TCA_MAX + 1]; 207762306a36Sopenharmony_ci struct net_device *dev; 207862306a36Sopenharmony_ci struct Qdisc *q = NULL; 207962306a36Sopenharmony_ci const struct Qdisc_class_ops *cops; 208062306a36Sopenharmony_ci unsigned long cl = 0; 208162306a36Sopenharmony_ci unsigned long new_cl; 208262306a36Sopenharmony_ci u32 portid; 208362306a36Sopenharmony_ci u32 clid; 208462306a36Sopenharmony_ci u32 qid; 208562306a36Sopenharmony_ci int err; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, 208862306a36Sopenharmony_ci rtm_tca_policy, extack); 208962306a36Sopenharmony_ci if (err < 0) 209062306a36Sopenharmony_ci return err; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci dev = __dev_get_by_index(net, tcm->tcm_ifindex); 209362306a36Sopenharmony_ci if (!dev) 209462306a36Sopenharmony_ci return -ENODEV; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci /* 209762306a36Sopenharmony_ci parent == TC_H_UNSPEC - unspecified parent. 209862306a36Sopenharmony_ci parent == TC_H_ROOT - class is root, which has no parent. 209962306a36Sopenharmony_ci parent == X:0 - parent is root class. 210062306a36Sopenharmony_ci parent == X:Y - parent is a node in hierarchy. 210162306a36Sopenharmony_ci parent == 0:Y - parent is X:Y, where X:0 is qdisc. 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci handle == 0:0 - generate handle from kernel pool. 210462306a36Sopenharmony_ci handle == 0:Y - class is X:Y, where X:0 is qdisc. 210562306a36Sopenharmony_ci handle == X:Y - clear. 210662306a36Sopenharmony_ci handle == X:0 - root class. 210762306a36Sopenharmony_ci */ 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci /* Step 1. Determine qdisc handle X:0 */ 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci portid = tcm->tcm_parent; 211262306a36Sopenharmony_ci clid = tcm->tcm_handle; 211362306a36Sopenharmony_ci qid = TC_H_MAJ(clid); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci if (portid != TC_H_ROOT) { 211662306a36Sopenharmony_ci u32 qid1 = TC_H_MAJ(portid); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci if (qid && qid1) { 211962306a36Sopenharmony_ci /* If both majors are known, they must be identical. */ 212062306a36Sopenharmony_ci if (qid != qid1) 212162306a36Sopenharmony_ci return -EINVAL; 212262306a36Sopenharmony_ci } else if (qid1) { 212362306a36Sopenharmony_ci qid = qid1; 212462306a36Sopenharmony_ci } else if (qid == 0) 212562306a36Sopenharmony_ci qid = rtnl_dereference(dev->qdisc)->handle; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci /* Now qid is genuine qdisc handle consistent 212862306a36Sopenharmony_ci * both with parent and child. 212962306a36Sopenharmony_ci * 213062306a36Sopenharmony_ci * TC_H_MAJ(portid) still may be unspecified, complete it now. 213162306a36Sopenharmony_ci */ 213262306a36Sopenharmony_ci if (portid) 213362306a36Sopenharmony_ci portid = TC_H_MAKE(qid, portid); 213462306a36Sopenharmony_ci } else { 213562306a36Sopenharmony_ci if (qid == 0) 213662306a36Sopenharmony_ci qid = rtnl_dereference(dev->qdisc)->handle; 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci /* OK. Locate qdisc */ 214062306a36Sopenharmony_ci q = qdisc_lookup(dev, qid); 214162306a36Sopenharmony_ci if (!q) 214262306a36Sopenharmony_ci return -ENOENT; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci /* An check that it supports classes */ 214562306a36Sopenharmony_ci cops = q->ops->cl_ops; 214662306a36Sopenharmony_ci if (cops == NULL) 214762306a36Sopenharmony_ci return -EINVAL; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci /* Now try to get class */ 215062306a36Sopenharmony_ci if (clid == 0) { 215162306a36Sopenharmony_ci if (portid == TC_H_ROOT) 215262306a36Sopenharmony_ci clid = qid; 215362306a36Sopenharmony_ci } else 215462306a36Sopenharmony_ci clid = TC_H_MAKE(qid, clid); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (clid) 215762306a36Sopenharmony_ci cl = cops->find(q, clid); 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci if (cl == 0) { 216062306a36Sopenharmony_ci err = -ENOENT; 216162306a36Sopenharmony_ci if (n->nlmsg_type != RTM_NEWTCLASS || 216262306a36Sopenharmony_ci !(n->nlmsg_flags & NLM_F_CREATE)) 216362306a36Sopenharmony_ci goto out; 216462306a36Sopenharmony_ci } else { 216562306a36Sopenharmony_ci switch (n->nlmsg_type) { 216662306a36Sopenharmony_ci case RTM_NEWTCLASS: 216762306a36Sopenharmony_ci err = -EEXIST; 216862306a36Sopenharmony_ci if (n->nlmsg_flags & NLM_F_EXCL) 216962306a36Sopenharmony_ci goto out; 217062306a36Sopenharmony_ci break; 217162306a36Sopenharmony_ci case RTM_DELTCLASS: 217262306a36Sopenharmony_ci err = tclass_del_notify(net, cops, skb, n, q, cl, extack); 217362306a36Sopenharmony_ci /* Unbind the class with flilters with 0 */ 217462306a36Sopenharmony_ci tc_bind_tclass(q, portid, clid, 0); 217562306a36Sopenharmony_ci goto out; 217662306a36Sopenharmony_ci case RTM_GETTCLASS: 217762306a36Sopenharmony_ci err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS, extack); 217862306a36Sopenharmony_ci goto out; 217962306a36Sopenharmony_ci default: 218062306a36Sopenharmony_ci err = -EINVAL; 218162306a36Sopenharmony_ci goto out; 218262306a36Sopenharmony_ci } 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci if (tca[TCA_INGRESS_BLOCK] || tca[TCA_EGRESS_BLOCK]) { 218662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Shared blocks are not supported for classes"); 218762306a36Sopenharmony_ci return -EOPNOTSUPP; 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci new_cl = cl; 219162306a36Sopenharmony_ci err = -EOPNOTSUPP; 219262306a36Sopenharmony_ci if (cops->change) 219362306a36Sopenharmony_ci err = cops->change(q, clid, portid, tca, &new_cl, extack); 219462306a36Sopenharmony_ci if (err == 0) { 219562306a36Sopenharmony_ci tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS, extack); 219662306a36Sopenharmony_ci /* We just create a new class, need to do reverse binding. */ 219762306a36Sopenharmony_ci if (cl != new_cl) 219862306a36Sopenharmony_ci tc_bind_tclass(q, portid, clid, new_cl); 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ciout: 220162306a36Sopenharmony_ci return err; 220262306a36Sopenharmony_ci} 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_cistruct qdisc_dump_args { 220562306a36Sopenharmony_ci struct qdisc_walker w; 220662306a36Sopenharmony_ci struct sk_buff *skb; 220762306a36Sopenharmony_ci struct netlink_callback *cb; 220862306a36Sopenharmony_ci}; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_cistatic int qdisc_class_dump(struct Qdisc *q, unsigned long cl, 221162306a36Sopenharmony_ci struct qdisc_walker *arg) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid, 221662306a36Sopenharmony_ci a->cb->nlh->nlmsg_seq, NLM_F_MULTI, 221762306a36Sopenharmony_ci RTM_NEWTCLASS, NULL); 221862306a36Sopenharmony_ci} 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_cistatic int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, 222162306a36Sopenharmony_ci struct tcmsg *tcm, struct netlink_callback *cb, 222262306a36Sopenharmony_ci int *t_p, int s_t) 222362306a36Sopenharmony_ci{ 222462306a36Sopenharmony_ci struct qdisc_dump_args arg; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci if (tc_qdisc_dump_ignore(q, false) || 222762306a36Sopenharmony_ci *t_p < s_t || !q->ops->cl_ops || 222862306a36Sopenharmony_ci (tcm->tcm_parent && 222962306a36Sopenharmony_ci TC_H_MAJ(tcm->tcm_parent) != q->handle)) { 223062306a36Sopenharmony_ci (*t_p)++; 223162306a36Sopenharmony_ci return 0; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci if (*t_p > s_t) 223462306a36Sopenharmony_ci memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); 223562306a36Sopenharmony_ci arg.w.fn = qdisc_class_dump; 223662306a36Sopenharmony_ci arg.skb = skb; 223762306a36Sopenharmony_ci arg.cb = cb; 223862306a36Sopenharmony_ci arg.w.stop = 0; 223962306a36Sopenharmony_ci arg.w.skip = cb->args[1]; 224062306a36Sopenharmony_ci arg.w.count = 0; 224162306a36Sopenharmony_ci q->ops->cl_ops->walk(q, &arg.w); 224262306a36Sopenharmony_ci cb->args[1] = arg.w.count; 224362306a36Sopenharmony_ci if (arg.w.stop) 224462306a36Sopenharmony_ci return -1; 224562306a36Sopenharmony_ci (*t_p)++; 224662306a36Sopenharmony_ci return 0; 224762306a36Sopenharmony_ci} 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_cistatic int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb, 225062306a36Sopenharmony_ci struct tcmsg *tcm, struct netlink_callback *cb, 225162306a36Sopenharmony_ci int *t_p, int s_t, bool recur) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci struct Qdisc *q; 225462306a36Sopenharmony_ci int b; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci if (!root) 225762306a36Sopenharmony_ci return 0; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0) 226062306a36Sopenharmony_ci return -1; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci if (!qdisc_dev(root) || !recur) 226362306a36Sopenharmony_ci return 0; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci if (tcm->tcm_parent) { 226662306a36Sopenharmony_ci q = qdisc_match_from_root(root, TC_H_MAJ(tcm->tcm_parent)); 226762306a36Sopenharmony_ci if (q && q != root && 226862306a36Sopenharmony_ci tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0) 226962306a36Sopenharmony_ci return -1; 227062306a36Sopenharmony_ci return 0; 227162306a36Sopenharmony_ci } 227262306a36Sopenharmony_ci hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) { 227362306a36Sopenharmony_ci if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0) 227462306a36Sopenharmony_ci return -1; 227562306a36Sopenharmony_ci } 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci return 0; 227862306a36Sopenharmony_ci} 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_cistatic int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci struct tcmsg *tcm = nlmsg_data(cb->nlh); 228362306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 228462306a36Sopenharmony_ci struct netdev_queue *dev_queue; 228562306a36Sopenharmony_ci struct net_device *dev; 228662306a36Sopenharmony_ci int t, s_t; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci if (nlmsg_len(cb->nlh) < sizeof(*tcm)) 228962306a36Sopenharmony_ci return 0; 229062306a36Sopenharmony_ci dev = dev_get_by_index(net, tcm->tcm_ifindex); 229162306a36Sopenharmony_ci if (!dev) 229262306a36Sopenharmony_ci return 0; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci s_t = cb->args[0]; 229562306a36Sopenharmony_ci t = 0; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci if (tc_dump_tclass_root(rtnl_dereference(dev->qdisc), 229862306a36Sopenharmony_ci skb, tcm, cb, &t, s_t, true) < 0) 229962306a36Sopenharmony_ci goto done; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci dev_queue = dev_ingress_queue(dev); 230262306a36Sopenharmony_ci if (dev_queue && 230362306a36Sopenharmony_ci tc_dump_tclass_root(rtnl_dereference(dev_queue->qdisc_sleeping), 230462306a36Sopenharmony_ci skb, tcm, cb, &t, s_t, false) < 0) 230562306a36Sopenharmony_ci goto done; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_cidone: 230862306a36Sopenharmony_ci cb->args[0] = t; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci dev_put(dev); 231162306a36Sopenharmony_ci return skb->len; 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 231562306a36Sopenharmony_cistatic int psched_show(struct seq_file *seq, void *v) 231662306a36Sopenharmony_ci{ 231762306a36Sopenharmony_ci seq_printf(seq, "%08x %08x %08x %08x\n", 231862306a36Sopenharmony_ci (u32)NSEC_PER_USEC, (u32)PSCHED_TICKS2NS(1), 231962306a36Sopenharmony_ci 1000000, 232062306a36Sopenharmony_ci (u32)NSEC_PER_SEC / hrtimer_resolution); 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cistatic int __net_init psched_net_init(struct net *net) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci struct proc_dir_entry *e; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci e = proc_create_single("psched", 0, net->proc_net, psched_show); 233062306a36Sopenharmony_ci if (e == NULL) 233162306a36Sopenharmony_ci return -ENOMEM; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci return 0; 233462306a36Sopenharmony_ci} 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_cistatic void __net_exit psched_net_exit(struct net *net) 233762306a36Sopenharmony_ci{ 233862306a36Sopenharmony_ci remove_proc_entry("psched", net->proc_net); 233962306a36Sopenharmony_ci} 234062306a36Sopenharmony_ci#else 234162306a36Sopenharmony_cistatic int __net_init psched_net_init(struct net *net) 234262306a36Sopenharmony_ci{ 234362306a36Sopenharmony_ci return 0; 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic void __net_exit psched_net_exit(struct net *net) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci} 234962306a36Sopenharmony_ci#endif 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_cistatic struct pernet_operations psched_net_ops = { 235262306a36Sopenharmony_ci .init = psched_net_init, 235362306a36Sopenharmony_ci .exit = psched_net_exit, 235462306a36Sopenharmony_ci}; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_RETPOLINE) 235762306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(tc_skip_wrapper); 235862306a36Sopenharmony_ci#endif 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_cistatic int __init pktsched_init(void) 236162306a36Sopenharmony_ci{ 236262306a36Sopenharmony_ci int err; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci err = register_pernet_subsys(&psched_net_ops); 236562306a36Sopenharmony_ci if (err) { 236662306a36Sopenharmony_ci pr_err("pktsched_init: " 236762306a36Sopenharmony_ci "cannot initialize per netns operations\n"); 236862306a36Sopenharmony_ci return err; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci register_qdisc(&pfifo_fast_ops); 237262306a36Sopenharmony_ci register_qdisc(&pfifo_qdisc_ops); 237362306a36Sopenharmony_ci register_qdisc(&bfifo_qdisc_ops); 237462306a36Sopenharmony_ci register_qdisc(&pfifo_head_drop_qdisc_ops); 237562306a36Sopenharmony_ci register_qdisc(&mq_qdisc_ops); 237662306a36Sopenharmony_ci register_qdisc(&noqueue_qdisc_ops); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, 0); 237962306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, 0); 238062306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc, 238162306a36Sopenharmony_ci 0); 238262306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, 0); 238362306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, 0); 238462306a36Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass, 238562306a36Sopenharmony_ci 0); 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci tc_wrapper_init(); 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci return 0; 239062306a36Sopenharmony_ci} 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_cisubsys_initcall(pktsched_init); 2393