Merge branch 'feature/fatfs_fast_seek' into 'master'

feature/fatfs: enable the usage of fast-seek algorithm

See merge request espressif/esp-idf!9916
This commit is contained in:
Angus Gratton 2020-11-17 07:17:31 +08:00
commit 6ce34a227e
7 changed files with 96 additions and 7 deletions

View File

@ -180,4 +180,26 @@ menu "FAT Filesystem support"
Disable this option if optimizing for performance. Enable this option if
optimizing for internal memory size.
config FATFS_USE_FASTSEEK
bool "Enable fast seek algorithm when using lseek function through VFS FAT"
default n
help
The fast seek feature enables fast backward/long seek operations without
FAT access by using an in-memory CLMT (cluster link map table).
Please note, fast-seek is only allowed for read-mode files, if a
file is opened in write-mode, the seek mechanism will automatically fallback
to the default implementation.
config FATFS_FAST_SEEK_BUFFER_SIZE
int "Fast seek CLMT buffer size"
default 64
depends on FATFS_USE_FASTSEEK
help
If fast seek algorithm is enabled, this defines the size of
CLMT buffer used by this algorithm in 32-bit word units.
This value should be chosen based on prior knowledge of
maximum elements of each file entry would store.
endmenu

View File

@ -44,7 +44,7 @@
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 0
#define FF_USE_FASTSEEK CONFIG_FATFS_USE_FASTSEEK
/* This option switches fast seek function. (0:Disable or 1:Enable) */

View File

@ -217,11 +217,27 @@ void test_fatfs_lseek(const char* filename)
TEST_ASSERT_EQUAL(18, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
char buf[20];
TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f));
const char ref_buf[] = "0123456789\n\0\0\0abc\n";
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
TEST_ASSERT_EQUAL(0, fclose(f));
#ifdef CONFIG_FATFS_USE_FASTSEEK
f = fopen(filename, "rb+");
TEST_ASSERT_NOT_NULL(f);
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
TEST_ASSERT_EQUAL(18, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, -4, SEEK_CUR));
TEST_ASSERT_EQUAL(14, ftell(f));
TEST_ASSERT_EQUAL(0, fseek(f, -14, SEEK_CUR));
TEST_ASSERT_EQUAL(0, ftell(f));
TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f));
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
TEST_ASSERT_EQUAL(0, fclose(f));
#endif
}
void test_fatfs_truncate_file(const char* filename)

View File

@ -307,6 +307,7 @@ static int vfs_fat_open(void* ctx, const char * path, int flags, int mode)
errno = ENFILE;
return -1;
}
FRESULT res = f_open(&fat_ctx->files[fd], path, fat_mode_conv(flags));
if (res != FR_OK) {
file_cleanup(fat_ctx, fd);
@ -315,6 +316,40 @@ static int vfs_fat_open(void* ctx, const char * path, int flags, int mode)
errno = fresult_to_errno(res);
return -1;
}
#ifdef CONFIG_FATFS_USE_FASTSEEK
FIL* file = &fat_ctx->files[fd];
//fast-seek is only allowed in read mode, since file cannot be expanded
//to use it.
if(!(fat_mode_conv(flags) & (FA_WRITE))) {
DWORD *clmt_mem = ff_memalloc(sizeof(DWORD) * CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE);
if (clmt_mem == NULL) {
f_close(file);
file_cleanup(fat_ctx, fd);
_lock_release(&fat_ctx->lock);
ESP_LOGE(TAG, "open: Failed to pre-allocate CLMT buffer for fast-seek");
errno = ENOMEM;
return -1;
}
file->cltbl = clmt_mem;
file->cltbl[0] = CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE;
res = f_lseek(file, CREATE_LINKMAP);
ESP_LOGD(TAG, "%s: fast-seek has: %s",
__func__,
(res == FR_OK) ? "activated" : "failed");
if(res != FR_OK) {
ESP_LOGW(TAG, "%s: fast-seek not activated reason code: %d",
__func__, res);
//If linkmap creation fails, fallback to the non fast seek.
ff_memfree(file->cltbl);
file->cltbl = NULL;
}
} else {
file->cltbl = NULL;
}
#endif
// O_APPEND need to be stored because it is not compatible with FA_OPEN_APPEND:
// - FA_OPEN_APPEND means to jump to the end of file only after open()
// - O_APPEND means to jump to the end only before each write()
@ -375,6 +410,7 @@ static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t of
const off_t prev_pos = f_tell(file);
FRESULT f_res = f_lseek(file, offset);
if (f_res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
errno = fresult_to_errno(f_res);
@ -414,6 +450,7 @@ static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, o
const off_t prev_pos = f_tell(file);
FRESULT f_res = f_lseek(file, offset);
if (f_res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
errno = fresult_to_errno(f_res);
@ -465,6 +502,12 @@ static int vfs_fat_close(void* ctx, int fd)
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
_lock_acquire(&fat_ctx->lock);
FIL* file = &fat_ctx->files[fd];
#ifdef CONFIG_FATFS_USE_FASTSEEK
ff_memfree(file->cltbl);
file->cltbl = NULL;
#endif
FRESULT res = f_close(file);
file_cleanup(fat_ctx, fd);
_lock_release(&fat_ctx->lock);
@ -494,6 +537,8 @@ static off_t vfs_fat_lseek(void* ctx, int fd, off_t offset, int mode)
errno = EINVAL;
return -1;
}
ESP_LOGD(TAG, "%s: offset=%ld, filesize:=%d", __func__, new_pos, f_size(file));
FRESULT res = f_lseek(file, new_pos);
if (res != FR_OK) {
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);

View File

@ -29,15 +29,17 @@ Most applications use the following workflow when working with ``esp_vfs_fat_``
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"``).
5. Optionally, call the FatFs library functions directly. In this case, use paths without a VFS prefix (for example, ``"/hello.txt"``).
5. Optionally, by enabling the option `CONFIG_FATFS_USE_FASTSEEK`, use the POSIX lseek function to perform it faster, the fast seek will 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. Close all open files.
6. Optionally, call the FatFs library functions directly. In this case, use paths without a VFS prefix (for example, ``"/hello.txt"``).
7. Call the FatFs function ``f_mount`` for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem.
7. Close all open files.
8. 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.
8. Call the FatFs function ``f_mount`` for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem.
9. 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.
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.
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.
The convenience functions ``esp_vfs_fat_sdmmc_mount``, ``esp_vfs_fat_sdspi_mount`` and ``esp_vfs_fat_sdcard_unmount`` wrap the steps described above and also handle SD card initialization. These two functions are described in the next section.

View File

@ -351,6 +351,7 @@ UT_002:
UT_003:
extends: .unit_test_template
parallel: 2
tags:
- ESP32_IDF
- UT_T1_SDMODE

View File

@ -0,0 +1,3 @@
TEST_COMPONENTS=fatfs
CONFIG_FATFS_USE_FASTSEEK=y
CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE=64