1 /* net/sched/sch_ingress.c - Ingress and clsact qdisc 2 * 3 * This program is free software; you can redistribute it and/or 4 * modify it under the terms of the GNU General Public License 5 * as published by the Free Software Foundation; either version 6 * 2 of the License, or (at your option) any later version. 7 * 8 * Authors: Jamal Hadi Salim 1999 9 */ 10 11 #include <linux/module.h> 12 #include <linux/types.h> 13 #include <linux/list.h> 14 #include <linux/skbuff.h> 15 #include <linux/rtnetlink.h> 16 17 #include <net/netlink.h> 18 #include <net/pkt_sched.h> 19 #include <net/pkt_cls.h> 20 21 struct ingress_sched_data { 22 struct tcf_block *block; 23 struct tcf_block_ext_info block_info; 24 struct mini_Qdisc_pair miniqp; 25 }; 26 27 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) 28 { 29 return NULL; 30 } 31 32 static unsigned long ingress_find(struct Qdisc *sch, u32 classid) 33 { 34 return TC_H_MIN(classid) + 1; 35 } 36 37 static unsigned long ingress_bind_filter(struct Qdisc *sch, 38 unsigned long parent, u32 classid) 39 { 40 return ingress_find(sch, classid); 41 } 42 43 static void ingress_unbind_filter(struct Qdisc *sch, unsigned long cl) 44 { 45 } 46 47 static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker) 48 { 49 } 50 51 static struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl) 52 { 53 struct ingress_sched_data *q = qdisc_priv(sch); 54 55 return q->block; 56 } 57 58 static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) 59 { 60 struct mini_Qdisc_pair *miniqp = priv; 61 62 mini_qdisc_pair_swap(miniqp, tp_head); 63 } 64 65 static int ingress_init(struct Qdisc *sch, struct nlattr *opt) 66 { 67 struct ingress_sched_data *q = qdisc_priv(sch); 68 struct net_device *dev = qdisc_dev(sch); 69 int err; 70 71 mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress); 72 73 q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; 74 q->block_info.chain_head_change = clsact_chain_head_change; 75 q->block_info.chain_head_change_priv = &q->miniqp; 76 77 err = tcf_block_get_ext(&q->block, sch, &q->block_info); 78 if (err) 79 return err; 80 81 net_inc_ingress_queue(); 82 sch->flags |= TCQ_F_CPUSTATS; 83 84 return 0; 85 } 86 87 static void ingress_destroy(struct Qdisc *sch) 88 { 89 struct ingress_sched_data *q = qdisc_priv(sch); 90 91 tcf_block_put_ext(q->block, sch, &q->block_info); 92 net_dec_ingress_queue(); 93 } 94 95 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb) 96 { 97 struct nlattr *nest; 98 99 nest = nla_nest_start(skb, TCA_OPTIONS); 100 if (nest == NULL) 101 goto nla_put_failure; 102 103 return nla_nest_end(skb, nest); 104 105 nla_put_failure: 106 nla_nest_cancel(skb, nest); 107 return -1; 108 } 109 110 static const struct Qdisc_class_ops ingress_class_ops = { 111 .leaf = ingress_leaf, 112 .find = ingress_find, 113 .walk = ingress_walk, 114 .tcf_block = ingress_tcf_block, 115 .bind_tcf = ingress_bind_filter, 116 .unbind_tcf = ingress_unbind_filter, 117 }; 118 119 static struct Qdisc_ops ingress_qdisc_ops __read_mostly = { 120 .cl_ops = &ingress_class_ops, 121 .id = "ingress", 122 .priv_size = sizeof(struct ingress_sched_data), 123 .init = ingress_init, 124 .destroy = ingress_destroy, 125 .dump = ingress_dump, 126 .owner = THIS_MODULE, 127 }; 128 129 struct clsact_sched_data { 130 struct tcf_block *ingress_block; 131 struct tcf_block *egress_block; 132 struct tcf_block_ext_info ingress_block_info; 133 struct tcf_block_ext_info egress_block_info; 134 struct mini_Qdisc_pair miniqp_ingress; 135 struct mini_Qdisc_pair miniqp_egress; 136 }; 137 138 static unsigned long clsact_find(struct Qdisc *sch, u32 classid) 139 { 140 switch (TC_H_MIN(classid)) { 141 case TC_H_MIN(TC_H_MIN_INGRESS): 142 case TC_H_MIN(TC_H_MIN_EGRESS): 143 return TC_H_MIN(classid); 144 default: 145 return 0; 146 } 147 } 148 149 static unsigned long clsact_bind_filter(struct Qdisc *sch, 150 unsigned long parent, u32 classid) 151 { 152 return clsact_find(sch, classid); 153 } 154 155 static struct tcf_block *clsact_tcf_block(struct Qdisc *sch, unsigned long cl) 156 { 157 struct clsact_sched_data *q = qdisc_priv(sch); 158 159 switch (cl) { 160 case TC_H_MIN(TC_H_MIN_INGRESS): 161 return q->ingress_block; 162 case TC_H_MIN(TC_H_MIN_EGRESS): 163 return q->egress_block; 164 default: 165 return NULL; 166 } 167 } 168 169 static int clsact_init(struct Qdisc *sch, struct nlattr *opt) 170 { 171 struct clsact_sched_data *q = qdisc_priv(sch); 172 struct net_device *dev = qdisc_dev(sch); 173 int err; 174 175 mini_qdisc_pair_init(&q->miniqp_ingress, sch, &dev->miniq_ingress); 176 177 q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; 178 q->ingress_block_info.chain_head_change = clsact_chain_head_change; 179 q->ingress_block_info.chain_head_change_priv = &q->miniqp_ingress; 180 181 err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info); 182 if (err) 183 return err; 184 185 mini_qdisc_pair_init(&q->miniqp_egress, sch, &dev->miniq_egress); 186 187 q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS; 188 q->egress_block_info.chain_head_change = clsact_chain_head_change; 189 q->egress_block_info.chain_head_change_priv = &q->miniqp_egress; 190 191 err = tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info); 192 if (err) 193 goto err_egress_block_get; 194 195 net_inc_ingress_queue(); 196 net_inc_egress_queue(); 197 198 sch->flags |= TCQ_F_CPUSTATS; 199 200 return 0; 201 202 err_egress_block_get: 203 tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); 204 return err; 205 } 206 207 static void clsact_destroy(struct Qdisc *sch) 208 { 209 struct clsact_sched_data *q = qdisc_priv(sch); 210 211 tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info); 212 tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); 213 214 net_dec_ingress_queue(); 215 net_dec_egress_queue(); 216 } 217 218 static const struct Qdisc_class_ops clsact_class_ops = { 219 .leaf = ingress_leaf, 220 .find = clsact_find, 221 .walk = ingress_walk, 222 .tcf_block = clsact_tcf_block, 223 .bind_tcf = clsact_bind_filter, 224 .unbind_tcf = ingress_unbind_filter, 225 }; 226 227 static struct Qdisc_ops clsact_qdisc_ops __read_mostly = { 228 .cl_ops = &clsact_class_ops, 229 .id = "clsact", 230 .priv_size = sizeof(struct clsact_sched_data), 231 .init = clsact_init, 232 .destroy = clsact_destroy, 233 .dump = ingress_dump, 234 .owner = THIS_MODULE, 235 }; 236 237 static int __init ingress_module_init(void) 238 { 239 int ret; 240 241 ret = register_qdisc(&ingress_qdisc_ops); 242 if (!ret) { 243 ret = register_qdisc(&clsact_qdisc_ops); 244 if (ret) 245 unregister_qdisc(&ingress_qdisc_ops); 246 } 247 248 return ret; 249 } 250 251 static void __exit ingress_module_exit(void) 252 { 253 unregister_qdisc(&ingress_qdisc_ops); 254 unregister_qdisc(&clsact_qdisc_ops); 255 } 256 257 module_init(ingress_module_init); 258 module_exit(ingress_module_exit); 259 260 MODULE_ALIAS("sch_clsact"); 261 MODULE_LICENSE("GPL"); 262