Btrfs: Btree defrag on the extent-mapping tree as well
authorChris Mason <chris.mason@oracle.com>
Fri, 10 Aug 2007 18:06:19 +0000 (14:06 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Fri, 10 Aug 2007 18:06:19 +0000 (14:06 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c
fs/btrfs/inode.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/tree-defrag.c

index c7e47e77723ff413c5350a7949fdeb02500b3a4b..ee1ae00d2827a34dff02d93f557b3ba5df861c52 100644 (file)
@@ -99,7 +99,6 @@ static int __btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
                if (ret)
                        return ret;
        } else {
-               WARN_ON(!root->ref_cows);
                clean_tree_block(trans, root, buf);
        }
 
@@ -162,13 +161,14 @@ static int close_blocks(u64 blocknr, u64 other)
 
 int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root, struct buffer_head *parent,
-                      int cache_only)
+                      int cache_only, u64 *last_ret)
 {
        struct btrfs_node *parent_node;
        struct buffer_head *cur_bh;
        struct buffer_head *tmp_bh;
        u64 blocknr;
-       u64 search_start = 0;
+       u64 search_start = *last_ret;
+       u64 last_block = 0;
        u64 other;
        u32 parent_nritems;
        int start_slot;
@@ -198,6 +198,8 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
        for (i = start_slot; i < end_slot; i++) {
                int close = 1;
                blocknr = btrfs_node_blockptr(parent_node, i);
+               if (last_block == 0)
+                       last_block = blocknr;
                if (i > 0) {
                        other = btrfs_node_blockptr(parent_node, i - 1);
                        close = close_blocks(blocknr, other);
@@ -206,8 +208,10 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                        other = btrfs_node_blockptr(parent_node, i + 1);
                        close = close_blocks(blocknr, other);
                }
-               if (close)
+               if (close) {
+                       last_block = blocknr;
                        continue;
+               }
 
                cur_bh = btrfs_find_tree_block(root, blocknr);
                if (!cur_bh || !buffer_uptodate(cur_bh) ||
@@ -219,9 +223,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                        brelse(cur_bh);
                        cur_bh = read_tree_block(root, blocknr);
                }
-               if (search_start == 0) {
-                       search_start = bh_blocknr(cur_bh) & ~((u64)65535);
-               }
+               if (search_start == 0)
+                       search_start = last_block & ~((u64)65535);
+
                err = __btrfs_cow_block(trans, root, cur_bh, parent, i,
                                        &tmp_bh, search_start,
                                        min(8, end_slot - i));
index 59e09e37ab9306f8cef1e26e50f352cb9317cafa..d3cd564b3b3f3af4504a1b3d1cc71475fd4256d5 100644 (file)
@@ -1019,6 +1019,8 @@ static inline void btrfs_memmove(struct btrfs_root *root,
        btrfs_item_offset((leaf)->items + (slot))))
 
 /* extent-tree.c */
+int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root);
 int btrfs_copy_pinned(struct btrfs_root *root, struct radix_tree_root *copy);
 struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
                                                         btrfs_fs_info *info,
@@ -1066,7 +1068,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
                      ins_len, int cow);
 int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root, struct buffer_head *parent,
-                      int cache_only);
+                      int cache_only, u64 *last_ret);
 void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p);
 struct btrfs_path *btrfs_alloc_path(void);
 void btrfs_free_path(struct btrfs_path *p);
index 120c448f58f8121645d33170d3a7ace85e09c181..3418bb62b99676a0abe536a507ff586b31b871c8 100644 (file)
@@ -396,6 +396,14 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root)
+{
+       finish_current_insert(trans, root->fs_info->extent_root);
+       del_pending_extents(trans, root->fs_info->extent_root);
+       return 0;
+}
+
 static int lookup_extent_ref(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root, u64 blocknr,
                             u64 num_blocks, u32 *refs)
@@ -1374,7 +1382,25 @@ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
                        BUG_ON(ret);
                        continue;
                }
