Skip to content

Conversation

alwa-nordic
Copy link
Contributor

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.

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.

Signed-off-by: Aleksander Wasaznik <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant