Added block-level erased-state checksums

Much like the erased-state checksums in our rbyds (ecksums), these
block-level erased-state checksums (becksums) allow us to detect failed
progs to erased parts of a block and are key to achieving efficient
incremental write performance with large blocks and frequent power
cycles/open-close cycles.

These are also key to achieving _reasonable_ write performance for
simple writes (linear, non-overwriting), since littlefs now relies
solely on becksums to efficiently append to blocks.

Though I suppose the previous block staging logic used with the CTZ
skip-list could be brought back to make becksums optional and avoid
btree lookups during simple writes (we do a _lot_ of btree
lookups)... I'll leave this open as a future optimization...

Unlike in-rbyd ecksums, becksums need to be stored out-of-band so our
data blocks only contain raw data. Since they are optional, an
additional tag in the file's btree makes sense.

Becksums are relatively simple, but they bring some challenges:

1. Adding becksums to file btrees is the first case we have for multiple
   struct tags per btree id.

   This isn't too complicated a problem, but requires some new internal
   btree APIs.

   Looking forward, which I probably shouldn't be doing this often,
   multiple struct tags will also be useful for parity and content ids
   as a part of data redundancy and data deduplication, though I think
   it's uncontroversial to consider this both heavier-weight features...

2. Becksums only work if unfilled blocks are aligned to the prog_size.

   This is the whole point of crystal_size -- to provide temporary
   storage for unaligned writes -- but actually aligning the block
   during writes turns out to be a bit tricky without a bunch of
   unecesssary btree lookups (we already do too many btree lookups!).

   The current implementation here discards the pcache to force
   alignment, taking advantage of the requirement that
   cache_size >= prog_size, but this is corrupting our block checksums.

Code cost:

           code          stack
  before: 31248           2792
  after:  32060 (+2.5%)   2864 (+2.5%)

Also lfsr_ftree_flush needs work. I'm usually open to gotos in C when
they improve internal logic, but even for me, the multiple goto jumps
from every left-neighbor lookup into the block writing loop is a bit
much...
This commit is contained in:
Christopher Haster
2023-12-13 12:19:01 -06:00
parent 26afd8b118
commit f29a4982c4
9 changed files with 382 additions and 174 deletions
+20 -7
View File
@@ -453,6 +453,20 @@ typedef struct lfsr_data {
} u;
} lfsr_data_t;
typedef struct lfsr_bptr {
// note data.size lines up with weight in lfsr_btree_t
lfsr_data_t data;
lfs_size_t cksize;
uint32_t cksum;
} lfsr_bptr_t;
// erased-state checksum
typedef struct lfsr_ecksum {
// size=-1 indicates no ecksum
lfs_ssize_t size;
uint32_t cksum;
} lfsr_ecksum_t;
// littlefs directory type
typedef struct lfs_dir {
struct lfs_dir *next;
@@ -500,13 +514,11 @@ typedef struct lfsr_bsprout {
lfsr_data_t data_;
} lfsr_bsprout_t;
typedef struct lfsr_bptr {
// note data.size lines up with weight in lfsr_btree_t
lfsr_data_t data;
lfs_size_t cksize;
uint32_t cksum;
// TODO how do we track ecksum?
} lfsr_bptr_t;
// a bleaf is just a bptr with all optional attrs
typedef struct lfsr_bleaf {
lfsr_bptr_t bptr;
lfsr_ecksum_t becksum;
} lfsr_bleaf_t;
// bshrubs must always be associated with an mdir
//
@@ -527,6 +539,7 @@ typedef struct lfsr_ftree {
lfsr_data_t data;
lfsr_bsprout_t bsprout;
lfsr_bptr_t bptr;
lfsr_bleaf_t bleaf;
lfsr_bshrub_t bshrub;
lfsr_btree_t btree;
} u;