Btrfs: do not needlessly restart the transaction for enospc
authorJosef Bacik <jbacik@fusionio.com>
Mon, 27 Aug 2012 21:48:15 +0000 (17:48 -0400)
committerChris Mason <chris.mason@fusionio.com>
Mon, 1 Oct 2012 19:19:04 +0000 (15:19 -0400)
We will stop and restart a transaction every time we move to a different leaf
when truncating a file.  This is for enospc reasons, but really we could
probably get away with doing this a little better by actually working until we
hit an ENOSPC.  So add a ->failfast flag to the block_rsv and set it when we do
truncates which will fail as soon as the block rsv runs out of space, and then
at that point we can stop and restart the transaction and refill the block rsv
and carry on.  This will make rm'ing of a file with lots of extents a bit
faster.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c
fs/btrfs/inode.c

index 4b81ea3fa1b2afe6dbe100ff870b660b006a4490..81c772b5dc8ec43a8d5bc040c26854ae1742c800 100644 (file)
@@ -1034,6 +1034,7 @@ struct btrfs_block_rsv {
        struct btrfs_space_info *space_info;
        spinlock_t lock;
        unsigned int full;
+       unsigned int failfast;
 };
 
 /*
index 62fc92f2b9d7e99b04a0fe4bf8a8a39e78723c72..df2f17b0ac5d353fd7058ff2e45ed4de37cdb936 100644 (file)
@@ -6308,7 +6308,7 @@ use_block_rsv(struct btrfs_trans_handle *trans,
        ret = block_rsv_use_bytes(block_rsv, blocksize);
        if (!ret)
                return block_rsv;
-       if (ret) {
+       if (ret && !block_rsv->failfast) {
                static DEFINE_RATELIMIT_STATE(_rs,
                                DEFAULT_RATELIMIT_INTERVAL,
                                /*DEFAULT_RATELIMIT_BURST*/ 2);
index 1b99fe8a129d8c0132e2b042618fa3dc37a0caf2..ca4fa05171abec4e271040b87437b2cc58f21826 100644 (file)
@@ -3448,12 +3448,6 @@ delete:
 
                if (path->slots[0] == 0 ||
                    path->slots[0] != pending_del_slot) {
-                       if (root->ref_cows &&
-                           BTRFS_I(inode)->location.objectid !=
-                                               BTRFS_FREE_INO_OBJECTID) {
-                               err = -EAGAIN;
-                               goto out;
-                       }
                        if (pending_del_nr) {
                                ret = btrfs_del_items(trans, root, path,
                                                pending_del_slot,
@@ -3826,6 +3820,7 @@ void btrfs_evict_inode(struct inode *inode)
                goto no_delete;
        }
        rsv->size = min_size;
+       rsv->failfast = 1;
        global_rsv = &root->fs_info->global_block_rsv;
 
        btrfs_i_size_write(inode, 0);
@@ -3870,7 +3865,7 @@ void btrfs_evict_inode(struct inode *inode)
                trans->block_rsv = rsv;
 
                ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0);
-               if (ret != -EAGAIN)
+               if (ret != -ENOSPC)
                        break;
 
                nr = trans->blocks_used;
@@ -6852,6 +6847,7 @@ static int btrfs_truncate(struct inode *inode)
        if (!rsv)
                return -ENOMEM;
        rsv->size = min_size;
+       rsv->failfast = 1;
 
        /*
         * 1 for the truncate slack space
@@ -6905,37 +6901,13 @@ static int btrfs_truncate(struct inode *inode)
         * safe.
         */
        set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags);
+       trans->block_rsv = rsv;
 
        while (1) {
-               ret = btrfs_block_rsv_refill(root, rsv, min_size);
-               if (ret) {
-                       /*
-                        * This can only happen with the original transaction we
-                        * started above, every other time we shouldn't have a
-                        * transaction started yet.
-                        */
-                       if (ret == -EAGAIN)
-                               goto end_trans;
-                       err = ret;
-                       break;
-               }
-
-               if (!trans) {
-                       /* Just need the 1 for updating the inode */
-                       trans = btrfs_start_transaction(root, 1);
-                       if (IS_ERR(trans)) {
-                               ret = err = PTR_ERR(trans);
-                               trans = NULL;
-                               break;
-                       }
-               }
-
-               trans->block_rsv = rsv;
-
                ret = btrfs_truncate_inode_items(trans, root, inode,
                                                 inode->i_size,
                                                 BTRFS_EXTENT_DATA_KEY);
-               if (ret != -EAGAIN) {
+               if (ret != -ENOSPC) {
                        err = ret;
                        break;
                }
@@ -6946,11 +6918,22 @@ static int btrfs_truncate(struct inode *inode)
                        err = ret;
                        break;
                }
-end_trans:
+
                nr = trans->blocks_used;
                btrfs_end_transaction(trans, root);
-               trans = NULL;
                btrfs_btree_balance_dirty(root, nr);
+
+               trans = btrfs_start_transaction(root, 2);
+               if (IS_ERR(trans)) {
+                       ret = err = PTR_ERR(trans);
+                       trans = NULL;
+                       break;
+               }
+
+               ret = btrfs_block_rsv_migrate(&root->fs_info->trans_block_rsv,
+                                             rsv, min_size);
+               BUG_ON(ret);    /* shouldn't happen */
+               trans->block_rsv = rsv;
        }
 
        if (ret == 0 && inode->i_nlink > 0) {