summaryrefslogtreecommitdiffstats
path: root/fs/bcachefs/inode.c
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2023-07-17 00:56:29 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2023-10-22 17:10:09 -0400
commitdde8cb11645b7d95766dfd13f273facde27923a9 (patch)
tree001d854afb26fa9424097129f97e0df095e2e037 /fs/bcachefs/inode.c
parent0a6d694584aeb739b976bf69646fa3c23ee117c3 (diff)
downloadlinux-dde8cb11645b7d95766dfd13f273facde27923a9.tar.gz
linux-dde8cb11645b7d95766dfd13f273facde27923a9.tar.bz2
linux-dde8cb11645b7d95766dfd13f273facde27923a9.zip
bcachefs: bcachefs_metadata_version_deleted_inodes
Add a new bitset btree for inodes pending deletion; this means we no longer have to scan the full inodes btree after an unclean shutdown. Specifically, this adds: - a trigger to update the deleted_inodes btree based on changes to the inodes btree - a new recovery pass - and check_inodes is now only a fsck pass. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs/bcachefs/inode.c')
-rw-r--r--fs/bcachefs/inode.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c
index 755cf7d177cd..294966e42850 100644
--- a/fs/bcachefs/inode.c
+++ b/fs/bcachefs/inode.c
@@ -2,6 +2,7 @@
#include "bcachefs.h"
#include "btree_key_cache.h"
+#include "btree_write_buffer.h"
#include "bkey_methods.h"
#include "btree_update.h"
#include "buckets.h"
@@ -519,6 +520,25 @@ void bch2_inode_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
__bch2_inode_unpacked_to_text(out, &inode);
}
+static inline u64 bkey_inode_flags(struct bkey_s_c k)
+{
+ switch (k.k->type) {
+ case KEY_TYPE_inode:
+ return le32_to_cpu(bkey_s_c_to_inode(k).v->bi_flags);
+ case KEY_TYPE_inode_v2:
+ return le64_to_cpu(bkey_s_c_to_inode_v2(k).v->bi_flags);
+ case KEY_TYPE_inode_v3:
+ return le64_to_cpu(bkey_s_c_to_inode_v3(k).v->bi_flags);
+ default:
+ return 0;
+ }
+}
+
+static inline bool bkey_is_deleted_inode(struct bkey_s_c k)
+{
+ return bkey_inode_flags(k) & BCH_INODE_UNLINKED;
+}
+
int bch2_trans_mark_inode(struct btree_trans *trans,
enum btree_id btree_id, unsigned level,
struct bkey_s_c old,
@@ -526,6 +546,8 @@ int bch2_trans_mark_inode(struct btree_trans *trans,
unsigned flags)
{
int nr = bkey_is_inode(&new->k) - bkey_is_inode(old.k);
+ bool old_deleted = bkey_is_deleted_inode(old);
+ bool new_deleted = bkey_is_deleted_inode(bkey_i_to_s_c(new));
if (nr) {
int ret = bch2_replicas_deltas_realloc(trans, 0);
@@ -537,6 +559,12 @@ int bch2_trans_mark_inode(struct btree_trans *trans,
d->nr_inodes += nr;
}
+ if (old_deleted != new_deleted) {
+ int ret = bch2_btree_bit_mod(trans, BTREE_ID_deleted_inodes, new->k.p, new_deleted);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -986,3 +1014,90 @@ err:
return ret ?: -BCH_ERR_transaction_restart_nested;
}
+
+static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
+{
+ struct bch_fs *c = trans->c;
+ struct btree_iter 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,
+ "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);
+ ret = bkey_err(k);
+ if (ret)
+ return ret;
+
+ ret = bkey_is_inode(k.k) ? 0 : -BCH_ERR_ENOENT_inode;
+ if (fsck_err_on(!bkey_is_inode(k.k), c,
+ "nonexistent inode %llu:%u in deleted_inodes btree",
+ pos.offset, pos.snapshot))
+ goto delete;
+
+ ret = bch2_inode_unpack(k, &inode);
+ if (ret)
+ goto err;
+
+ if (fsck_err_on(!(inode.bi_flags & BCH_INODE_UNLINKED), c,
+ "non-deleted inode %llu:%u in deleted_inodes btree",
+ pos.offset, pos.snapshot))
+ goto delete;
+
+ return 1;
+err:
+fsck_err:
+ return ret;
+delete:
+ return bch2_btree_bit_mod(trans, BTREE_ID_deleted_inodes, pos, false);
+}
+
+int bch2_delete_dead_inodes(struct bch_fs *c)
+{
+ struct btree_trans trans;
+ struct btree_iter iter;
+ struct bkey_s_c k;
+ int ret;
+
+ bch2_trans_init(&trans, c, 0, 0);
+
+ ret = bch2_btree_write_buffer_flush_sync(&trans);
+ if (ret)
+ goto err;
+
+ /*
+ * Weird transaction restart handling here because on successful delete,
+ * bch2_inode_rm_snapshot() will return a nested transaction restart,
+ * but we can't retry because the btree write buffer won't have been
+ * flushed and we'd spin:
+ */
+ 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));
+ if (ret < 0)
+ break;
+
+ if (ret) {
+ if (!test_bit(BCH_FS_RW, &c->flags)) {
+ bch2_trans_unlock(&trans);
+ bch2_fs_lazy_rw(c);
+ }
+
+ 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);
+err:
+ bch2_trans_exit(&trans);
+
+ return ret;
+}