mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-08 14:13:53 +00:00
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: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
7cb2a7895d
commit
4bd156c4b4
@ -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);
|
||||
|
||||
|
@ -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 *);
|
||||
|
Loading…
Reference in New Issue
Block a user