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.
This commit is contained in:
parent
9e28c75482
commit
3e25dfc16c
101
SPEC.md
101
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`.
|
||||
|
||||
---
|
||||
|
|
Loading…
Reference in New Issue