Skip to content

Commit

Permalink
net: Fix percpu counters deadlock
Browse files Browse the repository at this point in the history
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 <[email protected]>
Tested-by: Ingo Molnar <[email protected]>
Signed-off-by: Herbert Xu <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
herbertx authored and davem330 committed Dec 30, 2008
1 parent 0f23174 commit eb4dea5
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 8 deletions.
3 changes: 2 additions & 1 deletion net/dccp/proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,6 @@ void dccp_close(struct sock *sk, long timeout)
state = sk->sk_state;
sock_hold(sk);
sock_orphan(sk);
percpu_counter_inc(sk->sk_prot->orphan_count);

/*
* It is the last release_sock in its life. It will remove backlog.
Expand All @@ -978,6 +977,8 @@ void dccp_close(struct sock *sk, long timeout)
bh_lock_sock(sk);
WARN_ON(sock_owned_by_user(sk));

percpu_counter_inc(sk->sk_prot->orphan_count);

/* Have we already been destroyed by a softirq or backlog? */
if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
goto out;
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/inet_connection_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,6 @@ void inet_csk_listen_stop(struct sock *sk)

acc_req = req->dl_next;

percpu_counter_inc(sk->sk_prot->orphan_count);

local_bh_disable();
bh_lock_sock(child);
WARN_ON(sock_owned_by_user(child));
Expand All @@ -644,6 +642,8 @@ void inet_csk_listen_stop(struct sock *sk)

sock_orphan(child);

percpu_counter_inc(sk->sk_prot->orphan_count);

inet_csk_destroy_sock(child);

bh_unlock_sock(child);
Expand Down
13 changes: 9 additions & 4 deletions net/ipv4/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <net/tcp.h>
#include <net/udp.h>
#include <net/udplite.h>
#include <linux/bottom_half.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
Expand All @@ -50,13 +51,17 @@
static int sockstat_seq_show(struct seq_file *seq, void *v)
{
struct net *net = seq->private;
int orphans, sockets;

local_bh_disable();
orphans = percpu_counter_sum_positive(&tcp_orphan_count),
sockets = percpu_counter_sum_positive(&tcp_sockets_allocated),
local_bh_enable();

socket_seq_show(seq);
seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n",
sock_prot_inuse_get(net, &tcp_prot),
(int)percpu_counter_sum_positive(&tcp_orphan_count),
tcp_death_row.tw_count,
(int)percpu_counter_sum_positive(&tcp_sockets_allocated),
sock_prot_inuse_get(net, &tcp_prot), orphans,
tcp_death_row.tw_count, sockets,
atomic_read(&tcp_memory_allocated));
seq_printf(seq, "UDP: inuse %d mem %d\n",
sock_prot_inuse_get(net, &udp_prot),
Expand Down
3 changes: 2 additions & 1 deletion net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1836,7 +1836,6 @@ void tcp_close(struct sock *sk, long timeout)
state = sk->sk_state;
sock_hold(sk);
sock_orphan(sk);
percpu_counter_inc(sk->sk_prot->orphan_count);

/* It is the last release_sock in its life. It will remove backlog. */
release_sock(sk);
Expand All @@ -1849,6 +1848,8 @@ void tcp_close(struct sock *sk, long timeout)
bh_lock_sock(sk);
WARN_ON(sock_owned_by_user(sk));

percpu_counter_inc(sk->sk_prot->orphan_count);

/* Have we already been destroyed by a softirq or backlog? */
if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
goto out;
Expand Down
3 changes: 3 additions & 0 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
*/


#include <linux/bottom_half.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/module.h>
Expand Down Expand Up @@ -1797,7 +1798,9 @@ static int tcp_v4_init_sock(struct sock *sk)
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];

local_bh_disable();
percpu_counter_inc(&tcp_sockets_allocated);
local_bh_enable();

return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* 2 of the License, or (at your option) any later version.
*/

#include <linux/bottom_half.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
Expand Down Expand Up @@ -1830,7 +1831,9 @@ static int tcp_v6_init_sock(struct sock *sk)
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];

local_bh_disable();
percpu_counter_inc(&tcp_sockets_allocated);
local_bh_enable();

return 0;
}
Expand Down

0 comments on commit eb4dea5

Please sign in to comment.