162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * net/sched/sch_prio.c	Simple 3-band priority "scheduler".
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
662306a36Sopenharmony_ci * Fixes:       19990609: J Hadi Salim <hadi@nortelnetworks.com>:
762306a36Sopenharmony_ci *              Init --  EINVAL when opt undefined
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/skbuff.h>
1762306a36Sopenharmony_ci#include <net/netlink.h>
1862306a36Sopenharmony_ci#include <net/pkt_sched.h>
1962306a36Sopenharmony_ci#include <net/pkt_cls.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct prio_sched_data {
2262306a36Sopenharmony_ci	int bands;
2362306a36Sopenharmony_ci	struct tcf_proto __rcu *filter_list;
2462306a36Sopenharmony_ci	struct tcf_block *block;
2562306a36Sopenharmony_ci	u8  prio2band[TC_PRIO_MAX+1];
2662306a36Sopenharmony_ci	struct Qdisc *queues[TCQ_PRIO_BANDS];
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic struct Qdisc *
3162306a36Sopenharmony_ciprio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3462306a36Sopenharmony_ci	u32 band = skb->priority;
3562306a36Sopenharmony_ci	struct tcf_result res;
3662306a36Sopenharmony_ci	struct tcf_proto *fl;
3762306a36Sopenharmony_ci	int err;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
4062306a36Sopenharmony_ci	if (TC_H_MAJ(skb->priority) != sch->handle) {
4162306a36Sopenharmony_ci		fl = rcu_dereference_bh(q->filter_list);
4262306a36Sopenharmony_ci		err = tcf_classify(skb, NULL, fl, &res, false);
4362306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
4462306a36Sopenharmony_ci		switch (err) {
4562306a36Sopenharmony_ci		case TC_ACT_STOLEN:
4662306a36Sopenharmony_ci		case TC_ACT_QUEUED:
4762306a36Sopenharmony_ci		case TC_ACT_TRAP:
4862306a36Sopenharmony_ci			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
4962306a36Sopenharmony_ci			fallthrough;
5062306a36Sopenharmony_ci		case TC_ACT_SHOT:
5162306a36Sopenharmony_ci			return NULL;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci#endif
5462306a36Sopenharmony_ci		if (!fl || err < 0) {
5562306a36Sopenharmony_ci			if (TC_H_MAJ(band))
5662306a36Sopenharmony_ci				band = 0;
5762306a36Sopenharmony_ci			return q->queues[q->prio2band[band & TC_PRIO_MAX]];
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ci		band = res.classid;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	band = TC_H_MIN(band) - 1;
6262306a36Sopenharmony_ci	if (band >= q->bands)
6362306a36Sopenharmony_ci		return q->queues[q->prio2band[0]];
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return q->queues[band];
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int
6962306a36Sopenharmony_ciprio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	unsigned int len = qdisc_pkt_len(skb);
7262306a36Sopenharmony_ci	struct Qdisc *qdisc;
7362306a36Sopenharmony_ci	int ret;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	qdisc = prio_classify(skb, sch, &ret);
7662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
7762306a36Sopenharmony_ci	if (qdisc == NULL) {
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		if (ret & __NET_XMIT_BYPASS)
8062306a36Sopenharmony_ci			qdisc_qstats_drop(sch);
8162306a36Sopenharmony_ci		__qdisc_drop(skb, to_free);
8262306a36Sopenharmony_ci		return ret;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = qdisc_enqueue(skb, qdisc, to_free);
8762306a36Sopenharmony_ci	if (ret == NET_XMIT_SUCCESS) {
8862306a36Sopenharmony_ci		sch->qstats.backlog += len;
8962306a36Sopenharmony_ci		sch->q.qlen++;
9062306a36Sopenharmony_ci		return NET_XMIT_SUCCESS;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	if (net_xmit_drop_count(ret))
9362306a36Sopenharmony_ci		qdisc_qstats_drop(sch);
9462306a36Sopenharmony_ci	return ret;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct sk_buff *prio_peek(struct Qdisc *sch)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
10062306a36Sopenharmony_ci	int prio;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++) {
10362306a36Sopenharmony_ci		struct Qdisc *qdisc = q->queues[prio];
10462306a36Sopenharmony_ci		struct sk_buff *skb = qdisc->ops->peek(qdisc);
10562306a36Sopenharmony_ci		if (skb)
10662306a36Sopenharmony_ci			return skb;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	return NULL;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct sk_buff *prio_dequeue(struct Qdisc *sch)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
11462306a36Sopenharmony_ci	int prio;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++) {
11762306a36Sopenharmony_ci		struct Qdisc *qdisc = q->queues[prio];
11862306a36Sopenharmony_ci		struct sk_buff *skb = qdisc_dequeue_peeked(qdisc);
11962306a36Sopenharmony_ci		if (skb) {
12062306a36Sopenharmony_ci			qdisc_bstats_update(sch, skb);
12162306a36Sopenharmony_ci			qdisc_qstats_backlog_dec(sch, skb);
12262306a36Sopenharmony_ci			sch->q.qlen--;
12362306a36Sopenharmony_ci			return skb;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	return NULL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void
13162306a36Sopenharmony_ciprio_reset(struct Qdisc *sch)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int prio;
13462306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++)
13762306a36Sopenharmony_ci		qdisc_reset(q->queues[prio]);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int prio_offload(struct Qdisc *sch, struct tc_prio_qopt *qopt)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
14362306a36Sopenharmony_ci	struct tc_prio_qopt_offload opt = {
14462306a36Sopenharmony_ci		.handle = sch->handle,
14562306a36Sopenharmony_ci		.parent = sch->parent,
14662306a36Sopenharmony_ci	};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
14962306a36Sopenharmony_ci		return -EOPNOTSUPP;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (qopt) {
15262306a36Sopenharmony_ci		opt.command = TC_PRIO_REPLACE;
15362306a36Sopenharmony_ci		opt.replace_params.bands = qopt->bands;
15462306a36Sopenharmony_ci		memcpy(&opt.replace_params.priomap, qopt->priomap,
15562306a36Sopenharmony_ci		       TC_PRIO_MAX + 1);
15662306a36Sopenharmony_ci		opt.replace_params.qstats = &sch->qstats;
15762306a36Sopenharmony_ci	} else {
15862306a36Sopenharmony_ci		opt.command = TC_PRIO_DESTROY;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_PRIO, &opt);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void
16562306a36Sopenharmony_ciprio_destroy(struct Qdisc *sch)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	int prio;
16862306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	tcf_block_put(q->block);
17162306a36Sopenharmony_ci	prio_offload(sch, NULL);
17262306a36Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++)
17362306a36Sopenharmony_ci		qdisc_put(q->queues[prio]);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int prio_tune(struct Qdisc *sch, struct nlattr *opt,
17762306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
18062306a36Sopenharmony_ci	struct Qdisc *queues[TCQ_PRIO_BANDS];
18162306a36Sopenharmony_ci	int oldbands = q->bands, i;
18262306a36Sopenharmony_ci	struct tc_prio_qopt *qopt;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (nla_len(opt) < sizeof(*qopt))
18562306a36Sopenharmony_ci		return -EINVAL;
18662306a36Sopenharmony_ci	qopt = nla_data(opt);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < TCQ_MIN_PRIO_BANDS)
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	for (i = 0; i <= TC_PRIO_MAX; i++) {
19262306a36Sopenharmony_ci		if (qopt->priomap[i] >= qopt->bands)
19362306a36Sopenharmony_ci			return -EINVAL;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Before commit, make sure we can allocate all new qdiscs */
19762306a36Sopenharmony_ci	for (i = oldbands; i < qopt->bands; i++) {
19862306a36Sopenharmony_ci		queues[i] = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
19962306a36Sopenharmony_ci					      TC_H_MAKE(sch->handle, i + 1),
20062306a36Sopenharmony_ci					      extack);
20162306a36Sopenharmony_ci		if (!queues[i]) {
20262306a36Sopenharmony_ci			while (i > oldbands)
20362306a36Sopenharmony_ci				qdisc_put(queues[--i]);
20462306a36Sopenharmony_ci			return -ENOMEM;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	prio_offload(sch, qopt);
20962306a36Sopenharmony_ci	sch_tree_lock(sch);
21062306a36Sopenharmony_ci	q->bands = qopt->bands;
21162306a36Sopenharmony_ci	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (i = q->bands; i < oldbands; i++)
21462306a36Sopenharmony_ci		qdisc_tree_flush_backlog(q->queues[i]);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	for (i = oldbands; i < q->bands; i++) {
21762306a36Sopenharmony_ci		q->queues[i] = queues[i];
21862306a36Sopenharmony_ci		if (q->queues[i] != &noop_qdisc)
21962306a36Sopenharmony_ci			qdisc_hash_add(q->queues[i], true);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	sch_tree_unlock(sch);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	for (i = q->bands; i < oldbands; i++)
22562306a36Sopenharmony_ci		qdisc_put(q->queues[i]);
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int prio_init(struct Qdisc *sch, struct nlattr *opt,
23062306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
23362306a36Sopenharmony_ci	int err;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!opt)
23662306a36Sopenharmony_ci		return -EINVAL;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
23962306a36Sopenharmony_ci	if (err)
24062306a36Sopenharmony_ci		return err;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return prio_tune(sch, opt, extack);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int prio_dump_offload(struct Qdisc *sch)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct tc_prio_qopt_offload hw_stats = {
24862306a36Sopenharmony_ci		.command = TC_PRIO_STATS,
24962306a36Sopenharmony_ci		.handle = sch->handle,
25062306a36Sopenharmony_ci		.parent = sch->parent,
25162306a36Sopenharmony_ci		{
25262306a36Sopenharmony_ci			.stats = {
25362306a36Sopenharmony_ci				.bstats = &sch->bstats,
25462306a36Sopenharmony_ci				.qstats = &sch->qstats,
25562306a36Sopenharmony_ci			},
25662306a36Sopenharmony_ci		},
25762306a36Sopenharmony_ci	};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_PRIO, &hw_stats);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
26562306a36Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
26662306a36Sopenharmony_ci	struct tc_prio_qopt opt;
26762306a36Sopenharmony_ci	int err;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	opt.bands = q->bands;
27062306a36Sopenharmony_ci	memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX + 1);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	err = prio_dump_offload(sch);
27362306a36Sopenharmony_ci	if (err)
27462306a36Sopenharmony_ci		goto nla_put_failure;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
27762306a36Sopenharmony_ci		goto nla_put_failure;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return skb->len;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cinla_put_failure:
28262306a36Sopenharmony_ci	nlmsg_trim(skb, b);
28362306a36Sopenharmony_ci	return -1;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
28762306a36Sopenharmony_ci		      struct Qdisc **old, struct netlink_ext_ack *extack)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
29062306a36Sopenharmony_ci	struct tc_prio_qopt_offload graft_offload;
29162306a36Sopenharmony_ci	unsigned long band = arg - 1;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!new) {
29462306a36Sopenharmony_ci		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
29562306a36Sopenharmony_ci					TC_H_MAKE(sch->handle, arg), extack);
29662306a36Sopenharmony_ci		if (!new)
29762306a36Sopenharmony_ci			new = &noop_qdisc;
29862306a36Sopenharmony_ci		else
29962306a36Sopenharmony_ci			qdisc_hash_add(new, true);
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	*old = qdisc_replace(sch, new, &q->queues[band]);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	graft_offload.handle = sch->handle;
30562306a36Sopenharmony_ci	graft_offload.parent = sch->parent;
30662306a36Sopenharmony_ci	graft_offload.graft_params.band = band;
30762306a36Sopenharmony_ci	graft_offload.graft_params.child_handle = new->handle;
30862306a36Sopenharmony_ci	graft_offload.command = TC_PRIO_GRAFT;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, *old,
31162306a36Sopenharmony_ci				   TC_SETUP_QDISC_PRIO, &graft_offload,
31262306a36Sopenharmony_ci				   extack);
31362306a36Sopenharmony_ci	return 0;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic struct Qdisc *
31762306a36Sopenharmony_ciprio_leaf(struct Qdisc *sch, unsigned long arg)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
32062306a36Sopenharmony_ci	unsigned long band = arg - 1;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return q->queues[band];
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic unsigned long prio_find(struct Qdisc *sch, u32 classid)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
32862306a36Sopenharmony_ci	unsigned long band = TC_H_MIN(classid);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (band - 1 >= q->bands)
33162306a36Sopenharmony_ci		return 0;
33262306a36Sopenharmony_ci	return band;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	return prio_find(sch, classid);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void prio_unbind(struct Qdisc *q, unsigned long cl)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb,
34662306a36Sopenharmony_ci			   struct tcmsg *tcm)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	tcm->tcm_handle |= TC_H_MIN(cl);
35162306a36Sopenharmony_ci	tcm->tcm_info = q->queues[cl-1]->handle;
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
35662306a36Sopenharmony_ci				 struct gnet_dump *d)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
35962306a36Sopenharmony_ci	struct Qdisc *cl_q;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	cl_q = q->queues[cl - 1];
36262306a36Sopenharmony_ci	if (gnet_stats_copy_basic(d, cl_q->cpu_bstats,
36362306a36Sopenharmony_ci				  &cl_q->bstats, true) < 0 ||
36462306a36Sopenharmony_ci	    qdisc_qstats_copy(d, cl_q) < 0)
36562306a36Sopenharmony_ci		return -1;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void prio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
37362306a36Sopenharmony_ci	int prio;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (arg->stop)
37662306a36Sopenharmony_ci		return;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++) {
37962306a36Sopenharmony_ci		if (!tc_qdisc_stats_dump(sch, prio + 1, arg))
38062306a36Sopenharmony_ci			break;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic struct tcf_block *prio_tcf_block(struct Qdisc *sch, unsigned long cl,
38562306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (cl)
39062306a36Sopenharmony_ci		return NULL;
39162306a36Sopenharmony_ci	return q->block;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic const struct Qdisc_class_ops prio_class_ops = {
39562306a36Sopenharmony_ci	.graft		=	prio_graft,
39662306a36Sopenharmony_ci	.leaf		=	prio_leaf,
39762306a36Sopenharmony_ci	.find		=	prio_find,
39862306a36Sopenharmony_ci	.walk		=	prio_walk,
39962306a36Sopenharmony_ci	.tcf_block	=	prio_tcf_block,
40062306a36Sopenharmony_ci	.bind_tcf	=	prio_bind,
40162306a36Sopenharmony_ci	.unbind_tcf	=	prio_unbind,
40262306a36Sopenharmony_ci	.dump		=	prio_dump_class,
40362306a36Sopenharmony_ci	.dump_stats	=	prio_dump_class_stats,
40462306a36Sopenharmony_ci};
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic struct Qdisc_ops prio_qdisc_ops __read_mostly = {
40762306a36Sopenharmony_ci	.next		=	NULL,
40862306a36Sopenharmony_ci	.cl_ops		=	&prio_class_ops,
40962306a36Sopenharmony_ci	.id		=	"prio",
41062306a36Sopenharmony_ci	.priv_size	=	sizeof(struct prio_sched_data),
41162306a36Sopenharmony_ci	.enqueue	=	prio_enqueue,
41262306a36Sopenharmony_ci	.dequeue	=	prio_dequeue,
41362306a36Sopenharmony_ci	.peek		=	prio_peek,
41462306a36Sopenharmony_ci	.init		=	prio_init,
41562306a36Sopenharmony_ci	.reset		=	prio_reset,
41662306a36Sopenharmony_ci	.destroy	=	prio_destroy,
41762306a36Sopenharmony_ci	.change		=	prio_tune,
41862306a36Sopenharmony_ci	.dump		=	prio_dump,
41962306a36Sopenharmony_ci	.owner		=	THIS_MODULE,
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int __init prio_module_init(void)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	return register_qdisc(&prio_qdisc_ops);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic void __exit prio_module_exit(void)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	unregister_qdisc(&prio_qdisc_ops);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cimodule_init(prio_module_init)
43362306a36Sopenharmony_cimodule_exit(prio_module_exit)
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
436