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