-               next = read_tree_block(root, blocknr);
+               next = btrfs_find_tree_block(root, blocknr);
+               if (!next || !buffer_uptodate(next)) {
+                       brelse(next);
+                       mutex_unlock(&root->fs_info->fs_mutex);
+                       next = read_tree_block(root, blocknr);
+                       mutex_lock(&root->fs_info->fs_mutex);
+
+                       /* we dropped the lock, check one more time */
+                       ret = lookup_extent_ref(trans, root, blocknr, 1, &refs);
+                       BUG_ON(ret);
+                       if (refs != 1) {
+                               path->slots[*level]++;
+                               brelse(next);
+                               ret = btrfs_free_extent(trans, root,
+                                                       blocknr, 1, 1);
+                               BUG_ON(ret);
+                               continue;
+                       }
+               }
                WARN_ON(*level <= 0);
                if (path->nodes[*level-1])
                        btrfs_block_release(root, path->nodes[*level-1]);
index 12aa043b9f6f2e7fad6eaeb64dde8ae6d683d5f4..5c05ecbc5726e79e2716faddd8aa35d31534f6b2 100644 (file)
@@ -2159,9 +2159,7 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_ioctl_vol_args vol_args;
-       struct btrfs_trans_handle *trans;
        int ret = 0;
-       int err;
        struct btrfs_dir_item *di;
        int namelen;
        struct btrfs_path *path;
@@ -2201,25 +2199,8 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
 
        case BTRFS_IOC_DEFRAG:
                mutex_lock(&root->fs_info->fs_mutex);
-               trans = btrfs_start_transaction(root, 1);
-               memset(&root->defrag_progress, 0,
-                      sizeof(root->defrag_progress));
-               while (1) {
-                       root->defrag_running = 1;
-                       err = btrfs_defrag_leaves(trans, root, 0);
-
-                       btrfs_end_transaction(trans, root);
-                       mutex_unlock(&root->fs_info->fs_mutex);
-
-                       btrfs_btree_balance_dirty(root);
-
-                       mutex_lock(&root->fs_info->fs_mutex);
-                       trans = btrfs_start_transaction(root, 1);
-                       if (err != -EAGAIN)
-                               break;
-               }
-               root->defrag_running = 0;
-               btrfs_end_transaction(trans, root);
+               btrfs_defrag_root(root, 0);
+               btrfs_defrag_root(root->fs_info->extent_root, 0);
                mutex_unlock(&root->fs_info->fs_mutex);
                ret = 0;
                break;
index 2e797d5fb28148495dda85bc129d95eb2299a8d1..74f3de47423c193d6a249420cf8ba82cc9428f1f 100644 (file)
@@ -121,7 +121,9 @@ static int btrfs_sync_fs(struct super_block *sb, int wait)
                filemap_flush(root->fs_info->btree_inode->i_mapping);
                return 0;
        }
+       btrfs_clean_old_snapshots(root);
        mutex_lock(&root->fs_info->fs_mutex);
+       btrfs_defrag_dirty_roots(root->fs_info);
        trans = btrfs_start_transaction(root, 1);
        ret = btrfs_commit_transaction(trans, root);
        sb->s_dirt = 0;
index 204337c5ca0fa7d7d7e96c1d865ae03e49f3300d..c9d52dc83e4854e5d9c5f1d6a33c2f91b67f56e1 100644 (file)
@@ -317,18 +317,47 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans,
        return err;
 }
 
