Compare commits

..

1 Commits

Author SHA1 Message Date
Christopher Haster
f9324d1443 Exploring inlined mutable configuration
Currently littlefs uses a separate mutable state struct and immutable
config struct. This lets users place the config struct in ROM where
possible.

However the recent addition of LFS_STATICCFG raises the question of if
this split is still valuable.

If the config is copied into the mutable struct at runtime, this allows
a couple things:
1. Easier user interface, config can be stack allocated, no need to store
   the config struct for the lifetime of littlefs in OSs.
2. Avoids duplication when littlefs would need to change config based on
   observed superblocks, such as LFS_NAME_MAX limits
3. In theory, access to a single struct is faster/smaller as it avoids
   an additional load instruction.

Unfortunately, inlining the dynamic config runs into several issues:

1. The code size actually increases with this change! From digging into
   this it's for a couple reasons:

   - Copying the config over takes code.

   - C has notorious problems with pointer aliasing, accessing
     constants from a const struct actually allows C to assume the
     values aren't going to change in more situations.

     This suggests it may be possible to reduce the code size more by
     loading the config pointer into a variable, but I haven't explored
     this probably not-worth-it optimization.

   - Even assuming deduplication of superblock-dependent configuration,
     the config struct is significantly larger than the mutable struct,
     and it turns out combining these two exceeds the limits of
     immediate-relative-loads, discarding the possible code size
     improvement from avoiding a second dereference.

2. The implementation of dynamic configuration differs significantly from
   the static configuration. This adds mess into the compile-time #ifdefs
   needed to support both options.
2020-11-28 20:13:32 -06:00
16 changed files with 544 additions and 637 deletions

View File

@@ -50,7 +50,7 @@ _: &test-no-intrinsics
_: &test-no-inline _: &test-no-inline
- make test TFLAGS+="-nrk -DLFS_INLINE_MAX=0" - make test TFLAGS+="-nrk -DLFS_INLINE_MAX=0"
_: &test-byte-writes _: &test-byte-writes
- make test TFLAGS+="-nrk -DLFS_READ_SIZE=1 -DLFS_BUFFER_SIZE=1" - make test TFLAGS+="-nrk -DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=1"
_: &test-block-cycles _: &test-block-cycles
- make test TFLAGS+="-nrk -DLFS_BLOCK_CYCLES=1" - make test TFLAGS+="-nrk -DLFS_BLOCK_CYCLES=1"
_: &test-odd-block-count _: &test-odd-block-count
@@ -214,20 +214,14 @@ jobs:
- NAME=littlefs-minimal - NAME=littlefs-minimal
- CC="arm-linux-gnueabi-gcc --static -mthumb" - CC="arm-linux-gnueabi-gcc --static -mthumb"
- CFLAGS="-Werror - CFLAGS="-Werror
-DLFS_BD_READ -DLFS_STATICCFG -DLFS_FILE_STATICCFG
-DLFS_BD_PROG
-DLFS_BD_ERASE
-DLFS_BD_SYNC
-DLFS_READ_SIZE=16 -DLFS_READ_SIZE=16
-DLFS_PROG_SIZE=16 -DLFS_PROG_SIZE=16
-DLFS_BLOCK_SIZE=512 -DLFS_BLOCK_SIZE=512
-DLFS_BLOCK_COUNT=1024 -DLFS_BLOCK_COUNT=1024
-DLFS_BLOCK_CYCLES=1024 -DLFS_BLOCK_CYCLES=-1
-DLFS_BUFFER_SIZE=64 -DLFS_CACHE_SIZE=64
-DLFS_LOOKAHEAD_SIZE=16 -DLFS_LOOKAHEAD_SIZE=16
-DLFS_NAME_LIMIT=0
-DLFS_FILE_LIMIT=0
-DLFS_ATTR_LIMIT=0
-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR" -DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
if: branch !~ -prefix$ if: branch !~ -prefix$
install: install:

View File

@@ -32,7 +32,7 @@ override TFLAGS += -v
endif endif
all build: $(TARGET) all: $(TARGET)
asm: $(ASM) asm: $(ASM)

View File

