Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0 */
2 : : #include <linux/kernel.h>
3 : : #include <linux/slab.h>
4 : : #include <net/flow_offload.h>
5 : : #include <linux/rtnetlink.h>
6 : : #include <linux/mutex.h>
7 : :
8 : 0 : struct flow_rule *flow_rule_alloc(unsigned int num_actions)
9 : : {
10 : 0 : struct flow_rule *rule;
11 : :
12 [ # # ]: 0 : rule = kzalloc(struct_size(rule, action.entries, num_actions),
13 : : GFP_KERNEL);
14 [ # # ]: 0 : if (!rule)
15 : : return NULL;
16 : :
17 : 0 : rule->action.num_entries = num_actions;
18 : :
19 : 0 : return rule;
20 : : }
21 : : EXPORT_SYMBOL(flow_rule_alloc);
22 : :
23 : : #define FLOW_DISSECTOR_MATCH(__rule, __type, __out) \
24 : : const struct flow_match *__m = &(__rule)->match; \
25 : : struct flow_dissector *__d = (__m)->dissector; \
26 : : \
27 : : (__out)->key = skb_flow_dissector_target(__d, __type, (__m)->key); \
28 : : (__out)->mask = skb_flow_dissector_target(__d, __type, (__m)->mask); \
29 : :
30 : 0 : void flow_rule_match_meta(const struct flow_rule *rule,
31 : : struct flow_match_meta *out)
32 : : {
33 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_META, out);
34 : 0 : }
35 : : EXPORT_SYMBOL(flow_rule_match_meta);
36 : :
37 : 0 : void flow_rule_match_basic(const struct flow_rule *rule,
38 : : struct flow_match_basic *out)
39 : : {
40 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_BASIC, out);
41 : 0 : }
42 : : EXPORT_SYMBOL(flow_rule_match_basic);
43 : :
44 : 0 : void flow_rule_match_control(const struct flow_rule *rule,
45 : : struct flow_match_control *out)
46 : : {
47 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CONTROL, out);
48 : 0 : }
49 : : EXPORT_SYMBOL(flow_rule_match_control);
50 : :
51 : 0 : void flow_rule_match_eth_addrs(const struct flow_rule *rule,
52 : : struct flow_match_eth_addrs *out)
53 : : {
54 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS, out);
55 : 0 : }
56 : : EXPORT_SYMBOL(flow_rule_match_eth_addrs);
57 : :
58 : 0 : void flow_rule_match_vlan(const struct flow_rule *rule,
59 : : struct flow_match_vlan *out)
60 : : {
61 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_VLAN, out);
62 : 0 : }
63 : : EXPORT_SYMBOL(flow_rule_match_vlan);
64 : :
65 : 0 : void flow_rule_match_cvlan(const struct flow_rule *rule,
66 : : struct flow_match_vlan *out)
67 : : {
68 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CVLAN, out);
69 : 0 : }
70 : : EXPORT_SYMBOL(flow_rule_match_cvlan);
71 : :
72 : 0 : void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
73 : : struct flow_match_ipv4_addrs *out)
74 : : {
75 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS, out);
76 : 0 : }
77 : : EXPORT_SYMBOL(flow_rule_match_ipv4_addrs);
78 : :
79 : 0 : void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
80 : : struct flow_match_ipv6_addrs *out)
81 : : {
82 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS, out);
83 : 0 : }
84 : : EXPORT_SYMBOL(flow_rule_match_ipv6_addrs);
85 : :
86 : 0 : void flow_rule_match_ip(const struct flow_rule *rule,
87 : : struct flow_match_ip *out)
88 : : {
89 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IP, out);
90 : 0 : }
91 : : EXPORT_SYMBOL(flow_rule_match_ip);
92 : :
93 : 0 : void flow_rule_match_ports(const struct flow_rule *rule,
94 : : struct flow_match_ports *out)
95 : : {
96 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS, out);
97 : 0 : }
98 : : EXPORT_SYMBOL(flow_rule_match_ports);
99 : :
100 : 0 : void flow_rule_match_tcp(const struct flow_rule *rule,
101 : : struct flow_match_tcp *out)
102 : : {
103 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_TCP, out);
104 : 0 : }
105 : : EXPORT_SYMBOL(flow_rule_match_tcp);
106 : :
107 : 0 : void flow_rule_match_icmp(const struct flow_rule *rule,
108 : : struct flow_match_icmp *out)
109 : : {
110 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ICMP, out);
111 : 0 : }
112 : : EXPORT_SYMBOL(flow_rule_match_icmp);
113 : :
114 : 0 : void flow_rule_match_mpls(const struct flow_rule *rule,
115 : : struct flow_match_mpls *out)
116 : : {
117 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_MPLS, out);
118 : 0 : }
119 : : EXPORT_SYMBOL(flow_rule_match_mpls);
120 : :
121 : 0 : void flow_rule_match_enc_control(const struct flow_rule *rule,
122 : : struct flow_match_control *out)
123 : : {
124 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL, out);
125 : 0 : }
126 : : EXPORT_SYMBOL(flow_rule_match_enc_control);
127 : :
128 : 0 : void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule,
129 : : struct flow_match_ipv4_addrs *out)
130 : : {
131 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, out);
132 : 0 : }
133 : : EXPORT_SYMBOL(flow_rule_match_enc_ipv4_addrs);
134 : :
135 : 0 : void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule,
136 : : struct flow_match_ipv6_addrs *out)
137 : : {
138 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, out);
139 : 0 : }
140 : : EXPORT_SYMBOL(flow_rule_match_enc_ipv6_addrs);
141 : :
142 : 0 : void flow_rule_match_enc_ip(const struct flow_rule *rule,
143 : : struct flow_match_ip *out)
144 : : {
145 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IP, out);
146 : 0 : }
147 : : EXPORT_SYMBOL(flow_rule_match_enc_ip);
148 : :
149 : 0 : void flow_rule_match_enc_ports(const struct flow_rule *rule,
150 : : struct flow_match_ports *out)
151 : : {
152 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_PORTS, out);
153 : 0 : }
154 : : EXPORT_SYMBOL(flow_rule_match_enc_ports);
155 : :
156 : 0 : void flow_rule_match_enc_keyid(const struct flow_rule *rule,
157 : : struct flow_match_enc_keyid *out)
158 : : {
159 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_KEYID, out);
160 : 0 : }
161 : : EXPORT_SYMBOL(flow_rule_match_enc_keyid);
162 : :
163 : 0 : void flow_rule_match_enc_opts(const struct flow_rule *rule,
164 : : struct flow_match_enc_opts *out)
165 : : {
166 : 0 : FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_OPTS, out);
167 : 0 : }
168 : : EXPORT_SYMBOL(flow_rule_match_enc_opts);
169 : :
170 : 0 : struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb,
171 : : void *cb_ident, void *cb_priv,
172 : : void (*release)(void *cb_priv))
173 : : {
174 : 0 : struct flow_block_cb *block_cb;
175 : :
176 : 0 : block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
177 [ # # # # ]: 0 : if (!block_cb)
178 : : return ERR_PTR(-ENOMEM);
179 : :
180 : 0 : block_cb->cb = cb;
181 : 0 : block_cb->cb_ident = cb_ident;
182 : 0 : block_cb->cb_priv = cb_priv;
183 : 0 : block_cb->release = release;
184 : :
185 : 0 : return block_cb;
186 : : }
187 : : EXPORT_SYMBOL(flow_block_cb_alloc);
188 : :
189 : 0 : void flow_block_cb_free(struct flow_block_cb *block_cb)
190 : : {
191 [ # # ]: 0 : if (block_cb->release)
192 : 0 : block_cb->release(block_cb->cb_priv);
193 : :
194 : 0 : kfree(block_cb);
195 : 0 : }
196 : : EXPORT_SYMBOL(flow_block_cb_free);
197 : :
198 : 0 : struct flow_block_cb *flow_block_cb_lookup(struct flow_block *block,
199 : : flow_setup_cb_t *cb, void *cb_ident)
200 : : {
201 : 0 : struct flow_block_cb *block_cb;
202 : :
203 [ # # # # ]: 0 : list_for_each_entry(block_cb, &block->cb_list, list) {
204 [ # # # # ]: 0 : if (block_cb->cb == cb &&
205 [ # # # # ]: 0 : block_cb->cb_ident == cb_ident)
206 : 0 : return block_cb;
207 : : }
208 : :
209 : : return NULL;
210 : : }
211 : : EXPORT_SYMBOL(flow_block_cb_lookup);
212 : :
213 : 0 : void *flow_block_cb_priv(struct flow_block_cb *block_cb)
214 : : {
215 : 0 : return block_cb->cb_priv;
216 : : }
217 : : EXPORT_SYMBOL(flow_block_cb_priv);
218 : :
219 : 0 : void flow_block_cb_incref(struct flow_block_cb *block_cb)
220 : : {
221 : 0 : block_cb->refcnt++;
222 : 0 : }
223 : : EXPORT_SYMBOL(flow_block_cb_incref);
224 : :
225 : 0 : unsigned int flow_block_cb_decref(struct flow_block_cb *block_cb)
226 : : {
227 : 0 : return --block_cb->refcnt;
228 : : }
229 : : EXPORT_SYMBOL(flow_block_cb_decref);
230 : :
231 : 0 : bool flow_block_cb_is_busy(flow_setup_cb_t *cb, void *cb_ident,
232 : : struct list_head *driver_block_list)
233 : : {
234 : 0 : struct flow_block_cb *block_cb;
235 : :
236 [ # # # # ]: 0 : list_for_each_entry(block_cb, driver_block_list, driver_list) {
237 [ # # # # ]: 0 : if (block_cb->cb == cb &&
238 [ # # # # ]: 0 : block_cb->cb_ident == cb_ident)
239 : : return true;
240 : : }
241 : :
242 : : return false;
243 : : }
244 : : EXPORT_SYMBOL(flow_block_cb_is_busy);
245 : :
246 : 0 : int flow_block_cb_setup_simple(struct flow_block_offload *f,
247 : : struct list_head *driver_block_list,
248 : : flow_setup_cb_t *cb,
249 : : void *cb_ident, void *cb_priv,
250 : : bool ingress_only)
251 : : {
252 : 0 : struct flow_block_cb *block_cb;
253 : :
254 [ # # ]: 0 : if (ingress_only &&
255 [ # # ]: 0 : f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
256 : : return -EOPNOTSUPP;
257 : :
258 : 0 : f->driver_block_list = driver_block_list;
259 : :
260 [ # # # ]: 0 : switch (f->command) {
261 : : case FLOW_BLOCK_BIND:
262 [ # # ]: 0 : if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list))
263 : : return -EBUSY;
264 : :
265 : 0 : block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, NULL);
266 [ # # ]: 0 : if (IS_ERR(block_cb))
267 : 0 : return PTR_ERR(block_cb);
268 : :
269 : 0 : flow_block_cb_add(block_cb, f);
270 : 0 : list_add_tail(&block_cb->driver_list, driver_block_list);
271 : 0 : return 0;
272 : 0 : case FLOW_BLOCK_UNBIND:
273 : 0 : block_cb = flow_block_cb_lookup(f->block, cb, cb_ident);
274 [ # # ]: 0 : if (!block_cb)
275 : : return -ENOENT;
276 : :
277 : 0 : flow_block_cb_remove(block_cb, f);
278 : 0 : list_del(&block_cb->driver_list);
279 : 0 : return 0;
280 : : default:
281 : : return -EOPNOTSUPP;
282 : : }
283 : : }
284 : : EXPORT_SYMBOL(flow_block_cb_setup_simple);
285 : :
286 : : static LIST_HEAD(block_cb_list);
287 : :
288 : : static struct rhashtable indr_setup_block_ht;
289 : :
290 : : struct flow_indr_block_cb {
291 : : struct list_head list;
292 : : void *cb_priv;
293 : : flow_indr_block_bind_cb_t *cb;
294 : : void *cb_ident;
295 : : };
296 : :
297 : : struct flow_indr_block_dev {
298 : : struct rhash_head ht_node;
299 : : struct net_device *dev;
300 : : unsigned int refcnt;
301 : : struct list_head cb_list;
302 : : };
303 : :
304 : : static const struct rhashtable_params flow_indr_setup_block_ht_params = {
305 : : .key_offset = offsetof(struct flow_indr_block_dev, dev),
306 : : .head_offset = offsetof(struct flow_indr_block_dev, ht_node),
307 : : .key_len = sizeof(struct net_device *),
308 : : };
309 : :
310 : : static struct flow_indr_block_dev *
311 : 0 : flow_indr_block_dev_lookup(struct net_device *dev)
312 : : {
313 : 0 : return rhashtable_lookup_fast(&indr_setup_block_ht, &dev,
314 : : flow_indr_setup_block_ht_params);
315 : : }
316 : :
317 : : static struct flow_indr_block_dev *
318 : 0 : flow_indr_block_dev_get(struct net_device *dev)
319 : : {
320 : 0 : struct flow_indr_block_dev *indr_dev;
321 : :
322 : 0 : indr_dev = flow_indr_block_dev_lookup(dev);
323 [ # # ]: 0 : if (indr_dev)
324 : 0 : goto inc_ref;
325 : :
326 : 0 : indr_dev = kzalloc(sizeof(*indr_dev), GFP_KERNEL);
327 [ # # ]: 0 : if (!indr_dev)
328 : : return NULL;
329 : :
330 : 0 : INIT_LIST_HEAD(&indr_dev->cb_list);
331 : 0 : indr_dev->dev = dev;
332 [ # # ]: 0 : if (rhashtable_insert_fast(&indr_setup_block_ht, &indr_dev->ht_node,
333 : : flow_indr_setup_block_ht_params)) {
334 : 0 : kfree(indr_dev);
335 : 0 : return NULL;
336 : : }
337 : :
338 : 0 : inc_ref:
339 : 0 : indr_dev->refcnt++;
340 : 0 : return indr_dev;
341 : : }
342 : :
343 : 0 : static void flow_indr_block_dev_put(struct flow_indr_block_dev *indr_dev)
344 : : {
345 [ # # ]: 0 : if (--indr_dev->refcnt)
346 : : return;
347 : :
348 : 0 : rhashtable_remove_fast(&indr_setup_block_ht, &indr_dev->ht_node,
349 : : flow_indr_setup_block_ht_params);
350 : 0 : kfree(indr_dev);
351 : : }
352 : :
353 : : static struct flow_indr_block_cb *
354 : 0 : flow_indr_block_cb_lookup(struct flow_indr_block_dev *indr_dev,
355 : : flow_indr_block_bind_cb_t *cb, void *cb_ident)
356 : : {
357 : 0 : struct flow_indr_block_cb *indr_block_cb;
358 : :
359 [ # # # # ]: 0 : list_for_each_entry(indr_block_cb, &indr_dev->cb_list, list)
360 [ # # # # ]: 0 : if (indr_block_cb->cb == cb &&
361 [ # # # # ]: 0 : indr_block_cb->cb_ident == cb_ident)
362 : : return indr_block_cb;
363 : : return NULL;
364 : : }
365 : :
366 : : static struct flow_indr_block_cb *
367 : 0 : flow_indr_block_cb_add(struct flow_indr_block_dev *indr_dev, void *cb_priv,
368 : : flow_indr_block_bind_cb_t *cb, void *cb_ident)
369 : : {
370 : 0 : struct flow_indr_block_cb *indr_block_cb;
371 : :
372 : 0 : indr_block_cb = flow_indr_block_cb_lookup(indr_dev, cb, cb_ident);
373 [ # # ]: 0 : if (indr_block_cb)
374 : : return ERR_PTR(-EEXIST);
375 : :
376 : 0 : indr_block_cb = kzalloc(sizeof(*indr_block_cb), GFP_KERNEL);
377 [ # # ]: 0 : if (!indr_block_cb)
378 : : return ERR_PTR(-ENOMEM);
379 : :
380 : 0 : indr_block_cb->cb_priv = cb_priv;
381 : 0 : indr_block_cb->cb = cb;
382 : 0 : indr_block_cb->cb_ident = cb_ident;
383 : 0 : list_add(&indr_block_cb->list, &indr_dev->cb_list);
384 : :
385 : 0 : return indr_block_cb;
386 : : }
387 : :
388 : 0 : static void flow_indr_block_cb_del(struct flow_indr_block_cb *indr_block_cb)
389 : : {
390 : 0 : list_del(&indr_block_cb->list);
391 : 0 : kfree(indr_block_cb);
392 : : }
393 : :
394 : : static DEFINE_MUTEX(flow_indr_block_cb_lock);
395 : :
396 : 0 : static void flow_block_cmd(struct net_device *dev,
397 : : flow_indr_block_bind_cb_t *cb, void *cb_priv,
398 : : enum flow_block_command command)
399 : : {
400 : 0 : struct flow_indr_block_entry *entry;
401 : :
402 : 0 : mutex_lock(&flow_indr_block_cb_lock);
403 [ # # ]: 0 : list_for_each_entry(entry, &block_cb_list, list) {
404 : 0 : entry->cb(dev, cb, cb_priv, command);
405 : : }
406 : 0 : mutex_unlock(&flow_indr_block_cb_lock);
407 : 0 : }
408 : :
409 : 0 : int __flow_indr_block_cb_register(struct net_device *dev, void *cb_priv,
410 : : flow_indr_block_bind_cb_t *cb,
411 : : void *cb_ident)
412 : : {
413 : 0 : struct flow_indr_block_cb *indr_block_cb;
414 : 0 : struct flow_indr_block_dev *indr_dev;
415 : 0 : int err;
416 : :
417 : 0 : indr_dev = flow_indr_block_dev_get(dev);
418 [ # # ]: 0 : if (!indr_dev)
419 : : return -ENOMEM;
420 : :
421 : 0 : indr_block_cb = flow_indr_block_cb_add(indr_dev, cb_priv, cb, cb_ident);
422 [ # # ]: 0 : err = PTR_ERR_OR_ZERO(indr_block_cb);
423 [ # # ]: 0 : if (err)
424 : 0 : goto err_dev_put;
425 : :
426 : 0 : flow_block_cmd(dev, indr_block_cb->cb, indr_block_cb->cb_priv,
427 : : FLOW_BLOCK_BIND);
428 : :
429 : 0 : return 0;
430 : :
431 : : err_dev_put:
432 : 0 : flow_indr_block_dev_put(indr_dev);
433 : 0 : return err;
434 : : }
435 : : EXPORT_SYMBOL_GPL(__flow_indr_block_cb_register);
436 : :
437 : 0 : int flow_indr_block_cb_register(struct net_device *dev, void *cb_priv,
438 : : flow_indr_block_bind_cb_t *cb,
439 : : void *cb_ident)
440 : : {
441 : 0 : int err;
442 : :
443 : 0 : rtnl_lock();
444 : 0 : err = __flow_indr_block_cb_register(dev, cb_priv, cb, cb_ident);
445 : 0 : rtnl_unlock();
446 : :
447 : 0 : return err;
448 : : }
449 : : EXPORT_SYMBOL_GPL(flow_indr_block_cb_register);
450 : :
451 : 0 : void __flow_indr_block_cb_unregister(struct net_device *dev,
452 : : flow_indr_block_bind_cb_t *cb,
453 : : void *cb_ident)
454 : : {
455 : 0 : struct flow_indr_block_cb *indr_block_cb;
456 : 0 : struct flow_indr_block_dev *indr_dev;
457 : :
458 : 0 : indr_dev = flow_indr_block_dev_lookup(dev);
459 [ # # ]: 0 : if (!indr_dev)
460 : : return;
461 : :
462 : 0 : indr_block_cb = flow_indr_block_cb_lookup(indr_dev, cb, cb_ident);
463 [ # # ]: 0 : if (!indr_block_cb)
464 : : return;
465 : :
466 : 0 : flow_block_cmd(dev, indr_block_cb->cb, indr_block_cb->cb_priv,
467 : : FLOW_BLOCK_UNBIND);
468 : :
469 : 0 : flow_indr_block_cb_del(indr_block_cb);
470 : 0 : flow_indr_block_dev_put(indr_dev);
471 : : }
472 : : EXPORT_SYMBOL_GPL(__flow_indr_block_cb_unregister);
473 : :
474 : 0 : void flow_indr_block_cb_unregister(struct net_device *dev,
475 : : flow_indr_block_bind_cb_t *cb,
476 : : void *cb_ident)
477 : : {
478 : 0 : rtnl_lock();
479 : 0 : __flow_indr_block_cb_unregister(dev, cb, cb_ident);
480 : 0 : rtnl_unlock();
481 : 0 : }
482 : : EXPORT_SYMBOL_GPL(flow_indr_block_cb_unregister);
483 : :
484 : 0 : void flow_indr_block_call(struct net_device *dev,
485 : : struct flow_block_offload *bo,
486 : : enum flow_block_command command)
487 : : {
488 : 0 : struct flow_indr_block_cb *indr_block_cb;
489 : 0 : struct flow_indr_block_dev *indr_dev;
490 : :
491 : 0 : indr_dev = flow_indr_block_dev_lookup(dev);
492 [ # # ]: 0 : if (!indr_dev)
493 : : return;
494 : :
495 [ # # ]: 0 : list_for_each_entry(indr_block_cb, &indr_dev->cb_list, list)
496 : 0 : indr_block_cb->cb(dev, indr_block_cb->cb_priv, TC_SETUP_BLOCK,
497 : : bo);
498 : : }
499 : : EXPORT_SYMBOL_GPL(flow_indr_block_call);
500 : :
501 : 78 : void flow_indr_add_block_cb(struct flow_indr_block_entry *entry)
502 : : {
503 : 78 : mutex_lock(&flow_indr_block_cb_lock);
504 : 78 : list_add_tail(&entry->list, &block_cb_list);
505 : 78 : mutex_unlock(&flow_indr_block_cb_lock);
506 : 78 : }
507 : : EXPORT_SYMBOL_GPL(flow_indr_add_block_cb);
508 : :
509 : 0 : void flow_indr_del_block_cb(struct flow_indr_block_entry *entry)
510 : : {
511 : 0 : mutex_lock(&flow_indr_block_cb_lock);
512 : 0 : list_del(&entry->list);
513 : 0 : mutex_unlock(&flow_indr_block_cb_lock);
514 : 0 : }
515 : : EXPORT_SYMBOL_GPL(flow_indr_del_block_cb);
516 : :
517 : 78 : static int __init init_flow_indr_rhashtable(void)
518 : : {
519 : 78 : return rhashtable_init(&indr_setup_block_ht,
520 : : &flow_indr_setup_block_ht_params);
521 : : }
522 : : subsys_initcall(init_flow_indr_rhashtable);
|