diff options
author | David S. Miller <davem@davemloft.net> | 2017-11-08 20:03:10 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-11-08 20:03:10 -0500 |
commit | 623859ae06b85cabba79ce78f0d49e67783d4c34 (patch) | |
tree | 4600cfecab2372a77cfb9143e4b24b059d24433a | |
parent | 8f5624629105589bcc23d0e51cc01bd8103d09a5 (diff) | |
parent | 35c55fc156d85a396a975fc17636f560fc02fd65 (diff) |
Merge branch 'net-sched-race-fix'
Cong Wang says:
====================
net_sched: close the race between call_rcu() and cleanup_net()
This patchset tries to fix the race between call_rcu() and
cleanup_net() again. Without holding the netns refcnt the
tc_action_net_exit() in netns workqueue could be called before
filter destroy works in tc filter workqueue. This patchset
moves the netns refcnt from tc actions to tcf_exts, without
breaking per-netns tc actions.
Patch 1 reverts the previous fix, patch 2 introduces two new
API's to help to address the bug and the rest patches switch
to the new API's. Please see each patch for details.
I was not able to reproduce this bug, but now after adding
some delay in filter destroy work I manage to trigger the
crash. After this patchset, the crash is not reproducible
any more and the debugging printk's show the order is expected
too.
====================
Fixes: ddf97ccdd7cb ("net_sched: add network namespace support for tc actions")
Reported-by: Lucas Bates <lucasb@mojatatu.com>
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>
31 files changed, 198 insertions, 63 deletions
diff --git a/include/net/act_api.h b/include/net/act_api.h index 1e6df0eb058f..a10a3b1813f3 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h | |||
@@ -14,7 +14,6 @@ | |||
14 | struct tcf_idrinfo { | 14 | struct tcf_idrinfo { |
15 | spinlock_t lock; | 15 | spinlock_t lock; |
16 | struct idr action_idr; | 16 | struct idr action_idr; |
17 | struct net *net; | ||
18 | }; | 17 | }; |
19 | 18 | ||
20 | struct tc_action_ops; | 19 | struct tc_action_ops; |
@@ -106,7 +105,7 @@ struct tc_action_net { | |||
106 | 105 | ||
107 | static inline | 106 | static inline |
108 | int tc_action_net_init(struct tc_action_net *tn, | 107 | int tc_action_net_init(struct tc_action_net *tn, |
109 | const struct tc_action_ops *ops, struct net *net) | 108 | const struct tc_action_ops *ops) |
110 | { | 109 | { |
111 | int err = 0; | 110 | int err = 0; |
112 | 111 | ||
@@ -114,7 +113,6 @@ int tc_action_net_init(struct tc_action_net *tn, | |||
114 | if (!tn->idrinfo) | 113 | if (!tn->idrinfo) |
115 | return -ENOMEM; | 114 | return -ENOMEM; |
116 | tn->ops = ops; | 115 | tn->ops = ops; |
117 | tn->idrinfo->net = net; | ||
118 | spin_lock_init(&tn->idrinfo->lock); | 116 | spin_lock_init(&tn->idrinfo->lock); |
119 | idr_init(&tn->idrinfo->action_idr); | 117 | idr_init(&tn->idrinfo->action_idr); |
120 | return err; | 118 | return err; |
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 70ca2437740e..8826747ef83e 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h | |||
@@ -94,6 +94,7 @@ struct tcf_exts { | |||
94 | __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ | 94 | __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ |
95 | int nr_actions; | 95 | int nr_actions; |
96 | struct tc_action **actions; | 96 | struct tc_action **actions; |
97 | struct net *net; | ||
97 | #endif | 98 | #endif |
98 | /* Map to export classifier specific extension TLV types to the | 99 | /* Map to export classifier specific extension TLV types to the |
99 | * generic extensions API. Unsupported extensions must be set to 0. | 100 | * generic extensions API. Unsupported extensions must be set to 0. |
@@ -107,6 +108,7 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police) | |||
107 | #ifdef CONFIG_NET_CLS_ACT | 108 | #ifdef CONFIG_NET_CLS_ACT |
108 | exts->type = 0; | 109 | exts->type = 0; |
109 | exts->nr_actions = 0; | 110 | exts->nr_actions = 0; |
111 | exts->net = NULL; | ||
110 | exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *), | 112 | exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *), |
111 | GFP_KERNEL); | 113 | GFP_KERNEL); |
112 | if (!exts->actions) | 114 | if (!exts->actions) |
@@ -117,6 +119,28 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police) | |||
117 | return 0; | 119 | return 0; |
118 | } | 120 | } |
119 | 121 | ||
122 | /* Return false if the netns is being destroyed in cleanup_net(). Callers | ||
123 | * need to do cleanup synchronously in this case, otherwise may race with | ||
124 | * tc_action_net_exit(). Return true for other cases. | ||
125 | */ | ||
126 | static inline bool tcf_exts_get_net(struct tcf_exts *exts) | ||
127 | { | ||
128 | #ifdef CONFIG_NET_CLS_ACT | ||
129 | exts->net = maybe_get_net(exts->net); | ||
130 | return exts->net != NULL; | ||
131 | #else | ||
132 | return true; | ||
133 | #endif | ||
134 | } | ||
135 | |||
136 | static inline void tcf_exts_put_net(struct tcf_exts *exts) | ||
137 | { | ||
138 | #ifdef CONFIG_NET_CLS_ACT | ||
139 | if (exts->net) | ||
140 | put_net(exts->net); | ||
141 | #endif | ||
142 | } | ||
143 | |||
120 | static inline void tcf_exts_to_list(const struct tcf_exts *exts, | 144 | static inline void tcf_exts_to_list(const struct tcf_exts *exts, |
121 | struct list_head *actions) | 145 | struct list_head *actions) |
122 | { | 146 | { |
diff --git a/net/sched/act_api.c b/net/sched/act_api.c index ca2ff0b3123f..8f2c63514956 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c | |||
@@ -78,7 +78,6 @@ static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p) | |||
78 | spin_lock_bh(&idrinfo->lock); | 78 | spin_lock_bh(&idrinfo->lock); |
79 | idr_remove_ext(&idrinfo->action_idr, p->tcfa_index); | 79 | idr_remove_ext(&idrinfo->action_idr, p->tcfa_index); |
80 | spin_unlock_bh(&idrinfo->lock); | 80 | spin_unlock_bh(&idrinfo->lock); |
81 | put_net(idrinfo->net); | ||
82 | gen_kill_estimator(&p->tcfa_rate_est); | 81 | gen_kill_estimator(&p->tcfa_rate_est); |
83 | free_tcf(p); | 82 | free_tcf(p); |
84 | } | 83 | } |
@@ -337,7 +336,6 @@ err3: | |||
337 | p->idrinfo = idrinfo; | 336 | p->idrinfo = idrinfo; |
338 | p->ops = ops; | 337 | p->ops = ops; |
339 | INIT_LIST_HEAD(&p->list); | 338 | INIT_LIST_HEAD(&p->list); |
340 | get_net(idrinfo->net); | ||
341 | *a = p; | 339 | *a = p; |
342 | return 0; | 340 | return 0; |
343 | } | 341 | } |
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 9bce8cc84cbb..c0c707eb2c96 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c | |||
@@ -398,7 +398,7 @@ static __net_init int bpf_init_net(struct net *net) | |||
398 | { | 398 | { |
399 | struct tc_action_net *tn = net_generic(net, bpf_net_id); | 399 | struct tc_action_net *tn = net_generic(net, bpf_net_id); |
400 | 400 | ||
401 | return tc_action_net_init(tn, &act_bpf_ops, net); | 401 | return tc_action_net_init(tn, &act_bpf_ops); |
402 | } | 402 | } |
403 | 403 | ||
404 | static void __net_exit bpf_exit_net(struct net *net) | 404 | static void __net_exit bpf_exit_net(struct net *net) |
diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index 34e52d01a5dd..10b7a8855a6c 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c | |||
@@ -206,7 +206,7 @@ static __net_init int connmark_init_net(struct net *net) | |||
206 | { | 206 | { |
207 | struct tc_action_net *tn = net_generic(net, connmark_net_id); | 207 | struct tc_action_net *tn = net_generic(net, connmark_net_id); |
208 | 208 | ||
209 | return tc_action_net_init(tn, &act_connmark_ops, net); | 209 | return tc_action_net_init(tn, &act_connmark_ops); |
210 | } | 210 | } |
211 | 211 | ||
212 | static void __net_exit connmark_exit_net(struct net *net) | 212 | static void __net_exit connmark_exit_net(struct net *net) |
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 35171df2ebef..1c40caadcff9 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c | |||
@@ -626,7 +626,7 @@ static __net_init int csum_init_net(struct net *net) | |||
626 | { | 626 | { |
627 | struct tc_action_net *tn = net_generic(net, csum_net_id); | 627 | struct tc_action_net *tn = net_generic(net, csum_net_id); |
628 | 628 | ||
629 | return tc_action_net_init(tn, &act_csum_ops, net); | 629 | return tc_action_net_init(tn, &act_csum_ops); |
630 | } | 630 | } |
631 | 631 | ||
632 | static void __net_exit csum_exit_net(struct net *net) | 632 | static void __net_exit csum_exit_net(struct net *net) |
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index ef7f7f39d26d..e29a48ef7fc3 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c | |||
@@ -232,7 +232,7 @@ static __net_init int gact_init_net(struct net *net) | |||
232 | { | 232 | { |
233 | struct tc_action_net *tn = net_generic(net, gact_net_id); | 233 | struct tc_action_net *tn = net_generic(net, gact_net_id); |
234 | 234 | ||
235 | return tc_action_net_init(tn, &act_gact_ops, net); | 235 | return tc_action_net_init(tn, &act_gact_ops); |
236 | } | 236 | } |
237 | 237 | ||
238 | static void __net_exit gact_exit_net(struct net *net) | 238 | static void __net_exit gact_exit_net(struct net *net) |
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index f65e4b5058e0..8ccd35825b6b 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c | |||
@@ -818,7 +818,7 @@ static __net_init int ife_init_net(struct net *net) | |||
818 | { | 818 | { |
819 | struct tc_action_net *tn = net_generic(net, ife_net_id); | 819 | struct tc_action_net *tn = net_generic(net, ife_net_id); |
820 | 820 | ||
821 | return tc_action_net_init(tn, &act_ife_ops, net); | 821 | return tc_action_net_init(tn, &act_ife_ops); |
822 | } | 822 | } |
823 | 823 | ||
824 | static void __net_exit ife_exit_net(struct net *net) | 824 | static void __net_exit ife_exit_net(struct net *net) |
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index dbdf3b2470d5..d9e399a7e3d5 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c | |||
@@ -334,7 +334,7 @@ static __net_init int ipt_init_net(struct net *net) | |||
334 | { | 334 | { |
335 | struct tc_action_net *tn = net_generic(net, ipt_net_id); | 335 | struct tc_action_net *tn = net_generic(net, ipt_net_id); |
336 | 336 | ||
337 | return tc_action_net_init(tn, &act_ipt_ops, net); | 337 | return tc_action_net_init(tn, &act_ipt_ops); |
338 | } | 338 | } |
339 | 339 | ||
340 | static void __net_exit ipt_exit_net(struct net *net) | 340 | static void __net_exit ipt_exit_net(struct net *net) |
@@ -384,7 +384,7 @@ static __net_init int xt_init_net(struct net *net) | |||
384 | { | 384 | { |
385 | struct tc_action_net *tn = net_generic(net, xt_net_id); | 385 | struct tc_action_net *tn = net_generic(net, xt_net_id); |
386 | 386 | ||
387 | return tc_action_net_init(tn, &act_xt_ops, net); | 387 | return tc_action_net_init(tn, &act_xt_ops); |
388 | } | 388 | } |
389 | 389 | ||
390 | static void __net_exit xt_exit_net(struct net *net) | 390 | static void __net_exit xt_exit_net(struct net *net) |
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 84759cfd5a33..416627c66f08 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c | |||
@@ -343,7 +343,7 @@ static __net_init int mirred_init_net(struct net *net) | |||
343 | { | 343 | { |
344 | struct tc_action_net *tn = net_generic(net, mirred_net_id); | 344 | struct tc_action_net *tn = net_generic(net, mirred_net_id); |
345 | 345 | ||
346 | return tc_action_net_init(tn, &act_mirred_ops, net); | 346 | return tc_action_net_init(tn, &act_mirred_ops); |
347 | } | 347 | } |
348 | 348 | ||
349 | static void __net_exit mirred_exit_net(struct net *net) | 349 | static void __net_exit mirred_exit_net(struct net *net) |
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 7eeaaf9217b6..c365d01b99c8 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c | |||
@@ -307,7 +307,7 @@ static __net_init int nat_init_net(struct net *net) | |||
307 | { | 307 | { |
308 | struct tc_action_net *tn = net_generic(net, nat_net_id); | 308 | struct tc_action_net *tn = net_generic(net, nat_net_id); |
309 | 309 | ||
310 | return tc_action_net_init(tn, &act_nat_ops, net); | 310 | return tc_action_net_init(tn, &act_nat_ops); |
311 | } | 311 | } |
312 | 312 | ||
313 | static void __net_exit nat_exit_net(struct net *net) | 313 | static void __net_exit nat_exit_net(struct net *net) |
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index b3d82c334a5f..491fe5deb09e 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c | |||
@@ -450,7 +450,7 @@ static __net_init int pedit_init_net(struct net *net) | |||
450 | { | 450 | { |
451 | struct tc_action_net *tn = net_generic(net, pedit_net_id); | 451 | struct tc_action_net *tn = net_generic(net, pedit_net_id); |
452 | 452 | ||
453 | return tc_action_net_init(tn, &act_pedit_ops, net); | 453 | return tc_action_net_init(tn, &act_pedit_ops); |
454 | } | 454 | } |
455 | 455 | ||
456 | static void __net_exit pedit_exit_net(struct net *net) | 456 | static void __net_exit pedit_exit_net(struct net *net) |
diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 9ec42b26e4b9..3bb2ebf9e9ae 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c | |||
@@ -331,7 +331,7 @@ static __net_init int police_init_net(struct net *net) | |||
331 | { | 331 | { |
332 | struct tc_action_net *tn = net_generic(net, police_net_id); | 332 | struct tc_action_net *tn = net_generic(net, police_net_id); |
333 | 333 | ||
334 | return tc_action_net_init(tn, &act_police_ops, net); | 334 | return tc_action_net_init(tn, &act_police_ops); |
335 | } | 335 | } |
336 | 336 | ||
337 | static void __net_exit police_exit_net(struct net *net) | 337 | static void __net_exit police_exit_net(struct net *net) |
diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c index e69a1e3a39bf..8b5abcd2f32f 100644 --- a/net/sched/act_sample.c +++ b/net/sched/act_sample.c | |||
@@ -240,7 +240,7 @@ static __net_init int sample_init_net(struct net *net) | |||
240 | { | 240 | { |
241 | struct tc_action_net *tn = net_generic(net, sample_net_id); | 241 | struct tc_action_net *tn = net_generic(net, sample_net_id); |
242 | 242 | ||
243 | return tc_action_net_init(tn, &act_sample_ops, net); | 243 | return tc_action_net_init(tn, &act_sample_ops); |
244 | } | 244 | } |
245 | 245 | ||
246 | static void __net_exit sample_exit_net(struct net *net) | 246 | static void __net_exit sample_exit_net(struct net *net) |
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index a8d0ea95f894..e7b57e5071a3 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c | |||
@@ -201,7 +201,7 @@ static __net_init int simp_init_net(struct net *net) | |||
201 | { | 201 | { |
202 | struct tc_action_net *tn = net_generic(net, simp_net_id); | 202 | struct tc_action_net *tn = net_generic(net, simp_net_id); |
203 | 203 | ||
204 | return tc_action_net_init(tn, &act_simp_ops, net); | 204 | return tc_action_net_init(tn, &act_simp_ops); |
205 | } | 205 | } |
206 | 206 | ||
207 | static void __net_exit simp_exit_net(struct net *net) | 207 | static void __net_exit simp_exit_net(struct net *net) |
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index fbac62472e09..59949d61f20d 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c | |||
@@ -238,7 +238,7 @@ static __net_init int skbedit_init_net(struct net *net) | |||
238 | { | 238 | { |
239 | struct tc_action_net *tn = net_generic(net, skbedit_net_id); | 239 | struct tc_action_net *tn = net_generic(net, skbedit_net_id); |
240 | 240 | ||
241 | return tc_action_net_init(tn, &act_skbedit_ops, net); | 241 | return tc_action_net_init(tn, &act_skbedit_ops); |
242 | } | 242 | } |
243 | 243 | ||
244 | static void __net_exit skbedit_exit_net(struct net *net) | 244 | static void __net_exit skbedit_exit_net(struct net *net) |
diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c index 8e12d8897d2f..b642ad3d39dd 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c | |||
@@ -263,7 +263,7 @@ static __net_init int skbmod_init_net(struct net *net) | |||
263 | { | 263 | { |
264 | struct tc_action_net *tn = net_generic(net, skbmod_net_id); | 264 | struct tc_action_net *tn = net_generic(net, skbmod_net_id); |
265 | 265 | ||
266 | return tc_action_net_init(tn, &act_skbmod_ops, net); | 266 | return tc_action_net_init(tn, &act_skbmod_ops); |
267 | } | 267 | } |
268 | 268 | ||
269 | static void __net_exit skbmod_exit_net(struct net *net) | 269 | static void __net_exit skbmod_exit_net(struct net *net) |
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index c33faa373cf2..30c96274c638 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c | |||
@@ -322,7 +322,7 @@ static __net_init int tunnel_key_init_net(struct net *net) | |||
322 | { | 322 | { |
323 | struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); | 323 | struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); |
324 | 324 | ||
325 | return tc_action_net_init(tn, &act_tunnel_key_ops, net); | 325 | return tc_action_net_init(tn, &act_tunnel_key_ops); |
326 | } | 326 | } |
327 | 327 | ||
328 | static void __net_exit tunnel_key_exit_net(struct net *net) | 328 | static void __net_exit tunnel_key_exit_net(struct net *net) |
diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index 115fc33cc6d8..16eb067a8d8f 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c | |||
@@ -269,7 +269,7 @@ static __net_init int vlan_init_net(struct net *net) | |||
269 | { | 269 | { |
270 | struct tc_action_net *tn = net_generic(net, vlan_net_id); | 270 | struct tc_action_net *tn = net_generic(net, vlan_net_id); |
271 | 271 | ||
272 | return tc_action_net_init(tn, &act_vlan_ops, net); | 272 | return tc_action_net_init(tn, &act_vlan_ops); |
273 | } | 273 | } |
274 | 274 | ||
275 | static void __net_exit vlan_exit_net(struct net *net) | 275 | static void __net_exit vlan_exit_net(struct net *net) |
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b2d310745487..ecbb019efcbd 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c | |||
@@ -927,6 +927,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, | |||
927 | exts->actions[i++] = act; | 927 | exts->actions[i++] = act; |
928 | exts->nr_actions = i; | 928 | exts->nr_actions = i; |
929 | } | 929 | } |
930 | exts->net = net; | ||
930 | } | 931 | } |
931 | #else | 932 | #else |
932 | if ((exts->action && tb[exts->action]) || | 933 | if ((exts->action && tb[exts->action]) || |
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index f177649a2419..e43c56d5b96a 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c | |||
@@ -85,16 +85,21 @@ static int basic_init(struct tcf_proto *tp) | |||
85 | return 0; | 85 | return 0; |
86 | } | 86 | } |
87 | 87 | ||
88 | static void __basic_delete_filter(struct basic_filter *f) | ||
89 | { | ||
90 | tcf_exts_destroy(&f->exts); | ||
91 | tcf_em_tree_destroy(&f->ematches); | ||
92 | tcf_exts_put_net(&f->exts); | ||
93 | kfree(f); | ||
94 | } | ||
95 | |||
88 | static void basic_delete_filter_work(struct work_struct *work) | 96 | static void basic_delete_filter_work(struct work_struct *work) |
89 | { | 97 | { |
90 | struct basic_filter *f = container_of(work, struct basic_filter, work); | 98 | struct basic_filter *f = container_of(work, struct basic_filter, work); |
91 | 99 | ||
92 | rtnl_lock(); | 100 | rtnl_lock(); |
93 | tcf_exts_destroy(&f->exts); | 101 | __basic_delete_filter(f); |
94 | tcf_em_tree_destroy(&f->ematches); | ||
95 | rtnl_unlock(); | 102 | rtnl_unlock(); |
96 | |||
97 | kfree(f); | ||
98 | } | 103 | } |
99 | 104 | ||
100 | static void basic_delete_filter(struct rcu_head *head) | 105 | static void basic_delete_filter(struct rcu_head *head) |
@@ -113,7 +118,10 @@ static void basic_destroy(struct tcf_proto *tp) | |||
113 | list_for_each_entry_safe(f, n, &head->flist, link) { | 118 | list_for_each_entry_safe(f, n, &head->flist, link) { |
114 | list_del_rcu(&f->link); | 119 | list_del_rcu(&f->link); |
115 | tcf_unbind_filter(tp, &f->res); | 120 | tcf_unbind_filter(tp, &f->res); |
116 | call_rcu(&f->rcu, basic_delete_filter); | 121 | if (tcf_exts_get_net(&f->exts)) |
122 | call_rcu(&f->rcu, basic_delete_filter); | ||
123 | else | ||
124 | __basic_delete_filter(f); | ||
117 | } | 125 | } |
118 | kfree_rcu(head, rcu); | 126 | kfree_rcu(head, rcu); |
119 | } | 127 | } |
@@ -125,6 +133,7 @@ static int basic_delete(struct tcf_proto *tp, void *arg, bool *last) | |||
125 | 133 | ||
126 | list_del_rcu(&f->link); | 134 | list_del_rcu(&f->link); |
127 | tcf_unbind_filter(tp, &f->res); | 135 | tcf_unbind_filter(tp, &f->res); |
136 | tcf_exts_get_net(&f->exts); | ||
128 | call_rcu(&f->rcu, basic_delete_filter); | 137 | call_rcu(&f->rcu, basic_delete_filter); |
129 | *last = list_empty(&head->flist); | 138 | *last = list_empty(&head->flist); |
130 | return 0; | 139 | return 0; |
@@ -219,6 +228,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, | |||
219 | if (fold) { | 228 | if (fold) { |
220 | list_replace_rcu(&fold->link, &fnew->link); | 229 | list_replace_rcu(&fold->link, &fnew->link); |
221 | tcf_unbind_filter(tp, &fold->res); | 230 | tcf_unbind_filter(tp, &fold->res); |
231 | tcf_exts_get_net(&fold->exts); | ||
222 | call_rcu(&fold->rcu, basic_delete_filter); | 232 | call_rcu(&fold->rcu, basic_delete_filter); |
223 | } else { | 233 | } else { |
224 | list_add_rcu(&fnew->link, &head->flist); | 234 | list_add_rcu(&fnew->link, &head->flist); |
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 037a3ae86829..990eb4d91d54 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c | |||
@@ -249,6 +249,7 @@ static int cls_bpf_init(struct tcf_proto *tp) | |||
249 | static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) | 249 | static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) |
250 | { | 250 | { |
251 | tcf_exts_destroy(&prog->exts); | 251 | tcf_exts_destroy(&prog->exts); |
252 | tcf_exts_put_net(&prog->exts); | ||
252 | 253 | ||
253 | if (cls_bpf_is_ebpf(prog)) | 254 | if (cls_bpf_is_ebpf(prog)) |
254 | bpf_prog_put(prog->filter); | 255 | bpf_prog_put(prog->filter); |
@@ -282,7 +283,10 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) | |||
282 | cls_bpf_stop_offload(tp, prog); | 283 | cls_bpf_stop_offload(tp, prog); |
283 | list_del_rcu(&prog->link); | 284 | list_del_rcu(&prog->link); |
284 | tcf_unbind_filter(tp, &prog->res); | 285 | tcf_unbind_filter(tp, &prog->res); |
285 | call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); | 286 | if (tcf_exts_get_net(&prog->exts)) |
287 | call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); | ||
288 | else | ||
289 | __cls_bpf_delete_prog(prog); | ||
286 | } | 290 | } |
287 | 291 | ||
288 | static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last) | 292 | static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last) |
@@ -516,6 +520,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, | |||
516 | if (oldprog) { | 520 | if (oldprog) { |
517 | list_replace_rcu(&oldprog->link, &prog->link); | 521 | list_replace_rcu(&oldprog->link, &prog->link); |
518 | tcf_unbind_filter(tp, &oldprog->res); | 522 | tcf_unbind_filter(tp, &oldprog->res); |
523 | tcf_exts_get_net(&oldprog->exts); | ||
519 | call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu); | 524 | call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu); |
520 | } else { | 525 | } else { |
521 | list_add_rcu(&prog->link, &head->plist); | 526 | list_add_rcu(&prog->link, &head->plist); |
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index a97e069bee89..309d5899265f 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c | |||
@@ -60,15 +60,21 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { | |||
60 | [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, | 60 | [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, |
61 | }; | 61 | }; |
62 | 62 | ||
63 | static void __cls_cgroup_destroy(struct cls_cgroup_head *head) | ||
64 | { | ||
65 | tcf_exts_destroy(&head->exts); | ||
66 | tcf_em_tree_destroy(&head->ematches); | ||
67 | tcf_exts_put_net(&head->exts); | ||
68 | kfree(head); | ||
69 | } | ||
70 | |||
63 | static void cls_cgroup_destroy_work(struct work_struct *work) | 71 | static void cls_cgroup_destroy_work(struct work_struct *work) |
64 | { | 72 | { |
65 | struct cls_cgroup_head *head = container_of(work, | 73 | struct cls_cgroup_head *head = container_of(work, |
66 | struct cls_cgroup_head, | 74 | struct cls_cgroup_head, |
67 | work); | 75 | work); |
68 | rtnl_lock(); | 76 | rtnl_lock(); |
69 | tcf_exts_destroy(&head->exts); | 77 | __cls_cgroup_destroy(head); |
70 | tcf_em_tree_destroy(&head->ematches); | ||
71 | kfree(head); | ||
72 | rtnl_unlock(); | 78 | rtnl_unlock(); |
73 | } | 79 | } |
74 | 80 | ||
@@ -124,8 +130,10 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, | |||
124 | goto errout; | 130 | goto errout; |
125 | 131 | ||
126 | rcu_assign_pointer(tp->root, new); | 132 | rcu_assign_pointer(tp->root, new); |
127 | if (head) | 133 | if (head) { |
134 | tcf_exts_get_net(&head->exts); | ||
128 | call_rcu(&head->rcu, cls_cgroup_destroy_rcu); | 135 | call_rcu(&head->rcu, cls_cgroup_destroy_rcu); |
136 | } | ||
129 | return 0; | 137 | return 0; |
130 | errout: | 138 | errout: |
131 | tcf_exts_destroy(&new->exts); | 139 | tcf_exts_destroy(&new->exts); |
@@ -138,8 +146,12 @@ static void cls_cgroup_destroy(struct tcf_proto *tp) | |||
138 | struct cls_cgroup_head *head = rtnl_dereference(tp->root); | 146 | struct cls_cgroup_head *head = rtnl_dereference(tp->root); |
139 | 147 | ||
140 | /* Head can still be NULL due to cls_cgroup_init(). */ | 148 | /* Head can still be NULL due to cls_cgroup_init(). */ |
141 | if (head) | 149 | if (head) { |
142 | call_rcu(&head->rcu, cls_cgroup_destroy_rcu); | 150 | if (tcf_exts_get_net(&head->exts)) |
151 | call_rcu(&head->rcu, cls_cgroup_destroy_rcu); | ||
152 | else | ||
153 | __cls_cgroup_destroy(head); | ||
154 | } | ||
143 | } | 155 | } |
144 | 156 | ||
145 | static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last) | 157 | static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last) |
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 | } |
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 5b5722c8b32c..7a838d1c1c00 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c | |||
@@ -218,13 +218,19 @@ static int fl_init(struct tcf_proto *tp) | |||
218 | return 0; | 218 | return 0; |
219 | } | 219 | } |
220 | 220 | ||
221 | static void __fl_destroy_filter(struct cls_fl_filter *f) | ||
222 | { | ||
223 | tcf_exts_destroy(&f->exts); | ||
224 | tcf_exts_put_net(&f->exts); | ||
225 | kfree(f); | ||
226 | } | ||
227 | |||
221 | static void fl_destroy_filter_work(struct work_struct *work) | 228 | static void fl_destroy_filter_work(struct work_struct *work) |
222 | { | 229 | { |
223 | struct cls_fl_filter *f = container_of(work, struct cls_fl_filter, work); | 230 | struct cls_fl_filter *f = container_of(work, struct cls_fl_filter, work); |
224 | 231 | ||
225 | rtnl_lock(); | 232 | rtnl_lock(); |
226 | tcf_exts_destroy(&f->exts); | 233 | __fl_destroy_filter(f); |
227 | kfree(f); | ||
228 | rtnl_unlock(); | 234 | rtnl_unlock(); |
229 | } | 235 | } |
230 | 236 | ||
@@ -318,7 +324,10 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) | |||
318 | if (!tc_skip_hw(f->flags)) | 324 | if (!tc_skip_hw(f->flags)) |
319 | fl_hw_destroy_filter(tp, f); | 325 | fl_hw_destroy_filter(tp, f); |
320 | tcf_unbind_filter(tp, &f->res); | 326 | tcf_unbind_filter(tp, &f->res); |
321 | call_rcu(&f->rcu, fl_destroy_filter); | 327 | if (tcf_exts_get_net(&f->exts)) |
328 | call_rcu(&f->rcu, fl_destroy_filter); | ||
329 | else | ||
330 | __fl_destroy_filter(f); | ||
322 | } | 331 | } |
323 | 332 | ||
324 | static void fl_destroy_sleepable(struct work_struct *work) | 333 | static void fl_destroy_sleepable(struct work_struct *work) |
@@ -988,6 +997,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
988 | idr_replace_ext(&head->handle_idr, fnew, fnew->handle); | 997 | idr_replace_ext(&head->handle_idr, fnew, fnew->handle); |
989 | list_replace_rcu(&fold->list, &fnew->list); | 998 | list_replace_rcu(&fold->list, &fnew->list); |
990 | tcf_unbind_filter(tp, &fold->res); | 999 | tcf_unbind_filter(tp, &fold->res); |
1000 | tcf_exts_get_net(&fold->exts); | ||
991 | call_rcu(&fold->rcu, fl_destroy_filter); | 1001 | call_rcu(&fold->rcu, fl_destroy_filter); |
992 | } else { | 1002 | } else { |
993 | list_add_tail_rcu(&fnew->list, &head->filters); | 1003 | list_add_tail_rcu(&fnew->list, &head->filters); |
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 99183b8621ec..7f45e5ab8afc 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c | |||
@@ -122,13 +122,19 @@ static int fw_init(struct tcf_proto *tp) | |||
122 | return 0; | 122 | return 0; |
123 | } | 123 | } |
124 | 124 | ||
125 | static void __fw_delete_filter(struct fw_filter *f) | ||
126 | { | ||
127 | tcf_exts_destroy(&f->exts); | ||
128 | tcf_exts_put_net(&f->exts); | ||
129 | kfree(f); | ||
130 | } | ||
131 | |||
125 | static void fw_delete_filter_work(struct work_struct *work) | 132 | static void fw_delete_filter_work(struct work_struct *work) |
126 | { | 133 | { |
127 | struct fw_filter *f = container_of(work, struct fw_filter, work); | 134 | struct fw_filter *f = container_of(work, struct fw_filter, work); |
128 | 135 | ||
129 | rtnl_lock(); | 136 | rtnl_lock(); |
130 | tcf_exts_destroy(&f->exts); | 137 | __fw_delete_filter(f); |
131 | kfree(f); | ||
132 | rtnl_unlock(); | 138 | rtnl_unlock(); |
133 | } | 139 | } |
134 | 140 | ||
@@ -154,7 +160,10 @@ static void fw_destroy(struct tcf_proto *tp) | |||
154 | RCU_INIT_POINTER(head->ht[h], | 160 | RCU_INIT_POINTER(head->ht[h], |
155 | rtnl_dereference(f->next)); | 161 | rtnl_dereference(f->next)); |
156 | tcf_unbind_filter(tp, &f->res); | 162 | tcf_unbind_filter(tp, &f->res); |
157 | call_rcu(&f->rcu, fw_delete_filter); | 163 | if (tcf_exts_get_net(&f->exts)) |
164 | call_rcu(&f->rcu, fw_delete_filter); | ||
165 | else | ||
166 | __fw_delete_filter(f); | ||
158 | } | 167 | } |
159 | } | 168 | } |
160 | kfree_rcu(head, rcu); | 169 | kfree_rcu(head, rcu); |
@@ -179,6 +188,7 @@ static int fw_delete(struct tcf_proto *tp, void *arg, bool *last) | |||
179 | if (pfp == f) { | 188 | if (pfp == f) { |
180 | RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); | 189 | RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); |
181 | tcf_unbind_filter(tp, &f->res); | 190 | tcf_unbind_filter(tp, &f->res); |
191 | tcf_exts_get_net(&f->exts); | ||
182 | call_rcu(&f->rcu, fw_delete_filter); | 192 | call_rcu(&f->rcu, fw_delete_filter); |
183 | ret = 0; | 193 | ret = 0; |
184 | break; | 194 | break; |
@@ -299,6 +309,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
299 | RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); | 309 | RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); |
300 | rcu_assign_pointer(*fp, fnew); | 310 | rcu_assign_pointer(*fp, fnew); |
301 | tcf_unbind_filter(tp, &f->res); | 311 | tcf_unbind_filter(tp, &f->res); |
312 | tcf_exts_get_net(&f->exts); | ||
302 | call_rcu(&f->rcu, fw_delete_filter); | 313 | call_rcu(&f->rcu, fw_delete_filter); |
303 | 314 | ||
304 | *arg = fnew; | 315 | *arg = fnew; |
diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index c33f711b9019..3684153cd8a9 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c | |||
@@ -44,13 +44,19 @@ static int mall_init(struct tcf_proto *tp) | |||
44 | return 0; | 44 | return 0; |
45 | } | 45 | } |
46 | 46 | ||
47 | static void __mall_destroy(struct cls_mall_head *head) | ||
48 | { | ||
49 | tcf_exts_destroy(&head->exts); | ||
50 | tcf_exts_put_net(&head->exts); | ||
51 | kfree(head); | ||
52 | } | ||
53 | |||
47 | static void mall_destroy_work(struct work_struct *work) | 54 | static void mall_destroy_work(struct work_struct *work) |
48 | { | 55 | { |
49 | struct cls_mall_head *head = container_of(work, struct cls_mall_head, | 56 | struct cls_mall_head *head = container_of(work, struct cls_mall_head, |
50 | work); | 57 | work); |
51 | rtnl_lock(); | 58 | rtnl_lock(); |
52 | tcf_exts_destroy(&head->exts); | 59 | __mall_destroy(head); |
53 | kfree(head); | ||
54 | rtnl_unlock(); | 60 | rtnl_unlock(); |
55 | } | 61 | } |
56 | 62 | ||
@@ -109,7 +115,10 @@ static void mall_destroy(struct tcf_proto *tp) | |||
109 | if (tc_should_offload(dev, head->flags)) | 115 | if (tc_should_offload(dev, head->flags)) |
110 | mall_destroy_hw_filter(tp, head, (unsigned long) head); | 116 | mall_destroy_hw_filter(tp, head, (unsigned long) head); |
111 | 117 | ||
112 | call_rcu(&head->rcu, mall_destroy_rcu); | 118 | if (tcf_exts_get_net(&head->exts)) |
119 | call_rcu(&head->rcu, mall_destroy_rcu); | ||
120 | else | ||
121 | __mall_destroy(head); | ||
113 | } | 122 | } |
114 | 123 | ||
115 | static void *mall_get(struct tcf_proto *tp, u32 handle) | 124 | static void *mall_get(struct tcf_proto *tp, u32 handle) |
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 4b14ccd8b8f2..ac9a5b8825b9 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c | |||
@@ -257,13 +257,19 @@ static int route4_init(struct tcf_proto *tp) | |||
257 | return 0; | 257 | return 0; |
258 | } | 258 | } |
259 | 259 | ||
260 | static void __route4_delete_filter(struct route4_filter *f) | ||
261 | { | ||
262 | tcf_exts_destroy(&f->exts); | ||
263 | tcf_exts_put_net(&f->exts); | ||
264 | kfree(f); | ||
265 | } | ||
266 | |||
260 | static void route4_delete_filter_work(struct work_struct *work) | 267 | static void route4_delete_filter_work(struct work_struct *work) |
261 | { | 268 | { |
262 | struct route4_filter *f = container_of(work, struct route4_filter, work); | 269 | struct route4_filter *f = container_of(work, struct route4_filter, work); |
263 | 270 | ||
264 | rtnl_lock(); | 271 | rtnl_lock(); |
265 | tcf_exts_destroy(&f->exts); | 272 | __route4_delete_filter(f); |
266 | kfree(f); | ||
267 | rtnl_unlock(); | 273 | rtnl_unlock(); |
268 | } | 274 | } |
269 | 275 | ||
@@ -297,7 +303,10 @@ static void route4_destroy(struct tcf_proto *tp) | |||
297 | next = rtnl_dereference(f->next); | 303 | next = rtnl_dereference(f->next); |
298 | RCU_INIT_POINTER(b->ht[h2], next); | 304 | RCU_INIT_POINTER(b->ht[h2], next); |
299 | tcf_unbind_filter(tp, &f->res); | 305 | tcf_unbind_filter(tp, &f->res); |
300 | call_rcu(&f->rcu, route4_delete_filter); | 306 | if (tcf_exts_get_net(&f->exts)) |
307 | call_rcu(&f->rcu, route4_delete_filter); | ||
308 | else | ||
309 | __route4_delete_filter(f); | ||
301 | } | 310 | } |
302 | } | 311 | } |
303 | RCU_INIT_POINTER(head->table[h1], NULL); | 312 | RCU_INIT_POINTER(head->table[h1], NULL); |
@@ -338,6 +347,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last) | |||
338 | 347 | ||
339 | /* Delete it */ | 348 | /* Delete it */ |
340 | tcf_unbind_filter(tp, &f->res); | 349 | tcf_unbind_filter(tp, &f->res); |
350 | tcf_exts_get_net(&f->exts); | ||
341 | call_rcu(&f->rcu, route4_delete_filter); | 351 | call_rcu(&f->rcu, route4_delete_filter); |
342 | 352 | ||
343 | /* Strip RTNL protected tree */ | 353 | /* Strip RTNL protected tree */ |
@@ -541,6 +551,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, | |||
541 | *arg = f; | 551 | *arg = f; |
542 | if (fold) { | 552 | if (fold) { |
543 | tcf_unbind_filter(tp, &fold->res); | 553 | tcf_unbind_filter(tp, &fold->res); |
554 | tcf_exts_get_net(&fold->exts); | ||
544 | call_rcu(&fold->rcu, route4_delete_filter); | 555 | call_rcu(&fold->rcu, route4_delete_filter); |
545 | } | 556 | } |
546 | return 0; | 557 | return 0; |
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index bdbc541787f8..cf325625c99d 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h | |||
@@ -285,13 +285,19 @@ static int rsvp_init(struct tcf_proto *tp) | |||
285 | return -ENOBUFS; | 285 | return -ENOBUFS; |
286 | } | 286 | } |
287 | 287 | ||
288 | static void __rsvp_delete_filter(struct rsvp_filter *f) | ||
289 | { | ||
290 | tcf_exts_destroy(&f->exts); | ||
291 | tcf_exts_put_net(&f->exts); | ||
292 | kfree(f); | ||
293 | } | ||
294 | |||
288 | static void rsvp_delete_filter_work(struct work_struct *work) | 295 | static void rsvp_delete_filter_work(struct work_struct *work) |
289 | { | 296 | { |
290 | struct rsvp_filter *f = container_of(work, struct rsvp_filter, work); | 297 | struct rsvp_filter *f = container_of(work, struct rsvp_filter, work); |
291 | 298 | ||
292 | rtnl_lock(); | 299 | rtnl_lock(); |
293 | tcf_exts_destroy(&f->exts); | 300 | __rsvp_delete_filter(f); |
294 | kfree(f); | ||
295 | rtnl_unlock(); | 301 | rtnl_unlock(); |
296 | } | 302 | } |
297 | 303 | ||
@@ -310,7 +316,10 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) | |||
310 | * grace period, since converted-to-rcu actions are relying on that | 316 | * grace period, since converted-to-rcu actions are relying on that |
311 | * in cleanup() callback | 317 | * in cleanup() callback |
312 | */ | 318 | */ |
313 | call_rcu(&f->rcu, rsvp_delete_filter_rcu); | 319 | if (tcf_exts_get_net(&f->exts)) |
320 | call_rcu(&f->rcu, rsvp_delete_filter_rcu); | ||
321 | else | ||
322 | __rsvp_delete_filter(f); | ||
314 | } | 323 | } |
315 | 324 | ||
316 | static void rsvp_destroy(struct tcf_proto *tp) | 325 | static void rsvp_destroy(struct tcf_proto *tp) |
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index beaa95e09c25..a76937ee0b2d 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c | |||
@@ -139,13 +139,19 @@ static int tcindex_init(struct tcf_proto *tp) | |||
139 | return 0; | 139 | return 0; |
140 | } | 140 | } |
141 | 141 | ||
142 | static void __tcindex_destroy_rexts(struct tcindex_filter_result *r) | ||
143 | { | ||
144 | tcf_exts_destroy(&r->exts); | ||
145 | tcf_exts_put_net(&r->exts); | ||
146 | } | ||
147 | |||
142 | static void tcindex_destroy_rexts_work(struct work_struct *work) | 148 | static void tcindex_destroy_rexts_work(struct work_struct *work) |
143 | { | 149 | { |
144 | struct tcindex_filter_result *r; | 150 | struct tcindex_filter_result *r; |
145 | 151 | ||
146 | r = container_of(work, struct tcindex_filter_result, work); | 152 | r = container_of(work, struct tcindex_filter_result, work); |
147 | rtnl_lock(); | 153 | rtnl_lock(); |
148 | tcf_exts_destroy(&r->exts); | 154 | __tcindex_destroy_rexts(r); |
149 | rtnl_unlock(); | 155 | rtnl_unlock(); |
150 | } | 156 | } |
151 | 157 | ||
@@ -158,14 +164,20 @@ static void tcindex_destroy_rexts(struct rcu_head *head) | |||
158 | tcf_queue_work(&r->work); | 164 | tcf_queue_work(&r->work); |
159 | } | 165 | } |
160 | 166 | ||
167 | static void __tcindex_destroy_fexts(struct tcindex_filter *f) | ||
168 | { | ||
169 | tcf_exts_destroy(&f->result.exts); | ||
170 | tcf_exts_put_net(&f->result.exts); | ||
171 | kfree(f); | ||
172 | } | ||
173 | |||
161 | static void tcindex_destroy_fexts_work(struct work_struct *work) | 174 | static void tcindex_destroy_fexts_work(struct work_struct *work) |
162 | { | 175 | { |
163 | struct tcindex_filter *f = container_of(work, struct tcindex_filter, | 176 | struct tcindex_filter *f = container_of(work, struct tcindex_filter, |
164 | work); | 177 | work); |
165 | 178 | ||
166 | rtnl_lock(); | 179 | rtnl_lock(); |
167 | tcf_exts_destroy(&f->result.exts); | 180 | __tcindex_destroy_fexts(f); |
168 | kfree(f); | ||
169 | rtnl_unlock(); | 181 | rtnl_unlock(); |
170 | } | 182 | } |
171 | 183 | ||
@@ -210,10 +222,17 @@ found: | |||
210 | * grace period, since converted-to-rcu actions are relying on that | 222 | * grace period, since converted-to-rcu actions are relying on that |
211 | * in cleanup() callback | 223 | * in cleanup() callback |
212 | */ | 224 | */ |
213 | if (f) | 225 | if (f) { |
214 | call_rcu(&f->rcu, tcindex_destroy_fexts); | 226 | if (tcf_exts_get_net(&f->result.exts)) |
215 | else | 227 | call_rcu(&f->rcu, tcindex_destroy_fexts); |
216 | call_rcu(&r->rcu, tcindex_destroy_rexts); | 228 | else |
229 | __tcindex_destroy_fexts(f); | ||
230 | } else { | ||
231 | if (tcf_exts_get_net(&r->exts)) | ||
232 | call_rcu(&r->rcu, tcindex_destroy_rexts); | ||
233 | else | ||
234 | __tcindex_destroy_rexts(r); | ||
235 | } | ||
217 | 236 | ||
218 | *last = false; | 237 | *last = false; |
219 | return 0; | 238 | return 0; |
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index dadd1b344497..b58eccb21f03 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c | |||
@@ -399,6 +399,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n, | |||
399 | bool free_pf) | 399 | bool free_pf) |
400 | { | 400 | { |
401 | tcf_exts_destroy(&n->exts); | 401 | tcf_exts_destroy(&n->exts); |
402 | tcf_exts_put_net(&n->exts); | ||
402 | if (n->ht_down) | 403 | if (n->ht_down) |
403 | n->ht_down->refcnt--; | 404 | n->ht_down->refcnt--; |
404 | #ifdef CONFIG_CLS_U32_PERF | 405 | #ifdef CONFIG_CLS_U32_PERF |
@@ -476,6 +477,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) | |||
476 | RCU_INIT_POINTER(*kp, key->next); | 477 | RCU_INIT_POINTER(*kp, key->next); |
477 | 478 | ||
478 | tcf_unbind_filter(tp, &key->res); | 479 | tcf_unbind_filter(tp, &key->res); |
480 | tcf_exts_get_net(&key->exts); | ||
479 | call_rcu(&key->rcu, u32_delete_key_freepf_rcu); | 481 | call_rcu(&key->rcu, u32_delete_key_freepf_rcu); |
480 | return 0; | 482 | return 0; |
481 | } | 483 | } |
@@ -588,7 +590,10 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) | |||
588 | rtnl_dereference(n->next)); | 590 | rtnl_dereference(n->next)); |
589 | tcf_unbind_filter(tp, &n->res); | 591 | tcf_unbind_filter(tp, &n->res); |
590 | u32_remove_hw_knode(tp, n->handle); | 592 | u32_remove_hw_knode(tp, n->handle); |
591 | call_rcu(&n->rcu, u32_delete_key_freepf_rcu); | 593 | if (tcf_exts_get_net(&n->exts)) |
594 | call_rcu(&n->rcu, u32_delete_key_freepf_rcu); | ||
595 | else | ||
596 | u32_destroy_key(n->tp, n, true); | ||
592 | } | 597 | } |
593 | } | 598 | } |
594 | } | 599 | } |
@@ -949,6 +954,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
949 | 954 | ||
950 | u32_replace_knode(tp, tp_c, new); | 955 | u32_replace_knode(tp, tp_c, new); |
951 | tcf_unbind_filter(tp, &n->res); | 956 | tcf_unbind_filter(tp, &n->res); |
957 | tcf_exts_get_net(&n->exts); | ||
952 | call_rcu(&n->rcu, u32_delete_key_rcu); | 958 | call_rcu(&n->rcu, u32_delete_key_rcu); |
953 | return 0; | 959 | return 0; |
954 | } | 960 | } |