@@ -19,7 +19,9 @@ int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path,
(void*)bd, path, (void*)cfg, (void*)bd, path, (void*)cfg,
cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count, cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
cfg->erase_value); cfg->erase_value);
bd->cfg = cfg;
// copy over config
bd->cfg = *cfg;
// open file // open file
bd->fd = open(path, O_RDWR | O_CREAT, 0666); bd->fd = open(path, O_RDWR | O_CREAT, 0666);
@@ -52,18 +54,18 @@ int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block,
(void*)bd, block, off, buffer, size); (void*)bd, block, off, buffer, size);
// check if read is valid // check if read is valid
LFS_ASSERT(off % bd->cfg->read_size == 0); LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % bd->cfg->read_size == 0); LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// zero for reproducability (in case file is truncated) // zero for reproducability (in case file is truncated)
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
memset(buffer, bd->cfg->erase_value, size); memset(buffer, bd->cfg.erase_value, size);
} }
// read // read
off_t res1 = lseek(bd->fd, off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET); (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
@@ -88,14 +90,14 @@ int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block,
(void*)bd, block, off, buffer, size); (void*)bd, block, off, buffer, size);
// check if write is valid // check if write is valid
LFS_ASSERT(off % bd->cfg->prog_size == 0); LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % bd->cfg->prog_size == 0); LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// check that data was erased? only needed for testing // check that data was erased? only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
off_t res1 = lseek(bd->fd, off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET); (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
@@ -111,13 +113,13 @@ int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block,
return err; return err;
} }
LFS_ASSERT(c == bd->cfg->erase_value); LFS_ASSERT(c == bd->cfg.erase_value);
} }
} }
// program data // program data
off_t res1 = lseek(bd->fd, off_t res1 = lseek(bd->fd,
(off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET); (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
@@ -139,19 +141,19 @@ int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block) {
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)bd, block); LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
// check if erase is valid // check if erase is valid
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// erase, only needed for testing // erase, only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
off_t res1 = lseek(bd->fd, (off_t)block*bd->cfg->erase_size, SEEK_SET); off_t res1 = lseek(bd->fd, (off_t)block*bd->cfg.erase_size, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
return err; return err;
} }
for (lfs_off_t i = 0; i < bd->cfg->erase_size; i++) { for (lfs_off_t i = 0; i < bd->cfg.erase_size; i++) {
ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg.erase_value}, 1);
if (res2 < 0) { if (res2 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);

View File

@@ -46,7 +46,7 @@ struct lfs_filebd_cfg {
// filebd state // filebd state
typedef struct lfs_filebd { typedef struct lfs_filebd {
int fd; int fd;
const struct lfs_filebd_cfg *cfg; struct lfs_filebd_cfg cfg;
} lfs_filebd_t; } lfs_filebd_t;

View File

@@ -15,13 +15,15 @@ int lfs_rambd_createcfg(lfs_rambd_t *bd,
(void*)bd, (void*)cfg, (void*)bd, (void*)cfg,
cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count, cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
cfg->erase_value, cfg->buffer); cfg->erase_value, cfg->buffer);
bd->cfg = cfg;
// copy over config
bd->cfg = *cfg;
// allocate buffer? // allocate buffer?
if (bd->cfg->buffer) { if (bd->cfg.buffer) {
bd->buffer = bd->cfg->buffer; bd->buffer = bd->cfg.buffer;
} else { } else {
bd->buffer = lfs_malloc(bd->cfg->erase_size * bd->cfg->erase_count); bd->buffer = lfs_malloc(bd->cfg.erase_size * bd->cfg.erase_count);
if (!bd->buffer) { if (!bd->buffer) {
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
@@ -29,9 +31,9 @@ int lfs_rambd_createcfg(lfs_rambd_t *bd,
} }
// zero for reproducability? // zero for reproducability?
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
memset(bd->buffer, bd->cfg->erase_value, memset(bd->buffer, bd->cfg.erase_value,
bd->cfg->erase_size * bd->cfg->erase_count); bd->cfg.erase_size * bd->cfg.erase_count);
} }
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0);
@@ -41,7 +43,7 @@ int lfs_rambd_createcfg(lfs_rambd_t *bd,
int lfs_rambd_destroy(lfs_rambd_t *bd) { int lfs_rambd_destroy(lfs_rambd_t *bd) {
LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)bd); LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)bd);
// clean up memory // clean up memory
if (!bd->cfg->buffer) { if (!bd->cfg.buffer) {
lfs_free(bd->buffer); lfs_free(bd->buffer);
} }
LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0);
@@ -55,12 +57,12 @@ int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block,
(void*)bd, block, off, buffer, size); (void*)bd, block, off, buffer, size);
// check if read is valid // check if read is valid
LFS_ASSERT(off % bd->cfg->read_size == 0); LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % bd->cfg->read_size == 0); LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// read data // read data
memcpy(buffer, &bd->buffer[block*bd->cfg->erase_size + off], size); memcpy(buffer, &bd->buffer[block*bd->cfg.erase_size + off], size);
LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0);
return 0; return 0;
@@ -73,20 +75,20 @@ int lfs_rambd_prog(lfs_rambd_t *bd, lfs_block_t block,
(void*)bd, block, off, buffer, size); (void*)bd, block, off, buffer, size);
// check if write is valid // check if write is valid
LFS_ASSERT(off % bd->cfg->prog_size == 0); LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % bd->cfg->prog_size == 0); LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// check that data was erased? only needed for testing // check that data was erased? only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
for (lfs_off_t i = 0; i < size; i++) { for (lfs_off_t i = 0; i < size; i++) {
LFS_ASSERT(bd->buffer[block*bd->cfg->erase_size + off + i] == LFS_ASSERT(bd->buffer[block*bd->cfg.erase_size + off + i] ==
bd->cfg->erase_value); bd->cfg.erase_value);
} }
} }
// program data // program data
memcpy(&bd->buffer[block*bd->cfg->erase_size + off], buffer, size); memcpy(&bd->buffer[block*bd->cfg.erase_size + off], buffer, size);
LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0);
return 0; return 0;
@@ -96,12 +98,12 @@ int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block) {
LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)bd, block); LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
// check if erase is valid // check if erase is valid
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// erase, only needed for testing // erase, only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
memset(&bd->buffer[block*bd->cfg->erase_size], memset(&bd->buffer[block*bd->cfg.erase_size],
bd->cfg->erase_value, bd->cfg->erase_size); bd->cfg.erase_value, bd->cfg.erase_size);
} }
LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0);

View File

