summaryrefslogtreecommitdiffstats
path: root/net/bluetooth/hci_core.c
diff options
context:
space:
mode:
authorJohan Hedberg2013-10-01 21:44:50 +0200
committerMarcel Holtmann2013-10-02 08:27:08 +0200
commite1d08f406792219ace03aa02e53a6946abc15ec8 (patch)
tree0da4305a8246c93e233b71783913205b3bdb0dd4 /net/bluetooth/hci_core.c
parentBluetooth: Refactor hci_dev_open to a separate hci_dev_do_open function (diff)
downloadkernel-qcow2-linux-e1d08f406792219ace03aa02e53a6946abc15ec8.tar.gz
kernel-qcow2-linux-e1d08f406792219ace03aa02e53a6946abc15ec8.tar.xz
kernel-qcow2-linux-e1d08f406792219ace03aa02e53a6946abc15ec8.zip
Bluetooth: Fix workqueue synchronization in hci_dev_open
When hci_sock.c calls hci_dev_open it needs to ensure that there isn't pending work in progress, such as that which is scheduled for the initial setup procedure or the one for automatically powering off after the setup procedure. This adds the necessary calls to ensure that any previously scheduled work is completed before attempting to call hci_dev_do_open. This patch fixes a race with old user space versions where we might receive a HCIDEVUP ioctl before the setup procedure has been completed. When that happens the setup procedures callback may fail early and leave the device in an inconsistent state, causing e.g. the setup callback to be (incorrectly) called more than once. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/hci_core.c')
-rw-r--r--net/bluetooth/hci_core.c10
1 files changed, 10 insertions, 0 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 1bc43249d5a3..7cbdd33d9b38 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1273,6 +1273,16 @@ int hci_dev_open(__u16 dev)
if (!hdev)
return -ENODEV;
+ /* We need to ensure that no other power on/off work is pending
+ * before proceeding to call hci_dev_do_open. This is
+ * particularly important if the setup procedure has not yet
+ * completed.
+ */
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+ cancel_delayed_work(&hdev->power_off);
+
+ flush_workqueue(hdev->req_workqueue);
+
err = hci_dev_do_open(hdev);
hci_dev_put(hdev);