Skip to content

Commit 7050922

Browse files
committed
Added optional block-level caching
This adds caching of the most recent read/program blocks, allowing support of devices that don't have byte-level read+writes, along with reduced device access on devices that do support byte-level read+writes. Note: The current implementation is a bit eager to drop caches where it simplifies the cache layer. This layer is already complex enough. Note: It may be worthwhile to add a compile switch for caching to reduce code size, note sure. Note: This does add a dependency on malloc, which could have a porting layer, but I'm just using the functions from stdlib for now. These can be overwritten with noops if the user controls the system, and keeps things simple for now.
1 parent 789286a commit 7050922

File tree

4 files changed

+220
-24
lines changed

4 files changed

+220
-24
lines changed

lfs.c

Lines changed: 204 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,159 @@
99

1010
#include <string.h>
1111
#include <stdbool.h>
12+
#include <stdlib.h>
1213

1314

1415
/// Block device operations ///
16+
static int lfs_bd_flush(lfs_t *lfs) {
17+
if (lfs->pcache.off != -1) {
18+
int err = lfs->cfg->prog(lfs->cfg, lfs->pcache.block,
19+
lfs->pcache.off, lfs->cfg->prog_size,
20+
lfs->pcache.buffer);
21+
if (err) {
22+
return err;
23+
}
24+
25+
lfs->pcache.off = -1;
26+
}
27+
28+
return 0;
29+
}
30+
1531
static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
1632
lfs_off_t off, lfs_size_t size, void *buffer) {
17-
return lfs->cfg->read(lfs->cfg, block, off, size, buffer);
33+
uint8_t *data = buffer;
34+
35+
// flush overlapping programs
36+
while (size > 0) {
37+
if (block == lfs->pcache.block && off >= lfs->pcache.off &&
38+
off < lfs->pcache.off + lfs->cfg->prog_size) {
39+
// is already in cache?
40+
lfs_size_t diff = lfs_min(size,
41+
lfs->cfg->prog_size - (off-lfs->pcache.off));
42+
memcpy(data, &lfs->pcache.buffer[off-lfs->pcache.off], diff);
43+
44+
data += diff;
45+
off += diff;
46+
size -= diff;
47+
continue;
48+
} else if (block == lfs->rcache.block && off >= lfs->rcache.off &&
49+
off < lfs->rcache.off + lfs->cfg->read_size) {
50+
// is already in cache?
51+
lfs_size_t diff = lfs_min(size,
52+
lfs->cfg->read_size - (off-lfs->rcache.off));
53+
memcpy(data, &lfs->rcache.buffer[off-lfs->rcache.off], diff);
54+
55+
data += diff;
56+
off += diff;
57+
size -= diff;
58+
continue;
59+
}
60+
61+
// write out pending programs
62+
int err = lfs_bd_flush(lfs);
63+
if (err) {
64+
return err;
65+
}
66+
67+
if (off % lfs->cfg->read_size == 0 &&
68+
size >= lfs->cfg->read_size) {
69+
// bypass cache?
70+
lfs_size_t diff = size - (size % lfs->cfg->read_size);
71+
int err = lfs->cfg->read(lfs->cfg, block, off, diff, data);
72+
if (err) {
73+
return err;
74+
}
75+
76+
data += diff;
77+
off += diff;
78+
size -= diff;
79+
continue;
80+
}
81+
82+
// load to cache, first condition can no longer fail
83+
lfs->rcache.block = block;
84+
lfs->rcache.off = off - (off % lfs->cfg->read_size);
85+
// TODO remove reading, should be unnecessary
86+
err = lfs->cfg->read(lfs->cfg, lfs->rcache.block,
87+
lfs->rcache.off, lfs->cfg->read_size,
88+
lfs->rcache.buffer);
89+
if (err) {
90+
return err;
91+
}
92+
}
93+
94+
return 0;
1895
}
1996

