Bluetooth: ISO: Avoid creating child socket if PA sync is terminating
authorIulia Tanasescu <iulia.tanasescu@nxp.com>
Tue, 5 Dec 2023 16:11:40 +0000 (18:11 +0200)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 22 Dec 2023 17:57:00 +0000 (12:57 -0500)
When a PA sync socket is closed, the associated hcon is also unlinked
and cleaned up. If there are no other hcons marked with the
HCI_CONN_PA_SYNC flag, HCI_OP_LE_PA_TERM_SYNC is sent to controller.

Between the time of the command and the moment PA sync is terminated
in controller, residual BIGInfo reports might continue to come.
This causes a new PA sync hcon to be added, and a new socket to be
notified to user space.

This commit fixs this by adding a flag on a Broadcast listening
socket to mark when the PA sync child has been closed.

This flag is checked when BIGInfo reports are indicated in
iso_connect_ind, to avoid recreating a hcon and socket if
residual reports arrive before PA sync is terminated.

Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/iso.c

index e49f00e8a6a622de4f9dfcef466b3a82724e2b22..04f6572d35f17ca4464243f87dc682077a41b220 100644 (file)
@@ -54,6 +54,7 @@ static void iso_sock_kill(struct sock *sk);
 enum {
        BT_SK_BIG_SYNC,
        BT_SK_PA_SYNC,
+       BT_SK_PA_SYNC_TERM,
 };
 
 struct iso_pinfo {
@@ -82,6 +83,11 @@ static bool iso_match_sid(struct sock *sk, void *data);
 static bool iso_match_sync_handle(struct sock *sk, void *data);
 static void iso_sock_disconn(struct sock *sk);
 
+typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
+
+static struct sock *iso_get_sock_listen(bdaddr_t *src, bdaddr_t *dst,
+                                       iso_sock_match_t match, void *data);
+
 /* ---- ISO timers ---- */
 #define ISO_CONN_TIMEOUT       (HZ * 40)
 #define ISO_DISCONN_TIMEOUT    (HZ * 2)
@@ -190,10 +196,21 @@ static void iso_chan_del(struct sock *sk, int err)
        sock_set_flag(sk, SOCK_ZAPPED);
 }
 
+static bool iso_match_conn_sync_handle(struct sock *sk, void *data)
+{
+       struct hci_conn *hcon = data;
+
+       if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
+               return false;
+
+       return hcon->sync_handle == iso_pi(sk)->sync_handle;
+}
+
 static void iso_conn_del(struct hci_conn *hcon, int err)
 {
        struct iso_conn *conn = hcon->iso_data;
        struct sock *sk;
+       struct sock *parent;
 
        if (!conn)
                return;
@@ -209,6 +226,25 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
 
        if (sk) {
                lock_sock(sk);
+
+               /* While a PA sync hcon is in the process of closing,
+                * mark parent socket with a flag, so that any residual
+                * BIGInfo adv reports that arrive before PA sync is
+                * terminated are not processed anymore.
+                */
+               if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
+                       parent = iso_get_sock_listen(&hcon->src,
+                                                    &hcon->dst,
+                                                    iso_match_conn_sync_handle,
+                                                    hcon);
+
+                       if (parent) {
+                               set_bit(BT_SK_PA_SYNC_TERM,
+                                       &iso_pi(parent)->flags);
+                               sock_put(parent);
+                       }
+               }
+
                iso_sock_clear_timer(sk);
                iso_chan_del(sk, err);
                release_sock(sk);
@@ -545,8 +581,6 @@ static struct sock *__iso_get_sock_listen_by_sid(bdaddr_t *ba, bdaddr_t *bc,
        return NULL;
 }
 
-typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
-
 /* Find socket listening:
  * source bdaddr (Unicast)
  * destination bdaddr (Broadcast only)
@@ -1888,9 +1922,20 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
                /* Try to get PA sync listening socket, if it exists */
                sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
                                                iso_match_pa_sync_flag, NULL);
-               if (!sk)
+
+               if (!sk) {
                        sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
                                                 iso_match_sync_handle, ev2);
+
+                       /* If PA Sync is in process of terminating,
+                        * do not handle any more BIGInfo adv reports.
+                        */
+
+                       if (sk && test_bit(BT_SK_PA_SYNC_TERM,
+                                          &iso_pi(sk)->flags))
+                               return lm;
+               }
+
                if (sk) {
                        int err;