diff mbox series

[1/1] Bluetooth: ISO: Notify user space about failed bis connections

Message ID 20230623073842.16466-2-iulia.tanasescu@nxp.com (mailing list archive)
State Superseded
Headers show
Series Bluetooth: ISO: Notify user space about failed bis connections | 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
tedd_an/BuildKernel success BuildKernel PASS
tedd_an/CheckAllWarning success CheckAllWarning PASS
tedd_an/CheckSparse warning CheckSparse WARNING net/bluetooth/hci_event.c: note: in included file (through include/net/bluetooth/hci_core.h):
tedd_an/CheckSmatch warning CheckSparse WARNING net/bluetooth/hci_event.c: note: in included file (through include/net/bluetooth/hci_core.h):
tedd_an/BuildKernel32 success BuildKernel32 PASS
tedd_an/TestRunnerSetup success TestRunnerSetup PASS
tedd_an/TestRunner_l2cap-tester success TestRunner PASS
tedd_an/TestRunner_iso-tester fail TestRunner_iso-tester: Total: 80, Passed: 76 (95.0%), Failed: 4, Not Run: 0
tedd_an/TestRunner_bnep-tester success TestRunner PASS
tedd_an/TestRunner_mgmt-tester success TestRunner PASS
tedd_an/TestRunner_rfcomm-tester success TestRunner PASS
tedd_an/TestRunner_sco-tester success TestRunner PASS
tedd_an/TestRunner_ioctl-tester success TestRunner PASS
tedd_an/TestRunner_mesh-tester success TestRunner PASS
tedd_an/TestRunner_smp-tester success TestRunner PASS
tedd_an/TestRunner_userchan-tester success TestRunner PASS
tedd_an/IncrementalBuild success Incremental Build PASS

Commit Message

Iulia Tanasescu June 23, 2023, 7:38 a.m. UTC
Some use cases require the user to be informed if BIG synchronization
fails. This commit makes it so that even if the BIG sync established
event arrives with error status, a new hconn is added for each BIS,
and the iso layer is notified about the failed connections.

Unsuccesful bis connections will be marked using the BT_BIG_SYNC_FAILED
state. From the iso layer, the POLLERR event is triggered on the newly
allocated bis sockets, before adding them to the accept list of the
parent socket.

From user space, a new fd for each failed bis connection will be
obtained by calling accept. The user should check for the POLLERR
event on the new socket, to determine if the connection was successful
or not.

To ensure proper cleanup of the unsuccessful bises, the le_bis_cleanup
work callback has been added, similar to le_scan_cleanup. The callback
deletes the hci connection and notifies the disconnect to the iso layer,
so the socket is also cleaned up.

The HCI_CONN_BIG_SYNC flag has been added to mark whether the BIG sync
has been successfully established. This flag is checked at bis cleanup,
so the HCI LE BIG Terminate Sync command is only issued if needed.

The BT_SK_BIG_SYNC flag indicates if BIG create sync has been called
for a listening socket, to avoid issuing the command everytime a BIGInfo
advertising report is received.

Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
---
 include/net/bluetooth/bluetooth.h |  5 +-
 include/net/bluetooth/hci_core.h  | 25 ++++++++++
 net/bluetooth/hci_conn.c          | 78 +++++++++++++++++++++++++++++--
 net/bluetooth/hci_event.c         | 21 +++++++--
 net/bluetooth/iso.c               | 37 ++++++++++-----
 5 files changed, 145 insertions(+), 21 deletions(-)

Comments

bluez.test.bot@gmail.com June 23, 2023, 8:44 a.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=759714

---Test result---

Test Summary:
CheckPatch                    PASS      2.05 seconds
GitLint                       PASS      0.40 seconds
SubjectPrefix                 PASS      0.14 seconds
BuildKernel                   PASS      46.50 seconds
CheckAllWarning               PASS      50.56 seconds
CheckSparse                   WARNING   58.56 seconds
CheckSmatch                   WARNING   158.23 seconds
BuildKernel32                 PASS      44.82 seconds
TestRunnerSetup               PASS      635.79 seconds
TestRunner_l2cap-tester       PASS      22.48 seconds
TestRunner_iso-tester         FAIL      33.86 seconds
TestRunner_bnep-tester        PASS      8.03 seconds
TestRunner_mgmt-tester        PASS      171.90 seconds
TestRunner_rfcomm-tester      PASS      12.62 seconds
TestRunner_sco-tester         PASS      11.53 seconds
TestRunner_ioctl-tester       PASS      13.73 seconds
TestRunner_mesh-tester        PASS      10.09 seconds
TestRunner_smp-tester         PASS      11.44 seconds
TestRunner_userchan-tester    PASS      8.40 seconds
IncrementalBuild              PASS      42.04 seconds

Details
##############################
Test: CheckSparse - WARNING
Desc: Run sparse tool with linux kernel
Output:
net/bluetooth/hci_event.c: note: in included file (through include/net/bluetooth/hci_core.h):
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
net/bluetooth/hci_event.c: note: in included file (through include/net/bluetooth/hci_core.h):
##############################
Test: TestRunner_iso-tester - FAIL
Desc: Run iso-tester with test-runner
Output:
Total: 80, Passed: 76 (95.0%), Failed: 4, Not Run: 0

Failed Test Cases
ISO Receive - Success                                Failed       0.286 seconds
ISO Receive Timestamped - Success                    Failed       0.269 seconds
ISO Defer Receive - Success                          Failed       0.293 seconds
ISO 48_2_1 Defer Receive - Success                   Failed       0.284 seconds


---
Regards,
Linux Bluetooth
Luiz Augusto von Dentz June 26, 2023, 7:50 p.m. UTC | #2
Hi Iulia,

