From b7b4871da32de4e7b89e53104638ff25c6589839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Dahlstr=C3=B6m?= Date: Wed, 23 Jun 2021 23:00:35 +0200 Subject: [PATCH] add AFATFS_ASYNC_IO flag and document it --- Readme.md | 14 ++++++++++++++ lib/asyncfatfs.c | 22 ++++++++++++++++++++-- lib/asyncfatfs.h | 3 +++ lib/sdcard.h | 7 +++++++ tests/common.c | 12 ++++++++++++ tests/common.h | 2 ++ tests/sdcard_sim.h | 1 - tests/test_file_delete.c | 4 ++-- tests/test_file_modes.c | 4 ++-- tests/test_file_size.c | 6 +++--- tests/test_file_size_powerloss.c | 6 +++--- tests/test_logging_workload.c | 4 ++-- tests/test_root_fill.c | 4 ++-- tests/test_subdir_fill.c | 4 ++-- tests/test_volume_fill.c | 4 ++-- 15 files changed, 76 insertions(+), 21 deletions(-) diff --git a/Readme.md b/Readme.md index 93ce74c..d8ade1e 100644 --- a/Readme.md +++ b/Readme.md @@ -75,3 +75,17 @@ Cleanflight / Betaflight's "blackbox" logging system: [filesystem consumer code] You'll notice that since most filesystem operations will fail and ask you to retry when the card is busy, it becomes natural to call it using a state-machine from your app's main loop - where you only advance to the next state once the current operation succeeds, calling afatfs_poll() in-between so that the filesystem can complete its queued tasks. + +### Asynchronous I/O + +There is experimental support for removing the need to call `afatfs_poll()` and to remove the `sdcard_poll()` +primitive. This feature can be activated by compiling AsyncFatFS with the "AFATFS_ASYNC_IO" flag. When this flag is +set, `afatfs_poll()` will be called internally from the callback function provided to `sdcard_readBlock()` and +`sdcard_writeBlock()`. AsyncFatFS will no longer call `sdcard_poll()` if this is enabled. + +It is **strongly recommended** that `sdcard_readBlock()` and `sdcard_writeBlock()` implement some kind of scheduling +and never call the provided callback directly. Since the callback functions themselves will call `afatfs_poll()` +internally (in this mode), another call to any of the SD card primitives could be triggered. This will lead to +AsyncFatFS performing an unbounded amount of work (particularly at card init when writing the freefile) and using an +unbounded amount of stack size. If you have an infinite stack, then please go ahead. If not, then make sure that you +use the callbacks further up the stack, e.g. from your main loop. diff --git a/lib/asyncfatfs.c b/lib/asyncfatfs.c index df4305d..3a45e76 100644 --- a/lib/asyncfatfs.c +++ b/lib/asyncfatfs.c @@ -491,7 +491,9 @@ static void afatfs_fileOperationContinue(afatfsFile_t *file); static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file); static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file); static void afatfs_fileOperationsPoll(); +#ifdef AFATFS_ASYNC_IO static void afatfs_poll(); +#endif static uint32_t roundUpTo(uint32_t value, uint32_t rounding) { @@ -641,7 +643,9 @@ static void afatfs_sdcardReadComplete(sdcardBlockOperation_e operation, uint32_t break; } } +#ifdef AFATFS_ASYNC_IO afatfs_poll(); +#endif } /** @@ -673,7 +677,9 @@ static void afatfs_sdcardWriteComplete(sdcardBlockOperation_e operation, uint32_ break; } } +#ifdef AFATFS_ASYNC_IO afatfs_poll(); +#endif } /** @@ -3536,14 +3542,26 @@ static void afatfs_initContinue() } } +static bool afatfs_continue() +{ + bool cont = true; +#ifndef AFATFS_ASYNC_IO + cont = sdcard_poll(); +#endif + return cont ? afatfs_flush() : false; +} + /** * Check to see if there are any pending operations on the filesystem and perform a little work (without waiting on the * sdcard). */ -static void afatfs_poll() +#ifdef AFATFS_ASYNC_IO +static +#endif +void afatfs_poll() { // Only attempt to continue FS operations if the card is present & ready, otherwise we would just be wasting time - if (afatfs_flush()) { + if (afatfs_continue()) { switch (afatfs.filesystemState) { case AFATFS_FILESYSTEM_STATE_INITIALIZATION: afatfs_initContinue(); diff --git a/lib/asyncfatfs.h b/lib/asyncfatfs.h index c4b643a..c671a0d 100644 --- a/lib/asyncfatfs.h +++ b/lib/asyncfatfs.h @@ -65,6 +65,9 @@ void afatfs_findLast(afatfsFilePtr_t directory); bool afatfs_flush(); void afatfs_init(); bool afatfs_destroy(bool dirty); +#ifndef AFATFS_ASYNC_IO +void afatfs_poll(); +#endif uint32_t afatfs_getFreeBufferSpace(); uint32_t afatfs_getContiguousFreeSpace(); diff --git a/lib/sdcard.h b/lib/sdcard.h index ea6da3e..0edf17d 100644 --- a/lib/sdcard.h +++ b/lib/sdcard.h @@ -66,6 +66,13 @@ bool sdcard_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationComp */ sdcardOperationStatus_e sdcard_writeBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_operationCompleteCallback_c callback, uint32_t callbackData); +/** + * Will be called by afatfs_poll() periodically for the SD card to perform in-progress transfers. + * + * Returns true if the card is ready to accept new commands. + */ +bool sdcard_poll(); + /** * Begin writing a series of consecutive blocks beginning at the given block index. This will allow (but not require) * the SD card to pre-erase the number of blocks you specifiy, which can allow the writes to complete faster. diff --git a/tests/common.c b/tests/common.c index 6699d60..37f6332 100644 --- a/tests/common.c +++ b/tests/common.c @@ -1,4 +1,5 @@ #include "common.h" +#include "sdcard.h" #include #include @@ -65,3 +66,14 @@ void testAssert(bool condition, const char *errorMessage) exit(-1); } } + +void testPoll() +{ +#ifdef AFATFS_ASYNC_IO + // Let's reuse the sdcard_poll() function for asynchronous I/O mode, even if it's not strictly necessary. + // It could as well be implemented as a separate worker thread. + sdcard_poll(); +#else + afatfs_poll(); +#endif +} diff --git a/tests/common.h b/tests/common.h index c9ae5cd..f8e6dbc 100644 --- a/tests/common.h +++ b/tests/common.h @@ -7,4 +7,6 @@ bool validateLogTestEntries(afatfsFilePtr_t file, uint32_t *entryIndex, uint32_t void testAssert(bool condition, const char *errorMessage); +void testPoll(); + #define TEST_LOG_ENTRY_SIZE 16 diff --git a/tests/sdcard_sim.h b/tests/sdcard_sim.h index c91edb0..6ed6979 100644 --- a/tests/sdcard_sim.h +++ b/tests/sdcard_sim.h @@ -1,6 +1,5 @@ #include "sdcard.h" -bool sdcard_poll(); bool sdcard_sim_init(const char *filename); void sdcard_sim_destroy(); bool sdcard_sim_isReady(); diff --git a/tests/test_file_delete.c b/tests/test_file_delete.c index bcef9a9..c75d032 100644 --- a/tests/test_file_delete.c +++ b/tests/test_file_delete.c @@ -512,7 +512,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -530,7 +530,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_file_modes.c b/tests/test_file_modes.c index 3de4153..6fff276 100644 --- a/tests/test_file_modes.c +++ b/tests/test_file_modes.c @@ -217,7 +217,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -235,7 +235,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_file_size.c b/tests/test_file_size.c index fab7988..d300fea 100644 --- a/tests/test_file_size.c +++ b/tests/test_file_size.c @@ -57,7 +57,7 @@ static void initFilesystem() afatfs_init(); while (afatfs_getFilesystemState() != AFATFS_FILESYSTEM_STATE_READY) { - sdcard_poll(); + testPoll(); if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_FATAL) { fprintf(stderr, "[Fail] Fatal filesystem error during init\n"); @@ -252,7 +252,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -270,7 +270,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_file_size_powerloss.c b/tests/test_file_size_powerloss.c index be8b501..1e265f8 100644 --- a/tests/test_file_size_powerloss.c +++ b/tests/test_file_size_powerloss.c @@ -58,7 +58,7 @@ static void initFilesystem() afatfs_init(); while (afatfs_getFilesystemState() != AFATFS_FILESYSTEM_STATE_READY) { - sdcard_poll(); + testPoll(); if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_FATAL) { fprintf(stderr, "[Fail] Fatal filesystem error during init\n"); @@ -248,7 +248,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -266,7 +266,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_logging_workload.c b/tests/test_logging_workload.c index e36117d..09b88a5 100644 --- a/tests/test_logging_workload.c +++ b/tests/test_logging_workload.c @@ -207,7 +207,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -225,7 +225,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_root_fill.c b/tests/test_root_fill.c index 1032118..d1d09ea 100644 --- a/tests/test_root_fill.c +++ b/tests/test_root_fill.c @@ -134,7 +134,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -152,7 +152,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_subdir_fill.c b/tests/test_subdir_fill.c index 72a63e9..6bd1623 100644 --- a/tests/test_subdir_fill.c +++ b/tests/test_subdir_fill.c @@ -153,7 +153,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -171,7 +171,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy(); diff --git a/tests/test_volume_fill.c b/tests/test_volume_fill.c index f879afd..22f94f5 100644 --- a/tests/test_volume_fill.c +++ b/tests/test_volume_fill.c @@ -218,7 +218,7 @@ int main(int argc, char **argv) bool keepGoing = true; while (keepGoing) { - sdcard_poll(); + testPoll(); switch (afatfs_getFilesystemState()) { case AFATFS_FILESYSTEM_STATE_READY: @@ -236,7 +236,7 @@ int main(int argc, char **argv) } while (!afatfs_destroy(false)) { - sdcard_poll(); + testPoll(); } sdcard_sim_destroy();