@@ -37,9 +37,8 @@ struct lfs_rambd_cfg {
// Number of erasable blocks on the device. // Number of erasable blocks on the device.
lfs_size_t erase_count; lfs_size_t erase_count;
// 8-bit erase value to use for simulating erases. -1 does not simulate // 8-bit erase value to simulate erasing with. -1 indicates no erase
// erases, which can speed up testing by avoiding all the extra block-device // occurs, which is still a valid block device
// operations to store the erase value.
int32_t erase_value; int32_t erase_value;
// Optional statically allocated buffer for the block device. // Optional statically allocated buffer for the block device.
@@ -49,7 +48,7 @@ struct lfs_rambd_cfg {
// rambd state // rambd state
typedef struct lfs_rambd { typedef struct lfs_rambd {
uint8_t *buffer; uint8_t *buffer;
const struct lfs_rambd_cfg *cfg; struct lfs_rambd_cfg cfg;
} lfs_rambd_t; } lfs_rambd_t;

View File

@@ -23,15 +23,17 @@ int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
cfg->erase_value, cfg->erase_cycles, cfg->erase_value, cfg->erase_cycles,
cfg->badblock_behavior, cfg->power_cycles, cfg->badblock_behavior, cfg->power_cycles,
cfg->buffer, cfg->wear_buffer); cfg->buffer, cfg->wear_buffer);
bd->cfg = cfg;
// copy over config
bd->cfg = *cfg;
// setup testing things // setup testing things
bd->persist = path; bd->persist = path;
bd->power_cycles = bd->cfg->power_cycles; bd->power_cycles = bd->cfg.power_cycles;
if (bd->cfg->erase_cycles) { if (bd->cfg.erase_cycles) {
if (bd->cfg->wear_buffer) { if (bd->cfg.wear_buffer) {
bd->wear = bd->cfg->wear_buffer; bd->wear = bd->cfg.wear_buffer;
} else { } else {
bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->erase_count); bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->erase_count);
if (!bd->wear) { if (!bd->wear) {
@@ -40,18 +42,29 @@ int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
} }
} }
memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * bd->cfg->erase_count); memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * bd->cfg.erase_count);
} }
// create underlying block device // create underlying block device
if (bd->persist) { if (bd->persist) {
int err = lfs_filebd_createcfg(&bd->impl.filebd, path, int err = lfs_filebd_createcfg(&bd->impl.filebd, path,
bd->cfg->filebd_cfg); &(struct lfs_filebd_cfg){
.read_size=bd->cfg.read_size,
.prog_size=bd->cfg.prog_size,
.erase_size=bd->cfg.erase_size,
.erase_count=bd->cfg.erase_count,
.erase_value=bd->cfg.erase_value});
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
return err; return err;
} else { } else {
int err = lfs_rambd_createcfg(&bd->impl.rambd, int err = lfs_rambd_createcfg(&bd->impl.rambd,
bd->cfg->rambd_cfg); &(struct lfs_rambd_cfg){
.read_size=bd->cfg.read_size,
.prog_size=bd->cfg.prog_size,
.erase_size=bd->cfg.erase_size,
.erase_count=bd->cfg.erase_count,
.erase_value=bd->cfg.erase_value,
.buffer=bd->cfg.buffer});
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
return err; return err;
} }
@@ -59,7 +72,7 @@ int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
int lfs_testbd_destroy(lfs_testbd_t *bd) { int lfs_testbd_destroy(lfs_testbd_t *bd) {
LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)bd); LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)bd);
if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) { if (bd->cfg.erase_cycles && !bd->cfg.wear_buffer) {
lfs_free(bd->wear); lfs_free(bd->wear);
} }
@@ -118,13 +131,13 @@ int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block,
(void*)bd, block, off, buffer, size); (void*)bd, block, off, buffer, size);
// check if read is valid // check if read is valid
LFS_ASSERT(off % bd->cfg->read_size == 0); LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % bd->cfg->read_size == 0); LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// block bad? // block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles &&
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { bd->cfg.badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
@@ -142,19 +155,19 @@ int lfs_testbd_prog(lfs_testbd_t *bd, lfs_block_t block,
(void*)bd, block, off, buffer, size); (void*)bd, block, off, buffer, size);
// check if write is valid // check if write is valid
LFS_ASSERT(off % bd->cfg->prog_size == 0); LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % bd->cfg->prog_size == 0); LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// block bad? // block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles) {
if (bd->cfg->badblock_behavior == if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGERROR) { LFS_TESTBD_BADBLOCK_PROGERROR) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior == } else if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGNOOP || LFS_TESTBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior == bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) { LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
return 0; return 0;
@@ -187,16 +200,16 @@ int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)bd, block); LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
// check if erase is valid // check if erase is valid
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
// block bad? // block bad?
if (bd->cfg->erase_cycles) { if (bd->cfg.erase_cycles) {
if (bd->wear[block] >= bd->cfg->erase_cycles) { if (bd->wear[block] >= bd->cfg.erase_cycles) {
if (bd->cfg->badblock_behavior == if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASEERROR) { LFS_TESTBD_BADBLOCK_ERASEERROR) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior == } else if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) { LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0);
return 0; return 0;
@@ -243,8 +256,8 @@ lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd,
LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)bd, block); LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)bd, block);
// check if block is valid // check if block is valid
LFS_ASSERT(bd->cfg->erase_cycles); LFS_ASSERT(bd->cfg.erase_cycles);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]);
return bd->wear[block]; return bd->wear[block];
@@ -255,8 +268,8 @@ int lfs_testbd_setwear(lfs_testbd_t *bd,
LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)bd, block); LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)bd, block);
// check if block is valid // check if block is valid
LFS_ASSERT(bd->cfg->erase_cycles); LFS_ASSERT(bd->cfg.erase_cycles);
LFS_ASSERT(block < bd->cfg->erase_count); LFS_ASSERT(block < bd->cfg.erase_count);
bd->wear[block] = wear; bd->wear[block] = wear;

View File

@@ -44,11 +44,6 @@ typedef int32_t lfs_testbd_swear_t;
// testbd config, this is required for testing // testbd config, this is required for testing
struct lfs_testbd_cfg { struct lfs_testbd_cfg {
// Block device specific configuration, see the related config structs.
// May be NULL if the underlying implementation goes unused.
const struct lfs_rambd_cfg *rambd_cfg;
const struct lfs_filebd_cfg *filebd_cfg;
// Minimum size of block read. All read operations must be a // Minimum size of block read. All read operations must be a
// multiple of this value. // multiple of this value.
lfs_size_t read_size; lfs_size_t read_size;
@@ -79,6 +74,9 @@ struct lfs_testbd_cfg {
// the program with exit. Simulates power-loss. 0 disables. // the program with exit. Simulates power-loss. 0 disables.
uint32_t power_cycles; uint32_t power_cycles;
// Optional buffer for RAM block device.
void *buffer;
// Optional buffer for wear // Optional buffer for wear
void *wear_buffer; void *wear_buffer;
}; };
@@ -94,7 +92,7 @@ typedef struct lfs_testbd {
uint32_t power_cycles; uint32_t power_cycles;
lfs_testbd_wear_t *wear; lfs_testbd_wear_t *wear;
const struct lfs_testbd_cfg *cfg; struct lfs_testbd_cfg cfg;
} lfs_testbd_t; } lfs_testbd_t;

582
lfs.c

File diff suppressed because it is too large Load Diff

271
lfs.h
View File

