btrfs: clean snapshots one by one
[deliverable/linux.git] / fs / btrfs / relocation.c
index 50695dc5e2abb273a221a1d03446016b66039c53..4ef5f7455fb3dae14521af7b375a61bcd7d0cd6f 100644 (file)
@@ -619,10 +619,13 @@ static noinline_for_stack
 int find_inline_backref(struct extent_buffer *leaf, int slot,
                        unsigned long *ptr, unsigned long *end)
 {
+       struct btrfs_key key;
        struct btrfs_extent_item *ei;
        struct btrfs_tree_block_info *bi;
        u32 item_size;
 
+       btrfs_item_key_to_cpu(leaf, &key, slot);
+
        item_size = btrfs_item_size_nr(leaf, slot);
 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
        if (item_size < sizeof(*ei)) {
@@ -634,13 +637,18 @@ int find_inline_backref(struct extent_buffer *leaf, int slot,
        WARN_ON(!(btrfs_extent_flags(leaf, ei) &
                  BTRFS_EXTENT_FLAG_TREE_BLOCK));
 
-       if (item_size <= sizeof(*ei) + sizeof(*bi)) {
+       if (key.type == BTRFS_EXTENT_ITEM_KEY &&
+           item_size <= sizeof(*ei) + sizeof(*bi)) {
                WARN_ON(item_size < sizeof(*ei) + sizeof(*bi));
                return 1;
        }
 
-       bi = (struct btrfs_tree_block_info *)(ei + 1);
-       *ptr = (unsigned long)(bi + 1);
+       if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+               bi = (struct btrfs_tree_block_info *)(ei + 1);
+               *ptr = (unsigned long)(bi + 1);
+       } else {
+               *ptr = (unsigned long)(ei + 1);
+       }
        *end = (unsigned long)ei + item_size;
        return 0;
 }
@@ -708,7 +716,7 @@ again:
        end = 0;
        ptr = 0;
        key.objectid = cur->bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
+       key.type = BTRFS_METADATA_ITEM_KEY;
        key.offset = (u64)-1;
 
        path1->search_commit_root = 1;
@@ -766,7 +774,8 @@ again:
                                break;
                        }
 
-                       if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+                       if (key.type == BTRFS_EXTENT_ITEM_KEY ||
+                           key.type == BTRFS_METADATA_ITEM_KEY) {
                                ret = find_inline_backref(eb, path1->slots[0],
                                                          &ptr, &end);
                                if (ret)
@@ -1269,6 +1278,8 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
        }
        spin_unlock(&rc->reloc_root_tree.lock);
 
+       if (!node)
+               return 0;
        BUG_ON((struct btrfs_root *)node->data != root);
 
        if (!del) {
@@ -2237,6 +2248,21 @@ again:
        return err;
 }
 
+static noinline_for_stack
+void free_reloc_roots(struct list_head *list)
+{
+       struct btrfs_root *reloc_root;
+
+       while (!list_empty(list)) {
+               reloc_root = list_entry(list->next, struct btrfs_root,
+                                       root_list);
+               __update_reloc_root(reloc_root, 1);
+               free_extent_buffer(reloc_root->node);
+               free_extent_buffer(reloc_root->commit_root);
+               kfree(reloc_root);
+       }
+}
+
 static noinline_for_stack
 int merge_reloc_roots(struct reloc_control *rc)
 {
@@ -2244,7 +2270,7 @@ int merge_reloc_roots(struct reloc_control *rc)
        struct btrfs_root *reloc_root;
        LIST_HEAD(reloc_roots);
        int found = 0;
-       int ret;
+       int ret = 0;
 again:
        root = rc->extent_root;
 
@@ -2270,20 +2296,33 @@ again:
                        BUG_ON(root->reloc_root != reloc_root);
 
                        ret = merge_reloc_root(rc, root);
-                       BUG_ON(ret);
+                       if (ret)
+                               goto out;
                } else {
                        list_del_init(&reloc_root->root_list);
                }
                ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
-               BUG_ON(ret < 0);
+               if (ret < 0) {
+                       if (list_empty(&reloc_root->root_list))
+                               list_add_tail(&reloc_root->root_list,
+                                             &reloc_roots);
+                       goto out;
+               }
        }
 
        if (found) {
                found = 0;
                goto again;
        }
