提交 0ef3e66b 编写于 作者: C Chris Mason

Btrfs: Allocator fix variety pack

* Force chunk allocation when find_free_extent has to do a full scan
* Record the max key at the start of defrag so it doesn't run forever
* Block groups might not be contiguous, make a forward search for the
  next block group in extent-tree.c
* Get rid of extra checks for total fs size
* Fix relocate_one_reference to avoid relocating the same file data block
  twice when referenced by an older transaction
* Use the open device count when allocating chunks so that we don't
  try to allocate from devices that don't exist
Signed-off-by: NChris Mason <chris.mason@oracle.com>
上级 515dc322
...@@ -363,7 +363,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, ...@@ -363,7 +363,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
other = btrfs_node_blockptr(parent, i - 1); other = btrfs_node_blockptr(parent, i - 1);
close = close_blocks(blocknr, other, blocksize); close = close_blocks(blocknr, other, blocksize);
} }
if (close && i < end_slot - 2) { if (!close && i < end_slot - 2) {
other = btrfs_node_blockptr(parent, i + 1); other = btrfs_node_blockptr(parent, i + 1);
close = close_blocks(blocknr, other, blocksize); close = close_blocks(blocknr, other, blocksize);
} }
......
...@@ -464,6 +464,7 @@ struct btrfs_space_info { ...@@ -464,6 +464,7 @@ struct btrfs_space_info {
u64 bytes_used; u64 bytes_used;
u64 bytes_pinned; u64 bytes_pinned;
int full; int full;
int force_alloc;
struct list_head list; struct list_head list;
}; };
...@@ -589,6 +590,7 @@ struct btrfs_root { ...@@ -589,6 +590,7 @@ struct btrfs_root {
int ref_cows; int ref_cows;
int track_dirty; int track_dirty;
struct btrfs_key defrag_progress; struct btrfs_key defrag_progress;
struct btrfs_key defrag_max;
int defrag_running; int defrag_running;
int defrag_level; int defrag_level;
char *name; char *name;
......
...@@ -136,6 +136,35 @@ static int cache_block_group(struct btrfs_root *root, ...@@ -136,6 +136,35 @@ static int cache_block_group(struct btrfs_root *root,
return 0; return 0;
} }
struct btrfs_block_group_cache *btrfs_lookup_first_block_group(struct
btrfs_fs_info *info,
u64 bytenr)
{
struct extent_io_tree *block_group_cache;
struct btrfs_block_group_cache *block_group = NULL;
u64 ptr;
u64 start;
u64 end;
int ret;
bytenr = max_t(u64, bytenr,
BTRFS_SUPER_INFO_OFFSET + BTRFS_SUPER_INFO_SIZE);
block_group_cache = &info->block_group_cache;
ret = find_first_extent_bit(block_group_cache,
bytenr, &start, &end,
BLOCK_GROUP_DATA | BLOCK_GROUP_METADATA |
BLOCK_GROUP_SYSTEM);
if (ret) {
return NULL;
}
ret = get_state_private(block_group_cache, start, &ptr);
if (ret)
return NULL;
block_group = (struct btrfs_block_group_cache *)(unsigned long)ptr;
return block_group;
}
struct btrfs_block_group_cache *btrfs_lookup_block_group(struct struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
btrfs_fs_info *info, btrfs_fs_info *info,
u64 bytenr) u64 bytenr)
...@@ -175,7 +204,7 @@ static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits) ...@@ -175,7 +204,7 @@ static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
static int noinline find_search_start(struct btrfs_root *root, static int noinline find_search_start(struct btrfs_root *root,
struct btrfs_block_group_cache **cache_ret, struct btrfs_block_group_cache **cache_ret,
u64 *start_ret, int num, int data) u64 *start_ret, u64 num, int data)
{ {
int ret; int ret;
struct btrfs_block_group_cache *cache = *cache_ret; struct btrfs_block_group_cache *cache = *cache_ret;
...@@ -188,21 +217,21 @@ static int noinline find_search_start(struct btrfs_root *root, ...@@ -188,21 +217,21 @@ static int noinline find_search_start(struct btrfs_root *root,
u64 search_start = *start_ret; u64 search_start = *start_ret;
int wrapped = 0; int wrapped = 0;
if (!cache)
goto out;
total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
free_space_cache = &root->fs_info->free_space_cache; free_space_cache = &root->fs_info->free_space_cache;
if (!cache)
goto out;
again: again:
ret = cache_block_group(root, cache); ret = cache_block_group(root, cache);
if (ret) if (ret) {
goto out; goto out;
}
last = max(search_start, cache->key.objectid); last = max(search_start, cache->key.objectid);
if (!block_group_bits(cache, data) || cache->ro) { if (!block_group_bits(cache, data) || cache->ro)
goto new_group; goto new_group;
}
spin_lock_irq(&free_space_cache->lock); spin_lock_irq(&free_space_cache->lock);
state = find_first_extent_bit_state(free_space_cache, last, EXTENT_DIRTY); state = find_first_extent_bit_state(free_space_cache, last, EXTENT_DIRTY);
...@@ -217,20 +246,17 @@ static int noinline find_search_start(struct btrfs_root *root, ...@@ -217,20 +246,17 @@ static int noinline find_search_start(struct btrfs_root *root,
start = max(last, state->start); start = max(last, state->start);
last = state->end + 1; last = state->end + 1;
if (last - start < num) { if (last - start < num) {
if (last == cache->key.objectid + cache->key.offset)
cache_miss = start;
do { do {
state = extent_state_next(state); state = extent_state_next(state);
} while(state && !(state->state & EXTENT_DIRTY)); } while(state && !(state->state & EXTENT_DIRTY));
continue; continue;
} }
spin_unlock_irq(&free_space_cache->lock); spin_unlock_irq(&free_space_cache->lock);
if (cache->ro) if (cache->ro) {
goto new_group; goto new_group;
}
if (start + num > cache->key.objectid + cache->key.offset) if (start + num > cache->key.objectid + cache->key.offset)
goto new_group; goto new_group;
if (start + num > total_fs_bytes)
goto new_group;
if (!block_group_bits(cache, data)) { if (!block_group_bits(cache, data)) {
printk("block group bits don't match %Lu %d\n", cache->flags, data); printk("block group bits don't match %Lu %d\n", cache->flags, data);
} }
...@@ -248,7 +274,7 @@ static int noinline find_search_start(struct btrfs_root *root, ...@@ -248,7 +274,7 @@ static int noinline find_search_start(struct btrfs_root *root,
new_group: new_group:
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_first_block_group(root->fs_info, last);
if (!cache || cache->key.objectid >= total_fs_bytes) { if (!cache || cache->key.objectid >= total_fs_bytes) {
no_cache: no_cache:
if (!wrapped) { if (!wrapped) {
...@@ -261,13 +287,13 @@ static int noinline find_search_start(struct btrfs_root *root, ...@@ -261,13 +287,13 @@ static int noinline find_search_start(struct btrfs_root *root,
if (cache_miss && !cache->cached) { if (cache_miss && !cache->cached) {
cache_block_group(root, cache); cache_block_group(root, cache);
last = cache_miss; last = cache_miss;
cache = btrfs_lookup_block_group(root->fs_info, last); cache = btrfs_lookup_first_block_group(root->fs_info, last);
} }
cache_miss = 0;
cache = btrfs_find_block_group(root, cache, last, data, 0); cache = btrfs_find_block_group(root, cache, last, data, 0);
if (!cache) if (!cache)
goto no_cache; goto no_cache;
*cache_ret = cache; *cache_ret = cache;
cache_miss = 0;
goto again; goto again;
} }
...@@ -303,28 +329,26 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -303,28 +329,26 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
struct btrfs_fs_info *info = root->fs_info; struct btrfs_fs_info *info = root->fs_info;
u64 used; u64 used;
u64 last = 0; u64 last = 0;
u64 hint_last;
u64 start; u64 start;
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;
int factor = 10; int factor = 10;
int wrapped = 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 (data & BTRFS_BLOCK_GROUP_METADATA) if (data & BTRFS_BLOCK_GROUP_METADATA)
factor = 9; factor = 9;
bit = block_group_state_bits(data); bit = block_group_state_bits(data);
if (search_start && search_start < total_fs_bytes) { if (search_start) {
struct btrfs_block_group_cache *shint; struct btrfs_block_group_cache *shint;
shint = btrfs_lookup_block_group(info, search_start); shint = btrfs_lookup_first_block_group(info, search_start);
if (shint && block_group_bits(shint, data) && !shint->ro) { if (shint && block_group_bits(shint, data) && !shint->ro) {
used = btrfs_block_group_used(&shint->item); used = btrfs_block_group_used(&shint->item);
if (used + shint->pinned < if (used + shint->pinned <
...@@ -333,24 +357,18 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -333,24 +357,18 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
} }
} }
} }
if (hint && !hint->ro && block_group_bits(hint, data) && if (hint && !hint->ro && block_group_bits(hint, data)) {
hint->key.objectid < total_fs_bytes) {
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)) {
return hint; return hint;
} }
last = hint->key.objectid + hint->key.offset; last = hint->key.objectid + hint->key.offset;
hint_last = last;
} else { } else {
if (hint) if (hint)
hint_last = max(hint->key.objectid, search_start); last = max(hint->key.objectid, search_start);
else else
hint_last = search_start; last = search_start;
if (hint_last >= total_fs_bytes)
hint_last = search_start;
last = hint_last;
} }
again: again:
while(1) { while(1) {
...@@ -360,23 +378,17 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -360,23 +378,17 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
break; break;
ret = get_state_private(block_group_cache, start, &ptr); ret = get_state_private(block_group_cache, start, &ptr);
if (ret) if (ret) {
break; last = end + 1;
continue;
}
cache = (struct btrfs_block_group_cache *)(unsigned long)ptr; cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
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 (!cache->ro && block_group_bits(cache, data)) { if (!cache->ro && block_group_bits(cache, data)) {
if (full_search) free_check = div_factor(cache->key.offset, factor);
free_check = cache->key.offset;
else
free_check = div_factor(cache->key.offset,
factor);
if (used + cache->pinned < free_check) { if (used + cache->pinned < free_check) {
found_group = cache; found_group = cache;
goto found; goto found;
...@@ -384,9 +396,15 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root, ...@@ -384,9 +396,15 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
} }
cond_resched(); cond_resched();
} }
if (!full_search) { if (!wrapped) {
last = search_start;
wrapped = 1;
goto again;
}
if (!full_search && factor < 10) {
last = search_start; last = search_start;
full_search = 1; full_search = 1;
factor = 10;
goto again; goto again;
} }
found: found:
...@@ -1070,6 +1088,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, ...@@ -1070,6 +1088,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
found->bytes_used = bytes_used; found->bytes_used = bytes_used;
found->bytes_pinned = 0; found->bytes_pinned = 0;
found->full = 0; found->full = 0;
found->force_alloc = 0;
*space_info = found; *space_info = found;
return 0; return 0;
} }
...@@ -1120,7 +1139,7 @@ static u64 reduce_alloc_profile(struct btrfs_root *root, u64 flags) ...@@ -1120,7 +1139,7 @@ static u64 reduce_alloc_profile(struct btrfs_root *root, u64 flags)
static int do_chunk_alloc(struct btrfs_trans_handle *trans, static int do_chunk_alloc(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 alloc_bytes, struct btrfs_root *extent_root, u64 alloc_bytes,
u64 flags) u64 flags, int force)
{ {
struct btrfs_space_info *space_info; struct btrfs_space_info *space_info;
u64 thresh; u64 thresh;
...@@ -1138,11 +1157,16 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, ...@@ -1138,11 +1157,16 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
} }
BUG_ON(!space_info); BUG_ON(!space_info);
if (space_info->force_alloc) {
force = 1;
space_info->force_alloc = 0;
}
if (space_info->full) if (space_info->full)
return 0; return 0;
thresh = div_factor(space_info->total_bytes, 6); thresh = div_factor(space_info->total_bytes, 6);
if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) < if (!force &&
(space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) <
thresh) thresh)
return 0; return 0;
...@@ -1152,7 +1176,6 @@ printk("space info full %Lu\n", flags); ...@@ -1152,7 +1176,6 @@ printk("space info full %Lu\n", flags);
space_info->full = 1; space_info->full = 1;
return 0; return 0;
} }
BUG_ON(ret); BUG_ON(ret);
ret = btrfs_make_block_group(trans, extent_root, 0, flags, ret = btrfs_make_block_group(trans, extent_root, 0, flags,
...@@ -1619,11 +1642,16 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1619,11 +1642,16 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_block_group_cache *block_group; struct btrfs_block_group_cache *block_group;
int full_scan = 0; int full_scan = 0;
int wrapped = 0; int wrapped = 0;
int chunk_alloc_done = 0;
int empty_cluster = 2 * 1024 * 1024; int empty_cluster = 2 * 1024 * 1024;
int allowed_chunk_alloc = 0;
WARN_ON(num_bytes < root->sectorsize); WARN_ON(num_bytes < root->sectorsize);
btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
if (orig_root->ref_cows || empty_size)
allowed_chunk_alloc = 1;
if (data & BTRFS_BLOCK_GROUP_METADATA) { if (data & BTRFS_BLOCK_GROUP_METADATA) {
last_ptr = &root->fs_info->last_alloc; last_ptr = &root->fs_info->last_alloc;
empty_cluster = 256 * 1024; empty_cluster = 256 * 1024;
...@@ -1648,7 +1676,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1648,7 +1676,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
search_end = btrfs_super_total_bytes(&info->super_copy); search_end = 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_first_block_group(info, hint_byte);
if (!block_group) if (!block_group)
hint_byte = search_start; hint_byte = search_start;
block_group = btrfs_find_block_group(root, block_group, block_group = btrfs_find_block_group(root, block_group,
...@@ -1666,16 +1694,27 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1666,16 +1694,27 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
check_failed: check_failed:
if (!block_group) { if (!block_group) {
block_group = btrfs_lookup_block_group(info, search_start); block_group = btrfs_lookup_first_block_group(info,
search_start);
if (!block_group) if (!block_group)
block_group = btrfs_lookup_block_group(info, block_group = btrfs_lookup_first_block_group(info,
orig_search_start); orig_search_start);
} }
if (full_scan && !chunk_alloc_done) {
if (allowed_chunk_alloc) {
do_chunk_alloc(trans, root,
num_bytes + 2 * 1024 * 1024, data, 1);
allowed_chunk_alloc = 0;
} else if (block_group && block_group_bits(block_group, data)) {
block_group->space_info->force_alloc = 1;
}
chunk_alloc_done = 1;
}
ret = find_search_start(root, &block_group, &search_start, ret = find_search_start(root, &block_group, &search_start,
total_needed, data); total_needed, data);
if (ret == -ENOSPC && last_ptr && *last_ptr) { if (ret == -ENOSPC && last_ptr && *last_ptr) {
*last_ptr = 0; *last_ptr = 0;
block_group = btrfs_lookup_block_group(info, block_group = btrfs_lookup_first_block_group(info,
orig_search_start); orig_search_start);
search_start = orig_search_start; search_start = orig_search_start;
ret = find_search_start(root, &block_group, &search_start, ret = find_search_start(root, &block_group, &search_start,
...@@ -1692,7 +1731,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1692,7 +1731,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
empty_size += empty_cluster; empty_size += empty_cluster;
total_needed += empty_size; total_needed += empty_size;
} }
block_group = btrfs_lookup_block_group(info, block_group = btrfs_lookup_first_block_group(info,
orig_search_start); orig_search_start);
search_start = orig_search_start; search_start = orig_search_start;
ret = find_search_start(root, &block_group, ret = find_search_start(root, &block_group,
...@@ -1765,7 +1804,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1765,7 +1804,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
} else } else
wrapped = 1; wrapped = 1;
} }
block_group = btrfs_lookup_block_group(info, search_start); block_group = btrfs_lookup_first_block_group(info, search_start);
cond_resched(); cond_resched();
block_group = btrfs_find_block_group(root, block_group, block_group = btrfs_find_block_group(root, block_group,
search_start, data, 0); search_start, data, 0);
...@@ -1819,17 +1858,21 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1819,17 +1858,21 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
} }
again: again:
data = reduce_alloc_profile(root, data); data = reduce_alloc_profile(root, data);
if (root->ref_cows) { /*
* the only place that sets empty_size is btrfs_realloc_node, which
* is not called recursively on allocations
*/
if (empty_size || root->ref_cows) {
if (!(data & BTRFS_BLOCK_GROUP_METADATA)) { if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
ret = do_chunk_alloc(trans, root->fs_info->extent_root, ret = do_chunk_alloc(trans, root->fs_info->extent_root,
2 * 1024 * 1024, 2 * 1024 * 1024,
BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_METADATA |
(info->metadata_alloc_profile & (info->metadata_alloc_profile &
info->avail_metadata_alloc_bits)); info->avail_metadata_alloc_bits), 0);
BUG_ON(ret); BUG_ON(ret);
} }
ret = do_chunk_alloc(trans, root->fs_info->extent_root, ret = do_chunk_alloc(trans, root->fs_info->extent_root,
num_bytes + 2 * 1024 * 1024, data); num_bytes + 2 * 1024 * 1024, data, 0);
BUG_ON(ret); BUG_ON(ret);
} }
...@@ -1842,6 +1885,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1842,6 +1885,8 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
if (ret == -ENOSPC && num_bytes > min_alloc_size) { if (ret == -ENOSPC && num_bytes > min_alloc_size) {
num_bytes = num_bytes >> 1; num_bytes = num_bytes >> 1;
num_bytes = max(num_bytes, min_alloc_size); num_bytes = max(num_bytes, min_alloc_size);
do_chunk_alloc(trans, root->fs_info->extent_root,
num_bytes, data, 1);
goto again; goto again;
} }
if (ret) { if (ret) {
...@@ -2537,7 +2582,11 @@ static int find_root_for_ref(struct btrfs_root *root, ...@@ -2537,7 +2582,11 @@ static int find_root_for_ref(struct btrfs_root *root,
*/ */
static int noinline relocate_one_reference(struct btrfs_root *extent_root, static int noinline relocate_one_reference(struct btrfs_root *extent_root,
struct btrfs_path *path, struct btrfs_path *path,
struct btrfs_key *extent_key) struct btrfs_key *extent_key,
u64 *last_file_objectid,
u64 *last_file_offset,
u64 *last_file_root,
u64 last_extent)
{ {
struct inode *inode; struct inode *inode;
struct btrfs_root *found_root; struct btrfs_root *found_root;
...@@ -2576,6 +2625,12 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root, ...@@ -2576,6 +2625,12 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
found_key.offset = ref_offset; found_key.offset = ref_offset;
level = 0; level = 0;
if (last_extent == extent_key->objectid &&
*last_file_objectid == ref_objectid &&
*last_file_offset == ref_offset &&
*last_file_root == ref_root)
goto out;
ret = find_root_for_ref(extent_root, path, &found_key, ret = find_root_for_ref(extent_root, path, &found_key,
level, 1, &found_root, level, 1, &found_root,
extent_key->objectid); extent_key->objectid);
...@@ -2583,6 +2638,12 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root, ...@@ -2583,6 +2638,12 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
if (ret) if (ret)
goto out; goto out;
if (last_extent == extent_key->objectid &&
*last_file_objectid == ref_objectid &&
*last_file_offset == ref_offset &&
*last_file_root == ref_root)
goto out;
mutex_unlock(&extent_root->fs_info->fs_mutex); mutex_unlock(&extent_root->fs_info->fs_mutex);
inode = btrfs_iget_locked(extent_root->fs_info->sb, inode = btrfs_iget_locked(extent_root->fs_info->sb,
ref_objectid, found_root); ref_objectid, found_root);
...@@ -2603,6 +2664,10 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root, ...@@ -2603,6 +2664,10 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
mutex_lock(&extent_root->fs_info->fs_mutex); mutex_lock(&extent_root->fs_info->fs_mutex);
goto out; goto out;
} }
*last_file_objectid = inode->i_ino;
*last_file_root = found_root->root_key.objectid;
*last_file_offset = ref_offset;
relocate_inode_pages(inode, ref_offset, extent_key->offset); relocate_inode_pages(inode, ref_offset, extent_key->offset);
iput(inode); iput(inode);
mutex_lock(&extent_root->fs_info->fs_mutex); mutex_lock(&extent_root->fs_info->fs_mutex);
...@@ -2643,6 +2708,8 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root, ...@@ -2643,6 +2708,8 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
path->nodes[i] = NULL; path->nodes[i] = NULL;
} }
btrfs_release_path(found_root, path); btrfs_release_path(found_root, path);
if (found_root == found_root->fs_info->extent_root)
btrfs_extent_post_op(trans, found_root);
btrfs_end_transaction(trans, found_root); btrfs_end_transaction(trans, found_root);
} }
...@@ -2678,6 +2745,10 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root, ...@@ -2678,6 +2745,10 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root,
struct btrfs_key key; struct btrfs_key key;
struct btrfs_key found_key; struct btrfs_key found_key;
struct extent_buffer *leaf; struct extent_buffer *leaf;
u64 last_file_objectid = 0;
u64 last_file_root = 0;
u64 last_file_offset = (u64)-1;
u64 last_extent = 0;
u32 nritems; u32 nritems;
u32 item_size; u32 item_size;
int ret = 0; int ret = 0;
...@@ -2722,9 +2793,13 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root, ...@@ -2722,9 +2793,13 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root,
key.offset = found_key.offset + 1; key.offset = found_key.offset + 1;
item_size = btrfs_item_size_nr(leaf, path->slots[0]); item_size = btrfs_item_size_nr(leaf, path->slots[0]);
ret = relocate_one_reference(extent_root, path, extent_key); ret = relocate_one_reference(extent_root, path, extent_key,
&last_file_objectid,
&last_file_offset,
&last_file_root, last_extent);
if (ret) if (ret)
goto out; goto out;
last_extent = extent_key->objectid;
} }
ret = 0; ret = 0;
out: out:
...@@ -2770,6 +2845,32 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags) ...@@ -2770,6 +2845,32 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
return flags; return flags;
} }
int __alloc_chunk_for_shrink(struct btrfs_root *root,
struct btrfs_block_group_cache *shrink_block_group,
int force)
{
struct btrfs_trans_handle *trans;
u64 new_alloc_flags;
u64 calc;
if (btrfs_block_group_used(&shrink_block_group->item) > 0) {
trans = btrfs_start_transaction(root, 1);
new_alloc_flags = update_block_group_flags(root,
shrink_block_group->flags);
if (new_alloc_flags != shrink_block_group->flags) {
calc =
btrfs_block_group_used(&shrink_block_group->item);
} else {
calc = shrink_block_group->key.offset;
}
do_chunk_alloc(trans, root->fs_info->extent_root,
calc + 2 * 1024 * 1024, new_alloc_flags, force);
btrfs_end_transaction(trans, root);
}
return 0;
}
int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
{ {
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
...@@ -2778,7 +2879,6 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) ...@@ -2778,7 +2879,6 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
u64 cur_byte; u64 cur_byte;
u64 total_found; u64 total_found;
u64 shrink_last_byte; u64 shrink_last_byte;
u64 new_alloc_flags;
struct btrfs_block_group_cache *shrink_block_group; struct btrfs_block_group_cache *shrink_block_group;
struct btrfs_fs_info *info = root->fs_info; struct btrfs_fs_info *info = root->fs_info;
struct btrfs_key key; struct btrfs_key key;
...@@ -2792,7 +2892,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) ...@@ -2792,7 +2892,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
shrink_start); shrink_start);
BUG_ON(!shrink_block_group); BUG_ON(!shrink_block_group);
shrink_last_byte = shrink_start + shrink_block_group->key.offset; shrink_last_byte = shrink_block_group->key.objectid +
shrink_block_group->key.offset;
shrink_block_group->space_info->total_bytes -= shrink_block_group->space_info->total_bytes -=
shrink_block_group->key.offset; shrink_block_group->key.offset;
...@@ -2804,23 +2905,10 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) ...@@ -2804,23 +2905,10 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
(unsigned long long)shrink_start, (unsigned long long)shrink_start,
(unsigned long long)shrink_block_group->flags); (unsigned long long)shrink_block_group->flags);
__alloc_chunk_for_shrink(root, shrink_block_group, 1);
again: again:
if (btrfs_block_group_used(&shrink_block_group->item) > 0) {
u64 calc;
trans = btrfs_start_transaction(root, 1);
new_alloc_flags = update_block_group_flags(root,
shrink_block_group->flags);
if (new_alloc_flags != shrink_block_group->flags) {
calc =
btrfs_block_group_used(&shrink_block_group->item);
} else {
calc = shrink_block_group->key.offset;
}
do_chunk_alloc(trans, root->fs_info->extent_root,
calc + 2 * 1024 * 1024, new_alloc_flags);
btrfs_end_transaction(trans, root);
}
shrink_block_group->ro = 1; shrink_block_group->ro = 1;
total_found = 0; total_found = 0;
...@@ -2888,6 +2976,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) ...@@ -2888,6 +2976,8 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
if (btrfs_key_type(&found_key) != BTRFS_EXTENT_ITEM_KEY || if (btrfs_key_type(&found_key) != BTRFS_EXTENT_ITEM_KEY ||
found_key.objectid + found_key.offset <= cur_byte) { found_key.objectid + found_key.offset <= cur_byte) {
memcpy(&key, &found_key, sizeof(key));
key.offset++;
path->slots[0]++; path->slots[0]++;
goto next; goto next;
} }
...@@ -2897,6 +2987,7 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) ...@@ -2897,6 +2987,7 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
key.objectid = cur_byte; key.objectid = cur_byte;
btrfs_release_path(root, path); btrfs_release_path(root, path);
ret = relocate_one_extent(root, path, &found_key); ret = relocate_one_extent(root, path, &found_key);
__alloc_chunk_for_shrink(root, shrink_block_group, 0);
} }
btrfs_release_path(root, path); btrfs_release_path(root, path);
...@@ -2930,20 +3021,27 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start) ...@@ -2930,20 +3021,27 @@ int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
if (ret < 0) if (ret < 0)
goto out; goto out;
leaf = path->nodes[0]; clear_extent_bits(&info->block_group_cache, key.objectid,
nritems = btrfs_header_nritems(leaf); key.objectid + key.offset - 1,
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); (unsigned int)-1, GFP_NOFS);
kfree(shrink_block_group);
clear_extent_bits(&info->block_group_cache, found_key.objectid,
found_key.objectid + found_key.offset - 1, clear_extent_bits(&info->free_space_cache,
key.objectid, key.objectid + key.offset - 1,
(unsigned int)-1, GFP_NOFS); (unsigned int)-1, GFP_NOFS);
memset(shrink_block_group, 0, sizeof(*shrink_block_group));
kfree(shrink_block_group);
btrfs_del_item(trans, root, path); btrfs_del_item(trans, root, path);
clear_extent_dirty(&info->free_space_cache,
shrink_start, shrink_last_byte - 1,
GFP_NOFS);
btrfs_commit_transaction(trans, root); btrfs_commit_transaction(trans, root);
/* the code to unpin extents might set a few bits in the free
* space cache for this range again
*/
clear_extent_bits(&info->free_space_cache,
key.objectid, key.objectid + key.offset - 1,
(unsigned int)-1, GFP_NOFS);
out: out:
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
...@@ -3081,9 +3179,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, ...@@ -3081,9 +3179,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
BUG_ON(!cache); BUG_ON(!cache);
cache->key.objectid = chunk_offset; cache->key.objectid = chunk_offset;
cache->key.offset = size; cache->key.offset = size;
btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY); btrfs_set_key_type(&cache->key, BTRFS_BLOCK_GROUP_ITEM_KEY);
memset(&cache->item, 0, sizeof(cache->item));
btrfs_set_block_group_used(&cache->item, bytes_used); btrfs_set_block_group_used(&cache->item, bytes_used);
btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid); btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid);
cache->flags = type; cache->flags = type;
......
...@@ -198,6 +198,13 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, ...@@ -198,6 +198,13 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
goto out; goto out;
} }
if (root->defrag_progress.objectid == 0) { if (root->defrag_progress.objectid == 0) {
u32 nritems;
nritems = btrfs_header_nritems(root->node);
root->defrag_max.objectid = 0;
/* from above we know this is not a leaf */
btrfs_node_key_to_cpu(root->node, &root->defrag_max,
nritems - 1);
extent_buffer_get(root->node); extent_buffer_get(root->node);
ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp); ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp);
BUG_ON(ret); BUG_ON(ret);
...@@ -254,6 +261,16 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, ...@@ -254,6 +261,16 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
out: out:
if (path) if (path)
btrfs_free_path(path); btrfs_free_path(path);
if (ret == -EAGAIN) {
if (root->defrag_max.objectid > root->defrag_progress.objectid)
goto done;
if (root->defrag_max.type > root->defrag_progress.type)
goto done;
if (root->defrag_max.offset > root->defrag_progress.offset)
goto done;
ret = 0;
}
done:
if (ret != -EAGAIN) { if (ret != -EAGAIN) {
memset(&root->defrag_progress, 0, memset(&root->defrag_progress, 0,
sizeof(root->defrag_progress)); sizeof(root->defrag_progress));
......
...@@ -750,10 +750,6 @@ static int btrfs_rm_dev_item(struct btrfs_root *root, ...@@ -750,10 +750,6 @@ static int btrfs_rm_dev_item(struct btrfs_root *root,
if (bdev == fs_devices->latest_bdev) if (bdev == fs_devices->latest_bdev)
fs_devices->latest_bdev = next_dev->bdev; fs_devices->latest_bdev = next_dev->bdev;
total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
btrfs_set_super_total_bytes(&root->fs_info->super_copy,
total_bytes - device->total_bytes);
total_bytes = btrfs_super_num_devices(&root->fs_info->super_copy); total_bytes = btrfs_super_num_devices(&root->fs_info->super_copy);
btrfs_set_super_num_devices(&root->fs_info->super_copy, btrfs_set_super_num_devices(&root->fs_info->super_copy,
total_bytes - 1); total_bytes - 1);
...@@ -849,6 +845,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -849,6 +845,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
} }
root->fs_info->fs_devices->num_devices--; root->fs_info->fs_devices->num_devices--;
root->fs_info->fs_devices->open_devices--;
ret = btrfs_shrink_device(device, 0); ret = btrfs_shrink_device(device, 0);
if (ret) if (ret)
...@@ -873,7 +870,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -873,7 +870,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
if (device->bdev) { if (device->bdev) {
/* one close for the device struct or super_block */ /* one close for the device struct or super_block */
close_bdev_excl(device->bdev); close_bdev_excl(device->bdev);
root->fs_info->fs_devices->open_devices--;
} }
if (bdev) { if (bdev) {
/* one close for us */ /* one close for us */
...@@ -1450,7 +1446,7 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, ...@@ -1450,7 +1446,7 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
return -ENOSPC; return -ENOSPC;
if (type & (BTRFS_BLOCK_GROUP_RAID0)) { if (type & (BTRFS_BLOCK_GROUP_RAID0)) {
num_stripes = btrfs_super_num_devices(&info->super_copy); num_stripes = extent_root->fs_info->fs_devices->open_devices;
min_stripes = 2; min_stripes = 2;
} }
if (type & (BTRFS_BLOCK_GROUP_DUP)) { if (type & (BTRFS_BLOCK_GROUP_DUP)) {
...@@ -1459,13 +1455,13 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, ...@@ -1459,13 +1455,13 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
} }
if (type & (BTRFS_BLOCK_GROUP_RAID1)) { if (type & (BTRFS_BLOCK_GROUP_RAID1)) {
num_stripes = min_t(u64, 2, num_stripes = min_t(u64, 2,
btrfs_super_num_devices(&info->super_copy)); extent_root->fs_info->fs_devices->open_devices);
if (num_stripes < 2) if (num_stripes < 2)
return -ENOSPC; return -ENOSPC;
min_stripes = 2; min_stripes = 2;
} }
if (type & (BTRFS_BLOCK_GROUP_RAID10)) { if (type & (BTRFS_BLOCK_GROUP_RAID10)) {
num_stripes = btrfs_super_num_devices(&info->super_copy); num_stripes = extent_root->fs_info->fs_devices->open_devices;
if (num_stripes < 4) if (num_stripes < 4)
return -ENOSPC; return -ENOSPC;
num_stripes &= ~(u32)1; num_stripes &= ~(u32)1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册