Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <kent.overstreet@linux.dev>

+75 -22
+15 -4
fs/bcachefs/bcachefs_format.h
··· 370 370 x(backpointer, 28) \ 371 371 x(inode_v3, 29) \ 372 372 x(bucket_gens, 30) \ 373 - x(snapshot_tree, 31) 373 + x(snapshot_tree, 31) \ 374 + x(logged_op_truncate, 32) 374 375 375 376 enum bch_bkey_type { 376 377 #define x(name, nr) KEY_TYPE_##name = nr, ··· 848 847 __BCH_INODE_NODUMP = 3, 849 848 __BCH_INODE_NOATIME = 4, 850 849 851 - __BCH_INODE_I_SIZE_DIRTY = 5, 852 - __BCH_INODE_I_SECTORS_DIRTY = 6, 850 + __BCH_INODE_I_SIZE_DIRTY = 5, /* obsolete */ 851 + __BCH_INODE_I_SECTORS_DIRTY = 6, /* obsolete */ 853 852 __BCH_INODE_UNLINKED = 7, 854 853 __BCH_INODE_BACKPTR_UNTRUSTED = 8, 855 854 ··· 1183 1182 } __packed __aligned(8); 1184 1183 1185 1184 #define LRU_ID_STRIPES (1U << 16) 1185 + 1186 + /* Logged operations btree: */ 1187 + 1188 + struct bch_logged_op_truncate { 1189 + struct bch_val v; 1190 + __le32 subvol; 1191 + __le32 pad; 1192 + __le64 inum; 1193 + __le64 new_i_size; 1194 + }; 1186 1195 1187 1196 /* Optional/variable size superblock sections: */ 1188 1197 ··· 2262 2251 x(deleted_inodes, 16, BTREE_ID_SNAPSHOTS, \ 2263 2252 BIT_ULL(KEY_TYPE_set)) \ 2264 2253 x(logged_ops, 17, 0, \ 2265 - 0) 2254 + BIT_ULL(KEY_TYPE_logged_op_truncate)) 2266 2255 2267 2256 enum btree_id { 2268 2257 #define x(name, nr, ...) BTREE_ID_##name = nr,
+1
fs/bcachefs/bkey_methods.c
··· 10 10 #include "error.h" 11 11 #include "extents.h" 12 12 #include "inode.h" 13 + #include "io_misc.h" 13 14 #include "lru.h" 14 15 #include "quota.h" 15 16 #include "reflink.h"
+47 -17
fs/bcachefs/io_misc.c
··· 15 15 #include "inode.h" 16 16 #include "io_misc.h" 17 17 #include "io_write.h" 18 + #include "logged_ops.h" 18 19 #include "subvolume.h" 19 20 20 21 /* Overwrites whatever was present with zeroes: */ ··· 218 217 return ret; 219 218 } 220 219 220 + /* truncate: */ 221 + 222 + void bch2_logged_op_truncate_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k) 223 + { 224 + struct bkey_s_c_logged_op_truncate op = bkey_s_c_to_logged_op_truncate(k); 225 + 226 + prt_printf(out, "subvol=%u", le32_to_cpu(op.v->subvol)); 227 + prt_printf(out, " inum=%llu", le64_to_cpu(op.v->inum)); 228 + prt_printf(out, " new_i_size=%llu", le64_to_cpu(op.v->new_i_size)); 229 + } 230 + 221 231 static int truncate_set_isize(struct btree_trans *trans, 222 232 subvol_inum inum, 223 233 u64 new_i_size) ··· 245 233 return ret; 246 234 } 247 235 248 - int bch2_truncate(struct bch_fs *c, subvol_inum inum, u64 new_i_size, u64 *i_sectors_delta) 236 + static int __bch2_resume_logged_op_truncate(struct btree_trans *trans, 237 + struct bkey_i *op_k, 238 + u64 *i_sectors_delta) 249 239 { 250 - struct btree_trans trans; 240 + struct bch_fs *c = trans->c; 251 241 struct btree_iter fpunch_iter; 242 + struct bkey_i_logged_op_truncate *op = bkey_i_to_logged_op_truncate(op_k); 243 + subvol_inum inum = { le32_to_cpu(op->v.subvol), le64_to_cpu(op->v.inum) }; 244 + u64 new_i_size = le64_to_cpu(op->v.new_i_size); 252 245 int ret; 253 246 254 - bch2_trans_init(&trans, c, BTREE_ITER_MAX, 1024); 255 - bch2_trans_iter_init(&trans, &fpunch_iter, BTREE_ID_extents, 247 + ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL, 248 + truncate_set_isize(trans, inum, new_i_size)); 249 + if (ret) 250 + goto err; 251 + 252 + bch2_trans_iter_init(trans, &fpunch_iter, BTREE_ID_extents, 256 253 POS(inum.inum, round_up(new_i_size, block_bytes(c)) >> 9), 257 254 BTREE_ITER_INTENT); 255 + ret = bch2_fpunch_at(trans, &fpunch_iter, inum, U64_MAX, i_sectors_delta); 256 + bch2_trans_iter_exit(trans, &fpunch_iter); 258 257 259 - ret = commit_do(&trans, NULL, NULL, BTREE_INSERT_NOFAIL, 260 - truncate_set_isize(&trans, inum, new_i_size)); 261 - if (ret) 262 - goto err; 263 - 264 - ret = bch2_fpunch_at(&trans, &fpunch_iter, inum, U64_MAX, i_sectors_delta); 265 258 if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) 266 259 ret = 0; 267 - if (ret) 268 - goto err; 269 260 err: 270 - bch2_trans_iter_exit(&trans, &fpunch_iter); 271 - bch2_trans_exit(&trans); 272 - 273 - bch2_fs_fatal_err_on(ret, c, "%s: error truncating %u:%llu: %s", 274 - __func__, inum.subvol, inum.inum, bch2_err_str(ret)); 261 + bch2_logged_op_finish(trans, op_k); 275 262 return ret; 263 + } 264 + 265 + int bch2_resume_logged_op_truncate(struct btree_trans *trans, struct bkey_i *op_k) 266 + { 267 + return __bch2_resume_logged_op_truncate(trans, op_k, NULL); 268 + } 269 + 270 + int bch2_truncate(struct bch_fs *c, subvol_inum inum, u64 new_i_size, u64 *i_sectors_delta) 271 + { 272 + struct bkey_i_logged_op_truncate op; 273 + 274 + bkey_logged_op_truncate_init(&op.k_i); 275 + op.v.subvol = cpu_to_le32(inum.subvol); 276 + op.v.inum = cpu_to_le64(inum.inum); 277 + op.v.new_i_size = cpu_to_le64(new_i_size); 278 + 279 + return bch2_trans_run(c, 280 + bch2_logged_op_start(&trans, &op.k_i) ?: 281 + __bch2_resume_logged_op_truncate(&trans, &op.k_i, i_sectors_delta)); 276 282 } 277 283 278 284 static int adjust_i_size(struct btree_trans *trans, subvol_inum inum, u64 offset, s64 len)
+9
fs/bcachefs/io_misc.h
··· 9 9 subvol_inum, u64, s64 *); 10 10 int bch2_fpunch(struct bch_fs *c, subvol_inum, u64, u64, s64 *); 11 11 12 + void bch2_logged_op_truncate_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); 13 + 14 + #define bch2_bkey_ops_logged_op_truncate ((struct bkey_ops) { \ 15 + .val_to_text = bch2_logged_op_truncate_to_text, \ 16 + .min_val_size = 24, \ 17 + }) 18 + 19 + int bch2_resume_logged_op_truncate(struct btree_trans *, struct bkey_i *); 20 + 12 21 int bch2_truncate(struct bch_fs *, subvol_inum, u64, u64 *); 13 22 int bch2_fcollapse_finsert(struct bch_fs *, subvol_inum, u64, u64, bool, s64 *); 14 23
+1
fs/bcachefs/logged_ops.c
··· 4 4 #include "bkey_buf.h" 5 5 #include "btree_update.h" 6 6 #include "error.h" 7 + #include "io_misc.h" 7 8 #include "logged_ops.h" 8 9 #include "super.h" 9 10
+2 -1
fs/bcachefs/logged_ops.h
··· 4 4 5 5 #include "bkey.h" 6 6 7 - #define BCH_LOGGED_OPS() 7 + #define BCH_LOGGED_OPS() \ 8 + x(truncate) 8 9 9 10 static inline int bch2_logged_op_update(struct btree_trans *trans, struct bkey_i *op) 10 11 {