Skip to content

Commit 996cd8a

Browse files
committed
Revisited documentation
Mostly changed the wording around the goals/features of littlefs based on feedback from other developers. Also added in project links now that there are a few of those floating around. And made the README a bit easier to navigate.
1 parent 78c79ec commit 996cd8a

File tree

2 files changed

+95
-79
lines changed

2 files changed

+95
-79
lines changed

DESIGN.md

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## The design of the little filesystem
22

3-
The littlefs is a little fail-safe filesystem designed for embedded systems.
3+
A little fail-safe filesystem designed for embedded systems.
44

55
```
66
| | | .---._____
@@ -16,9 +16,9 @@ more about filesystem design by tackling the relative unsolved problem of
1616
managing a robust filesystem resilient to power loss on devices
1717
with limited RAM and ROM.
1818

19-
The embedded systems the littlefs is targeting are usually 32bit
20-
microcontrollers with around 32Kbytes of RAM and 512Kbytes of ROM. These are
21-
often paired with SPI NOR flash chips with about 4Mbytes of flash storage.
19+
The embedded systems the littlefs is targeting are usually 32 bit
20+
microcontrollers with around 32KB of RAM and 512KB of ROM. These are
21+
often paired with SPI NOR flash chips with about 4MB of flash storage.
2222

2323
Flash itself is a very interesting piece of technology with quite a bit of
2424
nuance. Unlike most other forms of storage, writing to flash requires two
@@ -32,17 +32,17 @@ has more information if you are interesting in how this works.
3232
This leaves us with an interesting set of limitations that can be simplified
3333
to three strong requirements:
3434

35-
1. **Fail-safe** - This is actually the main goal of the littlefs and the focus
36-
of this project. Embedded systems are usually designed without a shutdown
37-
routine and a notable lack of user interface for recovery, so filesystems
38-
targeting embedded systems should be prepared to lose power an any given
39-
time.
35+
1. **Power-loss resilient** - This is the main goal of the littlefs and the
36+
focus of this project. Embedded systems are usually designed without a
37+
shutdown routine and a notable lack of user interface for recovery, so
38+
filesystems targeting embedded systems must be prepared to lose power an
39+
any given time.
4040

4141
Despite this state of things, there are very few embedded filesystems that
42-
handle power loss in a reasonable manner, and can become corrupted if the
43-
user is unlucky enough.
42+
handle power loss in a reasonable manner, and most can become corrupted if
43+
the user is unlucky enough.
4444

45-
2. **Wear awareness** - Due to the destructive nature of flash, most flash
45+
2. **Wear leveling** - Due to the destructive nature of flash, most flash
4646
chips have a limited number of erase cycles, usually in the order of around
4747
100,000 erases per block for NOR flash. Filesystems that don't take wear
4848
into account can easily burn through blocks used to store frequently updated
@@ -78,9 +78,9 @@ summary of the general ideas behind some of them.
7878
Most of the existing filesystems fall into the one big category of filesystem
7979
designed in the early days of spinny magnet disks. While there is a vast amount
8080
of interesting technology and ideas in this area, the nature of spinny magnet
81-
disks encourage properties such as grouping writes near each other, that don't
81+
disks encourage properties, such as grouping writes near each other, that don't
8282
make as much sense on recent storage types. For instance, on flash, write
83-
locality is not as important and can actually increase wear destructively.
83+
locality is not important and can actually increase wear destructively.
8484

8585
One of the most popular designs for flash filesystems is called the
8686
[logging filesystem](https://en.wikipedia.org/wiki/Log-structured_file_system).
@@ -97,8 +97,7 @@ scaling as the size of storage increases. And most filesystems compensate by
9797
caching large parts of the filesystem in RAM, a strategy that is unavailable
9898
for embedded systems.
9999

100-
Another interesting filesystem design technique that the littlefs borrows the
101-
most from, is the [copy-on-write (COW)](https://en.wikipedia.org/wiki/Copy-on-write).
100+
Another interesting filesystem design technique is that of [copy-on-write (COW)](https://en.wikipedia.org/wiki/Copy-on-write).
102101
A good example of this is the [btrfs](https://en.wikipedia.org/wiki/Btrfs)
103102
filesystem. COW filesystems can easily recover from corrupted blocks and have
104103
natural protection against power loss. However, if they are not designed with
@@ -150,12 +149,12 @@ check our checksum we notice that block 1 was corrupted. So we fall back to
150149
block 2 and use the value 9.
151150

152151
Using this concept, the littlefs is able to update metadata blocks atomically.
153-
There are a few other tweaks, such as using a 32bit crc and using sequence
152+
There are a few other tweaks, such as using a 32 bit crc and using sequence
154153
arithmetic to handle revision count overflow, but the basic concept
155154
is the same. These metadata pairs define the backbone of the littlefs, and the
156155
rest of the filesystem is built on top of these atomic updates.
157156

158-
## Files
157+
## Non-meta data
159158

160159
Now, the metadata pairs do come with some drawbacks. Most notably, each pair
161160
requires two blocks for each block of data. I'm sure users would be very
@@ -224,12 +223,12 @@ Exhibit A: A linked-list
224223

225224
To get around this, the littlefs, at its heart, stores files backwards. Each
226225
block points to its predecessor, with the first block containing no pointers.
227-
If you think about this, it makes a bit of sense. Appending blocks just point
228-
to their predecessor and no other blocks need to be updated. If we update
229-
a block in the middle, we will need to copy out the blocks that follow,
230-
but can reuse the blocks before the modified block. Since most file operations
231-
either reset the file each write or append to files, this design avoids
232-
copying the file in the most common cases.
226+
If you think about for a while, it starts to make a bit of sense. Appending
227+
blocks just point to their predecessor and no other blocks need to be updated.
228+
If we update a block in the middle, we will need to copy out the blocks that
229+
follow, but can reuse the blocks before the modified block. Since most file
230+
operations either reset the file each write or append to files, this design
231+
avoids copying the file in the most common cases.
233232

234233
```
235234
Exhibit B: A backwards linked-list
@@ -351,7 +350,7 @@ file size doesn't have an obvious implementation.
351350

