diff mbox series

[2/2] Bluetooth: ISO: Send BIG Create Sync via hci_sync

Message ID 20241108160723.3399-3-iulia.tanasescu@nxp.com (mailing list archive)
State Superseded
Headers show
Series Bluetooth: ISO: PA/BIG sync fixes | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/CheckPatch success CheckPatch PASS
tedd_an/GitLint success Gitlint PASS
tedd_an/SubjectPrefix success Gitlint PASS

Commit Message

Iulia Tanasescu Nov. 8, 2024, 4:07 p.m. UTC
Before issuing the LE BIG Create Sync command, an available BIG handle
is chosen by iterating through the conn_hash list and finding the first
unused value.

If a BIG is terminated, the associated hcons are removed from the list
and the LE BIG Terminate Sync command is sent via hci_sync queue.
However, a new LE BIG Create sync command might be issued via
hci_send_cmd, before the previous BIG sync was terminated. This
can cause the same BIG handle to be reused and the LE BIG Create Sync
to fail with Command Disallowed.

< HCI Command: LE Broadcast Isochronous Group Create Sync (0x08|0x006b)
        BIG Handle: 0x00
        BIG Sync Handle: 0x0002
        Encryption: Unencrypted (0x00)
        Broadcast Code[16]: 00000000000000000000000000000000
        Maximum Number Subevents: 0x00
        Timeout: 20000 ms (0x07d0)
        Number of BIS: 1
        BIS ID: 0x01
> HCI Event: Command Status (0x0f) plen 4
      LE Broadcast Isochronous Group Create Sync (0x08|0x006b) ncmd 1
        Status: Command Disallowed (0x0c)
< HCI Command: LE Broadcast Isochronous Group Terminate Sync (0x08|0x006c)
        BIG Handle: 0x00

This commit fixes the ordering of the LE BIG Create Sync/LE BIG Terminate
Sync commands, to make sure that either the previous BIG sync is
terminated before reusing the handle, or that a new handle is chosen
for a new sync.

Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
---
 net/bluetooth/hci_conn.c | 19 ++++++++++++++++++-
 net/bluetooth/iso.c      |  4 ++++
 2 files changed, 22 insertions(+), 1 deletion(-)

Comments

Luiz Augusto von Dentz Nov. 8, 2024, 6 p.m. UTC | #1
Hi Iulia,

On Fri, Nov 8, 2024 at 11:07 AM Iulia Tanasescu <iulia.tanasescu@nxp.com> wrote:
>
> Before issuing the LE BIG Create Sync command, an available BIG handle
> is chosen by iterating through the conn_hash list and finding the first
> unused value.
>
> If a BIG is terminated, the associated hcons are removed from the list
> and the LE BIG Terminate Sync command is sent via hci_sync queue.
> However, a new LE BIG Create sync command might be issued via
> hci_send_cmd, before the previous BIG sync was terminated. This
> can cause the same BIG handle to be reused and the LE BIG Create Sync
> to fail with Command Disallowed.

We should be using hci_send_cmd anymore, so
hci_le_big_create_sync_pending shall be used whenever we want to queue
LE BIG Create Sync command, we shall also make it safe to be called
multiple times by using hci_cmd_sync_queue_once so it doesn't get
queued multiple times.

> < HCI Command: LE Broadcast Isochronous Group Create Sync (0x08|0x006b)
>         BIG Handle: 0x00
>         BIG Sync Handle: 0x0002
>         Encryption: Unencrypted (0x00)
>         Broadcast Code[16]: 00000000000000000000000000000000
>         Maximum Number Subevents: 0x00
>         Timeout: 20000 ms (0x07d0)
>         Number of BIS: 1
>         BIS ID: 0x01
> > HCI Event: Command Status (0x0f) plen 4
>       LE Broadcast Isochronous Group Create Sync (0x08|0x006b) ncmd 1
>         Status: Command Disallowed (0x0c)
> < HCI Command: LE Broadcast Isochronous Group Terminate Sync (0x08|0x006c)
>         BIG Handle: 0x00
>
> This commit fixes the ordering of the LE BIG Create Sync/LE BIG Terminate
> Sync commands, to make sure that either the previous BIG sync is
> terminated before reusing the handle, or that a new handle is chosen
> for a new sync.

