aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorRupesh Tatiya <rtatiya@codeaurora.org>2015-04-30 16:51:54 +0530
committerMister Oyster <oysterized@gmail.com>2017-04-11 10:59:09 +0200
commitfd91149633127f41960aa2a8d9b2829cbd7e677f (patch)
tree8ea28415e6ff33ba24a639912ed5dbf0ab44c5f6 /net
parentd9b0f3472f74a8cc5f7fd2f81fe3451e25fe3f9a (diff)
Bluetooth: Release locks before sleeping for L2CAP socket shutdown
If there are unacknowledged frames during an ongoing L2CAP transfer and if socket is shutdown, it will result in deadlock as locks are not released before going to sleep. The L2CAP receive thread will be waiting on locks to update unacknowledged frame count, whereas socket shutdown thread will be sleeping till unacknowledged count becomes 0 resulting in deadlock. Signed-off-by: Rupesh Tatiya <rtatiya@codeaurora.org>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/l2cap_core.c25
-rw-r--r--net/bluetooth/l2cap_sock.c13
2 files changed, 36 insertions, 2 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c90f6ffae..2bb685312 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1874,13 +1874,21 @@ done:
int __l2cap_wait_ack(struct sock *sk)
{
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_conn *conn;
DECLARE_WAITQUEUE(wait, current);
int err = 0;
int timeo = HZ/5;
add_wait_queue(sk_sleep(sk), &wait);
set_current_state(TASK_INTERRUPTIBLE);
- while (chan->unacked_frames > 0 && chan->conn) {
+
+ conn = chan->conn;
+ if (conn)
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
+ lock_sock(sk);
+
+ while (chan->unacked_frames > 0 && conn) {
if (!timeo)
timeo = HZ/5;
@@ -1890,14 +1898,29 @@ int __l2cap_wait_ack(struct sock *sk)
}
release_sock(sk);
+ l2cap_chan_unlock(chan);
+ if (conn)
+ mutex_unlock(&conn->chan_lock);
+
timeo = schedule_timeout(timeo);
+
+ if (conn)
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
lock_sock(sk);
+
set_current_state(TASK_INTERRUPTIBLE);
err = sock_error(sk);
if (err)
break;
}
+
+ release_sock(sk);
+ l2cap_chan_unlock(chan);
+ if (conn)
+ mutex_unlock(&conn->chan_lock);
+
set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
return err;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index eaab0f580..9d7b2ee5e 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -882,9 +882,20 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
lock_sock(sk);
if (!sk->sk_shutdown) {
- if (chan->mode == L2CAP_MODE_ERTM)
+ if (chan->mode == L2CAP_MODE_ERTM) {
+ release_sock(sk);
+ l2cap_chan_unlock(chan);
+ if (conn)
+ mutex_unlock(&conn->chan_lock);
+
err = __l2cap_wait_ack(sk);
+ if (conn)
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
+ lock_sock(sk);
+ }
+
sk->sk_shutdown = SHUTDOWN_MASK;
release_sock(sk);