Skip to content

Commit b76f09d

Browse files
Thalleycfriedt
authored andcommitted
Bluetooth: ISO: Fix issue with BIS tx_complete
BIS termination as broadcaster is handled different than ACL and CIS, and in rare chances the tx_complete for BIS may not have been completed in the system workqueue before iso_new was called for the same bt_conn struct (e.g. via bt_iso_cig_create), which would perform k_work_init(&conn->tx_complete_work, tx_complete_work); but where conn->tx_complete_work still existed in the system workqueue, which would cause the list of pending items on the system workqueue to be removed as the `next` pointer would be NULL. This also adds an assert in bt_conn_new to prevent this issue from appearing again. Signed-off-by: Emil Gydesen <[email protected]>
1 parent 5ec14f8 commit b76f09d

File tree

4 files changed

+21
-3
lines changed

4 files changed

+21
-3
lines changed

subsys/bluetooth/host/conn.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1501,8 +1501,11 @@ void bt_conn_unref(struct bt_conn *conn)
15011501

15021502
old = atomic_dec(&conn->ref);
15031503
/* Prevent from accessing connection object */
1504-
conn = NULL;
15051504
deallocated = (atomic_get(&old) == 1);
1505+
IF_ENABLED(CONFIG_BT_CONN_TX,
1506+
(__ASSERT(!(deallocated && k_work_is_pending(&conn->tx_complete_work)),
1507+
"tx_complete_work is pending when conn is deallocated")));
1508+
conn = NULL;
15061509

15071510
LOG_DBG("handle %u ref %ld -> %ld", conn_handle, old, (old - 1));
15081511

subsys/bluetooth/host/iso.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,15 @@ static void bt_iso_chan_disconnected(struct bt_iso_chan *chan, uint8_t reason)
511511
}
512512
#endif /* CONFIG_BT_ISO_CENTRAL */
513513
}
514+
} else if (IS_ENABLED(CONFIG_BT_ISO_BROADCASTER) &&
515+
conn_type == BT_ISO_CHAN_TYPE_BROADCASTER) {
516+
/* BIS do not get a HCI Disconnected event and will not handle cleanup of pending TX
517+
* complete in the same way as ACL and CIS do. Call bt_conn_tx_notify directly here
518+
* to flush the chan->iso->tx_complete for each disconnected BIS
519+
*/
520+
bt_conn_tx_notify(chan->iso, true);
521+
} else {
522+
/* No special handling for BT_ISO_CHAN_TYPE_SYNC_RECEIVER */
514523
}
515524
}
516525

tests/bluetooth/host/conn/mocks/kernel.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/*
2-
* Copyright (c) 2024 Nordic Semiconductor ASA
2+
* Copyright (c) 2024-2025 Nordic Semiconductor ASA
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
#include <stdbool.h>
8+
9+
#include <zephyr/fff.h>
710
#include <zephyr/kernel.h>
811

912
#include "kernel.h"
@@ -22,6 +25,7 @@ DEFINE_FAKE_VALUE_FUNC(int, k_work_submit, struct k_work *);
2225
DEFINE_FAKE_VALUE_FUNC(int, k_work_submit_to_queue, struct k_work_q *, struct k_work *);
2326
DEFINE_FAKE_VALUE_FUNC(int, k_work_reschedule, struct k_work_delayable *, k_timeout_t);
2427
DEFINE_FAKE_VALUE_FUNC(int, k_work_schedule, struct k_work_delayable *, k_timeout_t);
28+
DEFINE_FAKE_VALUE_FUNC(int, k_work_busy_get, const struct k_work *);
2529
DEFINE_FAKE_VOID_FUNC(k_queue_init, struct k_queue *);
2630
DEFINE_FAKE_VOID_FUNC(k_queue_append, struct k_queue *, void *);
2731
DEFINE_FAKE_VALUE_FUNC(int, k_queue_is_empty, struct k_queue *);

tests/bluetooth/host/conn/mocks/kernel.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/*
2-
* Copyright (c) 2024 Nordic Semiconductor ASA
2+
* Copyright (c) 2024-2025 Nordic Semiconductor ASA
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
6+
#include <stdbool.h>
67

78
#include <zephyr/kernel.h>
89
#include <zephyr/fff.h>
@@ -42,6 +43,7 @@ DECLARE_FAKE_VOID_FUNC(k_sem_give, struct k_sem *);
4243
DECLARE_FAKE_VALUE_FUNC(k_tid_t, k_sched_current_thread_query);
4344
DECLARE_FAKE_VOID_FUNC(k_work_init, struct k_work *, k_work_handler_t);
4445
DECLARE_FAKE_VOID_FUNC(k_work_init_delayable, struct k_work_delayable *, k_work_handler_t);
46+
DECLARE_FAKE_VALUE_FUNC(int, k_work_busy_get, const struct k_work *);
4547
DECLARE_FAKE_VALUE_FUNC(int, k_work_cancel_delayable, struct k_work_delayable *);
4648
DECLARE_FAKE_VALUE_FUNC(bool, k_work_flush, struct k_work *, struct k_work_sync *);
4749
DECLARE_FAKE_VALUE_FUNC(int, k_work_submit, struct k_work *);

0 commit comments

Comments
 (0)