If it is a fix of a previous change then we need to add the Fixes tag.
Regarding the order, hci_cmd_sync_queue also fix that since then the
command shall be executed in the order and in the situation where it
needs to be canceled we can do so with hci_cmd_sync_dequeue.

> Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
> ---
>  net/bluetooth/hci_conn.c | 19 ++++++++++++++++++-
>  net/bluetooth/iso.c      |  4 ++++
>  2 files changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index b5b78d469d54..ba74fac823c5 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -2180,11 +2180,20 @@ static bool hci_conn_check_create_big_sync(struct hci_conn *conn)
>         return true;
>  }
>
> -int hci_le_big_create_sync_pending(struct hci_dev *hdev)
> +static void big_create_sync_complete(struct hci_dev *hdev, void *data, int err)
> +{
> +       bt_dev_dbg(hdev, "");
> +
> +       if (err)
> +               bt_dev_err(hdev, "Unable to create BIG sync: %d", err);
> +}
> +
> +static int big_create_sync(struct hci_dev *hdev, void *data)
>  {
>         DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
>         struct hci_conn *conn;
>
> +       hci_dev_lock(hdev);

I'd add a comment why we are acquiring the dev lock here, sync
functions typically aren't allowed to do that except for minor updates
but never when you can sleep/yield waiting for something to complete.

>         rcu_read_lock();
>
>         pdu->num_bis = 0;
> @@ -2229,6 +2238,7 @@ int hci_le_big_create_sync_pending(struct hci_dev *hdev)
>
>  unlock:
>         rcu_read_unlock();
> +       hci_dev_unlock(hdev);
>
>         if (!pdu->num_bis)
>                 return 0;
> @@ -2237,6 +2247,13 @@ int hci_le_big_create_sync_pending(struct hci_dev *hdev)
>                             struct_size(pdu, bis, pdu->num_bis), pdu);
>  }
>
> +int hci_le_big_create_sync_pending(struct hci_dev *hdev)
> +{
> +       /* Queue big_create_sync */
> +       return hci_cmd_sync_queue(hdev, big_create_sync,
> +                                 NULL, big_create_sync_complete);
> +}
> +
>  int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
>                            struct bt_iso_qos *qos,
>                            __u16 sync_handle, __u8 num_bis, __u8 bis[])
> diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
> index 9e119da43147..ac1598b1e1b6 100644
> --- a/net/bluetooth/iso.c
> +++ b/net/bluetooth/iso.c
> @@ -1338,6 +1338,8 @@ static void iso_conn_big_sync(struct sock *sk)
>         if (!hdev)
>                 return;
>
> +       hci_dev_lock(hdev);

Same thing here, typically we don't use hci_dev_lock at the socket
layer as there is the likes of iso_conn to interface with HCI layer,
so we need to put some comments when there really is no other way and
hci_dev_lock must be used.

