Răsfoiți Sursa

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
Ivan Grokhotkov 7 ani în urmă
părinte
comite
59c96ae331

+ 6 - 0
components/spi_flash/cache_utils.c

@@ -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);

+ 58 - 0
components/spi_flash/test/test_spi_flash.c

@@ -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