Bluetooth: Move LE scan disable/restart behind req_workqueue
authorJohan Hedberg <johan.hedberg@intel.com>
Wed, 11 Nov 2015 06:11:23 +0000 (08:11 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 19 Nov 2015 16:50:30 +0000 (17:50 +0100)
To avoid any risks of races, place also these LE scan modification
work callbacks behind the same work queue as the other LE scan
changes.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c
net/bluetooth/hci_request.c
net/bluetooth/mgmt.c

index c2ca6a58d1e0d65440dc156d7eb2057682ddbe8e..1f75aebbd8c41f15b20d1f2e1f91377e20ace41a 100644 (file)
@@ -328,6 +328,8 @@ struct hci_dev {
        struct work_struct      tx_work;
 
        struct work_struct      bg_scan_update;
+       struct delayed_work     le_scan_disable;
+       struct delayed_work     le_scan_restart;
 
        struct sk_buff_head     rx_q;
        struct sk_buff_head     raw_q;
@@ -372,9 +374,6 @@ struct hci_dev {
 
        DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
 
-       struct delayed_work     le_scan_disable;
-       struct delayed_work     le_scan_restart;
-
        __s8                    adv_tx_power;
        __u8                    adv_data[HCI_MAX_AD_LENGTH];
        __u8                    adv_data_len;
index 029d7798cffabdd975d70324adf0ecef136ab066..0655521dd8bcbc70556b1cc9f12efac9bf82f457 100644 (file)
@@ -1527,9 +1527,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
        if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
                cancel_delayed_work(&hdev->service_cache);
 
-       cancel_delayed_work_sync(&hdev->le_scan_disable);
-       cancel_delayed_work_sync(&hdev->le_scan_restart);
-
        if (hci_dev_test_flag(hdev, HCI_MGMT))
                cancel_delayed_work_sync(&hdev->rpa_expired);
 
@@ -2889,169 +2886,6 @@ static void hci_conn_params_clear_all(struct hci_dev *hdev)
        BT_DBG("All LE connection parameters were removed");
 }
 
-static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
-       if (status) {
-               BT_ERR("Failed to start inquiry: status %d", status);
-
-               hci_dev_lock(hdev);
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-               hci_dev_unlock(hdev);
-               return;
-       }
-}
-
-static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
-                                         u16 opcode)
-{
-       /* General inquiry access code (GIAC) */
-       u8 lap[3] = { 0x33, 0x8b, 0x9e };
-       struct hci_cp_inquiry cp;
-       int err;
-
-       if (status) {
-               BT_ERR("Failed to disable LE scanning: status %d", status);
-               return;
-       }
-
-       hdev->discovery.scan_start = 0;
-
-       switch (hdev->discovery.type) {
-       case DISCOV_TYPE_LE:
-               hci_dev_lock(hdev);
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-               hci_dev_unlock(hdev);
-               break;
-
-       case DISCOV_TYPE_INTERLEAVED:
-               hci_dev_lock(hdev);
-
-               if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
-                            &hdev->quirks)) {
-                       /* If we were running LE only scan, change discovery
-                        * state. If we were running both LE and BR/EDR inquiry
-                        * simultaneously, and BR/EDR inquiry is already
-                        * finished, stop discovery, otherwise BR/EDR inquiry
-                        * will stop discovery when finished. If we will resolve
-                        * remote device name, do not change discovery state.
-                        */
-                       if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
-                           hdev->discovery.state != DISCOVERY_RESOLVING)
-                               hci_discovery_set_state(hdev,
-                                                       DISCOVERY_STOPPED);
-               } else {
-                       struct hci_request req;
-
-                       hci_inquiry_cache_flush(hdev);
-
-                       hci_req_init(&req, hdev);
-
-                       memset(&cp, 0, sizeof(cp));
-                       memcpy(&cp.lap, lap, sizeof(cp.lap));
-                       cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
-                       hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
-                       err = hci_req_run(&req, inquiry_complete);
-                       if (err) {
-                               BT_ERR("Inquiry request failed: err %d", err);
-                               hci_discovery_set_state(hdev,
-                                                       DISCOVERY_STOPPED);
-                       }
-               }
-
-               hci_dev_unlock(hdev);
-               break;
-       }
-}
-
-static void le_scan_disable_work(struct work_struct *work)
-{
-       struct hci_dev *hdev = container_of(work, struct hci_dev,
-                                           le_scan_disable.work);
-       struct hci_request req;
-       int err;
-
-       BT_DBG("%s", hdev->name);
-
-       cancel_delayed_work_sync(&hdev->le_scan_restart);
-
-       hci_req_init(&req, hdev);
-
-       hci_req_add_le_scan_disable(&req);
-
-       err = hci_req_run(&req, le_scan_disable_work_complete);
-       if (err)
-               BT_ERR("Disable LE scanning request failed: err %d", err);
-}
-
-static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status,
-                                         u16 opcode)
-{
-       unsigned long timeout, duration, scan_start, now;
-
-       BT_DBG("%s", hdev->name);
-
-       if (status) {
-               BT_ERR("Failed to restart LE scan: status %d", status);
-               return;
-       }
-
-       if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
-           !hdev->discovery.scan_start)
-               return;
-
-       /* When the scan was started, hdev->le_scan_disable has been queued
-        * after duration from scan_start. During scan restart this job
-        * has been canceled, and we need to queue it again after proper
-        * timeout, to make sure that scan does not run indefinitely.
-        */
-       duration = hdev->discovery.scan_duration;
-       scan_start = hdev->discovery.scan_start;
-       now = jiffies;
-       if (now - scan_start <= duration) {
-               int elapsed;
-
-               if (now >= scan_start)
-                       elapsed = now - scan_start;
-               else
-                       elapsed = ULONG_MAX - scan_start + now;
-
-               timeout = duration - elapsed;
-       } else {
-               timeout = 0;
-       }
-       queue_delayed_work(hdev->workqueue,
-                          &hdev->le_scan_disable, timeout);
-}
-
-static void le_scan_restart_work(struct work_struct *work)
-{
-       struct hci_dev *hdev = container_of(work, struct hci_dev,
-                                           le_scan_restart.work);
-       struct hci_request req;
-       struct hci_cp_le_set_scan_enable cp;
-       int err;
-
-       BT_DBG("%s", hdev->name);
-
-       /* If controller is not scanning we are done. */
-       if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
-               return;
-
-       hci_req_init(&req, hdev);
-
-       hci_req_add_le_scan_disable(&req);
-
-       memset(&cp, 0, sizeof(cp));
-       cp.enable = LE_SCAN_ENABLE;
-       cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-       hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
-
-       err = hci_req_run(&req, le_scan_restart_work_complete);
-       if (err)
-               BT_ERR("Restart LE scan request failed: err %d", err);
-}
-
 /* Copy the Identity Address of the controller.
  *
  * If the controller has a public BD_ADDR, then by default use that one.
@@ -3151,8 +2985,6 @@ struct hci_dev *hci_alloc_dev(void)
 
        INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
        INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
-       INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
-       INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
        INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
 
        skb_queue_head_init(&hdev->rx_q);
index 8aa06cc545c315a01683256fc048240d93ea365d..4588fe2bfc0e6d3be28645d579a9b54ca2804bf0 100644 (file)
@@ -858,12 +858,191 @@ static void bg_scan_update(struct work_struct *work)
        hci_dev_unlock(hdev);
 }
 
+static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+       if (status) {
+               BT_ERR("Failed to start inquiry: status %d", status);
+
+               hci_dev_lock(hdev);
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               hci_dev_unlock(hdev);
+               return;
+       }
+}
+
+static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status)
+{
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
+       struct hci_cp_inquiry cp;
+       int err;
+
+       if (status) {
+               BT_ERR("Failed to disable LE scanning: status %d", status);
+               return;
+       }
+
+       hdev->discovery.scan_start = 0;
+
+       switch (hdev->discovery.type) {
+       case DISCOV_TYPE_LE:
+               hci_dev_lock(hdev);
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               hci_dev_unlock(hdev);
+               break;
+
+       case DISCOV_TYPE_INTERLEAVED:
+               hci_dev_lock(hdev);
+
+               if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+                            &hdev->quirks)) {
+                       /* If we were running LE only scan, change discovery
+                        * state. If we were running both LE and BR/EDR inquiry
+                        * simultaneously, and BR/EDR inquiry is already
+                        * finished, stop discovery, otherwise BR/EDR inquiry
+                        * will stop discovery when finished. If we will resolve
+                        * remote device name, do not change discovery state.
+                        */
+                       if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
+                           hdev->discovery.state != DISCOVERY_RESOLVING)
+                               hci_discovery_set_state(hdev,
+                                                       DISCOVERY_STOPPED);
+               } else {
+                       struct hci_request req;
+
+                       hci_inquiry_cache_flush(hdev);
+
+                       hci_req_init(&req, hdev);
+
+                       memset(&cp, 0, sizeof(cp));
+                       memcpy(&cp.lap, lap, sizeof(cp.lap));
+                       cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
+                       hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+                       err = hci_req_run(&req, inquiry_complete);
+                       if (err) {
+                               BT_ERR("Inquiry request failed: err %d", err);
+                               hci_discovery_set_state(hdev,
+                                                       DISCOVERY_STOPPED);
+                       }
+               }
+
+               hci_dev_unlock(hdev);
+               break;
+       }
+}
+
+static void le_scan_disable(struct hci_request *req, unsigned long opt)
+{
+       hci_req_add_le_scan_disable(req);
+}
+
+static void le_scan_disable_work(struct work_struct *work)
+{
+       struct hci_dev *hdev = container_of(work, struct hci_dev,
+                                           le_scan_disable.work);
+       u8 status;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       cancel_delayed_work(&hdev->le_scan_restart);
+
+       err = hci_req_sync(hdev, le_scan_disable, 0, HCI_CMD_TIMEOUT, &status);
+       if (err)
+               return;
+
+       le_scan_disable_work_complete(hdev, status);
+}
+
+static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status)
+{
+       unsigned long timeout, duration, scan_start, now;
+
+       BT_DBG("%s", hdev->name);
+
+       if (status) {
+               BT_ERR("Failed to restart LE scan: status %d", status);
+               return;
+       }
+
+       hci_dev_lock(hdev);
+
+       if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
+           !hdev->discovery.scan_start)
+               goto unlock;
+
+       /* When the scan was started, hdev->le_scan_disable has been queued
+        * after duration from scan_start. During scan restart this job
+        * has been canceled, and we need to queue it again after proper
+        * timeout, to make sure that scan does not run indefinitely.
+        */
+       duration = hdev->discovery.scan_duration;
+       scan_start = hdev->discovery.scan_start;
+       now = jiffies;
+       if (now - scan_start <= duration) {
+               int elapsed;
+
+               if (now >= scan_start)
+                       elapsed = now - scan_start;
+               else
+                       elapsed = ULONG_MAX - scan_start + now;
+
+               timeout = duration - elapsed;
+       } else {
+               timeout = 0;
+       }
+
+       queue_delayed_work(hdev->req_workqueue,
+                          &hdev->le_scan_disable, timeout);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static void le_scan_restart(struct hci_request *req, unsigned long opt)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_scan_enable cp;
+
+       /* If controller is not scanning we are done. */
+       if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+               return;
+
+       hci_req_add_le_scan_disable(req);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.enable = LE_SCAN_ENABLE;
+       cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+}
+
+static void le_scan_restart_work(struct work_struct *work)
+{
+       struct hci_dev *hdev = container_of(work, struct hci_dev,
+                                           le_scan_restart.work);
+       u8 status;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       err = hci_req_sync(hdev, le_scan_restart, 0, HCI_CMD_TIMEOUT, &status);
+       if (err)
+               return;
+
+       le_scan_restart_work_complete(hdev, status);
+}
+
 void hci_request_setup(struct hci_dev *hdev)
 {
        INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
+       INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
+       INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
 }
 
 void hci_request_cancel_all(struct hci_dev *hdev)
 {
        cancel_work_sync(&hdev->bg_scan_update);
+       cancel_delayed_work_sync(&hdev->le_scan_disable);
+       cancel_delayed_work_sync(&hdev->le_scan_restart);
 }
index bb870c3aadaebdb0aa93266d58dd20c645143ddc..a229cfd0530eca9558af91b7b685ffd73237cb1a 100644 (file)
@@ -4367,7 +4367,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
                        hdev->discovery.scan_duration = timeout;
                }
 
-               queue_delayed_work(hdev->workqueue,
+               queue_delayed_work(hdev->req_workqueue,
                                   &hdev->le_scan_disable, timeout);
        }
 
@@ -8389,7 +8389,7 @@ static void restart_le_scan(struct hci_dev *hdev)
                       hdev->discovery.scan_duration))
                return;
 
-       queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+       queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_restart,
                           DISCOV_LE_RESTART_DELAY);
 }