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