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: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 660b05a..99a8b0f 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -192,11 +192,13 @@
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 @@
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 @@
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 @@
u64 end;
u64 free_check;
u64 ptr;
+ u64 total_fs_bytes;
int bit;
int ret;
int full_search = 0;
@@ -294,6 +299,7 @@
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 @@
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 @@
}
}
}
- 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 @@
else
hint_last = search_start;
+ if (hint_last >= total_fs_bytes)
+ hint_last = search_start;
last = hint_last;
}
again:
@@ -350,6 +358,9 @@
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 @@
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 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 @@
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 @@
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 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 2cb2dd3..2817570 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -148,6 +148,7 @@
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 @@
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 @@
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 @@
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;
}