+int btrfs_defrag_root(struct btrfs_root *root, int cacheonly)
+{
+       struct btrfs_fs_info *info = root->fs_info;
+       int ret;
+       struct btrfs_trans_handle *trans;
+
+       if (root->defrag_running)
+               return 0;
+
+       trans = btrfs_start_transaction(root, 1);
+       while (1) {
+               root->defrag_running = 1;
+               ret = btrfs_defrag_leaves(trans, root, cacheonly);
+               btrfs_end_transaction(trans, root);
+               mutex_unlock(&info->fs_mutex);
+
+               btrfs_btree_balance_dirty(root);
+               cond_resched();
+
+               mutex_lock(&info->fs_mutex);
+               trans = btrfs_start_transaction(root, 1);
+               if (ret != -EAGAIN)
+                       break;
+       }
+       root->defrag_running = 0;
+       radix_tree_tag_clear(&info->fs_roots_radix,
+                    (unsigned long)root->root_key.objectid,
+                    BTRFS_ROOT_DEFRAG_TAG);
+       btrfs_end_transaction(trans, root);
+       return 0;
+}
+
 int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info)
 {
        struct btrfs_root *gang[1];
        struct btrfs_root *root;
-       struct btrfs_root *tree_root = info->tree_root;
-       struct btrfs_trans_handle *trans;
        int i;
        int ret;
        int err = 0;
        u64 last = 0;
 
-       trans = btrfs_start_transaction(tree_root, 1);
        while(1) {
                ret = radix_tree_gang_lookup_tag(&info->fs_roots_radix,
                                                 (void **)gang, last,
@@ -339,37 +368,10 @@ int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info)
                for (i = 0; i < ret; i++) {
                        root = gang[i];
                        last = root->root_key.objectid + 1;
-                       radix_tree_tag_clear(&info->fs_roots_radix,
-                                    (unsigned long)root->root_key.objectid,
-                                    BTRFS_ROOT_DEFRAG_TAG);
-                       if (root->defrag_running)
-                               continue;
-
-                       while (1) {
-                               mutex_lock(&root->fs_info->trans_mutex);
-                               record_root_in_trans(root);
-                               mutex_unlock(&root->fs_info->trans_mutex);
-
-                               root->defrag_running = 1;
-                               err = btrfs_defrag_leaves(trans, root, 1);
-                               btrfs_end_transaction(trans, tree_root);
-                               mutex_unlock(&info->fs_mutex);
-
-                               btrfs_btree_balance_dirty(root);
-                               cond_resched();
-
-                               mutex_lock(&info->fs_mutex);
-                               trans = btrfs_start_transaction(tree_root, 1);
-                               if (err != -EAGAIN)
-                                       break;
-                       }
-                       root->defrag_running = 0;
-                       radix_tree_tag_clear(&info->fs_roots_radix,
-                                    (unsigned long)root->root_key.objectid,
-                                    BTRFS_ROOT_DEFRAG_TAG);
+                       btrfs_defrag_root(root, 1);
                }
        }
-       btrfs_end_transaction(trans, tree_root);
+       btrfs_defrag_root(info->extent_root, 1);
        return err;
 }
 
@@ -527,6 +529,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        return ret;
 }
 
+int btrfs_clean_old_snapshots(struct btrfs_root *root)
+{
+       struct list_head dirty_roots;
+       INIT_LIST_HEAD(&dirty_roots);
+
+       mutex_lock(&root->fs_info->trans_mutex);
+       list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
+       mutex_unlock(&root->fs_info->trans_mutex);
+
+       if (!list_empty(&dirty_roots)) {
+               drop_dirty_roots(root, &dirty_roots);
+       }
+       return 0;
+}
 void btrfs_transaction_cleaner(struct work_struct *work)
 {
        struct btrfs_fs_info *fs_info = container_of(work,
@@ -536,12 +552,10 @@ void btrfs_transaction_cleaner(struct work_struct *work)
        struct btrfs_root *root = fs_info->tree_root;
        struct btrfs_transaction *cur;
        struct btrfs_trans_handle *trans;
-       struct list_head dirty_roots;
        unsigned long now;
        unsigned long delay = HZ * 30;
        int ret;
 
-       INIT_LIST_HEAD(&dirty_roots);
        mutex_lock(&root->fs_info->fs_mutex);
        mutex_lock(&root->fs_info->trans_mutex);
        cur = root->fs_info->running_transaction;
@@ -561,14 +575,7 @@ void btrfs_transaction_cleaner(struct work_struct *work)
        ret = btrfs_commit_transaction(trans, root);
 out:
        mutex_unlock(&root->fs_info->fs_mutex);
-
-       mutex_lock(&root->fs_info->trans_mutex);
-       list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
-       mutex_unlock(&root->fs_info->trans_mutex);
-
-       if (!list_empty(&dirty_roots)) {
-               drop_dirty_roots(root, &dirty_roots);
-       }
+       btrfs_clean_old_snapshots(root);
        btrfs_transaction_queue_work(root, delay);
 }
 
index 65a395eeca917c865f21155c78bef8ea8f2adf80..d5f491d3757eae1ee7a04bf96483f3d3f6f7f8ed 100644 (file)
@@ -73,5 +73,7 @@ void btrfs_init_transaction_sys(void);
 void btrfs_exit_transaction_sys(void);
 int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list);
 int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info);
