feat(freertos): speed up SMP critical section enter and exit

The critical section port already disables interrupts and reads the core id for
the whole section, but spinlock_acquire()/spinlock_release() then disabled
interrupts again to the same level and re-read the core id register.

Add spinlock_acquire_impl()/spinlock_release_impl(), which take a caller-supplied
owner id and skip interrupt management, and reuse them from
spinlock_acquire()/spinlock_release() to avoid duplicated code. The Xtensa and
RISC-V ports now read the core id once and call the impl variants, removing one
core id read and one interrupt mask/restore per critical section enter and exit.

Closes https://github.com/espressif/esp-idf/issues/18908
This commit is contained in:
Sudeep Mohanty
2026-08-06 12:43:28 +02:00
parent cc6cc219f8
commit 9ab3da0da8
5 changed files with 232 additions and 92 deletions
@@ -143,13 +143,17 @@ BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout)
* saved level can be restored on the last call to exit the critical.
*/
BaseType_t xOldInterruptLevel = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL);
if (!spinlock_acquire(lock, timeout)) {
/* Interrupts are masked (to XCHAL_EXCM_LEVEL, the same level the spinlock uses),
* so the core id is stable and the spinlock does not need to mask again. Read the
* core id register once and reuse it for the owner id and the nesting index. */
uint32_t coreOwnerId = spinlock_owner_id();
BaseType_t coreID = spinlock_core_id_from_owner_id(coreOwnerId);
if (!spinlock_acquire_impl(lock, timeout, coreOwnerId)) {
//Timed out attempting to get spinlock. Restore previous interrupt level and return
XTOS_RESTORE_JUST_INTLEVEL((int) xOldInterruptLevel);
return pdFAIL;
}
//Spinlock acquired. Increment the IDF critical nesting count.
BaseType_t coreID = xPortGetCoreID();
BaseType_t newNesting = port_uxCriticalNestingIDF[coreID] + 1;
port_uxCriticalNestingIDF[coreID] = newNesting;
//If this is the first entry to a critical section. Save the old interrupt level.
@@ -171,8 +175,12 @@ void vPortExitCriticalIDF(portMUX_TYPE *lock)
* to re-enable interrupts if this is the last call to exit the critical. We
* can use the nesting count to determine whether this is the last exit call.
*/
spinlock_release(lock);
BaseType_t coreID = xPortGetCoreID();
/* Interrupts remain disabled for the whole critical section, so the core id is
* stable and the spinlock does not need to mask again. Read the core id register
* once and reuse it for the owner id and the nesting index. */
uint32_t coreOwnerId = spinlock_owner_id();
BaseType_t coreID = spinlock_core_id_from_owner_id(coreOwnerId);
spinlock_release_impl(lock, coreOwnerId);
BaseType_t nesting = port_uxCriticalNestingIDF[coreID];
/* Critical section nesting count must never be negative */