[PATCH] Bluetooth: HCI: Fix hci0 not release in usb disconnect process

zhangchen200426@163.com posted 1 patch 1 day, 2 hours ago
net/bluetooth/hci_core.c |  6 ++++++
net/bluetooth/hci_sync.c | 22 ++++++++++++++++------
2 files changed, 22 insertions(+), 6 deletions(-)
[PATCH] Bluetooth: HCI: Fix hci0 not release in usb disconnect process
Posted by zhangchen200426@163.com 1 day, 2 hours ago
From: zhangchen <zhangchen01@kylinos.cn>

If hci_resume_dev before hci_unregister_dev, the hci command will
timeout and the reference count of hdev will not reset to zero.
Then the node "hci0" will not release.

The output in question is as follows:
[ 3391.553518][ 7] [T247244] Bluetooth: hci0: command 0x0c01 tx timeout
[ 3391.553588][ 7] [T264732] Bluetooth: hci0: Opcode 0x0c01 failed: -110
[ 3393.569514][ 3] [T247244] Bluetooth: hci0: command 0x0c01 tx timeout
[ 3393.569515][ 3] [T264732] Bluetooth: hci0: Opcode 0x0c1a failed: -110
[ 3393.709645][ 6] [T104579] usb 10-1: new full-speed USB device number 95 using xhci-hcd
[ 3393.862194][ 6] [T104579] usb 10-1: New USB device found, idVendor=13d3, idProduct=3570, bcdDevice= 0.00
[ 3393.862205][ 6] [T104579] usb 10-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 3393.862208][ 6] [T104579] usb 10-1: Product: Bluetooth Radio
[ 3393.862210][ 6] [T104579] usb 10-1: Manufacturer: Realtek
[ 3393.862212][ 6] [T104579] usb 10-1: SerialNumber: 00e04c000001
[ 3393.867589][ 6] [T247244] Bluetooth: hci1: RTL: examining hci_ver=0b hci_rev=000b lmp_ver=0b lmp_subver=8852
[ 3393.868573][ 6] [T247244] Bluetooth: hci1: RTL: rom_version status=0 version=1
[ 3393.868583][ 6] [T247244] Bluetooth: hci1: RTL: loading rtl_bt/rtl8852bu_fw.bin
[ 3393.868672][ 6] [T247244] Bluetooth: hci1: RTL: loading rtl_bt/rtl8852bu_config.bin
[ 3393.869699][ 6] [T247244] Bluetooth: hci1: RTL: cfg_sz 6, total sz 65603

The call sequence in question is as follows:
usb disconnect:
  btusb_disconnect
   hci_unregister_dev
    hci_dev_set_flag
    hci_cmd_sync_clear
    hci_unregister_suspend_notifier
    hci_dev_do_close
    device_del

device resume:
  hci_suspend_notifier
   hci_resume_dev
    hci_resume_sync
     hci_set_event_mask_sync
      __hci_cmd_sync_status
       __hci_cmd_sync_status_sk
        __hci_cmd_sync_sk
         wait_event_interruptible_timeout

The output after adding debug information in question is as follows:
[ 6378.366215][ 6] [T434033] hci_resume_dev hci name hci0
[ 6378.366218][ 6] [T434033] hci_resume_sync set event mask sync
[ 6378.366219][ 6] [T434033] hci_set_event_mask_sync
[ 6378.366220][ 6] [T434033] __hci_cmd_sync_sk hci name hci0 Opcode 0x0c01
[ 6378.366227][ 6] [T434033] __hci_cmd_sync_sk wait event interruptible timeout
[ 6378.367632][ 6] [T420012] btusb_disconnect intf 0000000024117fc1
[ 6378.367637][ 6] [T420012] btusb_disconnect hci_unregister_dev
[ 6378.367638][ 6] [T420012] hci_unregister_dev 0000000064bfd783 name hci0 bus 1
[ 6378.367641][ 6] [T420012] hci_unregister_dev set flag
[ 6378.367804][ 6] [T420012] hci_unregister_dev cmd sync clear
[ 6378.367807][ 6] [T420012] hci_unregister_dev unregister suspend notifier
[ 6380.367544][ 6] [T434033] __hci_cmd_sync_sk cmd timeout
[ 6380.367542][ 6] [T197498] Bluetooth: hci0: command 0x0c01 tx timeout
[ 6380.367550][ 6] [T434033] __hci_cmd_sync_sk hci0 end: err -110
[ 6380.367552][ 6] [T434033] Bluetooth: hci0: Opcode 0x0c01 failed: -110
[ 6380.367555][ 6] [T434033] hci_resume_sync clear event filter
[ 6380.367556][ 6] [T434033] hci_resume_sync resume scan sync
[ 6380.367558][ 6] [T434033] __hci_cmd_sync_sk hci name hci0 Opcode 0x0c1a
[ 6380.367561][ 6] [T434033] __hci_cmd_sync_sk wait event interruptible timeout
[ 6382.383538][ 6] [T197498] Bluetooth: hci0: command 0x0c01 tx timeout
[ 6382.383593][ 6] [T434033] __hci_cmd_sync_sk hci0 end: err -110
[ 6382.383597][ 6] [T434033] Bluetooth: hci0: Opcode 0x0c1a failed: -110