>         if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
>                 err = hci_le_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
>                                              &iso_pi(sk)->qos,
> @@ -1348,6 +1350,8 @@ static void iso_conn_big_sync(struct sock *sk)
>                         bt_dev_err(hdev, "hci_le_big_create_sync: %d",
>                                    err);
>         }
> +
> +       hci_dev_unlock(hdev);
>  }
>
>  static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
> --
> 2.43.0
>
Iulia Tanasescu Nov. 11, 2024, 11:48 a.m. UTC | #2
Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> Sent: Friday, November 8, 2024 8:01 PM
> To: Iulia Tanasescu <iulia.tanasescu@nxp.com>
> Cc: linux-bluetooth@vger.kernel.org; Claudia Cristina Draghicescu
> <claudia.rosu@nxp.com>; Mihai-Octavian Urzica <mihai-
> octavian.urzica@nxp.com>; Andrei Istodorescu
> <andrei.istodorescu@nxp.com>
> Subject: Re: [PATCH 2/2] Bluetooth: ISO: Send BIG Create Sync via
> hci_sync
> 
> Hi Iulia,
> 
> On Fri, Nov 8, 2024 at 11:07 AM Iulia Tanasescu <iulia.tanasescu@nxp.com>
> wrote:
> >
> > Before issuing the LE BIG Create Sync command, an available BIG handle
> > is chosen by iterating through the conn_hash list and finding the
> > first unused value.
> >
> > If a BIG is terminated, the associated hcons are removed from the list
> > and the LE BIG Terminate Sync command is sent via hci_sync queue.
> > However, a new LE BIG Create sync command might be issued via
> > hci_send_cmd, before the previous BIG sync was terminated. This can
> > cause the same BIG handle to be reused and the LE BIG Create Sync to
> > fail with Command Disallowed.
> 
> We should be using hci_send_cmd anymore, so
> hci_le_big_create_sync_pending shall be used whenever we want to queue LE
> BIG Create Sync command, we shall also make it safe to be called multiple
> times by using hci_cmd_sync_queue_once so it doesn't get queued multiple
> times.
> 
> > < HCI Command: LE Broadcast Isochronous Group Create Sync
> (0x08|0x006b)
> >         BIG Handle: 0x00
> >         BIG Sync Handle: 0x0002
> >         Encryption: Unencrypted (0x00)
> >         Broadcast Code[16]: 00000000000000000000000000000000
> >         Maximum Number Subevents: 0x00
> >         Timeout: 20000 ms (0x07d0)
> >         Number of BIS: 1
> >         BIS ID: 0x01
> > > HCI Event: Command Status (0x0f) plen 4
> >       LE Broadcast Isochronous Group Create Sync (0x08|0x006b) ncmd 1
> >         Status: Command Disallowed (0x0c) < HCI Command: LE Broadcast
> > Isochronous Group Terminate Sync (0x08|0x006c)
> >         BIG Handle: 0x00
> >
> > This commit fixes the ordering of the LE BIG Create Sync/LE BIG
> > Terminate Sync commands, to make sure that either the previous BIG
> > sync is terminated before reusing the handle, or that a new handle is
> > chosen for a new sync.
> 
> If it is a fix of a previous change then we need to add the Fixes tag.
> Regarding the order, hci_cmd_sync_queue also fix that since then the
> command shall be executed in the order and in the situation where it needs to
> be canceled we can do so with hci_cmd_sync_dequeue.
> 
> > Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
> > ---
> >  net/bluetooth/hci_conn.c | 19 ++++++++++++++++++-
> >  net/bluetooth/iso.c      |  4 ++++
> >  2 files changed, 22 insertions(+), 1 deletion(-)
> >
> > diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index
> > b5b78d469d54..ba74fac823c5 100644
> > --- a/net/bluetooth/hci_conn.c
> > +++ b/net/bluetooth/hci_conn.c
> > @@ -2180,11 +2180,20 @@ static bool
> hci_conn_check_create_big_sync(struct hci_conn *conn)
> >         return true;
> >  }
> >
> > -int hci_le_big_create_sync_pending(struct hci_dev *hdev)
> > +static void big_create_sync_complete(struct hci_dev *hdev, void
> > +*data, int err) {
> > +       bt_dev_dbg(hdev, "");
> > +
> > +       if (err)
> > +               bt_dev_err(hdev, "Unable to create BIG sync: %d",
> > +err); }
> > +
> > +static int big_create_sync(struct hci_dev *hdev, void *data)
> >  {
> >         DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis,
> 0x11);
> >         struct hci_conn *conn;
> >
> > +       hci_dev_lock(hdev);
> 
> I'd add a comment why we are acquiring the dev lock here, sync functions
> typically aren't allowed to do that except for minor updates but never when
> you can sleep/yield waiting for something to complete.

I added hci_dev_lock here thinking that it might be necessary to protect
iterating through hdev->conn_hash.list, but I think rcu_read_lock and
list_for_each_entry_rcu already offer the required protection, so I
removed hci_dev_lock.

However, I think hci_dev_lock is necessary in iso.c, to protect
hci_le_big_create_sync_pending, since hci_cmd_sync_queue_once checks
hdev flags which might change. I added a comment to explain this.

