[PATCH] net: ipv6: avoid errors due to per-cpu atomic alloc

Subsystems: networking [general], networking [ipv4/ipv6], the rest

STALE3549d

4 messages, 3 authors, 2016-11-23 · open the first message on its own page

[PATCH] net: ipv6: avoid errors due to per-cpu atomic alloc

From: Mike Manning <hidden>
Date: 2016-11-22 11:02:23

Bursts of failures may occur when adding IPv6 routes via Netlink to the
kernel when testing under scale (e.g. 500 routes lost out of 1M). The
reason is that percpu.c:pcpu_balance_workfn() is not guaranteed to have
extended the area map in time for the atomic allocation using percpu.c:
pcpu_alloc() to succeed. This results in route additions failing with
an -ENOMEM error.

While the sender of the Netlink msg to add this route could check for
an ACK and retransmit in the case of an -ENOMEM error, the latter
should not occur in the first place if there is plenty of memory. The
solution is to use non-atomic alloc for rt6_info instead. While the
client may now be blocked for longer depending on the state of the
chunk being added to, this work has to be incurred at some point.

The alternative solution would be to provide configurable parameters
e.g. via sysctl in percpu.c for default map size, low/high empty pages
and map margins. For this solution, the map margin sizes need to be
stored per chunk, as large margins cannot be used if the dynamic early
slots map size is in use. This is not a preferred solution though, as
it requires tuning of these parameters to provide sufficient margins to
avoid -ENOMEM errors depending on system requirements.

