- Need to drop cache during block_size search.
- Removed "Corrupted" messages when root is not found to avoid too much debug
output. This also gets rid of a long-term point of confusion arround
"Corrupted" messages on a mount that is intended to fail.
This is necessary for accessing format-specific information stored in
the superblock after mounting.
As is common in other filesystems this also provides the filesystem
usage. However collecting the filesystem usage is more expensive in
littlefs than other filesystem (and less accurate because of CoW), so
this may need to be cached after mount in the future.
This ended up needing a bit of API rework since littlefs no longer needs
to know the actual erase_count. We can no longer rely on lfs_config to
contain all the information necessary to configure the block devices.
Changing the lfs_bd_config structs to be required is probably a good
idea anyways as it moves us more towards separating the bds from
littlefs, though we can't quite get rid of the lfs_config parameter
because of the block-device API in lfs_config. Eventually it would be
nice to get rid of it, but that would require API breakage.
This adds two new configuration options: erase_size and erase_count,
allowing block_size and block_count to be loaded from the superblock
during mount. For backwards compability these default to block_size and
block_count if zero.
---
Unfortunately this is a bit easier said than done. littlefs keeps its
superblock in the metadata pair located at blocks {0,1}, which is also
where the root directory lives (keep in mind small littlefs images may
only have 2 blocks in total). If we mutate blocks {0,1}, we have to
erase before programming, which means it's possible to have the only
superblock in block 1. This presents a puzzle because how do you find
block 1 if you don't know the size of a block?
One solution presented here is to search for block 1 by trying different
sizes until we find a superblock. This isn't great but there are some
properties of this search that help:
1. If we do find a superblock, the search will never take longer than a
mount with a known block_size. This is because we stop at block 1,
searching at most O(block_size) bytes, and metadata fetch is already
a O(block_size) operation.
This means the concern is limited to how long it takes to fail when
littlefs is not present on the disk.
2. We can assume the on-disk block_size is probably a factor of the
total size of the disk. After a bit of digging into the math, this
apparently reduces the runtime to the divisor function, d(n), which
is sublinear.
According to a blog post by Terence Tao this is bounded by the
ridiculous O(e^O(log(n)/log(log(n)))):
https://terrytao.wordpress.com/2008/09/23/the-divisor-bound
This is apparently somewhere between O(sqrt(n)) and O(log(n)), but
conveniently O(log(n)) on average and O(log(n)) for powers of 2.
I've left it as O(d(n)) in the documentation, which might be a bit
confusing, but I'm not sure how best to capture "mostly log(n)"
correctly.
3. If we don't know the block_size, or don't know that block_size is
aligned to the disk size, the best we can do is a O(n) search.
In this case I've added a warning, so at least it's distinguishable
from an infinite loop if debugging.
- Fixed prettyasserts.py parsing when '->' is in expr
- Made prettyasserts.py failures not crash (yay dynamic typing)
- Fixed the initial state of the emubd disk file to match the internal
state in RAM
- Fixed true/false getting changed to True/False in test.py/bench.py
defines
- Fixed accidental substring matching in plot.py's --by comparison
- Fixed a missed LFS_BLOCk_CYCLES in test_superblocks.toml that was
missed
- Changed test.py/bench.py -v to only show commands being run
Including the test output is still possible with test.py -v -O-, making
the implicit inclusion redundant and noisy.
- Added license comments to bench_runner/test_runner
The main benefit is small test ids everywhere, though this is with the
downside of needing longer names to properly prefix and avoid
collisions. But this fits into the rest of the scripts with globally
unique names a bit better. This is a C project after all.
The other small benefit is test generators may have an easier time since
per-case symbols can expect to be unique.
This mostly required names for each test case, declarations of
previously-implicit variables since the new test framework is more
conservative with what it declares (the small extra effort to add
declarations is well worth the simplicity and improved readability),
and tweaks to work with not-really-constant defines.
Also renamed test_ -> test, replacing the old ./scripts/test.py,
unfortunately git seems to have had a hard time with this.
With the superblock expansion stuff, the test_format tests have grown
to test more advanced superblock-related features. This is fine but
deserves a rename so it's more clear.
Also fixed a typo that meant tests never ran with block cycles.