Skip to content

Commit cf84216

Browse files
Vudentzjhedberg
authored andcommitted
Bluetooth: L2CAP: Offload processing of tx_queue to a work
This offloads the processing of tx_queue to a work so the callbacks calling resume don't start sending packets directly which can cause stack overflow. Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent 99066db commit cf84216

File tree

2 files changed

+83
-61
lines changed

2 files changed

+83
-61
lines changed

include/bluetooth/l2cap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ struct bt_l2cap_le_chan {
126126
struct k_fifo tx_queue;
127127
/** Channel Pending Transmission buffer */
128128
struct net_buf *tx_buf;
129+
/** Channel Transmission work */
130+
struct k_work tx_work;
129131
/** Segment SDU packet from upper layer */
130132
struct net_buf *_sdu;
131133
u16_t _sdu_len;

subsys/bluetooth/host/l2cap.c

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -724,13 +724,54 @@ static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan)
724724
}
725725
}
726726

727+
static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch)
728+
{
729+
struct net_buf *buf;
730+
731+
/* Return current buffer */
732+
if (ch->tx_buf) {
733+
buf = ch->tx_buf;
734+
ch->tx_buf = NULL;
735+
return buf;
736+
}
737+
738+
return net_buf_get(&ch->tx_queue, K_NO_WAIT);
739+
}
740+
741+
static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
742+
struct net_buf **buf, u16_t sent);
743+
744+
static void l2cap_chan_tx_process(struct k_work *work)
745+
{
746+
struct bt_l2cap_le_chan *ch;
747+
struct net_buf *buf;
748+
749+
ch = CONTAINER_OF(work, struct bt_l2cap_le_chan, tx_work);
750+
751+
/* Resume tx in case there are buffers in the queue */
752+
while ((buf = l2cap_chan_le_get_tx_buf(ch))) {
753+
int sent = data_sent(buf)->len;
754+
755+
BT_DBG("buf %p sent %u", buf, sent);
756+
757+
sent = l2cap_chan_le_send_sdu(ch, &buf, sent);
758+
if (sent < 0) {
759+
if (sent == -EAGAIN) {
760+
ch->tx_buf = buf;
761+
}
762+
break;
763+
}
764+
}
765+
}
766+
727767
static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan)
728768
{
729769
BT_DBG("chan %p", chan);
730770

731771
(void)memset(&chan->tx, 0, sizeof(chan->tx));
732772
k_sem_init(&chan->tx.credits, 0, UINT_MAX);
733773
k_fifo_init(&chan->tx_queue);
774+
k_work_init(&chan->tx_work, l2cap_chan_tx_process);
734775
}
735776

736777
static void l2cap_chan_tx_give_credits(struct bt_l2cap_le_chan *chan,
@@ -1180,7 +1221,15 @@ static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch,
11801221
return seg;
11811222
}
11821223

1183-
static void l2cap_chan_le_send_resume(struct bt_l2cap_le_chan *ch);
1224+
static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch)
1225+
{
1226+
if (!k_sem_count_get(&ch->tx.credits) ||
1227+
(k_fifo_is_empty(&ch->tx_queue) && !ch->tx_buf)) {
1228+
return;
1229+
}
1230+
1231+
k_work_submit(&ch->tx_work);
1232+
}
11841233

11851234
static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data)
11861235
{
@@ -1192,7 +1241,7 @@ static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data)
11921241
chan->ops->sent(chan);
11931242
}
11941243

1195-
l2cap_chan_le_send_resume(BT_L2CAP_LE_CHAN(chan));
1244+
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
11961245
}
11971246

11981247
static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data)
@@ -1201,14 +1250,14 @@ static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data)
12011250

12021251
BT_DBG("conn %p chan %p", conn, chan);
12031252

1204-
l2cap_chan_le_send_resume(BT_L2CAP_LE_CHAN(chan));
1253+
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
12051254
}
12061255

12071256
static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf,
12081257
u16_t sdu_hdr_len)
12091258
{
12101259
struct net_buf *seg;
1211-
int len;
1260+
int len, err;
12121261

12131262
/* Wait for credits */
12141263
if (k_sem_take(&ch->tx.credits, K_NO_WAIT)) {
@@ -1224,7 +1273,7 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf,
12241273

12251274
/* Channel may have been disconnected while waiting for a buffer */
12261275
if (!ch->chan.conn) {
1227-
net_buf_unref(buf);
1276+
net_buf_unref(seg);
12281277
return -ECONNRESET;
12291278
}
12301279

@@ -1237,11 +1286,22 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf,
12371286
* callback has been set.
12381287
*/
12391288
if ((buf == seg || !buf->len) && ch->chan.ops->sent) {
1240-
bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
1241-
l2cap_chan_sdu_sent, &ch->chan);
1289+
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
1290+
l2cap_chan_sdu_sent, &ch->chan);
12421291
} else {
1243-
bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
1244-
l2cap_chan_seg_sent, &ch->chan);
1292+
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
1293+
l2cap_chan_seg_sent, &ch->chan);
1294+
}
1295+
1296+
if (err) {
1297+
BT_WARN("Unable to send seg %d", err);
1298+
k_sem_give(&ch->tx.credits);
1299+
1300+
if (err == -ENOBUFS) {
1301+
return -EAGAIN;
1302+
}
1303+
1304+
return err;
12451305
}
12461306

