]> git.hungrycats.org Git - linux/commitdiff
Btrfs: use global reserve when deleting unused block group after ENOSPC
authorFilipe Manana <fdmanana@suse.com>
Thu, 12 Nov 2015 17:49:45 +0000 (17:49 +0000)
committerZygo Blaxell <zblaxell@serenity.furryterror.org>
Sat, 14 Nov 2015 02:59:59 +0000 (21:59 -0500)
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>
(cherry picked from commit 59b6a0d1cc59b739915d7f3d5c03bad56ec4ceb8)

Conflicts:
fs/btrfs/transaction.h

(cherry picked from commit b5faf3dee5856d3b4196417519ef4a37cf98ec57)

fs/btrfs/ctree.h
fs/btrfs/extent-tree.c
fs/btrfs/inode.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/volumes.c

index 82288c63f413ed9351bbb15aefae667b829a1f34..b901b7db69fd01ecf5f40e67ceff6e6dac406d56 100644 (file)
@@ -3481,6 +3481,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, u64 bytes_used,
                           u64 type, u64 chunk_objectid, u64 chunk_offset,
                           u64 size);
+struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
+                               struct btrfs_fs_info *fs_info);
 int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root, u64 group_start,
                             struct extent_map *em);
index aab8d09a58739bce3e44d356d4cf0110e11064e8..24144ae3bc7830b677a1e87f5cbe7ac104d24228 100644 (file)
@@ -10126,6 +10126,17 @@ out:
        return ret;
 }
 
+struct btrfs_trans_handle *
+btrfs_start_trans_remove_block_group(struct btrfs_fs_info *fs_info)
+{
+       /*
+        * 1 unit for adding the free space inode's orphan (located in the tree
+        * of tree roots).
+        */
+       return btrfs_start_transaction_fallback_global_rsv(fs_info->extent_root,
+                                                          1, 1);
+}
+
 /*
  * Process the unused_bgs list and remove any that don't have any allocated
  * space inside of them.
@@ -10191,8 +10202,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
                 * Want to do this before we do anything else so we can recover
                 * properly if we fail to join the transaction.
                 */
-               /* 1 for btrfs_orphan_reserve_metadata() */
-               trans = btrfs_start_transaction(root, 1);
+               trans = btrfs_start_trans_remove_block_group(fs_info);
                if (IS_ERR(trans)) {
                        btrfs_set_block_group_rw(root, block_group);
                        ret = PTR_ERR(trans);
index de66e0997b4eaf50fa3ad3b63b10f798a6a03fed..a9500fb3d50d8eac5f6fa813adf5aac57ae23798 100644 (file)
@@ -3991,9 +3991,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
  */
 static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
 {
-       struct btrfs_trans_handle *trans;
        struct btrfs_root *root = BTRFS_I(dir)->root;
-       int ret;
 
        /*
         * 1 for the possible orphan item
@@ -4002,27 +4000,7 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
         * 1 for the inode ref
         * 1 for the inode
         */
-       trans = btrfs_start_transaction(root, 5);
-       if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
-               return trans;
-
-       if (PTR_ERR(trans) == -ENOSPC) {
-               u64 num_bytes = btrfs_calc_trans_metadata_size(root, 5);
-
-               trans = btrfs_start_transaction(root, 0);
-               if (IS_ERR(trans))
-                       return trans;
-               ret = btrfs_cond_migrate_bytes(root->fs_info,
-                                              &root->fs_info->trans_block_rsv,
-                                              num_bytes, 5);
-               if (ret) {
-                       btrfs_end_transaction(trans, root);
-                       return ERR_PTR(ret);
-               }
-               trans->block_rsv = &root->fs_info->trans_block_rsv;
-               trans->bytes_reserved = num_bytes;
-       }
-       return trans;
+       return btrfs_start_transaction_fallback_global_rsv(root, 5, 5);
 }
 
 static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
index da3f70969c8c1ef957698466ed6e6a6b338c9ca1..39200d836e4469bb29ad30baf21087e87ea5790e 100644 (file)
@@ -607,6 +607,38 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
        return start_transaction(root, num_items, TRANS_START,
                                 BTRFS_RESERVE_FLUSH_ALL);
 }
+struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
+                                       struct btrfs_root *root,
+                                       unsigned int num_items,
+                                       int min_factor)
+{
+       struct btrfs_trans_handle *trans;
+       u64 num_bytes;
+       int ret;
+
+       trans = btrfs_start_transaction(root, num_items);
+       if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
+               return trans;
+
+       trans = btrfs_start_transaction(root, 0);
+       if (IS_ERR(trans))
+               return trans;
+
+       num_bytes = btrfs_calc_trans_metadata_size(root, num_items);
+       ret = btrfs_cond_migrate_bytes(root->fs_info,
+                                      &root->fs_info->trans_block_rsv,
+                                      num_bytes,
+                                      min_factor);
+       if (ret) {
+               btrfs_end_transaction(trans, root);
+               return ERR_PTR(ret);
+       }
+
+       trans->block_rsv = &root->fs_info->trans_block_rsv;
+       trans->bytes_reserved = num_bytes;
+
+       return trans;
+}
 
 struct btrfs_trans_handle *btrfs_start_transaction_lflush(
                                        struct btrfs_root *root, int num_items)
index 026cae435c28fa76e09857a71a411566c50911e8..a20e8b5ed81ae8f150640b1e1c527f463a79ec79 100644 (file)
@@ -185,7 +185,11 @@ static inline void btrfs_clear_skip_qgroup(struct btrfs_trans_handle *trans)
 int btrfs_end_transaction(struct btrfs_trans_handle *trans,
                          struct btrfs_root *root);
 struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
-                                                  int num_items);
+                                                  unsigned int num_items);
+struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
+                                       struct btrfs_root *root,
+                                       unsigned int num_items,
+                                       int min_factor);
 struct btrfs_trans_handle *btrfs_start_transaction_lflush(
                                        struct btrfs_root *root, int num_items);
 struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root);
index 062b4dbc5dda8fed067a870e02b34e8bdf1d7bd0..e62881879f00f26d8c2b965920c983d070dd507d 100644 (file)
@@ -2789,7 +2789,7 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
        if (ret)
                return ret;
 
-       trans = btrfs_start_transaction(root, 0);
+       trans = btrfs_start_trans_remove_block_group(root->fs_info);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
                btrfs_std_error(root->fs_info, ret);