Skip to content

Commit 0809267

Browse files
pvopsiff
authored andcommitted
Bluetooth: 6lowpan: Don't hold spin lock over sleeping functions
[ Upstream commit 98454bc ] disconnect_all_peers() calls sleeping function (l2cap_chan_close) under spinlock. Holding the lock doesn't actually do any good -- we work on a local copy of the list, and the lock doesn't protect against peer->chan having already been freed. Fix by taking refcounts of peer->chan instead. Clean up the code and old comments a bit. Take devices_lock instead of RCU, because the kfree_rcu(); l2cap_chan_put(); construct in chan_close_cb() does not guarantee peer->chan is necessarily valid in RCU. Also take l2cap_chan_lock() which is required for l2cap_chan_close(). Log: (bluez 6lowpan-tester Client Connect - Disable) ------ BUG: sleeping function called from invalid context at kernel/locking/mutex.c:575 ... <TASK> ... l2cap_send_disconn_req (net/bluetooth/l2cap_core.c:938 net/bluetooth/l2cap_core.c:1495) ... ? __pfx_l2cap_chan_close (net/bluetooth/l2cap_core.c:809) do_enable_set (net/bluetooth/6lowpan.c:1048 net/bluetooth/6lowpan.c:1068) ------ Fixes: 9030582 ("Bluetooth: 6lowpan: Converting rwlocks to use RCU") Signed-off-by: Pauli Virtanen <pav@iki.fi> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Sasha Levin <sashal@kernel.org> (cherry picked from commit a5059df18988b242f1cda0b22a2b463509c59c04) Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
1 parent 7a68f3c commit 0809267

1 file changed

Lines changed: 43 additions & 25 deletions

File tree

net/bluetooth/6lowpan.c

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ static bool enable_6lowpan;
5252
static struct l2cap_chan *listen_chan;
5353
static DEFINE_MUTEX(set_lock);
5454

55+
enum {
56+
LOWPAN_PEER_CLOSING,
57+
LOWPAN_PEER_MAXBITS
58+
};
59+
5560
struct lowpan_peer {
5661
struct list_head list;
5762
struct rcu_head rcu;
@@ -60,6 +65,8 @@ struct lowpan_peer {
6065
/* peer addresses in various formats */
6166
unsigned char lladdr[ETH_ALEN];
6267
struct in6_addr peer_addr;
68+
69+
DECLARE_BITMAP(flags, LOWPAN_PEER_MAXBITS);
6370
};
6471

6572
struct lowpan_btle_dev {
@@ -1013,41 +1020,52 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
10131020
static void disconnect_all_peers(void)
10141021
{
10151022
struct lowpan_btle_dev *entry;
1016-
struct lowpan_peer *peer, *tmp_peer, *new_peer;
1017-
struct list_head peers;
1018-
1019-
INIT_LIST_HEAD(&peers);
1023+
struct lowpan_peer *peer;
1024+
int nchans;
10201025

1021-
/* We make a separate list of peers as the close_cb() will
1022-
* modify the device peers list so it is better not to mess
1023-
* with the same list at the same time.
1026+
/* l2cap_chan_close() cannot be called from RCU, and lock ordering
1027+
* chan->lock > devices_lock prevents taking write side lock, so copy
1028+
* then close.
10241029
*/
10251030

10261031
rcu_read_lock();
1032+
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list)
1033+
list_for_each_entry_rcu(peer, &entry->peers, list)
1034+
clear_bit(LOWPAN_PEER_CLOSING, peer->flags);
1035+
rcu_read_unlock();
10271036

1028-
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
1029-
list_for_each_entry_rcu(peer, &entry->peers, list) {
1030-
new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
1031-
if (!new_peer)
1032-
break;
1037+
do {
1038+
struct l2cap_chan *chans[32];
1039+
int i;
10331040

1034-
new_peer->chan = peer->chan;
1035-
INIT_LIST_HEAD(&new_peer->list);
1041+
nchans = 0;
10361042

1037-
list_add(&new_peer->list, &peers);
1038-
}
1039-
}
1043+
spin_lock(&devices_lock);
10401044

1041-
rcu_read_unlock();
1045+
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
1046+
list_for_each_entry_rcu(peer, &entry->peers, list) {
1047+
if (test_and_set_bit(LOWPAN_PEER_CLOSING,
1048+
peer->flags))
1049+
continue;
10421050

1043-
spin_lock(&devices_lock);
1044-
list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
1045-
l2cap_chan_close(peer->chan, ENOENT);
1051+
l2cap_chan_hold(peer->chan);
1052+
chans[nchans++] = peer->chan;
10461053

1047-
list_del_rcu(&peer->list);
1048-
kfree_rcu(peer, rcu);
1049-
}
1050-
spin_unlock(&devices_lock);
1054+
if (nchans >= ARRAY_SIZE(chans))
1055+
goto done;
1056+
}
1057+
}
1058+
1059+
done:
1060+
spin_unlock(&devices_lock);
1061+
1062+
for (i = 0; i < nchans; ++i) {
1063+
l2cap_chan_lock(chans[i]);
1064+
l2cap_chan_close(chans[i], ENOENT);
1065+
l2cap_chan_unlock(chans[i]);
1066+
l2cap_chan_put(chans[i]);
1067+
}
1068+
} while (nchans);
10511069
}
10521070

10531071
struct set_enable {

0 commit comments

Comments
 (0)