btrfs: preallocate temporary extent buffer for inode logging when needed
[sfrench/cifs-2.6.git] / fs / btrfs / tree-log.c
index 043b8df5665ff71b1e76793c35c2f6e220c2b07e..d7693368f34f1058b19e45af96f6dc1e63be1a43 100644 (file)
@@ -3617,6 +3617,30 @@ out:
        return ret;
 }
 
+static int clone_leaf(struct btrfs_path *path, struct btrfs_log_ctx *ctx)
+{
+       const int slot = path->slots[0];
+
+       if (ctx->scratch_eb) {
+               copy_extent_buffer_full(ctx->scratch_eb, path->nodes[0]);
+       } else {
+               ctx->scratch_eb = btrfs_clone_extent_buffer(path->nodes[0]);
+               if (!ctx->scratch_eb)
+                       return -ENOMEM;
+       }
+
+       btrfs_release_path(path);
+       path->nodes[0] = ctx->scratch_eb;
+       path->slots[0] = slot;
+       /*
+        * Add extra ref to scratch eb so that it is not freed when callers
+        * release the path, so we can reuse it later if needed.
+        */
+       atomic_inc(&ctx->scratch_eb->refs);
+
+       return 0;
+}
+
 static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
                                  struct btrfs_inode *inode,
                                  struct btrfs_path *path,
@@ -3631,23 +3655,20 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
        bool last_found = false;
        int batch_start = 0;
        int batch_size = 0;
-       int i;
+       int ret;
 
        /*
         * We need to clone the leaf, release the read lock on it, and use the
         * clone before modifying the log tree. See the comment at copy_items()
         * about why we need to do this.
         */
-       src = btrfs_clone_extent_buffer(path->nodes[0]);
-       if (!src)
-               return -ENOMEM;
+       ret = clone_leaf(path, ctx);
+       if (ret < 0)
+               return ret;
 
-       i = path->slots[0];
-       btrfs_release_path(path);
-       path->nodes[0] = src;
-       path->slots[0] = i;
+       src = path->nodes[0];
 
-       for (; i < nritems; i++) {
+       for (int i = path->slots[0]; i < nritems; i++) {
                struct btrfs_dir_item *di;
                struct btrfs_key key;
                int ret;
@@ -4257,17 +4278,16 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
                               struct btrfs_path *dst_path,
                               struct btrfs_path *src_path,
                               int start_slot, int nr, int inode_only,
-                              u64 logged_isize)
+                              u64 logged_isize, struct btrfs_log_ctx *ctx)
 {
        struct btrfs_root *log = inode->root->log_root;
        struct btrfs_file_extent_item *extent;
        struct extent_buffer *src;
-       int ret = 0;
+       int ret;
        struct btrfs_key *ins_keys;
        u32 *ins_sizes;
        struct btrfs_item_batch batch;
        char *ins_data;
-       int i;
        int dst_index;
        const bool skip_csum = (inode->flags & BTRFS_INODE_NODATASUM);
        const u64 i_size = i_size_read(&inode->vfs_inode);
@@ -4300,14 +4320,11 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
         * while the other is holding the delayed node's mutex and wants to
         * write lock the same subvolume leaf for flushing delayed items.
         */
-       src = btrfs_clone_extent_buffer(src_path->nodes[0]);
-       if (!src)
-               return -ENOMEM;
+       ret = clone_leaf(src_path, ctx);
+       if (ret < 0)
+               return ret;
 
-       i = src_path->slots[0];
-       btrfs_release_path(src_path);
-       src_path->nodes[0] = src;
-       src_path->slots[0] = i;
+       src = src_path->nodes[0];
 
        ins_data = kmalloc(nr * sizeof(struct btrfs_key) +
                           nr * sizeof(u32), GFP_NOFS);
@@ -4322,7 +4339,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
        batch.nr = 0;
 
        dst_index = 0;
-       for (i = 0; i < nr; i++) {
+       for (int i = 0; i < nr; i++) {
                const int src_slot = start_slot + i;
                struct btrfs_root *csum_root;
                struct btrfs_ordered_sum *sums;
@@ -4429,7 +4446,7 @@ add_to_batch:
                goto out;
 
        dst_index = 0;
-       for (i = 0; i < nr; i++) {
+       for (int i = 0; i < nr; i++) {
                const int src_slot = start_slot + i;
                const int dst_slot = dst_path->slots[0] + dst_index;
                struct btrfs_key key;
@@ -4702,7 +4719,8 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
  */
 static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
                                      struct btrfs_inode *inode,
-                                     struct btrfs_path *path)
+                                     struct btrfs_path *path,
+                                     struct btrfs_log_ctx *ctx)
 {
        struct btrfs_root *root = inode->root;
        struct btrfs_key key;
@@ -4768,7 +4786,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
                if (slot >= btrfs_header_nritems(leaf)) {
                        if (ins_nr > 0) {
                                ret = copy_items(trans, inode, dst_path, path,
-                                                start_slot, ins_nr, 1, 0);
+                                                start_slot, ins_nr, 1, 0, ctx);
                                if (ret < 0)
                                        goto out;
                                ins_nr = 0;
@@ -4818,7 +4836,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
        }
        if (ins_nr > 0)
                ret = copy_items(trans, inode, dst_path, path,
-                                start_slot, ins_nr, 1, 0);
+                                start_slot, ins_nr, 1, 0, ctx);
 out:
        btrfs_release_path(path);
        btrfs_free_path(dst_path);
@@ -4897,7 +4915,7 @@ process:
        write_unlock(&tree->lock);
 
        if (!ret)
-               ret = btrfs_log_prealloc_extents(trans, inode, path);
+               ret = btrfs_log_prealloc_extents(trans, inode, path, ctx);
        if (ret)
                return ret;
 
@@ -4978,7 +4996,8 @@ static int logged_inode_size(struct btrfs_root *log, struct btrfs_inode *inode,
 static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans,
                                struct btrfs_inode *inode,
                                struct btrfs_path *path,
-                               struct btrfs_path *dst_path)
+                               struct btrfs_path *dst_path,
+                               struct btrfs_log_ctx *ctx)
 {
        struct btrfs_root *root = inode->root;
        int ret;
@@ -5007,7 +5026,7 @@ static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans,
                if (slot >= nritems) {
                        if (ins_nr > 0) {
                                ret = copy_items(trans, inode, dst_path, path,
-                                                start_slot, ins_nr, 1, 0);
+                                                start_slot, ins_nr, 1, 0, ctx);
                                if (ret < 0)
                                        return ret;
                                ins_nr = 0;
@@ -5033,7 +5052,7 @@ static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans,
        }
        if (ins_nr > 0) {
                ret = copy_items(trans, inode, dst_path, path,
-                                start_slot, ins_nr, 1, 0);
+                                start_slot, ins_nr, 1, 0, ctx);
                if (ret < 0)
                        return ret;
        }
@@ -5845,7 +5864,7 @@ again:
                                }
                                ret = copy_items(trans, inode, dst_path, path,
                                                 ins_start_slot, ins_nr,
-                                                inode_only, logged_isize);
+                                                inode_only, logged_isize, ctx);
                                if (ret < 0)
                                        return ret;
                                ins_nr = 0;
