aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/sch_generic.h4
-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
12 files changed, 99 insertions, 23 deletions
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index c605d305c577..6d778efcfdfd 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -213,7 +213,7 @@ struct tcf_proto_ops {
213 const struct tcf_proto *, 213 const struct tcf_proto *,
214 struct tcf_result *); 214 struct tcf_result *);
215 int (*init)(struct tcf_proto*); 215 int (*init)(struct tcf_proto*);
216 void (*destroy)(struct tcf_proto*); 216 bool (*destroy)(struct tcf_proto*, bool);
217 217
218 unsigned long (*get)(struct tcf_proto*, u32 handle); 218 unsigned long (*get)(struct tcf_proto*, u32 handle);
219 int (*change)(struct net *net, struct sk_buff *, 219 int (*change)(struct net *net, struct sk_buff *,
@@ -399,7 +399,7 @@ struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
399 const struct Qdisc_ops *ops, u32 parentid); 399 const struct Qdisc_ops *ops, u32 parentid);
400void __qdisc_calculate_pkt_len(struct sk_buff *skb, 400void __qdisc_calculate_pkt_len(struct sk_buff *skb,
401 const struct qdisc_size_table *stab); 401 const struct qdisc_size_table *stab);
402void tcf_destroy(struct tcf_proto *tp); 402bool tcf_destroy(struct tcf_proto *tp, bool force);
403void tcf_destroy_chain(struct tcf_proto __rcu **fl); 403void tcf_destroy_chain(struct tcf_proto __rcu **fl);
404 404
405/* Reset all TX qdiscs greater then index of a device. */ 405/* Reset all TX qdiscs greater then index of a device. */
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);