Bluetooth: invert locking order in connect path
authorGustavo F. Padovan <padovan@profusion.mobi>
Fri, 9 Dec 2011 06:48:17 +0000 (04:48 -0200)
committerGustavo F. Padovan <padovan@profusion.mobi>
Sun, 18 Dec 2011 19:07:57 +0000 (17:07 -0200)
This move some checking code that was in l2cap_sock_connect() to
l2cap_chan_connect(). Thus we can invert the lock calls, i.e., call
lock_sock() before hci_dev_lock() to avoid a deadlock scenario.

Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
include/net/bluetooth/l2cap.h
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c

index f79137476cb65558277acbf7bdcf4150addf7b4a..c0d168adf9be5e2533ab14ed913c06b4e0c529f4 100644 (file)
@@ -806,7 +806,8 @@ int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid);
 struct l2cap_chan *l2cap_chan_create(struct sock *sk);
 void l2cap_chan_close(struct l2cap_chan *chan, int reason);
 void l2cap_chan_destroy(struct l2cap_chan *chan);
-int l2cap_chan_connect(struct l2cap_chan *chan);
+inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+                                                               bdaddr_t *dst);
 int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
                                                                u32 priority);
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
index a78cdf7236dbce8668a8622e4c69c390544f20c0..d6165199fc8b59559dda85a1de0635193362970c 100644 (file)
@@ -1144,11 +1144,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
        return c1;
 }
 
-int l2cap_chan_connect(struct l2cap_chan *chan)
+inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst)
 {
        struct sock *sk = chan->sk;
        bdaddr_t *src = &bt_sk(sk)->src;
-       bdaddr_t *dst = &bt_sk(sk)->dst;
        struct l2cap_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev *hdev;
@@ -1164,6 +1163,61 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
 
        hci_dev_lock(hdev);
 
+       lock_sock(sk);
+
+       /* PSM must be odd and lsb of upper byte must be 0 */
+       if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
+                                       chan->chan_type != L2CAP_CHAN_RAW) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       switch (chan->mode) {
+       case L2CAP_MODE_BASIC:
+               break;
+       case L2CAP_MODE_ERTM:
+       case L2CAP_MODE_STREAMING:
+               if (!disable_ertm)
+                       break;
+               /* fall through */
+       default:
+               err = -ENOTSUPP;
+               goto done;
+       }
+
+       switch (sk->sk_state) {
+       case BT_CONNECT:
+       case BT_CONNECT2:
+       case BT_CONFIG:
+               /* Already connecting */
+               err = 0;
+               goto done;
+
+       case BT_CONNECTED:
+               /* Already connected */
+               err = -EISCONN;
+               goto done;
+
+       case BT_OPEN:
+       case BT_BOUND:
+               /* Can connect */
+               break;
+
+       default:
+               err = -EBADFD;
+               goto done;
+       }
+
+       /* Set destination address and psm */
+       bacpy(&bt_sk(sk)->dst, src);
+       chan->psm = psm;
+       chan->dcid = cid;
+
        auth_type = l2cap_get_auth_type(chan);
 
        if (chan->dcid == L2CAP_CID_LE_DATA)
index fbdc8b38d9eeb6ce5c94c079efb42c8f742dc70f..6c7d4323e7973501011ed2959de7e658dff46c6f 100644 (file)
@@ -121,70 +121,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
-       lock_sock(sk);
-
-       if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED
-                       && !(la.l2_psm || la.l2_cid)) {
-               err = -EINVAL;
-               goto done;
-       }
-
-       switch (chan->mode) {
-       case L2CAP_MODE_BASIC:
-               break;
-       case L2CAP_MODE_ERTM:
-       case L2CAP_MODE_STREAMING:
-               if (!disable_ertm)
-                       break;
-               /* fall through */
-       default:
-               err = -ENOTSUPP;
-               goto done;
-       }
-
-       switch (sk->sk_state) {
-       case BT_CONNECT:
-       case BT_CONNECT2:
-       case BT_CONFIG:
-               /* Already connecting */
-               goto wait;
-
-       case BT_CONNECTED:
-               /* Already connected */
-               err = -EISCONN;
-               goto done;
-
-       case BT_OPEN:
-       case BT_BOUND:
-               /* Can connect */
-               break;
-
-       default:
-               err = -EBADFD;
-               goto done;
-       }
-
-       /* PSM must be odd and lsb of upper byte must be 0 */
-       if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid &&
-                                       chan->chan_type != L2CAP_CHAN_RAW) {
-               err = -EINVAL;
-               goto done;
-       }
-
-       /* Set destination address and psm */
-       bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
-       chan->psm = la.l2_psm;
-       chan->dcid = la.l2_cid;
-
-       err = l2cap_chan_connect(chan);
+       err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
        if (err)
                goto done;
 
-wait:
        err = bt_sock_wait_state(sk, BT_CONNECTED,
                        sock_sndtimeo(sk, flags & O_NONBLOCK));
 done:
-       release_sock(sk);
+       if (sock_owned_by_user(sk))
+               release_sock(sk);
        return err;
 }