diff options
| author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-01-25 19:41:38 -0500 |
|---|---|---|
| committer | Herton Ronaldo Krzesinski <herton.krzesinski@canonical.com> | 2012-02-13 15:15:06 -0500 |
| commit | c27b65ee80163095313ec7f9a33507162ae64fd7 (patch) | |
| tree | 74c6534d410f346cd2de6f9074472012d6ebcd0f /net/core | |
| parent | 99d4ddfa4c375ee1a470ffba1408be42e42e1809 (diff) | |
netns: fix net_alloc_generic()
BugLink: http://bugs.launchpad.net/bugs/926309
[ 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>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
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) { |
