@@ -72,7 +72,7 @@ to three strong requirements:
72
72
73
73
## Existing designs?
74
74
75
- There are of course, many different existing filesystem. Heres a very rough
75
+ There are of course, many different existing filesystem. Here is a very rough
76
76
summary of the general ideas behind some of them.
77
77
78
78
Most of the existing filesystems fall into the one big category of filesystem
@@ -91,7 +91,7 @@ the changes to the files are stored on disk. This has several neat advantages,
91
91
such as the fact that the data is written in a cyclic log format naturally
92
92
wear levels as a side effect. And, with a bit of error detection, the entire
93
93
filesystem can easily be designed to be resilient to power loss. The
94
- journalling component of most modern day filesystems is actually a reduced
94
+ journaling component of most modern day filesystems is actually a reduced
95
95
form of a logging filesystem. However, logging filesystems have a difficulty
96
96
scaling as the size of storage increases. And most filesystems compensate by
97
97
caching large parts of the filesystem in RAM, a strategy that is unavailable
@@ -114,7 +114,7 @@ pairs, so that at any time there is always a backup containing the previous
114
114
state of the metadata.
115
115
116
116
Consider a small example where each metadata pair has a revision count,
117
- a number as data, and the xor of the block as a quick checksum. If
117
+ a number as data, and the XOR of the block as a quick checksum. If
118
118
we update the data to a value of 9, and then to a value of 5, here is
119
119
what the pair of blocks may look like after each update:
120
120
```
@@ -149,7 +149,7 @@ check our checksum we notice that block 1 was corrupted. So we fall back to
149
149
block 2 and use the value 9.
150
150
151
151
Using this concept, the littlefs is able to update metadata blocks atomically.
152
- There are a few other tweaks, such as using a 32 bit crc and using sequence
152
+ There are a few other tweaks, such as using a 32 bit CRC and using sequence
153
153
arithmetic to handle revision count overflow, but the basic concept
154
154
is the same. These metadata pairs define the backbone of the littlefs, and the
155
155
rest of the filesystem is built on top of these atomic updates.
@@ -289,15 +289,15 @@ The path to data block 0 is even more quick, requiring only two jumps:
289
289
290
290
We can find the runtime complexity by looking at the path to any block from
291
291
the block containing the most pointers. Every step along the path divides
292
- the search space for the block in half. This gives us a runtime of O(logn ).
292
+ the search space for the block in half. This gives us a runtime of O(log n ).
293
293
To get to the block with the most pointers, we can perform the same steps
294
- backwards, which puts the runtime at O(2logn ) = O(logn ). The interesting
294
+ backwards, which puts the runtime at O(2 log n ) = O(log n ). The interesting
295
295
part about this data structure is that this optimal path occurs naturally
296
296
if we greedily choose the pointer that covers the most distance without passing
297
297
our target block.
298
298
299
299
So now we have a representation of files that can be appended trivially with
300
- a runtime of O(1), and can be read with a worst case runtime of O(nlogn ).
300
+ a runtime of O(1), and can be read with a worst case runtime of O(n log n ).
301
301
Given that the the runtime is also divided by the amount of data we can store
302
302
in a block, this is pretty reasonable.
303
303
@@ -362,7 +362,7 @@ N = file size in bytes
362
362
363
363
And this works quite well, but is not trivial to calculate. This equation
364
364
requires O(n) to compute, which brings the entire runtime of reading a file
365
- to O(n^2logn ). Fortunately, the additional O(n) does not need to touch disk,
365
+ to O(n^2 log n ). Fortunately, the additional O(n) does not need to touch disk,
366
366
so it is not completely unreasonable. But if we could solve this equation into
367
367
a form that is easily computable, we can avoid a big slowdown.
368
368
@@ -383,7 +383,7 @@ ctz(i) = the number of trailing bits that are 0 in i
383
383
popcount(i) = the number of bits that are 1 in i
384
384
385
385
It's a bit bewildering that these two seemingly unrelated bitwise instructions
386
- are related by this property. But if we start to disect this equation we can
386
+ are related by this property. But if we start to dissect this equation we can
387
387
see that it does hold. As n approaches infinity, we do end up with an average
388
388
overhead of 2 pointers as we find earlier. And popcount seems to handle the
389
389
error from this average as it accumulates in the CTZ skip-list.
@@ -503,7 +503,7 @@ However, this approach had several issues:
503
503
- There was a lot of nuanced logic for adding blocks to the free list without
504
504
modifying the blocks, since the blocks remain active until the metadata is
505
505
updated.
506
- - The free list had to support both additions and removals in fifo order while
506
+ - The free list had to support both additions and removals in FIFO order while
507
507
minimizing block erases.
508
508
- The free list had to handle the case where the file system completely ran
509
509
out of blocks and may no longer be able to add blocks to the free list.
@@ -622,7 +622,7 @@ So, as a solution, the littlefs adopted a sort of threaded tree. Each
622
622
directory not only contains pointers to all of its children, but also a
623
623
pointer to the next directory. These pointers create a linked-list that
624
624
is threaded through all of the directories in the filesystem. Since we
625
- only use this linked list to check for existance , the order doesn't actually
625
+ only use this linked list to check for existence , the order doesn't actually
626
626
matter. As an added plus, we can repurpose the pointer for the individual
627
627
directory linked-lists and avoid using any additional space.
628
628
@@ -773,7 +773,7 @@ deorphan step that simply iterates through every directory in the linked-list
773
773
and checks it against every directory entry in the filesystem to see if it
774
774
has a parent. The deorphan step occurs on the first block allocation after
775
775
boot, so orphans should never cause the littlefs to run out of storage
776
- prematurely. Note that the deorphan step never needs to run in a readonly
776
+ prematurely. Note that the deorphan step never needs to run in a read-only
777
777
filesystem.
778
778
779
779
## The move problem
@@ -883,7 +883,7 @@ a power loss will occur during filesystem activity. We still need to handle
883
883
the condition, but runtime during a power loss takes a back seat to the runtime
884
884
during normal operations.
885
885
886
- So what littlefs does is unelegantly simple. When littlefs moves a file, it
886
+ So what littlefs does is inelegantly simple. When littlefs moves a file, it
887
887
marks the file as "moving". This is stored as a single bit in the directory
888
888
entry and doesn't take up much space. Then littlefs moves the directory,
889
889
finishing with the complete remove of the "moving" directory entry.
@@ -979,7 +979,7 @@ if it exists elsewhere in the filesystem.
979
979
So now that we have all of the pieces of a filesystem, we can look at a more
980
980
subtle attribute of embedded storage: The wear down of flash blocks.
981
981
982
- The first concern for the littlefs, is that prefectly valid blocks can suddenly
982
+ The first concern for the littlefs, is that perfectly valid blocks can suddenly
983
983
become unusable. As a nice side-effect of using a COW data-structure for files,
984
984
we can simply move on to a different block when a file write fails. All
985
985
modifications to files are performed in copies, so we will only replace the
@@ -1210,7 +1210,7 @@ So, to summarize:
1210
1210
metadata block is active
1211
1211
4 . Directory blocks contain either references to other directories or files
1212
1212
5 . Files are represented by copy-on-write CTZ skip-lists which support O(1)
1213
- append and O(nlogn ) reading
1213
+ append and O(n log n ) reading
1214
1214
6 . Blocks are allocated by scanning the filesystem for used blocks in a
1215
1215
fixed-size lookahead region is that stored in a bit-vector
1216
1216
7 . To facilitate scanning the filesystem, all directories are part of a
0 commit comments