113d2a1d2SPatrick McHardy /* 213d2a1d2SPatrick McHardy * net/sched/sch_drr.c Deficit Round Robin scheduler 313d2a1d2SPatrick McHardy * 413d2a1d2SPatrick McHardy * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> 513d2a1d2SPatrick McHardy * 613d2a1d2SPatrick McHardy * This program is free software; you can redistribute it and/or 713d2a1d2SPatrick McHardy * modify it under the terms of the GNU General Public License 813d2a1d2SPatrick McHardy * version 2 as published by the Free Software Foundation. 913d2a1d2SPatrick McHardy */ 1013d2a1d2SPatrick McHardy 1113d2a1d2SPatrick McHardy #include <linux/module.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 1313d2a1d2SPatrick McHardy #include <linux/init.h> 1413d2a1d2SPatrick McHardy #include <linux/errno.h> 1513d2a1d2SPatrick McHardy #include <linux/netdevice.h> 1613d2a1d2SPatrick McHardy #include <linux/pkt_sched.h> 1713d2a1d2SPatrick McHardy #include <net/sch_generic.h> 1813d2a1d2SPatrick McHardy #include <net/pkt_sched.h> 1913d2a1d2SPatrick McHardy #include <net/pkt_cls.h> 2013d2a1d2SPatrick McHardy 2113d2a1d2SPatrick McHardy struct drr_class { 2213d2a1d2SPatrick McHardy struct Qdisc_class_common common; 2313d2a1d2SPatrick McHardy unsigned int refcnt; 2413d2a1d2SPatrick McHardy unsigned int filter_cnt; 2513d2a1d2SPatrick McHardy 26c1a8f1f1SEric Dumazet struct gnet_stats_basic_packed bstats; 2713d2a1d2SPatrick McHardy struct gnet_stats_queue qstats; 28*1c0d32fdSEric Dumazet struct net_rate_estimator __rcu *rate_est; 2913d2a1d2SPatrick McHardy struct list_head alist; 3013d2a1d2SPatrick McHardy struct Qdisc *qdisc; 3113d2a1d2SPatrick McHardy 3213d2a1d2SPatrick McHardy u32 quantum; 3313d2a1d2SPatrick McHardy u32 deficit; 3413d2a1d2SPatrick McHardy }; 3513d2a1d2SPatrick McHardy 3613d2a1d2SPatrick McHardy struct drr_sched { 3713d2a1d2SPatrick McHardy struct list_head active; 3825d8c0d5SJohn Fastabend struct tcf_proto __rcu *filter_list; 3913d2a1d2SPatrick McHardy struct Qdisc_class_hash clhash; 4013d2a1d2SPatrick McHardy }; 4113d2a1d2SPatrick McHardy 4213d2a1d2SPatrick McHardy static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid) 4313d2a1d2SPatrick McHardy { 4413d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 4513d2a1d2SPatrick McHardy struct Qdisc_class_common *clc; 4613d2a1d2SPatrick McHardy 4713d2a1d2SPatrick McHardy clc = qdisc_class_find(&q->clhash, classid); 4813d2a1d2SPatrick McHardy if (clc == NULL) 4913d2a1d2SPatrick McHardy return NULL; 5013d2a1d2SPatrick McHardy return container_of(clc, struct drr_class, common); 5113d2a1d2SPatrick McHardy } 5213d2a1d2SPatrick McHardy 5313d2a1d2SPatrick McHardy static void drr_purge_queue(struct drr_class *cl) 5413d2a1d2SPatrick McHardy { 5513d2a1d2SPatrick McHardy unsigned int len = cl->qdisc->q.qlen; 562ccccf5fSWANG Cong unsigned int backlog = cl->qdisc->qstats.backlog; 5713d2a1d2SPatrick McHardy 5813d2a1d2SPatrick McHardy qdisc_reset(cl->qdisc); 592ccccf5fSWANG Cong qdisc_tree_reduce_backlog(cl->qdisc, len, backlog); 6013d2a1d2SPatrick McHardy } 6113d2a1d2SPatrick McHardy 6213d2a1d2SPatrick McHardy static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = { 6313d2a1d2SPatrick McHardy [TCA_DRR_QUANTUM] = { .type = NLA_U32 }, 6413d2a1d2SPatrick McHardy }; 6513d2a1d2SPatrick McHardy 6613d2a1d2SPatrick McHardy static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 6713d2a1d2SPatrick McHardy struct nlattr **tca, unsigned long *arg) 6813d2a1d2SPatrick McHardy { 6913d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 7013d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)*arg; 711844f747SJarek Poplawski struct nlattr *opt = tca[TCA_OPTIONS]; 7213d2a1d2SPatrick McHardy struct nlattr *tb[TCA_DRR_MAX + 1]; 7313d2a1d2SPatrick McHardy u32 quantum; 7413d2a1d2SPatrick McHardy int err; 7513d2a1d2SPatrick McHardy 761844f747SJarek Poplawski if (!opt) 771844f747SJarek Poplawski return -EINVAL; 781844f747SJarek Poplawski 791844f747SJarek Poplawski err = nla_parse_nested(tb, TCA_DRR_MAX, opt, drr_policy); 8013d2a1d2SPatrick McHardy if (err < 0) 8113d2a1d2SPatrick McHardy return err; 8213d2a1d2SPatrick McHardy 8313d2a1d2SPatrick McHardy if (tb[TCA_DRR_QUANTUM]) { 8413d2a1d2SPatrick McHardy quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]); 8513d2a1d2SPatrick McHardy if (quantum == 0) 8613d2a1d2SPatrick McHardy return -EINVAL; 8713d2a1d2SPatrick McHardy } else 8813d2a1d2SPatrick McHardy quantum = psched_mtu(qdisc_dev(sch)); 8913d2a1d2SPatrick McHardy 9013d2a1d2SPatrick McHardy if (cl != NULL) { 9171bcb09aSStephen Hemminger if (tca[TCA_RATE]) { 9222e0f8b9SJohn Fastabend err = gen_replace_estimator(&cl->bstats, NULL, 9322e0f8b9SJohn Fastabend &cl->rate_est, 94edb09eb1SEric Dumazet NULL, 95edb09eb1SEric Dumazet qdisc_root_sleeping_running(sch), 9671bcb09aSStephen Hemminger tca[TCA_RATE]); 9771bcb09aSStephen Hemminger if (err) 9871bcb09aSStephen Hemminger return err; 9971bcb09aSStephen Hemminger } 10071bcb09aSStephen Hemminger 10113d2a1d2SPatrick McHardy sch_tree_lock(sch); 10213d2a1d2SPatrick McHardy if (tb[TCA_DRR_QUANTUM]) 10313d2a1d2SPatrick McHardy cl->quantum = quantum; 10413d2a1d2SPatrick McHardy sch_tree_unlock(sch); 10513d2a1d2SPatrick McHardy 10613d2a1d2SPatrick McHardy return 0; 10713d2a1d2SPatrick McHardy } 10813d2a1d2SPatrick McHardy 10913d2a1d2SPatrick McHardy cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL); 11013d2a1d2SPatrick McHardy if (cl == NULL) 11113d2a1d2SPatrick McHardy return -ENOBUFS; 11213d2a1d2SPatrick McHardy 11313d2a1d2SPatrick McHardy cl->refcnt = 1; 11413d2a1d2SPatrick McHardy cl->common.classid = classid; 11513d2a1d2SPatrick McHardy cl->quantum = quantum; 1163511c913SChangli Gao cl->qdisc = qdisc_create_dflt(sch->dev_queue, 11713d2a1d2SPatrick McHardy &pfifo_qdisc_ops, classid); 11813d2a1d2SPatrick McHardy if (cl->qdisc == NULL) 11913d2a1d2SPatrick McHardy cl->qdisc = &noop_qdisc; 12013d2a1d2SPatrick McHardy 12171bcb09aSStephen Hemminger if (tca[TCA_RATE]) { 12222e0f8b9SJohn Fastabend err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est, 123edb09eb1SEric Dumazet NULL, 124edb09eb1SEric Dumazet qdisc_root_sleeping_running(sch), 12513d2a1d2SPatrick McHardy tca[TCA_RATE]); 12671bcb09aSStephen Hemminger if (err) { 12771bcb09aSStephen Hemminger qdisc_destroy(cl->qdisc); 12871bcb09aSStephen Hemminger kfree(cl); 12971bcb09aSStephen Hemminger return err; 13071bcb09aSStephen Hemminger } 13171bcb09aSStephen Hemminger } 13213d2a1d2SPatrick McHardy 13313d2a1d2SPatrick McHardy sch_tree_lock(sch); 13413d2a1d2SPatrick McHardy qdisc_class_hash_insert(&q->clhash, &cl->common); 13513d2a1d2SPatrick McHardy sch_tree_unlock(sch); 13613d2a1d2SPatrick McHardy 13713d2a1d2SPatrick McHardy qdisc_class_hash_grow(sch, &q->clhash); 13813d2a1d2SPatrick McHardy 13913d2a1d2SPatrick McHardy *arg = (unsigned long)cl; 14013d2a1d2SPatrick McHardy return 0; 14113d2a1d2SPatrick McHardy } 14213d2a1d2SPatrick McHardy 14313d2a1d2SPatrick McHardy static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl) 14413d2a1d2SPatrick McHardy { 145*1c0d32fdSEric Dumazet gen_kill_estimator(&cl->rate_est); 14613d2a1d2SPatrick McHardy qdisc_destroy(cl->qdisc); 14713d2a1d2SPatrick McHardy kfree(cl); 14813d2a1d2SPatrick McHardy } 14913d2a1d2SPatrick McHardy 15013d2a1d2SPatrick McHardy static int drr_delete_class(struct Qdisc *sch, unsigned long arg) 15113d2a1d2SPatrick McHardy { 15213d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 15313d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 15413d2a1d2SPatrick McHardy 15513d2a1d2SPatrick McHardy if (cl->filter_cnt > 0) 15613d2a1d2SPatrick McHardy return -EBUSY; 15713d2a1d2SPatrick McHardy 15813d2a1d2SPatrick McHardy sch_tree_lock(sch); 15913d2a1d2SPatrick McHardy 16013d2a1d2SPatrick McHardy drr_purge_queue(cl); 16113d2a1d2SPatrick McHardy qdisc_class_hash_remove(&q->clhash, &cl->common); 16213d2a1d2SPatrick McHardy 1637cd0a638SJarek Poplawski BUG_ON(--cl->refcnt == 0); 1647cd0a638SJarek Poplawski /* 1657cd0a638SJarek Poplawski * This shouldn't happen: we "hold" one cops->get() when called 1667cd0a638SJarek Poplawski * from tc_ctl_tclass; the destroy method is done from cops->put(). 1677cd0a638SJarek Poplawski */ 16813d2a1d2SPatrick McHardy 16913d2a1d2SPatrick McHardy sch_tree_unlock(sch); 17013d2a1d2SPatrick McHardy return 0; 17113d2a1d2SPatrick McHardy } 17213d2a1d2SPatrick McHardy 17313d2a1d2SPatrick McHardy static unsigned long drr_get_class(struct Qdisc *sch, u32 classid) 17413d2a1d2SPatrick McHardy { 17513d2a1d2SPatrick McHardy struct drr_class *cl = drr_find_class(sch, classid); 17613d2a1d2SPatrick McHardy 17713d2a1d2SPatrick McHardy if (cl != NULL) 17813d2a1d2SPatrick McHardy cl->refcnt++; 17913d2a1d2SPatrick McHardy 18013d2a1d2SPatrick McHardy return (unsigned long)cl; 18113d2a1d2SPatrick McHardy } 18213d2a1d2SPatrick McHardy 18313d2a1d2SPatrick McHardy static void drr_put_class(struct Qdisc *sch, unsigned long arg) 18413d2a1d2SPatrick McHardy { 18513d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 18613d2a1d2SPatrick McHardy 18713d2a1d2SPatrick McHardy if (--cl->refcnt == 0) 18813d2a1d2SPatrick McHardy drr_destroy_class(sch, cl); 18913d2a1d2SPatrick McHardy } 19013d2a1d2SPatrick McHardy 19125d8c0d5SJohn Fastabend static struct tcf_proto __rcu **drr_tcf_chain(struct Qdisc *sch, 19225d8c0d5SJohn Fastabend unsigned long cl) 19313d2a1d2SPatrick McHardy { 19413d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 19513d2a1d2SPatrick McHardy 19613d2a1d2SPatrick McHardy if (cl) 19713d2a1d2SPatrick McHardy return NULL; 19813d2a1d2SPatrick McHardy 19913d2a1d2SPatrick McHardy return &q->filter_list; 20013d2a1d2SPatrick McHardy } 20113d2a1d2SPatrick McHardy 20213d2a1d2SPatrick McHardy static unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent, 20313d2a1d2SPatrick McHardy u32 classid) 20413d2a1d2SPatrick McHardy { 20513d2a1d2SPatrick McHardy struct drr_class *cl = drr_find_class(sch, classid); 20613d2a1d2SPatrick McHardy 20713d2a1d2SPatrick McHardy if (cl != NULL) 20813d2a1d2SPatrick McHardy cl->filter_cnt++; 20913d2a1d2SPatrick McHardy 21013d2a1d2SPatrick McHardy return (unsigned long)cl; 21113d2a1d2SPatrick McHardy } 21213d2a1d2SPatrick McHardy 21313d2a1d2SPatrick McHardy static void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg) 21413d2a1d2SPatrick McHardy { 21513d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 21613d2a1d2SPatrick McHardy 21713d2a1d2SPatrick McHardy cl->filter_cnt--; 21813d2a1d2SPatrick McHardy } 21913d2a1d2SPatrick McHardy 22013d2a1d2SPatrick McHardy static int drr_graft_class(struct Qdisc *sch, unsigned long arg, 22113d2a1d2SPatrick McHardy struct Qdisc *new, struct Qdisc **old) 22213d2a1d2SPatrick McHardy { 22313d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 22413d2a1d2SPatrick McHardy 22513d2a1d2SPatrick McHardy if (new == NULL) { 2263511c913SChangli Gao new = qdisc_create_dflt(sch->dev_queue, 22713d2a1d2SPatrick McHardy &pfifo_qdisc_ops, cl->common.classid); 22813d2a1d2SPatrick McHardy if (new == NULL) 22913d2a1d2SPatrick McHardy new = &noop_qdisc; 23013d2a1d2SPatrick McHardy } 23113d2a1d2SPatrick McHardy 23286a7996cSWANG Cong *old = qdisc_replace(sch, new, &cl->qdisc); 23313d2a1d2SPatrick McHardy return 0; 23413d2a1d2SPatrick McHardy } 23513d2a1d2SPatrick McHardy 23613d2a1d2SPatrick McHardy static struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg) 23713d2a1d2SPatrick McHardy { 23813d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 23913d2a1d2SPatrick McHardy 24013d2a1d2SPatrick McHardy return cl->qdisc; 24113d2a1d2SPatrick McHardy } 24213d2a1d2SPatrick McHardy 24313d2a1d2SPatrick McHardy static void drr_qlen_notify(struct Qdisc *csh, unsigned long arg) 24413d2a1d2SPatrick McHardy { 24513d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 24613d2a1d2SPatrick McHardy 24713d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 0) 24813d2a1d2SPatrick McHardy list_del(&cl->alist); 24913d2a1d2SPatrick McHardy } 25013d2a1d2SPatrick McHardy 25113d2a1d2SPatrick McHardy static int drr_dump_class(struct Qdisc *sch, unsigned long arg, 25213d2a1d2SPatrick McHardy struct sk_buff *skb, struct tcmsg *tcm) 25313d2a1d2SPatrick McHardy { 25413d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 25513d2a1d2SPatrick McHardy struct nlattr *nest; 25613d2a1d2SPatrick McHardy 25713d2a1d2SPatrick McHardy tcm->tcm_parent = TC_H_ROOT; 25813d2a1d2SPatrick McHardy tcm->tcm_handle = cl->common.classid; 25913d2a1d2SPatrick McHardy tcm->tcm_info = cl->qdisc->handle; 26013d2a1d2SPatrick McHardy 26113d2a1d2SPatrick McHardy nest = nla_nest_start(skb, TCA_OPTIONS); 26213d2a1d2SPatrick McHardy if (nest == NULL) 26313d2a1d2SPatrick McHardy goto nla_put_failure; 2641b34ec43SDavid S. Miller if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum)) 2651b34ec43SDavid S. Miller goto nla_put_failure; 26613d2a1d2SPatrick McHardy return nla_nest_end(skb, nest); 26713d2a1d2SPatrick McHardy 26813d2a1d2SPatrick McHardy nla_put_failure: 26913d2a1d2SPatrick McHardy nla_nest_cancel(skb, nest); 27013d2a1d2SPatrick McHardy return -EMSGSIZE; 27113d2a1d2SPatrick McHardy } 27213d2a1d2SPatrick McHardy 27313d2a1d2SPatrick McHardy static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, 27413d2a1d2SPatrick McHardy struct gnet_dump *d) 27513d2a1d2SPatrick McHardy { 27613d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 27764015853SJohn Fastabend __u32 qlen = cl->qdisc->q.qlen; 27813d2a1d2SPatrick McHardy struct tc_drr_stats xstats; 27913d2a1d2SPatrick McHardy 28013d2a1d2SPatrick McHardy memset(&xstats, 0, sizeof(xstats)); 28164015853SJohn Fastabend if (qlen) 28213d2a1d2SPatrick McHardy xstats.deficit = cl->deficit; 28313d2a1d2SPatrick McHardy 284edb09eb1SEric Dumazet if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), 285edb09eb1SEric Dumazet d, NULL, &cl->bstats) < 0 || 286*1c0d32fdSEric Dumazet gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || 287b0ab6f92SJohn Fastabend gnet_stats_copy_queue(d, NULL, &cl->qdisc->qstats, qlen) < 0) 28813d2a1d2SPatrick McHardy return -1; 28913d2a1d2SPatrick McHardy 29013d2a1d2SPatrick McHardy return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); 29113d2a1d2SPatrick McHardy } 29213d2a1d2SPatrick McHardy 29313d2a1d2SPatrick McHardy static void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg) 29413d2a1d2SPatrick McHardy { 29513d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 29613d2a1d2SPatrick McHardy struct drr_class *cl; 29713d2a1d2SPatrick McHardy unsigned int i; 29813d2a1d2SPatrick McHardy 29913d2a1d2SPatrick McHardy if (arg->stop) 30013d2a1d2SPatrick McHardy return; 30113d2a1d2SPatrick McHardy 30213d2a1d2SPatrick McHardy for (i = 0; i < q->clhash.hashsize; i++) { 303b67bfe0dSSasha Levin hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) { 30413d2a1d2SPatrick McHardy if (arg->count < arg->skip) { 30513d2a1d2SPatrick McHardy arg->count++; 30613d2a1d2SPatrick McHardy continue; 30713d2a1d2SPatrick McHardy } 30813d2a1d2SPatrick McHardy if (arg->fn(sch, (unsigned long)cl, arg) < 0) { 30913d2a1d2SPatrick McHardy arg->stop = 1; 31013d2a1d2SPatrick McHardy return; 31113d2a1d2SPatrick McHardy } 31213d2a1d2SPatrick McHardy arg->count++; 31313d2a1d2SPatrick McHardy } 31413d2a1d2SPatrick McHardy } 31513d2a1d2SPatrick McHardy } 31613d2a1d2SPatrick McHardy 31713d2a1d2SPatrick McHardy static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, 31813d2a1d2SPatrick McHardy int *qerr) 31913d2a1d2SPatrick McHardy { 32013d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 32113d2a1d2SPatrick McHardy struct drr_class *cl; 32213d2a1d2SPatrick McHardy struct tcf_result res; 32325d8c0d5SJohn Fastabend struct tcf_proto *fl; 32413d2a1d2SPatrick McHardy int result; 32513d2a1d2SPatrick McHardy 32613d2a1d2SPatrick McHardy if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) { 32713d2a1d2SPatrick McHardy cl = drr_find_class(sch, skb->priority); 32813d2a1d2SPatrick McHardy if (cl != NULL) 32913d2a1d2SPatrick McHardy return cl; 33013d2a1d2SPatrick McHardy } 33113d2a1d2SPatrick McHardy 33213d2a1d2SPatrick McHardy *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 33325d8c0d5SJohn Fastabend fl = rcu_dereference_bh(q->filter_list); 3343b3ae880SDaniel Borkmann result = tc_classify(skb, fl, &res, false); 33513d2a1d2SPatrick McHardy if (result >= 0) { 33613d2a1d2SPatrick McHardy #ifdef CONFIG_NET_CLS_ACT 33713d2a1d2SPatrick McHardy switch (result) { 33813d2a1d2SPatrick McHardy case TC_ACT_QUEUED: 33913d2a1d2SPatrick McHardy case TC_ACT_STOLEN: 34013d2a1d2SPatrick McHardy *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 34113d2a1d2SPatrick McHardy case TC_ACT_SHOT: 34213d2a1d2SPatrick McHardy return NULL; 34313d2a1d2SPatrick McHardy } 34413d2a1d2SPatrick McHardy #endif 34513d2a1d2SPatrick McHardy cl = (struct drr_class *)res.class; 34613d2a1d2SPatrick McHardy if (cl == NULL) 34713d2a1d2SPatrick McHardy cl = drr_find_class(sch, res.classid); 34813d2a1d2SPatrick McHardy return cl; 34913d2a1d2SPatrick McHardy } 35013d2a1d2SPatrick McHardy return NULL; 35113d2a1d2SPatrick McHardy } 35213d2a1d2SPatrick McHardy 353520ac30fSEric Dumazet static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, 354520ac30fSEric Dumazet struct sk_buff **to_free) 35513d2a1d2SPatrick McHardy { 35613d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 35713d2a1d2SPatrick McHardy struct drr_class *cl; 358f54ba779SDavid S. Miller int err = 0; 35913d2a1d2SPatrick McHardy 36013d2a1d2SPatrick McHardy cl = drr_classify(skb, sch, &err); 36113d2a1d2SPatrick McHardy if (cl == NULL) { 36213d2a1d2SPatrick McHardy if (err & __NET_XMIT_BYPASS) 36325331d6cSJohn Fastabend qdisc_qstats_drop(sch); 364520ac30fSEric Dumazet __qdisc_drop(skb, to_free); 36513d2a1d2SPatrick McHardy return err; 36613d2a1d2SPatrick McHardy } 36713d2a1d2SPatrick McHardy 368520ac30fSEric Dumazet err = qdisc_enqueue(skb, cl->qdisc, to_free); 36913d2a1d2SPatrick McHardy if (unlikely(err != NET_XMIT_SUCCESS)) { 37013d2a1d2SPatrick McHardy if (net_xmit_drop_count(err)) { 37113d2a1d2SPatrick McHardy cl->qstats.drops++; 37225331d6cSJohn Fastabend qdisc_qstats_drop(sch); 37313d2a1d2SPatrick McHardy } 37413d2a1d2SPatrick McHardy return err; 37513d2a1d2SPatrick McHardy } 37613d2a1d2SPatrick McHardy 37713d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 1) { 37813d2a1d2SPatrick McHardy list_add_tail(&cl->alist, &q->active); 37913d2a1d2SPatrick McHardy cl->deficit = cl->quantum; 38013d2a1d2SPatrick McHardy } 38113d2a1d2SPatrick McHardy 3826a73b571SWANG Cong qdisc_qstats_backlog_inc(sch, skb); 38313d2a1d2SPatrick McHardy sch->q.qlen++; 38413d2a1d2SPatrick McHardy return err; 38513d2a1d2SPatrick McHardy } 38613d2a1d2SPatrick McHardy 38713d2a1d2SPatrick McHardy static struct sk_buff *drr_dequeue(struct Qdisc *sch) 38813d2a1d2SPatrick McHardy { 38913d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 39013d2a1d2SPatrick McHardy struct drr_class *cl; 39113d2a1d2SPatrick McHardy struct sk_buff *skb; 39213d2a1d2SPatrick McHardy unsigned int len; 39313d2a1d2SPatrick McHardy 3943f0947c3SPatrick McHardy if (list_empty(&q->active)) 3953f0947c3SPatrick McHardy goto out; 3963f0947c3SPatrick McHardy while (1) { 39713d2a1d2SPatrick McHardy cl = list_first_entry(&q->active, struct drr_class, alist); 39813d2a1d2SPatrick McHardy skb = cl->qdisc->ops->peek(cl->qdisc); 3996e765a00SFlorian Westphal if (skb == NULL) { 4006e765a00SFlorian Westphal qdisc_warn_nonwc(__func__, cl->qdisc); 4013f0947c3SPatrick McHardy goto out; 4026e765a00SFlorian Westphal } 40313d2a1d2SPatrick McHardy 40413d2a1d2SPatrick McHardy len = qdisc_pkt_len(skb); 40513d2a1d2SPatrick McHardy if (len <= cl->deficit) { 40613d2a1d2SPatrick McHardy cl->deficit -= len; 40713d2a1d2SPatrick McHardy skb = qdisc_dequeue_peeked(cl->qdisc); 408df3eb6cdSBernie Harris if (unlikely(skb == NULL)) 409df3eb6cdSBernie Harris goto out; 41013d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 0) 41113d2a1d2SPatrick McHardy list_del(&cl->alist); 4122dd875ffSEric Dumazet 4132dd875ffSEric Dumazet bstats_update(&cl->bstats, skb); 4149190b3b3SEric Dumazet qdisc_bstats_update(sch, skb); 4156a73b571SWANG Cong qdisc_qstats_backlog_dec(sch, skb); 41613d2a1d2SPatrick McHardy sch->q.qlen--; 41713d2a1d2SPatrick McHardy return skb; 41813d2a1d2SPatrick McHardy } 41913d2a1d2SPatrick McHardy 42013d2a1d2SPatrick McHardy cl->deficit += cl->quantum; 42113d2a1d2SPatrick McHardy list_move_tail(&cl->alist, &q->active); 42213d2a1d2SPatrick McHardy } 4233f0947c3SPatrick McHardy out: 42413d2a1d2SPatrick McHardy return NULL; 42513d2a1d2SPatrick McHardy } 42613d2a1d2SPatrick McHardy 42713d2a1d2SPatrick McHardy static int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt) 42813d2a1d2SPatrick McHardy { 42913d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 43013d2a1d2SPatrick McHardy int err; 43113d2a1d2SPatrick McHardy 43213d2a1d2SPatrick McHardy err = qdisc_class_hash_init(&q->clhash); 43313d2a1d2SPatrick McHardy if (err < 0) 43413d2a1d2SPatrick McHardy return err; 43513d2a1d2SPatrick McHardy INIT_LIST_HEAD(&q->active); 43613d2a1d2SPatrick McHardy return 0; 43713d2a1d2SPatrick McHardy } 43813d2a1d2SPatrick McHardy 43913d2a1d2SPatrick McHardy static void drr_reset_qdisc(struct Qdisc *sch) 44013d2a1d2SPatrick McHardy { 44113d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 44213d2a1d2SPatrick McHardy struct drr_class *cl; 44313d2a1d2SPatrick McHardy unsigned int i; 44413d2a1d2SPatrick McHardy 44513d2a1d2SPatrick McHardy for (i = 0; i < q->clhash.hashsize; i++) { 446b67bfe0dSSasha Levin hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) { 44713d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen) 44813d2a1d2SPatrick McHardy list_del(&cl->alist); 44913d2a1d2SPatrick McHardy qdisc_reset(cl->qdisc); 45013d2a1d2SPatrick McHardy } 45113d2a1d2SPatrick McHardy } 4526a73b571SWANG Cong sch->qstats.backlog = 0; 45313d2a1d2SPatrick McHardy sch->q.qlen = 0; 45413d2a1d2SPatrick McHardy } 45513d2a1d2SPatrick McHardy 45613d2a1d2SPatrick McHardy static void drr_destroy_qdisc(struct Qdisc *sch) 45713d2a1d2SPatrick McHardy { 45813d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 45913d2a1d2SPatrick McHardy struct drr_class *cl; 460b67bfe0dSSasha Levin struct hlist_node *next; 46113d2a1d2SPatrick McHardy unsigned int i; 46213d2a1d2SPatrick McHardy 46313d2a1d2SPatrick McHardy tcf_destroy_chain(&q->filter_list); 46413d2a1d2SPatrick McHardy 46513d2a1d2SPatrick McHardy for (i = 0; i < q->clhash.hashsize; i++) { 466b67bfe0dSSasha Levin hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i], 46713d2a1d2SPatrick McHardy common.hnode) 46813d2a1d2SPatrick McHardy drr_destroy_class(sch, cl); 46913d2a1d2SPatrick McHardy } 47013d2a1d2SPatrick McHardy qdisc_class_hash_destroy(&q->clhash); 47113d2a1d2SPatrick McHardy } 47213d2a1d2SPatrick McHardy 47313d2a1d2SPatrick McHardy static const struct Qdisc_class_ops drr_class_ops = { 47413d2a1d2SPatrick McHardy .change = drr_change_class, 47513d2a1d2SPatrick McHardy .delete = drr_delete_class, 47613d2a1d2SPatrick McHardy .get = drr_get_class, 47713d2a1d2SPatrick McHardy .put = drr_put_class, 47813d2a1d2SPatrick McHardy .tcf_chain = drr_tcf_chain, 47913d2a1d2SPatrick McHardy .bind_tcf = drr_bind_tcf, 48013d2a1d2SPatrick McHardy .unbind_tcf = drr_unbind_tcf, 48113d2a1d2SPatrick McHardy .graft = drr_graft_class, 48213d2a1d2SPatrick McHardy .leaf = drr_class_leaf, 48313d2a1d2SPatrick McHardy .qlen_notify = drr_qlen_notify, 48413d2a1d2SPatrick McHardy .dump = drr_dump_class, 48513d2a1d2SPatrick McHardy .dump_stats = drr_dump_class_stats, 48613d2a1d2SPatrick McHardy .walk = drr_walk, 48713d2a1d2SPatrick McHardy }; 48813d2a1d2SPatrick McHardy 48913d2a1d2SPatrick McHardy static struct Qdisc_ops drr_qdisc_ops __read_mostly = { 49013d2a1d2SPatrick McHardy .cl_ops = &drr_class_ops, 49113d2a1d2SPatrick McHardy .id = "drr", 49213d2a1d2SPatrick McHardy .priv_size = sizeof(struct drr_sched), 49313d2a1d2SPatrick McHardy .enqueue = drr_enqueue, 49413d2a1d2SPatrick McHardy .dequeue = drr_dequeue, 49513d2a1d2SPatrick McHardy .peek = qdisc_peek_dequeued, 49613d2a1d2SPatrick McHardy .init = drr_init_qdisc, 49713d2a1d2SPatrick McHardy .reset = drr_reset_qdisc, 49813d2a1d2SPatrick McHardy .destroy = drr_destroy_qdisc, 49913d2a1d2SPatrick McHardy .owner = THIS_MODULE, 50013d2a1d2SPatrick McHardy }; 50113d2a1d2SPatrick McHardy 50213d2a1d2SPatrick McHardy static int __init drr_init(void) 50313d2a1d2SPatrick McHardy { 50413d2a1d2SPatrick McHardy return register_qdisc(&drr_qdisc_ops); 50513d2a1d2SPatrick McHardy } 50613d2a1d2SPatrick McHardy 50713d2a1d2SPatrick McHardy static void __exit drr_exit(void) 50813d2a1d2SPatrick McHardy { 50913d2a1d2SPatrick McHardy unregister_qdisc(&drr_qdisc_ops); 51013d2a1d2SPatrick McHardy } 51113d2a1d2SPatrick McHardy 51213d2a1d2SPatrick McHardy module_init(drr_init); 51313d2a1d2SPatrick McHardy module_exit(drr_exit); 51413d2a1d2SPatrick McHardy MODULE_LICENSE("GPL"); 515