[PATCH v3 1/4] btrfs: support remount of ro fs with free space tree

Boris Burkov
2020-09-17T18:22:51Z

When a user attempts to remount a btrfs filesystem with
'mount -o remount,space_cache=v2', that operation silently succeeds.
Unfortunately, this is misleading, because the remount does not create
the free space tree. /proc/mounts will incorrectly show space_cache=v2,
but on the next mount, the file system will revert to the old
space_cache.

For now, we handle only the easier case, where the existing mount is
read-only and the new mount is read-write. In that case, we can create
the free space tree without contending with the block groups changing
as we go. If the remount is ro->ro, rw->ro, or rw->rw, we will not
create the free space tree, and print a warning to dmesg so that this
failure is more visible.

References: https://github.com/btrfs/btrfs-todo/issues/5
Signed-off-by: Boris Burkov <boris@bur.io>
---
v3:
- change failure to warning in dmesg for consistency
v2:
- move creation down to ro->rw case
- error on all other remount cases
- add a comment to help future remount modifiers

fs/btrfs/super.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 3ebe7240c63d..8dfd6089e31d 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -47,6 +47,7 @@
#include "tests/btrfs-tests.h"
#include "block-group.h"
#include "discard.h"
+#include "free-space-tree.h"

#include "qgroup.h"
#define CREATE_TRACE_POINTS
@@ -1838,6 +1839,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
u64 old_max_inline = fs_info->max_inline;
u32 old_thread_pool_size = fs_info->thread_pool_size;
u32 old_metadata_ratio = fs_info->metadata_ratio;
+ bool create_fst = false;
int ret;

sync_filesystem(sb);
@@ -1862,6 +1864,16 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
btrfs_resize_thread_pool(fs_info,
fs_info->thread_pool_size, old_thread_pool_size);

+ if (btrfs_test_opt(fs_info, FREE_SPACE_TREE) &&
+ !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+ create_fst = true;
+ if (!sb_rdonly(sb) || *flags & SB_RDONLY) {
+ btrfs_warn(fs_info,
+ "Remounting with free space tree only supported from read-only to read-write");
+ create_fst = false;
+ }
+ }
+
if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
goto out;

@@ -1924,6 +1936,21 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
goto restore;
}

+ /*
+ * NOTE: when remounting with a change that does writes, don't
+ * put it anywhere above this point, as we are not sure to be
+ * safe to write until we pass the above checks.
+ */
+ if (create_fst) {
+ ret = btrfs_create_free_space_tree(fs_info);
+ if (ret) {
+ btrfs_warn(fs_info,
+ "failed to create free space tree: %d", ret);
+ goto restore;
+ }
+ }
+
+
ret = btrfs_cleanup_fs_roots(fs_info);
if (ret)
goto restore;
--
2.24.1