162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * net/sched/sch_drr.c         Deficit Round Robin scheduler
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/netdevice.h>
1362306a36Sopenharmony_ci#include <linux/pkt_sched.h>
1462306a36Sopenharmony_ci#include <net/sch_generic.h>
1562306a36Sopenharmony_ci#include <net/pkt_sched.h>
1662306a36Sopenharmony_ci#include <net/pkt_cls.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct drr_class {
1962306a36Sopenharmony_ci	struct Qdisc_class_common	common;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	struct gnet_stats_basic_sync		bstats;
2262306a36Sopenharmony_ci	struct gnet_stats_queue		qstats;
2362306a36Sopenharmony_ci	struct net_rate_estimator __rcu *rate_est;
2462306a36Sopenharmony_ci	struct list_head		alist;
2562306a36Sopenharmony_ci	struct Qdisc			*qdisc;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	u32				quantum;
2862306a36Sopenharmony_ci	u32				deficit;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct drr_sched {
3262306a36Sopenharmony_ci	struct list_head		active;
3362306a36Sopenharmony_ci	struct tcf_proto __rcu		*filter_list;
3462306a36Sopenharmony_ci	struct tcf_block		*block;
3562306a36Sopenharmony_ci	struct Qdisc_class_hash		clhash;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
4162306a36Sopenharmony_ci	struct Qdisc_class_common *clc;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	clc = qdisc_class_find(&q->clhash, classid);
4462306a36Sopenharmony_ci	if (clc == NULL)
4562306a36Sopenharmony_ci		return NULL;
4662306a36Sopenharmony_ci	return container_of(clc, struct drr_class, common);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
5062306a36Sopenharmony_ci	[TCA_DRR_QUANTUM]	= { .type = NLA_U32 },
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
5462306a36Sopenharmony_ci			    struct nlattr **tca, unsigned long *arg,
5562306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
5862306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)*arg;
5962306a36Sopenharmony_ci	struct nlattr *opt = tca[TCA_OPTIONS];
6062306a36Sopenharmony_ci	struct nlattr *tb[TCA_DRR_MAX + 1];
6162306a36Sopenharmony_ci	u32 quantum;
6262306a36Sopenharmony_ci	int err;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (!opt) {
6562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "DRR options are required for this operation");
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_DRR_MAX, opt, drr_policy,
7062306a36Sopenharmony_ci					  extack);
7162306a36Sopenharmony_ci	if (err < 0)
7262306a36Sopenharmony_ci		return err;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (tb[TCA_DRR_QUANTUM]) {
7562306a36Sopenharmony_ci		quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]);
7662306a36Sopenharmony_ci		if (quantum == 0) {
7762306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero");
7862306a36Sopenharmony_ci			return -EINVAL;
7962306a36Sopenharmony_ci		}
8062306a36Sopenharmony_ci	} else
8162306a36Sopenharmony_ci		quantum = psched_mtu(qdisc_dev(sch));
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (cl != NULL) {
8462306a36Sopenharmony_ci		if (tca[TCA_RATE]) {
8562306a36Sopenharmony_ci			err = gen_replace_estimator(&cl->bstats, NULL,
8662306a36Sopenharmony_ci						    &cl->rate_est,
8762306a36Sopenharmony_ci						    NULL, true,
8862306a36Sopenharmony_ci						    tca[TCA_RATE]);
8962306a36Sopenharmony_ci			if (err) {
9062306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Failed to replace estimator");
9162306a36Sopenharmony_ci				return err;
9262306a36Sopenharmony_ci			}
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		sch_tree_lock(sch);
9662306a36Sopenharmony_ci		if (tb[TCA_DRR_QUANTUM])
9762306a36Sopenharmony_ci			cl->quantum = quantum;
9862306a36Sopenharmony_ci		sch_tree_unlock(sch);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		return 0;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL);
10462306a36Sopenharmony_ci	if (cl == NULL)
10562306a36Sopenharmony_ci		return -ENOBUFS;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	gnet_stats_basic_sync_init(&cl->bstats);
10862306a36Sopenharmony_ci	cl->common.classid = classid;
10962306a36Sopenharmony_ci	cl->quantum	   = quantum;
11062306a36Sopenharmony_ci	cl->qdisc	   = qdisc_create_dflt(sch->dev_queue,
11162306a36Sopenharmony_ci					       &pfifo_qdisc_ops, classid,
11262306a36Sopenharmony_ci					       NULL);
11362306a36Sopenharmony_ci	if (cl->qdisc == NULL)
11462306a36Sopenharmony_ci		cl->qdisc = &noop_qdisc;
11562306a36Sopenharmony_ci	else
11662306a36Sopenharmony_ci		qdisc_hash_add(cl->qdisc, true);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (tca[TCA_RATE]) {
11962306a36Sopenharmony_ci		err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
12062306a36Sopenharmony_ci					    NULL, true, tca[TCA_RATE]);
12162306a36Sopenharmony_ci		if (err) {
12262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Failed to replace estimator");
12362306a36Sopenharmony_ci			qdisc_put(cl->qdisc);
12462306a36Sopenharmony_ci			kfree(cl);
12562306a36Sopenharmony_ci			return err;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	sch_tree_lock(sch);
13062306a36Sopenharmony_ci	qdisc_class_hash_insert(&q->clhash, &cl->common);
13162306a36Sopenharmony_ci	sch_tree_unlock(sch);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	qdisc_class_hash_grow(sch, &q->clhash);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	*arg = (unsigned long)cl;
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	gen_kill_estimator(&cl->rate_est);
14262306a36Sopenharmony_ci	qdisc_put(cl->qdisc);
14362306a36Sopenharmony_ci	kfree(cl);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int drr_delete_class(struct Qdisc *sch, unsigned long arg,
14762306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
15062306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (qdisc_class_in_use(&cl->common)) {
15362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "DRR class is in use");
15462306a36Sopenharmony_ci		return -EBUSY;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	sch_tree_lock(sch);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	qdisc_purge_queue(cl->qdisc);
16062306a36Sopenharmony_ci	qdisc_class_hash_remove(&q->clhash, &cl->common);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	sch_tree_unlock(sch);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	drr_destroy_class(sch, cl);
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic unsigned long drr_search_class(struct Qdisc *sch, u32 classid)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	return (unsigned long)drr_find_class(sch, classid);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic struct tcf_block *drr_tcf_block(struct Qdisc *sch, unsigned long cl,
17462306a36Sopenharmony_ci				       struct netlink_ext_ack *extack)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (cl) {
17962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "DRR classid must be zero");
18062306a36Sopenharmony_ci		return NULL;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return q->block;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent,
18762306a36Sopenharmony_ci				  u32 classid)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct drr_class *cl = drr_find_class(sch, classid);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (cl)
19262306a36Sopenharmony_ci		qdisc_class_get(&cl->common);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return (unsigned long)cl;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	qdisc_class_put(&cl->common);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int drr_graft_class(struct Qdisc *sch, unsigned long arg,
20562306a36Sopenharmony_ci			   struct Qdisc *new, struct Qdisc **old,
20662306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (new == NULL) {
21162306a36Sopenharmony_ci		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
21262306a36Sopenharmony_ci					cl->common.classid, NULL);
21362306a36Sopenharmony_ci		if (new == NULL)
21462306a36Sopenharmony_ci			new = &noop_qdisc;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	*old = qdisc_replace(sch, new, &cl->qdisc);
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return cl->qdisc;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void drr_qlen_notify(struct Qdisc *csh, unsigned long arg)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	list_del(&cl->alist);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int drr_dump_class(struct Qdisc *sch, unsigned long arg,
23662306a36Sopenharmony_ci			  struct sk_buff *skb, struct tcmsg *tcm)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
23962306a36Sopenharmony_ci	struct nlattr *nest;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	tcm->tcm_parent	= TC_H_ROOT;
24262306a36Sopenharmony_ci	tcm->tcm_handle	= cl->common.classid;
24362306a36Sopenharmony_ci	tcm->tcm_info	= cl->qdisc->handle;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
24662306a36Sopenharmony_ci	if (nest == NULL)
24762306a36Sopenharmony_ci		goto nla_put_failure;
24862306a36Sopenharmony_ci	if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum))
24962306a36Sopenharmony_ci		goto nla_put_failure;
25062306a36Sopenharmony_ci	return nla_nest_end(skb, nest);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cinla_put_failure:
25362306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
25462306a36Sopenharmony_ci	return -EMSGSIZE;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
25862306a36Sopenharmony_ci				struct gnet_dump *d)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct drr_class *cl = (struct drr_class *)arg;
26162306a36Sopenharmony_ci	__u32 qlen = qdisc_qlen_sum(cl->qdisc);
26262306a36Sopenharmony_ci	struct Qdisc *cl_q = cl->qdisc;
26362306a36Sopenharmony_ci	struct tc_drr_stats xstats;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	memset(&xstats, 0, sizeof(xstats));
26662306a36Sopenharmony_ci	if (qlen)
26762306a36Sopenharmony_ci		xstats.deficit = cl->deficit;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 ||
27062306a36Sopenharmony_ci	    gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
27162306a36Sopenharmony_ci	    gnet_stats_copy_queue(d, cl_q->cpu_qstats, &cl_q->qstats, qlen) < 0)
27262306a36Sopenharmony_ci		return -1;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
28062306a36Sopenharmony_ci	struct drr_class *cl;
28162306a36Sopenharmony_ci	unsigned int i;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (arg->stop)
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
28762306a36Sopenharmony_ci		hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) {
28862306a36Sopenharmony_ci			if (!tc_qdisc_stats_dump(sch, (unsigned long)cl, arg))
28962306a36Sopenharmony_ci				return;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
29562306a36Sopenharmony_ci				      int *qerr)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
29862306a36Sopenharmony_ci	struct drr_class *cl;
29962306a36Sopenharmony_ci	struct tcf_result res;
30062306a36Sopenharmony_ci	struct tcf_proto *fl;
30162306a36Sopenharmony_ci	int result;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) {
30462306a36Sopenharmony_ci		cl = drr_find_class(sch, skb->priority);
30562306a36Sopenharmony_ci		if (cl != NULL)
30662306a36Sopenharmony_ci			return cl;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
31062306a36Sopenharmony_ci	fl = rcu_dereference_bh(q->filter_list);
31162306a36Sopenharmony_ci	result = tcf_classify(skb, NULL, fl, &res, false);
31262306a36Sopenharmony_ci	if (result >= 0) {
31362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
31462306a36Sopenharmony_ci		switch (result) {
31562306a36Sopenharmony_ci		case TC_ACT_QUEUED:
31662306a36Sopenharmony_ci		case TC_ACT_STOLEN:
31762306a36Sopenharmony_ci		case TC_ACT_TRAP:
31862306a36Sopenharmony_ci			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
31962306a36Sopenharmony_ci			fallthrough;
32062306a36Sopenharmony_ci		case TC_ACT_SHOT:
32162306a36Sopenharmony_ci			return NULL;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci#endif
32462306a36Sopenharmony_ci		cl = (struct drr_class *)res.class;
32562306a36Sopenharmony_ci		if (cl == NULL)
32662306a36Sopenharmony_ci			cl = drr_find_class(sch, res.classid);
32762306a36Sopenharmony_ci		return cl;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	return NULL;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
33362306a36Sopenharmony_ci		       struct sk_buff **to_free)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	unsigned int len = qdisc_pkt_len(skb);
33662306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
33762306a36Sopenharmony_ci	struct drr_class *cl;
33862306a36Sopenharmony_ci	int err = 0;
33962306a36Sopenharmony_ci	bool first;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	cl = drr_classify(skb, sch, &err);
34262306a36Sopenharmony_ci	if (cl == NULL) {
34362306a36Sopenharmony_ci		if (err & __NET_XMIT_BYPASS)
34462306a36Sopenharmony_ci			qdisc_qstats_drop(sch);
34562306a36Sopenharmony_ci		__qdisc_drop(skb, to_free);
34662306a36Sopenharmony_ci		return err;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	first = !cl->qdisc->q.qlen;
35062306a36Sopenharmony_ci	err = qdisc_enqueue(skb, cl->qdisc, to_free);
35162306a36Sopenharmony_ci	if (unlikely(err != NET_XMIT_SUCCESS)) {
35262306a36Sopenharmony_ci		if (net_xmit_drop_count(err)) {
35362306a36Sopenharmony_ci			cl->qstats.drops++;
35462306a36Sopenharmony_ci			qdisc_qstats_drop(sch);
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci		return err;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (first) {
36062306a36Sopenharmony_ci		list_add_tail(&cl->alist, &q->active);
36162306a36Sopenharmony_ci		cl->deficit = cl->quantum;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	sch->qstats.backlog += len;
36562306a36Sopenharmony_ci	sch->q.qlen++;
36662306a36Sopenharmony_ci	return err;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic struct sk_buff *drr_dequeue(struct Qdisc *sch)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
37262306a36Sopenharmony_ci	struct drr_class *cl;
37362306a36Sopenharmony_ci	struct sk_buff *skb;
37462306a36Sopenharmony_ci	unsigned int len;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (list_empty(&q->active))
37762306a36Sopenharmony_ci		goto out;
37862306a36Sopenharmony_ci	while (1) {
37962306a36Sopenharmony_ci		cl = list_first_entry(&q->active, struct drr_class, alist);
38062306a36Sopenharmony_ci		skb = cl->qdisc->ops->peek(cl->qdisc);
38162306a36Sopenharmony_ci		if (skb == NULL) {
38262306a36Sopenharmony_ci			qdisc_warn_nonwc(__func__, cl->qdisc);
38362306a36Sopenharmony_ci			goto out;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		len = qdisc_pkt_len(skb);
38762306a36Sopenharmony_ci		if (len <= cl->deficit) {
38862306a36Sopenharmony_ci			cl->deficit -= len;
38962306a36Sopenharmony_ci			skb = qdisc_dequeue_peeked(cl->qdisc);
39062306a36Sopenharmony_ci			if (unlikely(skb == NULL))
39162306a36Sopenharmony_ci				goto out;
39262306a36Sopenharmony_ci			if (cl->qdisc->q.qlen == 0)
39362306a36Sopenharmony_ci				list_del(&cl->alist);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci			bstats_update(&cl->bstats, skb);
39662306a36Sopenharmony_ci			qdisc_bstats_update(sch, skb);
39762306a36Sopenharmony_ci			qdisc_qstats_backlog_dec(sch, skb);
39862306a36Sopenharmony_ci			sch->q.qlen--;
39962306a36Sopenharmony_ci			return skb;
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		cl->deficit += cl->quantum;
40362306a36Sopenharmony_ci		list_move_tail(&cl->alist, &q->active);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ciout:
40662306a36Sopenharmony_ci	return NULL;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt,
41062306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
41362306a36Sopenharmony_ci	int err;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
41662306a36Sopenharmony_ci	if (err)
41762306a36Sopenharmony_ci		return err;
41862306a36Sopenharmony_ci	err = qdisc_class_hash_init(&q->clhash);
41962306a36Sopenharmony_ci	if (err < 0)
42062306a36Sopenharmony_ci		return err;
42162306a36Sopenharmony_ci	INIT_LIST_HEAD(&q->active);
42262306a36Sopenharmony_ci	return 0;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void drr_reset_qdisc(struct Qdisc *sch)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
42862306a36Sopenharmony_ci	struct drr_class *cl;
42962306a36Sopenharmony_ci	unsigned int i;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
43262306a36Sopenharmony_ci		hlist_for_each_entry(cl, &q->clhash.hash[i], common.hnode) {
43362306a36Sopenharmony_ci			if (cl->qdisc->q.qlen)
43462306a36Sopenharmony_ci				list_del(&cl->alist);
43562306a36Sopenharmony_ci			qdisc_reset(cl->qdisc);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic void drr_destroy_qdisc(struct Qdisc *sch)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct drr_sched *q = qdisc_priv(sch);
44362306a36Sopenharmony_ci	struct drr_class *cl;
44462306a36Sopenharmony_ci	struct hlist_node *next;
44562306a36Sopenharmony_ci	unsigned int i;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	tcf_block_put(q->block);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
45062306a36Sopenharmony_ci		hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
45162306a36Sopenharmony_ci					  common.hnode)
45262306a36Sopenharmony_ci			drr_destroy_class(sch, cl);
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci	qdisc_class_hash_destroy(&q->clhash);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic const struct Qdisc_class_ops drr_class_ops = {
45862306a36Sopenharmony_ci	.change		= drr_change_class,
45962306a36Sopenharmony_ci	.delete		= drr_delete_class,
46062306a36Sopenharmony_ci	.find		= drr_search_class,
46162306a36Sopenharmony_ci	.tcf_block	= drr_tcf_block,
46262306a36Sopenharmony_ci	.bind_tcf	= drr_bind_tcf,
46362306a36Sopenharmony_ci	.unbind_tcf	= drr_unbind_tcf,
46462306a36Sopenharmony_ci	.graft		= drr_graft_class,
46562306a36Sopenharmony_ci	.leaf		= drr_class_leaf,
46662306a36Sopenharmony_ci	.qlen_notify	= drr_qlen_notify,
46762306a36Sopenharmony_ci	.dump		= drr_dump_class,
46862306a36Sopenharmony_ci	.dump_stats	= drr_dump_class_stats,
46962306a36Sopenharmony_ci	.walk		= drr_walk,
47062306a36Sopenharmony_ci};
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct Qdisc_ops drr_qdisc_ops __read_mostly = {
47362306a36Sopenharmony_ci	.cl_ops		= &drr_class_ops,
47462306a36Sopenharmony_ci	.id		= "drr",
47562306a36Sopenharmony_ci	.priv_size	= sizeof(struct drr_sched),
47662306a36Sopenharmony_ci	.enqueue	= drr_enqueue,
47762306a36Sopenharmony_ci	.dequeue	= drr_dequeue,
47862306a36Sopenharmony_ci	.peek		= qdisc_peek_dequeued,
47962306a36Sopenharmony_ci	.init		= drr_init_qdisc,
48062306a36Sopenharmony_ci	.reset		= drr_reset_qdisc,
48162306a36Sopenharmony_ci	.destroy	= drr_destroy_qdisc,
48262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int __init drr_init(void)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	return register_qdisc(&drr_qdisc_ops);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic void __exit drr_exit(void)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	unregister_qdisc(&drr_qdisc_ops);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cimodule_init(drr_init);
49662306a36Sopenharmony_cimodule_exit(drr_exit);
49762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
498