Skip to content

Commit 10841b9

Browse files
joerchannashif
authored andcommitted
Bluetooth: host: Release ATT request buffers once sent
The ATT request buffers are held until the ATT response has been received. This means that the ATT request buffers are released by the RX thread, instead of the from the RX priority context of num_complete. This can cause a deadlock in the RX thread when we allocate buffers and all the available buffers are ATT requests, since the RX thread is the only thread that can release buffers. Release the ATT request buffers once they have been sent and instead handle ATT request resending by reconstructing the buffer from the GATT parameters. Also re-order the order of resource allocation by allocating the request context before the buffer. This ensures that we cannot allocate more buffers for ATT requests than there are ATT requests. Fixed a buf reference leak that could occur when the ATT request buffer has been allocated, but GATT returns an error before handing the responsebility of the buffer to ATT, for example when bt_att_req_alloc fails. This is fixed by moving the functionality of att_req_destroy to bt_att_req_free. Signed-off-by: Joakim Andersson <[email protected]>
1 parent 5cbfdf6 commit 10841b9

File tree

3 files changed

+382
-270
lines changed

3 files changed

+382
-270
lines changed

subsys/bluetooth/host/att.c

Lines changed: 56 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,6 @@ K_MEM_SLAB_DEFINE(chan_slab, sizeof(struct bt_att_chan),
112112
__alignof__(struct bt_att_chan));
113113
static struct bt_att_req cancel;
114114

115-
static void att_req_destroy(struct bt_att_req *req)
116-
{
117-
BT_DBG("req %p", req);
118-
119-
if (req->buf) {
120-
net_buf_unref(req->buf);
121-
req->buf = NULL;
122-
}
123-
124-
bt_att_req_free(req);
125-
}
126-
127115
typedef void (*bt_att_chan_sent_t)(struct bt_att_chan *chan);
128116

129117
static bt_att_chan_sent_t chan_cb(struct net_buf *buf);
@@ -201,19 +189,14 @@ static int chan_send(struct bt_att_chan *chan, struct net_buf *buf,
201189

202190
chan->sent = cb ? cb : chan_cb(buf);
203191

204-
/* Take a ref since bt_l2cap_send_cb takes ownership of the buffer */
205192
err = bt_l2cap_send_cb(chan->att->conn, BT_L2CAP_CID_ATT,
206-
net_buf_ref(buf), att_cb(chan->sent),
207-
&chan->chan.chan);
208-
if (!err) {
209-
net_buf_unref(buf);
210-
return 0;
193+
buf, att_cb(chan->sent),
194+
&chan->chan.chan);
195+
if (err) {
196+
net_buf_simple_restore(&buf->b, &state);
211197
}
212198

213-
net_buf_simple_restore(&buf->b, &state);
214-
215199
return err;
216-
217200
}
218201

219202
static int process_queue(struct bt_att_chan *chan, struct k_fifo *queue)
@@ -239,6 +222,7 @@ static int process_queue(struct bt_att_chan *chan, struct k_fifo *queue)
239222
/* Send requests without taking tx_sem */
240223
static int chan_req_send(struct bt_att_chan *chan, struct bt_att_req *req)
241224
{
225+
struct net_buf *buf;
242226
int err;
243227

244228
if (chan->chan.tx.mtu < net_buf_frags_len(req->buf)) {
@@ -250,19 +234,14 @@ static int chan_req_send(struct bt_att_chan *chan, struct bt_att_req *req)
250234

251235
chan->req = req;
252236

253-
/* Save request state so it can be resent */
254-
net_buf_simple_save(&req->buf->b, &req->state);
237+
/* Release since bt_l2cap_send_cb takes ownership of the buffer */
238+
buf = req->buf;
239+
req->buf = NULL;
255240

256-
/* Keep a reference for resending the req in case the security
257-
* needs to be changed.
258-
*/
259-
err = chan_send(chan, net_buf_ref(req->buf), NULL);
241+
err = chan_send(chan, buf, NULL);
260242
if (err) {
261-
/* Drop the extra reference if buffer could not be sent but
262-
* don't reset the buffer as it will likelly be pushed back to
263-
* request queue to be send later.
264-
*/
265-
net_buf_unref(req->buf);
243+
/* We still have the ownership of the buffer */
244+
req->buf = buf;
266245
}
267246

268247
return err;
@@ -607,19 +586,13 @@ static uint8_t att_handle_rsp(struct bt_att_chan *chan, void *pdu, uint16_t len,
607586
goto process;
608587
}
609588

610-
/* Release original buffer */
611-
if (chan->req->buf) {
612-
net_buf_unref(chan->req->buf);
613-
chan->req->buf = NULL;
614-
}
615-
616589
/* Reset func so it can be reused by the callback */
617590
func = chan->req->func;
618591
chan->req->func = NULL;
619592
params = chan->req->user_data;
620593

621594
/* free allocated request so its memory can be reused */
622-
att_req_destroy(chan->req);
595+
bt_att_req_free(chan->req);
623596
chan->req = NULL;
624597

625598
process:
@@ -2040,11 +2013,6 @@ static uint8_t att_error_rsp(struct bt_att_chan *chan, struct net_buf *buf)
20402013
goto done;
20412014
}
20422015

2043-
if (chan->req->buf) {
2044-
/* Restore state to be resent */
2045-
net_buf_simple_restore(&chan->req->buf->b, &chan->req->state);
2046-
}
2047-
20482016
err = rsp->error;
20492017
#if defined(CONFIG_BT_SMP)
20502018
/* Check if error can be handled by elevating security. */
@@ -2530,7 +2498,7 @@ static void att_reset(struct bt_att *att)
25302498
req->user_data);
25312499
}
25322500

