Skip to content

Commit

Permalink
Update littleFS to 2.1.4
Browse files Browse the repository at this point in the history
CL: vfs-fs-lfs: Update littleFS to 2.1.4
  • Loading branch information
rojer committed Dec 7, 2019
1 parent 4f20275 commit f5d1ef5
Show file tree
Hide file tree
Showing 13 changed files with 343 additions and 170 deletions.
27 changes: 12 additions & 15 deletions littlefs/.travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ script:
# run tests with a few different configurations
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=4"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=8 -DLFS_CACHE_SIZE=16 -DLFS_BLOCK_CYCLES=2"
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"

- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
Expand Down Expand Up @@ -248,34 +249,30 @@ jobs:
-m "Generated v$LFS_VERSION_MAJOR prefixes")
git reset --hard
# Update major version branches (vN and vN-prefix)
git push https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
git push --atomic https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
v$LFS_VERSION_MAJOR \
v$LFS_VERSION_MAJOR-prefix
# Create patch version tag (vN.N.N)
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
-d "{
\"ref\": \"refs/tags/$LFS_VERSION\",
\"sha\": \"$TRAVIS_COMMIT\"
}"
# Create minor release?
[[ "$LFS_VERSION" == *.0 ]] || exit 0
# Build release notes
PREV=$(git tag --sort=-v:refname -l "v*.0" | head -1)
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
if [ ! -z "$PREV" ]
then
echo "PREV $PREV"
CHANGES=$'### Changes\n\n'$( \
git log --oneline $PREV.. --grep='^Merge' --invert-grep)
CHANGES=$(git log --oneline $PREV.. --grep='^Merge' --invert-grep)
printf "CHANGES\n%s\n\n" "$CHANGES"
fi
# Create the release
case ${GEKY_BOT_DRAFT:-minor} in
true) DRAFT=true ;;
minor) DRAFT=$(jq -R 'endswith(".0")' <<< "$LFS_VERSION") ;;
false) DRAFT=false ;;
esac
# Create the release and patch version tag (vN.N.N)
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
-d "{
\"tag_name\": \"$LFS_VERSION\",
\"name\": \"${LFS_VERSION%.0}\",
\"draft\": true,
\"target_commitish\": \"$TRAVIS_COMMIT\",
\"draft\": $DRAFT,
\"body\": $(jq -sR '.' <<< "$CHANGES")
}" #"
SCRIPT
Expand Down
54 changes: 27 additions & 27 deletions littlefs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ have weaknesses that limit their usefulness. But if we merge the two they can
mutually solve each other's limitations.

This is the idea behind littlefs. At the sub-block level, littlefs is built
out of small, two blocks logs that provide atomic updates to metadata anywhere
out of small, two block logs that provide atomic updates to metadata anywhere
on the filesystem. At the super-block level, littlefs is a CObW tree of blocks
that can be evicted on demand.

Expand Down Expand Up @@ -676,7 +676,7 @@ block, this cost is fairly reasonable.
---

This is a new data structure, so we still have several questions. What is the
storage overage? Can the number of pointers exceed the size of a block? How do
storage overhead? Can the number of pointers exceed the size of a block? How do
we store a CTZ skip-list in our metadata pairs?

To find the storage overhead, we can look at the data structure as multiple
Expand Down Expand Up @@ -742,8 +742,8 @@ where:
2. popcount(![x]) = the number of bits that are 1 in ![x]

Initial tests of this surprising property seem to hold. As ![n] approaches
infinity, we end up with an average overhead of 2 pointers, which matches what
our assumption from earlier. During iteration, the popcount function seems to
infinity, we end up with an average overhead of 2 pointers, which matches our
assumption from earlier. During iteration, the popcount function seems to
handle deviations from this average. Of course, just to make sure I wrote a
quick script that verified this property for all 32-bit integers.

Expand All @@ -767,7 +767,7 @@ overflow, but we can avoid this by rearranging the equation a bit:

![off = N - (B-2w/8)n - (w/8)popcount(n)][ctz-formula7]

Our solution requires quite a bit of math, but computer are very good at math.
Our solution requires quite a bit of math, but computers are very good at math.
Now we can find both our block index and offset from a size in _O(1)_, letting
us store CTZ skip-lists with only a pointer and size.

Expand Down Expand Up @@ -850,7 +850,7 @@ nearly every write to the filesystem.

Normally, block allocation involves some sort of free list or bitmap stored on
the filesystem that is updated with free blocks. However, with power
resilience, keeping these structure consistent becomes difficult. It doesn't
resilience, keeping these structures consistent becomes difficult. It doesn't
help that any mistake in updating these structures can result in lost blocks
that are impossible to recover.

