Commit ad1af0fe authored by David S. Miller's avatar David S. Miller

tcp: Combat per-cpu skew in orphan tests.

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: default avatarAnton Blanchard <anton@samba.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Acked-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
parent b2bc8563
...@@ -268,11 +268,21 @@ static inline int between(__u32 seq1, __u32 seq2, __u32 seq3) ...@@ -268,11 +268,21 @@ static inline int between(__u32 seq1, __u32 seq2, __u32 seq3)
return seq3 - seq2 >= seq1 - seq2; 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) || struct percpu_counter *ocp = sk->sk_prot->orphan_count;
(sk->sk_wmem_queued > SOCK_MIN_SNDBUF && int orphans = percpu_counter_read_positive(ocp);
atomic_read(&tcp_memory_allocated) > sysctl_tcp_mem[2]);
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 */ /* syncookies: remember time of last synqueue overflow */
......
...@@ -2011,11 +2011,8 @@ void tcp_close(struct sock *sk, long timeout) ...@@ -2011,11 +2011,8 @@ void tcp_close(struct sock *sk, long timeout)
} }
} }
if (sk->sk_state != TCP_CLOSE) { if (sk->sk_state != TCP_CLOSE) {
int orphan_count = percpu_counter_read_positive(
sk->sk_prot->orphan_count);
sk_mem_reclaim(sk); sk_mem_reclaim(sk);
if (tcp_too_many_orphans(sk, orphan_count)) { if (tcp_too_many_orphans(sk, 0)) {
if (net_ratelimit()) if (net_ratelimit())
printk(KERN_INFO "TCP: too many of orphaned " printk(KERN_INFO "TCP: too many of orphaned "
"sockets\n"); "sockets\n");
......
...@@ -66,18 +66,18 @@ static void tcp_write_err(struct sock *sk) ...@@ -66,18 +66,18 @@ static void tcp_write_err(struct sock *sk)
static int tcp_out_of_resources(struct sock *sk, int do_reset) static int tcp_out_of_resources(struct sock *sk, int do_reset)
{ {
struct tcp_sock *tp = tcp_sk(sk); 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 /* If peer does not open window for long time, or did not transmit
* anything for long time, penalize it. */ * anything for long time, penalize it. */
if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset) 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 some dubious ICMP arrived, penalize even more. */
if (sk->sk_err_soft) 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()) if (net_ratelimit())
printk(KERN_INFO "Out of socket memory\n"); printk(KERN_INFO "Out of socket memory\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment