-
Notifications
You must be signed in to change notification settings - Fork 858
Add support for shrinking a filesystem #1094
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add support for shrinking a filesystem #1094
Conversation
7b8aaa0
to
a1533aa
Compare
This PR adds a new `lfs_fs_shrink`, which functions similarly to `lfs_fs_grow`, but supports reducing the block count. This functions first checks that none of the removed block are in use. If it is the case, it will fail.
a1533aa
to
2105e50
Compare
Tests passed ✓, Code: 17220 B (+0.6%), Stack: 1448 B (+0.0%), Structs: 812 B (+0.0%)
|
Hi @sosthene-nitrokey, thanks for the PR. I'm going to contradict past me a bit, but I think this would be better implemented as an extension to I've come around on the idea of add But in obnoxiously POSIX fashion I think we want to avoid adding a new function and instead allow To avoid unnecessary code cost, shrinking behavior would be opt-in behind So, instead of adding Also I appreciate the relevant test cases. Usually I have to beg for those :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some nits to consider. But this is some pretty great code. It would just be nice to move under ifdef LFS_SHRINKIFCHEAP
to fit into above future plans.
lfs.c
Outdated
{tag, &superblock})); | ||
lfs_block_t threshold = block_count; | ||
|
||
int err = lfs_fs_traverse_(lfs, lfs_shrink_check_block, &threshold, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Please limit to 80 chars/columns (I should really add a CI job for this)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hi OverLength ctermbg=darkblue
match OverLength /\%81v./
is a decent way to highlight >80 char lines if you're in vim.
lfs.c
Outdated
@@ -5233,38 +5233,67 @@ static int lfs_fs_gc_(lfs_t *lfs) { | |||
#endif | |||
|
|||
#ifndef LFS_READONLY | |||
static int lfs_fs_rewrite_block_count(lfs_t *lfs, lfs_size_t block_count) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Naming is a bit weird, but useful, in littlefs. Underscores indicate namespaces/subsystems for the most part. So this should be lfs_fs_rewriteblockcount
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. This function will be removed anyway following shrink being part of lfs_fs_grow
.
lfs.c
Outdated
return tag; | ||
} | ||
lfs_superblock_fromle32(&superblock); | ||
static int lfs_shrink_check_block(void * data, lfs_block_t block) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: see above, namespace quirkyness: This should be lfs_fs_shrink_checkblock
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
lfs.h
Outdated
// Shrinks the filesystem to a new size, updating the superblock with the new | ||
// block count. | ||
// | ||
// Note: This first checks that none of the blocks that are being removed are in use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: 80 cols
lfs.c
Outdated
} | ||
lfs_superblock_fromle32(&superblock); | ||
static int lfs_shrink_check_block(void * data, lfs_block_t block) { | ||
lfs_size_t threshold = *((lfs_size_t *) data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spacing: *((lfs_size_t*)data);
Maybe rename it to You could |
Ah, but the name abuse is part of the charm of POSIX, see Users might also think |
Thank you for the review! I made a fixup commit that applies your suggestions. I'm just not sure how to make it so that Regarding merging this functionality into |
For the name |
This makes sense to me. @geky maybe consider prioritizing clarity over POSIX-likeness, in cases where POSIX-likeness is quirky, unexpected, or unclear? Just my 2¢; apologies if you've already decided otherwise for the project. |
9a728cd
to
9b8f802
Compare
Thanks! It's looking good to me codewise now.
Ah, this is a bit messy at the moment because most ifdefs are large in scope. You can probably copy the You could limit the test suites via: CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" \
TESTFLAGS="test_superblocks test_grow test_shrink" \
make test But this is definitely not going to be a bottleneck vs powerloss/emulated/valgrind testing, so we may as well just test everything. There's also a minor CI rework, and an umbrella LFS_BIGGEST define, in the future, so I wouldn't worry too much about fragmentation at the moment. |
That is a better name, but what if I raise you |
That's a fair argument. Though I'm not sure if "dangerous" is the right word. Shrinking would still use copy-on-write updates, so worst case However it is a much more expensive operation. And there is also the potential benefit of compile-time gc with the separate Hmm. May have to think on this. Sorry if this ends up going the other way. The main reason for merging is to prefer fewer, more powerful (not more complicated!), functions to make it easier for users the keep the API in their head. The more fragmented your API becomes, the harder it is to know whether or not a relevant function exists as a user. Though maybe the benefits of separating outweigh the benefits of minimizing the API surface here...
At the risk of getting into controversial opinion territory, my opinion is consistency > clarity when it comes to API design. A clear name doesn't exactly help you if you can't find the function, and usually the name won't be able to capture the full idiosyncrasies of the function anyways. |
Tests passed ✓, Code: 17116 B (+0.0%), Stack: 1448 B (+0.0%), Structs: 812 B (+0.0%)
|
I still prefer |
Having slept on it, I still think this should be one function (
It's subjective, but it feels like the best option to me. Thanks for the feedback both of you, and for adopting the unified function @sosthene-nitrokey.
We can go with |
lfs.c
Outdated
#endif | ||
#ifdef LFS_SHRINKIFCHEAP | ||
lfs_block_t threshold = block_count; | ||
err = lfs_fs_traverse_(lfs, lfs_shrink_checkblock, &threshold, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry I should have caught this earlier.
Even with LFS_SHRINKNONRELOCATING enabled, we shouldn't traverse if unless block_count < lfs->block_count
. lfs_fs_traverse_
involves a lot of io, and users probably expect lfs_fs_grow
to be cheap if it is a simple grow operation.
Also I think this can just be &block_count
? It doesn't look like threshold is used after this.
lfs.c
Outdated
@@ -5233,40 +5233,63 @@ static int lfs_fs_gc_(lfs_t *lfs) { | |||
#endif | |||
|
|||
#ifndef LFS_READONLY | |||
#ifdef LFS_SHRINKIFCHEAP | |||
static int lfs_shrink_checkblock(void * data, lfs_block_t block) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spacing: void *data
lfs.c
Outdated
@@ -5233,40 +5233,63 @@ static int lfs_fs_gc_(lfs_t *lfs) { | |||
#endif | |||
|
|||
#ifndef LFS_READONLY | |||
#ifdef LFS_SHRINKIFCHEAP | |||
static int lfs_shrink_checkblock(void * data, lfs_block_t block) { | |||
lfs_size_t threshold = *((lfs_size_t *) data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spacing: *((lfs_size_t*)data)
tests/test_shrink.toml
Outdated
|
||
if = "AFTER_BLOCK_COUNT <= BLOCK_COUNT" | ||
code = ''' | ||
#ifdef LFS_SHRINKIFCHEAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes. This is the best way to ifdef
right now. I just noticed the earlier test failures.
I have a branch that adds ifdef
attrs to the test case itself, but it hasn't been upstreamed yet unfortunately...
Tests passed ✓, Code: 17116 B (+0.0%), Stack: 1448 B (+0.0%), Structs: 812 B (+0.0%)
|
Looks good to me, thanks for the tweaks 👍 Will bring this into the next minor release. |
This PR adds a new
lfs_fs_shrink
, which functions similarly tolfs_fs_grow
, but supports reducing the block count.This functions first checks that none of the removed block are in use. If it is the case, it will fail.
Closes #1093