18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/sch_drr.c Deficit Round Robin scheduler 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h> 148c2ecf20Sopenharmony_ci#include <net/sch_generic.h> 158c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 168c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct drr_class { 198c2ecf20Sopenharmony_ci struct Qdisc_class_common common; 208c2ecf20Sopenharmony_ci unsigned int filter_cnt; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci struct gnet_stats_basic_packed bstats; 238c2ecf20Sopenharmony_ci struct gnet_stats_queue qstats; 248c2ecf20Sopenharmony_ci struct net_rate_estimator __rcu *rate_est; 258c2ecf20Sopenharmony_ci struct list_head alist; 268c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci u32 quantum; 298c2ecf20Sopenharmony_ci u32 deficit; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct drr_sched { 338c2ecf20Sopenharmony_ci struct list_head active; 348c2ecf20Sopenharmony_ci struct tcf_proto __rcu *filter_list; 358c2ecf20Sopenharmony_ci struct tcf_block *block; 368c2ecf20Sopenharmony_ci struct Qdisc_class_hash clhash; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 428c2ecf20Sopenharmony_ci struct Qdisc_class_common *clc; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci clc = qdisc_class_find(&q->clhash, classid); 458c2ecf20Sopenharmony_ci if (clc == NULL) 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci return container_of(clc, struct drr_class, common); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = { 518c2ecf20Sopenharmony_ci [TCA_DRR_QUANTUM] = { .type = NLA_U32 }, 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 558c2ecf20Sopenharmony_ci struct nlattr **tca, unsigned long *arg, 568c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 598c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)*arg; 608c2ecf20Sopenharmony_ci struct nlattr *opt = tca[TCA_OPTIONS]; 618c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_DRR_MAX + 1]; 628c2ecf20Sopenharmony_ci u32 quantum; 638c2ecf20Sopenharmony_ci int err; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (!opt) { 668c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "DRR options are required for this operation"); 678c2ecf20Sopenharmony_ci return -EINVAL; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_DRR_MAX, opt, drr_policy, 718c2ecf20Sopenharmony_ci extack); 728c2ecf20Sopenharmony_ci if (err < 0) 738c2ecf20Sopenharmony_ci return err; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (tb[TCA_DRR_QUANTUM]) { 768c2ecf20Sopenharmony_ci quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]); 778c2ecf20Sopenharmony_ci if (quantum == 0) { 788c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero"); 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci } else 828c2ecf20Sopenharmony_ci quantum = psched_mtu(qdisc_dev(sch)); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (cl != NULL) { 858c2ecf20Sopenharmony_ci if (tca[TCA_RATE]) { 868c2ecf20Sopenharmony_ci err = gen_replace_estimator(&cl->bstats, NULL, 878c2ecf20Sopenharmony_ci &cl->rate_est, 888c2ecf20Sopenharmony_ci NULL, 898c2ecf20Sopenharmony_ci qdisc_root_sleeping_running(sch), 908c2ecf20Sopenharmony_ci tca[TCA_RATE]); 918c2ecf20Sopenharmony_ci if (err) { 928c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to replace estimator"); 938c2ecf20Sopenharmony_ci return err; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci sch_tree_lock(sch); 988c2ecf20Sopenharmony_ci if (tb[TCA_DRR_QUANTUM]) 998c2ecf20Sopenharmony_ci cl->quantum = quantum; 1008c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL); 1068c2ecf20Sopenharmony_ci if (cl == NULL) 1078c2ecf20Sopenharmony_ci return -ENOBUFS; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci cl->common.classid = classid; 1108c2ecf20Sopenharmony_ci cl->quantum = quantum; 1118c2ecf20Sopenharmony_ci cl->qdisc = qdisc_create_dflt(sch->dev_queue, 1128c2ecf20Sopenharmony_ci &pfifo_qdisc_ops, classid, 1138c2ecf20Sopenharmony_ci NULL); 1148c2ecf20Sopenharmony_ci if (cl->qdisc == NULL) 1158c2ecf20Sopenharmony_ci cl->qdisc = &noop_qdisc; 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci qdisc_hash_add(cl->qdisc, true); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (tca[TCA_RATE]) { 1208c2ecf20Sopenharmony_ci err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est, 1218c2ecf20Sopenharmony_ci NULL, 1228c2ecf20Sopenharmony_ci qdisc_root_sleeping_running(sch), 1238c2ecf20Sopenharmony_ci tca[TCA_RATE]); 1248c2ecf20Sopenharmony_ci if (err) { 1258c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to replace estimator"); 1268c2ecf20Sopenharmony_ci qdisc_put(cl->qdisc); 1278c2ecf20Sopenharmony_ci kfree(cl); 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci sch_tree_lock(sch); 1338c2ecf20Sopenharmony_ci qdisc_class_hash_insert(&q->clhash, &cl->common); 1348c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci qdisc_class_hash_grow(sch, &q->clhash); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci *arg = (unsigned long)cl; 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci gen_kill_estimator(&cl->rate_est); 1458c2ecf20Sopenharmony_ci qdisc_put(cl->qdisc); 1468c2ecf20Sopenharmony_ci kfree(cl); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int drr_delete_class(struct Qdisc *sch, unsigned long arg) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 1528c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (cl->filter_cnt > 0) 1558c2ecf20Sopenharmony_ci return -EBUSY; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci sch_tree_lock(sch); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci qdisc_purge_queue(cl->qdisc); 1608c2ecf20Sopenharmony_ci qdisc_class_hash_remove(&q->clhash, &cl->common); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci drr_destroy_class(sch, cl); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic unsigned long drr_search_class(struct Qdisc *sch, u32 classid) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return (unsigned long)drr_find_class(sch, classid); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic struct tcf_block *drr_tcf_block(struct Qdisc *sch, unsigned long cl, 1748c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (cl) { 1798c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "DRR classid must be zero"); 1808c2ecf20Sopenharmony_ci return NULL; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return q->block; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent, 1878c2ecf20Sopenharmony_ci u32 classid) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct drr_class *cl = drr_find_class(sch, classid); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (cl != NULL) 1928c2ecf20Sopenharmony_ci cl->filter_cnt++; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return (unsigned long)cl; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci cl->filter_cnt--; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int drr_graft_class(struct Qdisc *sch, unsigned long arg, 2058c2ecf20Sopenharmony_ci struct Qdisc *new, struct Qdisc **old, 2068c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (new == NULL) { 2118c2ecf20Sopenharmony_ci new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, 2128c2ecf20Sopenharmony_ci cl->common.classid, NULL); 2138c2ecf20Sopenharmony_ci if (new == NULL) 2148c2ecf20Sopenharmony_ci new = &noop_qdisc; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci *old = qdisc_replace(sch, new, &cl->qdisc); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return cl->qdisc; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void drr_qlen_notify(struct Qdisc *csh, unsigned long arg) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci list_del(&cl->alist); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int drr_dump_class(struct Qdisc *sch, unsigned long arg, 2368c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 2398c2ecf20Sopenharmony_ci struct nlattr *nest; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci tcm->tcm_parent = TC_H_ROOT; 2428c2ecf20Sopenharmony_ci tcm->tcm_handle = cl->common.classid; 2438c2ecf20Sopenharmony_ci tcm->tcm_info = cl->qdisc->handle; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 2468c2ecf20Sopenharmony_ci if (nest == NULL) 2478c2ecf20Sopenharmony_ci goto nla_put_failure; 2488c2ecf20Sopenharmony_ci if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum)) 2498c2ecf20Sopenharmony_ci goto nla_put_failure; 2508c2ecf20Sopenharmony_ci return nla_nest_end(skb, nest); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cinla_put_failure: 2538c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 2548c2ecf20Sopenharmony_ci return -EMSGSIZE; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, 2588c2ecf20Sopenharmony_ci struct gnet_dump *d) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 2618c2ecf20Sopenharmony_ci __u32 qlen = qdisc_qlen_sum(cl->qdisc); 2628c2ecf20Sopenharmony_ci struct Qdisc *cl_q = cl->qdisc; 2638c2ecf20Sopenharmony_ci struct tc_drr_stats xstats; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci memset(&xstats, 0, sizeof(xstats)); 2668c2ecf20Sopenharmony_ci if (qlen) 2678c2ecf20Sopenharmony_ci xstats.deficit = cl->deficit; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), 2708c2ecf20Sopenharmony_ci d, NULL, &cl->bstats) < 0 || 2718c2ecf20Sopenharmony_ci gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || 2728c2ecf20Sopenharmony_ci gnet_stats_copy_queue(d, cl_q->cpu_qstats, &cl_q->qstats, qlen) < 0) 2738c2ecf20Sopenharmony_ci return -1; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 2818c2ecf20Sopenharmony_ci struct drr_class *cl; 2828c2ecf20Sopenharmony_ci unsigned int i; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (arg->stop) 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (i = 0; i < q->clhash.hashsize; i++) { 2888c2ecf20Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) { 2898c2ecf20Sopenharmony_ci if (arg->count < arg->skip) { 2908c2ecf20Sopenharmony_ci arg->count++; 2918c2ecf20Sopenharmony_ci continue; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (arg->fn(sch, (unsigned long)cl, arg) < 0) { 2948c2ecf20Sopenharmony_ci arg->stop = 1; 2958c2ecf20Sopenharmony_ci return; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci arg->count++; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, 3038c2ecf20Sopenharmony_ci int *qerr) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 3068c2ecf20Sopenharmony_ci struct drr_class *cl; 3078c2ecf20Sopenharmony_ci struct tcf_result res; 3088c2ecf20Sopenharmony_ci struct tcf_proto *fl; 3098c2ecf20Sopenharmony_ci int result; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) { 3128c2ecf20Sopenharmony_ci cl = drr_find_class(sch, skb->priority); 3138c2ecf20Sopenharmony_ci if (cl != NULL) 3148c2ecf20Sopenharmony_ci return cl; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 3188c2ecf20Sopenharmony_ci fl = rcu_dereference_bh(q->filter_list); 3198c2ecf20Sopenharmony_ci result = tcf_classify(skb, fl, &res, false); 3208c2ecf20Sopenharmony_ci if (result >= 0) { 3218c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 3228c2ecf20Sopenharmony_ci switch (result) { 3238c2ecf20Sopenharmony_ci case TC_ACT_QUEUED: 3248c2ecf20Sopenharmony_ci case TC_ACT_STOLEN: 3258c2ecf20Sopenharmony_ci case TC_ACT_TRAP: 3268c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 3278c2ecf20Sopenharmony_ci fallthrough; 3288c2ecf20Sopenharmony_ci case TC_ACT_SHOT: 3298c2ecf20Sopenharmony_ci return NULL; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci#endif 3328c2ecf20Sopenharmony_ci cl = (struct drr_class *)res.class; 3338c2ecf20Sopenharmony_ci if (cl == NULL) 3348c2ecf20Sopenharmony_ci cl = drr_find_class(sch, res.classid); 3358c2ecf20Sopenharmony_ci return cl; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci return NULL; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, 3418c2ecf20Sopenharmony_ci struct sk_buff **to_free) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci unsigned int len = qdisc_pkt_len(skb); 3448c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 3458c2ecf20Sopenharmony_ci struct drr_class *cl; 3468c2ecf20Sopenharmony_ci int err = 0; 3478c2ecf20Sopenharmony_ci bool first; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci cl = drr_classify(skb, sch, &err); 3508c2ecf20Sopenharmony_ci if (cl == NULL) { 3518c2ecf20Sopenharmony_ci if (err & __NET_XMIT_BYPASS) 3528c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 3538c2ecf20Sopenharmony_ci __qdisc_drop(skb, to_free); 3548c2ecf20Sopenharmony_ci return err; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci first = !cl->qdisc->q.qlen; 3588c2ecf20Sopenharmony_ci err = qdisc_enqueue(skb, cl->qdisc, to_free); 3598c2ecf20Sopenharmony_ci if (unlikely(err != NET_XMIT_SUCCESS)) { 3608c2ecf20Sopenharmony_ci if (net_xmit_drop_count(err)) { 3618c2ecf20Sopenharmony_ci cl->qstats.drops++; 3628c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (first) { 3688c2ecf20Sopenharmony_ci list_add_tail(&cl->alist, &q->active); 3698c2ecf20Sopenharmony_ci cl->deficit = cl->quantum; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci sch->qstats.backlog += len; 3738c2ecf20Sopenharmony_ci sch->q.qlen++; 3748c2ecf20Sopenharmony_ci return err; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic struct sk_buff *drr_dequeue(struct Qdisc *sch) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 3808c2ecf20Sopenharmony_ci struct drr_class *cl; 3818c2ecf20Sopenharmony_ci struct sk_buff *skb; 3828c2ecf20Sopenharmony_ci unsigned int len; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (list_empty(&q->active)) 3858c2ecf20Sopenharmony_ci goto out; 3868c2ecf20Sopenharmony_ci while (1) { 3878c2ecf20Sopenharmony_ci cl = list_first_entry(&q->active, struct drr_class, alist); 3888c2ecf20Sopenharmony_ci skb = cl->qdisc->ops->peek(cl->qdisc); 3898c2ecf20Sopenharmony_ci if (skb == NULL) { 3908c2ecf20Sopenharmony_ci qdisc_warn_nonwc(__func__, cl->qdisc); 3918c2ecf20Sopenharmony_ci goto out; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci len = qdisc_pkt_len(skb); 3958c2ecf20Sopenharmony_ci if (len <= cl->deficit) { 3968c2ecf20Sopenharmony_ci cl->deficit -= len; 3978c2ecf20Sopenharmony_ci skb = qdisc_dequeue_peeked(cl->qdisc); 3988c2ecf20Sopenharmony_ci if (unlikely(skb == NULL)) 3998c2ecf20Sopenharmony_ci goto out; 4008c2ecf20Sopenharmony_ci if (cl->qdisc->q.qlen == 0) 4018c2ecf20Sopenharmony_ci list_del(&cl->alist); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci bstats_update(&cl->bstats, skb); 4048c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 4058c2ecf20Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 4068c2ecf20Sopenharmony_ci sch->q.qlen--; 4078c2ecf20Sopenharmony_ci return skb; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci cl->deficit += cl->quantum; 4118c2ecf20Sopenharmony_ci list_move_tail(&cl->alist, &q->active); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ciout: 4148c2ecf20Sopenharmony_ci return NULL; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt, 4188c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 4218c2ecf20Sopenharmony_ci int err; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci err = tcf_block_get(&q->block, &q->filter_list, sch, extack); 4248c2ecf20Sopenharmony_ci if (err) 4258c2ecf20Sopenharmony_ci return err; 4268c2ecf20Sopenharmony_ci err = qdisc_class_hash_init(&q->clhash); 4278c2ecf20Sopenharmony_ci if (err < 0) 4288c2ecf20Sopenharmony_ci return err; 4298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&q->active); 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void drr_reset_qdisc(struct Qdisc *sch) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 4368c2ecf20Sopenharmony_ci struct drr_class *cl; 4378c2ecf20Sopenharmony_ci unsigned int i; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci for (i = 0; i < q->clhash.hashsize; i++) { 4408c2ecf20Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) { 4418c2ecf20Sopenharmony_ci if (cl->qdisc->q.qlen) 4428c2ecf20Sopenharmony_ci list_del(&cl->alist); 4438c2ecf20Sopenharmony_ci qdisc_reset(cl->qdisc); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void drr_destroy_qdisc(struct Qdisc *sch) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 4518c2ecf20Sopenharmony_ci struct drr_class *cl; 4528c2ecf20Sopenharmony_ci struct hlist_node *next; 4538c2ecf20Sopenharmony_ci unsigned int i; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci tcf_block_put(q->block); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < q->clhash.hashsize; i++) { 4588c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i], 4598c2ecf20Sopenharmony_ci common.hnode) 4608c2ecf20Sopenharmony_ci drr_destroy_class(sch, cl); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci qdisc_class_hash_destroy(&q->clhash); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops drr_class_ops = { 4668c2ecf20Sopenharmony_ci .change = drr_change_class, 4678c2ecf20Sopenharmony_ci .delete = drr_delete_class, 4688c2ecf20Sopenharmony_ci .find = drr_search_class, 4698c2ecf20Sopenharmony_ci .tcf_block = drr_tcf_block, 4708c2ecf20Sopenharmony_ci .bind_tcf = drr_bind_tcf, 4718c2ecf20Sopenharmony_ci .unbind_tcf = drr_unbind_tcf, 4728c2ecf20Sopenharmony_ci .graft = drr_graft_class, 4738c2ecf20Sopenharmony_ci .leaf = drr_class_leaf, 4748c2ecf20Sopenharmony_ci .qlen_notify = drr_qlen_notify, 4758c2ecf20Sopenharmony_ci .dump = drr_dump_class, 4768c2ecf20Sopenharmony_ci .dump_stats = drr_dump_class_stats, 4778c2ecf20Sopenharmony_ci .walk = drr_walk, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic struct Qdisc_ops drr_qdisc_ops __read_mostly = { 4818c2ecf20Sopenharmony_ci .cl_ops = &drr_class_ops, 4828c2ecf20Sopenharmony_ci .id = "drr", 4838c2ecf20Sopenharmony_ci .priv_size = sizeof(struct drr_sched), 4848c2ecf20Sopenharmony_ci .enqueue = drr_enqueue, 4858c2ecf20Sopenharmony_ci .dequeue = drr_dequeue, 4868c2ecf20Sopenharmony_ci .peek = qdisc_peek_dequeued, 4878c2ecf20Sopenharmony_ci .init = drr_init_qdisc, 4888c2ecf20Sopenharmony_ci .reset = drr_reset_qdisc, 4898c2ecf20Sopenharmony_ci .destroy = drr_destroy_qdisc, 4908c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4918c2ecf20Sopenharmony_ci}; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int __init drr_init(void) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci return register_qdisc(&drr_qdisc_ops); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void __exit drr_exit(void) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci unregister_qdisc(&drr_qdisc_ops); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cimodule_init(drr_init); 5048c2ecf20Sopenharmony_cimodule_exit(drr_exit); 5058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 506