diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b04a5a02ecf3..c860ec4ea7b8 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -150,6 +150,8 @@ static struct bt_sock_list hci_sk_list = { .lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock) }; +static DEFINE_MUTEX(hci_sk_list_lock); + static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb) { struct hci_filter *flt; @@ -758,10 +760,13 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) if (event == HCI_DEV_UNREG) { struct sock *sk; + int put_count = 0; /* Detach sockets from device */ + mutex_lock(&hci_sk_list_lock); read_lock(&hci_sk_list.lock); sk_for_each(sk, &hci_sk_list.head) { + read_unlock(&hci_sk_list.lock); lock_sock(sk); if (hci_pi(sk)->hdev == hdev) { hci_pi(sk)->hdev = NULL; @@ -769,11 +774,15 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) sk->sk_state = BT_OPEN; sk->sk_state_change(sk); - hci_dev_put(hdev); + put_count++; } release_sock(sk); + read_lock(&hci_sk_list.lock); } read_unlock(&hci_sk_list.lock); + mutex_unlock(&hci_sk_list_lock); + while (put_count--) + hci_dev_put(hdev); } } @@ -838,6 +847,10 @@ static int hci_sock_release(struct socket *sock) if (!sk) return 0; + mutex_lock(&hci_sk_list_lock); + bt_sock_unlink(&hci_sk_list, sk); + mutex_unlock(&hci_sk_list_lock); + lock_sock(sk); switch (hci_pi(sk)->channel) { @@ -859,8 +872,6 @@ static int hci_sock_release(struct socket *sock) break; } - bt_sock_unlink(&hci_sk_list, sk); - hdev = hci_pi(sk)->hdev; if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {