Skip to content

Commit 8e207fe

Browse files
jori-nordiccarlescufi
authored andcommitted
Bluetooth: host: l2cap: workaround SDU deadlock
See the code comments. SDUs might enter a state where they will be blocked forever, as a workaround, we nudge them when another SDU has been sent. Signed-off-by: Jonathan Rico <[email protected]>
1 parent 3c1ca93 commit 8e207fe

File tree

1 file changed

+25
-0
lines changed

1 file changed

+25
-0
lines changed

subsys/bluetooth/host/l2cap.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,12 @@ static void l2cap_chan_tx_process(struct k_work *work)
917917
if (sent < 0) {
918918
if (sent == -EAGAIN) {
919919
ch->tx_buf = buf;
920+
/* If we don't reschedule, and the app doesn't nudge l2cap (e.g. by
921+
* sending another SDU), the channel will be stuck in limbo. To
922+
* prevent this, we attempt to re-schedule the work item for every
923+
* channel on every connection when an SDU has successfully been
924+
* sent.
925+
*/
920926
} else {
921927
net_buf_unref(buf);
922928
}
@@ -1847,6 +1853,17 @@ static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch)
18471853
k_work_submit(&ch->tx_work);
18481854
}
18491855

1856+
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
1857+
static void resume_all_channels(struct bt_conn *conn, void *data)
1858+
{
1859+
struct bt_l2cap_chan *chan;
1860+
1861+
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
1862+
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
1863+
}
1864+
}
1865+
#endif
1866+
18501867
static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data, int err)
18511868
{
18521869
struct l2cap_tx_meta_data *data = user_data;
@@ -1881,7 +1898,15 @@ static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data, int err)
18811898
cb(conn, cb_user_data, 0);
18821899
}
18831900

1901+
/* Resume the current channel */
18841902
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
1903+
1904+
if (IS_ENABLED(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)) {
1905+
/* Resume all other channels in case one might be stuck.
1906+
* The current channel has already been given priority.
1907+
*/
1908+
bt_conn_foreach(BT_CONN_TYPE_LE, resume_all_channels, NULL);
1909+
}
18851910
}
18861911

18871912
static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data, int err)

0 commit comments

Comments
 (0)