diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/qgroup.c | 183 |
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 | */ | ||
1011 | static 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; | ||
1066 | out: | ||
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 | */ | ||
1082 | static 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 | } | ||
1102 | out: | ||
1103 | if (ret) | ||
1104 | fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; | ||
1105 | return ret; | ||
1106 | } | ||
1107 | |||
1004 | int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, | 1108 | int 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); |
1051 | out: | 1165 | out: |
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); |
1095 | out: | 1216 | out: |
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 | */ | ||
1408 | static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, | 1525 | static 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; | ||
1480 | out: | 1552 | out: |
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 | ||