2097
static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block,
2198
lfs_off_t off, lfs_size_t size, const void *buffer) {
22-
return lfs->cfg->prog(lfs->cfg, block, off, size, buffer);
99+
const uint8_t *data = buffer;
100+
101+
if (block == lfs->rcache.block) {
102+
// invalidate read cache
103+
lfs->rcache.off = -1;
104+
}
105+
106+
while (size > 0) {
107+
if (block == lfs->pcache.block && off >= lfs->pcache.off &&
108+
off < lfs->pcache.off + lfs->cfg->prog_size) {
109+
// is already in cache?
110+
lfs_size_t diff = lfs_min(size,
111+
lfs->cfg->prog_size - (off-lfs->pcache.off));
112+
memcpy(&lfs->pcache.buffer[off-lfs->pcache.off], data, diff);
113+
114+
data += diff;
115+
off += diff;
116+
size -= diff;
117+
continue;
118+
}
119+
120+
// write out pending programs
121+
int err = lfs_bd_flush(lfs);
122+
if (err) {
123+
return err;
124+
}
125+
126+
if (off % lfs->cfg->prog_size == 0 &&
127+
size >= lfs->cfg->prog_size) {
128+
// bypass cache?
129+
lfs_size_t diff = size - (size % lfs->cfg->prog_size);
130+
int err = lfs->cfg->prog(lfs->cfg, block, off, diff, data);
131+
if (err) {
132+
return err;
133+
}
134+
135+
data += diff;
136+
off += diff;
137+
size -= diff;
138+
continue;
139+
}
140+
141+
// prepare cache, first condition can no longer fail
142+
lfs->pcache.block = block;
143+
lfs->pcache.off = off - (off % lfs->cfg->prog_size);
144+
err = lfs->cfg->read(lfs->cfg, lfs->pcache.block,
145+
lfs->pcache.off, lfs->cfg->prog_size,
146+
lfs->pcache.buffer);
147+
if (err) {
148+
return err;
149+
}
150+
}
151+
152+
return 0;
23153
}
24154

25155
static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
26156
return lfs->cfg->erase(lfs->cfg, block);
27157
}
28158

29159
static int lfs_bd_sync(lfs_t *lfs) {
160+
int err = lfs_bd_flush(lfs);
161+
if (err) {
162+
return err;
163+
}
164+
30165
return lfs->cfg->sync(lfs->cfg);
31166
}
32167

@@ -41,11 +176,9 @@ static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
41176
return err;
42177
}
43178

44-
if (c != *data) {
179+
if (c != data[i]) {
45180
return false;
46181
}
47-
48-
data += 1;
49182
}
50183

51184
return true;
@@ -452,13 +585,13 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
452585
}
453586

454587
while (off < lfs->cfg->block_size-4) {
455-
uint8_t data;
456-
int err = lfs_bd_read(lfs, dir->pair[0], off, 1, &data);
588+
uint8_t data = 0xff;
589+
crc = lfs_crc(crc, 1, &data);
590+
err = lfs_bd_prog(lfs, dir->pair[0], off, 1, &data);
457591
if (err) {
458592
return err;
459593
}
460594

461-
crc = lfs_crc(crc, 1, &data);
462595
off += 1;
463596
}
464597

@@ -512,13 +645,14 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
512645
}
513646

514647
while (woff < lfs->cfg->block_size-4) {
515-
uint8_t data;
516-
int err = lfs_bd_read(lfs, dir->pair[0], woff, 1, &data);
648+
uint8_t data = 0xff;
649+
crc = lfs_crc(crc, 1, &data);
650+
err = lfs_bd_prog(lfs, dir->pair[0], woff, 1, &data);
517651
if (err) {
518652
return err;
519653
}
520654

521-
crc = lfs_crc(crc, 1, &data);
655+
522656
woff += 1;
523657
}
524658

@@ -618,6 +752,7 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
618752
}
619753

620754
dir->off = sizeof(dir->d);
755+
continue;
621756
}
622757

623758
int err = lfs_bd_read(lfs, dir->pair[0], dir->off,
@@ -986,17 +1121,59 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
9861121

9871122

9881123
/// Generic filesystem operations ///
989-
int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
990-
lfs->cfg = config;
1124+
static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
1125+
lfs->cfg = cfg;
9911126
lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
1127+
lfs->rcache.off = -1;
1128+
lfs->pcache.off = -1;
1129+
1130+
if (lfs->cfg->read_buffer) {
1131+
lfs->rcache.buffer = lfs->cfg->read_buffer;
1132+
} else {
1133+
lfs->rcache.buffer = malloc(lfs->cfg->read_size);
1134+
if (!lfs->rcache.buffer) {
1135+
return LFS_ERROR_NO_MEM;
1136+
}
1137+
}
1138+
1139+
if (lfs->cfg->prog_buffer) {
1140+
lfs->pcache.buffer = lfs->cfg->prog_buffer;
1141+
} else {
1142+
lfs->pcache.buffer = malloc(lfs->cfg->prog_size);
1143+
if (!lfs->pcache.buffer) {
1144+
return LFS_ERROR_NO_MEM;
1145+
}
1146+
}
1147+
1148+
return 0;
1149+
}
1150+
1151+
static int lfs_deinit(lfs_t *lfs) {
1152+
// Free allocated memory
1153+
if (!lfs->cfg->read_buffer) {
1154+
free(lfs->rcache.buffer);
1155+
}
1156+
1157+
if (!lfs->cfg->prog_buffer) {
1158+
free(lfs->pcache.buffer);
1159+
}
1160+
1161+
return 0;
1162+
}
1163+
1164+
int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
1165+
int err = lfs_init(lfs, cfg);
1166+
if (err) {
1167+
return err;
1168+
}
9921169

