From fd31c12282cc30ca951e1e765109ca67449359a7 Mon Sep 17 00:00:00 2001 From: Matthieu Castet Date: Fri, 26 Apr 2024 21:53:49 +0200 Subject: [PATCH 1/4] trace cache operation Can be enabled with LFS_CACHE_TRACE it will output information with LFS_DEBUG --- lfs.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/lfs.c b/lfs.c index d35d5d6d..05d36696 100644 --- a/lfs.c +++ b/lfs.c @@ -28,6 +28,62 @@ enum { /// Caching block device operations /// +enum { + LFS_CACHE_TRACE_EVICT, + LFS_CACHE_TRACE_BYPASS, + LFS_CACHE_TRACE_DROP, +}; + +#ifdef LFS_CACHE_TRACE +static lfs_size_t read_cache_start_used = 0, read_cache_end_used = 0; +static lfs_size_t read_cache_traffic = 0; +static int read_cache_alloc_line = 0, read_cache_recache = 0; +static inline void lfs_cache_trace_log(lfs_t *lfs, int op, lfs_cache_t *rcache, lfs_block_t block, lfs_off_t off, lfs_size_t size, int line) +{ + switch (op) { + case LFS_CACHE_TRACE_EVICT: + case LFS_CACHE_TRACE_DROP: + if (rcache->block != LFS_BLOCK_NULL) { + LFS_DEBUG("cache %d:%d size %d access=[%d-%d] byte_read=%d line=%d\n", + rcache->block, rcache->off, rcache->size, + read_cache_start_used - rcache->off, + read_cache_end_used - rcache->off, + read_cache_traffic, read_cache_alloc_line); + if (rcache->block == block) + read_cache_recache++; + else if (read_cache_recache) { + LFS_DEBUG("cache recache for block %d number %d line=%d\n", + rcache->block, read_cache_recache, read_cache_alloc_line); + read_cache_recache = 0; + } + } + read_cache_start_used = off + lfs->cfg->cache_size; + read_cache_end_used = 0; + read_cache_traffic = 0; + read_cache_alloc_line = line; + + break; + case LFS_CACHE_TRACE_BYPASS: + LFS_DEBUG("cache no %d:%d size %d line=%d\n", block, off, size, line); + break; + } +} + +static inline void lfs_cache_trace_access(lfs_cache_t *rcache, lfs_off_t off, lfs_size_t size) +{ + (void)rcache; + if (read_cache_end_used < off + size) + read_cache_end_used = off + size; + if (read_cache_start_used > off) + read_cache_start_used = off; + + read_cache_traffic += size; +} +#else +#define lfs_cache_trace_log(...) +#define lfs_cache_trace_access(...) +#endif + static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) { // do not zero, cheaper if cache is readonly or only going to be // written with identical data (during relocates) @@ -41,11 +97,13 @@ static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { pcache->block = LFS_BLOCK_NULL; } -static int lfs_bd_read(lfs_t *lfs, +#define lfs_bd_read(...) lfs_bd_read_raw(__VA_ARGS__, __LINE__) +static int lfs_bd_read_raw(lfs_t *lfs, const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, lfs_block_t block, lfs_off_t off, - void *buffer, lfs_size_t size) { + void *buffer, lfs_size_t size, int line) { uint8_t *data = buffer; + (void)line; if (off+size > lfs->cfg->block_size || (lfs->block_count && block >= lfs->block_count)) { return LFS_ERR_CORRUPT; @@ -77,6 +135,7 @@ static int lfs_bd_read(lfs_t *lfs, // is already in rcache? diff = lfs_min(diff, rcache->size - (off-rcache->off)); memcpy(data, &rcache->buffer[off-rcache->off], diff); + lfs_cache_trace_access(rcache, off, diff); data += diff; off += diff; @@ -97,12 +156,15 @@ static int lfs_bd_read(lfs_t *lfs, return err; } + lfs_cache_trace_log(lfs, LFS_CACHE_TRACE_BYPASS, rcache, block, off, diff, line); data += diff; off += diff; size -= diff; + continue; } + lfs_cache_trace_log(lfs, LFS_CACHE_TRACE_EVICT, rcache, block, off, diff, line); // load to cache, first condition can no longer fail LFS_ASSERT(!lfs->block_count || block < lfs->block_count); rcache->block = block; From cb1f92118dcf38c21c3a274741e4dbfbea404cdd Mon Sep 17 00:00:00 2001 From: Matthieu Castet Date: Tue, 23 Apr 2024 15:57:48 +0200 Subject: [PATCH 2/4] lfs_dir_traverse: better try to use cache cache trace show lots of possible caching in lfs_dir_traverse cache recache for block 835 number 4938 line=1683 cache recache for block 882 number 4938 line=1683 cache recache for block 930 number 4938 line=1683 cache recache for block 978 number 4938 line=1683 test config .read_size = 4, .prog_size = 16, .block_size = 4096, .block_count = (8*1024), .cache_size = 1024, .metadata_max = 1024, for (int i = 0; i <= 1000; i++) { char test_path[32]; if (i > 100) { sprintf(test_path, "testdir/test%d", i-100); err = lfs_remove(&lfs, test_path); } sprintf(test_path, "testdir/test%d", i); uint32_t prng = i; lfs_size_t size = 4096 + (test_prng(&prng) & 3); lfs_file_t file; err = lfs_file_open(&lfs, &file, test_path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC); lfs_ssize_t res = lfs_file_write(&lfs, &file, chunk, size); err = lfs_file_close(&lfs, &file); } before patch top read operation (number of flash read size, number of flash op) 106060, 16976, 106060, 16976, 106560, 19645, 107084, 16977, 108820, 17624, total read/write total read=26174188 B write=4287328 B total num read=1445383 op write=7732 op after patch 15044, 41 15044, 41 15044, 41 15044, 41 16068, 42, total read=18809040 B write=4287328 B total num read=49477 op write=7732 op --- lfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfs.c b/lfs.c index 05d36696..e51a7606 100644 --- a/lfs.c +++ b/lfs.c @@ -980,7 +980,7 @@ static int lfs_dir_traverse(lfs_t *lfs, if (off+lfs_tag_dsize(ptag) < dir->off) { off += lfs_tag_dsize(ptag); int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), + NULL, &lfs->rcache, lfs->cfg->block_size, dir->pair[0], off, &tag, sizeof(tag)); if (err) { return err; From 30da07fbf992de5c071586feaf25dcc49948c953 Mon Sep 17 00:00:00 2001 From: Matthieu Castet Date: Tue, 30 Apr 2024 13:18:50 +0200 Subject: [PATCH 3/4] lfs_dir_getslice: reverse caching cache trace show possible caching in lfs_dir_getslice cache recache for block 83 number 23 line=790 cache recache for block 881 number 23 line=790 cache recache for block 929 number 23 line=790 cache recache for block 977 number 23 line=790 cache recache for block 128 number 24 line=790 For the same test than previous patch before patch top read operation (number of flash read size, number of flash op) 15044, 41 15044, 41 15044, 41 15044, 41 16068, 42 total read/write total read=18809040 B write=4287328 B total num read=49477 op write=7732 op after patch top read operation (number of flash read size, number of flash op) 15044, 41 15044, 41 15044, 41 15044, 41 16068, 42 total read/write total read=18852728 B write=4287328 B total num read=45678 op write=7732 op cache recache for block 882 number 2 line=998 cache recache for block 911 number 2 line=998 cache recache for block 930 number 2 line=998 cache recache for block 959 number 2 line=998 cache recache for block 978 number 2 line=998 --- lfs.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lfs.c b/lfs.c index e51a7606..7a01266a 100644 --- a/lfs.c +++ b/lfs.c @@ -99,11 +99,16 @@ static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { #define lfs_bd_read(...) lfs_bd_read_raw(__VA_ARGS__, __LINE__) static int lfs_bd_read_raw(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, + const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_ssize_t shint, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size, int line) { uint8_t *data = buffer; (void)line; + lfs_size_t hint = shint; + int reverse = shint < 0; + if (reverse) { + hint = -hint; + } if (off+size > lfs->cfg->block_size || (lfs->block_count && block >= lfs->block_count)) { return LFS_ERR_CORRUPT; @@ -168,10 +173,20 @@ static int lfs_bd_read_raw(lfs_t *lfs, // load to cache, first condition can no longer fail LFS_ASSERT(!lfs->block_count || block < lfs->block_count); rcache->block = block; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); + lfs_off_t moff = off; + if (reverse) { + lfs_size_t msize; + moff = off + size; + msize = lfs_min(hint, lfs->cfg->cache_size); + if (moff > msize) + moff = lfs_alignup(moff - msize, lfs->cfg->read_size); + else + moff = 0; + } + rcache->off = lfs_aligndown(moff, lfs->cfg->read_size); rcache->size = lfs_min( lfs_min( - lfs_alignup(off+hint, lfs->cfg->read_size), + lfs_alignup(moff+hint, lfs->cfg->read_size), lfs->cfg->block_size) - rcache->off, lfs->cfg->cache_size); @@ -787,7 +802,7 @@ static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, off -= lfs_tag_dsize(ntag); lfs_tag_t tag = ntag; int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(ntag), + NULL, &lfs->rcache, -lfs->cfg->block_size, dir->pair[0], off, &ntag, sizeof(ntag)); if (err) { return err; From 9270114767294d7b3cac3d9ee7bf80f0a8fbd9b8 Mon Sep 17 00:00:00 2001 From: Matthieu Castet Date: Tue, 23 Apr 2024 16:23:42 +0200 Subject: [PATCH 4/4] do direct write if file data doesn't fit in cache --- lfs.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/lfs.c b/lfs.c index 7a01266a..dee71653 100644 --- a/lfs.c +++ b/lfs.c @@ -250,13 +250,15 @@ static int lfs_bd_crc(lfs_t *lfs, } #ifndef LFS_READONLY -static int lfs_bd_flush(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) { - LFS_ASSERT(pcache->block < lfs->block_count); - lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size); - int err = lfs->cfg->prog(lfs->cfg, pcache->block, - pcache->off, pcache->buffer, diff); +static int lfs_bd_flush_direct(lfs_t *lfs, + lfs_cache_t *rcache, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t diff, bool validate) { + if (block != LFS_BLOCK_NULL && block != LFS_BLOCK_INLINE) { + LFS_ASSERT(block < lfs->block_count); + LFS_ASSERT(diff % lfs->cfg->prog_size == 0); + LFS_ASSERT(off % lfs->cfg->prog_size == 0); + int err = lfs->cfg->prog(lfs->cfg, block, + off, buffer, diff); LFS_ASSERT(err <= 0); if (err) { return err; @@ -267,7 +269,7 @@ static int lfs_bd_flush(lfs_t *lfs, lfs_cache_drop(lfs, rcache); int res = lfs_bd_cmp(lfs, NULL, rcache, diff, - pcache->block, pcache->off, pcache->buffer, diff); + block, off, buffer, diff); if (res < 0) { return res; } @@ -277,11 +279,24 @@ static int lfs_bd_flush(lfs_t *lfs, } } - lfs_cache_zero(lfs, pcache); } return 0; } + +static int lfs_bd_flush(lfs_t *lfs, + lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { + int ret = 0; + if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) { + lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size); + ret = lfs_bd_flush_direct(lfs, rcache, pcache->block, pcache->off, + pcache->buffer, diff, validate); + if (ret == 0) + lfs_cache_zero(lfs, pcache); + } + return ret; +} + #endif #ifndef LFS_READONLY @@ -338,6 +353,23 @@ static int lfs_bd_prog(lfs_t *lfs, // entire block or manually flushing the pcache LFS_ASSERT(pcache->block == LFS_BLOCK_NULL); + if (size >= lfs->cfg->cache_size) { + // data do not fit in cache, direct write + // XXX align on cache_size : the file flush logic between + // block is triggered by "pcache->size == lfs->cfg->cache_size" + // could be prog_size in theory + lfs_size_t diff = lfs_aligndown(size, lfs->cfg->cache_size); + int err = lfs_bd_flush_direct(lfs, rcache, block, off, + data, diff, validate); + if (err) { + return err; + } + data += diff; + off += diff; + size -= diff; + continue; + } + // prepare pcache, first condition can no longer fail pcache->block = block; pcache->off = lfs_aligndown(off, lfs->cfg->prog_size);