diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 0b1f01dd02de..020c1a1a6526 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5977,7 +5977,7 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) { /* One for parent inode, two for dir entries */ num_bytes = 3 * fs_info->nodesize; - ret = btrfs_qgroup_reserve_meta(root, num_bytes, true); + ret = btrfs_qgroup_reserve_meta_prealloc(root, num_bytes, true); if (ret) return ret; } else { @@ -5996,7 +5996,7 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, ret = btrfs_block_rsv_migrate(global_rsv, rsv, num_bytes, 1); if (ret && *qgroup_reserved) - btrfs_qgroup_free_meta(root, *qgroup_reserved); + btrfs_qgroup_free_meta_prealloc(root, *qgroup_reserved); return ret; } @@ -6072,7 +6072,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes) spin_unlock(&inode->lock); if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) { - ret = btrfs_qgroup_reserve_meta(root, + ret = btrfs_qgroup_reserve_meta_prealloc(root, nr_extents * fs_info->nodesize, true); if (ret) goto out_fail; @@ -6080,7 +6080,7 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes) ret = btrfs_inode_rsv_refill(inode, flush); if (unlikely(ret)) { - btrfs_qgroup_free_meta(root, + btrfs_qgroup_free_meta_prealloc(root, nr_extents * fs_info->nodesize); goto out_fail; } diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index c0deebfecd93..8831eaa14204 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -69,8 +69,10 @@ static const char *qgroup_rsv_type_str(enum btrfs_qgroup_rsv_type type) { if (type == BTRFS_QGROUP_RSV_DATA) return "data"; - if (type == BTRFS_QGROUP_RSV_META) - return "meta"; + if (type == BTRFS_QGROUP_RSV_META_PERTRANS) + return "meta_pertrans"; + if (type == BTRFS_QGROUP_RSV_META_PREALLOC) + return "meta_prealloc"; return NULL; } #endif @@ -3065,8 +3067,8 @@ int btrfs_qgroup_release_data(struct inode *inode, u64 start, u64 len) return __btrfs_qgroup_release_data(inode, NULL, start, len, 0); } -int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, - bool enforce) +int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, + enum btrfs_qgroup_rsv_type type, bool enforce) { struct btrfs_fs_info *fs_info = root->fs_info; int ret; @@ -3077,14 +3079,14 @@ int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, BUG_ON(num_bytes != round_down(num_bytes, fs_info->nodesize)); trace_qgroup_meta_reserve(root, (s64)num_bytes); - ret = qgroup_reserve(root, num_bytes, enforce, BTRFS_QGROUP_RSV_META); + ret = qgroup_reserve(root, num_bytes, enforce, type); if (ret < 0) return ret; atomic64_add(num_bytes, &root->qgroup_meta_rsv); return ret; } -void btrfs_qgroup_free_meta_all(struct btrfs_root *root) +void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; u64 reserved; @@ -3098,10 +3100,11 @@ void btrfs_qgroup_free_meta_all(struct btrfs_root *root) return; trace_qgroup_meta_reserve(root, -(s64)reserved); btrfs_qgroup_free_refroot(fs_info, root->objectid, reserved, - BTRFS_QGROUP_RSV_META); + BTRFS_QGROUP_RSV_META_PERTRANS); } -void btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes) +void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes, + enum btrfs_qgroup_rsv_type type) { struct btrfs_fs_info *fs_info = root->fs_info; @@ -3113,8 +3116,7 @@ void btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes) WARN_ON(atomic64_read(&root->qgroup_meta_rsv) < num_bytes); atomic64_sub(num_bytes, &root->qgroup_meta_rsv); trace_qgroup_meta_reserve(root, -(s64)num_bytes); - btrfs_qgroup_free_refroot(fs_info, root->objectid, num_bytes, - BTRFS_QGROUP_RSV_META); + btrfs_qgroup_free_refroot(fs_info, root->objectid, num_bytes, type); } /* diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h index 279e71a21695..987a5a49deb8 100644 --- a/fs/btrfs/qgroup.h +++ b/fs/btrfs/qgroup.h @@ -61,9 +61,31 @@ struct btrfs_qgroup_extent_record { struct ulist *old_roots; }; +/* + * Qgroup reservation types: + * + * DATA: + * space reserved for data + * + * META_PERTRANS: + * Space reserved for metadata (per-transaction) + * Due to the fact that qgroup data is only updated at transaction commit + * time, reserved space for metadata must be kept until transaction + * commits. + * Any metadata reserved that are used in btrfs_start_transaction() should + * be of this type. + * + * META_PREALLOC: + * There are cases where metadata space is reserved before starting + * transaction, and then btrfs_join_transaction() to get a trans handle. + * Any metadata reserved for such usage should be of this type. + * And after join_transaction() part (or all) of such reservation should + * be converted into META_PERTRANS. + */ enum btrfs_qgroup_rsv_type { BTRFS_QGROUP_RSV_DATA = 0, - BTRFS_QGROUP_RSV_META, + BTRFS_QGROUP_RSV_META_PERTRANS, + BTRFS_QGROUP_RSV_META_PREALLOC, BTRFS_QGROUP_RSV_LAST, }; @@ -269,9 +291,46 @@ int btrfs_qgroup_release_data(struct inode *inode, u64 start, u64 len); int btrfs_qgroup_free_data(struct inode *inode, struct extent_changeset *reserved, u64 start, u64 len); -int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, - bool enforce); -void btrfs_qgroup_free_meta_all(struct btrfs_root *root); -void btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes); +int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, + enum btrfs_qgroup_rsv_type type, bool enforce); +/* Reserve metadata space for pertrans and prealloc type */ +static inline int btrfs_qgroup_reserve_meta_pertrans(struct btrfs_root *root, + int num_bytes, bool enforce) +{ + return __btrfs_qgroup_reserve_meta(root, num_bytes, + BTRFS_QGROUP_RSV_META_PERTRANS, enforce); +} +static inline int btrfs_qgroup_reserve_meta_prealloc(struct btrfs_root *root, + int num_bytes, bool enforce) +{ + return __btrfs_qgroup_reserve_meta(root, num_bytes, + BTRFS_QGROUP_RSV_META_PREALLOC, enforce); +} + +void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes, + enum btrfs_qgroup_rsv_type type); + +/* Free per-transaction meta reservation for error handling */ +static inline void btrfs_qgroup_free_meta_pertrans(struct btrfs_root *root, + int num_bytes) +{ + __btrfs_qgroup_free_meta(root, num_bytes, + BTRFS_QGROUP_RSV_META_PERTRANS); +} + +/* Pre-allocated meta reservation can be freed at need */ +static inline void btrfs_qgroup_free_meta_prealloc(struct btrfs_root *root, + int num_bytes) +{ + __btrfs_qgroup_free_meta(root, num_bytes, + BTRFS_QGROUP_RSV_META_PREALLOC); +} + +/* + * Per-transaction meta reservation should be all freed at transaction commit + * time + */ +void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root); + void btrfs_qgroup_check_reserved_leak(struct inode *inode); #endif /* __BTRFS_QGROUP__ */ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 15f6541303bc..5c4cf0f9146b 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -498,8 +498,8 @@ start_transaction(struct btrfs_root *root, unsigned int num_items, */ if (num_items && root != fs_info->chunk_root) { qgroup_reserved = num_items * fs_info->nodesize; - ret = btrfs_qgroup_reserve_meta(root, qgroup_reserved, - enforce_qgroups); + ret = btrfs_qgroup_reserve_meta_pertrans(root, qgroup_reserved, + enforce_qgroups); if (ret) return ERR_PTR(ret); @@ -596,7 +596,7 @@ alloc_fail: btrfs_block_rsv_release(fs_info, &fs_info->trans_block_rsv, num_bytes); reserve_fail: - btrfs_qgroup_free_meta(root, qgroup_reserved); + btrfs_qgroup_free_meta_pertrans(root, qgroup_reserved); return ERR_PTR(ret); } @@ -1284,7 +1284,7 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) spin_lock(&fs_info->fs_roots_radix_lock); if (err) break; - btrfs_qgroup_free_meta_all(root); + btrfs_qgroup_free_meta_all_pertrans(root); } } spin_unlock(&fs_info->fs_roots_radix_lock); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 54b9af822a3a..eee778ba1414 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -73,8 +73,9 @@ TRACE_DEFINE_ENUM(COMMIT_TRANS); #define show_qgroup_rsv_type(type) \ __print_symbolic(type, \ - { BTRFS_QGROUP_RSV_DATA, "DATA" }, \ - { BTRFS_QGROUP_RSV_META, "META" }) + { BTRFS_QGROUP_RSV_DATA, "DATA" }, \ + { BTRFS_QGROUP_RSV_META_PERTRANS, "META_PERTRANS" }, \ + { BTRFS_QGROUP_RSV_META_PREALLOC, "META_PREALLOC" }) #define BTRFS_GROUP_FLAGS \ { BTRFS_BLOCK_GROUP_DATA, "DATA"}, \