diff options
author | Wang Shilong <wangsl-fnst@cn.fujitsu.com> | 2013-04-07 06:50:16 -0400 |
---|---|---|
committer | Josef Bacik <jbacik@fusionio.com> | 2013-05-06 15:54:38 -0400 |
commit | f2f6ed3d54648ec19dcdeec30f66843cf7a38487 (patch) | |
tree | cc82afe22a835994252961f21e422258393a8974 /fs/btrfs/qgroup.c | |
parent | 7708f029dca5f1b9e9d6ea01ab10cd83e4c74ff2 (diff) |
Btrfs: introduce a mutex lock for btrfs quota operations
The original code has one spin_lock 'qgroup_lock' to protect quota
configurations in memory. If we want to add a BTRFS_QGROUP_INFO_KEY,
it will be added to Btree firstly, and then update configurations in
memory,however, a race condition may happen between these operations.
For example:
->add_qgroup_info_item()
->add_qgroup_rb()
For the above case, del_qgroup_info_item() may happen just before
add_qgroup_rb().
What's worse, when we want to add a qgroup relation:
->add_qgroup_relation_item()
->add_qgroup_relations()
We don't have any checks whether 'src' and 'dst' exist before
add_qgroup_relation_item(), a race condition can also happen for
the above case.
To avoid race condition and have all the necessary checks, we introduce
a mutex lock 'qgroup_ioctl_lock', and we make all the user change operations
protected by the mutex lock.
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Reviewed-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/qgroup.c')
-rw-r--r-- | fs/btrfs/qgroup.c | 82 |
1 files changed, 57 insertions, 25 deletions
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 5be5a39dedc4..0a1f6861ae9a 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c | |||
@@ -791,6 +791,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans, | |||
791 | int ret = 0; | 791 | int ret = 0; |
792 | int slot; | 792 | int slot; |
793 | 793 | ||
794 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
794 | spin_lock(&fs_info->qgroup_lock); | 795 | spin_lock(&fs_info->qgroup_lock); |
795 | if (fs_info->quota_root) { | 796 | if (fs_info->quota_root) { |
796 | fs_info->pending_quota_state = 1; | 797 | fs_info->pending_quota_state = 1; |
@@ -900,6 +901,7 @@ out_free_root: | |||
900 | kfree(quota_root); | 901 | kfree(quota_root); |
901 | } | 902 | } |
902 | out: | 903 | out: |
904 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
903 | return ret; | 905 | return ret; |
904 | } | 906 | } |
905 | 907 | ||
@@ -910,10 +912,11 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans, | |||
910 | struct btrfs_root *quota_root; | 912 | struct btrfs_root *quota_root; |
911 | int ret = 0; | 913 | int ret = 0; |
912 | 914 | ||
915 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
913 | spin_lock(&fs_info->qgroup_lock); | 916 | spin_lock(&fs_info->qgroup_lock); |
914 | if (!fs_info->quota_root) { | 917 | if (!fs_info->quota_root) { |
915 | spin_unlock(&fs_info->qgroup_lock); | 918 | spin_unlock(&fs_info->qgroup_lock); |
916 | return 0; | 919 | goto out; |
917 | } | 920 | } |
918 | fs_info->quota_enabled = 0; | 921 | fs_info->quota_enabled = 0; |
919 | fs_info->pending_quota_state = 0; | 922 | fs_info->pending_quota_state = 0; |
@@ -922,8 +925,10 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans, | |||
922 | btrfs_free_qgroup_config(fs_info); | 925 | btrfs_free_qgroup_config(fs_info); |
923 | spin_unlock(&fs_info->qgroup_lock); | 926 | spin_unlock(&fs_info->qgroup_lock); |
924 | 927 | ||
925 | if (!quota_root) | 928 | if (!quota_root) { |
926 | return -EINVAL; | 929 | ret = -EINVAL; |
930 | goto out; | ||
931 | } | ||
927 | 932 | ||
928 | ret = btrfs_clean_quota_tree(trans, quota_root); | 933 | ret = btrfs_clean_quota_tree(trans, quota_root); |
929 | if (ret) | 934 | if (ret) |
@@ -944,6 +949,7 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans, | |||
944 | free_extent_buffer(quota_root->commit_root); | 949 | free_extent_buffer(quota_root->commit_root); |
945 | kfree(quota_root); | 950 | kfree(quota_root); |
946 | out: | 951 | out: |
952 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
947 | return ret; | 953 | return ret; |
948 | } | 954 | } |
949 | 955 | ||
@@ -959,24 +965,28 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, | |||
959 | struct btrfs_root *quota_root; | 965 | struct btrfs_root *quota_root; |
960 | int ret = 0; | 966 | int ret = 0; |
961 | 967 | ||
968 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
962 | quota_root = fs_info->quota_root; | 969 | quota_root = fs_info->quota_root; |
963 | if (!quota_root) | 970 | if (!quota_root) { |
964 | return -EINVAL; | 971 | ret = -EINVAL; |
972 | goto out; | ||
973 | } | ||
965 | 974 | ||
966 | ret = add_qgroup_relation_item(trans, quota_root, src, dst); | 975 | ret = add_qgroup_relation_item(trans, quota_root, src, dst); |
967 | if (ret) | 976 | if (ret) |
968 | return ret; | 977 | goto out; |
969 | 978 | ||
970 | ret = add_qgroup_relation_item(trans, quota_root, dst, src); | 979 | ret = add_qgroup_relation_item(trans, quota_root, dst, src); |
971 | if (ret) { | 980 | if (ret) { |
972 | del_qgroup_relation_item(trans, quota_root, src, dst); | 981 | del_qgroup_relation_item(trans, quota_root, src, dst); |
973 | return ret; | 982 | goto out; |
974 | } | 983 | } |
975 | 984 | ||
976 | spin_lock(&fs_info->qgroup_lock); | 985 | spin_lock(&fs_info->qgroup_lock); |
977 | ret = add_relation_rb(quota_root->fs_info, src, dst); | 986 | ret = add_relation_rb(quota_root->fs_info, src, dst); |
978 | spin_unlock(&fs_info->qgroup_lock); | 987 | spin_unlock(&fs_info->qgroup_lock); |
979 | 988 | out: | |
989 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
980 | return ret; | 990 | return ret; |
981 | } | 991 | } |
982 | 992 | ||
@@ -987,9 +997,12 @@ int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans, | |||
987 | int ret = 0; | 997 | int ret = 0; |
988 | int err; | 998 | int err; |
989 | 999 | ||
1000 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
990 | quota_root = fs_info->quota_root; | 1001 | quota_root = fs_info->quota_root; |
991 | if (!quota_root) | 1002 | if (!quota_root) { |
992 | return -EINVAL; | 1003 | ret = -EINVAL; |
1004 | goto out; | ||
1005 | } | ||
993 | 1006 | ||
994 | ret = del_qgroup_relation_item(trans, quota_root, src, dst); | 1007 | ret = del_qgroup_relation_item(trans, quota_root, src, dst); |
995 | err = del_qgroup_relation_item(trans, quota_root, dst, src); | 1008 | err = del_qgroup_relation_item(trans, quota_root, dst, src); |
@@ -1000,7 +1013,8 @@ int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans, | |||
1000 | del_relation_rb(fs_info, src, dst); | 1013 | del_relation_rb(fs_info, src, dst); |
1001 | 1014 | ||
1002 | spin_unlock(&fs_info->qgroup_lock); | 1015 | spin_unlock(&fs_info->qgroup_lock); |
1003 | 1016 | out: | |
1017 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
1004 | return ret; | 1018 | return ret; |
1005 | } | 1019 | } |
1006 | 1020 | ||
@@ -1011,9 +1025,12 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, | |||
1011 | struct btrfs_qgroup *qgroup; | 1025 | struct btrfs_qgroup *qgroup; |
1012 | int ret = 0; | 1026 | int ret = 0; |
1013 | 1027 | ||
1028 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
1014 | quota_root = fs_info->quota_root; | 1029 | quota_root = fs_info->quota_root; |
1015 | if (!quota_root) | 1030 | if (!quota_root) { |
1016 | return -EINVAL; | 1031 | ret = -EINVAL; |
1032 | goto out; | ||
1033 | } | ||
1017 | 1034 | ||
1018 | ret = add_qgroup_item(trans, quota_root, qgroupid); | 1035 | ret = add_qgroup_item(trans, quota_root, qgroupid); |
1019 | 1036 | ||
@@ -1023,7 +1040,8 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, | |||
1023 | 1040 | ||
1024 | if (IS_ERR(qgroup)) | 1041 | if (IS_ERR(qgroup)) |
1025 | ret = PTR_ERR(qgroup); | 1042 | ret = PTR_ERR(qgroup); |
1026 | 1043 | out: | |
1044 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
1027 | return ret; | 1045 | return ret; |
1028 | } | 1046 | } |
1029 | 1047 | ||
@@ -1034,9 +1052,12 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, | |||
1034 | struct btrfs_qgroup *qgroup; | 1052 | struct btrfs_qgroup *qgroup; |
1035 | int ret = 0; | 1053 | int ret = 0; |
1036 | 1054 | ||
1055 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
1037 | quota_root = fs_info->quota_root; | 1056 | quota_root = fs_info->quota_root; |
1038 | if (!quota_root) | 1057 | if (!quota_root) { |
1039 | return -EINVAL; | 1058 | ret = -EINVAL; |
1059 | goto out; | ||
1060 | } | ||
1040 | 1061 | ||
1041 | /* check if there are no relations to this qgroup */ | 1062 | /* check if there are no relations to this qgroup */ |
1042 | spin_lock(&fs_info->qgroup_lock); | 1063 | spin_lock(&fs_info->qgroup_lock); |
@@ -1044,7 +1065,8 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, | |||
1044 | if (qgroup) { | 1065 | if (qgroup) { |
1045 | if (!list_empty(&qgroup->groups) || !list_empty(&qgroup->members)) { | 1066 | if (!list_empty(&qgroup->groups) || !list_empty(&qgroup->members)) { |
1046 | spin_unlock(&fs_info->qgroup_lock); | 1067 | spin_unlock(&fs_info->qgroup_lock); |
1047 | return -EBUSY; | 1068 | ret = -EBUSY; |
1069 | goto out; | ||
1048 | } | 1070 | } |
1049 | } | 1071 | } |
1050 | spin_unlock(&fs_info->qgroup_lock); | 1072 | spin_unlock(&fs_info->qgroup_lock); |
@@ -1054,7 +1076,8 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, | |||
1054 | spin_lock(&fs_info->qgroup_lock); | 1076 | spin_lock(&fs_info->qgroup_lock); |
1055 | del_qgroup_rb(quota_root->fs_info, qgroupid); | 1077 | del_qgroup_rb(quota_root->fs_info, qgroupid); |
1056 | spin_unlock(&fs_info->qgroup_lock); | 1078 | spin_unlock(&fs_info->qgroup_lock); |
1057 | 1079 | out: | |
1080 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
1058 | return ret; | 1081 | return ret; |
1059 | } | 1082 | } |
1060 | 1083 | ||
@@ -1062,12 +1085,16 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, | |||
1062 | struct btrfs_fs_info *fs_info, u64 qgroupid, | 1085 | struct btrfs_fs_info *fs_info, u64 qgroupid, |
1063 | struct btrfs_qgroup_limit *limit) | 1086 | struct btrfs_qgroup_limit *limit) |
1064 | { | 1087 | { |
1065 | struct btrfs_root *quota_root = fs_info->quota_root; | 1088 | struct btrfs_root *quota_root; |
1066 | struct btrfs_qgroup *qgroup; | 1089 | struct btrfs_qgroup *qgroup; |
1067 | int ret = 0; | 1090 | int ret = 0; |
1068 | 1091 | ||
1069 | if (!quota_root) | 1092 | mutex_lock(&fs_info->qgroup_ioctl_lock); |
1070 | return -EINVAL; | 1093 | quota_root = fs_info->quota_root; |
1094 | if (!quota_root) { | ||
1095 | ret = -EINVAL; | ||
1096 | goto out; | ||
1097 | } | ||
1071 | 1098 | ||
1072 | ret = update_qgroup_limit_item(trans, quota_root, qgroupid, | 1099 | ret = update_qgroup_limit_item(trans, quota_root, qgroupid, |
1073 | limit->flags, limit->max_rfer, | 1100 | limit->flags, limit->max_rfer, |
@@ -1094,7 +1121,8 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, | |||
1094 | 1121 | ||
1095 | unlock: | 1122 | unlock: |
1096 | spin_unlock(&fs_info->qgroup_lock); | 1123 | spin_unlock(&fs_info->qgroup_lock); |
1097 | 1124 | out: | |
1125 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
1098 | return ret; | 1126 | return ret; |
1099 | } | 1127 | } |
1100 | 1128 | ||
@@ -1392,11 +1420,14 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
1392 | struct btrfs_qgroup *dstgroup; | 1420 | struct btrfs_qgroup *dstgroup; |
1393 | u32 level_size = 0; | 1421 | u32 level_size = 0; |
1394 | 1422 | ||
1423 | mutex_lock(&fs_info->qgroup_ioctl_lock); | ||
1395 | if (!fs_info->quota_enabled) | 1424 | if (!fs_info->quota_enabled) |
1396 | return 0; | 1425 | goto out; |
1397 | 1426 | ||
1398 | if (!quota_root) | 1427 | if (!quota_root) { |
1399 | return -EINVAL; | 1428 | ret = -EINVAL; |
1429 | goto out; | ||
1430 | } | ||
1400 | 1431 | ||
1401 | /* | 1432 | /* |
1402 | * create a tracking group for the subvol itself | 1433 | * create a tracking group for the subvol itself |
@@ -1523,6 +1554,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
1523 | unlock: | 1554 | unlock: |
1524 | spin_unlock(&fs_info->qgroup_lock); | 1555 | spin_unlock(&fs_info->qgroup_lock); |
1525 | out: | 1556 | out: |
1557 | mutex_unlock(&fs_info->qgroup_ioctl_lock); | ||
1526 | return ret; | 1558 | return ret; |
1527 | } | 1559 | } |
1528 | 1560 | ||