Skip to content

Commit d5e8005

Browse files
committed
Collapsed recursive deorphans into a single pass
Because a block can go bad at any time, if we're unlucky, we may end up generating multiple orphans in a single metadata write. This is exacerbated by the early eviction in dynamic wear-leveling. We can't track _all_ orphans, because that would require unbounded storage and significantly complicate things, but there are a handful of intentional orphans we do track because they are easy to resolve without the O(n^2) deorphan scan. These are anytime we intentionally remove a metadata-pair. Initially we cleaned up orphans as they occur with whatever knowledge we do have, and just accepted the extra O(n^2) deorphan scans in the unlucky case. However we can do a bit better by being lazy and leaving deorphaning up to the next metadata write. This needs to work with the known orphans while still setting the orphan flag on disk correctly. To accomplish this we replace the internal flag with a small counter. Note, this means that our internal representation of orphans differs from what's on disk. This is annoying but not the end of the world.
1 parent 21217d7 commit d5e8005

File tree

2 files changed

+82
-80
lines changed

2 files changed

+82
-80
lines changed

lfs.c

Lines changed: 75 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -451,31 +451,32 @@ static inline void lfs_global_zero(lfs_global_t *a) {
451451
}
452452

453453
static inline void lfs_global_fromle32(lfs_global_t *a) {
454-
lfs_pair_fromle32(a->s.movepair);
455-
a->s.moveid = lfs_fromle16(a->s.moveid);
454+
lfs_pair_fromle32(a->l.movepair);
455+
a->l.moveid = lfs_fromle16(a->l.moveid);
456456
}
457457

458458
static inline void lfs_global_tole32(lfs_global_t *a) {
459-
lfs_pair_tole32(a->s.movepair);
460-
a->s.moveid = lfs_tole16(a->s.moveid);
459+
lfs_pair_tole32(a->l.movepair);
460+
a->l.moveid = lfs_tole16(a->l.moveid);
461461
}
462462

463463
static inline void lfs_global_move(lfs_t *lfs,
464464
const lfs_block_t pair[2], uint16_t id) {
465465
lfs_global_t diff;
466466
lfs_global_zero(&diff);
467-
diff.s.movepair[0] ^= lfs->globals.s.movepair[0] ^ pair[0];
468-
diff.s.movepair[1] ^= lfs->globals.s.movepair[1] ^ pair[1];
469-
diff.s.moveid ^= lfs->globals.s.moveid ^ id;
467+
diff.l.movepair[0] ^= lfs->globals.g.movepair[0] ^ pair[0];
468+
diff.l.movepair[1] ^= lfs->globals.g.movepair[1] ^ pair[1];
469+
diff.l.moveid ^= lfs->globals.g.moveid ^ id;
470470
lfs_global_fromle32(&lfs->locals);
471471
lfs_global_xor(&lfs->locals, &diff);
472472
lfs_global_tole32(&lfs->locals);
473473
lfs_global_xor(&lfs->globals, &diff);
474474
}
475475

