Modified global state format to work with new tag format

The main difference here is a change from encoding "hasorphans" and
"hasmove" bits in the tag itself. This worked with the old format, but
in the new format the space these bits take up must be consistent for
each tag type. The tradeoff is that the new tag format allows for up to
256 different global states which may be useful in the future (for
example, a global free list).

The new format encodes this info in the data blob, using an additional
word of storage. This word is actually formatted the same as though it
was a tag, which simplified internal handling and may allow other tag
types in the future.

Format for global state:
[----                          96 bits                         ----]
[1|- 11 -|- 10 -|- 10 -|---                 64                  ---]
 ^    ^      ^      ^                        ^- move dir pair
 |    |      |      \-------------------------- unused, must be 0s
 |    |      \--------------------------------- move id
 |    \---------------------------------------- type, 0xfff for move
 \--------------------------------------------- has orphans

This also included another iteration over globals (renamed to gstate)
with some simplifications to how globals are handled.
This commit is contained in:
Christopher Haster
2019-01-04 17:23:36 -06:00
parent b989b4a89f
commit 66d751544d
3 changed files with 209 additions and 240 deletions

397
lfs.c
View File

@@ -331,56 +331,48 @@ struct lfs_diskoff {
lfs_off_t off; lfs_off_t off;
}; };
// operations on set of globals // operations on global state
static inline void lfs_global_xor(struct lfs_globals *a, static inline void lfs_gstate_xor(struct lfs_gstate *a,
const struct lfs_globals *b) { const struct lfs_gstate *b) {
uint32_t *a32 = (uint32_t *)a; for (int i = 0; i < 3; i++) {
const uint32_t *b32 = (const uint32_t *)b; ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i];
for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) {
a32[i] ^= b32[i];
} }
} }
static inline bool lfs_global_iszero(const struct lfs_globals *a) { static inline bool lfs_gstate_iszero(const struct lfs_gstate *a) {
const uint32_t *a32 = (const uint32_t *)a; for (int i = 0; i < 3; i++) {
for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) { if (((uint32_t*)a)[i] != 0) {
if (a32[i] != 0) {
return false; return false;
} }
} }
return true; return true;
} }
static inline void lfs_global_zero(struct lfs_globals *a) { static inline bool lfs_gstate_hasorphans(const struct lfs_gstate *a) {
lfs_global_xor(a, a); return lfs_tag_size(a->tag);
} }
static inline void lfs_global_fromle32(struct lfs_globals *a) { static inline uint8_t lfs_gstate_getorphans(const struct lfs_gstate *a) {
lfs_pair_fromle32(a->pair); return lfs_tag_size(a->tag);
a->id = lfs_fromle16(a->id);
} }
static inline void lfs_global_tole32(struct lfs_globals *a) { static inline bool lfs_gstate_hasmove(const struct lfs_gstate *a) {
lfs_pair_tole32(a->pair); return lfs_tag_type1(a->tag);
a->id = lfs_tole16(a->id);
} }
static inline void lfs_global_move(lfs_t *lfs, static inline bool lfs_gstate_hasmovehere(const struct lfs_gstate *a,
bool hasmove, const lfs_block_t pair[2], uint16_t id) { const lfs_block_t *pair) {
lfs_global_fromle32(&lfs->locals); return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0;
lfs_global_xor(&lfs->locals, &lfs->globals);
lfs->globals.hasmove = hasmove;
lfs->globals.pair[0] = pair[0];
lfs->globals.pair[1] = pair[1];
lfs->globals.id = id;
lfs_global_xor(&lfs->locals, &lfs->globals);
lfs_global_tole32(&lfs->locals);
} }
static inline void lfs_global_orphans(lfs_t *lfs, int8_t orphans) { static inline void lfs_gstate_fromle32(struct lfs_gstate *a) {
lfs->locals.orphans ^= (lfs->globals.orphans == 0); for (int i = 0; i < 3; i++) {
lfs->globals.orphans += orphans; ((uint32_t*)a)[i] = lfs_fromle32(((uint32_t*)a)[i]);
lfs->locals.orphans ^= (lfs->globals.orphans == 0); }
}
static inline void lfs_gstate_tole32(struct lfs_gstate *a) {
lfs_gstate_fromle32(a);
} }
// other endianness operations // other endianness operations
@@ -427,6 +419,9 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
lfs_mdir_t *parent); lfs_mdir_t *parent);
static int lfs_fs_relocate(lfs_t *lfs, static int lfs_fs_relocate(lfs_t *lfs,
const lfs_block_t oldpair[2], lfs_block_t newpair[2]); const lfs_block_t oldpair[2], lfs_block_t newpair[2]);
static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans);
static void lfs_fs_prepmove(lfs_t *lfs,
uint16_t id, const lfs_block_t pair[2]);
static int lfs_fs_forceconsistency(lfs_t *lfs); static int lfs_fs_forceconsistency(lfs_t *lfs);
static int lfs_deinit(lfs_t *lfs); static int lfs_deinit(lfs_t *lfs);
@@ -501,9 +496,8 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
lfs_tag_t ntag = dir->etag; lfs_tag_t ntag = dir->etag;
lfs_stag_t gdiff = 0; lfs_stag_t gdiff = 0;
if (lfs->globals.hasmove && if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair) &&
lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0 && lfs_tag_id(gtag) <= lfs_tag_id(lfs->gstate.tag)) {
lfs_tag_id(gtag) <= lfs->globals.id) {
// synthetic moves // synthetic moves
gdiff -= LFS_MKTAG(0, 1, 0); gdiff -= LFS_MKTAG(0, 1, 0);
} }
@@ -582,13 +576,12 @@ static int lfs_dir_traverse_filter(void *p,
static int lfs_dir_traverse(lfs_t *lfs, static int lfs_dir_traverse(lfs_t *lfs,
const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag,
const struct lfs_mattr *attrs, int attrcount, const struct lfs_mattr *attrs, int attrcount, bool hasseenmove,
lfs_tag_t tmask, lfs_tag_t ttag, lfs_tag_t tmask, lfs_tag_t ttag,
uint16_t begin, uint16_t end, int16_t diff, uint16_t begin, uint16_t end, int16_t diff,
int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) {
// iterate over directory and attrs, we iterate over attrs in reverse order // iterate over directory and attrs
// which lets us "append" commits while (true) {
while (off+lfs_tag_dsize(ptag) < dir->off || attrcount > 0) {
lfs_tag_t tag; lfs_tag_t tag;
const void *buffer; const void *buffer;
struct lfs_diskoff disk; struct lfs_diskoff disk;
@@ -606,7 +599,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
disk.off = off+sizeof(lfs_tag_t); disk.off = off+sizeof(lfs_tag_t);
buffer = &disk; buffer = &disk;
ptag = tag; ptag = tag;
} else { } else if (attrcount > 0) {
const struct lfs_mattr *a = attrs; const struct lfs_mattr *a = attrs;
for (int j = 0; j < attrcount-1; j++) { for (int j = 0; j < attrcount-1; j++) {
a = a->next; a = a->next;
@@ -615,6 +608,15 @@ static int lfs_dir_traverse(lfs_t *lfs,
tag = a->tag; tag = a->tag;
buffer = a->buffer; buffer = a->buffer;
attrcount -= 1; attrcount -= 1;
} else if (!hasseenmove &&
lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
// Wait, we have pending move? Handle this here (we need to
// or else we risk letting moves fall out of date)
tag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0);
buffer = NULL;
hasseenmove = true;
} else {
return 0;
} }
lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0);
@@ -627,7 +629,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
if (lfs_tag_id(tmask) != 0) { if (lfs_tag_id(tmask) != 0) {
// scan for duplicates and update tag based on creates/deletes // scan for duplicates and update tag based on creates/deletes
int filter = lfs_dir_traverse(lfs, int filter = lfs_dir_traverse(lfs,
dir, off, ptag, attrs, attrcount, dir, off, ptag, attrs, attrcount, hasseenmove,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
lfs_dir_traverse_filter, &tag); lfs_dir_traverse_filter, &tag);
if (filter < 0) { if (filter < 0) {
@@ -649,7 +651,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
uint16_t fromid = lfs_tag_size(tag); uint16_t fromid = lfs_tag_size(tag);
uint16_t toid = lfs_tag_id(tag); uint16_t toid = lfs_tag_id(tag);
int err = lfs_dir_traverse(lfs, int err = lfs_dir_traverse(lfs,
buffer, 0, 0xffffffff, NULL, 0, buffer, 0, 0xffffffff, NULL, 0, true,
LFS_MKTAG(0x600, 0x3ff, 0), LFS_MKTAG(0x600, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0),
fromid, fromid+1, toid-fromid+diff, fromid, fromid+1, toid-fromid+diff,
@@ -672,8 +674,6 @@ static int lfs_dir_traverse(lfs_t *lfs,
} }
} }
} }
return 0;
} }
static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
@@ -824,10 +824,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
} }
} else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) {
tempsplit = (lfs_tag_chunk(tag) & 1); tempsplit = (lfs_tag_chunk(tag) & 1);
err = lfs_bd_read(lfs, err = lfs_bd_read(lfs,
&lfs->pcache, &lfs->rcache, lfs->cfg->block_size, &lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
dir->pair[0], off+sizeof(tag), dir->pair[0], off+sizeof(tag), &temptail, 8);
&temptail, sizeof(temptail));
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
dir->erased = false; dir->erased = false;
@@ -863,12 +863,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// consider what we have good enough // consider what we have good enough
if (dir->off > 0) { if (dir->off > 0) {
// synthetic move // synthetic move
if (lfs->globals.hasmove && if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair)) {
lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { if (lfs_tag_id(lfs->gstate.tag) == lfs_tag_id(besttag)) {
if (lfs->globals.id == lfs_tag_id(besttag)) {
besttag |= 0x80000000; besttag |= 0x80000000;
} else if (besttag != -1 && } else if (besttag != -1 &&
lfs->globals.id < lfs_tag_id(besttag)) { lfs_tag_id(lfs->gstate.tag) < lfs_tag_id(besttag)) {
besttag -= LFS_MKTAG(0, 1, 0); besttag -= LFS_MKTAG(0, 1, 0);
} }
} }
@@ -904,20 +903,18 @@ static int lfs_dir_fetch(lfs_t *lfs,
return lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL); return lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL);
} }
static int lfs_dir_getglobals(lfs_t *lfs, const lfs_mdir_t *dir, static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir,
struct lfs_globals *globals) { struct lfs_gstate *gstate) {
struct lfs_globals locals; struct lfs_gstate temp;
lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0, 0), lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0),
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals); LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp);
if (res < 0 && res != LFS_ERR_NOENT) { if (res < 0 && res != LFS_ERR_NOENT) {
return res; return res;
} }
if (res != LFS_ERR_NOENT) { if (res != LFS_ERR_NOENT) {
locals.hasmove = (lfs_tag_type3(res) & 2); // xor together to find resulting gstate
locals.orphans = (lfs_tag_type3(res) & 1); lfs_gstate_xor(gstate, &temp);
// xor together to find resulting globals
lfs_global_xor(globals, &locals);
} }
return 0; return 0;
@@ -1158,13 +1155,6 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
return 0; return 0;
} }
static int lfs_dir_commitglobals(lfs_t *lfs, struct lfs_commit *commit,
struct lfs_globals *globals) {
return lfs_dir_commitattr(lfs, commit,
LFS_MKTAG(LFS_TYPE_GLOBALS + 2*globals->hasmove + globals->orphans,
0x3ff, 10), globals);
}
static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// align to program units // align to program units
lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t),
@@ -1181,8 +1171,8 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
// build crc tag // build crc tag
bool reset = ~lfs_frombe32(tag) >> 31; bool reset = ~lfs_frombe32(tag) >> 31;
tag = LFS_MKTAG(LFS_TYPE_CRC + reset, tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff,
0x3ff, off - (commit->off+sizeof(lfs_tag_t))); off - (commit->off+sizeof(lfs_tag_t)));
// write out crc // write out crc
uint32_t footer[2]; uint32_t footer[2];
@@ -1267,23 +1257,24 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
return 0; return 0;
} }
static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, const lfs_mdir_t *tail) { static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) {
// steal tail
dir->tail[0] = tail->tail[0];
dir->tail[1] = tail->tail[1];
dir->split = tail->split;
// steal state // steal state
int err = lfs_dir_getglobals(lfs, tail, &lfs->locals); int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta);
if (err) { if (err) {
return err; return err;
} }
// update pred's tail // steal tail
return lfs_dir_commit(lfs, dir, lfs_pair_tole32(tail->tail);
LFS_MKATTR(LFS_TYPE_TAIL + dir->split, err = lfs_dir_commit(lfs, dir,
0x3ff, dir->tail, sizeof(dir->tail), LFS_MKATTR(LFS_TYPE_TAIL + tail->split, 0x3ff, tail->tail, 8,
NULL)); NULL));
lfs_pair_fromle32(tail->tail);
if (err) {
return err;
}
return 0;
} }
static int lfs_dir_split(lfs_t *lfs, static int lfs_dir_split(lfs_t *lfs,
@@ -1341,7 +1332,6 @@ static int lfs_dir_compact(lfs_t *lfs,
lfs_mdir_t *source, uint16_t begin, uint16_t end) { lfs_mdir_t *source, uint16_t begin, uint16_t end) {
// save some state in case block is bad // save some state in case block is bad
const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]};
struct lfs_globals globals, locals;
bool relocated = false; bool relocated = false;
bool exhausted = false; bool exhausted = false;
@@ -1349,7 +1339,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// find size // find size
lfs_size_t size = 0; lfs_size_t size = 0;
int err = lfs_dir_traverse(lfs, int err = lfs_dir_traverse(lfs,
source, 0, 0xffffffff, attrs, attrcount, source, 0, 0xffffffff, attrs, attrcount, false,
LFS_MKTAG(0x400, 0x3ff, 0), LFS_MKTAG(0x400, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
begin, end, -begin, begin, end, -begin,
@@ -1358,10 +1348,10 @@ static int lfs_dir_compact(lfs_t *lfs,
return err; return err;
} }
// space is complicated, we need room for tail, crc, globals, // space is complicated, we need room for tail, crc, gstate,
// cleanup delete, and we cap at half a block to give room // cleanup delete, and we cap at half a block to give room
// for metadata updates // for metadata updates
if (size <= lfs_min(lfs->cfg->block_size - 38, if (size <= lfs_min(lfs->cfg->block_size - 36,
lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size))) { lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size))) {
break; break;
} }
@@ -1376,7 +1366,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// if we fail to split, we may be able to overcompact, unless // if we fail to split, we may be able to overcompact, unless
// we're too big for even the full block, in which case our // we're too big for even the full block, in which case our
// only option is to error // only option is to error
if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 38) { if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 36) {
break; break;
} }
return err; return err;
@@ -1424,9 +1414,7 @@ static int lfs_dir_compact(lfs_t *lfs,
if (true) { if (true) {
// There's nothing special about our global delta, so feed it into // There's nothing special about our global delta, so feed it into
// our local global delta // our local global delta
globals = lfs->globals; int err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta);
locals = lfs->locals;
int err = lfs_dir_getglobals(lfs, dir, &locals);
if (err) { if (err) {
return err; return err;
} }
@@ -1465,7 +1453,7 @@ static int lfs_dir_compact(lfs_t *lfs,
// traverse the directory, this time writing out all unique tags // traverse the directory, this time writing out all unique tags
err = lfs_dir_traverse(lfs, err = lfs_dir_traverse(lfs,
source, 0, 0xffffffff, attrs, attrcount, source, 0, 0xffffffff, attrs, attrcount, false,
LFS_MKTAG(0x400, 0x3ff, 0), LFS_MKTAG(0x400, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, 0, 0), LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
begin, end, -begin, begin, end, -begin,
@@ -1478,27 +1466,32 @@ static int lfs_dir_compact(lfs_t *lfs,
return err; return err;
} }
if (!relocated && !lfs_global_iszero(&locals)) { // commit tail, which may be new after last size check
// commit any globals, unless we're relocating, if (!lfs_pair_isnull(dir->tail)) {
// in which case our parent will steal our globals lfs_pair_tole32(dir->tail);
err = lfs_dir_commitglobals(lfs, &commit, &locals); err = lfs_dir_commitattr(lfs, &commit,
LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8),
dir->tail);
lfs_pair_fromle32(dir->tail);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
goto relocate; goto relocate;
} }
return err; return err;
} }
lfs_global_zero(&locals);
} }
if (!lfs_pair_isnull(dir->tail)) { // need to update gstate now that we've acknowledged moves
// commit tail, which may be new after last size check if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
lfs_pair_tole32(dir->tail); lfs_fs_prepmove(lfs, 0x3ff, NULL);
}
if (!relocated && !lfs_gstate_iszero(&lfs->gdelta)) {
// commit any globals, unless we're relocating,
// in which case our parent will steal our globals
err = lfs_dir_commitattr(lfs, &commit, err = lfs_dir_commitattr(lfs, &commit,
LFS_MKTAG(LFS_TYPE_TAIL + dir->split, LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff,
0x3ff, sizeof(dir->tail)), dir->tail); sizeof(lfs->gdelta)), &lfs->gdelta);
lfs_pair_fromle32(dir->tail);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
goto relocate; goto relocate;
@@ -1547,11 +1540,10 @@ relocate:
continue; continue;
} }
// successful commit, update globals if (!relocated) {
lfs->globals = globals; lfs->gstate = lfs->gpending;
lfs->locals = locals; lfs->gdelta = (struct lfs_gstate){0};
} else {
if (relocated) {
// update references if we relocated // update references if we relocated
LFS_DEBUG("Relocating %"PRIu32" %"PRIu32" to %"PRIu32" %"PRIu32, LFS_DEBUG("Relocating %"PRIu32" %"PRIu32" to %"PRIu32" %"PRIu32,
oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
@@ -1566,47 +1558,35 @@ relocate:
static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
const struct lfs_mattr *attrs) { const struct lfs_mattr *attrs) {
// check for globals work // calculate changes to the directory
struct lfs_mattr cancelattr;
struct lfs_globals cancels;
lfs_global_zero(&cancels);
if (lfs->globals.hasmove &&
lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) {
// Wait, we have the move? Just cancel this out here
// We need to, or else the move can become outdated
cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.id, 0);
cancelattr.next = attrs;
attrs = &cancelattr;
cancels.hasmove = lfs->globals.hasmove;
cancels.pair[0] = lfs->globals.pair[0];
cancels.pair[1] = lfs->globals.pair[1];
cancels.id = lfs->globals.id;
lfs_global_fromle32(&lfs->locals);
lfs_global_xor(&lfs->locals, &cancels);
lfs_global_tole32(&lfs->locals);
}
struct lfs_globals globals = lfs->globals;
struct lfs_globals locals = lfs->locals;
// calculate new directory size
lfs_tag_t deletetag = 0xffffffff; lfs_tag_t deletetag = 0xffffffff;
lfs_tag_t createtag = 0xffffffff; lfs_tag_t createtag = 0xffffffff;
int attrcount = 0; int attrcount = 0;
for (const struct lfs_mattr *a = attrs; a; a = a->next) { for (const struct lfs_mattr *a = attrs; a; a = a->next) {
if (lfs_tag_type3(a->tag) == LFS_TYPE_CREATE) { if (lfs_tag_type3(a->tag) == LFS_TYPE_CREATE) {
dir->count += 1;
createtag = a->tag; createtag = a->tag;
dir->count += 1;
} else if (lfs_tag_type3(a->tag) == LFS_TYPE_DELETE) { } else if (lfs_tag_type3(a->tag) == LFS_TYPE_DELETE) {
deletetag = a->tag;
LFS_ASSERT(dir->count > 0); LFS_ASSERT(dir->count > 0);
dir->count -= 1; dir->count -= 1;
deletetag = a->tag; } else if (lfs_tag_type1(a->tag) == LFS_TYPE_TAIL) {
dir->tail[0] = ((lfs_block_t*)a->buffer)[0];
dir->tail[1] = ((lfs_block_t*)a->buffer)[1];
dir->split = (lfs_tag_chunk(a->tag) & 1);
lfs_pair_fromle32(dir->tail);
} }
attrcount += 1; attrcount += 1;
} }
// do we have a pending move?
if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
deletetag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0);
LFS_ASSERT(dir->count > 0);
dir->count -= 1;
}
// should we actually drop the directory block? // should we actually drop the directory block?
if (lfs_tag_isvalid(deletetag) && dir->count == 0) { if (lfs_tag_isvalid(deletetag) && dir->count == 0) {
lfs_mdir_t pdir; lfs_mdir_t pdir;
@@ -1635,7 +1615,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
// traverse attrs that need to be written out // traverse attrs that need to be written out
lfs_pair_tole32(dir->tail); lfs_pair_tole32(dir->tail);
int err = lfs_dir_traverse(lfs, int err = lfs_dir_traverse(lfs,
dir, dir->off, dir->etag, attrs, attrcount, dir, dir->off, dir->etag, attrs, attrcount, false,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){
lfs, &commit}); lfs, &commit});
@@ -1647,22 +1627,27 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
return err; return err;
} }
// need to update gstate now that we've acknowledged moves
if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) {
lfs_fs_prepmove(lfs, 0x3ff, NULL);
}
// commit any global diffs if we have any // commit any global diffs if we have any
if (!lfs_global_iszero(&locals)) { if (!lfs_gstate_iszero(&lfs->gdelta)) {
err = lfs_dir_getglobals(lfs, dir, &locals); err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta);
if (err) { if (err) {
return err; return err;
} }
err = lfs_dir_commitglobals(lfs, &commit, &locals); err = lfs_dir_commitattr(lfs, &commit,
LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff,
sizeof(lfs->gdelta)), &lfs->gdelta);
if (err) { if (err) {
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
goto compact; goto compact;
} }
return err; return err;
} }
lfs_global_zero(&locals);
} }
// finalize commit with the crc // finalize commit with the crc
@@ -1677,9 +1662,9 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
// successful commit, update dir // successful commit, update dir
dir->off = commit.off; dir->off = commit.off;
dir->etag = commit.ptag; dir->etag = commit.ptag;
// successful commit, update globals // successful commit, update gstate
lfs->globals = globals; lfs->gstate = lfs->gpending;
lfs->locals = locals; lfs->gdelta = (struct lfs_gstate){0};
} else { } else {
compact: compact:
// fall back to compaction // fall back to compaction
@@ -1692,9 +1677,6 @@ compact:
} }
} }
// update globals that are affected
lfs_global_xor(&lfs->globals, &cancels);
// update any directories that are affected // update any directories that are affected
lfs_mdir_t copy = *dir; lfs_mdir_t copy = *dir;
@@ -1772,31 +1754,28 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
} }
// setup dir // setup dir
dir.tail[0] = pred.tail[0]; lfs_pair_tole32(pred.tail);
dir.tail[1] = pred.tail[1]; err = lfs_dir_commit(lfs, &dir,
err = lfs_dir_commit(lfs, &dir, NULL); LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pred.tail, 8,
NULL));
lfs_pair_fromle32(pred.tail);
if (err) { if (err) {
return err; return err;
} }
// current block end of list? // current block end of list?
if (!cwd.split) { if (cwd.split) {
// update atomically
cwd.tail[0] = dir.pair[0];
cwd.tail[1] = dir.pair[1];
} else {
// update tails, this creates a desync // update tails, this creates a desync
pred.tail[0] = dir.pair[0]; lfs_fs_preporphans(lfs, +1);
pred.tail[1] = dir.pair[1]; lfs_pair_tole32(dir.pair);
lfs_global_orphans(lfs, +1);
err = lfs_dir_commit(lfs, &pred, err = lfs_dir_commit(lfs, &pred,
LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8,
pred.tail, sizeof(pred.tail),
NULL)); NULL));
lfs_pair_fromle32(dir.pair);
if (err) { if (err) {
return err; return err;
} }
lfs_global_orphans(lfs, -1); lfs_fs_preporphans(lfs, -1);
} }
// now insert into our parent block // now insert into our parent block
@@ -1806,8 +1785,8 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
LFS_MKATTR(LFS_TYPE_DIR, id, path, nlen, LFS_MKATTR(LFS_TYPE_DIR, id, path, nlen,
LFS_MKATTR(LFS_TYPE_CREATE, id, NULL, 0, LFS_MKATTR(LFS_TYPE_CREATE, id, NULL, 0,
(!cwd.split) (!cwd.split)
? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8,
cwd.tail, sizeof(cwd.tail), NULL) NULL)
: NULL)))); : NULL))));
lfs_pair_fromle32(dir.pair); lfs_pair_fromle32(dir.pair);
if (err) { if (err) {
@@ -2867,7 +2846,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
} }
// mark fs as orphaned // mark fs as orphaned
lfs_global_orphans(lfs, +1); lfs_fs_preporphans(lfs, +1);
} }
// delete the entry // delete the entry
@@ -2880,7 +2859,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { if (lfs_tag_type3(tag) == LFS_TYPE_DIR) {
// fix orphan // fix orphan
lfs_global_orphans(lfs, -1); lfs_fs_preporphans(lfs, -1);
err = lfs_fs_pred(lfs, dir.pair, &cwd); err = lfs_fs_pred(lfs, dir.pair, &cwd);
if (err) { if (err) {
@@ -2948,7 +2927,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
// mark fs as orphaned // mark fs as orphaned
lfs_global_orphans(lfs, +1); lfs_fs_preporphans(lfs, +1);
} }
// create move to fix later // create move to fix later
@@ -2960,7 +2939,8 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// is a bit messy // is a bit messy
newoldtagid += 1; newoldtagid += 1;
} }
lfs_global_move(lfs, true, oldcwd.pair, newoldtagid);
lfs_fs_prepmove(lfs, newoldtagid, oldcwd.pair);
// move over all attributes // move over all attributes
err = lfs_dir_commit(lfs, &newcwd, err = lfs_dir_commit(lfs, &newcwd,
@@ -2985,7 +2965,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) {
// fix orphan // fix orphan
lfs_global_orphans(lfs, -1); lfs_fs_preporphans(lfs, -1);
err = lfs_fs_pred(lfs, prevdir.pair, &newcwd); err = lfs_fs_pred(lfs, prevdir.pair, &newcwd);
if (err) { if (err) {
@@ -3155,8 +3135,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->root[1] = 0xffffffff; lfs->root[1] = 0xffffffff;
lfs->mlist = NULL; lfs->mlist = NULL;
lfs->seed = 0; lfs->seed = 0;
lfs_global_zero(&lfs->globals); lfs->gstate = (struct lfs_gstate){0};
lfs_global_zero(&lfs->locals); lfs->gpending = (struct lfs_gstate){0};
lfs->gdelta = (struct lfs_gstate){0};
return 0; return 0;
@@ -3334,8 +3315,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
} }
} }
// has globals? // has gstate?
err = lfs_dir_getglobals(lfs, &dir, &lfs->locals); err = lfs_dir_getgstate(lfs, &dir, &lfs->gpending);
if (err) { if (err) {
return err; return err;
} }
@@ -3347,13 +3328,14 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
goto cleanup; goto cleanup;
} }
// update littlefs with globals // update littlefs with gstate
lfs_global_fromle32(&lfs->locals); lfs_gstate_fromle32(&lfs->gpending);
lfs_global_xor(&lfs->globals, &lfs->locals); lfs->gstate = lfs->gpending;
lfs_global_zero(&lfs->locals); if (lfs_gstate_hasmove(&lfs->gstate)) {
if (lfs->globals.hasmove) {
LFS_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu32, LFS_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu32,
lfs->globals.pair[0], lfs->globals.pair[1], lfs->globals.id); lfs->gstate.pair[0],
lfs->gstate.pair[1],
lfs_tag_id(lfs->gstate.tag));
} }
// setup free lookahead // setup free lookahead
@@ -3531,7 +3513,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
if (tag != LFS_ERR_NOENT) { if (tag != LFS_ERR_NOENT) {
// update disk, this creates a desync // update disk, this creates a desync
lfs_global_orphans(lfs, +1); lfs_fs_preporphans(lfs, +1);
lfs_pair_tole32(newpair); lfs_pair_tole32(newpair);
int err = lfs_dir_commit(lfs, &parent, int err = lfs_dir_commit(lfs, &parent,
@@ -3542,7 +3524,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
} }
// next step, clean up orphans // next step, clean up orphans
lfs_global_orphans(lfs, -1); lfs_fs_preporphans(lfs, -1);
} }
// find pred // find pred
@@ -3554,12 +3536,11 @@ static int lfs_fs_relocate(lfs_t *lfs,
// if we can't find dir, it must be new // if we can't find dir, it must be new
if (err != LFS_ERR_NOENT) { if (err != LFS_ERR_NOENT) {
// replace bad pair, either we clean up desync, or no desync occured // replace bad pair, either we clean up desync, or no desync occured
parent.tail[0] = newpair[0]; lfs_pair_tole32(newpair);
parent.tail[1] = newpair[1];
err = lfs_dir_commit(lfs, &parent, err = lfs_dir_commit(lfs, &parent,
LFS_MKATTR(LFS_TYPE_TAIL + parent.split, LFS_MKATTR(LFS_TYPE_TAIL + parent.split, 0x3ff, newpair, 8,
0x3ff, parent.tail, sizeof(parent.tail),
NULL)); NULL));
lfs_pair_fromle32(newpair);
if (err) { if (err) {
return err; return err;
} }
@@ -3568,18 +3549,52 @@ static int lfs_fs_relocate(lfs_t *lfs,
return 0; return 0;
} }
static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) {
lfs_gstate_fromle32(&lfs->gdelta);
lfs->gdelta.tag ^= lfs_gstate_hasorphans(&lfs->gpending);
lfs->gpending.tag += orphans;
lfs->gdelta.tag ^= lfs_gstate_hasorphans(&lfs->gpending);
lfs_gstate_tole32(&lfs->gdelta);
}
static void lfs_fs_prepmove(lfs_t *lfs,
uint16_t id, const lfs_block_t pair[2]) {
lfs_gstate_fromle32(&lfs->gdelta);
lfs_gstate_xor(&lfs->gdelta, &lfs->gpending);
if (id != 0x3ff) {
lfs->gpending.tag = LFS_MKTAG(LFS_TYPE_DELETE, id,
lfs_gstate_getorphans(&lfs->gpending));
lfs->gpending.pair[0] = pair[0];
lfs->gpending.pair[1] = pair[1];
} else {
lfs->gpending.tag = LFS_MKTAG(0, 0,
lfs_gstate_getorphans(&lfs->gpending));
lfs->gpending.pair[0] = 0;
lfs->gpending.pair[1] = 0;
}
lfs_gstate_xor(&lfs->gdelta, &lfs->gpending);
lfs_gstate_tole32(&lfs->gdelta);
}
static int lfs_fs_demove(lfs_t *lfs) { static int lfs_fs_demove(lfs_t *lfs) {
if (!lfs->globals.hasmove) { if (!lfs_gstate_hasmove(&lfs->gstate)) {
return 0; return 0;
} }
// Fix bad moves // Fix bad moves
LFS_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu32, LFS_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu32,
lfs->globals.pair[0], lfs->globals.pair[1], lfs->globals.id); lfs->gstate.pair[0],
lfs->gstate.pair[1],
lfs_tag_id(lfs->gstate.tag));
// fetch and delete the moved entry // fetch and delete the moved entry
lfs_mdir_t movedir; lfs_mdir_t movedir;
int err = lfs_dir_fetch(lfs, &movedir, lfs->globals.pair); int err = lfs_dir_fetch(lfs, &movedir, lfs->gstate.pair);
if (err) { if (err) {
return err; return err;
} }
@@ -3594,7 +3609,7 @@ static int lfs_fs_demove(lfs_t *lfs) {
} }
static int lfs_fs_deorphan(lfs_t *lfs) { static int lfs_fs_deorphan(lfs_t *lfs) {
if (!lfs->globals.orphans) { if (!lfs_gstate_hasorphans(&lfs->gstate)) {
return 0; return 0;
} }
@@ -3644,12 +3659,11 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
LFS_DEBUG("Fixing half-orphan %"PRIu32" %"PRIu32, LFS_DEBUG("Fixing half-orphan %"PRIu32" %"PRIu32,
pair[0], pair[1]); pair[0], pair[1]);
pdir.tail[0] = pair[0]; lfs_pair_tole32(pair);
pdir.tail[1] = pair[1];
err = lfs_dir_commit(lfs, &pdir, err = lfs_dir_commit(lfs, &pdir,
LFS_MKATTR(LFS_TYPE_SOFTTAIL, LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pair, 8,
0x3ff, pdir.tail, sizeof(pdir.tail),
NULL)); NULL));
lfs_pair_fromle32(pair);
if (err) { if (err) {
return err; return err;
} }
@@ -3662,7 +3676,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
} }
// mark orphans as fixed // mark orphans as fixed
lfs_global_orphans(lfs, -lfs->globals.orphans); lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate));
lfs->gstate = lfs->gpending;
return 0; return 0;
} }

