diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9189354c568f..d9c767cf773d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -668,7 +668,7 @@ struct l2cap_conn { struct l2cap_chan *smp; struct list_head chan_l; - struct mutex lock; + struct mutex chan_lock; struct kref ref; struct list_head users; }; @@ -970,7 +970,6 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err); void l2cap_send_conn_req(struct l2cap_chan *chan); struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn); -struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *conn); void l2cap_conn_put(struct l2cap_conn *conn); int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b22078b67972..25621db77eaa 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -119,6 +119,7 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, { struct l2cap_chan *c; + mutex_lock(&conn->chan_lock); c = __l2cap_get_chan_by_scid(conn, cid); if (c) { /* Only lock if chan reference is not 0 */ @@ -126,6 +127,7 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, if (c) l2cap_chan_lock(c); } + mutex_unlock(&conn->chan_lock); return c; } @@ -138,6 +140,7 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn, { struct l2cap_chan *c; + mutex_lock(&conn->chan_lock); c = __l2cap_get_chan_by_dcid(conn, cid); if (c) { /* Only lock if chan reference is not 0 */ @@ -145,6 +148,7 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn, if (c) l2cap_chan_lock(c); } + mutex_unlock(&conn->chan_lock); return c; } @@ -414,7 +418,7 @@ static void l2cap_chan_timeout(struct work_struct *work) if (!conn) return; - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); /* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling * this work. No need to call l2cap_chan_hold(chan) here again. */ @@ -435,7 +439,7 @@ static void l2cap_chan_timeout(struct work_struct *work) l2cap_chan_unlock(chan); l2cap_chan_put(chan); - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); } struct l2cap_chan *l2cap_chan_create(void) @@ -638,9 +642,9 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); __l2cap_chan_add(conn, chan); - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); } void l2cap_chan_del(struct l2cap_chan *chan, int err) @@ -728,9 +732,9 @@ void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func, if (!conn) return; - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); __l2cap_chan_list(conn, func, data); - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); } EXPORT_SYMBOL_GPL(l2cap_chan_list); @@ -742,7 +746,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work) struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan; - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { l2cap_chan_lock(chan); @@ -751,7 +755,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work) l2cap_chan_unlock(chan); } - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); } static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) @@ -1504,6 +1508,8 @@ static void l2cap_conn_start(struct l2cap_conn *conn) BT_DBG("conn %p", conn); + mutex_lock(&conn->chan_lock); + list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { l2cap_chan_lock(chan); @@ -1572,6 +1578,8 @@ static void l2cap_conn_start(struct l2cap_conn *conn) l2cap_chan_unlock(chan); } + + mutex_unlock(&conn->chan_lock); } static void l2cap_le_conn_ready(struct l2cap_conn *conn) @@ -1617,7 +1625,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) if (hcon->type == ACL_LINK) l2cap_request_info(conn); - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { @@ -1635,7 +1643,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) l2cap_chan_unlock(chan); } - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); if (hcon->type == LE_LINK) l2cap_le_conn_ready(conn); @@ -1650,10 +1658,14 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) BT_DBG("conn %p", conn); + mutex_lock(&conn->chan_lock); + list_for_each_entry(chan, &conn->chan_l, list) { if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) l2cap_chan_set_err(chan, err); } + + mutex_unlock(&conn->chan_lock); } static void l2cap_info_timeout(struct work_struct *work) @@ -1664,9 +1676,7 @@ static void l2cap_info_timeout(struct work_struct *work) conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; - mutex_lock(&conn->lock); l2cap_conn_start(conn); - mutex_unlock(&conn->lock); } /* @@ -1758,8 +1768,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - mutex_lock(&conn->lock); - kfree_skb(conn->rx_skb); skb_queue_purge(&conn->pending_rx); @@ -1778,6 +1786,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) /* Force the connection to be immediately dropped */ hcon->disc_timeout = 0; + mutex_lock(&conn->chan_lock); + /* Kill channels */ list_for_each_entry_safe(chan, l, &conn->chan_l, list) { l2cap_chan_hold(chan); @@ -1791,14 +1801,12 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) l2cap_chan_put(chan); } + mutex_unlock(&conn->chan_lock); + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) cancel_delayed_work_sync(&conn->info_timer); - hci_chan_del(conn->hchan); - conn->hchan = NULL; - hcon->l2cap_data = NULL; - mutex_unlock(&conn->lock); l2cap_conn_put(conn); } @@ -1806,6 +1814,7 @@ static void l2cap_conn_free(struct kref *ref) { struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref); + hci_chan_del(conn->hchan); hci_conn_put(conn->hcon); kfree(conn); } @@ -2916,6 +2925,8 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); + mutex_lock(&conn->chan_lock); + list_for_each_entry(chan, &conn->chan_l, list) { if (chan->chan_type != L2CAP_CHAN_RAW) continue; @@ -2930,6 +2941,8 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) if (chan->ops->recv(chan, nskb)) kfree_skb(nskb); } + + mutex_unlock(&conn->chan_lock); } /* ---- L2CAP signalling commands ---- */ @@ -3952,6 +3965,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, goto response; } + mutex_lock(&conn->chan_lock); l2cap_chan_lock(pchan); /* Check if the ACL is secure enough (if not SDP) */ @@ -4058,6 +4072,7 @@ response: } l2cap_chan_unlock(pchan); + mutex_unlock(&conn->chan_lock); l2cap_chan_put(pchan); } @@ -4096,19 +4111,27 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); + mutex_lock(&conn->chan_lock); + if (scid) { chan = __l2cap_get_chan_by_scid(conn, scid); - if (!chan) - return -EBADSLT; + if (!chan) { + err = -EBADSLT; + goto unlock; + } } else { chan = __l2cap_get_chan_by_ident(conn, cmd->ident); - if (!chan) - return -EBADSLT; + if (!chan) { + err = -EBADSLT; + goto unlock; + } } chan = l2cap_chan_hold_unless_zero(chan); - if (!chan) - return -EBADSLT; + if (!chan) { + err = -EBADSLT; + goto unlock; + } err = 0; @@ -4146,6 +4169,9 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, l2cap_chan_unlock(chan); l2cap_chan_put(chan); +unlock: + mutex_unlock(&conn->chan_lock); + return err; } @@ -4433,7 +4459,11 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, chan->ops->set_shutdown(chan); + l2cap_chan_unlock(chan); + mutex_lock(&conn->chan_lock); + l2cap_chan_lock(chan); l2cap_chan_del(chan, ECONNRESET); + mutex_unlock(&conn->chan_lock); chan->ops->close(chan); @@ -4470,7 +4500,11 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, return 0; } + l2cap_chan_unlock(chan); + mutex_lock(&conn->chan_lock); + l2cap_chan_lock(chan); l2cap_chan_del(chan, 0); + mutex_unlock(&conn->chan_lock); chan->ops->close(chan); @@ -4668,9 +4702,13 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x", dcid, mtu, mps, credits, result); + mutex_lock(&conn->chan_lock); + chan = __l2cap_get_chan_by_ident(conn, cmd->ident); - if (!chan) - return -EBADSLT; + if (!chan) { + err = -EBADSLT; + goto unlock; + } err = 0; @@ -4718,6 +4756,9 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, l2cap_chan_unlock(chan); +unlock: + mutex_unlock(&conn->chan_lock); + return err; } @@ -4829,6 +4870,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response; } + mutex_lock(&conn->chan_lock); l2cap_chan_lock(pchan); if (!smp_sufficient_security(conn->hcon, pchan->sec_level, @@ -4894,6 +4936,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, response_unlock: l2cap_chan_unlock(pchan); + mutex_unlock(&conn->chan_lock); l2cap_chan_put(pchan); if (result == L2CAP_CR_PEND) @@ -5027,6 +5070,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, goto response; } + mutex_lock(&conn->chan_lock); l2cap_chan_lock(pchan); if (!smp_sufficient_security(conn->hcon, pchan->sec_level, @@ -5101,6 +5145,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, unlock: l2cap_chan_unlock(pchan); + mutex_unlock(&conn->chan_lock); l2cap_chan_put(pchan); response: @@ -5137,6 +5182,8 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits, result); + mutex_lock(&conn->chan_lock); + cmd_len -= sizeof(*rsp); list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { @@ -5222,6 +5269,8 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, l2cap_chan_unlock(chan); } + mutex_unlock(&conn->chan_lock); + return err; } @@ -5334,6 +5383,8 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn, if (cmd_len < sizeof(*rej)) return -EPROTO; + mutex_lock(&conn->chan_lock); + chan = __l2cap_get_chan_by_ident(conn, cmd->ident); if (!chan) goto done; @@ -5348,6 +5399,7 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn, l2cap_chan_put(chan); done: + mutex_unlock(&conn->chan_lock); return 0; } @@ -6802,12 +6854,8 @@ static void process_pending_rx(struct work_struct *work) BT_DBG(""); - mutex_lock(&conn->lock); - while ((skb = skb_dequeue(&conn->pending_rx))) l2cap_recv_frame(conn, skb); - - mutex_unlock(&conn->lock); } static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) @@ -6846,7 +6894,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; mutex_init(&conn->ident_lock); - mutex_init(&conn->lock); + mutex_init(&conn->chan_lock); INIT_LIST_HEAD(&conn->chan_l); INIT_LIST_HEAD(&conn->users); @@ -7037,7 +7085,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, } } - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); l2cap_chan_lock(chan); if (cid && __l2cap_get_chan_by_dcid(conn, cid)) { @@ -7078,7 +7126,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, chan_unlock: l2cap_chan_unlock(chan); - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); done: hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -7290,7 +7338,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt); - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { l2cap_chan_lock(chan); @@ -7364,7 +7412,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_chan_unlock(chan); } - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); } /* Append fragment into frame respecting the maximum len of rx_skb */ @@ -7431,11 +7479,8 @@ static void l2cap_recv_reset(struct l2cap_conn *conn) conn->rx_len = 0; } -struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c) +static struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c) { - if (!c) - return NULL; - BT_DBG("conn %p orig refcnt %u", c, kref_read(&c->ref)); if (!kref_get_unless_zero(&c->ref)) @@ -7461,15 +7506,11 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) hci_dev_unlock(hcon->hdev); - if (!conn) { - kfree_skb(skb); - return; - } + if (!conn) + goto drop; BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags); - mutex_lock(&conn->lock); - switch (flags) { case ACL_START: case ACL_START_NO_FLUSH: @@ -7494,7 +7535,7 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) if (len == skb->len) { /* Complete frame received */ l2cap_recv_frame(conn, skb); - goto unlock; + return; } BT_DBG("Start: total len %d, frag len %u", len, skb->len); @@ -7556,11 +7597,10 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) break; } + l2cap_conn_put(conn); + drop: kfree_skb(skb); -unlock: - mutex_unlock(&conn->lock); - l2cap_conn_put(conn); } static struct hci_cb l2cap_cb = { diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index acd11b268b98..46ea0bee2259 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1326,10 +1326,9 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) /* prevent sk structure from being freed whilst unlocked */ sock_hold(sk); + chan = l2cap_pi(sk)->chan; /* prevent chan structure from being freed whilst unlocked */ - chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan); - if (!chan) - goto shutdown_already; + l2cap_chan_hold(chan); BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); @@ -1359,20 +1358,22 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) release_sock(sk); l2cap_chan_lock(chan); - /* prevent conn structure from being freed */ - conn = l2cap_conn_hold_unless_zero(chan->conn); + conn = chan->conn; + if (conn) + /* prevent conn structure from being freed */ + l2cap_conn_get(conn); l2cap_chan_unlock(chan); if (conn) /* mutex lock must be taken before l2cap_chan_lock() */ - mutex_lock(&conn->lock); + mutex_lock(&conn->chan_lock); l2cap_chan_lock(chan); l2cap_chan_close(chan, 0); l2cap_chan_unlock(chan); if (conn) { - mutex_unlock(&conn->lock); + mutex_unlock(&conn->chan_lock); l2cap_conn_put(conn); }