162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/sch_mqprio.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 John Fastabend <john.r.fastabend@intel.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/ethtool_netlink.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <net/netlink.h> 1762306a36Sopenharmony_ci#include <net/pkt_sched.h> 1862306a36Sopenharmony_ci#include <net/sch_generic.h> 1962306a36Sopenharmony_ci#include <net/pkt_cls.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "sch_mqprio_lib.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct mqprio_sched { 2462306a36Sopenharmony_ci struct Qdisc **qdiscs; 2562306a36Sopenharmony_ci u16 mode; 2662306a36Sopenharmony_ci u16 shaper; 2762306a36Sopenharmony_ci int hw_offload; 2862306a36Sopenharmony_ci u32 flags; 2962306a36Sopenharmony_ci u64 min_rate[TC_QOPT_MAX_QUEUE]; 3062306a36Sopenharmony_ci u64 max_rate[TC_QOPT_MAX_QUEUE]; 3162306a36Sopenharmony_ci u32 fp[TC_QOPT_MAX_QUEUE]; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int mqprio_enable_offload(struct Qdisc *sch, 3562306a36Sopenharmony_ci const struct tc_mqprio_qopt *qopt, 3662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 3962306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 4062306a36Sopenharmony_ci struct tc_mqprio_qopt_offload mqprio = { 4162306a36Sopenharmony_ci .qopt = *qopt, 4262306a36Sopenharmony_ci .extack = extack, 4362306a36Sopenharmony_ci }; 4462306a36Sopenharmony_ci int err, i; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci switch (priv->mode) { 4762306a36Sopenharmony_ci case TC_MQPRIO_MODE_DCB: 4862306a36Sopenharmony_ci if (priv->shaper != TC_MQPRIO_SHAPER_DCB) 4962306a36Sopenharmony_ci return -EINVAL; 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci case TC_MQPRIO_MODE_CHANNEL: 5262306a36Sopenharmony_ci mqprio.flags = priv->flags; 5362306a36Sopenharmony_ci if (priv->flags & TC_MQPRIO_F_MODE) 5462306a36Sopenharmony_ci mqprio.mode = priv->mode; 5562306a36Sopenharmony_ci if (priv->flags & TC_MQPRIO_F_SHAPER) 5662306a36Sopenharmony_ci mqprio.shaper = priv->shaper; 5762306a36Sopenharmony_ci if (priv->flags & TC_MQPRIO_F_MIN_RATE) 5862306a36Sopenharmony_ci for (i = 0; i < mqprio.qopt.num_tc; i++) 5962306a36Sopenharmony_ci mqprio.min_rate[i] = priv->min_rate[i]; 6062306a36Sopenharmony_ci if (priv->flags & TC_MQPRIO_F_MAX_RATE) 6162306a36Sopenharmony_ci for (i = 0; i < mqprio.qopt.num_tc; i++) 6262306a36Sopenharmony_ci mqprio.max_rate[i] = priv->max_rate[i]; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci default: 6562306a36Sopenharmony_ci return -EINVAL; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci mqprio_fp_to_offload(priv->fp, &mqprio); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQPRIO, 7162306a36Sopenharmony_ci &mqprio); 7262306a36Sopenharmony_ci if (err) 7362306a36Sopenharmony_ci return err; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci priv->hw_offload = mqprio.qopt.hw; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void mqprio_disable_offload(struct Qdisc *sch) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct tc_mqprio_qopt_offload mqprio = { { 0 } }; 8362306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 8462306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (priv->mode) { 8762306a36Sopenharmony_ci case TC_MQPRIO_MODE_DCB: 8862306a36Sopenharmony_ci case TC_MQPRIO_MODE_CHANNEL: 8962306a36Sopenharmony_ci dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQPRIO, 9062306a36Sopenharmony_ci &mqprio); 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void mqprio_destroy(struct Qdisc *sch) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 9862306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 9962306a36Sopenharmony_ci unsigned int ntx; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (priv->qdiscs) { 10262306a36Sopenharmony_ci for (ntx = 0; 10362306a36Sopenharmony_ci ntx < dev->num_tx_queues && priv->qdiscs[ntx]; 10462306a36Sopenharmony_ci ntx++) 10562306a36Sopenharmony_ci qdisc_put(priv->qdiscs[ntx]); 10662306a36Sopenharmony_ci kfree(priv->qdiscs); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) 11062306a36Sopenharmony_ci mqprio_disable_offload(sch); 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci netdev_set_num_tc(dev, 0); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt, 11662306a36Sopenharmony_ci const struct tc_mqprio_caps *caps, 11762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int err; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Limit qopt->hw to maximum supported offload value. Drivers have 12262306a36Sopenharmony_ci * the option of overriding this later if they don't support the a 12362306a36Sopenharmony_ci * given offload type. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci if (qopt->hw > TC_MQPRIO_HW_OFFLOAD_MAX) 12662306a36Sopenharmony_ci qopt->hw = TC_MQPRIO_HW_OFFLOAD_MAX; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* If hardware offload is requested, we will leave 3 options to the 12962306a36Sopenharmony_ci * device driver: 13062306a36Sopenharmony_ci * - populate the queue counts itself (and ignore what was requested) 13162306a36Sopenharmony_ci * - validate the provided queue counts by itself (and apply them) 13262306a36Sopenharmony_ci * - request queue count validation here (and apply them) 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci err = mqprio_validate_qopt(dev, qopt, 13562306a36Sopenharmony_ci !qopt->hw || caps->validate_queue_counts, 13662306a36Sopenharmony_ci false, extack); 13762306a36Sopenharmony_ci if (err) 13862306a36Sopenharmony_ci return err; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* If ndo_setup_tc is not present then hardware doesn't support offload 14162306a36Sopenharmony_ci * and we should return an error. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci if (qopt->hw && !dev->netdev_ops->ndo_setup_tc) { 14462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 14562306a36Sopenharmony_ci "Device does not support hardware offload"); 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct 15362306a36Sopenharmony_cinla_policy mqprio_tc_entry_policy[TCA_MQPRIO_TC_ENTRY_MAX + 1] = { 15462306a36Sopenharmony_ci [TCA_MQPRIO_TC_ENTRY_INDEX] = NLA_POLICY_MAX(NLA_U32, 15562306a36Sopenharmony_ci TC_QOPT_MAX_QUEUE), 15662306a36Sopenharmony_ci [TCA_MQPRIO_TC_ENTRY_FP] = NLA_POLICY_RANGE(NLA_U32, 15762306a36Sopenharmony_ci TC_FP_EXPRESS, 15862306a36Sopenharmony_ci TC_FP_PREEMPTIBLE), 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic const struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = { 16262306a36Sopenharmony_ci [TCA_MQPRIO_MODE] = { .len = sizeof(u16) }, 16362306a36Sopenharmony_ci [TCA_MQPRIO_SHAPER] = { .len = sizeof(u16) }, 16462306a36Sopenharmony_ci [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED }, 16562306a36Sopenharmony_ci [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED }, 16662306a36Sopenharmony_ci [TCA_MQPRIO_TC_ENTRY] = { .type = NLA_NESTED }, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int mqprio_parse_tc_entry(u32 fp[TC_QOPT_MAX_QUEUE], 17062306a36Sopenharmony_ci struct nlattr *opt, 17162306a36Sopenharmony_ci unsigned long *seen_tcs, 17262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct nlattr *tb[TCA_MQPRIO_TC_ENTRY_MAX + 1]; 17562306a36Sopenharmony_ci int err, tc; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci err = nla_parse_nested(tb, TCA_MQPRIO_TC_ENTRY_MAX, opt, 17862306a36Sopenharmony_ci mqprio_tc_entry_policy, extack); 17962306a36Sopenharmony_ci if (err < 0) 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (NL_REQ_ATTR_CHECK(extack, opt, tb, TCA_MQPRIO_TC_ENTRY_INDEX)) { 18362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "TC entry index missing"); 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci tc = nla_get_u32(tb[TCA_MQPRIO_TC_ENTRY_INDEX]); 18862306a36Sopenharmony_ci if (*seen_tcs & BIT(tc)) { 18962306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb[TCA_MQPRIO_TC_ENTRY_INDEX], 19062306a36Sopenharmony_ci "Duplicate tc entry"); 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci *seen_tcs |= BIT(tc); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (tb[TCA_MQPRIO_TC_ENTRY_FP]) 19762306a36Sopenharmony_ci fp[tc] = nla_get_u32(tb[TCA_MQPRIO_TC_ENTRY_FP]); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int mqprio_parse_tc_entries(struct Qdisc *sch, struct nlattr *nlattr_opt, 20362306a36Sopenharmony_ci int nlattr_opt_len, 20462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 20762306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 20862306a36Sopenharmony_ci bool have_preemption = false; 20962306a36Sopenharmony_ci unsigned long seen_tcs = 0; 21062306a36Sopenharmony_ci u32 fp[TC_QOPT_MAX_QUEUE]; 21162306a36Sopenharmony_ci struct nlattr *n; 21262306a36Sopenharmony_ci int tc, rem; 21362306a36Sopenharmony_ci int err = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) 21662306a36Sopenharmony_ci fp[tc] = priv->fp[tc]; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci nla_for_each_attr(n, nlattr_opt, nlattr_opt_len, rem) { 21962306a36Sopenharmony_ci if (nla_type(n) != TCA_MQPRIO_TC_ENTRY) 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci err = mqprio_parse_tc_entry(fp, n, &seen_tcs, extack); 22362306a36Sopenharmony_ci if (err) 22462306a36Sopenharmony_ci goto out; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) { 22862306a36Sopenharmony_ci priv->fp[tc] = fp[tc]; 22962306a36Sopenharmony_ci if (fp[tc] == TC_FP_PREEMPTIBLE) 23062306a36Sopenharmony_ci have_preemption = true; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (have_preemption && !ethtool_dev_mm_supported(dev)) { 23462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Device does not support preemption"); 23562306a36Sopenharmony_ci return -EOPNOTSUPP; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ciout: 23862306a36Sopenharmony_ci return err; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* Parse the other netlink attributes that represent the payload of 24262306a36Sopenharmony_ci * TCA_OPTIONS, which are appended right after struct tc_mqprio_qopt. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic int mqprio_parse_nlattr(struct Qdisc *sch, struct tc_mqprio_qopt *qopt, 24562306a36Sopenharmony_ci struct nlattr *opt, 24662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct nlattr *nlattr_opt = nla_data(opt) + NLA_ALIGN(sizeof(*qopt)); 24962306a36Sopenharmony_ci int nlattr_opt_len = nla_len(opt) - NLA_ALIGN(sizeof(*qopt)); 25062306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 25162306a36Sopenharmony_ci struct nlattr *tb[TCA_MQPRIO_MAX + 1] = {}; 25262306a36Sopenharmony_ci struct nlattr *attr; 25362306a36Sopenharmony_ci int i, rem, err; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (nlattr_opt_len >= nla_attr_size(0)) { 25662306a36Sopenharmony_ci err = nla_parse_deprecated(tb, TCA_MQPRIO_MAX, nlattr_opt, 25762306a36Sopenharmony_ci nlattr_opt_len, mqprio_policy, 25862306a36Sopenharmony_ci NULL); 25962306a36Sopenharmony_ci if (err < 0) 26062306a36Sopenharmony_ci return err; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!qopt->hw) { 26462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 26562306a36Sopenharmony_ci "mqprio TCA_OPTIONS can only contain netlink attributes in hardware mode"); 26662306a36Sopenharmony_ci return -EINVAL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (tb[TCA_MQPRIO_MODE]) { 27062306a36Sopenharmony_ci priv->flags |= TC_MQPRIO_F_MODE; 27162306a36Sopenharmony_ci priv->mode = nla_get_u16(tb[TCA_MQPRIO_MODE]); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (tb[TCA_MQPRIO_SHAPER]) { 27562306a36Sopenharmony_ci priv->flags |= TC_MQPRIO_F_SHAPER; 27662306a36Sopenharmony_ci priv->shaper = nla_get_u16(tb[TCA_MQPRIO_SHAPER]); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (tb[TCA_MQPRIO_MIN_RATE64]) { 28062306a36Sopenharmony_ci if (priv->shaper != TC_MQPRIO_SHAPER_BW_RATE) { 28162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb[TCA_MQPRIO_MIN_RATE64], 28262306a36Sopenharmony_ci "min_rate accepted only when shaper is in bw_rlimit mode"); 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci i = 0; 28662306a36Sopenharmony_ci nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], 28762306a36Sopenharmony_ci rem) { 28862306a36Sopenharmony_ci if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64) { 28962306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, attr, 29062306a36Sopenharmony_ci "Attribute type expected to be TCA_MQPRIO_MIN_RATE64"); 29162306a36Sopenharmony_ci return -EINVAL; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (nla_len(attr) != sizeof(u64)) { 29562306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, attr, 29662306a36Sopenharmony_ci "Attribute TCA_MQPRIO_MIN_RATE64 expected to have 8 bytes length"); 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (i >= qopt->num_tc) 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci priv->min_rate[i] = nla_get_u64(attr); 30362306a36Sopenharmony_ci i++; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci priv->flags |= TC_MQPRIO_F_MIN_RATE; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (tb[TCA_MQPRIO_MAX_RATE64]) { 30962306a36Sopenharmony_ci if (priv->shaper != TC_MQPRIO_SHAPER_BW_RATE) { 31062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb[TCA_MQPRIO_MAX_RATE64], 31162306a36Sopenharmony_ci "max_rate accepted only when shaper is in bw_rlimit mode"); 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci i = 0; 31562306a36Sopenharmony_ci nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], 31662306a36Sopenharmony_ci rem) { 31762306a36Sopenharmony_ci if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64) { 31862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, attr, 31962306a36Sopenharmony_ci "Attribute type expected to be TCA_MQPRIO_MAX_RATE64"); 32062306a36Sopenharmony_ci return -EINVAL; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (nla_len(attr) != sizeof(u64)) { 32462306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, attr, 32562306a36Sopenharmony_ci "Attribute TCA_MQPRIO_MAX_RATE64 expected to have 8 bytes length"); 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (i >= qopt->num_tc) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci priv->max_rate[i] = nla_get_u64(attr); 33262306a36Sopenharmony_ci i++; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci priv->flags |= TC_MQPRIO_F_MAX_RATE; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (tb[TCA_MQPRIO_TC_ENTRY]) { 33862306a36Sopenharmony_ci err = mqprio_parse_tc_entries(sch, nlattr_opt, nlattr_opt_len, 33962306a36Sopenharmony_ci extack); 34062306a36Sopenharmony_ci if (err) 34162306a36Sopenharmony_ci return err; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int mqprio_init(struct Qdisc *sch, struct nlattr *opt, 34862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 35162306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 35262306a36Sopenharmony_ci struct netdev_queue *dev_queue; 35362306a36Sopenharmony_ci struct Qdisc *qdisc; 35462306a36Sopenharmony_ci int i, err = -EOPNOTSUPP; 35562306a36Sopenharmony_ci struct tc_mqprio_qopt *qopt = NULL; 35662306a36Sopenharmony_ci struct tc_mqprio_caps caps; 35762306a36Sopenharmony_ci int len, tc; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE); 36062306a36Sopenharmony_ci BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (sch->parent != TC_H_ROOT) 36362306a36Sopenharmony_ci return -EOPNOTSUPP; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!netif_is_multiqueue(dev)) 36662306a36Sopenharmony_ci return -EOPNOTSUPP; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* make certain can allocate enough classids to handle queues */ 36962306a36Sopenharmony_ci if (dev->num_tx_queues >= TC_H_MIN_PRIORITY) 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!opt || nla_len(opt) < sizeof(*qopt)) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) 37662306a36Sopenharmony_ci priv->fp[tc] = TC_FP_EXPRESS; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci qdisc_offload_query_caps(dev, TC_SETUP_QDISC_MQPRIO, 37962306a36Sopenharmony_ci &caps, sizeof(caps)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci qopt = nla_data(opt); 38262306a36Sopenharmony_ci if (mqprio_parse_opt(dev, qopt, &caps, extack)) 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci len = nla_len(opt) - NLA_ALIGN(sizeof(*qopt)); 38662306a36Sopenharmony_ci if (len > 0) { 38762306a36Sopenharmony_ci err = mqprio_parse_nlattr(sch, qopt, opt, extack); 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* pre-allocate qdisc, attachment can't fail */ 39362306a36Sopenharmony_ci priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), 39462306a36Sopenharmony_ci GFP_KERNEL); 39562306a36Sopenharmony_ci if (!priv->qdiscs) 39662306a36Sopenharmony_ci return -ENOMEM; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci for (i = 0; i < dev->num_tx_queues; i++) { 39962306a36Sopenharmony_ci dev_queue = netdev_get_tx_queue(dev, i); 40062306a36Sopenharmony_ci qdisc = qdisc_create_dflt(dev_queue, 40162306a36Sopenharmony_ci get_default_qdisc_ops(dev, i), 40262306a36Sopenharmony_ci TC_H_MAKE(TC_H_MAJ(sch->handle), 40362306a36Sopenharmony_ci TC_H_MIN(i + 1)), extack); 40462306a36Sopenharmony_ci if (!qdisc) 40562306a36Sopenharmony_ci return -ENOMEM; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci priv->qdiscs[i] = qdisc; 40862306a36Sopenharmony_ci qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* If the mqprio options indicate that hardware should own 41262306a36Sopenharmony_ci * the queue mapping then run ndo_setup_tc otherwise use the 41362306a36Sopenharmony_ci * supplied and verified mapping 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci if (qopt->hw) { 41662306a36Sopenharmony_ci err = mqprio_enable_offload(sch, qopt, extack); 41762306a36Sopenharmony_ci if (err) 41862306a36Sopenharmony_ci return err; 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci netdev_set_num_tc(dev, qopt->num_tc); 42162306a36Sopenharmony_ci for (i = 0; i < qopt->num_tc; i++) 42262306a36Sopenharmony_ci netdev_set_tc_queue(dev, i, 42362306a36Sopenharmony_ci qopt->count[i], qopt->offset[i]); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Always use supplied priority mappings */ 42762306a36Sopenharmony_ci for (i = 0; i < TC_BITMASK + 1; i++) 42862306a36Sopenharmony_ci netdev_set_prio_tc_map(dev, i, qopt->prio_tc_map[i]); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci sch->flags |= TCQ_F_MQROOT; 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void mqprio_attach(struct Qdisc *sch) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 43762306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 43862306a36Sopenharmony_ci struct Qdisc *qdisc, *old; 43962306a36Sopenharmony_ci unsigned int ntx; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Attach underlying qdisc */ 44262306a36Sopenharmony_ci for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { 44362306a36Sopenharmony_ci qdisc = priv->qdiscs[ntx]; 44462306a36Sopenharmony_ci old = dev_graft_qdisc(qdisc->dev_queue, qdisc); 44562306a36Sopenharmony_ci if (old) 44662306a36Sopenharmony_ci qdisc_put(old); 44762306a36Sopenharmony_ci if (ntx < dev->real_num_tx_queues) 44862306a36Sopenharmony_ci qdisc_hash_add(qdisc, false); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci kfree(priv->qdiscs); 45162306a36Sopenharmony_ci priv->qdiscs = NULL; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic struct netdev_queue *mqprio_queue_get(struct Qdisc *sch, 45562306a36Sopenharmony_ci unsigned long cl) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 45862306a36Sopenharmony_ci unsigned long ntx = cl - 1; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (ntx >= dev->num_tx_queues) 46162306a36Sopenharmony_ci return NULL; 46262306a36Sopenharmony_ci return netdev_get_tx_queue(dev, ntx); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, 46662306a36Sopenharmony_ci struct Qdisc **old, struct netlink_ext_ack *extack) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 46962306a36Sopenharmony_ci struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (!dev_queue) 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (dev->flags & IFF_UP) 47562306a36Sopenharmony_ci dev_deactivate(dev); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci *old = dev_graft_qdisc(dev_queue, new); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (new) 48062306a36Sopenharmony_ci new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (dev->flags & IFF_UP) 48362306a36Sopenharmony_ci dev_activate(dev); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int dump_rates(struct mqprio_sched *priv, 48962306a36Sopenharmony_ci struct tc_mqprio_qopt *opt, struct sk_buff *skb) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct nlattr *nest; 49262306a36Sopenharmony_ci int i; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (priv->flags & TC_MQPRIO_F_MIN_RATE) { 49562306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_MQPRIO_MIN_RATE64); 49662306a36Sopenharmony_ci if (!nest) 49762306a36Sopenharmony_ci goto nla_put_failure; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci for (i = 0; i < opt->num_tc; i++) { 50062306a36Sopenharmony_ci if (nla_put(skb, TCA_MQPRIO_MIN_RATE64, 50162306a36Sopenharmony_ci sizeof(priv->min_rate[i]), 50262306a36Sopenharmony_ci &priv->min_rate[i])) 50362306a36Sopenharmony_ci goto nla_put_failure; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci nla_nest_end(skb, nest); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (priv->flags & TC_MQPRIO_F_MAX_RATE) { 50962306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_MQPRIO_MAX_RATE64); 51062306a36Sopenharmony_ci if (!nest) 51162306a36Sopenharmony_ci goto nla_put_failure; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (i = 0; i < opt->num_tc; i++) { 51462306a36Sopenharmony_ci if (nla_put(skb, TCA_MQPRIO_MAX_RATE64, 51562306a36Sopenharmony_ci sizeof(priv->max_rate[i]), 51662306a36Sopenharmony_ci &priv->max_rate[i])) 51762306a36Sopenharmony_ci goto nla_put_failure; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci nla_nest_end(skb, nest); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cinla_put_failure: 52462306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 52562306a36Sopenharmony_ci return -1; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int mqprio_dump_tc_entries(struct mqprio_sched *priv, 52962306a36Sopenharmony_ci struct sk_buff *skb) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct nlattr *n; 53262306a36Sopenharmony_ci int tc; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) { 53562306a36Sopenharmony_ci n = nla_nest_start(skb, TCA_MQPRIO_TC_ENTRY); 53662306a36Sopenharmony_ci if (!n) 53762306a36Sopenharmony_ci return -EMSGSIZE; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_MQPRIO_TC_ENTRY_INDEX, tc)) 54062306a36Sopenharmony_ci goto nla_put_failure; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_MQPRIO_TC_ENTRY_FP, priv->fp[tc])) 54362306a36Sopenharmony_ci goto nla_put_failure; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci nla_nest_end(skb, n); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cinla_put_failure: 55162306a36Sopenharmony_ci nla_nest_cancel(skb, n); 55262306a36Sopenharmony_ci return -EMSGSIZE; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 55862306a36Sopenharmony_ci struct mqprio_sched *priv = qdisc_priv(sch); 55962306a36Sopenharmony_ci struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb); 56062306a36Sopenharmony_ci struct tc_mqprio_qopt opt = { 0 }; 56162306a36Sopenharmony_ci struct Qdisc *qdisc; 56262306a36Sopenharmony_ci unsigned int ntx; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci sch->q.qlen = 0; 56562306a36Sopenharmony_ci gnet_stats_basic_sync_init(&sch->bstats); 56662306a36Sopenharmony_ci memset(&sch->qstats, 0, sizeof(sch->qstats)); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* MQ supports lockless qdiscs. However, statistics accounting needs 56962306a36Sopenharmony_ci * to account for all, none, or a mix of locked and unlocked child 57062306a36Sopenharmony_ci * qdiscs. Percpu stats are added to counters in-band and locking 57162306a36Sopenharmony_ci * qdisc totals are added at end. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { 57462306a36Sopenharmony_ci qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); 57562306a36Sopenharmony_ci spin_lock_bh(qdisc_lock(qdisc)); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, 57862306a36Sopenharmony_ci &qdisc->bstats, false); 57962306a36Sopenharmony_ci gnet_stats_add_queue(&sch->qstats, qdisc->cpu_qstats, 58062306a36Sopenharmony_ci &qdisc->qstats); 58162306a36Sopenharmony_ci sch->q.qlen += qdisc_qlen(qdisc); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci spin_unlock_bh(qdisc_lock(qdisc)); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mqprio_qopt_reconstruct(dev, &opt); 58762306a36Sopenharmony_ci opt.hw = priv->hw_offload; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) 59062306a36Sopenharmony_ci goto nla_put_failure; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if ((priv->flags & TC_MQPRIO_F_MODE) && 59362306a36Sopenharmony_ci nla_put_u16(skb, TCA_MQPRIO_MODE, priv->mode)) 59462306a36Sopenharmony_ci goto nla_put_failure; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if ((priv->flags & TC_MQPRIO_F_SHAPER) && 59762306a36Sopenharmony_ci nla_put_u16(skb, TCA_MQPRIO_SHAPER, priv->shaper)) 59862306a36Sopenharmony_ci goto nla_put_failure; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if ((priv->flags & TC_MQPRIO_F_MIN_RATE || 60162306a36Sopenharmony_ci priv->flags & TC_MQPRIO_F_MAX_RATE) && 60262306a36Sopenharmony_ci (dump_rates(priv, &opt, skb) != 0)) 60362306a36Sopenharmony_ci goto nla_put_failure; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (mqprio_dump_tc_entries(priv, skb)) 60662306a36Sopenharmony_ci goto nla_put_failure; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return nla_nest_end(skb, nla); 60962306a36Sopenharmony_cinla_put_failure: 61062306a36Sopenharmony_ci nlmsg_trim(skb, nla); 61162306a36Sopenharmony_ci return -1; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic struct Qdisc *mqprio_leaf(struct Qdisc *sch, unsigned long cl) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (!dev_queue) 61962306a36Sopenharmony_ci return NULL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return rtnl_dereference(dev_queue->qdisc_sleeping); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic unsigned long mqprio_find(struct Qdisc *sch, u32 classid) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 62762306a36Sopenharmony_ci unsigned int ntx = TC_H_MIN(classid); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* There are essentially two regions here that have valid classid 63062306a36Sopenharmony_ci * values. The first region will have a classid value of 1 through 63162306a36Sopenharmony_ci * num_tx_queues. All of these are backed by actual Qdiscs. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci if (ntx < TC_H_MIN_PRIORITY) 63462306a36Sopenharmony_ci return (ntx <= dev->num_tx_queues) ? ntx : 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* The second region represents the hardware traffic classes. These 63762306a36Sopenharmony_ci * are represented by classid values of TC_H_MIN_PRIORITY through 63862306a36Sopenharmony_ci * TC_H_MIN_PRIORITY + netdev_get_num_tc - 1 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci return ((ntx - TC_H_MIN_PRIORITY) < netdev_get_num_tc(dev)) ? ntx : 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int mqprio_dump_class(struct Qdisc *sch, unsigned long cl, 64462306a36Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci if (cl < TC_H_MIN_PRIORITY) { 64762306a36Sopenharmony_ci struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); 64862306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 64962306a36Sopenharmony_ci int tc = netdev_txq_to_tc(dev, cl - 1); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci tcm->tcm_parent = (tc < 0) ? 0 : 65262306a36Sopenharmony_ci TC_H_MAKE(TC_H_MAJ(sch->handle), 65362306a36Sopenharmony_ci TC_H_MIN(tc + TC_H_MIN_PRIORITY)); 65462306a36Sopenharmony_ci tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci tcm->tcm_parent = TC_H_ROOT; 65762306a36Sopenharmony_ci tcm->tcm_info = 0; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci tcm->tcm_handle |= TC_H_MIN(cl); 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, 66462306a36Sopenharmony_ci struct gnet_dump *d) 66562306a36Sopenharmony_ci __releases(d->lock) 66662306a36Sopenharmony_ci __acquires(d->lock) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci if (cl >= TC_H_MIN_PRIORITY) { 66962306a36Sopenharmony_ci int i; 67062306a36Sopenharmony_ci __u32 qlen; 67162306a36Sopenharmony_ci struct gnet_stats_queue qstats = {0}; 67262306a36Sopenharmony_ci struct gnet_stats_basic_sync bstats; 67362306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 67462306a36Sopenharmony_ci struct netdev_tc_txq tc = dev->tc_to_txq[cl & TC_BITMASK]; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci gnet_stats_basic_sync_init(&bstats); 67762306a36Sopenharmony_ci /* Drop lock here it will be reclaimed before touching 67862306a36Sopenharmony_ci * statistics this is required because the d->lock we 67962306a36Sopenharmony_ci * hold here is the look on dev_queue->qdisc_sleeping 68062306a36Sopenharmony_ci * also acquired below. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci if (d->lock) 68362306a36Sopenharmony_ci spin_unlock_bh(d->lock); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = tc.offset; i < tc.offset + tc.count; i++) { 68662306a36Sopenharmony_ci struct netdev_queue *q = netdev_get_tx_queue(dev, i); 68762306a36Sopenharmony_ci struct Qdisc *qdisc = rtnl_dereference(q->qdisc); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci spin_lock_bh(qdisc_lock(qdisc)); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci gnet_stats_add_basic(&bstats, qdisc->cpu_bstats, 69262306a36Sopenharmony_ci &qdisc->bstats, false); 69362306a36Sopenharmony_ci gnet_stats_add_queue(&qstats, qdisc->cpu_qstats, 69462306a36Sopenharmony_ci &qdisc->qstats); 69562306a36Sopenharmony_ci sch->q.qlen += qdisc_qlen(qdisc); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_unlock_bh(qdisc_lock(qdisc)); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci qlen = qdisc_qlen(sch) + qstats.qlen; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* Reclaim root sleeping lock before completing stats */ 70262306a36Sopenharmony_ci if (d->lock) 70362306a36Sopenharmony_ci spin_lock_bh(d->lock); 70462306a36Sopenharmony_ci if (gnet_stats_copy_basic(d, NULL, &bstats, false) < 0 || 70562306a36Sopenharmony_ci gnet_stats_copy_queue(d, NULL, &qstats, qlen) < 0) 70662306a36Sopenharmony_ci return -1; 70762306a36Sopenharmony_ci } else { 70862306a36Sopenharmony_ci struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci sch = rtnl_dereference(dev_queue->qdisc_sleeping); 71162306a36Sopenharmony_ci if (gnet_stats_copy_basic(d, sch->cpu_bstats, 71262306a36Sopenharmony_ci &sch->bstats, true) < 0 || 71362306a36Sopenharmony_ci qdisc_qstats_copy(d, sch) < 0) 71462306a36Sopenharmony_ci return -1; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci return 0; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 72262306a36Sopenharmony_ci unsigned long ntx; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (arg->stop) 72562306a36Sopenharmony_ci return; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* Walk hierarchy with a virtual class per tc */ 72862306a36Sopenharmony_ci arg->count = arg->skip; 72962306a36Sopenharmony_ci for (ntx = arg->skip; ntx < netdev_get_num_tc(dev); ntx++) { 73062306a36Sopenharmony_ci if (!tc_qdisc_stats_dump(sch, ntx + TC_H_MIN_PRIORITY, arg)) 73162306a36Sopenharmony_ci return; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Pad the values and skip over unused traffic classes */ 73562306a36Sopenharmony_ci if (ntx < TC_MAX_QUEUE) { 73662306a36Sopenharmony_ci arg->count = TC_MAX_QUEUE; 73762306a36Sopenharmony_ci ntx = TC_MAX_QUEUE; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Reset offset, sort out remaining per-queue qdiscs */ 74162306a36Sopenharmony_ci for (ntx -= TC_MAX_QUEUE; ntx < dev->num_tx_queues; ntx++) { 74262306a36Sopenharmony_ci if (arg->fn(sch, ntx + 1, arg) < 0) { 74362306a36Sopenharmony_ci arg->stop = 1; 74462306a36Sopenharmony_ci return; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci arg->count++; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic struct netdev_queue *mqprio_select_queue(struct Qdisc *sch, 75162306a36Sopenharmony_ci struct tcmsg *tcm) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci return mqprio_queue_get(sch, TC_H_MIN(tcm->tcm_parent)); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic const struct Qdisc_class_ops mqprio_class_ops = { 75762306a36Sopenharmony_ci .graft = mqprio_graft, 75862306a36Sopenharmony_ci .leaf = mqprio_leaf, 75962306a36Sopenharmony_ci .find = mqprio_find, 76062306a36Sopenharmony_ci .walk = mqprio_walk, 76162306a36Sopenharmony_ci .dump = mqprio_dump_class, 76262306a36Sopenharmony_ci .dump_stats = mqprio_dump_class_stats, 76362306a36Sopenharmony_ci .select_queue = mqprio_select_queue, 76462306a36Sopenharmony_ci}; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic struct Qdisc_ops mqprio_qdisc_ops __read_mostly = { 76762306a36Sopenharmony_ci .cl_ops = &mqprio_class_ops, 76862306a36Sopenharmony_ci .id = "mqprio", 76962306a36Sopenharmony_ci .priv_size = sizeof(struct mqprio_sched), 77062306a36Sopenharmony_ci .init = mqprio_init, 77162306a36Sopenharmony_ci .destroy = mqprio_destroy, 77262306a36Sopenharmony_ci .attach = mqprio_attach, 77362306a36Sopenharmony_ci .change_real_num_tx = mq_change_real_num_tx, 77462306a36Sopenharmony_ci .dump = mqprio_dump, 77562306a36Sopenharmony_ci .owner = THIS_MODULE, 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic int __init mqprio_module_init(void) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci return register_qdisc(&mqprio_qdisc_ops); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic void __exit mqprio_module_exit(void) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci unregister_qdisc(&mqprio_qdisc_ops); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cimodule_init(mqprio_module_init); 78962306a36Sopenharmony_cimodule_exit(mqprio_module_exit); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 792