Skip to content

Commit 3515dad

Browse files
authored
Fix multicore_lockout features so that the victim core cannot become stuck in an infinite loop if the lockout attempt times out (#2467)
* Fix issue 2454 flash safe execute lockout The lockout state is controlled by a shared variable. The FIFO is used to begin a lockout and acknowledge it (i.e. multicore_lockout_handshake works as before) but the end of the lockout is now signalled by updating the shared variable. This ensures that timeouts are recognised reliably by the victim core. __wfe and __sev are used to signal updates to the shared variable in order to avoid polling. * Update documentation for multicore_lockout_end functions * Simplification, remove magic number (not required) * Review improvements * Restore use of non-zero magic number
1 parent 47f288b commit 3515dad

File tree

2 files changed

+49
-32
lines changed

2 files changed

+49
-32
lines changed

src/rp2_common/pico_multicore/include/pico/multicore.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ static inline uint multicore_doorbell_irq_num(uint doorbell_num) {
437437
* The core which wishes to lockout the other core calls \ref multicore_lockout_start_blocking or
438438
* \ref multicore_lockout_start_timeout_us to interrupt the other "victim" core and wait for it to be in a
439439
* "locked out" state. Once the lockout is no longer needed it calls \ref multicore_lockout_end_blocking or
440-
* \ref multicore_lockout_end_timeout_us to release the lockout and wait for confirmation.
440+
* \ref multicore_lockout_end_timeout_us to release the lockout.
441441
*
442442
* \note Because multicore lockout uses the intercore FIFOs, the FIFOs <b>cannot</b> be used for any other purpose
443443
*/
@@ -491,27 +491,31 @@ void multicore_lockout_start_blocking(void);
491491
*/
492492
bool multicore_lockout_start_timeout_us(uint64_t timeout_us);
493493

494-
/*! \brief Release the other core from a locked out state amd wait for it to acknowledge
494+
/*! \brief Release the other core from a locked out state
495495
* \ingroup multicore_lockout
496496
*
497-
* \note The other core must previously have been "locked out" by calling a `multicore_lockout_start_` function
498-
* from this core
497+
* The other core must previously have been "locked out" by calling a `multicore_lockout_start_` function
498+
* from this core.
499+
*
500+
* \note The other core will leave the lockout state if this function is called.
501+
* The function only blocks for access to a lockout mutex, it does not wait for the other core
502+
* to leave the lockout state.
499503
*/
500504
void multicore_lockout_end_blocking(void);
501505

502-
/*! \brief Release the other core from a locked out state amd wait up to a time limit for it to acknowledge
506+
/*! \brief Release the other core from a locked out state
503507
* \ingroup multicore_lockout
504508
*
505509
* The other core must previously have been "locked out" by calling a `multicore_lockout_start_` function
506510
* from this core
507511
*
508-
* \note be very careful using small timeout values, as a timeout here will leave the "lockout" functionality
509-
* in a bad state. It is probably preferable to use \ref multicore_lockout_end_blocking anyway as if you have
510-
* already waited for the victim core to enter the lockout state, then the victim core will be ready to exit
511-
* the lockout state very quickly.
512+
* \note The other core will leave the lockout state if this function returns true.
513+
* The function only blocks for access to a lockout mutex, it does not wait for the other core
514+
* to leave the lockout state. If the lockout mutex could not be acquired, the function returns
515+
* false and no action is taken.
512516
*
513517
* \param timeout_us the timeout in microseconds
514-
* \return true if the other core successfully exited locked out state within the timeout, false otherwise
518+
* \return true if the other core will leave the lockout state, false otherwise
515519
*/
516520
bool multicore_lockout_end_timeout_us(uint64_t timeout_us);
517521

src/rp2_common/pico_multicore/multicore.c

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -207,25 +207,27 @@ void multicore_launch_core1_raw(void (*entry)(void), uint32_t *sp, uint32_t vect
207207
irq_set_enabled(irq_num, enabled);
208208
}
209209

210-
#define LOCKOUT_MAGIC_START 0x73a8831eu
211-
#define LOCKOUT_MAGIC_END (~LOCKOUT_MAGIC_START)
212-
210+
// A non-zero initialisation value is used in order to reduce the chance of
211+
// entering the lock handler on bootup due to a 0-word being present in the FIFO
212+
static volatile uint32_t lockout_request_id = 0x73a8831eu;
213213
static mutex_t lockout_mutex;
214-
static bool lockout_in_progress;
215214

216215
// note this method is in RAM because lockout is used when writing to flash
217216
// it only makes inline calls
218217
static void __isr __not_in_flash_func(multicore_lockout_handler)(void) {
219218
multicore_fifo_clear_irq();
220219
while (multicore_fifo_rvalid()) {
221-
if (sio_hw->fifo_rd == LOCKOUT_MAGIC_START) {
220+
uint32_t request_id = sio_hw->fifo_rd;
221+
if (request_id == lockout_request_id) {
222+
// valid lockout request received
222223
uint32_t save = save_and_disable_interrupts();
223-
multicore_fifo_push_blocking_inline(LOCKOUT_MAGIC_START);
224-
while (multicore_fifo_pop_blocking_inline() != LOCKOUT_MAGIC_END) {
225-
tight_loop_contents(); // not tight but endless potentially
224+
multicore_fifo_push_blocking_inline(request_id);
225+
// wait for the lockout to expire
226+
while (request_id == lockout_request_id) {
227+
// when lockout_request_id is updated, the other CPU core calls __sev
228+
__wfe();
226229
}
227230
restore_interrupts_from_disabled(save);
228-
multicore_fifo_push_blocking_inline(LOCKOUT_MAGIC_END);
229231
}
230232
}
231233
}
@@ -262,7 +264,7 @@ void multicore_lockout_victim_deinit(void) {
262264
}
263265
}
264266

265-
static bool multicore_lockout_handshake(uint32_t magic, absolute_time_t until) {
267+
static bool multicore_lockout_handshake(uint32_t request_id, absolute_time_t until) {
266268
uint irq_num = SIO_FIFO_IRQ_NUM(get_core_num());
267269
bool enabled = irq_is_enabled(irq_num);
268270
if (enabled) irq_set_enabled(irq_num, false);
@@ -272,7 +274,7 @@ static bool multicore_lockout_handshake(uint32_t magic, absolute_time_t until) {
272274
if (next_timeout_us < 0) {
273275
break;
274276
}
275-
multicore_fifo_push_timeout_us(magic, (uint64_t)next_timeout_us);
277+
multicore_fifo_push_timeout_us(request_id, (uint64_t)next_timeout_us);
276278
next_timeout_us = absolute_time_diff_us(get_absolute_time(), until);
277279
if (next_timeout_us < 0) {
278280
break;
@@ -281,22 +283,36 @@ static bool multicore_lockout_handshake(uint32_t magic, absolute_time_t until) {
281283
if (!multicore_fifo_pop_timeout_us((uint64_t)next_timeout_us, &word)) {
282284
break;
283285
}
284-
if (word == magic) {
286+
if (word == request_id) {
285287
rc = true;
286288
}
287289
} while (!rc);
288290
if (enabled) irq_set_enabled(irq_num, true);
289291
return rc;
290292
}
291293

294+
static uint32_t update_lockout_request_id(void) {
295+
// generate new number and then update shared variable
296+
uint32_t new_request_id = lockout_request_id + 1;
297+
lockout_request_id = new_request_id;
298+
// notify other core
299+
__sev();
300+
return new_request_id;
301+
}
302+
292303
static bool multicore_lockout_start_block_until(absolute_time_t until) {
293304
check_lockout_mutex_init();
294305
if (!mutex_enter_block_until(&lockout_mutex, until)) {
295306
return false;
296307
}
297-
hard_assert(!lockout_in_progress);
298-
bool rc = multicore_lockout_handshake(LOCKOUT_MAGIC_START, until);
299-
lockout_in_progress = rc;
308+
// generate a new request_id number
309+
uint32_t request_id = update_lockout_request_id();
310+
// attempt to lock out
311+
bool rc = multicore_lockout_handshake(request_id, until);
312+
if (!rc) {
313+
// lockout failed - cancel it
314+
update_lockout_request_id();
315+
}
300316
mutex_exit(&lockout_mutex);
301317
return rc;
302318
}
@@ -314,13 +330,10 @@ static bool multicore_lockout_end_block_until(absolute_time_t until) {
314330
if (!mutex_enter_block_until(&lockout_mutex, until)) {
315331
return false;
316332
}
317-
assert(lockout_in_progress);
318-
bool rc = multicore_lockout_handshake(LOCKOUT_MAGIC_END, until);
319-
if (rc) {
320-
lockout_in_progress = false;
321-
}
333+
// lockout finished - cancel it
334+
update_lockout_request_id();
322335
mutex_exit(&lockout_mutex);
323-
return rc;
336+
return true;
324337
}
325338

326339
bool multicore_lockout_end_timeout_us(uint64_t timeout_us) {
@@ -407,4 +420,4 @@ void multicore_doorbell_unclaim(uint doorbell_num, uint core_mask) {
407420
}
408421

409422

410-
#endif
423+
#endif

0 commit comments

Comments
 (0)