All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] btrfs: send: fix missing hole punching during incremental send
@ 2026-10-21 20:17 fdmanana
  2026-10-21 21:41 ` [PATCH v2] " fdmanana
  2026-10-23  8:51 ` [PATCH v3] " fdmanana
  0 siblings, 2 replies; 6+ messages in thread
From: fdmanana @ 2026-10-21 20:17 UTC (permalink / raw)
  To: linux-btrfs

From: Filipe Manana <fdmanana@suse.com>

When using the no-holes feature (enabled by default since btrfs-progs
v5.15) and doing an incremental send, we can miss issuing a hole punching
command when the following conditions happen:

1) A hole is punched in a tail section of a file extent item that happens
   to be the last item in a leaf (for example the extent covers the file
   range [1M, 3M) and a hole is punched in the range [2M, 3M)). Between
   the end of this file extent item's range and the i_size, there are
   other file extent items;

2) The next leaf was not modified in the send snapshot, remaining shared
   with the parent snapshot.

In this scenario we are unable to detect a hole was punched and never
issue a hole punch command because with no-holes there we don't have an
exlicit file extent item to mark a hole.

Fix this by teaching maybe_send_hole() to detect such case.
A test case for fstests will follow soon.

Reported-by: Tristan Elvers <tristanelvers@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/CAFoLKK+aErV4p6ZJPnVeH5SHQ5++0f=8ZgxPWe-E-CkeKBs8PA@mail.gmail.com/
Fixes: 16e7549f045d ("Btrfs: incompatible format change to remove hole extents")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
 fs/btrfs/send.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index c523bf950c89..3a4d86af9ee2 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -6371,6 +6371,11 @@ static int range_is_hole_in_parent(struct send_ctx *sctx,
 static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
 			   struct btrfs_key *key)
 {
+	BTRFS_PATH_AUTO_FREE(next_path);
+	struct btrfs_file_extent_item *ei;
+	u64 num_bytes;
+	u64 ram_bytes;
+	u64 next_offset;
 	int ret = 0;
 
 	if (sctx->cur_ino != key->objectid || !need_send_hole(sctx))
@@ -6405,6 +6410,66 @@ static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
 			ret = 0;
 	}
 	sctx->cur_inode_last_extent = btrfs_file_extent_end(path);
+
+	/*
+	 * If we are the last file extent item in a leaf, we may have had a tail
+	 * section that got a hole punched, and if there is no change in the
+	 * next leaf, we must find out if we have such a hole and punch it.
+	 */
+	if (path->slots[0] != btrfs_header_nritems(path->nodes[0]) - 1)
+		return ret;
+
+	ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			    struct btrfs_file_extent_item);
+	num_bytes = btrfs_file_extent_num_bytes(path->nodes[0], ei);
+	ram_bytes = btrfs_file_extent_ram_bytes(path->nodes[0], ei);
+	if (num_bytes == ram_bytes)
+		return ret;
+
+	next_path = alloc_path_for_send();
+	if (!next_path)
+		return -ENOMEM;
+
+	ret = btrfs_search_slot(NULL, sctx->send_root, key, next_path, 0, 0);
+	if (ret < 0)
+		return ret;
+	if (WARN_ON(ret != 0))
+		return -EUCLEAN;
+
+	ret = btrfs_next_leaf(sctx->send_root, next_path);
+	if (ret < 0) {
+		return ret;
+	} else if (ret > 0) {
+		/*
+		 * No next leaf, must punch hole to i_size and set the path back
+		 * to where it was before.
+		 */
+		next_offset = sctx->cur_inode_size;
+	} else {
+		struct btrfs_key next_key;
+
+		btrfs_item_key_to_cpu(next_path->nodes[0], &next_key,
+				      next_path->slots[0]);
+		if (next_key.objectid != key->objectid ||
+		    next_key.type != BTRFS_EXTENT_DATA_KEY)
+			next_offset = sctx->cur_inode_size;
+		else
+			next_offset = next_key.offset;
+	}
+
+	btrfs_free_path(next_path);
+	next_path = NULL;
+
+	ret = range_is_hole_in_parent(sctx, sctx->cur_inode_last_extent, next_offset);
+	if (ret < 0)
+		return ret;
+	else if (ret == 0)
+		ret = send_hole(sctx, next_offset);
+	else
+		ret = 0;
+
+	sctx->cur_inode_last_extent = next_offset;
+
 	return ret;
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-23  9:22 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-21 20:17 [PATCH] btrfs: send: fix missing hole punching during incremental send fdmanana
2026-10-21 21:41 ` [PATCH v2] " fdmanana
2026-10-23  6:39   ` Qu Wenruo
2026-10-23  8:51     ` Filipe Manana
2026-10-23  8:51 ` [PATCH v3] " fdmanana
2026-10-23  9:21   ` Qu Wenruo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.