diff options
author | Josef Bacik <jbacik@fb.com> | 2014-09-18 11:20:02 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-09-22 20:13:21 -0400 |
commit | 47ab2a6c689913db23ccae38349714edf8365e0a (patch) | |
tree | 5d1001d192e6ccd0159d59e31d705b38e17c09ef /fs/btrfs/volumes.c | |
parent | 8407f553268a4611f2542ed90677f0edfaa2c9c4 (diff) |
Btrfs: remove empty block groups automatically
One problem that has plagued us is that a user will use up all of his space with
data, remove a bunch of that data, and then try to create a bunch of small files
and run out of space. This happens because all the chunks were allocated for
data since the metadata requirements were so low. But now there's a bunch of
empty data block groups and not enough metadata space to do anything. This
patch solves this problem by automatically deleting empty block groups. If we
notice the used count go down to 0 when deleting or on mount notice that a block
group has a used count of 0 then we will queue it to be deleted.
When the cleaner thread runs we will double check to make sure the block group
is still empty and then we will delete it. This patch has the side effect of no
longer having a bunch of BUG_ON()'s in the chunk delete code, which will be
helpful for both this and relocate. Thanks,
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r-- | fs/btrfs/volumes.c | 115 |
1 files changed, 76 insertions, 39 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 63e632746d8a..f27c0f7c387e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -2568,58 +2568,49 @@ static int btrfs_del_sys_chunk(struct btrfs_root *root, u64 chunk_objectid, u64 | |||
2568 | return ret; | 2568 | return ret; |
2569 | } | 2569 | } |
2570 | 2570 | ||
2571 | static int btrfs_relocate_chunk(struct btrfs_root *root, | 2571 | int btrfs_remove_chunk(struct btrfs_trans_handle *trans, |
2572 | u64 chunk_tree, u64 chunk_objectid, | 2572 | struct btrfs_root *root, u64 chunk_offset) |
2573 | u64 chunk_offset) | ||
2574 | { | 2573 | { |
2575 | struct extent_map_tree *em_tree; | 2574 | struct extent_map_tree *em_tree; |
2576 | struct btrfs_root *extent_root; | ||
2577 | struct btrfs_trans_handle *trans; | ||
2578 | struct btrfs_device *device; | ||
2579 | struct extent_map *em; | 2575 | struct extent_map *em; |
2576 | struct btrfs_root *extent_root = root->fs_info->extent_root; | ||
2580 | struct map_lookup *map; | 2577 | struct map_lookup *map; |
2581 | u64 dev_extent_len = 0; | 2578 | u64 dev_extent_len = 0; |
2582 | int ret; | 2579 | u64 chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; |
2583 | int i; | 2580 | u64 chunk_tree = root->fs_info->chunk_root->objectid; |
2581 | int i, ret = 0; | ||
2584 | 2582 | ||
2583 | /* Just in case */ | ||
2585 | root = root->fs_info->chunk_root; | 2584 | root = root->fs_info->chunk_root; |
2586 | extent_root = root->fs_info->extent_root; | ||
2587 | em_tree = &root->fs_info->mapping_tree.map_tree; | 2585 | em_tree = &root->fs_info->mapping_tree.map_tree; |
2588 | 2586 | ||
2589 | ret = btrfs_can_relocate(extent_root, chunk_offset); | ||
2590 | if (ret) | ||
2591 | return -ENOSPC; | ||
2592 | |||
2593 | /* step one, relocate all the extents inside this chunk */ | ||
2594 | ret = btrfs_relocate_block_group(extent_root, chunk_offset); | ||
2595 | if (ret) | ||
2596 | return ret; | ||
2597 | |||
2598 | trans = btrfs_start_transaction(root, 0); | ||
2599 | if (IS_ERR(trans)) { | ||
2600 | ret = PTR_ERR(trans); | ||
2601 | btrfs_std_error(root->fs_info, ret); | ||
2602 | return ret; | ||
2603 | } | ||
2604 | |||
2605 | /* | ||
2606 | * step two, delete the device extents and the | ||
2607 | * chunk tree entries | ||
2608 | */ | ||
2609 | read_lock(&em_tree->lock); | 2587 | read_lock(&em_tree->lock); |
2610 | em = lookup_extent_mapping(em_tree, chunk_offset, 1); | 2588 | em = lookup_extent_mapping(em_tree, chunk_offset, 1); |
2611 | read_unlock(&em_tree->lock); | 2589 | read_unlock(&em_tree->lock); |
2612 | 2590 | ||
2613 | BUG_ON(!em || em->start > chunk_offset || | 2591 | if (!em || em->start > chunk_offset || |
2614 | em->start + em->len < chunk_offset); | 2592 | em->start + em->len < chunk_offset) { |
2593 | /* | ||
2594 | * This is a logic error, but we don't want to just rely on the | ||
2595 | * user having built with ASSERT enabled, so if ASSERT doens't | ||
2596 | * do anything we still error out. | ||
2597 | */ | ||
2598 | ASSERT(0); | ||
2599 | if (em) | ||
2600 | free_extent_map(em); | ||
2601 | return -EINVAL; | ||
2602 | } | ||
2615 | map = (struct map_lookup *)em->bdev; | 2603 | map = (struct map_lookup *)em->bdev; |
2616 | 2604 | ||
2617 | for (i = 0; i < map->num_stripes; i++) { | 2605 | for (i = 0; i < map->num_stripes; i++) { |
2618 | device = map->stripes[i].dev; | 2606 | struct btrfs_device *device = map->stripes[i].dev; |
2619 | ret = btrfs_free_dev_extent(trans, device, | 2607 | ret = btrfs_free_dev_extent(trans, device, |
2620 | map->stripes[i].physical, | 2608 | map->stripes[i].physical, |
2621 | &dev_extent_len); | 2609 | &dev_extent_len); |
2622 | BUG_ON(ret); | 2610 | if (ret) { |
2611 | btrfs_abort_transaction(trans, root, ret); | ||
2612 | goto out; | ||
2613 | } | ||
2623 | 2614 | ||
2624 | if (device->bytes_used > 0) { | 2615 | if (device->bytes_used > 0) { |
2625 | lock_chunks(root); | 2616 | lock_chunks(root); |
@@ -2634,23 +2625,34 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, | |||
2634 | 2625 | ||
2635 | if (map->stripes[i].dev) { | 2626 | if (map->stripes[i].dev) { |
2636 | ret = btrfs_update_device(trans, map->stripes[i].dev); | 2627 | ret = btrfs_update_device(trans, map->stripes[i].dev); |
2637 | BUG_ON(ret); | 2628 | if (ret) { |
2629 | btrfs_abort_transaction(trans, root, ret); | ||
2630 | goto out; | ||
2631 | } | ||
2638 | } | 2632 | } |
2639 | } | 2633 | } |
2640 | ret = btrfs_free_chunk(trans, root, chunk_tree, chunk_objectid, | 2634 | ret = btrfs_free_chunk(trans, root, chunk_tree, chunk_objectid, |
2641 | chunk_offset); | 2635 | chunk_offset); |
2642 | 2636 | if (ret) { | |
2643 | BUG_ON(ret); | 2637 | btrfs_abort_transaction(trans, root, ret); |
2638 | goto out; | ||
2639 | } | ||
2644 | 2640 | ||
2645 | trace_btrfs_chunk_free(root, map, chunk_offset, em->len); | 2641 | trace_btrfs_chunk_free(root, map, chunk_offset, em->len); |
2646 | 2642 | ||
2647 | if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) { | 2643 | if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) { |
2648 | ret = btrfs_del_sys_chunk(root, chunk_objectid, chunk_offset); | 2644 | ret = btrfs_del_sys_chunk(root, chunk_objectid, chunk_offset); |
2649 | BUG_ON(ret); | 2645 | if (ret) { |
2646 | btrfs_abort_transaction(trans, root, ret); | ||
2647 | goto out; | ||
2648 | } | ||
2650 | } | 2649 | } |
2651 | 2650 | ||
2652 | ret = btrfs_remove_block_group(trans, extent_root, chunk_offset); | 2651 | ret = btrfs_remove_block_group(trans, extent_root, chunk_offset); |
2653 | BUG_ON(ret); | 2652 | if (ret) { |
2653 | btrfs_abort_transaction(trans, extent_root, ret); | ||
2654 | goto out; | ||
2655 | } | ||
2654 | 2656 | ||
2655 | write_lock(&em_tree->lock); | 2657 | write_lock(&em_tree->lock); |
2656 | remove_extent_mapping(em_tree, em); | 2658 | remove_extent_mapping(em_tree, em); |
@@ -2658,11 +2660,46 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, | |||
2658 | 2660 | ||
2659 | /* once for the tree */ | 2661 | /* once for the tree */ |
2660 | free_extent_map(em); | 2662 | free_extent_map(em); |
2663 | out: | ||
2661 | /* once for us */ | 2664 | /* once for us */ |
2662 | free_extent_map(em); | 2665 | free_extent_map(em); |
2666 | return ret; | ||
2667 | } | ||
2668 | |||
2669 | static int btrfs_relocate_chunk(struct btrfs_root *root, | ||
2670 | u64 chunk_tree, u64 chunk_objectid, | ||
2671 | u64 chunk_offset) | ||
2672 | { | ||
2673 | struct btrfs_root *extent_root; | ||
2674 | struct btrfs_trans_handle *trans; | ||
2675 | int ret; | ||
2676 | |||
2677 | root = root->fs_info->chunk_root; | ||
2678 | extent_root = root->fs_info->extent_root; | ||
2679 | |||
2680 | ret = btrfs_can_relocate(extent_root, chunk_offset); | ||
2681 | if (ret) | ||
2682 | return -ENOSPC; | ||
2683 | |||
2684 | /* step one, relocate all the extents inside this chunk */ | ||
2685 | ret = btrfs_relocate_block_group(extent_root, chunk_offset); | ||
2686 | if (ret) | ||
2687 | return ret; | ||
2663 | 2688 | ||
2689 | trans = btrfs_start_transaction(root, 0); | ||
2690 | if (IS_ERR(trans)) { | ||
2691 | ret = PTR_ERR(trans); | ||
2692 | btrfs_std_error(root->fs_info, ret); | ||
2693 | return ret; | ||
2694 | } | ||
2695 | |||
2696 | /* | ||
2697 | * step two, delete the device extents and the | ||
2698 | * chunk tree entries | ||
2699 | */ | ||
2700 | ret = btrfs_remove_chunk(trans, root, chunk_offset); | ||
2664 | btrfs_end_transaction(trans, root); | 2701 | btrfs_end_transaction(trans, root); |
2665 | return 0; | 2702 | return ret; |
2666 | } | 2703 | } |
2667 | 2704 | ||
2668 | static int btrfs_relocate_sys_chunks(struct btrfs_root *root) | 2705 | static int btrfs_relocate_sys_chunks(struct btrfs_root *root) |