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 | ||
