xref: /openbmc/linux/net/sched/sch_drr.c (revision 3f0947c3ffaed33c1c38b79e4b17f75ba072d3e9)
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