9931170
// Create free list
9941171
lfs->free.begin = 0;
9951172
lfs->free.end = lfs->cfg->block_count-1;
9961173

9971174
// Create superblock dir
9981175
lfs_dir_t superdir;
999-
int err = lfs_dir_alloc(lfs, &superdir);
1176+
err = lfs_dir_alloc(lfs, &superdir);
10001177
if (err) {
10011178
return err;
10021179
}
@@ -1044,16 +1221,23 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
10441221
}
10451222

10461223
// sanity check that fetch works
1047-
return lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
1224+
err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
1225+
if (err) {
1226+
return err;
1227+
}
1228+
1229+
return lfs_deinit(lfs);
10481230
}
10491231

1050-
int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
1051-
lfs->cfg = config;
1052-
lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
1232+
int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
1233+
int err = lfs_init(lfs, cfg);
1234+
if (err) {
1235+
return err;
1236+
}
10531237

10541238
lfs_dir_t dir;
10551239
lfs_superblock_t superblock;
1056-
int err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
1240+
err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
10571241
if (!err) {
10581242
err = lfs_bd_read(lfs, dir.pair[0],
10591243
sizeof(dir.d), sizeof(superblock.d), &superblock.d);
@@ -1078,8 +1262,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
10781262
}
10791263

10801264
int lfs_unmount(lfs_t *lfs) {
1081-
// Do nothing for now
1082-
return 0;
1265+
return lfs_deinit(lfs);
10831266
}
10841267

10851268
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {

lfs.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum lfs_error {
2020
LFS_ERROR_IS_DIR = -7,
2121
LFS_ERROR_INVALID = -8,
2222
LFS_ERROR_NO_SPACE = -9,
23+
LFS_ERROR_NO_MEM = -10,
2324
};
2425

2526
enum lfs_type {
@@ -74,6 +75,12 @@ struct lfs_config {
7475

7576
// Number of erasable blocks on the device.
7677
lfs_size_t block_count;
78+
79+
// Optional, statically allocated read buffer. Must be read sized.
80+
void *read_buffer;
81+
82+
// Optional, statically allocated program buffer. Must be program sized.
83+
void *prog_buffer;
7784
};
7885

7986
// File info structure
@@ -159,6 +166,12 @@ typedef struct lfs {
159166
lfs_block_t end;
160167
} free;
161168

169+
struct {
170+
lfs_block_t block;
171+
lfs_off_t off;
172+
uint8_t *buffer;
173+
} rcache, pcache;
174+
162175
uint32_t lookahead[LFS_CFG_LOOKAHEAD/32];
163176
} lfs_t;
164177

lfs_util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define LFS_UTIL_H
99

1010
#include "lfs_config.h"
11+
#include <stdlib.h>
1112

1213

1314
// Builtin functions
@@ -34,5 +35,4 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) {
3435
uint32_t lfs_crc(uint32_t crc, lfs_size_t size, const void *buffer);
3536

3637

37-
3838
#endif

tests/template.fmt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ lfs_size_t rsize;
5555
uintmax_t res;
5656

5757
#ifndef LFS_READ_SIZE
58-
#define LFS_READ_SIZE 1
58+
#define LFS_READ_SIZE 64
5959
#endif
6060

6161
#ifndef LFS_PROG_SIZE
62-
#define LFS_PROG_SIZE 1
62+
#define LFS_PROG_SIZE 64
6363
#endif
6464

6565
#ifndef LFS_BLOCK_SIZE

0 commit comments

Comments
 (0)