xref: /openbmc/linux/net/sched/sch_drr.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
213d2a1d2SPatrick McHardy /*
313d2a1d2SPatrick McHardy  * net/sched/sch_drr.c         Deficit Round Robin scheduler
413d2a1d2SPatrick McHardy  *
513d2a1d2SPatrick McHardy  * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
613d2a1d2SPatrick McHardy  */
713d2a1d2SPatrick McHardy 
813d2a1d2SPatrick McHardy #include <linux/module.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
1013d2a1d2SPatrick McHardy #include <linux/init.h>
1113d2a1d2SPatrick McHardy #include <linux/errno.h>
1213d2a1d2SPatrick McHardy #include <linux/netdevice.h>
1313d2a1d2SPatrick McHardy #include <linux/pkt_sched.h>
1413d2a1d2SPatrick McHardy #include <net/sch_generic.h>
1513d2a1d2SPatrick McHardy #include <net/pkt_sched.h>
1613d2a1d2SPatrick McHardy #include <net/pkt_cls.h>
1713d2a1d2SPatrick McHardy 
1813d2a1d2SPatrick McHardy struct drr_class {
1913d2a1d2SPatrick McHardy 	struct Qdisc_class_common	common;
2013d2a1d2SPatrick McHardy 
2150dc9a85SAhmed S. Darwish 	struct gnet_stats_basic_sync		bstats;
2213d2a1d2SPatrick McHardy 	struct gnet_stats_queue		qstats;
231c0d32fdSEric Dumazet 	struct net_rate_estimator __rcu *rate_est;
2413d2a1d2SPatrick McHardy 	struct list_head		alist;
2513d2a1d2SPatrick McHardy 	struct Qdisc			*qdisc;
2613d2a1d2SPatrick McHardy 
2713d2a1d2SPatrick McHardy 	u32				quantum;
2813d2a1d2SPatrick McHardy 	u32				deficit;
2913d2a1d2SPatrick McHardy };
3013d2a1d2SPatrick McHardy 
3113d2a1d2SPatrick McHardy struct drr_sched {
3213d2a1d2SPatrick McHardy 	struct list_head		active;
3325d8c0d5SJohn Fastabend 	struct tcf_proto __rcu		*filter_list;
346529eabaSJiri Pirko 	struct tcf_block		*block;
3513d2a1d2SPatrick McHardy 	struct Qdisc_class_hash		clhash;
3613d2a1d2SPatrick McHardy };
3713d2a1d2SPatrick McHardy 
drr_find_class(struct Qdisc * sch,u32 classid)3813d2a1d2SPatrick McHardy static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
3913d2a1d2SPatrick McHardy {
4013d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
4113d2a1d2SPatrick McHardy 	struct Qdisc_class_common *clc;
4213d2a1d2SPatrick McHardy 
4313d2a1d2SPatrick McHardy 	clc = qdisc_class_find(&q->clhash, classid);
4413d2a1d2SPatrick McHardy 	if (clc == NULL)
4513d2a1d2SPatrick McHardy 		return NULL;
4613d2a1d2SPatrick McHardy 	return container_of(clc, struct drr_class, common);
4713d2a1d2SPatrick McHardy }
4813d2a1d2SPatrick McHardy 
4913d2a1d2SPatrick McHardy static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
5013d2a1d2SPatrick McHardy 	[TCA_DRR_QUANTUM]	= { .type = NLA_U32 },
5113d2a1d2SPatrick McHardy };
5213d2a1d2SPatrick McHardy 
drr_change_class(struct Qdisc * sch,u32 classid,u32 parentid,struct nlattr ** tca,unsigned long * arg,struct netlink_ext_ack * extack)5313d2a1d2SPatrick McHardy static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
54793d81d6SAlexander Aring 			    struct nlattr **tca, unsigned long *arg,
55793d81d6SAlexander Aring 			    struct netlink_ext_ack *extack)
5613d2a1d2SPatrick McHardy {
5713d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
5813d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)*arg;
591844f747SJarek Poplawski 	struct nlattr *opt = tca[TCA_OPTIONS];
6013d2a1d2SPatrick McHardy 	struct nlattr *tb[TCA_DRR_MAX + 1];
6113d2a1d2SPatrick McHardy 	u32 quantum;
6213d2a1d2SPatrick McHardy 	int err;
6313d2a1d2SPatrick McHardy 
64a7c31693SAlexander Aring 	if (!opt) {
65a7c31693SAlexander Aring 		NL_SET_ERR_MSG(extack, "DRR options are required for this operation");
661844f747SJarek Poplawski 		return -EINVAL;
67a7c31693SAlexander Aring 	}
681844f747SJarek Poplawski 
698cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, TCA_DRR_MAX, opt, drr_policy,
708cb08174SJohannes Berg 					  extack);
7113d2a1d2SPatrick McHardy 	if (err < 0)
7213d2a1d2SPatrick McHardy 		return err;
7313d2a1d2SPatrick McHardy 
7413d2a1d2SPatrick McHardy 	if (tb[TCA_DRR_QUANTUM]) {
7513d2a1d2SPatrick McHardy 		quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]);
76a7c31693SAlexander Aring 		if (quantum == 0) {
77a7c31693SAlexander Aring 			NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero");
7813d2a1d2SPatrick McHardy 			return -EINVAL;
79a7c31693SAlexander Aring 		}
8013d2a1d2SPatrick McHardy 	} else
8113d2a1d2SPatrick McHardy 		quantum = psched_mtu(qdisc_dev(sch));
8213d2a1d2SPatrick McHardy 
8313d2a1d2SPatrick McHardy 	if (cl != NULL) {
8471bcb09aSStephen Hemminger 		if (tca[TCA_RATE]) {
8522e0f8b9SJohn Fastabend 			err = gen_replace_estimator(&cl->bstats, NULL,
8622e0f8b9SJohn Fastabend 						    &cl->rate_est,
8729cbcd85SAhmed S. Darwish 						    NULL, true,
8871bcb09aSStephen Hemminger 						    tca[TCA_RATE]);
89a7c31693SAlexander Aring 			if (err) {
90a7c31693SAlexander Aring 				NL_SET_ERR_MSG(extack, "Failed to replace estimator");
9171bcb09aSStephen Hemminger 				return err;
9271bcb09aSStephen Hemminger 			}
93a7c31693SAlexander Aring 		}
9471bcb09aSStephen Hemminger 
9513d2a1d2SPatrick McHardy 		sch_tree_lock(sch);
9613d2a1d2SPatrick McHardy 		if (tb[TCA_DRR_QUANTUM])
9713d2a1d2SPatrick McHardy 			cl->quantum = quantum;
9813d2a1d2SPatrick McHardy 		sch_tree_unlock(sch);
9913d2a1d2SPatrick McHardy 
10013d2a1d2SPatrick McHardy 		return 0;
10113d2a1d2SPatrick McHardy 	}
10213d2a1d2SPatrick McHardy 
10313d2a1d2SPatrick McHardy 	cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL);
10413d2a1d2SPatrick McHardy 	if (cl == NULL)
10513d2a1d2SPatrick McHardy 		return -ENOBUFS;
10613d2a1d2SPatrick McHardy 
10750dc9a85SAhmed S. Darwish 	gnet_stats_basic_sync_init(&cl->bstats);
10813d2a1d2SPatrick McHardy 	cl->common.classid = classid;
10913d2a1d2SPatrick McHardy 	cl->quantum	   = quantum;
1103511c913SChangli Gao 	cl->qdisc	   = qdisc_create_dflt(sch->dev_queue,
111a38a9882SAlexander Aring 					       &pfifo_qdisc_ops, classid,
112a38a9882SAlexander Aring 					       NULL);
11313d2a1d2SPatrick McHardy 	if (cl->qdisc == NULL)
11413d2a1d2SPatrick McHardy 		cl->qdisc = &noop_qdisc;
11549b49971SJiri Kosina 	else
11649b49971SJiri Kosina 		qdisc_hash_add(cl->qdisc, true);
11713d2a1d2SPatrick McHardy 
11871bcb09aSStephen Hemminger 	if (tca[TCA_RATE]) {
11922e0f8b9SJohn Fastabend 		err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
12029cbcd85SAhmed S. Darwish 					    NULL, true, tca[TCA_RATE]);
12171bcb09aSStephen Hemminger 		if (err) {
122a7c31693SAlexander Aring 			NL_SET_ERR_MSG(extack, "Failed to replace estimator");
12386bd446bSVlad Buslov 			qdisc_put(cl->qdisc);
12471bcb09aSStephen Hemminger 			kfree(cl);
12571bcb09aSStephen Hemminger 			return err;
12671bcb09aSStephen Hemminger 		}
12771bcb09aSStephen Hemminger 	}
12813d2a1d2SPatrick McHardy 
12913d2a1d2SPatrick McHardy 	sch_tree_lock(sch);
13013d2a1d2SPatrick McHardy 	qdisc_class_hash_insert(&q->clhash, &cl->common);
13113d2a1d2SPatrick McHardy 	sch_tree_unlock(sch);
13213d2a1d2SPatrick McHardy 
13313d2a1d2SPatrick McHardy 	qdisc_class_hash_grow(sch, &q->clhash);
13413d2a1d2SPatrick McHardy 
13513d2a1d2SPatrick McHardy 	*arg = (unsigned long)cl;
13613d2a1d2SPatrick McHardy 	return 0;
13713d2a1d2SPatrick McHardy }
13813d2a1d2SPatrick McHardy 
drr_destroy_class(struct Qdisc * sch,struct drr_class * cl)13913d2a1d2SPatrick McHardy static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
14013d2a1d2SPatrick McHardy {
1411c0d32fdSEric Dumazet 	gen_kill_estimator(&cl->rate_est);
14286bd446bSVlad Buslov 	qdisc_put(cl->qdisc);
14313d2a1d2SPatrick McHardy 	kfree(cl);
14413d2a1d2SPatrick McHardy }
14513d2a1d2SPatrick McHardy 
drr_delete_class(struct Qdisc * sch,unsigned long arg,struct netlink_ext_ack * extack)1464dd78a73SMaxim Mikityanskiy static int drr_delete_class(struct Qdisc *sch, unsigned long arg,
1474dd78a73SMaxim Mikityanskiy 			    struct netlink_ext_ack *extack)
14813d2a1d2SPatrick McHardy {
14913d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
15013d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
15113d2a1d2SPatrick McHardy 
152*daf8d918SPedro Tammela 	if (qdisc_class_in_use(&cl->common)) {
153*daf8d918SPedro Tammela 		NL_SET_ERR_MSG(extack, "DRR class is in use");
15413d2a1d2SPatrick McHardy 		return -EBUSY;
155*daf8d918SPedro Tammela 	}
15613d2a1d2SPatrick McHardy 
15713d2a1d2SPatrick McHardy 	sch_tree_lock(sch);
15813d2a1d2SPatrick McHardy 
159e5f0e8f8SPaolo Abeni 	qdisc_purge_queue(cl->qdisc);
16013d2a1d2SPatrick McHardy 	qdisc_class_hash_remove(&q->clhash, &cl->common);
16113d2a1d2SPatrick McHardy 
16213d2a1d2SPatrick McHardy 	sch_tree_unlock(sch);
163143976ceSWANG Cong 
164143976ceSWANG Cong 	drr_destroy_class(sch, cl);
16513d2a1d2SPatrick McHardy 	return 0;
16613d2a1d2SPatrick McHardy }
16713d2a1d2SPatrick McHardy 
drr_search_class(struct Qdisc * sch,u32 classid)168143976ceSWANG Cong static unsigned long drr_search_class(struct Qdisc *sch, u32 classid)
16913d2a1d2SPatrick McHardy {
170143976ceSWANG Cong 	return (unsigned long)drr_find_class(sch, classid);
17113d2a1d2SPatrick McHardy }
17213d2a1d2SPatrick McHardy 
drr_tcf_block(struct Qdisc * sch,unsigned long cl,struct netlink_ext_ack * extack)173cbaacc4eSAlexander Aring static struct tcf_block *drr_tcf_block(struct Qdisc *sch, unsigned long cl,
174cbaacc4eSAlexander Aring 				       struct netlink_ext_ack *extack)
17513d2a1d2SPatrick McHardy {
17613d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
17713d2a1d2SPatrick McHardy 
178a7c31693SAlexander Aring 	if (cl) {
179a7c31693SAlexander Aring 		NL_SET_ERR_MSG(extack, "DRR classid must be zero");
18013d2a1d2SPatrick McHardy 		return NULL;
181a7c31693SAlexander Aring 	}
18213d2a1d2SPatrick McHardy 
1836529eabaSJiri Pirko 	return q->block;
18413d2a1d2SPatrick McHardy }
18513d2a1d2SPatrick McHardy 
drr_bind_tcf(struct Qdisc * sch,unsigned long parent,u32 classid)18613d2a1d2SPatrick McHardy static unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent,
18713d2a1d2SPatrick McHardy 				  u32 classid)
18813d2a1d2SPatrick McHardy {
18913d2a1d2SPatrick McHardy 	struct drr_class *cl = drr_find_class(sch, classid);
19013d2a1d2SPatrick McHardy 
1918798481bSPedro Tammela 	if (cl)
1928798481bSPedro Tammela 		qdisc_class_get(&cl->common);
19313d2a1d2SPatrick McHardy 
19413d2a1d2SPatrick McHardy 	return (unsigned long)cl;
19513d2a1d2SPatrick McHardy }
19613d2a1d2SPatrick McHardy 
drr_unbind_tcf(struct Qdisc * sch,unsigned long arg)19713d2a1d2SPatrick McHardy static void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg)
19813d2a1d2SPatrick McHardy {
19913d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
20013d2a1d2SPatrick McHardy 
2018798481bSPedro Tammela 	qdisc_class_put(&cl->common);
20213d2a1d2SPatrick McHardy }
20313d2a1d2SPatrick McHardy 
drr_graft_class(struct Qdisc * sch,unsigned long arg,struct Qdisc * new,struct Qdisc ** old,struct netlink_ext_ack * extack)20413d2a1d2SPatrick McHardy static int drr_graft_class(struct Qdisc *sch, unsigned long arg,
205653d6fd6SAlexander Aring 			   struct Qdisc *new, struct Qdisc **old,
206653d6fd6SAlexander Aring 			   struct netlink_ext_ack *extack)
20713d2a1d2SPatrick McHardy {
20813d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
20913d2a1d2SPatrick McHardy 
21013d2a1d2SPatrick McHardy 	if (new == NULL) {
211a38a9882SAlexander Aring 		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
212a38a9882SAlexander Aring 					cl->common.classid, NULL);
21313d2a1d2SPatrick McHardy 		if (new == NULL)
21413d2a1d2SPatrick McHardy 			new = &noop_qdisc;
21513d2a1d2SPatrick McHardy 	}
21613d2a1d2SPatrick McHardy 
21786a7996cSWANG Cong 	*old = qdisc_replace(sch, new, &cl->qdisc);
21813d2a1d2SPatrick McHardy 	return 0;
21913d2a1d2SPatrick McHardy }
22013d2a1d2SPatrick McHardy 
drr_class_leaf(struct Qdisc * sch,unsigned long arg)22113d2a1d2SPatrick McHardy static struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg)
22213d2a1d2SPatrick McHardy {
22313d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
22413d2a1d2SPatrick McHardy 
22513d2a1d2SPatrick McHardy 	return cl->qdisc;
22613d2a1d2SPatrick McHardy }
22713d2a1d2SPatrick McHardy 
drr_qlen_notify(struct Qdisc * csh,unsigned long arg)22813d2a1d2SPatrick McHardy static void drr_qlen_notify(struct Qdisc *csh, unsigned long arg)
22913d2a1d2SPatrick McHardy {
23013d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
23113d2a1d2SPatrick McHardy 
23213d2a1d2SPatrick McHardy 	list_del(&cl->alist);
23313d2a1d2SPatrick McHardy }
23413d2a1d2SPatrick McHardy 
drr_dump_class(struct Qdisc * sch,unsigned long arg,struct sk_buff * skb,struct tcmsg * tcm)23513d2a1d2SPatrick McHardy static int drr_dump_class(struct Qdisc *sch, unsigned long arg,
23613d2a1d2SPatrick McHardy 			  struct sk_buff *skb, struct tcmsg *tcm)
23713d2a1d2SPatrick McHardy {
23813d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
23913d2a1d2SPatrick McHardy 	struct nlattr *nest;
24013d2a1d2SPatrick McHardy 
24113d2a1d2SPatrick McHardy 	tcm->tcm_parent	= TC_H_ROOT;
24213d2a1d2SPatrick McHardy 	tcm->tcm_handle	= cl->common.classid;
24313d2a1d2SPatrick McHardy 	tcm->tcm_info	= cl->qdisc->handle;
24413d2a1d2SPatrick McHardy 
245ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
24613d2a1d2SPatrick McHardy 	if (nest == NULL)
24713d2a1d2SPatrick McHardy 		goto nla_put_failure;
2481b34ec43SDavid S. Miller 	if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum))
2491b34ec43SDavid S. Miller 		goto nla_put_failure;
25013d2a1d2SPatrick McHardy 	return nla_nest_end(skb, nest);
25113d2a1d2SPatrick McHardy 
25213d2a1d2SPatrick McHardy nla_put_failure:
25313d2a1d2SPatrick McHardy 	nla_nest_cancel(skb, nest);
25413d2a1d2SPatrick McHardy 	return -EMSGSIZE;
25513d2a1d2SPatrick McHardy }
25613d2a1d2SPatrick McHardy 
drr_dump_class_stats(struct Qdisc * sch,unsigned long arg,struct gnet_dump * d)25713d2a1d2SPatrick McHardy static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
25813d2a1d2SPatrick McHardy 				struct gnet_dump *d)
25913d2a1d2SPatrick McHardy {
26013d2a1d2SPatrick McHardy 	struct drr_class *cl = (struct drr_class *)arg;
2615dd431b6SPaolo Abeni 	__u32 qlen = qdisc_qlen_sum(cl->qdisc);
2625dd431b6SPaolo Abeni 	struct Qdisc *cl_q = cl->qdisc;
26313d2a1d2SPatrick McHardy 	struct tc_drr_stats xstats;
26413d2a1d2SPatrick McHardy 
26513d2a1d2SPatrick McHardy 	memset(&xstats, 0, sizeof(xstats));
26664015853SJohn Fastabend 	if (qlen)
26713d2a1d2SPatrick McHardy 		xstats.deficit = cl->deficit;
26813d2a1d2SPatrick McHardy 
26929cbcd85SAhmed S. Darwish 	if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 ||
2701c0d32fdSEric Dumazet 	    gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
2715dd431b6SPaolo Abeni 	    gnet_stats_copy_queue(d, cl_q->cpu_qstats, &cl_q->qstats, qlen) < 0)
27213d2a1d2SPatrick McHardy 		return -1;
27313d2a1d2SPatrick McHardy 
27413d2a1d2SPatrick McHardy 	return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
27513d2a1d2SPatrick McHardy }
27613d2a1d2SPatrick McHardy 
drr_walk(struct Qdisc * sch,struct qdisc_walker * arg)27713d2a1d2SPatrick McHardy static void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg)
27813d2a1d2SPatrick McHardy {
27913d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
28013d2a1d2SPatrick McHardy 	struct drr_class *cl;
28113d2a1d2SPatrick McHardy 	unsigned int i;
28213d2a1d2SPatrick McHardy 
28313d2a1d2SPatrick McHardy 	if (arg->stop)
28413d2a1d2SPatrick McHardy 		return;
28513d2a1d2SPatrick McHardy 
28613d2a1d2SPatrick McHardy 	for (i = 0; i < q->clhash.hashsize; i++) {
287b67bfe0dSSasha Levin 		hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) {
288e046fa89SZhengchao Shao 			if (!tc_qdisc_stats_dump(sch, (unsigned long)cl, arg))
28913d2a1d2SPatrick McHardy 				return;
29013d2a1d2SPatrick McHardy 		}
29113d2a1d2SPatrick McHardy 	}
29213d2a1d2SPatrick McHardy }
29313d2a1d2SPatrick McHardy 
drr_classify(struct sk_buff * skb,struct Qdisc * sch,int * qerr)29413d2a1d2SPatrick McHardy static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
29513d2a1d2SPatrick McHardy 				      int *qerr)
29613d2a1d2SPatrick McHardy {
29713d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
29813d2a1d2SPatrick McHardy 	struct drr_class *cl;
29913d2a1d2SPatrick McHardy 	struct tcf_result res;
30025d8c0d5SJohn Fastabend 	struct tcf_proto *fl;
30113d2a1d2SPatrick McHardy 	int result;
30213d2a1d2SPatrick McHardy 
30313d2a1d2SPatrick McHardy 	if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) {
30413d2a1d2SPatrick McHardy 		cl = drr_find_class(sch, skb->priority);
30513d2a1d2SPatrick McHardy 		if (cl != NULL)
30613d2a1d2SPatrick McHardy 			return cl;
30713d2a1d2SPatrick McHardy 	}
30813d2a1d2SPatrick McHardy 
30913d2a1d2SPatrick McHardy 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
31025d8c0d5SJohn Fastabend 	fl = rcu_dereference_bh(q->filter_list);
3113aa26055SDavide Caratti 	result = tcf_classify(skb, NULL, fl, &res, false);
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:
317e25ea21fSJiri Pirko 		case TC_ACT_TRAP:
31813d2a1d2SPatrick McHardy 			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
319964201deSGustavo A. R. Silva 			fallthrough;
32013d2a1d2SPatrick McHardy 		case TC_ACT_SHOT:
32113d2a1d2SPatrick McHardy 			return NULL;
32213d2a1d2SPatrick McHardy 		}
32313d2a1d2SPatrick McHardy #endif
32413d2a1d2SPatrick McHardy 		cl = (struct drr_class *)res.class;
32513d2a1d2SPatrick McHardy 		if (cl == NULL)
32613d2a1d2SPatrick McHardy 			cl = drr_find_class(sch, res.classid);
32713d2a1d2SPatrick McHardy 		return cl;
32813d2a1d2SPatrick McHardy 	}
32913d2a1d2SPatrick McHardy 	return NULL;
33013d2a1d2SPatrick McHardy }
33113d2a1d2SPatrick McHardy 
drr_enqueue(struct sk_buff * skb,struct Qdisc * sch,struct sk_buff ** to_free)332ac5c66f2SPetr Machata static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
333520ac30fSEric Dumazet 		       struct sk_buff **to_free)
33413d2a1d2SPatrick McHardy {
335f6bab199SToke Høiland-Jørgensen 	unsigned int len = qdisc_pkt_len(skb);
33613d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
33713d2a1d2SPatrick McHardy 	struct drr_class *cl;
338f54ba779SDavid S. Miller 	int err = 0;
33937d9cf1aSToke Høiland-Jørgensen 	bool first;
34013d2a1d2SPatrick McHardy 
34113d2a1d2SPatrick McHardy 	cl = drr_classify(skb, sch, &err);
34213d2a1d2SPatrick McHardy 	if (cl == NULL) {
34313d2a1d2SPatrick McHardy 		if (err & __NET_XMIT_BYPASS)
34425331d6cSJohn Fastabend 			qdisc_qstats_drop(sch);
345520ac30fSEric Dumazet 		__qdisc_drop(skb, to_free);
34613d2a1d2SPatrick McHardy 		return err;
34713d2a1d2SPatrick McHardy 	}
34813d2a1d2SPatrick McHardy 
34937d9cf1aSToke Høiland-Jørgensen 	first = !cl->qdisc->q.qlen;
350ac5c66f2SPetr Machata 	err = qdisc_enqueue(skb, cl->qdisc, to_free);
35113d2a1d2SPatrick McHardy 	if (unlikely(err != NET_XMIT_SUCCESS)) {
35213d2a1d2SPatrick McHardy 		if (net_xmit_drop_count(err)) {
35313d2a1d2SPatrick McHardy 			cl->qstats.drops++;
35425331d6cSJohn Fastabend 			qdisc_qstats_drop(sch);
35513d2a1d2SPatrick McHardy 		}
35613d2a1d2SPatrick McHardy 		return err;
35713d2a1d2SPatrick McHardy 	}
35813d2a1d2SPatrick McHardy 
35937d9cf1aSToke Høiland-Jørgensen 	if (first) {
36013d2a1d2SPatrick McHardy 		list_add_tail(&cl->alist, &q->active);
36113d2a1d2SPatrick McHardy 		cl->deficit = cl->quantum;
36213d2a1d2SPatrick McHardy 	}
36313d2a1d2SPatrick McHardy 
364f6bab199SToke Høiland-Jørgensen 	sch->qstats.backlog += len;
36513d2a1d2SPatrick McHardy 	sch->q.qlen++;
36613d2a1d2SPatrick McHardy 	return err;
36713d2a1d2SPatrick McHardy }
36813d2a1d2SPatrick McHardy 
drr_dequeue(struct Qdisc * sch)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 
3763f0947c3SPatrick McHardy 	if (list_empty(&q->active))
3773f0947c3SPatrick McHardy 		goto out;
3783f0947c3SPatrick McHardy 	while (1) {
37913d2a1d2SPatrick McHardy 		cl = list_first_entry(&q->active, struct drr_class, alist);
38013d2a1d2SPatrick McHardy 		skb = cl->qdisc->ops->peek(cl->qdisc);
3816e765a00SFlorian Westphal 		if (skb == NULL) {
3826e765a00SFlorian Westphal 			qdisc_warn_nonwc(__func__, cl->qdisc);
3833f0947c3SPatrick McHardy 			goto out;
3846e765a00SFlorian Westphal 		}
38513d2a1d2SPatrick McHardy 
38613d2a1d2SPatrick McHardy 		len = qdisc_pkt_len(skb);
38713d2a1d2SPatrick McHardy 		if (len <= cl->deficit) {
38813d2a1d2SPatrick McHardy 			cl->deficit -= len;
38913d2a1d2SPatrick McHardy 			skb = qdisc_dequeue_peeked(cl->qdisc);
390df3eb6cdSBernie Harris 			if (unlikely(skb == NULL))
391df3eb6cdSBernie Harris 				goto out;
39213d2a1d2SPatrick McHardy 			if (cl->qdisc->q.qlen == 0)
39313d2a1d2SPatrick McHardy 				list_del(&cl->alist);
3942dd875ffSEric Dumazet 
3952dd875ffSEric Dumazet 			bstats_update(&cl->bstats, skb);
3969190b3b3SEric Dumazet 			qdisc_bstats_update(sch, skb);
3976a73b571SWANG Cong 			qdisc_qstats_backlog_dec(sch, skb);
39813d2a1d2SPatrick McHardy 			sch->q.qlen--;
39913d2a1d2SPatrick McHardy 			return skb;
40013d2a1d2SPatrick McHardy 		}
40113d2a1d2SPatrick McHardy 
40213d2a1d2SPatrick McHardy 		cl->deficit += cl->quantum;
40313d2a1d2SPatrick McHardy 		list_move_tail(&cl->alist, &q->active);
40413d2a1d2SPatrick McHardy 	}
4053f0947c3SPatrick McHardy out:
40613d2a1d2SPatrick McHardy 	return NULL;
40713d2a1d2SPatrick McHardy }
40813d2a1d2SPatrick McHardy 
drr_init_qdisc(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)409e63d7dfdSAlexander Aring static int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt,
410e63d7dfdSAlexander Aring 			  struct netlink_ext_ack *extack)
41113d2a1d2SPatrick McHardy {
41213d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
41313d2a1d2SPatrick McHardy 	int err;
41413d2a1d2SPatrick McHardy 
4158d1a77f9SAlexander Aring 	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
4166529eabaSJiri Pirko 	if (err)
4176529eabaSJiri Pirko 		return err;
41813d2a1d2SPatrick McHardy 	err = qdisc_class_hash_init(&q->clhash);
41913d2a1d2SPatrick McHardy 	if (err < 0)
42013d2a1d2SPatrick McHardy 		return err;
42113d2a1d2SPatrick McHardy 	INIT_LIST_HEAD(&q->active);
42213d2a1d2SPatrick McHardy 	return 0;
42313d2a1d2SPatrick McHardy }
42413d2a1d2SPatrick McHardy 
drr_reset_qdisc(struct Qdisc * sch)42513d2a1d2SPatrick McHardy static void drr_reset_qdisc(struct Qdisc *sch)
42613d2a1d2SPatrick McHardy {
42713d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
42813d2a1d2SPatrick McHardy 	struct drr_class *cl;
42913d2a1d2SPatrick McHardy 	unsigned int i;
43013d2a1d2SPatrick McHardy 
43113d2a1d2SPatrick McHardy 	for (i = 0; i < q->clhash.hashsize; i++) {
432b67bfe0dSSasha Levin 		hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) {
43313d2a1d2SPatrick McHardy 			if (cl->qdisc->q.qlen)
43413d2a1d2SPatrick McHardy 				list_del(&cl->alist);
43513d2a1d2SPatrick McHardy 			qdisc_reset(cl->qdisc);
43613d2a1d2SPatrick McHardy 		}
43713d2a1d2SPatrick McHardy 	}
43813d2a1d2SPatrick McHardy }
43913d2a1d2SPatrick McHardy 
drr_destroy_qdisc(struct Qdisc * sch)44013d2a1d2SPatrick McHardy static void drr_destroy_qdisc(struct Qdisc *sch)
44113d2a1d2SPatrick McHardy {
44213d2a1d2SPatrick McHardy 	struct drr_sched *q = qdisc_priv(sch);
44313d2a1d2SPatrick McHardy 	struct drr_class *cl;
444b67bfe0dSSasha Levin 	struct hlist_node *next;
44513d2a1d2SPatrick McHardy 	unsigned int i;
44613d2a1d2SPatrick McHardy 
4476529eabaSJiri Pirko 	tcf_block_put(q->block);
44813d2a1d2SPatrick McHardy 
44913d2a1d2SPatrick McHardy 	for (i = 0; i < q->clhash.hashsize; i++) {
450b67bfe0dSSasha Levin 		hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
45113d2a1d2SPatrick McHardy 					  common.hnode)
45213d2a1d2SPatrick McHardy 			drr_destroy_class(sch, cl);
45313d2a1d2SPatrick McHardy 	}
45413d2a1d2SPatrick McHardy 	qdisc_class_hash_destroy(&q->clhash);
45513d2a1d2SPatrick McHardy }
45613d2a1d2SPatrick McHardy 
45713d2a1d2SPatrick McHardy static const struct Qdisc_class_ops drr_class_ops = {
45813d2a1d2SPatrick McHardy 	.change		= drr_change_class,
45913d2a1d2SPatrick McHardy 	.delete		= drr_delete_class,
460143976ceSWANG Cong 	.find		= drr_search_class,
4616529eabaSJiri Pirko 	.tcf_block	= drr_tcf_block,
46213d2a1d2SPatrick McHardy 	.bind_tcf	= drr_bind_tcf,
46313d2a1d2SPatrick McHardy 	.unbind_tcf	= drr_unbind_tcf,
46413d2a1d2SPatrick McHardy 	.graft		= drr_graft_class,
46513d2a1d2SPatrick McHardy 	.leaf		= drr_class_leaf,
46613d2a1d2SPatrick McHardy 	.qlen_notify	= drr_qlen_notify,
46713d2a1d2SPatrick McHardy 	.dump		= drr_dump_class,
46813d2a1d2SPatrick McHardy 	.dump_stats	= drr_dump_class_stats,
46913d2a1d2SPatrick McHardy 	.walk		= drr_walk,
47013d2a1d2SPatrick McHardy };
47113d2a1d2SPatrick McHardy 
47213d2a1d2SPatrick McHardy static struct Qdisc_ops drr_qdisc_ops __read_mostly = {
47313d2a1d2SPatrick McHardy 	.cl_ops		= &drr_class_ops,
47413d2a1d2SPatrick McHardy 	.id		= "drr",
47513d2a1d2SPatrick McHardy 	.priv_size	= sizeof(struct drr_sched),
47613d2a1d2SPatrick McHardy 	.enqueue	= drr_enqueue,
47713d2a1d2SPatrick McHardy 	.dequeue	= drr_dequeue,
47813d2a1d2SPatrick McHardy 	.peek		= qdisc_peek_dequeued,
47913d2a1d2SPatrick McHardy 	.init		= drr_init_qdisc,
48013d2a1d2SPatrick McHardy 	.reset		= drr_reset_qdisc,
48113d2a1d2SPatrick McHardy 	.destroy	= drr_destroy_qdisc,
48213d2a1d2SPatrick McHardy 	.owner		= THIS_MODULE,
48313d2a1d2SPatrick McHardy };
48413d2a1d2SPatrick McHardy 
drr_init(void)48513d2a1d2SPatrick McHardy static int __init drr_init(void)
48613d2a1d2SPatrick McHardy {
48713d2a1d2SPatrick McHardy 	return register_qdisc(&drr_qdisc_ops);
48813d2a1d2SPatrick McHardy }
48913d2a1d2SPatrick McHardy 
drr_exit(void)49013d2a1d2SPatrick McHardy static void __exit drr_exit(void)
49113d2a1d2SPatrick McHardy {
49213d2a1d2SPatrick McHardy 	unregister_qdisc(&drr_qdisc_ops);
49313d2a1d2SPatrick McHardy }
49413d2a1d2SPatrick McHardy 
49513d2a1d2SPatrick McHardy module_init(drr_init);
49613d2a1d2SPatrick McHardy module_exit(drr_exit);
49713d2a1d2SPatrick McHardy MODULE_LICENSE("GPL");
498