Btrfs: fix an oops when doing balance relocation
[linux-2.6-microblaze.git] / fs / btrfs / relocation.c
index 4a35572..7cdc760 100644 (file)
@@ -1264,10 +1264,10 @@ static int __must_check __add_reloc_root(struct btrfs_root *root)
 }
 
 /*
- * helper to update/delete the 'address of tree root -> reloc tree'
+ * helper to delete the 'address of tree root -> reloc tree'
  * mapping
  */
-static int __update_reloc_root(struct btrfs_root *root, int del)
+static void __del_reloc_root(struct btrfs_root *root)
 {
        struct rb_node *rb_node;
        struct mapping_node *node = NULL;
@@ -1275,7 +1275,7 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
 
        spin_lock(&rc->reloc_root_tree.lock);
        rb_node = tree_search(&rc->reloc_root_tree.rb_root,
-                             root->commit_root->start);
+                             root->node->start);
        if (rb_node) {
                node = rb_entry(rb_node, struct mapping_node, rb_node);
                rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
@@ -1283,23 +1283,45 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
        spin_unlock(&rc->reloc_root_tree.lock);
 
        if (!node)
-               return 0;
+               return;
        BUG_ON((struct btrfs_root *)node->data != root);
 
-       if (!del) {
-               spin_lock(&rc->reloc_root_tree.lock);
-               node->bytenr = root->node->start;
-               rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
-                                     node->bytenr, &node->rb_node);
-               spin_unlock(&rc->reloc_root_tree.lock);
-               if (rb_node)
-                       backref_tree_panic(rb_node, -EEXIST, node->bytenr);
-       } else {
-               spin_lock(&root->fs_info->trans_lock);
-               list_del_init(&root->root_list);
-               spin_unlock(&root->fs_info->trans_lock);
-               kfree(node);
+       spin_lock(&root->fs_info->trans_lock);
+       list_del_init(&root->root_list);
+       spin_unlock(&root->fs_info->trans_lock);
+       kfree(node);
+}
+
+/*
+ * helper to update the 'address of tree root -> reloc tree'
+ * mapping
+ */
+static int __update_reloc_root(struct btrfs_root *root, u64 new_bytenr)
+{
+       struct rb_node *rb_node;
+       struct mapping_node *node = NULL;
+       struct reloc_control *rc = root->fs_info->reloc_ctl;
+
+       spin_lock(&rc->reloc_root_tree.lock);
+       rb_node = tree_search(&rc->reloc_root_tree.rb_root,
+                             root->node->start);
+       if (rb_node) {
+               node = rb_entry(rb_node, struct mapping_node, rb_node);
+               rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
        }
+       spin_unlock(&rc->reloc_root_tree.lock);
+
+       if (!node)
+               return 0;
+       BUG_ON((struct btrfs_root *)node->data != root);
+
+       spin_lock(&rc->reloc_root_tree.lock);
+       node->bytenr = new_bytenr;
+       rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
+                             node->bytenr, &node->rb_node);
+       spin_unlock(&rc->reloc_root_tree.lock);
+       if (rb_node)
+               backref_tree_panic(rb_node, -EEXIST, node->bytenr);
        return 0;
 }
 
