18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* net/sched/sch_ingress.c - Ingress and clsact qdisc 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Authors: Jamal Hadi Salim 1999 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <net/netlink.h> 148c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 158c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct ingress_sched_data { 188c2ecf20Sopenharmony_ci struct tcf_block *block; 198c2ecf20Sopenharmony_ci struct tcf_block_ext_info block_info; 208c2ecf20Sopenharmony_ci struct mini_Qdisc_pair miniqp; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci return NULL; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic unsigned long ingress_find(struct Qdisc *sch, u32 classid) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return TC_H_MIN(classid) + 1; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic unsigned long ingress_bind_filter(struct Qdisc *sch, 348c2ecf20Sopenharmony_ci unsigned long parent, u32 classid) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci return ingress_find(sch, classid); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void ingress_unbind_filter(struct Qdisc *sch, unsigned long cl) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl, 488c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct ingress_sched_data *q = qdisc_priv(sch); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return q->block; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct mini_Qdisc_pair *miniqp = priv; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mini_qdisc_pair_swap(miniqp, tp_head); 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void ingress_ingress_block_set(struct Qdisc *sch, u32 block_index) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct ingress_sched_data *q = qdisc_priv(sch); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci q->block_info.block_index = block_index; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic u32 ingress_ingress_block_get(struct Qdisc *sch) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct ingress_sched_data *q = qdisc_priv(sch); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return q->block_info.block_index; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int ingress_init(struct Qdisc *sch, struct nlattr *opt, 778c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct ingress_sched_data *q = qdisc_priv(sch); 808c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 818c2ecf20Sopenharmony_ci int err; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (sch->parent != TC_H_INGRESS) 848c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci net_inc_ingress_queue(); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci q->block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS; 918c2ecf20Sopenharmony_ci q->block_info.chain_head_change = clsact_chain_head_change; 928c2ecf20Sopenharmony_ci q->block_info.chain_head_change_priv = &q->miniqp; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci err = tcf_block_get_ext(&q->block, sch, &q->block_info, extack); 958c2ecf20Sopenharmony_ci if (err) 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci mini_qdisc_pair_block_init(&q->miniqp, q->block); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void ingress_destroy(struct Qdisc *sch) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct ingress_sched_data *q = qdisc_priv(sch); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (sch->parent != TC_H_INGRESS) 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci tcf_block_put_ext(q->block, sch, &q->block_info); 1118c2ecf20Sopenharmony_ci net_dec_ingress_queue(); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int ingress_dump(struct Qdisc *sch, struct sk_buff *skb) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct nlattr *nest; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 1198c2ecf20Sopenharmony_ci if (nest == NULL) 1208c2ecf20Sopenharmony_ci goto nla_put_failure; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return nla_nest_end(skb, nest); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cinla_put_failure: 1258c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 1268c2ecf20Sopenharmony_ci return -1; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops ingress_class_ops = { 1308c2ecf20Sopenharmony_ci .flags = QDISC_CLASS_OPS_DOIT_UNLOCKED, 1318c2ecf20Sopenharmony_ci .leaf = ingress_leaf, 1328c2ecf20Sopenharmony_ci .find = ingress_find, 1338c2ecf20Sopenharmony_ci .walk = ingress_walk, 1348c2ecf20Sopenharmony_ci .tcf_block = ingress_tcf_block, 1358c2ecf20Sopenharmony_ci .bind_tcf = ingress_bind_filter, 1368c2ecf20Sopenharmony_ci .unbind_tcf = ingress_unbind_filter, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct Qdisc_ops ingress_qdisc_ops __read_mostly = { 1408c2ecf20Sopenharmony_ci .cl_ops = &ingress_class_ops, 1418c2ecf20Sopenharmony_ci .id = "ingress", 1428c2ecf20Sopenharmony_ci .priv_size = sizeof(struct ingress_sched_data), 1438c2ecf20Sopenharmony_ci .static_flags = TCQ_F_INGRESS | TCQ_F_CPUSTATS, 1448c2ecf20Sopenharmony_ci .init = ingress_init, 1458c2ecf20Sopenharmony_ci .destroy = ingress_destroy, 1468c2ecf20Sopenharmony_ci .dump = ingress_dump, 1478c2ecf20Sopenharmony_ci .ingress_block_set = ingress_ingress_block_set, 1488c2ecf20Sopenharmony_ci .ingress_block_get = ingress_ingress_block_get, 1498c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistruct clsact_sched_data { 1538c2ecf20Sopenharmony_ci struct tcf_block *ingress_block; 1548c2ecf20Sopenharmony_ci struct tcf_block *egress_block; 1558c2ecf20Sopenharmony_ci struct tcf_block_ext_info ingress_block_info; 1568c2ecf20Sopenharmony_ci struct tcf_block_ext_info egress_block_info; 1578c2ecf20Sopenharmony_ci struct mini_Qdisc_pair miniqp_ingress; 1588c2ecf20Sopenharmony_ci struct mini_Qdisc_pair miniqp_egress; 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic unsigned long clsact_find(struct Qdisc *sch, u32 classid) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci switch (TC_H_MIN(classid)) { 1648c2ecf20Sopenharmony_ci case TC_H_MIN(TC_H_MIN_INGRESS): 1658c2ecf20Sopenharmony_ci case TC_H_MIN(TC_H_MIN_EGRESS): 1668c2ecf20Sopenharmony_ci return TC_H_MIN(classid); 1678c2ecf20Sopenharmony_ci default: 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic unsigned long clsact_bind_filter(struct Qdisc *sch, 1738c2ecf20Sopenharmony_ci unsigned long parent, u32 classid) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci return clsact_find(sch, classid); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct tcf_block *clsact_tcf_block(struct Qdisc *sch, unsigned long cl, 1798c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci switch (cl) { 1848c2ecf20Sopenharmony_ci case TC_H_MIN(TC_H_MIN_INGRESS): 1858c2ecf20Sopenharmony_ci return q->ingress_block; 1868c2ecf20Sopenharmony_ci case TC_H_MIN(TC_H_MIN_EGRESS): 1878c2ecf20Sopenharmony_ci return q->egress_block; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci return NULL; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void clsact_ingress_block_set(struct Qdisc *sch, u32 block_index) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci q->ingress_block_info.block_index = block_index; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void clsact_egress_block_set(struct Qdisc *sch, u32 block_index) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci q->egress_block_info.block_index = block_index; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic u32 clsact_ingress_block_get(struct Qdisc *sch) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return q->ingress_block_info.block_index; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic u32 clsact_egress_block_get(struct Qdisc *sch) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return q->egress_block_info.block_index; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int clsact_init(struct Qdisc *sch, struct nlattr *opt, 2228c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 2258c2ecf20Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 2268c2ecf20Sopenharmony_ci int err; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (sch->parent != TC_H_CLSACT) 2298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci net_inc_ingress_queue(); 2328c2ecf20Sopenharmony_ci net_inc_egress_queue(); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci mini_qdisc_pair_init(&q->miniqp_ingress, sch, &dev->miniq_ingress); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci q->ingress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS; 2378c2ecf20Sopenharmony_ci q->ingress_block_info.chain_head_change = clsact_chain_head_change; 2388c2ecf20Sopenharmony_ci q->ingress_block_info.chain_head_change_priv = &q->miniqp_ingress; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info, 2418c2ecf20Sopenharmony_ci extack); 2428c2ecf20Sopenharmony_ci if (err) 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci mini_qdisc_pair_block_init(&q->miniqp_ingress, q->ingress_block); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci mini_qdisc_pair_init(&q->miniqp_egress, sch, &dev->miniq_egress); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci q->egress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS; 2508c2ecf20Sopenharmony_ci q->egress_block_info.chain_head_change = clsact_chain_head_change; 2518c2ecf20Sopenharmony_ci q->egress_block_info.chain_head_change_priv = &q->miniqp_egress; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info, extack); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void clsact_destroy(struct Qdisc *sch) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct clsact_sched_data *q = qdisc_priv(sch); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (sch->parent != TC_H_CLSACT) 2618c2ecf20Sopenharmony_ci return; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info); 2648c2ecf20Sopenharmony_ci tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci net_dec_ingress_queue(); 2678c2ecf20Sopenharmony_ci net_dec_egress_queue(); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops clsact_class_ops = { 2718c2ecf20Sopenharmony_ci .flags = QDISC_CLASS_OPS_DOIT_UNLOCKED, 2728c2ecf20Sopenharmony_ci .leaf = ingress_leaf, 2738c2ecf20Sopenharmony_ci .find = clsact_find, 2748c2ecf20Sopenharmony_ci .walk = ingress_walk, 2758c2ecf20Sopenharmony_ci .tcf_block = clsact_tcf_block, 2768c2ecf20Sopenharmony_ci .bind_tcf = clsact_bind_filter, 2778c2ecf20Sopenharmony_ci .unbind_tcf = ingress_unbind_filter, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic struct Qdisc_ops clsact_qdisc_ops __read_mostly = { 2818c2ecf20Sopenharmony_ci .cl_ops = &clsact_class_ops, 2828c2ecf20Sopenharmony_ci .id = "clsact", 2838c2ecf20Sopenharmony_ci .priv_size = sizeof(struct clsact_sched_data), 2848c2ecf20Sopenharmony_ci .static_flags = TCQ_F_INGRESS | TCQ_F_CPUSTATS, 2858c2ecf20Sopenharmony_ci .init = clsact_init, 2868c2ecf20Sopenharmony_ci .destroy = clsact_destroy, 2878c2ecf20Sopenharmony_ci .dump = ingress_dump, 2888c2ecf20Sopenharmony_ci .ingress_block_set = clsact_ingress_block_set, 2898c2ecf20Sopenharmony_ci .egress_block_set = clsact_egress_block_set, 2908c2ecf20Sopenharmony_ci .ingress_block_get = clsact_ingress_block_get, 2918c2ecf20Sopenharmony_ci .egress_block_get = clsact_egress_block_get, 2928c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int __init ingress_module_init(void) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci int ret; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ret = register_qdisc(&ingress_qdisc_ops); 3008c2ecf20Sopenharmony_ci if (!ret) { 3018c2ecf20Sopenharmony_ci ret = register_qdisc(&clsact_qdisc_ops); 3028c2ecf20Sopenharmony_ci if (ret) 3038c2ecf20Sopenharmony_ci unregister_qdisc(&ingress_qdisc_ops); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic void __exit ingress_module_exit(void) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci unregister_qdisc(&ingress_qdisc_ops); 3128c2ecf20Sopenharmony_ci unregister_qdisc(&clsact_qdisc_ops); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cimodule_init(ingress_module_init); 3168c2ecf20Sopenharmony_cimodule_exit(ingress_module_exit); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ciMODULE_ALIAS("sch_clsact"); 3198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 320