Skip to content

Commit e273a82

Browse files
authored
Merge pull request #487 from littlefs-project/fix-alloc-reset-modulus
Fix several wear-leveling issues found in lfs_alloc_reset
2 parents 1dc6ae9 + 0aba71d commit e273a82

File tree

2 files changed

+27
-14
lines changed

2 files changed

+27
-14
lines changed

lfs.c

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -452,14 +452,16 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
452452
return 0;
453453
}
454454

455+
// indicate allocated blocks have been committed into the filesystem, this
456+
// is to prevent blocks from being garbage collected in the middle of a
457+
// commit operation
455458
static void lfs_alloc_ack(lfs_t *lfs) {
456459
lfs->free.ack = lfs->cfg->block_count;
457460
}
458461

459-
// Invalidate the lookahead buffer. This is done during mounting and
460-
// failed traversals
461-
static void lfs_alloc_reset(lfs_t *lfs) {
462-
lfs->free.off = lfs->seed % lfs->cfg->block_size;
462+
// drop the lookahead buffer, this is done during mounting and failed
463+
// traversals in order to avoid invalid lookahead state
464+
static void lfs_alloc_drop(lfs_t *lfs) {
463465
lfs->free.size = 0;
464466
lfs->free.i = 0;
465467
lfs_alloc_ack(lfs);
@@ -505,7 +507,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
505507
memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
506508
int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true);
507509
if (err) {
508-
lfs_alloc_reset(lfs);
510+
lfs_alloc_drop(lfs);
509511
return err;
510512
}
511513
}
@@ -870,8 +872,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
870872
ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31;
871873

872874
// toss our crc into the filesystem seed for
873-
// pseudorandom numbers
874-
lfs->seed ^= crc;
875+
// pseudorandom numbers, note we use another crc here
876+
// as a collection function because it is sufficiently
877+
// random and convenient
878+
lfs->seed = lfs_crc(lfs->seed, &crc, sizeof(crc));
875879

876880
// update with what's found so far
877881
besttag = tempbesttag;
@@ -1261,12 +1265,13 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
12611265
}
12621266

12631267
static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
1264-
const lfs_off_t off1 = commit->off;
1265-
const uint32_t crc1 = commit->crc;
12661268
// align to program units
1267-
const lfs_off_t end = lfs_alignup(off1 + 2*sizeof(uint32_t),
1269+
const lfs_off_t end = lfs_alignup(commit->off + 2*sizeof(uint32_t),
12681270
lfs->cfg->prog_size);
12691271

1272+
lfs_off_t off1 = 0;
1273+
uint32_t crc1 = 0;
1274+
12701275
// create crc tags to fill up remainder of commit, note that
12711276
// padding is not crced, which lets fetches skip padding but
12721277
// makes committing a bit more complicated
@@ -1302,6 +1307,12 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
13021307
return err;
13031308
}
13041309

1310+
// keep track of non-padding checksum to verify
1311+
if (off1 == 0) {
1312+
off1 = commit->off + sizeof(uint32_t);
1313+
crc1 = commit->crc;
1314+
}
1315+
13051316
commit->off += sizeof(tag)+lfs_tag_size(tag);
13061317
commit->ptag = tag ^ ((lfs_tag_t)reset << 31);
13071318
commit->crc = 0xffffffff; // reset crc for next "commit"
@@ -1315,7 +1326,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
13151326

13161327
// successful commit, check checksums to make sure
13171328
lfs_off_t off = commit->begin;
1318-
lfs_off_t noff = off1 + sizeof(uint32_t);
1329+
lfs_off_t noff = off1;
13191330
while (off < end) {
13201331
uint32_t crc = 0xffffffff;
13211332
for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) {
@@ -3797,8 +3808,10 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
37973808
lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag);
37983809
lfs->gdisk = lfs->gstate;
37993810

3800-
// setup free lookahead
3801-
lfs_alloc_reset(lfs);
3811+
// setup free lookahead, to distribute allocations uniformly across
3812+
// boots, we start the allocator at a random location
3813+
lfs->free.off = lfs->seed % lfs->cfg->block_count;
3814+
lfs_alloc_drop(lfs);
38023815

38033816
LFS_TRACE("lfs_mount -> %d", 0);
38043817
return 0;

scripts/readtree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def main(args):
106106
struct.unpack('<HH', superblock[1].data[0:4].ljust(4, b'\xff'))))
107107
print("%-47s%s" % ("littlefs v%s.%s" % version,
108108
"data (truncated, if it fits)"
109-
if not any([args.no_truncate, args.tags, args.log, args.all]) else ""))
109+
if not any([args.no_truncate, args.log, args.all]) else ""))
110110

111111
# print gstate
112112
print("gstate 0x%s" % ''.join('%02x' % c for c in gstate))

0 commit comments

Comments
 (0)