bcachefs: Log truncate operations
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 10 Sep 2023 20:42:30 +0000 (16:42 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:12 +0000 (17:10 -0400)
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 <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs_format.h
fs/bcachefs/bkey_methods.c
fs/bcachefs/io_misc.c
fs/bcachefs/io_misc.h
fs/bcachefs/logged_ops.c
fs/bcachefs/logged_ops.h

index 31efa9e381cea379e771d1f179d44ac7b159ea6a..3c9e788f1c9ddfd9c9fdc4f402ae00bb4b18eb95 100644 (file)
@@ -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,
index 6547142db42806cfebfa92e5bca81bba600287df..91e28ee3efff8c11e2533c859f211d0b8ec2d88c 100644 (file)
@@ -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"
index 1afea613df4a9e476a25b3e33111223ec5ab9153..327b3dd642de2bd23726e0da42515549e194d259 100644 (file)
@@ -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;
index 894a7a04ba4bf7ff3d38615af3781db011224ca4..1b792451fff24cc1d11e1f754ef0e470511c27b3 100644 (file)
@@ -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 *);
 
index 28a0e7b33e499e8fd6615def31336d31bafce95a..e133c23ad51caa9c359182fd1e7010b79a73ec6f 100644 (file)
@@ -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"
 
index 9b758008c6bd0a8d5244c228c275f0eaf88e276f..b2f2ebea54b6fc3604687623e68f231f76fc8673 100644 (file)
@@ -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)
 {