mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
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:
@@ -542,13 +542,17 @@ BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TY
|
||||
* saved level can be restored on the last call to exit the critical.
|
||||
*/
|
||||
BaseType_t xOldInterruptLevel = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
if (!spinlock_acquire(mux, timeout)) {
|
||||
/* Interrupts are masked, so the core id is stable. Read it once and reuse it for
|
||||
* both the spinlock owner id and the per-core critical nesting state. Interrupts
|
||||
* stay masked for the whole critical section (to the same level the spinlock would
|
||||
* use), so the spinlock does not need to disable them again. */
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
if (!spinlock_acquire_impl(mux, timeout, spinlock_owner_id_for_core(coreID))) {
|
||||
//Timed out attempting to get spinlock. Restore previous interrupt level and return
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(xOldInterruptLevel);
|
||||
return pdFAIL;
|
||||
}
|
||||
//Spinlock acquired. Increment the critical nesting count.
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
BaseType_t newNesting = port_uxCriticalNesting[coreID] + 1;
|
||||
port_uxCriticalNesting[coreID] = newNesting;
|
||||
//If this is the first entry to a critical section. Save the old interrupt level.
|
||||
@@ -569,8 +573,11 @@ void __attribute__((optimize("-O3"))) vPortExitCriticalMultiCore(portMUX_TYPE *m
|
||||
* 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(mux);
|
||||
/* Interrupts remain disabled for the whole critical section, so the core id is
|
||||
* stable and the spinlock does not need to mask them again. Read the core id
|
||||
* once and reuse it for both the release owner id and the per-core nesting state. */
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
spinlock_release_impl(mux, spinlock_owner_id_for_core(coreID));
|
||||
BaseType_t nesting = port_uxCriticalNesting[coreID];
|
||||
|
||||
/* Critical section nesting count must never be negative */
|
||||
|
||||
@@ -511,13 +511,17 @@ BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TY
|
||||
* saved level can be restored on the last call to exit the critical.
|
||||
*/
|
||||
BaseType_t xOldInterruptLevel = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
if (!spinlock_acquire(mux, timeout)) {
|
||||
/* Interrupts are masked, so the core id is stable. Read the core id register once
|
||||
* (its value is already the spinlock owner id on Xtensa) and reuse it, so
|
||||
* spinlock_acquire does not read the core id register a second time. */
|
||||
uint32_t coreOwnerId = spinlock_owner_id();
|
||||
BaseType_t coreID = spinlock_core_id_from_owner_id(coreOwnerId);
|
||||
if (!spinlock_acquire_impl(mux, timeout, coreOwnerId)) {
|
||||
//Timed out attempting to get spinlock. Restore previous interrupt level and return
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(xOldInterruptLevel);
|
||||
return pdFAIL;
|
||||
}
|
||||
//Spinlock acquired. Increment the critical nesting count.
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
BaseType_t newNesting = port_uxCriticalNesting[coreID] + 1;
|
||||
port_uxCriticalNesting[coreID] = newNesting;
|
||||
//If this is the first entry to a critical section. Save the old interrupt level.
|
||||
@@ -538,8 +542,12 @@ void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux)
|
||||
* 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(mux);
|
||||
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 them again. Read the core id
|
||||
* register once (its value is the spinlock owner id on Xtensa) and reuse it. */
|
||||
uint32_t coreOwnerId = spinlock_owner_id();
|
||||
BaseType_t coreID = spinlock_core_id_from_owner_id(coreOwnerId);
|
||||
spinlock_release_impl(mux, coreOwnerId);
|
||||
BaseType_t nesting = port_uxCriticalNesting[coreID];
|
||||
|
||||
/* Critical section nesting count must never be negative */
|
||||
|
||||
Reference in New Issue
Block a user