+out:
+       if (ret) {
+               btrfs_std_error(root->fs_info, ret);
+               if (!list_empty(&reloc_roots))
+                       free_reloc_roots(&reloc_roots);
+       }
+
        BUG_ON(!RB_EMPTY_ROOT(&rc->reloc_root_tree.rb_root));
-       return 0;
+       return ret;
 }
 
 static void free_block_list(struct rb_root *blocks)
@@ -2738,8 +2777,13 @@ static int reada_tree_block(struct reloc_control *rc,
                            struct tree_block *block)
 {
        BUG_ON(block->key_ready);
-       readahead_tree_block(rc->extent_root, block->bytenr,
-                            block->key.objectid, block->key.offset);
+       if (block->key.type == BTRFS_METADATA_ITEM_KEY)
+               readahead_tree_block(rc->extent_root, block->bytenr,
+                                    block->key.objectid,
+                                    rc->extent_root->leafsize);
+       else
+               readahead_tree_block(rc->extent_root, block->bytenr,
+                                    block->key.objectid, block->key.offset);
        return 0;
 }
 
@@ -2818,8 +2862,10 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
        int err = 0;
 
        path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
+       if (!path) {
+               err = -ENOMEM;
+               goto out_path;
+       }
 
        rb_node = rb_first(blocks);
        while (rb_node) {
@@ -2858,10 +2904,11 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
                rb_node = rb_next(rb_node);
        }
 out:
-       free_block_list(blocks);
        err = finish_pending_nodes(trans, rc, path, err);
 
        btrfs_free_path(path);
+out_path:
+       free_block_list(blocks);
        return err;
 }
 