12471307
/* Check if there is no credits left clear output status and notify its
@@ -1314,44 +1374,6 @@ static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
13141374
return ret;
13151375
}
13161376

1317-
static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch)
1318-
{
1319-
struct net_buf *buf;
1320-
1321-
/* Return current buffer */
1322-
if (ch->tx_buf) {
1323-
buf = ch->tx_buf;
1324-
ch->tx_buf = NULL;
1325-
return buf;
1326-
}
1327-
1328-
return net_buf_get(&ch->tx_queue, K_NO_WAIT);
1329-
}
1330-
1331-
static void l2cap_chan_le_send_resume(struct bt_l2cap_le_chan *ch)
1332-
{
1333-
struct net_buf *buf;
1334-
1335-
if (!k_sem_count_get(&ch->tx.credits)) {
1336-
return;
1337-
}
1338-
1339-
/* Resume tx in case there are buffers in the queue */
1340-
while ((buf = l2cap_chan_le_get_tx_buf(ch))) {
1341-
u16_t sent = data_sent(buf)->len;
1342-
1343-
BT_DBG("buf %p sent %u", buf, sent);
1344-
1345-
sent = l2cap_chan_le_send_sdu(ch, &buf, sent);
1346-
if (sent < 0) {
1347-
if (sent == -EAGAIN) {
1348-
ch->tx_buf = buf;
1349-
}
1350-
break;
1351-
}
1352-
}
1353-
}
1354-
13551377
static void le_credits(struct bt_l2cap *l2cap, u8_t ident,
13561378
struct net_buf *buf)
13571379
{
@@ -1390,7 +1412,7 @@ static void le_credits(struct bt_l2cap *l2cap, u8_t ident,
13901412
BT_DBG("chan %p total credits %u", ch,
13911413
k_sem_count_get(&ch->tx.credits));
13921414

1393-
l2cap_chan_le_send_resume(ch);
1415+
l2cap_chan_tx_resume(ch);
13941416
}
13951417

13961418
static void reject_cmd(struct bt_l2cap *l2cap, u8_t ident,
@@ -1491,14 +1513,16 @@ static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan,
14911513
credits = chan->rx.init_credits;
14921514
}
14931515

1494-
l2cap_chan_rx_give_credits(chan, credits);
1495-
14961516
buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_LE_CREDITS, get_ident(),
14971517
sizeof(*ev));
14981518
if (!buf) {
1519+
BT_ERR("Unable to send credits update");
1520+
bt_l2cap_chan_disconnect(&chan->chan);
14991521
return;
15001522
}
15011523

1524+
l2cap_chan_rx_give_credits(chan, credits);
1525+
15021526
ev = net_buf_add(buf, sizeof(*ev));
15031527
ev->cid = sys_cpu_to_le16(chan->rx.cid);
15041528
ev->credits = sys_cpu_to_le16(credits);
@@ -1919,6 +1943,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
19191943

19201944
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
19211945
{
1946+
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
19221947
int err;
19231948

19241949
if (!buf) {
@@ -1936,24 +1961,19 @@ int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
19361961
return bt_l2cap_br_chan_send(chan, buf);
19371962
}
19381963

1939-
/* Attempt to resume first since there could be data from previous
1940-
* packets pending.
1941-
*/
1942-
l2cap_chan_le_send_resume(BT_L2CAP_LE_CHAN(chan));
1943-
1944-
/* Check if there are still pending segments */
1945-
if (BT_L2CAP_LE_CHAN(chan)->tx_buf) {
1946-
/* Queue buffer to be sent later */
1964+
/* Queue if there pending segments left from previous packets */
1965+
if (ch->tx_buf || !k_fifo_is_empty(&ch->tx_queue)) {
19471966
data_sent(buf)->len = 0;
1948-
net_buf_put(&(BT_L2CAP_LE_CHAN(chan))->tx_queue, buf);
1967+
net_buf_put(&ch->tx_queue, buf);
1968+
k_work_submit(&ch->tx_work);
19491969
return 0;
19501970
}
19511971

1952-
err = l2cap_chan_le_send_sdu(BT_L2CAP_LE_CHAN(chan), &buf, 0);
1972+
err = l2cap_chan_le_send_sdu(ch, &buf, 0);
19531973
if (err < 0) {
19541974
if (err == -EAGAIN && data_sent(buf)->len) {
19551975
/* Queue buffer if at least one segment could be sent */
1956-
net_buf_put(&(BT_L2CAP_LE_CHAN(chan))->tx_queue, buf);
1976+
net_buf_put(&ch->tx_queue, buf);
19571977
return data_sent(buf)->len;
19581978
}
19591979
BT_ERR("failed to send message %d", err);

0 commit comments

Comments
 (0)