Merge branch 'bugfix/fix_gatt_crash' into 'master'

fix(nimble): serialize NPL eventq ops to fix callout_reset race

Closes BLERP-2939

See merge request espressif/esp-idf!49967
This commit is contained in:
Rahul Tank
2026-06-29 10:21:02 +05:30
3 changed files with 265 additions and 43 deletions

View File

@@ -182,7 +182,6 @@ static void ble_hci_rx_acl(uint8_t *data, uint16_t len)
{
struct os_mbuf *m = NULL;
int rc;
int sr;
int retry_count = 1;
@@ -216,9 +215,7 @@ static void ble_hci_rx_acl(uint8_t *data, uint16_t len)
os_mbuf_free_chain(m);
return;
}
OS_ENTER_CRITICAL(sr);
ble_transport_to_hs_acl(m);
OS_EXIT_CRITICAL(sr);
}
#endif

View File

@@ -25,6 +25,8 @@
portMUX_TYPE ble_port_mutex = portMUX_INITIALIZER_UNLOCKED;
static SemaphoreHandle_t npl_eventq_sync;
#if BLE_NPL_USE_ESP_TIMER
static const char *TAG = "Timer";
#endif
@@ -197,6 +199,115 @@ IRAM_ATTR in_isr(void)
return xPortInIsrContext() != 0;
}
static void
npl_eventq_sync_init(void)
{
if (npl_eventq_sync == NULL) {
npl_eventq_sync = xSemaphoreCreateMutex();
BLE_LL_ASSERT(npl_eventq_sync);
}
}
static void
npl_eventq_lock(void)
{
if (!in_isr()) {
BLE_LL_ASSERT(npl_eventq_sync);
xSemaphoreTake(npl_eventq_sync, portMAX_DELAY);
}
}
static void
npl_eventq_unlock(void)
{
if (!in_isr()) {
xSemaphoreGive(npl_eventq_sync);
}
}
static bool IRAM_ATTR
npl_eventq_queued_get_isr(struct ble_npl_event_freertos *event)
{
bool queued;
portENTER_CRITICAL_ISR(&ble_port_mutex);
queued = event->queued;
portEXIT_CRITICAL_ISR(&ble_port_mutex);
return queued;
}
static void IRAM_ATTR
npl_eventq_queued_set_isr(struct ble_npl_event_freertos *event, bool queued)
{
portENTER_CRITICAL_ISR(&ble_port_mutex);
event->queued = queued;
portEXIT_CRITICAL_ISR(&ble_port_mutex);
}
static bool IRAM_ATTR
npl_eventq_queued_claim_isr(struct ble_npl_event_freertos *event)
{
bool already;
portENTER_CRITICAL_ISR(&ble_port_mutex);
already = event->queued;
if (!already) {
event->queued = true;
}
portEXIT_CRITICAL_ISR(&ble_port_mutex);
return already;
}
static void IRAM_ATTR
npl_eventq_queued_set_task(struct ble_npl_event_freertos *event, bool queued)
{
portENTER_CRITICAL(&ble_port_mutex);
event->queued = queued;
portEXIT_CRITICAL(&ble_port_mutex);
}
static bool IRAM_ATTR
npl_eventq_queued_get_task(struct ble_npl_event_freertos *event)
{
bool queued;
portENTER_CRITICAL(&ble_port_mutex);
queued = event->queued;
portEXIT_CRITICAL(&ble_port_mutex);
return queued;
}
static bool IRAM_ATTR
npl_eventq_queued_claim(struct ble_npl_event_freertos *event)
{
bool already;
portENTER_CRITICAL(&ble_port_mutex);
already = event->queued;
if (!already) {
event->queued = true;
}
portEXIT_CRITICAL(&ble_port_mutex);
return already;
}
static void IRAM_ATTR
npl_eventq_lost_event_clear(struct ble_npl_event *ev)
{
struct ble_npl_event_freertos *lost;
if (ev == NULL) {
return;
}
lost = (struct ble_npl_event_freertos *)ev->event;
if (lost == NULL) {
return;
}
lost->queued = false;
}
struct ble_npl_event *
IRAM_ATTR npl_freertos_eventq_get(struct ble_npl_eventq *evq, ble_npl_time_t tmo)
{
@@ -211,16 +322,63 @@ IRAM_ATTR npl_freertos_eventq_get(struct ble_npl_eventq *evq, ble_npl_time_t tmo
if( woken == pdTRUE ) {
portYIELD_FROM_ISR();
}
} else {
ret = xQueueReceive(eventq->q, &ev, tmo);
}
BLE_LL_ASSERT(ret == pdPASS || ret == errQUEUE_EMPTY);
BLE_LL_ASSERT(ret == pdPASS || ret == errQUEUE_EMPTY);
if (ev) {
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (event) {
event->queued = false;
}
if (ev) {
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (event) {
npl_eventq_queued_set_isr(event, false);
}
}
} else if (tmo == 0) {
npl_eventq_lock();
portENTER_CRITICAL(&ble_port_mutex);
ret = xQueueReceive(eventq->q, &ev, 0);
if (ret == pdPASS && ev != NULL) {
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (event) {
event->queued = false;
}
}
portEXIT_CRITICAL(&ble_port_mutex);
npl_eventq_unlock();
} else {
TickType_t deadline = 0;
TickType_t remaining;
if (tmo != portMAX_DELAY) {
deadline = xTaskGetTickCount() + tmo;
}
for (;;) {
if (tmo == portMAX_DELAY) {
ret = xQueuePeek(eventq->q, &ev, portMAX_DELAY);
} else {
remaining = deadline - xTaskGetTickCount();
if (remaining > tmo) {
return NULL;
}
ret = xQueuePeek(eventq->q, &ev, remaining);
}
if (ret != pdPASS) {
return NULL;
}
npl_eventq_lock();
portENTER_CRITICAL(&ble_port_mutex);
ret = xQueueReceive(eventq->q, &ev, 0);
if (ret == pdPASS && ev != NULL) {
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (event) {
event->queued = false;
}
portEXIT_CRITICAL(&ble_port_mutex);
npl_eventq_unlock();
break;
}
portEXIT_CRITICAL(&ble_port_mutex);
npl_eventq_unlock();
}
}
return ev;
@@ -234,22 +392,35 @@ IRAM_ATTR npl_freertos_eventq_put(struct ble_npl_eventq *evq, struct ble_npl_eve
struct ble_npl_eventq_freertos *eventq = (struct ble_npl_eventq_freertos *)evq->eventq;
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (event->queued) {
return;
}
event->queued = true;
if (in_isr()) {
if (npl_eventq_queued_claim_isr(event)) {
return;
}
ret = xQueueSendToBackFromISR(eventq->q, &ev, &woken);
if (ret != pdPASS) {
npl_eventq_queued_set_isr(event, false);
return;
}
if( woken == pdTRUE ) {
portYIELD_FROM_ISR();
}
return;
} else {
ret = xQueueSendToBack(eventq->q, &ev, portMAX_DELAY);
}
npl_eventq_lock();
BLE_LL_ASSERT(ret == pdPASS);
if (npl_eventq_queued_claim(event)) {
npl_eventq_unlock();
return;
}
ret = xQueueSendToBack(eventq->q, &ev, 0);
if (ret != pdPASS) {
ESP_LOGW("NimBLE", "eventq put: queue full, event dropped");
npl_eventq_queued_set_task(event, false);
}
npl_eventq_unlock();
}
}
void
@@ -260,22 +431,35 @@ IRAM_ATTR npl_freertos_eventq_put_to_front(struct ble_npl_eventq *evq, struct bl
struct ble_npl_eventq_freertos *eventq = (struct ble_npl_eventq_freertos *)evq->eventq;
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (event->queued) {
return;
}
event->queued = true;
if (in_isr()) {
if (npl_eventq_queued_claim_isr(event)) {
return;
}
ret = xQueueSendToFrontFromISR(eventq->q, &ev, &woken);
if (ret != pdPASS) {
npl_eventq_queued_set_isr(event, false);
return;
}
if( woken == pdTRUE ) {
portYIELD_FROM_ISR();
}
return;
} else {
ret = xQueueSendToFront(eventq->q, &ev, portMAX_DELAY);
}
npl_eventq_lock();
BLE_LL_ASSERT(ret == pdPASS);
if (npl_eventq_queued_claim(event)) {
npl_eventq_unlock();
return;
}
ret = xQueueSendToFront(eventq->q, &ev, 0);
if (ret != pdPASS) {
ESP_LOGW("NimBLE", "eventq put_to_front: queue full, event dropped");
npl_eventq_queued_set_task(event, false);
}
npl_eventq_unlock();
}
}
void
@@ -286,14 +470,11 @@ IRAM_ATTR npl_freertos_eventq_remove(struct ble_npl_eventq *evq,
BaseType_t ret;
int i;
int count;
bool removed;
BaseType_t woken, woken2;
struct ble_npl_eventq_freertos *eventq = (struct ble_npl_eventq_freertos *)evq->eventq;
struct ble_npl_event_freertos *event = (struct ble_npl_event_freertos *)ev->event;
if (!event->queued) {
return;
}
/*
* XXX We cannot extract element from inside FreeRTOS queue so as a quick
* workaround we'll just remove all elements and add them back except the
@@ -302,46 +483,77 @@ IRAM_ATTR npl_freertos_eventq_remove(struct ble_npl_eventq *evq,
*/
if (in_isr()) {
if (!npl_eventq_queued_get_isr(event)) {
return;
}
removed = false;
woken = pdFALSE;
portENTER_CRITICAL_ISR(&ble_port_mutex);
count = uxQueueMessagesWaitingFromISR(eventq->q);
for (i = 0; i < count; i++) {
ret = xQueueReceiveFromISR(eventq->q, &tmp_ev, &woken2);
BLE_LL_ASSERT(ret == pdPASS);
if (ret != pdPASS) {
break;
}
woken |= woken2;
if (tmp_ev == ev) {
removed = true;
continue;
}
ret = xQueueSendToBackFromISR(eventq->q, &tmp_ev, &woken2);
BLE_LL_ASSERT(ret == pdPASS);
if (ret != pdPASS) {
npl_eventq_lost_event_clear(tmp_ev);
break;
}
woken |= woken2;
}
if (removed) {
event->queued = false;
}
portEXIT_CRITICAL_ISR(&ble_port_mutex);
if( woken == pdTRUE ) {
portYIELD_FROM_ISR();
}
} else {
portENTER_CRITICAL(&ble_port_mutex);
removed = false;
npl_eventq_lock();
if (!npl_eventq_queued_get_task(event)) {
npl_eventq_unlock();
return;
}
portENTER_CRITICAL(&ble_port_mutex);
count = uxQueueMessagesWaiting(eventq->q);
for (i = 0; i < count; i++) {
ret = xQueueReceive(eventq->q, &tmp_ev, 0);
BLE_LL_ASSERT(ret == pdPASS);
if (ret != pdPASS) {
break;
}
if (tmp_ev == ev) {
removed = true;
continue;
}
ret = xQueueSendToBack(eventq->q, &tmp_ev, 0);
BLE_LL_ASSERT(ret == pdPASS);
if (ret != pdPASS) {
npl_eventq_lost_event_clear(tmp_ev);
break;
}
}
if (removed) {
event->queued = 0;
}
portEXIT_CRITICAL(&ble_port_mutex);
}
event->queued = 0;
npl_eventq_unlock();
}
}
ble_npl_error_t
@@ -1129,6 +1341,9 @@ int npl_freertos_set_controller_npl_info(ble_npl_count_info_t *ctrl_npl_info)
int npl_freertos_mempool_init(void)
{
int rc = -1;
npl_eventq_sync_init();
uint16_t ble_total_evt_count = 0;
uint16_t ble_total_co_count = 0;
uint16_t ble_total_evtq_count = 0;
@@ -1218,6 +1433,11 @@ int npl_freertos_mempool_init(void)
return 0;
_error:
if (npl_eventq_sync) {
vSemaphoreDelete(npl_eventq_sync);
npl_eventq_sync = NULL;
}
if (ble_freertos_ev_buf) {
bt_osi_mem_free_internal(ble_freertos_ev_buf);
ble_freertos_ev_buf = NULL;
@@ -1247,6 +1467,11 @@ _error:
void npl_freertos_mempool_deinit(void)
{
if (npl_eventq_sync) {
vSemaphoreDelete(npl_eventq_sync);
npl_eventq_sync = NULL;
}
if (ble_freertos_ev_buf) {
bt_osi_mem_free_internal(ble_freertos_ev_buf);
ble_freertos_ev_buf = NULL;