Skip to content

Commit a326e47

Browse files
committed
WIP Modified lfs_dir_compact to avoid redundant erases during split
1 parent ed49ea4 commit a326e47

File tree

1 file changed

+122
-90
lines changed

1 file changed

+122
-90
lines changed

lfs.c

Lines changed: 122 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ static int lfs_bd_read(lfs_t *lfs,
3939
void *buffer, lfs_size_t size) {
4040
uint8_t *data = buffer;
4141
LFS_ASSERT(block != 0xffffffff);
42+
if (off+size > lfs->cfg->block_size) {
43+
return LFS_ERR_CORRUPT;
44+
}
4245

4346
while (size > 0) {
4447
if (pcache && block == pcache->block &&
@@ -452,6 +455,7 @@ struct lfs_commit {
452455

453456
lfs_off_t begin;
454457
lfs_off_t end;
458+
lfs_off_t ack;
455459
};
456460

457461
struct lfs_diskoff {
@@ -503,6 +507,24 @@ static int32_t lfs_commit_get(lfs_t *lfs, lfs_block_t block, lfs_off_t off,
503507
return LFS_ERR_NOENT;
504508
}
505509

510+
static int lfs_commit_prog(lfs_t *lfs, struct lfs_commit *commit,
511+
const void *buffer, lfs_size_t size) {
512+
lfs_off_t skip = lfs_min(lfs_max(commit->ack, commit->off)
513+
- commit->off, size);
514+
int err = lfs_bd_prog(lfs,
515+
&lfs->pcache, &lfs->rcache, false,
516+
commit->block, commit->off + skip,
517+
(const uint8_t*)buffer + skip, size - skip);
518+
if (err) {
519+
return err;
520+
}
521+
522+
commit->crc = lfs_crc32(commit->crc, buffer, size);
523+
commit->off += size;
524+
commit->ack = lfs_max(commit->off, commit->ack);
525+
return 0;
526+
}
527+
506528
static int lfs_commit_attrs(lfs_t *lfs, struct lfs_commit *commit,
507529
uint16_t id, const struct lfs_attr *attrs);
508530

@@ -532,21 +554,14 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
532554

533555
// write out tag
534556
uint32_t ntag = lfs_tole32((tag & 0x7fffffff) ^ commit->ptag);
535-
commit->crc = lfs_crc32(commit->crc, &ntag, sizeof(ntag));
536-
int err = lfs_bd_prog(lfs,
537-
&lfs->pcache, &lfs->rcache, false,
538-
commit->block, commit->off, &ntag, sizeof(ntag));
557+
int err = lfs_commit_prog(lfs, commit, &ntag, sizeof(ntag));
539558
if (err) {
540559
return err;
541560
}
542-
commit->off += sizeof(ntag);
543561

544562
if (!(tag & 0x80000000)) {
545563
// from memory
546-
commit->crc = lfs_crc32(commit->crc, buffer, size);
547-
err = lfs_bd_prog(lfs,
548-
&lfs->pcache, &lfs->rcache, false,
549-
commit->block, commit->off, buffer, size);
564+
err = lfs_commit_prog(lfs, commit, buffer, size);
550565
if (err) {
551566
return err;
552567
}
@@ -563,17 +578,13 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
563578
return err;
564579
}
565580

566-
commit->crc = lfs_crc32(commit->crc, &dat, 1);
567-
err = lfs_bd_prog(lfs,
568-
&lfs->pcache, &lfs->rcache, false,
569-
commit->block, commit->off+i, &dat, 1);
581+
err = lfs_commit_prog(lfs, commit, &dat, 1);
570582
if (err) {
571583
return err;
572584
}
573585
}
574586
}
575587

576-
commit->off += size;
577588
commit->ptag = tag & 0x7fffffff;
578589
return 0;
579590
}
@@ -677,13 +688,11 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
677688

678689
// read erased state from next program unit
679690
uint32_t tag = 0;
680-
if (off < lfs->cfg->block_size) {
681-
int err = lfs_bd_read(lfs,
682-
&lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
683-
commit->block, off, &tag, sizeof(tag));
684-
if (err) {
685-
return err;
686-
}
691+
int err = lfs_bd_read(lfs,
692+
&lfs->pcache, &lfs->rcache, sizeof(tag),
693+
commit->block, off, &tag, sizeof(tag));
694+
if (err && err != LFS_ERR_CORRUPT) {
695+
return err;
687696
}
688697

689698
// build crc tag
@@ -697,7 +706,7 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
697706
footer[0] = lfs_tole32(tag ^ commit->ptag);
698707
commit->crc = lfs_crc32(commit->crc, &footer[0], sizeof(footer[0]));
699708
footer[1] = lfs_tole32(commit->crc);
700-
int err = lfs_bd_prog(lfs,
709+
err = lfs_bd_prog(lfs,
701710
&lfs->pcache, &lfs->rcache, false,
702711
commit->block, commit->off, &footer, sizeof(footer));
703712
if (err) {
@@ -824,12 +833,6 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs,
824833
lfs_global_zero(&templocals);
825834

826835
while (true) {
827-
// reached end of block
828-
if (off+sizeof(uint32_t) >= lfs->cfg->block_size) {
829-
dir->erased = false;
830-
break;
831-
}
832-
833836
// extract next tag
834837
uint32_t tag;
835838
int err = lfs_bd_read(lfs,
@@ -1076,79 +1079,88 @@ static int lfs_dir_compact(lfs_t *lfs,
10761079
lfs_global_zero(&dir->locals);
10771080

10781081
while (true) {
1079-
// last complete id
1080-
dir->count = end - begin;
1081-
int16_t ack = -1;
1082+
// setup compaction
1083+
bool splitted = false;
10821084
bool exhausted = false;
10831085

1084-
// increment revision count
1085-
dir->rev += 1;
1086-
if (lfs->cfg->block_cycles && dir->rev % lfs->cfg->block_cycles == 0) {
1087-
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
1088-
// we're writing too much to the superblock, should we expand?
1089-
lfs_ssize_t res = lfs_fs_size(lfs);
1090-
if (res < 0) {
1091-
return res;
1092-
}
1086+
struct lfs_commit commit;
1087+
commit.block = dir->pair[1];
1088+
commit.ack = 0;
1089+
1090+
commit:
1091+
// setup erase state
1092+
exhausted = false;
1093+
dir->count = end - begin;
1094+
int16_t ackid = -1;
1095+
1096+
// setup commit state
1097+
commit.off = 0;
1098+
commit.crc = 0xffffffff;
1099+
commit.ptag = 0;
1100+
1101+
// space is complicated, we need room for tail, crc, globals,
1102+
// cleanup delete, and we cap at half a block to give room
1103+
// for metadata updates
1104+
commit.begin = 0;
1105+
commit.end = lfs_min(
1106+
lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size),
1107+
lfs->cfg->block_size - 38);
1108+
1109+
if (!splitted) {
1110+
// increment revision count
1111+
dir->rev += 1;
1112+
if (lfs->cfg->block_cycles &&
1113+
dir->rev % lfs->cfg->block_cycles == 0) {
1114+
if (lfs_pair_cmp(dir->pair,
1115+
(const lfs_block_t[2]){0, 1}) == 0) {
1116+
// we're writing too much to the superblock,
1117+
// should we expand?
1118+
lfs_ssize_t res = lfs_fs_size(lfs);
1119+
if (res < 0) {
1120+
return res;
1121+
}
10931122

1094-
// do we have enough space to expand?
1095-
if (res < lfs->cfg->block_count/2) {
1096-
LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev);
1123+
// do we have enough space to expand?
1124+
if (res < lfs->cfg->block_count/2) {
1125+
LFS_DEBUG("Expanding superblock at rev %"PRIu32,
1126+
dir->rev);
1127+
exhausted = true;
1128+
goto split;
1129+
}
1130+
} else {
1131+
// we're writing too much, time to relocate
10971132
exhausted = true;
1098-
goto split;
1133+
goto relocate;
10991134
}
1100-
} else {
1101-
// we're writing too much, time to relocate
1102-
exhausted = true;
1103-
goto relocate;
11041135
}
1105-
}
11061136

1107-
// erase block to write to
1108-
int err = lfs_bd_erase(lfs, dir->pair[1]);
1109-
if (err) {
1110-
if (err == LFS_ERR_CORRUPT) {
1111-
goto relocate;
1137+
// erase block to write to
1138+
int err = lfs_bd_erase(lfs, dir->pair[1]);
1139+
if (err) {
1140+
if (err == LFS_ERR_CORRUPT) {
1141+
goto relocate;
1142+
}
1143+
return err;
11121144
}
1113-
return err;
11141145
}
11151146

11161147
// write out header
1117-
uint32_t crc = 0xffffffff;
11181148
uint32_t rev = lfs_tole32(dir->rev);
1119-
crc = lfs_crc32(crc, &rev, sizeof(rev));
1120-
err = lfs_bd_prog(lfs,
1121-
&lfs->pcache, &lfs->rcache, false,
1122-
dir->pair[1], 0, &rev, sizeof(rev));
1149+
int err = lfs_commit_prog(lfs, &commit, &rev, sizeof(rev));
11231150
if (err) {
11241151
if (err == LFS_ERR_CORRUPT) {
11251152
goto relocate;
11261153
}
11271154
return err;
11281155
}
11291156

1130-
// setup compaction
1131-
struct lfs_commit commit = {
1132-
.block = dir->pair[1],
1133-
.off = sizeof(dir->rev),
1134-
.crc = crc,
1135-
.ptag = 0,
1136-
1137-
// space is complicated, we need room for tail, crc, globals,
1138-
// and we cap at half a block to give room for metadata updates
1139-
.begin = 0,
1140-
.end = lfs_min(
1141-
lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size),
1142-
lfs->cfg->block_size - 34),
1143-
};
1144-
11451157
// commit with a move
1146-
for (uint16_t id = begin; id < end; id++) {
1158+
for (uint16_t id = begin; id < end || commit.off < commit.ack; id++) {
11471159
err = lfs_commit_move(lfs, &commit,
11481160
0x003ff000, LFS_MKTAG(0, id, 0),
11491161
0x003ff000, LFS_MKTAG(0, id - begin, 0),
11501162
source, attrs);
1151-
if (err) {
1163+
if (err && !(splitted && err == LFS_ERR_NOSPC)) {
11521164
if (err == LFS_ERR_NOSPC) {
11531165
goto split;
11541166
} else if (err == LFS_ERR_CORRUPT) {
@@ -1157,12 +1169,25 @@ static int lfs_dir_compact(lfs_t *lfs,
11571169
return err;
11581170
}
11591171

1160-
ack = id;
1172+
ackid = id;
11611173
}
11621174

11631175
// reopen reserved space at the end
11641176
commit.end = lfs->cfg->block_size - 8;
11651177

1178+
if (ackid >= end) {
1179+
// extra garbage attributes were written out during split,
1180+
// need to clean up
1181+
err = lfs_commit_attr(lfs, &commit,
1182+
LFS_MKTAG(LFS_TYPE_DELETE, ackid, 0), NULL);
1183+
if (err) {
1184+
if (err == LFS_ERR_CORRUPT) {
1185+
goto relocate;
1186+
}
1187+
return err;
1188+
}
1189+
}
1190+
11661191
if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
11671192
// move over (duplicate) superblock if we are root
11681193
err = lfs_commit_move(lfs, &commit,
@@ -1178,8 +1203,8 @@ static int lfs_dir_compact(lfs_t *lfs,
11781203
}
11791204

11801205
if (!relocated) {
1181-
// commit any globals, unless we're relocating, in which case our
1182-
// parent will steal our globals
1206+
// commit any globals, unless we're relocating,
1207+
// in which case our parent will steal our globals
11831208
err = lfs_commit_globals(lfs, &commit, &dir->locals);
11841209
if (err) {
11851210
if (err == LFS_ERR_CORRUPT) {
@@ -1222,8 +1247,13 @@ static int lfs_dir_compact(lfs_t *lfs,
12221247
split:
12231248
// commit no longer fits, need to split dir,
12241249
// drop caches and create tail
1225-
lfs_cache_drop(lfs, &lfs->pcache);
1226-
if (!exhausted && ack < 0) {
1250+
splitted = !exhausted;
1251+
if (lfs->pcache.block != 0xffffffff) {
1252+
commit.ack -= lfs->pcache.size;
1253+
lfs_cache_drop(lfs, &lfs->pcache);
1254+
}
1255+
1256+
if (!exhausted && ackid < 0) {
12271257
// If we can't fit in this block, we won't fit in next block
12281258
return LFS_ERR_NOSPC;
12291259
}
@@ -1234,25 +1264,26 @@ static int lfs_dir_compact(lfs_t *lfs,
12341264
return err;
12351265
}
12361266

1237-
if (exhausted) {
1238-
lfs->root[0] = tail.pair[0];
1239-
lfs->root[1] = tail.pair[1];
1240-
}
1241-
12421267
tail.split = dir->split;
12431268
tail.tail[0] = dir->tail[0];
12441269
tail.tail[1] = dir->tail[1];
12451270

1246-
err = lfs_dir_compact(lfs, &tail, attrs, source, ack+1, end);
1271+
err = lfs_dir_compact(lfs, &tail, attrs, source, ackid+1, end);
12471272
if (err) {
12481273
return err;
12491274
}
12501275

1251-
end = ack+1;
1276+
end = ackid+1;
12521277
dir->tail[0] = tail.pair[0];
12531278
dir->tail[1] = tail.pair[1];
12541279
dir->split = true;
1255-
continue;
1280+
1281+
if (exhausted) {
1282+
lfs->root[0] = tail.pair[0];
1283+
lfs->root[1] = tail.pair[1];
1284+
}
1285+
1286+
goto commit;
12561287

12571288
relocate:
12581289
// commit was corrupted, drop caches and prepare to relocate block
@@ -1363,6 +1394,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
13631394

13641395
.begin = dir->off,
13651396
.end = lfs->cfg->block_size - 8,
1397+
.ack = 0,
13661398
};
13671399

13681400
for (const lfs_mattr_t *a = attrs; a; a = a->next) {

0 commit comments

Comments
 (0)