Skip to content

Commit

Permalink
Bluetooth: ISO: Reassociate a socket with an active BIS
Browse files Browse the repository at this point in the history
[ Upstream commit fa224d0 ]

For ISO Broadcast, all BISes from a BIG have the same lifespan - they
cannot be created or terminated independently from each other.

This links together all BIS hcons that are part of the same BIG, so all
hcons are kept alive as long as the BIG is active.

If multiple BIS sockets are opened for a BIG handle, and only part of
them are closed at some point, the associated hcons will be marked as
open. If new sockets will later be opened for the same BIG, they will
be reassociated with the open BIS hcons.

All BIS hcons will be cleaned up and the BIG will be terminated when
the last BIS socket is closed from userspace.

Signed-off-by: Iulia Tanasescu <[email protected]>
Signed-off-by: Luiz Augusto von Dentz <[email protected]>
Stable-dep-of: 581dd2dc168f ("Bluetooth: hci_event: Fix using rcu_read_(un)lock while iterating")
Signed-off-by: Sasha Levin <[email protected]>
  • Loading branch information
iulia-tanasescu authored and gregkh committed Dec 19, 2024
1 parent 81c4b95 commit 11dc486
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 4 deletions.
24 changes: 24 additions & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,30 @@ static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *
return NULL;
}

static inline struct hci_conn *
hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state)
{
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 (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK ||
c->state != state)
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_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
{
Expand Down
32 changes: 30 additions & 2 deletions net/bluetooth/hci_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1054,8 +1054,9 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
hci_conn_failed(conn, reason);
break;
case ISO_LINK:
if (conn->state != BT_CONNECTED &&
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
if ((conn->state != BT_CONNECTED &&
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
hci_conn_failed(conn, reason);
break;
}
Expand Down Expand Up @@ -2134,7 +2135,17 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 base_len, __u8 *base)
{
struct hci_conn *conn;
struct hci_conn *parent;
__u8 eir[HCI_MAX_PER_AD_LENGTH];
struct hci_link *link;

/* Look for any BIS that is open for rebinding */
conn = hci_conn_hash_lookup_big_state(hdev, qos->bcast.big, BT_OPEN);
if (conn) {
memcpy(qos, &conn->iso_qos, sizeof(*qos));
conn->state = BT_CONNECTED;
return conn;
}

if (base_len && base)
base_len = eir_append_service_data(eir, 0, 0x1851,
Expand Down Expand Up @@ -2162,6 +2173,20 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst,
conn->iso_qos = *qos;
conn->state = BT_BOUND;

/* Link BISes together */
parent = hci_conn_hash_lookup_big(hdev,
conn->iso_qos.bcast.big);
if (parent && parent != conn) {
link = hci_conn_link(parent, conn);
if (!link) {
hci_conn_drop(conn);
return ERR_PTR(-ENOLINK);
}

/* Link takes the refcount */
hci_conn_drop(conn);
}

return conn;
}

Expand Down Expand Up @@ -2193,6 +2218,9 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
if (IS_ERR(conn))
return conn;

if (conn->state == BT_CONNECTED)
return conn;

data.big = qos->bcast.big;
data.bis = qos->bcast.bis;

Expand Down
79 changes: 77 additions & 2 deletions net/bluetooth/iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,19 +612,68 @@ static struct sock *iso_get_sock_listen(bdaddr_t *src, bdaddr_t *dst,
continue;

/* Exact match. */
if (!bacmp(&iso_pi(sk)->src, src))
if (!bacmp(&iso_pi(sk)->src, src)) {
sock_hold(sk);
break;
}

/* Closest match */
if (!bacmp(&iso_pi(sk)->src, BDADDR_ANY))
if (!bacmp(&iso_pi(sk)->src, BDADDR_ANY)) {
if (sk1)
sock_put(sk1);

sk1 = sk;
sock_hold(sk1);
}
}

if (sk && sk1)
sock_put(sk1);

read_unlock(&iso_sk_list.lock);

return sk ? sk : sk1;
}

static struct sock *iso_get_sock_big(struct sock *match_sk, bdaddr_t *src,
bdaddr_t *dst, uint8_t big)
{
struct sock *sk = NULL;

read_lock(&iso_sk_list.lock);

sk_for_each(sk, &iso_sk_list.head) {
if (match_sk == sk)
continue;

/* Look for sockets that have already been
* connected to the BIG
*/
if (sk->sk_state != BT_CONNECTED &&
sk->sk_state != BT_CONNECT)
continue;

/* Match Broadcast destination */
if (bacmp(&iso_pi(sk)->dst, dst))
continue;

/* Match BIG handle */
if (iso_pi(sk)->qos.bcast.big != big)
continue;

/* Match source address */
if (bacmp(&iso_pi(sk)->src, src))
continue;

sock_hold(sk);
break;
}

read_unlock(&iso_sk_list.lock);

return sk;
}

static void iso_sock_destruct(struct sock *sk)
{
BT_DBG("sk %p", sk);
Expand Down Expand Up @@ -677,6 +726,28 @@ static void iso_sock_kill(struct sock *sk)

static void iso_sock_disconn(struct sock *sk)
{
struct sock *bis_sk;
struct hci_conn *hcon = iso_pi(sk)->conn->hcon;

if (test_bit(HCI_CONN_BIG_CREATED, &hcon->flags)) {
bis_sk = iso_get_sock_big(sk, &iso_pi(sk)->src,
&iso_pi(sk)->dst,
iso_pi(sk)->qos.bcast.big);

/* If there are any other connected sockets for the
* same BIG, just delete the sk and leave the bis
* hcon active, in case later rebinding is needed.
*/
if (bis_sk) {
hcon->state = BT_OPEN;
iso_pi(sk)->conn->hcon = NULL;
iso_sock_clear_timer(sk);
iso_chan_del(sk, bt_to_errno(hcon->abort_reason));
sock_put(bis_sk);
return;
}
}

sk->sk_state = BT_DISCONN;
iso_sock_set_timer(sk, ISO_DISCONN_TIMEOUT);
iso_conn_lock(iso_pi(sk)->conn);
Expand Down Expand Up @@ -1724,6 +1795,7 @@ static void iso_conn_ready(struct iso_conn *conn)
parent->sk_data_ready(parent);

release_sock(parent);
sock_put(parent);
}
}

Expand Down Expand Up @@ -1819,6 +1891,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (err) {
bt_dev_err(hdev, "hci_le_big_create_sync: %d",
err);
sock_put(sk);
sk = NULL;
}
}
Expand Down Expand Up @@ -1847,6 +1920,8 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
*flags |= HCI_PROTO_DEFER;

sock_put(sk);

return lm;
}

Expand Down

0 comments on commit 11dc486

Please sign in to comment.