btrfs: improve the batch insertion of delayed items
[platform/kernel/linux-rpi.git] / fs / btrfs / delayed-inode.c
index 257c1e1..20f3e74 100644 (file)
@@ -672,176 +672,122 @@ static void btrfs_delayed_inode_release_metadata(struct btrfs_fs_info *fs_info,
 }
 
 /*
- * This helper will insert some continuous items into the same leaf according
- * to the free space of the leaf.
+ * Insert a single delayed item or a batch of delayed items that have consecutive
+ * keys if they exist.
  */
-static int btrfs_batch_insert_items(struct btrfs_root *root,
-                                   struct btrfs_path *path,
-                                   struct btrfs_delayed_item *item)
+static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans,
+                                    struct btrfs_root *root,
+                                    struct btrfs_path *path,
+                                    struct btrfs_delayed_item *first_item)
 {
-       struct btrfs_delayed_item *curr, *next;
-       int free_space;
-       int total_size = 0;
-       struct extent_buffer *leaf;
-       char *data_ptr;
-       struct btrfs_key *keys;
-       u32 *data_size;
-       struct list_head head;
-       int slot;
+       LIST_HEAD(batch);
+       struct btrfs_delayed_item *curr;
+       struct btrfs_delayed_item *next;
+       const int max_size = BTRFS_LEAF_DATA_SIZE(root->fs_info);
+       int total_size;
        int nitems;
-       int i;
-       int ret = 0;
-
-       BUG_ON(!path->nodes[0]);
-
-       leaf = path->nodes[0];
-       free_space = btrfs_leaf_free_space(leaf);
-       INIT_LIST_HEAD(&head);
+       unsigned int nofs_flag;
+       char *ins_data = NULL;
+       struct btrfs_key *ins_keys;
+       u32 *ins_sizes;
+       int ret;
 
-       next = item;
-       nitems = 0;
+       list_add_tail(&first_item->tree_list, &batch);
+       nitems = 1;
+       total_size = first_item->data_len + sizeof(struct btrfs_item);
+       curr = first_item;
 
-       /*
-        * count the number of the continuous items that we can insert in batch
-        */
-       while (total_size + next->data_len + sizeof(struct btrfs_item) <=
-              free_space) {
-               total_size += next->data_len + sizeof(struct btrfs_item);
-               list_add_tail(&next->tree_list, &head);
-               nitems++;
+       while (true) {
+               int next_size;
 
-               curr = next;
                next = __btrfs_next_delayed_item(curr);
-               if (!next)
+               if (!next || !btrfs_is_continuous_delayed_item(curr, next))
                        break;
 
-               if (!btrfs_is_continuous_delayed_item(curr, next))
+               next_size = next->data_len + sizeof(struct btrfs_item);
+               if (total_size + next_size > max_size)
                        break;
-       }
 
-       if (!nitems) {
-               ret = 0;
-               goto out;
+               list_add_tail(&next->tree_list, &batch);
+               nitems++;
+               total_size += next_size;
+               curr = next;
        }
 
-       keys = kmalloc_array(nitems, sizeof(struct btrfs_key), GFP_NOFS);
-       if (!keys) {
-               ret = -ENOMEM;
-               goto out;
-       }
+       if (nitems == 1) {
+               ins_keys = &first_item->key;
+               ins_sizes = &first_item->data_len;
+       } else {
+               int i = 0;
 
-       data_size = kmalloc_array(nitems, sizeof(u32), GFP_NOFS);
-       if (!data_size) {
-               ret = -ENOMEM;
-               goto error;
+               ins_data = kmalloc(nitems * sizeof(u32) +
+                                  nitems * sizeof(struct btrfs_key), GFP_NOFS);
+               if (!ins_data) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ins_sizes = (u32 *)ins_data;
+               ins_keys = (struct btrfs_key *)(ins_data + nitems * sizeof(u32));
+               list_for_each_entry(curr, &batch, tree_list) {
+                       ins_keys[i] = curr->key;
+                       ins_sizes[i] = curr->data_len;
+                       i++;
+               }
        }
 
-       /* get keys of all the delayed items */
-       i = 0;
-       list_for_each_entry(next, &head, tree_list) {
-               keys[i] = next->key;
-               data_size[i] = next->data_len;
-               i++;
-       }
+       nofs_flag = memalloc_nofs_save();
+       ret = btrfs_insert_empty_items(trans, root, path, ins_keys, ins_sizes,
+                                      nitems);
+       memalloc_nofs_restore(nofs_flag);
+       if (ret)
+               goto out;
 
-       /* insert the keys of the items */
-       setup_items_for_insert(root, path, keys, data_size, nitems);
+       list_for_each_entry(curr, &batch, tree_list) {
+               char *data_ptr;
 
-       /* insert the dir index items */
-       slot = path->slots[0];
-       list_for_each_entry_safe(curr, next, &head, tree_list) {
-               data_ptr = btrfs_item_ptr(leaf, slot, char);
-               write_extent_buffer(leaf, &curr->data,
-                                   (unsigned long)data_ptr,
-                                   curr->data_len);
-               slot++;
+               data_ptr = btrfs_item_ptr(path->nodes[0], path->slots[0], char);
+               write_extent_buffer(path->nodes[0], &curr->data,
+                                   (unsigned long)data_ptr, curr->data_len);
+               path->slots[0]++;
+       }
 
-               btrfs_delayed_item_release_metadata(root, curr);
+       /*
+        * Now release our path before releasing the delayed items and their
+        * metadata reservations, so that we don't block other tasks for more
+        * time than needed.
+        */
+       btrfs_release_path(path);
 
+       list_for_each_entry_safe(curr, next, &batch, tree_list) {
                list_del(&curr->tree_list);
+               btrfs_delayed_item_release_metadata(root, curr);
                btrfs_release_delayed_item(curr);
        }
-
-error:
-       kfree(data_size);
-       kfree(keys);
 out:
+       kfree(ins_data);
        return ret;
 }
 
-/*
- * This helper can just do simple insertion that needn't extend item for new
- * data, such as directory name index insertion, inode insertion.
- */
-static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans,
-                                    struct btrfs_root *root,
-                                    struct btrfs_path *path,
-                                    struct btrfs_delayed_item *delayed_item)
-{
-       struct extent_buffer *leaf;
-       unsigned int nofs_flag;
-       char *ptr;
-       int ret;
-
-       nofs_flag = memalloc_nofs_save();
-       ret = btrfs_insert_empty_item(trans, root, path, &delayed_item->key,
-                                     delayed_item->data_len);
-       memalloc_nofs_restore(nofs_flag);
-       if (ret < 0 && ret != -EEXIST)
-               return ret;
-
-       leaf = path->nodes[0];
-
-       ptr = btrfs_item_ptr(leaf, path->slots[0], char);
-
-       write_extent_buffer(leaf, delayed_item->data, (unsigned long)ptr,
-                           delayed_item->data_len);
-       btrfs_mark_buffer_dirty(leaf);
-
-       btrfs_delayed_item_release_metadata(root, delayed_item);
-       return 0;
-}
-
-/*
- * we insert an item first, then if there are some continuous items, we try
- * to insert those items into the same leaf.
- */
 static int btrfs_insert_delayed_items(struct btrfs_trans_handle *trans,
                                      struct btrfs_path *path,
                                      struct btrfs_root *root,
                                      struct btrfs_delayed_node *node)
 {
-       struct btrfs_delayed_item *curr, *prev;
        int ret = 0;
 
-do_again:
-       mutex_lock(&node->mutex);
-       curr = __btrfs_first_delayed_insertion_item(node);
-       if (!curr)
-               goto insert_end;
-
-       ret = btrfs_insert_delayed_item(trans, root, path, curr);
-       if (ret < 0) {
-               btrfs_release_path(path);
-               goto insert_end;
-       }
+       while (ret == 0) {
+               struct btrfs_delayed_item *curr;
 
-       prev = curr;
-       curr = __btrfs_next_delayed_item(prev);
-       if (curr && btrfs_is_continuous_delayed_item(prev, curr)) {
-               /* insert the continuous items into the same leaf */
-               path->slots[0]++;
-               btrfs_batch_insert_items(root, path, curr);
+               mutex_lock(&node->mutex);
+               curr = __btrfs_first_delayed_insertion_item(node);
+               if (!curr) {
+                       mutex_unlock(&node->mutex);
+                       break;
+               }
+               ret = btrfs_insert_delayed_item(trans, root, path, curr);
+               mutex_unlock(&node->mutex);
        }
-       btrfs_release_delayed_item(prev);
-       btrfs_mark_buffer_dirty(path->nodes[0]);
-
-       btrfs_release_path(path);
-       mutex_unlock(&node->mutex);
-       goto do_again;
 
-insert_end:
-       mutex_unlock(&node->mutex);
        return ret;
 }