Commit e9c5158ac26affd5d8ce006521bdfb7148090e18

Authored by Eric W. Biederman
Committed by David S. Miller
1 parent 3a765edadb

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>

Showing 5 changed files with 57 additions and 38 deletions Side-by-side Diff

include/net/fib_rules.h
... ... @@ -66,6 +66,7 @@
66 66 struct list_head rules_list;
67 67 struct module *owner;
68 68 struct net *fro_net;
  69 + struct rcu_head rcu;
69 70 };
70 71  
71 72 #define FRA_GENERIC_POLICY \
... ... @@ -102,7 +103,7 @@
102 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 107 extern void fib_rules_unregister(struct fib_rules_ops *);
107 108 extern void fib_rules_cleanup_ops(struct fib_rules_ops *);
108 109  
net/core/fib_rules.c
... ... @@ -72,7 +72,7 @@
72 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 77 int err = -EEXIST;
78 78 struct fib_rules_ops *o;
... ... @@ -102,6 +102,28 @@
102 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 127 EXPORT_SYMBOL_GPL(fib_rules_register);
106 128  
107 129 void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
... ... @@ -115,6 +137,15 @@
115 137 }
116 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 149 void fib_rules_unregister(struct fib_rules_ops *ops)
119 150 {
120 151 struct net *net = ops->fro_net;
... ... @@ -124,8 +155,7 @@
124 155 fib_rules_cleanup_ops(ops);
125 156 spin_unlock(&net->rules_mod_lock);
126 157  
127   - synchronize_rcu();
128   - release_net(net);
  158 + call_rcu(&ops->rcu, fib_rules_put_rcu);
129 159 }
130 160  
131 161 EXPORT_SYMBOL_GPL(fib_rules_unregister);
net/decnet/dn_rules.c
... ... @@ -33,7 +33,7 @@
33 33 #include <net/dn_dev.h>
34 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 38 struct dn_fib_rule
39 39 {
... ... @@ -56,7 +56,7 @@
56 56 };
57 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 60 res->r = arg.rule;
61 61  
62 62 return err;
... ... @@ -217,9 +217,9 @@
217 217 struct list_head *pos;
218 218 struct fib_rule *rule;
219 219  
220   - if (!list_empty(&dn_fib_rules_ops.rules_list)) {
221   - pos = dn_fib_rules_ops.rules_list.next;
222   - if (pos->next != &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;
  222 + if (pos->next != &dn_fib_rules_ops->rules_list) {
223 223 rule = list_entry(pos->next, struct fib_rule, list);
224 224 if (rule->pref)
225 225 return rule->pref - 1;
... ... @@ -234,7 +234,7 @@
234 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 238 .family = AF_DECnet,
239 239 .rule_size = sizeof(struct dn_fib_rule),
240 240 .addr_size = sizeof(u16),
241 241  
242 242  
243 243  
... ... @@ -247,20 +247,22 @@
247 247 .flush_cache = dn_fib_rule_flush_cache,
248 248 .nlgroup = RTNLGRP_DECnet_RULE,
249 249 .policy = dn_fib_rule_policy,
250   - .rules_list = LIST_HEAD_INIT(dn_fib_rules_ops.rules_list),
251 250 .owner = THIS_MODULE,
252 251 .fro_net = &init_net,
253 252 };
254 253  
255 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 260 RT_TABLE_MAIN, 0));
259   - fib_rules_register(&dn_fib_rules_ops);
260 261 }
261 262  
262 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 }
net/ipv4/fib_rules.c
... ... @@ -301,14 +301,10 @@
301 301 int err;
302 302 struct fib_rules_ops *ops;
303 303  
304   - ops = kmemdup(&fib4_rules_ops_template, sizeof(*ops), GFP_KERNEL);
305   - if (ops == NULL)
306   - return -ENOMEM;
307   - INIT_LIST_HEAD(&ops->rules_list);
308   - ops->fro_net = net;
  304 + ops = fib_rules_register(&fib4_rules_ops_template, net);
  305 + if (IS_ERR(ops))
  306 + return PTR_ERR(ops);
309 307  
310   - fib_rules_register(ops);
311   -
312 308 err = fib_default_rules_init(ops);
313 309 if (err < 0)
314 310 goto fail;
315 311  
... ... @@ -318,13 +314,11 @@
318 314 fail:
319 315 /* also cleans all rules already added */
320 316 fib_rules_unregister(ops);
321   - kfree(ops);
322 317 return err;
323 318 }
324 319  
325 320 void __net_exit fib4_rules_exit(struct net *net)
326 321 {
327 322 fib_rules_unregister(net->ipv4.rules_ops);
328   - kfree(net->ipv4.rules_ops);
329 323 }
net/ipv6/fib6_rules.c
... ... @@ -264,16 +264,14 @@
264 264  
265 265 static int fib6_rules_net_init(struct net *net)
266 266 {
  267 + struct fib_rules_ops *ops;
267 268 int err = -ENOMEM;
268 269  
269   - net->ipv6.fib6_rules_ops = kmemdup(&fib6_rules_ops_template,
270   - sizeof(*net->ipv6.fib6_rules_ops),
271   - GFP_KERNEL);
272   - if (!net->ipv6.fib6_rules_ops)
273   - goto out;
  270 + ops = fib_rules_register(&fib6_rules_ops_template, net);
  271 + if (IS_ERR(ops))
  272 + return PTR_ERR(ops);
  273 + net->ipv6.fib6_rules_ops = ops;
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 276 err = fib_default_rule_add(net->ipv6.fib6_rules_ops, 0,
279 277 RT6_TABLE_LOCAL, 0);
280 278  
281 279  
282 280  
283 281  
... ... @@ -283,25 +281,19 @@
283 281 err = fib_default_rule_add(net->ipv6.fib6_rules_ops,
284 282 0x7FFE, RT6_TABLE_MAIN, 0);
285 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 286 out:
292 287 return err;
293 288  
294   -out_fib6_default_rule_add:
295   - fib_rules_cleanup_ops(net->ipv6.fib6_rules_ops);
296 289 out_fib6_rules_ops:
297   - kfree(net->ipv6.fib6_rules_ops);
  290 + fib_rules_unregister(ops);
298 291 goto out;
299 292 }
300 293  
301 294 static void fib6_rules_net_exit(struct net *net)
302 295 {
303 296 fib_rules_unregister(net->ipv6.fib6_rules_ops);
304   - kfree(net->ipv6.fib6_rules_ops);
305 297 }
306 298  
307 299 static struct pernet_operations fib6_rules_net_ops = {