kiwibd: Added kiwibd, a lighter-weight variant of emubd

Useful for emulating much larger disks in a file (or in RAM). kiwibd
doesn't have all the features of emubd, but this allows it to prioritize
disk size and speed for benchmarking.

kiwibd still keeps some features useful for benchmarking/emulation:

- Optional erase value emulation, including nor-masking

- Read/prog/erase trackers for measuring bd operations

- Read/prog/erase sleeps for slowing down the simulation to a human
  viewable speed
This commit is contained in:
Christopher Haster
2025-08-13 12:10:05 -05:00
parent 6ba3204816
commit 232f039ccc
6 changed files with 731 additions and 19 deletions

View File

@ -1,6 +1,10 @@
/*
* Emulating block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
* emubd - High-level emulating block device with many bells and
* whistles for testing powerloss, wear, etc.
*
* Note emubd always backs the block device in RAM. Consider using
* kiwibd if you need a block device larger than the available RAM on
* the system.
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
@ -1030,7 +1034,7 @@ int lfs3_emubd_sync(const struct lfs3_cfg *cfg) {
}
/// Additional extended API for driving test features ///
/// Additional emubd features for testing ///
void lfs3_emubd_seed(const struct lfs3_cfg *cfg, uint32_t seed) {
LFS3_EMUBD_TRACE("lfs3_emubd_seed(%p, 0x%08"PRIx32")",

View File

@ -1,6 +1,10 @@
/*
* Emulating block device, wraps filebd and rambd while providing a bunch
* of hooks for testing littlefs in various conditions.
* emubd - High-level emulating block device with many bells and
* whistles for testing powerloss, wear, etc.
*
* Note emubd always backs the block device in RAM. Consider using
* kiwibd if you need a block device larger than the available RAM on
* the system.
*
* Copyright (c) 2022, The littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
@ -11,8 +15,6 @@
#include "lfs3.h"
#include "lfs3_util.h"
#include "bd/lfs3_rambd.h"
#include "bd/lfs3_filebd.h"
// Block device specific tracing
@ -99,7 +101,8 @@ struct lfs3_emubd_cfg {
uint32_t seed;
// Path to file to use as a mirror of the disk. This provides a way to view
// the current state of the block device.
// the current state of the block device, but does not eliminate the RAM
// requirement.
const char *disk_path;
// Artificial delay in nanoseconds, there is no purpose for this other
@ -157,8 +160,11 @@ typedef struct lfs3_emubd {
// Create an emulating block device using the geometry in lfs3_cfg
//
// Note that filebd is used if a path is provided, if path is NULL
// emubd will use rambd which can be much faster.
// If disk_path is provided, emubd will mirror the block device in the
// file. Note this is a write-only mirror intended for introspection,
// and does not eliminate the RAM requirement.
//
// TODO wait, why do we have both disk_path and path here?
int lfs3_emubd_create(const struct lfs3_cfg *cfg, const char *path);
int lfs3_emubd_createcfg(const struct lfs3_cfg *cfg, const char *path,
const struct lfs3_emubd_cfg *bdcfg);
@ -186,7 +192,7 @@ int lfs3_emubd_erase(const struct lfs3_cfg *cfg, lfs3_block_t block);
int lfs3_emubd_sync(const struct lfs3_cfg *cfg);
/// Additional extended API for driving test features ///
/// Additional emubd features for testing ///
// Set the current prng state
void lfs3_emubd_seed(const struct lfs3_cfg *cfg, uint32_t seed);

559
bd/lfs3_kiwibd.c Normal file
View File

@ -0,0 +1,559 @@
/*
* kiwibd - A lightweight variant of emubd, useful for emulating large
* disks backed by a file or in RAM.
*
* Unlike emubd, file-backed disks are _not_ mirrored in RAM. kiwibd has
* fewer features than emubd, prioritizing speed for benchmarking.
*
*
*/
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#include "bd/lfs3_kiwibd.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
// low-level flash memory emulation
// read data
static inline void lfs3_kiwibd_memread(const struct lfs3_cfg *cfg,
void *restrict dst, const void *restrict src, size_t size) {
(void)cfg;
memcpy(dst, src, size);
}
static inline void lfs3_kiwibd_memprog(const struct lfs3_cfg *cfg,
void *restrict dst, const void *restrict src, size_t size) {
lfs3_kiwibd_t *bd = cfg->context;
// emulating nor-masking?
if (bd->cfg->erase_value == -2) {
uint8_t *dst_ = dst;
const uint8_t *src_ = src;
for (size_t i = 0; i < size; i++) {
dst_[i] &= src_[i];
}
} else {
memcpy(dst, src, size);
}
}
static inline void lfs3_kiwibd_memerase(const struct lfs3_cfg *cfg,
void *restrict dst, size_t size) {
lfs3_kiwibd_t *bd = cfg->context;
// emulating erase value?
if (bd->cfg->erase_value != -1) {
memset(dst,
(bd->cfg->erase_value >= 0)
? bd->cfg->erase_value
: 0xff,
size);
}
}
// this is slightly different from lfs3_kiwibd_memerase in that we use
// lfs3_kiwibd_memzero when we need to unconditionally zero memory
static inline void lfs3_kiwibd_memzero(const struct lfs3_cfg *cfg,
void *restrict dst, size_t size) {
lfs3_kiwibd_t *bd = cfg->context;
memset(dst,
(bd->cfg->erase_value == -1) ? 0
: (bd->cfg->erase_value >= 0) ? bd->cfg->erase_value
: (bd->cfg->erase_value == -2) ? 0xff
: 0,
size);
}
// kiwibd create/destroy
int lfs3_kiwibd_createcfg(const struct lfs3_cfg *cfg, const char *path,
const struct lfs3_kiwibd_cfg *bdcfg) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_createcfg("
"%p {"
".context=%p, "
".read=%p, "
".prog=%p, "
".erase=%p, "
".sync=%p, "
".read_size=%"PRIu32", "
".prog_size=%"PRIu32", "
".block_size=%"PRIu32", "
".block_count=%"PRIu32"}, "
"\"%s\", "
"%p {"
".erase_value=%"PRId32", "
".disk_path=\"%s\", "
".buffer=%p, "
".read_sleep=%"PRIu64", "
".prog_sleep=%"PRIu64", "
".erase_sleep=%"PRIu64"})",
(void*)cfg,
cfg->context,
(void*)(uintptr_t)cfg->read,
(void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase,
(void*)(uintptr_t)cfg->sync,
cfg->read_size,
cfg->prog_size,
cfg->block_size,
cfg->block_count,
path,
(void*)bdcfg,
bdcfg->erase_value,
bdcfg->disk_path,
bdcfg->buffer,
bdcfg->read_sleep,
bdcfg->prog_sleep,
bdcfg->erase_sleep);
lfs3_kiwibd_t *bd = cfg->context;
bd->cfg = bdcfg;
// setup some initial state
bd->readed = 0;
bd->proged = 0;
bd->erased = 0;
if (bd->cfg->disk_path) {
bd->u.disk.fd = -1;
bd->u.disk.scratch = NULL;
} else {
bd->u.mem = NULL;
}
int err;
// if we have a disk_path, try to open the backing file
if (bd->cfg->disk_path) {
bd->u.disk.fd = open(bd->cfg->disk_path,
O_RDWR | O_CREAT, 0666);
if (bd->u.disk.fd < 0) {
err = -errno;
goto failed;
}
// allocate a scratch buffer to help with zeroing/masking/etc
bd->u.disk.scratch = malloc(cfg->block_size);
if (!bd->u.disk.scratch) {
err = LFS3_ERR_NOMEM;
goto failed;
}
// zero for reproducibility
lfs3_kiwibd_memzero(cfg, bd->u.disk.scratch, cfg->block_size);
for (lfs3_block_t i = 0; i < cfg->block_count; i++) {
ssize_t res = write(bd->u.disk.fd,
bd->u.disk.scratch,
cfg->block_size);
if (res < 0) {
err = -errno;
goto failed;
}
}
// otherwise, try to malloc a big memory array
} else {
bd->u.mem = malloc((size_t)cfg->block_size * cfg->block_count);
if (!bd->u.mem) {
err = LFS3_ERR_NOMEM;
goto failed;
}
// zero for reproducibility
lfs3_kiwibd_memzero(cfg, bd->u.mem,
(size_t)cfg->block_size * cfg->block_count);
}
LFS3_KIWIBD_TRACE("lfs3_kiwibd_createcfg -> %d", 0);
return 0;
failed:;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_createcfg -> %d", err);
// clean up memory
if (bd->cfg->disk_path) {
if (bd->u.disk.fd != -1) {
close(bd->u.disk.fd);
}
free(bd->u.disk.scratch);
} else {
free(bd->u.mem);
}
return err;
}
int lfs3_kiwibd_create(const struct lfs3_cfg *cfg, const char *path) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_create("
"%p {"
".context=%p, "
".read=%p, "
".prog=%p, "
".erase=%p, "
".sync=%p, "
".read_size=%"PRIu32", "
".prog_size=%"PRIu32", "
".block_size=%"PRIu32", "
".block_count=%"PRIu32"}, "
"\"%s\")",
(void*)cfg,
cfg->context,
(void*)(uintptr_t)cfg->read,
(void*)(uintptr_t)cfg->prog,
(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 lfs3_kiwibd_cfg defaults = {.erase_value=-1};
int err = lfs3_kiwibd_createcfg(cfg, path, &defaults);
LFS3_KIWIBD_TRACE("lfs3_kiwibd_create -> %d", err);
return err;
}
int lfs3_kiwibd_destroy(const struct lfs3_cfg *cfg) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_destroy(%p)", (void*)cfg);
lfs3_kiwibd_t *bd = cfg->context;
// clean up memory
if (bd->cfg->disk_path) {
close(bd->u.disk.fd);
free(bd->u.disk.scratch);
} else {
free(bd->u.mem);
}
LFS3_KIWIBD_TRACE("lfs3_kiwibd_destroy -> %d", 0);
return 0;
}
// block device API
int lfs3_kiwibd_read(const struct lfs3_cfg *cfg, lfs3_block_t block,
lfs3_off_t off, void *buffer, lfs3_size_t size) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs3_kiwibd_t *bd = cfg->context;
// check if read is valid
LFS3_ASSERT(block < cfg->block_count);
LFS3_ASSERT(off % cfg->read_size == 0);
LFS3_ASSERT(size % cfg->read_size == 0);
LFS3_ASSERT(off+size <= cfg->block_size);
// read in file?
if (bd->cfg->disk_path) {
lfs3_kiwibd_memerase(cfg,
bd->u.disk.scratch,
cfg->block_size);
off_t res = lseek(bd->u.disk.fd,
(off_t)block*cfg->block_size + (off_t)off,
SEEK_SET);
if (res < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_read -> %d", err);
return err;
}
ssize_t res_ = read(bd->u.disk.fd, buffer, size);
if (res_ < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_read -> %d", err);
return err;
}
// read in RAM?
} else {
lfs3_kiwibd_memread(cfg,
buffer,
&bd->u.mem[(size_t)block*cfg->block_size + (size_t)off],
size);
}
// track reads
bd->readed += size;
if (bd->cfg->read_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->read_sleep/1000000000,
.tv_nsec=bd->cfg->read_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_read -> %d", err);
return err;
}
}
LFS3_KIWIBD_TRACE("lfs3_kiwibd_read -> %d", 0);
return 0;
}
int lfs3_kiwibd_prog(const struct lfs3_cfg *cfg, lfs3_block_t block,
lfs3_off_t off, const void *buffer, lfs3_size_t size) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size);
lfs3_kiwibd_t *bd = cfg->context;
// check if write is valid
LFS3_ASSERT(block < cfg->block_count);
LFS3_ASSERT(off % cfg->prog_size == 0);
LFS3_ASSERT(size % cfg->prog_size == 0);
LFS3_ASSERT(off+size <= cfg->block_size);
// prog in file?
if (bd->cfg->disk_path) {
// were we erased properly?
if (bd->cfg->erase_value >= 0) {
off_t res = lseek(bd->u.disk.fd,
(off_t)block*cfg->block_size + (off_t)off,
SEEK_SET);
if (res < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
ssize_t res_ = read(bd->u.disk.fd, bd->u.disk.scratch, size);
if (res_ < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
for (lfs3_off_t i = 0; i < size; i++) {
LFS3_ASSERT(bd->u.disk.scratch[i] == bd->cfg->erase_value);
}
}
// masking progs?
if (bd->cfg->erase_value == -2) {
off_t res = lseek(bd->u.disk.fd,
(off_t)block*cfg->block_size + (off_t)off,
SEEK_SET);
if (res < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
ssize_t res_ = read(bd->u.disk.fd, bd->u.disk.scratch, size);
if (res_ < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
lfs3_kiwibd_memprog(cfg, bd->u.disk.scratch, buffer, size);
res = lseek(bd->u.disk.fd,
(off_t)block*cfg->block_size + (off_t)off,
SEEK_SET);
if (res < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
res_ = write(bd->u.disk.fd, bd->u.disk.scratch, size);
if (res_ < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
// normal progs?
} else {
off_t res = lseek(bd->u.disk.fd,
(off_t)block*cfg->block_size + (off_t)off,
SEEK_SET);
if (res < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
ssize_t res_ = write(bd->u.disk.fd, buffer, size);
if (res_ < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
}
// prog in RAM?
} else {
// were we erased properly?
if (bd->cfg->erase_value >= 0) {
for (lfs3_off_t i = 0; i < size; i++) {
LFS3_ASSERT(
bd->u.mem[(size_t)block*cfg->block_size + (size_t)off]
== bd->cfg->erase_value);
}
}
lfs3_kiwibd_memprog(cfg,
&bd->u.mem[(size_t)block*cfg->block_size + (size_t)off],
buffer,
size);
}
// track progs
bd->proged += size;
if (bd->cfg->prog_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->prog_sleep/1000000000,
.tv_nsec=bd->cfg->prog_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", err);
return err;
}
}
LFS3_KIWIBD_TRACE("lfs3_kiwibd_prog -> %d", 0);
return 0;
}
int lfs3_kiwibd_erase(const struct lfs3_cfg *cfg, lfs3_block_t block) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
(void*)cfg, block, cfg->block_size);
lfs3_kiwibd_t *bd = cfg->context;
// check if erase is valid
LFS3_ASSERT(block < cfg->block_count);
// emulate an erase value?
if (bd->cfg->erase_value != -1) {
// erase in file?
if (bd->cfg->disk_path) {
off_t res = lseek(bd->u.disk.fd,
(off_t)block*cfg->block_size,
SEEK_SET);
if (res < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erase -> %d", err);
return err;
}
lfs3_kiwibd_memerase(cfg,
bd->u.disk.scratch,
cfg->block_size);
ssize_t res_ = write(bd->u.disk.fd,
bd->u.disk.scratch,
cfg->block_size);
if (res_ < 0) {
int err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erase -> %d", err);
return err;
}
// erase in RAM?
} else {
lfs3_kiwibd_memerase(cfg,
&bd->u.mem[(size_t)block*cfg->block_size],
cfg->block_size);
}
}
erased:;
// track erases
bd->erased += cfg->block_size;
if (bd->cfg->erase_sleep) {
int err = nanosleep(&(struct timespec){
.tv_sec=bd->cfg->erase_sleep/1000000000,
.tv_nsec=bd->cfg->erase_sleep%1000000000},
NULL);
if (err) {
err = -errno;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erase -> %d", err);
return err;
}
}
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erase -> %d", 0);
return 0;
}
int lfs3_kiwibd_sync(const struct lfs3_cfg *cfg) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_sync(%p)", (void*)cfg);
// in theory we could actually sync here, but if our goal is
// performance, why bother?
//
// filebd may be a better block device is your goal is actual
// storage
// sync is a noop
(void)cfg;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_sync -> %d", 0);
return 0;
}
/// Additional kiwibd features ///
lfs3_kiwibd_sio_t lfs3_kiwibd_readed(const struct lfs3_cfg *cfg) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_readed(%p)", (void*)cfg);
lfs3_kiwibd_t *bd = cfg->context;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_readed -> %"PRIu64, bd->readed);
return bd->readed;
}
lfs3_kiwibd_sio_t lfs3_kiwibd_proged(const struct lfs3_cfg *cfg) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_proged(%p)", (void*)cfg);
lfs3_kiwibd_t *bd = cfg->context;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_proged -> %"PRIu64, bd->proged);
return bd->proged;
}
lfs3_kiwibd_sio_t lfs3_kiwibd_erased(const struct lfs3_cfg *cfg) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erased(%p)", (void*)cfg);
lfs3_kiwibd_t *bd = cfg->context;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_erased -> %"PRIu64, bd->erased);
return bd->erased;
}
int lfs3_kiwibd_setreaded(const struct lfs3_cfg *cfg,
lfs3_kiwibd_io_t readed) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_setreaded(%p, %"PRIu64")",
(void*)cfg, readed);
lfs3_kiwibd_t *bd = cfg->context;
bd->readed = readed;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_setreaded -> %d", 0);
return 0;
}
int lfs3_kiwibd_setproged(const struct lfs3_cfg *cfg,
lfs3_kiwibd_io_t proged) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_setproged(%p, %"PRIu64")",
(void*)cfg, proged);
lfs3_kiwibd_t *bd = cfg->context;
bd->proged = proged;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_setproged -> %d", 0);
return 0;
}
int lfs3_kiwibd_seterased(const struct lfs3_cfg *cfg,
lfs3_kiwibd_io_t erased) {
LFS3_KIWIBD_TRACE("lfs3_kiwibd_seterased(%p, %"PRIu64")",
(void*)cfg, erased);
lfs3_kiwibd_t *bd = cfg->context;
bd->erased = erased;
LFS3_KIWIBD_TRACE("lfs3_kiwibd_seterased -> %d", 0);
return 0;
}

