Bluetooth: host: Make connection lookup functions thread-safe

Make the connection lookup functions thread-safe by re-using the
bt_conn_ref returning NULL mechanism and keeping a valid reference.

Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
This commit is contained in:
Joakim Andersson 2020-11-19 17:11:09 +01:00 committed by Carles Cufí
parent fae964c008
commit 4350021f09
2 changed files with 89 additions and 50 deletions

View File

@ -404,7 +404,7 @@ struct bt_conn *bt_conn_new(struct bt_conn *conns, size_t size)
int i;
for (i = 0; i < size; i++) {
if (!atomic_get(&conns[i].ref)) {
if (atomic_cas(&conns[i].ref, 0, 1)) {
conn = &conns[i];
break;
}
@ -414,9 +414,7 @@ struct bt_conn *bt_conn_new(struct bt_conn *conns, size_t size)
return NULL;
}
(void)memset(conn, 0, sizeof(*conn));
atomic_set(&conn->ref, 1);
(void)memset(conn, 0, offsetof(struct bt_conn, ref));
return conn;
}
@ -567,17 +565,23 @@ struct bt_conn *bt_conn_lookup_addr_sco(const bt_addr_t *peer)
int i;
for (i = 0; i < ARRAY_SIZE(sco_conns); i++) {
if (!atomic_get(&sco_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&sco_conns[i]);
if (!conn) {
continue;
}
if (sco_conns[i].type != BT_CONN_TYPE_SCO) {
if (conn->type != BT_CONN_TYPE_SCO) {
bt_conn_unref(conn);
continue;
}
if (!bt_addr_cmp(peer, &sco_conns[i].sco.acl->br.dst)) {
return bt_conn_ref(&sco_conns[i]);
if (bt_addr_cmp(peer, &conn->sco.acl->br.dst) != 0) {
bt_conn_unref(conn);
continue;
}
return conn;
}
return NULL;
@ -588,17 +592,23 @@ struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer)
int i;
for (i = 0; i < ARRAY_SIZE(acl_conns); i++) {
if (!atomic_get(&acl_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&acl_conns[i]);
if (!conn) {
continue;
}
if (acl_conns[i].type != BT_CONN_TYPE_BR) {
if (conn->type != BT_CONN_TYPE_BR) {
bt_conn_unref(conn);
continue;
}
if (!bt_addr_cmp(peer, &acl_conns[i].br.dst)) {
return bt_conn_ref(&acl_conns[i]);
if (bt_addr_cmp(peer, &conn->br.dst) != 0) {
bt_conn_unref(conn);
continue;
}
return conn;
}
return NULL;
@ -1453,18 +1463,24 @@ struct bt_conn *conn_lookup_handle(struct bt_conn *conns, size_t size,
int i;
for (i = 0; i < size; i++) {
if (!atomic_get(&conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&conns[i]);
if (!conn) {
continue;
}
/* We only care about connections with a valid handle */
if (!bt_conn_is_handle_valid(&conns[i])) {
if (!bt_conn_is_handle_valid(conn)) {
bt_conn_unref(conn);
continue;
}
if (conns[i].handle == handle) {
return bt_conn_ref(&conns[i]);
if (conn->handle != handle) {
bt_conn_unref(conn);
continue;
}
return conn;
}
return NULL;
@ -1476,17 +1492,21 @@ struct bt_conn *conn_lookup_iso(struct bt_conn *conn)
int i;
for (i = 0; i < ARRAY_SIZE(iso_conns); i++) {
if (!atomic_get(&iso_conns[i].ref)) {
struct bt_conn *iso_conn = bt_conn_ref(&iso_conns[i]);
if (!iso_conn) {
continue;
}
if (&iso_conns[i] == conn) {
return bt_conn_ref(conn);
if (iso_conn == conn) {
return iso_conn;
}
if (iso_conns[i].iso.acl == conn) {
return bt_conn_ref(&iso_conns[i]);
if (conn->iso.acl == conn) {
return iso_conn;
}
bt_conn_unref(iso_conn);
}
return NULL;
@ -1735,17 +1755,23 @@ struct bt_conn *bt_conn_lookup_addr_le(uint8_t id, const bt_addr_le_t *peer)
int i;
for (i = 0; i < ARRAY_SIZE(acl_conns); i++) {
if (!atomic_get(&acl_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&acl_conns[i]);
if (!conn) {
continue;
}
if (acl_conns[i].type != BT_CONN_TYPE_LE) {
if (conn->type != BT_CONN_TYPE_LE) {
bt_conn_unref(conn);
continue;
}
if (bt_conn_is_peer_addr_le(&acl_conns[i], id, peer)) {
return bt_conn_ref(&acl_conns[i]);
if (!bt_conn_is_peer_addr_le(conn, id, peer)) {
bt_conn_unref(conn);
continue;
}
return conn;
}
return NULL;
@ -1757,21 +1783,28 @@ struct bt_conn *bt_conn_lookup_state_le(uint8_t id, const bt_addr_le_t *peer,
int i;
for (i = 0; i < ARRAY_SIZE(acl_conns); i++) {
if (!atomic_get(&acl_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&acl_conns[i]);
if (!conn) {
continue;
}
if (acl_conns[i].type != BT_CONN_TYPE_LE) {
if (conn->type != BT_CONN_TYPE_LE) {
bt_conn_ref(conn);
continue;
}
if (peer && !bt_conn_is_peer_addr_le(&acl_conns[i], id, peer)) {
if (peer && !bt_conn_is_peer_addr_le(conn, id, peer)) {
bt_conn_unref(conn);
continue;
}
if (acl_conns[i].state == state && acl_conns[i].id == id) {
return bt_conn_ref(&acl_conns[i]);
if (!(conn->state == state && conn->id == id)) {
bt_conn_unref(conn);
continue;
}
return conn;
}
return NULL;
@ -1783,24 +1816,31 @@ void bt_conn_foreach(int type, void (*func)(struct bt_conn *conn, void *data),
int i;
for (i = 0; i < ARRAY_SIZE(acl_conns); i++) {
if (!atomic_get(&acl_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&acl_conns[i]);
if (!conn) {
continue;
}
if (!(acl_conns[i].type & type)) {
if (!(conn->type & type)) {
bt_conn_unref(conn);
continue;
}
func(&acl_conns[i], data);
func(conn, data);
bt_conn_unref(conn);
}
#if defined(CONFIG_BT_BREDR)
if (type & BT_CONN_TYPE_SCO) {
for (i = 0; i < ARRAY_SIZE(sco_conns); i++) {
if (!atomic_get(&sco_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&sco_conns[i]);
if (!conn) {
continue;
}
func(&sco_conns[i], data);
func(conn, data);
bt_conn_unref(conn);
}
}
#endif /* defined(CONFIG_BT_BREDR) */
@ -1808,11 +1848,14 @@ void bt_conn_foreach(int type, void (*func)(struct bt_conn *conn, void *data),
#if defined(CONFIG_BT_ISO)
if (type & BT_CONN_TYPE_ISO) {
for (i = 0; i < ARRAY_SIZE(iso_conns); i++) {
if (!atomic_get(&iso_conns[i].ref)) {
struct bt_conn *conn = bt_conn_ref(&iso_conns[i]);
if (!conn) {
continue;
}
func(&iso_conns[i], data);
func(conn, data);
bt_conn_unref(conn);
}
}
#endif /* defined(CONFIG_BT_ISO) */
@ -2616,19 +2659,11 @@ uint8_t bt_conn_index(struct bt_conn *conn)
struct bt_conn *bt_conn_lookup_index(uint8_t index)
{
struct bt_conn *conn;
if (index >= ARRAY_SIZE(acl_conns)) {
return NULL;
}
conn = &acl_conns[index];
if (!atomic_get(&conn->ref)) {
return NULL;
}
return bt_conn_ref(conn);
return bt_conn_ref(&acl_conns[index]);
}
int bt_conn_init(void)
@ -2651,9 +2686,9 @@ int bt_conn_init(void)
/* Initialize background scan */
if (IS_ENABLED(CONFIG_BT_CENTRAL)) {
for (i = 0; i < ARRAY_SIZE(acl_conns); i++) {
struct bt_conn *conn = &acl_conns[i];
struct bt_conn *conn = bt_conn_ref(&acl_conns[i]);
if (!atomic_get(&conn->ref)) {
if (!conn) {
continue;
}
@ -2665,6 +2700,8 @@ int bt_conn_init(void)
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
}
#endif /* !defined(CONFIG_BT_WHITELIST) */
bt_conn_unref(conn);
}
}

View File

@ -169,8 +169,6 @@ struct bt_conn {
/* Active L2CAP/ISO channels */
sys_slist_t channels;
atomic_t ref;
/* Delayed work deferred tasks:
* - Peripheral delayed connection update.
* - Initiator connect create cancel.
@ -196,6 +194,10 @@ struct bt_conn {
uint16_t subversion;
} rv;
#endif
/* Must be at the end so that everything else in the structure can be
* memset to zero without affecting the ref.
*/
atomic_t ref;
};
void bt_conn_reset_rx_state(struct bt_conn *conn);