Btrfs: Throttle operations if the reference cache gets too large
authorChris Mason <chris.mason@oracle.com>
Tue, 29 Jul 2008 20:15:18 +0000 (16:15 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:04:05 +0000 (11:04 -0400)
A large reference cache is directly related to a lot of work pending
for the cleaner thread.  This throttles back new operations based on
the size of the reference cache so the cleaner thread will be able to keep
up.

Overall, this actually makes the FS faster because the cleaner thread will
be more likely to find things in cache.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h

index 4eca0aa1ce74cbafab03c45133492f3e236d2297..5517dfc6f71c0732bf6ec613c3a93b0b10f750aa 100644 (file)
@@ -580,6 +580,7 @@ struct btrfs_fs_info {
        int do_barriers;
        int closing;
        atomic_t throttles;
+       atomic_t throttle_gen;
 
        u64 total_pinned;
        struct list_head dirty_cowonly_roots;
index eccdf13a95acde8afae07645242e32e3defbcf49..27ffa9b7ddc8f194b8904316b4cddfb138e175b1 100644 (file)
@@ -1171,8 +1171,10 @@ static int transaction_kthread(void *arg)
                vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
                mutex_lock(&root->fs_info->transaction_kthread_mutex);
 
-               printk("btrfs: total reference cache size %Lu\n",
-                       root->fs_info->total_ref_cache_size);
+               if (root->fs_info->total_ref_cache_size > 20 * 1024 * 1024) {
+                       printk("btrfs: total reference cache size %Lu\n",
+                               root->fs_info->total_ref_cache_size);
+               }
 
                mutex_lock(&root->fs_info->trans_mutex);
                cur = root->fs_info->running_transaction;
@@ -1256,6 +1258,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
        btrfs_mapping_init(&fs_info->mapping_tree);
        atomic_set(&fs_info->nr_async_submits, 0);
        atomic_set(&fs_info->throttles, 0);
+       atomic_set(&fs_info->throttle_gen, 0);
        fs_info->sb = sb;
        fs_info->max_extent = (u64)-1;
        fs_info->max_inline = 8192 * 1024;
index 0e294cfaa60c16873e437421e7c1660fa6ba98ec..6290cf41d647d15555aa378901822146fcb5974f 100644 (file)
@@ -2650,6 +2650,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
                }
        }
        while(1) {
+               atomic_inc(&root->fs_info->throttle_gen);
                wret = walk_down_tree(trans, root, path, &level);
                if (wret > 0)
                        break;
index 3efec25e34b08cca524408f3b693c8d6016d0edd..ded5281f8463533cd8346b588432ef17ca6e20b0 100644 (file)
@@ -974,6 +974,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
                balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages);
                if (num_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1)
                        btrfs_btree_balance_dirty(root, 1);
+               btrfs_throttle(root);
                cond_resched();
        }
 out:
index 4f977ea5497e7f3abe47b7870cccc4287cdc8f5c..7c87f863d6f7184411f1b564ad5fc683b3eed7f2 100644 (file)
@@ -2482,7 +2482,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
        btrfs_update_inode_block_group(trans, dir);
 out_unlock:
        nr = trans->blocks_used;
