diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2009-12-03 15:22:55 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-03 15:22:55 -0500 |
commit | e9c5158ac26affd5d8ce006521bdfb7148090e18 (patch) | |
tree | a8d43fb446a1908175923f4109c977dcb84e4f91 | |
parent | 3a765edadb28cc736d185f67d1ba6bedcc85f4b9 (diff) |
net: Allow fib_rule_unregister to batch
Refactor the code so fib_rules_register always takes a template instead
of the actual fib_rules_ops structure that will be used. This is
required for network namespace support so 2 out of the 3 callers already
do this, it allows the error handling to be made common, and it allows
fib_rules_unregister to free the template for hte caller.
Modify fib_rules_unregister to use call_rcu instead of syncrhonize_rcu
to allw multiple namespaces to be cleaned up in the same rcu grace
period.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/fib_rules.h | 3 | ||||
-rw-r--r-- | net/core/fib_rules.c | 36 | ||||
-rw-r--r-- | net/decnet/dn_rules.c | 22 | ||||
-rw-r--r-- | net/ipv4/fib_rules.c | 12 | ||||
-rw-r--r-- | net/ipv6/fib6_rules.c | 22 |
5 files changed, 57 insertions, 38 deletions
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index d4e875a58f8b..c07ac9650ebc 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h | |||
@@ -66,6 +66,7 @@ struct fib_rules_ops { | |||
66 | struct list_head rules_list; | 66 | struct list_head rules_list; |
67 | struct module *owner; | 67 | struct module *owner; |
68 | struct net *fro_net; | 68 | struct net *fro_net; |
69 | struct rcu_head rcu; | ||
69 | }; | 70 | }; |
70 | 71 | ||
71 | #define FRA_GENERIC_POLICY \ | 72 | #define FRA_GENERIC_POLICY \ |
@@ -102,7 +103,7 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) | |||
102 | return frh->table; | 103 | return frh->table; |
103 | } | 104 | } |
104 | 105 | ||
105 | extern int fib_rules_register(struct fib_rules_ops *); | 106 | extern struct fib_rules_ops *fib_rules_register(struct fib_rules_ops *, struct net *); |
106 | extern void fib_rules_unregister(struct fib_rules_ops *); | 107 | extern void fib_rules_unregister(struct fib_rules_ops *); |
107 | extern void fib_rules_cleanup_ops(struct fib_rules_ops *); | 108 | extern void fib_rules_cleanup_ops(struct fib_rules_ops *); |
108 | 109 | ||
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index ef0e7d9e664b..02a3b2c69c1e 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c | |||
@@ -72,7 +72,7 @@ static void flush_route_cache(struct fib_rules_ops *ops) | |||
72 | ops->flush_cache(ops); | 72 | ops->flush_cache(ops); |
73 | } | 73 | } |
74 | 74 | ||
75 | int fib_rules_register(struct fib_rules_ops *ops) | 75 | static int __fib_rules_register(struct fib_rules_ops *ops) |
76 | { | 76 | { |
77 | int err = -EEXIST; | 77 | int err = -EEXIST; |
78 | struct fib_rules_ops *o; | 78 | struct fib_rules_ops *o; |
@@ -102,6 +102,28 @@ errout: | |||
102 | return err; | 102 | return err; |
103 | } | 103 | } |
104 | 104 | ||
105 | struct fib_rules_ops * | ||
106 | fib_rules_register(struct fib_rules_ops *tmpl, struct net *net) | ||
107 | { | ||
108 | struct fib_rules_ops *ops; | ||
109 | int err; | ||
110 | |||
111 | ops = kmemdup(tmpl, sizeof (*ops), GFP_KERNEL); | ||
112 | if (ops == NULL) | ||
113 | return ERR_PTR(-ENOMEM); | ||
114 | |||
115 | INIT_LIST_HEAD(&ops->rules_list); | ||
116 | ops->fro_net = net; | ||
117 | |||
118 | err = __fib_rules_register(ops); | ||
119 | if (err) { | ||
120 | kfree(ops); | ||
121 | ops = ERR_PTR(err); | ||
122 | } | ||
123 | |||
124 | return ops; | ||
125 | } | ||
126 | |||
105 | EXPORT_SYMBOL_GPL(fib_rules_register); | 127 | EXPORT_SYMBOL_GPL(fib_rules_register); |
106 | 128 | ||
107 | void fib_rules_cleanup_ops(struct fib_rules_ops *ops) | 129 | void fib_rules_cleanup_ops(struct fib_rules_ops *ops) |
@@ -115,6 +137,15 @@ void fib_rules_cleanup_ops(struct fib_rules_ops *ops) | |||
115 | } | 137 | } |
116 | EXPORT_SYMBOL_GPL(fib_rules_cleanup_ops); | 138 | EXPORT_SYMBOL_GPL(fib_rules_cleanup_ops); |
117 | 139 | ||
140 | static void fib_rules_put_rcu(struct rcu_head *head) | ||
141 | { | ||
142 | struct fib_rules_ops *ops = container_of(head, struct fib_rules_ops, rcu); | ||
143 | struct net *net = ops->fro_net; | ||
144 | |||
145 | release_net(net); | ||
146 | kfree(ops); | ||
147 | } | ||
148 | |||
118 | void fib_rules_unregister(struct fib_rules_ops *ops) | 149 | void fib_rules_unregister(struct fib_rules_ops *ops) |
119 | { | 150 | { |
120 | struct net *net = ops->fro_net; | 151 | struct net *net = ops->fro_net; |
@@ -124,8 +155,7 @@ void fib_rules_unregister(struct fib_rules_ops *ops) | |||
124 | fib_rules_cleanup_ops(ops); | 155 | fib_rules_cleanup_ops(ops); |
125 | spin_unlock(&net->rules_mod_lock); | 156 | spin_unlock(&net->rules_mod_lock); |
126 | 157 | ||
127 | synchronize_rcu(); | 158 | call_rcu(&ops->rcu, fib_rules_put_rcu); |
128 | release_net(net); | ||
129 | } | 159 | } |
130 | 160 | ||
131 | EXPORT_SYMBOL_GPL(fib_rules_unregister); | 161 | EXPORT_SYMBOL_GPL(fib_rules_unregister); |
diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 72495f25269f..7466c546f286 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c | |||
@@ -33,7 +33,7 @@ | |||
33 | #include <net/dn_dev.h> | 33 | #include <net/dn_dev.h> |
34 | #include <net/dn_route.h> | 34 | #include <net/dn_route.h> |
35 | 35 | ||
36 | static struct fib_rules_ops dn_fib_rules_ops; | 36 | static struct fib_rules_ops *dn_fib_rules_ops; |
37 | 37 | ||
38 | struct dn_fib_rule | 38 | struct dn_fib_rule |
39 | { | 39 | { |
@@ -56,7 +56,7 @@ int dn_fib_lookup(struct flowi *flp, struct dn_fib_res *res) | |||
56 | }; | 56 | }; |
57 | int err; | 57 | int err; |
58 | 58 | ||
59 | err = fib_rules_lookup(&dn_fib_rules_ops, flp, 0, &arg); | 59 | err = fib_rules_lookup(dn_fib_rules_ops, flp, 0, &arg); |
60 | res->r = arg.rule; | 60 | res->r = arg.rule; |
61 | 61 | ||
62 | return err; | 62 | return err; |
@@ -217,9 +217,9 @@ static u32 dn_fib_rule_default_pref(struct fib_rules_ops *ops) | |||
217 | struct list_head *pos; | 217 | struct list_head *pos; |
218 | struct fib_rule *rule; | 218 | struct fib_rule *rule; |
219 | 219 | ||
220 | if (!list_empty(&dn_fib_rules_ops.rules_list)) { | 220 | if (!list_empty(&dn_fib_rules_ops->rules_list)) { |
221 | pos = dn_fib_rules_ops.rules_list.next; | 221 | pos = dn_fib_rules_ops->rules_list.next; |
222 | if (pos->next != &dn_fib_rules_ops.rules_list) { | 222 | if (pos->next != &dn_fib_rules_ops->rules_list) { |
223 | rule = list_entry(pos->next, struct fib_rule, list); | 223 | rule = list_entry(pos->next, struct fib_rule, list); |
224 | if (rule->pref) | 224 | if (rule->pref) |
225 | return rule->pref - 1; | 225 | return rule->pref - 1; |
@@ -234,7 +234,7 @@ static void dn_fib_rule_flush_cache(struct fib_rules_ops *ops) | |||
234 | dn_rt_cache_flush(-1); | 234 | dn_rt_cache_flush(-1); |
235 | } | 235 | } |
236 | 236 | ||
237 | static struct fib_rules_ops dn_fib_rules_ops = { | 237 | static struct fib_rules_ops dn_fib_rules_ops_template = { |
238 | .family = AF_DECnet, | 238 | .family = AF_DECnet, |
239 | .rule_size = sizeof(struct dn_fib_rule), | 239 | .rule_size = sizeof(struct dn_fib_rule), |
240 | .addr_size = sizeof(u16), | 240 | .addr_size = sizeof(u16), |
@@ -247,21 +247,23 @@ static struct fib_rules_ops dn_fib_rules_ops = { | |||
247 | .flush_cache = dn_fib_rule_flush_cache, | 247 | .flush_cache = dn_fib_rule_flush_cache, |
248 | .nlgroup = RTNLGRP_DECnet_RULE, | 248 | .nlgroup = RTNLGRP_DECnet_RULE, |
249 | .policy = dn_fib_rule_policy, | 249 | .policy = dn_fib_rule_policy, |
250 | .rules_list = LIST_HEAD_INIT(dn_fib_rules_ops.rules_list), | ||
251 | .owner = THIS_MODULE, | 250 | .owner = THIS_MODULE, |
252 | .fro_net = &init_net, | 251 | .fro_net = &init_net, |
253 | }; | 252 | }; |
254 | 253 | ||
255 | void __init dn_fib_rules_init(void) | 254 | void __init dn_fib_rules_init(void) |
256 | { | 255 | { |
257 | BUG_ON(fib_default_rule_add(&dn_fib_rules_ops, 0x7fff, | 256 | dn_fib_rules_ops = |
257 | fib_rules_register(&dn_fib_rules_ops_template, &init_net); | ||
258 | BUG_ON(IS_ERR(dn_fib_rules_ops)); | ||
259 | BUG_ON(fib_default_rule_add(dn_fib_rules_ops, 0x7fff, | ||
258 | RT_TABLE_MAIN, 0)); | 260 | RT_TABLE_MAIN, 0)); |
259 | fib_rules_register(&dn_fib_rules_ops); | ||
260 | } | 261 | } |
261 | 262 | ||
262 | void __exit dn_fib_rules_cleanup(void) | 263 | void __exit dn_fib_rules_cleanup(void) |
263 | { | 264 | { |
264 | fib_rules_unregister(&dn_fib_rules_ops); | 265 | fib_rules_unregister(dn_fib_rules_ops); |
266 | rcu_barrier(); | ||
265 | } | 267 | } |
266 | 268 | ||
267 | 269 | ||
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 1239ed23cab6..ca2d07b1c706 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c | |||
@@ -301,13 +301,9 @@ int __net_init fib4_rules_init(struct net *net) | |||
301 | int err; | 301 | int err; |
302 | struct fib_rules_ops *ops; | 302 | struct fib_rules_ops *ops; |
303 | 303 | ||
304 | ops = kmemdup(&fib4_rules_ops_template, sizeof(*ops), GFP_KERNEL); | 304 | ops = fib_rules_register(&fib4_rules_ops_template, net); |
305 | if (ops == NULL) | 305 | if (IS_ERR(ops)) |
306 | return -ENOMEM; | 306 | return PTR_ERR(ops); |
307 | INIT_LIST_HEAD(&ops->rules_list); | ||
308 | ops->fro_net = net; | ||
309 | |||
310 | fib_rules_register(ops); | ||
311 | 307 | ||
312 | err = fib_default_rules_init(ops); | 308 | err = fib_default_rules_init(ops); |
313 | if (err < 0) | 309 | if (err < 0) |
@@ -318,12 +314,10 @@ int __net_init fib4_rules_init(struct net *net) | |||
318 | fail: | 314 | fail: |
319 | /* also cleans all rules already added */ | 315 | /* also cleans all rules already added */ |
320 | fib_rules_unregister(ops); | 316 | fib_rules_unregister(ops); |
321 | kfree(ops); | ||
322 | return err; | 317 | return err; |
323 | } | 318 | } |
324 | 319 | ||
325 | void __net_exit fib4_rules_exit(struct net *net) | 320 | void __net_exit fib4_rules_exit(struct net *net) |
326 | { | 321 | { |
327 | fib_rules_unregister(net->ipv4.rules_ops); | 322 | fib_rules_unregister(net->ipv4.rules_ops); |
328 | kfree(net->ipv4.rules_ops); | ||
329 | } | 323 | } |
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 3b38f49f2c28..b7aa7c64cc4a 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c | |||
@@ -264,16 +264,14 @@ static struct fib_rules_ops fib6_rules_ops_template = { | |||
264 | 264 | ||
265 | static int fib6_rules_net_init(struct net *net) | 265 | static int fib6_rules_net_init(struct net *net) |
266 | { | 266 | { |
267 | struct fib_rules_ops *ops; | ||
267 | int err = -ENOMEM; | 268 | int err = -ENOMEM; |
268 | 269 | ||
269 | net->ipv6.fib6_rules_ops = kmemdup(&fib6_rules_ops_template, | 270 | ops = fib_rules_register(&fib6_rules_ops_template, net); |
270 | sizeof(*net->ipv6.fib6_rules_ops), | 271 | if (IS_ERR(ops)) |
271 | GFP_KERNEL); | 272 | return PTR_ERR(ops); |
272 | if (!net->ipv6.fib6_rules_ops) | 273 | net->ipv6.fib6_rules_ops = ops; |
273 | goto out; | ||
274 | 274 | ||
275 | net->ipv6.fib6_rules_ops->fro_net = net; | ||
276 | INIT_LIST_HEAD(&net->ipv6.fib6_rules_ops->rules_list); | ||
277 | 275 | ||
278 | err = fib_default_rule_add(net->ipv6.fib6_rules_ops, 0, | 276 | err = fib_default_rule_add(net->ipv6.fib6_rules_ops, 0, |
279 | RT6_TABLE_LOCAL, 0); | 277 | RT6_TABLE_LOCAL, 0); |
@@ -283,25 +281,19 @@ static int fib6_rules_net_init(struct net *net) | |||
283 | err = fib_default_rule_add(net->ipv6.fib6_rules_ops, | 281 | err = fib_default_rule_add(net->ipv6.fib6_rules_ops, |
284 | 0x7FFE, RT6_TABLE_MAIN, 0); | 282 | 0x7FFE, RT6_TABLE_MAIN, 0); |
285 | if (err) | 283 | if (err) |
286 | goto out_fib6_default_rule_add; | 284 | goto out_fib6_rules_ops; |
287 | 285 | ||
288 | err = fib_rules_register(net->ipv6.fib6_rules_ops); | ||
289 | if (err) | ||
290 | goto out_fib6_default_rule_add; | ||
291 | out: | 286 | out: |
292 | return err; | 287 | return err; |
293 | 288 | ||
294 | out_fib6_default_rule_add: | ||
295 | fib_rules_cleanup_ops(net->ipv6.fib6_rules_ops); | ||
296 | out_fib6_rules_ops: | 289 | out_fib6_rules_ops: |
297 | kfree(net->ipv6.fib6_rules_ops); | 290 | fib_rules_unregister(ops); |
298 | goto out; | 291 | goto out; |
299 | } | 292 | } |
300 | 293 | ||
301 | static void fib6_rules_net_exit(struct net *net) | 294 | static void fib6_rules_net_exit(struct net *net) |
302 | { | 295 | { |
303 | fib_rules_unregister(net->ipv6.fib6_rules_ops); | 296 | fib_rules_unregister(net->ipv6.fib6_rules_ops); |
304 | kfree(net->ipv6.fib6_rules_ops); | ||
305 | } | 297 | } |
306 | 298 | ||
307 | static struct pernet_operations fib6_rules_net_ops = { | 299 | static struct pernet_operations fib6_rules_net_ops = { |