476-
static inline void lfs_global_deorphaned(lfs_t *lfs, bool deorphaned) {
477-
lfs->locals.s.deorphaned ^= lfs->globals.s.deorphaned ^ deorphaned;
478-
lfs->globals.s.deorphaned ^= lfs->globals.s.deorphaned ^ deorphaned;
476+
static inline void lfs_global_orphans(lfs_t *lfs, int8_t orphans) {
477+
lfs->locals.l.deorphaned ^= (lfs->globals.g.orphans == 0);
478+
lfs->locals.l.deorphaned ^= (lfs->globals.g.orphans + orphans == 0);
479+
lfs->globals.g.orphans += orphans;
479480
}
480481

481482

@@ -688,7 +689,7 @@ static int lfs_commit_globals(lfs_t *lfs, struct lfs_commit *commit,
688689

689690
lfs_global_xor(locals, &lfs->locals);
690691
int err = lfs_commit_attr(lfs, commit,
691-
LFS_MKTAG(LFS_TYPE_GLOBALS + locals->s.deorphaned, 0x3ff, 10),
692+
LFS_MKTAG(LFS_TYPE_GLOBALS + locals->l.deorphaned, 0x3ff, 10),
692693
locals);
693694
lfs_global_xor(locals, &lfs->locals);
694695
return err;
@@ -907,7 +908,7 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs,
907908
}
908909
lfs_pair_fromle32(temptail);
909910
} else if (lfs_tag_subtype(tag) == LFS_TYPE_GLOBALS) {
910-
templocals.s.deorphaned = (lfs_tag_type(tag) & 1);
911+
templocals.l.deorphaned = (lfs_tag_type(tag) & 1);
911912
err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag),
912913
&templocals, 10);
913914
if (err) {
@@ -973,11 +974,11 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs,
973974
// consider what we have good enough
974975
if (dir->off > 0) {
975976
// synthetic move
976-
if (lfs_pair_cmp(dir->pair, lfs->globals.s.movepair) == 0) {
977-
if (lfs->globals.s.moveid == lfs_tag_id(foundtag)) {
977+
if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0) {
978+
if (lfs->globals.g.moveid == lfs_tag_id(foundtag)) {
978979
foundtag = LFS_ERR_NOENT;
979980
} else if (lfs_tag_isvalid(foundtag) &&
980-
lfs->globals.s.moveid < lfs_tag_id(foundtag)) {
981+
lfs->globals.g.moveid < lfs_tag_id(foundtag)) {
981982
foundtag -= LFS_MKTAG(0, 1, 0);
982983
}
983984
}
@@ -1026,8 +1027,8 @@ static int32_t lfs_dir_find(lfs_t *lfs,
10261027
static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir,
10271028
uint32_t getmask, uint32_t gettag, void *buffer) {
10281029
int32_t getdiff = 0;
1029-
if (lfs_pair_cmp(dir->pair, lfs->globals.s.movepair) == 0 &&
1030-
lfs_tag_id(gettag) <= lfs->globals.s.moveid) {
1030+
if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0 &&
1031+
lfs_tag_id(gettag) <= lfs->globals.g.moveid) {
10311032
// synthetic moves
10321033
getdiff = LFS_MKTAG(0, 1, 0);
10331034
}
@@ -1270,17 +1271,17 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
12701271
lfs_mattr_t cancelattr;
12711272
lfs_global_t canceldiff;
12721273
lfs_global_zero(&canceldiff);
1273-
if (lfs_pair_cmp(dir->pair, lfs->globals.s.movepair) == 0) {
1274+
if (lfs_pair_cmp(dir->pair, lfs->globals.g.movepair) == 0) {
12741275
// Wait, we have the move? Just cancel this out here
12751276
// We need to, or else the move can become outdated
1276-
canceldiff.s.movepair[0] ^= lfs->globals.s.movepair[0] ^ 0xffffffff;
1277-
canceldiff.s.movepair[1] ^= lfs->globals.s.movepair[1] ^ 0xffffffff;
1278-
canceldiff.s.moveid ^= lfs->globals.s.moveid ^ 0x3ff;
1277+
canceldiff.l.movepair[0] ^= lfs->globals.g.movepair[0] ^ 0xffffffff;
1278+
canceldiff.l.movepair[1] ^= lfs->globals.g.movepair[1] ^ 0xffffffff;
1279+
canceldiff.l.moveid ^= lfs->globals.g.moveid ^ 0x3ff;
12791280
lfs_global_fromle32(&lfs->locals);
12801281
lfs_global_xor(&lfs->locals, &canceldiff);
12811282
lfs_global_tole32(&lfs->locals);
12821283

1283-
cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.s.moveid, 0);
1284+
cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.l.moveid, 0);
12841285
cancelattr.next = attrs;
12851286
attrs = &cancelattr;
12861287
}
@@ -2636,7 +2637,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
26362637
}
26372638

26382639
// mark fs as orphaned
2639-
lfs_global_deorphaned(lfs, false);
2640+
lfs_global_orphans(lfs, +1);
26402641
}
26412642

26422643
// delete the entry
@@ -2648,14 +2649,14 @@ int lfs_remove(lfs_t *lfs, const char *path) {
26482649
}
26492650

26502651
if (lfs_tag_type(tag) == LFS_TYPE_DIR) {
2652+
// fix orphan
2653+
lfs_global_orphans(lfs, -1);
2654+
26512655
err = lfs_fs_pred(lfs, dir.pair, &cwd);
26522656
if (err) {
26532657
return err;
26542658
}
26552659

2656-
// fix orphan
2657-
lfs_global_deorphaned(lfs, true);
2658-
26592660
// steal state
26602661
cwd.tail[0] = dir.tail[0];
26612662
cwd.tail[1] = dir.tail[1];
@@ -2696,36 +2697,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
26962697
uint16_t newid = lfs_tag_id(prevtag);
26972698

26982699
lfs_mdir_t prevdir;
2699-
if (prevtag != LFS_ERR_NOENT) {
2700-
// check that we have same type
2701-
if (lfs_tag_type(prevtag) != lfs_tag_type(oldtag)) {
2702-
return LFS_ERR_ISDIR;
2703-
}
2704-
2705-
if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) {
2706-
// must be empty before removal
2707-
lfs_block_t prevpair[2];
2708-
int32_t res = lfs_dir_get(lfs, &newcwd, 0x7c3ff000,
2709-
LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
2710-
if (res < 0) {
2711-
return res;
2712-
}
2713-
lfs_pair_fromle32(prevpair);
2714-
2715-
// must be empty before removal
2716-
err = lfs_dir_fetch(lfs, &prevdir, prevpair);
2717-
if (err) {
2718-
return err;
2719-
}
2720-
2721-
if (prevdir.count > 0 || prevdir.split) {
2722-
return LFS_ERR_NOTEMPTY;
2723-
}
2724-
2725-
// mark fs as orphaned
2726-
lfs_global_deorphaned(lfs, false);
2727-
}
2728-
} else {
2700+
if (prevtag == LFS_ERR_NOENT) {
27292701
// check that name fits
27302702
lfs_size_t nlen = strlen(newpath);
27312703
if (nlen > lfs->name_max) {
@@ -2734,6 +2706,30 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
27342706

27352707
// get next id
27362708
newid = newcwd.count;
2709+
} else if (lfs_tag_type(prevtag) != lfs_tag_type(oldtag)) {
2710+
return LFS_ERR_ISDIR;
2711+
} else if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) {
2712+
// must be empty before removal
2713+
lfs_block_t prevpair[2];
2714+
int32_t res = lfs_dir_get(lfs, &newcwd, 0x7c3ff000,
2715+
LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
2716+
if (res < 0) {
2717+
return res;
2718+
}
2719+
lfs_pair_fromle32(prevpair);
2720+
2721+
// must be empty before removal
2722+
err = lfs_dir_fetch(lfs, &prevdir, prevpair);
2723+
if (err) {
2724+
return err;
2725+
}
2726+
2727+
if (prevdir.count > 0 || prevdir.split) {
2728+
return LFS_ERR_NOTEMPTY;
2729+
}
2730+
2731+
// mark fs as orphaned
2732+
lfs_global_orphans(lfs, +1);
27372733
}
27382734

27392735
// create move to fix later
@@ -2758,14 +2754,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
27582754
}
27592755

27602756
if (prevtag != LFS_ERR_NOENT && lfs_tag_type(prevtag) == LFS_TYPE_DIR) {
2757+
// fix orphan
2758+
lfs_global_orphans(lfs, -1);
2759+
27612760
err = lfs_fs_pred(lfs, prevdir.pair, &newcwd);
27622761
if (err) {
27632762
return err;
27642763
}
27652764

2766-
// fix orphan
2767-
lfs_global_deorphaned(lfs, true);
2768-
27692765
// steal state
27702766
newcwd.tail[0] = prevdir.tail[0];
27712767
newcwd.tail[1] = prevdir.tail[1];
@@ -2917,10 +2913,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
29172913
lfs->root[1] = 0xffffffff;
29182914
lfs->mlist = NULL;
29192915
lfs->seed = 0;
2920-
lfs->globals.s.movepair[0] = 0xffffffff;
2921-
lfs->globals.s.movepair[1] = 0xffffffff;
2922-
lfs->globals.s.moveid = 0x3ff;
2923-
lfs->globals.s.deorphaned = true;
2916+
lfs->globals.g.movepair[0] = 0xffffffff;
2917+
lfs->globals.g.movepair[1] = 0xffffffff;
2918+
lfs->globals.g.moveid = 0x3ff;
2919+
lfs->globals.g.orphans = 0;
29242920
lfs_global_zero(&lfs->locals);
29252921

29262922
return 0;
@@ -3089,11 +3085,11 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
30893085
lfs_global_fromle32(&lfs->locals);
30903086
lfs_global_xor(&lfs->globals, &lfs->locals);
30913087
lfs_global_zero(&lfs->locals);
3092-
if (!lfs_pair_isnull(lfs->globals.s.movepair)) {
3088+
if (!lfs_pair_isnull(lfs->globals.g.movepair)) {
30933089
LFS_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu32,
3094-
lfs->globals.s.movepair[0],
3095-
lfs->globals.s.movepair[1],
3096-
lfs->globals.s.moveid);
3090+
lfs->globals.g.movepair[0],
3091+
lfs->globals.g.movepair[1],
3092+
lfs->globals.g.moveid);
30973093
}
30983094

30993095
// setup free lookahead
@@ -3254,7 +3250,8 @@ static int lfs_fs_relocate(lfs_t *lfs,
32543250

32553251
if (tag != LFS_ERR_NOENT) {
32563252
// update disk, this creates a desync
3257-
lfs_global_deorphaned(lfs, false);
3253+
lfs_global_orphans(lfs, +1);
3254+
32583255
lfs_pair_tole32(newpair);
32593256
int err = lfs_dir_commit(lfs, &parent,
32603257
&(lfs_mattr_t){.tag=tag, .buffer=newpair});
@@ -3263,8 +3260,8 @@ static int lfs_fs_relocate(lfs_t *lfs,
32633260
return err;
32643261
}
32653262

3266-
// clean up bad block, which should now be a desync
3267-
return lfs_fs_deorphan(lfs);
3263+
// next step, clean up orphans
3264+
lfs_global_orphans(lfs, -1);
32683265
}
32693266

32703267
// find pred
@@ -3275,7 +3272,7 @@ static int lfs_fs_relocate(lfs_t *lfs,
32753272

32763273
// if we can't find dir, it must be new
32773274
if (err != LFS_ERR_NOENT) {
3278-
// just replace bad pair, no desync can occur
3275+
// replace bad pair, either we clean up desync, or no desync occured
32793276
parent.tail[0] = newpair[0];
32803277
parent.tail[1] = newpair[1];
32813278
err = lfs_dir_commit(lfs, &parent,
@@ -3359,20 +3356,20 @@ static int lfs_fs_deorphan(lfs_t *lfs) {
33593356
}
33603357

33613358
// mark orphans as fixed
3362-
lfs_global_deorphaned(lfs, true);
3359+
lfs_global_orphans(lfs, -lfs->globals.g.orphans);
33633360
return 0;
33643361
}
33653362

33663363
static int lfs_fs_demove(lfs_t *lfs) {
33673364
// Fix bad moves
33683365
LFS_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu32,
3369-
lfs->globals.s.movepair[0],
3370-
lfs->globals.s.movepair[1],
3371-
lfs->globals.s.moveid);
3366+
lfs->globals.g.movepair[0],
3367+
lfs->globals.g.movepair[1],
3368+
lfs->globals.g.moveid);
33723369

33733370
// fetch and delete the moved entry
33743371
lfs_mdir_t movedir;
3375-
int err = lfs_dir_fetch(lfs, &movedir, lfs->globals.s.movepair);
3372+
int err = lfs_dir_fetch(lfs, &movedir, lfs->globals.g.movepair);
33763373
if (err) {
33773374
return err;
33783375
}
@@ -3387,14 +3384,14 @@ static int lfs_fs_demove(lfs_t *lfs) {
33873384
}
33883385

33893386
static int lfs_fs_forceconsistency(lfs_t *lfs) {
3390-
if (!lfs->globals.s.deorphaned) {
3387+
if (lfs->globals.g.orphans) {
33913388
int err = lfs_fs_deorphan(lfs);
33923389
if (err) {
33933390
return err;
33943391
}
33953392
}
33963393

3397-
if (lfs->globals.s.moveid != 0x3ff) {
3394+
if (lfs->globals.g.moveid != 0x3ff) {
33983395
int err = lfs_fs_demove(lfs);
33993396
if (err) {
34003397
return err;

lfs.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,13 @@ typedef union lfs_global {
302302
struct {
303303
lfs_block_t movepair[2];
304304
uint16_t moveid;
305-
bool deorphaned;
306-
} s;
305+
uint8_t deorphaned;
306+
} l;
307+
struct {
308+
lfs_block_t movepair[2];
309+
uint16_t moveid;
310+
uint8_t orphans;
311+
} g;
307312
} lfs_global_t;
308313

309314
typedef struct lfs_mdir {

0 commit comments

Comments
 (0)