18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/sch_ets.c         Enhanced Transmission Selection scheduler
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Description
68c2ecf20Sopenharmony_ci * -----------
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * The Enhanced Transmission Selection scheduler is a classful queuing
98c2ecf20Sopenharmony_ci * discipline that merges functionality of PRIO and DRR qdiscs in one scheduler.
108c2ecf20Sopenharmony_ci * ETS makes it easy to configure a set of strict and bandwidth-sharing bands to
118c2ecf20Sopenharmony_ci * implement the transmission selection described in 802.1Qaz.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Although ETS is technically classful, it's not possible to add and remove
148c2ecf20Sopenharmony_ci * classes at will. Instead one specifies number of classes, how many are
158c2ecf20Sopenharmony_ci * PRIO-like and how many DRR-like, and quanta for the latter.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Algorithm
188c2ecf20Sopenharmony_ci * ---------
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * The strict classes, if any, are tried for traffic first: first band 0, if it
218c2ecf20Sopenharmony_ci * has no traffic then band 1, etc.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * When there is no traffic in any of the strict queues, the bandwidth-sharing
248c2ecf20Sopenharmony_ci * ones are tried next. Each band is assigned a deficit counter, initialized to
258c2ecf20Sopenharmony_ci * "quantum" of that band. ETS maintains a list of active bandwidth-sharing
268c2ecf20Sopenharmony_ci * bands whose qdiscs are non-empty. A packet is dequeued from the band at the
278c2ecf20Sopenharmony_ci * head of the list if the packet size is smaller or equal to the deficit
288c2ecf20Sopenharmony_ci * counter. If the counter is too small, it is increased by "quantum" and the
298c2ecf20Sopenharmony_ci * scheduler moves on to the next band in the active list.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/module.h>
338c2ecf20Sopenharmony_ci#include <net/gen_stats.h>
348c2ecf20Sopenharmony_ci#include <net/netlink.h>
358c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
368c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
378c2ecf20Sopenharmony_ci#include <net/sch_generic.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct ets_class {
408c2ecf20Sopenharmony_ci	struct list_head alist; /* In struct ets_sched.active. */
418c2ecf20Sopenharmony_ci	struct Qdisc *qdisc;
428c2ecf20Sopenharmony_ci	u32 quantum;
438c2ecf20Sopenharmony_ci	u32 deficit;
448c2ecf20Sopenharmony_ci	struct gnet_stats_basic_packed bstats;
458c2ecf20Sopenharmony_ci	struct gnet_stats_queue qstats;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct ets_sched {
498c2ecf20Sopenharmony_ci	struct list_head active;
508c2ecf20Sopenharmony_ci	struct tcf_proto __rcu *filter_list;
518c2ecf20Sopenharmony_ci	struct tcf_block *block;
528c2ecf20Sopenharmony_ci	unsigned int nbands;
538c2ecf20Sopenharmony_ci	unsigned int nstrict;
548c2ecf20Sopenharmony_ci	u8 prio2band[TC_PRIO_MAX + 1];
558c2ecf20Sopenharmony_ci	struct ets_class classes[TCQ_ETS_MAX_BANDS];
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct nla_policy ets_policy[TCA_ETS_MAX + 1] = {
598c2ecf20Sopenharmony_ci	[TCA_ETS_NBANDS] = { .type = NLA_U8 },
608c2ecf20Sopenharmony_ci	[TCA_ETS_NSTRICT] = { .type = NLA_U8 },
618c2ecf20Sopenharmony_ci	[TCA_ETS_QUANTA] = { .type = NLA_NESTED },
628c2ecf20Sopenharmony_ci	[TCA_ETS_PRIOMAP] = { .type = NLA_NESTED },
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct nla_policy ets_priomap_policy[TCA_ETS_MAX + 1] = {
668c2ecf20Sopenharmony_ci	[TCA_ETS_PRIOMAP_BAND] = { .type = NLA_U8 },
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic const struct nla_policy ets_quanta_policy[TCA_ETS_MAX + 1] = {
708c2ecf20Sopenharmony_ci	[TCA_ETS_QUANTA_BAND] = { .type = NLA_U32 },
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct nla_policy ets_class_policy[TCA_ETS_MAX + 1] = {
748c2ecf20Sopenharmony_ci	[TCA_ETS_QUANTA_BAND] = { .type = NLA_U32 },
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int ets_quantum_parse(struct Qdisc *sch, const struct nlattr *attr,
788c2ecf20Sopenharmony_ci			     unsigned int *quantum,
798c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	*quantum = nla_get_u32(attr);
828c2ecf20Sopenharmony_ci	if (!*quantum) {
838c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ETS quantum cannot be zero");
848c2ecf20Sopenharmony_ci		return -EINVAL;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic struct ets_class *
908c2ecf20Sopenharmony_ciets_class_from_arg(struct Qdisc *sch, unsigned long arg)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return &q->classes[arg - 1];
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic u32 ets_class_id(struct Qdisc *sch, const struct ets_class *cl)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
1008c2ecf20Sopenharmony_ci	int band = cl - q->classes;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return TC_H_MAKE(sch->handle, band + 1);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void ets_offload_change(struct Qdisc *sch)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
1088c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
1098c2ecf20Sopenharmony_ci	struct tc_ets_qopt_offload qopt;
1108c2ecf20Sopenharmony_ci	unsigned int w_psum_prev = 0;
1118c2ecf20Sopenharmony_ci	unsigned int q_psum = 0;
1128c2ecf20Sopenharmony_ci	unsigned int q_sum = 0;
1138c2ecf20Sopenharmony_ci	unsigned int quantum;
1148c2ecf20Sopenharmony_ci	unsigned int w_psum;
1158c2ecf20Sopenharmony_ci	unsigned int weight;
1168c2ecf20Sopenharmony_ci	unsigned int i;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
1198c2ecf20Sopenharmony_ci		return;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	qopt.command = TC_ETS_REPLACE;
1228c2ecf20Sopenharmony_ci	qopt.handle = sch->handle;
1238c2ecf20Sopenharmony_ci	qopt.parent = sch->parent;
1248c2ecf20Sopenharmony_ci	qopt.replace_params.bands = q->nbands;
1258c2ecf20Sopenharmony_ci	qopt.replace_params.qstats = &sch->qstats;
1268c2ecf20Sopenharmony_ci	memcpy(&qopt.replace_params.priomap,
1278c2ecf20Sopenharmony_ci	       q->prio2band, sizeof(q->prio2band));
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	for (i = 0; i < q->nbands; i++)
1308c2ecf20Sopenharmony_ci		q_sum += q->classes[i].quantum;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for (i = 0; i < q->nbands; i++) {
1338c2ecf20Sopenharmony_ci		quantum = q->classes[i].quantum;
1348c2ecf20Sopenharmony_ci		q_psum += quantum;
1358c2ecf20Sopenharmony_ci		w_psum = quantum ? q_psum * 100 / q_sum : 0;
1368c2ecf20Sopenharmony_ci		weight = w_psum - w_psum_prev;
1378c2ecf20Sopenharmony_ci		w_psum_prev = w_psum;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		qopt.replace_params.quanta[i] = quantum;
1408c2ecf20Sopenharmony_ci		qopt.replace_params.weights[i] = weight;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETS, &qopt);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void ets_offload_destroy(struct Qdisc *sch)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
1498c2ecf20Sopenharmony_ci	struct tc_ets_qopt_offload qopt;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
1528c2ecf20Sopenharmony_ci		return;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	qopt.command = TC_ETS_DESTROY;
1558c2ecf20Sopenharmony_ci	qopt.handle = sch->handle;
1568c2ecf20Sopenharmony_ci	qopt.parent = sch->parent;
1578c2ecf20Sopenharmony_ci	dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETS, &qopt);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic void ets_offload_graft(struct Qdisc *sch, struct Qdisc *new,
1618c2ecf20Sopenharmony_ci			      struct Qdisc *old, unsigned long arg,
1628c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
1658c2ecf20Sopenharmony_ci	struct tc_ets_qopt_offload qopt;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	qopt.command = TC_ETS_GRAFT;
1688c2ecf20Sopenharmony_ci	qopt.handle = sch->handle;
1698c2ecf20Sopenharmony_ci	qopt.parent = sch->parent;
1708c2ecf20Sopenharmony_ci	qopt.graft_params.band = arg - 1;
1718c2ecf20Sopenharmony_ci	qopt.graft_params.child_handle = new->handle;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	qdisc_offload_graft_helper(dev, sch, new, old, TC_SETUP_QDISC_ETS,
1748c2ecf20Sopenharmony_ci				   &qopt, extack);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int ets_offload_dump(struct Qdisc *sch)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct tc_ets_qopt_offload qopt;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	qopt.command = TC_ETS_STATS;
1828c2ecf20Sopenharmony_ci	qopt.handle = sch->handle;
1838c2ecf20Sopenharmony_ci	qopt.parent = sch->parent;
1848c2ecf20Sopenharmony_ci	qopt.stats.bstats = &sch->bstats;
1858c2ecf20Sopenharmony_ci	qopt.stats.qstats = &sch->qstats;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_ETS, &qopt);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic bool ets_class_is_strict(struct ets_sched *q, const struct ets_class *cl)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	unsigned int band = cl - q->classes;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return band < q->nstrict;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int ets_class_change(struct Qdisc *sch, u32 classid, u32 parentid,
1988c2ecf20Sopenharmony_ci			    struct nlattr **tca, unsigned long *arg,
1998c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct ets_class *cl = ets_class_from_arg(sch, *arg);
2028c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
2038c2ecf20Sopenharmony_ci	struct nlattr *opt = tca[TCA_OPTIONS];
2048c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_ETS_MAX + 1];
2058c2ecf20Sopenharmony_ci	unsigned int quantum;
2068c2ecf20Sopenharmony_ci	int err;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Classes can be added and removed only through Qdisc_ops.change
2098c2ecf20Sopenharmony_ci	 * interface.
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci	if (!cl) {
2128c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Fine-grained class addition and removal is not supported");
2138c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (!opt) {
2178c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ETS options are required for this operation");
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	err = nla_parse_nested(tb, TCA_ETS_MAX, opt, ets_class_policy, extack);
2228c2ecf20Sopenharmony_ci	if (err < 0)
2238c2ecf20Sopenharmony_ci		return err;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (!tb[TCA_ETS_QUANTA_BAND])
2268c2ecf20Sopenharmony_ci		/* Nothing to configure. */
2278c2ecf20Sopenharmony_ci		return 0;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (ets_class_is_strict(q, cl)) {
2308c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Strict bands do not have a configurable quantum");
2318c2ecf20Sopenharmony_ci		return -EINVAL;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	err = ets_quantum_parse(sch, tb[TCA_ETS_QUANTA_BAND], &quantum,
2358c2ecf20Sopenharmony_ci				extack);
2368c2ecf20Sopenharmony_ci	if (err)
2378c2ecf20Sopenharmony_ci		return err;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	sch_tree_lock(sch);
2408c2ecf20Sopenharmony_ci	cl->quantum = quantum;
2418c2ecf20Sopenharmony_ci	sch_tree_unlock(sch);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	ets_offload_change(sch);
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int ets_class_graft(struct Qdisc *sch, unsigned long arg,
2488c2ecf20Sopenharmony_ci			   struct Qdisc *new, struct Qdisc **old,
2498c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct ets_class *cl = ets_class_from_arg(sch, arg);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (!new) {
2548c2ecf20Sopenharmony_ci		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
2558c2ecf20Sopenharmony_ci					ets_class_id(sch, cl), NULL);
2568c2ecf20Sopenharmony_ci		if (!new)
2578c2ecf20Sopenharmony_ci			new = &noop_qdisc;
2588c2ecf20Sopenharmony_ci		else
2598c2ecf20Sopenharmony_ci			qdisc_hash_add(new, true);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	*old = qdisc_replace(sch, new, &cl->qdisc);
2638c2ecf20Sopenharmony_ci	ets_offload_graft(sch, new, *old, arg, extack);
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct Qdisc *ets_class_leaf(struct Qdisc *sch, unsigned long arg)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct ets_class *cl = ets_class_from_arg(sch, arg);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return cl->qdisc;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic unsigned long ets_class_find(struct Qdisc *sch, u32 classid)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	unsigned long band = TC_H_MIN(classid);
2778c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (band - 1 >= q->nbands)
2808c2ecf20Sopenharmony_ci		return 0;
2818c2ecf20Sopenharmony_ci	return band;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic void ets_class_qlen_notify(struct Qdisc *sch, unsigned long arg)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct ets_class *cl = ets_class_from_arg(sch, arg);
2878c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* We get notified about zero-length child Qdiscs as well if they are
2908c2ecf20Sopenharmony_ci	 * offloaded. Those aren't on the active list though, so don't attempt
2918c2ecf20Sopenharmony_ci	 * to remove them.
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	if (!ets_class_is_strict(q, cl) && sch->q.qlen)
2948c2ecf20Sopenharmony_ci		list_del(&cl->alist);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int ets_class_dump(struct Qdisc *sch, unsigned long arg,
2988c2ecf20Sopenharmony_ci			  struct sk_buff *skb, struct tcmsg *tcm)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct ets_class *cl = ets_class_from_arg(sch, arg);
3018c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
3028c2ecf20Sopenharmony_ci	struct nlattr *nest;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	tcm->tcm_parent = TC_H_ROOT;
3058c2ecf20Sopenharmony_ci	tcm->tcm_handle = ets_class_id(sch, cl);
3068c2ecf20Sopenharmony_ci	tcm->tcm_info = cl->qdisc->handle;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
3098c2ecf20Sopenharmony_ci	if (!nest)
3108c2ecf20Sopenharmony_ci		goto nla_put_failure;
3118c2ecf20Sopenharmony_ci	if (!ets_class_is_strict(q, cl)) {
3128c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, TCA_ETS_QUANTA_BAND, cl->quantum))
3138c2ecf20Sopenharmony_ci			goto nla_put_failure;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	return nla_nest_end(skb, nest);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cinla_put_failure:
3188c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
3198c2ecf20Sopenharmony_ci	return -EMSGSIZE;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int ets_class_dump_stats(struct Qdisc *sch, unsigned long arg,
3238c2ecf20Sopenharmony_ci				struct gnet_dump *d)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct ets_class *cl = ets_class_from_arg(sch, arg);
3268c2ecf20Sopenharmony_ci	struct Qdisc *cl_q = cl->qdisc;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
3298c2ecf20Sopenharmony_ci				  d, NULL, &cl_q->bstats) < 0 ||
3308c2ecf20Sopenharmony_ci	    qdisc_qstats_copy(d, cl_q) < 0)
3318c2ecf20Sopenharmony_ci		return -1;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void ets_qdisc_walk(struct Qdisc *sch, struct qdisc_walker *arg)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
3398c2ecf20Sopenharmony_ci	int i;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (arg->stop)
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	for (i = 0; i < q->nbands; i++) {
3458c2ecf20Sopenharmony_ci		if (arg->count < arg->skip) {
3468c2ecf20Sopenharmony_ci			arg->count++;
3478c2ecf20Sopenharmony_ci			continue;
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci		if (arg->fn(sch, i + 1, arg) < 0) {
3508c2ecf20Sopenharmony_ci			arg->stop = 1;
3518c2ecf20Sopenharmony_ci			break;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		arg->count++;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic struct tcf_block *
3588c2ecf20Sopenharmony_ciets_qdisc_tcf_block(struct Qdisc *sch, unsigned long cl,
3598c2ecf20Sopenharmony_ci		    struct netlink_ext_ack *extack)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (cl) {
3648c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ETS classid must be zero");
3658c2ecf20Sopenharmony_ci		return NULL;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return q->block;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic unsigned long ets_qdisc_bind_tcf(struct Qdisc *sch, unsigned long parent,
3728c2ecf20Sopenharmony_ci					u32 classid)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	return ets_class_find(sch, classid);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void ets_qdisc_unbind_tcf(struct Qdisc *sch, unsigned long arg)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch,
3828c2ecf20Sopenharmony_ci				      int *qerr)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
3858c2ecf20Sopenharmony_ci	u32 band = skb->priority;
3868c2ecf20Sopenharmony_ci	struct tcf_result res;
3878c2ecf20Sopenharmony_ci	struct tcf_proto *fl;
3888c2ecf20Sopenharmony_ci	int err;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
3918c2ecf20Sopenharmony_ci	if (TC_H_MAJ(skb->priority) != sch->handle) {
3928c2ecf20Sopenharmony_ci		fl = rcu_dereference_bh(q->filter_list);
3938c2ecf20Sopenharmony_ci		err = tcf_classify(skb, fl, &res, false);
3948c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
3958c2ecf20Sopenharmony_ci		switch (err) {
3968c2ecf20Sopenharmony_ci		case TC_ACT_STOLEN:
3978c2ecf20Sopenharmony_ci		case TC_ACT_QUEUED:
3988c2ecf20Sopenharmony_ci		case TC_ACT_TRAP:
3998c2ecf20Sopenharmony_ci			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
4008c2ecf20Sopenharmony_ci			fallthrough;
4018c2ecf20Sopenharmony_ci		case TC_ACT_SHOT:
4028c2ecf20Sopenharmony_ci			return NULL;
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci#endif
4058c2ecf20Sopenharmony_ci		if (!fl || err < 0) {
4068c2ecf20Sopenharmony_ci			if (TC_H_MAJ(band))
4078c2ecf20Sopenharmony_ci				band = 0;
4088c2ecf20Sopenharmony_ci			return &q->classes[q->prio2band[band & TC_PRIO_MAX]];
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci		band = res.classid;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	band = TC_H_MIN(band) - 1;
4138c2ecf20Sopenharmony_ci	if (band >= q->nbands)
4148c2ecf20Sopenharmony_ci		return &q->classes[q->prio2band[0]];
4158c2ecf20Sopenharmony_ci	return &q->classes[band];
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic int ets_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
4198c2ecf20Sopenharmony_ci			     struct sk_buff **to_free)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	unsigned int len = qdisc_pkt_len(skb);
4228c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
4238c2ecf20Sopenharmony_ci	struct ets_class *cl;
4248c2ecf20Sopenharmony_ci	int err = 0;
4258c2ecf20Sopenharmony_ci	bool first;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	cl = ets_classify(skb, sch, &err);
4288c2ecf20Sopenharmony_ci	if (!cl) {
4298c2ecf20Sopenharmony_ci		if (err & __NET_XMIT_BYPASS)
4308c2ecf20Sopenharmony_ci			qdisc_qstats_drop(sch);
4318c2ecf20Sopenharmony_ci		__qdisc_drop(skb, to_free);
4328c2ecf20Sopenharmony_ci		return err;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	first = !cl->qdisc->q.qlen;
4368c2ecf20Sopenharmony_ci	err = qdisc_enqueue(skb, cl->qdisc, to_free);
4378c2ecf20Sopenharmony_ci	if (unlikely(err != NET_XMIT_SUCCESS)) {
4388c2ecf20Sopenharmony_ci		if (net_xmit_drop_count(err)) {
4398c2ecf20Sopenharmony_ci			cl->qstats.drops++;
4408c2ecf20Sopenharmony_ci			qdisc_qstats_drop(sch);
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci		return err;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (first && !ets_class_is_strict(q, cl)) {
4468c2ecf20Sopenharmony_ci		list_add_tail(&cl->alist, &q->active);
4478c2ecf20Sopenharmony_ci		cl->deficit = cl->quantum;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	sch->qstats.backlog += len;
4518c2ecf20Sopenharmony_ci	sch->q.qlen++;
4528c2ecf20Sopenharmony_ci	return err;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic struct sk_buff *
4568c2ecf20Sopenharmony_ciets_qdisc_dequeue_skb(struct Qdisc *sch, struct sk_buff *skb)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	qdisc_bstats_update(sch, skb);
4598c2ecf20Sopenharmony_ci	qdisc_qstats_backlog_dec(sch, skb);
4608c2ecf20Sopenharmony_ci	sch->q.qlen--;
4618c2ecf20Sopenharmony_ci	return skb;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic struct sk_buff *ets_qdisc_dequeue(struct Qdisc *sch)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
4678c2ecf20Sopenharmony_ci	struct ets_class *cl;
4688c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4698c2ecf20Sopenharmony_ci	unsigned int band;
4708c2ecf20Sopenharmony_ci	unsigned int len;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	while (1) {
4738c2ecf20Sopenharmony_ci		for (band = 0; band < q->nstrict; band++) {
4748c2ecf20Sopenharmony_ci			cl = &q->classes[band];
4758c2ecf20Sopenharmony_ci			skb = qdisc_dequeue_peeked(cl->qdisc);
4768c2ecf20Sopenharmony_ci			if (skb)
4778c2ecf20Sopenharmony_ci				return ets_qdisc_dequeue_skb(sch, skb);
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (list_empty(&q->active))
4818c2ecf20Sopenharmony_ci			goto out;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		cl = list_first_entry(&q->active, struct ets_class, alist);
4848c2ecf20Sopenharmony_ci		skb = cl->qdisc->ops->peek(cl->qdisc);
4858c2ecf20Sopenharmony_ci		if (!skb) {
4868c2ecf20Sopenharmony_ci			qdisc_warn_nonwc(__func__, cl->qdisc);
4878c2ecf20Sopenharmony_ci			goto out;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		len = qdisc_pkt_len(skb);
4918c2ecf20Sopenharmony_ci		if (len <= cl->deficit) {
4928c2ecf20Sopenharmony_ci			cl->deficit -= len;
4938c2ecf20Sopenharmony_ci			skb = qdisc_dequeue_peeked(cl->qdisc);
4948c2ecf20Sopenharmony_ci			if (unlikely(!skb))
4958c2ecf20Sopenharmony_ci				goto out;
4968c2ecf20Sopenharmony_ci			if (cl->qdisc->q.qlen == 0)
4978c2ecf20Sopenharmony_ci				list_del(&cl->alist);
4988c2ecf20Sopenharmony_ci			return ets_qdisc_dequeue_skb(sch, skb);
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		cl->deficit += cl->quantum;
5028c2ecf20Sopenharmony_ci		list_move_tail(&cl->alist, &q->active);
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ciout:
5058c2ecf20Sopenharmony_ci	return NULL;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic int ets_qdisc_priomap_parse(struct nlattr *priomap_attr,
5098c2ecf20Sopenharmony_ci				   unsigned int nbands, u8 *priomap,
5108c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	const struct nlattr *attr;
5138c2ecf20Sopenharmony_ci	int prio = 0;
5148c2ecf20Sopenharmony_ci	u8 band;
5158c2ecf20Sopenharmony_ci	int rem;
5168c2ecf20Sopenharmony_ci	int err;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	err = __nla_validate_nested(priomap_attr, TCA_ETS_MAX,
5198c2ecf20Sopenharmony_ci				    ets_priomap_policy, NL_VALIDATE_STRICT,
5208c2ecf20Sopenharmony_ci				    extack);
5218c2ecf20Sopenharmony_ci	if (err)
5228c2ecf20Sopenharmony_ci		return err;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	nla_for_each_nested(attr, priomap_attr, rem) {
5258c2ecf20Sopenharmony_ci		switch (nla_type(attr)) {
5268c2ecf20Sopenharmony_ci		case TCA_ETS_PRIOMAP_BAND:
5278c2ecf20Sopenharmony_ci			if (prio > TC_PRIO_MAX) {
5288c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Too many priorities in ETS priomap");
5298c2ecf20Sopenharmony_ci				return -EINVAL;
5308c2ecf20Sopenharmony_ci			}
5318c2ecf20Sopenharmony_ci			band = nla_get_u8(attr);
5328c2ecf20Sopenharmony_ci			if (band >= nbands) {
5338c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Invalid band number in ETS priomap");
5348c2ecf20Sopenharmony_ci				return -EINVAL;
5358c2ecf20Sopenharmony_ci			}
5368c2ecf20Sopenharmony_ci			priomap[prio++] = band;
5378c2ecf20Sopenharmony_ci			break;
5388c2ecf20Sopenharmony_ci		default:
5398c2ecf20Sopenharmony_ci			WARN_ON_ONCE(1); /* Validate should have caught this. */
5408c2ecf20Sopenharmony_ci			return -EINVAL;
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return 0;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int ets_qdisc_quanta_parse(struct Qdisc *sch, struct nlattr *quanta_attr,
5488c2ecf20Sopenharmony_ci				  unsigned int nbands, unsigned int nstrict,
5498c2ecf20Sopenharmony_ci				  unsigned int *quanta,
5508c2ecf20Sopenharmony_ci				  struct netlink_ext_ack *extack)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	const struct nlattr *attr;
5538c2ecf20Sopenharmony_ci	int band = nstrict;
5548c2ecf20Sopenharmony_ci	int rem;
5558c2ecf20Sopenharmony_ci	int err;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	err = __nla_validate_nested(quanta_attr, TCA_ETS_MAX,
5588c2ecf20Sopenharmony_ci				    ets_quanta_policy, NL_VALIDATE_STRICT,
5598c2ecf20Sopenharmony_ci				    extack);
5608c2ecf20Sopenharmony_ci	if (err < 0)
5618c2ecf20Sopenharmony_ci		return err;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	nla_for_each_nested(attr, quanta_attr, rem) {
5648c2ecf20Sopenharmony_ci		switch (nla_type(attr)) {
5658c2ecf20Sopenharmony_ci		case TCA_ETS_QUANTA_BAND:
5668c2ecf20Sopenharmony_ci			if (band >= nbands) {
5678c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "ETS quanta has more values than bands");
5688c2ecf20Sopenharmony_ci				return -EINVAL;
5698c2ecf20Sopenharmony_ci			}
5708c2ecf20Sopenharmony_ci			err = ets_quantum_parse(sch, attr, &quanta[band++],
5718c2ecf20Sopenharmony_ci						extack);
5728c2ecf20Sopenharmony_ci			if (err)
5738c2ecf20Sopenharmony_ci				return err;
5748c2ecf20Sopenharmony_ci			break;
5758c2ecf20Sopenharmony_ci		default:
5768c2ecf20Sopenharmony_ci			WARN_ON_ONCE(1); /* Validate should have caught this. */
5778c2ecf20Sopenharmony_ci			return -EINVAL;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	return 0;
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,
5858c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	unsigned int quanta[TCQ_ETS_MAX_BANDS] = {0};
5888c2ecf20Sopenharmony_ci	struct Qdisc *queues[TCQ_ETS_MAX_BANDS];
5898c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
5908c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_ETS_MAX + 1];
5918c2ecf20Sopenharmony_ci	unsigned int oldbands = q->nbands;
5928c2ecf20Sopenharmony_ci	u8 priomap[TC_PRIO_MAX + 1];
5938c2ecf20Sopenharmony_ci	unsigned int nstrict = 0;
5948c2ecf20Sopenharmony_ci	unsigned int nbands;
5958c2ecf20Sopenharmony_ci	unsigned int i;
5968c2ecf20Sopenharmony_ci	int err;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (!opt) {
5998c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ETS options are required for this operation");
6008c2ecf20Sopenharmony_ci		return -EINVAL;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	err = nla_parse_nested(tb, TCA_ETS_MAX, opt, ets_policy, extack);
6048c2ecf20Sopenharmony_ci	if (err < 0)
6058c2ecf20Sopenharmony_ci		return err;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (!tb[TCA_ETS_NBANDS]) {
6088c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Number of bands is a required argument");
6098c2ecf20Sopenharmony_ci		return -EINVAL;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci	nbands = nla_get_u8(tb[TCA_ETS_NBANDS]);
6128c2ecf20Sopenharmony_ci	if (nbands < 1 || nbands > TCQ_ETS_MAX_BANDS) {
6138c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid number of bands");
6148c2ecf20Sopenharmony_ci		return -EINVAL;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci	/* Unless overridden, traffic goes to the last band. */
6178c2ecf20Sopenharmony_ci	memset(priomap, nbands - 1, sizeof(priomap));
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (tb[TCA_ETS_NSTRICT]) {
6208c2ecf20Sopenharmony_ci		nstrict = nla_get_u8(tb[TCA_ETS_NSTRICT]);
6218c2ecf20Sopenharmony_ci		if (nstrict > nbands) {
6228c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid number of strict bands");
6238c2ecf20Sopenharmony_ci			return -EINVAL;
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (tb[TCA_ETS_PRIOMAP]) {
6288c2ecf20Sopenharmony_ci		err = ets_qdisc_priomap_parse(tb[TCA_ETS_PRIOMAP],
6298c2ecf20Sopenharmony_ci					      nbands, priomap, extack);
6308c2ecf20Sopenharmony_ci		if (err)
6318c2ecf20Sopenharmony_ci			return err;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (tb[TCA_ETS_QUANTA]) {
6358c2ecf20Sopenharmony_ci		err = ets_qdisc_quanta_parse(sch, tb[TCA_ETS_QUANTA],
6368c2ecf20Sopenharmony_ci					     nbands, nstrict, quanta, extack);
6378c2ecf20Sopenharmony_ci		if (err)
6388c2ecf20Sopenharmony_ci			return err;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	/* If there are more bands than strict + quanta provided, the remaining
6418c2ecf20Sopenharmony_ci	 * ones are ETS with quantum of MTU. Initialize the missing values here.
6428c2ecf20Sopenharmony_ci	 */
6438c2ecf20Sopenharmony_ci	for (i = nstrict; i < nbands; i++) {
6448c2ecf20Sopenharmony_ci		if (!quanta[i])
6458c2ecf20Sopenharmony_ci			quanta[i] = psched_mtu(qdisc_dev(sch));
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/* Before commit, make sure we can allocate all new qdiscs */
6498c2ecf20Sopenharmony_ci	for (i = oldbands; i < nbands; i++) {
6508c2ecf20Sopenharmony_ci		queues[i] = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
6518c2ecf20Sopenharmony_ci					      ets_class_id(sch, &q->classes[i]),
6528c2ecf20Sopenharmony_ci					      extack);
6538c2ecf20Sopenharmony_ci		if (!queues[i]) {
6548c2ecf20Sopenharmony_ci			while (i > oldbands)
6558c2ecf20Sopenharmony_ci				qdisc_put(queues[--i]);
6568c2ecf20Sopenharmony_ci			return -ENOMEM;
6578c2ecf20Sopenharmony_ci		}
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	sch_tree_lock(sch);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	q->nbands = nbands;
6638c2ecf20Sopenharmony_ci	for (i = nstrict; i < q->nstrict; i++) {
6648c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&q->classes[i].alist);
6658c2ecf20Sopenharmony_ci		if (q->classes[i].qdisc->q.qlen) {
6668c2ecf20Sopenharmony_ci			list_add_tail(&q->classes[i].alist, &q->active);
6678c2ecf20Sopenharmony_ci			q->classes[i].deficit = quanta[i];
6688c2ecf20Sopenharmony_ci		}
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci	for (i = q->nbands; i < oldbands; i++) {
6718c2ecf20Sopenharmony_ci		if (i >= q->nstrict && q->classes[i].qdisc->q.qlen)
6728c2ecf20Sopenharmony_ci			list_del(&q->classes[i].alist);
6738c2ecf20Sopenharmony_ci		qdisc_tree_flush_backlog(q->classes[i].qdisc);
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci	q->nstrict = nstrict;
6768c2ecf20Sopenharmony_ci	memcpy(q->prio2band, priomap, sizeof(priomap));
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	for (i = 0; i < q->nbands; i++)
6798c2ecf20Sopenharmony_ci		q->classes[i].quantum = quanta[i];
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	for (i = oldbands; i < q->nbands; i++) {
6828c2ecf20Sopenharmony_ci		q->classes[i].qdisc = queues[i];
6838c2ecf20Sopenharmony_ci		if (q->classes[i].qdisc != &noop_qdisc)
6848c2ecf20Sopenharmony_ci			qdisc_hash_add(q->classes[i].qdisc, true);
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	sch_tree_unlock(sch);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	ets_offload_change(sch);
6908c2ecf20Sopenharmony_ci	for (i = q->nbands; i < oldbands; i++) {
6918c2ecf20Sopenharmony_ci		qdisc_put(q->classes[i].qdisc);
6928c2ecf20Sopenharmony_ci		memset(&q->classes[i], 0, sizeof(q->classes[i]));
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci	return 0;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic int ets_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
6988c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
7018c2ecf20Sopenharmony_ci	int err;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (!opt)
7048c2ecf20Sopenharmony_ci		return -EINVAL;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
7078c2ecf20Sopenharmony_ci	if (err)
7088c2ecf20Sopenharmony_ci		return err;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&q->active);
7118c2ecf20Sopenharmony_ci	return ets_qdisc_change(sch, opt, extack);
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic void ets_qdisc_reset(struct Qdisc *sch)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
7178c2ecf20Sopenharmony_ci	int band;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	for (band = q->nstrict; band < q->nbands; band++) {
7208c2ecf20Sopenharmony_ci		if (q->classes[band].qdisc->q.qlen)
7218c2ecf20Sopenharmony_ci			list_del(&q->classes[band].alist);
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci	for (band = 0; band < q->nbands; band++)
7248c2ecf20Sopenharmony_ci		qdisc_reset(q->classes[band].qdisc);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic void ets_qdisc_destroy(struct Qdisc *sch)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
7308c2ecf20Sopenharmony_ci	int band;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	ets_offload_destroy(sch);
7338c2ecf20Sopenharmony_ci	tcf_block_put(q->block);
7348c2ecf20Sopenharmony_ci	for (band = 0; band < q->nbands; band++)
7358c2ecf20Sopenharmony_ci		qdisc_put(q->classes[band].qdisc);
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic int ets_qdisc_dump(struct Qdisc *sch, struct sk_buff *skb)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct ets_sched *q = qdisc_priv(sch);
7418c2ecf20Sopenharmony_ci	struct nlattr *opts;
7428c2ecf20Sopenharmony_ci	struct nlattr *nest;
7438c2ecf20Sopenharmony_ci	int band;
7448c2ecf20Sopenharmony_ci	int prio;
7458c2ecf20Sopenharmony_ci	int err;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	err = ets_offload_dump(sch);
7488c2ecf20Sopenharmony_ci	if (err)
7498c2ecf20Sopenharmony_ci		return err;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
7528c2ecf20Sopenharmony_ci	if (!opts)
7538c2ecf20Sopenharmony_ci		goto nla_err;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, TCA_ETS_NBANDS, q->nbands))
7568c2ecf20Sopenharmony_ci		goto nla_err;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (q->nstrict &&
7598c2ecf20Sopenharmony_ci	    nla_put_u8(skb, TCA_ETS_NSTRICT, q->nstrict))
7608c2ecf20Sopenharmony_ci		goto nla_err;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	if (q->nbands > q->nstrict) {
7638c2ecf20Sopenharmony_ci		nest = nla_nest_start(skb, TCA_ETS_QUANTA);
7648c2ecf20Sopenharmony_ci		if (!nest)
7658c2ecf20Sopenharmony_ci			goto nla_err;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		for (band = q->nstrict; band < q->nbands; band++) {
7688c2ecf20Sopenharmony_ci			if (nla_put_u32(skb, TCA_ETS_QUANTA_BAND,
7698c2ecf20Sopenharmony_ci					q->classes[band].quantum))
7708c2ecf20Sopenharmony_ci				goto nla_err;
7718c2ecf20Sopenharmony_ci		}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		nla_nest_end(skb, nest);
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	nest = nla_nest_start(skb, TCA_ETS_PRIOMAP);
7778c2ecf20Sopenharmony_ci	if (!nest)
7788c2ecf20Sopenharmony_ci		goto nla_err;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	for (prio = 0; prio <= TC_PRIO_MAX; prio++) {
7818c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, TCA_ETS_PRIOMAP_BAND, q->prio2band[prio]))
7828c2ecf20Sopenharmony_ci			goto nla_err;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	return nla_nest_end(skb, opts);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cinla_err:
7908c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, opts);
7918c2ecf20Sopenharmony_ci	return -EMSGSIZE;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops ets_class_ops = {
7958c2ecf20Sopenharmony_ci	.change		= ets_class_change,
7968c2ecf20Sopenharmony_ci	.graft		= ets_class_graft,
7978c2ecf20Sopenharmony_ci	.leaf		= ets_class_leaf,
7988c2ecf20Sopenharmony_ci	.find		= ets_class_find,
7998c2ecf20Sopenharmony_ci	.qlen_notify	= ets_class_qlen_notify,
8008c2ecf20Sopenharmony_ci	.dump		= ets_class_dump,
8018c2ecf20Sopenharmony_ci	.dump_stats	= ets_class_dump_stats,
8028c2ecf20Sopenharmony_ci	.walk		= ets_qdisc_walk,
8038c2ecf20Sopenharmony_ci	.tcf_block	= ets_qdisc_tcf_block,
8048c2ecf20Sopenharmony_ci	.bind_tcf	= ets_qdisc_bind_tcf,
8058c2ecf20Sopenharmony_ci	.unbind_tcf	= ets_qdisc_unbind_tcf,
8068c2ecf20Sopenharmony_ci};
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic struct Qdisc_ops ets_qdisc_ops __read_mostly = {
8098c2ecf20Sopenharmony_ci	.cl_ops		= &ets_class_ops,
8108c2ecf20Sopenharmony_ci	.id		= "ets",
8118c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct ets_sched),
8128c2ecf20Sopenharmony_ci	.enqueue	= ets_qdisc_enqueue,
8138c2ecf20Sopenharmony_ci	.dequeue	= ets_qdisc_dequeue,
8148c2ecf20Sopenharmony_ci	.peek		= qdisc_peek_dequeued,
8158c2ecf20Sopenharmony_ci	.change		= ets_qdisc_change,
8168c2ecf20Sopenharmony_ci	.init		= ets_qdisc_init,
8178c2ecf20Sopenharmony_ci	.reset		= ets_qdisc_reset,
8188c2ecf20Sopenharmony_ci	.destroy	= ets_qdisc_destroy,
8198c2ecf20Sopenharmony_ci	.dump		= ets_qdisc_dump,
8208c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
8218c2ecf20Sopenharmony_ci};
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic int __init ets_init(void)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	return register_qdisc(&ets_qdisc_ops);
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic void __exit ets_exit(void)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	unregister_qdisc(&ets_qdisc_ops);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cimodule_init(ets_init);
8348c2ecf20Sopenharmony_cimodule_exit(ets_exit);
8358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
836