aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-12-30 02:04:08 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-30 02:04:08 -0500
commiteb4dea5853046727bfbb579f0c9a8cae7369f7c6 (patch)
tree79e18d6284494ab63a890885b0eecce9431a9597
parent0f23174aa8c1aa7a2a6050a72a60d290ef9ee578 (diff)
net: Fix percpu counters deadlock
When we converted the protocol atomic counters such as the orphan count and the total socket count deadlocks were introduced due to the mismatch in BH status of the spots that used the percpu counter operations. Based on the diagnosis and patch by Peter Zijlstra, this patch fixes these issues by disabling BH where we may be in process context. Reported-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Tested-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/dccp/proto.c3
-rw-r--r--net/ipv4/inet_connection_sock.c4
-rw-r--r--net/ipv4/proc.c13
-rw-r--r--net/ipv4/tcp.c3
-rw-r--r--net/ipv4/tcp_ipv4.c3
-rw-r--r--net/ipv6/tcp_ipv6.c3
6 files changed, 21 insertions, 8 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index d5c2bacb713c..1747ccae8e8d 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -964,7 +964,6 @@ adjudge_to_death:
964 state = sk->sk_state; 964 state = sk->sk_state;
965 sock_hold(sk); 965 sock_hold(sk);
966 sock_orphan(sk); 966 sock_orphan(sk);
967 percpu_counter_inc(sk->sk_prot->orphan_count);
968 967
969 /* 968 /*
970 * It is the last release_sock in its life. It will remove backlog. 969 * It is the last release_sock in its life. It will remove backlog.
@@ -978,6 +977,8 @@ adjudge_to_death:
978 bh_lock_sock(sk); 977 bh_lock_sock(sk);
979 WARN_ON(sock_owned_by_user(sk)); 978 WARN_ON(sock_owned_by_user(sk));
980 979
980 percpu_counter_inc(sk->sk_prot->orphan_count);
981
981 /* Have we already been destroyed by a softirq or backlog? */ 982 /* Have we already been destroyed by a softirq or backlog? */
982 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) 983 if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
983 goto out; 984 goto out;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index c7cda1ca8e65..f26ab38680de 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -633,8 +633,6 @@ void inet_csk_listen_stop(struct sock *sk)
633 633
634 acc_req = req->dl_next; 634 acc_req = req->dl_next;
635 635
636 percpu_counter_inc(sk->sk_prot->orphan_count);
637
638 local_bh_disable(); 636 local_bh_disable();
639 bh_lock_sock(child); 637 bh_lock_sock(child);
640 WARN_ON(sock_owned_by_user(child)); 638 WARN_ON(sock_owned_by_user(child));
@@ -644,6 +642,8 @@ void inet_csk_listen_stop(struct sock *sk)
644 642
645 sock_orphan(child); 643 sock_orphan(child);
646 644
645 percpu_counter_inc(sk->sk_prot->orphan_count);
646
647 inet_csk_destroy_sock(child); 647 inet_csk_destroy_sock(child);
648 648
649 bh_unlock_sock(child); 649 bh_unlock_sock(child);
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 614958b7c276..eb62e58bff79 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -38,6 +38,7 @@
38#include <net/tcp.h> 38#include <net/tcp.h>
39#include <net/udp.h> 39#include <net/udp.h>
40#include <net/udplite.h> 40#include <net/udplite.h>
41#include <linux/bottom_half.h>
41#include <linux/inetdevice.h> 42#include <linux/inetdevice.h>
42#include <linux/proc_fs.h> 43#include <linux/proc_fs.h>
43#include <linux/seq_file.h> 44#include <linux/seq_file.h>
@@ -50,13 +51,17 @@
50static int sockstat_seq_show(struct seq_file *seq, void *v) 51static int sockstat_seq_show(struct seq_file *seq, void *v)
51{ 52{
52 struct net *net = seq->private; 53 struct net *net = seq->private;
54 int orphans, sockets;
55
56 local_bh_disable();
57 orphans = percpu_counter_sum_positive(&tcp_orphan_count),
58 sockets = percpu_counter_sum_positive(&tcp_sockets_allocated),
59 local_bh_enable();
53 60
54 socket_seq_show(seq); 61 socket_seq_show(seq);
55 seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n", 62 seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n",
56 sock_prot_inuse_get(net, &tcp_prot), 63 sock_prot_inuse_get(net, &tcp_prot), orphans,
57 (int)percpu_counter_sum_positive(&tcp_orphan_count), 64 tcp_death_row.tw_count, sockets,
58 tcp_death_row.tw_count,
59 (int)percpu_counter_sum_positive(&tcp_sockets_allocated),
60 atomic_read(&tcp_memory_allocated)); 65 atomic_read(&tcp_memory_allocated));
61 seq_printf(seq, "UDP: inuse %d mem %d\n", 66 seq_printf(seq, "UDP: inuse %d mem %d\n",
62 sock_prot_inuse_get(net, &udp_prot), 67 sock_prot_inuse_get(net, &udp_prot),
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 1f3d52946b3b..f28acf11fc67 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1836,7 +1836,6 @@ adjudge_to_death:
1836 state = sk->sk_state; 1836 state = sk->sk_state;
1837 sock_hold(sk); 1837 sock_hold(sk);
1838 sock_orphan(sk); 1838 sock_orphan(sk);
1839 percpu_counter_inc(sk->sk_prot->orphan_count);
1840 1839
1841 /* It is the last release_sock in its life. It will remove backlog. */ 1840 /* It is the last release_sock in its life. It will remove backlog. */
1842 release_sock(sk); 1841 release_sock(sk);
@@ -1849,6 +1848,8 @@ adjudge_to_death:
1849 bh_lock_sock(sk); 1848 bh_lock_sock(sk);
1850 WARN_ON(sock_owned_by_user(sk)); 1849 WARN_ON(sock_owned_by_user(sk));
1851 1850
1851 percpu_counter_inc(sk->sk_prot->orphan_count);
1852
1852 /* Have we already been destroyed by a softirq or backlog? */ 1853 /* Have we already been destroyed by a softirq or backlog? */
1853 if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE) 1854 if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
1854 goto out; 1855 goto out;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 10172487921b..9d839fa9331e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -51,6 +51,7 @@
51 */ 51 */
52 52
53 53
54#include <linux/bottom_half.h>
54#include <linux/types.h> 55#include <linux/types.h>
55#include <linux/fcntl.h> 56#include <linux/fcntl.h>
56#include <linux/module.h> 57#include <linux/module.h>
@@ -1797,7 +1798,9 @@ static int tcp_v4_init_sock(struct sock *sk)
1797 sk->sk_sndbuf = sysctl_tcp_wmem[1]; 1798 sk->sk_sndbuf = sysctl_tcp_wmem[1];
1798 sk->sk_rcvbuf = sysctl_tcp_rmem[1]; 1799 sk->sk_rcvbuf = sysctl_tcp_rmem[1];
1799 1800
1801 local_bh_disable();
1800 percpu_counter_inc(&tcp_sockets_allocated); 1802 percpu_counter_inc(&tcp_sockets_allocated);
1803 local_bh_enable();
1801 1804
1802 return 0; 1805 return 0;
1803} 1806}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 8702b06cb60a..e8b8337a8310 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -23,6 +23,7 @@
23 * 2 of the License, or (at your option) any later version. 23 * 2 of the License, or (at your option) any later version.
24 */ 24 */
25 25
26#include <linux/bottom_half.h>
26#include <linux/module.h> 27#include <linux/module.h>
27#include <linux/errno.h> 28#include <linux/errno.h>
28#include <linux/types.h> 29#include <linux/types.h>
@@ -1830,7 +1831,9 @@ static int tcp_v6_init_sock(struct sock *sk)
1830 sk->sk_sndbuf = sysctl_tcp_wmem[1]; 1831 sk->sk_sndbuf = sysctl_tcp_wmem[1];
1831 sk->sk_rcvbuf = sysctl_tcp_rmem[1]; 1832 sk->sk_rcvbuf = sysctl_tcp_rmem[1];
1832 1833
1834 local_bh_disable();
1833 percpu_counter_inc(&tcp_sockets_allocated); 1835 percpu_counter_inc(&tcp_sockets_allocated);
1836 local_bh_enable();
1834 1837
1835 return 0; 1838 return 0;
1836} 1839}