btrfs: introduce delayed_refs_rsv
[sfrench/cifs-2.6.git] / fs / btrfs / transaction.c
index 67e84939b758e5095c4e140104c98d5e7304e91c..e18eb75e6fa36ca3dba738d116c38e84dd3a8dae 100644 (file)
@@ -454,7 +454,7 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
                  bool enforce_qgroups)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
                  bool enforce_qgroups)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
-
+       struct btrfs_block_rsv *delayed_refs_rsv = &fs_info->delayed_refs_rsv;
        struct btrfs_trans_handle *h;
        struct btrfs_transaction *cur_trans;
        u64 num_bytes = 0;
        struct btrfs_trans_handle *h;
        struct btrfs_transaction *cur_trans;
        u64 num_bytes = 0;
@@ -483,13 +483,28 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
         * the appropriate flushing if need be.
         */
        if (num_items && root != fs_info->chunk_root) {
         * the appropriate flushing if need be.
         */
        if (num_items && root != fs_info->chunk_root) {
+               struct btrfs_block_rsv *rsv = &fs_info->trans_block_rsv;
+               u64 delayed_refs_bytes = 0;
+
                qgroup_reserved = num_items * fs_info->nodesize;
                ret = btrfs_qgroup_reserve_meta_pertrans(root, qgroup_reserved,
                                enforce_qgroups);
                if (ret)
                        return ERR_PTR(ret);
 
                qgroup_reserved = num_items * fs_info->nodesize;
                ret = btrfs_qgroup_reserve_meta_pertrans(root, qgroup_reserved,
                                enforce_qgroups);
                if (ret)
                        return ERR_PTR(ret);
 
+               /*
+                * We want to reserve all the bytes we may need all at once, so
+                * we only do 1 enospc flushing cycle per transaction start.  We
+                * accomplish this by simply assuming we'll do 2 x num_items
+                * worth of delayed refs updates in this trans handle, and
+                * refill that amount for whatever is missing in the reserve.
+                */
                num_bytes = btrfs_calc_trans_metadata_size(fs_info, num_items);
                num_bytes = btrfs_calc_trans_metadata_size(fs_info, num_items);
+               if (delayed_refs_rsv->full == 0) {
+                       delayed_refs_bytes = num_bytes;
+                       num_bytes <<= 1;
+               }
+
                /*
                 * Do the reservation for the relocation root creation
                 */
                /*
                 * Do the reservation for the relocation root creation
                 */
@@ -498,8 +513,24 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
                        reloc_reserved = true;
                }
 
                        reloc_reserved = true;
                }
 
-               ret = btrfs_block_rsv_add(root, &fs_info->trans_block_rsv,
-                                         num_bytes, flush);
+               ret = btrfs_block_rsv_add(root, rsv, num_bytes, flush);
+               if (ret)
+                       goto reserve_fail;
+               if (delayed_refs_bytes) {
+                       btrfs_migrate_to_delayed_refs_rsv(fs_info, rsv,
+                                                         delayed_refs_bytes);
+                       num_bytes -= delayed_refs_bytes;
+               }
+       } else if (num_items == 0 && flush == BTRFS_RESERVE_FLUSH_ALL &&
+                  !delayed_refs_rsv->full) {
+               /*
+                * Some people call with btrfs_start_transaction(root, 0)
+                * because they can be throttled, but have some other mechanism
+                * for reserving space.  We still want these guys to refill the
+                * delayed block_rsv so just add 1 items worth of reservation
+                * here.
+                */
+               ret = btrfs_delayed_refs_rsv_refill(fs_info, flush);
                if (ret)
                        goto reserve_fail;
        }
                if (ret)
                        goto reserve_fail;
        }