diff options
| author | Rupesh Tatiya <rtatiya@codeaurora.org> | 2015-04-30 16:51:54 +0530 |
|---|---|---|
| committer | Mister Oyster <oysterized@gmail.com> | 2017-04-11 10:59:09 +0200 |
| commit | fd91149633127f41960aa2a8d9b2829cbd7e677f (patch) | |
| tree | 8ea28415e6ff33ba24a639912ed5dbf0ab44c5f6 /net | |
| parent | d9b0f3472f74a8cc5f7fd2f81fe3451e25fe3f9a (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.c | 25 | ||||
| -rw-r--r-- | net/bluetooth/l2cap_sock.c | 13 |
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); |