Signed-off-by: Mike Manning <redacted>
---
 net/ipv6/route.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 1b57e11..0e9bb76 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -347,7 +347,7 @@ struct rt6_info *ip6_dst_alloc(struct net *net,
 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
 
 	if (rt) {
-		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
+		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_KERNEL);
 		if (rt->rt6i_pcpu) {
 			int cpu;
 
-- 
1.7.10.4

Re: [PATCH] net: ipv6: avoid errors due to per-cpu atomic alloc

From: Hannes Frederic Sowa <hidden>
Date: 2016-11-22 12:18:54

On 22.11.2016 11:34, Mike Manning wrote:
quoted hunk
Bursts of failures may occur when adding IPv6 routes via Netlink to the
kernel when testing under scale (e.g. 500 routes lost out of 1M). The
reason is that percpu.c:pcpu_balance_workfn() is not guaranteed to have
extended the area map in time for the atomic allocation using percpu.c:
pcpu_alloc() to succeed. This results in route additions failing with
an -ENOMEM error.

While the sender of the Netlink msg to add this route could check for
an ACK and retransmit in the case of an -ENOMEM error, the latter
should not occur in the first place if there is plenty of memory. The
solution is to use non-atomic alloc for rt6_info instead. While the
client may now be blocked for longer depending on the state of the
chunk being added to, this work has to be incurred at some point.

The alternative solution would be to provide configurable parameters
e.g. via sysctl in percpu.c for default map size, low/high empty pages
and map margins. For this solution, the map margin sizes need to be
stored per chunk, as large margins cannot be used if the dynamic early
slots map size is in use. This is not a preferred solution though, as
it requires tuning of these parameters to provide sufficient margins to
avoid -ENOMEM errors depending on system requirements.

Signed-off-by: Mike Manning <redacted>
---
 net/ipv6/route.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 1b57e11..0e9bb76 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -347,7 +347,7 @@ struct rt6_info *ip6_dst_alloc(struct net *net,
 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
 
 	if (rt) {
-		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
+		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_KERNEL);
 		if (rt->rt6i_pcpu) {
 			int cpu;
Nak, this doesn't work, as ip6_dst_alloc must be callable from
non-blocking code paths unfortunately.

Re: [PATCH] net: ipv6: avoid errors due to per-cpu atomic alloc

From: Mike Manning <hidden>
Date: 2016-11-22 13:18:38

On 11/22/2016 12:18 PM, Hannes Frederic Sowa wrote:
On 22.11.2016 11:34, Mike Manning wrote:
quoted
Bursts of failures may occur when adding IPv6 routes via Netlink to the
kernel when testing under scale (e.g. 500 routes lost out of 1M). The
reason is that percpu.c:pcpu_balance_workfn() is not guaranteed to have
extended the area map in time for the atomic allocation using percpu.c:
pcpu_alloc() to succeed. This results in route additions failing with
an -ENOMEM error.

While the sender of the Netlink msg to add this route could check for
an ACK and retransmit in the case of an -ENOMEM error, the latter
should not occur in the first place if there is plenty of memory. The
solution is to use non-atomic alloc for rt6_info instead. While the
client may now be blocked for longer depending on the state of the
chunk being added to, this work has to be incurred at some point.

The alternative solution would be to provide configurable parameters
e.g. via sysctl in percpu.c for default map size, low/high empty pages
and map margins. For this solution, the map margin sizes need to be
stored per chunk, as large margins cannot be used if the dynamic early
slots map size is in use. This is not a preferred solution though, as
it requires tuning of these parameters to provide sufficient margins to
avoid -ENOMEM errors depending on system requirements.

Signed-off-by: Mike Manning <redacted>
---
 net/ipv6/route.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 1b57e11..0e9bb76 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -347,7 +347,7 @@ struct rt6_info *ip6_dst_alloc(struct net *net,
 	struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
 
 	if (rt) {
-		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC);
+		rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_KERNEL);
 		if (rt->rt6i_pcpu) {
 			int cpu;
Nak, this doesn't work, as ip6_dst_alloc must be callable from
non-blocking code paths unfortunately.
Thanks for the prompt reply.

Do you consider the alternative of providing configurable parameters for per-cpu
alloc as viable, or is there a better way of dealing with this?

While I have tested such param changes under scale as avoiding the -ENOMEM errors, it
would be good to get confirmation that this approach is acceptable prior to coding the
sysctl handling for these.

[lkp] [net] 50d4a9ef15: INFO:suspicious_RCU_usage

From: kernel test robot <hidden>
Date: 2016-11-23 02:28:13

FYI, we noticed the following commit:

commit 50d4a9ef15cee93344653f6ca8f9bab62e76e972 ("net: ipv6: avoid errors due to per-cpu atomic alloc")
url: https://github.com/0day-ci/linux/commits/Mike-Manning/net-ipv6-avoid-errors-due-to-per-cpu-atomic-alloc/20161122-202055


in testcase: trinity
with following parameters:

	runtime: 300s

test-description: Trinity is a linux system call fuzz tester.
test-url: http://codemonkey.org.uk/projects/trinity/


on test machine: qemu-system-i386 -enable-kvm -m 320M

caused below changes:


+-----------------------------------------------------------------------------+------------+------------+
|                                                                             | 3b404a5198 | 50d4a9ef15 |
+-----------------------------------------------------------------------------+------------+------------+
| boot_successes                                                              | 2          | 0          |
| boot_failures                                                               | 4          | 8          |
| calltrace:init                                                              | 4          | 8          |
| IP-Config:Auto-configuration_of_network_failed                              | 4          | 8          |
| INFO:suspicious_RCU_usage                                                   | 0          | 8          |
| calltrace:addrconf_notify                                                   | 0          | 8          |
| calltrace:ip_auto_config                                                    | 0          | 8          |
| WARNING:at_kernel/locking/mutex.c:#mutex_lock_nested                        | 0          | 8          |
| BUG:sleeping_function_called_from_invalid_context_at_kernel/locking/mutex.c | 0          | 1          |
| INFO:lockdep_is_turned_off                                                  | 0          | 1          |
| calltrace:SyS_ioctl                                                         | 0          | 1          |
+-----------------------------------------------------------------------------+------------+------------+




[   20.158999] ### dt-test ### end of unittest - 149 passed, 0 failed
[   20.170208] 
[   20.172448] ===============================
[   20.178239] [ INFO: suspicious RCU usage. ]
[   20.183917] 4.9.0-rc6-00087-g50d4a9e #1 Not tainted
[   20.190554] -------------------------------
[   20.196388] kernel/sched/core.c:7729 Illegal context switch in RCU-bh read-side critical section!
[   20.210614] 
[   20.210614] other info that might help us debug this:
[   20.210614] 
[   20.221405] 
[   20.221405] rcu_scheduler_active = 1, debug_locks = 1
[   20.230284] 3 locks held by swapper/0/1:
[   20.235791]  #0: 
[   20.238261]  (
rtnl_mutex
[   20.242057] ){+.+.+.}
, at: 
[   20.246518] [<43f61b50>] rtnl_lock+0xf/0x11
[   20.252294]  #1: 
[   20.254819]  (
rcu_read_lock_bh
[   20.259422] ){......}
, at: 
[   20.263542] [<4401af1b>] ipv6_add_addr+0x47/0x43e
[   20.270211]  #2: 
[   20.272644]  (
addrconf_hash_lock
[   20.277542] ){+.....}
, at: 
[   20.281681] [<4401afaa>] ipv6_add_addr+0xd6/0x43e
[   20.288459] 
[   20.288459] stack backtrace:
[   20.294554] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.9.0-rc6-00087-g50d4a9e #1
[   20.305143] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Debian-1.8.2-1 04/01/2014
[   20.317757]  5344bc84 43bf3a0a 53450000 00000001 5344bca0 43aa5c4f 4441008b 4440e52f
[   20.330680]  4440fa0e 0000026c 00000000 5344bcb4 43a896a6 53450000 00000000 00000000
[   20.358226]  5344bcd0 43a89863 43b2b5c1 02080020 5344bcd4 4450ff80 4ffa5000 5344bd00
[   20.367429] Call Trace:
[   20.370671]  [<43bf3a0a>] dump_stack+0x75/0xa9
[   20.376473]  [<43aa5c4f>] lockdep_rcu_suspicious+0xbb/0xc4
[   20.383600]  [<43a896a6>] ___might_sleep+0x82/0x1d2
[   20.389988]  [<43a89863>] __might_sleep+0x6d/0x74
[   20.396177]  [<43b2b5c1>] ? __slab_alloc+0x49/0x59
[   20.404660]  [<441541ee>] mutex_lock_nested+0x1e/0x2c5
[   20.411304]  [<43aa3263>] ? trace_hardirqs_on+0xb/0xd
[   20.417850]  [<43b14b56>] pcpu_alloc+0x84/0x42d
[   20.423785]  [<43f5a9a2>] ? dst_alloc+0x5f/0x6e
[   20.429678]  [<43b15308>] __alloc_percpu_gfp+0xb/0xd
[   20.436133]  [<44023f10>] ip6_dst_alloc+0x23/0x70
[   20.442194]  [<440265cf>] addrconf_dst_alloc+0x34/0xce
[   20.448861]  [<4401b02f>] ipv6_add_addr+0x15b/0x43e
[   20.455188]  [<4401eec6>] add_addr+0x19/0x5a
[   20.460731]  [<44020bf5>] addrconf_notify+0x565/0x93e
[   20.467366]  [<43f719a0>] ? pktgen_device_event+0x100/0x25c
[   20.474501]  [<43a7fd41>] notifier_call_chain+0x25/0x47
[   20.481300]  [<43a7fff4>] raw_notifier_call_chain+0xc/0xe
[   20.488305]  [<43f4ceef>] call_netdevice_notifiers_info+0x41/0x49
[   20.496217]  [<43f4ff3d>] call_netdevice_notifiers+0xc/0xe
[   20.503271]  [<43f53e20>] __dev_notify_flags+0x4a/0x74
[   20.509920]  [<43f543ab>] dev_change_flags+0x43/0x4b
[   20.516371]  [<445ec72b>] ip_auto_config+0xc0/0xddb
[   20.522658]  [<43aab38d>] ? vprintk_default+0x12/0x14
[   20.529297]  [<43fd20c5>] ? tcp_set_default_congestion_control+0x10/0x58
[   20.537921]  [<43fd2108>] ? tcp_set_default_congestion_control+0x53/0x58
[   20.546538]  [<44156fc9>] ? _raw_spin_unlock+0x1d/0x27
[   20.553212]  [<445ec66b>] ? root_nfs_parse_addr+0x8d/0x8d
[   20.560246]  [<445a7c29>] do_one_initcall+0x8b/0x11d
[   20.585588]  [<43a7e80c>] ? parse_args+0x198/0x26c
[   20.592263]  [<445a7da7>] kernel_init_freeable+0xec/0x174
[   20.599324]  [<4414fed1>] ? rest_init+0x111/0x111
[   20.605448]  [<4414fed9>] kernel_init+0x8/0xd0
[   20.611253]  [<44157503>] ret_from_fork+0x1b/0x28
[   20.617704] ------------[ cut here ]------------
[   20.623731] WARNING: CPU: 0 PID: 1 at kernel/locking/mutex.c:529 mutex_lock_nested+0x86/0x2c5
[   20.637016] DEBUG_LOCKS_WARN_ON(in_interrupt())
[   20.642915] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.9.0-rc6-00087-g50d4a9e #1
[   20.652634] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Debian-1.8.2-1 04/01/2014
[   20.663948]  5344bc8c 43bf3a0a 5344bcb8 44154256 5344bca4 43a6a143 00000211 4450ff80
[   20.675261]  53450000 00000000 5344bcc0 43a6a182 00000009 00000000 5344bcb8 44404245
[   20.686568]  5344bcd4 5344bd00 44154256 4440fa0e 00000211 44404245 4440f9ff 43aa3263
[   20.697838] Call Trace:
[   20.701178]  [<43bf3a0a>] dump_stack+0x75/0xa9
[   20.724842]  [<44154256>] ? mutex_lock_nested+0x86/0x2c5
[   20.731774]  [<43a6a143>] __warn+0xad/0xc4
[   20.737146]  [<43a6a182>] warn_slowpath_fmt+0x28/0x2d
[   20.743729]  [<44154256>] mutex_lock_nested+0x86/0x2c5
[   20.750400]  [<43aa3263>] ? trace_hardirqs_on+0xb/0xd
[   20.756950]  [<43b14b56>] pcpu_alloc+0x84/0x42d
[   20.762878]  [<43f5a9a2>] ? dst_alloc+0x5f/0x6e
[   20.768744]  [<43b15308>] __alloc_percpu_gfp+0xb/0xd
[   20.775201]  [<44023f10>] ip6_dst_alloc+0x23/0x70
[   20.781388]  [<440265cf>] addrconf_dst_alloc+0x34/0xce
[   20.787994]  [<4401b02f>] ipv6_add_addr+0x15b/0x43e
[   20.794380]  [<4401eec6>] add_addr+0x19/0x5a
[   20.799984]  [<44020bf5>] addrconf_notify+0x565/0x93e
[   20.806598]  [<43f719a0>] ? pktgen_device_event+0x100/0x25c
[   20.813858]  [<43a7fd41>] notifier_call_chain+0x25/0x47
[   20.820633]  [<43a7fff4>] raw_notifier_call_chain+0xc/0xe
[   20.827629]  [<43f4ceef>] call_netdevice_notifiers_info+0x41/0x49
[   20.835598]  [<43f4ff3d>] call_netdevice_notifiers+0xc/0xe
[   20.842725]  [<43f53e20>] __dev_notify_flags+0x4a/0x74
[   20.849370]  [<43f543ab>] dev_change_flags+0x43/0x4b
[   20.855851]  [<445ec72b>] ip_auto_config+0xc0/0xddb
[   20.862271]  [<43aab38d>] ? vprintk_default+0x12/0x14
[   20.868812]  [<43fd20c5>] ? tcp_set_default_congestion_control+0x10/0x58
[   20.877495]  [<43fd2108>] ? tcp_set_default_congestion_control+0x53/0x58
[   20.886053]  [<44156fc9>] ? _raw_spin_unlock+0x1d/0x27
[   20.892791]  [<445ec66b>] ? root_nfs_parse_addr+0x8d/0x8d
[   20.899731]  [<445a7c29>] do_one_initcall+0x8b/0x11d
[   20.906240]  [<43a7e80c>] ? parse_args+0x198/0x26c
[   20.912468]  [<445a7da7>] kernel_init_freeable+0xec/0x174
[   20.919462]  [<4414fed1>] ? rest_init+0x111/0x111
[   20.925597]  [<4414fed9>] kernel_init+0x8/0xd0
[   20.931391]  [<44157503>] ret_from_fork+0x1b/0x28
[   20.937679] ---[ end trace 3474b18006c0c631 ]---
[   20.944287] IP-Config: Failed to open ipddp0
[   20.950711] usb0: eth_open


To reproduce:

        git clone git://git.kernel.org/pub/scm/linux/kernel/git/wfg/lkp-tests.git
        cd lkp-tests
        bin/lkp install job.yaml  # job file is attached in this email
        bin/lkp run     job.yaml



Thanks,
Xiaolong
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help