| From 7c11c309aae7ef96e223eac6cdf03b35105cade5 Mon Sep 17 00:00:00 2001 |
| From: David S. Miller <davem@davemloft.net> |
| Date: Wed, 25 Aug 2010 02:27:49 -0700 |
| Subject: tcp: Combat per-cpu skew in orphan tests. |
| |
| |
| From: David S. Miller <davem@davemloft.net> |
| |
| [ Upstream commit ad1af0fedba14f82b240a03fe20eb9b2fdbd0357 ] |
| |
| As reported by Anton Blanchard when we use |
| percpu_counter_read_positive() to make our orphan socket limit checks, |
| the check can be off by up to num_cpus_online() * batch (which is 32 |
| by default) which on a 128 cpu machine can be as large as the default |
| orphan limit itself. |
| |
| Fix this by doing the full expensive sum check if the optimized check |
| triggers. |
| |
| Reported-by: Anton Blanchard <anton@samba.org> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Acked-by: Eric Dumazet <eric.dumazet@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| include/net/tcp.h | 18 ++++++++++++++---- |
| net/ipv4/tcp.c | 5 +---- |
| net/ipv4/tcp_timer.c | 8 ++++---- |
| 3 files changed, 19 insertions(+), 12 deletions(-) |
| |
| --- a/include/net/tcp.h |
| +++ b/include/net/tcp.h |
| @@ -259,11 +259,21 @@ static inline int between(__u32 seq1, __ |
| return seq3 - seq2 >= seq1 - seq2; |
| } |
| |
| -static inline int tcp_too_many_orphans(struct sock *sk, int num) |
| +static inline bool tcp_too_many_orphans(struct sock *sk, int shift) |
| { |
| - return (num > sysctl_tcp_max_orphans) || |
| - (sk->sk_wmem_queued > SOCK_MIN_SNDBUF && |
| - atomic_read(&tcp_memory_allocated) > sysctl_tcp_mem[2]); |
| + struct percpu_counter *ocp = sk->sk_prot->orphan_count; |
| + int orphans = percpu_counter_read_positive(ocp); |
| + |
| + if (orphans << shift > sysctl_tcp_max_orphans) { |
| + orphans = percpu_counter_sum_positive(ocp); |
| + if (orphans << shift > sysctl_tcp_max_orphans) |
| + return true; |
| + } |
| + |
| + if (sk->sk_wmem_queued > SOCK_MIN_SNDBUF && |
| + atomic_read(&tcp_memory_allocated) > sysctl_tcp_mem[2]) |
| + return true; |
| + return false; |
| } |
| |
| /* syncookies: remember time of last synqueue overflow */ |
| --- a/net/ipv4/tcp.c |
| +++ b/net/ipv4/tcp.c |
| @@ -1976,11 +1976,8 @@ adjudge_to_death: |
| } |
| } |
| if (sk->sk_state != TCP_CLOSE) { |
| - int orphan_count = percpu_counter_read_positive( |
| - sk->sk_prot->orphan_count); |
| - |
| sk_mem_reclaim(sk); |
| - if (tcp_too_many_orphans(sk, orphan_count)) { |
| + if (tcp_too_many_orphans(sk, 0)) { |
| if (net_ratelimit()) |
| printk(KERN_INFO "TCP: too many of orphaned " |
| "sockets\n"); |
| --- a/net/ipv4/tcp_timer.c |
| +++ b/net/ipv4/tcp_timer.c |
| @@ -65,18 +65,18 @@ static void tcp_write_err(struct sock *s |
| static int tcp_out_of_resources(struct sock *sk, int do_reset) |
| { |
| struct tcp_sock *tp = tcp_sk(sk); |
| - int orphans = percpu_counter_read_positive(&tcp_orphan_count); |
| + int shift = 0; |
| |
| /* If peer does not open window for long time, or did not transmit |
| * anything for long time, penalize it. */ |
| if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset) |
| - orphans <<= 1; |
| + shift++; |
| |
| /* If some dubious ICMP arrived, penalize even more. */ |
| if (sk->sk_err_soft) |
| - orphans <<= 1; |
| + shift++; |
| |
| - if (tcp_too_many_orphans(sk, orphans)) { |
| + if (tcp_too_many_orphans(sk, shift)) { |
| if (net_ratelimit()) |
| printk(KERN_INFO "Out of socket memory\n"); |
| |