feat(vfs/fatfs): Add option for automatic immediate fsync

This commit is contained in:
Tomáš Rohlínek 2023-09-22 08:55:00 +02:00
parent 17ae394fd1
commit 525776f748
No known key found for this signature in database
GPG Key ID: BDE1CEDD10F7E372
7 changed files with 124 additions and 10 deletions

View File

@ -230,4 +230,13 @@ menu "FAT Filesystem support"
accessing target media for given file descriptor!
See 'Improving I/O performance' section of 'Maximizing Execution Speed' documentation page
for more details.
config FATFS_IMMEDIATE_FSYNC
bool "Enable automatic f_sync"
default n
help
Enables automatic calling of f_sync() to flush recent file changes after each call of vfs_fat_write(),
vfs_fat_pwrite(), vfs_fat_link(), vfs_fat_truncate() and vfs_fat_ftruncate() functions.
This feature improves file-consistency and size reporting accuracy for the FatFS,
at a price on decreased performance due to frequent disk operations
endmenu

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,6 +10,7 @@
#include <time.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "unity.h"
#include "esp_partition.h"
#include "esp_log.h"
@ -274,3 +275,14 @@ TEST_CASE("FATFS prefers SPI RAM for allocations", "[fatfs]")
test_teardown();
}
#endif // CONFIG_SPIRAM
#if CONFIG_FATFS_IMMEDIATE_FSYNC
TEST_CASE("Size is correct after write when immediate fsync is enabled", "[fatfs]")
{
test_setup();
test_fatfs_size("/spiflash/size.txt", "random text\n preferably something relatively long\n");
test_teardown();
}
#endif // CONFIG_FATFS_IMMEDIATE_FSYNC

View File

@ -0,0 +1 @@
CONFIG_FATFS_IMMEDIATE_FSYNC=y

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -434,6 +434,40 @@ void test_fatfs_stat(const char* filename, const char* root_dir)
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
}
void test_fatfs_size(const char* filename, const char* content) {
size_t expected_size = strlen(content);
int fd = open(filename, O_CREAT | O_WRONLY);
TEST_ASSERT_NOT_EQUAL(-1, fd);
ssize_t wr = write(fd, content, expected_size);
TEST_ASSERT_NOT_EQUAL(-1, wr);
struct stat st;
TEST_ASSERT_EQUAL(0, stat(filename, &st));
TEST_ASSERT_EQUAL(wr, st.st_size);
ssize_t wr2 = pwrite(fd, content, expected_size, expected_size);
TEST_ASSERT_NOT_EQUAL(-1, wr2);
TEST_ASSERT_EQUAL(0, stat(filename, &st));
TEST_ASSERT_EQUAL(wr + wr2, st.st_size);
TEST_ASSERT_EQUAL(0, ftruncate(fd, wr));
TEST_ASSERT_EQUAL(0, stat(filename, &st));
TEST_ASSERT_EQUAL(wr, st.st_size);
TEST_ASSERT_EQUAL(0, close(fd));
wr /= 2;
TEST_ASSERT_EQUAL(0, truncate(filename, wr));
TEST_ASSERT_EQUAL(0, stat(filename, &st));
TEST_ASSERT_EQUAL(wr, st.st_size);
}
void test_fatfs_mtime_dst(const char* filename, const char* root_dir)
{
struct timeval tv = { 1653638041, 0 };

View File

@ -51,6 +51,8 @@ void test_fatfs_ftruncate_file(const char* path);
void test_fatfs_stat(const char* filename, const char* root_dir);
void test_fatfs_size(const char* filename, const char* content);
void test_fatfs_mtime_dst(const char* filename, const char* root_dir);
void test_fatfs_utime(const char* filename, const char* root_dir);

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -416,6 +416,20 @@ static ssize_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size)
return -1;
}
}
#if CONFIG_FATFS_IMMEDIATE_FSYNC
_lock_acquire(&fat_ctx->lock);
if (written > 0) {
res = f_sync(file);
if (res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
errno = fresult_to_errno(res);
return -1;
}
}
_lock_release(&fat_ctx->lock);
#endif
return written;
}
@ -514,6 +528,18 @@ static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, o
ret = -1; // in case the write was successful but the seek wasn't
}
#if CONFIG_FATFS_IMMEDIATE_FSYNC
if (wr > 0) {
FRESULT f_res2 = f_sync(file); // We need new result to check whether we can overwrite errno
if (f_res2 != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res2);
if (f_res == FR_OK)
errno = fresult_to_errno(f_res2);
ret = -1;
}
}
#endif
pwrite_release:
_lock_release(&fat_ctx->lock);
return ret;
@ -727,6 +753,13 @@ static int vfs_fat_link(void* ctx, const char* n1, const char* n2)
}
size_left -= will_copy;
}
#if CONFIG_FATFS_IMMEDIATE_FSYNC
_lock_acquire(&fat_ctx->lock);
res = f_sync(pf2);
_lock_release(&fat_ctx->lock);
#endif
fail3:
f_close(pf2);
fail2:
@ -985,14 +1018,25 @@ static int vfs_fat_truncate(void* ctx, const char *path, off_t length)
}
res = f_truncate(file);
_lock_release(&fat_ctx->lock);
if (res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
errno = fresult_to_errno(res);
ret = -1;
goto close;
}
#if CONFIG_FATFS_IMMEDIATE_FSYNC
res = f_sync(file);
if (res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
errno = fresult_to_errno(res);
ret = -1;
}
#endif
_lock_release(&fat_ctx->lock);
close:
res = f_close(file);
@ -1055,8 +1099,18 @@ static int vfs_fat_ftruncate(void* ctx, int fd, off_t length)
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
errno = fresult_to_errno(res);
ret = -1;
goto out;
}
#if CONFIG_FATFS_IMMEDIATE_FSYNC
res = f_sync(file);
if (res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
errno = fresult_to_errno(res);
ret = -1;
}
#endif
out:
_lock_release(&fat_ctx->lock);
return ret;

