Btrfs: Fix extent_buffer and extent_state leaks
authorChris Mason <chris.mason@oracle.com>
Mon, 15 Oct 2007 20:14:48 +0000 (16:14 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:03:56 +0000 (11:03 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_map.c
fs/btrfs/extent_map.h
fs/btrfs/transaction.c

index 9427b79..50ef351 100644 (file)
@@ -87,6 +87,7 @@ static int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
+       cow->alloc_addr = (unsigned long)__builtin_return_address(0);
        if (buf->len != root->sectorsize || cow->len != root->sectorsize)
                WARN_ON(1);
 
@@ -132,6 +133,7 @@ int btrfs_cow_block(struct btrfs_trans_handle *trans,
                    struct extent_buffer **cow_ret)
 {
        u64 search_start;
+       int ret;
        if (trans->transaction != root->fs_info->running_transaction) {
                printk(KERN_CRIT "trans %Lu running %Lu\n", trans->transid,
                       root->fs_info->running_transaction->transid);
@@ -148,8 +150,10 @@ int btrfs_cow_block(struct btrfs_trans_handle *trans,
        }
 
        search_start = extent_buffer_blocknr(buf) & ~((u64)65535);
-       return __btrfs_cow_block(trans, root, buf, parent,
+       ret = __btrfs_cow_block(trans, root, buf, parent,
                                 parent_slot, cow_ret, search_start, 0);
+       (*cow_ret)->alloc_addr = (unsigned long)__builtin_return_address(0);
+       return ret;
 }
 
 static int close_blocks(u64 blocknr, u64 other)
@@ -1013,8 +1017,10 @@ again:
                                if (sret)
                                        return sret;
                                b = p->nodes[level];
-                               if (!b)
+                               if (!b) {
+                                       btrfs_release_path(NULL, p);
                                        goto again;
+                               }
                                slot = p->slots[level];
                                BUG_ON(btrfs_header_nritems(b) == 1);
                        }
index aed0861..5262b28 100644 (file)
@@ -303,8 +303,8 @@ struct btrfs_fs_info {
        struct radix_tree_root pinned_radix;
        struct radix_tree_root block_group_radix;
        struct radix_tree_root block_group_data_radix;
-       struct radix_tree_root extent_map_radix;
        struct radix_tree_root extent_ins_radix;
+       struct extent_map_tree free_space_cache;
        u64 generation;
        u64 last_trans_committed;
        struct btrfs_transaction *running_transaction;
index 8242933..09f4e69 100644 (file)
@@ -46,18 +46,25 @@ struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
                                            u64 blocknr)
 {
        struct inode *btree_inode = root->fs_info->btree_inode;
-       return find_extent_buffer(&BTRFS_I(btree_inode)->extent_tree,
+       struct extent_buffer *eb;
+       eb = find_extent_buffer(&BTRFS_I(btree_inode)->extent_tree,
                                   blocknr * root->sectorsize,
                                   root->sectorsize, GFP_NOFS);
+       if (eb)
+               eb->alloc_addr = (unsigned long)__builtin_return_address(0);
+       return eb;
 }
 
 struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
                                                 u64 blocknr)
 {
        struct inode *btree_inode = root->fs_info->btree_inode;
-       return alloc_extent_buffer(&BTRFS_I(btree_inode)->extent_tree,
+       struct extent_buffer *eb;
+       eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->extent_tree,
                                   blocknr * root->sectorsize,
                                   root->sectorsize, GFP_NOFS);
+       eb->alloc_addr = (unsigned long)__builtin_return_address(0);
+       return eb;
 }
 
 struct extent_map *btree_get_extent(struct inode *inode, struct page *page,
@@ -226,6 +233,7 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 blocknr)
                return NULL;
        read_extent_buffer_pages(&BTRFS_I(btree_inode)->extent_tree,
                                 buf, 1);
+       buf->alloc_addr = (unsigned long)__builtin_return_address(0);
        return buf;
 }
 
@@ -426,7 +434,6 @@ struct btrfs_root *open_ctree(struct super_block *sb)
        }
        init_bit_radix(&fs_info->pinned_radix);
        init_bit_radix(&fs_info->pending_del_radix);
-       init_bit_radix(&fs_info->extent_map_radix);
        init_bit_radix(&fs_info->extent_ins_radix);
        INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS);
        INIT_RADIX_TREE(&fs_info->block_group_radix, GFP_KERNEL);
