Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Bluetooth: 6lowpan: Don't hold spin lock over sleeping functions

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: 90305829635d ("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>

authored by

Pauli Virtanen and committed by
Luiz Augusto von Dentz
98454bc8 e060088d

+46 -28
+46 -28
net/bluetooth/6lowpan.c
··· 53 53 static struct l2cap_chan *listen_chan; 54 54 static DEFINE_MUTEX(set_lock); 55 55 56 + enum { 57 + LOWPAN_PEER_CLOSING, 58 + LOWPAN_PEER_MAXBITS 59 + }; 60 + 56 61 struct lowpan_peer { 57 62 struct list_head list; 58 63 struct rcu_head rcu; ··· 66 61 /* peer addresses in various formats */ 67 62 unsigned char lladdr[ETH_ALEN]; 68 63 struct in6_addr peer_addr; 64 + 65 + DECLARE_BITMAP(flags, LOWPAN_PEER_MAXBITS); 69 66 }; 70 67 71 68 struct lowpan_btle_dev { ··· 1021 1014 static void disconnect_all_peers(void) 1022 1015 { 1023 1016 struct lowpan_btle_dev *entry; 1024 - struct lowpan_peer *peer, *tmp_peer, *new_peer; 1025 - struct list_head peers; 1017 + struct lowpan_peer *peer; 1018 + int nchans; 1026 1019 1027 - INIT_LIST_HEAD(&peers); 1028 - 1029 - /* We make a separate list of peers as the close_cb() will 1030 - * modify the device peers list so it is better not to mess 1031 - * with the same list at the same time. 1020 + /* l2cap_chan_close() cannot be called from RCU, and lock ordering 1021 + * chan->lock > devices_lock prevents taking write side lock, so copy 1022 + * then close. 1032 1023 */ 1033 1024 1034 1025 rcu_read_lock(); 1035 - 1036 - list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { 1037 - list_for_each_entry_rcu(peer, &entry->peers, list) { 1038 - new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); 1039 - if (!new_peer) 1040 - break; 1041 - 1042 - new_peer->chan = peer->chan; 1043 - INIT_LIST_HEAD(&new_peer->list); 1044 - 1045 - list_add(&new_peer->list, &peers); 1046 - } 1047 - } 1048 - 1026 + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) 1027 + list_for_each_entry_rcu(peer, &entry->peers, list) 1028 + clear_bit(LOWPAN_PEER_CLOSING, peer->flags); 1049 1029 rcu_read_unlock(); 1050 1030 1051 - spin_lock(&devices_lock); 1052 - list_for_each_entry_safe(peer, tmp_peer, &peers, list) { 1053 - l2cap_chan_close(peer->chan, ENOENT); 1031 + do { 1032 + struct l2cap_chan *chans[32]; 1033 + int i; 1054 1034 1055 - list_del_rcu(&peer->list); 1056 - kfree_rcu(peer, rcu); 1057 - } 1058 - spin_unlock(&devices_lock); 1035 + nchans = 0; 1036 + 1037 + spin_lock(&devices_lock); 1038 + 1039 + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { 1040 + list_for_each_entry_rcu(peer, &entry->peers, list) { 1041 + if (test_and_set_bit(LOWPAN_PEER_CLOSING, 1042 + peer->flags)) 1043 + continue; 1044 + 1045 + l2cap_chan_hold(peer->chan); 1046 + chans[nchans++] = peer->chan; 1047 + 1048 + if (nchans >= ARRAY_SIZE(chans)) 1049 + goto done; 1050 + } 1051 + } 1052 + 1053 + done: 1054 + spin_unlock(&devices_lock); 1055 + 1056 + for (i = 0; i < nchans; ++i) { 1057 + l2cap_chan_lock(chans[i]); 1058 + l2cap_chan_close(chans[i], ENOENT); 1059 + l2cap_chan_unlock(chans[i]); 1060 + l2cap_chan_put(chans[i]); 1061 + } 1062 + } while (nchans); 1059 1063 } 1060 1064 1061 1065 struct set_enable {