Skip to content

Commit bc7ce86

Browse files
Vudentzcarlescufi
authored andcommitted
Bluetooth: ATT: Fix not processing pending requests
Since the TX semaphore is used for all types of PDUs a request may have to be put on the request list while there is no pending request pending which means no response will be generated to trigger att_process, previously this condition was handled by setting the request as currently pending and append its buffer to tx_queue but this is no longer efficient since there could be more than one channel active the code should try all of them before queueing back to request list. To fix this the request list will now be processed each time a PDU has been sent. Fixes zephyrproject-rtos#26070 Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent 4418ba7 commit bc7ce86

File tree

1 file changed

+47
-26
lines changed
  • subsys/bluetooth/host

1 file changed

+47
-26
lines changed

subsys/bluetooth/host/att.c

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,8 @@ static int process_queue(struct bt_att_chan *chan, struct k_fifo *queue)
196196
struct net_buf *buf;
197197
int err;
198198

199-
while ((buf = net_buf_get(queue, K_NO_WAIT))) {
200-
/* Check if the queued buf is a request */
201-
if (chan->req && chan->req->buf == buf) {
202-
/* Save request state so it can be resent */
203-
net_buf_simple_save(&chan->req->buf->b,
204-
&chan->req->state);
205-
}
206-
199+
buf = net_buf_get(queue, K_NO_WAIT);
200+
if (buf) {
207201
err = chan_send(chan, buf, NULL);
208202
if (err) {
209203
/* Push it back if it could not be send */
@@ -217,6 +211,33 @@ static int process_queue(struct bt_att_chan *chan, struct k_fifo *queue)
217211
return -ENOENT;
218212
}
219213

214+
/* Send requests without taking tx_sem */
215+
static int chan_req_send(struct bt_att_chan *chan, struct bt_att_req *req)
216+
{
217+
int err;
218+
219+
if (chan->chan.tx.mtu < net_buf_frags_len(req->buf)) {
220+
return -EMSGSIZE;
221+
}
222+
223+
BT_DBG("chan %p req %p len %zu", chan, req,
224+
net_buf_frags_len(req->buf));
225+
226+
chan->req = req;
227+
228+
/* Save request state so it can be resent */
229+
net_buf_simple_save(&req->buf->b, &req->state);
230+
231+
/* Keep a reference for resending in case of an error */
232+
err = chan_send(chan, net_buf_ref(req->buf), NULL);
233+
if (err < 0) {
234+
net_buf_unref(req->buf);
235+
chan->req = NULL;
236+
}
237+
238+
return err;
239+
}
240+
220241
static void bt_att_sent(struct bt_l2cap_chan *ch)
221242
{
222243
struct bt_att_chan *chan = ATT_CHAN(ch);
@@ -231,7 +252,23 @@ static void bt_att_sent(struct bt_l2cap_chan *ch)
231252

232253
atomic_clear_bit(chan->flags, ATT_PENDING_SENT);
233254

234-
/* Process channel queue first */
255+
/* Process pending requests first since they require a response they
256+
* can only be processed one at time while if other queues were
257+
* processed before they may always contain a buffer starving the
258+
* request queue.
259+
*/
260+
if (!chan->req && !sys_slist_is_empty(&att->reqs)) {
261+
sys_snode_t *node = sys_slist_get(&att->reqs);
262+
263+
if (chan_req_send(chan, ATT_REQ(node)) >= 0) {
264+
return;
265+
}
266+
267+
/* Prepend back to the list as it could not be sent */
268+
sys_slist_prepend(&att->reqs, node);
269+
}
270+
271+
/* Process channel queue */
235272
err = process_queue(chan, &chan->tx_queue);
236273
if (!err) {
237274
return;
@@ -448,29 +485,13 @@ static int bt_att_chan_req_send(struct bt_att_chan *chan,
448485

449486
BT_DBG("req %p", req);
450487

451-
if (chan->chan.tx.mtu < net_buf_frags_len(req->buf)) {
452-
return -EMSGSIZE;
453-
}
454-
455488
if (k_sem_take(&chan->tx_sem, K_NO_WAIT) < 0) {
456489
return -EAGAIN;
457490
}
458491

459-
BT_DBG("chan %p req %p len %zu", chan, req,
460-
net_buf_frags_len(req->buf));
461-
462-
chan->req = req;
463-
464-
/* Save request state so it can be resent */
465-
net_buf_simple_save(&req->buf->b, &req->state);
466-
467-
/* Keep a reference for resending in case of an error */
468-
err = chan_send(chan, net_buf_ref(req->buf), NULL);
492+
err = chan_req_send(chan, req);
469493
if (err < 0) {
470-
net_buf_unref(req->buf);
471-
req->buf = NULL;
472494
k_sem_give(&chan->tx_sem);
473-
chan->req = NULL;
474495
}
475496

476497
return err;

0 commit comments

Comments
 (0)