@@ -449,6 +456,8 @@ struct btrfs_root *open_ctree(struct super_block *sb)
        extent_map_tree_init(&BTRFS_I(fs_info->btree_inode)->extent_tree,
                             fs_info->btree_inode->i_mapping,
                             GFP_NOFS);
+       extent_map_tree_init(&fs_info->free_space_cache,
+                            fs_info->btree_inode->i_mapping, GFP_NOFS);
        fs_info->do_barriers = 1;
        fs_info->closing = 0;
 
@@ -594,8 +603,10 @@ int close_ctree(struct btrfs_root *root)
 
        if (fs_info->extent_root->node)
                free_extent_buffer(fs_info->extent_root->node);
+
        if (fs_info->tree_root->node)
                free_extent_buffer(fs_info->tree_root->node);
+
        free_extent_buffer(fs_info->sb_buffer);
        truncate_inode_pages(fs_info->btree_inode->i_mapping, 0);
        iput(fs_info->btree_inode);
index 089c41c..74cfbee 100644 (file)
@@ -34,21 +34,19 @@ static int cache_block_group(struct btrfs_root *root,
        int ret;
        struct btrfs_key key;
        struct extent_buffer *leaf;
-       struct radix_tree_root *extent_radix;
+       struct extent_map_tree *free_space_cache;
        int slot;
-       u64 i;
        u64 last = 0;
        u64 hole_size;
        u64 first_free;
        int found = 0;
 
        root = root->fs_info->extent_root;
-       extent_radix = &root->fs_info->extent_map_radix;
+       free_space_cache = &root->fs_info->free_space_cache;
 
        if (block_group->cached)
                return 0;
-       if (block_group->data)
-               return 0;
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -98,9 +96,11 @@ static int cache_block_group(struct btrfs_root *root,
                                last = first_free;
                                found = 1;
                        }
-                       hole_size = key.objectid - last;
-                       for (i = 0; i < hole_size; i++) {
-                               set_radix_bit(extent_radix, last + i);
+                       if (key.objectid > last) {
+                               hole_size = key.objectid - last;
+                               set_extent_dirty(free_space_cache, last,
+                                                last + hole_size - 1,
+                                                GFP_NOFS);
                        }
                        last = key.objectid + key.offset;
                }
@@ -114,9 +114,8 @@ next:
            block_group->key.offset > last) {
                hole_size = block_group->key.objectid +
                        block_group->key.offset - last;
-               for (i = 0; i < hole_size; i++) {
-                       set_radix_bit(extent_radix, last + i);
-               }
+               set_extent_dirty(free_space_cache, last,
+                                last + hole_size - 1, GFP_NOFS);
        }
        block_group->cached = 1;
 err:
@@ -150,47 +149,33 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
        return NULL;
 }
 
