diff options
Diffstat (limited to 'fs/locks.c')
-rw-r--r-- | fs/locks.c | 99 |
1 files changed, 67 insertions, 32 deletions
diff --git a/fs/locks.c b/fs/locks.c index 528fedfda15e..653faabb07f4 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -203,11 +203,11 @@ static struct kmem_cache *flctx_cache __read_mostly; | |||
203 | static struct kmem_cache *filelock_cache __read_mostly; | 203 | static struct kmem_cache *filelock_cache __read_mostly; |
204 | 204 | ||
205 | static struct file_lock_context * | 205 | static struct file_lock_context * |
206 | locks_get_lock_context(struct inode *inode) | 206 | locks_get_lock_context(struct inode *inode, int type) |
207 | { | 207 | { |
208 | struct file_lock_context *new; | 208 | struct file_lock_context *new; |
209 | 209 | ||
210 | if (likely(inode->i_flctx)) | 210 | if (likely(inode->i_flctx) || type == F_UNLCK) |
211 | goto out; | 211 | goto out; |
212 | 212 | ||
213 | new = kmem_cache_alloc(flctx_cache, GFP_KERNEL); | 213 | new = kmem_cache_alloc(flctx_cache, GFP_KERNEL); |
@@ -223,14 +223,7 @@ locks_get_lock_context(struct inode *inode) | |||
223 | * Assign the pointer if it's not already assigned. If it is, then | 223 | * Assign the pointer if it's not already assigned. If it is, then |
224 | * free the context we just allocated. | 224 | * free the context we just allocated. |
225 | */ | 225 | */ |
226 | spin_lock(&inode->i_lock); | 226 | if (cmpxchg(&inode->i_flctx, NULL, new)) |
227 | if (likely(!inode->i_flctx)) { | ||
228 | inode->i_flctx = new; | ||
229 | new = NULL; | ||
230 | } | ||
231 | spin_unlock(&inode->i_lock); | ||
232 | |||
233 | if (new) | ||
234 | kmem_cache_free(flctx_cache, new); | 227 | kmem_cache_free(flctx_cache, new); |
235 | out: | 228 | out: |
236 | return inode->i_flctx; | 229 | return inode->i_flctx; |
@@ -276,8 +269,10 @@ void locks_release_private(struct file_lock *fl) | |||
276 | } | 269 | } |
277 | 270 | ||
278 | if (fl->fl_lmops) { | 271 | if (fl->fl_lmops) { |
279 | if (fl->fl_lmops->lm_put_owner) | 272 | if (fl->fl_lmops->lm_put_owner) { |
280 | fl->fl_lmops->lm_put_owner(fl); | 273 | fl->fl_lmops->lm_put_owner(fl->fl_owner); |
274 | fl->fl_owner = NULL; | ||
275 | } | ||
281 | fl->fl_lmops = NULL; | 276 | fl->fl_lmops = NULL; |
282 | } | 277 | } |
283 | } | 278 | } |
@@ -333,7 +328,7 @@ void locks_copy_conflock(struct file_lock *new, struct file_lock *fl) | |||
333 | 328 | ||
334 | if (fl->fl_lmops) { | 329 | if (fl->fl_lmops) { |
335 | if (fl->fl_lmops->lm_get_owner) | 330 | if (fl->fl_lmops->lm_get_owner) |
336 | fl->fl_lmops->lm_get_owner(new, fl); | 331 | fl->fl_lmops->lm_get_owner(fl->fl_owner); |
337 | } | 332 | } |
338 | } | 333 | } |
339 | EXPORT_SYMBOL(locks_copy_conflock); | 334 | EXPORT_SYMBOL(locks_copy_conflock); |
@@ -592,11 +587,15 @@ posix_owner_key(struct file_lock *fl) | |||
592 | 587 | ||
593 | static void locks_insert_global_blocked(struct file_lock *waiter) | 588 | static void locks_insert_global_blocked(struct file_lock *waiter) |
594 | { | 589 | { |
590 | lockdep_assert_held(&blocked_lock_lock); | ||
591 | |||
595 | hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter)); | 592 | hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter)); |
596 | } | 593 | } |
597 | 594 | ||
598 | static void locks_delete_global_blocked(struct file_lock *waiter) | 595 | static void locks_delete_global_blocked(struct file_lock *waiter) |
599 | { | 596 | { |
597 | lockdep_assert_held(&blocked_lock_lock); | ||
598 | |||
600 | hash_del(&waiter->fl_link); | 599 | hash_del(&waiter->fl_link); |
601 | } | 600 | } |
602 | 601 | ||
@@ -730,7 +729,7 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s | |||
730 | /* POSIX locks owned by the same process do not conflict with | 729 | /* POSIX locks owned by the same process do not conflict with |
731 | * each other. | 730 | * each other. |
732 | */ | 731 | */ |
733 | if (!IS_POSIX(sys_fl) || posix_same_owner(caller_fl, sys_fl)) | 732 | if (posix_same_owner(caller_fl, sys_fl)) |
734 | return (0); | 733 | return (0); |
735 | 734 | ||
736 | /* Check whether they overlap */ | 735 | /* Check whether they overlap */ |
@@ -748,7 +747,7 @@ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *s | |||
748 | /* FLOCK locks referring to the same filp do not conflict with | 747 | /* FLOCK locks referring to the same filp do not conflict with |
749 | * each other. | 748 | * each other. |
750 | */ | 749 | */ |
751 | if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) | 750 | if (caller_fl->fl_file == sys_fl->fl_file) |
752 | return (0); | 751 | return (0); |
753 | if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) | 752 | if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) |
754 | return 0; | 753 | return 0; |
@@ -838,6 +837,8 @@ static int posix_locks_deadlock(struct file_lock *caller_fl, | |||
838 | { | 837 | { |
839 | int i = 0; | 838 | int i = 0; |
840 | 839 | ||
840 | lockdep_assert_held(&blocked_lock_lock); | ||
841 | |||
841 | /* | 842 | /* |
842 | * This deadlock detector can't reasonably detect deadlocks with | 843 | * This deadlock detector can't reasonably detect deadlocks with |
843 | * FL_OFDLCK locks, since they aren't owned by a process, per-se. | 844 | * FL_OFDLCK locks, since they aren't owned by a process, per-se. |
@@ -871,9 +872,12 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
871 | bool found = false; | 872 | bool found = false; |
872 | LIST_HEAD(dispose); | 873 | LIST_HEAD(dispose); |
873 | 874 | ||
874 | ctx = locks_get_lock_context(inode); | 875 | ctx = locks_get_lock_context(inode, request->fl_type); |
875 | if (!ctx) | 876 | if (!ctx) { |
876 | return -ENOMEM; | 877 | if (request->fl_type != F_UNLCK) |
878 | return -ENOMEM; | ||
879 | return (request->fl_flags & FL_EXISTS) ? -ENOENT : 0; | ||
880 | } | ||
877 | 881 | ||
878 | if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { | 882 | if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { |
879 | new_fl = locks_alloc_lock(); | 883 | new_fl = locks_alloc_lock(); |
@@ -939,9 +943,9 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
939 | bool added = false; | 943 | bool added = false; |
940 | LIST_HEAD(dispose); | 944 | LIST_HEAD(dispose); |
941 | 945 | ||
942 | ctx = locks_get_lock_context(inode); | 946 | ctx = locks_get_lock_context(inode, request->fl_type); |
943 | if (!ctx) | 947 | if (!ctx) |
944 | return -ENOMEM; | 948 | return (request->fl_type == F_UNLCK) ? 0 : -ENOMEM; |
945 | 949 | ||
946 | /* | 950 | /* |
947 | * We may need two file_lock structures for this operation, | 951 | * We may need two file_lock structures for this operation, |
@@ -964,8 +968,6 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
964 | */ | 968 | */ |
965 | if (request->fl_type != F_UNLCK) { | 969 | if (request->fl_type != F_UNLCK) { |
966 | list_for_each_entry(fl, &ctx->flc_posix, fl_list) { | 970 | list_for_each_entry(fl, &ctx->flc_posix, fl_list) { |
967 | if (!IS_POSIX(fl)) | ||
968 | continue; | ||
969 | if (!posix_locks_conflict(request, fl)) | 971 | if (!posix_locks_conflict(request, fl)) |
970 | continue; | 972 | continue; |
971 | if (conflock) | 973 | if (conflock) |
@@ -1388,9 +1390,8 @@ any_leases_conflict(struct inode *inode, struct file_lock *breaker) | |||
1388 | int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) | 1390 | int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) |
1389 | { | 1391 | { |
1390 | int error = 0; | 1392 | int error = 0; |
1391 | struct file_lock *new_fl; | ||
1392 | struct file_lock_context *ctx = inode->i_flctx; | 1393 | struct file_lock_context *ctx = inode->i_flctx; |
1393 | struct file_lock *fl; | 1394 | struct file_lock *new_fl, *fl, *tmp; |
1394 | unsigned long break_time; | 1395 | unsigned long break_time; |
1395 | int want_write = (mode & O_ACCMODE) != O_RDONLY; | 1396 | int want_write = (mode & O_ACCMODE) != O_RDONLY; |
1396 | LIST_HEAD(dispose); | 1397 | LIST_HEAD(dispose); |
@@ -1420,7 +1421,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) | |||
1420 | break_time++; /* so that 0 means no break time */ | 1421 | break_time++; /* so that 0 means no break time */ |
1421 | } | 1422 | } |
1422 | 1423 | ||
1423 | list_for_each_entry(fl, &ctx->flc_lease, fl_list) { | 1424 | list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) { |
1424 | if (!leases_conflict(fl, new_fl)) | 1425 | if (!leases_conflict(fl, new_fl)) |
1425 | continue; | 1426 | continue; |
1426 | if (want_write) { | 1427 | if (want_write) { |
@@ -1606,7 +1607,8 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr | |||
1606 | lease = *flp; | 1607 | lease = *flp; |
1607 | trace_generic_add_lease(inode, lease); | 1608 | trace_generic_add_lease(inode, lease); |
1608 | 1609 | ||
1609 | ctx = locks_get_lock_context(inode); | 1610 | /* Note that arg is never F_UNLCK here */ |
1611 | ctx = locks_get_lock_context(inode, arg); | ||
1610 | if (!ctx) | 1612 | if (!ctx) |
1611 | return -ENOMEM; | 1613 | return -ENOMEM; |
1612 | 1614 | ||
@@ -2556,15 +2558,10 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, | |||
2556 | : (fl->fl_type == F_WRLCK) ? "WRITE" : "READ "); | 2558 | : (fl->fl_type == F_WRLCK) ? "WRITE" : "READ "); |
2557 | } | 2559 | } |
2558 | if (inode) { | 2560 | if (inode) { |
2559 | #ifdef WE_CAN_BREAK_LSLK_NOW | 2561 | /* userspace relies on this representation of dev_t */ |
2560 | seq_printf(f, "%d %s:%ld ", fl_pid, | ||
2561 | inode->i_sb->s_id, inode->i_ino); | ||
2562 | #else | ||
2563 | /* userspace relies on this representation of dev_t ;-( */ | ||
2564 | seq_printf(f, "%d %02x:%02x:%ld ", fl_pid, | 2562 | seq_printf(f, "%d %02x:%02x:%ld ", fl_pid, |
2565 | MAJOR(inode->i_sb->s_dev), | 2563 | MAJOR(inode->i_sb->s_dev), |
2566 | MINOR(inode->i_sb->s_dev), inode->i_ino); | 2564 | MINOR(inode->i_sb->s_dev), inode->i_ino); |
2567 | #endif | ||
2568 | } else { | 2565 | } else { |
2569 | seq_printf(f, "%d <none>:0 ", fl_pid); | 2566 | seq_printf(f, "%d <none>:0 ", fl_pid); |
2570 | } | 2567 | } |
@@ -2593,6 +2590,44 @@ static int locks_show(struct seq_file *f, void *v) | |||
2593 | return 0; | 2590 | return 0; |
2594 | } | 2591 | } |
2595 | 2592 | ||
2593 | static void __show_fd_locks(struct seq_file *f, | ||
2594 | struct list_head *head, int *id, | ||
2595 | struct file *filp, struct files_struct *files) | ||
2596 | { | ||
2597 | struct file_lock *fl; | ||
2598 | |||
2599 | list_for_each_entry(fl, head, fl_list) { | ||
2600 | |||
2601 | if (filp != fl->fl_file) | ||
2602 | continue; | ||
2603 | if (fl->fl_owner != files && | ||
2604 | fl->fl_owner != filp) | ||
2605 | continue; | ||
2606 | |||
2607 | (*id)++; | ||
2608 | seq_puts(f, "lock:\t"); | ||
2609 | lock_get_status(f, fl, *id, ""); | ||
2610 | } | ||
2611 | } | ||
2612 | |||
2613 | void show_fd_locks(struct seq_file *f, | ||
2614 | struct file *filp, struct files_struct *files) | ||
2615 | { | ||
2616 | struct inode *inode = file_inode(filp); | ||
2617 | struct file_lock_context *ctx; | ||
2618 | int id = 0; | ||
2619 | |||
2620 | ctx = inode->i_flctx; | ||
2621 | if (!ctx) | ||
2622 | return; | ||
2623 | |||
2624 | spin_lock(&ctx->flc_lock); | ||
2625 | __show_fd_locks(f, &ctx->flc_flock, &id, filp, files); | ||
2626 | __show_fd_locks(f, &ctx->flc_posix, &id, filp, files); | ||
2627 | __show_fd_locks(f, &ctx->flc_lease, &id, filp, files); | ||
2628 | spin_unlock(&ctx->flc_lock); | ||
2629 | } | ||
2630 | |||
2596 | static void *locks_start(struct seq_file *f, loff_t *pos) | 2631 | static void *locks_start(struct seq_file *f, loff_t *pos) |
2597 | __acquires(&blocked_lock_lock) | 2632 | __acquires(&blocked_lock_lock) |
2598 | { | 2633 | { |