bcachefs: Fix bch2_delete_dead_inodes()
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 2 Nov 2023 19:28:15 +0000 (15:28 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 5 Nov 2023 02:19:13 +0000 (22:19 -0400)
 - the fsck_err() check for the filesystem being clean was incorrect,
   causing us to always fail to delete unlinked inodes
 - if a snapshot had been taken, the unlinked inode needs to be
   propagated to snapshot leaves so the unlink can happen there - fixed.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/inode.c
fs/bcachefs/inode.h

index 925d1b7f28877de06c1e424eb91ec2b691f75716..789f8958f685ef6e18bbc6e20480e2acae63289e 100644 (file)
@@ -362,9 +362,10 @@ int bch2_inode_peek(struct btree_trans *trans,
        return ret;
 }
 
-int bch2_inode_write(struct btree_trans *trans,
+int bch2_inode_write_flags(struct btree_trans *trans,
                     struct btree_iter *iter,
-                    struct bch_inode_unpacked *inode)
+                    struct bch_inode_unpacked *inode,
+                    enum btree_update_flags flags)
 {
        struct bkey_inode_buf *inode_p;
 
@@ -374,7 +375,7 @@ int bch2_inode_write(struct btree_trans *trans,
 
        bch2_inode_pack_inlined(inode_p, inode);
        inode_p->inode.k.p.snapshot = iter->snapshot;
-       return bch2_trans_update(trans, iter, &inode_p->inode.k_i, 0);
+       return bch2_trans_update(trans, iter, &inode_p->inode.k_i, flags);
 }
 
 struct bkey_i *bch2_inode_to_v3(struct btree_trans *trans, struct bkey_i *k)
@@ -1055,24 +1056,18 @@ err:
        return ret ?: -BCH_ERR_transaction_restart_nested;
 }
 
-static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
+static int may_delete_deleted_inode(struct btree_trans *trans,
+                                   struct btree_iter *iter,
+                                   struct bpos pos,
+                                   bool *need_another_pass)
 {
        struct bch_fs *c = trans->c;
-       struct btree_iter iter;
+       struct btree_iter inode_iter;
        struct bkey_s_c k;
        struct bch_inode_unpacked inode;
        int ret;
 
-       if (bch2_snapshot_is_internal_node(c, pos.snapshot))
-               return 0;
-
-       if (!fsck_err_on(c->sb.clean, c,
-                        deleted_inode_but_clean,
-                        "filesystem marked as clean but have deleted inode %llu:%u",
-                        pos.offset, pos.snapshot))
-               return 0;
-
-       k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes, pos, BTREE_ITER_CACHED);
+       k = bch2_bkey_get_iter(trans, &inode_iter, BTREE_ID_inodes, pos, BTREE_ITER_CACHED);
        ret = bkey_err(k);
        if (ret)
                return ret;
@@ -1086,7 +1081,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
 
        ret = bch2_inode_unpack(k, &inode);
        if (ret)
-               goto err;
+               goto out;
 
        if (fsck_err_on(S_ISDIR(inode.bi_mode), c,
                        deleted_inode_is_dir,
@@ -1100,12 +1095,46 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
                        pos.offset, pos.snapshot))
                goto delete;
 
-       return 1;
-err:
+       if (c->sb.clean &&
+           !fsck_err(c,
+                     deleted_inode_but_clean,
+                     "filesystem marked as clean but have deleted inode %llu:%u",
+                     pos.offset, pos.snapshot)) {
+               ret = 0;
+               goto out;
+       }
+
+       if (bch2_snapshot_is_internal_node(c, pos.snapshot)) {
+               struct bpos new_min_pos;
+
+               ret = bch2_propagate_key_to_snapshot_leaves(trans, inode_iter.btree_id, k, &new_min_pos);
+               if (ret)
+                       goto out;
+
+               inode.bi_flags &= ~BCH_INODE_UNLINKED;
+
+               ret = bch2_inode_write_flags(trans, &inode_iter, &inode,
+                                            BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
+               bch_err_msg(c, ret, "clearing inode unlinked flag");
+               if (ret)
+                       goto out;
+
+               /*
+                * We'll need another write buffer flush to pick up the new
+                * unlinked inodes in the snapshot leaves:
+                */
+               *need_another_pass = true;
+               return 0;
+       }
+
+       ret = 1;
+out:
 fsck_err:
+       bch2_trans_iter_exit(trans, &inode_iter);
        return ret;
 delete:
-       return bch2_btree_bit_mod(trans, BTREE_ID_deleted_inodes, pos, false);
+       ret = bch2_btree_bit_mod(trans, BTREE_ID_deleted_inodes, pos, false);
+       goto out;
 }
 
 int bch2_delete_dead_inodes(struct bch_fs *c)
@@ -1113,7 +1142,10 @@ int bch2_delete_dead_inodes(struct bch_fs *c)
        struct btree_trans *trans = bch2_trans_get(c);
        struct btree_iter iter;
        struct bkey_s_c k;
+       bool need_another_pass;
        int ret;
+again:
+       need_another_pass = false;
 
        ret = bch2_btree_write_buffer_flush_sync(trans);
        if (ret)
@@ -1127,7 +1159,8 @@ int bch2_delete_dead_inodes(struct bch_fs *c)
         */
        for_each_btree_key(trans, iter, BTREE_ID_deleted_inodes, POS_MIN,
                           BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k, ret) {
-               ret = lockrestart_do(trans, may_delete_deleted_inode(trans, k.k->p));
+               ret = lockrestart_do(trans, may_delete_deleted_inode(trans, &iter, k.k->p,
+                                                                    &need_another_pass));
                if (ret < 0)
                        break;
 
@@ -1137,12 +1170,17 @@ int bch2_delete_dead_inodes(struct bch_fs *c)
                                bch2_fs_lazy_rw(c);
                        }
 
+                       bch_verbose(c, "deleting unlinked inode %llu:%u", k.k->p.offset, k.k->p.snapshot);
+
                        ret = bch2_inode_rm_snapshot(trans, k.k->p.offset, k.k->p.snapshot);
                        if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
                                break;
                }
        }
        bch2_trans_iter_exit(trans, &iter);
+
+       if (!ret && need_another_pass)
+               goto again;
 err:
        bch2_trans_put(trans);
 
index 74c62e6c16cc0669b82ffc74f6fd34a88843044c..b09fda452d6fff385622d01687807854a4c7531e 100644 (file)
@@ -3,6 +3,7 @@
 #define _BCACHEFS_INODE_H
 
 #include "bkey.h"
+#include "bkey_methods.h"
 #include "opts.h"
 
 enum bkey_invalid_flags;
@@ -101,8 +102,16 @@ void bch2_inode_unpacked_to_text(struct printbuf *, struct bch_inode_unpacked *)
 
 int bch2_inode_peek(struct btree_trans *, struct btree_iter *,
                    struct bch_inode_unpacked *, subvol_inum, unsigned);
-int bch2_inode_write(struct btree_trans *, struct btree_iter *,
-                    struct bch_inode_unpacked *);
+
+int bch2_inode_write_flags(struct btree_trans *, struct btree_iter *,
+                    struct bch_inode_unpacked *, enum btree_update_flags);
+
+static inline int bch2_inode_write(struct btree_trans *trans,
+                    struct btree_iter *iter,
+                    struct bch_inode_unpacked *inode)
+{
+       return bch2_inode_write_flags(trans, iter, inode, 0);
+}
 
 void bch2_inode_init_early(struct bch_fs *,
                           struct bch_inode_unpacked *);