-static u64 leaf_range(struct btrfs_root *root)
-{
-       u64 size = BTRFS_LEAF_DATA_SIZE(root);
-       do_div(size, sizeof(struct btrfs_extent_item) +
-               sizeof(struct btrfs_item));
-       return size;
-}
-
 static u64 find_search_start(struct btrfs_root *root,
                             struct btrfs_block_group_cache **cache_ret,
-                            u64 search_start, int num)
+                            u64 search_start, int num, int data)
 {
-       unsigned long gang[8];
        int ret;
        struct btrfs_block_group_cache *cache = *cache_ret;
        u64 last = max(search_start, cache->key.objectid);
+       u64 start = 0;
+       u64 end = 0;
 
-       if (cache->data)
-               goto out;
 again:
        ret = cache_block_group(root, cache);
        if (ret)
                goto out;
        while(1) {
-               ret = find_first_radix_bit(&root->fs_info->extent_map_radix,
-                                          gang, last, ARRAY_SIZE(gang));
-               if (!ret)
+               ret = find_first_extent_bit(&root->fs_info->free_space_cache,
+                                           last, &start, &end, EXTENT_DIRTY);
+               if (ret)
                        goto out;
-               last = gang[ret-1] + 1;
-               if (num > 1) {
-                       if (ret != ARRAY_SIZE(gang)) {
-                               goto new_group;
-                       }
-                       if (gang[ret-1] - gang[0] > leaf_range(root)) {
-                               continue;
-                       }
-               }
-               if (gang[0] >= cache->key.objectid + cache->key.offset) {
+
+               start = max(last, start);
+               last = end + 1;
+               if (end + 1 - start < num)
+                       continue;
+               if (start + num > cache->key.objectid + cache->key.offset)
                        goto new_group;
-               }
-               return gang[0];
+               return start;
        }
 out:
        return max(cache->last_alloc, search_start);
@@ -202,7 +187,7 @@ new_group:
                return max((*cache_ret)->last_alloc, search_start);
        }
        cache = btrfs_find_block_group(root, cache,
-                                      last + cache->key.offset - 1, 0, 0);
+                                      last + cache->key.offset - 1, data, 0);
        *cache_ret = cache;
        goto again;
 }
