162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/sch_drr.c Deficit Round Robin scheduler 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/pkt_sched.h> 1462306a36Sopenharmony_ci#include <net/sch_generic.h> 1562306a36Sopenharmony_ci#include <net/pkt_sched.h> 1662306a36Sopenharmony_ci#include <net/pkt_cls.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct drr_class { 1962306a36Sopenharmony_ci struct Qdisc_class_common common; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci struct gnet_stats_basic_sync bstats; 2262306a36Sopenharmony_ci struct gnet_stats_queue qstats; 2362306a36Sopenharmony_ci struct net_rate_estimator __rcu *rate_est; 2462306a36Sopenharmony_ci struct list_head alist; 2562306a36Sopenharmony_ci struct Qdisc *qdisc; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci u32 quantum; 2862306a36Sopenharmony_ci u32 deficit; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct drr_sched { 3262306a36Sopenharmony_ci struct list_head active; 3362306a36Sopenharmony_ci struct tcf_proto __rcu *filter_list; 3462306a36Sopenharmony_ci struct tcf_block *block; 3562306a36Sopenharmony_ci struct Qdisc_class_hash clhash; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 4162306a36Sopenharmony_ci struct Qdisc_class_common *clc; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci clc = qdisc_class_find(&q->clhash, classid); 4462306a36Sopenharmony_ci if (clc == NULL) 4562306a36Sopenharmony_ci return NULL; 4662306a36Sopenharmony_ci return container_of(clc, struct drr_class, common); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = { 5062306a36Sopenharmony_ci [TCA_DRR_QUANTUM] = { .type = NLA_U32 }, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 5462306a36Sopenharmony_ci struct nlattr **tca, unsigned long *arg, 5562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 5862306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)*arg; 5962306a36Sopenharmony_ci struct nlattr *opt = tca[TCA_OPTIONS]; 6062306a36Sopenharmony_ci struct nlattr *tb[TCA_DRR_MAX + 1]; 6162306a36Sopenharmony_ci u32 quantum; 6262306a36Sopenharmony_ci int err; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!opt) { 6562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "DRR options are required for this operation"); 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_DRR_MAX, opt, drr_policy, 7062306a36Sopenharmony_ci extack); 7162306a36Sopenharmony_ci if (err < 0) 7262306a36Sopenharmony_ci return err; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (tb[TCA_DRR_QUANTUM]) { 7562306a36Sopenharmony_ci quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]); 7662306a36Sopenharmony_ci if (quantum == 0) { 7762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero"); 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } else 8162306a36Sopenharmony_ci quantum = psched_mtu(qdisc_dev(sch)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (cl != NULL) { 8462306a36Sopenharmony_ci if (tca[TCA_RATE]) { 8562306a36Sopenharmony_ci err = gen_replace_estimator(&cl->bstats, NULL, 8662306a36Sopenharmony_ci &cl->rate_est, 8762306a36Sopenharmony_ci NULL, true, 8862306a36Sopenharmony_ci tca[TCA_RATE]); 8962306a36Sopenharmony_ci if (err) { 9062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to replace estimator"); 9162306a36Sopenharmony_ci return err; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci sch_tree_lock(sch); 9662306a36Sopenharmony_ci if (tb[TCA_DRR_QUANTUM]) 9762306a36Sopenharmony_ci cl->quantum = quantum; 9862306a36Sopenharmony_ci sch_tree_unlock(sch); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL); 10462306a36Sopenharmony_ci if (cl == NULL) 10562306a36Sopenharmony_ci return -ENOBUFS; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci gnet_stats_basic_sync_init(&cl->bstats); 10862306a36Sopenharmony_ci cl->common.classid = classid; 10962306a36Sopenharmony_ci cl->quantum = quantum; 11062306a36Sopenharmony_ci cl->qdisc = qdisc_create_dflt(sch->dev_queue, 11162306a36Sopenharmony_ci &pfifo_qdisc_ops, classid, 11262306a36Sopenharmony_ci NULL); 11362306a36Sopenharmony_ci if (cl->qdisc == NULL) 11462306a36Sopenharmony_ci cl->qdisc = &noop_qdisc; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci qdisc_hash_add(cl->qdisc, true); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (tca[TCA_RATE]) { 11962306a36Sopenharmony_ci err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est, 12062306a36Sopenharmony_ci NULL, true, tca[TCA_RATE]); 12162306a36Sopenharmony_ci if (err) { 12262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Failed to replace estimator"); 12362306a36Sopenharmony_ci qdisc_put(cl->qdisc); 12462306a36Sopenharmony_ci kfree(cl); 12562306a36Sopenharmony_ci return err; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci sch_tree_lock(sch); 13062306a36Sopenharmony_ci qdisc_class_hash_insert(&q->clhash, &cl->common); 13162306a36Sopenharmony_ci sch_tree_unlock(sch); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci qdisc_class_hash_grow(sch, &q->clhash); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci *arg = (unsigned long)cl; 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci gen_kill_estimator(&cl->rate_est); 14262306a36Sopenharmony_ci qdisc_put(cl->qdisc); 14362306a36Sopenharmony_ci kfree(cl); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int drr_delete_class(struct Qdisc *sch, unsigned long arg, 14762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 15062306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (qdisc_class_in_use(&cl->common)) { 15362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "DRR class is in use"); 15462306a36Sopenharmony_ci return -EBUSY; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci sch_tree_lock(sch); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci qdisc_purge_queue(cl->qdisc); 16062306a36Sopenharmony_ci qdisc_class_hash_remove(&q->clhash, &cl->common); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci sch_tree_unlock(sch); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci drr_destroy_class(sch, cl); 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic unsigned long drr_search_class(struct Qdisc *sch, u32 classid) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return (unsigned long)drr_find_class(sch, classid); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic struct tcf_block *drr_tcf_block(struct Qdisc *sch, unsigned long cl, 17462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (cl) { 17962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "DRR classid must be zero"); 18062306a36Sopenharmony_ci return NULL; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return q->block; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent, 18762306a36Sopenharmony_ci u32 classid) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct drr_class *cl = drr_find_class(sch, classid); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (cl) 19262306a36Sopenharmony_ci qdisc_class_get(&cl->common); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return (unsigned long)cl; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci qdisc_class_put(&cl->common); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int drr_graft_class(struct Qdisc *sch, unsigned long arg, 20562306a36Sopenharmony_ci struct Qdisc *new, struct Qdisc **old, 20662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (new == NULL) { 21162306a36Sopenharmony_ci new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, 21262306a36Sopenharmony_ci cl->common.classid, NULL); 21362306a36Sopenharmony_ci if (new == NULL) 21462306a36Sopenharmony_ci new = &noop_qdisc; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci *old = qdisc_replace(sch, new, &cl->qdisc); 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return cl->qdisc; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void drr_qlen_notify(struct Qdisc *csh, unsigned long arg) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci list_del(&cl->alist); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int drr_dump_class(struct Qdisc *sch, unsigned long arg, 23662306a36Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 23962306a36Sopenharmony_ci struct nlattr *nest; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci tcm->tcm_parent = TC_H_ROOT; 24262306a36Sopenharmony_ci tcm->tcm_handle = cl->common.classid; 24362306a36Sopenharmony_ci tcm->tcm_info = cl->qdisc->handle; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 24662306a36Sopenharmony_ci if (nest == NULL) 24762306a36Sopenharmony_ci goto nla_put_failure; 24862306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum)) 24962306a36Sopenharmony_ci goto nla_put_failure; 25062306a36Sopenharmony_ci return nla_nest_end(skb, nest); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cinla_put_failure: 25362306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 25462306a36Sopenharmony_ci return -EMSGSIZE; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, 25862306a36Sopenharmony_ci struct gnet_dump *d) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct drr_class *cl = (struct drr_class *)arg; 26162306a36Sopenharmony_ci __u32 qlen = qdisc_qlen_sum(cl->qdisc); 26262306a36Sopenharmony_ci struct Qdisc *cl_q = cl->qdisc; 26362306a36Sopenharmony_ci struct tc_drr_stats xstats; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci memset(&xstats, 0, sizeof(xstats)); 26662306a36Sopenharmony_ci if (qlen) 26762306a36Sopenharmony_ci xstats.deficit = cl->deficit; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || 27062306a36Sopenharmony_ci gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || 27162306a36Sopenharmony_ci gnet_stats_copy_queue(d, cl_q->cpu_qstats, &cl_q->qstats, qlen) < 0) 27262306a36Sopenharmony_ci return -1; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 28062306a36Sopenharmony_ci struct drr_class *cl; 28162306a36Sopenharmony_ci unsigned int i; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (arg->stop) 28462306a36Sopenharmony_ci return; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci for (i = 0; i < q->clhash.hashsize; i++) { 28762306a36Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) { 28862306a36Sopenharmony_ci if (!tc_qdisc_stats_dump(sch, (unsigned long)cl, arg)) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, 29562306a36Sopenharmony_ci int *qerr) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 29862306a36Sopenharmony_ci struct drr_class *cl; 29962306a36Sopenharmony_ci struct tcf_result res; 30062306a36Sopenharmony_ci struct tcf_proto *fl; 30162306a36Sopenharmony_ci int result; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) { 30462306a36Sopenharmony_ci cl = drr_find_class(sch, skb->priority); 30562306a36Sopenharmony_ci if (cl != NULL) 30662306a36Sopenharmony_ci return cl; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 31062306a36Sopenharmony_ci fl = rcu_dereference_bh(q->filter_list); 31162306a36Sopenharmony_ci result = tcf_classify(skb, NULL, fl, &res, false); 31262306a36Sopenharmony_ci if (result >= 0) { 31362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 31462306a36Sopenharmony_ci switch (result) { 31562306a36Sopenharmony_ci case TC_ACT_QUEUED: 31662306a36Sopenharmony_ci case TC_ACT_STOLEN: 31762306a36Sopenharmony_ci case TC_ACT_TRAP: 31862306a36Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 31962306a36Sopenharmony_ci fallthrough; 32062306a36Sopenharmony_ci case TC_ACT_SHOT: 32162306a36Sopenharmony_ci return NULL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci#endif 32462306a36Sopenharmony_ci cl = (struct drr_class *)res.class; 32562306a36Sopenharmony_ci if (cl == NULL) 32662306a36Sopenharmony_ci cl = drr_find_class(sch, res.classid); 32762306a36Sopenharmony_ci return cl; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci return NULL; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, 33362306a36Sopenharmony_ci struct sk_buff **to_free) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci unsigned int len = qdisc_pkt_len(skb); 33662306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 33762306a36Sopenharmony_ci struct drr_class *cl; 33862306a36Sopenharmony_ci int err = 0; 33962306a36Sopenharmony_ci bool first; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci cl = drr_classify(skb, sch, &err); 34262306a36Sopenharmony_ci if (cl == NULL) { 34362306a36Sopenharmony_ci if (err & __NET_XMIT_BYPASS) 34462306a36Sopenharmony_ci qdisc_qstats_drop(sch); 34562306a36Sopenharmony_ci __qdisc_drop(skb, to_free); 34662306a36Sopenharmony_ci return err; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci first = !cl->qdisc->q.qlen; 35062306a36Sopenharmony_ci err = qdisc_enqueue(skb, cl->qdisc, to_free); 35162306a36Sopenharmony_ci if (unlikely(err != NET_XMIT_SUCCESS)) { 35262306a36Sopenharmony_ci if (net_xmit_drop_count(err)) { 35362306a36Sopenharmony_ci cl->qstats.drops++; 35462306a36Sopenharmony_ci qdisc_qstats_drop(sch); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci return err; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (first) { 36062306a36Sopenharmony_ci list_add_tail(&cl->alist, &q->active); 36162306a36Sopenharmony_ci cl->deficit = cl->quantum; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci sch->qstats.backlog += len; 36562306a36Sopenharmony_ci sch->q.qlen++; 36662306a36Sopenharmony_ci return err; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic struct sk_buff *drr_dequeue(struct Qdisc *sch) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 37262306a36Sopenharmony_ci struct drr_class *cl; 37362306a36Sopenharmony_ci struct sk_buff *skb; 37462306a36Sopenharmony_ci unsigned int len; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (list_empty(&q->active)) 37762306a36Sopenharmony_ci goto out; 37862306a36Sopenharmony_ci while (1) { 37962306a36Sopenharmony_ci cl = list_first_entry(&q->active, struct drr_class, alist); 38062306a36Sopenharmony_ci skb = cl->qdisc->ops->peek(cl->qdisc); 38162306a36Sopenharmony_ci if (skb == NULL) { 38262306a36Sopenharmony_ci qdisc_warn_nonwc(__func__, cl->qdisc); 38362306a36Sopenharmony_ci goto out; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci len = qdisc_pkt_len(skb); 38762306a36Sopenharmony_ci if (len <= cl->deficit) { 38862306a36Sopenharmony_ci cl->deficit -= len; 38962306a36Sopenharmony_ci skb = qdisc_dequeue_peeked(cl->qdisc); 39062306a36Sopenharmony_ci if (unlikely(skb == NULL)) 39162306a36Sopenharmony_ci goto out; 39262306a36Sopenharmony_ci if (cl->qdisc->q.qlen == 0) 39362306a36Sopenharmony_ci list_del(&cl->alist); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci bstats_update(&cl->bstats, skb); 39662306a36Sopenharmony_ci qdisc_bstats_update(sch, skb); 39762306a36Sopenharmony_ci qdisc_qstats_backlog_dec(sch, skb); 39862306a36Sopenharmony_ci sch->q.qlen--; 39962306a36Sopenharmony_ci return skb; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci cl->deficit += cl->quantum; 40362306a36Sopenharmony_ci list_move_tail(&cl->alist, &q->active); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ciout: 40662306a36Sopenharmony_ci return NULL; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt, 41062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 41362306a36Sopenharmony_ci int err; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci err = tcf_block_get(&q->block, &q->filter_list, sch, extack); 41662306a36Sopenharmony_ci if (err) 41762306a36Sopenharmony_ci return err; 41862306a36Sopenharmony_ci err = qdisc_class_hash_init(&q->clhash); 41962306a36Sopenharmony_ci if (err < 0) 42062306a36Sopenharmony_ci return err; 42162306a36Sopenharmony_ci INIT_LIST_HEAD(&q->active); 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void drr_reset_qdisc(struct Qdisc *sch) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 42862306a36Sopenharmony_ci struct drr_class *cl; 42962306a36Sopenharmony_ci unsigned int i; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci for (i = 0; i < q->clhash.hashsize; i++) { 43262306a36Sopenharmony_ci hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) { 43362306a36Sopenharmony_ci if (cl->qdisc->q.qlen) 43462306a36Sopenharmony_ci list_del(&cl->alist); 43562306a36Sopenharmony_ci qdisc_reset(cl->qdisc); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void drr_destroy_qdisc(struct Qdisc *sch) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct drr_sched *q = qdisc_priv(sch); 44362306a36Sopenharmony_ci struct drr_class *cl; 44462306a36Sopenharmony_ci struct hlist_node *next; 44562306a36Sopenharmony_ci unsigned int i; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci tcf_block_put(q->block); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci for (i = 0; i < q->clhash.hashsize; i++) { 45062306a36Sopenharmony_ci hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i], 45162306a36Sopenharmony_ci common.hnode) 45262306a36Sopenharmony_ci drr_destroy_class(sch, cl); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci qdisc_class_hash_destroy(&q->clhash); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct Qdisc_class_ops drr_class_ops = { 45862306a36Sopenharmony_ci .change = drr_change_class, 45962306a36Sopenharmony_ci .delete = drr_delete_class, 46062306a36Sopenharmony_ci .find = drr_search_class, 46162306a36Sopenharmony_ci .tcf_block = drr_tcf_block, 46262306a36Sopenharmony_ci .bind_tcf = drr_bind_tcf, 46362306a36Sopenharmony_ci .unbind_tcf = drr_unbind_tcf, 46462306a36Sopenharmony_ci .graft = drr_graft_class, 46562306a36Sopenharmony_ci .leaf = drr_class_leaf, 46662306a36Sopenharmony_ci .qlen_notify = drr_qlen_notify, 46762306a36Sopenharmony_ci .dump = drr_dump_class, 46862306a36Sopenharmony_ci .dump_stats = drr_dump_class_stats, 46962306a36Sopenharmony_ci .walk = drr_walk, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic struct Qdisc_ops drr_qdisc_ops __read_mostly = { 47362306a36Sopenharmony_ci .cl_ops = &drr_class_ops, 47462306a36Sopenharmony_ci .id = "drr", 47562306a36Sopenharmony_ci .priv_size = sizeof(struct drr_sched), 47662306a36Sopenharmony_ci .enqueue = drr_enqueue, 47762306a36Sopenharmony_ci .dequeue = drr_dequeue, 47862306a36Sopenharmony_ci .peek = qdisc_peek_dequeued, 47962306a36Sopenharmony_ci .init = drr_init_qdisc, 48062306a36Sopenharmony_ci .reset = drr_reset_qdisc, 48162306a36Sopenharmony_ci .destroy = drr_destroy_qdisc, 48262306a36Sopenharmony_ci .owner = THIS_MODULE, 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int __init drr_init(void) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci return register_qdisc(&drr_qdisc_ops); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void __exit drr_exit(void) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci unregister_qdisc(&drr_qdisc_ops); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cimodule_init(drr_init); 49662306a36Sopenharmony_cimodule_exit(drr_exit); 49762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 498