btrfs: reuse cloned extent buffer during fiemap to avoid re-allocations
[linux-2.6-microblaze.git] / fs / btrfs / extent_io.c
index fbb05b0..7441245 100644 (file)
@@ -2752,7 +2752,7 @@ static int emit_last_fiemap_cache(struct fiemap_extent_info *fieinfo,
 
 static int fiemap_next_leaf_item(struct btrfs_inode *inode, struct btrfs_path *path)
 {
-       struct extent_buffer *clone;
+       struct extent_buffer *clone = path->nodes[0];
        struct btrfs_key key;
        int slot;
        int ret;
@@ -2761,29 +2761,45 @@ static int fiemap_next_leaf_item(struct btrfs_inode *inode, struct btrfs_path *p
        if (path->slots[0] < btrfs_header_nritems(path->nodes[0]))
                return 0;
 
+       /*
+        * Add a temporary extra ref to an already cloned extent buffer to
+        * prevent btrfs_next_leaf() freeing it, we want to reuse it to avoid
+        * the cost of allocating a new one.
+        */
+       ASSERT(test_bit(EXTENT_BUFFER_UNMAPPED, &clone->bflags));
+       atomic_inc(&clone->refs);
+
        ret = btrfs_next_leaf(inode->root, path);
        if (ret != 0)
-               return ret;
+               goto out;
 
        /*
         * Don't bother with cloning if there are no more file extent items for
         * our inode.
         */
        btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
-       if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY)
-               return 1;
+       if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY) {
+               ret = 1;
+               goto out;
+       }
 
        /* See the comment at fiemap_search_slot() about why we clone. */
-       clone = btrfs_clone_extent_buffer(path->nodes[0]);
-       if (!clone)
-               return -ENOMEM;
+       copy_extent_buffer_full(clone, path->nodes[0]);
+       /*
+        * Important to preserve the start field, for the optimizations when
+        * checking if extents are shared (see extent_fiemap()).
+        */
+       clone->start = path->nodes[0]->start;
 
        slot = path->slots[0];
        btrfs_release_path(path);
        path->nodes[0] = clone;
        path->slots[0] = slot;
+out:
+       if (ret)
+               free_extent_buffer(clone);
 
-       return 0;
+       return ret;
 }
 
 /*