Browse Source

Added FCRC tags and an explanation of how FCRCs work to SPEC.md

See SPEC.md for more info.

Also considered adding an explanation to DESIGN.md, but there's not a
great place for it. Maybe FCRCs are too low-level for the high-level
design document. Though may be worth reconsidering if DESIGN.md gets
revisited.
Christopher Haster 2 years ago
parent
commit
3e25dfc16c
1 changed files with 90 additions and 11 deletions
  1. 90 11
      SPEC.md

+ 90 - 11
SPEC.md

@@ -1,10 +1,10 @@
 ## littlefs technical specification
 
-This is the technical specification of the little filesystem. This document
-covers the technical details of how the littlefs is stored on disk for
-introspection and tooling. This document assumes you are familiar with the
-design of the littlefs, for more info on how littlefs works check
-out [DESIGN.md](DESIGN.md).
+This is the technical specification of the little filesystem with on-disk
+version lfs2.1. This document covers the technical details of how the littlefs
+is stored on disk for introspection and tooling. This document assumes you are
+familiar with the design of the littlefs, for more info on how littlefs works
+check out [DESIGN.md](DESIGN.md).
 
 ```
    | | |     .---._____
@@ -133,12 +133,6 @@ tags XORed together, starting with `0xffffffff`.
 '-------------------'                    '-------------------'
 ```
 
-One last thing to note before we get into the details around tag encoding. Each
-tag contains a valid bit used to indicate if the tag and containing commit is
-valid. This valid bit is the first bit found in the tag and the commit and can
-be used to tell if we've attempted to write to the remaining space in the
-block.
-
 Here's a more complete example of metadata block containing 4 entries:
 
 ```
@@ -191,6 +185,53 @@ Here's a more complete example of metadata block containing 4 entries:
                                              '---- most recent D
 ```
 
+Two things to note before we get into the details around tag encoding:
+
+1. Each tag contains a valid bit used to indicate if the tag and containing
+   commit is valid. After XORing, this bit should always be zero.
+
+   At the end of each commit, the valid bit of the previous tag is XORed
+   with the lowest bit in the type field of the CRC tag. This allows
+   the CRC tag to force the next commit to fail the valid bit test if it
+   has not yet been written to.
+
+2. The valid bit alone is not enough info to know if the next commit has been
+   erased. We don't know the order bits will be programmed in a program block,
+   so it's possible that the next commit had an attempted program that left the
+   valid bit unchanged.
+
+   To ensure we only ever program erased bytes, each commit can contain an
+   optional forward-CRC (FCRC). An FCRC contains a checksum of some amount of
+   bytes in the next commit at the time it was erased.
+
+   ```
+   .-------------------. \      \
+   |  revision count   | |      |
+   |-------------------| |      |
+   |     metadata      | |      |
+   |                   | +---.  +-- current commit
+   |                   | |   |  |
+   |-------------------| |   |  |
+   |       FCRC       ---|-. |  |
+   |-------------------| / | |  |
+   |        CRC       -----|-'  /
+   |-------------------|   |
+   |      padding      |   |        padding (does't need CRC)
+   |                   |   |
+   |-------------------| \ |    \
+   |      erased?      | +-'    |
+   |         |         | |      +-- next commit
+   |         v         | /      |
+   |                   |        /
+   |                   |
+   '-------------------'
+   ```
+
+   If the FCRC is missing or the checksum does not match, we must assume a
+   commit was attempted but failed due to power-loss.
+
+   Note that end-of-block commits do not need an FCRC.
+
 ## Metadata tags
 
 So in littlefs, 32-bit tags describe every type of metadata. And this means
@@ -785,3 +826,41 @@ CRC fields:
    are made about the contents.
 
 ---
+#### `0x5ff` LFS_TYPE_FCRC
+
+Added in lfs2.1, the optional FCRC tag contains a checksum of some amount of
+bytes in the next commit at the time it was erased. This allows us to ensure
+that we only ever program erased bytes, even if a previous commit failed due
+to power-loss.
+
+When programming a commit, the FCRC size must be at least as large as the
+program block size. However, the program block is not saved on disk, and can
+change between mounts, so the FCRC size on disk may be different than the
+current program block size.
+
+If the FCRC is missing or the checksum does not match, we must assume a
+commit was attempted but failed due to power-loss.
+
+Layout of the FCRC tag:
+
+```
+        tag                          data
+[--      32      --][--      32      --|--      32      --]
+[1|- 11 -| 10 | 10 ][--      32      --|--      32      --]
+ ^    ^     ^    ^            ^- fcrc size       ^- fcrc
+ |    |     |    '- size (8)
+ |    |     '------ id (0x3ff)
+ |    '------------ type (0x5ff)
+ '----------------- valid bit
+```
+
+FCRC fields:
+
+1. **FCRC size (32-bits)** - Number of bytes after this commit's CRC tag's
+   padding to include in the FCRC.
+
+2. **FCRC (32-bits)** - CRC of the bytes after this commit's CRC tag's padding
+   when erased. Like the CRC tag, this uses a CRC-32 with a polynomial of
+   `0x04c11db7` initialized with `0xffffffff`.
+
+---