From 46ed0658a695932ee0ed4598a062f82f60dcd4dc Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Thu, 4 Jun 2020 16:33:00 +0200 Subject: [PATCH] l2cap/coc: make coc_continue_tx() thread safe ble_l2cap_coc_continue_tx() is executed in different tasks: it is (i) called from ble_l2cap_coc_send() in one or more different task contexts from user application(s) and (ii) called from ble_l2cap_coc_le_credits_update() from the NimBLE host task. Typically, the host task has a higher priority then the user tasks, so that ble_l2cap_coc_continue_tx() can be interrupted by itself. This leads to potentially inconsistent state for `chan->coc_tx->credits` and `chan->coc_tx->data_offset`, consequently leading to dropped packets. This commit fixes this by putting all the code code working on these variables into a critical section. --- nimble/host/src/ble_l2cap_coc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nimble/host/src/ble_l2cap_coc.c b/nimble/host/src/ble_l2cap_coc.c index b15646f62..2e67977d6 100644 --- a/nimble/host/src/ble_l2cap_coc.c +++ b/nimble/host/src/ble_l2cap_coc.c @@ -403,9 +403,12 @@ ble_l2cap_coc_continue_tx(struct ble_l2cap_chan *chan) uint16_t sdu_size_offset; int rc; + ble_hs_lock(); + /* If there is no data to send, just return success */ tx = &chan->coc_tx; if (!tx->sdu) { + ble_hs_unlock(); return 0; } @@ -458,10 +461,8 @@ ble_l2cap_coc_continue_tx(struct ble_l2cap_chan *chan) goto failed; } - ble_hs_lock(); conn = ble_hs_conn_find_assert(chan->conn_handle); rc = ble_l2cap_tx(conn, chan, txom); - ble_hs_unlock(); if (rc) { /* txom is consumed by l2cap */ @@ -491,14 +492,18 @@ ble_l2cap_coc_continue_tx(struct ble_l2cap_chan *chan) if (tx->sdu) { /* Not complete SDU sent, wait for credits */ tx->flags |= BLE_L2CAP_COC_FLAG_STALLED; + ble_hs_unlock(); return BLE_HS_ESTALLED; } + ble_hs_unlock(); return 0; failed: os_mbuf_free_chain(tx->sdu); tx->sdu = NULL; + ble_hs_unlock(); + os_mbuf_free_chain(txom); if (tx->flags & BLE_L2CAP_COC_FLAG_STALLED) { ble_l2cap_event_coc_unstalled(chan, rc);