From 1c6ed642bf2d999816d90c979101decad4c25cab Mon Sep 17 00:00:00 2001 From: Felipe Moura Date: Tue, 29 Sep 2026 18:20:08 -0300 Subject: [PATCH] espressif: stop leaking a Wi-Fi interrupt handle on every esp_wifi_start() set_intr_wrapper() allocates a new intr_handle_data_t from the kernel heap each time the Wi-Fi driver calls it, and the driver calls it on every esp_wifi_start() -- twice per start on esp32s3 -- not only the first time. clear_intr_wrapper() is a no-op, so the IRQ still holds the handle from the previous start: esp_set_handle() refuses to replace it with -EINVAL, the return value is ignored, and the new block is lost. Any application that stops and restarts Wi-Fi to save power therefore loses a few bytes of kernel heap per cycle, without bound. Look up the vector descriptor first, then reuse the handle already registered for the IRQ and only allocate and register one when there is none. A failed descriptor lookup no longer touches the registered handle. The same code is present in the esp32, esp32s2, esp32s3, esp32c3 and esp32c6 Wi-Fi adapters; all five are fixed the same way. Signed-off-by: Felipe Moura Assisted-by: Claude Opus 5.5 --- arch/risc-v/src/esp32c3/esp_wifi_adapter.c | 39 ++++++++++++------ arch/risc-v/src/esp32c6/esp_wifi_adapter.c | 39 ++++++++++++------ arch/xtensa/src/esp32/esp32_wifi_adapter.c | 41 ++++++++++++------- .../xtensa/src/esp32s2/esp32s2_wifi_adapter.c | 41 ++++++++++++------- .../xtensa/src/esp32s3/esp32s3_wifi_adapter.c | 41 ++++++++++++------- 5 files changed, 135 insertions(+), 66 deletions(-) diff --git a/arch/risc-v/src/esp32c3/esp_wifi_adapter.c b/arch/risc-v/src/esp32c3/esp_wifi_adapter.c index 30be4f099dc..b95031714a6 100644 --- a/arch/risc-v/src/esp32c3/esp_wifi_adapter.c +++ b/arch/risc-v/src/esp32c3/esp_wifi_adapter.c @@ -558,6 +558,7 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) { intr_handle_t handle; + vector_desc_t *desc; int irq = ESP_SOURCE2IRQ(intr_source); esp_err_t err; @@ -569,26 +570,40 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, esprv_int_set_priority(intr_num, intr_prio); esprv_int_set_type(intr_num, INTR_TYPE_LEVEL); - handle = kmm_calloc(1, sizeof(intr_handle_data_t)); - if (handle == NULL) - { - wlerr("Failed to kmm_calloc\n"); - return; - } - - handle->vector_desc = get_desc_for_int(intr_num, cpu_no); - if (handle->vector_desc == NULL) + desc = get_desc_for_int(intr_num, cpu_no); + if (desc == NULL) { wlerr("get_desc_for_int failed\n"); - kmm_free(handle); return; } + /* The Wi-Fi driver calls this on every esp_wifi_start(), not only on + * the first one, and nothing clears the handle in between (see + * clear_intr_wrapper()). Reuse the registered handle: a new one would + * be refused by esp_set_handle() and leaked. + */ + + handle = esp_get_handle(cpu_no, irq); + if (handle == IRQ_UNMAPPED) + { + handle = kmm_calloc(1, sizeof(intr_handle_data_t)); + if (handle == NULL) + { + wlerr("Failed to kmm_calloc\n"); + return; + } + + /* Register the handle - it contains all needed information + * (cpuint, cpu) + */ + + esp_set_handle(cpu_no, irq, handle); + } + + handle->vector_desc = desc; handle->vector_desc->source = intr_source; handle->shared_vector_desc = NULL; - esp_set_handle(cpu_no, irq, handle); - err = esp_intr_set_in_iram(handle, false); if (err != ESP_OK) { diff --git a/arch/risc-v/src/esp32c6/esp_wifi_adapter.c b/arch/risc-v/src/esp32c6/esp_wifi_adapter.c index f770a0f2e2b..c18f7122f37 100644 --- a/arch/risc-v/src/esp32c6/esp_wifi_adapter.c +++ b/arch/risc-v/src/esp32c6/esp_wifi_adapter.c @@ -591,6 +591,7 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) { intr_handle_t handle; + vector_desc_t *desc; int irq = ESP_SOURCE2IRQ(intr_source); esp_err_t err; @@ -602,26 +603,40 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, esprv_int_set_priority(intr_num, intr_prio); esprv_int_set_type(intr_num, INTR_TYPE_LEVEL); - handle = kmm_calloc(1, sizeof(intr_handle_data_t)); - if (handle == NULL) - { - wlerr("Failed to kmm_calloc\n"); - return; - } - - handle->vector_desc = get_desc_for_int(intr_num, cpu_no); - if (handle->vector_desc == NULL) + desc = get_desc_for_int(intr_num, cpu_no); + if (desc == NULL) { wlerr("get_desc_for_int failed\n"); - kmm_free(handle); return; } + /* The Wi-Fi driver calls this on every esp_wifi_start(), not only on + * the first one, and nothing clears the handle in between (see + * clear_intr_wrapper()). Reuse the registered handle: a new one would + * be refused by esp_set_handle() and leaked. + */ + + handle = esp_get_handle(cpu_no, irq); + if (handle == IRQ_UNMAPPED) + { + handle = kmm_calloc(1, sizeof(intr_handle_data_t)); + if (handle == NULL) + { + wlerr("Failed to kmm_calloc\n"); + return; + } + + /* Register the handle - it contains all needed information + * (cpuint, cpu) + */ + + esp_set_handle(cpu_no, irq, handle); + } + + handle->vector_desc = desc; handle->vector_desc->source = intr_source; handle->shared_vector_desc = NULL; - esp_set_handle(cpu_no, irq, handle); - err = esp_intr_set_in_iram(handle, false); if (err != ESP_OK) { diff --git a/arch/xtensa/src/esp32/esp32_wifi_adapter.c b/arch/xtensa/src/esp32/esp32_wifi_adapter.c index 2b60394f1d7..0bbf485a303 100644 --- a/arch/xtensa/src/esp32/esp32_wifi_adapter.c +++ b/arch/xtensa/src/esp32/esp32_wifi_adapter.c @@ -1765,6 +1765,7 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) { intr_handle_t handle; + vector_desc_t *desc; int irq = ESP_SOURCE2IRQ(intr_source); esp_err_t err; @@ -1774,28 +1775,40 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, esp_rom_route_intr_matrix(cpu_no, intr_source, intr_num); - handle = kmm_calloc(1, sizeof(intr_handle_data_t)); - if (handle == NULL) - { - wlerr("Failed to kmm_calloc\n"); - return; - } - - handle->vector_desc = get_desc_for_int(intr_num, cpu_no); - if (handle->vector_desc == NULL) + desc = get_desc_for_int(intr_num, cpu_no); + if (desc == NULL) { wlerr("get_desc_for_int failed\n"); - kmm_free(handle); return; } + /* The Wi-Fi driver calls this on every esp_wifi_start(), not only on + * the first one, and nothing clears the handle in between (see + * clear_intr_wrapper()). Reuse the registered handle: a new one would + * be refused by esp_set_handle() and leaked. + */ + + handle = esp_get_handle(cpu_no, irq); + if (handle == IRQ_UNMAPPED) + { + handle = kmm_calloc(1, sizeof(intr_handle_data_t)); + if (handle == NULL) + { + wlerr("Failed to kmm_calloc\n"); + return; + } + + /* Register the handle - it contains all needed information + * (cpuint, cpu) + */ + + esp_set_handle(cpu_no, irq, handle); + } + + handle->vector_desc = desc; handle->vector_desc->source = intr_source; handle->shared_vector_desc = NULL; - /* Register the handle - it contains all needed information (cpuint, cpu) */ - - esp_set_handle(cpu_no, irq, handle); - err = esp_intr_set_in_iram(handle, false); if (err != OK) { diff --git a/arch/xtensa/src/esp32s2/esp32s2_wifi_adapter.c b/arch/xtensa/src/esp32s2/esp32s2_wifi_adapter.c index 58fea3062b2..bc1a44a7a66 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_wifi_adapter.c +++ b/arch/xtensa/src/esp32s2/esp32s2_wifi_adapter.c @@ -1623,6 +1623,7 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) { intr_handle_t handle; + vector_desc_t *desc; int irq = ESP_SOURCE2IRQ(intr_source); esp_err_t err; @@ -1632,28 +1633,40 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, esp_rom_route_intr_matrix(cpu_no, intr_source, intr_num); - handle = kmm_calloc(1, sizeof(intr_handle_data_t)); - if (handle == NULL) - { - wlerr("Failed to kmm_calloc\n"); - return; - } - - handle->vector_desc = get_desc_for_int(intr_num, cpu_no); - if (handle->vector_desc == NULL) + desc = get_desc_for_int(intr_num, cpu_no); + if (desc == NULL) { wlerr("get_desc_for_int failed\n"); - kmm_free(handle); return; } + /* The Wi-Fi driver calls this on every esp_wifi_start(), not only on + * the first one, and nothing clears the handle in between (see + * clear_intr_wrapper()). Reuse the registered handle: a new one would + * be refused by esp_set_handle() and leaked. + */ + + handle = esp_get_handle(cpu_no, irq); + if (handle == IRQ_UNMAPPED) + { + handle = kmm_calloc(1, sizeof(intr_handle_data_t)); + if (handle == NULL) + { + wlerr("Failed to kmm_calloc\n"); + return; + } + + /* Register the handle - it contains all needed information + * (cpuint, cpu) + */ + + esp_set_handle(cpu_no, irq, handle); + } + + handle->vector_desc = desc; handle->vector_desc->source = intr_source; handle->shared_vector_desc = NULL; - /* Register the handle - it contains all needed information (cpuint, cpu) */ - - esp_set_handle(cpu_no, irq, handle); - err = esp_intr_set_in_iram(handle, false); if (err != OK) { diff --git a/arch/xtensa/src/esp32s3/esp32s3_wifi_adapter.c b/arch/xtensa/src/esp32s3/esp32s3_wifi_adapter.c index ca7f5dfced1..278fa70bc3a 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_wifi_adapter.c +++ b/arch/xtensa/src/esp32s3/esp32s3_wifi_adapter.c @@ -1755,6 +1755,7 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) { intr_handle_t handle; + vector_desc_t *desc; int irq = ESP_SOURCE2IRQ(intr_source); esp_err_t err; @@ -1764,28 +1765,40 @@ static void set_intr_wrapper(int32_t cpu_no, uint32_t intr_source, esp_rom_route_intr_matrix(cpu_no, intr_source, intr_num); - handle = kmm_calloc(1, sizeof(intr_handle_data_t)); - if (handle == NULL) - { - wlerr("Failed to kmm_calloc\n"); - return; - } - - handle->vector_desc = get_desc_for_int(intr_num, cpu_no); - if (handle->vector_desc == NULL) + desc = get_desc_for_int(intr_num, cpu_no); + if (desc == NULL) { wlerr("get_desc_for_int failed\n"); - kmm_free(handle); return; } + /* The Wi-Fi driver calls this on every esp_wifi_start(), not only on + * the first one, and nothing clears the handle in between (see + * clear_intr_wrapper()). Reuse the registered handle: a new one would + * be refused by esp_set_handle() and leaked. + */ + + handle = esp_get_handle(cpu_no, irq); + if (handle == IRQ_UNMAPPED) + { + handle = kmm_calloc(1, sizeof(intr_handle_data_t)); + if (handle == NULL) + { + wlerr("Failed to kmm_calloc\n"); + return; + } + + /* Register the handle - it contains all needed information + * (cpuint, cpu) + */ + + esp_set_handle(cpu_no, irq, handle); + } + + handle->vector_desc = desc; handle->vector_desc->source = intr_source; handle->shared_vector_desc = NULL; - /* Register the handle - it contains all needed information (cpuint, cpu) */ - - esp_set_handle(cpu_no, irq, handle); - err = esp_intr_set_in_iram(handle, false); if (err != OK) {