Skip to content

Commit a8c176e

Browse files
committed
extmod/zephyr_ble: Fix soft reset crash due to net_buf pool state.
After a MicroPython soft reset, Zephyr net_buf pools retain stale runtime state (free list, uninit_count) from the previous BLE session. When bt_enable() runs again, it assumes pools are in their initial state with all buffers uninitialized and the free list empty. This mismatch causes a HardFault when allocating or freeing buffers. Add mp_net_buf_pool_state_reset() to restore all net_buf pools to their initial state before BLE reinitialization. The function iterates over all statically-defined pools and resets: - .free lifo (set head/tail to NULL) - .uninit_count (reset to buf_count) - .lock spinlock Tested with 8+ consecutive BLE init/deinit + soft_reset cycles on Pico W. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
1 parent d4240b9 commit a8c176e

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

extmod/zephyr_ble/modbluetooth_zephyr.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <zephyr/device.h>
4141
#include "extmod/modbluetooth.h"
4242
#include "extmod/zephyr_ble/hal/zephyr_ble_work.h"
43+
#include "extmod/zephyr_ble/net_buf_pool_registry.h"
4344

4445
// Access Zephyr's internal bt_dev for force-reset on deinit failure
4546
// The include path should have lib/zephyr/subsys/bluetooth/host already
@@ -465,6 +466,12 @@ int mp_bluetooth_init(void) {
465466
// This must also be started before bt_enable() to process work items
466467
mp_bluetooth_zephyr_work_thread_start();
467468

469+
// Reset net_buf pool state before BLE initialization.
470+
// After a soft reset, pools retain stale runtime state (free list,
471+
// uninit_count) from the previous session. This causes crashes when
472+
// bt_enable() tries to allocate buffers from corrupted pools.
473+
mp_net_buf_pool_state_reset();
474+
468475
// Enter init phase - work will be processed synchronously in this loop
469476
extern void mp_bluetooth_zephyr_init_phase_enter(void);
470477
mp_bluetooth_zephyr_init_phase_enter();

extmod/zephyr_ble/net_buf_pool_registry.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,49 @@ void mp_net_buf_pool_reset(void) {
102102
// The registry system (mp_net_buf_pool_register/get) is retained for potential future use
103103
// if dynamic pool registration is needed, but is not currently used.
104104

105+
// ============================================================================
106+
// Pool State Reset for Soft Reset Support
107+
// ============================================================================
108+
// Net_buf pools are statically initialized but maintain runtime state:
109+
// - .free: k_lifo containing free buffers
110+
// - .uninit_count: number of uninitialized buffers remaining
111+
// - .lock: spinlock for concurrent access
112+
//
113+
// After a MicroPython soft reset, these pools retain stale state from the
114+
// previous BLE session. When bt_enable() runs again, it assumes pools are
115+
// in their initial state (all buffers uninitialized, free list empty).
116+
// This mismatch causes crashes when allocating/freeing buffers.
117+
//
118+
// mp_net_buf_pool_state_reset() restores pools to their initial state,
119+
// allowing clean BLE reinitialization after soft reset.
120+
121+
#include <zephyr/net_buf.h>
122+
#include <zephyr/sys/iterable_sections.h>
123+
124+
void mp_net_buf_pool_state_reset(void) {
125+
// Iterate over all statically-defined net_buf pools
126+
STRUCT_SECTION_FOREACH(net_buf_pool, pool) {
127+
// Reset the free list (k_lifo) to empty
128+
// k_lifo contains a k_queue with sys_slist_t data_q
129+
pool->free._queue.data_q.head = NULL;
130+
pool->free._queue.data_q.tail = NULL;
131+
pool->free._queue.lock.unused = 0;
132+
133+
// Reset uninit_count to buf_count so all buffers are re-initialized
134+
// on first allocation (this is how pools start after static init)
135+
pool->uninit_count = pool->buf_count;
136+
137+
// Reset the pool spinlock
138+
pool->lock.unused = 0;
139+
140+
#if defined(CONFIG_NET_BUF_POOL_USAGE)
141+
// Reset usage tracking counters
142+
atomic_set(&pool->avail_count, pool->buf_count);
143+
pool->max_used = 0;
144+
#endif
145+
}
146+
}
147+
105148
// ============================================================================
106149
// Note on Zephyr iterable sections
107150
// ============================================================================

extmod/zephyr_ble/net_buf_pool_registry.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,9 @@ void mp_net_buf_pool_update_end(void);
4545
// Reset registration (for testing)
4646
void mp_net_buf_pool_reset(void);
4747

48+
// Reset all net_buf pool runtime state for soft reset support.
49+
// This must be called before bt_enable() when reinitializing BLE after
50+
// a soft reset, to restore pools to their initial state.
51+
void mp_net_buf_pool_state_reset(void);
52+
4853
#endif // MICROPY_INCLUDED_EXTMOD_ZEPHYR_BLE_NET_BUF_POOL_REGISTRY_H

0 commit comments

Comments
 (0)