The output after adding debug information in normal is as follows:
[50.039156][ 6] [ T8360] btusb_disconnect intf 00000000fca35842
[50.039160][ 6] [ T8360] btusb_disconnect hci_unregister_dev
[50.039162][ 6] [ T8360] hci_unregister_dev 000000002422b946 name hci0 bus 1
[50.039164][ 6] [ T8360] hci_unregister_dev set flag
[50.039224][ 6] [ T8360] hci_unregister_dev cmd sync clear
[50.039227][ 5] [ T8360] hci_unregister_dev unregister suspend notifier
[50.043542][ 5] [ T8284] hci_resume_dev hci name hci0

This patch add hci_cancel_cmd_sync in hci_unregister_dev to wake up
hdev->req_wait_q, and stop __hci_cmd_sync_sk by judging the
HCI_UNREGISTER flag. Then stopping hci_resume_dev process based on the
returned error code.

Fixes: 182ee45da083 ("Bluetooth: hci_sync: Rework hci_suspend_notifier")
Cc: stable@vger.kernel.org
Signed-off-by: zhangchen <zhangchen01@kylinos.cn>
---
 net/bluetooth/hci_core.c |  6 ++++++
 net/bluetooth/hci_sync.c | 22 ++++++++++++++++------
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 3418d7b964a1..c977bcba3e76 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -50,6 +50,7 @@
 static void hci_rx_work(struct work_struct *work);
 static void hci_cmd_work(struct work_struct *work);
 static void hci_tx_work(struct work_struct *work);
+static void hci_cancel_cmd_sync(struct hci_dev *hdev, int err);
 
 /* HCI device list */
 LIST_HEAD(hci_dev_list);
@@ -2695,6 +2696,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
 	hci_dev_set_flag(hdev, HCI_UNREGISTER);
 	mutex_unlock(&hdev->unregister_lock);
 
+	hci_cancel_cmd_sync(hdev, EINTR);
+
 	write_lock(&hci_dev_list_lock);
 	list_del(&hdev->list);
 	write_unlock(&hci_dev_list_lock);
@@ -2877,6 +2880,9 @@ int hci_resume_dev(struct hci_dev *hdev)
 	ret = hci_resume_sync(hdev);
 	hci_req_sync_unlock(hdev);
 
+	if (ret && hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return 0;
+
 	mgmt_resuming(hdev, hdev->wake_reason, &hdev->wake_addr,
 		      hdev->wake_addr_type);
 
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 6e76798ec786..f48d34fbfff2 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -174,10 +174,11 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
 		return ERR_PTR(err);
 
 	err = wait_event_interruptible_timeout(hdev->req_wait_q,
-					       hdev->req_status != HCI_REQ_PEND,
+					       hdev->req_status != HCI_REQ_PEND ||
+					       hci_dev_test_flag(hdev, HCI_UNREGISTER),
 					       timeout);
 
-	if (err == -ERESTARTSYS)
+	if (err == -ERESTARTSYS || hci_dev_test_flag(hdev, HCI_UNREGISTER))
 		return ERR_PTR(-EINTR);
 
 	switch (hdev->req_status) {
@@ -6296,6 +6297,7 @@ static int hci_resume_scan_sync(struct hci_dev *hdev)
  */
 int hci_resume_sync(struct hci_dev *hdev)
 {
+	int err;
 	/* If not marked as suspended there nothing to do */
 	if (!hdev->suspended)
 		return 0;
@@ -6303,10 +6305,14 @@ int hci_resume_sync(struct hci_dev *hdev)
 	hdev->suspended = false;
 
 	/* Restore event mask */
-	hci_set_event_mask_sync(hdev);
+	err = hci_set_event_mask_sync(hdev);
+	if (err && hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return err;
 
 	/* Clear any event filters and restore scan state */
-	hci_clear_event_filter_sync(hdev);
+	err = hci_clear_event_filter_sync(hdev);
+	if (err && hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return err;
 
 	/* Resume scanning */
 	hci_resume_scan_sync(hdev);
@@ -6315,10 +6321,14 @@ int hci_resume_sync(struct hci_dev *hdev)
 	hci_resume_monitor_sync(hdev);
 
 	/* Resume other advertisements */
-	hci_resume_advertising_sync(hdev);
+	err = hci_resume_advertising_sync(hdev);
+	if (err && hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return err;
 
 	/* Resume discovery */
-	hci_resume_discovery_sync(hdev);
+	err = hci_resume_discovery_sync(hdev);
+	if (err && hci_dev_test_flag(hdev, HCI_UNREGISTER))
+		return err;
 
 	return 0;
 }
-- 
2.25.1