From bd4de3c8a376995fffd3b89cea3a4c30152b1c01 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 1 Oct 2024 15:35:37 +0200 Subject: [PATCH 1/2] fix(freertos): Fixed priority inversion when setting event group bits This commit fixes a priority inversion when a lower priority task set event group bits to unblock a higher priority task but the lower priority task continued to run. --- components/freertos/FreeRTOS-Kernel/tasks.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/components/freertos/FreeRTOS-Kernel/tasks.c b/components/freertos/FreeRTOS-Kernel/tasks.c index 8d06ea30b0d0..368a20599fd5 100644 --- a/components/freertos/FreeRTOS-Kernel/tasks.c +++ b/components/freertos/FreeRTOS-Kernel/tasks.c @@ -4123,10 +4123,19 @@ void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, if( taskIS_YIELD_REQUIRED( pxUnblockedTCB, pdFALSE ) == pdTRUE ) { /* The unblocked task has a priority above that of the calling task, so - * a context switch is required. This function is called with the - * scheduler suspended so xYieldPending is set so the context switch - * occurs immediately that the scheduler is resumed (unsuspended). */ - xYieldPending[ xCurCoreID ] = pdTRUE; + * a context switch is required. */ + #if ( configNUM_CORES > 1 ) + + /* In SMP mode, this function is called from a critical section, so we + * yield the current core to schedule the unblocked task. */ + portYIELD_WITHIN_API(); + #else /* configNUM_CORES > 1 */ + + /* In single-core mode, this function is called with the scheduler suspended + * so xYieldPending is set so the context switch occurs immediately once the + * scheduler is resumed (unsuspended). */ + xYieldPending[ xCurCoreID ] = pdTRUE; + #endif /* configNUM_CORES > 1 */ } } } From fc84220d90ad2f96d3d7a0365384121e7627d579 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 2 Oct 2024 10:43:15 +0200 Subject: [PATCH 2/2] test(freertos): Added unit test for event groups to test priority inversion This commit adds a FreeRTOS unit test to verify that event groups do not cause priority inversion when unblocking a higher priority task. --- .../event_groups/test_freertos_eventgroups.c | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/components/freertos/test_apps/freertos/kernel/event_groups/test_freertos_eventgroups.c b/components/freertos/test_apps/freertos/kernel/event_groups/test_freertos_eventgroups.c index b14f1ea1ca24..00ff6980c9ea 100644 --- a/components/freertos/test_apps/freertos/kernel/event_groups/test_freertos_eventgroups.c +++ b/components/freertos/test_apps/freertos/kernel/event_groups/test_freertos_eventgroups.c @@ -132,6 +132,57 @@ TEST_CASE("FreeRTOS Event Group Sync", "[freertos]") vEventGroupDelete(eg); } +static TaskHandle_t run_order[2]; +static uint32_t run_order_index = 0; + +void task_test_eg_prio(void *arg) +{ + TaskHandle_t main_task_hdl = (TaskHandle_t)arg; + + /* Notify the main task that this task has been created */ + xTaskNotifyGive(main_task_hdl); + + /* Wait for the event group bits to be set */ + TEST_ASSERT_EQUAL(1, xEventGroupWaitBits(eg, 1, pdTRUE, pdTRUE, portMAX_DELAY)); + + /* Record the task handle in the run order array */ + run_order[run_order_index++] = xTaskGetCurrentTaskHandle(); + + /* Suspend the task */ + vTaskSuspend(NULL); +} + +TEST_CASE("FreeRTOS Event Groups do not cause priority inversion when higher priority task is unblocked", "[freertos]") +{ + run_order[0] = NULL; + run_order[1] = NULL; + run_order_index = 0; + + /* Initialize the event group */ + eg = xEventGroupCreate(); + + /* Create a task with higher priority than the task that will set the event group bits */ + TaskHandle_t higher_prio_hdl; + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(task_test_eg_prio, "task_test_eg_prio", 2048, (void *)xTaskGetCurrentTaskHandle(), CONFIG_UNITY_FREERTOS_PRIORITY + 1, &higher_prio_hdl, CONFIG_UNITY_FREERTOS_CPU)); + + /* Wait for the task to be created */ + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + /* Set the event group bits */ + xEventGroupSetBits(eg, 1); + + /* Record the task handle in the run order array */ + run_order[run_order_index++] = xTaskGetCurrentTaskHandle(); + + /* Verify that the higher priority task was unblocked and immediately scheduled and the lower priority task was preempted */ + TEST_ASSERT_EQUAL(higher_prio_hdl, run_order[0]); + TEST_ASSERT_EQUAL(xTaskGetCurrentTaskHandle(), run_order[1]); + + /* Clean up */ + vEventGroupDelete(eg); + vTaskDelete(higher_prio_hdl); +} + /*-----------------Test case for event group trace facilities-----------------*/ #ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY /*