]> www.infradead.org Git - nvme.git/commitdiff
btrfs: extract the reference dropping code into it's own helper
authorJosef Bacik <josef@toxicpanda.com>
Tue, 7 May 2024 18:12:09 +0000 (14:12 -0400)
committerDavid Sterba <dsterba@suse.com>
Thu, 11 Jul 2024 13:33:24 +0000 (15:33 +0200)
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>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent-tree.c

index 24d2f7f64f48e64a3854f5a90be3aa7072fe1788..92549499b3538b1e7a617804a258fc30a2997391 100644 (file)
@@ -5506,6 +5506,90 @@ 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.
  *
@@ -5603,76 +5687,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;