bcachefs: BTREE_ID_subvolume_children
Add a btree to record a parent -> child subvolume relationships, according to the filesystem heirarchy. The subvolume_children btree is a bitset btree: if a bit is set at pos p, that means p.offset is a child of subvolume p.inode. This will be used for efficiently listing subvolumes, as well as recursive deletion. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
b8628a2529
commit
b26d79147f
@ -504,6 +504,7 @@ enum gc_phase {
|
|||||||
GC_PHASE_BTREE_deleted_inodes,
|
GC_PHASE_BTREE_deleted_inodes,
|
||||||
GC_PHASE_BTREE_logged_ops,
|
GC_PHASE_BTREE_logged_ops,
|
||||||
GC_PHASE_BTREE_rebalance_work,
|
GC_PHASE_BTREE_rebalance_work,
|
||||||
|
GC_PHASE_BTREE_subvolume_children,
|
||||||
|
|
||||||
GC_PHASE_PENDING_DELETE,
|
GC_PHASE_PENDING_DELETE,
|
||||||
};
|
};
|
||||||
|
@ -841,7 +841,8 @@ struct bch_sb_field_downgrade {
|
|||||||
x(deleted_inodes, BCH_VERSION(1, 2)) \
|
x(deleted_inodes, BCH_VERSION(1, 2)) \
|
||||||
x(rebalance_work, BCH_VERSION(1, 3)) \
|
x(rebalance_work, BCH_VERSION(1, 3)) \
|
||||||
x(member_seq, BCH_VERSION(1, 4)) \
|
x(member_seq, BCH_VERSION(1, 4)) \
|
||||||
x(subvolume_fs_parent, BCH_VERSION(1, 5))
|
x(subvolume_fs_parent, BCH_VERSION(1, 5)) \
|
||||||
|
x(btree_subvolume_children, BCH_VERSION(1, 6))
|
||||||
|
|
||||||
enum bcachefs_metadata_version {
|
enum bcachefs_metadata_version {
|
||||||
bcachefs_metadata_version_min = 9,
|
bcachefs_metadata_version_min = 9,
|
||||||
@ -1489,7 +1490,9 @@ enum btree_id_flags {
|
|||||||
BIT_ULL(KEY_TYPE_logged_op_truncate)| \
|
BIT_ULL(KEY_TYPE_logged_op_truncate)| \
|
||||||
BIT_ULL(KEY_TYPE_logged_op_finsert)) \
|
BIT_ULL(KEY_TYPE_logged_op_finsert)) \
|
||||||
x(rebalance_work, 18, BTREE_ID_SNAPSHOT_FIELD, \
|
x(rebalance_work, 18, BTREE_ID_SNAPSHOT_FIELD, \
|
||||||
BIT_ULL(KEY_TYPE_set)|BIT_ULL(KEY_TYPE_cookie))
|
BIT_ULL(KEY_TYPE_set)|BIT_ULL(KEY_TYPE_cookie)) \
|
||||||
|
x(subvolume_children, 19, 0, \
|
||||||
|
BIT_ULL(KEY_TYPE_set))
|
||||||
|
|
||||||
enum btree_id {
|
enum btree_id {
|
||||||
#define x(name, nr, ...) BTREE_ID_##name = nr,
|
#define x(name, nr, ...) BTREE_ID_##name = nr,
|
||||||
|
@ -654,6 +654,7 @@ const char *bch2_btree_node_type_str(enum btree_node_type);
|
|||||||
BIT_ULL(BKEY_TYPE_inodes)| \
|
BIT_ULL(BKEY_TYPE_inodes)| \
|
||||||
BIT_ULL(BKEY_TYPE_stripes)| \
|
BIT_ULL(BKEY_TYPE_stripes)| \
|
||||||
BIT_ULL(BKEY_TYPE_reflink)| \
|
BIT_ULL(BKEY_TYPE_reflink)| \
|
||||||
|
BIT_ULL(BKEY_TYPE_subvolumes)| \
|
||||||
BIT_ULL(BKEY_TYPE_btree))
|
BIT_ULL(BKEY_TYPE_btree))
|
||||||
|
|
||||||
#define BTREE_NODE_TYPE_HAS_ATOMIC_TRIGGERS \
|
#define BTREE_NODE_TYPE_HAS_ATOMIC_TRIGGERS \
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
x(check_snapshot_trees, 18, PASS_ONLINE|PASS_FSCK) \
|
x(check_snapshot_trees, 18, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(check_snapshots, 19, PASS_ONLINE|PASS_FSCK) \
|
x(check_snapshots, 19, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(check_subvols, 20, PASS_ONLINE|PASS_FSCK) \
|
x(check_subvols, 20, PASS_ONLINE|PASS_FSCK) \
|
||||||
|
x(check_subvol_children, 35, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(delete_dead_snapshots, 21, PASS_ONLINE|PASS_FSCK) \
|
x(delete_dead_snapshots, 21, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(fs_upgrade_for_subvolumes, 22, 0) \
|
x(fs_upgrade_for_subvolumes, 22, 0) \
|
||||||
x(resume_logged_ops, 23, PASS_ALWAYS) \
|
x(resume_logged_ops, 23, PASS_ALWAYS) \
|
||||||
|
@ -48,7 +48,10 @@
|
|||||||
BIT_ULL(BCH_RECOVERY_PASS_set_fs_needs_rebalance)) \
|
BIT_ULL(BCH_RECOVERY_PASS_set_fs_needs_rebalance)) \
|
||||||
x(subvolume_fs_parent, \
|
x(subvolume_fs_parent, \
|
||||||
BIT_ULL(BCH_RECOVERY_PASS_check_dirents), \
|
BIT_ULL(BCH_RECOVERY_PASS_check_dirents), \
|
||||||
BCH_FSCK_ERR_subvol_fs_path_parent_wrong)
|
BCH_FSCK_ERR_subvol_fs_path_parent_wrong) \
|
||||||
|
x(btree_subvolume_children, \
|
||||||
|
BIT_ULL(BCH_RECOVERY_PASS_check_subvols), \
|
||||||
|
BCH_FSCK_ERR_subvol_children_not_set)
|
||||||
|
|
||||||
#define DOWNGRADE_TABLE()
|
#define DOWNGRADE_TABLE()
|
||||||
|
|
||||||
|
@ -13,12 +13,24 @@
|
|||||||
|
|
||||||
static int bch2_subvolume_delete(struct btree_trans *, u32);
|
static int bch2_subvolume_delete(struct btree_trans *, u32);
|
||||||
|
|
||||||
|
static struct bpos subvolume_children_pos(struct bkey_s_c k)
|
||||||
|
{
|
||||||
|
if (k.k->type != KEY_TYPE_subvolume)
|
||||||
|
return POS_MIN;
|
||||||
|
|
||||||
|
struct bkey_s_c_subvolume s = bkey_s_c_to_subvolume(k);
|
||||||
|
if (!s.v->fs_path_parent)
|
||||||
|
return POS_MIN;
|
||||||
|
return POS(le32_to_cpu(s.v->fs_path_parent), s.k->p.offset);
|
||||||
|
}
|
||||||
|
|
||||||
static int check_subvol(struct btree_trans *trans,
|
static int check_subvol(struct btree_trans *trans,
|
||||||
struct btree_iter *iter,
|
struct btree_iter *iter,
|
||||||
struct bkey_s_c k)
|
struct bkey_s_c k)
|
||||||
{
|
{
|
||||||
struct bch_fs *c = trans->c;
|
struct bch_fs *c = trans->c;
|
||||||
struct bkey_s_c_subvolume subvol;
|
struct bkey_s_c_subvolume subvol;
|
||||||
|
struct btree_iter subvol_children_iter = {};
|
||||||
struct bch_snapshot snapshot;
|
struct bch_snapshot snapshot;
|
||||||
struct printbuf buf = PRINTBUF;
|
struct printbuf buf = PRINTBUF;
|
||||||
unsigned snapid;
|
unsigned snapid;
|
||||||
@ -57,6 +69,28 @@ static int check_subvol(struct btree_trans *trans,
|
|||||||
n->v.fs_path_parent = 0;
|
n->v.fs_path_parent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subvol.v->fs_path_parent) {
|
||||||
|
struct bpos pos = subvolume_children_pos(k);
|
||||||
|
|
||||||
|
struct bkey_s_c subvol_children_k =
|
||||||
|
bch2_bkey_get_iter(trans, &subvol_children_iter,
|
||||||
|
BTREE_ID_subvolume_children, pos, 0);
|
||||||
|
ret = bkey_err(subvol_children_k);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (fsck_err_on(subvol_children_k.k->type != KEY_TYPE_set,
|
||||||
|
c, subvol_children_not_set,
|
||||||
|
"subvolume not set in subvolume_children btree at %llu:%llu\n%s",
|
||||||
|
pos.inode, pos.offset,
|
||||||
|
(printbuf_reset(&buf),
|
||||||
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
||||||
|
ret = bch2_btree_bit_mod(trans, BTREE_ID_subvolume_children, pos, true);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct bch_inode_unpacked inode;
|
struct bch_inode_unpacked inode;
|
||||||
struct btree_iter inode_iter = {};
|
struct btree_iter inode_iter = {};
|
||||||
ret = bch2_inode_peek_nowarn(trans, &inode_iter, &inode,
|
ret = bch2_inode_peek_nowarn(trans, &inode_iter, &inode,
|
||||||
@ -119,6 +153,7 @@ static int check_subvol(struct btree_trans *trans,
|
|||||||
}
|
}
|
||||||
err:
|
err:
|
||||||
fsck_err:
|
fsck_err:
|
||||||
|
bch2_trans_iter_exit(trans, &subvol_children_iter);
|
||||||
printbuf_exit(&buf);
|
printbuf_exit(&buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -134,6 +169,42 @@ int bch2_check_subvols(struct bch_fs *c)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_subvol_child(struct btree_trans *trans,
|
||||||
|
struct btree_iter *child_iter,
|
||||||
|
struct bkey_s_c child_k)
|
||||||
|
{
|
||||||
|
struct bch_fs *c = trans->c;
|
||||||
|
struct bch_subvolume s;
|
||||||
|
int ret = bch2_bkey_get_val_typed(trans, BTREE_ID_subvolumes, POS(0, child_k.k->p.offset),
|
||||||
|
0, subvolume, &s);
|
||||||
|
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (fsck_err_on(ret ||
|
||||||
|
le32_to_cpu(s.fs_path_parent) != child_k.k->p.inode,
|
||||||
|
c, subvol_children_bad,
|
||||||
|
"incorrect entry in subvolume_children btree %llu:%llu",
|
||||||
|
child_k.k->p.inode, child_k.k->p.offset)) {
|
||||||
|
ret = bch2_btree_delete_at(trans, child_iter, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
err:
|
||||||
|
fsck_err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bch2_check_subvol_children(struct bch_fs *c)
|
||||||
|
{
|
||||||
|
int ret = bch2_trans_run(c,
|
||||||
|
for_each_btree_key_commit(trans, iter,
|
||||||
|
BTREE_ID_subvolume_children, POS_MIN, BTREE_ITER_PREFETCH, k,
|
||||||
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
||||||
|
check_subvol_child(trans, &iter, k)));
|
||||||
|
bch_err_fn(c, ret);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Subvolumes: */
|
/* Subvolumes: */
|
||||||
|
|
||||||
int bch2_subvolume_invalid(struct bch_fs *c, struct bkey_s_c k,
|
int bch2_subvolume_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||||
@ -164,6 +235,33 @@ void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int subvolume_children_mod(struct btree_trans *trans, struct bpos pos, bool set)
|
||||||
|
{
|
||||||
|
return !bpos_eq(pos, POS_MIN)
|
||||||
|
? bch2_btree_bit_mod(trans, BTREE_ID_subvolume_children, pos, set)
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bch2_subvolume_trigger(struct btree_trans *trans,
|
||||||
|
enum btree_id btree_id, unsigned level,
|
||||||
|
struct bkey_s_c old, struct bkey_s new,
|
||||||
|
unsigned flags)
|
||||||
|
{
|
||||||
|
if (flags & BTREE_TRIGGER_TRANSACTIONAL) {
|
||||||
|
struct bpos children_pos_old = subvolume_children_pos(old);
|
||||||
|
struct bpos children_pos_new = subvolume_children_pos(new.s_c);
|
||||||
|
|
||||||
|
if (!bpos_eq(children_pos_old, children_pos_new)) {
|
||||||
|
int ret = subvolume_children_mod(trans, children_pos_old, false) ?:
|
||||||
|
subvolume_children_mod(trans, children_pos_new, true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static __always_inline int
|
static __always_inline int
|
||||||
bch2_subvolume_get_inlined(struct btree_trans *trans, unsigned subvol,
|
bch2_subvolume_get_inlined(struct btree_trans *trans, unsigned subvol,
|
||||||
bool inconsistent_if_not_found,
|
bool inconsistent_if_not_found,
|
||||||
|
@ -8,14 +8,18 @@
|
|||||||
enum bkey_invalid_flags;
|
enum bkey_invalid_flags;
|
||||||
|
|
||||||
int bch2_check_subvols(struct bch_fs *);
|
int bch2_check_subvols(struct bch_fs *);
|
||||||
|
int bch2_check_subvol_children(struct bch_fs *);
|
||||||
|
|
||||||
int bch2_subvolume_invalid(struct bch_fs *, struct bkey_s_c,
|
int bch2_subvolume_invalid(struct bch_fs *, struct bkey_s_c,
|
||||||
enum bkey_invalid_flags, struct printbuf *);
|
enum bkey_invalid_flags, struct printbuf *);
|
||||||
void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
|
||||||
|
int bch2_subvolume_trigger(struct btree_trans *, enum btree_id, unsigned,
|
||||||
|
struct bkey_s_c, struct bkey_s, unsigned);
|
||||||
|
|
||||||
#define bch2_bkey_ops_subvolume ((struct bkey_ops) { \
|
#define bch2_bkey_ops_subvolume ((struct bkey_ops) { \
|
||||||
.key_invalid = bch2_subvolume_invalid, \
|
.key_invalid = bch2_subvolume_invalid, \
|
||||||
.val_to_text = bch2_subvolume_to_text, \
|
.val_to_text = bch2_subvolume_to_text, \
|
||||||
|
.trigger = bch2_subvolume_trigger, \
|
||||||
.min_val_size = 16, \
|
.min_val_size = 16, \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user