stm32wl(mem): fix getFreeHeap() underreporting on dynamic sbrk heap

mallinfo().fordblks counts only free bytes within the committed arena.
On STM32WL (newlib sbrk heap) the arena grows lazily from _end toward SP,
so fordblks reads near-zero at early boot even when ~48 KB of addressable
space remains. This caused NodeDB::isFull() to fire prematurely and evict
nodes on a freshly booted device.

Fix getFreeHeap() to include uncommitted sbrk headroom (SP - sbrk(0)) so
the returned value reflects true available memory throughout the boot
lifecycle.

Introduce MESHTASTIC_DYNAMIC_SBRK_HEAP as an opt-in build flag (set in
stm32.ini) so the fix is gated to platforms with a dynamic sbrk heap
rather than a static heap. Future platforms with the same heap model can
opt in by adding this flag.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Andrew Yong
2026-04-16 13:57:21 +01:00
committed by Chloe Bethel
co-authored by Chloe Bethel
parent 026213aab7
commit 0ee5777c15
2 changed files with 18 additions and 5 deletions
+17 -5
View File
@@ -10,8 +10,20 @@
#include "memGet.h"
#include "configuration.h"
#ifdef ARCH_STM32WL
#if defined(MESHTASTIC_DYNAMIC_SBRK_HEAP)
#include <malloc.h>
#include <unistd.h> // sbrk
// Returns the uncommitted sbrk headroom: addressable space between the current heap
// break and the stack pointer that has not yet been committed to the arena.
// Currently used on: ARCH_STM32WL
static uint32_t sbrkHeadroom()
{
uint32_t sp;
__asm volatile("mov %0, sp" : "=r"(sp));
uint32_t heap_end = (uint32_t)sbrk(0);
return (sp > heap_end) ? (sp - heap_end) : 0;
}
#endif
MemGet memGet;
@@ -28,9 +40,9 @@ uint32_t MemGet::getFreeHeap()
return dbgHeapFree();
#elif defined(ARCH_RP2040)
return rp2040.getFreeHeap();
#elif defined(ARCH_STM32WL)
#elif defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) // Currently: ARCH_STM32WL
struct mallinfo m = mallinfo();
return m.fordblks; // Total free space (bytes)
return m.fordblks + sbrkHeadroom(); // Free space within arena + uncommitted sbrk headroom
#else
// this platform does not have heap management function implemented
return UINT32_MAX;
@@ -49,9 +61,9 @@ uint32_t MemGet::getHeapSize()
return dbgHeapTotal();
#elif defined(ARCH_RP2040)
return rp2040.getTotalHeap();
#elif defined(ARCH_STM32WL)
#elif defined(MESHTASTIC_DYNAMIC_SBRK_HEAP) // Currently: ARCH_STM32WL
struct mallinfo m = mallinfo();
return m.arena; // Non-mmapped space allocated (bytes)
return m.arena + sbrkHeadroom(); // Non-mmapped space allocated + uncommitted sbrk headroom
#else
// this platform does not have heap management function implemented
return UINT32_MAX;