8
lfs.h
View File

@@ -381,12 +381,10 @@ typedef struct lfs {
} *mlist; } *mlist;
uint32_t seed; uint32_t seed;
struct lfs_globals { struct lfs_gstate {
uint32_t tag;
lfs_block_t pair[2]; lfs_block_t pair[2];
uint16_t id; } gstate, gpending, gdelta;
bool hasmove;
uint8_t orphans;
} globals, locals;
struct lfs_free { struct lfs_free {
lfs_block_t off; lfs_block_t off;

View File

@@ -164,28 +164,6 @@ static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a); return lfs_fromle32(a);
} }
// Convert between 16-bit little-endian and native order
static inline uint16_t lfs_fromle16(uint16_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap16(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8);
#endif
}
static inline uint16_t lfs_tole16(uint16_t a) {
return lfs_fromle16(a);
}
// Convert between 32-bit big-endian and native order // Convert between 32-bit big-endian and native order
static inline uint32_t lfs_frombe32(uint32_t a) { static inline uint32_t lfs_frombe32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \ #if !defined(LFS_NO_INTRINSICS) && ( \
@@ -210,28 +188,6 @@ static inline uint32_t lfs_tobe32(uint32_t a) {
return lfs_frombe32(a); return lfs_frombe32(a);
} }
// Convert between 16-bit big-endian and native order
static inline uint16_t lfs_frombe16(uint16_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return __builtin_bswap16(a);
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return a;
#else
return (((uint8_t*)&a)[0] << 8) |
(((uint8_t*)&a)[1] << 0);
#endif
}
static inline uint16_t lfs_tobe16(uint16_t a) {
return lfs_frombe16(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7 // Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);