diff options
author | Dongsheng Yang <yangds.fnst@cn.fujitsu.com> | 2014-12-12 03:44:35 -0500 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2015-04-13 10:52:47 -0400 |
commit | 31193213f1f9c13f6485007ef1e233b119e46910 (patch) | |
tree | 755184ecff35a6387944eeefa4d2448917a43bbb /fs/btrfs/qgroup.c | |
parent | 804ca127fb93988c6a9d5f2bf4a8f1a780c9a2d0 (diff) |
Btrfs: qgroup: Introduce a may_use to account space_info->bytes_may_use.
Currently, for pre_alloc or delay_alloc, the bytes will be accounted
in space_info by the three guys.
space_info->bytes_may_use --- space_info->reserved --- space_info->used.
But on the other hand, in qgroup, there are only two counters to account the
bytes, qgroup->reserved and qgroup->excl. And qg->reserved accounts
bytes in space_info->bytes_may_use and qg->excl accounts bytes in
space_info->used. So the bytes in space_info->reserved is not accounted
in qgroup. If so, there is a window we can exceed the quota limit when
bytes is in space_info->reserved.
Example:
# btrfs quota enable /mnt
# btrfs qgroup limit -e 10M /mnt
# for((i=0;i<20;i++));do fallocate -l 1M /mnt/data$i; done
# sync
# btrfs qgroup show -pcre /mnt
qgroupid rfer excl max_rfer max_excl parent child
-------- ---- ---- -------- -------- ------ -----
0/5 20987904 20987904 0 10485760 --- ---
qg->excl is 20987904 larger than max_excl 10485760.
This patch introduce a new counter named may_use to qgroup, then
there are three counters in qgroup to account bytes in space_info
as below.
space_info->bytes_may_use --- space_info->reserved --- space_info->used.
qgroup->may_use --- qgroup->reserved --- qgroup->excl
With this patch applied:
# btrfs quota enable /mnt
# btrfs qgroup limit -e 10M /mnt
# for((i=0;i<20;i++));do fallocate -l 1M /mnt/data$i; done
fallocate: /mnt/data9: fallocate failed: Disk quota exceeded
fallocate: /mnt/data10: fallocate failed: Disk quota exceeded
fallocate: /mnt/data11: fallocate failed: Disk quota exceeded
fallocate: /mnt/data12: fallocate failed: Disk quota exceeded
fallocate: /mnt/data13: fallocate failed: Disk quota exceeded
fallocate: /mnt/data14: fallocate failed: Disk quota exceeded
fallocate: /mnt/data15: fallocate failed: Disk quota exceeded
fallocate: /mnt/data16: fallocate failed: Disk quota exceeded
fallocate: /mnt/data17: fallocate failed: Disk quota exceeded
fallocate: /mnt/data18: fallocate failed: Disk quota exceeded
fallocate: /mnt/data19: fallocate failed: Disk quota exceeded
# sync
# btrfs qgroup show -pcre /mnt
qgroupid rfer excl max_rfer max_excl parent child
-------- ---- ---- -------- -------- ------ -----
0/5 9453568 9453568 0 10485760 --- ---
Reported-by: Cyril SCETBON <cyril.scetbon@free.fr>
Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/qgroup.c')
-rw-r--r-- | fs/btrfs/qgroup.c | 68 |
1 files changed, 64 insertions, 4 deletions
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index de321c90130c..cd291733dc3e 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c | |||
@@ -72,6 +72,7 @@ struct btrfs_qgroup { | |||
72 | /* | 72 | /* |
73 | * reservation tracking | 73 | * reservation tracking |
74 | */ | 74 | */ |
75 | u64 may_use; | ||
75 | u64 reserved; | 76 | u64 reserved; |
76 | 77 | ||
77 | /* | 78 | /* |
@@ -1417,6 +1418,8 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, | |||
1417 | WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes); | 1418 | WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes); |
1418 | qgroup->excl += sign * oper->num_bytes; | 1419 | qgroup->excl += sign * oper->num_bytes; |
1419 | qgroup->excl_cmpr += sign * oper->num_bytes; | 1420 | qgroup->excl_cmpr += sign * oper->num_bytes; |
1421 | if (sign > 0) | ||
1422 | qgroup->reserved -= oper->num_bytes; | ||
1420 | 1423 | ||
1421 | qgroup_dirty(fs_info, qgroup); | 1424 | qgroup_dirty(fs_info, qgroup); |
1422 | 1425 | ||
@@ -1436,6 +1439,8 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, | |||
1436 | qgroup->rfer_cmpr += sign * oper->num_bytes; | 1439 | qgroup->rfer_cmpr += sign * oper->num_bytes; |
1437 | WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes); | 1440 | WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes); |
1438 | qgroup->excl += sign * oper->num_bytes; | 1441 | qgroup->excl += sign * oper->num_bytes; |
1442 | if (sign > 0) | ||
1443 | qgroup->reserved -= oper->num_bytes; | ||
1439 | qgroup->excl_cmpr += sign * oper->num_bytes; | 1444 | qgroup->excl_cmpr += sign * oper->num_bytes; |
1440 | qgroup_dirty(fs_info, qgroup); | 1445 | qgroup_dirty(fs_info, qgroup); |
1441 | 1446 | ||
@@ -2378,6 +2383,61 @@ out: | |||
2378 | return ret; | 2383 | return ret; |
2379 | } | 2384 | } |
2380 | 2385 | ||
2386 | int btrfs_qgroup_update_reserved_bytes(struct btrfs_fs_info *fs_info, | ||
2387 | u64 ref_root, | ||
2388 | u64 num_bytes, | ||
2389 | int sign) | ||
2390 | { | ||
2391 | struct btrfs_root *quota_root; | ||
2392 | struct btrfs_qgroup *qgroup; | ||
2393 | int ret = 0; | ||
2394 | struct ulist_node *unode; | ||
2395 | struct ulist_iterator uiter; | ||
2396 | |||
2397 | if (!is_fstree(ref_root) || !fs_info->quota_enabled) | ||
2398 | return 0; | ||
2399 | |||
2400 | if (num_bytes == 0) | ||
2401 | return 0; | ||
2402 | |||
2403 | spin_lock(&fs_info->qgroup_lock); | ||
2404 | quota_root = fs_info->quota_root; | ||
2405 | if (!quota_root) | ||
2406 | goto out; | ||
2407 | |||
2408 | qgroup = find_qgroup_rb(fs_info, ref_root); | ||
2409 | if (!qgroup) | ||
2410 | goto out; | ||
2411 | |||
2412 | ulist_reinit(fs_info->qgroup_ulist); | ||
2413 | ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid, | ||
2414 | (uintptr_t)qgroup, GFP_ATOMIC); | ||
2415 | if (ret < 0) | ||
2416 | goto out; | ||
2417 | |||
2418 | ULIST_ITER_INIT(&uiter); | ||
2419 | while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) { | ||
2420 | struct btrfs_qgroup *qg; | ||
2421 | struct btrfs_qgroup_list *glist; | ||
2422 | |||
2423 | qg = u64_to_ptr(unode->aux); | ||
2424 | |||
2425 | qg->reserved += sign * num_bytes; | ||
2426 | |||
2427 | list_for_each_entry(glist, &qg->groups, next_group) { | ||
2428 | ret = ulist_add(fs_info->qgroup_ulist, | ||
2429 | glist->group->qgroupid, | ||
2430 | (uintptr_t)glist->group, GFP_ATOMIC); | ||
2431 | if (ret < 0) | ||
2432 | goto out; | ||
2433 | } | ||
2434 | } | ||
2435 | |||
2436 | out: | ||
2437 | spin_unlock(&fs_info->qgroup_lock); | ||
2438 | return ret; | ||
2439 | } | ||
2440 | |||
2381 | /* | 2441 | /* |
2382 | * reserve some space for a qgroup and all its parents. The reservation takes | 2442 | * reserve some space for a qgroup and all its parents. The reservation takes |
2383 | * place with start_transaction or dealloc_reserve, similar to ENOSPC | 2443 | * place with start_transaction or dealloc_reserve, similar to ENOSPC |
@@ -2426,14 +2486,14 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes) | |||
2426 | qg = u64_to_ptr(unode->aux); | 2486 | qg = u64_to_ptr(unode->aux); |
2427 | 2487 | ||
2428 | if ((qg->lim_flags & BTRFS_QGROUP_LIMIT_MAX_RFER) && | 2488 | if ((qg->lim_flags & BTRFS_QGROUP_LIMIT_MAX_RFER) && |
2429 | qg->reserved + (s64)qg->rfer + num_bytes > | 2489 | qg->reserved + qg->may_use + (s64)qg->rfer + num_bytes > |
2430 | qg->max_rfer) { | 2490 | qg->max_rfer) { |
2431 | ret = -EDQUOT; | 2491 | ret = -EDQUOT; |
2432 | goto out; | 2492 | goto out; |
2433 | } | 2493 | } |
2434 | 2494 | ||
2435 | if ((qg->lim_flags & BTRFS_QGROUP_LIMIT_MAX_EXCL) && | 2495 | if ((qg->lim_flags & BTRFS_QGROUP_LIMIT_MAX_EXCL) && |
2436 | qg->reserved + (s64)qg->excl + num_bytes > | 2496 | qg->reserved + qg->may_use + (s64)qg->excl + num_bytes > |
2437 | qg->max_excl) { | 2497 | qg->max_excl) { |
2438 | ret = -EDQUOT; | 2498 | ret = -EDQUOT; |
2439 | goto out; | 2499 | goto out; |
@@ -2457,7 +2517,7 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes) | |||
2457 | 2517 | ||
2458 | qg = u64_to_ptr(unode->aux); | 2518 | qg = u64_to_ptr(unode->aux); |
2459 | 2519 | ||
2460 | qg->reserved += num_bytes; | 2520 | qg->may_use += num_bytes; |
2461 | } | 2521 | } |
2462 | 2522 | ||
2463 | out: | 2523 | out: |
@@ -2503,7 +2563,7 @@ void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes) | |||
2503 | 2563 | ||
2504 | qg = u64_to_ptr(unode->aux); | 2564 | qg = u64_to_ptr(unode->aux); |
2505 | 2565 | ||
2506 | qg->reserved -= num_bytes; | 2566 | qg->may_use -= num_bytes; |
2507 | 2567 | ||
2508 | list_for_each_entry(glist, &qg->groups, next_group) { | 2568 | list_for_each_entry(glist, &qg->groups, next_group) { |
2509 | ret = ulist_add(fs_info->qgroup_ulist, | 2569 | ret = ulist_add(fs_info->qgroup_ulist, |