@@ -5864,7 +5883,7 @@ again:
                                goto next_slot;
                        ret = copy_items(trans, inode, dst_path, path,
                                         ins_start_slot,
-                                        ins_nr, inode_only, logged_isize);
+                                        ins_nr, inode_only, logged_isize, ctx);
                        if (ret < 0)
                                return ret;
                        ins_nr = 0;
@@ -5881,7 +5900,7 @@ again:
                }
 
                ret = copy_items(trans, inode, dst_path, path, ins_start_slot,
-                                ins_nr, inode_only, logged_isize);
+                                ins_nr, inode_only, logged_isize, ctx);
                if (ret < 0)
                        return ret;
                ins_nr = 1;
@@ -5896,7 +5915,7 @@ next_slot:
                if (ins_nr) {
                        ret = copy_items(trans, inode, dst_path, path,
                                         ins_start_slot, ins_nr, inode_only,
-                                        logged_isize);
+                                        logged_isize, ctx);
                        if (ret < 0)
                                return ret;
                        ins_nr = 0;
@@ -5921,7 +5940,7 @@ next_key:
        }
        if (ins_nr) {
                ret = copy_items(trans, inode, dst_path, path, ins_start_slot,
-                                ins_nr, inode_only, logged_isize);
+                                ins_nr, inode_only, logged_isize, ctx);
                if (ret)
                        return ret;
        }
@@ -5932,7 +5951,7 @@ next_key:
                 * lock the same leaf with btrfs_log_prealloc_extents() below.
                 */
                btrfs_release_path(path);
-               ret = btrfs_log_prealloc_extents(trans, inode, dst_path);
+               ret = btrfs_log_prealloc_extents(trans, inode, dst_path, ctx);
        }
 
        return ret;
@@ -6524,7 +6543,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
 
        btrfs_release_path(path);
        btrfs_release_path(dst_path);
-       ret = btrfs_log_all_xattrs(trans, inode, path, dst_path);
+       ret = btrfs_log_all_xattrs(trans, inode, path, dst_path, ctx);
        if (ret)
                goto out_unlock;
        xattrs_logged = true;
@@ -6551,7 +6570,7 @@ log_extents:
                 * BTRFS_INODE_COPY_EVERYTHING set.
                 */
                if (!xattrs_logged && inode->logged_trans < trans->transid) {
-                       ret = btrfs_log_all_xattrs(trans, inode, path, dst_path);
+                       ret = btrfs_log_all_xattrs(trans, inode, path, dst_path, ctx);
                        if (ret)
                                goto out_unlock;
                        btrfs_release_path(path);
@@ -7500,6 +7519,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
 
        btrfs_init_log_ctx(&ctx, &inode->vfs_inode);
        ctx.logging_new_name = true;
+       btrfs_init_log_ctx_scratch_eb(&ctx);
        /*
         * We don't care about the return value. If we fail to log the new name
         * then we know the next attempt to sync the log will fallback to a full
@@ -7508,6 +7528,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
         * inconsistent state after a rename operation.
         */
        btrfs_log_inode_parent(trans, inode, parent, LOG_INODE_EXISTS, &ctx);
+       free_extent_buffer(ctx.scratch_eb);
        ASSERT(list_empty(&ctx.conflict_inodes));
 out:
        /*