diff options
author | Cong Wang <xiyou.wangcong@gmail.com> | 2017-11-06 16:47:23 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-11-08 20:03:09 -0500 |
commit | 22f7cec93f0af86c4b66bf34a977da9d7cef076e (patch) | |
tree | 7ef156054146fce727443685455fb4f90f67b305 | |
parent | ed1481681414e4d4264ad46864d5c1da5ff6ccb1 (diff) |
cls_flow: use tcf_exts_get_net() before call_rcu()
Hold netns refcnt before call_rcu() and release it after
the tcf_exts_destroy() is done.
Note, on ->destroy() path we have to respect the return value
of tcf_exts_get_net(), on other paths it should always return
true, so we don't need to care.
Cc: Lucas Bates <lucasb@mojatatu.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/sched/cls_flow.c | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 67f3a2af6aab..85f765cff697 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c | |||
@@ -372,15 +372,21 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { | |||
372 | [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, | 372 | [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, |
373 | }; | 373 | }; |
374 | 374 | ||
375 | static void flow_destroy_filter_work(struct work_struct *work) | 375 | static void __flow_destroy_filter(struct flow_filter *f) |
376 | { | 376 | { |
377 | struct flow_filter *f = container_of(work, struct flow_filter, work); | ||
378 | |||
379 | rtnl_lock(); | ||
380 | del_timer_sync(&f->perturb_timer); | 377 | del_timer_sync(&f->perturb_timer); |
381 | tcf_exts_destroy(&f->exts); | 378 | tcf_exts_destroy(&f->exts); |
382 | tcf_em_tree_destroy(&f->ematches); | 379 | tcf_em_tree_destroy(&f->ematches); |
380 | tcf_exts_put_net(&f->exts); | ||
383 | kfree(f); | 381 | kfree(f); |
382 | } | ||
383 | |||
384 | static void flow_destroy_filter_work(struct work_struct *work) | ||
385 | { | ||
386 | struct flow_filter *f = container_of(work, struct flow_filter, work); | ||
387 | |||
388 | rtnl_lock(); | ||
389 | __flow_destroy_filter(f); | ||
384 | rtnl_unlock(); | 390 | rtnl_unlock(); |
385 | } | 391 | } |
386 | 392 | ||
@@ -552,8 +558,10 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, | |||
552 | 558 | ||
553 | *arg = fnew; | 559 | *arg = fnew; |
554 | 560 | ||
555 | if (fold) | 561 | if (fold) { |
562 | tcf_exts_get_net(&fold->exts); | ||
556 | call_rcu(&fold->rcu, flow_destroy_filter); | 563 | call_rcu(&fold->rcu, flow_destroy_filter); |
564 | } | ||
557 | return 0; | 565 | return 0; |
558 | 566 | ||
559 | err2: | 567 | err2: |
@@ -570,6 +578,7 @@ static int flow_delete(struct tcf_proto *tp, void *arg, bool *last) | |||
570 | struct flow_filter *f = arg; | 578 | struct flow_filter *f = arg; |
571 | 579 | ||
572 | list_del_rcu(&f->list); | 580 | list_del_rcu(&f->list); |
581 | tcf_exts_get_net(&f->exts); | ||
573 | call_rcu(&f->rcu, flow_destroy_filter); | 582 | call_rcu(&f->rcu, flow_destroy_filter); |
574 | *last = list_empty(&head->filters); | 583 | *last = list_empty(&head->filters); |
575 | return 0; | 584 | return 0; |
@@ -594,7 +603,10 @@ static void flow_destroy(struct tcf_proto *tp) | |||
594 | 603 | ||
595 | list_for_each_entry_safe(f, next, &head->filters, list) { | 604 | list_for_each_entry_safe(f, next, &head->filters, list) { |
596 | list_del_rcu(&f->list); | 605 | list_del_rcu(&f->list); |
597 | call_rcu(&f->rcu, flow_destroy_filter); | 606 | if (tcf_exts_get_net(&f->exts)) |
607 | call_rcu(&f->rcu, flow_destroy_filter); | ||
608 | else | ||
609 | __flow_destroy_filter(f); | ||
598 | } | 610 | } |
599 | kfree_rcu(head, rcu); | 611 | kfree_rcu(head, rcu); |
600 | } | 612 | } |