diff options
Diffstat (limited to 'fs/locks.c')
-rw-r--r-- | fs/locks.c | 108 |
1 files changed, 57 insertions, 51 deletions
diff --git a/fs/locks.c b/fs/locks.c index 055df53f19de..e50bb4d9e757 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -157,9 +157,6 @@ static int target_leasetype(struct file_lock *fl) | |||
157 | int leases_enable = 1; | 157 | int leases_enable = 1; |
158 | int lease_break_time = 45; | 158 | int lease_break_time = 45; |
159 | 159 | ||
160 | #define for_each_lock(inode, lockp) \ | ||
161 | for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) | ||
162 | |||
163 | /* | 160 | /* |
164 | * The global file_lock_list is only used for displaying /proc/locks, so we | 161 | * The global file_lock_list is only used for displaying /proc/locks, so we |
165 | * keep a list on each CPU, with each list protected by its own spinlock via | 162 | * keep a list on each CPU, with each list protected by its own spinlock via |
@@ -218,6 +215,7 @@ locks_get_lock_context(struct inode *inode) | |||
218 | goto out; | 215 | goto out; |
219 | 216 | ||
220 | INIT_LIST_HEAD(&new->flc_flock); | 217 | INIT_LIST_HEAD(&new->flc_flock); |
218 | INIT_LIST_HEAD(&new->flc_posix); | ||
221 | 219 | ||
222 | /* | 220 | /* |
223 | * Assign the pointer if it's not already assigned. If it is, then | 221 | * Assign the pointer if it's not already assigned. If it is, then |
@@ -241,6 +239,7 @@ locks_free_lock_context(struct file_lock_context *ctx) | |||
241 | { | 239 | { |
242 | if (ctx) { | 240 | if (ctx) { |
243 | WARN_ON_ONCE(!list_empty(&ctx->flc_flock)); | 241 | WARN_ON_ONCE(!list_empty(&ctx->flc_flock)); |
242 | WARN_ON_ONCE(!list_empty(&ctx->flc_posix)); | ||
244 | kmem_cache_free(flctx_cache, ctx); | 243 | kmem_cache_free(flctx_cache, ctx); |
245 | } | 244 | } |
246 | } | 245 | } |
@@ -809,21 +808,26 @@ void | |||
809 | posix_test_lock(struct file *filp, struct file_lock *fl) | 808 | posix_test_lock(struct file *filp, struct file_lock *fl) |
810 | { | 809 | { |
811 | struct file_lock *cfl; | 810 | struct file_lock *cfl; |
811 | struct file_lock_context *ctx; | ||
812 | struct inode *inode = file_inode(filp); | 812 | struct inode *inode = file_inode(filp); |
813 | 813 | ||
814 | ctx = inode->i_flctx; | ||
815 | if (!ctx || list_empty_careful(&ctx->flc_posix)) { | ||
816 | fl->fl_type = F_UNLCK; | ||
817 | return; | ||
818 | } | ||
819 | |||
814 | spin_lock(&inode->i_lock); | 820 | spin_lock(&inode->i_lock); |
815 | for (cfl = file_inode(filp)->i_flock; cfl; cfl = cfl->fl_next) { | 821 | list_for_each_entry(cfl, &ctx->flc_posix, fl_list) { |
816 | if (!IS_POSIX(cfl)) | 822 | if (posix_locks_conflict(fl, cfl)) { |
817 | continue; | 823 | locks_copy_conflock(fl, cfl); |
818 | if (posix_locks_conflict(fl, cfl)) | 824 | if (cfl->fl_nspid) |
819 | break; | 825 | fl->fl_pid = pid_vnr(cfl->fl_nspid); |
826 | goto out; | ||
827 | } | ||
820 | } | 828 | } |
821 | if (cfl) { | 829 | fl->fl_type = F_UNLCK; |
822 | locks_copy_conflock(fl, cfl); | 830 | out: |
823 | if (cfl->fl_nspid) | ||
824 | fl->fl_pid = pid_vnr(cfl->fl_nspid); | ||
825 | } else | ||
826 | fl->fl_type = F_UNLCK; | ||
827 | spin_unlock(&inode->i_lock); | 831 | spin_unlock(&inode->i_lock); |
828 | return; | 832 | return; |
829 | } | 833 | } |
@@ -983,16 +987,20 @@ out: | |||
983 | 987 | ||
984 | static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) | 988 | static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) |
985 | { | 989 | { |
986 | struct file_lock *fl; | 990 | struct file_lock *fl, *tmp; |
987 | struct file_lock *new_fl = NULL; | 991 | struct file_lock *new_fl = NULL; |
988 | struct file_lock *new_fl2 = NULL; | 992 | struct file_lock *new_fl2 = NULL; |
989 | struct file_lock *left = NULL; | 993 | struct file_lock *left = NULL; |
990 | struct file_lock *right = NULL; | 994 | struct file_lock *right = NULL; |
991 | struct file_lock **before; | 995 | struct file_lock_context *ctx; |
992 | int error; | 996 | int error; |
993 | bool added = false; | 997 | bool added = false; |
994 | LIST_HEAD(dispose); | 998 | LIST_HEAD(dispose); |
995 | 999 | ||
1000 | ctx = locks_get_lock_context(inode); | ||
1001 | if (!ctx) | ||
1002 | return -ENOMEM; | ||
1003 | |||
996 | /* | 1004 | /* |
997 | * We may need two file_lock structures for this operation, | 1005 | * We may need two file_lock structures for this operation, |
998 | * so we get them in advance to avoid races. | 1006 | * so we get them in advance to avoid races. |
@@ -1013,8 +1021,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1013 | * blocker's list of waiters and the global blocked_hash. | 1021 | * blocker's list of waiters and the global blocked_hash. |
1014 | */ | 1022 | */ |
1015 | if (request->fl_type != F_UNLCK) { | 1023 | if (request->fl_type != F_UNLCK) { |
1016 | for_each_lock(inode, before) { | 1024 | list_for_each_entry(fl, &ctx->flc_posix, fl_list) { |
1017 | fl = *before; | ||
1018 | if (!IS_POSIX(fl)) | 1025 | if (!IS_POSIX(fl)) |
1019 | continue; | 1026 | continue; |
1020 | if (!posix_locks_conflict(request, fl)) | 1027 | if (!posix_locks_conflict(request, fl)) |
@@ -1044,29 +1051,25 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1044 | if (request->fl_flags & FL_ACCESS) | 1051 | if (request->fl_flags & FL_ACCESS) |
1045 | goto out; | 1052 | goto out; |
1046 | 1053 | ||
1047 | /* | 1054 | /* Find the first old lock with the same owner as the new lock */ |
1048 | * Find the first old lock with the same owner as the new lock. | 1055 | list_for_each_entry(fl, &ctx->flc_posix, fl_list) { |
1049 | */ | 1056 | if (posix_same_owner(request, fl)) |
1050 | 1057 | break; | |
1051 | before = &inode->i_flock; | ||
1052 | |||
1053 | /* First skip locks owned by other processes. */ | ||
1054 | while ((fl = *before) && (!IS_POSIX(fl) || | ||
1055 | !posix_same_owner(request, fl))) { | ||
1056 | before = &fl->fl_next; | ||
1057 | } | 1058 | } |
1058 | 1059 | ||
1059 | /* Process locks with this owner. */ | 1060 | /* Process locks with this owner. */ |
1060 | while ((fl = *before) && posix_same_owner(request, fl)) { | 1061 | list_for_each_entry_safe_from(fl, tmp, &ctx->flc_posix, fl_list) { |
1061 | /* Detect adjacent or overlapping regions (if same lock type) | 1062 | if (!posix_same_owner(request, fl)) |
1062 | */ | 1063 | break; |
1064 | |||
1065 | /* Detect adjacent or overlapping regions (if same lock type) */ | ||
1063 | if (request->fl_type == fl->fl_type) { | 1066 | if (request->fl_type == fl->fl_type) { |
1064 | /* In all comparisons of start vs end, use | 1067 | /* In all comparisons of start vs end, use |
1065 | * "start - 1" rather than "end + 1". If end | 1068 | * "start - 1" rather than "end + 1". If end |
1066 | * is OFFSET_MAX, end + 1 will become negative. | 1069 | * is OFFSET_MAX, end + 1 will become negative. |
1067 | */ | 1070 | */ |
1068 | if (fl->fl_end < request->fl_start - 1) | 1071 | if (fl->fl_end < request->fl_start - 1) |
1069 | goto next_lock; | 1072 | continue; |
1070 | /* If the next lock in the list has entirely bigger | 1073 | /* If the next lock in the list has entirely bigger |
1071 | * addresses than the new one, insert the lock here. | 1074 | * addresses than the new one, insert the lock here. |
1072 | */ | 1075 | */ |
@@ -1087,18 +1090,17 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1087 | else | 1090 | else |
1088 | request->fl_end = fl->fl_end; | 1091 | request->fl_end = fl->fl_end; |
1089 | if (added) { | 1092 | if (added) { |
1090 | locks_delete_lock(before, &dispose); | 1093 | locks_delete_lock_ctx(fl, &dispose); |
1091 | continue; | 1094 | continue; |
1092 | } | 1095 | } |
1093 | request = fl; | 1096 | request = fl; |
1094 | added = true; | 1097 | added = true; |
1095 | } | 1098 | } else { |
1096 | else { | ||
1097 | /* Processing for different lock types is a bit | 1099 | /* Processing for different lock types is a bit |
1098 | * more complex. | 1100 | * more complex. |
1099 | */ | 1101 | */ |
1100 | if (fl->fl_end < request->fl_start) | 1102 | if (fl->fl_end < request->fl_start) |
1101 | goto next_lock; | 1103 | continue; |
1102 | if (fl->fl_start > request->fl_end) | 1104 | if (fl->fl_start > request->fl_end) |
1103 | break; | 1105 | break; |
1104 | if (request->fl_type == F_UNLCK) | 1106 | if (request->fl_type == F_UNLCK) |
@@ -1117,7 +1119,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1117 | * one (This may happen several times). | 1119 | * one (This may happen several times). |
1118 | */ | 1120 | */ |
1119 | if (added) { | 1121 | if (added) { |
1120 | locks_delete_lock(before, &dispose); | 1122 | locks_delete_lock_ctx(fl, &dispose); |
1121 | continue; | 1123 | continue; |
1122 | } | 1124 | } |
1123 | /* | 1125 | /* |
@@ -1133,15 +1135,11 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1133 | locks_copy_lock(new_fl, request); | 1135 | locks_copy_lock(new_fl, request); |
1134 | request = new_fl; | 1136 | request = new_fl; |
1135 | new_fl = NULL; | 1137 | new_fl = NULL; |
1136 | locks_delete_lock(before, &dispose); | 1138 | locks_insert_lock_ctx(request, &fl->fl_list); |
1137 | locks_insert_lock(before, request); | 1139 | locks_delete_lock_ctx(fl, &dispose); |
1138 | added = true; | 1140 | added = true; |
1139 | } | 1141 | } |
1140 | } | 1142 | } |
1141 | /* Go on to next lock. | ||
1142 | */ | ||
1143 | next_lock: | ||
1144 | before = &fl->fl_next; | ||
1145 | } | 1143 | } |
1146 | 1144 | ||
1147 | /* | 1145 | /* |
@@ -1166,7 +1164,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1166 | goto out; | 1164 | goto out; |
1167 | } | 1165 | } |
1168 | locks_copy_lock(new_fl, request); | 1166 | locks_copy_lock(new_fl, request); |
1169 | locks_insert_lock(before, new_fl); | 1167 | locks_insert_lock_ctx(new_fl, &fl->fl_list); |
1170 | new_fl = NULL; | 1168 | new_fl = NULL; |
1171 | } | 1169 | } |
1172 | if (right) { | 1170 | if (right) { |
@@ -1177,7 +1175,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1177 | left = new_fl2; | 1175 | left = new_fl2; |
1178 | new_fl2 = NULL; | 1176 | new_fl2 = NULL; |
1179 | locks_copy_lock(left, right); | 1177 | locks_copy_lock(left, right); |
1180 | locks_insert_lock(before, left); | 1178 | locks_insert_lock_ctx(left, &fl->fl_list); |
1181 | } | 1179 | } |
1182 | right->fl_start = request->fl_end + 1; | 1180 | right->fl_start = request->fl_end + 1; |
1183 | locks_wake_up_blocks(right); | 1181 | locks_wake_up_blocks(right); |
@@ -1257,22 +1255,29 @@ EXPORT_SYMBOL(posix_lock_file_wait); | |||
1257 | */ | 1255 | */ |
1258 | int locks_mandatory_locked(struct file *file) | 1256 | int locks_mandatory_locked(struct file *file) |
1259 | { | 1257 | { |
1258 | int ret; | ||
1260 | struct inode *inode = file_inode(file); | 1259 | struct inode *inode = file_inode(file); |
1260 | struct file_lock_context *ctx; | ||
1261 | struct file_lock *fl; | 1261 | struct file_lock *fl; |
1262 | 1262 | ||
1263 | ctx = inode->i_flctx; | ||
1264 | if (!ctx || list_empty_careful(&ctx->flc_posix)) | ||
1265 | return 0; | ||
1266 | |||
1263 | /* | 1267 | /* |
1264 | * Search the lock list for this inode for any POSIX locks. | 1268 | * Search the lock list for this inode for any POSIX locks. |
1265 | */ | 1269 | */ |
1266 | spin_lock(&inode->i_lock); | 1270 | spin_lock(&inode->i_lock); |
1267 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | 1271 | ret = 0; |
1268 | if (!IS_POSIX(fl)) | 1272 | list_for_each_entry(fl, &ctx->flc_posix, fl_list) { |
1269 | continue; | ||
1270 | if (fl->fl_owner != current->files && | 1273 | if (fl->fl_owner != current->files && |
1271 | fl->fl_owner != file) | 1274 | fl->fl_owner != file) { |
1275 | ret = -EAGAIN; | ||
1272 | break; | 1276 | break; |
1277 | } | ||
1273 | } | 1278 | } |
1274 | spin_unlock(&inode->i_lock); | 1279 | spin_unlock(&inode->i_lock); |
1275 | return fl ? -EAGAIN : 0; | 1280 | return ret; |
1276 | } | 1281 | } |
1277 | 1282 | ||
1278 | /** | 1283 | /** |
@@ -2389,13 +2394,14 @@ out: | |||
2389 | void locks_remove_posix(struct file *filp, fl_owner_t owner) | 2394 | void locks_remove_posix(struct file *filp, fl_owner_t owner) |
2390 | { | 2395 | { |
2391 | struct file_lock lock; | 2396 | struct file_lock lock; |
2397 | struct file_lock_context *ctx = file_inode(filp)->i_flctx; | ||
2392 | 2398 | ||
2393 | /* | 2399 | /* |
2394 | * If there are no locks held on this file, we don't need to call | 2400 | * If there are no locks held on this file, we don't need to call |
2395 | * posix_lock_file(). Another process could be setting a lock on this | 2401 | * posix_lock_file(). Another process could be setting a lock on this |
2396 | * file at the same time, but we wouldn't remove that lock anyway. | 2402 | * file at the same time, but we wouldn't remove that lock anyway. |
2397 | */ | 2403 | */ |
2398 | if (!file_inode(filp)->i_flock) | 2404 | if (!ctx || list_empty(&ctx->flc_posix)) |
2399 | return; | 2405 | return; |
2400 | 2406 | ||
2401 | lock.fl_type = F_UNLCK; | 2407 | lock.fl_type = F_UNLCK; |