aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorQu Wenruo <quwenruo@cn.fujitsu.com>2015-02-27 03:24:27 -0500
committerChris Mason <clm@fb.com>2015-04-13 10:52:59 -0400
commit9c8b35b1ba21bbf7527056f394aa6109424c55ef (patch)
tree3668997a63a88cd40d1b892dd0a7bd03f770f432 /fs/btrfs
parent8ea0ec9e011eb542a3e7b1171776aa4877cf8a90 (diff)
btrfs: quota: Automatically update related qgroups or mark INCONSISTENT flags when assigning/deleting a qgroup relations.
Operation like qgroups assigning/deleting qgroup relations will mostly cause qgroup data inconsistent, since it needs to do the full rescan to determine whether shared extents are exclusive or still shared in parent qgroups. But there are some exceptions, like qgroup with only exclusive extents (qgroup->excl == qgroup->rfer), in that case, we only needs to modify all its parents' excl and rfer. So this patch adds a quick path for such qgroup in qgroup assign/remove routine, and if quick path failed, the qgroup status will be marked INCONSISTENT, and return 1 to info user-land. BTW since the quick path is much the same of qgroup_excl_accounting(), so move the core of it to __qgroup_excl_accounting() and reuse it. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> Reviewed-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/qgroup.c183
1 files changed, 126 insertions, 57 deletions
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index f5f2baae7aa3..3d6546581bb9 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -1001,6 +1001,110 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info,
1001 list_add(&qgroup->dirty, &fs_info->dirty_qgroups); 1001 list_add(&qgroup->dirty, &fs_info->dirty_qgroups);
1002} 1002}
1003 1003
1004/*
1005 * The easy accounting, if we are adding/removing the only ref for an extent
1006 * then this qgroup and all of the parent qgroups get their refrence and
1007 * exclusive counts adjusted.
1008 *
1009 * Caller should hold fs_info->qgroup_lock.
1010 */
1011static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
1012 struct ulist *tmp, u64 ref_root,
1013 u64 num_bytes, int sign)
1014{
1015 struct btrfs_qgroup *qgroup;
1016 struct btrfs_qgroup_list *glist;
1017 struct ulist_node *unode;
1018 struct ulist_iterator uiter;
1019 int ret = 0;
1020
1021 qgroup = find_qgroup_rb(fs_info, ref_root);
1022 if (!qgroup)
1023 goto out;
1024
1025 qgroup->rfer += sign * num_bytes;
1026 qgroup->rfer_cmpr += sign * num_bytes;
1027
1028 WARN_ON(sign < 0 && qgroup->excl < num_bytes);
1029 qgroup->excl += sign * num_bytes;
1030 qgroup->excl_cmpr += sign * num_bytes;
1031 if (sign > 0)
1032 qgroup->reserved -= num_bytes;
1033
1034 qgroup_dirty(fs_info, qgroup);
1035
1036 /* Get all of the parent groups that contain this qgroup */
1037 list_for_each_entry(glist, &qgroup->groups, next_group) {
1038 ret = ulist_add(tmp, glist->group->qgroupid,
1039 ptr_to_u64(glist->group), GFP_ATOMIC);
1040 if (ret < 0)
1041 goto out;
1042 }
1043
1044 /* Iterate all of the parents and adjust their reference counts */
1045 ULIST_ITER_INIT(&uiter);
1046 while ((unode = ulist_next(tmp, &uiter))) {
1047 qgroup = u64_to_ptr(unode->aux);
1048 qgroup->rfer += sign * num_bytes;
1049 qgroup->rfer_cmpr += sign * num_bytes;
1050 WARN_ON(sign < 0 && qgroup->excl < num_bytes);
1051 qgroup->excl += sign * num_bytes;
1052 if (sign > 0)
1053 qgroup->reserved -= num_bytes;
1054 qgroup->excl_cmpr += sign * num_bytes;
1055 qgroup_dirty(fs_info, qgroup);
1056
1057 /* Add any parents of the parents */
1058 list_for_each_entry(glist, &qgroup->groups, next_group) {
1059 ret = ulist_add(tmp, glist->group->qgroupid,
1060 ptr_to_u64(glist->group), GFP_ATOMIC);
1061 if (ret < 0)
1062 goto out;
1063 }
1064 }
1065 ret = 0;
1066out:
1067 return ret;
1068}
1069
1070
1071/*
1072 * Quick path for updating qgroup with only excl refs.
1073 *
1074 * In that case, just update all parent will be enough.
1075 * Or we needs to do a full rescan.
1076 * Caller should also hold fs_info->qgroup_lock.
1077 *
1078 * Return 0 for quick update, return >0 for need to full rescan
1079 * and mark INCONSISTENT flag.
1080 * Return < 0 for other error.
1081 */
1082static int quick_update_accounting(struct btrfs_fs_info *fs_info,
1083 struct ulist *tmp, u64 src, u64 dst,
1084 int sign)
1085{
1086 struct btrfs_qgroup *qgroup;
1087 int ret = 1;
1088 int err = 0;
1089
1090 qgroup = find_qgroup_rb(fs_info, src);
1091 if (!qgroup)
1092 goto out;
1093 if (qgroup->excl == qgroup->rfer) {
1094 ret = 0;
1095 err = __qgroup_excl_accounting(fs_info, tmp, dst,
1096 qgroup->excl, sign);
1097 if (err < 0) {
1098 ret = err;
1099 goto out;
1100 }
1101 }
1102out:
1103 if (ret)
1104 fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
1105 return ret;
1106}
1107
1004int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, 1108int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
1005 struct btrfs_fs_info *fs_info, u64 src, u64 dst) 1109 struct btrfs_fs_info *fs_info, u64 src, u64 dst)
1006{ 1110{
@@ -1008,8 +1112,13 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
1008 struct btrfs_qgroup *parent; 1112 struct btrfs_qgroup *parent;
1009 struct btrfs_qgroup *member; 1113 struct btrfs_qgroup *member;
1010 struct btrfs_qgroup_list *list; 1114 struct btrfs_qgroup_list *list;
1115 struct ulist *tmp;
1011 int ret = 0; 1116 int ret = 0;
1012 1117
1118 tmp = ulist_alloc(GFP_NOFS);
1119 if (!tmp)
1120 return -ENOMEM;
1121
1013 /* Check the level of src and dst first */ 1122 /* Check the level of src and dst first */
1014 if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst)) 1123 if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst))
1015 return -EINVAL; 1124 return -EINVAL;
@@ -1047,9 +1156,15 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
1047 1156
1048 spin_lock(&fs_info->qgroup_lock); 1157 spin_lock(&fs_info->qgroup_lock);
1049 ret = add_relation_rb(quota_root->fs_info, src, dst); 1158 ret = add_relation_rb(quota_root->fs_info, src, dst);
1159 if (ret < 0) {
1160 spin_unlock(&fs_info->qgroup_lock);
1161 goto out;
1162 }
1163 ret = quick_update_accounting(fs_info, tmp, src, dst, 1);
1050 spin_unlock(&fs_info->qgroup_lock); 1164 spin_unlock(&fs_info->qgroup_lock);
1051out: 1165out:
1052 mutex_unlock(&fs_info->qgroup_ioctl_lock); 1166 mutex_unlock(&fs_info->qgroup_ioctl_lock);
1167 ulist_free(tmp);
1053 return ret; 1168 return ret;
1054} 1169}
1055 1170
@@ -1060,9 +1175,14 @@ int __del_qgroup_relation(struct btrfs_trans_handle *trans,
1060 struct btrfs_qgroup *parent; 1175 struct btrfs_qgroup *parent;
1061 struct btrfs_qgroup *member; 1176 struct btrfs_qgroup *member;
1062 struct btrfs_qgroup_list *list; 1177 struct btrfs_qgroup_list *list;
1178 struct ulist *tmp;
1063 int ret = 0; 1179 int ret = 0;
1064 int err; 1180 int err;
1065 1181
1182 tmp = ulist_alloc(GFP_NOFS);
1183 if (!tmp)
1184 return -ENOMEM;
1185
1066 quota_root = fs_info->quota_root; 1186 quota_root = fs_info->quota_root;
1067 if (!quota_root) { 1187 if (!quota_root) {
1068 ret = -EINVAL; 1188 ret = -EINVAL;
@@ -1091,8 +1211,10 @@ exist:
1091 1211
1092 spin_lock(&fs_info->qgroup_lock); 1212 spin_lock(&fs_info->qgroup_lock);
1093 del_relation_rb(fs_info, src, dst); 1213 del_relation_rb(fs_info, src, dst);
1214 ret = quick_update_accounting(fs_info, tmp, src, dst, -1);
1094 spin_unlock(&fs_info->qgroup_lock); 1215 spin_unlock(&fs_info->qgroup_lock);
1095out: 1216out:
1217 ulist_free(tmp);
1096 return ret; 1218 return ret;
1097} 1219}
1098 1220
@@ -1400,19 +1522,10 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
1400 return 0; 1522 return 0;
1401} 1523}
1402 1524
1403/*
1404 * The easy accounting, if we are adding/removing the only ref for an extent
1405 * then this qgroup and all of the parent qgroups get their refrence and
1406 * exclusive counts adjusted.
1407 */
1408static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, 1525static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
1409 struct btrfs_qgroup_operation *oper) 1526 struct btrfs_qgroup_operation *oper)
1410{ 1527{
1411 struct btrfs_qgroup *qgroup;
1412 struct ulist *tmp; 1528 struct ulist *tmp;
1413 struct btrfs_qgroup_list *glist;
1414 struct ulist_node *unode;
1415 struct ulist_iterator uiter;
1416 int sign = 0; 1529 int sign = 0;
1417 int ret = 0; 1530 int ret = 0;
1418 1531
@@ -1423,9 +1536,7 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
1423 spin_lock(&fs_info->qgroup_lock); 1536 spin_lock(&fs_info->qgroup_lock);
1424 if (!fs_info->quota_root) 1537 if (!fs_info->quota_root)
1425 goto out; 1538 goto out;
1426 qgroup = find_qgroup_rb(fs_info, oper->ref_root); 1539
1427 if (!qgroup)
1428 goto out;
1429 switch (oper->type) { 1540 switch (oper->type) {
1430 case BTRFS_QGROUP_OPER_ADD_EXCL: 1541 case BTRFS_QGROUP_OPER_ADD_EXCL:
1431 sign = 1; 1542 sign = 1;
@@ -1436,47 +1547,8 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
1436 default: 1547 default:
1437 ASSERT(0); 1548 ASSERT(0);
1438 } 1549 }
1439 qgroup->rfer += sign * oper->num_bytes; 1550 ret = __qgroup_excl_accounting(fs_info, tmp, oper->ref_root,
1440 qgroup->rfer_cmpr += sign * oper->num_bytes; 1551 oper->num_bytes, sign);
1441
1442 WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes);
1443 qgroup->excl += sign * oper->num_bytes;
1444 qgroup->excl_cmpr += sign * oper->num_bytes;
1445 if (sign > 0)
1446 qgroup->reserved -= oper->num_bytes;
1447
1448 qgroup_dirty(fs_info, qgroup);
1449
1450 /* Get all of the parent groups that contain this qgroup */
1451 list_for_each_entry(glist, &qgroup->groups, next_group) {
1452 ret = ulist_add(tmp, glist->group->qgroupid,
1453 ptr_to_u64(glist->group), GFP_ATOMIC);
1454 if (ret < 0)
1455 goto out;
1456 }
1457
1458 /* Iterate all of the parents and adjust their reference counts */
1459 ULIST_ITER_INIT(&uiter);
1460 while ((unode = ulist_next(tmp, &uiter))) {
1461 qgroup = u64_to_ptr(unode->aux);
1462 qgroup->rfer += sign * oper->num_bytes;
1463 qgroup->rfer_cmpr += sign * oper->num_bytes;
1464 WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes);
1465 qgroup->excl += sign * oper->num_bytes;
1466 if (sign > 0)
1467 qgroup->reserved -= oper->num_bytes;
1468 qgroup->excl_cmpr += sign * oper->num_bytes;
1469 qgroup_dirty(fs_info, qgroup);
1470
1471 /* Add any parents of the parents */
1472 list_for_each_entry(glist, &qgroup->groups, next_group) {
1473 ret = ulist_add(tmp, glist->group->qgroupid,
1474 ptr_to_u64(glist->group), GFP_ATOMIC);
1475 if (ret < 0)
1476 goto out;
1477 }
1478 }
1479 ret = 0;
1480out: 1552out:
1481 spin_unlock(&fs_info->qgroup_lock); 1553 spin_unlock(&fs_info->qgroup_lock);
1482 ulist_free(tmp); 1554 ulist_free(tmp);
@@ -2732,10 +2804,7 @@ out:
2732 ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root); 2804 ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root);
2733 if (ret < 0) { 2805 if (ret < 0) {
2734 err = ret; 2806 err = ret;
2735 btrfs_err(fs_info, "fail to update qgroup status: %d\n", 2807 btrfs_err(fs_info, "fail to update qgroup status: %d\n", err);
2736 err);
2737 btrfs_abort_transaction(trans, fs_info->quota_root, err);
2738 goto done;
2739 } 2808 }
2740 btrfs_end_transaction(trans, fs_info->quota_root); 2809 btrfs_end_transaction(trans, fs_info->quota_root);
2741 2810