Commit e9c5158ac26affd5d8ce006521bdfb7148090e18
Committed by
David S. Miller
1 parent
3a765edadb
Exists in
master
and in
20 other branches
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 = { |