> 
> >         rcu_read_lock();
> >
> >         pdu->num_bis = 0;
> > @@ -2229,6 +2238,7 @@ int hci_le_big_create_sync_pending(struct
> > hci_dev *hdev)
> >
> >  unlock:
> >         rcu_read_unlock();
> > +       hci_dev_unlock(hdev);
> >
> >         if (!pdu->num_bis)
> >                 return 0;
> > @@ -2237,6 +2247,13 @@ int hci_le_big_create_sync_pending(struct
> hci_dev *hdev)
> >                             struct_size(pdu, bis, pdu->num_bis), pdu);
> > }
> >
> > +int hci_le_big_create_sync_pending(struct hci_dev *hdev) {
> > +       /* Queue big_create_sync */
> > +       return hci_cmd_sync_queue(hdev, big_create_sync,
> > +                                 NULL, big_create_sync_complete); }
> > +
> >  int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
> >                            struct bt_iso_qos *qos,
> >                            __u16 sync_handle, __u8 num_bis, __u8
> > bis[]) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index
> > 9e119da43147..ac1598b1e1b6 100644
> > --- a/net/bluetooth/iso.c
> > +++ b/net/bluetooth/iso.c
> > @@ -1338,6 +1338,8 @@ static void iso_conn_big_sync(struct sock *sk)
> >         if (!hdev)
> >                 return;
> >
> > +       hci_dev_lock(hdev);
> 
> Same thing here, typically we don't use hci_dev_lock at the socket layer as
> there is the likes of iso_conn to interface with HCI layer, so we need to put
> some comments when there really is no other way and hci_dev_lock must be
> used.
> 
> >         if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
> >                 err = hci_le_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
> >                                              &iso_pi(sk)->qos, @@
> > -1348,6 +1350,8 @@ static void iso_conn_big_sync(struct sock *sk)
> >                         bt_dev_err(hdev, "hci_le_big_create_sync: %d",
> >                                    err);
> >         }
> > +
> > +       hci_dev_unlock(hdev);
> >  }
> >
> >  static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
> > --
> > 2.43.0
> >
> 
> 
> --
> Luiz Augusto von Dentz

Regards,
Iulia
diff mbox series

Patch

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b5b78d469d54..ba74fac823c5 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2180,11 +2180,20 @@  static bool hci_conn_check_create_big_sync(struct hci_conn *conn)
 	return true;
 }
 
-int hci_le_big_create_sync_pending(struct hci_dev *hdev)
+static void big_create_sync_complete(struct hci_dev *hdev, void *data, int err)
+{
+	bt_dev_dbg(hdev, "");
+
+	if (err)
+		bt_dev_err(hdev, "Unable to create BIG sync: %d", err);
+}
+
+static int big_create_sync(struct hci_dev *hdev, void *data)
 {
 	DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
 	struct hci_conn *conn;
 
+	hci_dev_lock(hdev);
 	rcu_read_lock();
 
 	pdu->num_bis = 0;
@@ -2229,6 +2238,7 @@  int hci_le_big_create_sync_pending(struct hci_dev *hdev)
 
 unlock:
 	rcu_read_unlock();
+	hci_dev_unlock(hdev);
 
 	if (!pdu->num_bis)
 		return 0;
@@ -2237,6 +2247,13 @@  int hci_le_big_create_sync_pending(struct hci_dev *hdev)
 			    struct_size(pdu, bis, pdu->num_bis), pdu);
 }
 
+int hci_le_big_create_sync_pending(struct hci_dev *hdev)
+{
+	/* Queue big_create_sync */
+	return hci_cmd_sync_queue(hdev, big_create_sync,
+				  NULL, big_create_sync_complete);
+}
+
 int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
 			   struct bt_iso_qos *qos,
 			   __u16 sync_handle, __u8 num_bis, __u8 bis[])
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 9e119da43147..ac1598b1e1b6 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -1338,6 +1338,8 @@  static void iso_conn_big_sync(struct sock *sk)
 	if (!hdev)
 		return;
 
+	hci_dev_lock(hdev);
+
 	if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
 		err = hci_le_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
 					     &iso_pi(sk)->qos,
@@ -1348,6 +1350,8 @@  static void iso_conn_big_sync(struct sock *sk)
 			bt_dev_err(hdev, "hci_le_big_create_sync: %d",
 				   err);
 	}
+
+	hci_dev_unlock(hdev);
 }
 
 static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,