On Fri, Jun 23, 2023 at 12:43 AM Iulia Tanasescu
<iulia.tanasescu@nxp.com> wrote:
>
> Some use cases require the user to be informed if BIG synchronization
> fails. This commit makes it so that even if the BIG sync established
> event arrives with error status, a new hconn is added for each BIS,
> and the iso layer is notified about the failed connections.
>
> Unsuccesful bis connections will be marked using the BT_BIG_SYNC_FAILED
> state. From the iso layer, the POLLERR event is triggered on the newly
> allocated bis sockets, before adding them to the accept list of the
> parent socket.
>
> From user space, a new fd for each failed bis connection will be
> obtained by calling accept. The user should check for the POLLERR
> event on the new socket, to determine if the connection was successful
> or not.
>
> To ensure proper cleanup of the unsuccessful bises, the le_bis_cleanup
> work callback has been added, similar to le_scan_cleanup. The callback
> deletes the hci connection and notifies the disconnect to the iso layer,
> so the socket is also cleaned up.
>
> The HCI_CONN_BIG_SYNC flag has been added to mark whether the BIG sync
> has been successfully established. This flag is checked at bis cleanup,
> so the HCI LE BIG Terminate Sync command is only issued if needed.
>
> The BT_SK_BIG_SYNC flag indicates if BIG create sync has been called
> for a listening socket, to avoid issuing the command everytime a BIGInfo
> advertising report is received.
>
> Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
> ---
>  include/net/bluetooth/bluetooth.h |  5 +-
>  include/net/bluetooth/hci_core.h  | 25 ++++++++++
>  net/bluetooth/hci_conn.c          | 78 +++++++++++++++++++++++++++++--
>  net/bluetooth/hci_event.c         | 21 +++++++--
>  net/bluetooth/iso.c               | 37 ++++++++++-----
>  5 files changed, 145 insertions(+), 21 deletions(-)
>
> diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
> index 34998ae8ed78..b05147cf4c57 100644
> --- a/include/net/bluetooth/bluetooth.h
> +++ b/include/net/bluetooth/bluetooth.h
> @@ -292,7 +292,8 @@ enum {
>         BT_CONNECT2,
>         BT_CONFIG,
>         BT_DISCONN,
> -       BT_CLOSED
> +       BT_CLOSED,
> +       BT_BIG_SYNC_FAILED,

I would mess around with these states, they are generic for all sockets.

>  };
>
>  /* If unused will be removed by compiler */
> @@ -317,6 +318,8 @@ static inline const char *state_to_string(int state)
>                 return "BT_DISCONN";
>         case BT_CLOSED:
>                 return "BT_CLOSED";
> +       case BT_BIG_SYNC_FAILED:
> +               return "BT_BIG_SYNC_FAILED";
>         }
>
>         return "invalid state";
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 05a9b3ab3f56..81e83ecdd267 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -761,6 +761,7 @@ struct hci_conn {
>         struct delayed_work idle_work;
>         struct delayed_work le_conn_timeout;
>         struct work_struct  le_scan_cleanup;
> +       struct work_struct  le_bis_cleanup;

Also not a fan of adding yet another work, le_scan_cleanup already
have its own problems:

https://patchwork.kernel.org/project/bluetooth/patch/45455ee45ccb3313618a48c01be714e14d372257.1687525956.git.pav@iki.fi/

In fact Im not sure why we couldn't use conn->cleanup?

>
>         struct device   dev;
>         struct dentry   *debugfs;
> @@ -978,6 +979,7 @@ enum {
>         HCI_CONN_PER_ADV,
>         HCI_CONN_BIG_CREATED,
>         HCI_CONN_CREATE_CIS,
> +       HCI_CONN_BIG_SYNC,
>  };
>
>  static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
> @@ -1288,6 +1290,29 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
>         return NULL;
>  }
>
> +static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *hdev,
> +                                                       __u8 handle)
> +{
> +       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->type != ISO_LINK)
> +                       continue;
> +
> +               if (handle == c->iso_qos.bcast.big) {
> +                       rcu_read_unlock();
> +                       return c;
> +               }
> +       }
> +
> +       rcu_read_unlock();
> +
> +       return NULL;
> +}
> +
>  static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
>                                                         __u8 type, __u16 state)
>  {
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 62a7ccfdfe63..e4aa7731b987 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -207,6 +207,36 @@ static void le_scan_cleanup(struct work_struct *work)
>         hci_conn_put(conn);
>  }
>
> +static void le_bis_cleanup(struct work_struct *work)
> +{
> +       struct hci_conn *conn = container_of(work, struct hci_conn,
> +                                            le_bis_cleanup);
> +       struct hci_dev *hdev = conn->hdev;
> +       struct hci_conn *c = NULL;
> +
> +       BT_DBG("%s hcon %p", hdev->name, conn);
> +
> +       hci_dev_lock(hdev);
> +
> +       /* Check that the hci_conn is still around */
> +       rcu_read_lock();
> +       list_for_each_entry_rcu(c, &hdev->conn_hash.list, list) {
> +               if (c == conn)
> +                       break;
> +       }
> +       rcu_read_unlock();
> +
> +       if (c == conn) {
> +               conn->state = BT_CLOSED;
> +               hci_disconn_cfm(conn, hci_proto_disconn_ind(conn));
> +               hci_conn_del(conn);
> +       }
> +
> +       hci_dev_unlock(hdev);
> +       hci_dev_put(hdev);
> +       hci_conn_put(conn);
> +}
> +
>  static void hci_connect_le_scan_remove(struct hci_conn *conn)
>  {
>         BT_DBG("%s hcon %p", conn->hdev->name, conn);
> @@ -229,6 +259,28 @@ static void hci_connect_le_scan_remove(struct hci_conn *conn)
>         schedule_work(&conn->le_scan_cleanup);
>  }
>
> +static void hci_bis_remove(struct hci_conn *conn)
> +{
> +       BT_DBG("%s hcon %p", conn->hdev->name, conn);
> +
> +       /* We can't call hci_conn_del/hci_conn_cleanup here since that
> +        * could deadlock with another hci_conn_del() call that's holding
> +        * hci_dev_lock and doing cancel_delayed_work_sync(&conn->disc_work).
> +        * Instead, grab temporary extra references to the hci_dev and
> +        * hci_conn and perform the necessary cleanup in a separate work
> +        * callback.
> +        */
> +
> +       hci_dev_hold(conn->hdev);
> +       hci_conn_get(conn);
> +
> +       /* Even though we hold a reference to the hdev, many other
> +        * things might get cleaned up meanwhile, including the hdev's
> +        * own workqueue, so we can't use that for scheduling.
> +        */
> +       schedule_work(&conn->le_bis_cleanup);
> +}