@@ -1383,6 +1405,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
 {
        struct btrfs_root *reloc_root;
        struct reloc_control *rc = root->fs_info->reloc_ctl;
+       struct btrfs_block_rsv *rsv;
        int clear_rsv = 0;
        int ret;
 
@@ -1396,13 +1419,14 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
            root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
                return 0;
 
-       if (!trans->block_rsv) {
+       if (!trans->reloc_reserved) {
+               rsv = trans->block_rsv;
                trans->block_rsv = rc->block_rsv;
                clear_rsv = 1;
        }
        reloc_root = create_reloc_root(trans, root, root->root_key.objectid);
        if (clear_rsv)
-               trans->block_rsv = NULL;
+               trans->block_rsv = rsv;
 
        ret = __add_reloc_root(reloc_root);
        BUG_ON(ret < 0);
@@ -1418,7 +1442,6 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
 {
        struct btrfs_root *reloc_root;
        struct btrfs_root_item *root_item;
-       int del = 0;
        int ret;
 
        if (!root->reloc_root)
@@ -1430,11 +1453,9 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
        if (root->fs_info->reloc_ctl->merge_reloc_tree &&
            btrfs_root_refs(root_item) == 0) {
                root->reloc_root = NULL;
-               del = 1;
+               __del_reloc_root(reloc_root);
        }
 
-       __update_reloc_root(reloc_root, del);
-
        if (reloc_root->commit_root != reloc_root->node) {
                btrfs_set_root_node(root_item, reloc_root->node);
                free_extent_buffer(reloc_root->commit_root);
@@ -1775,8 +1796,7 @@ again:
                        new_ptr_gen = 0;
                }
 
-               if (new_bytenr > 0 && new_bytenr == old_bytenr) {
-                       WARN_ON(1);
+               if (WARN_ON(new_bytenr > 0 && new_bytenr == old_bytenr)) {
                        ret = level;
                        break;
                }
@@ -2058,7 +2078,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
        LIST_HEAD(inode_list);
        struct btrfs_key key;
        struct btrfs_key next_key;
-       struct btrfs_trans_handle *trans;
+       struct btrfs_trans_handle *trans = NULL;
        struct btrfs_root *reloc_root;
        struct btrfs_root_item *root_item;
        struct btrfs_path *path;
@@ -2107,18 +2127,19 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
        memset(&next_key, 0, sizeof(next_key));
 
        while (1) {
-               trans = btrfs_start_transaction(root, 0);
-               BUG_ON(IS_ERR(trans));
-               trans->block_rsv = rc->block_rsv;
-
                ret = btrfs_block_rsv_refill(root, rc->block_rsv, min_reserved,
                                             BTRFS_RESERVE_FLUSH_ALL);
                if (ret) {
-                       BUG_ON(ret != -EAGAIN);
-                       ret = btrfs_commit_transaction(trans, root);
-                       BUG_ON(ret);
-                       continue;
+                       err = ret;
+                       goto out;
+               }
+               trans = btrfs_start_transaction(root, 0);
+               if (IS_ERR(trans)) {
+                       err = PTR_ERR(trans);
+                       trans = NULL;
+                       goto out;
                }
+               trans->block_rsv = rc->block_rsv;
 
                replaced = 0;
                max_level = level;
@@ -2164,6 +2185,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
                root_item->drop_level = level;
 
                btrfs_end_transaction_throttle(trans, root);
+               trans = NULL;
 
                btrfs_btree_balance_dirty(root);
 
@@ -2192,7 +2214,8 @@ out:
                btrfs_update_reloc_root(trans, root);
        }
 
-       btrfs_end_transaction_throttle(trans, root);
+       if (trans)
+               btrfs_end_transaction_throttle(trans, root);
 
        btrfs_btree_balance_dirty(root);
 
@@ -2283,7 +2306,7 @@ void free_reloc_roots(struct list_head *list)
        while (!list_empty(list)) {
                reloc_root = list_entry(list->next, struct btrfs_root,
                                        root_list);
-               __update_reloc_root(reloc_root, 1);
+               __del_reloc_root(reloc_root);
                free_extent_buffer(reloc_root->node);
                free_extent_buffer(reloc_root->commit_root);
                kfree(reloc_root);
@@ -2328,7 +2351,7 @@ again:
 
                        ret = merge_reloc_root(rc, root);
                        if (ret) {
-                               __update_reloc_root(reloc_root, 1);
+                               __del_reloc_root(reloc_root);
                                free_extent_buffer(reloc_root->node);
                                free_extent_buffer(reloc_root->commit_root);
                                kfree(reloc_root);
@@ -3258,7 +3281,7 @@ static int add_tree_block(struct reloc_control *rc,
        struct rb_node *rb_node;
        u32 item_size;
        int level = -1;
-       int generation;
+       u64 generation;
 
        eb =  path->nodes[0];
        item_size = btrfs_item_size_nr(eb, path->slots[0]);
@@ -3407,7 +3430,6 @@ static int delete_block_group_cache(struct btrfs_fs_info *fs_info,
                                    struct inode *inode, u64 ino)
 {
        struct btrfs_key key;
-       struct btrfs_path *path;
        struct btrfs_root *root = fs_info->tree_root;
        struct btrfs_trans_handle *trans;
        int ret = 0;
@@ -3432,22 +3454,14 @@ truncate:
        if (ret)
                goto out;
 
-       path = btrfs_alloc_path();
-       if (!path) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
        trans = btrfs_join_transaction(root);
        if (IS_ERR(trans)) {
-               btrfs_free_path(path);
                ret = PTR_ERR(trans);
                goto out;
        }
 
-       ret = btrfs_truncate_free_space_cache(root, trans, path, inode);
+       ret = btrfs_truncate_free_space_cache(root, trans, inode);
 
-       btrfs_free_path(path);
        btrfs_end_transaction(trans, root);
        btrfs_btree_balance_dirty(root);
 out:
@@ -3549,10 +3563,8 @@ static int find_data_references(struct reloc_control *rc,
                                err = ret;
                                goto out;
                        }
-                       if (ret > 0) {
-                               WARN_ON(1);
+                       if (WARN_ON(ret > 0))
                                goto out;
-                       }
 
                        leaf = path->nodes[0];
                        nritems = btrfs_header_nritems(leaf);
@@ -3572,11 +3584,9 @@ static int find_data_references(struct reloc_control *rc,
                }
 
                btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
-               if (key.objectid != ref_objectid ||
-                   key.type != BTRFS_EXTENT_DATA_KEY) {
-                       WARN_ON(1);
+               if (WARN_ON(key.objectid != ref_objectid ||
+                   key.type != BTRFS_EXTENT_DATA_KEY))
                        break;
-               }
 
                fi = btrfs_item_ptr(leaf, path->slots[0],
                                    struct btrfs_file_extent_item);
@@ -4001,16 +4011,6 @@ restart:
                        }
                }
 
-               ret = btrfs_block_rsv_check(rc->extent_root, rc->block_rsv, 5);
-               if (ret < 0) {
-                       if (ret != -ENOSPC) {
-                               err = ret;
-                               WARN_ON(1);
-                               break;
-                       }
-                       rc->commit_transaction = 1;
-               }
-
                if (rc->commit_transaction) {
                        rc->commit_transaction = 0;
                        ret = btrfs_commit_transaction(trans, rc->extent_root);
@@ -4241,12 +4241,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
        printk(KERN_INFO "btrfs: relocating block group %llu flags %llu\n",
               rc->block_group->key.objectid, rc->block_group->flags);
 
-       ret = btrfs_start_all_delalloc_inodes(fs_info, 0);
+       ret = btrfs_start_delalloc_roots(fs_info, 0);
        if (ret < 0) {
                err = ret;
                goto out;
        }
-       btrfs_wait_all_ordered_extents(fs_info);
+       btrfs_wait_ordered_roots(fs_info, -1);
 
        while (1) {
                mutex_lock(&fs_info->cleaner_mutex);
@@ -4264,7 +4264,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
                        rc->extents_found);
 
                if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) {
-                       btrfs_wait_ordered_range(rc->data_inode, 0, (u64)-1);
+                       ret = btrfs_wait_ordered_range(rc->data_inode, 0,
+                                                      (u64)-1);
+                       if (ret) {
+                               err = ret;
+                               goto out;
+                       }
                        invalidate_mapping_pages(rc->data_inode->i_mapping,
                                                 0, -1);
                        rc->stage = UPDATE_DATA_PTRS;
@@ -4481,6 +4486,7 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
        struct btrfs_root *root = BTRFS_I(inode)->root;
        int ret;
        u64 disk_bytenr;
+       u64 new_bytenr;
        LIST_HEAD(list);
 
        ordered = btrfs_lookup_ordered_extent(inode, file_pos);
@@ -4492,13 +4498,24 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
        if (ret)
                goto out;
 
-       disk_bytenr = ordered->start;
        while (!list_empty(&list)) {
                sums = list_entry(list.next, struct btrfs_ordered_sum, list);
                list_del_init(&sums->list);
 
-               sums->bytenr = disk_bytenr;
-               disk_bytenr += sums->len;
+               /*
+                * We need to offset the new_bytenr based on where the csum is.
+                * We need to do this because we will read in entire prealloc
+                * extents but we may have written to say the middle of the
+                * prealloc extent, so we need to make sure the csum goes with
+                * the right disk offset.
+                *
+                * We can do this because the data reloc inode refers strictly
+                * to the on disk bytes, so we don't have to worry about
+                * disk_len vs real len like with real inodes since it's all
+                * disk length.
+                */
+               new_bytenr = ordered->start + (sums->bytenr - disk_bytenr);
+               sums->bytenr = new_bytenr;
 
                btrfs_add_ordered_sum(inode, ordered, sums);
        }
@@ -4524,6 +4541,11 @@ int btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
        BUG_ON(rc->stage == UPDATE_DATA_PTRS &&
               root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID);
 
+       if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
+               if (buf == root->node)
+                       __update_reloc_root(root, cow->start);
+       }
+
        level = btrfs_header_level(buf);
        if (btrfs_header_generation(buf) <=
            btrfs_root_last_snapshot(&root->root_item))