Skip to content

Commit 8287475

Browse files
adam900710kdave
authored andcommitted
btrfs: qgroup: Use root::qgroup_meta_rsv_* to record qgroup meta reserved space
For quota disabled->enable case, it's possible that at reservation time quota was not enabled so no bytes were really reserved, while at release time, quota was enabled so we will try to release some bytes we didn't really own. Such situation can cause metadata reserveation underflow, for both types, also less possible for per-trans type since quota enable will commit transaction. To address this, record qgroup meta reserved bytes into root::qgroup_meta_rsv_pertrans and ::prealloc. So at releasing time we won't free any bytes we didn't reserve. For DATA, it's already handled by io_tree, so nothing needs to be done there. Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 4f5427c commit 8287475

File tree

3 files changed

+68
-4
lines changed

3 files changed

+68
-4
lines changed

fs/btrfs/ctree.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,11 @@ struct btrfs_root {
12641264
int send_in_progress;
12651265
struct btrfs_subvolume_writers *subv_writers;
12661266
atomic_t will_be_snapshotted;
1267+
1268+
/* For qgroup metadata reserved space */
1269+
spinlock_t qgroup_meta_rsv_lock;
1270+
u64 qgroup_meta_rsv_pertrans;
1271+
u64 qgroup_meta_rsv_prealloc;
12671272
};
12681273

12691274
struct btrfs_file_private {

fs/btrfs/disk-io.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
11471147
spin_lock_init(&root->accounting_lock);
11481148
spin_lock_init(&root->log_extents_lock[0]);
11491149
spin_lock_init(&root->log_extents_lock[1]);
1150+
spin_lock_init(&root->qgroup_meta_rsv_lock);
11501151
mutex_init(&root->objectid_mutex);
11511152
mutex_init(&root->log_mutex);
11521153
mutex_init(&root->ordered_extent_mutex);

fs/btrfs/qgroup.c

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2538,11 +2538,11 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
25382538
if (!qgroup)
25392539
goto out;
25402540

2541-
/*
2542-
* We're freeing all pertrans rsv, get current value from level 0
2543-
* qgroup as real num_bytes to free.
2544-
*/
25452541
if (num_bytes == (u64)-1)
2542+
/*
2543+
* We're freeing all pertrans rsv, get reserved value from
2544+
* level 0 qgroup as real num_bytes to free.
2545+
*/
25462546
num_bytes = qgroup->rsv.values[type];
25472547

25482548
ulist_reinit(fs_info->qgroup_ulist);
@@ -3087,6 +3087,46 @@ int btrfs_qgroup_release_data(struct inode *inode, u64 start, u64 len)
30873087
return __btrfs_qgroup_release_data(inode, NULL, start, len, 0);
30883088
}
30893089

3090+
static void add_root_meta_rsv(struct btrfs_root *root, int num_bytes,
3091+
enum btrfs_qgroup_rsv_type type)
3092+
{
3093+
if (type != BTRFS_QGROUP_RSV_META_PREALLOC &&
3094+
type != BTRFS_QGROUP_RSV_META_PERTRANS)
3095+
return;
3096+
if (num_bytes == 0)
3097+
return;
3098+
3099+
spin_lock(&root->qgroup_meta_rsv_lock);
3100+
if (type == BTRFS_QGROUP_RSV_META_PREALLOC)
3101+
root->qgroup_meta_rsv_prealloc += num_bytes;
3102+
else
3103+
root->qgroup_meta_rsv_pertrans += num_bytes;
3104+
spin_unlock(&root->qgroup_meta_rsv_lock);
3105+
}
3106+
3107+
static int sub_root_meta_rsv(struct btrfs_root *root, int num_bytes,
3108+
enum btrfs_qgroup_rsv_type type)
3109+
{
3110+
if (type != BTRFS_QGROUP_RSV_META_PREALLOC &&
3111+
type != BTRFS_QGROUP_RSV_META_PERTRANS)
3112+
return 0;
3113+
if (num_bytes == 0)
3114+
return 0;
3115+
3116+
spin_lock(&root->qgroup_meta_rsv_lock);
3117+
if (type == BTRFS_QGROUP_RSV_META_PREALLOC) {
3118+
num_bytes = min_t(u64, root->qgroup_meta_rsv_prealloc,
3119+
num_bytes);
3120+
root->qgroup_meta_rsv_prealloc -= num_bytes;
3121+
} else {
3122+
num_bytes = min_t(u64, root->qgroup_meta_rsv_pertrans,
3123+
num_bytes);
3124+
root->qgroup_meta_rsv_pertrans -= num_bytes;
3125+
}
3126+
spin_unlock(&root->qgroup_meta_rsv_lock);
3127+
return num_bytes;
3128+
}
3129+
30903130
int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
30913131
enum btrfs_qgroup_rsv_type type, bool enforce)
30923132
{
@@ -3102,6 +3142,15 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
31023142
ret = qgroup_reserve(root, num_bytes, enforce, type);
31033143
if (ret < 0)
31043144
return ret;
3145+
/*
3146+
* Record what we have reserved into root.
3147+
*
3148+
* To avoid quota disabled->enabled underflow.
3149+
* In that case, we may try to free space we haven't reserved
3150+
* (since quota was disabled), so record what we reserved into root.
3151+
* And ensure later release won't underflow this number.
3152+
*/
3153+
add_root_meta_rsv(root, num_bytes, type);
31053154
return ret;
31063155
}
31073156

@@ -3129,6 +3178,12 @@ void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes,
31293178
!is_fstree(root->objectid))
31303179
return;
31313180

3181+
/*
3182+
* reservation for META_PREALLOC can happen before quota is enabled,
3183+
* which can lead to underflow.
3184+
* Here ensure we will only free what we really have reserved.
3185+
*/
3186+
num_bytes = sub_root_meta_rsv(root, num_bytes, type);
31323187
BUG_ON(num_bytes != round_down(num_bytes, fs_info->nodesize));
31333188
trace_qgroup_meta_reserve(root, -(s64)num_bytes);
31343189
btrfs_qgroup_free_refroot(fs_info, root->objectid, num_bytes, type);
@@ -3187,6 +3242,9 @@ void btrfs_qgroup_convert_reserved_meta(struct btrfs_root *root, int num_bytes)
31873242
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
31883243
!is_fstree(root->objectid))
31893244
return;
3245+
/* Same as btrfs_qgroup_free_meta_prealloc() */
3246+
num_bytes = sub_root_meta_rsv(root, num_bytes,
3247+
BTRFS_QGROUP_RSV_META_PREALLOC);
31903248
qgroup_convert_meta(fs_info, root->objectid, num_bytes);
31913249
}
31923250

0 commit comments

Comments
 (0)