This is a bad idea, we are already dealing with the same shortcomings
with respect to scan le, what we could probably do is to defer
queue_sync, but I'd wait until we find a proper solution to scan so we
apply here as well.

>  static void hci_acl_create_connection(struct hci_conn *conn)
>  {
>         struct hci_dev *hdev = conn->hdev;
> @@ -686,6 +738,12 @@ static void hci_conn_timeout(struct work_struct *work)
>                 return;
>         }
>
> +       /* Cleanup bises that failed to be established */
> +       if (conn->state == BT_BIG_SYNC_FAILED) {
> +               hci_bis_remove(conn);
> +               return;
> +       }
> +
>         hci_abort_conn(conn, hci_proto_disconn_ind(conn));
>  }
>
> @@ -793,6 +851,7 @@ struct iso_list_data {
>         int count;
>         struct iso_cig_params pdu;
>         bool big_term;
> +       bool big_sync_term;
>  };
>
>  static void bis_list(struct hci_conn *conn, void *data)
> @@ -880,24 +939,26 @@ static int big_terminate_sync(struct hci_dev *hdev, void *data)
>         if (d->count)
>                 return 0;
>
> -       hci_le_big_terminate_sync(hdev, d->big);
> +       if (d->big_sync_term)
> +               hci_le_big_terminate_sync(hdev, d->big);
>
>         return hci_le_pa_terminate_sync(hdev, d->sync_handle);
>  }
>
> -static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
> +static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *conn)
>  {
>         struct iso_list_data *d;
>         int ret;
>
> -       bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle);
> +       bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, conn->sync_handle);
>
>         d = kzalloc(sizeof(*d), GFP_KERNEL);
>         if (!d)
>                 return -ENOMEM;
>
>         d->big = big;
> -       d->sync_handle = sync_handle;
> +       d->sync_handle = conn->sync_handle;
> +       d->big_sync_term = test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags);
>
>         ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
>                                  terminate_big_destroy);
> @@ -933,8 +994,14 @@ static void bis_cleanup(struct hci_conn *conn)
>
>                 hci_le_terminate_big(hdev, conn);
>         } else {
> +               bis = hci_conn_hash_lookup_big_any_dst(hdev,
> +                                                      conn->iso_qos.bcast.big);
> +
> +               if (bis)
> +                       return;
> +
>                 hci_le_big_terminate(hdev, conn->iso_qos.bcast.big,
> -                                    conn->sync_handle);
> +                                    conn);
>         }
>  }
>
> @@ -1067,6 +1134,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
>         INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
>         INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
>         INIT_WORK(&conn->le_scan_cleanup, le_scan_cleanup);
> +       INIT_WORK(&conn->le_bis_cleanup, le_bis_cleanup);
>
>         atomic_set(&conn->refcnt, 0);
>
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index b1aefe4bb751..5629a118ece4 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -7020,9 +7020,6 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
>                                 flex_array_size(ev, bis, ev->num_bis)))
>                 return;
>
> -       if (ev->status)
> -               return;
> -
>         hci_dev_lock(hdev);
>
>         for (i = 0; i < ev->num_bis; i++) {
> @@ -7046,9 +7043,25 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
>                 bis->iso_qos.bcast.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
>                 bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu);
>
> -               hci_iso_setup_path(bis);
> +               if (!ev->status) {
> +                       set_bit(HCI_CONN_BIG_SYNC, &bis->flags);
> +                       hci_iso_setup_path(bis);
> +               }
>         }
>
> +       /* In case BIG sync failed, notify each failed connection to
> +        * the user after all hci connections have been added
> +        */
> +       if (ev->status)
> +               for (i = 0; i < ev->num_bis; i++) {
> +                       u16 handle = le16_to_cpu(ev->bis[i]);
> +
> +                       bis = hci_conn_hash_lookup_handle(hdev, handle);
> +
> +                       bis->state = BT_BIG_SYNC_FAILED;
> +                       hci_connect_cfm(bis, ev->status);
> +               }
> +
>         hci_dev_unlock(hdev);
>  }
>
> diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
> index 84d238d0639a..a2571d8ea055 100644
> --- a/net/bluetooth/iso.c
> +++ b/net/bluetooth/iso.c
> @@ -48,6 +48,11 @@ static void iso_sock_kill(struct sock *sk);
>  #define EIR_SERVICE_DATA_LENGTH 4
>  #define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH)
>
> +/* iso_pinfo flags values */
> +enum {
> +       BT_SK_BIG_SYNC,
> +};
> +
>  struct iso_pinfo {
>         struct bt_sock          bt;
>         bdaddr_t                src;
> @@ -58,7 +63,7 @@ struct iso_pinfo {
>         __u8                    bc_num_bis;
>         __u8                    bc_bis[ISO_MAX_NUM_BIS];
>         __u16                   sync_handle;
> -       __u32                   flags;
> +       unsigned long           flags;
>         struct bt_iso_qos       qos;
>         bool                    qos_user_set;
>         __u8                    base_len;
> @@ -1569,6 +1574,12 @@ static void iso_conn_ready(struct iso_conn *conn)
>                 hci_conn_hold(hcon);
>                 iso_chan_add(conn, sk, parent);
>
> +               if (ev && ((struct hci_evt_le_big_sync_estabilished *)ev)->status) {
> +                       /* Trigger error signal on chilld socket */
> +                       sk->sk_err = ECONNREFUSED;
> +                       sk->sk_error_report(sk);
> +               }
> +
>                 if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
>                         sk->sk_state = BT_CONNECT2;
>                 else
> @@ -1637,15 +1648,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
>                         if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
>                                 iso_pi(sk)->bc_num_bis = ev2->num_bis;
>
> -                       err = hci_le_big_create_sync(hdev,
> -                                                    &iso_pi(sk)->qos,
> -                                                    iso_pi(sk)->sync_handle,
> -                                                    iso_pi(sk)->bc_num_bis,
> -                                                    iso_pi(sk)->bc_bis);
> -                       if (err) {
> -                               bt_dev_err(hdev, "hci_le_big_create_sync: %d",
> -                                          err);
> -                               sk = NULL;
> +                       if (!test_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
> +                               set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags);
> +
> +                               err = hci_le_big_create_sync(hdev,
> +                                                            &iso_pi(sk)->qos,
> +                                                            iso_pi(sk)->sync_handle,
> +                                                            iso_pi(sk)->bc_num_bis,
> +                                                            iso_pi(sk)->bc_bis);
> +                               if (err) {
> +                                       bt_dev_err(hdev, "hci_le_big_create_sync: %d",
> +                                                  err);
> +                                       sk = NULL;
> +                               }
>                         }
>                 }
>         } else {
> @@ -1688,7 +1703,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
>
>         BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
>
> -       if (!status) {
> +       if (!status || hcon->state == BT_BIG_SYNC_FAILED) {
>                 struct iso_conn *conn;
>
>                 conn = iso_conn_add(hcon);
> --
> 2.34.1
>
Iulia Tanasescu June 28, 2023, 3:10 p.m. UTC | #3
Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> Sent: Monday, June 26, 2023 10:51 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>; Silviu Florian Barbulescu
> <silviu.barbulescu@nxp.com>; Vlad Pruteanu <vlad.pruteanu@nxp.com>;
> Andrei Istodorescu <andrei.istodorescu@nxp.com>
> Subject: Re: [PATCH 1/1] Bluetooth: ISO: Notify user space about failed
> bis connections
> 
> Hi Iulia,
> 
> On Fri, Jun 23, 2023 at 12:43AM Iulia Tanasescu <iulia.tanasescu@nxp.com>
> wrote:
> >
> > Some use cases require the user to be informed if BIG synchronization
> > fails. This commit makes it so that even if the BIG sync established
> > event arrives with error status, a new hconn is added for each BIS,
> > and the iso layer is notified about the failed connections.
> >
> > Unsuccesful bis connections will be marked using the
> > BT_BIG_SYNC_FAILED state. From the iso layer, the POLLERR event is
> > triggered on the newly allocated bis sockets, before adding them to
> > the accept list of the parent socket.
> >
> > From user space, a new fd for each failed bis connection will be
> > obtained by calling accept. The user should check for the POLLERR
> > event on the new socket, to determine if the connection was successful
> > or not.
> >
> > To ensure proper cleanup of the unsuccessful bises, the le_bis_cleanup
> > work callback has been added, similar to le_scan_cleanup. The callback
> > deletes the hci connection and notifies the disconnect to the iso
> > layer, so the socket is also cleaned up.
> >
> > The HCI_CONN_BIG_SYNC flag has been added to mark whether the BIG
> sync
> > has been successfully established. This flag is checked at bis
> > cleanup, so the HCI LE BIG Terminate Sync command is only issued if
> needed.
> >
> > The BT_SK_BIG_SYNC flag indicates if BIG create sync has been called
> > for a listening socket, to avoid issuing the command everytime a
> > BIGInfo advertising report is received.
> >
> > Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
> > ---
> >  include/net/bluetooth/bluetooth.h |  5 +-
> > include/net/bluetooth/hci_core.h  | 25 ++++++++++
> >  net/bluetooth/hci_conn.c          | 78 +++++++++++++++++++++++++++++--
> >  net/bluetooth/hci_event.c         | 21 +++++++--
> >  net/bluetooth/iso.c               | 37 ++++++++++-----
> >  5 files changed, 145 insertions(+), 21 deletions(-)
> >
> > diff --git a/include/net/bluetooth/bluetooth.h
> > b/include/net/bluetooth/bluetooth.h
> > index 34998ae8ed78..b05147cf4c57 100644
> > --- a/include/net/bluetooth/bluetooth.h
> > +++ b/include/net/bluetooth/bluetooth.h
> > @@ -292,7 +292,8 @@ enum {
> >         BT_CONNECT2,
> >         BT_CONFIG,
> >         BT_DISCONN,
> > -       BT_CLOSED
> > +       BT_CLOSED,
> > +       BT_BIG_SYNC_FAILED,
> 
> I would mess around with these states, they are generic for all sockets.
> 
> >  };
> >
> >  /* If unused will be removed by compiler */ @@ -317,6 +318,8 @@
> > static inline const char *state_to_string(int state)
> >                 return "BT_DISCONN";
> >         case BT_CLOSED:
> >                 return "BT_CLOSED";
> > +       case BT_BIG_SYNC_FAILED:
> > +               return "BT_BIG_SYNC_FAILED";
> >         }
> >
> >         return "invalid state";
> > diff --git a/include/net/bluetooth/hci_core.h
> > b/include/net/bluetooth/hci_core.h
> > index 05a9b3ab3f56..81e83ecdd267 100644
> > --- a/include/net/bluetooth/hci_core.h
> > +++ b/include/net/bluetooth/hci_core.h
> > @@ -761,6 +761,7 @@ struct hci_conn {
> >         struct delayed_work idle_work;
> >         struct delayed_work le_conn_timeout;
> >         struct work_struct  le_scan_cleanup;
> > +       struct work_struct  le_bis_cleanup;
> 
> Also not a fan of adding yet another work, le_scan_cleanup already have its
> own problems:
> 
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatch
> work.kernel.org%2Fproject%2Fbluetooth%2Fpatch%2F45455ee45ccb3313618
> a48c01be714e14d372257.1687525956.git.pav%40iki.fi%2F&data=05%7C01%7
> Ciulia.tanasescu%40nxp.com%7Cf66a681e941d4d08f62a08db767ea5ce%7C6
> 86ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C638234058603291830%7
> CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJB
> TiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=rrWS4LjpQkqlx
> e1oK94BAcwHQ5o%2BwqwtaEwSlmCQgZ0%3D&reserved=0
> 
> In fact Im not sure why we couldn't use conn->cleanup?
> 

I submitted a new version of the patch, where I removed the le_bis_cleanup
work and instead I am just cleaning up the connection by removing it from
the hash list and calling bis_cleanup. I think this should be enough and,
as far as I have tested, it doesn't seem to create any deadlocks or other
issues. If you think this version is still not safe, I could just call
conn->cleanup and maybe leave a TODO comment to apply a proper method to
clean the connection up when a solution is found.

> >
> >         struct device   dev;
> >         struct dentry   *debugfs;
> > @@ -978,6 +979,7 @@ enum {
> >         HCI_CONN_PER_ADV,
> >         HCI_CONN_BIG_CREATED,
> >         HCI_CONN_CREATE_CIS,
> > +       HCI_CONN_BIG_SYNC,
> >  };
> >
> >  static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) @@
> > -1288,6 +1290,29 @@ static inline struct hci_conn
> *hci_conn_hash_lookup_big(struct hci_dev *hdev,
> >         return NULL;
> >  }
> >
> > +static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct
> hci_dev *hdev,
> > +                                                       __u8 handle) {
> > +       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->type != ISO_LINK)
> > +                       continue;
> > +
> > +               if (handle == c->iso_qos.bcast.big) {
> > +                       rcu_read_unlock();
> > +                       return c;
> > +               }
> > +       }
> > +
> > +       rcu_read_unlock();
> > +
> > +       return NULL;
> > +}
> > +
> >  static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev
> *hdev,
> >                                                         __u8 type,
> > __u16 state)  { diff --git a/net/bluetooth/hci_conn.c
> > b/net/bluetooth/hci_conn.c index 62a7ccfdfe63..e4aa7731b987 100644
> > --- a/net/bluetooth/hci_conn.c
> > +++ b/net/bluetooth/hci_conn.c
> > @@ -207,6 +207,36 @@ static void le_scan_cleanup(struct work_struct
> *work)
> >         hci_conn_put(conn);
> >  }
> >
> > +static void le_bis_cleanup(struct work_struct *work) {
> > +       struct hci_conn *conn = container_of(work, struct hci_conn,
> > +                                            le_bis_cleanup);
> > +       struct hci_dev *hdev = conn->hdev;
> > +       struct hci_conn *c = NULL;
> > +
> > +       BT_DBG("%s hcon %p", hdev->name, conn);
> > +
> > +       hci_dev_lock(hdev);
> > +
> > +       /* Check that the hci_conn is still around */
> > +       rcu_read_lock();
> > +       list_for_each_entry_rcu(c, &hdev->conn_hash.list, list) {
> > +               if (c == conn)
> > +                       break;
> > +       }
> > +       rcu_read_unlock();
> > +
> > +       if (c == conn) {
> > +               conn->state = BT_CLOSED;
> > +               hci_disconn_cfm(conn, hci_proto_disconn_ind(conn));
> > +               hci_conn_del(conn);
> > +       }
> > +
> > +       hci_dev_unlock(hdev);
> > +       hci_dev_put(hdev);
> > +       hci_conn_put(conn);
> > +}
> > +
> >  static void hci_connect_le_scan_remove(struct hci_conn *conn)  {
> >         BT_DBG("%s hcon %p", conn->hdev->name, conn); @@ -229,6
> > +259,28 @@ static void hci_connect_le_scan_remove(struct hci_conn
> *conn)
> >         schedule_work(&conn->le_scan_cleanup);
> >  }
> >
> > +static void hci_bis_remove(struct hci_conn *conn) {
> > +       BT_DBG("%s hcon %p", conn->hdev->name, conn);
> > +
> > +       /* We can't call hci_conn_del/hci_conn_cleanup here since that
> > +        * could deadlock with another hci_conn_del() call that's holding
> > +        * hci_dev_lock and doing cancel_delayed_work_sync(&conn-
> >disc_work).
> > +        * Instead, grab temporary extra references to the hci_dev and
> > +        * hci_conn and perform the necessary cleanup in a separate work
> > +        * callback.
> > +        */
> > +
> > +       hci_dev_hold(conn->hdev);
> > +       hci_conn_get(conn);
> > +
> > +       /* Even though we hold a reference to the hdev, many other
> > +        * things might get cleaned up meanwhile, including the hdev's
> > +        * own workqueue, so we can't use that for scheduling.
> > +        */
> > +       schedule_work(&conn->le_bis_cleanup);
> > +}
> 
> This is a bad idea, we are already dealing with the same shortcomings with
> respect to scan le, what we could probably do is to defer queue_sync, but I'd
> wait until we find a proper solution to scan so we apply here as well.
> 
> >  static void hci_acl_create_connection(struct hci_conn *conn)  {
> >         struct hci_dev *hdev = conn->hdev; @@ -686,6 +738,12 @@ static
> > void hci_conn_timeout(struct work_struct *work)
> >                 return;
> >         }
> >
> > +       /* Cleanup bises that failed to be established */
> > +       if (conn->state == BT_BIG_SYNC_FAILED) {
> > +               hci_bis_remove(conn);
> > +               return;
> > +       }
> > +
> >         hci_abort_conn(conn, hci_proto_disconn_ind(conn));  }
> >
> > @@ -793,6 +851,7 @@ struct iso_list_data {
> >         int count;
> >         struct iso_cig_params pdu;
> >         bool big_term;
> > +       bool big_sync_term;
> >  };
> >
> >  static void bis_list(struct hci_conn *conn, void *data) @@ -880,24
> > +939,26 @@ static int big_terminate_sync(struct hci_dev *hdev, void *data)
> >         if (d->count)
> >                 return 0;
> >
> > -       hci_le_big_terminate_sync(hdev, d->big);
> > +       if (d->big_sync_term)
> > +               hci_le_big_terminate_sync(hdev, d->big);
> >
> >         return hci_le_pa_terminate_sync(hdev, d->sync_handle);  }
> >
> > -static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16
> > sync_handle)
> > +static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct
> > +hci_conn *conn)
> >  {
> >         struct iso_list_data *d;
> >         int ret;
> >
> > -       bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big,
> sync_handle);
> > +       bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big,
> > + conn->sync_handle);
> >
> >         d = kzalloc(sizeof(*d), GFP_KERNEL);
> >         if (!d)
> >                 return -ENOMEM;
> >
> >         d->big = big;
> > -       d->sync_handle = sync_handle;
> > +       d->sync_handle = conn->sync_handle;
> > +       d->big_sync_term = test_and_clear_bit(HCI_CONN_BIG_SYNC,
> > + &conn->flags);
> >
> >         ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
> >                                  terminate_big_destroy); @@ -933,8
> > +994,14 @@ static void bis_cleanup(struct hci_conn *conn)
> >
> >                 hci_le_terminate_big(hdev, conn);
> >         } else {
> > +               bis = hci_conn_hash_lookup_big_any_dst(hdev,
> > +
> > + conn->iso_qos.bcast.big);
> > +
> > +               if (bis)
> > +                       return;
> > +
> >                 hci_le_big_terminate(hdev, conn->iso_qos.bcast.big,
> > -                                    conn->sync_handle);
> > +                                    conn);
> >         }
> >  }
> >
> > @@ -1067,6 +1134,7 @@ struct hci_conn *hci_conn_add(struct hci_dev
> *hdev, int type, bdaddr_t *dst,
> >         INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
> >         INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
> >         INIT_WORK(&conn->le_scan_cleanup, le_scan_cleanup);
> > +       INIT_WORK(&conn->le_bis_cleanup, le_bis_cleanup);
> >
> >         atomic_set(&conn->refcnt, 0);
> >
> > diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> > index b1aefe4bb751..5629a118ece4 100644
> > --- a/net/bluetooth/hci_event.c
> > +++ b/net/bluetooth/hci_event.c
> > @@ -7020,9 +7020,6 @@ static void
> hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
> >                                 flex_array_size(ev, bis, ev->num_bis)))
> >                 return;
> >
> > -       if (ev->status)
> > -               return;
> > -
> >         hci_dev_lock(hdev);
> >
> >         for (i = 0; i < ev->num_bis; i++) { @@ -7046,9 +7043,25 @@
> > static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void
> *data,
> >                 bis->iso_qos.bcast.in.latency = le16_to_cpu(ev->interval) * 125 /
> 100;
> >                 bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu);
> >
> > -               hci_iso_setup_path(bis);
> > +               if (!ev->status) {
> > +                       set_bit(HCI_CONN_BIG_SYNC, &bis->flags);
> > +                       hci_iso_setup_path(bis);
> > +               }
> >         }
> >
> > +       /* In case BIG sync failed, notify each failed connection to
> > +        * the user after all hci connections have been added
> > +        */
> > +       if (ev->status)
> > +               for (i = 0; i < ev->num_bis; i++) {
> > +                       u16 handle = le16_to_cpu(ev->bis[i]);
> > +
> > +                       bis = hci_conn_hash_lookup_handle(hdev,
> > + handle);
> > +
> > +                       bis->state = BT_BIG_SYNC_FAILED;
> > +                       hci_connect_cfm(bis, ev->status);
> > +               }
> > +
> >         hci_dev_unlock(hdev);
> >  }
> >
> > diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index
> > 84d238d0639a..a2571d8ea055 100644
> > --- a/net/bluetooth/iso.c
> > +++ b/net/bluetooth/iso.c
> > @@ -48,6 +48,11 @@ static void iso_sock_kill(struct sock *sk);
> > #define EIR_SERVICE_DATA_LENGTH 4  #define BASE_MAX_LENGTH
> > (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH)
> >
> > +/* iso_pinfo flags values */
> > +enum {
> > +       BT_SK_BIG_SYNC,
> > +};
> > +
> >  struct iso_pinfo {
> >         struct bt_sock          bt;
> >         bdaddr_t                src;
> > @@ -58,7 +63,7 @@ struct iso_pinfo {
> >         __u8                    bc_num_bis;
> >         __u8                    bc_bis[ISO_MAX_NUM_BIS];
> >         __u16                   sync_handle;
> > -       __u32                   flags;
> > +       unsigned long           flags;
> >         struct bt_iso_qos       qos;
> >         bool                    qos_user_set;
> >         __u8                    base_len;
> > @@ -1569,6 +1574,12 @@ static void iso_conn_ready(struct iso_conn
> *conn)
> >                 hci_conn_hold(hcon);
> >                 iso_chan_add(conn, sk, parent);
> >
> > +               if (ev && ((struct hci_evt_le_big_sync_estabilished *)ev)->status) {
> > +                       /* Trigger error signal on chilld socket */
> > +                       sk->sk_err = ECONNREFUSED;
> > +                       sk->sk_error_report(sk);
> > +               }
> > +
> >                 if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
> >                         sk->sk_state = BT_CONNECT2;
> >                 else
> > @@ -1637,15 +1648,19 @@ int iso_connect_ind(struct hci_dev *hdev,
> bdaddr_t *bdaddr, __u8 *flags)
> >                         if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
> >                                 iso_pi(sk)->bc_num_bis = ev2->num_bis;
> >
> > -                       err = hci_le_big_create_sync(hdev,
> > -                                                    &iso_pi(sk)->qos,
> > -                                                    iso_pi(sk)->sync_handle,
> > -                                                    iso_pi(sk)->bc_num_bis,
> > -                                                    iso_pi(sk)->bc_bis);
> > -                       if (err) {
> > -                               bt_dev_err(hdev, "hci_le_big_create_sync: %d",
> > -                                          err);
> > -                               sk = NULL;
> > +                       if (!test_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
> > +                               set_bit(BT_SK_BIG_SYNC,
> > + &iso_pi(sk)->flags);
> > +
> > +                               err = hci_le_big_create_sync(hdev,
> > +                                                            &iso_pi(sk)->qos,
> > +                                                            iso_pi(sk)->sync_handle,
> > +                                                            iso_pi(sk)->bc_num_bis,
> > +                                                            iso_pi(sk)->bc_bis);
> > +                               if (err) {
> > +                                       bt_dev_err(hdev, "hci_le_big_create_sync: %d",
> > +                                                  err);
> > +                                       sk = NULL;
> > +                               }
> >                         }
> >                 }
> >         } else {
> > @@ -1688,7 +1703,7 @@ static void iso_connect_cfm(struct hci_conn
> > *hcon, __u8 status)
> >
> >         BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst,
> > status);
> >
> > -       if (!status) {
> > +       if (!status || hcon->state == BT_BIG_SYNC_FAILED) {
> >                 struct iso_conn *conn;
> >
> >                 conn = iso_conn_add(hcon);
> > --
> > 2.34.1
> >
> 
> 
> --
> Luiz Augusto von Dentz


Regards,
Iulia
diff mbox series

Patch

diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 34998ae8ed78..b05147cf4c57 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -292,7 +292,8 @@  enum {
 	BT_CONNECT2,
 	BT_CONFIG,
 	BT_DISCONN,
-	BT_CLOSED
+	BT_CLOSED,
+	BT_BIG_SYNC_FAILED,
 };
 
 /* If unused will be removed by compiler */
@@ -317,6 +318,8 @@  static inline const char *state_to_string(int state)
 		return "BT_DISCONN";
 	case BT_CLOSED:
 		return "BT_CLOSED";
+	case BT_BIG_SYNC_FAILED:
+		return "BT_BIG_SYNC_FAILED";
 	}
 
 	return "invalid state";
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 05a9b3ab3f56..81e83ecdd267 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -761,6 +761,7 @@  struct hci_conn {
 	struct delayed_work idle_work;
 	struct delayed_work le_conn_timeout;
 	struct work_struct  le_scan_cleanup;
+	struct work_struct  le_bis_cleanup;
 
 	struct device	dev;
 	struct dentry	*debugfs;
@@ -978,6 +979,7 @@  enum {
 	HCI_CONN_PER_ADV,
 	HCI_CONN_BIG_CREATED,
 	HCI_CONN_CREATE_CIS,
+	HCI_CONN_BIG_SYNC,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
@@ -1288,6 +1290,29 @@  static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
 	return NULL;
 }
 
+static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *hdev,
+							__u8 handle)
+{
+	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->type != ISO_LINK)
+			continue;
+
+		if (handle == c->iso_qos.bcast.big) {
+			rcu_read_unlock();
+			return c;
+		}
+	}
+
+	rcu_read_unlock();
+
+	return NULL;
+}
+
 static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
 							__u8 type, __u16 state)
 {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 62a7ccfdfe63..e4aa7731b987 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -207,6 +207,36 @@  static void le_scan_cleanup(struct work_struct *work)
 	hci_conn_put(conn);
 }
 