2533-
att_req_destroy(req);
2501+
bt_att_req_free(req);
25342502
}
25352503

25362504
k_mem_slab_free(&att_slab, (void **)&att);
@@ -2646,12 +2614,43 @@ static void bt_att_disconnected(struct bt_l2cap_chan *chan)
26462614
}
26472615

26482616
#if defined(CONFIG_BT_SMP)
2617+
static uint8_t att_req_retry(struct bt_att_chan *att_chan)
2618+
{
2619+
struct bt_att_req *req = att_chan->req;
2620+
struct net_buf *buf;
2621+
2622+
/* Resend buffer */
2623+
if (!req->encode) {
2624+
/* This request does not support resending */
2625+
return BT_ATT_ERR_AUTHENTICATION;
2626+
}
2627+
2628+
2629+
buf = bt_att_chan_create_pdu(att_chan, req->att_op, req->len);
2630+
if (!buf) {
2631+
return BT_ATT_ERR_UNLIKELY;
2632+
}
2633+
2634+
if (req->encode(buf, req->len, req->user_data)) {
2635+
net_buf_unref(buf);
2636+
return BT_ATT_ERR_UNLIKELY;
2637+
}
2638+
2639+
if (chan_send(att_chan, buf, NULL)) {
2640+
net_buf_unref(buf);
2641+
return BT_ATT_ERR_UNLIKELY;
2642+
}
2643+
2644+
return BT_ATT_ERR_SUCCESS;
2645+
}
2646+
26492647
static void bt_att_encrypt_change(struct bt_l2cap_chan *chan,
26502648
uint8_t hci_status)
26512649
{
26522650
struct bt_att_chan *att_chan = ATT_CHAN(chan);
26532651
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
26542652
struct bt_conn *conn = ch->chan.conn;
2653+
uint8_t err;
26552654

26562655
BT_DBG("chan %p conn %p handle %u sec_level 0x%02x status 0x%02x", ch,
26572656
conn, conn->handle, conn->sec_level, hci_status);
@@ -2686,14 +2685,10 @@ static void bt_att_encrypt_change(struct bt_l2cap_chan *chan,
26862685

26872686
BT_DBG("Retrying");
26882687

2689-
/* Resend buffer */
2690-
2691-
/* Since packets are created in ATT and released in L2CAP we need to
2692-
* take a new reference to "create" the packet in ATT again.
2693-
*/
2694-
if (chan_send(att_chan, net_buf_ref(att_chan->req->buf), NULL)) {
2695-
net_buf_unref(att_chan->req->buf);
2696-
att_handle_rsp(att_chan, NULL, 0, BT_ATT_ERR_AUTHENTICATION);
2688+
err = att_req_retry(att_chan);
2689+
if (err) {
2690+
BT_DBG("Retry failed (%d)", err);
2691+
att_handle_rsp(att_chan, NULL, 0, err);
26972692
}
26982693
}
26992694
#endif /* CONFIG_BT_SMP */
@@ -2932,6 +2927,7 @@ struct bt_att_req *bt_att_req_alloc(k_timeout_t timeout)
29322927

29332928
/* Reserve space for request */
29342929
if (k_mem_slab_alloc(&req_slab, (void **)&req, timeout)) {
2930+
BT_DBG("No space for req");
29352931
return NULL;
29362932
}
29372933

@@ -2946,6 +2942,11 @@ void bt_att_req_free(struct bt_att_req *req)
29462942
{
29472943
BT_DBG("req %p", req);
29482944

2945+
if (req->buf) {
2946+
net_buf_unref(req->buf);
2947+
req->buf = NULL;
2948+
}
2949+
29492950
k_mem_slab_free(&req_slab, (void **)&req);
29502951
}
29512952

@@ -3003,8 +3004,6 @@ int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req)
30033004

30043005
att = att_get(conn);
30053006
if (!att) {
3006-
net_buf_unref(req->buf);
3007-
req->buf = NULL;
30083007
return -ENOTCONN;
30093008
}
30103009

@@ -3037,7 +3036,7 @@ static bool bt_att_chan_req_cancel(struct bt_att_chan *chan,
30373036

30383037
chan->req = &cancel;
30393038

3040-
att_req_destroy(req);
3039+
bt_att_req_free(req);
30413040

30423041
return true;
30433042
}
@@ -3068,5 +3067,5 @@ void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req)
30683067
/* Remove request from the list */
30693068
sys_slist_find_and_remove(&att->reqs, &req->node);
30703069

3071-
att_req_destroy(req);
3070+
bt_att_req_free(req);
30723071
}

subsys/bluetooth/host/att_internal.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,19 @@ typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err,
263263
const void *pdu, uint16_t length,
264264
void *user_data);
265265

266+
typedef int (*bt_att_encode_t)(struct net_buf *buf, size_t len,
267+
void *user_data);
268+
266269
/* ATT request context */
267270
struct bt_att_req {
268271
sys_snode_t node;
269272
bt_att_func_t func;
270-
struct net_buf_simple_state state;
271273
struct net_buf *buf;
272274
#if defined(CONFIG_BT_SMP)
273-
bool retrying;
275+
bt_att_encode_t encode;
276+
uint8_t retrying : 1;
277+
uint8_t att_op;
278+
size_t len;
274279
#endif /* CONFIG_BT_SMP */
275280
void *user_data;
276281
};

0 commit comments

Comments
 (0)