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