Fix multicore_lockout features so that the victim core cannot become stuck in an infinite loop if the lockout attempt times out #2467
No reviewers
Labels
No labels
bazel
bluetooth
board settings
boot_stage2
bug
build
c++
can't reproduce
clang
clib-interface
cmsis
code cleanliness
code size
compiler version issues
cyw43_driver
debug
discussion
documentation
duplicate
dynamic_clock
elf2uf2
enhancement
errata
filesystem
flash layout
floating point
FreeRTOS
hardware_adc
hardware_claim
hardware_clocks
hardware_dma
hardware_exception
hardware_flash
hardware_gpio
hardware_i2c
hardware_irq
hardware_pio
hardware_pwm
hardware_rcp
hardware_rtc
hardware_spi
hardware_sync
hardware_uart
hardware_vreg
hardware_watchdog
hardware_xosc
help wanted
host
IAR
installation
invalid
linker scripts
lto
lwip
mbedtls
missing API
needs reporter detail
networking
newlib
newlib-multicore
non-c
non-cmake
non-gcc
openocd
orphan PR
OS integration
pico_async_context
pico_base
pico_binary_info
pico_flash
picolibc
pico_lwip
pico_malloc
pico_multicore
pico_printf
pico_runtime
pico_stdio
pico_stdio_usb
pico_sync
pico_time
pico_util
pioasm
port
question
review
review2
rtos interop
runtime init
static library
svd
test
tinyusb
vscode
wifi integration
wontfix
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-raspberrypi/pico-sdk!2467
Loading…
Reference in a new issue
No description provided.
Delete branch "2454-flash-safe-execute-lockout-forever-fix"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #2454
This bug can be triggered if the lockout request times out. If the victim CPU core has interrupts disabled for a long time, it will not respond quickly enough to the lockout request, which times out. However, the request is still pending in the FIFO, and when it is eventually handled by the victim CPU core, that core enters an infinite loop, waiting for a LOCKOUT_MAGIC_END message which never arrives.
I considered the solution of always sending a LOCKOUT_MAGIC_END message even on timeout, but this wouldn't work if the FIFO was already full. I could not use a blocking wait, as this would not respect the timeout, and as far as I can tell, there is no hardware support for clearing the FIFO without also resetting the CPU.
In this PR, the lockout state is controlled by a shared variable. The FIFO is used to begin a lockout and acknowledge it as before. But the end of the lockout is now signalled by updating the shared variable. This ensures that the end of the lockout request is recognised reliably by the victim CPU core, regardless of whether the end was caused by a timeout or whether the lockout completed normally. __wfe and __sev are used to signal updates to the shared variable in order to avoid polling.
The semantics of the
multicore_lockout_end_...functions change: they will no longer wait for the lockout to end. This is described further in the updated doxygen and in the comments below.AFAICT it looks like
lockout_request_idonly gets incremented and never gets decremented - does that mean that theoretically it might eventually overflow?Does this PR mean that any of the Doxygen at https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2_common/pico_multicore/include/pico/multicore.h#L418 also needs updating to match, or is that all still "correct"?
That's correct, but overflow (with wrap to 0) is ok. The numerical value of the lockout_request_id does not matter, all that is important is that the current lockout request has a different value to any others that may still be pending in the FIFO.
You're right, this did need updating. And, I didn't realise this until now, but the semantics of
multicore_lockout_end_blockingandmulticore_lockout_end_timeout_usare different now, because these functions do not wait for the lockout to end. They just guarantee that it will end.I thought about whether it was a good idea to push ahead with this behaviour change. I think it is a good idea, because it's simpler, and I don't see the value in knowing that the end of the lockout has been acknowledged by the victim core. The new design avoids the possibility for
multicore_lockout_end_timeout_usthat "a timeout here will leave the "lockout" functionality in a bad state" (as previously stated in doxygen). Timeouts always leave the lockout functionality in a usable state, both for start and end. I think the old blocking behaviour for themulticore_lockout_end_...functions could be preserved but at the cost of a more complex design, and I feel like it wouldn't be justified... however, I may be missing something!In that case, perhaps the
LOCKOUT_MAGIC_STARTis a bit of a red herring, and it'd be better to remove it entirely and just initialiselockout_request_idwith0?Agree, done.
@ -202,25 +202,27 @@ void multicore_launch_core1_raw(void (*entry)(void), uint32_t *sp, uint32_t vectirq_set_enabled(irq_num, enabled);Small style point, we would probably use
volatile uint32_texplicitly here vsio_rw_32which is really intended for memory mapped IO@ -271,3 +272,4 @@multicore_fifo_push_timeout_us(request_id, (uint64_t)next_timeout_us);next_timeout_us = absolute_time_diff_us(get_absolute_time(), until);if (next_timeout_us < 0) {break;can you add a
(void)i'm pretty sure without this it makes at least one of the many GCC or Clang compiler versions unhappyThanks, this looks great except a few nits
@ -202,25 +202,27 @@ void multicore_launch_core1_raw(void (*entry)(void), uint32_t *sp, uint32_t vectirq_set_enabled(irq_num, enabled);Thanks, replaced.
@ -271,3 +272,4 @@multicore_fifo_push_timeout_us(request_id, (uint64_t)next_timeout_us);next_timeout_us = absolute_time_diff_us(get_absolute_time(), until);if (next_timeout_us < 0) {break;Thanks, fixed.
After further testing I found that beginning at 0 causes a problem as a 0-value word is received by CPU core 0 via the FIFO after reset (on RP2350). Core 0 becomes stuck in
multicore_lockout_victim_inituntil core 1 requests a lockout. To avoid this the number should begin at a non-zero value. I restored the original "magic" number and added a comment to explain this.