View File

@ -26,21 +26,23 @@ Most applications use the following workflow when working with ``esp_vfs_fat_``
2. Call :cpp:func:`ff_diskio_register` to register the disk I/O driver for the drive number used in Step 1.
3. Call the FatFs function ``f_mount``, and optionally ``f_fdisk``, ``f_mkfs``, to mount the filesystem using the same drive number which was passed to :cpp:func:`esp_vfs_fat_register`. For more information, see `FatFs documentation <http://elm-chan.org/fsw/ff/doc/mount.html>`_.
3. Call the FatFs function :cpp:func:`f_mount`, and optionally :cpp:func:`f_fdisk`, :cpp:func:`f_mkfs`, to mount the filesystem using the same drive number which was passed to :cpp:func:`esp_vfs_fat_register`. For more information, see `FatFs documentation <http://elm-chan.org/fsw/ff/doc/mount.html>`_.
4. Call the C standard library and POSIX API functions to perform such actions on files as open, read, write, erase, copy, etc. Use paths starting with the path prefix passed to :cpp:func:`esp_vfs_register` (for example, ``"/sdcard/hello.txt"``). The filesystem uses `8.3 filenames <https://en.wikipedia.org/wiki/8.3_filename>`_ format (SFN) by default. If you need to use long filenames (LFN), enable the :ref:`CONFIG_FATFS_LONG_FILENAMES` option. More details on the FatFs filenames are available `here <http://elm-chan.org/fsw/ff/doc/filename.html>`_.
5. Optionally, by enabling the option :ref:`CONFIG_FATFS_USE_FASTSEEK`, you can use the POSIX lseek function to perform it faster. The fast seek does not work for files in write mode, so to take advantage of fast seek, you should open (or close and then reopen) the file in read-only mode.
6. Optionally, call the FatFs library functions directly. In this case, use paths without a VFS prefix (for example, ``"/hello.txt"``).
6. Optionally, by enabling the option :ref:`CONFIG_FATFS_IMMEDIATE_FSYNC`, you can enable automatic calling of :cpp:func:`f_sync` to flush recent file changes after each call of vfs_fat_write(), vfs_fat_pwrite(), vfs_fat_link(), vfs_fat_truncate() and vfs_fat_ftruncate() functions. This feature improves file-consistency and size reporting accuracy for the FatFS, at a price on decreased performance due to frequent disk operations
7. Close all open files.
7. Optionally, call the FatFs library functions directly. In this case, use paths without a VFS prefix (for example, ``"/hello.txt"``).
8. Call the FatFs function ``f_mount`` for the same drive number with NULL ``FATFS*`` argument to unmount the filesystem.
8. Close all open files.
9. Call the FatFs function :cpp:func:`ff_diskio_register` with NULL ``ff_diskio_impl_t*`` argument and the same drive number to unregister the disk I/O driver.
9. Call the FatFs function :cpp:func:`f_mount` for the same drive number with NULL ``FATFS*`` argument to unmount the filesystem.
10. Call :cpp:func:`esp_vfs_fat_unregister_path` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated in Step 1.
10. Call the FatFs function :cpp:func:`ff_diskio_register` with NULL ``ff_diskio_impl_t*`` argument and the same drive number to unregister the disk I/O driver.
11. Call :cpp:func:`esp_vfs_fat_unregister_path` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated in Step 1.
The convenience functions :cpp:func:`esp_vfs_fat_sdmmc_mount`, :cpp:func:`esp_vfs_fat_sdspi_mount`, and :cpp:func:`esp_vfs_fat_sdcard_unmount` wrap the steps described above and also handle SD card initialization. These functions are described in the next section.