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.

Change-Id: Id54c04afc4cd0d2adeb2fa69149a701fc4062ad2
Signed-off-by: Rupesh Tatiya <rtatiya@codeaurora.org>
This commit is contained in:
Rupesh Tatiya 2015-04-30 16:51:54 +05:30
parent 7a7dca4023
commit ac699ff0c3
2 changed files with 36 additions and 2 deletions

View File

@ -1877,13 +1877,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;
@ -1893,14 +1901,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;

View File

@ -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);