bcachefs: Don't run indirect extent trigger unless inserting/deleting
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 3 Dec 2023 18:05:21 +0000 (13:05 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 4 Dec 2023 21:04:55 +0000 (16:04 -0500)
This fixes a transaction path overflow reported in the snapshot deletion
path, when moving extents to the correct snapshot.

The root of the issue is that creating/deleting a reflink pointer can
generate an unbounded number of updates, if it is allowed to reference
an unbounded number of indirect extents; to prevent this, merging of
reflink pointers has been disabled.

But there's a hole, which is that copygc/rebalance may fragment existing
extents in the course of moving them around, and if an indirect extent
becomes too fragmented we'll then become unable to delete the reflink
pointer.

The eventual solution is going to be to tweak trigger handling so that
we can process large reflink pointers incrementally when necessary, and
notice that trigger updates don't need to be run for the part of the
reflink pointer not changing. That is going to be a bigger project
though, for another patch.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/reflink.c

index 6e1bfe9feb59e4abe96e1dc74b30196fa5766f48..37d16e04e6715a56c8fdd328803fcb796c629a43 100644 (file)
@@ -121,6 +121,14 @@ int bch2_trans_mark_reflink_v(struct btree_trans *trans,
 {
        check_indirect_extent_deleting(new, &flags);
 
+       if (old.k->type == KEY_TYPE_reflink_v &&
+           new->k.type == KEY_TYPE_reflink_v &&
+           old.k->u64s == new->k.u64s &&
+           !memcmp(bkey_s_c_to_reflink_v(old).v->start,
+                   bkey_i_to_reflink_v(new)->v.start,
+                   bkey_val_bytes(&new->k) - 8))
+               return 0;
+
        return bch2_trans_mark_extent(trans, btree_id, level, old, new, flags);
 }