Commit c31f8830 authored by Chris Mason's avatar Chris Mason

Btrfs: online shrinking fixes

While shrinking the FS, the allocation functions need to make sure
they don't try to allocate bytes past the end of the FS.

nodatacow needed an extra check to force cows when the existing extents are
past the end of the FS.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent b0331a4c
...@@ -192,11 +192,13 @@ static u64 noinline find_search_start(struct btrfs_root *root, ...@@ -192,11 +192,13 @@ static u64 noinline find_search_start(struct btrfs_root *root,
u64 start = 0; u64 start = 0;
u64 end = 0; u64 end = 0;
u64 cache_miss = 0; u64 cache_miss = 0;
u64 total_fs_bytes;
int wrapped = 0; int wrapped = 0;
if (!cache) { if (!cache) {
goto out; goto out;
} }
total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
again: again:
ret = cache_block_group(root, cache); ret = cache_block_group(root, cache);
if (ret) if (ret)
...@@ -223,6 +225,8 @@ static u64 noinline find_search_start(struct btrfs_root *root, ...@@ -223,6 +225,8 @@ static u64 noinline find_search_start(struct btrfs_root *root,
if (data != BTRFS_BLOCK_GROUP_MIXED && if (data != BTRFS_BLOCK_GROUP_MIXED &&
start + num > cache->key.objectid + cache->key.offset) start + num > cache->key.objectid + cache->key.offset)
goto new_group; goto new_group;
if (start + num > total_fs_bytes)
goto new_group;
return start; return start;
} }
out: out:
...@@ -239,7 +243,7 @@ static u64 noinline find_search_start(struct btrfs_root *root, ...@@ -239,7 +243,7 @@ static u64 noinline find_search_start(struct btrfs_root *root,
last = cache->key.objectid + cache->key.offset; last = cache->key.objectid + cache->key.offset;
wrapped: wrapped:
cache = btrfs_lookup_block_group(root->fs_info, last); cache = btrfs_lookup_block_group(root->fs_info, last);
if (!cache) { if (!cache || cache->key.objectid >= total_fs_bytes) {
no_cache: no_cache:
if (!wrapped) { if (!wrapped) {
wrapped = 1; wrapped = 1;
...@@ -287,6 +291,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -287,6 +291,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
u64 end; u64 end;
u64 free_check; u64 free_check;
u64 ptr; u64 ptr;
u64 total_fs_bytes;
int bit; int bit;
int ret; int ret;
int full_search = 0; int full_search = 0;
...@@ -294,6 +299,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -294,6 +299,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
int data_swap = 0; int data_swap = 0;
block_group_cache = &info->block_group_cache; block_group_cache = &info->block_group_cache;
total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
if (!owner) if (!owner)
factor = 8; factor = 8;
...@@ -306,7 +312,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -306,7 +312,7 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
else else
bit = BLOCK_GROUP_METADATA; bit = BLOCK_GROUP_METADATA;
if (search_start) { if (search_start && search_start < total_fs_bytes) {
struct btrfs_block_group_cache *shint; struct btrfs_block_group_cache *shint;
shint = btrfs_lookup_block_group(info, search_start); shint = btrfs_lookup_block_group(info, search_start);
if (shint && (shint->data == data || if (shint && (shint->data == data ||
...@@ -318,8 +324,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -318,8 +324,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
} }
} }
} }
if (hint && (hint->data == data || if (hint && hint->key.objectid < total_fs_bytes &&
hint->data == BTRFS_BLOCK_GROUP_MIXED)) { (hint->data == data || hint->data == BTRFS_BLOCK_GROUP_MIXED)) {
used = btrfs_block_group_used(&hint->item); used = btrfs_block_group_used(&hint->item);
if (used + hint->pinned < if (used + hint->pinned <
div_factor(hint->key.offset, factor)) { div_factor(hint->key.offset, factor)) {
...@@ -333,6 +339,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -333,6 +339,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
else else
hint_last = search_start; hint_last = search_start;
if (hint_last >= total_fs_bytes)
hint_last = search_start;
last = hint_last; last = hint_last;
} }
again: again:
...@@ -350,6 +358,9 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -350,6 +358,9 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
last = cache->key.objectid + cache->key.offset; last = cache->key.objectid + cache->key.offset;
used = btrfs_block_group_used(&cache->item); used = btrfs_block_group_used(&cache->item);
if (cache->key.objectid > total_fs_bytes)
break;
if (full_search) if (full_search)
free_check = cache->key.offset; free_check = cache->key.offset;
else else
...@@ -1420,8 +1431,8 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1420,8 +1431,8 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
data = BTRFS_BLOCK_GROUP_MIXED; data = BTRFS_BLOCK_GROUP_MIXED;
} }
if (search_end == (u64)-1) search_end = min(search_end,
search_end = btrfs_super_total_bytes(&info->super_copy); btrfs_super_total_bytes(&info->super_copy));
if (hint_byte) { if (hint_byte) {
block_group = btrfs_lookup_block_group(info, hint_byte); block_group = btrfs_lookup_block_group(info, hint_byte);
if (!block_group) if (!block_group)
...@@ -1617,7 +1628,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1617,7 +1628,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
{ {
int ret; int ret;
int pending_ret; int pending_ret;
u64 super_used, root_used; u64 super_used;
u64 root_used;
u64 search_start = 0; u64 search_start = 0;
u64 new_hint; u64 new_hint;
struct btrfs_fs_info *info = root->fs_info; struct btrfs_fs_info *info = root->fs_info;
...@@ -1636,6 +1648,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1636,6 +1648,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
search_start, search_end, hint_byte, ins, search_start, search_end, hint_byte, ins,
trans->alloc_exclude_start, trans->alloc_exclude_start,
trans->alloc_exclude_nr, data); trans->alloc_exclude_nr, data);
if (ret)
printk("find free extent returns %d\n", ret);
BUG_ON(ret); BUG_ON(ret);
if (ret) if (ret)
return ret; return ret;
...@@ -2292,8 +2306,6 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root, ...@@ -2292,8 +2306,6 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root,
while(1) { while(1) {
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
BUG_ON(ret == 0);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -2340,6 +2352,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 new_size) ...@@ -2340,6 +2352,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 new_size)
int progress = 0; int progress = 0;
btrfs_set_super_total_bytes(&info->super_copy, new_size); 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; block_group_cache = &info->block_group_cache;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
root = root->fs_info->extent_root; root = root->fs_info->extent_root;
......
...@@ -148,6 +148,7 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) ...@@ -148,6 +148,7 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end)
u64 bytenr; u64 bytenr;
u64 cow_end; u64 cow_end;
u64 loops = 0; u64 loops = 0;
u64 total_fs_bytes;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_buffer *leaf; struct extent_buffer *leaf;
int found_type; int found_type;
...@@ -157,6 +158,7 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) ...@@ -157,6 +158,7 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end)
int err; int err;
struct btrfs_key found_key; struct btrfs_key found_key;
total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
path = btrfs_alloc_path(); path = btrfs_alloc_path();
BUG_ON(!path); BUG_ON(!path);
again: again:
...@@ -189,8 +191,10 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) ...@@ -189,8 +191,10 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end)
found_type = btrfs_file_extent_type(leaf, item); found_type = btrfs_file_extent_type(leaf, item);
extent_start = found_key.offset; extent_start = found_key.offset;
if (found_type == BTRFS_FILE_EXTENT_REG) { if (found_type == BTRFS_FILE_EXTENT_REG) {
extent_end = extent_start + u64 extent_num_bytes;
btrfs_file_extent_num_bytes(leaf, item);
extent_num_bytes = btrfs_file_extent_num_bytes(leaf, item);
extent_end = extent_start + extent_num_bytes;
err = 0; err = 0;
if (loops && start != extent_start) if (loops && start != extent_start)
...@@ -204,6 +208,13 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end) ...@@ -204,6 +208,13 @@ static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end)
if (bytenr == 0) if (bytenr == 0)
goto not_found; 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) { if (btrfs_count_snapshots_in_path(root, path, bytenr) != 1) {
goto not_found; goto not_found;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment