• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* net/sched/sch_ingress.c - Ingress and clsact qdisc
3  *
4  * Authors:     Jamal Hadi Salim 1999
5  */
6 
7 #include <linux/module.h>
8 #include <linux/types.h>
9 #include <linux/list.h>
10 #include <linux/skbuff.h>
11 #include <linux/rtnetlink.h>
12 
13 #include <net/netlink.h>
14 #include <net/pkt_sched.h>
15 #include <net/pkt_cls.h>
16 
17 struct ingress_sched_data {
18 	struct tcf_block *block;
19 	struct tcf_block_ext_info block_info;
20 	struct mini_Qdisc_pair miniqp;
21 };
22 
ingress_leaf(struct Qdisc * sch,unsigned long arg)23 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
24 {
25 	return NULL;
26 }
27 
ingress_find(struct Qdisc * sch,u32 classid)28 static unsigned long ingress_find(struct Qdisc *sch, u32 classid)
29 {
30 	return TC_H_MIN(classid) + 1;
31 }
32 
ingress_bind_filter(struct Qdisc * sch,unsigned long parent,u32 classid)33 static unsigned long ingress_bind_filter(struct Qdisc *sch,
34 					 unsigned long parent, u32 classid)
35 {
36 	return ingress_find(sch, classid);
37 }
38 
ingress_unbind_filter(struct Qdisc * sch,unsigned long cl)39 static void ingress_unbind_filter(struct Qdisc *sch, unsigned long cl)
40 {
41 }
42 
ingress_walk(struct Qdisc * sch,struct qdisc_walker * walker)43 static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker)
44 {
45 }
46 
ingress_tcf_block(struct Qdisc * sch,unsigned long cl,struct netlink_ext_ack * extack)47 static struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl,
48 					   struct netlink_ext_ack *extack)
49 {
50 	struct ingress_sched_data *q = qdisc_priv(sch);
51 
52 	return q->block;
53 }
54 
clsact_chain_head_change(struct tcf_proto * tp_head,void * priv)55 static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv)
56 {
57 	struct mini_Qdisc_pair *miniqp = priv;
58 
59 	mini_qdisc_pair_swap(miniqp, tp_head);
60 };
61 
ingress_ingress_block_set(struct Qdisc * sch,u32 block_index)62 static void ingress_ingress_block_set(struct Qdisc *sch, u32 block_index)
63 {
64 	struct ingress_sched_data *q = qdisc_priv(sch);
65 
66 	q->block_info.block_index = block_index;
67 }
68 
ingress_ingress_block_get(struct Qdisc * sch)69 static u32 ingress_ingress_block_get(struct Qdisc *sch)
70 {
71 	struct ingress_sched_data *q = qdisc_priv(sch);
72 
73 	return q->block_info.block_index;
74 }
75 
ingress_init(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)76 static int ingress_init(struct Qdisc *sch, struct nlattr *opt,
77 			struct netlink_ext_ack *extack)
78 {
79 	struct ingress_sched_data *q = qdisc_priv(sch);
80 	struct net_device *dev = qdisc_dev(sch);
81 
82 	if (sch->parent != TC_H_INGRESS)
83 		return -EOPNOTSUPP;
84 
85 	net_inc_ingress_queue();
86 
87 	mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress);
88 
89 	q->block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
90 	q->block_info.chain_head_change = clsact_chain_head_change;
91 	q->block_info.chain_head_change_priv = &q->miniqp;
92 
93 	return tcf_block_get_ext(&q->block, sch, &q->block_info, extack);
94 }
95 
ingress_destroy(struct Qdisc * sch)96 static void ingress_destroy(struct Qdisc *sch)
97 {
98 	struct ingress_sched_data *q = qdisc_priv(sch);
99 
100 	if (sch->parent != TC_H_INGRESS)
101 		return;
102 
103 	tcf_block_put_ext(q->block, sch, &q->block_info);
104 	net_dec_ingress_queue();
105 }
106 
ingress_dump(struct Qdisc * sch,struct sk_buff * skb)107 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
108 {
109 	struct nlattr *nest;
110 
111 	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
112 	if (nest == NULL)
113 		goto nla_put_failure;
114 
115 	return nla_nest_end(skb, nest);
116 
117 nla_put_failure:
118 	nla_nest_cancel(skb, nest);
119 	return -1;
120 }
121 
122 static const struct Qdisc_class_ops ingress_class_ops = {
123 	.flags		=	QDISC_CLASS_OPS_DOIT_UNLOCKED,
124 	.leaf		=	ingress_leaf,
125 	.find		=	ingress_find,
126 	.walk		=	ingress_walk,
127 	.tcf_block	=	ingress_tcf_block,
128 	.bind_tcf	=	ingress_bind_filter,
129 	.unbind_tcf	=	ingress_unbind_filter,
130 };
131 
132 static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
133 	.cl_ops			=	&ingress_class_ops,
134 	.id			=	"ingress",
135 	.priv_size		=	sizeof(struct ingress_sched_data),
136 	.static_flags		=	TCQ_F_INGRESS | TCQ_F_CPUSTATS,
137 	.init			=	ingress_init,
138 	.destroy		=	ingress_destroy,
139 	.dump			=	ingress_dump,
140 	.ingress_block_set	=	ingress_ingress_block_set,
141 	.ingress_block_get	=	ingress_ingress_block_get,
142 	.owner			=	THIS_MODULE,
143 };
144 
145 struct clsact_sched_data {
146 	struct tcf_block *ingress_block;
147 	struct tcf_block *egress_block;
148 	struct tcf_block_ext_info ingress_block_info;
149 	struct tcf_block_ext_info egress_block_info;
150 	struct mini_Qdisc_pair miniqp_ingress;
151 	struct mini_Qdisc_pair miniqp_egress;
152 };
153 
clsact_find(struct Qdisc * sch,u32 classid)154 static unsigned long clsact_find(struct Qdisc *sch, u32 classid)
155 {
156 	switch (TC_H_MIN(classid)) {
157 	case TC_H_MIN(TC_H_MIN_INGRESS):
158 	case TC_H_MIN(TC_H_MIN_EGRESS):
159 		return TC_H_MIN(classid);
160 	default:
161 		return 0;
162 	}
163 }
164 
clsact_bind_filter(struct Qdisc * sch,unsigned long parent,u32 classid)165 static unsigned long clsact_bind_filter(struct Qdisc *sch,
166 					unsigned long parent, u32 classid)
167 {
168 	return clsact_find(sch, classid);
169 }
170 
clsact_tcf_block(struct Qdisc * sch,unsigned long cl,struct netlink_ext_ack * extack)171 static struct tcf_block *clsact_tcf_block(struct Qdisc *sch, unsigned long cl,
172 					  struct netlink_ext_ack *extack)
173 {
174 	struct clsact_sched_data *q = qdisc_priv(sch);
175 
176 	switch (cl) {
177 	case TC_H_MIN(TC_H_MIN_INGRESS):
178 		return q->ingress_block;
179 	case TC_H_MIN(TC_H_MIN_EGRESS):
180 		return q->egress_block;
181 	default:
182 		return NULL;
183 	}
184 }
185 
clsact_ingress_block_set(struct Qdisc * sch,u32 block_index)186 static void clsact_ingress_block_set(struct Qdisc *sch, u32 block_index)
187 {
188 	struct clsact_sched_data *q = qdisc_priv(sch);
189 
190 	q->ingress_block_info.block_index = block_index;
191 }
192 
clsact_egress_block_set(struct Qdisc * sch,u32 block_index)193 static void clsact_egress_block_set(struct Qdisc *sch, u32 block_index)
194 {
195 	struct clsact_sched_data *q = qdisc_priv(sch);
196 
197 	q->egress_block_info.block_index = block_index;
198 }
199 
clsact_ingress_block_get(struct Qdisc * sch)200 static u32 clsact_ingress_block_get(struct Qdisc *sch)
201 {
202 	struct clsact_sched_data *q = qdisc_priv(sch);
203 
204 	return q->ingress_block_info.block_index;
205 }
206 
clsact_egress_block_get(struct Qdisc * sch)207 static u32 clsact_egress_block_get(struct Qdisc *sch)
208 {
209 	struct clsact_sched_data *q = qdisc_priv(sch);
210 
211 	return q->egress_block_info.block_index;
212 }
213 
clsact_init(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)214 static int clsact_init(struct Qdisc *sch, struct nlattr *opt,
215 		       struct netlink_ext_ack *extack)
216 {
217 	struct clsact_sched_data *q = qdisc_priv(sch);
218 	struct net_device *dev = qdisc_dev(sch);
219 	int err;
220 
221 	if (sch->parent != TC_H_CLSACT)
222 		return -EOPNOTSUPP;
223 
224 	net_inc_ingress_queue();
225 	net_inc_egress_queue();
226 
227 	mini_qdisc_pair_init(&q->miniqp_ingress, sch, &dev->miniq_ingress);
228 
229 	q->ingress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
230 	q->ingress_block_info.chain_head_change = clsact_chain_head_change;
231 	q->ingress_block_info.chain_head_change_priv = &q->miniqp_ingress;
232 
233 	err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info,
234 				extack);
235 	if (err)
236 		return err;
237 
238 	mini_qdisc_pair_init(&q->miniqp_egress, sch, &dev->miniq_egress);
239 
240 	q->egress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS;
241 	q->egress_block_info.chain_head_change = clsact_chain_head_change;
242 	q->egress_block_info.chain_head_change_priv = &q->miniqp_egress;
243 
244 	return tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info, extack);
245 }
246 
clsact_destroy(struct Qdisc * sch)247 static void clsact_destroy(struct Qdisc *sch)
248 {
249 	struct clsact_sched_data *q = qdisc_priv(sch);
250 
251 	if (sch->parent != TC_H_CLSACT)
252 		return;
253 
254 	tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info);
255 	tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info);
256 
257 	net_dec_ingress_queue();
258 	net_dec_egress_queue();
259 }
260 
261 static const struct Qdisc_class_ops clsact_class_ops = {
262 	.flags		=	QDISC_CLASS_OPS_DOIT_UNLOCKED,
263 	.leaf		=	ingress_leaf,
264 	.find		=	clsact_find,
265 	.walk		=	ingress_walk,
266 	.tcf_block	=	clsact_tcf_block,
267 	.bind_tcf	=	clsact_bind_filter,
268 	.unbind_tcf	=	ingress_unbind_filter,
269 };
270 
271 static struct Qdisc_ops clsact_qdisc_ops __read_mostly = {
272 	.cl_ops			=	&clsact_class_ops,
273 	.id			=	"clsact",
274 	.priv_size		=	sizeof(struct clsact_sched_data),
275 	.static_flags		=	TCQ_F_INGRESS | TCQ_F_CPUSTATS,
276 	.init			=	clsact_init,
277 	.destroy		=	clsact_destroy,
278 	.dump			=	ingress_dump,
279 	.ingress_block_set	=	clsact_ingress_block_set,
280 	.egress_block_set	=	clsact_egress_block_set,
281 	.ingress_block_get	=	clsact_ingress_block_get,
282 	.egress_block_get	=	clsact_egress_block_get,
283 	.owner			=	THIS_MODULE,
284 };
285 
ingress_module_init(void)286 static int __init ingress_module_init(void)
287 {
288 	int ret;
289 
290 	ret = register_qdisc(&ingress_qdisc_ops);
291 	if (!ret) {
292 		ret = register_qdisc(&clsact_qdisc_ops);
293 		if (ret)
294 			unregister_qdisc(&ingress_qdisc_ops);
295 	}
296 
297 	return ret;
298 }
299 
ingress_module_exit(void)300 static void __exit ingress_module_exit(void)
301 {
302 	unregister_qdisc(&ingress_qdisc_ops);
303 	unregister_qdisc(&clsact_qdisc_ops);
304 }
305 
306 module_init(ingress_module_init);
307 module_exit(ingress_module_exit);
308 
309 MODULE_ALIAS("sch_clsact");
310 MODULE_LICENSE("GPL");
311