Added bench.py and bench_runner.c for benchmarking
These are really just different flavors of test.py and test_runner.c
without support for power-loss testing, but with support for measuring
the cumulative number of bytes read, programmed, and erased.
Note that the existing define parameterization should work perfectly
fine for running benchmarks across various dimensions:
./scripts/bench.py \
runners/bench_runner \
bench_file_read \
-gnor \
-DSIZE='range(0,131072,1024)'
Also added a couple basic benchmarks as a starting point.
This commit is contained in:
+159
-106
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Testing block device, wraps filebd and rambd while providing a bunch
|
||||
* Emulating block device, wraps filebd and rambd while providing a bunch
|
||||
* of hooks for testing littlefs in various conditions.
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
@@ -11,7 +11,7 @@
|
||||
#define _POSIX_C_SOURCE 199309L
|
||||
#endif
|
||||
|
||||
#include "bd/lfs_testbd.h"
|
||||
#include "bd/lfs_emubd.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
@@ -29,14 +29,14 @@
|
||||
// Note we can only modify a block if we have exclusive access to it (rc == 1)
|
||||
//
|
||||
|
||||
static lfs_testbd_block_t *lfs_testbd_incblock(lfs_testbd_block_t *block) {
|
||||
static lfs_emubd_block_t *lfs_emubd_incblock(lfs_emubd_block_t *block) {
|
||||
if (block) {
|
||||
block->rc += 1;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
static void lfs_testbd_decblock(lfs_testbd_block_t *block) {
|
||||
static void lfs_emubd_decblock(lfs_emubd_block_t *block) {
|
||||
if (block) {
|
||||
block->rc -= 1;
|
||||
if (block->rc == 0) {
|
||||
@@ -45,34 +45,34 @@ static void lfs_testbd_decblock(lfs_testbd_block_t *block) {
|
||||
}
|
||||
}
|
||||
|
||||
static lfs_testbd_block_t *lfs_testbd_mutblock(
|
||||
static lfs_emubd_block_t *lfs_emubd_mutblock(
|
||||
const struct lfs_config *cfg,
|
||||
lfs_testbd_block_t **block) {
|
||||
lfs_testbd_block_t *block_ = *block;
|
||||
lfs_emubd_block_t **block) {
|
||||
lfs_emubd_block_t *block_ = *block;
|
||||
if (block_ && block_->rc == 1) {
|
||||
// rc == 1? can modify
|
||||
return block_;
|
||||
|
||||
} else if (block_) {
|
||||
// rc > 1? need to create a copy
|
||||
lfs_testbd_block_t *nblock = malloc(
|
||||
sizeof(lfs_testbd_block_t) + cfg->block_size);
|
||||
lfs_emubd_block_t *nblock = malloc(
|
||||
sizeof(lfs_emubd_block_t) + cfg->block_size);
|
||||
if (!nblock) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(nblock, block_,
|
||||
sizeof(lfs_testbd_block_t) + cfg->block_size);
|
||||
sizeof(lfs_emubd_block_t) + cfg->block_size);
|
||||
nblock->rc = 1;
|
||||
|
||||
lfs_testbd_decblock(block_);
|
||||
lfs_emubd_decblock(block_);
|
||||
*block = nblock;
|
||||
return nblock;
|
||||
|
||||
} else {
|
||||
// no block? need to allocate
|
||||
lfs_testbd_block_t *nblock = malloc(
|
||||
sizeof(lfs_testbd_block_t) + cfg->block_size);
|
||||
lfs_emubd_block_t *nblock = malloc(
|
||||
sizeof(lfs_emubd_block_t) + cfg->block_size);
|
||||
if (!nblock) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ static lfs_testbd_block_t *lfs_testbd_mutblock(
|
||||
nblock->wear = 0;
|
||||
|
||||
// zero for consistency
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
memset(nblock->data,
|
||||
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
|
||||
cfg->block_size);
|
||||
@@ -92,11 +92,11 @@ static lfs_testbd_block_t *lfs_testbd_mutblock(
|
||||
}
|
||||
|
||||
|
||||
// testbd create/destroy
|
||||
// emubd create/destroy
|
||||
|
||||
int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_testbd_config *bdcfg) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, "
|
||||
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_emubd_config *bdcfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
|
||||
@@ -113,25 +113,28 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
bdcfg->badblock_behavior, bdcfg->power_cycles,
|
||||
bdcfg->powerloss_behavior, (void*)(uintptr_t)bdcfg->powerloss_cb,
|
||||
bdcfg->powerloss_data, bdcfg->track_branches);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
bd->cfg = bdcfg;
|
||||
|
||||
// allocate our block array, all blocks start as uninitialized
|
||||
bd->blocks = malloc(cfg->block_count * sizeof(lfs_testbd_block_t*));
|
||||
bd->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
|
||||
if (!bd->blocks) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
memset(bd->blocks, 0, cfg->block_count * sizeof(lfs_testbd_block_t*));
|
||||
memset(bd->blocks, 0, cfg->block_count * sizeof(lfs_emubd_block_t*));
|
||||
|
||||
// setup testing things
|
||||
bd->read = 0;
|
||||
bd->prog = 0;
|
||||
bd->erased = 0;
|
||||
bd->power_cycles = bd->cfg->power_cycles;
|
||||
bd->disk = NULL;
|
||||
|
||||
if (bd->cfg->disk_path) {
|
||||
bd->disk = malloc(sizeof(lfs_testbd_disk_t));
|
||||
bd->disk = malloc(sizeof(lfs_emubd_disk_t));
|
||||
if (!bd->disk) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
bd->disk->rc = 1;
|
||||
@@ -146,7 +149,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
#endif
|
||||
if (bd->disk->fd < 0) {
|
||||
int err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -155,7 +158,7 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
if (bd->cfg->erase_value != -1) {
|
||||
bd->disk->scratch = malloc(cfg->block_size);
|
||||
if (!bd->disk->scratch) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
memset(bd->disk->scratch,
|
||||
@@ -164,12 +167,12 @@ int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
}
|
||||
}
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_createcfg -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, "
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create(%p {.context=%p, "
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
|
||||
@@ -179,19 +182,19 @@ int lfs_testbd_create(const struct lfs_config *cfg, const char *path) {
|
||||
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
|
||||
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
|
||||
path);
|
||||
static const struct lfs_testbd_config defaults = {.erase_value=-1};
|
||||
int err = lfs_testbd_createcfg(cfg, path, &defaults);
|
||||
LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err);
|
||||
static const struct lfs_emubd_config defaults = {.erase_value=-1};
|
||||
int err = lfs_emubd_createcfg(cfg, path, &defaults);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_create -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int lfs_testbd_destroy(const struct lfs_config *cfg) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
int lfs_emubd_destroy(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_destroy(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// decrement reference counts
|
||||
for (lfs_block_t i = 0; i < cfg->block_count; i++) {
|
||||
lfs_testbd_decblock(bd->blocks[i]);
|
||||
lfs_emubd_decblock(bd->blocks[i]);
|
||||
}
|
||||
free(bd->blocks);
|
||||
|
||||
@@ -205,7 +208,7 @@ int lfs_testbd_destroy(const struct lfs_config *cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_destroy -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -213,12 +216,12 @@ int lfs_testbd_destroy(const struct lfs_config *cfg) {
|
||||
|
||||
// block device API
|
||||
|
||||
int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_read(%p, "
|
||||
LFS_EMUBD_TRACE("lfs_emubd_read(%p, "
|
||||
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
|
||||
(void*)cfg, block, off, buffer, size);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if read is valid
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
@@ -227,12 +230,12 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// get the block
|
||||
const lfs_testbd_block_t *b = bd->blocks[block];
|
||||
const lfs_emubd_block_t *b = bd->blocks[block];
|
||||
if (b) {
|
||||
// block bad?
|
||||
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles &&
|
||||
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
|
||||
bd->cfg->badblock_behavior == LFS_EMUBD_BADBLOCK_READERROR) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", LFS_ERR_CORRUPT);
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
|
||||
@@ -243,8 +246,10 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
memset(buffer,
|
||||
(bd->cfg->erase_value != -1) ? bd->cfg->erase_value : 0,
|
||||
size);
|
||||
}
|
||||
}
|
||||
|
||||
// track reads
|
||||
bd->read += size;
|
||||
if (bd->cfg->read_sleep) {
|
||||
int err = nanosleep(&(struct timespec){
|
||||
.tv_sec=bd->cfg->read_sleep/1000000000,
|
||||
@@ -252,21 +257,21 @@ int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
NULL);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_read -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog(%p, "
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog(%p, "
|
||||
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
|
||||
(void*)cfg, block, off, buffer, size);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if write is valid
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
@@ -275,23 +280,23 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
LFS_ASSERT(off+size <= cfg->block_size);
|
||||
|
||||
// get the block
|
||||
lfs_testbd_block_t *b = lfs_testbd_mutblock(cfg, &bd->blocks[block]);
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
if (!b) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// block bad?
|
||||
if (bd->cfg->erase_cycles && b->wear >= bd->cfg->erase_cycles) {
|
||||
if (bd->cfg->badblock_behavior ==
|
||||
LFS_TESTBD_BADBLOCK_PROGERROR) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
|
||||
LFS_EMUBD_BADBLOCK_PROGERROR) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_CORRUPT);
|
||||
return LFS_ERR_CORRUPT;
|
||||
} else if (bd->cfg->badblock_behavior ==
|
||||
LFS_TESTBD_BADBLOCK_PROGNOOP ||
|
||||
LFS_EMUBD_BADBLOCK_PROGNOOP ||
|
||||
bd->cfg->badblock_behavior ==
|
||||
LFS_TESTBD_BADBLOCK_ERASENOOP) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
|
||||
LFS_EMUBD_BADBLOCK_ERASENOOP) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -313,18 +318,20 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
int err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t res2 = write(bd->disk->fd, buffer, size);
|
||||
if (res2 < 0) {
|
||||
int err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// track progs
|
||||
bd->prog += size;
|
||||
if (bd->cfg->prog_sleep) {
|
||||
int err = nanosleep(&(struct timespec){
|
||||
.tv_sec=bd->cfg->prog_sleep/1000000000,
|
||||
@@ -332,7 +339,7 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
NULL);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -346,21 +353,21 @@ int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
}
|
||||
}
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if erase is valid
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// get the block
|
||||
lfs_testbd_block_t *b = lfs_testbd_mutblock(cfg, &bd->blocks[block]);
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
if (!b) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -368,12 +375,12 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
if (bd->cfg->erase_cycles) {
|
||||
if (b->wear >= bd->cfg->erase_cycles) {
|
||||
if (bd->cfg->badblock_behavior ==
|
||||
LFS_TESTBD_BADBLOCK_ERASEERROR) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
|
||||
LFS_EMUBD_BADBLOCK_ERASEERROR) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", LFS_ERR_CORRUPT);
|
||||
return LFS_ERR_CORRUPT;
|
||||
} else if (bd->cfg->badblock_behavior ==
|
||||
LFS_TESTBD_BADBLOCK_ERASENOOP) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0);
|
||||
LFS_EMUBD_BADBLOCK_ERASENOOP) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
@@ -393,7 +400,7 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
SEEK_SET);
|
||||
if (res1 < 0) {
|
||||
int err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -402,12 +409,14 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
cfg->block_size);
|
||||
if (res2 < 0) {
|
||||
int err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// track erases
|
||||
bd->erased += cfg->block_size;
|
||||
if (bd->cfg->erase_sleep) {
|
||||
int err = nanosleep(&(struct timespec){
|
||||
.tv_sec=bd->cfg->erase_sleep/1000000000,
|
||||
@@ -415,7 +424,7 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
NULL);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_erase -> %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -429,98 +438,142 @@ int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
}
|
||||
}
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_prog -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_testbd_sync(const struct lfs_config *cfg) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg);
|
||||
int lfs_emubd_sync(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_sync(%p)", (void*)cfg);
|
||||
|
||||
// do nothing
|
||||
(void)cfg;
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_sync -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Additional extended API for driving test features ///
|
||||
|
||||
// simulated wear operations
|
||||
lfs_emubd_sio_t lfs_emubd_getread(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getread(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getread -> %"PRIu64, bd->read);
|
||||
return bd->read;
|
||||
}
|
||||
|
||||
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
|
||||
lfs_emubd_sio_t lfs_emubd_getprog(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getprog(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getprog -> %"PRIu64, bd->prog);
|
||||
return bd->prog;
|
||||
}
|
||||
|
||||
lfs_emubd_sio_t lfs_emubd_geterased(const struct lfs_config *cfg) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_geterased(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_geterased -> %"PRIu64, bd->erased);
|
||||
return bd->erased;
|
||||
}
|
||||
|
||||
int lfs_emubd_setread(const struct lfs_config *cfg, lfs_emubd_io_t read) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setread(%p, %"PRIu64")", (void*)cfg, read);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
bd->read = read;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setread -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_emubd_setprog(const struct lfs_config *cfg, lfs_emubd_io_t prog) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setprog(%p, %"PRIu64")", (void*)cfg, prog);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
bd->prog = prog;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setprog -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_seterased(%p, %"PRIu64")", (void*)cfg, erased);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
bd->erased = erased;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_seterased -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lfs_emubd_swear_t lfs_emubd_getwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getwear(%p, %"PRIu32")", (void*)cfg, block);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if block is valid
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// get the wear
|
||||
lfs_testbd_wear_t wear;
|
||||
const lfs_testbd_block_t *b = bd->blocks[block];
|
||||
lfs_emubd_wear_t wear;
|
||||
const lfs_emubd_block_t *b = bd->blocks[block];
|
||||
if (b) {
|
||||
wear = b->wear;
|
||||
} else {
|
||||
wear = 0;
|
||||
}
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, wear);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getwear -> %"PRIu32, wear);
|
||||
return wear;
|
||||
}
|
||||
|
||||
int lfs_testbd_setwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block, lfs_testbd_wear_t wear) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
int lfs_emubd_setwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block, lfs_emubd_wear_t wear) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear(%p, %"PRIu32")", (void*)cfg, block);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// check if block is valid
|
||||
LFS_ASSERT(block < cfg->block_count);
|
||||
|
||||
// set the wear
|
||||
lfs_testbd_block_t *b = lfs_testbd_mutblock(cfg, &bd->blocks[block]);
|
||||
lfs_emubd_block_t *b = lfs_emubd_mutblock(cfg, &bd->blocks[block]);
|
||||
if (!b) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_setwear -> %"PRIu32, LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %"PRIu32, LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
b->wear = wear;
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_setwear -> %"PRIu32, 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setwear -> %"PRIu32, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lfs_testbd_spowercycles_t lfs_testbd_getpowercycles(
|
||||
lfs_emubd_spowercycles_t lfs_emubd_getpowercycles(
|
||||
const struct lfs_config *cfg) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_getpowercycles(%p)", (void*)cfg);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getpowercycles(%p)", (void*)cfg);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_getpowercycles -> %"PRIi32, bd->power_cycles);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getpowercycles -> %"PRIi32, bd->power_cycles);
|
||||
return bd->power_cycles;
|
||||
}
|
||||
|
||||
int lfs_testbd_setpowercycles(const struct lfs_config *cfg,
|
||||
lfs_testbd_powercycles_t power_cycles) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_setpowercycles(%p, %"PRIi32")",
|
||||
int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
|
||||
lfs_emubd_powercycles_t power_cycles) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_setpowercycles(%p, %"PRIi32")",
|
||||
(void*)cfg, power_cycles);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
bd->power_cycles = power_cycles;
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_getpowercycles -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_getpowercycles -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_testbd_copy(const struct lfs_config *cfg, lfs_testbd_t *copy) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_copy(%p, %p)", (void*)cfg, (void*)copy);
|
||||
lfs_testbd_t *bd = cfg->context;
|
||||
int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy) {
|
||||
LFS_EMUBD_TRACE("lfs_emubd_copy(%p, %p)", (void*)cfg, (void*)copy);
|
||||
lfs_emubd_t *bd = cfg->context;
|
||||
|
||||
// lazily copy over our block array
|
||||
copy->blocks = malloc(cfg->block_count * sizeof(lfs_testbd_block_t*));
|
||||
copy->blocks = malloc(cfg->block_count * sizeof(lfs_emubd_block_t*));
|
||||
if (!copy->blocks) {
|
||||
LFS_TESTBD_TRACE("lfs_testbd_copy -> %d", LFS_ERR_NOMEM);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", LFS_ERR_NOMEM);
|
||||
return LFS_ERR_NOMEM;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cfg->block_count; i++) {
|
||||
copy->blocks[i] = lfs_testbd_incblock(bd->blocks[i]);
|
||||
copy->blocks[i] = lfs_emubd_incblock(bd->blocks[i]);
|
||||
}
|
||||
|
||||
// other state
|
||||
@@ -531,7 +584,7 @@ int lfs_testbd_copy(const struct lfs_config *cfg, lfs_testbd_t *copy) {
|
||||
}
|
||||
copy->cfg = bd->cfg;
|
||||
|
||||
LFS_TESTBD_TRACE("lfs_testbd_copy -> %d", 0);
|
||||
LFS_EMUBD_TRACE("lfs_emubd_copy -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Testing block device, wraps filebd and rambd while providing a bunch
|
||||
* Emulating block device, wraps filebd and rambd while providing a bunch
|
||||
* of hooks for testing littlefs in various conditions.
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_TESTBD_H
|
||||
#define LFS_TESTBD_H
|
||||
#ifndef LFS_EMUBD_H
|
||||
#define LFS_EMUBD_H
|
||||
|
||||
#include "lfs.h"
|
||||
#include "lfs_util.h"
|
||||
@@ -21,11 +21,11 @@ extern "C"
|
||||
|
||||
|
||||
// Block device specific tracing
|
||||
#ifndef LFS_TESTBD_TRACE
|
||||
#ifdef LFS_TESTBD_YES_TRACE
|
||||
#define LFS_TESTBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
|
||||
#ifndef LFS_EMUBD_TRACE
|
||||
#ifdef LFS_EMUBD_YES_TRACE
|
||||
#define LFS_EMUBD_TRACE(...) LFS_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define LFS_TESTBD_TRACE(...)
|
||||
#define LFS_EMUBD_TRACE(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -35,34 +35,38 @@ extern "C"
|
||||
//
|
||||
// Not that read-noop is not allowed. Read _must_ return a consistent (but
|
||||
// may be arbitrary) value on every read.
|
||||
typedef enum lfs_testbd_badblock_behavior {
|
||||
LFS_TESTBD_BADBLOCK_PROGERROR,
|
||||
LFS_TESTBD_BADBLOCK_ERASEERROR,
|
||||
LFS_TESTBD_BADBLOCK_READERROR,
|
||||
LFS_TESTBD_BADBLOCK_PROGNOOP,
|
||||
LFS_TESTBD_BADBLOCK_ERASENOOP,
|
||||
} lfs_testbd_badblock_behavior_t;
|
||||
typedef enum lfs_emubd_badblock_behavior {
|
||||
LFS_EMUBD_BADBLOCK_PROGERROR,
|
||||
LFS_EMUBD_BADBLOCK_ERASEERROR,
|
||||
LFS_EMUBD_BADBLOCK_READERROR,
|
||||
LFS_EMUBD_BADBLOCK_PROGNOOP,
|
||||
LFS_EMUBD_BADBLOCK_ERASENOOP,
|
||||
} lfs_emubd_badblock_behavior_t;
|
||||
|
||||
// Mode determining how power-loss behaves during testing. For now this
|
||||
// only supports a noop behavior, leaving the data on-disk untouched.
|
||||
typedef enum lfs_testbd_powerloss_behavior {
|
||||
LFS_TESTBD_POWERLOSS_NOOP,
|
||||
} lfs_testbd_powerloss_behavior_t;
|
||||
typedef enum lfs_emubd_powerloss_behavior {
|
||||
LFS_EMUBD_POWERLOSS_NOOP,
|
||||
} lfs_emubd_powerloss_behavior_t;
|
||||
|
||||
// Type for measuring read/program/erase operations
|
||||
typedef uint64_t lfs_emubd_io_t;
|
||||
typedef int64_t lfs_emubd_sio_t;
|
||||
|
||||
// Type for measuring wear
|
||||
typedef uint32_t lfs_testbd_wear_t;
|
||||
typedef int32_t lfs_testbd_swear_t;
|
||||
typedef uint32_t lfs_emubd_wear_t;
|
||||
typedef int32_t lfs_emubd_swear_t;
|
||||
|
||||
// Type for tracking power-cycles
|
||||
typedef uint32_t lfs_testbd_powercycles_t;
|
||||
typedef int32_t lfs_testbd_spowercycles_t;
|
||||
typedef uint32_t lfs_emubd_powercycles_t;
|
||||
typedef int32_t lfs_emubd_spowercycles_t;
|
||||
|
||||
// Type for delays in nanoseconds
|
||||
typedef uint64_t lfs_testbd_sleep_t;
|
||||
typedef int64_t lfs_testbd_ssleep_t;
|
||||
typedef uint64_t lfs_emubd_sleep_t;
|
||||
typedef int64_t lfs_emubd_ssleep_t;
|
||||
|
||||
// testbd config, this is required for testing
|
||||
struct lfs_testbd_config {
|
||||
// emubd config, this is required for testing
|
||||
struct lfs_emubd_config {
|
||||
// 8-bit erase value to use for simulating erases. -1 does not simulate
|
||||
// erases, which can speed up testing by avoiding the extra block-device
|
||||
// operations to store the erase value.
|
||||
@@ -73,15 +77,15 @@ struct lfs_testbd_config {
|
||||
uint32_t erase_cycles;
|
||||
|
||||
// The mode determining how bad-blocks fail
|
||||
lfs_testbd_badblock_behavior_t badblock_behavior;
|
||||
lfs_emubd_badblock_behavior_t badblock_behavior;
|
||||
|
||||
// Number of write operations (erase/prog) before triggering a power-loss.
|
||||
// power_cycles=0 disables this. The exact behavior of power-loss is
|
||||
// controlled by a combination of powerloss_behavior and powerloss_cb.
|
||||
lfs_testbd_powercycles_t power_cycles;
|
||||
lfs_emubd_powercycles_t power_cycles;
|
||||
|
||||
// The mode determining how power-loss affects disk
|
||||
lfs_testbd_powerloss_behavior_t powerloss_behavior;
|
||||
lfs_emubd_powerloss_behavior_t powerloss_behavior;
|
||||
|
||||
// Function to call to emulate power-loss. The exact behavior of power-loss
|
||||
// is up to the runner to provide.
|
||||
@@ -100,98 +104,119 @@ struct lfs_testbd_config {
|
||||
|
||||
// Artificial delay in nanoseconds, there is no purpose for this other
|
||||
// than slowing down the simulation.
|
||||
lfs_testbd_sleep_t read_sleep;
|
||||
lfs_emubd_sleep_t read_sleep;
|
||||
|
||||
// Artificial delay in nanoseconds, there is no purpose for this other
|
||||
// than slowing down the simulation.
|
||||
lfs_testbd_sleep_t prog_sleep;
|
||||
lfs_emubd_sleep_t prog_sleep;
|
||||
|
||||
// Artificial delay in nanoseconds, there is no purpose for this other
|
||||
// than slowing down the simulation.
|
||||
lfs_testbd_sleep_t erase_sleep;
|
||||
lfs_emubd_sleep_t erase_sleep;
|
||||
};
|
||||
|
||||
// A reference counted block
|
||||
typedef struct lfs_testbd_block {
|
||||
typedef struct lfs_emubd_block {
|
||||
uint32_t rc;
|
||||
lfs_testbd_wear_t wear;
|
||||
lfs_emubd_wear_t wear;
|
||||
|
||||
uint8_t data[];
|
||||
} lfs_testbd_block_t;
|
||||
} lfs_emubd_block_t;
|
||||
|
||||
// Disk mirror
|
||||
typedef struct lfs_testbd_disk {
|
||||
typedef struct lfs_emubd_disk {
|
||||
uint32_t rc;
|
||||
int fd;
|
||||
uint8_t *scratch;
|
||||
} lfs_testbd_disk_t;
|
||||
} lfs_emubd_disk_t;
|
||||
|
||||
// testbd state
|
||||
typedef struct lfs_testbd {
|
||||
// emubd state
|
||||
typedef struct lfs_emubd {
|
||||
// array of copy-on-write blocks
|
||||
lfs_testbd_block_t **blocks;
|
||||
lfs_emubd_block_t **blocks;
|
||||
|
||||
// some other test state
|
||||
uint32_t power_cycles;
|
||||
lfs_testbd_disk_t *disk;
|
||||
lfs_emubd_io_t read;
|
||||
lfs_emubd_io_t prog;
|
||||
lfs_emubd_io_t erased;
|
||||
lfs_emubd_powercycles_t power_cycles;
|
||||
lfs_emubd_disk_t *disk;
|
||||
|
||||
const struct lfs_testbd_config *cfg;
|
||||
} lfs_testbd_t;
|
||||
const struct lfs_emubd_config *cfg;
|
||||
} lfs_emubd_t;
|
||||
|
||||
|
||||
/// Block device API ///
|
||||
|
||||
// Create a test block device using the geometry in lfs_config
|
||||
// Create an emulating block device using the geometry in lfs_config
|
||||
//
|
||||
// Note that filebd is used if a path is provided, if path is NULL
|
||||
// testbd will use rambd which can be much faster.
|
||||
int lfs_testbd_create(const struct lfs_config *cfg, const char *path);
|
||||
int lfs_testbd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_testbd_config *bdcfg);
|
||||
// emubd will use rambd which can be much faster.
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
|
||||
int lfs_emubd_createcfg(const struct lfs_config *cfg, const char *path,
|
||||
const struct lfs_emubd_config *bdcfg);
|
||||
|
||||
// Clean up memory associated with block device
|
||||
int lfs_testbd_destroy(const struct lfs_config *cfg);
|
||||
int lfs_emubd_destroy(const struct lfs_config *cfg);
|
||||
|
||||
// Read a block
|
||||
int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
// Program a block
|
||||
//
|
||||
// The block must have previously been erased.
|
||||
int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Erase a block
|
||||
//
|
||||
// A block must be erased before being programmed. The
|
||||
// state of an erased block is undefined.
|
||||
int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block);
|
||||
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
|
||||
|
||||
// Sync the block device
|
||||
int lfs_testbd_sync(const struct lfs_config *cfg);
|
||||
int lfs_emubd_sync(const struct lfs_config *cfg);
|
||||
|
||||
|
||||
/// Additional extended API for driving test features ///
|
||||
|
||||
// Get total amount of bytes read
|
||||
lfs_emubd_sio_t lfs_emubd_getread(const struct lfs_config *cfg);
|
||||
|
||||
// Get total amount of bytes programmed
|
||||
lfs_emubd_sio_t lfs_emubd_getprog(const struct lfs_config *cfg);
|
||||
|
||||
// Get total amount of bytes erased
|
||||
lfs_emubd_sio_t lfs_emubd_geterased(const struct lfs_config *cfg);
|
||||
|
||||
// Manually set amount of bytes read
|
||||
int lfs_emubd_setread(const struct lfs_config *cfg, lfs_emubd_io_t read);
|
||||
|
||||
// Manually set amount of bytes programmed
|
||||
int lfs_emubd_setprog(const struct lfs_config *cfg, lfs_emubd_io_t prog);
|
||||
|
||||
// Manually set amount of bytes erased
|
||||
int lfs_emubd_seterased(const struct lfs_config *cfg, lfs_emubd_io_t erased);
|
||||
|
||||
// Get simulated wear on a given block
|
||||
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_config *cfg,
|
||||
lfs_emubd_swear_t lfs_emubd_getwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block);
|
||||
|
||||
// Manually set simulated wear on a given block
|
||||
int lfs_testbd_setwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block, lfs_testbd_wear_t wear);
|
||||
int lfs_emubd_setwear(const struct lfs_config *cfg,
|
||||
lfs_block_t block, lfs_emubd_wear_t wear);
|
||||
|
||||
// Get the remaining power-cycles
|
||||
lfs_testbd_spowercycles_t lfs_testbd_getpowercycles(
|
||||
lfs_emubd_spowercycles_t lfs_emubd_getpowercycles(
|
||||
const struct lfs_config *cfg);
|
||||
|
||||
// Manually set the remaining power-cycles
|
||||
int lfs_testbd_setpowercycles(const struct lfs_config *cfg,
|
||||
lfs_testbd_powercycles_t power_cycles);
|
||||
int lfs_emubd_setpowercycles(const struct lfs_config *cfg,
|
||||
lfs_emubd_powercycles_t power_cycles);
|
||||
|
||||
// Create a copy-on-write copy of the state of this block device
|
||||
int lfs_testbd_copy(const struct lfs_config *cfg, lfs_testbd_t *copy);
|
||||
int lfs_emubd_copy(const struct lfs_config *cfg, lfs_emubd_t *copy);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
Reference in New Issue
Block a user