diff options
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r-- | fs/btrfs/extent-tree.c | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 4bd04f3fa8bb..4c7c9467f224 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -7402,6 +7402,93 @@ out: | |||
7402 | } | 7402 | } |
7403 | #endif | 7403 | #endif |
7404 | 7404 | ||
7405 | /* | ||
7406 | * checks to see if its even possible to relocate this block group. | ||
7407 | * | ||
7408 | * @return - -1 if it's not a good idea to relocate this block group, 0 if its | ||
7409 | * ok to go ahead and try. | ||
7410 | */ | ||
7411 | int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) | ||
7412 | { | ||
7413 | struct btrfs_block_group_cache *block_group; | ||
7414 | struct btrfs_space_info *space_info; | ||
7415 | struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices; | ||
7416 | struct btrfs_device *device; | ||
7417 | int full = 0; | ||
7418 | int ret = 0; | ||
7419 | |||
7420 | block_group = btrfs_lookup_block_group(root->fs_info, bytenr); | ||
7421 | |||
7422 | /* odd, couldn't find the block group, leave it alone */ | ||
7423 | if (!block_group) | ||
7424 | return -1; | ||
7425 | |||
7426 | /* no bytes used, we're good */ | ||
7427 | if (!btrfs_block_group_used(&block_group->item)) | ||
7428 | goto out; | ||
7429 | |||
7430 | space_info = block_group->space_info; | ||
7431 | spin_lock(&space_info->lock); | ||
7432 | |||
7433 | full = space_info->full; | ||
7434 | |||
7435 | /* | ||
7436 | * if this is the last block group we have in this space, we can't | ||
7437 | * relocate it. | ||
7438 | */ | ||
7439 | if (space_info->total_bytes == block_group->key.offset) { | ||
7440 | ret = -1; | ||
7441 | spin_unlock(&space_info->lock); | ||
7442 | goto out; | ||
7443 | } | ||
7444 | |||
7445 | /* | ||
7446 | * need to make sure we have room in the space to handle all of the | ||
7447 | * extents from this block group. If we can, we're good | ||
7448 | */ | ||
7449 | if (space_info->bytes_used + space_info->bytes_reserved + | ||
7450 | space_info->bytes_pinned + space_info->bytes_readonly + | ||
7451 | btrfs_block_group_used(&block_group->item) < | ||
7452 | space_info->total_bytes) { | ||
7453 | spin_unlock(&space_info->lock); | ||
7454 | goto out; | ||
7455 | } | ||
7456 | spin_unlock(&space_info->lock); | ||
7457 | |||
7458 | /* | ||
7459 | * ok we don't have enough space, but maybe we have free space on our | ||
7460 | * devices to allocate new chunks for relocation, so loop through our | ||
7461 | * alloc devices and guess if we have enough space. However, if we | ||
7462 | * were marked as full, then we know there aren't enough chunks, and we | ||
7463 | * can just return. | ||
7464 | */ | ||
7465 | ret = -1; | ||
7466 | if (full) | ||
7467 | goto out; | ||
7468 | |||
7469 | mutex_lock(&root->fs_info->chunk_mutex); | ||
7470 | list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) { | ||
7471 | u64 min_free = btrfs_block_group_used(&block_group->item); | ||
7472 | u64 dev_offset, max_avail; | ||
7473 | |||
7474 | /* | ||
7475 | * check to make sure we can actually find a chunk with enough | ||
7476 | * space to fit our block group in. | ||
7477 | */ | ||
7478 | if (device->total_bytes > device->bytes_used + min_free) { | ||
7479 | ret = find_free_dev_extent(NULL, device, min_free, | ||
7480 | &dev_offset, &max_avail); | ||
7481 | if (!ret) | ||
7482 | break; | ||
7483 | ret = -1; | ||
7484 | } | ||
7485 | } | ||
7486 | mutex_unlock(&root->fs_info->chunk_mutex); | ||
7487 | out: | ||
7488 | btrfs_put_block_group(block_group); | ||
7489 | return ret; | ||
7490 | } | ||
7491 | |||
7405 | static int find_first_block_group(struct btrfs_root *root, | 7492 | static int find_first_block_group(struct btrfs_root *root, |
7406 | struct btrfs_path *path, struct btrfs_key *key) | 7493 | struct btrfs_path *path, struct btrfs_key *key) |
7407 | { | 7494 | { |