Bluetooth: hci_core: Cancel sync command if sending a frame failed
authorBenjamin Berg <bberg@redhat.com>
Fri, 3 Dec 2021 14:59:01 +0000 (15:59 +0100)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 3 Dec 2021 18:41:59 +0000 (10:41 -0800)
If sending a frame failed any sync command associated with it will never
be completed. As such, cancel any such command immediately to avoid
timing out.

Signed-off-by: Benjamin Berg <bberg@redhat.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_core.c

index fdc0dcf..5cadecd 100644 (file)
@@ -2906,7 +2906,7 @@ int hci_unregister_cb(struct hci_cb *cb)
 }
 EXPORT_SYMBOL(hci_unregister_cb);
 
-static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+static int hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
        int err;
 
@@ -2929,14 +2929,17 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        if (!test_bit(HCI_RUNNING, &hdev->flags)) {
                kfree_skb(skb);
-               return;
+               return -EINVAL;
        }
 
        err = hdev->send(hdev, skb);
        if (err < 0) {
                bt_dev_err(hdev, "sending frame failed (%d)", err);
                kfree_skb(skb);
+               return err;
        }
+
+       return 0;
 }
 
 /* Send HCI command */
@@ -3843,10 +3846,15 @@ static void hci_cmd_work(struct work_struct *work)
 
                hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
                if (hdev->sent_cmd) {
+                       int res;
                        if (hci_req_status_pend(hdev))
                                hci_dev_set_flag(hdev, HCI_CMD_PENDING);
                        atomic_dec(&hdev->cmd_cnt);
-                       hci_send_frame(hdev, skb);
+
+                       res = hci_send_frame(hdev, skb);
+                       if (res < 0)
+                               hci_cmd_sync_cancel(hdev, -res);
+
                        if (test_bit(HCI_RESET, &hdev->flags))
                                cancel_delayed_work(&hdev->cmd_timer);
                        else