+int btrfs_defrag_root(struct btrfs_root *root, int cacheonly);
+int btrfs_clean_old_snapshots(struct btrfs_root *root);
 
 #endif
index 7ea66b4aa5c2397247dd1bb7da9dc5ac9012d18f..a09064a9a41cc9f549e4f9bfea352a1bde5941a8 100644 (file)
@@ -42,16 +42,20 @@ static void reada_defrag(struct btrfs_root *root,
 static int defrag_walk_down(struct btrfs_trans_handle *trans,
                            struct btrfs_root *root,
                            struct btrfs_path *path, int *level,
-                           int cache_only)
+                           int cache_only, u64 *last_ret)
 {
        struct buffer_head *next;
        struct buffer_head *cur;
        u64 blocknr;
        int ret = 0;
+       int is_extent = 0;
 
        WARN_ON(*level < 0);
        WARN_ON(*level >= BTRFS_MAX_LEVEL);
 
+       if (root->fs_info->extent_root == root)
+               is_extent = 1;
+
        while(*level > 0) {
                WARN_ON(*level < 0);
                WARN_ON(*level >= BTRFS_MAX_LEVEL);
@@ -70,7 +74,10 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
                if (*level == 1) {
                        ret = btrfs_realloc_node(trans, root,
                                                 path->nodes[*level],
-                                                cache_only);
+                                                cache_only, last_ret);
+                       if (is_extent)
+                               btrfs_extent_post_op(trans, root);
+
                        break;
                }
                blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
@@ -90,8 +97,13 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
                ret = btrfs_cow_block(trans, root, next, path->nodes[*level],
                                      path->slots[*level], &next);
                BUG_ON(ret);
-               ret = btrfs_realloc_node(trans, root, next, cache_only);
+               ret = btrfs_realloc_node(trans, root, next, cache_only,
+                                        last_ret);
                BUG_ON(ret);
+
+               if (is_extent)
+                       btrfs_extent_post_op(trans, root);
+
                WARN_ON(*level <= 0);
                if (path->nodes[*level-1])
                        btrfs_block_release(root, path->nodes[*level-1]);
@@ -148,10 +160,14 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
        int level;
        int orig_level;
        int i;
+       int is_extent = 0;
+       u64 last_ret = 0;
+
+       if (root->fs_info->extent_root == root)
+               is_extent = 1;
 
-       if (root->ref_cows == 0) {
+       if (root->ref_cows == 0 && !is_extent)
                goto out;
-       }
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -165,16 +181,21 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                get_bh(root->node);
                ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp);
                BUG_ON(ret);
-               ret = btrfs_realloc_node(trans, root, root->node, cache_only);
+               ret = btrfs_realloc_node(trans, root, root->node, cache_only,
+                                        &last_ret);
                BUG_ON(ret);
                path->nodes[level] = root->node;
                path->slots[level] = 0;
+               if (is_extent)
+                       btrfs_extent_post_op(trans, root);
        } else {
                level = root->defrag_level;
                path->lowest_level = level;
                wret = btrfs_search_slot(trans, root, &root->defrag_progress,
                                         path, 0, 1);
 
+               if (is_extent)
+                       btrfs_extent_post_op(trans, root);
                if (wret < 0) {
                        ret = wret;
                        goto out;
@@ -188,7 +209,8 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
        }
 
        while(1) {
-               wret = defrag_walk_down(trans, root, path, &level, cache_only);
+               wret = defrag_walk_down(trans, root, path, &level, cache_only,
+                                       &last_ret);
                if (wret > 0)
                        break;
                if (wret < 0)