+static void le_bis_cleanup(struct work_struct *work)
+{
+	struct hci_conn *conn = container_of(work, struct hci_conn,
+					     le_bis_cleanup);
+	struct hci_dev *hdev = conn->hdev;
+	struct hci_conn *c = NULL;
+
+	BT_DBG("%s hcon %p", hdev->name, conn);
+
+	hci_dev_lock(hdev);
+
+	/* Check that the hci_conn is still around */
+	rcu_read_lock();
+	list_for_each_entry_rcu(c, &hdev->conn_hash.list, list) {
+		if (c == conn)
+			break;
+	}
+	rcu_read_unlock();
+
+	if (c == conn) {
+		conn->state = BT_CLOSED;
+		hci_disconn_cfm(conn, hci_proto_disconn_ind(conn));
+		hci_conn_del(conn);
+	}
+
+	hci_dev_unlock(hdev);
+	hci_dev_put(hdev);
+	hci_conn_put(conn);
+}
+
 static void hci_connect_le_scan_remove(struct hci_conn *conn)
 {
 	BT_DBG("%s hcon %p", conn->hdev->name, conn);
@@ -229,6 +259,28 @@  static void hci_connect_le_scan_remove(struct hci_conn *conn)
 	schedule_work(&conn->le_scan_cleanup);
 }
 
