diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 660b05a4baf5dd30ac0cc00c8171b918d349da5a..99a8b0f0d31867cbc33c72a249b47ac04360b498 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -192,11 +192,13 @@ static u64 noinline find_search_start(struct btrfs_root *root, u64 start = 0; u64 end = 0; u64 cache_miss = 0; + u64 total_fs_bytes; int wrapped = 0; if (!cache) { goto out; } + total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); again: ret = cache_block_group(root, cache); if (ret) @@ -223,6 +225,8 @@ static u64 noinline find_search_start(struct btrfs_root *root, if (data != BTRFS_BLOCK_GROUP_MIXED && start + num > cache->key.objectid + cache->key.offset) goto new_group; + if (start + num > total_fs_bytes) + goto new_group; return start; } out: @@ -239,7 +243,7 @@ static u64 noinline find_search_start(struct btrfs_root *root, last = cache->key.objectid + cache->key.offset; wrapped: cache = btrfs_lookup_block_group(root->fs_info, last); - if (!cache) { + if (!cache || cache->key.objectid >= total_fs_bytes) { no_cache: if (!wrapped) { wrapped = 1; @@ -287,6 +291,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, u64 end; u64 free_check; u64 ptr; + u64 total_fs_bytes; int bit; int ret; int full_search = 0; @@ -294,6 +299,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, int data_swap = 0; block_group_cache = &info->block_group_cache; + total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); if (!owner) factor = 8; @@ -306,7 +312,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, else bit = BLOCK_GROUP_METADATA; - if (search_start) { + if (search_start && search_start < total_fs_bytes) { struct btrfs_block_group_cache *shint; shint = btrfs_lookup_block_group(info, search_start); if (shint && (shint->data == data || @@ -318,8 +324,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, } } } - if (hint && (hint->data == data || - hint->data == BTRFS_BLOCK_GROUP_MIXED)) { + if (hint && hint->key.objectid < total_fs_bytes && + (hint->data == data || hint->data == BTRFS_BLOCK_GROUP_MIXED)) { used = btrfs_block_group_used(&hint->item); if (used + hint->pinned < div_factor(hint->key.offset, factor)) { @@ -333,6 +339,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, else hint_last = search_start; + if (hint_last >= total_fs_bytes) + hint_last = search_start; last = hint_last; } again: @@ -350,6 +358,9 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, last = cache->key.objectid + cache->key.offset; used = btrfs_block_group_used(&cache->item); + if (cache->key.objectid > total_fs_bytes) + break; + if (full_search) free_check = cache->key.offset; else @@ -1420,8 +1431,8 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, data = BTRFS_BLOCK_GROUP_MIXED; } - if (search_end == (u64)-1) - search_end = btrfs_super_total_bytes(&info->super_copy); + search_end = min(search_end, + btrfs_super_total_bytes(&info->super_copy)); if (hint_byte) { block_group = btrfs_lookup_block_group(info, hint_byte); if (!block_group) @@ -1617,7 +1628,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, { int ret; int pending_ret; - u64 super_used, root_used; + u64 super_used; + u64 root_used; u64 search_start = 0; u64 new_hint; struct btrfs_fs_info *info = root->fs_info; @@ -1636,6 +1648,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, search_start, search_end, hint_byte, ins, trans->alloc_exclude_start, trans->alloc_exclude_nr, data); +if (ret) +printk("find free extent returns %d\n", ret); BUG_ON(ret); if (ret) return ret; @@ -2292,8 +2306,6 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root, while(1) { ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); - BUG_ON(ret == 0); - if (ret < 0) goto out; @@ -2340,6 +2352,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 new_size) int progress = 0; btrfs_set_super_total_bytes(&info->super_copy, new_size); + clear_extent_dirty(&info->free_space_cache, new_size, (u64)-1, + GFP_NOFS); block_group_cache = &info->block_group_cache; path = btrfs_alloc_path(); root = root->fs_info->extent_root; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2cb2dd32407e6390b00c6724876f16b78af1aa63..281757007ddbf61cdacc9ebf645c4f0ae4e9eb79 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -148,6 +148,7 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) u64 bytenr; u64 cow_end; u64 loops = 0; + u64 total_fs_bytes; struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_buffer *leaf; int found_type; @@ -157,6 +158,7 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) int err; struct btrfs_key found_key; + total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); path = btrfs_alloc_path(); BUG_ON(!path); again: @@ -189,8 +191,10 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) found_type = btrfs_file_extent_type(leaf, item); extent_start = found_key.offset; if (found_type == BTRFS_FILE_EXTENT_REG) { - extent_end = extent_start + - btrfs_file_extent_num_bytes(leaf, item); + u64 extent_num_bytes; + + extent_num_bytes = btrfs_file_extent_num_bytes(leaf, item); + extent_end = extent_start + extent_num_bytes; err = 0; if (loops && start != extent_start) @@ -204,6 +208,13 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) if (bytenr == 0) goto not_found; + /* + * we may be called by the resizer, make sure we're inside + * the limits of the FS + */ + if (bytenr + extent_num_bytes > total_fs_bytes) + goto not_found; + if (btrfs_count_snapshots_in_path(root, path, bytenr) != 1) { goto not_found; }