18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_cbq.c Class-Based Queueing discipline. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 158c2ecf20Sopenharmony_ci#include <net/netlink.h> 168c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 178c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Class-Based Queueing (CBQ) algorithm. 218c2ecf20Sopenharmony_ci ======================================= 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci Sources: [1] Sally Floyd and Van Jacobson, "Link-sharing and Resource 248c2ecf20Sopenharmony_ci Management Models for Packet Networks", 258c2ecf20Sopenharmony_ci IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci [2] Sally Floyd, "Notes on CBQ and Guaranteed Service", 1995 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci [3] Sally Floyd, "Notes on Class-Based Queueing: Setting 308c2ecf20Sopenharmony_ci Parameters", 1996 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci [4] Sally Floyd and Michael Speer, "Experimental Results 338c2ecf20Sopenharmony_ci for Class-Based Queueing", 1998, not published. 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci ----------------------------------------------------------------------- 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci Algorithm skeleton was taken from NS simulator cbq.cc. 388c2ecf20Sopenharmony_ci If someone wants to check this code against the LBL version, 398c2ecf20Sopenharmony_ci he should take into account that ONLY the skeleton was borrowed, 408c2ecf20Sopenharmony_ci the implementation is different. Particularly: 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci --- The WRR algorithm is different. Our version looks more 438c2ecf20Sopenharmony_ci reasonable (I hope) and works when quanta are allowed to be 448c2ecf20Sopenharmony_ci less than MTU, which is always the case when real time classes 458c2ecf20Sopenharmony_ci have small rates. Note, that the statement of [3] is 468c2ecf20Sopenharmony_ci incomplete, delay may actually be estimated even if class 478c2ecf20Sopenharmony_ci per-round allotment is less than MTU. Namely, if per-round 488c2ecf20Sopenharmony_ci allotment is W*r_i, and r_1+...+r_k = r < 1 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci delay_i <= ([MTU/(W*r_i)]*W*r + W*r + k*MTU)/B 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci In the worst case we have IntServ estimate with D = W*r+k*MTU 538c2ecf20Sopenharmony_ci and C = MTU*r. The proof (if correct at all) is trivial. 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci --- It seems that cbq-2.0 is not very accurate. At least, I cannot 578c2ecf20Sopenharmony_ci interpret some places, which look like wrong translations 588c2ecf20Sopenharmony_ci from NS. Anyone is advised to find these differences 598c2ecf20Sopenharmony_ci and explain to me, why I am wrong 8). 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci --- Linux has no EOI event, so that we cannot estimate true class 628c2ecf20Sopenharmony_ci idle time. Workaround is to consider the next dequeue event 638c2ecf20Sopenharmony_ci as sign that previous packet is finished. This is wrong because of 648c2ecf20Sopenharmony_ci internal device queueing, but on a permanently loaded link it is true. 658c2ecf20Sopenharmony_ci Moreover, combined with clock integrator, this scheme looks 668c2ecf20Sopenharmony_ci very close to an ideal solution. */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct cbq_sched_data; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct cbq_class { 728c2ecf20Sopenharmony_ci struct Qdisc_class_common common; 738c2ecf20Sopenharmony_ci struct cbq_class *next_alive; /* next class with backlog in this priority band */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Parameters */ 768c2ecf20Sopenharmony_ci unsigned char priority; /* class priority */ 778c2ecf20Sopenharmony_ci unsigned char priority2; /* priority to be used after overlimit */ 788c2ecf20Sopenharmony_ci unsigned char ewma_log; /* time constant for idle time calculation */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci u32 defmap; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Link-sharing scheduler parameters */ 838c2ecf20Sopenharmony_ci long maxidle; /* Class parameters: see below. */ 848c2ecf20Sopenharmony_ci long offtime; 858c2ecf20Sopenharmony_ci long minidle; 868c2ecf20Sopenharmony_ci u32 avpkt; 878c2ecf20Sopenharmony_ci struct qdisc_rate_table *R_tab; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* General scheduler (WRR) parameters */ 908c2ecf20Sopenharmony_ci long allot; 918c2ecf20Sopenharmony_ci long quantum; /* Allotment per WRR round */ 928c2ecf20Sopenharmony_ci long weight; /* Relative allotment: see below */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci struct Qdisc *qdisc; /* Ptr to CBQ discipline */ 958c2ecf20Sopenharmony_ci struct cbq_class *split; /* Ptr to split node */ 968c2ecf20Sopenharmony_ci struct cbq_class *share; /* Ptr to LS parent in the class tree */ 978c2ecf20Sopenharmony_ci struct cbq_class *tparent; /* Ptr to tree parent in the class tree */ 988c2ecf20Sopenharmony_ci struct cbq_class *borrow; /* NULL if class is bandwidth limited; 998c2ecf20Sopenharmony_ci parent otherwise */ 1008c2ecf20Sopenharmony_ci struct cbq_class *sibling; /* Sibling chain */ 1018c2ecf20Sopenharmony_ci struct cbq_class *children; /* Pointer to children chain */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci struct Qdisc *q; /* Elementary queueing discipline */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* Variables */ 1078c2ecf20Sopenharmony_ci unsigned char cpriority; /* Effective priority */ 1088c2ecf20Sopenharmony_ci unsigned char delayed; 1098c2ecf20Sopenharmony_ci unsigned char level; /* level of the class in hierarchy: 1108c2ecf20Sopenharmony_ci 0 for leaf classes, and maximal 1118c2ecf20Sopenharmony_ci level of children + 1 for nodes. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci psched_time_t last; /* Last end of service */ 1158c2ecf20Sopenharmony_ci psched_time_t undertime; 1168c2ecf20Sopenharmony_ci long avgidle; 1178c2ecf20Sopenharmony_ci long deficit; /* Saved deficit for WRR */ 1188c2ecf20Sopenharmony_ci psched_time_t penalized; 1198c2ecf20Sopenharmony_ci struct gnet_stats_basic_packed bstats; 1208c2ecf20Sopenharmony_ci struct gnet_stats_queue qstats; 1218c2ecf20Sopenharmony_ci struct net_rate_estimator __rcu *rate_est; 1228c2ecf20Sopenharmony_ci struct tc_cbq_xstats xstats; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci struct tcf_proto __rcu *filter_list; 1258c2ecf20Sopenharmony_ci struct tcf_block *block; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci int filters; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci struct cbq_class *defaults[TC_PRIO_MAX + 1]; 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct cbq_sched_data { 1338c2ecf20Sopenharmony_ci struct Qdisc_class_hash clhash; /* Hash table of all classes */ 1348c2ecf20Sopenharmony_ci int nclasses[TC_CBQ_MAXPRIO + 1]; 1358c2ecf20Sopenharmony_ci unsigned int quanta[TC_CBQ_MAXPRIO + 1]; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci struct cbq_class link; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci unsigned int activemask; 1408c2ecf20Sopenharmony_ci struct cbq_class *active[TC_CBQ_MAXPRIO + 1]; /* List of all classes 1418c2ecf20Sopenharmony_ci with backlog */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 1448c2ecf20Sopenharmony_ci struct cbq_class *rx_class; 1458c2ecf20Sopenharmony_ci#endif 1468c2ecf20Sopenharmony_ci struct cbq_class *tx_class; 1478c2ecf20Sopenharmony_ci struct cbq_class *tx_borrowed; 1488c2ecf20Sopenharmony_ci int tx_len; 1498c2ecf20Sopenharmony_ci psched_time_t now; /* Cached timestamp */ 1508c2ecf20Sopenharmony_ci unsigned int pmask; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci struct hrtimer delay_timer; 1538c2ecf20Sopenharmony_ci struct qdisc_watchdog watchdog; /* Watchdog timer, 1548c2ecf20Sopenharmony_ci started when CBQ has 1558c2ecf20Sopenharmony_ci backlog, but cannot 1568c2ecf20Sopenharmony_ci transmit just now */ 1578c2ecf20Sopenharmony_ci psched_tdiff_t wd_expires; 1588c2ecf20Sopenharmony_ci int toplevel; 1598c2ecf20Sopenharmony_ci u32 hgenerator; 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#define L2T(cl, len) qdisc_l2t((cl)->R_tab, len) 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic inline struct cbq_class * 1668c2ecf20Sopenharmony_cicbq_class_lookup(struct cbq_sched_data *q, u32 classid) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct Qdisc_class_common *clc; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci clc = qdisc_class_find(&q->clhash, classid); 1718c2ecf20Sopenharmony_ci if (clc == NULL) 1728c2ecf20Sopenharmony_ci return NULL; 1738c2ecf20Sopenharmony_ci return container_of(clc, struct cbq_class, common); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct cbq_class * 1798c2ecf20Sopenharmony_cicbq_reclassify(struct sk_buff *skb, struct cbq_class *this) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct cbq_class *cl; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci for (cl = this->tparent; cl; cl = cl->tparent) { 1848c2ecf20Sopenharmony_ci struct cbq_class *new = cl->defaults[TC_PRIO_BESTEFFORT]; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (new != NULL && new != this) 1878c2ecf20Sopenharmony_ci return new; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci return NULL; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci#endif 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* Classify packet. The procedure is pretty complicated, but 1958c2ecf20Sopenharmony_ci * it allows us to combine link sharing and priority scheduling 1968c2ecf20Sopenharmony_ci * transparently. 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * Namely, you can put link sharing rules (f.e. route based) at root of CBQ, 1998c2ecf20Sopenharmony_ci * so that it resolves to split nodes. Then packets are classified 2008c2ecf20Sopenharmony_ci * by logical priority, or a more specific classifier may be attached 2018c2ecf20Sopenharmony_ci * to the split node. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic struct cbq_class * 2058c2ecf20Sopenharmony_cicbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 2088c2ecf20Sopenharmony_ci struct cbq_class *head = &q->link; 2098c2ecf20Sopenharmony_ci struct cbq_class **defmap; 2108c2ecf20Sopenharmony_ci struct cbq_class *cl = NULL; 2118c2ecf20Sopenharmony_ci u32 prio = skb->priority; 2128c2ecf20Sopenharmony_ci struct tcf_proto *fl; 2138c2ecf20Sopenharmony_ci struct tcf_result res; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * Step 1. If skb->priority points to one of our classes, use it. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci if (TC_H_MAJ(prio ^ sch->handle) == 0 && 2198c2ecf20Sopenharmony_ci (cl = cbq_class_lookup(q, prio)) != NULL) 2208c2ecf20Sopenharmony_ci return cl; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 2238c2ecf20Sopenharmony_ci for (;;) { 2248c2ecf20Sopenharmony_ci int result = 0; 2258c2ecf20Sopenharmony_ci defmap = head->defaults; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci fl = rcu_dereference_bh(head->filter_list); 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * Step 2+n. Apply classifier. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci result = tcf_classify(skb, fl, &res, true); 2328c2ecf20Sopenharmony_ci if (!fl || result < 0) 2338c2ecf20Sopenharmony_ci goto fallback; 2348c2ecf20Sopenharmony_ci if (result == TC_ACT_SHOT) 2358c2ecf20Sopenharmony_ci return NULL; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci cl = (void *)res.class; 2388c2ecf20Sopenharmony_ci if (!cl) { 2398c2ecf20Sopenharmony_ci if (TC_H_MAJ(res.classid)) 2408c2ecf20Sopenharmony_ci cl = cbq_class_lookup(q, res.classid); 2418c2ecf20Sopenharmony_ci else if ((cl = defmap[res.classid & TC_PRIO_MAX]) == NULL) 2428c2ecf20Sopenharmony_ci cl = defmap[TC_PRIO_BESTEFFORT]; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (cl == NULL) 2458c2ecf20Sopenharmony_ci goto fallback; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci if (cl->level >= head->level) 2488c2ecf20Sopenharmony_ci goto fallback; 2498c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 2508c2ecf20Sopenharmony_ci switch (result) { 2518c2ecf20Sopenharmony_ci case TC_ACT_QUEUED: 2528c2ecf20Sopenharmony_ci case TC_ACT_STOLEN: 2538c2ecf20Sopenharmony_ci case TC_ACT_TRAP: 2548c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 2558c2ecf20Sopenharmony_ci fallthrough; 2568c2ecf20Sopenharmony_ci case TC_ACT_RECLASSIFY: 2578c2ecf20Sopenharmony_ci return cbq_reclassify(skb, cl); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci#endif 2608c2ecf20Sopenharmony_ci if (cl->level == 0) 2618c2ecf20Sopenharmony_ci return cl; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * Step 3+n. If classifier selected a link sharing class, 2658c2ecf20Sopenharmony_ci * apply agency specific classifier. 2668c2ecf20Sopenharmony_ci * Repeat this procdure until we hit a leaf node. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci head = cl; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cifallback: 2728c2ecf20Sopenharmony_ci cl = head; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Step 4. No success... 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci if (TC_H_MAJ(prio) == 0 && 2788c2ecf20Sopenharmony_ci !(cl = head->defaults[prio & TC_PRIO_MAX]) && 2798c2ecf20Sopenharmony_ci !(cl = head->defaults[TC_PRIO_BESTEFFORT])) 2808c2ecf20Sopenharmony_ci return head; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return cl; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* 2868c2ecf20Sopenharmony_ci * A packet has just been enqueued on the empty class. 2878c2ecf20Sopenharmony_ci * cbq_activate_class adds it to the tail of active class list 2888c2ecf20Sopenharmony_ci * of its priority band. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic inline void cbq_activate_class(struct cbq_class *cl) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(cl->qdisc); 2948c2ecf20Sopenharmony_ci int prio = cl->cpriority; 2958c2ecf20Sopenharmony_ci struct cbq_class *cl_tail; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci cl_tail = q->active[prio]; 2988c2ecf20Sopenharmony_ci q->active[prio] = cl; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (cl_tail != NULL) { 3018c2ecf20Sopenharmony_ci cl->next_alive = cl_tail->next_alive; 3028c2ecf20Sopenharmony_ci cl_tail->next_alive = cl; 3038c2ecf20Sopenharmony_ci } else { 3048c2ecf20Sopenharmony_ci cl->next_alive = cl; 3058c2ecf20Sopenharmony_ci q->activemask |= (1<<prio); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/* 3108c2ecf20Sopenharmony_ci * Unlink class from active chain. 3118c2ecf20Sopenharmony_ci * Note that this same procedure is done directly in cbq_dequeue* 3128c2ecf20Sopenharmony_ci * during round-robin procedure. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic void cbq_deactivate_class(struct cbq_class *this) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(this->qdisc); 3188c2ecf20Sopenharmony_ci int prio = this->cpriority; 3198c2ecf20Sopenharmony_ci struct cbq_class *cl; 3208c2ecf20Sopenharmony_ci struct cbq_class *cl_prev = q->active[prio]; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci do { 3238c2ecf20Sopenharmony_ci cl = cl_prev->next_alive; 3248c2ecf20Sopenharmony_ci if (cl == this) { 3258c2ecf20Sopenharmony_ci cl_prev->next_alive = cl->next_alive; 3268c2ecf20Sopenharmony_ci cl->next_alive = NULL; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (cl == q->active[prio]) { 3298c2ecf20Sopenharmony_ci q->active[prio] = cl_prev; 3308c2ecf20Sopenharmony_ci if (cl == q->active[prio]) { 3318c2ecf20Sopenharmony_ci q->active[prio] = NULL; 3328c2ecf20Sopenharmony_ci q->activemask &= ~(1<<prio); 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci return; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } while ((cl_prev = cl) != q->active[prio]); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void 3428c2ecf20Sopenharmony_cicbq_mark_toplevel(struct cbq_sched_data *q, struct cbq_class *cl) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci int toplevel = q->toplevel; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (toplevel > cl->level) { 3478c2ecf20Sopenharmony_ci psched_time_t now = psched_get_time(); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci do { 3508c2ecf20Sopenharmony_ci if (cl->undertime < now) { 3518c2ecf20Sopenharmony_ci q->toplevel = cl->level; 3528c2ecf20Sopenharmony_ci return; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } while ((cl = cl->borrow) != NULL && toplevel > cl->level); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int 3598c2ecf20Sopenharmony_cicbq_enqueue(struct sk_buff *skb, struct Qdisc *sch, 3608c2ecf20Sopenharmony_ci struct sk_buff **to_free) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 3638c2ecf20Sopenharmony_ci int ret; 3648c2ecf20Sopenharmony_ci struct cbq_class *cl = cbq_classify(skb, sch, &ret); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 3678c2ecf20Sopenharmony_ci q->rx_class = cl; 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci if (cl == NULL) { 3708c2ecf20Sopenharmony_ci if (ret & __NET_XMIT_BYPASS) 3718c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 3728c2ecf20Sopenharmony_ci __qdisc_drop(skb, to_free); 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret = qdisc_enqueue(skb, cl->q, to_free); 3778c2ecf20Sopenharmony_ci if (ret == NET_XMIT_SUCCESS) { 3788c2ecf20Sopenharmony_ci sch->q.qlen++; 3798c2ecf20Sopenharmony_ci cbq_mark_toplevel(q, cl); 3808c2ecf20Sopenharmony_ci if (!cl->next_alive) 3818c2ecf20Sopenharmony_ci cbq_activate_class(cl); 3828c2ecf20Sopenharmony_ci return ret; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (net_xmit_drop_count(ret)) { 3868c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 3878c2ecf20Sopenharmony_ci cbq_mark_toplevel(q, cl); 3888c2ecf20Sopenharmony_ci cl->qstats.drops++; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci return ret; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* Overlimit action: penalize leaf class by adding offtime */ 3948c2ecf20Sopenharmony_cistatic void cbq_overlimit(struct cbq_class *cl) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(cl->qdisc); 3978c2ecf20Sopenharmony_ci psched_tdiff_t delay = cl->undertime - q->now; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!cl->delayed) { 4008c2ecf20Sopenharmony_ci delay += cl->offtime; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * Class goes to sleep, so that it will have no 4048c2ecf20Sopenharmony_ci * chance to work avgidle. Let's forgive it 8) 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * BTW cbq-2.0 has a crap in this 4078c2ecf20Sopenharmony_ci * place, apparently they forgot to shift it by cl->ewma_log. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci if (cl->avgidle < 0) 4108c2ecf20Sopenharmony_ci delay -= (-cl->avgidle) - ((-cl->avgidle) >> cl->ewma_log); 4118c2ecf20Sopenharmony_ci if (cl->avgidle < cl->minidle) 4128c2ecf20Sopenharmony_ci cl->avgidle = cl->minidle; 4138c2ecf20Sopenharmony_ci if (delay <= 0) 4148c2ecf20Sopenharmony_ci delay = 1; 4158c2ecf20Sopenharmony_ci cl->undertime = q->now + delay; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci cl->xstats.overactions++; 4188c2ecf20Sopenharmony_ci cl->delayed = 1; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci if (q->wd_expires == 0 || q->wd_expires > delay) 4218c2ecf20Sopenharmony_ci q->wd_expires = delay; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Dirty work! We must schedule wakeups based on 4248c2ecf20Sopenharmony_ci * real available rate, rather than leaf rate, 4258c2ecf20Sopenharmony_ci * which may be tiny (even zero). 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci if (q->toplevel == TC_CBQ_MAXLEVEL) { 4288c2ecf20Sopenharmony_ci struct cbq_class *b; 4298c2ecf20Sopenharmony_ci psched_tdiff_t base_delay = q->wd_expires; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci for (b = cl->borrow; b; b = b->borrow) { 4328c2ecf20Sopenharmony_ci delay = b->undertime - q->now; 4338c2ecf20Sopenharmony_ci if (delay < base_delay) { 4348c2ecf20Sopenharmony_ci if (delay <= 0) 4358c2ecf20Sopenharmony_ci delay = 1; 4368c2ecf20Sopenharmony_ci base_delay = delay; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci q->wd_expires = base_delay; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic psched_tdiff_t cbq_undelay_prio(struct cbq_sched_data *q, int prio, 4458c2ecf20Sopenharmony_ci psched_time_t now) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct cbq_class *cl; 4488c2ecf20Sopenharmony_ci struct cbq_class *cl_prev = q->active[prio]; 4498c2ecf20Sopenharmony_ci psched_time_t sched = now; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (cl_prev == NULL) 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci do { 4558c2ecf20Sopenharmony_ci cl = cl_prev->next_alive; 4568c2ecf20Sopenharmony_ci if (now - cl->penalized > 0) { 4578c2ecf20Sopenharmony_ci cl_prev->next_alive = cl->next_alive; 4588c2ecf20Sopenharmony_ci cl->next_alive = NULL; 4598c2ecf20Sopenharmony_ci cl->cpriority = cl->priority; 4608c2ecf20Sopenharmony_ci cl->delayed = 0; 4618c2ecf20Sopenharmony_ci cbq_activate_class(cl); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (cl == q->active[prio]) { 4648c2ecf20Sopenharmony_ci q->active[prio] = cl_prev; 4658c2ecf20Sopenharmony_ci if (cl == q->active[prio]) { 4668c2ecf20Sopenharmony_ci q->active[prio] = NULL; 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci cl = cl_prev->next_alive; 4728c2ecf20Sopenharmony_ci } else if (sched - cl->penalized > 0) 4738c2ecf20Sopenharmony_ci sched = cl->penalized; 4748c2ecf20Sopenharmony_ci } while ((cl_prev = cl) != q->active[prio]); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return sched - now; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic enum hrtimer_restart cbq_undelay(struct hrtimer *timer) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct cbq_sched_data *q = container_of(timer, struct cbq_sched_data, 4828c2ecf20Sopenharmony_ci delay_timer); 4838c2ecf20Sopenharmony_ci struct Qdisc *sch = q->watchdog.qdisc; 4848c2ecf20Sopenharmony_ci psched_time_t now; 4858c2ecf20Sopenharmony_ci psched_tdiff_t delay = 0; 4868c2ecf20Sopenharmony_ci unsigned int pmask; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci now = psched_get_time(); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci pmask = q->pmask; 4918c2ecf20Sopenharmony_ci q->pmask = 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci while (pmask) { 4948c2ecf20Sopenharmony_ci int prio = ffz(~pmask); 4958c2ecf20Sopenharmony_ci psched_tdiff_t tmp; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci pmask &= ~(1<<prio); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci tmp = cbq_undelay_prio(q, prio, now); 5008c2ecf20Sopenharmony_ci if (tmp > 0) { 5018c2ecf20Sopenharmony_ci q->pmask |= 1<<prio; 5028c2ecf20Sopenharmony_ci if (tmp < delay || delay == 0) 5038c2ecf20Sopenharmony_ci delay = tmp; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (delay) { 5088c2ecf20Sopenharmony_ci ktime_t time; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci time = 0; 5118c2ecf20Sopenharmony_ci time = ktime_add_ns(time, PSCHED_TICKS2NS(now + delay)); 5128c2ecf20Sopenharmony_ci hrtimer_start(&q->delay_timer, time, HRTIMER_MODE_ABS_PINNED); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci __netif_schedule(qdisc_root(sch)); 5168c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* 5208c2ecf20Sopenharmony_ci * It is mission critical procedure. 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * We "regenerate" toplevel cutoff, if transmitting class 5238c2ecf20Sopenharmony_ci * has backlog and it is not regulated. It is not part of 5248c2ecf20Sopenharmony_ci * original CBQ description, but looks more reasonable. 5258c2ecf20Sopenharmony_ci * Probably, it is wrong. This question needs further investigation. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic inline void 5298c2ecf20Sopenharmony_cicbq_update_toplevel(struct cbq_sched_data *q, struct cbq_class *cl, 5308c2ecf20Sopenharmony_ci struct cbq_class *borrowed) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci if (cl && q->toplevel >= borrowed->level) { 5338c2ecf20Sopenharmony_ci if (cl->q->q.qlen > 1) { 5348c2ecf20Sopenharmony_ci do { 5358c2ecf20Sopenharmony_ci if (borrowed->undertime == PSCHED_PASTPERFECT) { 5368c2ecf20Sopenharmony_ci q->toplevel = borrowed->level; 5378c2ecf20Sopenharmony_ci return; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci } while ((borrowed = borrowed->borrow) != NULL); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci#if 0 5428c2ecf20Sopenharmony_ci /* It is not necessary now. Uncommenting it 5438c2ecf20Sopenharmony_ci will save CPU cycles, but decrease fairness. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci q->toplevel = TC_CBQ_MAXLEVEL; 5468c2ecf20Sopenharmony_ci#endif 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void 5518c2ecf20Sopenharmony_cicbq_update(struct cbq_sched_data *q) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct cbq_class *this = q->tx_class; 5548c2ecf20Sopenharmony_ci struct cbq_class *cl = this; 5558c2ecf20Sopenharmony_ci int len = q->tx_len; 5568c2ecf20Sopenharmony_ci psched_time_t now; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci q->tx_class = NULL; 5598c2ecf20Sopenharmony_ci /* Time integrator. We calculate EOS time 5608c2ecf20Sopenharmony_ci * by adding expected packet transmission time. 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci now = q->now + L2T(&q->link, len); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci for ( ; cl; cl = cl->share) { 5658c2ecf20Sopenharmony_ci long avgidle = cl->avgidle; 5668c2ecf20Sopenharmony_ci long idle; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci cl->bstats.packets++; 5698c2ecf20Sopenharmony_ci cl->bstats.bytes += len; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * (now - last) is total time between packet right edges. 5738c2ecf20Sopenharmony_ci * (last_pktlen/rate) is "virtual" busy time, so that 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * idle = (now - last) - last_pktlen/rate 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci idle = now - cl->last; 5798c2ecf20Sopenharmony_ci if ((unsigned long)idle > 128*1024*1024) { 5808c2ecf20Sopenharmony_ci avgidle = cl->maxidle; 5818c2ecf20Sopenharmony_ci } else { 5828c2ecf20Sopenharmony_ci idle -= L2T(cl, len); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* true_avgidle := (1-W)*true_avgidle + W*idle, 5858c2ecf20Sopenharmony_ci * where W=2^{-ewma_log}. But cl->avgidle is scaled: 5868c2ecf20Sopenharmony_ci * cl->avgidle == true_avgidle/W, 5878c2ecf20Sopenharmony_ci * hence: 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci avgidle += idle - (avgidle>>cl->ewma_log); 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (avgidle <= 0) { 5938c2ecf20Sopenharmony_ci /* Overlimit or at-limit */ 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (avgidle < cl->minidle) 5968c2ecf20Sopenharmony_ci avgidle = cl->minidle; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci cl->avgidle = avgidle; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Calculate expected time, when this class 6018c2ecf20Sopenharmony_ci * will be allowed to send. 6028c2ecf20Sopenharmony_ci * It will occur, when: 6038c2ecf20Sopenharmony_ci * (1-W)*true_avgidle + W*delay = 0, i.e. 6048c2ecf20Sopenharmony_ci * idle = (1/W - 1)*(-true_avgidle) 6058c2ecf20Sopenharmony_ci * or 6068c2ecf20Sopenharmony_ci * idle = (1 - W)*(-cl->avgidle); 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci idle = (-avgidle) - ((-avgidle) >> cl->ewma_log); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * That is not all. 6128c2ecf20Sopenharmony_ci * To maintain the rate allocated to the class, 6138c2ecf20Sopenharmony_ci * we add to undertime virtual clock, 6148c2ecf20Sopenharmony_ci * necessary to complete transmitted packet. 6158c2ecf20Sopenharmony_ci * (len/phys_bandwidth has been already passed 6168c2ecf20Sopenharmony_ci * to the moment of cbq_update) 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci idle -= L2T(&q->link, len); 6208c2ecf20Sopenharmony_ci idle += L2T(cl, len); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci cl->undertime = now + idle; 6238c2ecf20Sopenharmony_ci } else { 6248c2ecf20Sopenharmony_ci /* Underlimit */ 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci cl->undertime = PSCHED_PASTPERFECT; 6278c2ecf20Sopenharmony_ci if (avgidle > cl->maxidle) 6288c2ecf20Sopenharmony_ci cl->avgidle = cl->maxidle; 6298c2ecf20Sopenharmony_ci else 6308c2ecf20Sopenharmony_ci cl->avgidle = avgidle; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci if ((s64)(now - cl->last) > 0) 6338c2ecf20Sopenharmony_ci cl->last = now; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci cbq_update_toplevel(q, this, q->tx_borrowed); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic inline struct cbq_class * 6408c2ecf20Sopenharmony_cicbq_under_limit(struct cbq_class *cl) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(cl->qdisc); 6438c2ecf20Sopenharmony_ci struct cbq_class *this_cl = cl; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (cl->tparent == NULL) 6468c2ecf20Sopenharmony_ci return cl; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (cl->undertime == PSCHED_PASTPERFECT || q->now >= cl->undertime) { 6498c2ecf20Sopenharmony_ci cl->delayed = 0; 6508c2ecf20Sopenharmony_ci return cl; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci do { 6548c2ecf20Sopenharmony_ci /* It is very suspicious place. Now overlimit 6558c2ecf20Sopenharmony_ci * action is generated for not bounded classes 6568c2ecf20Sopenharmony_ci * only if link is completely congested. 6578c2ecf20Sopenharmony_ci * Though it is in agree with ancestor-only paradigm, 6588c2ecf20Sopenharmony_ci * it looks very stupid. Particularly, 6598c2ecf20Sopenharmony_ci * it means that this chunk of code will either 6608c2ecf20Sopenharmony_ci * never be called or result in strong amplification 6618c2ecf20Sopenharmony_ci * of burstiness. Dangerous, silly, and, however, 6628c2ecf20Sopenharmony_ci * no another solution exists. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci cl = cl->borrow; 6658c2ecf20Sopenharmony_ci if (!cl) { 6668c2ecf20Sopenharmony_ci this_cl->qstats.overlimits++; 6678c2ecf20Sopenharmony_ci cbq_overlimit(this_cl); 6688c2ecf20Sopenharmony_ci return NULL; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci if (cl->level > q->toplevel) 6718c2ecf20Sopenharmony_ci return NULL; 6728c2ecf20Sopenharmony_ci } while (cl->undertime != PSCHED_PASTPERFECT && q->now < cl->undertime); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci cl->delayed = 0; 6758c2ecf20Sopenharmony_ci return cl; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic inline struct sk_buff * 6798c2ecf20Sopenharmony_cicbq_dequeue_prio(struct Qdisc *sch, int prio) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 6828c2ecf20Sopenharmony_ci struct cbq_class *cl_tail, *cl_prev, *cl; 6838c2ecf20Sopenharmony_ci struct sk_buff *skb; 6848c2ecf20Sopenharmony_ci int deficit; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci cl_tail = cl_prev = q->active[prio]; 6878c2ecf20Sopenharmony_ci cl = cl_prev->next_alive; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci do { 6908c2ecf20Sopenharmony_ci deficit = 0; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* Start round */ 6938c2ecf20Sopenharmony_ci do { 6948c2ecf20Sopenharmony_ci struct cbq_class *borrow = cl; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (cl->q->q.qlen && 6978c2ecf20Sopenharmony_ci (borrow = cbq_under_limit(cl)) == NULL) 6988c2ecf20Sopenharmony_ci goto skip_class; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (cl->deficit <= 0) { 7018c2ecf20Sopenharmony_ci /* Class exhausted its allotment per 7028c2ecf20Sopenharmony_ci * this round. Switch to the next one. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci deficit = 1; 7058c2ecf20Sopenharmony_ci cl->deficit += cl->quantum; 7068c2ecf20Sopenharmony_ci goto next_class; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci skb = cl->q->dequeue(cl->q); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* Class did not give us any skb :-( 7128c2ecf20Sopenharmony_ci * It could occur even if cl->q->q.qlen != 0 7138c2ecf20Sopenharmony_ci * f.e. if cl->q == "tbf" 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_ci if (skb == NULL) 7168c2ecf20Sopenharmony_ci goto skip_class; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci cl->deficit -= qdisc_pkt_len(skb); 7198c2ecf20Sopenharmony_ci q->tx_class = cl; 7208c2ecf20Sopenharmony_ci q->tx_borrowed = borrow; 7218c2ecf20Sopenharmony_ci if (borrow != cl) { 7228c2ecf20Sopenharmony_ci#ifndef CBQ_XSTATS_BORROWS_BYTES 7238c2ecf20Sopenharmony_ci borrow->xstats.borrows++; 7248c2ecf20Sopenharmony_ci cl->xstats.borrows++; 7258c2ecf20Sopenharmony_ci#else 7268c2ecf20Sopenharmony_ci borrow->xstats.borrows += qdisc_pkt_len(skb); 7278c2ecf20Sopenharmony_ci cl->xstats.borrows += qdisc_pkt_len(skb); 7288c2ecf20Sopenharmony_ci#endif 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci q->tx_len = qdisc_pkt_len(skb); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (cl->deficit <= 0) { 7338c2ecf20Sopenharmony_ci q->active[prio] = cl; 7348c2ecf20Sopenharmony_ci cl = cl->next_alive; 7358c2ecf20Sopenharmony_ci cl->deficit += cl->quantum; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci return skb; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ciskip_class: 7408c2ecf20Sopenharmony_ci if (cl->q->q.qlen == 0 || prio != cl->cpriority) { 7418c2ecf20Sopenharmony_ci /* Class is empty or penalized. 7428c2ecf20Sopenharmony_ci * Unlink it from active chain. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci cl_prev->next_alive = cl->next_alive; 7458c2ecf20Sopenharmony_ci cl->next_alive = NULL; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* Did cl_tail point to it? */ 7488c2ecf20Sopenharmony_ci if (cl == cl_tail) { 7498c2ecf20Sopenharmony_ci /* Repair it! */ 7508c2ecf20Sopenharmony_ci cl_tail = cl_prev; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Was it the last class in this band? */ 7538c2ecf20Sopenharmony_ci if (cl == cl_tail) { 7548c2ecf20Sopenharmony_ci /* Kill the band! */ 7558c2ecf20Sopenharmony_ci q->active[prio] = NULL; 7568c2ecf20Sopenharmony_ci q->activemask &= ~(1<<prio); 7578c2ecf20Sopenharmony_ci if (cl->q->q.qlen) 7588c2ecf20Sopenharmony_ci cbq_activate_class(cl); 7598c2ecf20Sopenharmony_ci return NULL; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci q->active[prio] = cl_tail; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci if (cl->q->q.qlen) 7658c2ecf20Sopenharmony_ci cbq_activate_class(cl); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci cl = cl_prev; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cinext_class: 7718c2ecf20Sopenharmony_ci cl_prev = cl; 7728c2ecf20Sopenharmony_ci cl = cl->next_alive; 7738c2ecf20Sopenharmony_ci } while (cl_prev != cl_tail); 7748c2ecf20Sopenharmony_ci } while (deficit); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci q->active[prio] = cl_prev; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return NULL; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic inline struct sk_buff * 7828c2ecf20Sopenharmony_cicbq_dequeue_1(struct Qdisc *sch) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 7858c2ecf20Sopenharmony_ci struct sk_buff *skb; 7868c2ecf20Sopenharmony_ci unsigned int activemask; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci activemask = q->activemask & 0xFF; 7898c2ecf20Sopenharmony_ci while (activemask) { 7908c2ecf20Sopenharmony_ci int prio = ffz(~activemask); 7918c2ecf20Sopenharmony_ci activemask &= ~(1<<prio); 7928c2ecf20Sopenharmony_ci skb = cbq_dequeue_prio(sch, prio); 7938c2ecf20Sopenharmony_ci if (skb) 7948c2ecf20Sopenharmony_ci return skb; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci return NULL; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic struct sk_buff * 8008c2ecf20Sopenharmony_cicbq_dequeue(struct Qdisc *sch) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct sk_buff *skb; 8038c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 8048c2ecf20Sopenharmony_ci psched_time_t now; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci now = psched_get_time(); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (q->tx_class) 8098c2ecf20Sopenharmony_ci cbq_update(q); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci q->now = now; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci for (;;) { 8148c2ecf20Sopenharmony_ci q->wd_expires = 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci skb = cbq_dequeue_1(sch); 8178c2ecf20Sopenharmony_ci if (skb) { 8188c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 8198c2ecf20Sopenharmony_ci sch->q.qlen--; 8208c2ecf20Sopenharmony_ci return skb; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* All the classes are overlimit. 8248c2ecf20Sopenharmony_ci * 8258c2ecf20Sopenharmony_ci * It is possible, if: 8268c2ecf20Sopenharmony_ci * 8278c2ecf20Sopenharmony_ci * 1. Scheduler is empty. 8288c2ecf20Sopenharmony_ci * 2. Toplevel cutoff inhibited borrowing. 8298c2ecf20Sopenharmony_ci * 3. Root class is overlimit. 8308c2ecf20Sopenharmony_ci * 8318c2ecf20Sopenharmony_ci * Reset 2d and 3d conditions and retry. 8328c2ecf20Sopenharmony_ci * 8338c2ecf20Sopenharmony_ci * Note, that NS and cbq-2.0 are buggy, peeking 8348c2ecf20Sopenharmony_ci * an arbitrary class is appropriate for ancestor-only 8358c2ecf20Sopenharmony_ci * sharing, but not for toplevel algorithm. 8368c2ecf20Sopenharmony_ci * 8378c2ecf20Sopenharmony_ci * Our version is better, but slower, because it requires 8388c2ecf20Sopenharmony_ci * two passes, but it is unavoidable with top-level sharing. 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (q->toplevel == TC_CBQ_MAXLEVEL && 8428c2ecf20Sopenharmony_ci q->link.undertime == PSCHED_PASTPERFECT) 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci q->toplevel = TC_CBQ_MAXLEVEL; 8468c2ecf20Sopenharmony_ci q->link.undertime = PSCHED_PASTPERFECT; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* No packets in scheduler or nobody wants to give them to us :-( 8508c2ecf20Sopenharmony_ci * Sigh... start watchdog timer in the last case. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (sch->q.qlen) { 8548c2ecf20Sopenharmony_ci qdisc_qstats_overlimit(sch); 8558c2ecf20Sopenharmony_ci if (q->wd_expires) 8568c2ecf20Sopenharmony_ci qdisc_watchdog_schedule(&q->watchdog, 8578c2ecf20Sopenharmony_ci now + q->wd_expires); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci return NULL; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/* CBQ class maintanance routines */ 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic void cbq_adjust_levels(struct cbq_class *this) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci if (this == NULL) 8678c2ecf20Sopenharmony_ci return; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci do { 8708c2ecf20Sopenharmony_ci int level = 0; 8718c2ecf20Sopenharmony_ci struct cbq_class *cl; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci cl = this->children; 8748c2ecf20Sopenharmony_ci if (cl) { 8758c2ecf20Sopenharmony_ci do { 8768c2ecf20Sopenharmony_ci if (cl->level > level) 8778c2ecf20Sopenharmony_ci level = cl->level; 8788c2ecf20Sopenharmony_ci } while ((cl = cl->sibling) != this->children); 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci this->level = level + 1; 8818c2ecf20Sopenharmony_ci } while ((this = this->tparent) != NULL); 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic void cbq_normalize_quanta(struct cbq_sched_data *q, int prio) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct cbq_class *cl; 8878c2ecf20Sopenharmony_ci unsigned int h; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (q->quanta[prio] == 0) 8908c2ecf20Sopenharmony_ci return; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci for (h = 0; h < q->clhash.hashsize; h++) { 8938c2ecf20Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[h], common.hnode) { 8948c2ecf20Sopenharmony_ci /* BUGGGG... Beware! This expression suffer of 8958c2ecf20Sopenharmony_ci * arithmetic overflows! 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci if (cl->priority == prio) { 8988c2ecf20Sopenharmony_ci cl->quantum = (cl->weight*cl->allot*q->nclasses[prio])/ 8998c2ecf20Sopenharmony_ci q->quanta[prio]; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci if (cl->quantum <= 0 || 9028c2ecf20Sopenharmony_ci cl->quantum > 32*qdisc_dev(cl->qdisc)->mtu) { 9038c2ecf20Sopenharmony_ci pr_warn("CBQ: class %08x has bad quantum==%ld, repaired.\n", 9048c2ecf20Sopenharmony_ci cl->common.classid, cl->quantum); 9058c2ecf20Sopenharmony_ci cl->quantum = qdisc_dev(cl->qdisc)->mtu/2 + 1; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic void cbq_sync_defmap(struct cbq_class *cl) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(cl->qdisc); 9148c2ecf20Sopenharmony_ci struct cbq_class *split = cl->split; 9158c2ecf20Sopenharmony_ci unsigned int h; 9168c2ecf20Sopenharmony_ci int i; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (split == NULL) 9198c2ecf20Sopenharmony_ci return; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci for (i = 0; i <= TC_PRIO_MAX; i++) { 9228c2ecf20Sopenharmony_ci if (split->defaults[i] == cl && !(cl->defmap & (1<<i))) 9238c2ecf20Sopenharmony_ci split->defaults[i] = NULL; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci for (i = 0; i <= TC_PRIO_MAX; i++) { 9278c2ecf20Sopenharmony_ci int level = split->level; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (split->defaults[i]) 9308c2ecf20Sopenharmony_ci continue; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci for (h = 0; h < q->clhash.hashsize; h++) { 9338c2ecf20Sopenharmony_ci struct cbq_class *c; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci hlist_for_each_entry(c, &q->clhash.hash[h], 9368c2ecf20Sopenharmony_ci common.hnode) { 9378c2ecf20Sopenharmony_ci if (c->split == split && c->level < level && 9388c2ecf20Sopenharmony_ci c->defmap & (1<<i)) { 9398c2ecf20Sopenharmony_ci split->defaults[i] = c; 9408c2ecf20Sopenharmony_ci level = c->level; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic void cbq_change_defmap(struct cbq_class *cl, u32 splitid, u32 def, u32 mask) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct cbq_class *split = NULL; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (splitid == 0) { 9528c2ecf20Sopenharmony_ci split = cl->split; 9538c2ecf20Sopenharmony_ci if (!split) 9548c2ecf20Sopenharmony_ci return; 9558c2ecf20Sopenharmony_ci splitid = split->common.classid; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (split == NULL || split->common.classid != splitid) { 9598c2ecf20Sopenharmony_ci for (split = cl->tparent; split; split = split->tparent) 9608c2ecf20Sopenharmony_ci if (split->common.classid == splitid) 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (split == NULL) 9658c2ecf20Sopenharmony_ci return; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (cl->split != split) { 9688c2ecf20Sopenharmony_ci cl->defmap = 0; 9698c2ecf20Sopenharmony_ci cbq_sync_defmap(cl); 9708c2ecf20Sopenharmony_ci cl->split = split; 9718c2ecf20Sopenharmony_ci cl->defmap = def & mask; 9728c2ecf20Sopenharmony_ci } else 9738c2ecf20Sopenharmony_ci cl->defmap = (cl->defmap & ~mask) | (def & mask); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci cbq_sync_defmap(cl); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic void cbq_unlink_class(struct cbq_class *this) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct cbq_class *cl, **clp; 9818c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(this->qdisc); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci qdisc_class_hash_remove(&q->clhash, &this->common); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (this->tparent) { 9868c2ecf20Sopenharmony_ci clp = &this->sibling; 9878c2ecf20Sopenharmony_ci cl = *clp; 9888c2ecf20Sopenharmony_ci do { 9898c2ecf20Sopenharmony_ci if (cl == this) { 9908c2ecf20Sopenharmony_ci *clp = cl->sibling; 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci clp = &cl->sibling; 9948c2ecf20Sopenharmony_ci } while ((cl = *clp) != this->sibling); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (this->tparent->children == this) { 9978c2ecf20Sopenharmony_ci this->tparent->children = this->sibling; 9988c2ecf20Sopenharmony_ci if (this->sibling == this) 9998c2ecf20Sopenharmony_ci this->tparent->children = NULL; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci } else { 10028c2ecf20Sopenharmony_ci WARN_ON(this->sibling != this); 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic void cbq_link_class(struct cbq_class *this) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(this->qdisc); 10098c2ecf20Sopenharmony_ci struct cbq_class *parent = this->tparent; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci this->sibling = this; 10128c2ecf20Sopenharmony_ci qdisc_class_hash_insert(&q->clhash, &this->common); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (parent == NULL) 10158c2ecf20Sopenharmony_ci return; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (parent->children == NULL) { 10188c2ecf20Sopenharmony_ci parent->children = this; 10198c2ecf20Sopenharmony_ci } else { 10208c2ecf20Sopenharmony_ci this->sibling = parent->children->sibling; 10218c2ecf20Sopenharmony_ci parent->children->sibling = this; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic void 10268c2ecf20Sopenharmony_cicbq_reset(struct Qdisc *sch) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 10298c2ecf20Sopenharmony_ci struct cbq_class *cl; 10308c2ecf20Sopenharmony_ci int prio; 10318c2ecf20Sopenharmony_ci unsigned int h; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci q->activemask = 0; 10348c2ecf20Sopenharmony_ci q->pmask = 0; 10358c2ecf20Sopenharmony_ci q->tx_class = NULL; 10368c2ecf20Sopenharmony_ci q->tx_borrowed = NULL; 10378c2ecf20Sopenharmony_ci qdisc_watchdog_cancel(&q->watchdog); 10388c2ecf20Sopenharmony_ci hrtimer_cancel(&q->delay_timer); 10398c2ecf20Sopenharmony_ci q->toplevel = TC_CBQ_MAXLEVEL; 10408c2ecf20Sopenharmony_ci q->now = psched_get_time(); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci for (prio = 0; prio <= TC_CBQ_MAXPRIO; prio++) 10438c2ecf20Sopenharmony_ci q->active[prio] = NULL; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci for (h = 0; h < q->clhash.hashsize; h++) { 10468c2ecf20Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[h], common.hnode) { 10478c2ecf20Sopenharmony_ci qdisc_reset(cl->q); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci cl->next_alive = NULL; 10508c2ecf20Sopenharmony_ci cl->undertime = PSCHED_PASTPERFECT; 10518c2ecf20Sopenharmony_ci cl->avgidle = cl->maxidle; 10528c2ecf20Sopenharmony_ci cl->deficit = cl->quantum; 10538c2ecf20Sopenharmony_ci cl->cpriority = cl->priority; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic int cbq_set_lss(struct cbq_class *cl, struct tc_cbq_lssopt *lss) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci if (lss->change & TCF_CBQ_LSS_FLAGS) { 10628c2ecf20Sopenharmony_ci cl->share = (lss->flags & TCF_CBQ_LSS_ISOLATED) ? NULL : cl->tparent; 10638c2ecf20Sopenharmony_ci cl->borrow = (lss->flags & TCF_CBQ_LSS_BOUNDED) ? NULL : cl->tparent; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci if (lss->change & TCF_CBQ_LSS_EWMA) 10668c2ecf20Sopenharmony_ci cl->ewma_log = lss->ewma_log; 10678c2ecf20Sopenharmony_ci if (lss->change & TCF_CBQ_LSS_AVPKT) 10688c2ecf20Sopenharmony_ci cl->avpkt = lss->avpkt; 10698c2ecf20Sopenharmony_ci if (lss->change & TCF_CBQ_LSS_MINIDLE) 10708c2ecf20Sopenharmony_ci cl->minidle = -(long)lss->minidle; 10718c2ecf20Sopenharmony_ci if (lss->change & TCF_CBQ_LSS_MAXIDLE) { 10728c2ecf20Sopenharmony_ci cl->maxidle = lss->maxidle; 10738c2ecf20Sopenharmony_ci cl->avgidle = lss->maxidle; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci if (lss->change & TCF_CBQ_LSS_OFFTIME) 10768c2ecf20Sopenharmony_ci cl->offtime = lss->offtime; 10778c2ecf20Sopenharmony_ci return 0; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void cbq_rmprio(struct cbq_sched_data *q, struct cbq_class *cl) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci q->nclasses[cl->priority]--; 10838c2ecf20Sopenharmony_ci q->quanta[cl->priority] -= cl->weight; 10848c2ecf20Sopenharmony_ci cbq_normalize_quanta(q, cl->priority); 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic void cbq_addprio(struct cbq_sched_data *q, struct cbq_class *cl) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci q->nclasses[cl->priority]++; 10908c2ecf20Sopenharmony_ci q->quanta[cl->priority] += cl->weight; 10918c2ecf20Sopenharmony_ci cbq_normalize_quanta(q, cl->priority); 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic int cbq_set_wrr(struct cbq_class *cl, struct tc_cbq_wrropt *wrr) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(cl->qdisc); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (wrr->allot) 10998c2ecf20Sopenharmony_ci cl->allot = wrr->allot; 11008c2ecf20Sopenharmony_ci if (wrr->weight) 11018c2ecf20Sopenharmony_ci cl->weight = wrr->weight; 11028c2ecf20Sopenharmony_ci if (wrr->priority) { 11038c2ecf20Sopenharmony_ci cl->priority = wrr->priority - 1; 11048c2ecf20Sopenharmony_ci cl->cpriority = cl->priority; 11058c2ecf20Sopenharmony_ci if (cl->priority >= cl->priority2) 11068c2ecf20Sopenharmony_ci cl->priority2 = TC_CBQ_MAXPRIO - 1; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci cbq_addprio(q, cl); 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int cbq_set_fopt(struct cbq_class *cl, struct tc_cbq_fopt *fopt) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci cbq_change_defmap(cl, fopt->split, fopt->defmap, fopt->defchange); 11168c2ecf20Sopenharmony_ci return 0; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic const struct nla_policy cbq_policy[TCA_CBQ_MAX + 1] = { 11208c2ecf20Sopenharmony_ci [TCA_CBQ_LSSOPT] = { .len = sizeof(struct tc_cbq_lssopt) }, 11218c2ecf20Sopenharmony_ci [TCA_CBQ_WRROPT] = { .len = sizeof(struct tc_cbq_wrropt) }, 11228c2ecf20Sopenharmony_ci [TCA_CBQ_FOPT] = { .len = sizeof(struct tc_cbq_fopt) }, 11238c2ecf20Sopenharmony_ci [TCA_CBQ_OVL_STRATEGY] = { .len = sizeof(struct tc_cbq_ovl) }, 11248c2ecf20Sopenharmony_ci [TCA_CBQ_RATE] = { .len = sizeof(struct tc_ratespec) }, 11258c2ecf20Sopenharmony_ci [TCA_CBQ_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, 11268c2ecf20Sopenharmony_ci [TCA_CBQ_POLICE] = { .len = sizeof(struct tc_cbq_police) }, 11278c2ecf20Sopenharmony_ci}; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int cbq_opt_parse(struct nlattr *tb[TCA_CBQ_MAX + 1], 11308c2ecf20Sopenharmony_ci struct nlattr *opt, 11318c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci int err; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (!opt) { 11368c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "CBQ options are required for this operation"); 11378c2ecf20Sopenharmony_ci return -EINVAL; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_CBQ_MAX, opt, 11418c2ecf20Sopenharmony_ci cbq_policy, extack); 11428c2ecf20Sopenharmony_ci if (err < 0) 11438c2ecf20Sopenharmony_ci return err; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_WRROPT]) { 11468c2ecf20Sopenharmony_ci const struct tc_cbq_wrropt *wrr = nla_data(tb[TCA_CBQ_WRROPT]); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (wrr->priority > TC_CBQ_MAXPRIO) { 11498c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "priority is bigger than TC_CBQ_MAXPRIO"); 11508c2ecf20Sopenharmony_ci err = -EINVAL; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci return err; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int cbq_init(struct Qdisc *sch, struct nlattr *opt, 11578c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 11608c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_CBQ_MAX + 1]; 11618c2ecf20Sopenharmony_ci struct tc_ratespec *r; 11628c2ecf20Sopenharmony_ci int err; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci qdisc_watchdog_init(&q->watchdog, sch); 11658c2ecf20Sopenharmony_ci hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED); 11668c2ecf20Sopenharmony_ci q->delay_timer.function = cbq_undelay; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci err = cbq_opt_parse(tb, opt, extack); 11698c2ecf20Sopenharmony_ci if (err < 0) 11708c2ecf20Sopenharmony_ci return err; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (!tb[TCA_CBQ_RTAB] || !tb[TCA_CBQ_RATE]) { 11738c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Rate specification missing or incomplete"); 11748c2ecf20Sopenharmony_ci return -EINVAL; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci r = nla_data(tb[TCA_CBQ_RATE]); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci q->link.R_tab = qdisc_get_rtab(r, tb[TCA_CBQ_RTAB], extack); 11808c2ecf20Sopenharmony_ci if (!q->link.R_tab) 11818c2ecf20Sopenharmony_ci return -EINVAL; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci err = tcf_block_get(&q->link.block, &q->link.filter_list, sch, extack); 11848c2ecf20Sopenharmony_ci if (err) 11858c2ecf20Sopenharmony_ci goto put_rtab; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci err = qdisc_class_hash_init(&q->clhash); 11888c2ecf20Sopenharmony_ci if (err < 0) 11898c2ecf20Sopenharmony_ci goto put_block; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci q->link.sibling = &q->link; 11928c2ecf20Sopenharmony_ci q->link.common.classid = sch->handle; 11938c2ecf20Sopenharmony_ci q->link.qdisc = sch; 11948c2ecf20Sopenharmony_ci q->link.q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, 11958c2ecf20Sopenharmony_ci sch->handle, NULL); 11968c2ecf20Sopenharmony_ci if (!q->link.q) 11978c2ecf20Sopenharmony_ci q->link.q = &noop_qdisc; 11988c2ecf20Sopenharmony_ci else 11998c2ecf20Sopenharmony_ci qdisc_hash_add(q->link.q, true); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci q->link.priority = TC_CBQ_MAXPRIO - 1; 12028c2ecf20Sopenharmony_ci q->link.priority2 = TC_CBQ_MAXPRIO - 1; 12038c2ecf20Sopenharmony_ci q->link.cpriority = TC_CBQ_MAXPRIO - 1; 12048c2ecf20Sopenharmony_ci q->link.allot = psched_mtu(qdisc_dev(sch)); 12058c2ecf20Sopenharmony_ci q->link.quantum = q->link.allot; 12068c2ecf20Sopenharmony_ci q->link.weight = q->link.R_tab->rate.rate; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci q->link.ewma_log = TC_CBQ_DEF_EWMA; 12098c2ecf20Sopenharmony_ci q->link.avpkt = q->link.allot/2; 12108c2ecf20Sopenharmony_ci q->link.minidle = -0x7FFFFFFF; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci q->toplevel = TC_CBQ_MAXLEVEL; 12138c2ecf20Sopenharmony_ci q->now = psched_get_time(); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci cbq_link_class(&q->link); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_LSSOPT]) 12188c2ecf20Sopenharmony_ci cbq_set_lss(&q->link, nla_data(tb[TCA_CBQ_LSSOPT])); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci cbq_addprio(q, &q->link); 12218c2ecf20Sopenharmony_ci return 0; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ciput_block: 12248c2ecf20Sopenharmony_ci tcf_block_put(q->link.block); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ciput_rtab: 12278c2ecf20Sopenharmony_ci qdisc_put_rtab(q->link.R_tab); 12288c2ecf20Sopenharmony_ci return err; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int cbq_dump_rate(struct sk_buff *skb, struct cbq_class *cl) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CBQ_RATE, sizeof(cl->R_tab->rate), &cl->R_tab->rate)) 12368c2ecf20Sopenharmony_ci goto nla_put_failure; 12378c2ecf20Sopenharmony_ci return skb->len; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cinla_put_failure: 12408c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 12418c2ecf20Sopenharmony_ci return -1; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic int cbq_dump_lss(struct sk_buff *skb, struct cbq_class *cl) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 12478c2ecf20Sopenharmony_ci struct tc_cbq_lssopt opt; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci opt.flags = 0; 12508c2ecf20Sopenharmony_ci if (cl->borrow == NULL) 12518c2ecf20Sopenharmony_ci opt.flags |= TCF_CBQ_LSS_BOUNDED; 12528c2ecf20Sopenharmony_ci if (cl->share == NULL) 12538c2ecf20Sopenharmony_ci opt.flags |= TCF_CBQ_LSS_ISOLATED; 12548c2ecf20Sopenharmony_ci opt.ewma_log = cl->ewma_log; 12558c2ecf20Sopenharmony_ci opt.level = cl->level; 12568c2ecf20Sopenharmony_ci opt.avpkt = cl->avpkt; 12578c2ecf20Sopenharmony_ci opt.maxidle = cl->maxidle; 12588c2ecf20Sopenharmony_ci opt.minidle = (u32)(-cl->minidle); 12598c2ecf20Sopenharmony_ci opt.offtime = cl->offtime; 12608c2ecf20Sopenharmony_ci opt.change = ~0; 12618c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CBQ_LSSOPT, sizeof(opt), &opt)) 12628c2ecf20Sopenharmony_ci goto nla_put_failure; 12638c2ecf20Sopenharmony_ci return skb->len; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cinla_put_failure: 12668c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 12678c2ecf20Sopenharmony_ci return -1; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic int cbq_dump_wrr(struct sk_buff *skb, struct cbq_class *cl) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 12738c2ecf20Sopenharmony_ci struct tc_cbq_wrropt opt; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci memset(&opt, 0, sizeof(opt)); 12768c2ecf20Sopenharmony_ci opt.flags = 0; 12778c2ecf20Sopenharmony_ci opt.allot = cl->allot; 12788c2ecf20Sopenharmony_ci opt.priority = cl->priority + 1; 12798c2ecf20Sopenharmony_ci opt.cpriority = cl->cpriority + 1; 12808c2ecf20Sopenharmony_ci opt.weight = cl->weight; 12818c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CBQ_WRROPT, sizeof(opt), &opt)) 12828c2ecf20Sopenharmony_ci goto nla_put_failure; 12838c2ecf20Sopenharmony_ci return skb->len; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cinla_put_failure: 12868c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 12878c2ecf20Sopenharmony_ci return -1; 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic int cbq_dump_fopt(struct sk_buff *skb, struct cbq_class *cl) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 12938c2ecf20Sopenharmony_ci struct tc_cbq_fopt opt; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (cl->split || cl->defmap) { 12968c2ecf20Sopenharmony_ci opt.split = cl->split ? cl->split->common.classid : 0; 12978c2ecf20Sopenharmony_ci opt.defmap = cl->defmap; 12988c2ecf20Sopenharmony_ci opt.defchange = ~0; 12998c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_CBQ_FOPT, sizeof(opt), &opt)) 13008c2ecf20Sopenharmony_ci goto nla_put_failure; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci return skb->len; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cinla_put_failure: 13058c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 13068c2ecf20Sopenharmony_ci return -1; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int cbq_dump_attr(struct sk_buff *skb, struct cbq_class *cl) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci if (cbq_dump_lss(skb, cl) < 0 || 13128c2ecf20Sopenharmony_ci cbq_dump_rate(skb, cl) < 0 || 13138c2ecf20Sopenharmony_ci cbq_dump_wrr(skb, cl) < 0 || 13148c2ecf20Sopenharmony_ci cbq_dump_fopt(skb, cl) < 0) 13158c2ecf20Sopenharmony_ci return -1; 13168c2ecf20Sopenharmony_ci return 0; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int cbq_dump(struct Qdisc *sch, struct sk_buff *skb) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 13228c2ecf20Sopenharmony_ci struct nlattr *nest; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 13258c2ecf20Sopenharmony_ci if (nest == NULL) 13268c2ecf20Sopenharmony_ci goto nla_put_failure; 13278c2ecf20Sopenharmony_ci if (cbq_dump_attr(skb, &q->link) < 0) 13288c2ecf20Sopenharmony_ci goto nla_put_failure; 13298c2ecf20Sopenharmony_ci return nla_nest_end(skb, nest); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_cinla_put_failure: 13328c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 13338c2ecf20Sopenharmony_ci return -1; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic int 13378c2ecf20Sopenharmony_cicbq_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci q->link.xstats.avgidle = q->link.avgidle; 13428c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &q->link.xstats, sizeof(q->link.xstats)); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic int 13468c2ecf20Sopenharmony_cicbq_dump_class(struct Qdisc *sch, unsigned long arg, 13478c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 13508c2ecf20Sopenharmony_ci struct nlattr *nest; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (cl->tparent) 13538c2ecf20Sopenharmony_ci tcm->tcm_parent = cl->tparent->common.classid; 13548c2ecf20Sopenharmony_ci else 13558c2ecf20Sopenharmony_ci tcm->tcm_parent = TC_H_ROOT; 13568c2ecf20Sopenharmony_ci tcm->tcm_handle = cl->common.classid; 13578c2ecf20Sopenharmony_ci tcm->tcm_info = cl->q->handle; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 13608c2ecf20Sopenharmony_ci if (nest == NULL) 13618c2ecf20Sopenharmony_ci goto nla_put_failure; 13628c2ecf20Sopenharmony_ci if (cbq_dump_attr(skb, cl) < 0) 13638c2ecf20Sopenharmony_ci goto nla_put_failure; 13648c2ecf20Sopenharmony_ci return nla_nest_end(skb, nest); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cinla_put_failure: 13678c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 13688c2ecf20Sopenharmony_ci return -1; 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_cistatic int 13728c2ecf20Sopenharmony_cicbq_dump_class_stats(struct Qdisc *sch, unsigned long arg, 13738c2ecf20Sopenharmony_ci struct gnet_dump *d) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 13768c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 13778c2ecf20Sopenharmony_ci __u32 qlen; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci cl->xstats.avgidle = cl->avgidle; 13808c2ecf20Sopenharmony_ci cl->xstats.undertime = 0; 13818c2ecf20Sopenharmony_ci qdisc_qstats_qlen_backlog(cl->q, &qlen, &cl->qstats.backlog); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (cl->undertime != PSCHED_PASTPERFECT) 13848c2ecf20Sopenharmony_ci cl->xstats.undertime = cl->undertime - q->now; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), 13878c2ecf20Sopenharmony_ci d, NULL, &cl->bstats) < 0 || 13888c2ecf20Sopenharmony_ci gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || 13898c2ecf20Sopenharmony_ci gnet_stats_copy_queue(d, NULL, &cl->qstats, qlen) < 0) 13908c2ecf20Sopenharmony_ci return -1; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &cl->xstats, sizeof(cl->xstats)); 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_cistatic int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, 13968c2ecf20Sopenharmony_ci struct Qdisc **old, struct netlink_ext_ack *extack) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (new == NULL) { 14018c2ecf20Sopenharmony_ci new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, 14028c2ecf20Sopenharmony_ci cl->common.classid, extack); 14038c2ecf20Sopenharmony_ci if (new == NULL) 14048c2ecf20Sopenharmony_ci return -ENOBUFS; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci *old = qdisc_replace(sch, new, &cl->q); 14088c2ecf20Sopenharmony_ci return 0; 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic struct Qdisc *cbq_leaf(struct Qdisc *sch, unsigned long arg) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return cl->q; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic void cbq_qlen_notify(struct Qdisc *sch, unsigned long arg) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci cbq_deactivate_class(cl); 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_cistatic unsigned long cbq_find(struct Qdisc *sch, u32 classid) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci return (unsigned long)cbq_class_lookup(q, classid); 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci WARN_ON(cl->filters); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci tcf_block_put(cl->block); 14398c2ecf20Sopenharmony_ci qdisc_put(cl->q); 14408c2ecf20Sopenharmony_ci qdisc_put_rtab(cl->R_tab); 14418c2ecf20Sopenharmony_ci gen_kill_estimator(&cl->rate_est); 14428c2ecf20Sopenharmony_ci if (cl != &q->link) 14438c2ecf20Sopenharmony_ci kfree(cl); 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic void cbq_destroy(struct Qdisc *sch) 14478c2ecf20Sopenharmony_ci{ 14488c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 14498c2ecf20Sopenharmony_ci struct hlist_node *next; 14508c2ecf20Sopenharmony_ci struct cbq_class *cl; 14518c2ecf20Sopenharmony_ci unsigned int h; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 14548c2ecf20Sopenharmony_ci q->rx_class = NULL; 14558c2ecf20Sopenharmony_ci#endif 14568c2ecf20Sopenharmony_ci /* 14578c2ecf20Sopenharmony_ci * Filters must be destroyed first because we don't destroy the 14588c2ecf20Sopenharmony_ci * classes from root to leafs which means that filters can still 14598c2ecf20Sopenharmony_ci * be bound to classes which have been destroyed already. --TGR '04 14608c2ecf20Sopenharmony_ci */ 14618c2ecf20Sopenharmony_ci for (h = 0; h < q->clhash.hashsize; h++) { 14628c2ecf20Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[h], common.hnode) { 14638c2ecf20Sopenharmony_ci tcf_block_put(cl->block); 14648c2ecf20Sopenharmony_ci cl->block = NULL; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci for (h = 0; h < q->clhash.hashsize; h++) { 14688c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(cl, next, &q->clhash.hash[h], 14698c2ecf20Sopenharmony_ci common.hnode) 14708c2ecf20Sopenharmony_ci cbq_destroy_class(sch, cl); 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci qdisc_class_hash_destroy(&q->clhash); 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic int 14768c2ecf20Sopenharmony_cicbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **tca, 14778c2ecf20Sopenharmony_ci unsigned long *arg, struct netlink_ext_ack *extack) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci int err; 14808c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 14818c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)*arg; 14828c2ecf20Sopenharmony_ci struct nlattr *opt = tca[TCA_OPTIONS]; 14838c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_CBQ_MAX + 1]; 14848c2ecf20Sopenharmony_ci struct cbq_class *parent; 14858c2ecf20Sopenharmony_ci struct qdisc_rate_table *rtab = NULL; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci err = cbq_opt_parse(tb, opt, extack); 14888c2ecf20Sopenharmony_ci if (err < 0) 14898c2ecf20Sopenharmony_ci return err; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_OVL_STRATEGY] || tb[TCA_CBQ_POLICE]) { 14928c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Neither overlimit strategy nor policing attributes can be used for changing class params"); 14938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci if (cl) { 14978c2ecf20Sopenharmony_ci /* Check parent */ 14988c2ecf20Sopenharmony_ci if (parentid) { 14998c2ecf20Sopenharmony_ci if (cl->tparent && 15008c2ecf20Sopenharmony_ci cl->tparent->common.classid != parentid) { 15018c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid parent id"); 15028c2ecf20Sopenharmony_ci return -EINVAL; 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci if (!cl->tparent && parentid != TC_H_ROOT) { 15058c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Parent must be root"); 15068c2ecf20Sopenharmony_ci return -EINVAL; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_RATE]) { 15118c2ecf20Sopenharmony_ci rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]), 15128c2ecf20Sopenharmony_ci tb[TCA_CBQ_RTAB], extack); 15138c2ecf20Sopenharmony_ci if (rtab == NULL) 15148c2ecf20Sopenharmony_ci return -EINVAL; 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (tca[TCA_RATE]) { 15188c2ecf20Sopenharmony_ci err = gen_replace_estimator(&cl->bstats, NULL, 15198c2ecf20Sopenharmony_ci &cl->rate_est, 15208c2ecf20Sopenharmony_ci NULL, 15218c2ecf20Sopenharmony_ci qdisc_root_sleeping_running(sch), 15228c2ecf20Sopenharmony_ci tca[TCA_RATE]); 15238c2ecf20Sopenharmony_ci if (err) { 15248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to replace specified rate estimator"); 15258c2ecf20Sopenharmony_ci qdisc_put_rtab(rtab); 15268c2ecf20Sopenharmony_ci return err; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* Change class parameters */ 15318c2ecf20Sopenharmony_ci sch_tree_lock(sch); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci if (cl->next_alive != NULL) 15348c2ecf20Sopenharmony_ci cbq_deactivate_class(cl); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci if (rtab) { 15378c2ecf20Sopenharmony_ci qdisc_put_rtab(cl->R_tab); 15388c2ecf20Sopenharmony_ci cl->R_tab = rtab; 15398c2ecf20Sopenharmony_ci } 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_LSSOPT]) 15428c2ecf20Sopenharmony_ci cbq_set_lss(cl, nla_data(tb[TCA_CBQ_LSSOPT])); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_WRROPT]) { 15458c2ecf20Sopenharmony_ci cbq_rmprio(q, cl); 15468c2ecf20Sopenharmony_ci cbq_set_wrr(cl, nla_data(tb[TCA_CBQ_WRROPT])); 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_FOPT]) 15508c2ecf20Sopenharmony_ci cbq_set_fopt(cl, nla_data(tb[TCA_CBQ_FOPT])); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (cl->q->q.qlen) 15538c2ecf20Sopenharmony_ci cbq_activate_class(cl); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return 0; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (parentid == TC_H_ROOT) 15618c2ecf20Sopenharmony_ci return -EINVAL; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci if (!tb[TCA_CBQ_WRROPT] || !tb[TCA_CBQ_RATE] || !tb[TCA_CBQ_LSSOPT]) { 15648c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "One of the following attributes MUST be specified: WRR, rate or link sharing"); 15658c2ecf20Sopenharmony_ci return -EINVAL; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]), tb[TCA_CBQ_RTAB], 15698c2ecf20Sopenharmony_ci extack); 15708c2ecf20Sopenharmony_ci if (rtab == NULL) 15718c2ecf20Sopenharmony_ci return -EINVAL; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci if (classid) { 15748c2ecf20Sopenharmony_ci err = -EINVAL; 15758c2ecf20Sopenharmony_ci if (TC_H_MAJ(classid ^ sch->handle) || 15768c2ecf20Sopenharmony_ci cbq_class_lookup(q, classid)) { 15778c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified class not found"); 15788c2ecf20Sopenharmony_ci goto failure; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci } else { 15818c2ecf20Sopenharmony_ci int i; 15828c2ecf20Sopenharmony_ci classid = TC_H_MAKE(sch->handle, 0x8000); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci for (i = 0; i < 0x8000; i++) { 15858c2ecf20Sopenharmony_ci if (++q->hgenerator >= 0x8000) 15868c2ecf20Sopenharmony_ci q->hgenerator = 1; 15878c2ecf20Sopenharmony_ci if (cbq_class_lookup(q, classid|q->hgenerator) == NULL) 15888c2ecf20Sopenharmony_ci break; 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci err = -ENOSR; 15918c2ecf20Sopenharmony_ci if (i >= 0x8000) { 15928c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unable to generate classid"); 15938c2ecf20Sopenharmony_ci goto failure; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci classid = classid|q->hgenerator; 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci parent = &q->link; 15998c2ecf20Sopenharmony_ci if (parentid) { 16008c2ecf20Sopenharmony_ci parent = cbq_class_lookup(q, parentid); 16018c2ecf20Sopenharmony_ci err = -EINVAL; 16028c2ecf20Sopenharmony_ci if (!parent) { 16038c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to find parentid"); 16048c2ecf20Sopenharmony_ci goto failure; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci err = -ENOBUFS; 16098c2ecf20Sopenharmony_ci cl = kzalloc(sizeof(*cl), GFP_KERNEL); 16108c2ecf20Sopenharmony_ci if (cl == NULL) 16118c2ecf20Sopenharmony_ci goto failure; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci err = tcf_block_get(&cl->block, &cl->filter_list, sch, extack); 16148c2ecf20Sopenharmony_ci if (err) { 16158c2ecf20Sopenharmony_ci kfree(cl); 16168c2ecf20Sopenharmony_ci goto failure; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if (tca[TCA_RATE]) { 16208c2ecf20Sopenharmony_ci err = gen_new_estimator(&cl->bstats, NULL, &cl->rate_est, 16218c2ecf20Sopenharmony_ci NULL, 16228c2ecf20Sopenharmony_ci qdisc_root_sleeping_running(sch), 16238c2ecf20Sopenharmony_ci tca[TCA_RATE]); 16248c2ecf20Sopenharmony_ci if (err) { 16258c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Couldn't create new estimator"); 16268c2ecf20Sopenharmony_ci tcf_block_put(cl->block); 16278c2ecf20Sopenharmony_ci kfree(cl); 16288c2ecf20Sopenharmony_ci goto failure; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci cl->R_tab = rtab; 16338c2ecf20Sopenharmony_ci rtab = NULL; 16348c2ecf20Sopenharmony_ci cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid, 16358c2ecf20Sopenharmony_ci NULL); 16368c2ecf20Sopenharmony_ci if (!cl->q) 16378c2ecf20Sopenharmony_ci cl->q = &noop_qdisc; 16388c2ecf20Sopenharmony_ci else 16398c2ecf20Sopenharmony_ci qdisc_hash_add(cl->q, true); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci cl->common.classid = classid; 16428c2ecf20Sopenharmony_ci cl->tparent = parent; 16438c2ecf20Sopenharmony_ci cl->qdisc = sch; 16448c2ecf20Sopenharmony_ci cl->allot = parent->allot; 16458c2ecf20Sopenharmony_ci cl->quantum = cl->allot; 16468c2ecf20Sopenharmony_ci cl->weight = cl->R_tab->rate.rate; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci sch_tree_lock(sch); 16498c2ecf20Sopenharmony_ci cbq_link_class(cl); 16508c2ecf20Sopenharmony_ci cl->borrow = cl->tparent; 16518c2ecf20Sopenharmony_ci if (cl->tparent != &q->link) 16528c2ecf20Sopenharmony_ci cl->share = cl->tparent; 16538c2ecf20Sopenharmony_ci cbq_adjust_levels(parent); 16548c2ecf20Sopenharmony_ci cl->minidle = -0x7FFFFFFF; 16558c2ecf20Sopenharmony_ci cbq_set_lss(cl, nla_data(tb[TCA_CBQ_LSSOPT])); 16568c2ecf20Sopenharmony_ci cbq_set_wrr(cl, nla_data(tb[TCA_CBQ_WRROPT])); 16578c2ecf20Sopenharmony_ci if (cl->ewma_log == 0) 16588c2ecf20Sopenharmony_ci cl->ewma_log = q->link.ewma_log; 16598c2ecf20Sopenharmony_ci if (cl->maxidle == 0) 16608c2ecf20Sopenharmony_ci cl->maxidle = q->link.maxidle; 16618c2ecf20Sopenharmony_ci if (cl->avpkt == 0) 16628c2ecf20Sopenharmony_ci cl->avpkt = q->link.avpkt; 16638c2ecf20Sopenharmony_ci if (tb[TCA_CBQ_FOPT]) 16648c2ecf20Sopenharmony_ci cbq_set_fopt(cl, nla_data(tb[TCA_CBQ_FOPT])); 16658c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci qdisc_class_hash_grow(sch, &q->clhash); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci *arg = (unsigned long)cl; 16708c2ecf20Sopenharmony_ci return 0; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cifailure: 16738c2ecf20Sopenharmony_ci qdisc_put_rtab(rtab); 16748c2ecf20Sopenharmony_ci return err; 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_cistatic int cbq_delete(struct Qdisc *sch, unsigned long arg) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 16808c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (cl->filters || cl->children || cl == &q->link) 16838c2ecf20Sopenharmony_ci return -EBUSY; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci sch_tree_lock(sch); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci qdisc_purge_queue(cl->q); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci if (cl->next_alive) 16908c2ecf20Sopenharmony_ci cbq_deactivate_class(cl); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci if (q->tx_borrowed == cl) 16938c2ecf20Sopenharmony_ci q->tx_borrowed = q->tx_class; 16948c2ecf20Sopenharmony_ci if (q->tx_class == cl) { 16958c2ecf20Sopenharmony_ci q->tx_class = NULL; 16968c2ecf20Sopenharmony_ci q->tx_borrowed = NULL; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 16998c2ecf20Sopenharmony_ci if (q->rx_class == cl) 17008c2ecf20Sopenharmony_ci q->rx_class = NULL; 17018c2ecf20Sopenharmony_ci#endif 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci cbq_unlink_class(cl); 17048c2ecf20Sopenharmony_ci cbq_adjust_levels(cl->tparent); 17058c2ecf20Sopenharmony_ci cl->defmap = 0; 17068c2ecf20Sopenharmony_ci cbq_sync_defmap(cl); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci cbq_rmprio(q, cl); 17098c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci cbq_destroy_class(sch, cl); 17128c2ecf20Sopenharmony_ci return 0; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic struct tcf_block *cbq_tcf_block(struct Qdisc *sch, unsigned long arg, 17168c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 17178c2ecf20Sopenharmony_ci{ 17188c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 17198c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci if (cl == NULL) 17228c2ecf20Sopenharmony_ci cl = &q->link; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci return cl->block; 17258c2ecf20Sopenharmony_ci} 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_cistatic unsigned long cbq_bind_filter(struct Qdisc *sch, unsigned long parent, 17288c2ecf20Sopenharmony_ci u32 classid) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 17318c2ecf20Sopenharmony_ci struct cbq_class *p = (struct cbq_class *)parent; 17328c2ecf20Sopenharmony_ci struct cbq_class *cl = cbq_class_lookup(q, classid); 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci if (cl) { 17358c2ecf20Sopenharmony_ci if (p && p->level <= cl->level) 17368c2ecf20Sopenharmony_ci return 0; 17378c2ecf20Sopenharmony_ci cl->filters++; 17388c2ecf20Sopenharmony_ci return (unsigned long)cl; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci return 0; 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic void cbq_unbind_filter(struct Qdisc *sch, unsigned long arg) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci struct cbq_class *cl = (struct cbq_class *)arg; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci cl->filters--; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic void cbq_walk(struct Qdisc *sch, struct qdisc_walker *arg) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci struct cbq_sched_data *q = qdisc_priv(sch); 17538c2ecf20Sopenharmony_ci struct cbq_class *cl; 17548c2ecf20Sopenharmony_ci unsigned int h; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci if (arg->stop) 17578c2ecf20Sopenharmony_ci return; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci for (h = 0; h < q->clhash.hashsize; h++) { 17608c2ecf20Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[h], common.hnode) { 17618c2ecf20Sopenharmony_ci if (arg->count < arg->skip) { 17628c2ecf20Sopenharmony_ci arg->count++; 17638c2ecf20Sopenharmony_ci continue; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci if (arg->fn(sch, (unsigned long)cl, arg) < 0) { 17668c2ecf20Sopenharmony_ci arg->stop = 1; 17678c2ecf20Sopenharmony_ci return; 17688c2ecf20Sopenharmony_ci } 17698c2ecf20Sopenharmony_ci arg->count++; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops cbq_class_ops = { 17758c2ecf20Sopenharmony_ci .graft = cbq_graft, 17768c2ecf20Sopenharmony_ci .leaf = cbq_leaf, 17778c2ecf20Sopenharmony_ci .qlen_notify = cbq_qlen_notify, 17788c2ecf20Sopenharmony_ci .find = cbq_find, 17798c2ecf20Sopenharmony_ci .change = cbq_change_class, 17808c2ecf20Sopenharmony_ci .delete = cbq_delete, 17818c2ecf20Sopenharmony_ci .walk = cbq_walk, 17828c2ecf20Sopenharmony_ci .tcf_block = cbq_tcf_block, 17838c2ecf20Sopenharmony_ci .bind_tcf = cbq_bind_filter, 17848c2ecf20Sopenharmony_ci .unbind_tcf = cbq_unbind_filter, 17858c2ecf20Sopenharmony_ci .dump = cbq_dump_class, 17868c2ecf20Sopenharmony_ci .dump_stats = cbq_dump_class_stats, 17878c2ecf20Sopenharmony_ci}; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_cistatic struct Qdisc_ops cbq_qdisc_ops __read_mostly = { 17908c2ecf20Sopenharmony_ci .next = NULL, 17918c2ecf20Sopenharmony_ci .cl_ops = &cbq_class_ops, 17928c2ecf20Sopenharmony_ci .id = "cbq", 17938c2ecf20Sopenharmony_ci .priv_size = sizeof(struct cbq_sched_data), 17948c2ecf20Sopenharmony_ci .enqueue = cbq_enqueue, 17958c2ecf20Sopenharmony_ci .dequeue = cbq_dequeue, 17968c2ecf20Sopenharmony_ci .peek = qdisc_peek_dequeued, 17978c2ecf20Sopenharmony_ci .init = cbq_init, 17988c2ecf20Sopenharmony_ci .reset = cbq_reset, 17998c2ecf20Sopenharmony_ci .destroy = cbq_destroy, 18008c2ecf20Sopenharmony_ci .change = NULL, 18018c2ecf20Sopenharmony_ci .dump = cbq_dump, 18028c2ecf20Sopenharmony_ci .dump_stats = cbq_dump_stats, 18038c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 18048c2ecf20Sopenharmony_ci}; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_cistatic int __init cbq_module_init(void) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci return register_qdisc(&cbq_qdisc_ops); 18098c2ecf20Sopenharmony_ci} 18108c2ecf20Sopenharmony_cistatic void __exit cbq_module_exit(void) 18118c2ecf20Sopenharmony_ci{ 18128c2ecf20Sopenharmony_ci unregister_qdisc(&cbq_qdisc_ops); 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_cimodule_init(cbq_module_init) 18158c2ecf20Sopenharmony_cimodule_exit(cbq_module_exit) 18168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1817