mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-28 04:05:39 +00:00
freertos: Fix SMP round robin scheduling
The previous SMP freertos round robin would skip over tasks when time slicing. This commit implements a Best Effort Round Robin where selected tasks are put to the back of the list, thus makes the time slicing more fair. - Documentation has been updated accordingly. - Tidy up vTaskSwitchContext() to match v10.4.3 more - Increased esp_ipc task stack size to avoid overflow Closes https://github.com/espressif/esp-idf/issues/7256
This commit is contained in:
177
components/freertos/test/test_freertos_scheduling_round_robin.c
Normal file
177
components/freertos/test/test_freertos_scheduling_round_robin.c
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "hal/interrupt_controller_hal.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
/*
|
||||
Test Best Effort Round Robin Scheduling:
|
||||
|
||||
The following test case tests the "Best Effort Round Robin Scheduling" that fixes the skipping behavior found in older
|
||||
versions of the ESP-IDF SMP FreeRTOS (see docs for more details about Best Effort Round Robin Scheduling).
|
||||
|
||||
This test...
|
||||
- Only runs under dual core configuration
|
||||
- Will disable the tick interrupts of both cores
|
||||
|
||||
Test flow as follows:
|
||||
|
||||
1. Stop preemption on core 0 by raising the priority of the unity task
|
||||
2. Stop preemption on core 0 by creating a blocker task
|
||||
3. Disable tick interrupts on both cores
|
||||
4. Create N spin tasks on each core, each with a sequential task_code
|
||||
5. Unblock those spin tasks in a sequential order
|
||||
6. Lower priority of unity task and stop the blocker task so that spin tasks are run
|
||||
7. Each time a spin task is run (i.e., an iteration) it will send its task code to a queue
|
||||
8. Spin tasks will clean themselves up
|
||||
9. The queue should contain the task codes of the spin tasks in the order they were started in, thus showing that round
|
||||
robin schedules the tasks in a sequential order.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_FREERTOS_UNICORE) && (defined(CONFIG_FREERTOS_CORETIMER_0) || defined(CONFIG_FREERTOS_CORETIMER_1))
|
||||
|
||||
#define SPIN_TASK_PRIO (CONFIG_UNITY_FREERTOS_PRIORITY + 1)
|
||||
#define SPIN_TASK_NUM_ITER 3
|
||||
#define TASK_STACK_SIZE 1024
|
||||
#define NUM_PINNED_SPIN_TASK_PER_CORE 3
|
||||
#if defined(CONFIG_FREERTOS_CORETIMER_0)
|
||||
#define TICK_INTR_IDX 6
|
||||
#else //defined(CONFIG_FREERTOS_CORETIMER_1)
|
||||
#define TICK_INTR_IDX 15
|
||||
#endif
|
||||
|
||||
static QueueHandle_t core0_run_order_queue;
|
||||
static QueueHandle_t core1_run_order_queue;
|
||||
static uint32_t total_iter_count[portNUM_PROCESSORS] = {0};
|
||||
|
||||
static void spin_task(void *arg)
|
||||
{
|
||||
uint32_t task_code = (uint32_t)arg;
|
||||
QueueHandle_t run_order_queue = ((task_code >> 4) == 0) ? core0_run_order_queue : core1_run_order_queue;
|
||||
|
||||
//Wait to be started
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
for (int i = 0; i < SPIN_TASK_NUM_ITER; i++) {
|
||||
xQueueSend(run_order_queue, &task_code, 0);
|
||||
//No need for critical sections as tick interrupt is disabled
|
||||
total_iter_count[xPortGetCoreID()]++;
|
||||
taskYIELD();
|
||||
}
|
||||
|
||||
//Last iteration of the last spin task on this core. Reenable this core's tick interrupt
|
||||
if (total_iter_count[xPortGetCoreID()] == (NUM_PINNED_SPIN_TASK_PER_CORE * SPIN_TASK_NUM_ITER)) {
|
||||
interrupt_controller_hal_enable_interrupts(1 <<TICK_INTR_IDX);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void blocker_task(void *arg)
|
||||
{
|
||||
volatile bool *exit_loop = (volatile bool *)arg;
|
||||
|
||||
//Disable tick interrupts on core 1 the duration of the test
|
||||
taskDISABLE_INTERRUPTS();
|
||||
interrupt_controller_hal_disable_interrupts(1 << TICK_INTR_IDX);
|
||||
taskENABLE_INTERRUPTS();
|
||||
|
||||
while (!*exit_loop) {
|
||||
;
|
||||
}
|
||||
|
||||
//Wait to be resumed
|
||||
vTaskSuspend(NULL);
|
||||
|
||||
//Reenable tick interrupt on core 1
|
||||
taskDISABLE_INTERRUPTS();
|
||||
interrupt_controller_hal_enable_interrupts(1 << TICK_INTR_IDX);
|
||||
taskENABLE_INTERRUPTS();
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Test FreeRTOS Scheduling Round Robin", "[freertos]")
|
||||
{
|
||||
core0_run_order_queue = xQueueCreate(SPIN_TASK_NUM_ITER * NUM_PINNED_SPIN_TASK_PER_CORE, sizeof(uint32_t));
|
||||
core1_run_order_queue = xQueueCreate(SPIN_TASK_NUM_ITER * NUM_PINNED_SPIN_TASK_PER_CORE, sizeof(uint32_t));
|
||||
|
||||
/* Increase priority of unity task so that the spin tasks don't preempt us
|
||||
during task creation. */
|
||||
vTaskPrioritySet(NULL, SPIN_TASK_PRIO + 1);
|
||||
/* Create a task on core 1 of the same priority to block core 1 */
|
||||
volatile bool suspend_blocker = false;
|
||||
TaskHandle_t blocker_task_hdl;
|
||||
xTaskCreatePinnedToCore(blocker_task, "blk", TASK_STACK_SIZE, (void *)&suspend_blocker, SPIN_TASK_PRIO + 1, &blocker_task_hdl, 1);
|
||||
|
||||
//Disable tick interrupts on core 0 the duration of the test
|
||||
taskDISABLE_INTERRUPTS();
|
||||
interrupt_controller_hal_disable_interrupts(1 << TICK_INTR_IDX);
|
||||
taskENABLE_INTERRUPTS();
|
||||
|
||||
TaskHandle_t core0_task_hdls[NUM_PINNED_SPIN_TASK_PER_CORE];
|
||||
TaskHandle_t core1_task_hdls[NUM_PINNED_SPIN_TASK_PER_CORE];
|
||||
|
||||
for (int i = 0; i < NUM_PINNED_SPIN_TASK_PER_CORE; i++) {
|
||||
//Create a spin task pinned to core 0
|
||||
xTaskCreatePinnedToCore(spin_task, "spin", TASK_STACK_SIZE, (void *)(0x00 + i), SPIN_TASK_PRIO, &core0_task_hdls[i], 0);
|
||||
//Create a spin task pinned to core 1
|
||||
xTaskCreatePinnedToCore(spin_task, "spin", TASK_STACK_SIZE, (void *)(0x10 + i), SPIN_TASK_PRIO, &core1_task_hdls[i], 1);
|
||||
}
|
||||
|
||||
/* Start the tasks in a particular order. This order should be reflected in
|
||||
in the round robin scheduling on each core */
|
||||
for (int i = 0; i < NUM_PINNED_SPIN_TASK_PER_CORE; i++) {
|
||||
//Start a spin task on core 0
|
||||
xTaskNotifyGive(core0_task_hdls[i]);
|
||||
//Start a spin task on core 1
|
||||
xTaskNotifyGive(core1_task_hdls[i]);
|
||||
}
|
||||
|
||||
//Lower priority of this task and stop blocker task to allow the spin tasks to be scheduled
|
||||
suspend_blocker = true;
|
||||
vTaskPrioritySet(NULL, UNITY_FREERTOS_PRIORITY);
|
||||
|
||||
//Give a enough delay to allow all iterations of the round robin to occur
|
||||
esp_rom_delay_us(10000);
|
||||
|
||||
for (int i = 0; i < SPIN_TASK_NUM_ITER; i++) {
|
||||
for (int j = 0; j < NUM_PINNED_SPIN_TASK_PER_CORE; j++) {
|
||||
uint32_t core0_entry;
|
||||
uint32_t core1_entry;
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(core0_run_order_queue, &core0_entry, 0));
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(core1_run_order_queue, &core1_entry, 0));
|
||||
TEST_ASSERT_EQUAL(0x00 + j, core0_entry);
|
||||
TEST_ASSERT_EQUAL(0x10 + j, core1_entry);
|
||||
}
|
||||
}
|
||||
|
||||
//Resume the blocker task for cleanup
|
||||
vTaskResume(blocker_task_hdl);
|
||||
//Reenable tick interrupt on core 0
|
||||
taskDISABLE_INTERRUPTS();
|
||||
interrupt_controller_hal_enable_interrupts(1 << TICK_INTR_IDX);
|
||||
taskENABLE_INTERRUPTS();
|
||||
vTaskDelay(10); //Wait for blocker task to clean up
|
||||
//Clean up queues
|
||||
vQueueDelete(core0_run_order_queue);
|
||||
vQueueDelete(core1_run_order_queue);
|
||||
}
|
||||
|
||||
#endif //!defined(CONFIG_FREERTOS_UNICORE) && (efined(CONFIG_FREERTOS_CORETIMER_0) || defined(CONFIG_FREERTOS_CORETIMER_1))
|
||||
Reference in New Issue
Block a user