Skip to content

Commit 5d3ab35

Browse files
alwa-nordicrlubos
authored andcommitted
[nrf fromlist] Bluetooth: Host: Invoke tx callbacks when channel is deleted
When bt_l2cap_send_pdu() succeeds, it transfers buffer ownership to the stack, which must eventually invoke the provided callback. This contract is honored in all paths where transmission becomes impossible: - Normal transmission: callback invoked with err=0 after HCI Number of Completed Packets event (tx_notify_process) - Send errors (after tx allocated): callback invoked with err=-ESHUTDOWN via conn_tx_destroy - Send errors (before tx allocated): callback invoked with the specific error code in send_buf error_return path - Connection disconnect: callbacks invoked with err=-ESHUTDOWN via process_unack_tx -> conn_tx_destroy for all PDUs in tx_pending However, when a channel is deleted (l2cap_chan_del), PDUs remaining in the tx_queue are dropped without invoking their callbacks, violating the ownership contract. Fix this by extracting and invoking any non-NULL callbacks from the closure stored in buf->user_data before releasing the buffers. The callback is invoked with err=-ESHUTDOWN, making this path analogous to process_unack_tx: both drain queues of unsent PDUs when transmission becomes impossible due to external events (channel deletion vs connection disconnect). The only difference is the buffer lifecycle stage - in l2cap_chan_del, PDUs are still in tx_queue (closure in buf->user_data), while in process_unack_tx, they've progressed to tx_pending (callback in bt_conn_tx struct). Note: conn_tx_destroy() cannot be used here because no bt_conn_tx struct has been allocated yet - the closure is still in buf->user_data. Upstream PR #: 97056 Signed-off-by: Aleksander Wasaznik <[email protected]>
1 parent b994652 commit 5d3ab35

File tree

1 file changed

+15
-0
lines changed

1 file changed

+15
-0
lines changed

subsys/bluetooth/host/l2cap.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,21 @@ static void l2cap_chan_del(struct bt_l2cap_chan *chan)
273273
* `l2cap_chan_destroy()` as it is not called for fixed channels.
274274
*/
275275
while ((buf = k_fifo_get(&le_chan->tx_queue, K_NO_WAIT))) {
276+
bt_conn_tx_cb_t cb = closure_cb(buf->user_data);
277+
278+
if (cb) {
279+
void *user_data = closure_data(buf->user_data);
280+
281+
/* When bt_l2cap_send_pdu() succeeds, the stack takes ownership
282+
* and must invoke the callback eventually. Since these PDUs will
283+
* never be transmitted, invoke the callback now with an error.
284+
* Note: We cannot use conn_tx_destroy() here because no bt_conn_tx
285+
* struct has been allocated yet - the closure is still in the
286+
* buf->user_data.
287+
*/
288+
cb(chan->conn, user_data, -ESHUTDOWN);
289+
}
290+
276291
net_buf_unref(buf);
277292
}
278293

0 commit comments

Comments
 (0)