Removed xored-globals from the mdir struct

The xored-globals have a very large footprint. In the worst case, the
xored-globals are stored on each metadata-pair, twice in memory. They
must be very small, but are also very useful, so at risk of growing
in the future (hint global free-list?).

Initially we also stored a copy in each mdir structure, since this
avoided extra disk access to look up the globals when we need to modify
the global state on a metadata-pair. But we can easily just fetch the
globals when needed.

This is more costly in terms of runtime, but reduces RAM impact of
globals, which was previously needed for each open dir and file.
This commit is contained in:
Christopher Haster 2018-09-12 01:34:03 -05:00
parent 5eeeb9d6ac
commit 7bacf9b1e0
2 changed files with 108 additions and 97 deletions

204
lfs.c
View File

@ -394,6 +394,8 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
/// Internal operations predeclared here ///
static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
const struct lfs_mattr *attrs);
static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2],
lfs_mdir_t *pdir);
static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
@ -537,7 +539,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
}
static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
lfs_mdir_t *dir, const lfs_block_t pair[2],
lfs_mdir_t *dir, const lfs_block_t pair[2],
lfs_tag_t matchmask, lfs_tag_t matchtag,
int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) {
// find the block with the most recent revision
@ -558,7 +560,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
}
// now fetch the actual dir (and find match)
lfs_stag_t foundtag = LFS_ERR_NOENT;
lfs_stag_t foundtag = 0;
dir->pair[0] = pair[0];
dir->pair[1] = pair[1];
dir->off = 0;
@ -581,7 +583,6 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
.split = false,
.count = 0,
};
lfs_global_zero(&temp.locals);
temp.rev = lfs_tole32(temp.rev);
uint32_t crc = lfs_crc(0xffffffff, &temp.rev, sizeof(temp.rev));
@ -669,7 +670,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
if (lfs_tag_subtype(tag) == LFS_TYPE_NAME) {
temp.count += 1;
if (lfs_tag_isvalid(tempfoundtag) &&
if (tempfoundtag &&
lfs_tag_id(tag) <= lfs_tag_id(tempfoundtag)) {
tempfoundtag += LFS_MKTAG(0, 1, 0);
}
@ -678,8 +679,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
temp.count -= 1;
if (lfs_tag_id(tag) == lfs_tag_id(tempfoundtag)) {
tempfoundtag = LFS_ERR_NOENT;
} else if (lfs_tag_isvalid(tempfoundtag) &&
tempfoundtag = 0;
} else if (tempfoundtag &&
lfs_tag_id(tag) < lfs_tag_id(tempfoundtag)) {
tempfoundtag -= LFS_MKTAG(0, 1, 0);
}
@ -696,24 +697,12 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
}
}
lfs_pair_fromle32(temp.tail);
} else if (lfs_tag_subtype(tag) == LFS_TYPE_GLOBALS) {
temp.locals.l.deorphaned = (lfs_tag_type(tag) & 1);
err = lfs_bd_read(lfs,
&lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
block, off+sizeof(tag),
&temp.locals, 10);
if (err) {
if (err == LFS_ERR_CORRUPT) {
dir->erased = false;
break;
}
}
}
if ((tag & matchmask) == (matchtag & matchmask)) {
// found a match?
if (lfs_tag_isdelete(tag)) {
tempfoundtag = LFS_ERR_NOENT;
tempfoundtag = 0;
} else if (cb) {
int res = cb(data, tag, &(struct lfs_diskoff){
block, off+sizeof(tag)});
@ -728,6 +717,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
if (res) {
tempfoundtag = tag;
}
} else {
tempfoundtag = tag;
}
}
}
@ -741,8 +732,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// synthetic move
if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0) {
if (lfs->globals.g.moveid == lfs_tag_id(foundtag)) {
foundtag = LFS_ERR_NOENT;
} else if (lfs_tag_isvalid(foundtag) &&
foundtag = 0;
} else if (foundtag &&
lfs->globals.g.moveid < lfs_tag_id(foundtag)) {
foundtag -= LFS_MKTAG(0, 1, 0);
}
@ -763,12 +754,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
static int lfs_dir_fetch(lfs_t *lfs,
lfs_mdir_t *dir, const lfs_block_t pair[2]) {
lfs_stag_t res = lfs_dir_fetchmatch(lfs, dir, pair, 0, 0, NULL, NULL);
if (res < 0 && res != LFS_ERR_NOENT) {
return res;
}
return 0;
return lfs_dir_fetchmatch(lfs, dir, pair,
0xffffffff, 0x00000000, NULL, NULL);
}
static lfs_stag_t lfs_dir_findmatch(lfs_t *lfs,
@ -781,7 +768,7 @@ static lfs_stag_t lfs_dir_findmatch(lfs_t *lfs,
while ((dir->split || fs) && !lfs_pair_isnull(dir->tail)) {
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
findmask, findtag, cb, data);
if (tag != LFS_ERR_NOENT) {
if (tag) {
return tag;
}
}
@ -821,7 +808,7 @@ static int lfs_dir_get_match(void *data,
return tag & 0x7fffffff;
}
static lfs_stag_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir,
static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
lfs_tag_t getmask, lfs_tag_t gettag, void *buffer) {
lfs_stag_t getdiff = 0;
if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0 &&
@ -993,20 +980,6 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
return 0;
}
static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit,
lfs_global_t *locals) {
if (lfs_global_iszero(&lfs->locals)) {
return 0;
}
lfs_global_xor(locals, &lfs->locals);
int err = lfs_commit_attr(lfs, commit,
LFS_MKTAG(LFS_TYPE_GLOBALS + locals->l.deorphaned, 0x3ff, 10),
locals);
lfs_global_xor(locals, &lfs->locals);
return err;
}
static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit,
bool compacting) {
// align to program units
@ -1105,12 +1078,36 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
dir->tail[1] = 0xffffffff;
dir->erased = false;
dir->split = false;
lfs_global_zero(&dir->locals);
// don't write out yet, let caller take care of that
return 0;
}
static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, const lfs_mdir_t *tail) {
// steal tail
dir->tail[0] = tail->tail[0];
dir->tail[1] = tail->tail[1];
dir->split = tail->split;
// steal state
lfs_global_t locals;
lfs_stag_t res = lfs_dir_get(lfs, tail, 0x7c000000,
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals);
if (res < 0 && res != LFS_ERR_NOENT) {
return res;
}
if (res != LFS_ERR_NOENT) {
locals.l.deorphaned = (lfs_tag_type(res) & 1);
lfs_global_xor(&lfs->locals, &locals);
}
return lfs_dir_commit(lfs, dir,
LFS_MKATTR(LFS_TYPE_TAIL + dir->split,
0x3ff, dir->tail, sizeof(dir->tail),
NULL));
}
static int lfs_dir_compact(lfs_t *lfs,
lfs_mdir_t *dir, const struct lfs_mattr *attrs,
lfs_mdir_t *source, uint16_t begin, uint16_t end) {
@ -1120,9 +1117,19 @@ static int lfs_dir_compact(lfs_t *lfs,
// There's nothing special about our global delta, so feed it back
// into the global global delta
lfs_global_xor(&lfs->locals, &dir->locals);
lfs_global_zero(&dir->locals);
lfs_global_t locals;
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c000000,
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals);
if (res < 0 && res != LFS_ERR_NOENT) {
return res;
}
if (res != LFS_ERR_NOENT) {
locals.l.deorphaned = (lfs_tag_type(res) & 1);
lfs_global_xor(&lfs->locals, &locals);
}
// begin loop to commit compaction to blocks until a compact sticks
while (true) {
// setup compaction
bool splitted = false;
@ -1160,7 +1167,7 @@ commit:
(const lfs_block_t[2]){0, 1}) == 0) {
// we're writing too much to the superblock,
// should we expand?
lfs_ssize_t res = lfs_fs_size(lfs);
res = lfs_fs_size(lfs);
if (res < 0) {
return res;
}
@ -1235,10 +1242,12 @@ commit:
}
}
if (!relocated) {
if (!relocated && !lfs_global_iszero(&lfs->locals)) {
// commit any globals, unless we're relocating,
// in which case our parent will steal our globals
err = lfs_commit_globals(lfs, &commit, &dir->locals);
err = lfs_commit_attr(lfs, &commit,
LFS_MKTAG(LFS_TYPE_GLOBALS + lfs->locals.l.deorphaned,
0x3ff, 10), &lfs->locals);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
@ -1343,7 +1352,6 @@ relocate:
if (!relocated) {
// successful commit, update globals
lfs_global_xor(&dir->locals, &lfs->locals);
lfs_global_zero(&lfs->locals);
} else {
// update references if we relocated
@ -1398,15 +1406,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
}
if (err != LFS_ERR_NOENT && pdir.split) {
// steal tail and global state
pdir.split = dir->split;
pdir.tail[0] = dir->tail[0];
pdir.tail[1] = dir->tail[1];
lfs_global_xor(&lfs->locals, &dir->locals);
return lfs_dir_commit(lfs, &pdir,
LFS_MKATTR(LFS_TYPE_TAIL + pdir.split, 0x3ff,
pdir.tail, sizeof(pdir.tail),
NULL));
return lfs_dir_drop(lfs, &pdir, dir);
}
}
}
@ -1449,15 +1449,34 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
}
}
int err = lfs_commit_globals(lfs, &commit, &dir->locals);
if (err) {
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
goto compact;
// commit any global diffs if we have any
if (!lfs_global_iszero(&lfs->locals)) {
lfs_global_t locals;
lfs_global_zero(&locals);
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c000000,
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals);
if (res < 0 && res != LFS_ERR_NOENT) {
return res;
}
if (res != LFS_ERR_NOENT) {
locals.l.deorphaned = (lfs_tag_type(res) & 1);
}
lfs_global_xor(&locals, &lfs->locals);
int err = lfs_commit_attr(lfs, &commit,
LFS_MKTAG(LFS_TYPE_GLOBALS + locals.l.deorphaned,
0x3ff, 10), &locals);
if (err) {
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
goto compact;
}
return err;
}
return err;
}
err = lfs_commit_crc(lfs, &commit, false);
// finalize commit with the crc
int err = lfs_commit_crc(lfs, &commit, false);
if (err) {
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
goto compact;
@ -1469,13 +1488,13 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
dir->off = commit.off;
dir->etag = commit.ptag;
// successful commit, update globals
lfs_global_xor(&dir->locals, &lfs->locals);
lfs_global_zero(&lfs->locals);
break;
compact:
// fall back to compaction
lfs_cache_drop(lfs, &lfs->pcache);
err = lfs_dir_compact(lfs, dir, attrs, dir, 0, dir->count);
if (err) {
return err;
@ -2777,14 +2796,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
return err;
}
// steal state
cwd.tail[0] = dir.tail[0];
cwd.tail[1] = dir.tail[1];
lfs_global_xor(&lfs->locals, &dir.locals);
err = lfs_dir_commit(lfs, &cwd,
LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff,
cwd.tail, sizeof(cwd.tail),
NULL));
err = lfs_dir_drop(lfs, &cwd, &dir);
if (err) {
return err;
}
@ -2884,14 +2896,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
return err;
}
// steal state
newcwd.tail[0] = prevdir.tail[0];
newcwd.tail[1] = prevdir.tail[1];
lfs_global_xor(&lfs->locals, &prevdir.locals);
err = lfs_dir_commit(lfs, &newcwd,
LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff,
newcwd.tail, sizeof(newcwd.tail),
NULL));
err = lfs_dir_drop(lfs, &newcwd, &prevdir);
if (err) {
return err;
}
@ -3207,14 +3212,26 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
// scan for any global updates
lfs_mdir_t dir = {.tail = {0, 1}};
while (!lfs_pair_isnull(dir.tail)) {
err = lfs_dir_fetch(lfs, &dir, dir.tail);
if (err) {
res = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 0x7c000000,
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), NULL, NULL);
if (res < 0) {
err = LFS_ERR_INVAL;
goto cleanup;
}
// xor together indirect deletes
lfs_global_xor(&lfs->locals, &dir.locals);
if (res) {
lfs_global_t locals;
res = lfs_dir_get(lfs, &dir, 0x7c000000,
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals);
if (res < 0) {
err = res;
goto cleanup;
}
locals.l.deorphaned = (lfs_tag_type(res) & 1);
// xor together indirect deletes
lfs_global_xor(&lfs->locals, &locals);
}
}
// update littlefs with globals
@ -3438,8 +3455,8 @@ static int lfs_fs_relocate(lfs_t *lfs,
parent.tail[0] = newpair[0];
parent.tail[1] = newpair[1];
err = lfs_dir_commit(lfs, &parent,
LFS_MKATTR(LFS_TYPE_TAIL + parent.split, 0x3ff,
parent.tail, sizeof(parent.tail),
LFS_MKATTR(LFS_TYPE_TAIL + parent.split,
0x3ff, parent.tail, sizeof(parent.tail),
NULL));
if (err) {
return err;
@ -3475,12 +3492,7 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
LFS_DEBUG("Fixing orphan %"PRIu32" %"PRIu32,
pdir.tail[0], pdir.tail[1]);
pdir.tail[0] = dir.tail[0];
pdir.tail[1] = dir.tail[1];
err = lfs_dir_commit(lfs, &pdir,
LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff,
pdir.tail, sizeof(pdir.tail),
NULL));
err = lfs_dir_drop(lfs, &pdir, &dir);
if (err) {
return err;
}
@ -3503,8 +3515,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
pdir.tail[0] = pair[0];
pdir.tail[1] = pair[1];
err = lfs_dir_commit(lfs, &pdir,
LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff,
pdir.tail, sizeof(pdir.tail),
LFS_MKATTR(LFS_TYPE_SOFTTAIL,
0x3ff, pdir.tail, sizeof(pdir.tail),
NULL));
if (err) {
return err;

1
lfs.h
View File

@ -315,7 +315,6 @@ typedef struct lfs_mdir {
bool erased;
bool split;
lfs_block_t tail[2];
lfs_global_t locals;
} lfs_mdir_t;
// littlefs directory type