]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: hci_sync: Attempt to dequeue connection attempt
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 13 Feb 2024 14:59:32 +0000 (09:59 -0500)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 6 Mar 2024 22:24:06 +0000 (17:24 -0500)
If connection is still queued/pending in the cmd_sync queue it means no
command has been generated and it should be safe to just dequeue the
callback when it is being aborted.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/hci_core.h
include/net/bluetooth/hci_sync.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_sync.c

index 2bdea85b7c447c178678da27ddf30aa88fefc371..317d495cfcf5e8cec777ce254dde2a0925fdb54e 100644 (file)
@@ -1083,6 +1083,24 @@ static inline unsigned int hci_conn_count(struct hci_dev *hdev)
        return c->acl_num + c->amp_num + c->sco_num + c->le_num + c->iso_num;
 }
 
+static inline bool hci_conn_valid(struct hci_dev *hdev, struct hci_conn *conn)
+{
+       struct hci_conn_hash *h = &hdev->conn_hash;
+       struct hci_conn  *c;
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(c, &h->list, list) {
+               if (c == conn) {
+                       rcu_read_unlock();
+                       return true;
+               }
+       }
+       rcu_read_unlock();
+
+       return false;
+}
+
 static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
@@ -1493,6 +1511,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
 struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                u8 dst_type, bool dst_resolved, u8 sec_level,
                                u16 conn_timeout, u8 role);
+void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status);
 struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
                                 u8 sec_level, u8 auth_type,
                                 enum conn_reasons conn_reason, u16 timeout);
index 4ff4aa68ee196d5e14dc09be5c14cce29ea24498..6a9d063e9f472dad520031f2b1ba4f1119731a72 100644 (file)
@@ -48,11 +48,11 @@ 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);
+int hci_cmd_sync_queue_once(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 *
 hci_cmd_sync_lookup_entry(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_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
-                           void *data, hci_cmd_sync_work_destroy_t destroy);
 void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
                               struct hci_cmd_sync_work_entry *entry);
 bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
@@ -139,8 +139,6 @@ struct hci_conn;
 
 int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
 
-int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
-
 int hci_le_create_cis_sync(struct hci_dev *hdev);
 
 int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
@@ -152,3 +150,7 @@ int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle);
 int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle);
 
 int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn);
+
+int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn);
+
+int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn);
index 587eb27f374c98dd3bce6e915f4b01b7cc1d107c..21e0b4064d05d6a235ce626ebc7dca9273b79922 100644 (file)
@@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = {
 };
 
 /* This function requires the caller holds hdev->lock */
-static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
+void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
 {
        struct hci_conn_params *params;
        struct hci_dev *hdev = conn->hdev;
@@ -1124,6 +1124,9 @@ void hci_conn_del(struct hci_conn *conn)
         * rest of hci_conn_del.
         */
        hci_conn_cleanup(conn);
+
+       /* Dequeue callbacks using connection pointer as data */
+       hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
 }
 
 struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type)
@@ -1258,53 +1261,6 @@ u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle)
        return 0;
 }
 
-static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
-{
-       struct hci_conn *conn;
-       u16 handle = PTR_UINT(data);
-
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
-       if (!conn)
-               return;
-
-       bt_dev_dbg(hdev, "err %d", err);
-
-       hci_dev_lock(hdev);
-
-       if (!err) {
-               hci_connect_le_scan_cleanup(conn, 0x00);
-               goto done;
-       }
-
-       /* Check if connection is still pending */
-       if (conn != hci_lookup_le_connect(hdev))
-               goto done;
-
-       /* Flush to make sure we send create conn cancel command if needed */
-       flush_delayed_work(&conn->le_conn_timeout);
-       hci_conn_failed(conn, bt_status(err));
-
-done:
-       hci_dev_unlock(hdev);
-}
-
-static int hci_connect_le_sync(struct hci_dev *hdev, void *data)
-{
-       struct hci_conn *conn;
-       u16 handle = PTR_UINT(data);
-
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
-       if (!conn)
-               return 0;
-
-       bt_dev_dbg(hdev, "conn %p", conn);
-
-       clear_bit(HCI_CONN_SCANNING, &conn->flags);
-       conn->state = BT_CONNECT;
-
-       return hci_le_create_conn_sync(hdev, conn);
-}
-
 struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                u8 dst_type, bool dst_resolved, u8 sec_level,
                                u16 conn_timeout, u8 role)
