Btrfs: Add delayed iput
[linux-2.6-block.git] / fs / btrfs / inode.c
index 82740a3c628a0add03951a21932f6b26c8a7b2e6..168e8c040aab1c958fa72eba8ae1170316c9a823 100644 (file)
@@ -2022,6 +2022,54 @@ zeroit:
        return -EIO;
 }
 
+struct delayed_iput {
+       struct list_head list;
+       struct inode *inode;
+};
+
+void btrfs_add_delayed_iput(struct inode *inode)
+{
+       struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
+       struct delayed_iput *delayed;
+
+       if (atomic_add_unless(&inode->i_count, -1, 1))
+               return;
+
+       delayed = kmalloc(sizeof(*delayed), GFP_NOFS | __GFP_NOFAIL);
+       delayed->inode = inode;
+
+       spin_lock(&fs_info->delayed_iput_lock);
+       list_add_tail(&delayed->list, &fs_info->delayed_iputs);
+       spin_unlock(&fs_info->delayed_iput_lock);
+}
+
+void btrfs_run_delayed_iputs(struct btrfs_root *root)
+{
+       LIST_HEAD(list);
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct delayed_iput *delayed;
+       int empty;
+
+       spin_lock(&fs_info->delayed_iput_lock);
+       empty = list_empty(&fs_info->delayed_iputs);
+       spin_unlock(&fs_info->delayed_iput_lock);
+       if (empty)
+               return;
+
+       down_read(&root->fs_info->cleanup_work_sem);
+       spin_lock(&fs_info->delayed_iput_lock);
+       list_splice_init(&fs_info->delayed_iputs, &list);
+       spin_unlock(&fs_info->delayed_iput_lock);
+
+       while (!list_empty(&list)) {
+               delayed = list_entry(list.next, struct delayed_iput, list);
+               list_del(&delayed->list);
+               iput(delayed->inode);
+               kfree(delayed);
+       }
+       up_read(&root->fs_info->cleanup_work_sem);
+}
+
 /*
  * This creates an orphan entry for the given inode in case something goes
  * wrong in the middle of an unlink/truncate.
@@ -5568,7 +5616,7 @@ out_fail:
  * some fairly slow code that needs optimization. This walks the list
  * of all the inodes with pending delalloc and forces them to disk.
  */
-int btrfs_start_delalloc_inodes(struct btrfs_root *root)
+int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
 {
        struct list_head *head = &root->fs_info->delalloc_inodes;
        struct btrfs_inode *binode;
@@ -5587,7 +5635,10 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root)
                spin_unlock(&root->fs_info->delalloc_lock);
                if (inode) {
                        filemap_flush(inode->i_mapping);
-                       iput(inode);
+                       if (delay_iput)
+                               btrfs_add_delayed_iput(inode);
+                       else
+                               iput(inode);
                }
                cond_resched();
                spin_lock(&root->fs_info->delalloc_lock);