18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/sch_prio.c	Simple 3-band priority "scheduler".
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
68c2ecf20Sopenharmony_ci * Fixes:       19990609: J Hadi Salim <hadi@nortelnetworks.com>:
78c2ecf20Sopenharmony_ci *              Init --  EINVAL when opt undefined
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
178c2ecf20Sopenharmony_ci#include <net/netlink.h>
188c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
198c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct prio_sched_data {
228c2ecf20Sopenharmony_ci	int bands;
238c2ecf20Sopenharmony_ci	struct tcf_proto __rcu *filter_list;
248c2ecf20Sopenharmony_ci	struct tcf_block *block;
258c2ecf20Sopenharmony_ci	u8  prio2band[TC_PRIO_MAX+1];
268c2ecf20Sopenharmony_ci	struct Qdisc *queues[TCQ_PRIO_BANDS];
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct Qdisc *
318c2ecf20Sopenharmony_ciprio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
348c2ecf20Sopenharmony_ci	u32 band = skb->priority;
358c2ecf20Sopenharmony_ci	struct tcf_result res;
368c2ecf20Sopenharmony_ci	struct tcf_proto *fl;
378c2ecf20Sopenharmony_ci	int err;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
408c2ecf20Sopenharmony_ci	if (TC_H_MAJ(skb->priority) != sch->handle) {
418c2ecf20Sopenharmony_ci		fl = rcu_dereference_bh(q->filter_list);
428c2ecf20Sopenharmony_ci		err = tcf_classify(skb, fl, &res, false);
438c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
448c2ecf20Sopenharmony_ci		switch (err) {
458c2ecf20Sopenharmony_ci		case TC_ACT_STOLEN:
468c2ecf20Sopenharmony_ci		case TC_ACT_QUEUED:
478c2ecf20Sopenharmony_ci		case TC_ACT_TRAP:
488c2ecf20Sopenharmony_ci			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
498c2ecf20Sopenharmony_ci			fallthrough;
508c2ecf20Sopenharmony_ci		case TC_ACT_SHOT:
518c2ecf20Sopenharmony_ci			return NULL;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci#endif
548c2ecf20Sopenharmony_ci		if (!fl || err < 0) {
558c2ecf20Sopenharmony_ci			if (TC_H_MAJ(band))
568c2ecf20Sopenharmony_ci				band = 0;
578c2ecf20Sopenharmony_ci			return q->queues[q->prio2band[band & TC_PRIO_MAX]];
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci		band = res.classid;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	band = TC_H_MIN(band) - 1;
628c2ecf20Sopenharmony_ci	if (band >= q->bands)
638c2ecf20Sopenharmony_ci		return q->queues[q->prio2band[0]];
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return q->queues[band];
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int
698c2ecf20Sopenharmony_ciprio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned int len = qdisc_pkt_len(skb);
728c2ecf20Sopenharmony_ci	struct Qdisc *qdisc;
738c2ecf20Sopenharmony_ci	int ret;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	qdisc = prio_classify(skb, sch, &ret);
768c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
778c2ecf20Sopenharmony_ci	if (qdisc == NULL) {
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (ret & __NET_XMIT_BYPASS)
808c2ecf20Sopenharmony_ci			qdisc_qstats_drop(sch);
818c2ecf20Sopenharmony_ci		__qdisc_drop(skb, to_free);
828c2ecf20Sopenharmony_ci		return ret;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci#endif
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = qdisc_enqueue(skb, qdisc, to_free);
878c2ecf20Sopenharmony_ci	if (ret == NET_XMIT_SUCCESS) {
888c2ecf20Sopenharmony_ci		sch->qstats.backlog += len;
898c2ecf20Sopenharmony_ci		sch->q.qlen++;
908c2ecf20Sopenharmony_ci		return NET_XMIT_SUCCESS;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	if (net_xmit_drop_count(ret))
938c2ecf20Sopenharmony_ci		qdisc_qstats_drop(sch);
948c2ecf20Sopenharmony_ci	return ret;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic struct sk_buff *prio_peek(struct Qdisc *sch)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
1008c2ecf20Sopenharmony_ci	int prio;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++) {
1038c2ecf20Sopenharmony_ci		struct Qdisc *qdisc = q->queues[prio];
1048c2ecf20Sopenharmony_ci		struct sk_buff *skb = qdisc->ops->peek(qdisc);
1058c2ecf20Sopenharmony_ci		if (skb)
1068c2ecf20Sopenharmony_ci			return skb;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	return NULL;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic struct sk_buff *prio_dequeue(struct Qdisc *sch)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
1148c2ecf20Sopenharmony_ci	int prio;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++) {
1178c2ecf20Sopenharmony_ci		struct Qdisc *qdisc = q->queues[prio];
1188c2ecf20Sopenharmony_ci		struct sk_buff *skb = qdisc_dequeue_peeked(qdisc);
1198c2ecf20Sopenharmony_ci		if (skb) {
1208c2ecf20Sopenharmony_ci			qdisc_bstats_update(sch, skb);
1218c2ecf20Sopenharmony_ci			qdisc_qstats_backlog_dec(sch, skb);
1228c2ecf20Sopenharmony_ci			sch->q.qlen--;
1238c2ecf20Sopenharmony_ci			return skb;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	return NULL;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void
1318c2ecf20Sopenharmony_ciprio_reset(struct Qdisc *sch)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int prio;
1348c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++)
1378c2ecf20Sopenharmony_ci		qdisc_reset(q->queues[prio]);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int prio_offload(struct Qdisc *sch, struct tc_prio_qopt *qopt)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
1438c2ecf20Sopenharmony_ci	struct tc_prio_qopt_offload opt = {
1448c2ecf20Sopenharmony_ci		.handle = sch->handle,
1458c2ecf20Sopenharmony_ci		.parent = sch->parent,
1468c2ecf20Sopenharmony_ci	};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
1498c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (qopt) {
1528c2ecf20Sopenharmony_ci		opt.command = TC_PRIO_REPLACE;
1538c2ecf20Sopenharmony_ci		opt.replace_params.bands = qopt->bands;
1548c2ecf20Sopenharmony_ci		memcpy(&opt.replace_params.priomap, qopt->priomap,
1558c2ecf20Sopenharmony_ci		       TC_PRIO_MAX + 1);
1568c2ecf20Sopenharmony_ci		opt.replace_params.qstats = &sch->qstats;
1578c2ecf20Sopenharmony_ci	} else {
1588c2ecf20Sopenharmony_ci		opt.command = TC_PRIO_DESTROY;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_PRIO, &opt);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void
1658c2ecf20Sopenharmony_ciprio_destroy(struct Qdisc *sch)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int prio;
1688c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	tcf_block_put(q->block);
1718c2ecf20Sopenharmony_ci	prio_offload(sch, NULL);
1728c2ecf20Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++)
1738c2ecf20Sopenharmony_ci		qdisc_put(q->queues[prio]);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int prio_tune(struct Qdisc *sch, struct nlattr *opt,
1778c2ecf20Sopenharmony_ci		     struct netlink_ext_ack *extack)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
1808c2ecf20Sopenharmony_ci	struct Qdisc *queues[TCQ_PRIO_BANDS];
1818c2ecf20Sopenharmony_ci	int oldbands = q->bands, i;
1828c2ecf20Sopenharmony_ci	struct tc_prio_qopt *qopt;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (nla_len(opt) < sizeof(*qopt))
1858c2ecf20Sopenharmony_ci		return -EINVAL;
1868c2ecf20Sopenharmony_ci	qopt = nla_data(opt);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2)
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	for (i = 0; i <= TC_PRIO_MAX; i++) {
1928c2ecf20Sopenharmony_ci		if (qopt->priomap[i] >= qopt->bands)
1938c2ecf20Sopenharmony_ci			return -EINVAL;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Before commit, make sure we can allocate all new qdiscs */
1978c2ecf20Sopenharmony_ci	for (i = oldbands; i < qopt->bands; i++) {
1988c2ecf20Sopenharmony_ci		queues[i] = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
1998c2ecf20Sopenharmony_ci					      TC_H_MAKE(sch->handle, i + 1),
2008c2ecf20Sopenharmony_ci					      extack);
2018c2ecf20Sopenharmony_ci		if (!queues[i]) {
2028c2ecf20Sopenharmony_ci			while (i > oldbands)
2038c2ecf20Sopenharmony_ci				qdisc_put(queues[--i]);
2048c2ecf20Sopenharmony_ci			return -ENOMEM;
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	prio_offload(sch, qopt);
2098c2ecf20Sopenharmony_ci	sch_tree_lock(sch);
2108c2ecf20Sopenharmony_ci	q->bands = qopt->bands;
2118c2ecf20Sopenharmony_ci	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	for (i = q->bands; i < oldbands; i++)
2148c2ecf20Sopenharmony_ci		qdisc_tree_flush_backlog(q->queues[i]);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (i = oldbands; i < q->bands; i++) {
2178c2ecf20Sopenharmony_ci		q->queues[i] = queues[i];
2188c2ecf20Sopenharmony_ci		if (q->queues[i] != &noop_qdisc)
2198c2ecf20Sopenharmony_ci			qdisc_hash_add(q->queues[i], true);
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	sch_tree_unlock(sch);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	for (i = q->bands; i < oldbands; i++)
2258c2ecf20Sopenharmony_ci		qdisc_put(q->queues[i]);
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int prio_init(struct Qdisc *sch, struct nlattr *opt,
2308c2ecf20Sopenharmony_ci		     struct netlink_ext_ack *extack)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
2338c2ecf20Sopenharmony_ci	int err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!opt)
2368c2ecf20Sopenharmony_ci		return -EINVAL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
2398c2ecf20Sopenharmony_ci	if (err)
2408c2ecf20Sopenharmony_ci		return err;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return prio_tune(sch, opt, extack);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int prio_dump_offload(struct Qdisc *sch)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct tc_prio_qopt_offload hw_stats = {
2488c2ecf20Sopenharmony_ci		.command = TC_PRIO_STATS,
2498c2ecf20Sopenharmony_ci		.handle = sch->handle,
2508c2ecf20Sopenharmony_ci		.parent = sch->parent,
2518c2ecf20Sopenharmony_ci		{
2528c2ecf20Sopenharmony_ci			.stats = {
2538c2ecf20Sopenharmony_ci				.bstats = &sch->bstats,
2548c2ecf20Sopenharmony_ci				.qstats = &sch->qstats,
2558c2ecf20Sopenharmony_ci			},
2568c2ecf20Sopenharmony_ci		},
2578c2ecf20Sopenharmony_ci	};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_PRIO, &hw_stats);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
2658c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
2668c2ecf20Sopenharmony_ci	struct tc_prio_qopt opt;
2678c2ecf20Sopenharmony_ci	int err;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	opt.bands = q->bands;
2708c2ecf20Sopenharmony_ci	memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX + 1);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	err = prio_dump_offload(sch);
2738c2ecf20Sopenharmony_ci	if (err)
2748c2ecf20Sopenharmony_ci		goto nla_put_failure;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
2778c2ecf20Sopenharmony_ci		goto nla_put_failure;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return skb->len;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cinla_put_failure:
2828c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
2838c2ecf20Sopenharmony_ci	return -1;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
2878c2ecf20Sopenharmony_ci		      struct Qdisc **old, struct netlink_ext_ack *extack)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
2908c2ecf20Sopenharmony_ci	struct tc_prio_qopt_offload graft_offload;
2918c2ecf20Sopenharmony_ci	unsigned long band = arg - 1;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (!new) {
2948c2ecf20Sopenharmony_ci		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
2958c2ecf20Sopenharmony_ci					TC_H_MAKE(sch->handle, arg), extack);
2968c2ecf20Sopenharmony_ci		if (!new)
2978c2ecf20Sopenharmony_ci			new = &noop_qdisc;
2988c2ecf20Sopenharmony_ci		else
2998c2ecf20Sopenharmony_ci			qdisc_hash_add(new, true);
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	*old = qdisc_replace(sch, new, &q->queues[band]);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	graft_offload.handle = sch->handle;
3058c2ecf20Sopenharmony_ci	graft_offload.parent = sch->parent;
3068c2ecf20Sopenharmony_ci	graft_offload.graft_params.band = band;
3078c2ecf20Sopenharmony_ci	graft_offload.graft_params.child_handle = new->handle;
3088c2ecf20Sopenharmony_ci	graft_offload.command = TC_PRIO_GRAFT;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, *old,
3118c2ecf20Sopenharmony_ci				   TC_SETUP_QDISC_PRIO, &graft_offload,
3128c2ecf20Sopenharmony_ci				   extack);
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic struct Qdisc *
3178c2ecf20Sopenharmony_ciprio_leaf(struct Qdisc *sch, unsigned long arg)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3208c2ecf20Sopenharmony_ci	unsigned long band = arg - 1;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return q->queues[band];
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic unsigned long prio_find(struct Qdisc *sch, u32 classid)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3288c2ecf20Sopenharmony_ci	unsigned long band = TC_H_MIN(classid);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (band - 1 >= q->bands)
3318c2ecf20Sopenharmony_ci		return 0;
3328c2ecf20Sopenharmony_ci	return band;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	return prio_find(sch, classid);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic void prio_unbind(struct Qdisc *q, unsigned long cl)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb,
3468c2ecf20Sopenharmony_ci			   struct tcmsg *tcm)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	tcm->tcm_handle |= TC_H_MIN(cl);
3518c2ecf20Sopenharmony_ci	tcm->tcm_info = q->queues[cl-1]->handle;
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
3568c2ecf20Sopenharmony_ci				 struct gnet_dump *d)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3598c2ecf20Sopenharmony_ci	struct Qdisc *cl_q;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	cl_q = q->queues[cl - 1];
3628c2ecf20Sopenharmony_ci	if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
3638c2ecf20Sopenharmony_ci				  d, cl_q->cpu_bstats, &cl_q->bstats) < 0 ||
3648c2ecf20Sopenharmony_ci	    qdisc_qstats_copy(d, cl_q) < 0)
3658c2ecf20Sopenharmony_ci		return -1;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic void prio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3738c2ecf20Sopenharmony_ci	int prio;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (arg->stop)
3768c2ecf20Sopenharmony_ci		return;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	for (prio = 0; prio < q->bands; prio++) {
3798c2ecf20Sopenharmony_ci		if (arg->count < arg->skip) {
3808c2ecf20Sopenharmony_ci			arg->count++;
3818c2ecf20Sopenharmony_ci			continue;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci		if (arg->fn(sch, prio + 1, arg) < 0) {
3848c2ecf20Sopenharmony_ci			arg->stop = 1;
3858c2ecf20Sopenharmony_ci			break;
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci		arg->count++;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic struct tcf_block *prio_tcf_block(struct Qdisc *sch, unsigned long cl,
3928c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct prio_sched_data *q = qdisc_priv(sch);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (cl)
3978c2ecf20Sopenharmony_ci		return NULL;
3988c2ecf20Sopenharmony_ci	return q->block;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops prio_class_ops = {
4028c2ecf20Sopenharmony_ci	.graft		=	prio_graft,
4038c2ecf20Sopenharmony_ci	.leaf		=	prio_leaf,
4048c2ecf20Sopenharmony_ci	.find		=	prio_find,
4058c2ecf20Sopenharmony_ci	.walk		=	prio_walk,
4068c2ecf20Sopenharmony_ci	.tcf_block	=	prio_tcf_block,
4078c2ecf20Sopenharmony_ci	.bind_tcf	=	prio_bind,
4088c2ecf20Sopenharmony_ci	.unbind_tcf	=	prio_unbind,
4098c2ecf20Sopenharmony_ci	.dump		=	prio_dump_class,
4108c2ecf20Sopenharmony_ci	.dump_stats	=	prio_dump_class_stats,
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic struct Qdisc_ops prio_qdisc_ops __read_mostly = {
4148c2ecf20Sopenharmony_ci	.next		=	NULL,
4158c2ecf20Sopenharmony_ci	.cl_ops		=	&prio_class_ops,
4168c2ecf20Sopenharmony_ci	.id		=	"prio",
4178c2ecf20Sopenharmony_ci	.priv_size	=	sizeof(struct prio_sched_data),
4188c2ecf20Sopenharmony_ci	.enqueue	=	prio_enqueue,
4198c2ecf20Sopenharmony_ci	.dequeue	=	prio_dequeue,
4208c2ecf20Sopenharmony_ci	.peek		=	prio_peek,
4218c2ecf20Sopenharmony_ci	.init		=	prio_init,
4228c2ecf20Sopenharmony_ci	.reset		=	prio_reset,
4238c2ecf20Sopenharmony_ci	.destroy	=	prio_destroy,
4248c2ecf20Sopenharmony_ci	.change		=	prio_tune,
4258c2ecf20Sopenharmony_ci	.dump		=	prio_dump,
4268c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
4278c2ecf20Sopenharmony_ci};
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int __init prio_module_init(void)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	return register_qdisc(&prio_qdisc_ops);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void __exit prio_module_exit(void)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	unregister_qdisc(&prio_qdisc_ops);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cimodule_init(prio_module_init)
4408c2ecf20Sopenharmony_cimodule_exit(prio_module_exit)
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
443