352351
We can start by just writing down an equation. The first idea that comes to
353352
mind is to just use a for loop to sum together blocks until we reach our
354-
file size. We can write equation equation as a summation:
353+
file size. We can write this equation as a summation:
355354

356355
![summation1](https://latex.codecogs.com/svg.latex?N%20%3D%20%5Csum_i%5En%5Cleft%5BB-%5Cfrac%7Bw%7D%7B8%7D%5Cleft%28%5Ctext%7Bctz%7D%28i%29+1%5Cright%29%5Cright%5D)
357356

@@ -374,7 +373,7 @@ The [On-Line Encyclopedia of Integer Sequences (OEIS)](https://oeis.org/).
374373
If we work out the first couple of values in our summation, we find that CTZ
375374
maps to [A001511](https://oeis.org/A001511), and its partial summation maps
376375
to [A005187](https://oeis.org/A005187), and surprisingly, both of these
377-
sequences have relatively trivial equations! This leads us to the completely
376+
sequences have relatively trivial equations! This leads us to a rather
378377
unintuitive property:
379378

380379
![mindblown](https://latex.codecogs.com/svg.latex?%5Csum_i%5En%5Cleft%28%5Ctext%7Bctz%7D%28i%29+1%5Cright%29%20%3D%202n-%5Ctext%7Bpopcount%7D%28n%29)
@@ -383,7 +382,7 @@ where:
383382
ctz(i) = the number of trailing bits that are 0 in i
384383
popcount(i) = the number of bits that are 1 in i
385384

386-
I find it bewildering that these two seemingly unrelated bitwise instructions
385+
It's a bit bewildering that these two seemingly unrelated bitwise instructions
387386
are related by this property. But if we start to disect this equation we can
388387
see that it does hold. As n approaches infinity, we do end up with an average
389388
overhead of 2 pointers as we find earlier. And popcount seems to handle the
@@ -1154,21 +1153,26 @@ develops errors and needs to be moved.
11541153

11551154
The second concern for the littlefs, is that blocks in the filesystem may wear
11561155
unevenly. In this situation, a filesystem may meet an early demise where
1157-
there are no more non-corrupted blocks that aren't in use. It may be entirely
1158-
possible that files were written once and left unmodified, wasting the
1159-
potential erase cycles of the blocks it sits on.
1156+
there are no more non-corrupted blocks that aren't in use. It's common to
1157+
have files that were written once and left unmodified, wasting the potential
1158+
erase cycles of the blocks it sits on.
11601159

11611160
Wear leveling is a term that describes distributing block writes evenly to
11621161
avoid the early termination of a flash part. There are typically two levels
11631162
of wear leveling:
1164-
1. Dynamic wear leveling - Blocks are distributed evenly during blocks writes.
1165-
Note that the issue with write-once files still exists in this case.
1166-
2. Static wear leveling - Unmodified blocks are evicted for new block writes.
1167-
This provides the longest lifetime for a flash device.
1168-
1169-
Now, it's possible to use the revision count on metadata pairs to approximate
1170-
the wear of a metadata block. And combined with the COW nature of files, the
1171-
littlefs could provide a form of dynamic wear leveling.
1163+
1. Dynamic wear leveling - Wear is distributed evenly across all **dynamic**
1164+
blocks. Usually this is accomplished by simply choosing the unused block
1165+
with the lowest amount of wear. Note this does not solve the problem of
1166+
static data.
1167+
2. Static wear leveling - Wear is distributed evenly across all **dynamic**
1168+
and **static** blocks. Unmodified blocks may be evicted for new block
1169+
writes. This does handle the problem of static data but may lead to
1170+
wear amplification.
1171+
1172+
In littlefs's case, it's possible to use the revision count on metadata pairs
1173+
to approximate the wear of a metadata block. And combined with the COW nature
1174+
of files, littlefs could provide your usually implementation of dynamic wear
1175+
leveling.
11721176

11731177
However, the littlefs does not. This is for a few reasons. Most notably, even
11741178
if the littlefs did implement dynamic wear leveling, this would still not
@@ -1179,19 +1183,20 @@ As a flash device reaches the end of its life, the metadata blocks will
11791183
naturally be the first to go since they are updated most often. In this
11801184
situation, the littlefs is designed to simply move on to another set of
11811185
metadata blocks. This travelling means that at the end of a flash device's
1182-
life, the filesystem will have worn the device down as evenly as a dynamic
1183-
wear leveling filesystem could anyways. Simply put, if the lifetime of flash
1184-
is a serious concern, static wear leveling is the only valid solution.
1186+
life, the filesystem will have worn the device down nearly as evenly as the
1187+
usual dynamic wear leveling could. More aggressive wear leveling would come
1188+
with a code-size cost for marginal benefit.
1189+
11851190

1186-
This is a very important takeaway to note. If your storage stack uses highly
1187-
sensitive storage such as NAND flash. In most cases you are going to be better
1188-
off just using a [flash translation layer (FTL)](https://en.wikipedia.org/wiki/Flash_translation_layer).
1191+
One important takeaway to note, if your storage stack uses highly sensitive
1192+
storage such as NAND flash, static wear leveling is the only valid solution.
1193+
In most cases you are going to be better off using a full [flash translation layer (FTL)](https://en.wikipedia.org/wiki/Flash_translation_layer).
11891194
NAND flash already has many limitations that make it poorly suited for an
11901195
embedded system: low erase cycles, very large blocks, errors that can develop
11911196
even during reads, errors that can develop during writes of neighboring blocks.
11921197
Managing sensitive storage such as NAND flash is out of scope for the littlefs.
11931198
The littlefs does have some properties that may be beneficial on top of a FTL,
1194-
such as limiting the number of writes where possible. But if you have the
1199+
such as limiting the number of writes where possible, but if you have the
11951200
storage requirements that necessitate the need of NAND flash, you should have
11961201
the RAM to match and just use an FTL or flash filesystem.
11971202

README.md

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,17 @@ A little fail-safe filesystem designed for embedded systems.
1111
| | |
1212
```
1313

14-
**Fail-safe** - The littlefs is designed to work consistently with random
15-
power failures. During filesystem operations the storage on disk is always
16-
kept in a valid state. The filesystem also has strong copy-on-write garuntees.
17-
When updating a file, the original file will remain unmodified until the
18-
file is closed, or sync is called.
19-
20-
**Wear awareness** - While the littlefs does not implement static wear
21-
leveling, the littlefs takes into account write errors reported by the
22-
underlying block device and uses a limited form of dynamic wear leveling
23-
to manage blocks that go bad during the lifetime of the filesystem.
24-
25-
**Bounded ram/rom** - The littlefs is designed to work in a
26-
limited amount of memory, recursion is avoided, and dynamic memory is kept
27-
to a minimum. The littlefs allocates two fixed-size buffers for general
28-
operations, and one fixed-size buffer per file. If there is only ever one file
29-
in use, all memory can be provided statically and the littlefs can be used
30-
in a system without dynamic memory.
14+
**Bounded RAM/ROM** - The littlefs is designed to work with a limited amount
15+
of memory. Recursion is avoided and dynamic memory is limited to configurable
16+
buffers that can be provided statically.
17+
18+
**Power-loss resilient** - The littlefs is designed for systems that may have
19+
random power failures. The littlefs has strong copy-on-write guaruntees and
20+
storage on disk is always kept in a valid state.
21+
22+
**Wear leveling** - Since the most common form of embedded storage is erodible
23+
flash memories, littlefs provides a form of dynamic wear leveling for systems
24+
that can not fit a full flash translation layer.
3125

3226
## Example
3327

@@ -96,43 +90,44 @@ int main(void) {
9690
Detailed documentation (or at least as much detail as is currently available)
9791
can be cound in the comments in [lfs.h](lfs.h).
9892
99-
As you may have noticed, the littlefs takes in a configuration structure that
93+
As you may have noticed, littlefs takes in a configuration structure that
10094
defines how the filesystem operates. The configuration struct provides the
10195
filesystem with the block device operations and dimensions, tweakable
10296
parameters that tradeoff memory usage for performance, and optional
10397
static buffers if the user wants to avoid dynamic memory.
10498
10599
The state of the littlefs is stored in the `lfs_t` type which is left up
106100
to the user to allocate, allowing multiple filesystems to be in use
107-
simultaneously. With the `lfs_t` and configuration struct, a user can either
101+
simultaneously. With the `lfs_t` and configuration struct, a user can
108102
format a block device or mount the filesystem.
109103
110104
Once mounted, the littlefs provides a full set of posix-like file and
111105
directory functions, with the deviation that the allocation of filesystem
112-
structures must be provided by the user. An important addition is that
113-
no file updates will actually be written to disk until a sync or close
114-
is called.
106+
structures must be provided by the user.
107+
108+
All posix operations, such as remove and rename, are atomic, even in event
109+
of power-loss. Additionally, no file updates are actually commited to the
110+
filesystem until sync or close is called on the file.
115111
116112
## Other notes
117113
118114
All littlefs have the potential to return a negative error code. The errors
119115
can be either one of those found in the `enum lfs_error` in [lfs.h](lfs.h),
120116
or an error returned by the user's block device operations.
121117
122-
It should also be noted that the littlefs does not do anything to insure
123-
that the data written to disk is machine portable. It should be fine as
124-
long as the machines involved share endianness and don't have really
125-
strange padding requirements. If the question does come up, the littlefs
126-
metadata should be stored on disk in little-endian format.
118+
It should also be noted that the current implementation of littlefs doesn't
119+
really do anything to insure that the data written to disk is machine portable.
120+
This is fine as long as all of the involved machines share endianness
121+
(little-endian) and don't have strange padding requirements.
122+
123+
## Reference material
127124
128-
## Design
125+
[DESIGN.md](DESIGN.md) - DESIGN.md contains a fully detailed dive into how
126+
littlefs actually works. I would encourage you to read it since the
127+
solutions and tradeoffs at work here are quite interesting.
129128
130-
the littlefs was developed with the goal of learning more about filesystem
131-
design by tackling the relative unsolved problem of managing a robust
132-
filesystem resilient to power loss on devices with limited RAM and ROM.
133-
More detail on the solutions and tradeoffs incorporated into this filesystem
134-
can be found in [DESIGN.md](DESIGN.md). The specification for the layout
135-
of the filesystem on disk can be found in [SPEC.md](SPEC.md).
129+
[SPEC.md](SPEC.md) - SPEC.md contains the on-disk specification of littlefs
130+
with all the nitty-gritty details. Can be useful for developing tooling.
136131
137132
## Testing
138133
@@ -143,3 +138,19 @@ The tests assume a linux environment and can be started with make:
143138
``` bash
144139
make test
145140
```
141+
142+
## Related projects
143+
144+
[mbed-littlefs](https://github.com/armmbed/mbed-littlefs) - The easiest way to
145+
get started with littlefs is to jump into [mbed](https://os.mbed.com/), which
146+
already has block device drivers for most forms of embedded storage. The
147+
mbed-littlefs provides the mbed wrapper for littlefs.
148+
149+
[littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse)
150+
wrapper for littlefs. The project allows you to mount littlefs directly in a
151+
Linux machine. Can be useful for debugging littlefs if you have an SD card
152+
handy.
153+
154+
[littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for
155+
littlefs. I'm not sure why you would want this, but it is handy for demos.
156+
You can see it in action [here](http://littlefs.geky.net/demo.html).

0 commit comments

Comments
 (0)