diff options
author | Christopher Haster <chaster@utexas.edu> | 2019-07-29 05:53:13 +0300 |
---|---|---|
committer | Christopher Haster <chaster@utexas.edu> | 2019-07-29 05:53:13 +0300 |
commit | e1f3b90b56c31503473ab19cc1c6b466cc699eff (patch) | |
tree | b3065f1fb5d7ea7f0040191a94ca5ded1a439adb | |
parent | 51fabc672b43a6285bced7e6574989cfc203ebd9 (diff) | |
parent | 74fe46de3de98cecdff8681ccd53c481c31352e6 (diff) |
Merge remote-tracking branch 'origin/master' into debug-improvements
-rw-r--r-- | .travis.yml | 16 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | emubd/lfs_emubd.c | 5 | ||||
-rw-r--r-- | emubd/lfs_emubd.h | 16 | ||||
-rw-r--r-- | lfs.c | 225 | ||||
-rw-r--r-- | lfs.h | 19 | ||||
-rw-r--r-- | scripts/template.fmt | 2 | ||||
-rwxr-xr-x | tests/test_files.sh | 73 |
8 files changed, 229 insertions, 129 deletions
diff --git a/.travis.yml b/.travis.yml index dcfedf6..c7a9bfb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,20 @@ script: - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0" + - make clean test QUIET=1 CFLAGS+="-DLFS_EMUBD_ERASE_VALUE=0xff" - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" + # additional configurations that don't support all tests (this should be + # fixed but at the moment it is what it is) + - make test_files QUIET=1 + CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096" + - make test_files QUIET=1 + CFLAGS+="-DLFS_READ_SIZE=\(2*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)" + - make test_files QUIET=1 + CFLAGS+="-DLFS_READ_SIZE=\(8*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)" + - make test_files QUIET=1 + CFLAGS+="-DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704" + # compile and find the code size with the smallest configuration - make clean size OBJ="$(ls lfs*.o | tr '\n' ' ')" @@ -111,7 +123,7 @@ jobs: if: branch !~ -prefix$ install: - sudo apt-get install libfuse-dev - - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha + - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 - fusermount -V - gcc --version before_script: @@ -146,7 +158,7 @@ jobs: if: branch !~ -prefix$ install: - sudo apt-get install libfuse-dev - - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha v2 + - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2 - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1 - fusermount -V - gcc --version @@ -60,7 +60,7 @@ test: \ test_%: tests/test_%.sh ifdef QUIET - @./$< | sed -n '/^[-=]/p' + @./$< | sed -nu '/^[-=]/p' else ./$< endif diff --git a/emubd/lfs_emubd.c b/emubd/lfs_emubd.c index 2f729fb..ee20f27 100644 --- a/emubd/lfs_emubd.c +++ b/emubd/lfs_emubd.c @@ -11,7 +11,6 @@ #include <stdlib.h> #include <stdio.h> #include <limits.h> -#include <dirent.h> #include <sys/stat.h> #include <unistd.h> #include <assert.h> @@ -96,7 +95,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { snprintf(emu->child, LFS_NAME_MAX, ".stats"); FILE *f = fopen(emu->path, "r"); if (!f) { - memset(&emu->stats, 0, sizeof(emu->stats)); + memset(&emu->stats, LFS_EMUBD_ERASE_VALUE, sizeof(emu->stats)); } else { size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); lfs_emubd_fromle32(emu); @@ -265,7 +264,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, // update history and stats if (block != emu->history.blocks[0]) { - memcpy(&emu->history.blocks[1], &emu->history.blocks[0], + memmove(&emu->history.blocks[1], &emu->history.blocks[0], sizeof(emu->history) - sizeof(emu->history.blocks[0])); emu->history.blocks[0] = block; } diff --git a/emubd/lfs_emubd.h b/emubd/lfs_emubd.h index 64afa3e..0fd78c1 100644 --- a/emubd/lfs_emubd.h +++ b/emubd/lfs_emubd.h @@ -17,20 +17,8 @@ extern "C" // Config options -#ifndef LFS_EMUBD_READ_SIZE -#define LFS_EMUBD_READ_SIZE 1 -#endif - -#ifndef LFS_EMUBD_PROG_SIZE -#define LFS_EMUBD_PROG_SIZE 1 -#endif - -#ifndef LFS_EMUBD_ERASE_SIZE -#define LFS_EMUBD_ERASE_SIZE 512 -#endif - -#ifndef LFS_EMUBD_TOTAL_SIZE -#define LFS_EMUBD_TOTAL_SIZE 524288 +#ifndef LFS_EMUBD_ERASE_VALUE +#define LFS_EMUBD_ERASE_VALUE 0x00 #endif @@ -1,19 +1,8 @@ /* * The little filesystem * - * Copyright (c) 2017 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause */ #include "lfs.h" #include "lfs_util.h" @@ -420,7 +409,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, static int lfs_dir_compact(lfs_t *lfs, lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, lfs_mdir_t *source, uint16_t begin, uint16_t end); -static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file); +static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file); static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); static void lfs_fs_prepmove(lfs_t *lfs, @@ -636,11 +625,17 @@ static int lfs_dir_traverse_filter(void *p, lfs_tag_t *filtertag = p; (void)buffer; + // which mask depends on unique bit in tag structure + uint32_t mask = (tag & LFS_MKTAG(0x100, 0, 0)) + ? LFS_MKTAG(0x7ff, 0x3ff, 0) + : LFS_MKTAG(0x700, 0x3ff, 0); + // check for redundancy - uint32_t mask = LFS_MKTAG(0x7ff, 0x3ff, 0); if ((mask & tag) == (mask & *filtertag) || - (mask & tag) == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { + lfs_tag_isdelete(*filtertag) || + (LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == ( + LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | + (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { return true; } @@ -1238,65 +1233,85 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { // align to program units - lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), + const lfs_off_t off1 = commit->off + sizeof(lfs_tag_t); + const lfs_off_t end = lfs_alignup(off1 + sizeof(uint32_t), lfs->cfg->prog_size); - // read erased state from next program unit - lfs_tag_t tag; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - commit->block, off, &tag, sizeof(tag)); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // build crc tag - bool reset = ~lfs_frombe32(tag) >> 31; - tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, - off - (commit->off+sizeof(lfs_tag_t))); + // create crc tags to fill up remainder of commit, note that + // padding is not crcd, which lets fetches skip padding but + // makes committing a bit more complicated + while (commit->off < end) { + lfs_off_t off = commit->off + sizeof(lfs_tag_t); + lfs_off_t noff = lfs_min(end - off, 0x3fe) + off; + if (noff < end) { + noff = lfs_min(noff, end - 2*sizeof(uint32_t)); + } - // write out crc - uint32_t footer[2]; - footer[0] = lfs_tobe32(tag ^ commit->ptag); - commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); - footer[1] = lfs_tole32(commit->crc); - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off, &footer, sizeof(footer)); - if (err) { - return err; - } - commit->off += sizeof(tag)+lfs_tag_size(tag); - commit->ptag = tag ^ (reset << 31); + // read erased state from next program unit + lfs_tag_t tag = 0xffffffff; + int err = lfs_bd_read(lfs, + NULL, &lfs->rcache, sizeof(tag), + commit->block, noff, &tag, sizeof(tag)); + if (err && err != LFS_ERR_CORRUPT) { + return err; + } - // flush buffers - err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); - if (err) { - return err; - } + // build crc tag + bool reset = ~lfs_frombe32(tag) >> 31; + tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, noff - off); - // successful commit, check checksum to make sure - uint32_t crc = 0xffffffff; - lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin; - for (lfs_off_t i = 0; i < size; i++) { - // leave it up to caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, size-i, - commit->block, commit->begin+i, &dat, 1); + // write out crc + uint32_t footer[2]; + footer[0] = lfs_tobe32(tag ^ commit->ptag); + commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); + footer[1] = lfs_tole32(commit->crc); + err = lfs_bd_prog(lfs, + &lfs->pcache, &lfs->rcache, false, + commit->block, commit->off, &footer, sizeof(footer)); if (err) { return err; } - crc = lfs_crc(crc, &dat, 1); + commit->off += sizeof(tag)+lfs_tag_size(tag); + commit->ptag = tag ^ (reset << 31); + commit->crc = 0xffffffff; // reset crc for next "commit" } + // flush buffers + int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); if (err) { return err; } - if (crc != commit->crc) { - return LFS_ERR_CORRUPT; + // successful commit, check checksums to make sure + lfs_off_t off = commit->begin; + lfs_off_t noff = off1; + while (off < end) { + uint32_t crc = 0xffffffff; + for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) { + // leave it up to caching to make this efficient + uint8_t dat; + err = lfs_bd_read(lfs, + NULL, &lfs->rcache, noff+sizeof(uint32_t)-i, + commit->block, i, &dat, 1); + if (err) { + return err; + } + + crc = lfs_crc(crc, &dat, 1); + } + + // detected write error? + if (crc != 0) { + return LFS_ERR_CORRUPT; + } + + // skip padding + off = lfs_min(end - noff, 0x3fe) + noff; + if (off < end) { + off = lfs_min(off, end - 2*sizeof(uint32_t)); + } + noff = off + sizeof(uint32_t); } return 0; @@ -1459,7 +1474,7 @@ static int lfs_dir_compact(lfs_t *lfs, // increment revision count dir->rev += 1; - if (lfs->cfg->block_cycles && + if (lfs->cfg->block_cycles > 0 && (dir->rev % (lfs->cfg->block_cycles+1) == 0)) { if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { // oh no! we're writing too much to the superblock, @@ -1589,11 +1604,11 @@ static int lfs_dir_compact(lfs_t *lfs, } // successful compaction, swap dir pair to indicate most recent + LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); lfs_pair_swap(dir->pair); dir->count = end - begin; dir->off = commit.off; dir->etag = commit.ptag; - dir->erased = (dir->off % lfs->cfg->prog_size == 0); // note we able to have already handled move here if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { lfs_gstate_xormove(&lfs->gpending, @@ -1649,11 +1664,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 && f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) && f->ctz.size > lfs->cfg->cache_size) { - f->flags &= ~LFS_F_READING; - f->off = 0; - - lfs_alloc_ack(lfs); - int err = lfs_file_relocate(lfs, f); + int err = lfs_file_outline(lfs, f); if (err) { return err; } @@ -1764,6 +1775,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, } // successful commit, update dir + LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); dir->off = commit.off; dir->etag = commit.ptag; @@ -2295,6 +2307,10 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", (void*)lfs, (void*)file, path, flags, (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); + + // do not allow open for already opened file + LFS_ASSERT(0 == (file->flags & LFS_F_OPENED)); + // deorphan if we haven't yet, needed at most once after poweron if ((flags & 3) != LFS_O_RDONLY) { int err = lfs_fs_forceconsistency(lfs); @@ -2307,7 +2323,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, // setup simple file details int err; file->cfg = cfg; - file->flags = flags; + file->flags = flags | LFS_F_OPENED; file->pos = 0; file->cache.buffer = NULL; @@ -2452,6 +2468,8 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file); + LFS_ASSERT(file->flags & LFS_F_OPENED); + int err = lfs_file_sync(lfs, file); // remove from list of mdirs @@ -2467,11 +2485,14 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { lfs_free(file->cache.buffer); } + file->flags &= ~LFS_F_OPENED; LFS_TRACE("lfs_file_close -> %d", err); return err; } static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { + LFS_ASSERT(file->flags & LFS_F_OPENED); + while (true) { // just relocate what exists into new block lfs_block_t nblock; @@ -2529,7 +2550,6 @@ static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { lfs_cache_zero(lfs, &lfs->pcache); file->block = nblock; - file->flags &= ~LFS_F_INLINE; file->flags |= LFS_F_WRITING; return 0; @@ -2541,7 +2561,21 @@ relocate: } } +static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) { + file->off = file->pos; + lfs_alloc_ack(lfs); + int err = lfs_file_relocate(lfs, file); + if (err) { + return err; + } + + file->flags &= ~LFS_F_INLINE; + return 0; +} + static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { + LFS_ASSERT(file->flags & LFS_F_OPENED); + if (file->flags & LFS_F_READING) { if (!(file->flags & LFS_F_INLINE)) { lfs_cache_drop(lfs, &file->cache); @@ -2557,7 +2591,7 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { lfs_file_t orig = { .ctz.head = file->ctz.head, .ctz.size = file->ctz.size, - .flags = LFS_O_RDONLY, + .flags = LFS_O_RDONLY | LFS_F_OPENED, .pos = file->pos, .cache = lfs->rcache, }; @@ -2621,6 +2655,8 @@ relocate: int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); + LFS_ASSERT(file->flags & LFS_F_OPENED); + while (true) { int err = lfs_file_flush(lfs, file); if (err) { @@ -2674,8 +2710,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { relocate: // inline file doesn't fit anymore - file->off = file->pos; - err = lfs_file_relocate(lfs, file); + err = lfs_file_outline(lfs, file); if (err) { file->flags |= LFS_F_ERRED; LFS_TRACE("lfs_file_sync -> %d", err); @@ -2688,14 +2723,12 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size) { LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")", (void*)lfs, (void*)file, buffer, size); + LFS_ASSERT(file->flags & LFS_F_OPENED); + LFS_ASSERT((file->flags & 3) != LFS_O_WRONLY); + uint8_t *data = buffer; lfs_size_t nsize = size; - if ((file->flags & 3) == LFS_O_WRONLY) { - LFS_TRACE("lfs_file_read -> %"PRId32, LFS_ERR_BADF); - return LFS_ERR_BADF; - } - if (file->flags & LFS_F_WRITING) { // flush out any writes int err = lfs_file_flush(lfs, file); @@ -2770,14 +2803,12 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size) { LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")", (void*)lfs, (void*)file, buffer, size); + LFS_ASSERT(file->flags & LFS_F_OPENED); + LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY); + const uint8_t *data = buffer; lfs_size_t nsize = size; - if ((file->flags & 3) == LFS_O_RDONLY) { - LFS_TRACE("lfs_file_write -> %"PRId32, LFS_ERR_BADF); - return LFS_ERR_BADF; - } - if (file->flags & LFS_F_READING) { // drop any reads int err = lfs_file_flush(lfs, file); @@ -2816,9 +2847,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, lfs_min(0x3fe, lfs_min( lfs->cfg->cache_size, lfs->cfg->block_size/8))) { // inline file doesn't fit anymore - file->off = file->pos; - lfs_alloc_ack(lfs); - int err = lfs_file_relocate(lfs, file); + int err = lfs_file_outline(lfs, file); if (err) { file->flags |= LFS_F_ERRED; LFS_TRACE("lfs_file_write -> %"PRId32, err); @@ -2905,6 +2934,8 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, lfs_soff_t off, int whence) { LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)", (void*)lfs, (void*)file, off, whence); + LFS_ASSERT(file->flags & LFS_F_OPENED); + // write out everything beforehand, may be noop if rdonly int err = lfs_file_flush(lfs, file); if (err) { @@ -2937,10 +2968,8 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")", (void*)lfs, (void*)file, size); - if ((file->flags & 3) == LFS_O_RDONLY) { - LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_BADF); - return LFS_ERR_BADF; - } + LFS_ASSERT(file->flags & LFS_F_OPENED); + LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY); if (size > LFS_FILE_MAX) { LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_INVAL); @@ -3003,6 +3032,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file); + LFS_ASSERT(file->flags & LFS_F_OPENED); (void)lfs; LFS_TRACE("lfs_file_tell -> %"PRId32, file->pos); return file->pos; @@ -3022,6 +3052,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file); + LFS_ASSERT(file->flags & LFS_F_OPENED); (void)lfs; if (file->flags & LFS_F_WRITING) { LFS_TRACE("lfs_file_size -> %"PRId32, @@ -3344,8 +3375,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4)) <= lfs->cfg->block_size); - // we don't support some corner cases - LFS_ASSERT(lfs->cfg->block_cycles < 0xffffffff); + // block_cycles = 0 is no longer supported. + // + // block_cycles is the number of erase cycles before littlefs evicts + // metadata logs as a part of wear leveling. Suggested values are in the + // range of 100-1000, or set block_cycles to -1 to disable block-level + // wear-leveling. + LFS_ASSERT(lfs->cfg->block_cycles != 0); + // setup read cache if (lfs->cfg->read_buffer) { @@ -3493,7 +3530,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { }; lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( + err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), @@ -4533,7 +4570,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { entry1.d.type &= ~0x80; } - + // also fetch name char name[LFS_NAME_MAX+1]; memset(name, 0, sizeof(name)); @@ -136,6 +136,7 @@ enum lfs_open_flags { LFS_F_READING = 0x040000, // File has been read since last flush LFS_F_ERRED = 0x080000, // An error occured during write LFS_F_INLINE = 0x100000, // Currently inlined in directory entry + LFS_F_OPENED = 0x200000, // File has been opened }; // File seek flags @@ -190,9 +191,13 @@ struct lfs_config { // Number of erasable blocks on the device. lfs_size_t block_count; - // Number of erase cycles before we should move data to another block. - // May be zero, in which case no block-level wear-leveling is performed. - uint32_t block_cycles; + // 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. + // + // Set to -1 to disable block-level wear-leveling. + int32_t block_cycles; // Size of block caches. Each cache buffers a portion of a block in RAM. // The littlefs needs a read cache, a program cache, and one additional @@ -204,7 +209,7 @@ struct lfs_config { // Size of the lookahead buffer in bytes. A larger lookahead buffer // increases the number of blocks found during an allocation pass. The // lookahead buffer is stored as a compact bitmap, so each byte of RAM - // can track 8 blocks. Must be a multiple of 4. + // can track 8 blocks. Must be a multiple of 8. lfs_size_t lookahead_size; // Optional statically allocated read buffer. Must be cache_size. @@ -216,7 +221,7 @@ struct lfs_config { void *prog_buffer; // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 64-bit boundary. By default lfs_malloc is used to + // and aligned to a 32-bit boundary. By default lfs_malloc is used to // allocate this buffer. void *lookahead_buffer; @@ -528,7 +533,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, // Change the position of the file // // The change in position is determined by the offset and whence flag. -// Returns the old position of the file, or a negative error code on failure. +// Returns the new position of the file, or a negative error code on failure. lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, lfs_soff_t off, int whence); @@ -545,7 +550,7 @@ lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); // Change the position of the file to the beginning of the file // -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) +// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET) // Returns a negative error code on failure. int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); diff --git a/scripts/template.fmt b/scripts/template.fmt index b0c74ff..969e198 100644 --- a/scripts/template.fmt +++ b/scripts/template.fmt @@ -63,7 +63,7 @@ char path[1024]; #endif #ifndef LFS_CACHE_SIZE -#define LFS_CACHE_SIZE 64 +#define LFS_CACHE_SIZE (64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE) #endif #ifndef LFS_LOOKAHEAD_SIZE diff --git a/tests/test_files.sh b/tests/test_files.sh index be3f4e6..f6535f6 100755 --- a/tests/test_files.sh +++ b/tests/test_files.sh @@ -140,20 +140,79 @@ scripts/test.py << TEST lfs_unmount(&lfs) => 0; TEST -echo "--- Many file test ---" +echo "--- Many files test ---" scripts/test.py << TEST lfs_format(&lfs, &cfg) => 0; TEST scripts/test.py << TEST - // Create 300 files of 6 bytes + // Create 300 files of 7 bytes lfs_mount(&lfs, &cfg) => 0; - lfs_mkdir(&lfs, "directory") => 0; for (unsigned i = 0; i < 300; i++) { sprintf(path, "file_%03d", i); - lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; - lfs_size_t size = 6; - memcpy(buffer, "Hello", size); - lfs_file_write(&lfs, &file, buffer, size) => size; + lfs_file_open(&lfs, &file, path, + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfs_size_t size = 7; + uint8_t wbuffer[1024]; + uint8_t rbuffer[1024]; + snprintf((char*)wbuffer, size, "Hi %03d", i); + lfs_file_write(&lfs, &file, wbuffer, size) => size; + lfs_file_rewind(&lfs, &file) => 0; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(wbuffer, rbuffer, size) => 0; + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; +TEST + +echo "--- Many files with flush test ---" +scripts/test.py << TEST + lfs_format(&lfs, &cfg) => 0; +TEST +scripts/test.py << TEST + // Create 300 files of 7 bytes + lfs_mount(&lfs, &cfg) => 0; + for (unsigned i = 0; i < 300; i++) { + sprintf(path, "file_%03d", i); + lfs_file_open(&lfs, &file, path, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfs_size_t size = 7; + uint8_t wbuffer[1024]; + uint8_t rbuffer[1024]; + snprintf((char*)wbuffer, size, "Hi %03d", i); + lfs_file_write(&lfs, &file, wbuffer, size) => size; + lfs_file_close(&lfs, &file) => 0; + + lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(wbuffer, rbuffer, size) => 0; + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; +TEST + +echo "--- Many files with power cycle test ---" +scripts/test.py << TEST + lfs_format(&lfs, &cfg) => 0; +TEST +scripts/test.py << TEST + // Create 300 files of 7 bytes + lfs_mount(&lfs, &cfg) => 0; + for (unsigned i = 0; i < 300; i++) { + sprintf(path, "file_%03d", i); + lfs_file_open(&lfs, &file, path, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfs_size_t size = 7; + uint8_t wbuffer[1024]; + uint8_t rbuffer[1024]; + snprintf((char*)wbuffer, size, "Hi %03d", i); + lfs_file_write(&lfs, &file, wbuffer, size) => size; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(wbuffer, rbuffer, size) => 0; lfs_file_close(&lfs, &file) => 0; } lfs_unmount(&lfs) => 0; |