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> 1213d2a1d2SPatrick McHardy #include <linux/init.h> 1313d2a1d2SPatrick McHardy #include <linux/errno.h> 1413d2a1d2SPatrick McHardy #include <linux/netdevice.h> 1513d2a1d2SPatrick McHardy #include <linux/pkt_sched.h> 1613d2a1d2SPatrick McHardy #include <net/sch_generic.h> 1713d2a1d2SPatrick McHardy #include <net/pkt_sched.h> 1813d2a1d2SPatrick McHardy #include <net/pkt_cls.h> 1913d2a1d2SPatrick McHardy 2013d2a1d2SPatrick McHardy struct drr_class { 2113d2a1d2SPatrick McHardy struct Qdisc_class_common common; 2213d2a1d2SPatrick McHardy unsigned int refcnt; 2313d2a1d2SPatrick McHardy unsigned int filter_cnt; 2413d2a1d2SPatrick McHardy 2513d2a1d2SPatrick McHardy struct gnet_stats_basic bstats; 2613d2a1d2SPatrick McHardy struct gnet_stats_queue qstats; 2713d2a1d2SPatrick McHardy struct gnet_stats_rate_est rate_est; 2813d2a1d2SPatrick McHardy struct list_head alist; 2913d2a1d2SPatrick McHardy struct Qdisc *qdisc; 3013d2a1d2SPatrick McHardy 3113d2a1d2SPatrick McHardy u32 quantum; 3213d2a1d2SPatrick McHardy u32 deficit; 3313d2a1d2SPatrick McHardy }; 3413d2a1d2SPatrick McHardy 3513d2a1d2SPatrick McHardy struct drr_sched { 3613d2a1d2SPatrick McHardy struct list_head active; 3713d2a1d2SPatrick McHardy struct tcf_proto *filter_list; 3813d2a1d2SPatrick McHardy struct Qdisc_class_hash clhash; 3913d2a1d2SPatrick McHardy }; 4013d2a1d2SPatrick McHardy 4113d2a1d2SPatrick McHardy static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid) 4213d2a1d2SPatrick McHardy { 4313d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 4413d2a1d2SPatrick McHardy struct Qdisc_class_common *clc; 4513d2a1d2SPatrick McHardy 4613d2a1d2SPatrick McHardy clc = qdisc_class_find(&q->clhash, classid); 4713d2a1d2SPatrick McHardy if (clc == NULL) 4813d2a1d2SPatrick McHardy return NULL; 4913d2a1d2SPatrick McHardy return container_of(clc, struct drr_class, common); 5013d2a1d2SPatrick McHardy } 5113d2a1d2SPatrick McHardy 5213d2a1d2SPatrick McHardy static void drr_purge_queue(struct drr_class *cl) 5313d2a1d2SPatrick McHardy { 5413d2a1d2SPatrick McHardy unsigned int len = cl->qdisc->q.qlen; 5513d2a1d2SPatrick McHardy 5613d2a1d2SPatrick McHardy qdisc_reset(cl->qdisc); 5713d2a1d2SPatrick McHardy qdisc_tree_decrease_qlen(cl->qdisc, len); 5813d2a1d2SPatrick McHardy } 5913d2a1d2SPatrick McHardy 6013d2a1d2SPatrick McHardy static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = { 6113d2a1d2SPatrick McHardy [TCA_DRR_QUANTUM] = { .type = NLA_U32 }, 6213d2a1d2SPatrick McHardy }; 6313d2a1d2SPatrick McHardy 6413d2a1d2SPatrick McHardy static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 6513d2a1d2SPatrick McHardy struct nlattr **tca, unsigned long *arg) 6613d2a1d2SPatrick McHardy { 6713d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 6813d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)*arg; 6913d2a1d2SPatrick McHardy struct nlattr *tb[TCA_DRR_MAX + 1]; 7013d2a1d2SPatrick McHardy u32 quantum; 7113d2a1d2SPatrick McHardy int err; 7213d2a1d2SPatrick McHardy 7313d2a1d2SPatrick McHardy err = nla_parse_nested(tb, TCA_DRR_MAX, tca[TCA_OPTIONS], drr_policy); 7413d2a1d2SPatrick McHardy if (err < 0) 7513d2a1d2SPatrick McHardy return err; 7613d2a1d2SPatrick McHardy 7713d2a1d2SPatrick McHardy if (tb[TCA_DRR_QUANTUM]) { 7813d2a1d2SPatrick McHardy quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]); 7913d2a1d2SPatrick McHardy if (quantum == 0) 8013d2a1d2SPatrick McHardy return -EINVAL; 8113d2a1d2SPatrick McHardy } else 8213d2a1d2SPatrick McHardy quantum = psched_mtu(qdisc_dev(sch)); 8313d2a1d2SPatrick McHardy 8413d2a1d2SPatrick McHardy if (cl != NULL) { 8513d2a1d2SPatrick McHardy sch_tree_lock(sch); 8613d2a1d2SPatrick McHardy if (tb[TCA_DRR_QUANTUM]) 8713d2a1d2SPatrick McHardy cl->quantum = quantum; 8813d2a1d2SPatrick McHardy sch_tree_unlock(sch); 8913d2a1d2SPatrick McHardy 9013d2a1d2SPatrick McHardy if (tca[TCA_RATE]) 9113d2a1d2SPatrick McHardy gen_replace_estimator(&cl->bstats, &cl->rate_est, 9213d2a1d2SPatrick McHardy qdisc_root_sleeping_lock(sch), 9313d2a1d2SPatrick McHardy tca[TCA_RATE]); 9413d2a1d2SPatrick McHardy return 0; 9513d2a1d2SPatrick McHardy } 9613d2a1d2SPatrick McHardy 9713d2a1d2SPatrick McHardy cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL); 9813d2a1d2SPatrick McHardy if (cl == NULL) 9913d2a1d2SPatrick McHardy return -ENOBUFS; 10013d2a1d2SPatrick McHardy 10113d2a1d2SPatrick McHardy cl->refcnt = 1; 10213d2a1d2SPatrick McHardy cl->common.classid = classid; 10313d2a1d2SPatrick McHardy cl->quantum = quantum; 10413d2a1d2SPatrick McHardy cl->qdisc = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, 10513d2a1d2SPatrick McHardy &pfifo_qdisc_ops, classid); 10613d2a1d2SPatrick McHardy if (cl->qdisc == NULL) 10713d2a1d2SPatrick McHardy cl->qdisc = &noop_qdisc; 10813d2a1d2SPatrick McHardy 10913d2a1d2SPatrick McHardy if (tca[TCA_RATE]) 11013d2a1d2SPatrick McHardy gen_replace_estimator(&cl->bstats, &cl->rate_est, 11113d2a1d2SPatrick McHardy qdisc_root_sleeping_lock(sch), 11213d2a1d2SPatrick McHardy tca[TCA_RATE]); 11313d2a1d2SPatrick McHardy 11413d2a1d2SPatrick McHardy sch_tree_lock(sch); 11513d2a1d2SPatrick McHardy qdisc_class_hash_insert(&q->clhash, &cl->common); 11613d2a1d2SPatrick McHardy sch_tree_unlock(sch); 11713d2a1d2SPatrick McHardy 11813d2a1d2SPatrick McHardy qdisc_class_hash_grow(sch, &q->clhash); 11913d2a1d2SPatrick McHardy 12013d2a1d2SPatrick McHardy *arg = (unsigned long)cl; 12113d2a1d2SPatrick McHardy return 0; 12213d2a1d2SPatrick McHardy } 12313d2a1d2SPatrick McHardy 12413d2a1d2SPatrick McHardy static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl) 12513d2a1d2SPatrick McHardy { 12613d2a1d2SPatrick McHardy gen_kill_estimator(&cl->bstats, &cl->rate_est); 12713d2a1d2SPatrick McHardy qdisc_destroy(cl->qdisc); 12813d2a1d2SPatrick McHardy kfree(cl); 12913d2a1d2SPatrick McHardy } 13013d2a1d2SPatrick McHardy 13113d2a1d2SPatrick McHardy static int drr_delete_class(struct Qdisc *sch, unsigned long arg) 13213d2a1d2SPatrick McHardy { 13313d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 13413d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 13513d2a1d2SPatrick McHardy 13613d2a1d2SPatrick McHardy if (cl->filter_cnt > 0) 13713d2a1d2SPatrick McHardy return -EBUSY; 13813d2a1d2SPatrick McHardy 13913d2a1d2SPatrick McHardy sch_tree_lock(sch); 14013d2a1d2SPatrick McHardy 14113d2a1d2SPatrick McHardy drr_purge_queue(cl); 14213d2a1d2SPatrick McHardy qdisc_class_hash_remove(&q->clhash, &cl->common); 14313d2a1d2SPatrick McHardy 14413d2a1d2SPatrick McHardy if (--cl->refcnt == 0) 14513d2a1d2SPatrick McHardy drr_destroy_class(sch, cl); 14613d2a1d2SPatrick McHardy 14713d2a1d2SPatrick McHardy sch_tree_unlock(sch); 14813d2a1d2SPatrick McHardy return 0; 14913d2a1d2SPatrick McHardy } 15013d2a1d2SPatrick McHardy 15113d2a1d2SPatrick McHardy static unsigned long drr_get_class(struct Qdisc *sch, u32 classid) 15213d2a1d2SPatrick McHardy { 15313d2a1d2SPatrick McHardy struct drr_class *cl = drr_find_class(sch, classid); 15413d2a1d2SPatrick McHardy 15513d2a1d2SPatrick McHardy if (cl != NULL) 15613d2a1d2SPatrick McHardy cl->refcnt++; 15713d2a1d2SPatrick McHardy 15813d2a1d2SPatrick McHardy return (unsigned long)cl; 15913d2a1d2SPatrick McHardy } 16013d2a1d2SPatrick McHardy 16113d2a1d2SPatrick McHardy static void drr_put_class(struct Qdisc *sch, unsigned long arg) 16213d2a1d2SPatrick McHardy { 16313d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 16413d2a1d2SPatrick McHardy 16513d2a1d2SPatrick McHardy if (--cl->refcnt == 0) 16613d2a1d2SPatrick McHardy drr_destroy_class(sch, cl); 16713d2a1d2SPatrick McHardy } 16813d2a1d2SPatrick McHardy 16913d2a1d2SPatrick McHardy static struct tcf_proto **drr_tcf_chain(struct Qdisc *sch, unsigned long cl) 17013d2a1d2SPatrick McHardy { 17113d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 17213d2a1d2SPatrick McHardy 17313d2a1d2SPatrick McHardy if (cl) 17413d2a1d2SPatrick McHardy return NULL; 17513d2a1d2SPatrick McHardy 17613d2a1d2SPatrick McHardy return &q->filter_list; 17713d2a1d2SPatrick McHardy } 17813d2a1d2SPatrick McHardy 17913d2a1d2SPatrick McHardy static unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent, 18013d2a1d2SPatrick McHardy u32 classid) 18113d2a1d2SPatrick McHardy { 18213d2a1d2SPatrick McHardy struct drr_class *cl = drr_find_class(sch, classid); 18313d2a1d2SPatrick McHardy 18413d2a1d2SPatrick McHardy if (cl != NULL) 18513d2a1d2SPatrick McHardy cl->filter_cnt++; 18613d2a1d2SPatrick McHardy 18713d2a1d2SPatrick McHardy return (unsigned long)cl; 18813d2a1d2SPatrick McHardy } 18913d2a1d2SPatrick McHardy 19013d2a1d2SPatrick McHardy static void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg) 19113d2a1d2SPatrick McHardy { 19213d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 19313d2a1d2SPatrick McHardy 19413d2a1d2SPatrick McHardy cl->filter_cnt--; 19513d2a1d2SPatrick McHardy } 19613d2a1d2SPatrick McHardy 19713d2a1d2SPatrick McHardy static int drr_graft_class(struct Qdisc *sch, unsigned long arg, 19813d2a1d2SPatrick McHardy struct Qdisc *new, struct Qdisc **old) 19913d2a1d2SPatrick McHardy { 20013d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 20113d2a1d2SPatrick McHardy 20213d2a1d2SPatrick McHardy if (new == NULL) { 20313d2a1d2SPatrick McHardy new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, 20413d2a1d2SPatrick McHardy &pfifo_qdisc_ops, cl->common.classid); 20513d2a1d2SPatrick McHardy if (new == NULL) 20613d2a1d2SPatrick McHardy new = &noop_qdisc; 20713d2a1d2SPatrick McHardy } 20813d2a1d2SPatrick McHardy 20913d2a1d2SPatrick McHardy sch_tree_lock(sch); 21013d2a1d2SPatrick McHardy drr_purge_queue(cl); 211b94c8afcSPatrick McHardy *old = cl->qdisc; 212b94c8afcSPatrick McHardy cl->qdisc = new; 21313d2a1d2SPatrick McHardy sch_tree_unlock(sch); 21413d2a1d2SPatrick McHardy return 0; 21513d2a1d2SPatrick McHardy } 21613d2a1d2SPatrick McHardy 21713d2a1d2SPatrick McHardy static struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg) 21813d2a1d2SPatrick McHardy { 21913d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 22013d2a1d2SPatrick McHardy 22113d2a1d2SPatrick McHardy return cl->qdisc; 22213d2a1d2SPatrick McHardy } 22313d2a1d2SPatrick McHardy 22413d2a1d2SPatrick McHardy static void drr_qlen_notify(struct Qdisc *csh, unsigned long arg) 22513d2a1d2SPatrick McHardy { 22613d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 22713d2a1d2SPatrick McHardy 22813d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 0) 22913d2a1d2SPatrick McHardy list_del(&cl->alist); 23013d2a1d2SPatrick McHardy } 23113d2a1d2SPatrick McHardy 23213d2a1d2SPatrick McHardy static int drr_dump_class(struct Qdisc *sch, unsigned long arg, 23313d2a1d2SPatrick McHardy struct sk_buff *skb, struct tcmsg *tcm) 23413d2a1d2SPatrick McHardy { 23513d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 23613d2a1d2SPatrick McHardy struct nlattr *nest; 23713d2a1d2SPatrick McHardy 23813d2a1d2SPatrick McHardy tcm->tcm_parent = TC_H_ROOT; 23913d2a1d2SPatrick McHardy tcm->tcm_handle = cl->common.classid; 24013d2a1d2SPatrick McHardy tcm->tcm_info = cl->qdisc->handle; 24113d2a1d2SPatrick McHardy 24213d2a1d2SPatrick McHardy nest = nla_nest_start(skb, TCA_OPTIONS); 24313d2a1d2SPatrick McHardy if (nest == NULL) 24413d2a1d2SPatrick McHardy goto nla_put_failure; 24513d2a1d2SPatrick McHardy NLA_PUT_U32(skb, TCA_DRR_QUANTUM, cl->quantum); 24613d2a1d2SPatrick McHardy return nla_nest_end(skb, nest); 24713d2a1d2SPatrick McHardy 24813d2a1d2SPatrick McHardy nla_put_failure: 24913d2a1d2SPatrick McHardy nla_nest_cancel(skb, nest); 25013d2a1d2SPatrick McHardy return -EMSGSIZE; 25113d2a1d2SPatrick McHardy } 25213d2a1d2SPatrick McHardy 25313d2a1d2SPatrick McHardy static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, 25413d2a1d2SPatrick McHardy struct gnet_dump *d) 25513d2a1d2SPatrick McHardy { 25613d2a1d2SPatrick McHardy struct drr_class *cl = (struct drr_class *)arg; 25713d2a1d2SPatrick McHardy struct tc_drr_stats xstats; 25813d2a1d2SPatrick McHardy 25913d2a1d2SPatrick McHardy memset(&xstats, 0, sizeof(xstats)); 26013d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen) 26113d2a1d2SPatrick McHardy xstats.deficit = cl->deficit; 26213d2a1d2SPatrick McHardy 26313d2a1d2SPatrick McHardy if (gnet_stats_copy_basic(d, &cl->bstats) < 0 || 26413d2a1d2SPatrick McHardy gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || 26513d2a1d2SPatrick McHardy gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0) 26613d2a1d2SPatrick McHardy return -1; 26713d2a1d2SPatrick McHardy 26813d2a1d2SPatrick McHardy return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); 26913d2a1d2SPatrick McHardy } 27013d2a1d2SPatrick McHardy 27113d2a1d2SPatrick McHardy static void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg) 27213d2a1d2SPatrick McHardy { 27313d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 27413d2a1d2SPatrick McHardy struct drr_class *cl; 27513d2a1d2SPatrick McHardy struct hlist_node *n; 27613d2a1d2SPatrick McHardy unsigned int i; 27713d2a1d2SPatrick McHardy 27813d2a1d2SPatrick McHardy if (arg->stop) 27913d2a1d2SPatrick McHardy return; 28013d2a1d2SPatrick McHardy 28113d2a1d2SPatrick McHardy for (i = 0; i < q->clhash.hashsize; i++) { 28213d2a1d2SPatrick McHardy hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) { 28313d2a1d2SPatrick McHardy if (arg->count < arg->skip) { 28413d2a1d2SPatrick McHardy arg->count++; 28513d2a1d2SPatrick McHardy continue; 28613d2a1d2SPatrick McHardy } 28713d2a1d2SPatrick McHardy if (arg->fn(sch, (unsigned long)cl, arg) < 0) { 28813d2a1d2SPatrick McHardy arg->stop = 1; 28913d2a1d2SPatrick McHardy return; 29013d2a1d2SPatrick McHardy } 29113d2a1d2SPatrick McHardy arg->count++; 29213d2a1d2SPatrick McHardy } 29313d2a1d2SPatrick McHardy } 29413d2a1d2SPatrick McHardy } 29513d2a1d2SPatrick McHardy 29613d2a1d2SPatrick McHardy static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, 29713d2a1d2SPatrick McHardy int *qerr) 29813d2a1d2SPatrick McHardy { 29913d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 30013d2a1d2SPatrick McHardy struct drr_class *cl; 30113d2a1d2SPatrick McHardy struct tcf_result res; 30213d2a1d2SPatrick McHardy int result; 30313d2a1d2SPatrick McHardy 30413d2a1d2SPatrick McHardy if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) { 30513d2a1d2SPatrick McHardy cl = drr_find_class(sch, skb->priority); 30613d2a1d2SPatrick McHardy if (cl != NULL) 30713d2a1d2SPatrick McHardy return cl; 30813d2a1d2SPatrick McHardy } 30913d2a1d2SPatrick McHardy 31013d2a1d2SPatrick McHardy *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 31113d2a1d2SPatrick McHardy result = tc_classify(skb, q->filter_list, &res); 31213d2a1d2SPatrick McHardy if (result >= 0) { 31313d2a1d2SPatrick McHardy #ifdef CONFIG_NET_CLS_ACT 31413d2a1d2SPatrick McHardy switch (result) { 31513d2a1d2SPatrick McHardy case TC_ACT_QUEUED: 31613d2a1d2SPatrick McHardy case TC_ACT_STOLEN: 31713d2a1d2SPatrick McHardy *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 31813d2a1d2SPatrick McHardy case TC_ACT_SHOT: 31913d2a1d2SPatrick McHardy return NULL; 32013d2a1d2SPatrick McHardy } 32113d2a1d2SPatrick McHardy #endif 32213d2a1d2SPatrick McHardy cl = (struct drr_class *)res.class; 32313d2a1d2SPatrick McHardy if (cl == NULL) 32413d2a1d2SPatrick McHardy cl = drr_find_class(sch, res.classid); 32513d2a1d2SPatrick McHardy return cl; 32613d2a1d2SPatrick McHardy } 32713d2a1d2SPatrick McHardy return NULL; 32813d2a1d2SPatrick McHardy } 32913d2a1d2SPatrick McHardy 33013d2a1d2SPatrick McHardy static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch) 33113d2a1d2SPatrick McHardy { 33213d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 33313d2a1d2SPatrick McHardy struct drr_class *cl; 33413d2a1d2SPatrick McHardy unsigned int len; 33513d2a1d2SPatrick McHardy int err; 33613d2a1d2SPatrick McHardy 33713d2a1d2SPatrick McHardy cl = drr_classify(skb, sch, &err); 33813d2a1d2SPatrick McHardy if (cl == NULL) { 33913d2a1d2SPatrick McHardy if (err & __NET_XMIT_BYPASS) 34013d2a1d2SPatrick McHardy sch->qstats.drops++; 34113d2a1d2SPatrick McHardy kfree_skb(skb); 34213d2a1d2SPatrick McHardy return err; 34313d2a1d2SPatrick McHardy } 34413d2a1d2SPatrick McHardy 34513d2a1d2SPatrick McHardy len = qdisc_pkt_len(skb); 34613d2a1d2SPatrick McHardy err = qdisc_enqueue(skb, cl->qdisc); 34713d2a1d2SPatrick McHardy if (unlikely(err != NET_XMIT_SUCCESS)) { 34813d2a1d2SPatrick McHardy if (net_xmit_drop_count(err)) { 34913d2a1d2SPatrick McHardy cl->qstats.drops++; 35013d2a1d2SPatrick McHardy sch->qstats.drops++; 35113d2a1d2SPatrick McHardy } 35213d2a1d2SPatrick McHardy return err; 35313d2a1d2SPatrick McHardy } 35413d2a1d2SPatrick McHardy 35513d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 1) { 35613d2a1d2SPatrick McHardy list_add_tail(&cl->alist, &q->active); 35713d2a1d2SPatrick McHardy cl->deficit = cl->quantum; 35813d2a1d2SPatrick McHardy } 35913d2a1d2SPatrick McHardy 36013d2a1d2SPatrick McHardy cl->bstats.packets++; 36113d2a1d2SPatrick McHardy cl->bstats.bytes += len; 36213d2a1d2SPatrick McHardy sch->bstats.packets++; 36313d2a1d2SPatrick McHardy sch->bstats.bytes += len; 36413d2a1d2SPatrick McHardy 36513d2a1d2SPatrick McHardy sch->q.qlen++; 36613d2a1d2SPatrick McHardy return err; 36713d2a1d2SPatrick McHardy } 36813d2a1d2SPatrick McHardy 36913d2a1d2SPatrick McHardy static struct sk_buff *drr_dequeue(struct Qdisc *sch) 37013d2a1d2SPatrick McHardy { 37113d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 37213d2a1d2SPatrick McHardy struct drr_class *cl; 37313d2a1d2SPatrick McHardy struct sk_buff *skb; 37413d2a1d2SPatrick McHardy unsigned int len; 37513d2a1d2SPatrick McHardy 376*3f0947c3SPatrick McHardy if (list_empty(&q->active)) 377*3f0947c3SPatrick McHardy goto out; 378*3f0947c3SPatrick McHardy while (1) { 37913d2a1d2SPatrick McHardy cl = list_first_entry(&q->active, struct drr_class, alist); 38013d2a1d2SPatrick McHardy skb = cl->qdisc->ops->peek(cl->qdisc); 38113d2a1d2SPatrick McHardy if (skb == NULL) 382*3f0947c3SPatrick McHardy goto out; 38313d2a1d2SPatrick McHardy 38413d2a1d2SPatrick McHardy len = qdisc_pkt_len(skb); 38513d2a1d2SPatrick McHardy if (len <= cl->deficit) { 38613d2a1d2SPatrick McHardy cl->deficit -= len; 38713d2a1d2SPatrick McHardy skb = qdisc_dequeue_peeked(cl->qdisc); 38813d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 0) 38913d2a1d2SPatrick McHardy list_del(&cl->alist); 39013d2a1d2SPatrick McHardy sch->q.qlen--; 39113d2a1d2SPatrick McHardy return skb; 39213d2a1d2SPatrick McHardy } 39313d2a1d2SPatrick McHardy 39413d2a1d2SPatrick McHardy cl->deficit += cl->quantum; 39513d2a1d2SPatrick McHardy list_move_tail(&cl->alist, &q->active); 39613d2a1d2SPatrick McHardy } 397*3f0947c3SPatrick McHardy out: 39813d2a1d2SPatrick McHardy return NULL; 39913d2a1d2SPatrick McHardy } 40013d2a1d2SPatrick McHardy 40113d2a1d2SPatrick McHardy static unsigned int drr_drop(struct Qdisc *sch) 40213d2a1d2SPatrick McHardy { 40313d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 40413d2a1d2SPatrick McHardy struct drr_class *cl; 40513d2a1d2SPatrick McHardy unsigned int len; 40613d2a1d2SPatrick McHardy 40713d2a1d2SPatrick McHardy list_for_each_entry(cl, &q->active, alist) { 40813d2a1d2SPatrick McHardy if (cl->qdisc->ops->drop) { 40913d2a1d2SPatrick McHardy len = cl->qdisc->ops->drop(cl->qdisc); 41013d2a1d2SPatrick McHardy if (len > 0) { 41198aa9c80SJarek Poplawski sch->q.qlen--; 41213d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen == 0) 41313d2a1d2SPatrick McHardy list_del(&cl->alist); 41413d2a1d2SPatrick McHardy return len; 41513d2a1d2SPatrick McHardy } 41613d2a1d2SPatrick McHardy } 41713d2a1d2SPatrick McHardy } 41813d2a1d2SPatrick McHardy return 0; 41913d2a1d2SPatrick McHardy } 42013d2a1d2SPatrick McHardy 42113d2a1d2SPatrick McHardy static int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt) 42213d2a1d2SPatrick McHardy { 42313d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 42413d2a1d2SPatrick McHardy int err; 42513d2a1d2SPatrick McHardy 42613d2a1d2SPatrick McHardy err = qdisc_class_hash_init(&q->clhash); 42713d2a1d2SPatrick McHardy if (err < 0) 42813d2a1d2SPatrick McHardy return err; 42913d2a1d2SPatrick McHardy INIT_LIST_HEAD(&q->active); 43013d2a1d2SPatrick McHardy return 0; 43113d2a1d2SPatrick McHardy } 43213d2a1d2SPatrick McHardy 43313d2a1d2SPatrick McHardy static void drr_reset_qdisc(struct Qdisc *sch) 43413d2a1d2SPatrick McHardy { 43513d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 43613d2a1d2SPatrick McHardy struct drr_class *cl; 43713d2a1d2SPatrick McHardy struct hlist_node *n; 43813d2a1d2SPatrick McHardy unsigned int i; 43913d2a1d2SPatrick McHardy 44013d2a1d2SPatrick McHardy for (i = 0; i < q->clhash.hashsize; i++) { 44113d2a1d2SPatrick McHardy hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) { 44213d2a1d2SPatrick McHardy if (cl->qdisc->q.qlen) 44313d2a1d2SPatrick McHardy list_del(&cl->alist); 44413d2a1d2SPatrick McHardy qdisc_reset(cl->qdisc); 44513d2a1d2SPatrick McHardy } 44613d2a1d2SPatrick McHardy } 44713d2a1d2SPatrick McHardy sch->q.qlen = 0; 44813d2a1d2SPatrick McHardy } 44913d2a1d2SPatrick McHardy 45013d2a1d2SPatrick McHardy static void drr_destroy_qdisc(struct Qdisc *sch) 45113d2a1d2SPatrick McHardy { 45213d2a1d2SPatrick McHardy struct drr_sched *q = qdisc_priv(sch); 45313d2a1d2SPatrick McHardy struct drr_class *cl; 45413d2a1d2SPatrick McHardy struct hlist_node *n, *next; 45513d2a1d2SPatrick McHardy unsigned int i; 45613d2a1d2SPatrick McHardy 45713d2a1d2SPatrick McHardy tcf_destroy_chain(&q->filter_list); 45813d2a1d2SPatrick McHardy 45913d2a1d2SPatrick McHardy for (i = 0; i < q->clhash.hashsize; i++) { 46013d2a1d2SPatrick McHardy hlist_for_each_entry_safe(cl, n, next, &q->clhash.hash[i], 46113d2a1d2SPatrick McHardy common.hnode) 46213d2a1d2SPatrick McHardy drr_destroy_class(sch, cl); 46313d2a1d2SPatrick McHardy } 46413d2a1d2SPatrick McHardy qdisc_class_hash_destroy(&q->clhash); 46513d2a1d2SPatrick McHardy } 46613d2a1d2SPatrick McHardy 46713d2a1d2SPatrick McHardy static const struct Qdisc_class_ops drr_class_ops = { 46813d2a1d2SPatrick McHardy .change = drr_change_class, 46913d2a1d2SPatrick McHardy .delete = drr_delete_class, 47013d2a1d2SPatrick McHardy .get = drr_get_class, 47113d2a1d2SPatrick McHardy .put = drr_put_class, 47213d2a1d2SPatrick McHardy .tcf_chain = drr_tcf_chain, 47313d2a1d2SPatrick McHardy .bind_tcf = drr_bind_tcf, 47413d2a1d2SPatrick McHardy .unbind_tcf = drr_unbind_tcf, 47513d2a1d2SPatrick McHardy .graft = drr_graft_class, 47613d2a1d2SPatrick McHardy .leaf = drr_class_leaf, 47713d2a1d2SPatrick McHardy .qlen_notify = drr_qlen_notify, 47813d2a1d2SPatrick McHardy .dump = drr_dump_class, 47913d2a1d2SPatrick McHardy .dump_stats = drr_dump_class_stats, 48013d2a1d2SPatrick McHardy .walk = drr_walk, 48113d2a1d2SPatrick McHardy }; 48213d2a1d2SPatrick McHardy 48313d2a1d2SPatrick McHardy static struct Qdisc_ops drr_qdisc_ops __read_mostly = { 48413d2a1d2SPatrick McHardy .cl_ops = &drr_class_ops, 48513d2a1d2SPatrick McHardy .id = "drr", 48613d2a1d2SPatrick McHardy .priv_size = sizeof(struct drr_sched), 48713d2a1d2SPatrick McHardy .enqueue = drr_enqueue, 48813d2a1d2SPatrick McHardy .dequeue = drr_dequeue, 48913d2a1d2SPatrick McHardy .peek = qdisc_peek_dequeued, 49013d2a1d2SPatrick McHardy .drop = drr_drop, 49113d2a1d2SPatrick McHardy .init = drr_init_qdisc, 49213d2a1d2SPatrick McHardy .reset = drr_reset_qdisc, 49313d2a1d2SPatrick McHardy .destroy = drr_destroy_qdisc, 49413d2a1d2SPatrick McHardy .owner = THIS_MODULE, 49513d2a1d2SPatrick McHardy }; 49613d2a1d2SPatrick McHardy 49713d2a1d2SPatrick McHardy static int __init drr_init(void) 49813d2a1d2SPatrick McHardy { 49913d2a1d2SPatrick McHardy return register_qdisc(&drr_qdisc_ops); 50013d2a1d2SPatrick McHardy } 50113d2a1d2SPatrick McHardy 50213d2a1d2SPatrick McHardy static void __exit drr_exit(void) 50313d2a1d2SPatrick McHardy { 50413d2a1d2SPatrick McHardy unregister_qdisc(&drr_qdisc_ops); 50513d2a1d2SPatrick McHardy } 50613d2a1d2SPatrick McHardy 50713d2a1d2SPatrick McHardy module_init(drr_init); 50813d2a1d2SPatrick McHardy module_exit(drr_exit); 50913d2a1d2SPatrick McHardy MODULE_LICENSE("GPL"); 510