| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| Date: Thu, 21 Sep 2017 15:35:57 +0200 |
| Subject: Bluetooth: avoid recursive locking in |
| hci_send_to_channel() |
| |
| Mart reported a deadlock in -RT in the call path: |
| hci_send_monitor_ctrl_event() -> hci_send_to_channel() |
| |
| because both functions acquire the same read lock hci_sk_list.lock. This |
| is also a mainline issue because the qrwlock implementation is writer |
| fair (the traditional rwlock implementation is reader biased). |
| |
| To avoid the deadlock there is now __hci_send_to_channel() which expects |
| the readlock to be held. |
| |
| Cc: Marcel Holtmann <marcel@holtmann.org> |
| Cc: Johan Hedberg <johan.hedberg@intel.com> |
| Cc: rt-stable@vger.kernel.org |
| Fixes: 38ceaa00d02d ("Bluetooth: Add support for sending MGMT commands and events to monitor") |
| Reported-by: Mart van de Wege <mvdwege@gmail.com> |
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| --- |
| net/bluetooth/hci_sock.c | 17 +++++++++++------ |
| 1 file changed, 11 insertions(+), 6 deletions(-) |
| |
| --- a/net/bluetooth/hci_sock.c |
| +++ b/net/bluetooth/hci_sock.c |
| @@ -251,15 +251,13 @@ void hci_send_to_sock(struct hci_dev *hd |
| } |
| |
| /* Send frame to sockets with specific channel */ |
| -void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, |
| - int flag, struct sock *skip_sk) |
| +static void __hci_send_to_channel(unsigned short channel, struct sk_buff *skb, |
| + int flag, struct sock *skip_sk) |
| { |
| struct sock *sk; |
| |
| BT_DBG("channel %u len %d", channel, skb->len); |
| |
| - read_lock(&hci_sk_list.lock); |
| - |
| sk_for_each(sk, &hci_sk_list.head) { |
| struct sk_buff *nskb; |
| |
| @@ -285,6 +283,13 @@ void hci_send_to_channel(unsigned short |
| kfree_skb(nskb); |
| } |
| |
| +} |
| + |
| +void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, |
| + int flag, struct sock *skip_sk) |
| +{ |
| + read_lock(&hci_sk_list.lock); |
| + __hci_send_to_channel(channel, skb, flag, skip_sk); |
| read_unlock(&hci_sk_list.lock); |
| } |
| |
| @@ -388,8 +393,8 @@ void hci_send_monitor_ctrl_event(struct |
| hdr->index = index; |
| hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); |
| |
| - hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, |
| - HCI_SOCK_TRUSTED, NULL); |
| + __hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, |
| + HCI_SOCK_TRUSTED, NULL); |
| kfree_skb(skb); |
| } |
| |