aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2009-12-03 15:22:55 -0500
committerDavid S. Miller <davem@davemloft.net>2009-12-03 15:22:55 -0500
commite9c5158ac26affd5d8ce006521bdfb7148090e18 (patch)
treea8d43fb446a1908175923f4109c977dcb84e4f91
parent3a765edadb28cc736d185f67d1ba6bedcc85f4b9 (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.h3
-rw-r--r--net/core/fib_rules.c36
-rw-r--r--net/decnet/dn_rules.c22
-rw-r--r--net/ipv4/fib_rules.c12
-rw-r--r--net/ipv6/fib6_rules.c22
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
105extern int fib_rules_register(struct fib_rules_ops *); 106extern struct fib_rules_ops *fib_rules_register(struct fib_rules_ops *, struct net *);
106extern void fib_rules_unregister(struct fib_rules_ops *); 107extern void fib_rules_unregister(struct fib_rules_ops *);
107extern void fib_rules_cleanup_ops(struct fib_rules_ops *); 108extern 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
75int fib_rules_register(struct fib_rules_ops *ops) 75static 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
105struct fib_rules_ops *
106fib_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
105EXPORT_SYMBOL_GPL(fib_rules_register); 127EXPORT_SYMBOL_GPL(fib_rules_register);
106 128
107void fib_rules_cleanup_ops(struct fib_rules_ops *ops) 129void 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}
116EXPORT_SYMBOL_GPL(fib_rules_cleanup_ops); 138EXPORT_SYMBOL_GPL(fib_rules_cleanup_ops);
117 139
140static 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
118void fib_rules_unregister(struct fib_rules_ops *ops) 149void 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
131EXPORT_SYMBOL_GPL(fib_rules_unregister); 161EXPORT_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
36static struct fib_rules_ops dn_fib_rules_ops; 36static struct fib_rules_ops *dn_fib_rules_ops;
37 37
38struct dn_fib_rule 38struct 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
237static struct fib_rules_ops dn_fib_rules_ops = { 237static 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
255void __init dn_fib_rules_init(void) 254void __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
262void __exit dn_fib_rules_cleanup(void) 263void __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)
318fail: 314fail:
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
325void __net_exit fib4_rules_exit(struct net *net) 320void __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
265static int fib6_rules_net_init(struct net *net) 265static 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;
291out: 286out:
292 return err; 287 return err;
293 288
294out_fib6_default_rule_add:
295 fib_rules_cleanup_ops(net->ipv6.fib6_rules_ops);
296out_fib6_rules_ops: 289out_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
301static void fib6_rules_net_exit(struct net *net) 294static 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
307static struct pernet_operations fib6_rules_net_ops = { 299static struct pernet_operations fib6_rules_net_ops = {