Merge remote-tracking branch 'origin/master' into debug-improvements

This commit is contained in:
Christopher Haster
2019-07-28 21:53:13 -05:00
8 changed files with 229 additions and 129 deletions

View File

@@ -23,8 +23,20 @@ script:
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256" - 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_INLINE_MAX=0"
- make clean test QUIET=1 CFLAGS+="-DLFS_EMUBD_ERASE_VALUE=0xff"
- make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" - 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 # compile and find the code size with the smallest configuration
- make clean size - make clean size
OBJ="$(ls lfs*.o | tr '\n' ' ')" OBJ="$(ls lfs*.o | tr '\n' ' ')"
@@ -111,7 +123,7 @@ jobs:
if: branch !~ -prefix$ if: branch !~ -prefix$
install: install:
- sudo apt-get install libfuse-dev - 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 - fusermount -V
- gcc --version - gcc --version
before_script: before_script:
@@ -146,7 +158,7 @@ jobs:
if: branch !~ -prefix$ if: branch !~ -prefix$
install: install:
- sudo apt-get install libfuse-dev - 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 - git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1
- fusermount -V - fusermount -V
- gcc --version - gcc --version

View File

@@ -60,7 +60,7 @@ test: \
test_%: tests/test_%.sh test_%: tests/test_%.sh
ifdef QUIET ifdef QUIET
@./$< | sed -n '/^[-=]/p' @./$< | sed -nu '/^[-=]/p'
else else
./$< ./$<
endif endif

View File

@@ -11,7 +11,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <limits.h> #include <limits.h>
#include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <assert.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"); snprintf(emu->child, LFS_NAME_MAX, ".stats");
FILE *f = fopen(emu->path, "r"); FILE *f = fopen(emu->path, "r");
if (!f) { if (!f) {
memset(&emu->stats, 0, sizeof(emu->stats)); memset(&emu->stats, LFS_EMUBD_ERASE_VALUE, sizeof(emu->stats));
} else { } else {
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
lfs_emubd_fromle32(emu); 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 // update history and stats
if (block != emu->history.blocks[0]) { 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])); sizeof(emu->history) - sizeof(emu->history.blocks[0]));
emu->history.blocks[0] = block; emu->history.blocks[0] = block;
} }

View File

@@ -17,20 +17,8 @@ extern "C"
// Config options // Config options
#ifndef LFS_EMUBD_READ_SIZE #ifndef LFS_EMUBD_ERASE_VALUE
#define LFS_EMUBD_READ_SIZE 1 #define LFS_EMUBD_ERASE_VALUE 0x00
#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
#endif #endif

161
lfs.c
View File

