aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched/cls_flow.c
diff options
context:
space:
mode:
authorCong Wang <cwang@twopensource.com>2015-03-06 14:47:59 -0500
committerDavid S. Miller <davem@davemloft.net>2015-03-09 15:35:55 -0400
commit1e052be69d045c8d0f82ff1116fd3e5a79661745 (patch)
treec237348cf8f28ca178c3ebfcec0e0013ef18b4c5 /net/sched/cls_flow.c
parentfc6c6c2b8a2e1fbaa9e864af62c873dae15420ea (diff)
net_sched: destroy proto tp when all filters are gone
Kernel automatically creates a tp for each (kind, protocol, priority) tuple, which has handle 0, when we add a new filter, but it still is left there after we remove our own, unless we don't specify the handle (literally means all the filters under the tuple). For example this one is left: # tc filter show dev eth0 filter parent 8001: protocol arp pref 49152 basic The user-space is hard to clean up these for kernel because filters like u32 are organized in a complex way. So kernel is responsible to remove it after all filters are gone. Each type of filter has its own way to store the filters, so each type has to provide its way to check if all filters are gone. Cc: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Cong Wang <cwang@twopensource.com> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Acked-by: Jamal Hadi Salim<jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched/cls_flow.c')
-rw-r--r--net/sched/cls_flow.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 461410394d08..a620c4e288a5 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -557,17 +557,21 @@ static int flow_init(struct tcf_proto *tp)
557 return 0; 557 return 0;
558} 558}
559 559
560static void flow_destroy(struct tcf_proto *tp) 560static bool flow_destroy(struct tcf_proto *tp, bool force)
561{ 561{
562 struct flow_head *head = rtnl_dereference(tp->root); 562 struct flow_head *head = rtnl_dereference(tp->root);
563 struct flow_filter *f, *next; 563 struct flow_filter *f, *next;
564 564
565 if (!force && !list_empty(&head->filters))
566 return false;
567
565 list_for_each_entry_safe(f, next, &head->filters, list) { 568 list_for_each_entry_safe(f, next, &head->filters, list) {
566 list_del_rcu(&f->list); 569 list_del_rcu(&f->list);
567 call_rcu(&f->rcu, flow_destroy_filter); 570 call_rcu(&f->rcu, flow_destroy_filter);
568 } 571 }
569 RCU_INIT_POINTER(tp->root, NULL); 572 RCU_INIT_POINTER(tp->root, NULL);
570 kfree_rcu(head, rcu); 573 kfree_rcu(head, rcu);
574 return true;
571} 575}
572 576
573static unsigned long flow_get(struct tcf_proto *tp, u32 handle) 577static unsigned long flow_get(struct tcf_proto *tp, u32 handle)