linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/15] btrfs: snapshot delete cleanups
@ 2024-04-19 18:16 Josef Bacik
  2024-04-19 18:16 ` [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down Josef Bacik
                   ` (15 more replies)
  0 siblings, 16 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:16 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Hello,

In light of the recent fix for snapshot delete I looked around at the code to
see if it could be cleaned up.  I still feel like this could be reworked to make
the two stages clearer, but this series brings a lot of cleanups and
re-factoring as well as comments and documentation that hopefully make this code
easier for others to work in.  I've broken up the do_walk_down() function into
discreet peices that are better documented and describe their use.  I've also
taken the opportunity to remove a bunch of BUG_ON()'s in this code.  I've run
this through the CI a few times as I made a couple of errors, but it's passing
cleanly now.  Thanks,

Josef

Josef Bacik (15):
  btrfs: don't do find_extent_buffer in do_walk_down
  btrfs: push ->owner_root check into btrfs_read_extent_buffer
  btrfs: use btrfs_read_extent_buffer in do_walk_down
  btrfs: push lookup_info into walk_control
  btrfs: move the eb uptodate code into it's own helper
  btrfs: remove need_account in do_walk_down
  btrfs: unify logic to decide if we need to walk down into a node
  btrfs: extract the reference dropping code into it's own helper
  btrfs: don't BUG_ON ENOMEM in walk_down_proc
  btrfs: handle errors from ref mods during UPDATE_BACKREF
  btrfs: replace BUG_ON with ASSERT in walk_down_proc
  btrfs: clean up our handling of refs == 0 in snapshot delete
  btrfs: convert correctness BUG_ON()'s to ASSERT()'s in walk_up_proc
  btrfs: handle errors from btrfs_dec_ref properly
  btrfs: add documentation around snapshot delete

 fs/btrfs/ctree.c       |   7 +-
 fs/btrfs/disk-io.c     |   6 +-
 fs/btrfs/extent-tree.c | 507 +++++++++++++++++++++++++++--------------
 3 files changed, 338 insertions(+), 182 deletions(-)

-- 
2.43.0


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

* [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
@ 2024-04-19 18:16 ` Josef Bacik
  2024-04-23 21:55   ` Qu Wenruo
  2024-04-19 18:16 ` [PATCH 02/15] btrfs: push ->owner_root check into btrfs_read_extent_buffer Josef Bacik
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:16 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

We do find_extent_buffer(), and then if we don't find the eb in cache we
call btrfs_find_create_tree_block(), which calls find_extent_buffer()
first and then allocates the extent buffer.

The reason we're doing this is because if we don't find the extent
buffer in cache we set reada = 1.  However this doesn't matter, because
lower down we only trigger reada if !btrfs_buffer_uptodate(eb), which is
what the case would be if we didn't find the extent buffer in cache and
had to allocate it.

Clean this up to simply call btrfs_find_create_tree_block(), and then
use the fact that we're having to read the extent buffer off of disk to
go ahead and kick off readahead.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 023920d0d971..64bb8c69e57a 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5434,7 +5434,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	struct btrfs_key key;
 	struct extent_buffer *next;
 	int level = wc->level;
-	int reada = 0;
 	int ret = 0;
 	bool need_account = false;
 
@@ -5460,14 +5459,11 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	btrfs_node_key_to_cpu(path->nodes[level], &check.first_key,
 			      path->slots[level]);
 
-	next = find_extent_buffer(fs_info, bytenr);
-	if (!next) {
-		next = btrfs_find_create_tree_block(fs_info, bytenr,
-				btrfs_root_id(root), level - 1);
-		if (IS_ERR(next))
-			return PTR_ERR(next);
-		reada = 1;
-	}
+	next = btrfs_find_create_tree_block(fs_info, bytenr,
+					    btrfs_root_id(root), level - 1);
+	if (IS_ERR(next))
+		return PTR_ERR(next);
+
 	btrfs_tree_lock(next);
 
 	ret = btrfs_lookup_extent_info(trans, fs_info, bytenr, level - 1, 1,
@@ -5518,7 +5514,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	}
 
 	if (!next) {
-		if (reada && level == 1)
+		if (level == 1)
 			reada_walk_down(trans, root, wc, path);
 		next = read_tree_block(fs_info, bytenr, &check);
 		if (IS_ERR(next)) {
-- 
2.43.0


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

* [PATCH 02/15] btrfs: push ->owner_root check into btrfs_read_extent_buffer
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
  2024-04-19 18:16 ` [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down Josef Bacik
@ 2024-04-19 18:16 ` Josef Bacik
  2024-04-23 22:09   ` Qu Wenruo
  2024-04-19 18:16 ` [PATCH 03/15] btrfs: use btrfs_read_extent_buffer in do_walk_down Josef Bacik
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:16 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Currently we're only doing this in read_tree_block(), however
btrfs_check_eb_owner() properly deals with ->owner_root being set to 0,
and in fact we're duplicating this check in read_block_for_search().
Push this check up into btrfs_read_extent_buffer() and fixup
read_block_for_search() to just return the result from
btrfs_read_extent_buffer() and drop the duplicate check.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ctree.c   | 7 +------
 fs/btrfs/disk-io.c | 6 ++----
 2 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 1a49b9232990..48aa14046343 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -1551,12 +1551,7 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p,
 		if (ret) {
 			free_extent_buffer(tmp);
 			btrfs_release_path(p);
-			return -EIO;
-		}
-		if (btrfs_check_eb_owner(tmp, btrfs_root_id(root))) {
-			free_extent_buffer(tmp);
-			btrfs_release_path(p);
-			return -EUCLEAN;
+			return ret;
 		}
 
 		if (unlock_up)
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index c2dc88f909b0..64523dc1060d 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -251,6 +251,8 @@ int btrfs_read_extent_buffer(struct extent_buffer *eb,
 	if (failed && !ret && failed_mirror)
 		btrfs_repair_eb_io_failure(eb, failed_mirror);
 
+	if (!ret)
+		ret = btrfs_check_eb_owner(eb, check->owner_root);
 	return ret;
 }
 
@@ -635,10 +637,6 @@ struct extent_buffer *read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
 		free_extent_buffer_stale(buf);
 		return ERR_PTR(ret);
 	}
-	if (btrfs_check_eb_owner(buf, check->owner_root)) {
-		free_extent_buffer_stale(buf);
-		return ERR_PTR(-EUCLEAN);
-	}
 	return buf;
 
 }
-- 
2.43.0


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

* [PATCH 03/15] btrfs: use btrfs_read_extent_buffer in do_walk_down
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
  2024-04-19 18:16 ` [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down Josef Bacik
  2024-04-19 18:16 ` [PATCH 02/15] btrfs: push ->owner_root check into btrfs_read_extent_buffer Josef Bacik
@ 2024-04-19 18:16 ` Josef Bacik
  2024-04-19 18:16 ` [PATCH 04/15] btrfs: push lookup_info into walk_control Josef Bacik
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:16 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Currently if our extent buffer isn't uptodate we will drop the lock,
free it, and then call read_tree_block() for the bytenr.  This is
inefficient, we already have the extent buffer, we can simply call
btrfs_read_extent_buffer().

Collapse these two cases down into one if statement, if we are not
uptodate we can drop the lock, trigger readahead, and do the read using
btrfs_read_extent_buffer(), and carry on.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 64bb8c69e57a..b8ef918ee440 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5508,22 +5508,15 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 
 	if (!btrfs_buffer_uptodate(next, generation, 0)) {
 		btrfs_tree_unlock(next);
-		free_extent_buffer(next);
-		next = NULL;
-		*lookup_info = 1;
-	}
-
-	if (!next) {
 		if (level == 1)
 			reada_walk_down(trans, root, wc, path);
-		next = read_tree_block(fs_info, bytenr, &check);
-		if (IS_ERR(next)) {
-			return PTR_ERR(next);
-		} else if (!extent_buffer_uptodate(next)) {
+		ret = btrfs_read_extent_buffer(next, &check);
+		if (ret) {
 			free_extent_buffer(next);
-			return -EIO;
+			return ret;
 		}
 		btrfs_tree_lock(next);
+		*lookup_info = 1;
 	}
 
 	level--;
-- 
2.43.0


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

* [PATCH 04/15] btrfs: push lookup_info into walk_control
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (2 preceding siblings ...)
  2024-04-19 18:16 ` [PATCH 03/15] btrfs: use btrfs_read_extent_buffer in do_walk_down Josef Bacik
@ 2024-04-19 18:16 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 05/15] btrfs: move the eb uptodate code into it's own helper Josef Bacik
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:16 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Instead of using a flag we're passing around everywhere, add a field to
walk_control that we're already passing around everywhere and use that
instead.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index b8ef918ee440..0ebbad032c73 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5221,6 +5221,7 @@ struct walk_control {
 	int reada_slot;
 	int reada_count;
 	int restarted;
+	int lookup_info;
 };
 
 #define DROP_REFERENCE	1
@@ -5317,7 +5318,7 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
 static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 				   struct btrfs_root *root,
 				   struct btrfs_path *path,
-				   struct walk_control *wc, int lookup_info)
+				   struct walk_control *wc)
 {
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	int level = wc->level;
@@ -5332,7 +5333,7 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 	 * when reference count of tree block is 1, it won't increase
 	 * again. once full backref flag is set, we never clear it.
 	 */
-	if (lookup_info &&
+	if (wc->lookup_info &&
 	    ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) ||
 	     (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag)))) {
 		BUG_ON(!path->locks[level]);
@@ -5424,7 +5425,7 @@ static int check_ref_exists(struct btrfs_trans_handle *trans,
 static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 				 struct btrfs_root *root,
 				 struct btrfs_path *path,
-				 struct walk_control *wc, int *lookup_info)
+				 struct walk_control *wc)
 {
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	u64 bytenr;
@@ -5446,7 +5447,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	 */
 	if (wc->stage == UPDATE_BACKREF &&
 	    generation <= root->root_key.offset) {
-		*lookup_info = 1;
+		wc->lookup_info = 1;
 		return 1;
 	}
 
@@ -5478,7 +5479,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 		ret = -EIO;
 		goto out_unlock;
 	}
