mirror of
https://gitee.com/openharmony/third_party_littlefs
synced 2025-02-17 06:08:16 +00:00
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:
parent
5eeeb9d6ac
commit
7bacf9b1e0
204
lfs.c
204
lfs.c
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user