diff options
author | Cong Wang <xiyou.wangcong@gmail.com> | 2017-11-01 13:23:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-11-02 21:30:38 -0400 |
commit | ceffcc5e254b450e6159f173e4538215cebf1b59 (patch) | |
tree | e774e0c77b5f8a2979794ed08cc93b6365852114 | |
parent | a159d3c4b8291998c018f0dbddd4678315264a1e (diff) |
net_sched: hold netns refcnt for each action
TC actions have been destroyed asynchronously for a long time,
previously in a RCU callback and now in a workqueue. If we
don't hold a refcnt for its netns, we could use the per netns
data structure, struct tcf_idrinfo, after it has been freed by
netns workqueue.
Hold refcnt to ensure netns destroy happens after all actions
are gone.
Fixes: ddf97ccdd7cb ("net_sched: add network namespace support for tc actions")
Reported-by: Lucas Bates <lucasb@mojatatu.com>
Tested-by: 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.h | 4 | ||||
-rw-r--r-- | net/sched/act_api.c | 2 | ||||
-rw-r--r-- | net/sched/act_bpf.c | 2 | ||||
-rw-r--r-- | net/sched/act_connmark.c | 2 | ||||
-rw-r--r-- | net/sched/act_csum.c | 2 | ||||
-rw-r--r-- | net/sched/act_gact.c | 2 | ||||
-rw-r--r-- | net/sched/act_ife.c | 2 | ||||
-rw-r--r-- | net/sched/act_ipt.c | 4 | ||||
-rw-r--r-- | net/sched/act_mirred.c | 2 | ||||
-rw-r--r-- | net/sched/act_nat.c | 2 | ||||
-rw-r--r-- | net/sched/act_pedit.c | 2 | ||||
-rw-r--r-- | net/sched/act_police.c | 2 | ||||
-rw-r--r-- | net/sched/act_sample.c | 2 | ||||
-rw-r--r-- | net/sched/act_simple.c | 2 | ||||
-rw-r--r-- | net/sched/act_skbedit.c | 2 | ||||
-rw-r--r-- | net/sched/act_skbmod.c | 2 | ||||
-rw-r--r-- | net/sched/act_tunnel_key.c | 2 | ||||
-rw-r--r-- | net/sched/act_vlan.c | 2 |
18 files changed, 22 insertions, 18 deletions
diff --git a/include/net/act_api.h b/include/net/act_api.h index 5072446d5f06..c68551255032 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h | |||
@@ -13,6 +13,7 @@ | |||
13 | struct tcf_idrinfo { | 13 | struct tcf_idrinfo { |
14 | spinlock_t lock; | 14 | spinlock_t lock; |
15 | struct idr action_idr; | 15 | struct idr action_idr; |
16 | struct net *net; | ||
16 | }; | 17 | }; |
17 | 18 | ||
18 | struct tc_action_ops; | 19 | struct tc_action_ops; |
@@ -104,7 +105,7 @@ struct tc_action_net { | |||
104 | 105 | ||
105 | static inline | 106 | static inline |
106 | int tc_action_net_init(struct tc_action_net *tn, | 107 | int tc_action_net_init(struct tc_action_net *tn, |
107 | const struct tc_action_ops *ops) | 108 | const struct tc_action_ops *ops, struct net *net) |
108 | { | 109 | { |
109 | int err = 0; | 110 | int err = 0; |
110 | 111 | ||
@@ -112,6 +113,7 @@ int tc_action_net_init(struct tc_action_net *tn, | |||
112 | if (!tn->idrinfo) | 113 | if (!tn->idrinfo) |
113 | return -ENOMEM; | 114 | return -ENOMEM; |
114 | tn->ops = ops; | 115 | tn->ops = ops; |
116 | tn->idrinfo->net = net; | ||
115 | spin_lock_init(&tn->idrinfo->lock); | 117 | spin_lock_init(&tn->idrinfo->lock); |
116 | idr_init(&tn->idrinfo->action_idr); | 118 | idr_init(&tn->idrinfo->action_idr); |
117 | return err; | 119 | return err; |
diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 8f2c63514956..ca2ff0b3123f 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c | |||
@@ -78,6 +78,7 @@ 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); | ||
81 | gen_kill_estimator(&p->tcfa_rate_est); | 82 | gen_kill_estimator(&p->tcfa_rate_est); |
82 | free_tcf(p); | 83 | free_tcf(p); |
83 | } | 84 | } |
@@ -336,6 +337,7 @@ err3: | |||
336 | p->idrinfo = idrinfo; | 337 | p->idrinfo = idrinfo; |
337 | p->ops = ops; | 338 | p->ops = ops; |
338 | INIT_LIST_HEAD(&p->list); | 339 | INIT_LIST_HEAD(&p->list); |
340 | get_net(idrinfo->net); | ||
339 | *a = p; | 341 | *a = p; |
340 | return 0; | 342 | return 0; |
341 | } | 343 | } |
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index c0c707eb2c96..9bce8cc84cbb 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); | 401 | return tc_action_net_init(tn, &act_bpf_ops, net); |
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 10b7a8855a6c..34e52d01a5dd 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); | 209 | return tc_action_net_init(tn, &act_connmark_ops, net); |
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 1c40caadcff9..35171df2ebef 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); | 629 | return tc_action_net_init(tn, &act_csum_ops, net); |
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 e29a48ef7fc3..ef7f7f39d26d 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); | 235 | return tc_action_net_init(tn, &act_gact_ops, net); |
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 8ccd35825b6b..f65e4b5058e0 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); | 821 | return tc_action_net_init(tn, &act_ife_ops, net); |
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 d9e399a7e3d5..dbdf3b2470d5 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); | 337 | return tc_action_net_init(tn, &act_ipt_ops, net); |
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); | 387 | return tc_action_net_init(tn, &act_xt_ops, net); |
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 416627c66f08..84759cfd5a33 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); | 346 | return tc_action_net_init(tn, &act_mirred_ops, net); |
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 c365d01b99c8..7eeaaf9217b6 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); | 310 | return tc_action_net_init(tn, &act_nat_ops, net); |
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 491fe5deb09e..b3d82c334a5f 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); | 453 | return tc_action_net_init(tn, &act_pedit_ops, net); |
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 3bb2ebf9e9ae..9ec42b26e4b9 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); | 334 | return tc_action_net_init(tn, &act_police_ops, net); |
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 8b5abcd2f32f..e69a1e3a39bf 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); | 243 | return tc_action_net_init(tn, &act_sample_ops, net); |
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 e7b57e5071a3..a8d0ea95f894 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); | 204 | return tc_action_net_init(tn, &act_simp_ops, net); |
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 59949d61f20d..fbac62472e09 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); | 241 | return tc_action_net_init(tn, &act_skbedit_ops, net); |
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 b642ad3d39dd..8e12d8897d2f 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); | 266 | return tc_action_net_init(tn, &act_skbmod_ops, net); |
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 30c96274c638..c33faa373cf2 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); | 325 | return tc_action_net_init(tn, &act_tunnel_key_ops, net); |
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 16eb067a8d8f..115fc33cc6d8 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); | 272 | return tc_action_net_init(tn, &act_vlan_ops, net); |
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) |