@@ -625,7 +610,6 @@ static int update_block_group(struct btrfs_trans_handle *trans,
        u64 total = num;
        u64 old_val;
        u64 block_in_group;
-       u64 i;
        int ret;
 
        while(total) {
@@ -644,12 +628,6 @@ static int update_block_group(struct btrfs_trans_handle *trans,
                if (alloc) {
                        if (blocknr > cache->last_alloc)
                                cache->last_alloc = blocknr;
-                       if (!cache->data) {
-                               for (i = 0; i < num; i++) {
-                                       clear_radix_bit(&info->extent_map_radix,
-                                                       blocknr + i);
-                               }
-                       }
                        if (cache->data != data &&
                            old_val < (cache->key.offset >> 1)) {
                                cache->data = data;
@@ -677,11 +655,10 @@ static int update_block_group(struct btrfs_trans_handle *trans,
                        old_val -= num;
                        if (blocknr < cache->first_free)
                                cache->first_free = blocknr;
-                       if (!cache->data && mark_free) {
-                               for (i = 0; i < num; i++) {
-                                       set_radix_bit(&info->extent_map_radix,
-                                                     blocknr + i);
-                               }
+                       if (mark_free) {
+                               set_extent_dirty(&info->free_space_cache,
+                                                blocknr, blocknr + num - 1,
+                                                GFP_NOFS);
                        }
                        if (old_val < (cache->key.offset >> 1) &&
                            old_val + num >= (cache->key.offset >> 1)) {
@@ -732,7 +709,9 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
        int ret;
        int i;
        struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
-       struct radix_tree_root *extent_radix = &root->fs_info->extent_map_radix;
+       struct extent_map_tree *free_space_cache;
+
+       free_space_cache = &root->fs_info->free_space_cache;
 
        while(1) {
                ret = find_first_radix_bit(unpin_radix, gang, 0,
@@ -751,8 +730,11 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
                                block_group->pinned--;
                                if (gang[i] < block_group->last_alloc)
                                        block_group->last_alloc = gang[i];
-                               if (!block_group->data)
-                                       set_radix_bit(extent_radix, gang[i]);
+                               if (!block_group->data) {
+                                       set_extent_dirty(free_space_cache,
+                                                        gang[i], gang[i],
+                                                        GFP_NOFS);
+                               }
                        }
                }
        }
@@ -995,6 +977,7 @@ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
        struct btrfs_block_group_cache *block_group;
        int full_scan = 0;
        int wrapped = 0;
+       u64 cached_search_start = 0;
 
        WARN_ON(num_blocks < 1);
        btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
@@ -1017,11 +1000,9 @@ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
        path = btrfs_alloc_path();
 
 check_failed:
-       if (!block_group->data)
-               search_start = find_search_start(root, &block_group,
-                                                search_start, total_needed);
-       else if (!full_scan)
-               search_start = max(block_group->last_alloc, search_start);
+       search_start = find_search_start(root, &block_group,
+                                        search_start, total_needed, data);
+       cached_search_start = search_start;
 
        btrfs_init_path(path);
        ins->objectid = search_start;
@@ -1097,6 +1078,7 @@ check_failed:
 
                start_found = 1;
                last_block = key.objectid + key.offset;
+
                if (!full_scan && last_block >= block_group->key.objectid +
                    block_group->key.offset) {
                        btrfs_release_path(root, path);
@@ -1138,6 +1120,9 @@ check_pending:
        }
        ins->offset = num_blocks;
        btrfs_free_path(path);
+       if (0 && ins->objectid != cached_search_start) {
+printk("\tcached was %Lu found %Lu\n", cached_search_start, ins->objectid);
+       }
        return 0;
 
 new_group:
@@ -1209,6 +1194,10 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        btrfs_set_root_used(&root->root_item, root_blocks_used +
                                   num_blocks);
 
+       clear_extent_dirty(&root->fs_info->free_space_cache,
+                          ins->objectid, ins->objectid + ins->offset - 1,
+                          GFP_NOFS);
+
        if (root == extent_root) {
                BUG_ON(num_blocks != 1);
                set_radix_bit(&root->fs_info->extent_ins_radix, ins->objectid);
@@ -1227,6 +1216,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        BUG_ON(ret);
        finish_current_insert(trans, extent_root);
        pending_ret = del_pending_extents(trans, extent_root);
+
        if (ret) {
                return ret;
        }
@@ -1265,6 +1255,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                return ERR_PTR(-ENOMEM);
        }
        btrfs_set_buffer_uptodate(buf);
+       buf->alloc_addr = (unsigned long)__builtin_return_address(0);
        set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
                         buf->start + buf->len - 1, GFP_NOFS);
        /*
@@ -1492,6 +1483,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
        orig_level = level;
        if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
                path->nodes[level] = root->node;
+               extent_buffer_get(root->node);
                path->slots[level] = 0;
        } else {
                struct btrfs_key key;
@@ -1524,7 +1516,6 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
                if (wret < 0)
                        ret = wret;
                ret = -EAGAIN;
-               extent_buffer_get(root->node);
                break;
        }
        for (i = 0; i <= orig_level; i++) {
@@ -1562,8 +1553,8 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
 {
        int ret;
        int ret2;
-       unsigned long gang[16];
-       int i;
+       u64 start;
+       u64 end;
 
        ret = free_block_group_radix(&info->block_group_radix);
        ret2 = free_block_group_radix(&info->block_group_data_radix);
@@ -1573,13 +1564,12 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                return ret2;
 
        while(1) {
-               ret = find_first_radix_bit(&info->extent_map_radix,
-                                          gang, 0, ARRAY_SIZE(gang));
-               if (!ret)
+               ret = find_first_extent_bit(&info->free_space_cache, 0,
+                                           &start, &end, EXTENT_DIRTY);
+               if (ret)
                        break;
-               for (i = 0; i < ret; i++) {
-                       clear_radix_bit(&info->extent_map_radix, gang[i]);
-               }
+               clear_extent_dirty(&info->free_space_cache, start,
+                                  end, GFP_NOFS);
        }
        return 0;
 }
Simple merge
Simple merge
Simple merge