From 6b16dafb4db903fc1f8509d4170af56ae70ee56e Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 15 Dec 2020 12:59:32 +1000 Subject: [PATCH 1/2] Add metadata_max and inline_file_max to config We have seen poor read performance on NAND flashes with 128kB blocks. The root cause is inline files having to traverse many sets of metadata pairs inside the current block before being fully reconstructed. Simply disabling inline files is not enough, as the metadata will still fill up the block and eventually need to be compacted. By allowing configuration of how much size metadata takes up, along with limiting (or disabling) inline file size, we achieve read performance improvements on an order of magnitude. --- lfs.c | 24 +++++++++++++++++++----- lfs.h | 18 +++++++++++++++++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/lfs.c b/lfs.c index d7439fe..5585b40 100644 --- a/lfs.c +++ b/lfs.c @@ -1589,7 +1589,7 @@ static int lfs_dir_compact(lfs_t *lfs, // for metadata updates. if (end - begin < 0xff && size <= lfs_min(lfs->cfg->block_size - 36, - lfs_alignup(lfs->cfg->block_size/2, + lfs_alignup(lfs->metadata_max/2, lfs->cfg->prog_size))) { break; } @@ -1674,7 +1674,7 @@ static int lfs_dir_compact(lfs_t *lfs, .crc = 0xffffffff, .begin = 0, - .end = lfs->cfg->block_size - 8, + .end = lfs->metadata_max - 8, }; // erase block to write to @@ -1884,7 +1884,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, .crc = 0xffffffff, .begin = dir->off, - .end = lfs->cfg->block_size - 8, + .end = lfs->metadata_max - 8, }; // traverse attrs that need to be written out @@ -2966,7 +2966,7 @@ static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, if ((file->flags & LFS_F_INLINE) && lfs_max(file->pos+nsize, file->ctz.size) > lfs_min(0x3fe, lfs_min( - lfs->cfg->cache_size, lfs->cfg->block_size/8))) { + lfs->cfg->cache_size, lfs->inline_file_max))) { // inline file doesn't fit anymore int err = lfs_file_outline(lfs, file); if (err) { @@ -3536,6 +3536,20 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->attr_max = LFS_ATTR_MAX; } + LFS_ASSERT(lfs->cfg->metadata_max <= lfs->cfg->block_size); + lfs->metadata_max = lfs->cfg->metadata_max; + if (!lfs->metadata_max) { + lfs->metadata_max = lfs->cfg->block_size; + } + + LFS_ASSERT(lfs->cfg->inline_file_max <= LFS_FILE_MAX); + lfs->inline_file_max = lfs->cfg->inline_file_max; + if (!lfs->inline_file_max) { + lfs->inline_file_max = lfs->cfg->block_size / 8; + } else if(lfs->inline_file_max == -1) { + lfs->inline_file_max = 0; + } + // setup default state lfs->root[0] = LFS_BLOCK_NULL; lfs->root[1] = LFS_BLOCK_NULL; @@ -3829,7 +3843,7 @@ int lfs_fs_rawtraverse(lfs_t *lfs, if (err) { return err; } - } else if (includeorphans && + } else if (includeorphans && lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) { for (int i = 0; i < 2; i++) { err = cb(data, (&ctz.head)[i]); diff --git a/lfs.h b/lfs.h index 3b02b6a..8a4b1d0 100644 --- a/lfs.h +++ b/lfs.h @@ -207,7 +207,7 @@ struct lfs_config { // Number of erasable blocks on the device. lfs_size_t block_count; - // Number of erase cycles before littlefs evicts metadata logs and moves + // Number of erase cycles before littlefs evicts metadata logs and moves // the metadata to another block. Suggested values are in the // range 100-1000, with large values having better performance at the cost // of less consistent wear distribution. @@ -256,6 +256,20 @@ struct lfs_config { // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to // LFS_ATTR_MAX when zero. lfs_size_t attr_max; + + // Optional upper limit on total space given to metadata pairs in bytes. On + // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB) + // can help bound the metadata compaction time. Must be <= block_size. + // Defaults to block_size when zero. + lfs_size_t metadata_max; + + // Optional upper limit on inline files in bytes. On devices with large + // blocks (e.g. 128kB) setting this to a low size or disabling inline files + // can help bound file read overhead. Must be <= LFS_FILE_MAX. Defaults to + // block_size/8 when zero. + // + // Set to -1 to disable inline files. + lfs_ssize_t inline_file_max; }; // File info structure @@ -406,6 +420,8 @@ typedef struct lfs { lfs_size_t name_max; lfs_size_t file_max; lfs_size_t attr_max; + lfs_size_t metadata_max; + lfs_ssize_t inline_file_max; #ifdef LFS_MIGRATE struct lfs1 *lfs1; From 37f4de297672ee3ec05103d60e09bb7748c5e5b3 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 18 Dec 2020 13:05:20 +1000 Subject: [PATCH 2/2] Remove inline_files_max and lfs_t entry for metadata_max --- lfs.c | 25 +++++++++---------------- lfs.h | 10 ---------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/lfs.c b/lfs.c index 5585b40..50fb9a4 100644 --- a/lfs.c +++ b/lfs.c @@ -1589,7 +1589,8 @@ static int lfs_dir_compact(lfs_t *lfs, // for metadata updates. if (end - begin < 0xff && size <= lfs_min(lfs->cfg->block_size - 36, - lfs_alignup(lfs->metadata_max/2, + lfs_alignup((lfs->cfg->metadata_max ? + lfs->cfg->metadata_max : lfs->cfg->block_size)/2, lfs->cfg->prog_size))) { break; } @@ -1674,7 +1675,8 @@ static int lfs_dir_compact(lfs_t *lfs, .crc = 0xffffffff, .begin = 0, - .end = lfs->metadata_max - 8, + .end = (lfs->cfg->metadata_max ? + lfs->cfg->metadata_max : lfs->cfg->block_size) - 8, }; // erase block to write to @@ -1884,7 +1886,8 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, .crc = 0xffffffff, .begin = dir->off, - .end = lfs->metadata_max - 8, + .end = (lfs->cfg->metadata_max ? + lfs->cfg->metadata_max : lfs->cfg->block_size) - 8, }; // traverse attrs that need to be written out @@ -2966,7 +2969,9 @@ static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, if ((file->flags & LFS_F_INLINE) && lfs_max(file->pos+nsize, file->ctz.size) > lfs_min(0x3fe, lfs_min( - lfs->cfg->cache_size, lfs->inline_file_max))) { + lfs->cfg->cache_size, + (lfs->cfg->metadata_max ? + lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) { // inline file doesn't fit anymore int err = lfs_file_outline(lfs, file); if (err) { @@ -3537,18 +3542,6 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { } LFS_ASSERT(lfs->cfg->metadata_max <= lfs->cfg->block_size); - lfs->metadata_max = lfs->cfg->metadata_max; - if (!lfs->metadata_max) { - lfs->metadata_max = lfs->cfg->block_size; - } - - LFS_ASSERT(lfs->cfg->inline_file_max <= LFS_FILE_MAX); - lfs->inline_file_max = lfs->cfg->inline_file_max; - if (!lfs->inline_file_max) { - lfs->inline_file_max = lfs->cfg->block_size / 8; - } else if(lfs->inline_file_max == -1) { - lfs->inline_file_max = 0; - } // setup default state lfs->root[0] = LFS_BLOCK_NULL; diff --git a/lfs.h b/lfs.h index 8a4b1d0..c7ec6d3 100644 --- a/lfs.h +++ b/lfs.h @@ -262,14 +262,6 @@ struct lfs_config { // can help bound the metadata compaction time. Must be <= block_size. // Defaults to block_size when zero. lfs_size_t metadata_max; - - // Optional upper limit on inline files in bytes. On devices with large - // blocks (e.g. 128kB) setting this to a low size or disabling inline files - // can help bound file read overhead. Must be <= LFS_FILE_MAX. Defaults to - // block_size/8 when zero. - // - // Set to -1 to disable inline files. - lfs_ssize_t inline_file_max; }; // File info structure @@ -420,8 +412,6 @@ typedef struct lfs { lfs_size_t name_max; lfs_size_t file_max; lfs_size_t attr_max; - lfs_size_t metadata_max; - lfs_ssize_t inline_file_max; #ifdef LFS_MIGRATE struct lfs1 *lfs1;