@@ -1,19 +1,8 @@
/* /*
* The little filesystem * The little filesystem
* *
* Copyright (c) 2017 ARM Limited * Copyright (c) 2017, Arm Limited. All rights reserved.
* * SPDX-License-Identifier: BSD-3-Clause
* 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.
*/ */
#include "lfs.h" #include "lfs.h"
#include "lfs_util.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, static int lfs_dir_compact(lfs_t *lfs,
lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
lfs_mdir_t *source, uint16_t begin, uint16_t end); 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 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_preporphans(lfs_t *lfs, int8_t orphans);
static void lfs_fs_prepmove(lfs_t *lfs, static void lfs_fs_prepmove(lfs_t *lfs,
@@ -636,10 +625,16 @@ static int lfs_dir_traverse_filter(void *p,
lfs_tag_t *filtertag = p; lfs_tag_t *filtertag = p;
(void)buffer; (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 // check for redundancy
uint32_t mask = LFS_MKTAG(0x7ff, 0x3ff, 0);
if ((mask & tag) == (mask & *filtertag) || if ((mask & tag) == (mask & *filtertag) ||
(mask & tag) == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | lfs_tag_isdelete(*filtertag) ||
(LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == (
LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) |
(LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) {
return true; return true;
} }
@@ -1238,22 +1233,32 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// align to program units // 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); lfs->cfg->prog_size);
// 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));
}
// read erased state from next program unit // read erased state from next program unit
lfs_tag_t tag; lfs_tag_t tag = 0xffffffff;
int err = lfs_bd_read(lfs, int err = lfs_bd_read(lfs,
NULL, &lfs->rcache, sizeof(tag), NULL, &lfs->rcache, sizeof(tag),
commit->block, off, &tag, sizeof(tag)); commit->block, noff, &tag, sizeof(tag));
if (err && err != LFS_ERR_CORRUPT) { if (err && err != LFS_ERR_CORRUPT) {
return err; return err;
} }
// build crc tag // build crc tag
bool reset = ~lfs_frombe32(tag) >> 31; bool reset = ~lfs_frombe32(tag) >> 31;
tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, noff - off);
off - (commit->off+sizeof(lfs_tag_t)));
// write out crc // write out crc
uint32_t footer[2]; uint32_t footer[2];
@@ -1266,24 +1271,29 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
if (err) { if (err) {
return err; return err;
} }
commit->off += sizeof(tag)+lfs_tag_size(tag); commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->ptag = tag ^ (reset << 31); commit->ptag = tag ^ (reset << 31);
commit->crc = 0xffffffff; // reset crc for next "commit"
}
// flush buffers // flush buffers
err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false);
if (err) { if (err) {
return err; return err;
} }
// successful commit, check checksum to make sure // 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; uint32_t crc = 0xffffffff;
lfs_size_t size = commit->off - lfs_tag_size(tag) - commit->begin; for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) {
for (lfs_off_t i = 0; i < size; i++) {
// leave it up to caching to make this efficient // leave it up to caching to make this efficient
uint8_t dat; uint8_t dat;
err = lfs_bd_read(lfs, err = lfs_bd_read(lfs,
NULL, &lfs->rcache, size-i, NULL, &lfs->rcache, noff+sizeof(uint32_t)-i,
commit->block, commit->begin+i, &dat, 1); commit->block, i, &dat, 1);
if (err) { if (err) {
return err; return err;
} }
@@ -1291,12 +1301,17 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
crc = lfs_crc(crc, &dat, 1); crc = lfs_crc(crc, &dat, 1);
} }
if (err) { // detected write error?
return err; if (crc != 0) {
return LFS_ERR_CORRUPT;
} }
if (crc != commit->crc) { // skip padding
return LFS_ERR_CORRUPT; 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; return 0;
@@ -1459,7 +1474,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// increment revision count // increment revision count
dir->rev += 1; dir->rev += 1;
if (lfs->cfg->block_cycles && if (lfs->cfg->block_cycles > 0 &&
(dir->rev % (lfs->cfg->block_cycles+1) == 0)) { (dir->rev % (lfs->cfg->block_cycles+1) == 0)) {
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 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, // 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 // successful compaction, swap dir pair to indicate most recent
LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
lfs_pair_swap(dir->pair); lfs_pair_swap(dir->pair);
dir->count = end - begin; dir->count = end - begin;
dir->off = commit.off; dir->off = commit.off;
dir->etag = commit.ptag; dir->etag = commit.ptag;
dir->erased = (dir->off % lfs->cfg->prog_size == 0);
// note we able to have already handled move here // note we able to have already handled move here
if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
lfs_gstate_xormove(&lfs->gpending, 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 && if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 &&
f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) && f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) &&
f->ctz.size > lfs->cfg->cache_size) { f->ctz.size > lfs->cfg->cache_size) {
f->flags &= ~LFS_F_READING; int err = lfs_file_outline(lfs, f);
f->off = 0;
lfs_alloc_ack(lfs);
int err = lfs_file_relocate(lfs, f);
if (err) { if (err) {
return err; return err;
} }
@@ -1764,6 +1775,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
} }
// successful commit, update dir // successful commit, update dir
LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
dir->off = commit.off; dir->off = commit.off;
dir->etag = commit.ptag; 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"})", ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})",
(void*)lfs, (void*)file, path, flags, (void*)lfs, (void*)file, path, flags,
(void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); (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 // deorphan if we haven't yet, needed at most once after poweron
if ((flags & 3) != LFS_O_RDONLY) { if ((flags & 3) != LFS_O_RDONLY) {
int err = lfs_fs_forceconsistency(lfs); 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 // setup simple file details
int err; int err;
file->cfg = cfg; file->cfg = cfg;
file->flags = flags; file->flags = flags | LFS_F_OPENED;
file->pos = 0; file->pos = 0;
file->cache.buffer = NULL; 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) { int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)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); int err = lfs_file_sync(lfs, file);
// remove from list of mdirs // 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); lfs_free(file->cache.buffer);
} }
file->flags &= ~LFS_F_OPENED;
LFS_TRACE("lfs_file_close -> %d", err); LFS_TRACE("lfs_file_close -> %d", err);
return err; return err;
} }
static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
LFS_ASSERT(file->flags & LFS_F_OPENED);
while (true) { while (true) {
// just relocate what exists into new block // just relocate what exists into new block
lfs_block_t nblock; 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); lfs_cache_zero(lfs, &lfs->pcache);
file->block = nblock; file->block = nblock;
file->flags &= ~LFS_F_INLINE;
file->flags |= LFS_F_WRITING; file->flags |= LFS_F_WRITING;
return 0; 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) { 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_READING) {
if (!(file->flags & LFS_F_INLINE)) { if (!(file->flags & LFS_F_INLINE)) {
lfs_cache_drop(lfs, &file->cache); 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 = { lfs_file_t orig = {
.ctz.head = file->ctz.head, .ctz.head = file->ctz.head,
.ctz.size = file->ctz.size, .ctz.size = file->ctz.size,
.flags = LFS_O_RDONLY, .flags = LFS_O_RDONLY | LFS_F_OPENED,
.pos = file->pos, .pos = file->pos,
.cache = lfs->rcache, .cache = lfs->rcache,
}; };
@@ -2621,6 +2655,8 @@ relocate:
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(file->flags & LFS_F_OPENED);
while (true) { while (true) {
int err = lfs_file_flush(lfs, file); int err = lfs_file_flush(lfs, file);
if (err) { if (err) {
@@ -2674,8 +2710,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
relocate: relocate:
// inline file doesn't fit anymore // inline file doesn't fit anymore
file->off = file->pos; err = lfs_file_outline(lfs, file);
err = lfs_file_relocate(lfs, file);
if (err) { if (err) {
file->flags |= LFS_F_ERRED; file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_sync -> %d", err); 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) { void *buffer, lfs_size_t size) {
LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")", LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")",
(void*)lfs, (void*)file, buffer, size); (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; uint8_t *data = buffer;
lfs_size_t nsize = size; 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) { if (file->flags & LFS_F_WRITING) {
// flush out any writes // flush out any writes
int err = lfs_file_flush(lfs, file); 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) { const void *buffer, lfs_size_t size) {
LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")", LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")",
(void*)lfs, (void*)file, buffer, size); (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; const uint8_t *data = buffer;
lfs_size_t nsize = size; 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) { if (file->flags & LFS_F_READING) {
// drop any reads // drop any reads
int err = lfs_file_flush(lfs, file); 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_min(0x3fe, lfs_min(
lfs->cfg->cache_size, lfs->cfg->block_size/8))) { lfs->cfg->cache_size, lfs->cfg->block_size/8))) {
// inline file doesn't fit anymore // inline file doesn't fit anymore
file->off = file->pos; int err = lfs_file_outline(lfs, file);
lfs_alloc_ack(lfs);
int err = lfs_file_relocate(lfs, file);
if (err) { if (err) {
file->flags |= LFS_F_ERRED; file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_write -> %"PRId32, err); 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_soff_t off, int whence) {
LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)", LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)",
(void*)lfs, (void*)file, off, whence); (void*)lfs, (void*)file, off, whence);
LFS_ASSERT(file->flags & LFS_F_OPENED);
// write out everything beforehand, may be noop if rdonly // write out everything beforehand, may be noop if rdonly
int err = lfs_file_flush(lfs, file); int err = lfs_file_flush(lfs, file);
if (err) { 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) { int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")", LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")",
(void*)lfs, (void*)file, size); (void*)lfs, (void*)file, size);
if ((file->flags & 3) == LFS_O_RDONLY) { LFS_ASSERT(file->flags & LFS_F_OPENED);
LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_BADF); LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY);
return LFS_ERR_BADF;
}
if (size > LFS_FILE_MAX) { if (size > LFS_FILE_MAX) {
LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_INVAL); 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_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) {
LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file); LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(file->flags & LFS_F_OPENED);
(void)lfs; (void)lfs;
LFS_TRACE("lfs_file_tell -> %"PRId32, file->pos); LFS_TRACE("lfs_file_tell -> %"PRId32, file->pos);
return 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_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file); LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file);
LFS_ASSERT(file->flags & LFS_F_OPENED);
(void)lfs; (void)lfs;
if (file->flags & LFS_F_WRITING) { if (file->flags & LFS_F_WRITING) {
LFS_TRACE("lfs_file_size -> %"PRId32, 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_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
<= lfs->cfg->block_size); <= lfs->cfg->block_size);
// we don't support some corner cases // block_cycles = 0 is no longer supported.
LFS_ASSERT(lfs->cfg->block_cycles < 0xffffffff); //
// 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 // setup read cache
if (lfs->cfg->read_buffer) { if (lfs->cfg->read_buffer) {

19
lfs.h
View File

@@ -136,6 +136,7 @@ enum lfs_open_flags {
LFS_F_READING = 0x040000, // File has been read since last flush LFS_F_READING = 0x040000, // File has been read since last flush
LFS_F_ERRED = 0x080000, // An error occured during write LFS_F_ERRED = 0x080000, // An error occured during write
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
LFS_F_OPENED = 0x200000, // File has been opened
}; };
// File seek flags // File seek flags
@@ -190,9 +191,13 @@ struct lfs_config {
// Number of erasable blocks on the device. // Number of erasable blocks on the device.
lfs_size_t block_count; lfs_size_t block_count;
// Number of erase cycles before we should move data to another block. // Number of erase cycles before littlefs evicts metadata logs and moves
// May be zero, in which case no block-level wear-leveling is performed. // the metadata to another block. Suggested values are in the
uint32_t block_cycles; // 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. // 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 // 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 // Size of the lookahead buffer in bytes. A larger lookahead buffer
// increases the number of blocks found during an allocation pass. The // increases the number of blocks found during an allocation pass. The
// lookahead buffer is stored as a compact bitmap, so each byte of RAM // 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; lfs_size_t lookahead_size;
// Optional statically allocated read buffer. Must be cache_size. // Optional statically allocated read buffer. Must be cache_size.
@@ -216,7 +221,7 @@ struct lfs_config {
void *prog_buffer; void *prog_buffer;
// Optional statically allocated lookahead buffer. Must be lookahead_size // 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. // allocate this buffer.
void *lookahead_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 // Change the position of the file
// //
// The change in position is determined by the offset and whence flag. // 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 lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
lfs_soff_t off, int whence); 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 // 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. // Returns a negative error code on failure.
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);

View File

@@ -63,7 +63,7 @@ char path[1024];
#endif #endif
#ifndef LFS_CACHE_SIZE #ifndef LFS_CACHE_SIZE
#define LFS_CACHE_SIZE 64 #define LFS_CACHE_SIZE (64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)
#endif #endif
#ifndef LFS_LOOKAHEAD_SIZE #ifndef LFS_LOOKAHEAD_SIZE

View File

@@ -140,20 +140,79 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Many file test ---" echo "--- Many files test ---"
scripts/test.py << TEST scripts/test.py << TEST
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
scripts/test.py << TEST scripts/test.py << TEST
// Create 300 files of 6 bytes // Create 300 files of 7 bytes
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "directory") => 0;
for (unsigned i = 0; i < 300; i++) { for (unsigned i = 0; i < 300; i++) {
sprintf(path, "file_%03d", i); sprintf(path, "file_%03d", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file, path,
lfs_size_t size = 6; LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
memcpy(buffer, "Hello", size); lfs_size_t size = 7;
lfs_file_write(&lfs, &file, buffer, size) => size; 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_file_close(&lfs, &file) => 0;
} }
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;