+static void hci_bis_remove(struct hci_conn *conn)
+{
+	BT_DBG("%s hcon %p", conn->hdev->name, conn);
+
+	/* We can't call hci_conn_del/hci_conn_cleanup here since that
+	 * could deadlock with another hci_conn_del() call that's holding
+	 * hci_dev_lock and doing cancel_delayed_work_sync(&conn->disc_work).
+	 * Instead, grab temporary extra references to the hci_dev and
+	 * hci_conn and perform the necessary cleanup in a separate work
+	 * callback.
+	 */
+
+	hci_dev_hold(conn->hdev);
+	hci_conn_get(conn);
+
+	/* Even though we hold a reference to the hdev, many other
+	 * things might get cleaned up meanwhile, including the hdev's
+	 * own workqueue, so we can't use that for scheduling.
+	 */
+	schedule_work(&conn->le_bis_cleanup);
+}
+
 static void hci_acl_create_connection(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
@@ -686,6 +738,12 @@  static void hci_conn_timeout(struct work_struct *work)
 		return;
 	}
 
+	/* Cleanup bises that failed to be established */
+	if (conn->state == BT_BIG_SYNC_FAILED) {
+		hci_bis_remove(conn);
+		return;
+	}
+
 	hci_abort_conn(conn, hci_proto_disconn_ind(conn));
 }
 
