btrfs: reserve extra space during evict
[sfrench/cifs-2.6.git] / fs / btrfs / inode.c
index a443645cf8153f57f902e4f7ff686b97f54682ec..3f180b857e202bc628a65ff0955606f880193de5 100644 (file)
@@ -5293,13 +5293,15 @@ static struct btrfs_trans_handle *evict_refill_and_join(struct btrfs_root *root,
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv;
+       u64 delayed_refs_extra = btrfs_calc_trans_metadata_size(fs_info, 1);
        int failures = 0;
 
        for (;;) {
                struct btrfs_trans_handle *trans;
                int ret;
 
-               ret = btrfs_block_rsv_refill(root, rsv, rsv->size,
+               ret = btrfs_block_rsv_refill(root, rsv,
+                                            rsv->size + delayed_refs_extra,
                                             BTRFS_RESERVE_FLUSH_LIMIT);
 
                if (ret && ++failures > 2) {
@@ -5308,9 +5310,28 @@ static struct btrfs_trans_handle *evict_refill_and_join(struct btrfs_root *root,
                        return ERR_PTR(-ENOSPC);
                }
 
+               /*
+                * Evict can generate a large amount of delayed refs without
+                * having a way to add space back since we exhaust our temporary
+                * block rsv.  We aren't allowed to do FLUSH_ALL in this case
+                * because we could deadlock with so many things in the flushing
+                * code, so we have to try and hold some extra space to
+                * compensate for our delayed ref generation.  If we can't get
+                * that space then we need see if we can steal our minimum from
+                * the global reserve.  We will be ratelimited by the amount of
+                * space we have for the delayed refs rsv, so we'll end up
+                * committing and trying again.
+                */
                trans = btrfs_join_transaction(root);
-               if (IS_ERR(trans) || !ret)
+               if (IS_ERR(trans) || !ret) {
+                       if (!IS_ERR(trans)) {
+                               trans->block_rsv = &fs_info->trans_block_rsv;
+                               trans->bytes_reserved = delayed_refs_extra;
+                               btrfs_block_rsv_migrate(rsv, trans->block_rsv,
+                                                       delayed_refs_extra, 1);
+                       }
                        return trans;
+               }
 
                /*
                 * Try to steal from the global reserve if there is space for