diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-01-25 19:41:38 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-02-03 12:19:03 -0500 |
commit | 561331eae0a03d0c4cf60f3cf485aa3e8aa5ab48 (patch) | |
tree | 39c7f32c2c47bc3b463f9685eda5df9137a930b0 /net/core | |
parent | 4df9c291640da8992e146076f57a8e563c449e31 (diff) |
netns: fix net_alloc_generic()
[ Upstream commit 073862ba5d249c20bd5c49fc6d904ff0e1f6a672 ]
When a new net namespace is created, we should attach to it a "struct
net_generic" with enough slots (even empty), or we can hit the following
BUG_ON() :
[ 200.752016] kernel BUG at include/net/netns/generic.h:40!
...
[ 200.752016] [<ffffffff825c3cea>] ? get_cfcnfg+0x3a/0x180
[ 200.752016] [<ffffffff821cf0b0>] ? lockdep_rtnl_is_held+0x10/0x20
[ 200.752016] [<ffffffff825c41be>] caif_device_notify+0x2e/0x530
[ 200.752016] [<ffffffff810d61b7>] notifier_call_chain+0x67/0x110
[ 200.752016] [<ffffffff810d67c1>] raw_notifier_call_chain+0x11/0x20
[ 200.752016] [<ffffffff821bae82>] call_netdevice_notifiers+0x32/0x60
[ 200.752016] [<ffffffff821c2b26>] register_netdevice+0x196/0x300
[ 200.752016] [<ffffffff821c2ca9>] register_netdev+0x19/0x30
[ 200.752016] [<ffffffff81c1c67a>] loopback_net_init+0x4a/0xa0
[ 200.752016] [<ffffffff821b5e62>] ops_init+0x42/0x180
[ 200.752016] [<ffffffff821b600b>] setup_net+0x6b/0x100
[ 200.752016] [<ffffffff821b6466>] copy_net_ns+0x86/0x110
[ 200.752016] [<ffffffff810d5789>] create_new_namespaces+0xd9/0x190
net_alloc_generic() should take into account the maximum index into the
ptr array, as a subsystem might use net_generic() anytime.
This also reduces number of reallocations in net_assign_generic()
Reported-by: Sasha Levin <levinsasha928@gmail.com>
Tested-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Sjur Brændeland <sjur.brandeland@stericsson.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/net_namespace.c | 31 |
1 files changed, 16 insertions, 15 deletions
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index ea489db1bc2..0b0211d7fc3 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
@@ -29,6 +29,20 @@ EXPORT_SYMBOL(init_net); | |||
29 | 29 | ||
30 | #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ | 30 | #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ |
31 | 31 | ||
32 | static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS; | ||
33 | |||
34 | static struct net_generic *net_alloc_generic(void) | ||
35 | { | ||
36 | struct net_generic *ng; | ||
37 | size_t generic_size = offsetof(struct net_generic, ptr[max_gen_ptrs]); | ||
38 | |||
39 | ng = kzalloc(generic_size, GFP_KERNEL); | ||
40 | if (ng) | ||
41 | ng->len = max_gen_ptrs; | ||
42 | |||
43 | return ng; | ||
44 | } | ||
45 | |||
32 | static int net_assign_generic(struct net *net, int id, void *data) | 46 | static int net_assign_generic(struct net *net, int id, void *data) |
33 | { | 47 | { |
34 | struct net_generic *ng, *old_ng; | 48 | struct net_generic *ng, *old_ng; |
@@ -42,8 +56,7 @@ static int net_assign_generic(struct net *net, int id, void *data) | |||
42 | if (old_ng->len >= id) | 56 | if (old_ng->len >= id) |
43 | goto assign; | 57 | goto assign; |
44 | 58 | ||
45 | ng = kzalloc(sizeof(struct net_generic) + | 59 | ng = net_alloc_generic(); |
46 | id * sizeof(void *), GFP_KERNEL); | ||
47 | if (ng == NULL) | 60 | if (ng == NULL) |
48 | return -ENOMEM; | 61 | return -ENOMEM; |
49 | 62 | ||
@@ -58,7 +71,6 @@ static int net_assign_generic(struct net *net, int id, void *data) | |||
58 | * the old copy for kfree after a grace period. | 71 | * the old copy for kfree after a grace period. |
59 | */ | 72 | */ |
60 | 73 | ||
61 | ng->len = id; | ||
62 | memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); | 74 | memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); |
63 | 75 | ||
64 | rcu_assign_pointer(net->gen, ng); | 76 | rcu_assign_pointer(net->gen, ng); |
@@ -159,18 +171,6 @@ out_undo: | |||
159 | goto out; | 171 | goto out; |
160 | } | 172 | } |
161 | 173 | ||
162 | static struct net_generic *net_alloc_generic(void) | ||
163 | { | ||
164 | struct net_generic *ng; | ||
165 | size_t generic_size = sizeof(struct net_generic) + | ||
166 | INITIAL_NET_GEN_PTRS * sizeof(void *); | ||
167 | |||
168 | ng = kzalloc(generic_size, GFP_KERNEL); | ||
169 | if (ng) | ||
170 | ng->len = INITIAL_NET_GEN_PTRS; | ||
171 | |||
172 | return ng; | ||
173 | } | ||
174 | 174 | ||
175 | #ifdef CONFIG_NET_NS | 175 | #ifdef CONFIG_NET_NS |
176 | static struct kmem_cache *net_cachep; | 176 | static struct kmem_cache *net_cachep; |
@@ -481,6 +481,7 @@ again: | |||
481 | } | 481 | } |
482 | return error; | 482 | return error; |
483 | } | 483 | } |
484 | max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id); | ||
484 | } | 485 | } |
485 | error = __register_pernet_operations(list, ops); | 486 | error = __register_pernet_operations(list, ops); |
486 | if (error) { | 487 | if (error) { |