@@ -793,6 +851,7 @@  struct iso_list_data {
 	int count;
 	struct iso_cig_params pdu;
 	bool big_term;
+	bool big_sync_term;
 };
 
 static void bis_list(struct hci_conn *conn, void *data)
@@ -880,24 +939,26 @@  static int big_terminate_sync(struct hci_dev *hdev, void *data)
 	if (d->count)
 		return 0;
 
-	hci_le_big_terminate_sync(hdev, d->big);
+	if (d->big_sync_term)
+		hci_le_big_terminate_sync(hdev, d->big);
 
 	return hci_le_pa_terminate_sync(hdev, d->sync_handle);
 }
 
-static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
+static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *conn)
 {
 	struct iso_list_data *d;
 	int ret;
 
-	bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle);
+	bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, conn->sync_handle);
 
 	d = kzalloc(sizeof(*d), GFP_KERNEL);
 	if (!d)
 		return -ENOMEM;
 
 	d->big = big;
-	d->sync_handle = sync_handle;
+	d->sync_handle = conn->sync_handle;
+	d->big_sync_term = test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags);
 
 	ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
 				 terminate_big_destroy);
@@ -933,8 +994,14 @@  static void bis_cleanup(struct hci_conn *conn)
 
 		hci_le_terminate_big(hdev, conn);
 	} else {
+		bis = hci_conn_hash_lookup_big_any_dst(hdev,
+						       conn->iso_qos.bcast.big);
+
+		if (bis)
+			return;
+
 		hci_le_big_terminate(hdev, conn->iso_qos.bcast.big,
-				     conn->sync_handle);
+				     conn);
 	}
 }
 
