summaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2015-11-16 05:52:21 -0500
committerMarcel Holtmann <marcel@holtmann.org>2015-12-09 18:51:47 -0500
commit01b1cb87d37fb19cdaa5e7002416fdde156873d0 (patch)
tree87fdc0ba8b3872cea7267fb3a3b3b7ed101ffbba /net/bluetooth
parentad2c8c73d29702c3193f739390f6661f9a4ecad9 (diff)
Bluetooth: Run page scan updates through hdev->req_workqueue
Since Add/Remove Device perform the page scan updates independently from the HCI command completion we've introduced a potential race when multiple mgmt commands are queued. Doing the page scan updates through the req_workqueue ensures that the state changes are performed in a race-free manner. At the same time, to make the request helper more widely usable, extend it to also cover Inquiry Scan changes since those are behind the same HCI command. This is also reflected in the new name of the API as well as the work struct name. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/hci_event.c4
-rw-r--r--net/bluetooth/hci_request.c27
-rw-r--r--net/bluetooth/hci_request.h8
-rw-r--r--net/bluetooth/mgmt.c16
4 files changed, 34 insertions, 21 deletions
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d57c11c1c6b5..703e37f1a955 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2176,7 +2176,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
2176 hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, 2176 hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
2177 sizeof(cp), &cp); 2177 sizeof(cp), &cp);
2178 2178
2179 hci_update_page_scan(hdev); 2179 hci_req_update_scan(hdev);
2180 } 2180 }
2181 2181
2182 /* Set packet type for incoming connection */ 2182 /* Set packet type for incoming connection */
@@ -2362,7 +2362,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
2362 if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags)) 2362 if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
2363 hci_remove_link_key(hdev, &conn->dst); 2363 hci_remove_link_key(hdev, &conn->dst);
2364 2364
2365 hci_update_page_scan(hdev); 2365 hci_req_update_scan(hdev);
2366 } 2366 }
2367 2367
2368 params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); 2368 params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index e639671f54bd..78c026b4ffa1 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -637,7 +637,7 @@ static bool disconnected_whitelist_entries(struct hci_dev *hdev)
637 return false; 637 return false;
638} 638}
639 639
640void __hci_update_page_scan(struct hci_request *req) 640void __hci_req_update_scan(struct hci_request *req)
641{ 641{
642 struct hci_dev *hdev = req->hdev; 642 struct hci_dev *hdev = req->hdev;
643 u8 scan; 643 u8 scan;
@@ -657,22 +657,29 @@ void __hci_update_page_scan(struct hci_request *req)
657 else 657 else
658 scan = SCAN_DISABLED; 658 scan = SCAN_DISABLED;
659 659
660 if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
661 return;
662
663 if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) 660 if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
664 scan |= SCAN_INQUIRY; 661 scan |= SCAN_INQUIRY;
665 662
663 if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE) &&
664 test_bit(HCI_ISCAN, &hdev->flags) == !!(scan & SCAN_INQUIRY))
665 return;
666
666 hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); 667 hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
667} 668}
668 669
669void hci_update_page_scan(struct hci_dev *hdev) 670static int update_scan(struct hci_request *req, unsigned long opt)
670{ 671{
671 struct hci_request req; 672 hci_dev_lock(req->hdev);
673 __hci_req_update_scan(req);
674 hci_dev_unlock(req->hdev);
675 return 0;
676}
672 677
673 hci_req_init(&req, hdev); 678static void scan_update_work(struct work_struct *work)
674 __hci_update_page_scan(&req); 679{
675 hci_req_run(&req, NULL); 680 struct hci_dev *hdev = container_of(work, struct hci_dev, scan_update);
681
682 hci_req_sync(hdev, update_scan, 0, HCI_CMD_TIMEOUT, NULL);
676} 683}
677 684
678/* This function controls the background scanning based on hdev->pend_le_conns 685/* This function controls the background scanning based on hdev->pend_le_conns
@@ -1270,6 +1277,7 @@ void hci_request_setup(struct hci_dev *hdev)
1270{ 1277{
1271 INIT_WORK(&hdev->discov_update, discov_update); 1278 INIT_WORK(&hdev->discov_update, discov_update);
1272 INIT_WORK(&hdev->bg_scan_update, bg_scan_update); 1279 INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
1280 INIT_WORK(&hdev->scan_update, scan_update_work);
1273 INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); 1281 INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
1274 INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work); 1282 INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
1275} 1283}
@@ -1280,6 +1288,7 @@ void hci_request_cancel_all(struct hci_dev *hdev)
1280 1288
1281 cancel_work_sync(&hdev->discov_update); 1289 cancel_work_sync(&hdev->discov_update);
1282 cancel_work_sync(&hdev->bg_scan_update); 1290 cancel_work_sync(&hdev->bg_scan_update);
1291 cancel_work_sync(&hdev->scan_update);
1283 cancel_delayed_work_sync(&hdev->le_scan_disable); 1292 cancel_delayed_work_sync(&hdev->le_scan_disable);
1284 cancel_delayed_work_sync(&hdev->le_scan_restart); 1293 cancel_delayed_work_sync(&hdev->le_scan_restart);
1285} 1294}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 6b9e59f7f7a9..cc8275520fb2 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -61,8 +61,12 @@ void hci_req_add_le_passive_scan(struct hci_request *req);
61/* Returns true if HCI commands were queued */ 61/* Returns true if HCI commands were queued */
62bool hci_req_stop_discovery(struct hci_request *req); 62bool hci_req_stop_discovery(struct hci_request *req);
63 63
64void hci_update_page_scan(struct hci_dev *hdev); 64static inline void hci_req_update_scan(struct hci_dev *hdev)
65void __hci_update_page_scan(struct hci_request *req); 65{
66 queue_work(hdev->req_workqueue, &hdev->scan_update);
67}
68
69void __hci_req_update_scan(struct hci_request *req);
66 70
67int hci_update_random_address(struct hci_request *req, bool require_privacy, 71int hci_update_random_address(struct hci_request *req, bool require_privacy,
68 u8 *own_addr_type); 72 u8 *own_addr_type);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 3d9d2e4839c5..0d20e1328528 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1810,7 +1810,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
1810 * entries. 1810 * entries.
1811 */ 1811 */
1812 hci_req_init(&req, hdev); 1812 hci_req_init(&req, hdev);
1813 __hci_update_page_scan(&req); 1813 __hci_req_update_scan(&req);
1814 update_class(&req); 1814 update_class(&req);
1815 hci_req_run(&req, NULL); 1815 hci_req_run(&req, NULL);
1816 1816
@@ -2058,7 +2058,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status,
2058 2058
2059 if (conn_changed || discov_changed) { 2059 if (conn_changed || discov_changed) {
2060 new_settings(hdev, cmd->sk); 2060 new_settings(hdev, cmd->sk);
2061 hci_update_page_scan(hdev); 2061 hci_req_update_scan(hdev);
2062 if (discov_changed) 2062 if (discov_changed)
2063 mgmt_update_adv_data(hdev); 2063 mgmt_update_adv_data(hdev);
2064 hci_update_background_scan(hdev); 2064 hci_update_background_scan(hdev);
@@ -2092,7 +2092,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
2092 return err; 2092 return err;
2093 2093
2094 if (changed) { 2094 if (changed) {
2095 hci_update_page_scan(hdev); 2095 hci_req_update_scan(hdev);
2096 hci_update_background_scan(hdev); 2096 hci_update_background_scan(hdev);
2097 return new_settings(hdev, sk); 2097 return new_settings(hdev, sk);
2098 } 2098 }
@@ -5041,7 +5041,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
5041 hci_req_init(&req, hdev); 5041 hci_req_init(&req, hdev);
5042 5042
5043 write_fast_connectable(&req, false); 5043 write_fast_connectable(&req, false);
5044 __hci_update_page_scan(&req); 5044 __hci_req_update_scan(&req);
5045 5045
5046 /* Since only the advertising data flags will change, there 5046 /* Since only the advertising data flags will change, there
5047 * is no need to update the scan response data. 5047 * is no need to update the scan response data.
@@ -5927,7 +5927,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
5927 if (err) 5927 if (err)
5928 goto unlock; 5928 goto unlock;
5929 5929
5930 hci_update_page_scan(hdev); 5930 hci_req_update_scan(hdev);
5931 5931
5932 goto added; 5932 goto added;
5933 } 5933 }
@@ -6024,7 +6024,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
6024 goto unlock; 6024 goto unlock;
6025 } 6025 }
6026 6026
6027 hci_update_page_scan(hdev); 6027 hci_req_update_scan(hdev);
6028 6028
6029 device_removed(sk, hdev, &cp->addr.bdaddr, 6029 device_removed(sk, hdev, &cp->addr.bdaddr,
6030 cp->addr.type); 6030 cp->addr.type);
@@ -6089,7 +6089,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
6089 kfree(b); 6089 kfree(b);
6090 } 6090 }
6091 6091
6092 hci_update_page_scan(hdev); 6092 hci_req_update_scan(hdev);
6093 6093
6094 list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { 6094 list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
6095 if (p->auto_connect == HCI_AUTO_CONN_DISABLED) 6095 if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -7397,7 +7397,7 @@ static int powered_update_hci(struct hci_dev *hdev)
7397 write_fast_connectable(&req, true); 7397 write_fast_connectable(&req, true);
7398 else 7398 else
7399 write_fast_connectable(&req, false); 7399 write_fast_connectable(&req, false);
7400 __hci_update_page_scan(&req); 7400 __hci_req_update_scan(&req);
7401 update_class(&req); 7401 update_class(&req);
7402 update_name(&req); 7402 update_name(&req);
7403 update_eir(&req); 7403 update_eir(&req);