@@ -46,13 +46,8 @@ typedef uint32_t lfs_block_t;
// info struct. Limited to <= 1022. Stored in superblock and must be // info struct. Limited to <= 1022. Stored in superblock and must be
// respected by other littlefs drivers. // respected by other littlefs drivers.
#ifndef LFS_NAME_MAX #ifndef LFS_NAME_MAX
#if defined(LFS_NAME_LIMIT) && \
LFS_NAME_LIMIT > 0 && LFS_NAME_MAX <= 1022
#define LFS_NAME_MAX LFS_NAME_LIMIT
#else
#define LFS_NAME_MAX 255 #define LFS_NAME_MAX 255
#endif #endif
#endif
// Maximum size of a file in bytes, may be redefined to limit to support other // Maximum size of a file in bytes, may be redefined to limit to support other
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the // drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
@@ -60,24 +55,14 @@ typedef uint32_t lfs_block_t;
// incorrect values due to using signed integers. Stored in superblock and // incorrect values due to using signed integers. Stored in superblock and
// must be respected by other littlefs drivers. // must be respected by other littlefs drivers.
#ifndef LFS_FILE_MAX #ifndef LFS_FILE_MAX
#if defined(LFS_FILE_LIMIT) && \
LFS_FILE_LIMIT > 0 && LFS_FILE_LIMIT <= 4294967296
#define LFS_FILE_MAX LFS_FILE_LIMIT
#else
#define LFS_FILE_MAX 2147483647 #define LFS_FILE_MAX 2147483647
#endif #endif
#endif
// Maximum size of custom attributes in bytes, may be redefined, but there is // Maximum size of custom attributes in bytes, may be redefined, but there is
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. // no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
#ifndef LFS_ATTR_MAX #ifndef LFS_ATTR_MAX
#if defined(LFS_ATTR_LIMIT) && \
LFS_ATTR_LIMIT > 0 && LFS_ATTR_LIMIT <= 1022
#define LFS_ATTR_MAX LFS_FILE_LIMIT
#else
#define LFS_ATTR_MAX 1022 #define LFS_ATTR_MAX 1022
#endif #endif
#endif
// File types // File types
enum lfs_type { enum lfs_type {
@@ -140,129 +125,50 @@ enum lfs_whence_flags {
}; };
#if !defined(LFS_STATICCFG)
// Configuration provided during initialization of the littlefs // Configuration provided during initialization of the littlefs
// If every config option is provided at compile time, littlefs switches
// to "LFS_STATICCFG" mode. The dynamic lfs_cfg struct is not included in
// the lfs_t struct, and *cfg functions are no longer available.
#if defined(LFS_BD_READ) && \
defined(LFS_BD_PROG) && \
defined(LFS_BD_ERASE) && \
defined(LFS_BD_SYNC) && \
defined(LFS_READ_SIZE) && \
defined(LFS_PROG_SIZE) && \
defined(LFS_BLOCK_SIZE) && \
defined(LFS_BLOCK_COUNT) && \
defined(LFS_BLOCK_CYCLES) && \
defined(LFS_BUFFER_SIZE) && \
defined(LFS_LOOKAHEAD_SIZE) && \
defined(LFS_READ_BUFFER) && \
defined(LFS_PROG_BUFFER) && \
defined(LFS_LOOKAHEAD_BUFFER) && \
defined(LFS_NAME_LIMIT) && \
defined(LFS_FILE_LIMIT) && \
defined(LFS_ATTR_LIMIT)
#define LFS_STATICCFG
#endif
// Dynamic config struct
#ifndef LFS_STATICCFG
struct lfs_cfg { struct lfs_cfg {
#endif
// Opaque user provided context that can be used to pass // Opaque user provided context that can be used to pass
// information to the block device operations // information to the block device operations
#if !(defined(LFS_BD_READ) && \ void *ctx;
defined(LFS_BD_PROG) && \
defined(LFS_BD_ERASE) && \
defined(LFS_BD_SYNC))
void *bd_ctx;
#endif
// Read a region in a block. Negative error codes are propogated // Read a region in a block. Negative error codes are propogated
// to the user. // to the user.
#ifndef LFS_BD_READ int (*read)(void *ctx, lfs_block_t block,
int (*bd_read)(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size); lfs_off_t off, void *buffer, lfs_size_t size);
#define LFS_CFG_BD_READ(cfg, block, off, buffer, size) \
(cfg)->bd_read((cfg)->bd_ctx, block, off, buffer, size)
#else
#define LFS_CFG_BD_READ(cfg, block, off, buffer, size) \
lfs_bd_read(block, off, buffer, size)
#endif
// Program a region in a block. The block must have previously // Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user. // been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad. // May return LFS_ERR_CORRUPT if the block should be considered bad.
#ifndef LFS_BD_PROG int (*prog)(void *ctx, lfs_block_t block,
int (*bd_prog)(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size); lfs_off_t off, const void *buffer, lfs_size_t size);
#define LFS_CFG_BD_PROG(cfg, block, off, buffer, size) \
(cfg)->bd_prog((cfg)->bd_ctx, block, off, buffer, size)
#else
#define LFS_CFG_BD_PROG(cfg, block, off, buffer, size) \
lfs_bd_prog(block, off, buffer, size)
#endif
// Erase a block. A block must be erased before being programmed. // Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes // The state of an erased block is undefined. Negative error codes
// are propogated to the user. // are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad. // May return LFS_ERR_CORRUPT if the block should be considered bad.
#ifndef LFS_BD_ERASE int (*erase)(void *ctx, lfs_block_t block);
int (*bd_erase)(void *ctx, lfs_block_t block);
#define LFS_CFG_BD_ERASE(cfg, block) \
(cfg)->bd_erase((cfg)->bd_ctx, block)
#else
#define LFS_CFG_BD_ERASE(cfg, block) \
lfs_bd_erase(block)
#endif
// Sync the state of the underlying block device. Negative error codes // Sync the state of the underlying block device. Negative error codes
// are propogated to the user. // are propogated to the user.
#ifndef LFS_BD_SYNC int (*sync)(void *ctx);
int (*bd_sync)(void *ctx);
#define LFS_CFG_BD_SYNC(cfg) \
(cfg)->bd_sync((cfg)->bd_ctx)
#else
#define LFS_CFG_BD_SYNC(cfg) \
lfs_bd_sync()
#endif
// Minimum size of a block read. All read operations will be a // Minimum size of a block read. All read operations will be a
// multiple of this value. // multiple of this value.
#ifndef LFS_READ_SIZE
lfs_size_t read_size; lfs_size_t read_size;
#define LFS_CFG_READ_SIZE(cfg) (cfg)->read_size
#else
#define LFS_CFG_READ_SIZE(cfg) LFS_READ_SIZE
#endif
// Minimum size of a block program. All program operations will be a // Minimum size of a block program. All program operations will be a
// multiple of this value. // multiple of this value.
#ifndef LFS_PROG_SIZE
lfs_size_t prog_size; lfs_size_t prog_size;
#define LFS_CFG_PROG_SIZE(cfg) (cfg)->prog_size
#else
#define LFS_CFG_PROG_SIZE(cfg) LFS_PROG_SIZE
#endif
// Size of an erasable block. This does not impact ram consumption and // Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, non-inlined files // may be larger than the physical erase size. However, non-inlined files
// take up at minimum one block. Must be a multiple of the read // take up at minimum one block. Must be a multiple of the read
// and program sizes. // and program sizes.
#ifndef LFS_BLOCK_SIZE
lfs_size_t block_size; lfs_size_t block_size;
#define LFS_CFG_BLOCK_SIZE(cfg) (cfg)->block_size
#else
#define LFS_CFG_BLOCK_SIZE(cfg) LFS_BLOCK_SIZE
#endif
// Number of erasable blocks on the device. // Number of erasable blocks on the device.
#ifndef LFS_BLOCK_COUNT
lfs_size_t block_count; lfs_size_t block_count;
#define LFS_CFG_BLOCK_COUNT(cfg) (cfg)->block_count
#else
#define LFS_CFG_BLOCK_COUNT(cfg) LFS_BLOCK_COUNT
#endif
// 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 // the metadata to another block. Suggested values are in the
@@ -270,139 +176,104 @@ struct lfs_cfg {
// of less consistent wear distribution. // of less consistent wear distribution.
// //
// Set to -1 to disable block-level wear-leveling. // Set to -1 to disable block-level wear-leveling.
#ifndef LFS_BLOCK_CYCLES
int32_t block_cycles; int32_t block_cycles;
#define LFS_CFG_BLOCK_CYCLES(cfg) (cfg)->block_cycles
#else
#define LFS_CFG_BLOCK_CYCLES(cfg) LFS_BLOCK_CYCLES
#endif
// Size of internal buffers used to cache slices of blocks in RAM. // Size of block caches. Each cache buffers a portion of a block in RAM.
// The littlefs needs a read buffer, a program buffer, and one additional // The littlefs needs a read cache, a program cache, and one additional
// buffer per file. Larger buffers can improve performance by storing more // cache per file. Larger caches can improve performance by storing more
// data and reducing the number of disk accesses. Must be a multiple of // data and reducing the number of disk accesses. Must be a multiple of
// the read and program sizes, and a factor of the block size. // the read and program sizes, and a factor of the block size.
#ifndef LFS_BUFFER_SIZE lfs_size_t cache_size;
lfs_size_t buffer_size;
#define LFS_CFG_BUFFER_SIZE(cfg) (cfg)->buffer_size
#else
#define LFS_CFG_BUFFER_SIZE(cfg) LFS_BUFFER_SIZE
#endif
// 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 8. // can track 8 blocks. Must be a multiple of 8.
#ifndef LFS_LOOKAHEAD_SIZE
lfs_size_t lookahead_size; lfs_size_t lookahead_size;
#define LFS_CFG_LOOKAHEAD_SIZE(cfg) (cfg)->lookahead_size
#else
#define LFS_CFG_LOOKAHEAD_SIZE(cfg) LFS_LOOKAHEAD_SIZE
#endif
// Optional statically allocated read buffer. Must be cache_size. // Optional statically allocated read buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer. // By default lfs_malloc is used to allocate this buffer.
#ifndef LFS_READ_BUFFER
void *read_buffer; void *read_buffer;
#define LFS_CFG_READ_BUFFER(cfg) (cfg)->read_buffer
#else
#define LFS_CFG_READ_BUFFER(cfg) LFS_READ_BUFFER
#endif
// Optional statically allocated program buffer. Must be cache_size. // Optional statically allocated program buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer. // By default lfs_malloc is used to allocate this buffer.
#ifndef LFS_PROG_BUFFER
void *prog_buffer; void *prog_buffer;
#define LFS_CFG_PROG_BUFFER(cfg) (cfg)->prog_buffer
#else
#define LFS_CFG_PROG_BUFFER(cfg) LFS_PROG_BUFFER
#endif
// Optional statically allocated lookahead buffer. Must be lookahead_size // Optional statically allocated lookahead buffer. Must be lookahead_size
// and aligned to a 32-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.
#ifndef LFS_LOOKAHEAD_BUFFER
void *lookahead_buffer; void *lookahead_buffer;
#define LFS_CFG_LOOKAHEAD_BUFFER(cfg) (cfg)->lookahead_buffer
#else
#define LFS_CFG_LOOKAHEAD_BUFFER(cfg) LFS_LOOKAHEAD_BUFFER
#endif
// Optional upper limit on length of file names in bytes. No downside for // Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by // larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers. // superblock and must be respected by other littlefs drivers.
#ifndef LFS_NAME_LIMIT lfs_size_t name_max;
lfs_size_t name_limit;
#define LFS_CFG_NAME_LIMIT(cfg) (cfg)->name_limit
#else
#define LFS_CFG_NAME_LIMIT(cfg) LFS_NAME_LIMIT
#endif
// Optional upper limit on files in bytes. No downside for larger files // Optional upper limit on files in bytes. No downside for larger files
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
// in superblock and must be respected by other littlefs drivers. // in superblock and must be respected by other littlefs drivers.
#ifndef LFS_FILE_LIMIT lfs_size_t file_max;
lfs_size_t file_limit;
#define LFS_CFG_FILE_LIMIT(cfg) (cfg)->file_limit
#else
#define LFS_CFG_FILE_LIMIT(cfg) LFS_FILE_LIMIT
#endif
// Optional upper limit on custom attributes in bytes. No downside for // Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero. // LFS_ATTR_MAX when zero.
#ifndef LFS_ATTR_LIMIT lfs_size_t attr_max;
lfs_size_t attr_limit;
#define LFS_CFG_ATTR_LIMIT(cfg) (cfg)->attr_limit
#else
#define LFS_CFG_ATTR_LIMIT(cfg) LFS_ATTR_LIMIT
#endif
#ifndef LFS_STATICCFG
}; };
#endif #else
// Static configuration if LFS_STATICCFG is defined, there are defaults
// for some of these, but some are required. For full documentation, see
// the lfs_cfg struct above.
// Configurable callbacks are a bit special, when LFS_BD_* is defined, // Block device operations
// LFS_CFG_* instead expands into a call to an extern lfs_bd_*, which int lfs_read(lfs_block_t block,
// must be defined by the user. This preserves type-safety of the
// callbacks.
#ifdef LFS_BD_READ
extern int lfs_bd_read(lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size); lfs_off_t off, void *buffer, lfs_size_t size);
#endif int lfs_prog(lfs_block_t block,
#ifdef LFS_BD_PROG
extern int lfs_bd_prog(lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size); lfs_off_t off, const void *buffer, lfs_size_t size);
int lfs_erase(lfs_block_t block);
int lfs_sync(void);
// Required configuration
#ifndef LFS_READ_SIZE
#error "LFS_STATICCFG requires LFS_READ_SIZE"
#endif #endif
#ifdef LFS_BD_ERASE #ifndef LFS_PROG_SIZE
extern int lfs_bd_erase(lfs_block_t block); #error "LFS_STATICCFG requires LFS_PROG_SIZE"
#endif #endif
#ifdef LFS_BD_SYNC #ifndef LFS_BLOCK_SIZE
extern int lfs_bd_sync(void); #error "LFS_STATICCFG requires LFS_BLOCK_SIZE"
#endif
#ifndef LFS_BLOCK_COUNT
#error "LFS_STATICCFG requires LFS_BLOCK_COUNT"
#endif
#ifndef LFS_BLOCK_CYCLES
#error "LFS_STATICCFG requires LFS_BLOCK_CYCLES"
#endif
#ifndef LFS_CACHE_SIZE
#error "LFS_STATICCFG requires LFS_CACHE_SIZE"
#endif
#ifndef LFS_LOOKAHEAD_SIZE
#error "LFS_STATICCFG requires LFS_LOOKAHEAD_SIZE"
#endif #endif
// If every config option is provided at compile time, littlefs switches // Optional configuration
// to "LFS_FILE_STATICCFG" mode. The dynamic lfs_file_cfg struct is not #ifndef LFS_READ_BUFFER
// included in the lfs_file_t struct, and *cfg functions are no longer #define LFS_READ_BUFFER NULL
// available. #endif
#if defined(LFS_FILE_BUFFER) && \ #ifndef LFS_PROG_BUFFER
defined(LFS_FILE_ATTRS) && \ #define LFS_PROG_BUFFER NULL
defined(LFS_FILE_ATTR_COUNT) #endif
#define LFS_STATICCFG #ifndef LFS_LOOKAHEAD_BUFFER
#define LFS_LOOKAHEAD_BUFFER NULL
#endif
#endif #endif
#ifndef LFS_FILE_STATICCFG #if !defined(LFS_FILE_STATICCFG)
// Optional configuration provided during lfs_file_opencfg // Optional configuration provided during lfs_file_opencfg
struct lfs_file_cfg { struct lfs_file_cfg {
#endif
// Optional statically allocated file buffer. Must be cache_size. // Optional statically allocated file buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer. // By default lfs_malloc is used to allocate this buffer.
#ifndef LFS_FILE_BUFFER
void *buffer; void *buffer;
#define LFS_FILE_CFG_BUFFER(cfg) (cfg)->buffer
#else
#define LFS_FILE_CFG_BUFFER(cfg) LFS_FILE_BUFFER
#endif
// Optional list of custom attributes related to the file. If the file // Optional list of custom attributes related to the file. If the file
// is opened with read access, these attributes will be read from disk // is opened with read access, these attributes will be read from disk
@@ -415,22 +286,23 @@ struct lfs_file_cfg {
// than the buffer, it will be padded with zeros. If the stored attribute // than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not // is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly. // found, it will be created implicitly.
#ifndef LFS_FILE_ATTRS
struct lfs_attr *attrs; struct lfs_attr *attrs;
#define LFS_FILE_CFG_ATTRS(cfg) (cfg)->attrs
#else
#define LFS_FILE_CFG_ATTRS(cfg) LFS_FILE_ATTRS
#endif
// Number of custom attributes in the list // Number of custom attributes in the list
#ifndef LFS_FILE_ATTR_COUNT
lfs_size_t attr_count; lfs_size_t attr_count;
#define LFS_FILE_CFG_ATTR_COUNT(cfg) (cfg)->attr_count
#else
#define LFS_FILE_CFG_ATTR_COUNT(cfg) LFS_FILE_ATTR_COUNT
#endif
#ifndef LFS_FILE_STATICCFG
}; };
#else
// Static configuration if LFS_FILE_STATICCFG is defined. For full
// documentation, see the lfs_file_cfg struct above.
#ifndef LFS_FILE_BUFFER
#define LFS_FILE_BUFFER NULL
#endif
#ifndef LFS_FILE_ATTRS
#define LFS_FILE_ATTRS ((struct lfs_attr*)NULL)
#endif
#ifndef LFS_FILE_ATTR_COUNT
#define LFS_FILE_ATTR_COUNT 0
#endif
#endif #endif
// File info structure // File info structure
@@ -512,7 +384,7 @@ typedef struct lfs_file {
lfs_cache_t cache; lfs_cache_t cache;
#ifndef LFS_FILE_STATICCFG #ifndef LFS_FILE_STATICCFG
const struct lfs_file_cfg *cfg; struct lfs_file_cfg cfg;
#endif #endif
} lfs_file_t; } lfs_file_t;
@@ -520,9 +392,9 @@ typedef struct lfs_superblock {
uint32_t version; uint32_t version;
lfs_size_t block_size; lfs_size_t block_size;
lfs_size_t block_count; lfs_size_t block_count;
lfs_size_t name_limit; lfs_size_t name_max;
lfs_size_t file_limit; lfs_size_t file_max;
lfs_size_t attr_limit; lfs_size_t attr_max;
} lfs_superblock_t; } lfs_superblock_t;
typedef struct lfs_gstate { typedef struct lfs_gstate {
@@ -557,11 +429,8 @@ typedef struct lfs {
} free; } free;
#ifndef LFS_STATICCFG #ifndef LFS_STATICCFG
const struct lfs_cfg *cfg; struct lfs_cfg cfg;
#endif #endif
lfs_size_t name_limit;
lfs_size_t file_limit;
lfs_size_t attr_limit;
#ifdef LFS_MIGRATE #ifdef LFS_MIGRATE
struct lfs1 *lfs1; struct lfs1 *lfs1;

View File

@@ -78,23 +78,12 @@ DEFINES = {
'LFS_BLOCK_SIZE': 512, 'LFS_BLOCK_SIZE': 512,
'LFS_BLOCK_COUNT': 1024, 'LFS_BLOCK_COUNT': 1024,
'LFS_BLOCK_CYCLES': -1, 'LFS_BLOCK_CYCLES': -1,
'LFS_BUFFER_SIZE': '(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)', 'LFS_CACHE_SIZE': '(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)',
'LFS_LOOKAHEAD_SIZE': 16, 'LFS_LOOKAHEAD_SIZE': 16,
'LFS_ERASE_VALUE': 0xff, 'LFS_ERASE_VALUE': 0xff,
'LFS_ERASE_CYCLES': 0, 'LFS_ERASE_CYCLES': 0,
'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_PROGERROR', 'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_PROGERROR',
} }
CFG = {
'read_size': 'LFS_READ_SIZE',
'prog_size': 'LFS_PROG_SIZE',
'read_size': 'LFS_READ_SIZE',
'prog_size': 'LFS_PROG_SIZE',
'block_size': 'LFS_BLOCK_SIZE',
'block_count': 'LFS_BLOCK_COUNT',
'block_cycles': 'LFS_BLOCK_CYCLES',
'buffer_size': 'LFS_BUFFER_SIZE',
'lookahead_size': 'LFS_LOOKAHEAD_SIZE',
}
PROLOGUE = """ PROLOGUE = """
// prologue // prologue
__attribute__((unused)) lfs_t lfs; __attribute__((unused)) lfs_t lfs;
@@ -108,29 +97,21 @@ PROLOGUE = """
__attribute__((unused)) int err; __attribute__((unused)) int err;
__attribute__((unused)) const struct lfs_cfg cfg = { __attribute__((unused)) const struct lfs_cfg cfg = {
.bd_ctx = &bd, .ctx = &bd,
.bd_read = lfs_testbd_readctx, .read = lfs_testbd_readctx,
.bd_prog = lfs_testbd_progctx, .prog = lfs_testbd_progctx,
.bd_erase = lfs_testbd_erasectx, .erase = lfs_testbd_erasectx,
.bd_sync = lfs_testbd_syncctx, .sync = lfs_testbd_syncctx,
%(cfg)s .read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES,
.cache_size = LFS_CACHE_SIZE,
.lookahead_size = LFS_LOOKAHEAD_SIZE,
}; };
__attribute__((unused)) const struct lfs_testbd_cfg bdcfg = { __attribute__((unused)) const struct lfs_testbd_cfg bdcfg = {
.rambd_cfg = &(const struct lfs_rambd_cfg){
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.erase_size = LFS_BLOCK_SIZE,
.erase_count = LFS_BLOCK_COUNT,
.erase_value = LFS_ERASE_VALUE,
},
.filebd_cfg = &(const struct lfs_filebd_cfg){
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.erase_size = LFS_BLOCK_SIZE,
.erase_count = LFS_BLOCK_COUNT,
.erase_value = LFS_ERASE_VALUE,
},
.read_size = LFS_READ_SIZE, .read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE, .prog_size = LFS_PROG_SIZE,
.erase_size = LFS_BLOCK_SIZE, .erase_size = LFS_BLOCK_SIZE,
@@ -207,11 +188,7 @@ class TestCase:
for k in sorted(self.perms[0].defines) for k in sorted(self.perms[0].defines)
if k not in self.defines))) if k not in self.defines)))
f.write(PROLOGUE % dict( f.write(PROLOGUE)
cfg='\n'.join(
8*' '+'.%s = %s,\n' % (k, d)
for k, d in sorted(CFG.items())
if d not in self.suite.defines)))
f.write('\n') f.write('\n')
f.write(4*' '+'// test case %d\n' % self.caseno) f.write(4*' '+'// test case %d\n' % self.caseno)
f.write(4*' '+'#line %d "%s"\n' % (self.code_lineno, self.suite.path)) f.write(4*' '+'#line %d "%s"\n' % (self.code_lineno, self.suite.path))
@@ -220,7 +197,6 @@ class TestCase:
f.write(self.code) f.write(self.code)
# epilogue # epilogue
f.write(4*' '+'#line %d "%s"\n' % (f.lineno+1, f.path))
f.write(EPILOGUE) f.write(EPILOGUE)
f.write('}\n') f.write('}\n')
@@ -524,32 +500,18 @@ class TestSuite:
return self.perms return self.perms
def build(self, **args): def build(self, **args):
# intercept writes to keep track of linenos
def lineno_open(path, flags):
f = open(path, flags)
f.path = path
f.lineno = 1
write = f.write
def lineno_write(s):
f.lineno += s.count('\n')
write(s)
f.write = lineno_write
return f
# build test files # build test files
tf = lineno_open(self.path + '.test.tc', 'w') tf = open(self.path + '.test.c.t', 'w')
tf.write(BEFORE_TESTS) tf.write(BEFORE_TESTS)
if self.code is not None: if self.code is not None:
tf.write('#line %d "%s"\n' % (self.code_lineno, self.path)) tf.write('#line %d "%s"\n' % (self.code_lineno, self.path))
tf.write(self.code) tf.write(self.code)
tf.write('#line %d "%s"\n' % (tf.lineno+1, tf.path))
tfs = {None: tf} tfs = {None: tf}
for case in self.cases: for case in self.cases:
if case.in_ not in tfs: if case.in_ not in tfs:
tfs[case.in_] = lineno_open(self.path+'.'+ tfs[case.in_] = open(self.path+'.'+
case.in_.replace('/', '.')[:-2]+'.tc', 'w') case.in_.replace('/', '.')+'.t', 'w')
tfs[case.in_].write('#line 1 "%s"\n' % case.in_) tfs[case.in_].write('#line 1 "%s"\n' % case.in_)
with open(case.in_) as f: with open(case.in_) as f:
for line in f: for line in f:
@@ -600,12 +562,12 @@ class TestSuite:
mk.write('%s: %s | %s\n' % ( mk.write('%s: %s | %s\n' % (
self.path+'.test.c', self.path+'.test.c',
self.path, self.path,
self.path+'.test.tc')) self.path+'.test.c.t'))
else: else:
mk.write('%s: %s %s | %s\n' % ( mk.write('%s: %s %s | %s\n' % (
self.path+'.'+path.replace('/', '.'), self.path+'.'+path.replace('/', '.'),
self.path, path, self.path, path,
self.path+'.'+path.replace('/', '.')[:-2]+'.tc')) self.path+'.'+path.replace('/', '.')+'.t'))
mk.write('\t./scripts/explode_asserts.py $| -o $@\n') mk.write('\t./scripts/explode_asserts.py $| -o $@\n')
self.makefile = self.path + '.mk' self.makefile = self.path + '.mk'

View File

@@ -492,17 +492,17 @@ code = '''
// create one block hole for half a directory // create one block hole for half a directory
lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < LFS_BLOCK_SIZE; i += 2) { for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2); memcpy(&buffer[i], "hi", 2);
} }
lfs_file_write(&lfs, &file, buffer, LFS_BLOCK_SIZE) => LFS_BLOCK_SIZE; lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size;
lfs_file_close(&lfs, &file) => 0; lfs_file_close(&lfs, &file) => 0;
lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < (LFS_BLOCK_COUNT-4)*(LFS_BLOCK_SIZE-8); i < (cfg.block_count-4)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -518,10 +518,10 @@ code = '''
lfs_mkdir(&lfs, "splitdir") => 0; lfs_mkdir(&lfs, "splitdir") => 0;
lfs_file_open(&lfs, &file, "splitdir/bump", lfs_file_open(&lfs, &file, "splitdir/bump",
LFS_O_WRONLY | LFS_O_CREAT) => 0; LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < LFS_BLOCK_SIZE; i += 2) { for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2); memcpy(&buffer[i], "hi", 2);
} }
lfs_file_write(&lfs, &file, buffer, 2*LFS_BLOCK_SIZE) => LFS_ERR_NOSPC; lfs_file_write(&lfs, &file, buffer, 2*cfg.block_size) => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file) => 0; lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -541,7 +541,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2)/2)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -552,7 +552,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2+1)/2)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -569,7 +569,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2)/2)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -583,7 +583,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2+1)/2)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -606,7 +606,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2)/2)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -617,7 +617,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2+1)/2)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }
@@ -634,7 +634,7 @@ code = '''
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((LFS_BLOCK_COUNT-2)/2 - 1)*(LFS_BLOCK_SIZE-8); i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file, buffer, size) => size; lfs_file_write(&lfs, &file, buffer, size) => size;
} }

View File

@@ -12,8 +12,7 @@ code = '''
lfs_formatcfg(&lfs, &cfg) => 0; lfs_formatcfg(&lfs, &cfg) => 0;
// change tail-pointer to invalid pointers // change tail-pointer to invalid pointers
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
@@ -39,8 +38,7 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
// change the dir pointer to be invalid // change the dir pointer to be invalid
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
// make sure id 1 == our directory // make sure id 1 == our directory
@@ -88,8 +86,7 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
// change the file pointer to be invalid // change the file pointer to be invalid
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
// make sure id 1 == our file // make sure id 1 == our file
@@ -140,8 +137,7 @@ code = '''
lfs_file_close(&lfs, &file) => 0; lfs_file_close(&lfs, &file) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
// change pointer in CTZ skip-list to be invalid // change pointer in CTZ skip-list to be invalid
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
// make sure id 1 == our file and get our CTZ structure // make sure id 1 == our file and get our CTZ structure
@@ -194,8 +190,7 @@ code = '''
lfs_formatcfg(&lfs, &cfg) => 0; lfs_formatcfg(&lfs, &cfg) => 0;
// create an invalid gstate // create an invalid gstate
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){ lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){
@@ -221,8 +216,7 @@ code = '''
lfs_formatcfg(&lfs, &cfg) => 0; lfs_formatcfg(&lfs, &cfg) => 0;
// change tail-pointer to point to ourself // change tail-pointer to point to ourself
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS(
@@ -244,8 +238,7 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
// find child // find child
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_block_t pair[2]; lfs_block_t pair[2];
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;
@@ -275,8 +268,7 @@ code = '''
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
// find child // find child
lfs.cfg = &cfg; lfs_initcommon(&lfs, &cfg) => 0;
lfs_initcommon(&lfs) => 0;
lfs_mdir_t mdir; lfs_mdir_t mdir;
lfs_block_t pair[2]; lfs_block_t pair[2];
lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0;

View File

@@ -59,7 +59,7 @@ code = '''
[[case]] # reentrant testing for orphans, basically just spam mkdir/remove [[case]] # reentrant testing for orphans, basically just spam mkdir/remove
reentrant = true reentrant = true
# TODO fix this case, caused by non-DAG trees # TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && LFS_BUFFER_SIZE != 64)' if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
define = [ define = [
{FILES=6, DEPTH=1, CYCLES=20}, {FILES=6, DEPTH=1, CYCLES=20},
{FILES=26, DEPTH=1, CYCLES=20}, {FILES=26, DEPTH=1, CYCLES=20},

View File

@@ -148,7 +148,7 @@ code = '''
# almost every tree operation needs a relocation # almost every tree operation needs a relocation
reentrant = true reentrant = true
# TODO fix this case, caused by non-DAG trees # TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && LFS_BUFFER_SIZE != 64)' if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
define = [ define = [
{FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, {FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
@@ -210,7 +210,7 @@ code = '''
[[case]] # reentrant testing for relocations, but now with random renames! [[case]] # reentrant testing for relocations, but now with random renames!
reentrant = true reentrant = true
# TODO fix this case, caused by non-DAG trees # TODO fix this case, caused by non-DAG trees
if = '!(DEPTH == 3 && LFS_BUFFER_SIZE != 64)' if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)'
define = [ define = [
{FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, {FILES=6, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},
{FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1},

View File

@@ -100,7 +100,7 @@ code = '''
lfs_file_open(&lfs, &file, "sequence", lfs_file_open(&lfs, &file, "sequence",
LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0; LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
size = lfs_min(LFS_BUFFER_SIZE, sizeof(buffer)/2); size = lfs_min(lfs.cfg.cache_size, sizeof(buffer)/2);
lfs_size_t qsize = size / 4; lfs_size_t qsize = size / 4;
uint8_t *wb = buffer; uint8_t *wb = buffer;
uint8_t *rb = buffer + size; uint8_t *rb = buffer + size;