@@ -1371,9 +1327,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        conn->sec_level = BT_SECURITY_LOW;
        conn->conn_timeout = conn_timeout;
 
-       err = hci_cmd_sync_queue(hdev, hci_connect_le_sync,
-                                UINT_PTR(conn->handle),
-                                create_le_conn_complete);
+       err = hci_connect_le_sync(hdev, conn);
        if (err) {
                hci_conn_del(conn);
                return ERR_PTR(err);
@@ -2909,12 +2863,10 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
 
 static int abort_conn_sync(struct hci_dev *hdev, void *data)
 {
-       struct hci_conn *conn;
-       u16 handle = PTR_UINT(data);
+       struct hci_conn *conn = data;
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
-       if (!conn)
-               return 0;
+       if (!hci_conn_valid(hdev, conn))
+               return -ECANCELED;
 
        return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
 }
@@ -2949,8 +2901,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
                        hci_cmd_sync_cancel(hdev, -ECANCELED);
                        break;
                }
+       /* Cancel connect attempt if still queued/pending */
+       } else if (!hci_cancel_connect_sync(hdev, conn)) {
+               return 0;
        }
 
-       return hci_cmd_sync_queue(hdev, abort_conn_sync, UINT_PTR(conn->handle),
-                                 NULL);
+       return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL);
 }
index 5b314bf844f847976dc23e179bf47d4f496088ea..b7d8e99e2a30e0acc0a59266408a12a9fb8625ac 100644 (file)
@@ -6285,12 +6285,21 @@ static int hci_le_ext_create_conn_sync(struct hci_dev *hdev,
                                        conn->conn_timeout, NULL);
 }
 
-int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
+static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
 {
        struct hci_cp_le_create_conn cp;
        struct hci_conn_params *params;
        u8 own_addr_type;
        int err;
+       struct hci_conn *conn = data;
+
+       if (!hci_conn_valid(hdev, conn))
+               return -ECANCELED;
+
+       bt_dev_dbg(hdev, "conn %p", conn);
+
+       clear_bit(HCI_CONN_SCANNING, &conn->flags);
+       conn->state = BT_CONNECT;
 
        /* If requested to connect as peripheral use directed advertising */
        if (conn->role == HCI_ROLE_SLAVE) {
@@ -6611,16 +6620,11 @@ int hci_update_adv_data(struct hci_dev *hdev, u8 instance)
 
 static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
 {
-       struct hci_conn *conn;
-       u16 handle = PTR_UINT(data);
+       struct hci_conn *conn = data;
        struct inquiry_entry *ie;
        struct hci_cp_create_conn cp;
        int err;
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
-       if (!conn)
-               return 0;
-
        /* Many controllers disallow HCI Create Connection while it is doing
         * HCI Inquiry. So we cancel the Inquiry first before issuing HCI Create
         * Connection. This may cause the MGMT discovering state to become false
@@ -6679,6 +6683,58 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
 
 int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
 {
-       return hci_cmd_sync_queue(hdev, hci_acl_create_conn_sync,
-                                 UINT_PTR(conn->handle), NULL);
+       return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
+                                      NULL);
+}
+
+static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
+{
+       struct hci_conn *conn = data;
+
+       bt_dev_dbg(hdev, "err %d", err);
+
+       if (err == -ECANCELED)
+               return;
+
+       hci_dev_lock(hdev);
+
+       if (!err) {
+               hci_connect_le_scan_cleanup(conn, 0x00);
+               goto done;
+       }
+
+       /* Check if connection is still pending */
+       if (conn != hci_lookup_le_connect(hdev))
+               goto done;
+
+       /* Flush to make sure we send create conn cancel command if needed */
+       flush_delayed_work(&conn->le_conn_timeout);
+       hci_conn_failed(conn, bt_status(err));
+
+done:
+       hci_dev_unlock(hdev);
+}
+
+int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
+{
+       return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
+                                      create_le_conn_complete);
+}
+
+int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
+{
+       if (conn->state != BT_OPEN)
+               return -EINVAL;
+
+       switch (conn->type) {
+       case ACL_LINK:
+               return !hci_cmd_sync_dequeue_once(hdev,
+                                                 hci_acl_create_conn_sync,
+                                                 conn, NULL);
+       case LE_LINK:
+               return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
+                                                 conn, create_le_conn_complete);
+       }
+
+       return -ENOENT;
 }