aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.cz>2013-03-12 11:13:28 -0400
committerJosef Bacik <jbacik@fusionio.com>2013-05-06 15:54:21 -0400
commit9d1a2a3ad59f7ae810bf04a5a05995bf2d79300c (patch)
tree1648c620a5264275004bd09c207292697b450367 /fs/btrfs
parent6841ebee6b02abe178abd30f40806e385cd96777 (diff)
btrfs: clean snapshots one by one
Each time pick one dead root from the list and let the caller know if it's needed to continue. This should improve responsiveness during umount and balance which at some point waits for cleaning all currently queued dead roots. A new dead root is added to the end of the list, so the snapshots disappear in the order of deletion. The snapshot cleaning work is now done only from the cleaner thread and the others wake it if needed. Signed-off-by: David Sterba <dsterba@suse.cz> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/disk-io.c17
-rw-r--r--fs/btrfs/extent-tree.c8
-rw-r--r--fs/btrfs/relocation.c3
-rw-r--r--fs/btrfs/transaction.c56
-rw-r--r--fs/btrfs/transaction.h2
5 files changed, 57 insertions, 29 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 898263f56d96..b8ed1d4fe509 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1658,15 +1658,20 @@ static int cleaner_kthread(void *arg)
1658 struct btrfs_root *root = arg; 1658 struct btrfs_root *root = arg;
1659 1659
1660 do { 1660 do {
1661 int again = 0;
1662
1661 if (!(root->fs_info->sb->s_flags & MS_RDONLY) && 1663 if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
1662 mutex_trylock(&root->fs_info->cleaner_mutex)) { 1664 down_read_trylock(&root->fs_info->sb->s_umount)) {
1663 btrfs_run_delayed_iputs(root); 1665 if (mutex_trylock(&root->fs_info->cleaner_mutex)) {
1664 btrfs_clean_old_snapshots(root); 1666 btrfs_run_delayed_iputs(root);
1665 mutex_unlock(&root->fs_info->cleaner_mutex); 1667 again = btrfs_clean_one_deleted_snapshot(root);
1668 mutex_unlock(&root->fs_info->cleaner_mutex);
1669 }
1666 btrfs_run_defrag_inodes(root->fs_info); 1670 btrfs_run_defrag_inodes(root->fs_info);
1671 up_read(&root->fs_info->sb->s_umount);
1667 } 1672 }
1668 1673
1669 if (!try_to_freeze()) { 1674 if (!try_to_freeze() && !again) {
1670 set_current_state(TASK_INTERRUPTIBLE); 1675 set_current_state(TASK_INTERRUPTIBLE);
1671 if (!kthread_should_stop()) 1676 if (!kthread_should_stop())
1672 schedule(); 1677 schedule();
@@ -3358,8 +3363,8 @@ int btrfs_commit_super(struct btrfs_root *root)
3358 3363
3359 mutex_lock(&root->fs_info->cleaner_mutex); 3364 mutex_lock(&root->fs_info->cleaner_mutex);
3360 btrfs_run_delayed_iputs(root); 3365 btrfs_run_delayed_iputs(root);
3361 btrfs_clean_old_snapshots(root);
3362 mutex_unlock(&root->fs_info->cleaner_mutex); 3366 mutex_unlock(&root->fs_info->cleaner_mutex);
3367 wake_up_process(root->fs_info->cleaner_kthread);
3363 3368
3364 /* wait until ongoing cleanup work done */ 3369 /* wait until ongoing cleanup work done */
3365 down_write(&root->fs_info->cleanup_work_sem); 3370 down_write(&root->fs_info->cleanup_work_sem);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 7505856df9f3..be09157ff91b 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -7286,6 +7286,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
7286 * reference count by one. if update_ref is true, this function 7286 * reference count by one. if update_ref is true, this function
7287 * also make sure backrefs for the shared block and all lower level 7287 * also make sure backrefs for the shared block and all lower level
7288 * blocks are properly updated. 7288 * blocks are properly updated.
7289 *
7290 * If called with for_reloc == 0, may exit early with -EAGAIN
7289 */ 7291 */
7290int btrfs_drop_snapshot(struct btrfs_root *root, 7292int btrfs_drop_snapshot(struct btrfs_root *root,
7291 struct btrfs_block_rsv *block_rsv, int update_ref, 7293 struct btrfs_block_rsv *block_rsv, int update_ref,
@@ -7386,6 +7388,12 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
7386 wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root); 7388 wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root);
7387 7389
7388 while (1) { 7390 while (1) {
7391 if (!for_reloc && btrfs_fs_closing(root->fs_info)) {
7392 pr_debug("btrfs: drop snapshot early exit\n");
7393 err = -EAGAIN;
7394 goto out_end_trans;
7395 }
7396
7389 ret = walk_down_tree(trans, root, path, wc); 7397 ret = walk_down_tree(trans, root, path, wc);
7390 if (ret < 0) { 7398 if (ret < 0) {
7391 err = ret; 7399 err = ret;
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 86f192ffc212..4ef5f7455fb3 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -4148,10 +4148,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
4148 4148
4149 while (1) { 4149 while (1) {
4150 mutex_lock(&fs_info->cleaner_mutex); 4150 mutex_lock(&fs_info->cleaner_mutex);
4151
4152 btrfs_clean_old_snapshots(fs_info->tree_root);
4153 ret = relocate_block_group(rc); 4151 ret = relocate_block_group(rc);
4154
4155 mutex_unlock(&fs_info->cleaner_mutex); 4152 mutex_unlock(&fs_info->cleaner_mutex);
4156 if (ret < 0) { 4153 if (ret < 0) {
4157 err = ret; 4154 err = ret;
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 6c0a72ab6de0..5a5ea99f29ed 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -948,7 +948,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
948int btrfs_add_dead_root(struct btrfs_root *root) 948int btrfs_add_dead_root(struct btrfs_root *root)
949{ 949{
950 spin_lock(&root->fs_info->trans_lock); 950 spin_lock(&root->fs_info->trans_lock);
951 list_add(&root->root_list, &root->fs_info->dead_roots); 951 list_add_tail(&root->root_list, &root->fs_info->dead_roots);
952 spin_unlock(&root->fs_info->trans_lock); 952 spin_unlock(&root->fs_info->trans_lock);
953 return 0; 953 return 0;
954} 954}
@@ -1873,31 +1873,49 @@ cleanup_transaction:
1873} 1873}
1874 1874
1875/* 1875/*
1876 * interface function to delete all the snapshots we have scheduled for deletion 1876 * return < 0 if error
1877 * 0 if there are no more dead_roots at the time of call
1878 * 1 there are more to be processed, call me again
1879 *
1880 * The return value indicates there are certainly more snapshots to delete, but
1881 * if there comes a new one during processing, it may return 0. We don't mind,
1882 * because btrfs_commit_super will poke cleaner thread and it will process it a
1883 * few seconds later.
1877 */ 1884 */
1878int btrfs_clean_old_snapshots(struct btrfs_root *root) 1885int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
1879{ 1886{
1880 LIST_HEAD(list); 1887 int ret;
1881 struct btrfs_fs_info *fs_info = root->fs_info; 1888 struct btrfs_fs_info *fs_info = root->fs_info;
1882 1889
1890 if (fs_info->sb->s_flags & MS_RDONLY) {
1891 pr_debug("btrfs: cleaner called for RO fs!\n");
1892 return 0;
1893 }
1894
1883 spin_lock(&fs_info->trans_lock); 1895 spin_lock(&fs_info->trans_lock);
1884 list_splice_init(&fs_info->dead_roots, &list); 1896 if (list_empty(&fs_info->dead_roots)) {
1897 spin_unlock(&fs_info->trans_lock);
1898 return 0;
1899 }
1900 root = list_first_entry(&fs_info->dead_roots,
1901 struct btrfs_root, root_list);
1902 list_del(&root->root_list);
1885 spin_unlock(&fs_info->trans_lock); 1903 spin_unlock(&fs_info->trans_lock);
1886 1904
1887 while (!list_empty(&list)) { 1905 pr_debug("btrfs: cleaner removing %llu\n",
1888 int ret; 1906 (unsigned long long)root->objectid);
1889
1890 root = list_entry(list.next, struct btrfs_root, root_list);
1891 list_del(&root->root_list);
1892 1907
1893 btrfs_kill_all_delayed_nodes(root); 1908 btrfs_kill_all_delayed_nodes(root);
1894 1909
1895 if (btrfs_header_backref_rev(root->node) < 1910 if (btrfs_header_backref_rev(root->node) <
1896 BTRFS_MIXED_BACKREF_REV) 1911 BTRFS_MIXED_BACKREF_REV)
1897 ret = btrfs_drop_snapshot(root, NULL, 0, 0); 1912 ret = btrfs_drop_snapshot(root, NULL, 0, 0);
1898 else 1913 else
1899 ret =btrfs_drop_snapshot(root, NULL, 1, 0); 1914 ret = btrfs_drop_snapshot(root, NULL, 1, 0);
1900 BUG_ON(ret < 0); 1915 /*
1901 } 1916 * If we encounter a transaction abort during snapshot cleaning, we
1902 return 0; 1917 * don't want to crash here
1918 */
1919 BUG_ON(ret < 0 && ret != -EAGAIN && ret != -EROFS);
1920 return 1;
1903} 1921}
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 3c8e0d25c8e4..f6edd5e6baa3 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -123,7 +123,7 @@ int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
123 123
124int btrfs_add_dead_root(struct btrfs_root *root); 124int btrfs_add_dead_root(struct btrfs_root *root);
125int btrfs_defrag_root(struct btrfs_root *root); 125int btrfs_defrag_root(struct btrfs_root *root);
126int btrfs_clean_old_snapshots(struct btrfs_root *root); 126int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root);
127int btrfs_commit_transaction(struct btrfs_trans_handle *trans, 127int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
128 struct btrfs_root *root); 128 struct btrfs_root *root);
129int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans, 129int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,