Bluetooth: hci_sync: Only allow hci_cmd_sync_queue if running
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 21 Apr 2023 18:37:55 +0000 (11:37 -0700)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 24 Apr 2023 05:07:43 +0000 (22:07 -0700)
This makes sure hci_cmd_sync_queue only queue new work if HCI_RUNNING
has been set otherwise there is a risk of commands being sent while
turning off.

Because hci_cmd_sync_queue can no longer queue work while HCI_RUNNING is
not set it cannot be used to power on adapters so instead
hci_cmd_sync_submit is introduced which bypass the HCI_RUNNING check, so
it behaves like the old implementation.

Link: https://lore.kernel.org/all/CAB4PzUpDMvdc8j2MdeSAy1KkAE-D3woprCwAdYWeOc-3v3c9Sw@mail.gmail.com/
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/hci_sync.h
net/bluetooth/hci_sync.c
net/bluetooth/mgmt.c

index f61b249..2495be4 100644 (file)
@@ -41,6 +41,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev);
 void hci_cmd_sync_cancel(struct hci_dev *hdev, int err);
 void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err);
 
+int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
+                       void *data, hci_cmd_sync_work_destroy_t destroy);
 int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
                       void *data, hci_cmd_sync_work_destroy_t destroy);
 
index 771aaa8..647a8ce 100644 (file)
@@ -684,8 +684,12 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
 }
 EXPORT_SYMBOL(hci_cmd_sync_cancel);
 
-int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
-                      void *data, hci_cmd_sync_work_destroy_t destroy)
+/* Submit HCI command to be run in as cmd_sync_work:
+ *
+ * - hdev must _not_ be unregistered
+ */
+int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
+                       void *data, hci_cmd_sync_work_destroy_t destroy)
 {
        struct hci_cmd_sync_work_entry *entry;
 
@@ -708,6 +712,23 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
 
        return 0;
 }
+EXPORT_SYMBOL(hci_cmd_sync_submit);
+
+/* Queue HCI command:
+ *
+ * - hdev must be running
+ */
+int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
+                      void *data, hci_cmd_sync_work_destroy_t destroy)
+{
+       /* Only queue command if hdev is running which means it had been opened
+        * and is either on init phase or is already up.
+        */
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -ENETDOWN;
+
+       return hci_cmd_sync_submit(hdev, func, data, destroy);
+}
 EXPORT_SYMBOL(hci_cmd_sync_queue);
 
 int hci_update_eir_sync(struct hci_dev *hdev)
index 13c7458..f7b2d09 100644 (file)
@@ -1400,11 +1400,15 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        /* Cancel potentially blocking sync operation before power off */
-       if (cp->val == 0x00)
+       if (cp->val == 0x00) {
                __hci_cmd_sync_cancel(hdev, -EHOSTDOWN);
-
-       err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd,
-                                mgmt_set_powered_complete);
+               err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd,
+                                        mgmt_set_powered_complete);
+       } else {
+               /* Use hci_cmd_sync_submit since hdev might not be running */
+               err = hci_cmd_sync_submit(hdev, set_powered_sync, cmd,
+                                         mgmt_set_powered_complete);
+       }
 
        if (err < 0)
                mgmt_pending_remove(cmd);