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

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

 



Hi Iulia,

On Fri, Jun 23, 2023 at 12:43 AM Iulia Tanasescu
<iulia.tanasescu@xxxxxxx> 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@xxxxxxx>
> ---
>  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@xxxxxx/

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
>


-- 
Luiz Augusto von Dentz




[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