aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2015-11-13 18:57:16 -0500
committerChris Mason <clm@fb.com>2015-11-25 08:19:50 -0500
commit8eab77ff167b62760d878f1d19312eb9f7d4c176 (patch)
tree5643956c3953881b148c58616591c3e4948ea687
parent89b6c8d1e4a178a347b94f339b959f02710e7060 (diff)
Btrfs: use global reserve when deleting unused block group after ENOSPC
It's possible to reach a state where the cleaner kthread isn't able to start a transaction to delete an unused block group due to lack of enough free metadata space and due to lack of unallocated device space to allocate a new metadata block group as well. If this happens try to use space from the global block group reserve just like we do for unlink operations, so that we don't reach a permanent state where starting a transaction for filesystem operations (file creation, renames, etc) keeps failing with -ENOSPC. Such an unfortunate state was observed on a machine where over a dozen unused data block groups existed and the cleaner kthread was failing to delete them due to ENOSPC error when attempting to start a transaction, and even running balance with a -dusage=0 filter failed with ENOSPC as well. Also unmounting and mounting again the filesystem didn't help. Allowing the cleaner kthread to use the global block reserve to delete the unused data block groups fixed the problem. Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Jeff Mahoney <jeffm@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/extent-tree.c14
-rw-r--r--fs/btrfs/inode.c24
-rw-r--r--fs/btrfs/transaction.c32
-rw-r--r--fs/btrfs/transaction.h4
-rw-r--r--fs/btrfs/volumes.c2
6 files changed, 52 insertions, 26 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index a2e73f6053a8..1573be6f9518 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3479,6 +3479,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
3479 struct btrfs_root *root, u64 bytes_used, 3479 struct btrfs_root *root, u64 bytes_used,
3480 u64 type, u64 chunk_objectid, u64 chunk_offset, 3480 u64 type, u64 chunk_objectid, u64 chunk_offset,
3481 u64 size); 3481 u64 size);
3482struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
3483 struct btrfs_fs_info *fs_info);
3482int btrfs_remove_block_group(struct btrfs_trans_handle *trans, 3484int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
3483 struct btrfs_root *root, u64 group_start, 3485 struct btrfs_root *root, u64 group_start,
3484 struct extent_map *em); 3486 struct extent_map *em);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index acf3ed11cfb6..78200932c1cf 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -10256,6 +10256,17 @@ out:
10256 return ret; 10256 return ret;
10257} 10257}
10258 10258
10259struct btrfs_trans_handle *
10260btrfs_start_trans_remove_block_group(struct btrfs_fs_info *fs_info)
10261{
10262 /*
10263 * 1 unit for adding the free space inode's orphan (located in the tree
10264 * of tree roots).
10265 */
10266 return btrfs_start_transaction_fallback_global_rsv(fs_info->extent_root,
10267 1, 1);
10268}
10269
10259/* 10270/*
10260 * Process the unused_bgs list and remove any that don't have any allocated 10271 * Process the unused_bgs list and remove any that don't have any allocated
10261 * space inside of them. 10272 * space inside of them.
@@ -10322,8 +10333,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
10322 * Want to do this before we do anything else so we can recover 10333 * Want to do this before we do anything else so we can recover
10323 * properly if we fail to join the transaction. 10334 * properly if we fail to join the transaction.
10324 */ 10335 */
10325 /* 1 for btrfs_orphan_reserve_metadata() */ 10336 trans = btrfs_start_trans_remove_block_group(fs_info);
10326 trans = btrfs_start_transaction(root, 1);
10327 if (IS_ERR(trans)) { 10337 if (IS_ERR(trans)) {
10328 btrfs_dec_block_group_ro(root, block_group); 10338 btrfs_dec_block_group_ro(root, block_group);
10329 ret = PTR_ERR(trans); 10339 ret = PTR_ERR(trans);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 6e93349d8aa2..f82d1f4460dd 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4046,9 +4046,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
4046 */ 4046 */
4047static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir) 4047static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
4048{ 4048{
4049 struct btrfs_trans_handle *trans;
4050 struct btrfs_root *root = BTRFS_I(dir)->root; 4049 struct btrfs_root *root = BTRFS_I(dir)->root;
4051 int ret;
4052 4050
4053 /* 4051 /*
4054 * 1 for the possible orphan item 4052 * 1 for the possible orphan item
@@ -4057,27 +4055,7 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
4057 * 1 for the inode ref 4055 * 1 for the inode ref
4058 * 1 for the inode 4056 * 1 for the inode
4059 */ 4057 */
4060 trans = btrfs_start_transaction(root, 5); 4058 return btrfs_start_transaction_fallback_global_rsv(root, 5, 5);
4061 if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
4062 return trans;
4063
4064 if (PTR_ERR(trans) == -ENOSPC) {
4065 u64 num_bytes = btrfs_calc_trans_metadata_size(root, 5);
4066
4067 trans = btrfs_start_transaction(root, 0);
4068 if (IS_ERR(trans))
4069 return trans;
4070 ret = btrfs_cond_migrate_bytes(root->fs_info,
4071 &root->fs_info->trans_block_rsv,
4072 num_bytes, 5);
4073 if (ret) {
4074 btrfs_end_transaction(trans, root);
4075 return ERR_PTR(ret);
4076 }
4077 trans->block_rsv = &root->fs_info->trans_block_rsv;
4078 trans->bytes_reserved = num_bytes;
4079 }
4080 return trans;
4081} 4059}
4082 4060
4083static int btrfs_unlink(struct inode *dir, struct dentry *dentry) 4061static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 418c6a2ad7d8..3367a3c6f214 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -592,6 +592,38 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
592 return start_transaction(root, num_items, TRANS_START, 592 return start_transaction(root, num_items, TRANS_START,
593 BTRFS_RESERVE_FLUSH_ALL); 593 BTRFS_RESERVE_FLUSH_ALL);
594} 594}
595struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
596 struct btrfs_root *root,
597 unsigned int num_items,
598 int min_factor)
599{
600 struct btrfs_trans_handle *trans;
601 u64 num_bytes;
602 int ret;
603
604 trans = btrfs_start_transaction(root, num_items);
605 if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
606 return trans;
607
608 trans = btrfs_start_transaction(root, 0);
609 if (IS_ERR(trans))
610 return trans;
611
612 num_bytes = btrfs_calc_trans_metadata_size(root, num_items);
613 ret = btrfs_cond_migrate_bytes(root->fs_info,
614 &root->fs_info->trans_block_rsv,
615 num_bytes,
616 min_factor);
617 if (ret) {
618 btrfs_end_transaction(trans, root);
619 return ERR_PTR(ret);
620 }
621
622 trans->block_rsv = &root->fs_info->trans_block_rsv;
623 trans->bytes_reserved = num_bytes;
624
625 return trans;
626}
595 627
596struct btrfs_trans_handle *btrfs_start_transaction_lflush( 628struct btrfs_trans_handle *btrfs_start_transaction_lflush(
597 struct btrfs_root *root, 629 struct btrfs_root *root,
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index b05b2f64d913..0da21ca9b3fb 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -185,6 +185,10 @@ int btrfs_end_transaction(struct btrfs_trans_handle *trans,
185 struct btrfs_root *root); 185 struct btrfs_root *root);
186struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, 186struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
187 unsigned int num_items); 187 unsigned int num_items);
188struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
189 struct btrfs_root *root,
190 unsigned int num_items,
191 int min_factor);
188struct btrfs_trans_handle *btrfs_start_transaction_lflush( 192struct btrfs_trans_handle *btrfs_start_transaction_lflush(
189 struct btrfs_root *root, 193 struct btrfs_root *root,
190 unsigned int num_items); 194 unsigned int num_items);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index d198dd3360d3..e0bd364e958d 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -2853,7 +2853,7 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, u64 chunk_offset)
2853 if (ret) 2853 if (ret)
2854 return ret; 2854 return ret;
2855 2855
2856 trans = btrfs_start_transaction(root, 0); 2856 trans = btrfs_start_trans_remove_block_group(root->fs_info);
2857 if (IS_ERR(trans)) { 2857 if (IS_ERR(trans)) {
2858 ret = PTR_ERR(trans); 2858 ret = PTR_ERR(trans);
2859 btrfs_std_error(root->fs_info, ret, NULL); 2859 btrfs_std_error(root->fs_info, ret, NULL);