xref: /kernel/linux/linux-5.10/net/sched/sch_mqprio.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/sch_mqprio.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010 John Fastabend <john.r.fastabend@intel.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/string.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <net/netlink.h>
168c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
178c2ecf20Sopenharmony_ci#include <net/sch_generic.h>
188c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct mqprio_sched {
218c2ecf20Sopenharmony_ci	struct Qdisc		**qdiscs;
228c2ecf20Sopenharmony_ci	u16 mode;
238c2ecf20Sopenharmony_ci	u16 shaper;
248c2ecf20Sopenharmony_ci	int hw_offload;
258c2ecf20Sopenharmony_ci	u32 flags;
268c2ecf20Sopenharmony_ci	u64 min_rate[TC_QOPT_MAX_QUEUE];
278c2ecf20Sopenharmony_ci	u64 max_rate[TC_QOPT_MAX_QUEUE];
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void mqprio_destroy(struct Qdisc *sch)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
338c2ecf20Sopenharmony_ci	struct mqprio_sched *priv = qdisc_priv(sch);
348c2ecf20Sopenharmony_ci	unsigned int ntx;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (priv->qdiscs) {
378c2ecf20Sopenharmony_ci		for (ntx = 0;
388c2ecf20Sopenharmony_ci		     ntx < dev->num_tx_queues && priv->qdiscs[ntx];
398c2ecf20Sopenharmony_ci		     ntx++)
408c2ecf20Sopenharmony_ci			qdisc_put(priv->qdiscs[ntx]);
418c2ecf20Sopenharmony_ci		kfree(priv->qdiscs);
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) {
458c2ecf20Sopenharmony_ci		struct tc_mqprio_qopt_offload mqprio = { { 0 } };
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci		switch (priv->mode) {
488c2ecf20Sopenharmony_ci		case TC_MQPRIO_MODE_DCB:
498c2ecf20Sopenharmony_ci		case TC_MQPRIO_MODE_CHANNEL:
508c2ecf20Sopenharmony_ci			dev->netdev_ops->ndo_setup_tc(dev,
518c2ecf20Sopenharmony_ci						      TC_SETUP_QDISC_MQPRIO,
528c2ecf20Sopenharmony_ci						      &mqprio);
538c2ecf20Sopenharmony_ci			break;
548c2ecf20Sopenharmony_ci		default:
558c2ecf20Sopenharmony_ci			return;
568c2ecf20Sopenharmony_ci		}
578c2ecf20Sopenharmony_ci	} else {
588c2ecf20Sopenharmony_ci		netdev_set_num_tc(dev, 0);
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int i, j;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* Verify num_tc is not out of max range */
678c2ecf20Sopenharmony_ci	if (qopt->num_tc > TC_MAX_QUEUE)
688c2ecf20Sopenharmony_ci		return -EINVAL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Verify priority mapping uses valid tcs */
718c2ecf20Sopenharmony_ci	for (i = 0; i < TC_BITMASK + 1; i++) {
728c2ecf20Sopenharmony_ci		if (qopt->prio_tc_map[i] >= qopt->num_tc)
738c2ecf20Sopenharmony_ci			return -EINVAL;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Limit qopt->hw to maximum supported offload value.  Drivers have
778c2ecf20Sopenharmony_ci	 * the option of overriding this later if they don't support the a
788c2ecf20Sopenharmony_ci	 * given offload type.
798c2ecf20Sopenharmony_ci	 */
808c2ecf20Sopenharmony_ci	if (qopt->hw > TC_MQPRIO_HW_OFFLOAD_MAX)
818c2ecf20Sopenharmony_ci		qopt->hw = TC_MQPRIO_HW_OFFLOAD_MAX;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* If hardware offload is requested we will leave it to the device
848c2ecf20Sopenharmony_ci	 * to either populate the queue counts itself or to validate the
858c2ecf20Sopenharmony_ci	 * provided queue counts.  If ndo_setup_tc is not present then
868c2ecf20Sopenharmony_ci	 * hardware doesn't support offload and we should return an error.
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	if (qopt->hw)
898c2ecf20Sopenharmony_ci		return dev->netdev_ops->ndo_setup_tc ? 0 : -EINVAL;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	for (i = 0; i < qopt->num_tc; i++) {
928c2ecf20Sopenharmony_ci		unsigned int last = qopt->offset[i] + qopt->count[i];
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		/* Verify the queue count is in tx range being equal to the
958c2ecf20Sopenharmony_ci		 * real_num_tx_queues indicates the last queue is in use.
968c2ecf20Sopenharmony_ci		 */
978c2ecf20Sopenharmony_ci		if (qopt->offset[i] >= dev->real_num_tx_queues ||
988c2ecf20Sopenharmony_ci		    !qopt->count[i] ||
998c2ecf20Sopenharmony_ci		    last > dev->real_num_tx_queues)
1008c2ecf20Sopenharmony_ci			return -EINVAL;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		/* Verify that the offset and counts do not overlap */
1038c2ecf20Sopenharmony_ci		for (j = i + 1; j < qopt->num_tc; j++) {
1048c2ecf20Sopenharmony_ci			if (last > qopt->offset[j])
1058c2ecf20Sopenharmony_ci				return -EINVAL;
1068c2ecf20Sopenharmony_ci		}
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic const struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = {
1138c2ecf20Sopenharmony_ci	[TCA_MQPRIO_MODE]	= { .len = sizeof(u16) },
1148c2ecf20Sopenharmony_ci	[TCA_MQPRIO_SHAPER]	= { .len = sizeof(u16) },
1158c2ecf20Sopenharmony_ci	[TCA_MQPRIO_MIN_RATE64]	= { .type = NLA_NESTED },
1168c2ecf20Sopenharmony_ci	[TCA_MQPRIO_MAX_RATE64]	= { .type = NLA_NESTED },
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
1208c2ecf20Sopenharmony_ci		      const struct nla_policy *policy, int len)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	int nested_len = nla_len(nla) - NLA_ALIGN(len);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (nested_len >= nla_attr_size(0))
1258c2ecf20Sopenharmony_ci		return nla_parse_deprecated(tb, maxtype,
1268c2ecf20Sopenharmony_ci					    nla_data(nla) + NLA_ALIGN(len),
1278c2ecf20Sopenharmony_ci					    nested_len, policy, NULL);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int mqprio_parse_nlattr(struct Qdisc *sch, struct tc_mqprio_qopt *qopt,
1348c2ecf20Sopenharmony_ci			       struct nlattr *opt,
1358c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct mqprio_sched *priv = qdisc_priv(sch);
1388c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_MQPRIO_MAX + 1];
1398c2ecf20Sopenharmony_ci	struct nlattr *attr;
1408c2ecf20Sopenharmony_ci	int i, rem, err;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	err = parse_attr(tb, TCA_MQPRIO_MAX, opt, mqprio_policy,
1438c2ecf20Sopenharmony_ci			 sizeof(*qopt));
1448c2ecf20Sopenharmony_ci	if (err < 0)
1458c2ecf20Sopenharmony_ci		return err;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!qopt->hw) {
1488c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
1498c2ecf20Sopenharmony_ci			       "mqprio TCA_OPTIONS can only contain netlink attributes in hardware mode");
1508c2ecf20Sopenharmony_ci		return -EINVAL;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (tb[TCA_MQPRIO_MODE]) {
1548c2ecf20Sopenharmony_ci		priv->flags |= TC_MQPRIO_F_MODE;
1558c2ecf20Sopenharmony_ci		priv->mode = *(u16 *)nla_data(tb[TCA_MQPRIO_MODE]);
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (tb[TCA_MQPRIO_SHAPER]) {
1598c2ecf20Sopenharmony_ci		priv->flags |= TC_MQPRIO_F_SHAPER;
1608c2ecf20Sopenharmony_ci		priv->shaper = *(u16 *)nla_data(tb[TCA_MQPRIO_SHAPER]);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (tb[TCA_MQPRIO_MIN_RATE64]) {
1648c2ecf20Sopenharmony_ci		if (priv->shaper != TC_MQPRIO_SHAPER_BW_RATE) {
1658c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, tb[TCA_MQPRIO_MIN_RATE64],
1668c2ecf20Sopenharmony_ci					    "min_rate accepted only when shaper is in bw_rlimit mode");
1678c2ecf20Sopenharmony_ci			return -EINVAL;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci		i = 0;
1708c2ecf20Sopenharmony_ci		nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64],
1718c2ecf20Sopenharmony_ci				    rem) {
1728c2ecf20Sopenharmony_ci			if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64) {
1738c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_ATTR(extack, attr,
1748c2ecf20Sopenharmony_ci						    "Attribute type expected to be TCA_MQPRIO_MIN_RATE64");
1758c2ecf20Sopenharmony_ci				return -EINVAL;
1768c2ecf20Sopenharmony_ci			}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			if (nla_len(attr) != sizeof(u64)) {
1798c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_ATTR(extack, attr,
1808c2ecf20Sopenharmony_ci						    "Attribute TCA_MQPRIO_MIN_RATE64 expected to have 8 bytes length");
1818c2ecf20Sopenharmony_ci				return -EINVAL;
1828c2ecf20Sopenharmony_ci			}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci			if (i >= qopt->num_tc)
1858c2ecf20Sopenharmony_ci				break;
1868c2ecf20Sopenharmony_ci			priv->min_rate[i] = *(u64 *)nla_data(attr);
1878c2ecf20Sopenharmony_ci			i++;
1888c2ecf20Sopenharmony_ci		}
1898c2ecf20Sopenharmony_ci		priv->flags |= TC_MQPRIO_F_MIN_RATE;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (tb[TCA_MQPRIO_MAX_RATE64]) {
1938c2ecf20Sopenharmony_ci		if (priv->shaper != TC_MQPRIO_SHAPER_BW_RATE) {
1948c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, tb[TCA_MQPRIO_MAX_RATE64],
1958c2ecf20Sopenharmony_ci					    "max_rate accepted only when shaper is in bw_rlimit mode");
1968c2ecf20Sopenharmony_ci			return -EINVAL;
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci		i = 0;
1998c2ecf20Sopenharmony_ci		nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64],
2008c2ecf20Sopenharmony_ci				    rem) {
2018c2ecf20Sopenharmony_ci			if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64) {
2028c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_ATTR(extack, attr,
2038c2ecf20Sopenharmony_ci						    "Attribute type expected to be TCA_MQPRIO_MAX_RATE64");
2048c2ecf20Sopenharmony_ci				return -EINVAL;
2058c2ecf20Sopenharmony_ci			}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci			if (nla_len(attr) != sizeof(u64)) {
2088c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_ATTR(extack, attr,
2098c2ecf20Sopenharmony_ci						    "Attribute TCA_MQPRIO_MAX_RATE64 expected to have 8 bytes length");
2108c2ecf20Sopenharmony_ci				return -EINVAL;
2118c2ecf20Sopenharmony_ci			}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			if (i >= qopt->num_tc)
2148c2ecf20Sopenharmony_ci				break;
2158c2ecf20Sopenharmony_ci			priv->max_rate[i] = *(u64 *)nla_data(attr);
2168c2ecf20Sopenharmony_ci			i++;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci		priv->flags |= TC_MQPRIO_F_MAX_RATE;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int mqprio_init(struct Qdisc *sch, struct nlattr *opt,
2258c2ecf20Sopenharmony_ci		       struct netlink_ext_ack *extack)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
2288c2ecf20Sopenharmony_ci	struct mqprio_sched *priv = qdisc_priv(sch);
2298c2ecf20Sopenharmony_ci	struct netdev_queue *dev_queue;
2308c2ecf20Sopenharmony_ci	struct Qdisc *qdisc;
2318c2ecf20Sopenharmony_ci	int i, err = -EOPNOTSUPP;
2328c2ecf20Sopenharmony_ci	struct tc_mqprio_qopt *qopt = NULL;
2338c2ecf20Sopenharmony_ci	int len;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE);
2368c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (sch->parent != TC_H_ROOT)
2398c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (!netif_is_multiqueue(dev))
2428c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* make certain can allocate enough classids to handle queues */
2458c2ecf20Sopenharmony_ci	if (dev->num_tx_queues >= TC_H_MIN_PRIORITY)
2468c2ecf20Sopenharmony_ci		return -ENOMEM;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!opt || nla_len(opt) < sizeof(*qopt))
2498c2ecf20Sopenharmony_ci		return -EINVAL;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	qopt = nla_data(opt);
2528c2ecf20Sopenharmony_ci	if (mqprio_parse_opt(dev, qopt))
2538c2ecf20Sopenharmony_ci		return -EINVAL;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	len = nla_len(opt) - NLA_ALIGN(sizeof(*qopt));
2568c2ecf20Sopenharmony_ci	if (len > 0) {
2578c2ecf20Sopenharmony_ci		err = mqprio_parse_nlattr(sch, qopt, opt, extack);
2588c2ecf20Sopenharmony_ci		if (err)
2598c2ecf20Sopenharmony_ci			return err;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* pre-allocate qdisc, attachment can't fail */
2638c2ecf20Sopenharmony_ci	priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]),
2648c2ecf20Sopenharmony_ci			       GFP_KERNEL);
2658c2ecf20Sopenharmony_ci	if (!priv->qdiscs)
2668c2ecf20Sopenharmony_ci		return -ENOMEM;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	for (i = 0; i < dev->num_tx_queues; i++) {
2698c2ecf20Sopenharmony_ci		dev_queue = netdev_get_tx_queue(dev, i);
2708c2ecf20Sopenharmony_ci		qdisc = qdisc_create_dflt(dev_queue,
2718c2ecf20Sopenharmony_ci					  get_default_qdisc_ops(dev, i),
2728c2ecf20Sopenharmony_ci					  TC_H_MAKE(TC_H_MAJ(sch->handle),
2738c2ecf20Sopenharmony_ci						    TC_H_MIN(i + 1)), extack);
2748c2ecf20Sopenharmony_ci		if (!qdisc)
2758c2ecf20Sopenharmony_ci			return -ENOMEM;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		priv->qdiscs[i] = qdisc;
2788c2ecf20Sopenharmony_ci		qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* If the mqprio options indicate that hardware should own
2828c2ecf20Sopenharmony_ci	 * the queue mapping then run ndo_setup_tc otherwise use the
2838c2ecf20Sopenharmony_ci	 * supplied and verified mapping
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	if (qopt->hw) {
2868c2ecf20Sopenharmony_ci		struct tc_mqprio_qopt_offload mqprio = {.qopt = *qopt};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		switch (priv->mode) {
2898c2ecf20Sopenharmony_ci		case TC_MQPRIO_MODE_DCB:
2908c2ecf20Sopenharmony_ci			if (priv->shaper != TC_MQPRIO_SHAPER_DCB)
2918c2ecf20Sopenharmony_ci				return -EINVAL;
2928c2ecf20Sopenharmony_ci			break;
2938c2ecf20Sopenharmony_ci		case TC_MQPRIO_MODE_CHANNEL:
2948c2ecf20Sopenharmony_ci			mqprio.flags = priv->flags;
2958c2ecf20Sopenharmony_ci			if (priv->flags & TC_MQPRIO_F_MODE)
2968c2ecf20Sopenharmony_ci				mqprio.mode = priv->mode;
2978c2ecf20Sopenharmony_ci			if (priv->flags & TC_MQPRIO_F_SHAPER)
2988c2ecf20Sopenharmony_ci				mqprio.shaper = priv->shaper;
2998c2ecf20Sopenharmony_ci			if (priv->flags & TC_MQPRIO_F_MIN_RATE)
3008c2ecf20Sopenharmony_ci				for (i = 0; i < mqprio.qopt.num_tc; i++)
3018c2ecf20Sopenharmony_ci					mqprio.min_rate[i] = priv->min_rate[i];
3028c2ecf20Sopenharmony_ci			if (priv->flags & TC_MQPRIO_F_MAX_RATE)
3038c2ecf20Sopenharmony_ci				for (i = 0; i < mqprio.qopt.num_tc; i++)
3048c2ecf20Sopenharmony_ci					mqprio.max_rate[i] = priv->max_rate[i];
3058c2ecf20Sopenharmony_ci			break;
3068c2ecf20Sopenharmony_ci		default:
3078c2ecf20Sopenharmony_ci			return -EINVAL;
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci		err = dev->netdev_ops->ndo_setup_tc(dev,
3108c2ecf20Sopenharmony_ci						    TC_SETUP_QDISC_MQPRIO,
3118c2ecf20Sopenharmony_ci						    &mqprio);
3128c2ecf20Sopenharmony_ci		if (err)
3138c2ecf20Sopenharmony_ci			return err;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		priv->hw_offload = mqprio.qopt.hw;
3168c2ecf20Sopenharmony_ci	} else {
3178c2ecf20Sopenharmony_ci		netdev_set_num_tc(dev, qopt->num_tc);
3188c2ecf20Sopenharmony_ci		for (i = 0; i < qopt->num_tc; i++)
3198c2ecf20Sopenharmony_ci			netdev_set_tc_queue(dev, i,
3208c2ecf20Sopenharmony_ci					    qopt->count[i], qopt->offset[i]);
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* Always use supplied priority mappings */
3248c2ecf20Sopenharmony_ci	for (i = 0; i < TC_BITMASK + 1; i++)
3258c2ecf20Sopenharmony_ci		netdev_set_prio_tc_map(dev, i, qopt->prio_tc_map[i]);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	sch->flags |= TCQ_F_MQROOT;
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void mqprio_attach(struct Qdisc *sch)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
3348c2ecf20Sopenharmony_ci	struct mqprio_sched *priv = qdisc_priv(sch);
3358c2ecf20Sopenharmony_ci	struct Qdisc *qdisc, *old;
3368c2ecf20Sopenharmony_ci	unsigned int ntx;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* Attach underlying qdisc */
3398c2ecf20Sopenharmony_ci	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
3408c2ecf20Sopenharmony_ci		qdisc = priv->qdiscs[ntx];
3418c2ecf20Sopenharmony_ci		old = dev_graft_qdisc(qdisc->dev_queue, qdisc);
3428c2ecf20Sopenharmony_ci		if (old)
3438c2ecf20Sopenharmony_ci			qdisc_put(old);
3448c2ecf20Sopenharmony_ci		if (ntx < dev->real_num_tx_queues)
3458c2ecf20Sopenharmony_ci			qdisc_hash_add(qdisc, false);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	kfree(priv->qdiscs);
3488c2ecf20Sopenharmony_ci	priv->qdiscs = NULL;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic void mqprio_change_real_num_tx(struct Qdisc *sch,
3528c2ecf20Sopenharmony_ci				      unsigned int new_real_tx)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
3558c2ecf20Sopenharmony_ci	struct Qdisc *qdisc;
3568c2ecf20Sopenharmony_ci	unsigned int i;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	for (i = new_real_tx; i < dev->real_num_tx_queues; i++) {
3598c2ecf20Sopenharmony_ci		qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping;
3608c2ecf20Sopenharmony_ci		/* Only update the default qdiscs we created,
3618c2ecf20Sopenharmony_ci		 * qdiscs with handles are always hashed.
3628c2ecf20Sopenharmony_ci		 */
3638c2ecf20Sopenharmony_ci		if (qdisc != &noop_qdisc && !qdisc->handle)
3648c2ecf20Sopenharmony_ci			qdisc_hash_del(qdisc);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	for (i = dev->real_num_tx_queues; i < new_real_tx; i++) {
3678c2ecf20Sopenharmony_ci		qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping;
3688c2ecf20Sopenharmony_ci		if (qdisc != &noop_qdisc && !qdisc->handle)
3698c2ecf20Sopenharmony_ci			qdisc_hash_add(qdisc, false);
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic struct netdev_queue *mqprio_queue_get(struct Qdisc *sch,
3748c2ecf20Sopenharmony_ci					     unsigned long cl)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
3778c2ecf20Sopenharmony_ci	unsigned long ntx = cl - 1;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (ntx >= dev->num_tx_queues)
3808c2ecf20Sopenharmony_ci		return NULL;
3818c2ecf20Sopenharmony_ci	return netdev_get_tx_queue(dev, ntx);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
3858c2ecf20Sopenharmony_ci			struct Qdisc **old, struct netlink_ext_ack *extack)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
3888c2ecf20Sopenharmony_ci	struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (!dev_queue)
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (dev->flags & IFF_UP)
3948c2ecf20Sopenharmony_ci		dev_deactivate(dev);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	*old = dev_graft_qdisc(dev_queue, new);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (new)
3998c2ecf20Sopenharmony_ci		new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (dev->flags & IFF_UP)
4028c2ecf20Sopenharmony_ci		dev_activate(dev);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return 0;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int dump_rates(struct mqprio_sched *priv,
4088c2ecf20Sopenharmony_ci		      struct tc_mqprio_qopt *opt, struct sk_buff *skb)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct nlattr *nest;
4118c2ecf20Sopenharmony_ci	int i;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (priv->flags & TC_MQPRIO_F_MIN_RATE) {
4148c2ecf20Sopenharmony_ci		nest = nla_nest_start_noflag(skb, TCA_MQPRIO_MIN_RATE64);
4158c2ecf20Sopenharmony_ci		if (!nest)
4168c2ecf20Sopenharmony_ci			goto nla_put_failure;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		for (i = 0; i < opt->num_tc; i++) {
4198c2ecf20Sopenharmony_ci			if (nla_put(skb, TCA_MQPRIO_MIN_RATE64,
4208c2ecf20Sopenharmony_ci				    sizeof(priv->min_rate[i]),
4218c2ecf20Sopenharmony_ci				    &priv->min_rate[i]))
4228c2ecf20Sopenharmony_ci				goto nla_put_failure;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci		nla_nest_end(skb, nest);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (priv->flags & TC_MQPRIO_F_MAX_RATE) {
4288c2ecf20Sopenharmony_ci		nest = nla_nest_start_noflag(skb, TCA_MQPRIO_MAX_RATE64);
4298c2ecf20Sopenharmony_ci		if (!nest)
4308c2ecf20Sopenharmony_ci			goto nla_put_failure;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		for (i = 0; i < opt->num_tc; i++) {
4338c2ecf20Sopenharmony_ci			if (nla_put(skb, TCA_MQPRIO_MAX_RATE64,
4348c2ecf20Sopenharmony_ci				    sizeof(priv->max_rate[i]),
4358c2ecf20Sopenharmony_ci				    &priv->max_rate[i]))
4368c2ecf20Sopenharmony_ci				goto nla_put_failure;
4378c2ecf20Sopenharmony_ci		}
4388c2ecf20Sopenharmony_ci		nla_nest_end(skb, nest);
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cinla_put_failure:
4438c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
4448c2ecf20Sopenharmony_ci	return -1;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
4508c2ecf20Sopenharmony_ci	struct mqprio_sched *priv = qdisc_priv(sch);
4518c2ecf20Sopenharmony_ci	struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb);
4528c2ecf20Sopenharmony_ci	struct tc_mqprio_qopt opt = { 0 };
4538c2ecf20Sopenharmony_ci	struct Qdisc *qdisc;
4548c2ecf20Sopenharmony_ci	unsigned int ntx, tc;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	sch->q.qlen = 0;
4578c2ecf20Sopenharmony_ci	memset(&sch->bstats, 0, sizeof(sch->bstats));
4588c2ecf20Sopenharmony_ci	memset(&sch->qstats, 0, sizeof(sch->qstats));
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* MQ supports lockless qdiscs. However, statistics accounting needs
4618c2ecf20Sopenharmony_ci	 * to account for all, none, or a mix of locked and unlocked child
4628c2ecf20Sopenharmony_ci	 * qdiscs. Percpu stats are added to counters in-band and locking
4638c2ecf20Sopenharmony_ci	 * qdisc totals are added at end.
4648c2ecf20Sopenharmony_ci	 */
4658c2ecf20Sopenharmony_ci	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
4668c2ecf20Sopenharmony_ci		qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping;
4678c2ecf20Sopenharmony_ci		spin_lock_bh(qdisc_lock(qdisc));
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if (qdisc_is_percpu_stats(qdisc)) {
4708c2ecf20Sopenharmony_ci			__u32 qlen = qdisc_qlen_sum(qdisc);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci			__gnet_stats_copy_basic(NULL, &sch->bstats,
4738c2ecf20Sopenharmony_ci						qdisc->cpu_bstats,
4748c2ecf20Sopenharmony_ci						&qdisc->bstats);
4758c2ecf20Sopenharmony_ci			__gnet_stats_copy_queue(&sch->qstats,
4768c2ecf20Sopenharmony_ci						qdisc->cpu_qstats,
4778c2ecf20Sopenharmony_ci						&qdisc->qstats, qlen);
4788c2ecf20Sopenharmony_ci			sch->q.qlen		+= qlen;
4798c2ecf20Sopenharmony_ci		} else {
4808c2ecf20Sopenharmony_ci			sch->q.qlen		+= qdisc->q.qlen;
4818c2ecf20Sopenharmony_ci			sch->bstats.bytes	+= qdisc->bstats.bytes;
4828c2ecf20Sopenharmony_ci			sch->bstats.packets	+= qdisc->bstats.packets;
4838c2ecf20Sopenharmony_ci			sch->qstats.backlog	+= qdisc->qstats.backlog;
4848c2ecf20Sopenharmony_ci			sch->qstats.drops	+= qdisc->qstats.drops;
4858c2ecf20Sopenharmony_ci			sch->qstats.requeues	+= qdisc->qstats.requeues;
4868c2ecf20Sopenharmony_ci			sch->qstats.overlimits	+= qdisc->qstats.overlimits;
4878c2ecf20Sopenharmony_ci		}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci		spin_unlock_bh(qdisc_lock(qdisc));
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	opt.num_tc = netdev_get_num_tc(dev);
4938c2ecf20Sopenharmony_ci	memcpy(opt.prio_tc_map, dev->prio_tc_map, sizeof(opt.prio_tc_map));
4948c2ecf20Sopenharmony_ci	opt.hw = priv->hw_offload;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	for (tc = 0; tc < netdev_get_num_tc(dev); tc++) {
4978c2ecf20Sopenharmony_ci		opt.count[tc] = dev->tc_to_txq[tc].count;
4988c2ecf20Sopenharmony_ci		opt.offset[tc] = dev->tc_to_txq[tc].offset;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
5028c2ecf20Sopenharmony_ci		goto nla_put_failure;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if ((priv->flags & TC_MQPRIO_F_MODE) &&
5058c2ecf20Sopenharmony_ci	    nla_put_u16(skb, TCA_MQPRIO_MODE, priv->mode))
5068c2ecf20Sopenharmony_ci		goto nla_put_failure;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if ((priv->flags & TC_MQPRIO_F_SHAPER) &&
5098c2ecf20Sopenharmony_ci	    nla_put_u16(skb, TCA_MQPRIO_SHAPER, priv->shaper))
5108c2ecf20Sopenharmony_ci		goto nla_put_failure;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if ((priv->flags & TC_MQPRIO_F_MIN_RATE ||
5138c2ecf20Sopenharmony_ci	     priv->flags & TC_MQPRIO_F_MAX_RATE) &&
5148c2ecf20Sopenharmony_ci	    (dump_rates(priv, &opt, skb) != 0))
5158c2ecf20Sopenharmony_ci		goto nla_put_failure;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return nla_nest_end(skb, nla);
5188c2ecf20Sopenharmony_cinla_put_failure:
5198c2ecf20Sopenharmony_ci	nlmsg_trim(skb, nla);
5208c2ecf20Sopenharmony_ci	return -1;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic struct Qdisc *mqprio_leaf(struct Qdisc *sch, unsigned long cl)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (!dev_queue)
5288c2ecf20Sopenharmony_ci		return NULL;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return dev_queue->qdisc_sleeping;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic unsigned long mqprio_find(struct Qdisc *sch, u32 classid)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
5368c2ecf20Sopenharmony_ci	unsigned int ntx = TC_H_MIN(classid);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* There are essentially two regions here that have valid classid
5398c2ecf20Sopenharmony_ci	 * values. The first region will have a classid value of 1 through
5408c2ecf20Sopenharmony_ci	 * num_tx_queues. All of these are backed by actual Qdiscs.
5418c2ecf20Sopenharmony_ci	 */
5428c2ecf20Sopenharmony_ci	if (ntx < TC_H_MIN_PRIORITY)
5438c2ecf20Sopenharmony_ci		return (ntx <= dev->num_tx_queues) ? ntx : 0;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* The second region represents the hardware traffic classes. These
5468c2ecf20Sopenharmony_ci	 * are represented by classid values of TC_H_MIN_PRIORITY through
5478c2ecf20Sopenharmony_ci	 * TC_H_MIN_PRIORITY + netdev_get_num_tc - 1
5488c2ecf20Sopenharmony_ci	 */
5498c2ecf20Sopenharmony_ci	return ((ntx - TC_H_MIN_PRIORITY) < netdev_get_num_tc(dev)) ? ntx : 0;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic int mqprio_dump_class(struct Qdisc *sch, unsigned long cl,
5538c2ecf20Sopenharmony_ci			 struct sk_buff *skb, struct tcmsg *tcm)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	if (cl < TC_H_MIN_PRIORITY) {
5568c2ecf20Sopenharmony_ci		struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl);
5578c2ecf20Sopenharmony_ci		struct net_device *dev = qdisc_dev(sch);
5588c2ecf20Sopenharmony_ci		int tc = netdev_txq_to_tc(dev, cl - 1);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		tcm->tcm_parent = (tc < 0) ? 0 :
5618c2ecf20Sopenharmony_ci			TC_H_MAKE(TC_H_MAJ(sch->handle),
5628c2ecf20Sopenharmony_ci				  TC_H_MIN(tc + TC_H_MIN_PRIORITY));
5638c2ecf20Sopenharmony_ci		tcm->tcm_info = dev_queue->qdisc_sleeping->handle;
5648c2ecf20Sopenharmony_ci	} else {
5658c2ecf20Sopenharmony_ci		tcm->tcm_parent = TC_H_ROOT;
5668c2ecf20Sopenharmony_ci		tcm->tcm_info = 0;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	tcm->tcm_handle |= TC_H_MIN(cl);
5698c2ecf20Sopenharmony_ci	return 0;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
5738c2ecf20Sopenharmony_ci				   struct gnet_dump *d)
5748c2ecf20Sopenharmony_ci	__releases(d->lock)
5758c2ecf20Sopenharmony_ci	__acquires(d->lock)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	if (cl >= TC_H_MIN_PRIORITY) {
5788c2ecf20Sopenharmony_ci		int i;
5798c2ecf20Sopenharmony_ci		__u32 qlen = 0;
5808c2ecf20Sopenharmony_ci		struct gnet_stats_queue qstats = {0};
5818c2ecf20Sopenharmony_ci		struct gnet_stats_basic_packed bstats = {0};
5828c2ecf20Sopenharmony_ci		struct net_device *dev = qdisc_dev(sch);
5838c2ecf20Sopenharmony_ci		struct netdev_tc_txq tc = dev->tc_to_txq[cl & TC_BITMASK];
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		/* Drop lock here it will be reclaimed before touching
5868c2ecf20Sopenharmony_ci		 * statistics this is required because the d->lock we
5878c2ecf20Sopenharmony_ci		 * hold here is the look on dev_queue->qdisc_sleeping
5888c2ecf20Sopenharmony_ci		 * also acquired below.
5898c2ecf20Sopenharmony_ci		 */
5908c2ecf20Sopenharmony_ci		if (d->lock)
5918c2ecf20Sopenharmony_ci			spin_unlock_bh(d->lock);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		for (i = tc.offset; i < tc.offset + tc.count; i++) {
5948c2ecf20Sopenharmony_ci			struct netdev_queue *q = netdev_get_tx_queue(dev, i);
5958c2ecf20Sopenharmony_ci			struct Qdisc *qdisc = rtnl_dereference(q->qdisc);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci			spin_lock_bh(qdisc_lock(qdisc));
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci			if (qdisc_is_percpu_stats(qdisc)) {
6008c2ecf20Sopenharmony_ci				qlen = qdisc_qlen_sum(qdisc);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci				__gnet_stats_copy_basic(NULL, &bstats,
6038c2ecf20Sopenharmony_ci							qdisc->cpu_bstats,
6048c2ecf20Sopenharmony_ci							&qdisc->bstats);
6058c2ecf20Sopenharmony_ci				__gnet_stats_copy_queue(&qstats,
6068c2ecf20Sopenharmony_ci							qdisc->cpu_qstats,
6078c2ecf20Sopenharmony_ci							&qdisc->qstats,
6088c2ecf20Sopenharmony_ci							qlen);
6098c2ecf20Sopenharmony_ci			} else {
6108c2ecf20Sopenharmony_ci				qlen		+= qdisc->q.qlen;
6118c2ecf20Sopenharmony_ci				bstats.bytes	+= qdisc->bstats.bytes;
6128c2ecf20Sopenharmony_ci				bstats.packets	+= qdisc->bstats.packets;
6138c2ecf20Sopenharmony_ci				qstats.backlog	+= qdisc->qstats.backlog;
6148c2ecf20Sopenharmony_ci				qstats.drops	+= qdisc->qstats.drops;
6158c2ecf20Sopenharmony_ci				qstats.requeues	+= qdisc->qstats.requeues;
6168c2ecf20Sopenharmony_ci				qstats.overlimits += qdisc->qstats.overlimits;
6178c2ecf20Sopenharmony_ci			}
6188c2ecf20Sopenharmony_ci			spin_unlock_bh(qdisc_lock(qdisc));
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		/* Reclaim root sleeping lock before completing stats */
6228c2ecf20Sopenharmony_ci		if (d->lock)
6238c2ecf20Sopenharmony_ci			spin_lock_bh(d->lock);
6248c2ecf20Sopenharmony_ci		if (gnet_stats_copy_basic(NULL, d, NULL, &bstats) < 0 ||
6258c2ecf20Sopenharmony_ci		    gnet_stats_copy_queue(d, NULL, &qstats, qlen) < 0)
6268c2ecf20Sopenharmony_ci			return -1;
6278c2ecf20Sopenharmony_ci	} else {
6288c2ecf20Sopenharmony_ci		struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		sch = dev_queue->qdisc_sleeping;
6318c2ecf20Sopenharmony_ci		if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d,
6328c2ecf20Sopenharmony_ci					  sch->cpu_bstats, &sch->bstats) < 0 ||
6338c2ecf20Sopenharmony_ci		    qdisc_qstats_copy(d, sch) < 0)
6348c2ecf20Sopenharmony_ci			return -1;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci	return 0;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
6428c2ecf20Sopenharmony_ci	unsigned long ntx;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (arg->stop)
6458c2ecf20Sopenharmony_ci		return;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/* Walk hierarchy with a virtual class per tc */
6488c2ecf20Sopenharmony_ci	arg->count = arg->skip;
6498c2ecf20Sopenharmony_ci	for (ntx = arg->skip; ntx < netdev_get_num_tc(dev); ntx++) {
6508c2ecf20Sopenharmony_ci		if (arg->fn(sch, ntx + TC_H_MIN_PRIORITY, arg) < 0) {
6518c2ecf20Sopenharmony_ci			arg->stop = 1;
6528c2ecf20Sopenharmony_ci			return;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci		arg->count++;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* Pad the values and skip over unused traffic classes */
6588c2ecf20Sopenharmony_ci	if (ntx < TC_MAX_QUEUE) {
6598c2ecf20Sopenharmony_ci		arg->count = TC_MAX_QUEUE;
6608c2ecf20Sopenharmony_ci		ntx = TC_MAX_QUEUE;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* Reset offset, sort out remaining per-queue qdiscs */
6648c2ecf20Sopenharmony_ci	for (ntx -= TC_MAX_QUEUE; ntx < dev->num_tx_queues; ntx++) {
6658c2ecf20Sopenharmony_ci		if (arg->fn(sch, ntx + 1, arg) < 0) {
6668c2ecf20Sopenharmony_ci			arg->stop = 1;
6678c2ecf20Sopenharmony_ci			return;
6688c2ecf20Sopenharmony_ci		}
6698c2ecf20Sopenharmony_ci		arg->count++;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic struct netdev_queue *mqprio_select_queue(struct Qdisc *sch,
6748c2ecf20Sopenharmony_ci						struct tcmsg *tcm)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	return mqprio_queue_get(sch, TC_H_MIN(tcm->tcm_parent));
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops mqprio_class_ops = {
6808c2ecf20Sopenharmony_ci	.graft		= mqprio_graft,
6818c2ecf20Sopenharmony_ci	.leaf		= mqprio_leaf,
6828c2ecf20Sopenharmony_ci	.find		= mqprio_find,
6838c2ecf20Sopenharmony_ci	.walk		= mqprio_walk,
6848c2ecf20Sopenharmony_ci	.dump		= mqprio_dump_class,
6858c2ecf20Sopenharmony_ci	.dump_stats	= mqprio_dump_class_stats,
6868c2ecf20Sopenharmony_ci	.select_queue	= mqprio_select_queue,
6878c2ecf20Sopenharmony_ci};
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic struct Qdisc_ops mqprio_qdisc_ops __read_mostly = {
6908c2ecf20Sopenharmony_ci	.cl_ops		= &mqprio_class_ops,
6918c2ecf20Sopenharmony_ci	.id		= "mqprio",
6928c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct mqprio_sched),
6938c2ecf20Sopenharmony_ci	.init		= mqprio_init,
6948c2ecf20Sopenharmony_ci	.destroy	= mqprio_destroy,
6958c2ecf20Sopenharmony_ci	.attach		= mqprio_attach,
6968c2ecf20Sopenharmony_ci	.change_real_num_tx = mqprio_change_real_num_tx,
6978c2ecf20Sopenharmony_ci	.dump		= mqprio_dump,
6988c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
6998c2ecf20Sopenharmony_ci};
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int __init mqprio_module_init(void)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	return register_qdisc(&mqprio_qdisc_ops);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic void __exit mqprio_module_exit(void)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	unregister_qdisc(&mqprio_qdisc_ops);
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cimodule_init(mqprio_module_init);
7128c2ecf20Sopenharmony_cimodule_exit(mqprio_module_exit);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
715