bcachefs: BCH_FEATURE_no_alloc_info
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 3 Apr 2025 18:19:23 +0000 (14:19 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:14:20 +0000 (20:14 -0400)
If a filesystem is going to only be used read-only, and will be a
deployable image, we can strip out alloc info for a substantial
reduction in metadata size - around half, due to backpointers.

Alloc info will be regenerated on first read-write mount.

Remounting RW is disallowed for now, since we don't yet have
check_allocations running in RW mode.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs_format.h
fs/bcachefs/errcode.h
fs/bcachefs/recovery.c
fs/bcachefs/recovery.h
fs/bcachefs/recovery_passes.c
fs/bcachefs/recovery_passes_types.h
fs/bcachefs/sb-members.c
fs/bcachefs/super.c

index 22ee49408d11077fe976868d85783bed8bac85e5..c0041391e2e8265ba6aa62c59e9e6d6d0ee66654 100644 (file)
@@ -923,7 +923,8 @@ static inline void SET_BCH_SB_BACKGROUND_COMPRESSION_TYPE(struct bch_sb *sb, __u
        x(alloc_v2,                     17)     \
        x(extents_across_btree_nodes,   18)     \
        x(incompat_version_field,       19)     \
-       x(casefolding,                  20)
+       x(casefolding,                  20)     \
+       x(no_alloc_info,                21)
 
 #define BCH_SB_FEATURES_ALWAYS                         \
        (BIT_ULL(BCH_FEATURE_new_extent_overwrite)|     \
index 051938657cc991dd0dd7139722ed09667d5850ad..8a4435660d86b9ba6f3ba8ab750e15bc2d46a902 100644 (file)
        x(EROFS,                        erofs_unfixed_errors)                   \
        x(EROFS,                        erofs_norecovery)                       \
        x(EROFS,                        erofs_nochanges)                        \
+       x(EROFS,                        erofs_no_alloc_info)                    \
        x(EROFS,                        insufficient_devices)                   \
        x(0,                            operation_blocked)                      \
        x(BCH_ERR_operation_blocked,    btree_cache_cannibalize_lock_blocked)   \
index 4c336f20d5ebaaaa2c5b9b99111097646490b2f8..b5ab77f3c69200dd225d2ac91dec3f21f786fba7 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/sort.h>
 #include <linux/stat.h>
 
-
 int bch2_btree_lost_data(struct bch_fs *c, enum btree_id btree)
 {
        u64 b = BIT_ULL(btree);
@@ -114,11 +113,8 @@ static void kill_btree(struct bch_fs *c, enum btree_id btree)
 }
 
 /* for -o reconstruct_alloc: */
-static void bch2_reconstruct_alloc(struct bch_fs *c)
+void bch2_reconstruct_alloc(struct bch_fs *c)
 {
-       bch2_journal_log_msg(c, "dropping alloc info");
-       bch_info(c, "dropping and reconstructing all alloc info");
-
        mutex_lock(&c->sb_lock);
        struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
 
@@ -160,6 +156,8 @@ static void bch2_reconstruct_alloc(struct bch_fs *c)
 
        c->opts.recovery_passes |= bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0]));
 
+       c->disk_sb.sb->features[0] &= ~cpu_to_le64(BIT_ULL(BCH_FEATURE_no_alloc_info));
+
        bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
 
@@ -889,8 +887,26 @@ use_clean:
        if (ret)
                goto err;
 
-       if (c->opts.reconstruct_alloc)
+       if (!c->opts.read_only &&
+           (c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info))) {
+               bch_info(c, "mounting a filesystem with no alloc info read-write; will recreate");
+
                bch2_reconstruct_alloc(c);
+       } else if (c->opts.reconstruct_alloc) {
+               bch2_journal_log_msg(c, "dropping alloc info");
+               bch_info(c, "dropping and reconstructing all alloc info");
+
+               bch2_reconstruct_alloc(c);
+       }
+
+       if (c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info)) {
+               /* We can't go RW to fix errors without alloc info */
+               if (c->opts.fix_errors == FSCK_FIX_yes ||
+                   c->opts.fix_errors == FSCK_FIX_ask)
+                       c->opts.fix_errors = FSCK_FIX_no;
+               if (c->opts.errors == BCH_ON_ERROR_fix_safe)
+                       c->opts.errors = BCH_ON_ERROR_continue;
+       }
 
        /*
         * After an unclean shutdown, skip then next few journal sequence
index b0d55754b21b98f93f8c221417dbaeb60fcc1fd4..d858ba674eaa457fcc7856c40363d1a5285ab176 100644 (file)
@@ -3,6 +3,7 @@
 #define _BCACHEFS_RECOVERY_H
 
 int bch2_btree_lost_data(struct bch_fs *, enum btree_id);
+void bch2_reconstruct_alloc(struct bch_fs *);
 
 int bch2_journal_replay(struct bch_fs *);
 
index b4de21f80811c44b7c2f3635c9a5ba726cb6fdd4..87150dd30f4b3a1201b24ebcd9c05ae3c816f13e 100644 (file)
@@ -47,8 +47,18 @@ static int bch2_set_may_go_rw(struct bch_fs *c)
 
        set_bit(BCH_FS_may_go_rw, &c->flags);
 
-       if (keys->nr || !c->opts.read_only || c->opts.fsck || !c->sb.clean || c->opts.recovery_passes)
+       if (keys->nr ||
+           !c->opts.read_only ||
+           !c->sb.clean ||
+           c->opts.recovery_passes ||
+           (c->opts.fsck && !(c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info)))) {
+               if (c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info)) {
+                       bch_info(c, "mounting a filesystem with no alloc info read-write; will recreate");
+                       bch2_reconstruct_alloc(c);
+               }
+
                return bch2_fs_read_write_early(c);
+       }
        return 0;
 }
 
@@ -240,6 +250,8 @@ static bool should_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pa
 {
        struct recovery_pass_fn *p = recovery_pass_fns + pass;
 
+       if ((p->when & PASS_ALLOC) && (c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info)))
+               return false;
        if (c->opts.recovery_passes_exclude & BIT_ULL(pass))
                return false;
        if (c->opts.recovery_passes & BIT_ULL(pass))
index 4671ccf2d5600d771bced638a079a440d2c26e6c..f9d565bb50dd94cede472985ad1ae1facb21b972 100644 (file)
@@ -7,6 +7,8 @@
 #define PASS_UNCLEAN           BIT(2)
 #define PASS_ALWAYS            BIT(3)
 #define PASS_ONLINE            BIT(4)
+#define PASS_ALLOC             BIT(5)
+#define PASS_FSCK_ALLOC                (PASS_FSCK|PASS_ALLOC)
 
 #ifdef CONFIG_BCACHEFS_DEBUG
 #define PASS_FSCK_DEBUG                BIT(1)
        x(stripes_read,                          1, 0)                                  \
        x(initialize_subvolumes,                 2, 0)                                  \
        x(snapshots_read,                        3, PASS_ALWAYS)                        \
-       x(check_allocations,                     5, PASS_FSCK)                          \
-       x(trans_mark_dev_sbs,                    6, PASS_ALWAYS|PASS_SILENT)            \
-       x(fs_journal_alloc,                      7, PASS_ALWAYS|PASS_SILENT)            \
+       x(check_allocations,                     5, PASS_FSCK_ALLOC)                    \
+       x(trans_mark_dev_sbs,                    6, PASS_ALWAYS|PASS_SILENT|PASS_ALLOC) \
+       x(fs_journal_alloc,                      7, PASS_ALWAYS|PASS_SILENT|PASS_ALLOC) \
        x(set_may_go_rw,                         8, PASS_ALWAYS|PASS_SILENT)            \
        x(journal_replay,                        9, PASS_ALWAYS)                        \
-       x(check_alloc_info,                     10, PASS_ONLINE|PASS_FSCK)              \
-       x(check_lrus,                           11, PASS_ONLINE|PASS_FSCK)              \
-       x(check_btree_backpointers,             12, PASS_ONLINE|PASS_FSCK)              \
+       x(check_alloc_info,                     10, PASS_ONLINE|PASS_FSCK_ALLOC)        \
+       x(check_lrus,                           11, PASS_ONLINE|PASS_FSCK_ALLOC)        \
+       x(check_btree_backpointers,             12, PASS_ONLINE|PASS_FSCK_ALLOC)        \
        x(check_backpointers_to_extents,        13, PASS_ONLINE|PASS_FSCK_DEBUG)        \
-       x(check_extents_to_backpointers,        14, PASS_ONLINE|PASS_FSCK)              \
-       x(check_alloc_to_lru_refs,              15, PASS_ONLINE|PASS_FSCK)              \
+       x(check_extents_to_backpointers,        14, PASS_ONLINE|PASS_FSCK_ALLOC)        \
+       x(check_alloc_to_lru_refs,              15, PASS_ONLINE|PASS_FSCK_ALLOC)        \
        x(fs_freespace_init,                    16, PASS_ALWAYS|PASS_SILENT)            \
        x(bucket_gens_init,                     17, 0)                                  \
        x(reconstruct_snapshots,                38, 0)                                  \
index 77809ee23c455dc1a9bfcccd1f5a7deb36c7614a..39ce94875dde4ab57be389ef066f811c86462f93 100644 (file)
@@ -190,6 +190,12 @@ static int validate_member(struct printbuf *err,
                return -BCH_ERR_invalid_sb_members;
        }
 
+       if (BCH_MEMBER_FREESPACE_INITIALIZED(&m) &&
+           sb->features[0] & cpu_to_le64(BIT_ULL(BCH_FEATURE_no_alloc_info))) {
+               prt_printf(err, "device %u: freespace initialized but fs has no alloc info", i);
+               return -BCH_ERR_invalid_sb_members;
+       }
+
        return 0;
 }
 
index 60e632e22b98161578874cf3d58a4935e7484c36..6ab3e63ef13913d4321c6c86e9f8c88f68a68325 100644 (file)
@@ -443,6 +443,9 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
 
        BUG_ON(!test_bit(BCH_FS_may_go_rw, &c->flags));
 
+       if (WARN_ON(c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info)))
+               return -BCH_ERR_erofs_no_alloc_info;
+
        if (test_bit(BCH_FS_initial_gc_unfixed, &c->flags)) {
                bch_err(c, "cannot go rw, unfixed btree errors");
                return -BCH_ERR_erofs_unfixed_errors;
@@ -535,6 +538,9 @@ int bch2_fs_read_write(struct bch_fs *c)
        if (c->opts.nochanges)
                return -BCH_ERR_erofs_nochanges;
 
+       if (c->sb.features & BIT_ULL(BCH_FEATURE_no_alloc_info))
+               return -BCH_ERR_erofs_no_alloc_info;
+
        return __bch2_fs_read_write(c, false);
 }