diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b04a5a02ecf3..0c62f8761ea8 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -148,8 +148,9 @@ static const struct hci_sec_filter hci_sec_filter = { 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; @@ -757,24 +758,27 @@ 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 */ - read_lock(&hci_sk_list.lock); + mutex_lock(&hci_sk_list_lock); sk_for_each(sk, &hci_sk_list.head) { lock_sock(sk); if (hci_pi(sk)->hdev == hdev) { hci_pi(sk)->hdev = NULL; sk->sk_err = EPIPE; sk->sk_state = BT_OPEN; sk->sk_state_change(sk); - hci_dev_put(hdev); + put_count++; } release_sock(sk); } - read_unlock(&hci_sk_list.lock); + mutex_unlock(&hci_sk_list_lock); + while (put_count--) + hci_dev_put(hdev); } } static struct hci_mgmt_chan *__hci_mgmt_chan_find(unsigned short channel) @@ -837,8 +841,12 @@ 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) { case HCI_CHANNEL_MONITOR: @@ -858,10 +866,8 @@ static int hci_sock_release(struct socket *sock) hci_sock_free_cookie(sk); break; } - bt_sock_unlink(&hci_sk_list, sk); - hdev = hci_pi(sk)->hdev; if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { /* When releasing a user channel exclusive access, @@ -2048,9 +2054,11 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol, sock->state = SS_UNCONNECTED; sk->sk_state = BT_OPEN; + mutex_lock(&hci_sk_list_lock); bt_sock_link(&hci_sk_list, sk); + mutex_unlock(&hci_sk_list_lock); return 0; } static const struct net_proto_family hci_sock_family_ops = {