aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched
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
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')
-rw-r--r--net/sched/cls_api.c14
-rw-r--r--net/sched/cls_basic.c6
-rw-r--r--net/sched/cls_bpf.c6
-rw-r--r--net/sched/cls_cgroup.c6
-rw-r--r--net/sched/cls_flow.c6
-rw-r--r--net/sched/cls_fw.c11
-rw-r--r--net/sched/cls_route.c12
-rw-r--r--net/sched/cls_rsvp.h12
-rw-r--r--net/sched/cls_tcindex.c6
-rw-r--r--net/sched/cls_u32.c25
-rw-r--r--net/sched/sch_api.c14
11 files changed, 97 insertions, 21 deletions
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index baef987fe2c0..8b0470e418dc 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -286,7 +286,7 @@ replay:
286 RCU_INIT_POINTER(*back, next); 286 RCU_INIT_POINTER(*back, next);
287 287
288 tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER); 288 tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
289 tcf_destroy(tp); 289 tcf_destroy(tp, true);
290 err = 0; 290 err = 0;
291 goto errout; 291 goto errout;
292 } 292 }
@@ -301,14 +301,20 @@ replay:
301 err = -EEXIST; 301 err = -EEXIST;
302 if (n->nlmsg_flags & NLM_F_EXCL) { 302 if (n->nlmsg_flags & NLM_F_EXCL) {
303 if (tp_created) 303 if (tp_created)
304 tcf_destroy(tp); 304 tcf_destroy(tp, true);
305 goto errout; 305 goto errout;
306 } 306 }
307 break; 307 break;
308 case RTM_DELTFILTER: 308 case RTM_DELTFILTER:
309 err = tp->ops->delete(tp, fh); 309 err = tp->ops->delete(tp, fh);
310 if (err == 0) 310 if (err == 0) {
311 tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER); 311 tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
312 if (tcf_destroy(tp, false)) {
313 struct tcf_proto *next = rtnl_dereference(tp->next);
314
315 RCU_INIT_POINTER(*back, next);
316 }
317 }
312 goto errout; 318 goto errout;
313 case RTM_GETTFILTER: 319 case RTM_GETTFILTER:
314 err = tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER); 320 err = tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
@@ -329,7 +335,7 @@ replay:
329 tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER); 335 tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
330 } else { 336 } else {
331 if (tp_created) 337 if (tp_created)
332 tcf_destroy(tp); 338 tcf_destroy(tp, true);
333 } 339 }
334 340
335errout: 341errout:
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index fc399db86f11..0b8c3ace671f 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -96,11 +96,14 @@ static void basic_delete_filter(struct rcu_head *head)
96 kfree(f); 96 kfree(f);
97} 97}
98 98
99static void basic_destroy(struct tcf_proto *tp) 99static bool basic_destroy(struct tcf_proto *tp, bool force)
100{ 100{
101 struct basic_head *head = rtnl_dereference(tp->root); 101 struct basic_head *head = rtnl_dereference(tp->root);
102 struct basic_filter *f, *n; 102 struct basic_filter *f, *n;
103 103
104 if (!force && !list_empty(&head->flist))
105 return false;
106
104 list_for_each_entry_safe(f, n, &head->flist, link) { 107 list_for_each_entry_safe(f, n, &head->flist, link) {
105 list_del_rcu(&f->link); 108 list_del_rcu(&f->link);
106 tcf_unbind_filter(tp, &f->res); 109 tcf_unbind_filter(tp, &f->res);
@@ -108,6 +111,7 @@ static void basic_destroy(struct tcf_proto *tp)
108 } 111 }
109 RCU_INIT_POINTER(tp->root, NULL); 112 RCU_INIT_POINTER(tp->root, NULL);
110 kfree_rcu(head, rcu); 113 kfree_rcu(head, rcu);
114 return true;
111} 115}
112 116
113static int basic_delete(struct tcf_proto *tp, unsigned long arg) 117static int basic_delete(struct tcf_proto *tp, unsigned long arg)
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 6f7ed8f8e6ee..243c9f225a73 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -137,11 +137,14 @@ static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg)
137 return 0; 137 return 0;
138} 138}
139 139
140static void cls_bpf_destroy(struct tcf_proto *tp) 140static bool cls_bpf_destroy(struct tcf_proto *tp, bool force)
141{ 141{
142 struct cls_bpf_head *head = rtnl_dereference(tp->root); 142 struct cls_bpf_head *head = rtnl_dereference(tp->root);
143 struct cls_bpf_prog *prog, *tmp; 143 struct cls_bpf_prog *prog, *tmp;
144 144
145 if (!force && !list_empty(&head->plist))
146 return false;
147
145 list_for_each_entry_safe(prog, tmp, &head->plist, link) { 148 list_for_each_entry_safe(prog, tmp, &head->plist, link) {
146 list_del_rcu(&prog->link); 149 list_del_rcu(&prog->link);
147 tcf_unbind_filter(tp, &prog->res); 150 tcf_unbind_filter(tp, &prog->res);
@@ -150,6 +153,7 @@ static void cls_bpf_destroy(struct tcf_proto *tp)
150 153
151 RCU_INIT_POINTER(tp->root, NULL); 154 RCU_INIT_POINTER(tp->root, NULL);
152 kfree_rcu(head, rcu); 155 kfree_rcu(head, rcu);
156 return true;
153} 157}
154 158
155static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) 159static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 221697ab0247..ea611b216412 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -143,14 +143,18 @@ errout:
143 return err; 143 return err;
144} 144}
145 145
146static void cls_cgroup_destroy(struct tcf_proto *tp) 146static bool cls_cgroup_destroy(struct tcf_proto *tp, bool force)
147{ 147{
148 struct cls_cgroup_head *head = rtnl_dereference(tp->root); 148 struct cls_cgroup_head *head = rtnl_dereference(tp->root);
149 149
150 if (!force)
151 return false;
152
150 if (head) { 153 if (head) {
151 RCU_INIT_POINTER(tp->root, NULL); 154 RCU_INIT_POINTER(tp->root, NULL);
152 call_rcu(&head->rcu, cls_cgroup_destroy_rcu); 155 call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
153 } 156 }
157 return true;
154} 158}
155 159
156static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg) 160static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg)
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)
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 9d9aa3e82b10..715e01e5910a 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -133,14 +133,20 @@ static void fw_delete_filter(struct rcu_head *head)
133 kfree(f); 133 kfree(f);
134} 134}
135 135
136static void fw_destroy(struct tcf_proto *tp) 136static bool fw_destroy(struct tcf_proto *tp, bool force)
137{ 137{
138 struct fw_head *head = rtnl_dereference(tp->root); 138 struct fw_head *head = rtnl_dereference(tp->root);
139 struct fw_filter *f; 139 struct fw_filter *f;
140 int h; 140 int h;
141 141
142 if (head == NULL) 142 if (head == NULL)
143 return; 143 return true;
144
145 if (!force) {
146 for (h = 0; h < HTSIZE; h++)
147 if (rcu_access_pointer(head->ht[h]))
148 return false;
149 }
144 150
145 for (h = 0; h < HTSIZE; h++) { 151 for (h = 0; h < HTSIZE; h++) {
146 while ((f = rtnl_dereference(head->ht[h])) != NULL) { 152 while ((f = rtnl_dereference(head->ht[h])) != NULL) {
@@ -152,6 +158,7 @@ static void fw_destroy(struct tcf_proto *tp)
152 } 158 }
153 RCU_INIT_POINTER(tp->root, NULL); 159 RCU_INIT_POINTER(tp->root, NULL);
154 kfree_rcu(head, rcu); 160 kfree_rcu(head, rcu);
161 return true;
155} 162}
156 163
157static int fw_delete(struct tcf_proto *tp, unsigned long arg) 164static int fw_delete(struct tcf_proto *tp, unsigned long arg)
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index bb8a60235d01..08a3b0a6f5ab 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -277,13 +277,20 @@ route4_delete_filter(struct rcu_head *head)
277 kfree(f); 277 kfree(f);
278} 278}
279 279
280static void route4_destroy(struct tcf_proto *tp) 280static bool route4_destroy(struct tcf_proto *tp, bool force)
281{ 281{
282 struct route4_head *head = rtnl_dereference(tp->root); 282 struct route4_head *head = rtnl_dereference(tp->root);
283 int h1, h2; 283 int h1, h2;
284 284
285 if (head == NULL) 285 if (head == NULL)
286 return; 286 return true;
287
288 if (!force) {
289 for (h1 = 0; h1 <= 256; h1++) {
290 if (rcu_access_pointer(head->table[h1]))
291 return false;
292 }
293 }
287 294
288 for (h1 = 0; h1 <= 256; h1++) { 295 for (h1 = 0; h1 <= 256; h1++) {
289 struct route4_bucket *b; 296 struct route4_bucket *b;
@@ -308,6 +315,7 @@ static void route4_destroy(struct tcf_proto *tp)
308 } 315 }
309 RCU_INIT_POINTER(tp->root, NULL); 316 RCU_INIT_POINTER(tp->root, NULL);
310 kfree_rcu(head, rcu); 317 kfree_rcu(head, rcu);
318 return true;
311} 319}
312 320
313static int route4_delete(struct tcf_proto *tp, unsigned long arg) 321static int route4_delete(struct tcf_proto *tp, unsigned long arg)
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index edd8ade3fbc1..02fa82792dab 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -291,13 +291,20 @@ rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
291 kfree_rcu(f, rcu); 291 kfree_rcu(f, rcu);
292} 292}
293 293
294static void rsvp_destroy(struct tcf_proto *tp) 294static bool rsvp_destroy(struct tcf_proto *tp, bool force)
295{ 295{
296 struct rsvp_head *data = rtnl_dereference(tp->root); 296 struct rsvp_head *data = rtnl_dereference(tp->root);
297 int h1, h2; 297 int h1, h2;
298 298
299 if (data == NULL) 299 if (data == NULL)
300 return; 300 return true;
301
302 if (!force) {
303 for (h1 = 0; h1 < 256; h1++) {
304 if (rcu_access_pointer(data->ht[h1]))
305 return false;
306 }
307 }
301 308
302 RCU_INIT_POINTER(tp->root, NULL); 309 RCU_INIT_POINTER(tp->root, NULL);
303 310
@@ -319,6 +326,7 @@ static void rsvp_destroy(struct tcf_proto *tp)
319 } 326 }
320 } 327 }
321 kfree_rcu(data, rcu); 328 kfree_rcu(data, rcu);
329 return true;
322} 330}
323 331
324static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) 332static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index bd49bf547a47..a557dbaf5afe 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -468,11 +468,14 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker)
468 } 468 }
469} 469}
470 470
471static void tcindex_destroy(struct tcf_proto *tp) 471static bool tcindex_destroy(struct tcf_proto *tp, bool force)
472{ 472{
473 struct tcindex_data *p = rtnl_dereference(tp->root); 473 struct tcindex_data *p = rtnl_dereference(tp->root);
474 struct tcf_walker walker; 474 struct tcf_walker walker;
475 475
476 if (!force)
477 return false;
478
476 pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); 479 pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p);
477 walker.count = 0; 480 walker.count = 0;
478 walker.skip = 0; 481 walker.skip = 0;
@@ -481,6 +484,7 @@ static void tcindex_destroy(struct tcf_proto *tp)
481 484
482 RCU_INIT_POINTER(tp->root, NULL); 485 RCU_INIT_POINTER(tp->root, NULL);
483 call_rcu(&p->rcu, __tcindex_destroy); 486 call_rcu(&p->rcu, __tcindex_destroy);
487 return true;
484} 488}
485 489
486 490
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 09487afbfd51..375e51b71c80 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -460,13 +460,35 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
460 return -ENOENT; 460 return -ENOENT;
461} 461}
462 462
463static void u32_destroy(struct tcf_proto *tp) 463static bool ht_empty(struct tc_u_hnode *ht)
464{
465 unsigned int h;
466
467 for (h = 0; h <= ht->divisor; h++)
468 if (rcu_access_pointer(ht->ht[h]))
469 return false;
470
471 return true;
472}
473
474static bool u32_destroy(struct tcf_proto *tp, bool force)
464{ 475{
465 struct tc_u_common *tp_c = tp->data; 476 struct tc_u_common *tp_c = tp->data;
466 struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); 477 struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
467 478
468 WARN_ON(root_ht == NULL); 479 WARN_ON(root_ht == NULL);
469 480
481 if (!force) {
482 if (root_ht) {
483 if (root_ht->refcnt > 1)
484 return false;
485 if (root_ht->refcnt == 1) {
486 if (!ht_empty(root_ht))
487 return false;
488 }
489 }
490 }
491
470 if (root_ht && --root_ht->refcnt == 0) 492 if (root_ht && --root_ht->refcnt == 0)
471 u32_destroy_hnode(tp, root_ht); 493 u32_destroy_hnode(tp, root_ht);
472 494
@@ -491,6 +513,7 @@ static void u32_destroy(struct tcf_proto *tp)
491 } 513 }
492 514
493 tp->data = NULL; 515 tp->data = NULL;
516 return true;
494} 517}
495 518
496static int u32_delete(struct tcf_proto *tp, unsigned long arg) 519static int u32_delete(struct tcf_proto *tp, unsigned long arg)
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 243b7d169d61..ad9eed70bc8f 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1858,11 +1858,15 @@ reclassify:
1858} 1858}
1859EXPORT_SYMBOL(tc_classify); 1859EXPORT_SYMBOL(tc_classify);
1860 1860
1861void tcf_destroy(struct tcf_proto *tp) 1861bool tcf_destroy(struct tcf_proto *tp, bool force)
1862{ 1862{
1863 tp->ops->destroy(tp); 1863 if (tp->ops->destroy(tp, force)) {
1864 module_put(tp->ops->owner); 1864 module_put(tp->ops->owner);
1865 kfree_rcu(tp, rcu); 1865 kfree_rcu(tp, rcu);
1866 return true;
1867 }
1868
1869 return false;
1866} 1870}
1867 1871
1868void tcf_destroy_chain(struct tcf_proto __rcu **fl) 1872void tcf_destroy_chain(struct tcf_proto __rcu **fl)
@@ -1871,7 +1875,7 @@ void tcf_destroy_chain(struct tcf_proto __rcu **fl)
1871 1875
1872 while ((tp = rtnl_dereference(*fl)) != NULL) { 1876 while ((tp = rtnl_dereference(*fl)) != NULL) {
1873 RCU_INIT_POINTER(*fl, tp->next); 1877 RCU_INIT_POINTER(*fl, tp->next);
1874 tcf_destroy(tp); 1878 tcf_destroy(tp, true);
1875 } 1879 }
1876} 1880}
1877EXPORT_SYMBOL(tcf_destroy_chain); 1881EXPORT_SYMBOL(tcf_destroy_chain);