aboutsummaryrefslogtreecommitdiffstats
path: root/net/rds/tcp_send.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-09-22 08:43:39 -0400
committerDavid S. Miller <davem@davemloft.net>2010-09-25 01:26:10 -0400
commitf064af1e500a2bf4607706f0f458163bdb2a6ea5 (patch)
treeebbbd1c3c00030c70cab6fe587a5c03252a46ebc /net/rds/tcp_send.c
parent605c82bab5abe0816e5e32716875c245f89f39da (diff)
net: fix a lockdep splat
We have for each socket : One spinlock (sk_slock.slock) One rwlock (sk_callback_lock) Possible scenarios are : (A) (this is used in net/sunrpc/xprtsock.c) read_lock(&sk->sk_callback_lock) (without blocking BH) <BH> spin_lock(&sk->sk_slock.slock); ... read_lock(&sk->sk_callback_lock); ... (B) write_lock_bh(&sk->sk_callback_lock) stuff write_unlock_bh(&sk->sk_callback_lock) (C) spin_lock_bh(&sk->sk_slock) ... write_lock_bh(&sk->sk_callback_lock) stuff write_unlock_bh(&sk->sk_callback_lock) spin_unlock_bh(&sk->sk_slock) This (C) case conflicts with (A) : CPU1 [A] CPU2 [C] read_lock(callback_lock) <BH> spin_lock_bh(slock) <wait to spin_lock(slock)> <wait to write_lock_bh(callback_lock)> We have one problematic (C) use case in inet_csk_listen_stop() : local_bh_disable(); bh_lock_sock(child); // spin_lock_bh(&sk->sk_slock) WARN_ON(sock_owned_by_user(child)); ... sock_orphan(child); // write_lock_bh(&sk->sk_callback_lock) lockdep is not happy with this, as reported by Tetsuo Handa It seems only way to deal with this is to use read_lock_bh(callbacklock) everywhere. Thanks to Jarek for pointing a bug in my first attempt and suggesting this solution. Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Tested-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> CC: Jarek Poplawski <jarkao2@gmail.com> Tested-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/rds/tcp_send.c')
-rw-r--r--net/rds/tcp_send.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c
index a28b895ff0d..2f012a07d94 100644
--- a/net/rds/tcp_send.c
+++ b/net/rds/tcp_send.c
@@ -224,7 +224,7 @@ void rds_tcp_write_space(struct sock *sk)
224 struct rds_connection *conn; 224 struct rds_connection *conn;
225 struct rds_tcp_connection *tc; 225 struct rds_tcp_connection *tc;
226 226
227 read_lock(&sk->sk_callback_lock); 227 read_lock_bh(&sk->sk_callback_lock);
228 conn = sk->sk_user_data; 228 conn = sk->sk_user_data;
229 if (conn == NULL) { 229 if (conn == NULL) {
230 write_space = sk->sk_write_space; 230 write_space = sk->sk_write_space;
@@ -244,7 +244,7 @@ void rds_tcp_write_space(struct sock *sk)
244 queue_delayed_work(rds_wq, &conn->c_send_w, 0); 244 queue_delayed_work(rds_wq, &conn->c_send_w, 0);
245 245
246out: 246out:
247 read_unlock(&sk->sk_callback_lock); 247 read_unlock_bh(&sk->sk_callback_lock);
248 248
249 /* 249 /*
250 * write_space is only called when data leaves tcp's send queue if 250 * write_space is only called when data leaves tcp's send queue if