@@ -1067,6 +1134,7 @@  struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
 	INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
 	INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
 	INIT_WORK(&conn->le_scan_cleanup, le_scan_cleanup);
+	INIT_WORK(&conn->le_bis_cleanup, le_bis_cleanup);
 
 	atomic_set(&conn->refcnt, 0);
 
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index b1aefe4bb751..5629a118ece4 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -7020,9 +7020,6 @@  static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
 				flex_array_size(ev, bis, ev->num_bis)))
 		return;
 
-	if (ev->status)
-		return;
-
 	hci_dev_lock(hdev);
 
 	for (i = 0; i < ev->num_bis; i++) {
@@ -7046,9 +7043,25 @@  static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
 		bis->iso_qos.bcast.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
 		bis->iso_qos.bcast.in.sdu = le16_to_cpu(ev->max_pdu);
 
-		hci_iso_setup_path(bis);
+		if (!ev->status) {
+			set_bit(HCI_CONN_BIG_SYNC, &bis->flags);
+			hci_iso_setup_path(bis);
+		}
 	}
 
+	/* In case BIG sync failed, notify each failed connection to
+	 * the user after all hci connections have been added
+	 */
+	if (ev->status)
+		for (i = 0; i < ev->num_bis; i++) {
+			u16 handle = le16_to_cpu(ev->bis[i]);
+
+			bis = hci_conn_hash_lookup_handle(hdev, handle);
+
+			bis->state = BT_BIG_SYNC_FAILED;
+			hci_connect_cfm(bis, ev->status);
+		}
+
 	hci_dev_unlock(hdev);
 }
 
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 84d238d0639a..a2571d8ea055 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -48,6 +48,11 @@  static void iso_sock_kill(struct sock *sk);
 #define EIR_SERVICE_DATA_LENGTH 4
 #define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH)
 
