Commit 4bd156c4 authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: Fix bch2_delete_dead_inodes()



 - 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: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 7cb2a789
Loading
Loading
Loading
Loading
+58 −20
Original line number Diff line number Diff line
@@ -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 @@ int bch2_inode_rm_snapshot(struct btree_trans *trans, u64 inum, u32 snapshot)
	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);

+11 −2
Original line number Diff line number Diff line
@@ -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 *);