aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2017-11-08 20:03:10 -0500
committerDavid S. Miller <davem@davemloft.net>2017-11-08 20:03:10 -0500
commit623859ae06b85cabba79ce78f0d49e67783d4c34 (patch)
tree4600cfecab2372a77cfb9143e4b24b059d24433a
parent8f5624629105589bcc23d0e51cc01bd8103d09a5 (diff)
parent35c55fc156d85a396a975fc17636f560fc02fd65 (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>
-rw-r--r--include/net/act_api.h4
-rw-r--r--include/net/pkt_cls.h24
-rw-r--r--net/sched/act_api.c2
-rw-r--r--net/sched/act_bpf.c2
-rw-r--r--net/sched/act_connmark.c2
-rw-r--r--net/sched/act_csum.c2
-rw-r--r--net/sched/act_gact.c2
-rw-r--r--net/sched/act_ife.c2
-rw-r--r--net/sched/act_ipt.c4
-rw-r--r--net/sched/act_mirred.c2
-rw-r--r--net/sched/act_nat.c2
-rw-r--r--net/sched/act_pedit.c2
-rw-r--r--net/sched/act_police.c2
-rw-r--r--net/sched/act_sample.c2
-rw-r--r--net/sched/act_simple.c2
-rw-r--r--net/sched/act_skbedit.c2
-rw-r--r--net/sched/act_skbmod.c2
-rw-r--r--net/sched/act_tunnel_key.c2
-rw-r--r--net/sched/act_vlan.c2
-rw-r--r--net/sched/cls_api.c1
-rw-r--r--net/sched/cls_basic.c20
-rw-r--r--net/sched/cls_bpf.c7
-rw-r--r--net/sched/cls_cgroup.c24
-rw-r--r--net/sched/cls_flow.c24
-rw-r--r--net/sched/cls_flower.c16
-rw-r--r--net/sched/cls_fw.c17
-rw-r--r--net/sched/cls_matchall.c15
-rw-r--r--net/sched/cls_route.c17
-rw-r--r--net/sched/cls_rsvp.h15
-rw-r--r--net/sched/cls_tcindex.c33
-rw-r--r--net/sched/cls_u32.c8
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 @@
14struct tcf_idrinfo { 14struct 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
20struct tc_action_ops; 19struct tc_action_ops;
@@ -106,7 +105,7 @@ struct tc_action_net {
106 105
107static inline 106static inline
108int tc_action_net_init(struct tc_action_net *tn, 107int 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 */
126static 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
136static 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
120static inline void tcf_exts_to_list(const struct tcf_exts *exts, 144static 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
404static void __net_exit bpf_exit_net(struct net *net) 404static 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
212static void __net_exit connmark_exit_net(struct net *net) 212static 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
632static void __net_exit csum_exit_net(struct net *net) 632static 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
238static void __net_exit gact_exit_net(struct net *net) 238static 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
824static void __net_exit ife_exit_net(struct net *net) 824static 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
340static void __net_exit ipt_exit_net(struct net *net) 340static 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
390static void __net_exit xt_exit_net(struct net *net) 390static 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
349static void __net_exit mirred_exit_net(struct net *net) 349static 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
313static void __net_exit nat_exit_net(struct net *net) 313static 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
456static void __net_exit pedit_exit_net(struct net *net) 456static 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
337static void __net_exit police_exit_net(struct net *net) 337static 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
246static void __net_exit sample_exit_net(struct net *net) 246static 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
207static void __net_exit simp_exit_net(struct net *net) 207static 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
244static void __net_exit skbedit_exit_net(struct net *net) 244static 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
269static void __net_exit skbmod_exit_net(struct net *net) 269static 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
328static void __net_exit tunnel_key_exit_net(struct net *net) 328static 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
275static void __net_exit vlan_exit_net(struct net *net) 275static 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
88static 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
88static void basic_delete_filter_work(struct work_struct *work) 96static 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
100static void basic_delete_filter(struct rcu_head *head) 105static 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)
249static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) 249static 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
288static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last) 292static 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
63static 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
63static void cls_cgroup_destroy_work(struct work_struct *work) 71static 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;
130errout: 138errout:
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
145static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last) 157static 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
375static void flow_destroy_filter_work(struct work_struct *work) 375static 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
384static 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
559err2: 567err2:
@@ -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
221static 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
221static void fl_destroy_filter_work(struct work_struct *work) 228static 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
324static void fl_destroy_sleepable(struct work_struct *work) 333static 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
125static 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
125static void fw_delete_filter_work(struct work_struct *work) 132static 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
47static 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
47static void mall_destroy_work(struct work_struct *work) 54static 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
115static void *mall_get(struct tcf_proto *tp, u32 handle) 124static 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
260static 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
260static void route4_delete_filter_work(struct work_struct *work) 267static 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
288static 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
288static void rsvp_delete_filter_work(struct work_struct *work) 295static 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
316static void rsvp_destroy(struct tcf_proto *tp) 325static 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
142static 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
142static void tcindex_destroy_rexts_work(struct work_struct *work) 148static 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
167static 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
161static void tcindex_destroy_fexts_work(struct work_struct *work) 174static 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 }