diff options
Diffstat (limited to 'net/sched/cls_tcindex.c')
-rw-r--r-- | net/sched/cls_tcindex.c | 80 |
1 files changed, 48 insertions, 32 deletions
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 9ccc93f257db..38bb882bb958 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c | |||
@@ -48,7 +48,7 @@ struct tcindex_data { | |||
48 | u32 hash; /* hash table size; 0 if undefined */ | 48 | u32 hash; /* hash table size; 0 if undefined */ |
49 | u32 alloc_hash; /* allocated size */ | 49 | u32 alloc_hash; /* allocated size */ |
50 | u32 fall_through; /* 0: only classify if explicit match */ | 50 | u32 fall_through; /* 0: only classify if explicit match */ |
51 | struct rcu_head rcu; | 51 | struct rcu_work rwork; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | static inline int tcindex_filter_is_set(struct tcindex_filter_result *r) | 54 | static inline int tcindex_filter_is_set(struct tcindex_filter_result *r) |
@@ -221,17 +221,11 @@ found: | |||
221 | return 0; | 221 | return 0; |
222 | } | 222 | } |
223 | 223 | ||
224 | static int tcindex_destroy_element(struct tcf_proto *tp, | 224 | static void tcindex_destroy_work(struct work_struct *work) |
225 | void *arg, struct tcf_walker *walker) | ||
226 | { | ||
227 | bool last; | ||
228 | |||
229 | return tcindex_delete(tp, arg, &last, NULL); | ||
230 | } | ||
231 | |||
232 | static void __tcindex_destroy(struct rcu_head *head) | ||
233 | { | 225 | { |
234 | struct tcindex_data *p = container_of(head, struct tcindex_data, rcu); | 226 | struct tcindex_data *p = container_of(to_rcu_work(work), |
227 | struct tcindex_data, | ||
228 | rwork); | ||
235 | 229 | ||
236 | kfree(p->perfect); | 230 | kfree(p->perfect); |
237 | kfree(p->h); | 231 | kfree(p->h); |
@@ -258,9 +252,11 @@ static int tcindex_filter_result_init(struct tcindex_filter_result *r) | |||
258 | return tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); | 252 | return tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); |
259 | } | 253 | } |
260 | 254 | ||
261 | static void __tcindex_partial_destroy(struct rcu_head *head) | 255 | static void tcindex_partial_destroy_work(struct work_struct *work) |
262 | { | 256 | { |
263 | struct tcindex_data *p = container_of(head, struct tcindex_data, rcu); | 257 | struct tcindex_data *p = container_of(to_rcu_work(work), |
258 | struct tcindex_data, | ||
259 | rwork); | ||
264 | 260 | ||
265 | kfree(p->perfect); | 261 | kfree(p->perfect); |
266 | kfree(p); | 262 | kfree(p); |
@@ -275,7 +271,7 @@ static void tcindex_free_perfect_hash(struct tcindex_data *cp) | |||
275 | kfree(cp->perfect); | 271 | kfree(cp->perfect); |
276 | } | 272 | } |
277 | 273 | ||
278 | static int tcindex_alloc_perfect_hash(struct tcindex_data *cp) | 274 | static int tcindex_alloc_perfect_hash(struct net *net, struct tcindex_data *cp) |
279 | { | 275 | { |
280 | int i, err = 0; | 276 | int i, err = 0; |
281 | 277 | ||
@@ -289,6 +285,9 @@ static int tcindex_alloc_perfect_hash(struct tcindex_data *cp) | |||
289 | TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); | 285 | TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); |
290 | if (err < 0) | 286 | if (err < 0) |
291 | goto errout; | 287 | goto errout; |
288 | #ifdef CONFIG_NET_CLS_ACT | ||
289 | cp->perfect[i].exts.net = net; | ||
290 | #endif | ||
292 | } | 291 | } |
293 | 292 | ||
294 | return 0; | 293 | return 0; |
@@ -305,9 +304,9 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
305 | struct nlattr *est, bool ovr, struct netlink_ext_ack *extack) | 304 | struct nlattr *est, bool ovr, struct netlink_ext_ack *extack) |
306 | { | 305 | { |
307 | struct tcindex_filter_result new_filter_result, *old_r = r; | 306 | struct tcindex_filter_result new_filter_result, *old_r = r; |
308 | struct tcindex_filter_result cr; | ||
309 | struct tcindex_data *cp = NULL, *oldp; | 307 | struct tcindex_data *cp = NULL, *oldp; |
310 | struct tcindex_filter *f = NULL; /* make gcc behave */ | 308 | struct tcindex_filter *f = NULL; /* make gcc behave */ |
309 | struct tcf_result cr = {}; | ||
311 | int err, balloc = 0; | 310 | int err, balloc = 0; |
312 | struct tcf_exts e; | 311 | struct tcf_exts e; |
313 | 312 | ||
@@ -337,7 +336,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
337 | if (p->perfect) { | 336 | if (p->perfect) { |
338 | int i; | 337 | int i; |
339 | 338 | ||
340 | if (tcindex_alloc_perfect_hash(cp) < 0) | 339 | if (tcindex_alloc_perfect_hash(net, cp) < 0) |
341 | goto errout; | 340 | goto errout; |
342 | for (i = 0; i < cp->hash; i++) | 341 | for (i = 0; i < cp->hash; i++) |
343 | cp->perfect[i].res = p->perfect[i].res; | 342 | cp->perfect[i].res = p->perfect[i].res; |
@@ -348,11 +347,8 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
348 | err = tcindex_filter_result_init(&new_filter_result); | 347 | err = tcindex_filter_result_init(&new_filter_result); |
349 | if (err < 0) | 348 | if (err < 0) |
350 | goto errout1; | 349 | goto errout1; |
351 | err = tcindex_filter_result_init(&cr); | ||
352 | if (err < 0) | ||
353 | goto errout1; | ||
354 | if (old_r) | 350 | if (old_r) |
355 | cr.res = r->res; | 351 | cr = r->res; |
356 | 352 | ||
357 | if (tb[TCA_TCINDEX_HASH]) | 353 | if (tb[TCA_TCINDEX_HASH]) |
358 | cp->hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); | 354 | cp->hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); |
@@ -406,7 +402,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
406 | err = -ENOMEM; | 402 | err = -ENOMEM; |
407 | if (!cp->perfect && !cp->h) { | 403 | if (!cp->perfect && !cp->h) { |
408 | if (valid_perfect_hash(cp)) { | 404 | if (valid_perfect_hash(cp)) { |
409 | if (tcindex_alloc_perfect_hash(cp) < 0) | 405 | if (tcindex_alloc_perfect_hash(net, cp) < 0) |
410 | goto errout_alloc; | 406 | goto errout_alloc; |
411 | balloc = 1; | 407 | balloc = 1; |
412 | } else { | 408 | } else { |
@@ -443,8 +439,8 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
443 | } | 439 | } |
444 | 440 | ||
445 | if (tb[TCA_TCINDEX_CLASSID]) { | 441 | if (tb[TCA_TCINDEX_CLASSID]) { |
446 | cr.res.classid = nla_get_u32(tb[TCA_TCINDEX_CLASSID]); | 442 | cr.classid = nla_get_u32(tb[TCA_TCINDEX_CLASSID]); |
447 | tcf_bind_filter(tp, &cr.res, base); | 443 | tcf_bind_filter(tp, &cr, base); |
448 | } | 444 | } |
449 | 445 | ||
450 | if (old_r && old_r != r) { | 446 | if (old_r && old_r != r) { |
@@ -456,7 +452,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
456 | } | 452 | } |
457 | 453 | ||
458 | oldp = p; | 454 | oldp = p; |
459 | r->res = cr.res; | 455 | r->res = cr; |
460 | tcf_exts_change(&r->exts, &e); | 456 | tcf_exts_change(&r->exts, &e); |
461 | 457 | ||
462 | rcu_assign_pointer(tp->root, cp); | 458 | rcu_assign_pointer(tp->root, cp); |
@@ -475,10 +471,12 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
475 | ; /* nothing */ | 471 | ; /* nothing */ |
476 | 472 | ||
477 | rcu_assign_pointer(*fp, f); | 473 | rcu_assign_pointer(*fp, f); |
474 | } else { | ||
475 | tcf_exts_destroy(&new_filter_result.exts); | ||
478 | } | 476 | } |
479 | 477 | ||
480 | if (oldp) | 478 | if (oldp) |
481 | call_rcu(&oldp->rcu, __tcindex_partial_destroy); | 479 | tcf_queue_work(&oldp->rwork, tcindex_partial_destroy_work); |
482 | return 0; | 480 | return 0; |
483 | 481 | ||
484 | errout_alloc: | 482 | errout_alloc: |
@@ -487,7 +485,6 @@ errout_alloc: | |||
487 | else if (balloc == 2) | 485 | else if (balloc == 2) |
488 | kfree(cp->h); | 486 | kfree(cp->h); |
489 | errout1: | 487 | errout1: |
490 | tcf_exts_destroy(&cr.exts); | ||
491 | tcf_exts_destroy(&new_filter_result.exts); | 488 | tcf_exts_destroy(&new_filter_result.exts); |
492 | errout: | 489 | errout: |
493 | kfree(cp); | 490 | kfree(cp); |
@@ -562,15 +559,34 @@ static void tcindex_destroy(struct tcf_proto *tp, | |||
562 | struct netlink_ext_ack *extack) | 559 | struct netlink_ext_ack *extack) |
563 | { | 560 | { |
564 | struct tcindex_data *p = rtnl_dereference(tp->root); | 561 | struct tcindex_data *p = rtnl_dereference(tp->root); |
565 | struct tcf_walker walker; | 562 | int i; |
566 | 563 | ||
567 | pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); | 564 | pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); |
568 | walker.count = 0; | ||
569 | walker.skip = 0; | ||
570 | walker.fn = tcindex_destroy_element; | ||
571 | tcindex_walk(tp, &walker); | ||
572 | 565 | ||
573 | call_rcu(&p->rcu, __tcindex_destroy); | 566 | if (p->perfect) { |
567 | for (i = 0; i < p->hash; i++) { | ||
568 | struct tcindex_filter_result *r = p->perfect + i; | ||
569 | |||
570 | tcf_unbind_filter(tp, &r->res); | ||
571 | if (tcf_exts_get_net(&r->exts)) | ||
572 | tcf_queue_work(&r->rwork, | ||
573 | tcindex_destroy_rexts_work); | ||
574 | else | ||
575 | __tcindex_destroy_rexts(r); | ||
576 | } | ||
577 | } | ||
578 | |||
579 | for (i = 0; p->h && i < p->hash; i++) { | ||
580 | struct tcindex_filter *f, *next; | ||
581 | bool last; | ||
582 | |||
583 | for (f = rtnl_dereference(p->h[i]); f; f = next) { | ||
584 | next = rtnl_dereference(f->next); | ||
585 | tcindex_delete(tp, &f->result, &last, NULL); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | tcf_queue_work(&p->rwork, tcindex_destroy_work); | ||
574 | } | 590 | } |
575 | 591 | ||
576 | 592 | ||