[PATCH v2 08/16] Bluetooth: Fix stop_discovery()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



According to the Bluetooth spec, the inquiry cancel command should
only be issued after the inquiry command has been issued, a command
status event has been received for the inquiry command, and before
the inquiry complete event occurs.

As HCI_INQUIRY flag is only set just after an inquiry command status
event occurs and it is cleared just after an inquiry complete event
occurs, the inquiry cancel command should be issued only if HCI_INQUIRY
flag is set.

This spec constraint raises two possible race condition we must handle.

1. A MGMT_OP_STOP_DISCOVERY command is issued just after a
   MGMT_OP_START_DISCOVERY. The controller didn't send the inquiry
   command status yet therefore the HCI_INQUIRY flag is not set. Thus,
   stop_discovery() will send no inquiry cancel command and the
   discovery procedure won't be stopped. This race is addressed by
   checking for pending MGMT_OP_STOP_DISCOVERY command in inquiry
   command status event handler.

2. While the MGMT_OP_STOP_DISCOVERY is being processed the controller
   sends the inquiry complete event and the HCI_INQUIRY flag is
   cleared. stop_discovery() will add a pending MGMT_OP_STOP_DISCOVERY
   command which won't be removed since there is no ongoing discovery.
   This race is addressed by synchronizing stop_discovery and inquiry
   complete event handler threads.

Signed-off-by: Andre Guedes <andre.guedes@xxxxxxxxxxxxx>
---
 include/net/bluetooth/hci_core.h |    2 ++
 net/bluetooth/hci_event.c        |   10 ++++++++++
 net/bluetooth/mgmt.c             |   25 ++++++++++++++++++++++++-
 3 files changed, 36 insertions(+), 1 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 0ccd724..1ff59f2 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -861,6 +861,8 @@ int mgmt_start_discovery_complete(u16 index);
 int mgmt_start_discovery_failed(u16 index, u8 status);
 int mgmt_stop_discovery_complete(u16 index);
 int mgmt_stop_discovery_failed(u16 index, u8 status);
+int mgmt_has_pending_stop_discov(u16 index);
+int mgmt_cancel_discovery(u16 index);
 
 /* HCI info for socket */
 #define hci_pi(sk) ((struct hci_pinfo *) sk)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index ea9e105..c75211c 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -963,6 +963,11 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 
 	set_bit(HCI_INQUIRY, &hdev->flags);
 
+	if (mgmt_has_pending_stop_discov(hdev->id)) {
+		mgmt_cancel_discovery(hdev->id);
+		return;
+	}
+
 	mgmt_discovering(hdev->id, 1);
 }
 
@@ -1356,7 +1361,12 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
 	hci_req_complete(hdev, HCI_OP_INQUIRY, status);
 
 	mgmt_discovering(hdev->id, 0);
+
+	hci_dev_lock(hdev);
+
 	mgmt_start_discovery_complete(hdev->id);
+
+	hci_dev_unlock(hdev);
 }
 
 static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index e43940e..bbb0daa 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1670,6 +1670,21 @@ static int cancel_inquiry(struct hci_dev *hdev)
 	return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
 }
 
+int mgmt_cancel_discovery(u16 index)
+{
+	struct hci_dev *hdev;
+	int res = 0;
+
+	hdev = hci_dev_get(index);
+
+	if (test_bit(HCI_INQUIRY, &hdev->flags))
+		res = cancel_inquiry(hdev);
+
+	hci_dev_put(hdev);
+
+	return res;
+}
+
 static int stop_discovery(struct sock *sk, u16 index)
 {
 	struct hci_dev *hdev;
@@ -1701,7 +1716,7 @@ static int stop_discovery(struct sock *sk, u16 index)
 		goto failed;
 	}
 
-	err = cancel_inquiry(hdev);
+	err = mgmt_cancel_discovery(index);
 	if (err < 0)
 		mgmt_pending_remove(cmd);
 
@@ -2393,3 +2408,11 @@ int mgmt_stop_discovery_failed(u16 index, u8 status)
 
 	return err;
 }
+
+int mgmt_has_pending_stop_discov(u16 index)
+{
+	if (mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index))
+		return 1;
+
+	return 0;
+}
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux