18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2008, Intel Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Alexander Duyck <alexander.h.duyck@intel.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 158c2ecf20Sopenharmony_ci#include <net/netlink.h> 168c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 178c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct multiq_sched_data { 208c2ecf20Sopenharmony_ci u16 bands; 218c2ecf20Sopenharmony_ci u16 max_bands; 228c2ecf20Sopenharmony_ci u16 curband; 238c2ecf20Sopenharmony_ci struct tcf_proto __rcu *filter_list; 248c2ecf20Sopenharmony_ci struct tcf_block *block; 258c2ecf20Sopenharmony_ci struct Qdisc **queues; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic struct Qdisc * 308c2ecf20Sopenharmony_cimultiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 338c2ecf20Sopenharmony_ci u32 band; 348c2ecf20Sopenharmony_ci struct tcf_result res; 358c2ecf20Sopenharmony_ci struct tcf_proto *fl = rcu_dereference_bh(q->filter_list); 368c2ecf20Sopenharmony_ci int err; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 398c2ecf20Sopenharmony_ci err = tcf_classify(skb, fl, &res, false); 408c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 418c2ecf20Sopenharmony_ci switch (err) { 428c2ecf20Sopenharmony_ci case TC_ACT_STOLEN: 438c2ecf20Sopenharmony_ci case TC_ACT_QUEUED: 448c2ecf20Sopenharmony_ci case TC_ACT_TRAP: 458c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 468c2ecf20Sopenharmony_ci fallthrough; 478c2ecf20Sopenharmony_ci case TC_ACT_SHOT: 488c2ecf20Sopenharmony_ci return NULL; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci band = skb_get_queue_mapping(skb); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (band >= q->bands) 548c2ecf20Sopenharmony_ci return q->queues[0]; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return q->queues[band]; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int 608c2ecf20Sopenharmony_cimultiq_enqueue(struct sk_buff *skb, struct Qdisc *sch, 618c2ecf20Sopenharmony_ci struct sk_buff **to_free) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci qdisc = multiq_classify(skb, sch, &ret); 678c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 688c2ecf20Sopenharmony_ci if (qdisc == NULL) { 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (ret & __NET_XMIT_BYPASS) 718c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 728c2ecf20Sopenharmony_ci __qdisc_drop(skb, to_free); 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ret = qdisc_enqueue(skb, qdisc, to_free); 788c2ecf20Sopenharmony_ci if (ret == NET_XMIT_SUCCESS) { 798c2ecf20Sopenharmony_ci sch->q.qlen++; 808c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci if (net_xmit_drop_count(ret)) 838c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic struct sk_buff *multiq_dequeue(struct Qdisc *sch) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 908c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 918c2ecf20Sopenharmony_ci struct sk_buff *skb; 928c2ecf20Sopenharmony_ci int band; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci for (band = 0; band < q->bands; band++) { 958c2ecf20Sopenharmony_ci /* cycle through bands to ensure fairness */ 968c2ecf20Sopenharmony_ci q->curband++; 978c2ecf20Sopenharmony_ci if (q->curband >= q->bands) 988c2ecf20Sopenharmony_ci q->curband = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Check that target subqueue is available before 1018c2ecf20Sopenharmony_ci * pulling an skb to avoid head-of-line blocking. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci if (!netif_xmit_stopped( 1048c2ecf20Sopenharmony_ci netdev_get_tx_queue(qdisc_dev(sch), q->curband))) { 1058c2ecf20Sopenharmony_ci qdisc = q->queues[q->curband]; 1068c2ecf20Sopenharmony_ci skb = qdisc->dequeue(qdisc); 1078c2ecf20Sopenharmony_ci if (skb) { 1088c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 1098c2ecf20Sopenharmony_ci sch->q.qlen--; 1108c2ecf20Sopenharmony_ci return skb; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci return NULL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct sk_buff *multiq_peek(struct Qdisc *sch) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 1218c2ecf20Sopenharmony_ci unsigned int curband = q->curband; 1228c2ecf20Sopenharmony_ci struct Qdisc *qdisc; 1238c2ecf20Sopenharmony_ci struct sk_buff *skb; 1248c2ecf20Sopenharmony_ci int band; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (band = 0; band < q->bands; band++) { 1278c2ecf20Sopenharmony_ci /* cycle through bands to ensure fairness */ 1288c2ecf20Sopenharmony_ci curband++; 1298c2ecf20Sopenharmony_ci if (curband >= q->bands) 1308c2ecf20Sopenharmony_ci curband = 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Check that target subqueue is available before 1338c2ecf20Sopenharmony_ci * pulling an skb to avoid head-of-line blocking. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci if (!netif_xmit_stopped( 1368c2ecf20Sopenharmony_ci netdev_get_tx_queue(qdisc_dev(sch), curband))) { 1378c2ecf20Sopenharmony_ci qdisc = q->queues[curband]; 1388c2ecf20Sopenharmony_ci skb = qdisc->ops->peek(qdisc); 1398c2ecf20Sopenharmony_ci if (skb) 1408c2ecf20Sopenharmony_ci return skb; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci return NULL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void 1488c2ecf20Sopenharmony_cimultiq_reset(struct Qdisc *sch) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci u16 band; 1518c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci for (band = 0; band < q->bands; band++) 1548c2ecf20Sopenharmony_ci qdisc_reset(q->queues[band]); 1558c2ecf20Sopenharmony_ci q->curband = 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void 1598c2ecf20Sopenharmony_cimultiq_destroy(struct Qdisc *sch) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int band; 1628c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci tcf_block_put(q->block); 1658c2ecf20Sopenharmony_ci for (band = 0; band < q->bands; band++) 1668c2ecf20Sopenharmony_ci qdisc_put(q->queues[band]); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci kfree(q->queues); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int multiq_tune(struct Qdisc *sch, struct nlattr *opt, 1728c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 1758c2ecf20Sopenharmony_ci struct tc_multiq_qopt *qopt; 1768c2ecf20Sopenharmony_ci struct Qdisc **removed; 1778c2ecf20Sopenharmony_ci int i, n_removed = 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!netif_is_multiqueue(qdisc_dev(sch))) 1808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1818c2ecf20Sopenharmony_ci if (nla_len(opt) < sizeof(*qopt)) 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci qopt = nla_data(opt); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci qopt->bands = qdisc_dev(sch)->real_num_tx_queues; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci removed = kmalloc(sizeof(*removed) * (q->max_bands - q->bands), 1898c2ecf20Sopenharmony_ci GFP_KERNEL); 1908c2ecf20Sopenharmony_ci if (!removed) 1918c2ecf20Sopenharmony_ci return -ENOMEM; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci sch_tree_lock(sch); 1948c2ecf20Sopenharmony_ci q->bands = qopt->bands; 1958c2ecf20Sopenharmony_ci for (i = q->bands; i < q->max_bands; i++) { 1968c2ecf20Sopenharmony_ci if (q->queues[i] != &noop_qdisc) { 1978c2ecf20Sopenharmony_ci struct Qdisc *child = q->queues[i]; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci q->queues[i] = &noop_qdisc; 2008c2ecf20Sopenharmony_ci qdisc_purge_queue(child); 2018c2ecf20Sopenharmony_ci removed[n_removed++] = child; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < n_removed; i++) 2088c2ecf20Sopenharmony_ci qdisc_put(removed[i]); 2098c2ecf20Sopenharmony_ci kfree(removed); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < q->bands; i++) { 2128c2ecf20Sopenharmony_ci if (q->queues[i] == &noop_qdisc) { 2138c2ecf20Sopenharmony_ci struct Qdisc *child, *old; 2148c2ecf20Sopenharmony_ci child = qdisc_create_dflt(sch->dev_queue, 2158c2ecf20Sopenharmony_ci &pfifo_qdisc_ops, 2168c2ecf20Sopenharmony_ci TC_H_MAKE(sch->handle, 2178c2ecf20Sopenharmony_ci i + 1), extack); 2188c2ecf20Sopenharmony_ci if (child) { 2198c2ecf20Sopenharmony_ci sch_tree_lock(sch); 2208c2ecf20Sopenharmony_ci old = q->queues[i]; 2218c2ecf20Sopenharmony_ci q->queues[i] = child; 2228c2ecf20Sopenharmony_ci if (child != &noop_qdisc) 2238c2ecf20Sopenharmony_ci qdisc_hash_add(child, true); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (old != &noop_qdisc) 2268c2ecf20Sopenharmony_ci qdisc_purge_queue(old); 2278c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 2288c2ecf20Sopenharmony_ci qdisc_put(old); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int multiq_init(struct Qdisc *sch, struct nlattr *opt, 2368c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 2398c2ecf20Sopenharmony_ci int i, err; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci q->queues = NULL; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!opt) 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci err = tcf_block_get(&q->block, &q->filter_list, sch, extack); 2478c2ecf20Sopenharmony_ci if (err) 2488c2ecf20Sopenharmony_ci return err; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci q->max_bands = qdisc_dev(sch)->num_tx_queues; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL); 2538c2ecf20Sopenharmony_ci if (!q->queues) 2548c2ecf20Sopenharmony_ci return -ENOBUFS; 2558c2ecf20Sopenharmony_ci for (i = 0; i < q->max_bands; i++) 2568c2ecf20Sopenharmony_ci q->queues[i] = &noop_qdisc; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return multiq_tune(sch, opt, extack); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int multiq_dump(struct Qdisc *sch, struct sk_buff *skb) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 2648c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 2658c2ecf20Sopenharmony_ci struct tc_multiq_qopt opt; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci opt.bands = q->bands; 2688c2ecf20Sopenharmony_ci opt.max_bands = q->max_bands; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) 2718c2ecf20Sopenharmony_ci goto nla_put_failure; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return skb->len; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cinla_put_failure: 2768c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 2778c2ecf20Sopenharmony_ci return -1; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, 2818c2ecf20Sopenharmony_ci struct Qdisc **old, struct netlink_ext_ack *extack) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 2848c2ecf20Sopenharmony_ci unsigned long band = arg - 1; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (new == NULL) 2878c2ecf20Sopenharmony_ci new = &noop_qdisc; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci *old = qdisc_replace(sch, new, &q->queues[band]); 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct Qdisc * 2948c2ecf20Sopenharmony_cimultiq_leaf(struct Qdisc *sch, unsigned long arg) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 2978c2ecf20Sopenharmony_ci unsigned long band = arg - 1; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return q->queues[band]; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic unsigned long multiq_find(struct Qdisc *sch, u32 classid) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 3058c2ecf20Sopenharmony_ci unsigned long band = TC_H_MIN(classid); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (band - 1 >= q->bands) 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci return band; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent, 3138c2ecf20Sopenharmony_ci u32 classid) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci return multiq_find(sch, classid); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void multiq_unbind(struct Qdisc *q, unsigned long cl) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int multiq_dump_class(struct Qdisc *sch, unsigned long cl, 3248c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci tcm->tcm_handle |= TC_H_MIN(cl); 3298c2ecf20Sopenharmony_ci tcm->tcm_info = q->queues[cl - 1]->handle; 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl, 3348c2ecf20Sopenharmony_ci struct gnet_dump *d) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 3378c2ecf20Sopenharmony_ci struct Qdisc *cl_q; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci cl_q = q->queues[cl - 1]; 3408c2ecf20Sopenharmony_ci if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), 3418c2ecf20Sopenharmony_ci d, cl_q->cpu_bstats, &cl_q->bstats) < 0 || 3428c2ecf20Sopenharmony_ci qdisc_qstats_copy(d, cl_q) < 0) 3438c2ecf20Sopenharmony_ci return -1; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void multiq_walk(struct Qdisc *sch, struct qdisc_walker *arg) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 3518c2ecf20Sopenharmony_ci int band; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (arg->stop) 3548c2ecf20Sopenharmony_ci return; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci for (band = 0; band < q->bands; band++) { 3578c2ecf20Sopenharmony_ci if (arg->count < arg->skip) { 3588c2ecf20Sopenharmony_ci arg->count++; 3598c2ecf20Sopenharmony_ci continue; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci if (arg->fn(sch, band + 1, arg) < 0) { 3628c2ecf20Sopenharmony_ci arg->stop = 1; 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci arg->count++; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic struct tcf_block *multiq_tcf_block(struct Qdisc *sch, unsigned long cl, 3708c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct multiq_sched_data *q = qdisc_priv(sch); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (cl) 3758c2ecf20Sopenharmony_ci return NULL; 3768c2ecf20Sopenharmony_ci return q->block; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops multiq_class_ops = { 3808c2ecf20Sopenharmony_ci .graft = multiq_graft, 3818c2ecf20Sopenharmony_ci .leaf = multiq_leaf, 3828c2ecf20Sopenharmony_ci .find = multiq_find, 3838c2ecf20Sopenharmony_ci .walk = multiq_walk, 3848c2ecf20Sopenharmony_ci .tcf_block = multiq_tcf_block, 3858c2ecf20Sopenharmony_ci .bind_tcf = multiq_bind, 3868c2ecf20Sopenharmony_ci .unbind_tcf = multiq_unbind, 3878c2ecf20Sopenharmony_ci .dump = multiq_dump_class, 3888c2ecf20Sopenharmony_ci .dump_stats = multiq_dump_class_stats, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic struct Qdisc_ops multiq_qdisc_ops __read_mostly = { 3928c2ecf20Sopenharmony_ci .next = NULL, 3938c2ecf20Sopenharmony_ci .cl_ops = &multiq_class_ops, 3948c2ecf20Sopenharmony_ci .id = "multiq", 3958c2ecf20Sopenharmony_ci .priv_size = sizeof(struct multiq_sched_data), 3968c2ecf20Sopenharmony_ci .enqueue = multiq_enqueue, 3978c2ecf20Sopenharmony_ci .dequeue = multiq_dequeue, 3988c2ecf20Sopenharmony_ci .peek = multiq_peek, 3998c2ecf20Sopenharmony_ci .init = multiq_init, 4008c2ecf20Sopenharmony_ci .reset = multiq_reset, 4018c2ecf20Sopenharmony_ci .destroy = multiq_destroy, 4028c2ecf20Sopenharmony_ci .change = multiq_tune, 4038c2ecf20Sopenharmony_ci .dump = multiq_dump, 4048c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int __init multiq_module_init(void) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci return register_qdisc(&multiq_qdisc_ops); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void __exit multiq_module_exit(void) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci unregister_qdisc(&multiq_qdisc_ops); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cimodule_init(multiq_module_init) 4188c2ecf20Sopenharmony_cimodule_exit(multiq_module_exit) 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 421