@@ -3143,12 +3190,17 @@ static int add_tree_block(struct reloc_control *rc,
        eb =  path->nodes[0];
        item_size = btrfs_item_size_nr(eb, path->slots[0]);
 
-       if (item_size >= sizeof(*ei) + sizeof(*bi)) {
+       if (extent_key->type == BTRFS_METADATA_ITEM_KEY ||
+           item_size >= sizeof(*ei) + sizeof(*bi)) {
                ei = btrfs_item_ptr(eb, path->slots[0],
                                struct btrfs_extent_item);
-               bi = (struct btrfs_tree_block_info *)(ei + 1);
+               if (extent_key->type == BTRFS_EXTENT_ITEM_KEY) {
+                       bi = (struct btrfs_tree_block_info *)(ei + 1);
+                       level = btrfs_tree_block_level(eb, bi);
+               } else {
+                       level = (int)extent_key->offset;
+               }
                generation = btrfs_extent_generation(eb, ei);
-               level = btrfs_tree_block_level(eb, bi);
        } else {
 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
                u64 ref_owner;
@@ -3177,7 +3229,7 @@ static int add_tree_block(struct reloc_control *rc,
                return -ENOMEM;
 
        block->bytenr = extent_key->objectid;
-       block->key.objectid = extent_key->offset;
+       block->key.objectid = rc->extent_root->leafsize;
        block->key.offset = generation;
        block->level = level;
        block->key_ready = 0;
@@ -3219,9 +3271,15 @@ static int __add_tree_block(struct reloc_control *rc,
        ret = btrfs_search_slot(NULL, rc->extent_root, &key, path, 0, 0);
        if (ret < 0)
                goto out;
-       BUG_ON(ret);
 
        btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       if (ret > 0) {
+               if (key.objectid == bytenr &&
+                   key.type == BTRFS_METADATA_ITEM_KEY)
+                       ret = 0;
+       }
+       BUG_ON(ret);
+
        ret = add_tree_block(rc, &key, path, blocks);
 out:
        btrfs_free_path(path);
@@ -3242,7 +3300,8 @@ static int block_use_full_backref(struct reloc_control *rc,
                return 1;
 
        ret = btrfs_lookup_extent_info(NULL, rc->extent_root,
-                                      eb->start, eb->len, NULL, &flags);
+                                      eb->start, btrfs_header_level(eb), 1,
+                                      NULL, &flags);
        BUG_ON(ret);
 
        if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
@@ -3611,12 +3670,25 @@ next:
                        break;
                }
 
-               if (key.type != BTRFS_EXTENT_ITEM_KEY ||
+               if (key.type != BTRFS_EXTENT_ITEM_KEY &&
+                   key.type != BTRFS_METADATA_ITEM_KEY) {
+                       path->slots[0]++;
+                       goto next;
+               }
+
+               if (key.type == BTRFS_EXTENT_ITEM_KEY &&
                    key.objectid + key.offset <= rc->search_start) {
                        path->slots[0]++;
                        goto next;
                }
 
+               if (key.type == BTRFS_METADATA_ITEM_KEY &&
+                   key.objectid + rc->extent_root->leafsize <=
+                   rc->search_start) {
+                       path->slots[0]++;
+                       goto next;
+               }
+
                ret = find_first_extent_bit(&rc->processed_blocks,
                                            key.objectid, &start, &end,
                                            EXTENT_DIRTY, NULL);
@@ -3625,7 +3697,11 @@ next:
                        btrfs_release_path(path);
                        rc->search_start = end + 1;
                } else {
-                       rc->search_start = key.objectid + key.offset;
+                       if (key.type == BTRFS_EXTENT_ITEM_KEY)
+                               rc->search_start = key.objectid + key.offset;
+                       else
+                               rc->search_start = key.objectid +
+                                       rc->extent_root->leafsize;
                        memcpy(extent_key, &key, sizeof(key));
                        return 0;
                }
@@ -3698,7 +3774,15 @@ int prepare_to_relocate(struct reloc_control *rc)
        set_reloc_control(rc);
 
        trans = btrfs_join_transaction(rc->extent_root);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans)) {
+               unset_reloc_control(rc);
+               /*
+                * extent tree is not a ref_cow tree and has no reloc_root to
+                * cleanup.  And callers are responsible to free the above
+                * block rsv.
+                */
+               return PTR_ERR(trans);
+       }
        btrfs_commit_transaction(trans, rc->extent_root);
        return 0;
 }
@@ -3730,7 +3814,11 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
        while (1) {
                progress++;
                trans = btrfs_start_transaction(rc->extent_root, 0);
-               BUG_ON(IS_ERR(trans));
+               if (IS_ERR(trans)) {
+                       err = PTR_ERR(trans);
+                       trans = NULL;
+                       break;
+               }
 restart:
                if (update_backref_cache(trans, &rc->backref_cache)) {
                        btrfs_end_transaction(trans, rc->extent_root);
@@ -4060,10 +4148,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
 
        while (1) {
                mutex_lock(&fs_info->cleaner_mutex);
-
-               btrfs_clean_old_snapshots(fs_info->tree_root);
                ret = relocate_block_group(rc);
-
                mutex_unlock(&fs_info->cleaner_mutex);
                if (ret < 0) {
                        err = ret;
@@ -4264,14 +4349,9 @@ int btrfs_recover_relocation(struct btrfs_root *root)
 out_free:
        kfree(rc);
 out:
-       while (!list_empty(&reloc_roots)) {
-               reloc_root = list_entry(reloc_roots.next,
-                                       struct btrfs_root, root_list);
-               list_del(&reloc_root->root_list);
-               free_extent_buffer(reloc_root->node);
-               free_extent_buffer(reloc_root->commit_root);
-               kfree(reloc_root);
-       }
+       if (!list_empty(&reloc_roots))
+               free_reloc_roots(&reloc_roots);
+
        btrfs_free_path(path);
 
        if (err == 0) {
This page took 0.031685 seconds and 5 git commands to generate.