diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index d3927cd48d82a..83fca88b6972b 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -74,5 +74,9 @@ list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) # Enable the panic handler wrapper idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) +# Patch LWIP memory pool allocators (see lwip_patch.c) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) + # Define the project. project(micropython) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index c7d326c89e8ae..c7179b6125a1e 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -133,3 +133,7 @@ CONFIG_NEWLIB_NANO_FORMAT=y # Due to limitations in the PMP system this feature breaks native emitters # so is disabled by default. CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=n + +# Further limit total sockets in TIME-WAIT when there are many short-lived +# connections. +CONFIG_LWIP_MAX_ACTIVE_TCP=12 diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 2c81e8c2b3a50..565f6feec2324 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -108,6 +108,7 @@ list(APPEND MICROPY_SOURCE_PORT network_wlan.c mpnimbleport.c modsocket.c + lwip_patch.c modesp.c esp32_nvs.c esp32_partition.c diff --git a/ports/esp32/lwip_patch.c b/ports/esp32/lwip_patch.c new file mode 100644 index 0000000000000..9c5e10fcbb404 --- /dev/null +++ b/ports/esp32/lwip_patch.c @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "lwip/memp.h" + +// This is a link-time patch to enforce the limit of max active TCP PCBs. A +// workaround for upstream issue https://github.com/espressif/esp-idf/issues/9670 +// +// Without this limit the number of TCP PCBs in TIME-WAIT is unbounded, which can +// have two problems on systems with a lot of short-lived TCP connections: +// +// - Higher memory usage. +// - Increased chance of stalled TCP connections due to port reuse. + +static unsigned active_tcp_pcbs; + +void *__real_memp_malloc(memp_t type); +void __real_memp_free(memp_t type, void *mem); + +void *__wrap_memp_malloc(memp_t type) { + if (type != MEMP_TCP_PCB) { + return __real_memp_malloc(type); + } + + if (active_tcp_pcbs >= MEMP_NUM_TCP_PCB) { + return NULL; + } + + void *res = __real_memp_malloc(MEMP_TCP_PCB); + if (res != NULL) { + ++active_tcp_pcbs; + } + return res; +} + +void __wrap_memp_free(memp_t type, void *mem) { + __real_memp_free(type, mem); + if (type == MEMP_TCP_PCB && mem != NULL) { + assert(active_tcp_pcbs); + --active_tcp_pcbs; + } +}