diff options
Diffstat (limited to 'net/bluetooth/l2cap_sock.c')
-rw-r--r-- | net/bluetooth/l2cap_sock.c | 37 |
1 files changed, 25 insertions, 12 deletions
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 31f106e61ca2..f65caf41953f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -285,6 +285,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; + /* Listening channels need to use nested locking in order not to + * cause lockdep warnings when the created child channels end up + * being locked in the same thread as the parent channel. + */ + atomic_set(&chan->nesting, L2CAP_NESTING_PARENT); + chan->state = BT_LISTEN; sk->sk_state = BT_LISTEN; @@ -301,7 +307,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, long timeo; int err = 0; - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + lock_sock_nested(sk, L2CAP_NESTING_PARENT); timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); @@ -333,7 +339,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, release_sock(sk); timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + lock_sock_nested(sk, L2CAP_NESTING_PARENT); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); @@ -1096,6 +1102,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) chan = l2cap_pi(sk)->chan; conn = chan->conn; + BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); + if (conn) mutex_lock(&conn->chan_lock); @@ -1153,12 +1161,16 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) { struct sock *sk; - BT_DBG("parent %p", parent); + BT_DBG("parent %p state %s", parent, + state_to_string(parent->sk_state)); /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; + BT_DBG("child chan %p state %s", chan, + state_to_string(chan->state)); + l2cap_chan_lock(chan); __clear_chan_timer(chan); l2cap_chan_close(chan, ECONNRESET); @@ -1246,7 +1258,16 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err) struct sock *sk = chan->data; struct sock *parent; - lock_sock(sk); + BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); + + /* This callback can be called both for server (BT_LISTEN) + * sockets as well as "normal" ones. To avoid lockdep warnings + * with child socket locking (through l2cap_sock_cleanup_listen) + * we need separation into separate nesting levels. The simplest + * way to accomplish this is to inherit the nesting level used + * for the channel. + */ + lock_sock_nested(sk, atomic_read(&chan->nesting)); parent = bt_sk(sk)->parent; @@ -1315,13 +1336,6 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, return skb; } -static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan, - unsigned char *kdata, - struct iovec *iov, int len) -{ - return memcpy_fromiovec(kdata, iov, len); -} - static void l2cap_sock_ready_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; @@ -1406,7 +1420,6 @@ static const struct l2cap_ops l2cap_chan_ops = { .set_shutdown = l2cap_sock_set_shutdown_cb, .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, - .memcpy_fromiovec = l2cap_sock_memcpy_fromiovec_cb, }; static void l2cap_sock_destruct(struct sock *sk) |