-	*lookup_info = 0;
+	wc->lookup_info = 0;
 
 	if (wc->stage == DROP_REFERENCE) {
 		if (wc->refs[level - 1] > 1) {
@@ -5516,7 +5517,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 			return ret;
 		}
 		btrfs_tree_lock(next);
-		*lookup_info = 1;
+		wc->lookup_info = 1;
 	}
 
 	level--;
@@ -5605,7 +5606,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 			goto out_unlock;
 	}
 no_delete:
-	*lookup_info = 1;
+	wc->lookup_info = 1;
 	ret = 1;
 
 out_unlock:
@@ -5739,11 +5740,11 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
 				   struct walk_control *wc)
 {
 	int level = wc->level;
-	int lookup_info = 1;
 	int ret = 0;
 
+	wc->lookup_info = 1;
 	while (level >= 0) {
-		ret = walk_down_proc(trans, root, path, wc, lookup_info);
+		ret = walk_down_proc(trans, root, path, wc);
 		if (ret)
 			break;
 
@@ -5754,7 +5755,7 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
 		    btrfs_header_nritems(path->nodes[level]))
 			break;
 
-		ret = do_walk_down(trans, root, path, wc, &lookup_info);
+		ret = do_walk_down(trans, root, path, wc);
 		if (ret > 0) {
 			path->slots[level]++;
 			continue;
-- 
2.43.0


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

* [PATCH 05/15] btrfs: move the eb uptodate code into it's own helper
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (3 preceding siblings ...)
  2024-04-19 18:16 ` [PATCH 04/15] btrfs: push lookup_info into walk_control Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 06/15] btrfs: remove need_account in do_walk_down Josef Bacik
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

do_walk_down() already has a bunch of things going on, and there's a bit
of code related to reading in the next eb if we decide we need it.  Move
this code off into it's own helper to clean up do_walk_down() a little
bit.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 68 +++++++++++++++++++++++++++++-------------
 1 file changed, 48 insertions(+), 20 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 0ebbad032c73..9132cfb7fee1 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5409,6 +5409,51 @@ static int check_ref_exists(struct btrfs_trans_handle *trans,
 	return 1;
 }
 
+/*
+ * We may not have an uptodate block, so if we are going to walk down into this
+ * block we need to drop the lock, read it off of the disk, re-lock it and
+ * return to continue dropping the snapshot.
+ */
+static int check_next_block_uptodate(struct btrfs_trans_handle *trans,
+				     struct btrfs_root *root,
+				     struct btrfs_path *path,
+				     struct walk_control *wc,
+				     struct extent_buffer *next)
+{
+	struct btrfs_tree_parent_check check = { 0 };
+	u64 generation;
+	int level = wc->level;
+	int ret;
+
+	btrfs_assert_tree_write_locked(next);
+
+	generation = btrfs_node_ptr_generation(path->nodes[level],
+					       path->slots[level]);
+
+	if (btrfs_buffer_uptodate(next, generation, 0))
+		return 0;
+
+	check.level = level - 1;
+	check.transid = generation;
+	check.owner_root = btrfs_root_id(root);
+	check.has_first_key = true;
+	btrfs_node_key_to_cpu(path->nodes[level], &check.first_key,
+			      path->slots[level]);
+
+	btrfs_tree_unlock(next);
+	if (level == 1)
+		reada_walk_down(trans, root, wc, path);
+	ret = btrfs_read_extent_buffer(next, &check);
+	if (ret) {
+		free_extent_buffer(next);
+		return ret;
+	}
+	btrfs_tree_lock(next);
+	wc->lookup_info = 1;
+	return 0;
+}
+
+
 /*
  * helper to process tree block pointer.
  *
@@ -5431,7 +5476,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	u64 bytenr;
 	u64 generation;
 	u64 owner_root = 0;
-	struct btrfs_tree_parent_check check = { 0 };
 	struct btrfs_key key;
 	struct extent_buffer *next;
 	int level = wc->level;
@@ -5453,13 +5497,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 
 	bytenr = btrfs_node_blockptr(path->nodes[level], path->slots[level]);
 
-	check.level = level - 1;
-	check.transid = generation;
-	check.owner_root = btrfs_root_id(root);
-	check.has_first_key = true;
-	btrfs_node_key_to_cpu(path->nodes[level], &check.first_key,
-			      path->slots[level]);
-
 	next = btrfs_find_create_tree_block(fs_info, bytenr,
 					    btrfs_root_id(root), level - 1);
 	if (IS_ERR(next))
@@ -5507,18 +5544,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 			goto skip;
 	}
 
-	if (!btrfs_buffer_uptodate(next, generation, 0)) {
-		btrfs_tree_unlock(next);
-		if (level == 1)
-			reada_walk_down(trans, root, wc, path);
-		ret = btrfs_read_extent_buffer(next, &check);
-		if (ret) {
-			free_extent_buffer(next);
-			return ret;
-		}
-		btrfs_tree_lock(next);
-		wc->lookup_info = 1;
-	}
+	ret = check_next_block_uptodate(trans, root, path, wc, next);
+	if (ret)
+		return ret;
 
 	level--;
 	ASSERT(level == btrfs_header_level(next));
-- 
2.43.0


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

* [PATCH 06/15] btrfs: remove need_account in do_walk_down
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (4 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 05/15] btrfs: move the eb uptodate code into it's own helper Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 07/15] btrfs: unify logic to decide if we need to walk down into a node Josef Bacik
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

We only set this if wc->refs[level - 1] > 1, and we check this way up
above where we need it because the first thing we do before dropping our
refs is reset wc->refs[level - 1] to 0.  Re-order re-setting of wc->refs
to after our drop logic, and then remove the need_account variable and
simply check wc->refs[level - 1] directly instead of using a variable.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 9132cfb7fee1..afe0694c675b 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5480,7 +5480,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	struct extent_buffer *next;
 	int level = wc->level;
 	int ret = 0;
-	bool need_account = false;
 
 	generation = btrfs_node_ptr_generation(path->nodes[level],
 					       path->slots[level]);
@@ -5520,7 +5519,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 
 	if (wc->stage == DROP_REFERENCE) {
 		if (wc->refs[level - 1] > 1) {
-			need_account = true;
 			if (level == 1 &&
 			    (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF))
 				goto skip;
@@ -5563,8 +5561,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 		wc->reada_slot = 0;
 	return 0;
 skip:
-	wc->refs[level - 1] = 0;
-	wc->flags[level - 1] = 0;
 	if (wc->stage == DROP_REFERENCE) {
 		struct btrfs_ref ref = {
 			.action = BTRFS_DROP_DELAYED_REF,
@@ -5609,7 +5605,8 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 		 * already accounted them at merge time (replace_path),
 		 * thus we could skip expensive subtree trace here.
 		 */
-		if (btrfs_root_id(root) != BTRFS_TREE_RELOC_OBJECTID && need_account) {
+		if (btrfs_root_id(root) != BTRFS_TREE_RELOC_OBJECTID &&
+		    wc->refs[level - 1] > 1) {
 			ret = btrfs_qgroup_trace_subtree(trans, next,
 							 generation, level - 1);
 			if (ret) {
@@ -5634,6 +5631,8 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 			goto out_unlock;
 	}
 no_delete:
+	wc->refs[level - 1] = 0;
+	wc->flags[level - 1] = 0;
 	wc->lookup_info = 1;
 	ret = 1;
 
-- 
2.43.0


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

* [PATCH 07/15] btrfs: unify logic to decide if we need to walk down into a node
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (5 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 06/15] btrfs: remove need_account in do_walk_down Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 08/15] btrfs: extract the reference dropping code into it's own helper Josef Bacik
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

We currently duplicate the logic for walking into a node during snapshot
delete.  In one case it is during the actual delete, and in the other we
use it for deciding if we should reada the block or not.

Extract this code into it's own helper and comment fully what we're
doing, and then update the two users to use the new helper.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 135 +++++++++++++++++++++++++++--------------
 1 file changed, 91 insertions(+), 44 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index afe0694c675b..0f3c1ce27d17 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5227,6 +5227,81 @@ struct walk_control {
 #define DROP_REFERENCE	1
 #define UPDATE_BACKREF	2
 
+/*
+ * Decide if we need to walk down into this node to adjust the references.
+ *
+ * @root	- the root we are currently deleting.
+ * @wc		- the walk control for this deletion.
+ * @eb		- the parent eb that we're currently visiting.
+ * @refs	- the number of refs for wc->level - 1.
+ * @flags	- the flags for wc->level - 1.
+ * @slot	- the slot in the eb that we're currently checking.
+ *
+ * This is meant to be called when we're evaluating if a node we point to at
+ * wc->level should be read and walked into, or if we can simply delete our
+ * reference to it.  We return true if we should walk into the node, false if we
+ * can skip it.
+ *
+ * We have assertions in here to make sure this is called correctly.  We assume
+ * that sanity checking on the blocks read to this point has been done, so any
+ * corrupted file systems must have been caught before calling this function.
+ */
+static bool visit_node_for_delete(struct btrfs_root *root,
+				  struct walk_control *wc,
+				  struct extent_buffer *eb,
+				  u64 refs, u64 flags, int slot)
+{
+	struct btrfs_key key;
+	u64 generation;
+	int level = wc->level;
+
+	ASSERT(level > 0);
+	ASSERT(wc->refs[level - 1] > 0);
+
+	/*
+	 * The update backref stage we only want to skip if we already have
+	 * FULL_BACKREF set, otherwise we need to read.
+	 */
+	if (wc->stage == UPDATE_BACKREF) {
+		if (level == 1 && flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+			return false;
+		return true;
+	}
+
+	/*
+	 * We're the last ref on this block, we must walk into it and process
+	 * any refs it's pointing at.
+	 */
+	if (wc->refs[level - 1] == 1)
+		return true;
+
+	/*
+	 * If we're already FULL_BACKREF then we know we can just drop our
+	 * current reference.
+	 */
+	if (level == 1 && flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
+		return false;
+
+	/*
+	 * This block is older than our creation generation, we can drop our
+	 * reference to it.
+	 */
+	generation = btrfs_node_ptr_generation(eb, slot);
+	if (!wc->update_ref || generation <= root->root_key.offset)
+		return false;
+
+	/*
+	 * This block was processed from a previous snapshot deletion run, we
+	 * can skip it.
+	 */
+	btrfs_node_key_to_cpu(eb, &key, slot);
+	if (btrfs_comp_cpu_keys(&key, &wc->update_progress) < 0)
+		return false;
+
+	/* All other cases we need to wander into the node. */
+	return true;
+}
+
 static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
 				     struct btrfs_root *root,
 				     struct walk_control *wc,
@@ -5238,7 +5313,6 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
 	u64 refs;
 	u64 flags;
 	u32 nritems;
-	struct btrfs_key key;
 	struct extent_buffer *eb;
 	int ret;
 	int slot;
@@ -5280,26 +5354,9 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
 			continue;
 		BUG_ON(refs == 0);
 
-		if (wc->stage == DROP_REFERENCE) {
-			if (refs == 1)
-				goto reada;
-
-			if (wc->level == 1 &&
-			    (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF))
-				continue;
-			if (!wc->update_ref ||
-			    generation <= root->root_key.offset)
-				continue;
-			btrfs_node_key_to_cpu(eb, &key, slot);
-			ret = btrfs_comp_cpu_keys(&key,
-						  &wc->update_progress);
-			if (ret < 0)
-				continue;
-		} else {
-			if (wc->level == 1 &&
-			    (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF))
-				continue;
-		}
+		/* If we don't need to visit this node don't reada. */
+		if (!visit_node_for_delete(root, wc, eb, refs, flags, slot))
+			continue;
 reada:
 		btrfs_readahead_node_child(eb, slot);
 		nread++;
@@ -5476,7 +5533,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	u64 bytenr;
 	u64 generation;
 	u64 owner_root = 0;
-	struct btrfs_key key;
 	struct extent_buffer *next;
 	int level = wc->level;
 	int ret = 0;
@@ -5517,29 +5573,20 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 	}
 	wc->lookup_info = 0;
 
-	if (wc->stage == DROP_REFERENCE) {
-		if (wc->refs[level - 1] > 1) {
-			if (level == 1 &&
-			    (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF))
-				goto skip;
+	/* If we don't have to walk into this node skip it. */
+	if (!visit_node_for_delete(root, wc, path->nodes[level],
+				   wc->refs[level - 1], wc->flags[level - 1],
+				   path->slots[level]))
+		goto skip;
 
-			if (!wc->update_ref ||
-			    generation <= root->root_key.offset)
-				goto skip;
-
-			btrfs_node_key_to_cpu(path->nodes[level], &key,
-					      path->slots[level]);
-			ret = btrfs_comp_cpu_keys(&key, &wc->update_progress);
-			if (ret < 0)
-				goto skip;
-
-			wc->stage = UPDATE_BACKREF;
-			wc->shared_level = level - 1;
-		}
-	} else {
-		if (level == 1 &&
-		    (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF))
-			goto skip;
+	/*
+	 * We have to walk down into this node, and if we're currently at the
+	 * DROP_REFERNCE stage and this block is shared then we need to switch
+	 * to the UPDATE_BACKREF stage in order to convert to FULL_BACKREF.
+	 */
+	if (wc->stage == DROP_REFERENCE && wc->refs[level - 1] > 1) {
+		wc->stage = UPDATE_BACKREF;
+		wc->shared_level = level - 1;
 	}
 
 	ret = check_next_block_uptodate(trans, root, path, wc, next);
-- 
2.43.0


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

* [PATCH 08/15] btrfs: extract the reference dropping code into it's own helper
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (6 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 07/15] btrfs: unify logic to decide if we need to walk down into a node Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 09/15] btrfs: don't BUG_ON ENOMEM in walk_down_proc Josef Bacik
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

This is a big chunk of code in do_walk_down that will conditionally
remove the reference for the child block we're currently evaluating.
Extract it out into it's own helper and call that from do_walk_down
instead.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 162 +++++++++++++++++++++++------------------
 1 file changed, 92 insertions(+), 70 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 0f3c1ce27d17..17dabedda997 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5510,6 +5510,95 @@ static int check_next_block_uptodate(struct btrfs_trans_handle *trans,
 	return 0;
 }
 
+/*
+ * If we determine that we don't have to visit wc->level - 1 then we need to
+ * determine if we can drop our reference.
+ *
+ * If we are UPDATE_BACKREF then we will not, we need to update our backrefs.
+ *
+ * If we are DROP_REFERENCE this will figure out if we need to drop our current
+ * reference, skipping it if we dropped it from a previous incompleted drop, or
+ * dropping it if we still have a reference to it.
+ */
+static int maybe_drop_reference(struct btrfs_trans_handle *trans,
+				struct btrfs_root *root,
+				struct btrfs_path *path,
+				struct walk_control *wc,
+				struct extent_buffer *next,
+				u64 owner_root)
+{
+	struct btrfs_ref ref = {
+		.action = BTRFS_DROP_DELAYED_REF,
+		.bytenr = next->start,
+		.num_bytes = root->fs_info->nodesize,
+		.owning_root = owner_root,
+		.ref_root = btrfs_root_id(root),
+	};
+	int level = wc->level;
+	int ret;
+
+	/* We are UPDATE_BACKREF, we're not dropping anything. */
+	if (wc->stage == UPDATE_BACKREF)
+		return 0;
+
+	if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
+		ref.parent = path->nodes[level]->start;
+	} else {
+		ASSERT(btrfs_root_id(root) ==
+		       btrfs_header_owner(path->nodes[level]));
+		if (btrfs_root_id(root) !=
+		    btrfs_header_owner(path->nodes[level])) {
+			btrfs_err(root->fs_info,
+				  "mismatched block owner");
+			return -EIO;
+		}
+	}
+
+	/*
+	 * If we had a drop_progress we need to verify the refs are set as
+	 * expected.  If we find our ref then we know that from here on out
+	 * everything should be correct, and we can clear the
+	 * ->restarted flag.
+	 */
+	if (wc->restarted) {
+		ret = check_ref_exists(trans, root, next->start, ref.parent,
+				       level - 1);
+		if (ret <= 0)
+			return ret;
+		ret = 0;
+		wc->restarted = 0;
+	}
+
+	/*
+	 * Reloc tree doesn't contribute to qgroup numbers, and we have already
+	 * accounted them at merge time (replace_path), thus we could skip
+	 * expensive subtree trace here.
+	 */
+	if (btrfs_root_id(root) != BTRFS_TREE_RELOC_OBJECTID &&
+	    wc->refs[level - 1] > 1) {
+		u64 generation = btrfs_node_ptr_generation(path->nodes[level],
+							   path->slots[level]);
+
+		ret = btrfs_qgroup_trace_subtree(trans, next, generation, level - 1);
+		if (ret) {
+			btrfs_err_rl(root->fs_info,
+				     "Error %d accounting shared subtree. Quota is out of sync, rescan required.",
+				     ret);
+		}
+	}
+
+	/*
+	 * We need to update the next key in our walk control so we can
+	 * update the drop_progress key accordingly.  We don't care if
+	 * find_next_key doesn't find a key because that means we're at
+	 * the end and are going to clean up now.
+	 */
+	wc->drop_level = level;
+	find_next_key(path, level, &wc->drop_progress);
+
+	btrfs_init_tree_ref(&ref, level - 1, 0, false);
+	return btrfs_free_extent(trans, &ref);
+}
 
 /*
  * helper to process tree block pointer.
@@ -5608,76 +5697,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 		wc->reada_slot = 0;
 	return 0;
 skip:
-	if (wc->stage == DROP_REFERENCE) {
-		struct btrfs_ref ref = {
-			.action = BTRFS_DROP_DELAYED_REF,
-			.bytenr = bytenr,
-			.num_bytes = fs_info->nodesize,
-			.owning_root = owner_root,
-			.ref_root = btrfs_root_id(root),
-		};
-		if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
-			ref.parent = path->nodes[level]->start;
-		} else {
-			ASSERT(btrfs_root_id(root) ==
-			       btrfs_header_owner(path->nodes[level]));
-			if (btrfs_root_id(root) !=
-			    btrfs_header_owner(path->nodes[level])) {
-				btrfs_err(root->fs_info,
-						"mismatched block owner");
-				ret = -EIO;
-				goto out_unlock;
-			}
-		}
-
-		/*
-		 * If we had a drop_progress we need to verify the refs are set
-		 * as expected.  If we find our ref then we know that from here
-		 * on out everything should be correct, and we can clear the
-		 * ->restarted flag.
-		 */
-		if (wc->restarted) {
-			ret = check_ref_exists(trans, root, bytenr, ref.parent,
-					       level - 1);
-			if (ret < 0)
-				goto out_unlock;
-			if (ret == 0)
-				goto no_delete;
-			ret = 0;
-			wc->restarted = 0;
-		}
-
-		/*
-		 * Reloc tree doesn't contribute to qgroup numbers, and we have
-		 * already accounted them at merge time (replace_path),
-		 * thus we could skip expensive subtree trace here.
-		 */
-		if (btrfs_root_id(root) != BTRFS_TREE_RELOC_OBJECTID &&
-		    wc->refs[level - 1] > 1) {
-			ret = btrfs_qgroup_trace_subtree(trans, next,
-							 generation, level - 1);
-			if (ret) {
-				btrfs_err_rl(fs_info,
-					     "Error %d accounting shared subtree. Quota is out of sync, rescan required.",
-					     ret);
-			}
-		}
-
-		/*
-		 * We need to update the next key in our walk control so we can
-		 * update the drop_progress key accordingly.  We don't care if
-		 * find_next_key doesn't find a key because that means we're at
-		 * the end and are going to clean up now.
-		 */
-		wc->drop_level = level;
-		find_next_key(path, level, &wc->drop_progress);
-
-		btrfs_init_tree_ref(&ref, level - 1, 0, false);
-		ret = btrfs_free_extent(trans, &ref);
-		if (ret)
-			goto out_unlock;
-	}
-no_delete:
+	ret = maybe_drop_reference(trans, root, path, wc, next, owner_root);
+	if (ret)
+		goto out_unlock;
 	wc->refs[level - 1] = 0;
 	wc->flags[level - 1] = 0;
 	wc->lookup_info = 1;
-- 
2.43.0


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

* [PATCH 09/15] btrfs: don't BUG_ON ENOMEM in walk_down_proc
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (7 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 08/15] btrfs: extract the reference dropping code into it's own helper Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 10/15] btrfs: handle errors from ref mods during UPDATE_BACKREF Josef Bacik
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

We handle errors here properly, ENOMEM isn't fatal, return the error.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 17dabedda997..61bea83bce19 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5399,7 +5399,6 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 					       &wc->refs[level],
 					       &wc->flags[level],
 					       NULL);
-		BUG_ON(ret == -ENOMEM);
 		if (ret)
 			return ret;
 		BUG_ON(wc->refs[level] == 0);
-- 
2.43.0


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

* [PATCH 10/15] btrfs: handle errors from ref mods during UPDATE_BACKREF
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (8 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 09/15] btrfs: don't BUG_ON ENOMEM in walk_down_proc Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 11/15] btrfs: replace BUG_ON with ASSERT in walk_down_proc Josef Bacik
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

We have blanket BUG_ON(ret) after every one of these reference mod
attempts, which is just incorrect.  If we encounter any errors during
walk_down_tree() we will abort, so abort on any one of these failures.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 61bea83bce19..889e27911f0c 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5419,11 +5419,20 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 	if (!(wc->flags[level] & flag)) {
 		BUG_ON(!path->locks[level]);
 		ret = btrfs_inc_ref(trans, root, eb, 1);
-		BUG_ON(ret); /* -ENOMEM */
+		if (ret) {
+			btrfs_abort_transaction(trans, ret);
+			return ret;
+		}
 		ret = btrfs_dec_ref(trans, root, eb, 0);
-		BUG_ON(ret); /* -ENOMEM */
+		if (ret) {
+			btrfs_abort_transaction(trans, ret);
+			return ret;
+		}
 		ret = btrfs_set_disk_extent_flags(trans, eb, flag);
-		BUG_ON(ret); /* -ENOMEM */
+		if (ret) {
+			btrfs_abort_transaction(trans, ret);
+			return ret;
+		}
 		wc->flags[level] |= flag;
 	}
 
-- 
2.43.0


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

* [PATCH 11/15] btrfs: replace BUG_ON with ASSERT in walk_down_proc
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (9 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 10/15] btrfs: handle errors from ref mods during UPDATE_BACKREF Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 12/15] btrfs: clean up our handling of refs == 0 in snapshot delete Josef Bacik
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

We have a couple of areas where we check to make sure the tree block is
locked before looking up or messing with references.  This is old code
so it has this as BUG_ON().  Convert this to ASSERT() for developers.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 889e27911f0c..43fe12b073c3 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5393,7 +5393,7 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 	if (wc->lookup_info &&
 	    ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) ||
 	     (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag)))) {
-		BUG_ON(!path->locks[level]);
+		ASSERT(path->locks[level]);
 		ret = btrfs_lookup_extent_info(trans, fs_info,
 					       eb->start, level, 1,
 					       &wc->refs[level],
@@ -5417,7 +5417,7 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 
 	/* wc->stage == UPDATE_BACKREF */
 	if (!(wc->flags[level] & flag)) {
-		BUG_ON(!path->locks[level]);
+		ASSERT(path->locks[level]);
 		ret = btrfs_inc_ref(trans, root, eb, 1);
 		if (ret) {
 			btrfs_abort_transaction(trans, ret);
-- 
2.43.0


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

* [PATCH 12/15] btrfs: clean up our handling of refs == 0 in snapshot delete
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (10 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 11/15] btrfs: replace BUG_ON with ASSERT in walk_down_proc Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-24 12:23   ` David Sterba
  2024-04-19 18:17 ` [PATCH 13/15] btrfs: convert correctness BUG_ON()'s to ASSERT()'s in walk_up_proc Josef Bacik
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

In reada we BUG_ON(refs == 0), which could be unkind since we aren't
holding a lock on the extent leaf and thus could get a transient
incorrect answer.  In walk_down_proc we also BUG_ON(refs == 0), which
could happen if we have extent tree corruption.  Change that to return
-EUCLEAN.  In do_walk_down() we catch this case and handle it correctly,
however we return -EIO, which -EUCLEAN is a more appropriate error code.
Finally in walk_up_proc we have the same BUG_ON(refs == 0), so convert
that to proper error handling.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 43fe12b073c3..5eb39f405fd5 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5352,7 +5352,15 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
 		/* We don't care about errors in readahead. */
 		if (ret < 0)
 			continue;
-		BUG_ON(refs == 0);
+
+		/*
+		 * This could be racey, it's conceivable that we raced and end
+		 * up with a bogus refs count, if that's the case just skip, if
+		 * we are actually corrupt we will notice when we look up
+		 * everything again with our locks.
+		 */
+		if (refs == 0)
+			continue;
 
 		/* If we don't need to visit this node don't reada. */
 		if (!visit_node_for_delete(root, wc, eb, refs, flags, slot))
@@ -5401,7 +5409,10 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
 					       NULL);
 		if (ret)
 			return ret;
-		BUG_ON(wc->refs[level] == 0);
+		if (unlikely(wc->refs[level] == 0)) {
+			btrfs_err(fs_info, "Missing references.");
+			return -EUCLEAN;
+		}
 	}
 
 	if (wc->stage == DROP_REFERENCE) {
@@ -5665,7 +5676,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
 
 	if (unlikely(wc->refs[level - 1] == 0)) {
 		btrfs_err(fs_info, "Missing references.");
-		ret = -EIO;
+		ret = -EUCLEAN;
 		goto out_unlock;
 	}
 	wc->lookup_info = 0;
@@ -5776,7 +5787,10 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
 				path->locks[level] = 0;
 				return ret;
 			}
-			BUG_ON(wc->refs[level] == 0);
+			if (unlikely(wc->refs[level] == 0)) {
+				btrfs_err(fs_info, "Missing refs.");
+				return -EUCLEAN;
+			}
 			if (wc->refs[level] == 1) {
 				btrfs_tree_unlock_rw(eb, path->locks[level]);
 				path->locks[level] = 0;
-- 
2.43.0


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

* [PATCH 13/15] btrfs: convert correctness BUG_ON()'s to ASSERT()'s in walk_up_proc
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (11 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 12/15] btrfs: clean up our handling of refs == 0 in snapshot delete Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 14/15] btrfs: handle errors from btrfs_dec_ref properly Josef Bacik
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

In walk_up_proc we have several sanity checks that should only trip if
the programmer made a mistake.  Convert these to ASSERT()'s instead of
BUG_ON()'s.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 5eb39f405fd5..d3d2f61b7363 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5755,7 +5755,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
 	u64 parent = 0;
 
 	if (wc->stage == UPDATE_BACKREF) {
-		BUG_ON(wc->shared_level < level);
+		ASSERT(wc->shared_level >= level);
 		if (level < wc->shared_level)
 			goto out;
 
@@ -5773,7 +5773,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
 		 * count is one.
 		 */
 		if (!path->locks[level]) {
-			BUG_ON(level == 0);
+			ASSERT(level > 0);
 			btrfs_tree_lock(eb);
 			path->locks[level] = BTRFS_WRITE_LOCK;
 
@@ -5800,7 +5800,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
 	}
 
 	/* wc->stage == DROP_REFERENCE */
-	BUG_ON(wc->refs[level] > 1 && !path->locks[level]);
+	ASSERT(path->locks[level] || wc->refs[level] == 1);
 
 	if (wc->refs[level] == 1) {
 		if (level == 0) {
-- 
2.43.0


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

* [PATCH 14/15] btrfs: handle errors from btrfs_dec_ref properly
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (12 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 13/15] btrfs: convert correctness BUG_ON()'s to ASSERT()'s in walk_up_proc Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-19 18:17 ` [PATCH 15/15] btrfs: add documentation around snapshot delete Josef Bacik
  2024-04-24 12:31 ` [PATCH 00/15] btrfs: snapshot delete cleanups David Sterba
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

In walk_up_proc() we BUG_ON(ret) from btrfs_dec_ref().  This is
incorrect, we have proper error handling here, return the error.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index d3d2f61b7363..0f59339aac5d 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5808,7 +5808,10 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
 				ret = btrfs_dec_ref(trans, root, eb, 1);
 			else
 				ret = btrfs_dec_ref(trans, root, eb, 0);
-			BUG_ON(ret); /* -ENOMEM */
+			if (ret) {
+				btrfs_abort_transaction(trans, ret);
+				return ret;
+			}
 			if (is_fstree(btrfs_root_id(root))) {
 				ret = btrfs_qgroup_trace_leaf_items(trans, eb);
 				if (ret) {
-- 
2.43.0


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

* [PATCH 15/15] btrfs: add documentation around snapshot delete
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (13 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 14/15] btrfs: handle errors from btrfs_dec_ref properly Josef Bacik
@ 2024-04-19 18:17 ` Josef Bacik
  2024-04-24 12:31 ` [PATCH 00/15] btrfs: snapshot delete cleanups David Sterba
  15 siblings, 0 replies; 20+ messages in thread
From: Josef Bacik @ 2024-04-19 18:17 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

Snapshot delete has some complicated looking code that is weirdly subtle
at times.  I've cleaned it up the best I can without re-writing it, but
there are still a lot of details that are non-obvious.  Add a bunch of
comments to the main parts of the code to help future developers better
understand the mechanics of snapshot deletion.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent-tree.c | 52 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 0f59339aac5d..83be60f60422 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -5224,7 +5224,20 @@ struct walk_control {
 	int lookup_info;
 };
 
+/*
+ * This is our normal stage.  We are traversing blocks the current snapshot owns
+ * and we are dropping any of our references to any children we are able to, and
+ * then freeing the block once we've processed all of the children.
+ */
 #define DROP_REFERENCE	1
+
+/*
+ * We enter this stage when we have to walk into a child block (meaning we can't
+ * simply drop our reference to it from our current parent node) and there are
+ * more than one reference on it.  If we are the owner of any of the children
+ * blocks from the current parent node then we have to do the FULL_BACKREF dance
+ * on them in order to drop our normal ref and add the shared ref.
+ */
 #define UPDATE_BACKREF	2
 
 /*
@@ -5855,6 +5868,27 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
 	return -EUCLEAN;
 }
 
+/*
+ * walk_down_tree consists of two steps.
+ *
+ * walk_down_proc().  Look up the reference count and reference of our current
+ * wc->level.  At this point path->nodes[wc->level] should be populated and
+ * uptodate, and in most cases should already be locked.  If we are in
+ * DROP_REFERENCE and our refcount is > 1 then we've entered a shared node and
+ * we can walk back up the tree.  If we are UPDATE_BACKREF we have to set
+ * FULL_BACKREF on this node if it's not already set, and then do the
+ * FULL_BACKREF conversion dance, which is to drop the root reference and add
+ * the shared reference to all of this nodes children.
+ *
+ * do_walk_down().  This is where we actually start iterating on the children of
+ * our current path->nodes[wc->level].  For DROP_REFERENCE that means dropping
+ * our reference to the children that return false from visit_node_for_delete(),
+ * which has various conditions where we know we can just drop our reference
+ * without visiting the node.  For UPDATE_BACKREF we will skip any children that
+ * visit_node_for_delete() returns false for, only walking down when necessary.
+ * The bulk of the work for UPDATE_BACKREF occurs in the walk_up_tree() part of
+ * snapshot deletion.
+ */
 static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
 				   struct btrfs_root *root,
 				   struct btrfs_path *path,
@@ -5887,6 +5921,24 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
 	return (ret == 1) ? 0 : ret;
 }
 
+/*
+ * walk_up_tree is responsible for making sure we visit every slot on our
+ * current node, and if we're at the end of that node then we call
+ * walk_up_proc() on our current node which will do one of a few things based on
+ * our stage.
+ *
+ * UPDATE_BACKREF.  If we wc->level is currently less than our wc->shared_level
+ * then we need to walk back up the tree, and then going back down into the
+ * other slots via walk_down_tree to update any other children from our original
+ * wc->shared_level.  Once we're at or above our wc->shared_level we can switch
+ * back to DROP_REFERENCE, lookup the current nodes refs and flags, and carry
+ * on.
+ *
+ * DROP_REFERENCE. If our refs == 1 then we're going to free this tree block.
+ * If we're level 0 then we need to btrfs_dec_ref() on all of the data extents
+ * in our current leaf.  After that we call btrfs_free_tree_block() on the
+ * current node and walk up to the next node to walk down the next slot.
+ */
 static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
 				 struct btrfs_root *root,
 				 struct btrfs_path *path,
-- 
2.43.0


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

* Re: [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down
  2024-04-19 18:16 ` [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down Josef Bacik
@ 2024-04-23 21:55   ` Qu Wenruo
  0 siblings, 0 replies; 20+ messages in thread
From: Qu Wenruo @ 2024-04-23 21:55 UTC (permalink / raw)
  To: Josef Bacik, linux-btrfs, kernel-team



在 2024/4/20 03:46, Josef Bacik 写道:
> We do find_extent_buffer(), and then if we don't find the eb in cache we
> call btrfs_find_create_tree_block(), which calls find_extent_buffer()
> first and then allocates the extent buffer.
>
> The reason we're doing this is because if we don't find the extent
> buffer in cache we set reada = 1.  However this doesn't matter, because
> lower down we only trigger reada if !btrfs_buffer_uptodate(eb), which is
> what the case would be if we didn't find the extent buffer in cache and
> had to allocate it.
>
> Clean this up to simply call btrfs_find_create_tree_block(), and then
> use the fact that we're having to read the extent buffer off of disk to
> go ahead and kick off readahead.
>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>

Reviewed-by: Qu Wenruo <wqu@suse.com>

Thanks,
Qu
> ---
>   fs/btrfs/extent-tree.c | 16 ++++++----------
>   1 file changed, 6 insertions(+), 10 deletions(-)
>
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 023920d0d971..64bb8c69e57a 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -5434,7 +5434,6 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
>   	struct btrfs_key key;
>   	struct extent_buffer *next;
>   	int level = wc->level;
> -	int reada = 0;
>   	int ret = 0;
>   	bool need_account = false;
>
> @@ -5460,14 +5459,11 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
>   	btrfs_node_key_to_cpu(path->nodes[level], &check.first_key,
>   			      path->slots[level]);
>
> -	next = find_extent_buffer(fs_info, bytenr);
> -	if (!next) {
> -		next = btrfs_find_create_tree_block(fs_info, bytenr,
> -				btrfs_root_id(root), level - 1);
> -		if (IS_ERR(next))
> -			return PTR_ERR(next);
> -		reada = 1;
> -	}
> +	next = btrfs_find_create_tree_block(fs_info, bytenr,
> +					    btrfs_root_id(root), level - 1);
> +	if (IS_ERR(next))
> +		return PTR_ERR(next);
> +
>   	btrfs_tree_lock(next);
>
>   	ret = btrfs_lookup_extent_info(trans, fs_info, bytenr, level - 1, 1,
> @@ -5518,7 +5514,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
>   	}
>
>   	if (!next) {
> -		if (reada && level == 1)
> +		if (level == 1)
>   			reada_walk_down(trans, root, wc, path);
>   		next = read_tree_block(fs_info, bytenr, &check);
>   		if (IS_ERR(next)) {

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

* Re: [PATCH 02/15] btrfs: push ->owner_root check into btrfs_read_extent_buffer
  2024-04-19 18:16 ` [PATCH 02/15] btrfs: push ->owner_root check into btrfs_read_extent_buffer Josef Bacik
@ 2024-04-23 22:09   ` Qu Wenruo
  0 siblings, 0 replies; 20+ messages in thread
From: Qu Wenruo @ 2024-04-23 22:09 UTC (permalink / raw)
  To: Josef Bacik, linux-btrfs, kernel-team



在 2024/4/20 03:46, Josef Bacik 写道:
> Currently we're only doing this in read_tree_block(), however
> btrfs_check_eb_owner() properly deals with ->owner_root being set to 0,
> and in fact we're duplicating this check in read_block_for_search().
> Push this check up into btrfs_read_extent_buffer() and fixup
> read_block_for_search() to just return the result from
> btrfs_read_extent_buffer() and drop the duplicate check.

Since end_bbio_meta_read() is already calling
btrfs_validate_extent_buffer() with bbio->parent_check copied from the
callers, can we just remove the btrfs_check_eb_owner() calls directly
from all the higher layer callers?

Even the check in btrfs_read_extent_buffer() seems unnecessary now.

Thanks,
Qu
>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>   fs/btrfs/ctree.c   | 7 +------
>   fs/btrfs/disk-io.c | 6 ++----
>   2 files changed, 3 insertions(+), 10 deletions(-)
>
> diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
> index 1a49b9232990..48aa14046343 100644
> --- a/fs/btrfs/ctree.c
> +++ b/fs/btrfs/ctree.c
> @@ -1551,12 +1551,7 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p,
>   		if (ret) {
>   			free_extent_buffer(tmp);
>   			btrfs_release_path(p);
> -			return -EIO;
> -		}
> -		if (btrfs_check_eb_owner(tmp, btrfs_root_id(root))) {
> -			free_extent_buffer(tmp);
> -			btrfs_release_path(p);
> -			return -EUCLEAN;
> +			return ret;
>   		}
>
>   		if (unlock_up)
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index c2dc88f909b0..64523dc1060d 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -251,6 +251,8 @@ int btrfs_read_extent_buffer(struct extent_buffer *eb,
>   	if (failed && !ret && failed_mirror)
>   		btrfs_repair_eb_io_failure(eb, failed_mirror);
>
> +	if (!ret)
> +		ret = btrfs_check_eb_owner(eb, check->owner_root);
>   	return ret;
>   }
>
> @@ -635,10 +637,6 @@ struct extent_buffer *read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
>   		free_extent_buffer_stale(buf);
>   		return ERR_PTR(ret);
>   	}
> -	if (btrfs_check_eb_owner(buf, check->owner_root)) {
> -		free_extent_buffer_stale(buf);
> -		return ERR_PTR(-EUCLEAN);
> -	}
>   	return buf;
>
>   }

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

* Re: [PATCH 12/15] btrfs: clean up our handling of refs == 0 in snapshot delete
  2024-04-19 18:17 ` [PATCH 12/15] btrfs: clean up our handling of refs == 0 in snapshot delete Josef Bacik
@ 2024-04-24 12:23   ` David Sterba
  0 siblings, 0 replies; 20+ messages in thread
From: David Sterba @ 2024-04-24 12:23 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team

On Fri, Apr 19, 2024 at 02:17:07PM -0400, Josef Bacik wrote:
> In reada we BUG_ON(refs == 0), which could be unkind since we aren't
> holding a lock on the extent leaf and thus could get a transient
> incorrect answer.  In walk_down_proc we also BUG_ON(refs == 0), which
> could happen if we have extent tree corruption.  Change that to return
> -EUCLEAN.  In do_walk_down() we catch this case and handle it correctly,
> however we return -EIO, which -EUCLEAN is a more appropriate error code.
> Finally in walk_up_proc we have the same BUG_ON(refs == 0), so convert
> that to proper error handling.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>  fs/btrfs/extent-tree.c | 22 ++++++++++++++++++----
>  1 file changed, 18 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 43fe12b073c3..5eb39f405fd5 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -5352,7 +5352,15 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
>  		/* We don't care about errors in readahead. */
>  		if (ret < 0)
>  			continue;
> -		BUG_ON(refs == 0);
> +
> +		/*
> +		 * This could be racey, it's conceivable that we raced and end
> +		 * up with a bogus refs count, if that's the case just skip, if
> +		 * we are actually corrupt we will notice when we look up
> +		 * everything again with our locks.
> +		 */
> +		if (refs == 0)
> +			continue;
>  
>  		/* If we don't need to visit this node don't reada. */
>  		if (!visit_node_for_delete(root, wc, eb, refs, flags, slot))
> @@ -5401,7 +5409,10 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
>  					       NULL);
>  		if (ret)
>  			return ret;
> -		BUG_ON(wc->refs[level] == 0);
> +		if (unlikely(wc->refs[level] == 0)) {
> +			btrfs_err(fs_info, "Missing references.");

Has this message been copied from somewhere? It's quite useless and
could be either dropped completely or at least be more specific about
the error.

> +			return -EUCLEAN;
> +		}
>  	}
>  
>  	if (wc->stage == DROP_REFERENCE) {
> @@ -5665,7 +5676,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
>  
>  	if (unlikely(wc->refs[level - 1] == 0)) {
>  		btrfs_err(fs_info, "Missing references.");

Ah right, so this looks like the source.

> -		ret = -EIO;
> +		ret = -EUCLEAN;
>  		goto out_unlock;
>  	}
>  	wc->lookup_info = 0;
> @@ -5776,7 +5787,10 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
>  				path->locks[level] = 0;
>  				return ret;
>  			}
> -			BUG_ON(wc->refs[level] == 0);
> +			if (unlikely(wc->refs[level] == 0)) {
> +				btrfs_err(fs_info, "Missing refs.");
> +				return -EUCLEAN;
> +			}
>  			if (wc->refs[level] == 1) {
>  				btrfs_tree_unlock_rw(eb, path->locks[level]);
>  				path->locks[level] = 0;
> -- 
> 2.43.0
> 

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

* Re: [PATCH 00/15] btrfs: snapshot delete cleanups
  2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
                   ` (14 preceding siblings ...)
  2024-04-19 18:17 ` [PATCH 15/15] btrfs: add documentation around snapshot delete Josef Bacik
@ 2024-04-24 12:31 ` David Sterba
  15 siblings, 0 replies; 20+ messages in thread
From: David Sterba @ 2024-04-24 12:31 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team

On Fri, Apr 19, 2024 at 02:16:55PM -0400, Josef Bacik wrote:
> Hello,
> 
> In light of the recent fix for snapshot delete I looked around at the code to
> see if it could be cleaned up.  I still feel like this could be reworked to make
> the two stages clearer, but this series brings a lot of cleanups and
> re-factoring as well as comments and documentation that hopefully make this code
> easier for others to work in.  I've broken up the do_walk_down() function into
> discreet peices that are better documented and describe their use.  I've also
> taken the opportunity to remove a bunch of BUG_ON()'s in this code.  I've run
> this through the CI a few times as I made a couple of errors, but it's passing
> cleanly now.  Thanks,

We're past rc5, this series looks generally ok to me so I'd rather get
it merged to for-next. We can do minor refinements later (either still
within for-next or as separate cleanup pass).

There are some comments so I'd not send rev-by for the whole series but
consider it as such. My testing did not hit any problems either.

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

end of thread, other threads:[~2024-04-24 12:39 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-19 18:16 [PATCH 00/15] btrfs: snapshot delete cleanups Josef Bacik
2024-04-19 18:16 ` [PATCH 01/15] btrfs: don't do find_extent_buffer in do_walk_down Josef Bacik
2024-04-23 21:55   ` Qu Wenruo
2024-04-19 18:16 ` [PATCH 02/15] btrfs: push ->owner_root check into btrfs_read_extent_buffer Josef Bacik
2024-04-23 22:09   ` Qu Wenruo
2024-04-19 18:16 ` [PATCH 03/15] btrfs: use btrfs_read_extent_buffer in do_walk_down Josef Bacik
2024-04-19 18:16 ` [PATCH 04/15] btrfs: push lookup_info into walk_control Josef Bacik
2024-04-19 18:17 ` [PATCH 05/15] btrfs: move the eb uptodate code into it's own helper Josef Bacik
2024-04-19 18:17 ` [PATCH 06/15] btrfs: remove need_account in do_walk_down Josef Bacik
2024-04-19 18:17 ` [PATCH 07/15] btrfs: unify logic to decide if we need to walk down into a node Josef Bacik
2024-04-19 18:17 ` [PATCH 08/15] btrfs: extract the reference dropping code into it's own helper Josef Bacik
2024-04-19 18:17 ` [PATCH 09/15] btrfs: don't BUG_ON ENOMEM in walk_down_proc Josef Bacik
2024-04-19 18:17 ` [PATCH 10/15] btrfs: handle errors from ref mods during UPDATE_BACKREF Josef Bacik
2024-04-19 18:17 ` [PATCH 11/15] btrfs: replace BUG_ON with ASSERT in walk_down_proc Josef Bacik
2024-04-19 18:17 ` [PATCH 12/15] btrfs: clean up our handling of refs == 0 in snapshot delete Josef Bacik
2024-04-24 12:23   ` David Sterba
2024-04-19 18:17 ` [PATCH 13/15] btrfs: convert correctness BUG_ON()'s to ASSERT()'s in walk_up_proc Josef Bacik
2024-04-19 18:17 ` [PATCH 14/15] btrfs: handle errors from btrfs_dec_ref properly Josef Bacik
2024-04-19 18:17 ` [PATCH 15/15] btrfs: add documentation around snapshot delete Josef Bacik
2024-04-24 12:31 ` [PATCH 00/15] btrfs: snapshot delete cleanups David Sterba

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).