-       btrfs_end_transaction(trans, root);
+       btrfs_end_transaction_throttle(trans, root);
 fail:
        if (drop_inode) {
                inode_dec_link_count(inode);
@@ -2535,7 +2535,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
                drop_inode = 1;
 
        nr = trans->blocks_used;
-       btrfs_end_transaction(trans, root);
+       btrfs_end_transaction_throttle(trans, root);
 fail:
        if (drop_inode) {
                inode_dec_link_count(inode);
@@ -2609,7 +2609,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
 out_fail:
        nr = trans->blocks_used;
-       btrfs_end_transaction(trans, root);
+       btrfs_end_transaction_throttle(trans, root);
 
 out_unlock:
        if (drop_on_err)
@@ -3434,7 +3434,7 @@ static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry,
                goto out_fail;
 
 out_fail:
-       btrfs_end_transaction(trans, root);
+       btrfs_end_transaction_throttle(trans, root);
 out_unlock:
        return ret;
 }
@@ -3548,7 +3548,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
 
 out_unlock:
        nr = trans->blocks_used;
-       btrfs_end_transaction(trans, root);
+       btrfs_end_transaction_throttle(trans, root);
 out_fail:
        if (drop_inode) {
                inode_dec_link_count(inode);
index fcef3cae0c925cb31a34dfa10133e042f382f338..b8be6703189ad2f33c26d0bd0033c210f44984b4 100644 (file)
@@ -202,35 +202,64 @@ static noinline int wait_for_commit(struct btrfs_root *root,
        return 0;
 }
 
+void btrfs_throttle(struct btrfs_root *root)
+{
+       struct btrfs_fs_info *info = root->fs_info;
+
+harder:
+       if (atomic_read(&info->throttles)) {
+               DEFINE_WAIT(wait);
+               int thr;
+               int harder_count = 0;
+               thr = atomic_read(&info->throttle_gen);
+
+               do {
+                       prepare_to_wait(&info->transaction_throttle,
+                                       &wait, TASK_UNINTERRUPTIBLE);
+                       if (!atomic_read(&info->throttles)) {
+                               finish_wait(&info->transaction_throttle, &wait);
+                               break;
+                       }
+                       schedule();
+                       finish_wait(&info->transaction_throttle, &wait);
+               } while (thr == atomic_read(&info->throttle_gen));
+
+               if (harder_count < 5 &&
+                   info->total_ref_cache_size > 5 * 1024 * 1024) {
+                       harder_count++;
+                       goto harder;
+               }
+
+               if (harder_count < 10 &&
+                   info->total_ref_cache_size > 10 * 1024 * 1024) {
+                       harder_count++;
+                       goto harder;
+               }
+       }
+}
+
 static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
                          struct btrfs_root *root, int throttle)
 {
        struct btrfs_transaction *cur_trans;
+       struct btrfs_fs_info *info = root->fs_info;
 
-       mutex_lock(&root->fs_info->trans_mutex);
-       cur_trans = root->fs_info->running_transaction;
+       mutex_lock(&info->trans_mutex);
+       cur_trans = info->running_transaction;
        WARN_ON(cur_trans != trans->transaction);
        WARN_ON(cur_trans->num_writers < 1);
        cur_trans->num_writers--;
 
        if (waitqueue_active(&cur_trans->writer_wait))
                wake_up(&cur_trans->writer_wait);
-
-       if (throttle && atomic_read(&root->fs_info->throttles)) {
-               DEFINE_WAIT(wait);
-               mutex_unlock(&root->fs_info->trans_mutex);
-               prepare_to_wait(&root->fs_info->transaction_throttle, &wait,
-                               TASK_UNINTERRUPTIBLE);
-               if (atomic_read(&root->fs_info->throttles))
-                       schedule();
-               finish_wait(&root->fs_info->transaction_throttle, &wait);
-               mutex_lock(&root->fs_info->trans_mutex);
-       }
-
        put_transaction(cur_trans);
-       mutex_unlock(&root->fs_info->trans_mutex);
+       mutex_unlock(&info->trans_mutex);
        memset(trans, 0, sizeof(*trans));
        kmem_cache_free(btrfs_trans_handle_cachep, trans);
+
+       if (throttle)
+               btrfs_throttle(root);
+
        return 0;
 }
 
index 11fbdeceb26ce63a7c5bfa87c63804d6dfac2d92..df2ca2aad1c0468618fa6ddf18bc2b633a407c3b 100644 (file)
@@ -91,4 +91,5 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root);
 int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
                                   struct btrfs_root *root);
+void btrfs_throttle(struct btrfs_root *root);
 #endif