Expand Down Expand Up @@ -894,9 +894,9 @@ high-risk error conditions.
---

Our block allocator needs to find free blocks efficiently. You could traverse
through every block on storage and check each one against our filesystem tree,
however the runtime would be abhorrent. We need to somehow collect multiple
blocks each traversal.
through every block on storage and check each one against our filesystem tree;
however, the runtime would be abhorrent. We need to somehow collect multiple
blocks per traversal.

Looking at existing designs, some larger filesystems that use a similar "drop
it on the floor" strategy store a bitmap of the entire storage in [RAM]. This
Expand All @@ -920,8 +920,8 @@ a brute force traversal. Instead of a bitmap the size of storage, we keep track
of a small, fixed-size bitmap called the lookahead buffer. During block
allocation, we take blocks from the lookahead buffer. If the lookahead buffer
is empty, we scan the filesystem for more free blocks, populating our lookahead
buffer. Each scan we use an increasing offset, circling the storage as blocks
are allocated.
buffer. In each scan we use an increasing offset, circling the storage as
blocks are allocated.

Here's what it might look like to allocate 4 blocks on a decently busy
filesystem with a 32 bit lookahead and a total of 128 blocks (512 KiB
Expand Down Expand Up @@ -950,7 +950,7 @@ alloc = 112 lookahead: ffff8000
```

This lookahead approach has a runtime complexity of _O(n&sup2;)_ to completely
scan storage, however, bitmaps are surprisingly compact, and in practice only
scan storage; however, bitmaps are surprisingly compact, and in practice only
one or two passes are usually needed to find free blocks. Additionally, the
performance of the allocator can be optimized by adjusting the block size or
size of the lookahead buffer, trading either write granularity or RAM for
Expand Down Expand Up @@ -1173,9 +1173,9 @@ We may find that the new block is also bad, but hopefully after repeating this
cycle we'll eventually find a new block where a write succeeds. If we don't,
that means that all blocks in our storage are bad, and we've reached the end of
our device's usable life. At this point, littlefs will return an "out of space"
error, which is technically true, there are no more good blocks, but as an
added benefit also matches the error condition expected by users of dynamically
sized data.
error. This is technically true, as there are no more good blocks, but as an
added benefit it also matches the error condition expected by users of
dynamically sized data.

---

Expand All @@ -1187,7 +1187,7 @@ original data even after it has been corrupted. One such mechanism for this is
ECC is an extension to the idea of a checksum. Where a checksum such as CRC can
detect that an error has occurred in the data, ECC can detect and actually
correct some amount of errors. However, there is a limit to how many errors ECC
can detect, call the [Hamming bound][wikipedia-hamming-bound]. As the number of
can detect: the [Hamming bound][wikipedia-hamming-bound]. As the number of
errors approaches the Hamming bound, we may still be able to detect errors, but
can no longer fix the data. If we've reached this point the block is
unrecoverable.
Expand All @@ -1202,7 +1202,7 @@ chip itself.
In littlefs, ECC is entirely optional. Read errors can instead be prevented
proactively by wear leveling. But it's important to note that ECC can be used
at the block device level to modestly extend the life of a device. littlefs
respects any errors reported by the block device, allow a block device to
respects any errors reported by the block device, allowing a block device to
provide additional aggressive error detection.

---
Expand Down Expand Up @@ -1231,7 +1231,7 @@ Generally, wear leveling algorithms fall into one of two categories:
we need to consider all blocks, including blocks that already contain data.

As a tradeoff for code size and complexity, littlefs (currently) only provides
dynamic wear leveling. This is a best efforts solution. Wear is not distributed
dynamic wear leveling. This is a best effort solution. Wear is not distributed
perfectly, but it is distributed among the free blocks and greatly extends the
life of a device.

Expand Down Expand Up @@ -1378,7 +1378,7 @@ We can make several improvements. First, instead of giving each file its own
metadata pair, we can store multiple files in a single metadata pair. One way
to do this is to directly associate a directory with a metadata pair (or a
linked list of metadata pairs). This makes it easy for multiple files to share
the directory's metadata pair for logging and reduce the collective storage
the directory's metadata pair for logging and reduces the collective storage
overhead.

The strict binding of metadata pairs and directories also gives users
Expand Down Expand Up @@ -1816,12 +1816,12 @@ while manipulating the directory tree (foreshadowing!).

## The move problem

We have one last challenge. The move problem. Phrasing the problem is simple:
We have one last challenge: the move problem. Phrasing the problem is simple:

How do you atomically move a file between two directories?

In littlefs we can atomically commit to directories, but we can't create
an atomic commit that span multiple directories. The filesystem must go
an atomic commit that spans multiple directories. The filesystem must go
through a minimum of two distinct states to complete a move.

To make matters worse, file moves are a common form of synchronization for
Expand All @@ -1831,13 +1831,13 @@ atomic moves right.
So what can we do?

- We definitely can't just let power-loss result in duplicated or lost files.
This could easily break user's code and would only reveal itself in extreme
This could easily break users' code and would only reveal itself in extreme
cases. We were only able to be lazy about the threaded linked-list because
it isn't user facing and we can handle the corner cases internally.

- Some filesystems propagate COW operations up the tree until finding a common
parent. Unfortunately this interacts poorly with our threaded tree and brings
back the issue of upward propagation of wear.
- Some filesystems propagate COW operations up the tree until a common parent
is found. Unfortunately this interacts poorly with our threaded tree and
brings back the issue of upward propagation of wear.

- In a previous version of littlefs we tried to solve this problem by going
back and forth between the source and destination, marking and unmarking the
Expand All @@ -1852,7 +1852,7 @@ introduction of a mechanism called "global state".
---

Global state is a small set of state that can be updated from _any_ metadata
pair. Combining global state with metadata pair's ability to update multiple
pair. Combining global state with metadata pairs' ability to update multiple
entries in one commit gives us a powerful tool for crafting complex atomic
operations.

Expand Down Expand Up @@ -1910,7 +1910,7 @@ the filesystem is mounted.

You may have noticed that global state is very expensive. We keep a copy in
RAM and a delta in an unbounded number of metadata pairs. Even if we reset
the global state to its initial value we can't easily clean up the deltas on
the global state to its initial value, we can't easily clean up the deltas on
disk. For this reason, it's very important that we keep the size of global
state bounded and extremely small. But, even with a strict budget, global
state is incredibly valuable.
Expand Down
3 changes: 2 additions & 1 deletion littlefs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ override CFLAGS += -DLFS_YES_TRACE
endif
override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init
override CFLAGS += -Wextra -Wshadow -Wjump-misses-init -Wundef
# Remove missing-field-initializers because of GCC bug
override CFLAGS += -Wno-missing-field-initializers

Expand All @@ -55,6 +55,7 @@ test: \
test_attrs \
test_move \
test_orphan \
test_relocations \
test_corrupt
@rm test.c
test_%: tests/test_%.sh
Expand Down
3 changes: 2 additions & 1 deletion littlefs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const struct lfs_config cfg = {
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};

// entry point
Expand Down Expand Up @@ -109,7 +110,7 @@ directory functions, with the deviation that the allocation of filesystem
structures must be provided by the user.
All POSIX operations, such as remove and rename, are atomic, even in event
of power-loss. Additionally, no file updates are not actually committed to
of power-loss. Additionally, file updates are not actually committed to
the filesystem until sync or close is called on the file.
## Other notes
Expand Down
23 changes: 17 additions & 6 deletions littlefs/emubd/lfs_emubd.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
*/
#include "emubd/lfs_emubd.h"

#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include <inttypes.h>


// Emulated block device utils
Expand Down Expand Up @@ -102,6 +102,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_create -> %"PRId32, err);
fclose(f);
return err;
}

Expand All @@ -124,6 +125,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_create -> %"PRId32, err);
fclose(f);
return err;
}

Expand Down Expand Up @@ -178,13 +180,15 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
if (err) {
err = -errno;
LFS_TRACE("lfs_emubd_read -> %d", err);
fclose(f);
return err;
}

size_t res = fread(data, 1, size, f);
if (res < size && !feof(f)) {
err = -errno;
LFS_TRACE("lfs_emubd_read -> %d", err);
fclose(f);
return err;
}

Expand Down Expand Up @@ -230,20 +234,23 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (err) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}

size_t res = fwrite(data, 1, size, f);
if (res < size) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}

err = fseek(f, off, SEEK_SET);
if (err) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}

Expand All @@ -252,6 +259,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}

Expand Down Expand Up @@ -340,6 +348,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
if (res < 1) {
int err = -errno;
LFS_TRACE("lfs_emubd_sync -> %d", err);
fclose(f);
return err;
}

Expand All @@ -364,6 +373,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_sync -> %d", err);
fclose(f);
return err;
}

Expand All @@ -388,6 +398,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_sync -> %d", err);
fclose(f);
return err;
}

Expand Down
Loading

0 comments on commit f5d1ef5

Please sign in to comment.