aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
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