From b030e262b517b6bddc4bfa88ed8d335ef9de7671 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 10 Sep 2023 16:42:30 -0400 Subject: [PATCH] bcachefs: Log truncate operations Previously, we guaranteed atomicity of truncate after unclean shutdown with the BCH_INODE_I_SIZE_DIRTY flag - which required a full scan of the inodes btree. Recently the deleted inodes btree was added so that we no longer have to scan for deleted inodes, but truncate was unfinished and that change left it broken. This patch uses the new logged operations btree to fix truncate atomicity; we now log an operation that can be replayed at the start of a truncate. Signed-off-by: Kent Overstreet --- fs/bcachefs/bcachefs_format.h | 19 ++++++++--- fs/bcachefs/bkey_methods.c | 1 + fs/bcachefs/io_misc.c | 64 +++++++++++++++++++++++++---------- fs/bcachefs/io_misc.h | 9 +++++ fs/bcachefs/logged_ops.c | 1 + fs/bcachefs/logged_ops.h | 3 +- 6 files changed, 75 insertions(+), 22 deletions(-) diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h index 31efa9e381cea..3c9e788f1c9dd 100644 --- a/fs/bcachefs/bcachefs_format.h +++ b/fs/bcachefs/bcachefs_format.h @@ -370,7 +370,8 @@ static inline void bkey_init(struct bkey *k) x(backpointer, 28) \ x(inode_v3, 29) \ x(bucket_gens, 30) \ - x(snapshot_tree, 31) + x(snapshot_tree, 31) \ + x(logged_op_truncate, 32) enum bch_bkey_type { #define x(name, nr) KEY_TYPE_##name = nr, @@ -847,8 +848,8 @@ enum { __BCH_INODE_NODUMP = 3, __BCH_INODE_NOATIME = 4, - __BCH_INODE_I_SIZE_DIRTY = 5, - __BCH_INODE_I_SECTORS_DIRTY = 6, + __BCH_INODE_I_SIZE_DIRTY = 5, /* obsolete */ + __BCH_INODE_I_SECTORS_DIRTY = 6, /* obsolete */ __BCH_INODE_UNLINKED = 7, __BCH_INODE_BACKPTR_UNTRUSTED = 8, @@ -1183,6 +1184,16 @@ struct bch_lru { #define LRU_ID_STRIPES (1U << 16) +/* Logged operations btree: */ + +struct bch_logged_op_truncate { + struct bch_val v; + __le32 subvol; + __le32 pad; + __le64 inum; + __le64 new_i_size; +}; + /* Optional/variable size superblock sections: */ struct bch_sb_field { @@ -2251,7 +2262,7 @@ enum btree_id_flags { x(deleted_inodes, 16, BTREE_ID_SNAPSHOTS, \ BIT_ULL(KEY_TYPE_set)) \ x(logged_ops, 17, 0, \ - 0) + BIT_ULL(KEY_TYPE_logged_op_truncate)) enum btree_id { #define x(name, nr, ...) BTREE_ID_##name = nr, diff --git a/fs/bcachefs/bkey_methods.c b/fs/bcachefs/bkey_methods.c index 6547142db4280..91e28ee3efff8 100644 --- a/fs/bcachefs/bkey_methods.c +++ b/fs/bcachefs/bkey_methods.c @@ -10,6 +10,7 @@ #include "error.h" #include "extents.h" #include "inode.h" +#include "io_misc.h" #include "lru.h" #include "quota.h" #include "reflink.h" diff --git a/fs/bcachefs/io_misc.c b/fs/bcachefs/io_misc.c index 1afea613df4a9..327b3dd642de2 100644 --- a/fs/bcachefs/io_misc.c +++ b/fs/bcachefs/io_misc.c @@ -15,6 +15,7 @@ #include "inode.h" #include "io_misc.h" #include "io_write.h" +#include "logged_ops.h" #include "subvolume.h" /* Overwrites whatever was present with zeroes: */ @@ -217,6 +218,17 @@ int bch2_fpunch(struct bch_fs *c, subvol_inum inum, u64 start, u64 end, return ret; } +/* truncate: */ + +void bch2_logged_op_truncate_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) +{ + struct bkey_s_c_logged_op_truncate op = bkey_s_c_to_logged_op_truncate(k); + + prt_printf(out, "subvol=%u", le32_to_cpu(op.v->subvol)); + prt_printf(out, " inum=%llu", le64_to_cpu(op.v->inum)); + prt_printf(out, " new_i_size=%llu", le64_to_cpu(op.v->new_i_size)); +} + static int truncate_set_isize(struct btree_trans *trans, subvol_inum inum, u64 new_i_size) @@ -233,36 +245,54 @@ static int truncate_set_isize(struct btree_trans *trans, return ret; } -int bch2_truncate(struct bch_fs *c, subvol_inum inum, u64 new_i_size, u64 *i_sectors_delta) +static int __bch2_resume_logged_op_truncate(struct btree_trans *trans, + struct bkey_i *op_k, + u64 *i_sectors_delta) { - struct btree_trans trans; + struct bch_fs *c = trans->c; struct btree_iter fpunch_iter; + struct bkey_i_logged_op_truncate *op = bkey_i_to_logged_op_truncate(op_k); + subvol_inum inum = { le32_to_cpu(op->v.subvol), le64_to_cpu(op->v.inum) }; + u64 new_i_size = le64_to_cpu(op->v.new_i_size); int ret; - bch2_trans_init(&trans, c, BTREE_ITER_MAX, 1024); - bch2_trans_iter_init(&trans, &fpunch_iter, BTREE_ID_extents, - POS(inum.inum, round_up(new_i_size, block_bytes(c)) >> 9), - BTREE_ITER_INTENT); - - ret = commit_do(&trans, NULL, NULL, BTREE_INSERT_NOFAIL, - truncate_set_isize(&trans, inum, new_i_size)); + ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, + truncate_set_isize(trans, inum, new_i_size)); if (ret) goto err; - ret = bch2_fpunch_at(&trans, &fpunch_iter, inum, U64_MAX, i_sectors_delta); + bch2_trans_iter_init(trans, &fpunch_iter, BTREE_ID_extents, + POS(inum.inum, round_up(new_i_size, block_bytes(c)) >> 9), + BTREE_ITER_INTENT); + ret = bch2_fpunch_at(trans, &fpunch_iter, inum, U64_MAX, i_sectors_delta); + bch2_trans_iter_exit(trans, &fpunch_iter); + if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) ret = 0; - if (ret) - goto err; err: - bch2_trans_iter_exit(&trans, &fpunch_iter); - bch2_trans_exit(&trans); - - bch2_fs_fatal_err_on(ret, c, "%s: error truncating %u:%llu: %s", - __func__, inum.subvol, inum.inum, bch2_err_str(ret)); + bch2_logged_op_finish(trans, op_k); return ret; } +int bch2_resume_logged_op_truncate(struct btree_trans *trans, struct bkey_i *op_k) +{ + return __bch2_resume_logged_op_truncate(trans, op_k, NULL); +} + +int bch2_truncate(struct bch_fs *c, subvol_inum inum, u64 new_i_size, u64 *i_sectors_delta) +{ + struct bkey_i_logged_op_truncate op; + + bkey_logged_op_truncate_init(&op.k_i); + op.v.subvol = cpu_to_le32(inum.subvol); + op.v.inum = cpu_to_le64(inum.inum); + op.v.new_i_size = cpu_to_le64(new_i_size); + + return bch2_trans_run(c, + bch2_logged_op_start(&trans, &op.k_i) ?: + __bch2_resume_logged_op_truncate(&trans, &op.k_i, i_sectors_delta)); +} + static int adjust_i_size(struct btree_trans *trans, subvol_inum inum, u64 offset, s64 len) { struct btree_iter iter; diff --git a/fs/bcachefs/io_misc.h b/fs/bcachefs/io_misc.h index 894a7a04ba4bf..1b792451fff24 100644 --- a/fs/bcachefs/io_misc.h +++ b/fs/bcachefs/io_misc.h @@ -9,6 +9,15 @@ int bch2_fpunch_at(struct btree_trans *, struct btree_iter *, subvol_inum, u64, s64 *); int bch2_fpunch(struct bch_fs *c, subvol_inum, u64, u64, s64 *); +void bch2_logged_op_truncate_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + +#define bch2_bkey_ops_logged_op_truncate ((struct bkey_ops) { \ + .val_to_text = bch2_logged_op_truncate_to_text, \ + .min_val_size = 24, \ +}) + +int bch2_resume_logged_op_truncate(struct btree_trans *, struct bkey_i *); + int bch2_truncate(struct bch_fs *, subvol_inum, u64, u64 *); int bch2_fcollapse_finsert(struct bch_fs *, subvol_inum, u64, u64, bool, s64 *); diff --git a/fs/bcachefs/logged_ops.c b/fs/bcachefs/logged_ops.c index 28a0e7b33e499..e133c23ad51ca 100644 --- a/fs/bcachefs/logged_ops.c +++ b/fs/bcachefs/logged_ops.c @@ -4,6 +4,7 @@ #include "bkey_buf.h" #include "btree_update.h" #include "error.h" +#include "io_misc.h" #include "logged_ops.h" #include "super.h" diff --git a/fs/bcachefs/logged_ops.h b/fs/bcachefs/logged_ops.h index 9b758008c6bd0..b2f2ebea54b6f 100644 --- a/fs/bcachefs/logged_ops.h +++ b/fs/bcachefs/logged_ops.h @@ -4,7 +4,8 @@ #include "bkey.h" -#define BCH_LOGGED_OPS() +#define BCH_LOGGED_OPS() \ + x(truncate) static inline int bch2_logged_op_update(struct btree_trans *trans, struct bkey_i *op) { -- 2.30.2