143
bd/lfs3_kiwibd.h Normal file
View File

@ -0,0 +1,143 @@
/*
* kiwibd - A lightweight variant of emubd, useful for emulating large
* disks backed by a file or in RAM.
*
* Unlike emubd, file-backed disks are _not_ mirrored in RAM. kiwibd has
* fewer features than emubd, prioritizing speed for benchmarking.
*
*
*/
#ifndef LFS3_KIWIBD_H
#define LFS3_KIWIBD_H
#include "lfs3.h"
#include "lfs3_util.h"
// Block device specific tracing
#ifndef LFS3_KIWIBD_TRACE
#ifdef LFS3_KIWIBD_YES_TRACE
#define LFS3_KIWIBD_TRACE(...) LFS3_TRACE(__VA_ARGS__)
#else
#define LFS3_KIWIBD_TRACE(...)
#endif
#endif
// Type for measuring read/program/erase operations
typedef uint64_t lfs3_kiwibd_io_t;
typedef int64_t lfs3_kiwibd_sio_t;
// Type for delays in nanoseconds
typedef uint64_t lfs3_kiwibd_sleep_t;
typedef int64_t lfs3_kiwibd_ssleep_t;
// kiwibd config, this is required for testing
struct lfs3_kiwibd_cfg {
// 8-bit erase value to use for simulating erases. -1 simulates a noop
// erase, which is faster than simulating a fixed erase value. -2 emulates
// nor-masking, which is useful for testing other filesystems (littlefs
// does _not_ rely on this!).
int32_t erase_value;
// Path to file to back the block device. If provided kiwibd uses this
// instead of RAM, allowing emulation of block devices > available RAM.
const char *disk_path;
// Optional statically allocated buffer for the block device. Ignored
// if disk_path is provided.
void *buffer;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs3_kiwibd_sleep_t read_sleep;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs3_kiwibd_sleep_t prog_sleep;
// Artificial delay in nanoseconds, there is no purpose for this other
// than slowing down the simulation.
lfs3_kiwibd_sleep_t erase_sleep;
};
// kiwibd state
typedef struct lfs3_kiwibd {
// backing disk
union {
struct {
int fd;
uint8_t *scratch;
} disk;
uint8_t *mem;
} u;
// amount read/progged/erased
lfs3_kiwibd_io_t readed;
lfs3_kiwibd_io_t proged;
lfs3_kiwibd_io_t erased;
const struct lfs3_kiwibd_cfg *cfg;
} lfs3_kiwibd_t;
/// Block device API ///
// Create a kiwibd using the geometry in lfs3_cfg
//
// If disk_path is provided, it will be used to back the kiwibd,
// otherwise kiwibd will try to use RAM.
//
// TODO wait, why do we have both disk_path and path here?
int lfs3_kiwibd_create(const struct lfs3_cfg *cfg, const char *path);
int lfs3_kiwibd_createcfg(const struct lfs3_cfg *cfg, const char *path,
const struct lfs3_kiwibd_cfg *bdcfg);
// Clean up memory associated with block device
int lfs3_kiwibd_destroy(const struct lfs3_cfg *cfg);
// Read a block
int lfs3_kiwibd_read(const struct lfs3_cfg *cfg, lfs3_block_t block,
lfs3_off_t off, void *buffer, lfs3_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs3_kiwibd_prog(const struct lfs3_cfg *cfg, lfs3_block_t block,
lfs3_off_t off, const void *buffer, lfs3_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs3_kiwibd_erase(const struct lfs3_cfg *cfg, lfs3_block_t block);
// Sync the block device
int lfs3_kiwibd_sync(const struct lfs3_cfg *cfg);
/// Additional kiwibd features ///
// Get total amount of bytes read
lfs3_kiwibd_sio_t lfs3_kiwibd_readed(const struct lfs3_cfg *cfg);
// Get total amount of bytes programmed
lfs3_kiwibd_sio_t lfs3_kiwibd_proged(const struct lfs3_cfg *cfg);
// Get total amount of bytes erased
lfs3_kiwibd_sio_t lfs3_kiwibd_erased(const struct lfs3_cfg *cfg);
// Manually set amount of bytes read
int lfs3_kiwibd_setreaded(const struct lfs3_cfg *cfg,
lfs3_kiwibd_io_t readed);
// Manually set amount of bytes programmed
int lfs3_kiwibd_setproged(const struct lfs3_cfg *cfg,
lfs3_kiwibd_io_t proged);
// Manually set amount of bytes erased
int lfs3_kiwibd_seterased(const struct lfs3_cfg *cfg,
lfs3_kiwibd_io_t erased);
#endif

View File

@ -24,17 +24,17 @@ int lfs3_rambd_createcfg(const struct lfs3_cfg *cfg,
// allocate buffer?
if (bd->cfg->buffer) {
bd->buffer = bd->cfg->buffer;
bd->mem = bd->cfg->buffer;
} else {
bd->buffer = lfs3_malloc(cfg->block_size * cfg->block_count);
if (!bd->buffer) {
bd->mem = lfs3_malloc(cfg->block_size * cfg->block_count);
if (!bd->mem) {
LFS3_RAMBD_TRACE("lfs3_rambd_createcfg -> %d", LFS3_ERR_NOMEM);
return LFS3_ERR_NOMEM;
}
}
// zero for reproducibility
memset(bd->buffer, 0, cfg->block_size * cfg->block_count);
memset(bd->mem, 0, cfg->block_size * cfg->block_count);
LFS3_RAMBD_TRACE("lfs3_rambd_createcfg -> %d", 0);
return 0;
@ -60,7 +60,7 @@ int lfs3_rambd_destroy(const struct lfs3_cfg *cfg) {
// clean up memory
lfs3_rambd_t *bd = cfg->context;
if (!bd->cfg->buffer) {
lfs3_free(bd->buffer);
lfs3_free(bd->mem);
}
LFS3_RAMBD_TRACE("lfs3_rambd_destroy -> %d", 0);
return 0;
@ -80,7 +80,7 @@ int lfs3_rambd_read(const struct lfs3_cfg *cfg, lfs3_block_t block,
LFS3_ASSERT(off+size <= cfg->block_size);
// read data
memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size);
memcpy(buffer, &bd->mem[block*cfg->block_size + off], size);
LFS3_RAMBD_TRACE("lfs3_rambd_read -> %d", 0);
return 0;
@ -100,7 +100,7 @@ int lfs3_rambd_prog(const struct lfs3_cfg *cfg, lfs3_block_t block,
LFS3_ASSERT(off+size <= cfg->block_size);
// program data
memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size);
memcpy(&bd->mem[block*cfg->block_size + off], buffer, size);
LFS3_RAMBD_TRACE("lfs3_rambd_prog -> %d", 0);
return 0;

View File

@ -29,7 +29,7 @@ struct lfs3_rambd_cfg {
// rambd state
typedef struct lfs3_rambd {
uint8_t *buffer;
uint8_t *mem;
const struct lfs3_rambd_cfg *cfg;
} lfs3_rambd_t;