+/* iso_pinfo flags values */
+enum {
+	BT_SK_BIG_SYNC,
+};
+
 struct iso_pinfo {
 	struct bt_sock		bt;
 	bdaddr_t		src;
@@ -58,7 +63,7 @@  struct iso_pinfo {
 	__u8			bc_num_bis;
 	__u8			bc_bis[ISO_MAX_NUM_BIS];
 	__u16			sync_handle;
-	__u32			flags;
+	unsigned long		flags;
 	struct bt_iso_qos	qos;
 	bool			qos_user_set;
 	__u8			base_len;
@@ -1569,6 +1574,12 @@  static void iso_conn_ready(struct iso_conn *conn)
 		hci_conn_hold(hcon);
 		iso_chan_add(conn, sk, parent);
 
+		if (ev && ((struct hci_evt_le_big_sync_estabilished *)ev)->status) {
+			/* Trigger error signal on chilld socket */
+			sk->sk_err = ECONNREFUSED;
+			sk->sk_error_report(sk);
+		}
+
 		if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
 			sk->sk_state = BT_CONNECT2;
 		else
@@ -1637,15 +1648,19 @@  int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
 			if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
 				iso_pi(sk)->bc_num_bis = ev2->num_bis;
 
-			err = hci_le_big_create_sync(hdev,
-						     &iso_pi(sk)->qos,
-						     iso_pi(sk)->sync_handle,
-						     iso_pi(sk)->bc_num_bis,
-						     iso_pi(sk)->bc_bis);
-			if (err) {
-				bt_dev_err(hdev, "hci_le_big_create_sync: %d",
-					   err);
-				sk = NULL;
+			if (!test_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
+				set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags);
+
+				err = hci_le_big_create_sync(hdev,
+							     &iso_pi(sk)->qos,
+							     iso_pi(sk)->sync_handle,
+							     iso_pi(sk)->bc_num_bis,
+							     iso_pi(sk)->bc_bis);
+				if (err) {
+					bt_dev_err(hdev, "hci_le_big_create_sync: %d",
+						   err);
+					sk = NULL;
+				}
 			}
 		}
 	} else {
@@ -1688,7 +1703,7 @@  static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
 
 	BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
 
-	if (!status) {
+	if (!status || hcon->state == BT_BIG_SYNC_FAILED) {
 		struct iso_conn *conn;
 
 		conn = iso_conn_add(hcon);