Merge branch 'bugfix/spi_flash_deadlock' into 'master'

spi_flash: raise priority of the task performing spi_flash operation

See merge request idf/esp-idf!2609
This commit is contained in:
Ivan Grokhotkov 2018-07-02 18:39:16 +08:00
commit 59c96ae331
2 changed files with 64 additions and 0 deletions

View File

@ -110,6 +110,10 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
assert(other_cpuid == 1);
spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]);
} else {
// Temporarily raise current task priority to prevent a deadlock while
// waiting for IPC task to start on the other CPU
int old_prio = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);
// Signal to the spi_flash_op_block_task on the other CPU that we need it to
// disable cache there and block other tasks from executing.
s_flash_op_can_start = false;
@ -121,6 +125,8 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
}
// Disable scheduler on the current CPU
vTaskSuspendAll();
// Can now set the priority back to the normal one
vTaskPrioritySet(NULL, old_prio);
// This is guaranteed to run on CPU <cpuid> because the other CPU is now
// occupied by highest priority task
assert(xPortGetCoreID() == cpuid);

View File

@ -167,3 +167,61 @@ TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash]
free(read_arg.buf);
}
#if portNUM_PROCESSORS > 1
TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash]")
{
typedef struct {
QueueHandle_t queue;
volatile bool done;
} deadlock_test_arg_t;
/* Create two tasks: high-priority consumer on CPU0, low-priority producer on CPU1.
* Consumer polls the queue until it gets some data, then yields.
* Run flash operation on CPU0. Check that when IPC1 task blocks out the producer,
* the task which does flash operation does not get blocked by the consumer.
*/
void producer_task(void* varg)
{
int dummy = 0;
deadlock_test_arg_t* arg = (deadlock_test_arg_t*) varg;
while (!arg->done) {
xQueueSend(arg->queue, &dummy, 0);
vTaskDelay(1);
}
vTaskDelete(NULL);
}
void consumer_task(void* varg)
{
int dummy;
deadlock_test_arg_t* arg = (deadlock_test_arg_t*) varg;
while (!arg->done) {
if (xQueueReceive(arg->queue, &dummy, 0) == pdTRUE) {
vTaskDelay(1);
}
}
vTaskDelete(NULL);
}
deadlock_test_arg_t arg = {
.queue = xQueueCreate(32, sizeof(int)),
.done = false
};
TEST_ASSERT(xTaskCreatePinnedToCore(&producer_task, "producer", 4096, &arg, 5, NULL, 1));
TEST_ASSERT(xTaskCreatePinnedToCore(&consumer_task, "consumer", 4096, &arg, 10, NULL, 0));
for (int i = 0; i < 1000; i++) {
uint32_t dummy;
TEST_ESP_OK(spi_flash_read(0, &dummy, sizeof(dummy)));
}
arg.done = true;
vTaskDelay(5);
vQueueDelete(arg.queue);
/* Check that current task priority is still correct */
TEST_ASSERT_EQUAL_INT(uxTaskPriorityGet(NULL), UNITY_FREERTOS_PRIORITY);
}
#endif // portNUM_PROCESSORS > 1