aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/qgroup.c
diff options
context:
space:
mode:
authorDongsheng Yang <yangds.fnst@cn.fujitsu.com>2014-12-12 03:44:35 -0500
committerChris Mason <clm@fb.com>2015-04-13 10:52:47 -0400
commit31193213f1f9c13f6485007ef1e233b119e46910 (patch)
tree755184ecff35a6387944eeefa4d2448917a43bbb /fs/btrfs/qgroup.c
parent804ca127fb93988c6a9d5f2bf4a8f1a780c9a2d0 (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.c68
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
2386int 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
2436out:
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
2463out: 2523out:
@@ -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,