diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-16 10:58:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-16 10:58:47 -0400 |
commit | 53b95d6341c142a02538e41bdf1405ef8888bf8b (patch) | |
tree | f87d6f57029b6c101c9909cd143de34196c63b1f /fs | |
parent | da06df548e2b82848bcc32342234d0f04340a41c (diff) | |
parent | 2ece173e4715031c031de9114491eee80a69cf68 (diff) |
Merge tag 'locks-v3.17-2' of git://git.samba.org/jlayton/linux
Pull file locking bugfixes from Jeff Layton:
"Most of these patches are to fix a long-standing regression that crept
in when the BKL was removed from the file-locking code. The code was
converted to use a conventional spinlock, but some fl_release_private
ops can block and you can end up sleeping inside the lock.
There's also a patch to make /proc/locks show delegations as 'DELEG'"
* tag 'locks-v3.17-2' of git://git.samba.org/jlayton/linux:
locks: update Locking documentation to clarify fl_release_private behavior
locks: move locks_free_lock calls in do_fcntl_add_lease outside spinlock
locks: defer freeing locks in locks_delete_lock until after i_lock has been dropped
locks: don't reuse file_lock in __posix_lock_file
locks: don't call locks_release_private from locks_copy_lock
locks: show delegations as "DELEG" in /proc/locks
Diffstat (limited to 'fs')
-rw-r--r-- | fs/locks.c | 86 |
1 files changed, 57 insertions, 29 deletions
diff --git a/fs/locks.c b/fs/locks.c index a6f54802d277..cb66fb05ad4a 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -247,6 +247,18 @@ void locks_free_lock(struct file_lock *fl) | |||
247 | } | 247 | } |
248 | EXPORT_SYMBOL(locks_free_lock); | 248 | EXPORT_SYMBOL(locks_free_lock); |
249 | 249 | ||
250 | static void | ||
251 | locks_dispose_list(struct list_head *dispose) | ||
252 | { | ||
253 | struct file_lock *fl; | ||
254 | |||
255 | while (!list_empty(dispose)) { | ||
256 | fl = list_first_entry(dispose, struct file_lock, fl_block); | ||
257 | list_del_init(&fl->fl_block); | ||
258 | locks_free_lock(fl); | ||
259 | } | ||
260 | } | ||
261 | |||
250 | void locks_init_lock(struct file_lock *fl) | 262 | void locks_init_lock(struct file_lock *fl) |
251 | { | 263 | { |
252 | memset(fl, 0, sizeof(struct file_lock)); | 264 | memset(fl, 0, sizeof(struct file_lock)); |
@@ -285,7 +297,8 @@ EXPORT_SYMBOL(__locks_copy_lock); | |||
285 | 297 | ||
286 | void locks_copy_lock(struct file_lock *new, struct file_lock *fl) | 298 | void locks_copy_lock(struct file_lock *new, struct file_lock *fl) |
287 | { | 299 | { |
288 | locks_release_private(new); | 300 | /* "new" must be a freshly-initialized lock */ |
301 | WARN_ON_ONCE(new->fl_ops); | ||
289 | 302 | ||
290 | __locks_copy_lock(new, fl); | 303 | __locks_copy_lock(new, fl); |
291 | new->fl_file = fl->fl_file; | 304 | new->fl_file = fl->fl_file; |
@@ -650,12 +663,16 @@ static void locks_unlink_lock(struct file_lock **thisfl_p) | |||
650 | * | 663 | * |
651 | * Must be called with i_lock held! | 664 | * Must be called with i_lock held! |
652 | */ | 665 | */ |
653 | static void locks_delete_lock(struct file_lock **thisfl_p) | 666 | static void locks_delete_lock(struct file_lock **thisfl_p, |
667 | struct list_head *dispose) | ||
654 | { | 668 | { |
655 | struct file_lock *fl = *thisfl_p; | 669 | struct file_lock *fl = *thisfl_p; |
656 | 670 | ||
657 | locks_unlink_lock(thisfl_p); | 671 | locks_unlink_lock(thisfl_p); |
658 | locks_free_lock(fl); | 672 | if (dispose) |
673 | list_add(&fl->fl_block, dispose); | ||
674 | else | ||
675 | locks_free_lock(fl); | ||
659 | } | 676 | } |
660 | 677 | ||
661 | /* Determine if lock sys_fl blocks lock caller_fl. Common functionality | 678 | /* Determine if lock sys_fl blocks lock caller_fl. Common functionality |
@@ -811,6 +828,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
811 | struct inode * inode = file_inode(filp); | 828 | struct inode * inode = file_inode(filp); |
812 | int error = 0; | 829 | int error = 0; |
813 | int found = 0; | 830 | int found = 0; |
831 | LIST_HEAD(dispose); | ||
814 | 832 | ||
815 | if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { | 833 | if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { |
816 | new_fl = locks_alloc_lock(); | 834 | new_fl = locks_alloc_lock(); |
@@ -833,7 +851,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
833 | if (request->fl_type == fl->fl_type) | 851 | if (request->fl_type == fl->fl_type) |
834 | goto out; | 852 | goto out; |
835 | found = 1; | 853 | found = 1; |
836 | locks_delete_lock(before); | 854 | locks_delete_lock(before, &dispose); |
837 | break; | 855 | break; |
838 | } | 856 | } |
839 | 857 | ||
@@ -880,6 +898,7 @@ out: | |||
880 | spin_unlock(&inode->i_lock); | 898 | spin_unlock(&inode->i_lock); |
881 | if (new_fl) | 899 | if (new_fl) |
882 | locks_free_lock(new_fl); | 900 | locks_free_lock(new_fl); |
901 | locks_dispose_list(&dispose); | ||
883 | return error; | 902 | return error; |
884 | } | 903 | } |
885 | 904 | ||
@@ -893,6 +912,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
893 | struct file_lock **before; | 912 | struct file_lock **before; |
894 | int error; | 913 | int error; |
895 | bool added = false; | 914 | bool added = false; |
915 | LIST_HEAD(dispose); | ||
896 | 916 | ||
897 | /* | 917 | /* |
898 | * We may need two file_lock structures for this operation, | 918 | * We may need two file_lock structures for this operation, |
@@ -988,7 +1008,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
988 | else | 1008 | else |
989 | request->fl_end = fl->fl_end; | 1009 | request->fl_end = fl->fl_end; |
990 | if (added) { | 1010 | if (added) { |
991 | locks_delete_lock(before); | 1011 | locks_delete_lock(before, &dispose); |
992 | continue; | 1012 | continue; |
993 | } | 1013 | } |
994 | request = fl; | 1014 | request = fl; |
@@ -1018,21 +1038,24 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1018 | * one (This may happen several times). | 1038 | * one (This may happen several times). |
1019 | */ | 1039 | */ |
1020 | if (added) { | 1040 | if (added) { |
1021 | locks_delete_lock(before); | 1041 | locks_delete_lock(before, &dispose); |
1022 | continue; | 1042 | continue; |
1023 | } | 1043 | } |
1024 | /* Replace the old lock with the new one. | 1044 | /* |
1025 | * Wake up anybody waiting for the old one, | 1045 | * Replace the old lock with new_fl, and |
1026 | * as the change in lock type might satisfy | 1046 | * remove the old one. It's safe to do the |
1027 | * their needs. | 1047 | * insert here since we know that we won't be |
1048 | * using new_fl later, and that the lock is | ||
1049 | * just replacing an existing lock. | ||
1028 | */ | 1050 | */ |
1029 | locks_wake_up_blocks(fl); | 1051 | error = -ENOLCK; |
1030 | fl->fl_start = request->fl_start; | 1052 | if (!new_fl) |
1031 | fl->fl_end = request->fl_end; | 1053 | goto out; |
1032 | fl->fl_type = request->fl_type; | 1054 | locks_copy_lock(new_fl, request); |
1033 | locks_release_private(fl); | 1055 | request = new_fl; |
1034 | locks_copy_private(fl, request); | 1056 | new_fl = NULL; |
1035 | request = fl; | 1057 | locks_delete_lock(before, &dispose); |
1058 | locks_insert_lock(before, request); | ||
1036 | added = true; | 1059 | added = true; |
1037 | } | 1060 | } |
1038 | } | 1061 | } |
@@ -1093,6 +1116,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1093 | locks_free_lock(new_fl); | 1116 | locks_free_lock(new_fl); |
1094 | if (new_fl2) | 1117 | if (new_fl2) |
1095 | locks_free_lock(new_fl2); | 1118 | locks_free_lock(new_fl2); |
1119 | locks_dispose_list(&dispose); | ||
1096 | return error; | 1120 | return error; |
1097 | } | 1121 | } |
1098 | 1122 | ||
@@ -1268,7 +1292,7 @@ int lease_modify(struct file_lock **before, int arg) | |||
1268 | printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync); | 1292 | printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync); |
1269 | fl->fl_fasync = NULL; | 1293 | fl->fl_fasync = NULL; |
1270 | } | 1294 | } |
1271 | locks_delete_lock(before); | 1295 | locks_delete_lock(before, NULL); |
1272 | } | 1296 | } |
1273 | return 0; | 1297 | return 0; |
1274 | } | 1298 | } |
@@ -1737,13 +1761,10 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) | |||
1737 | ret = fl; | 1761 | ret = fl; |
1738 | spin_lock(&inode->i_lock); | 1762 | spin_lock(&inode->i_lock); |
1739 | error = __vfs_setlease(filp, arg, &ret); | 1763 | error = __vfs_setlease(filp, arg, &ret); |
1740 | if (error) { | 1764 | if (error) |
1741 | spin_unlock(&inode->i_lock); | 1765 | goto out_unlock; |
1742 | locks_free_lock(fl); | 1766 | if (ret == fl) |
1743 | goto out_free_fasync; | 1767 | fl = NULL; |
1744 | } | ||
1745 | if (ret != fl) | ||
1746 | locks_free_lock(fl); | ||
1747 | 1768 | ||
1748 | /* | 1769 | /* |
1749 | * fasync_insert_entry() returns the old entry if any. | 1770 | * fasync_insert_entry() returns the old entry if any. |
@@ -1755,9 +1776,10 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) | |||
1755 | new = NULL; | 1776 | new = NULL; |
1756 | 1777 | ||
1757 | error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); | 1778 | error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); |
1779 | out_unlock: | ||
1758 | spin_unlock(&inode->i_lock); | 1780 | spin_unlock(&inode->i_lock); |
1759 | 1781 | if (fl) | |
1760 | out_free_fasync: | 1782 | locks_free_lock(fl); |
1761 | if (new) | 1783 | if (new) |
1762 | fasync_free(new); | 1784 | fasync_free(new); |
1763 | return error; | 1785 | return error; |
@@ -2320,6 +2342,7 @@ void locks_remove_file(struct file *filp) | |||
2320 | struct inode * inode = file_inode(filp); | 2342 | struct inode * inode = file_inode(filp); |
2321 | struct file_lock *fl; | 2343 | struct file_lock *fl; |
2322 | struct file_lock **before; | 2344 | struct file_lock **before; |
2345 | LIST_HEAD(dispose); | ||
2323 | 2346 | ||
2324 | if (!inode->i_flock) | 2347 | if (!inode->i_flock) |
2325 | return; | 2348 | return; |
@@ -2365,12 +2388,13 @@ void locks_remove_file(struct file *filp) | |||
2365 | fl->fl_type, fl->fl_flags, | 2388 | fl->fl_type, fl->fl_flags, |
2366 | fl->fl_start, fl->fl_end); | 2389 | fl->fl_start, fl->fl_end); |
2367 | 2390 | ||
2368 | locks_delete_lock(before); | 2391 | locks_delete_lock(before, &dispose); |
2369 | continue; | 2392 | continue; |
2370 | } | 2393 | } |
2371 | before = &fl->fl_next; | 2394 | before = &fl->fl_next; |
2372 | } | 2395 | } |
2373 | spin_unlock(&inode->i_lock); | 2396 | spin_unlock(&inode->i_lock); |
2397 | locks_dispose_list(&dispose); | ||
2374 | } | 2398 | } |
2375 | 2399 | ||
2376 | /** | 2400 | /** |
@@ -2452,7 +2476,11 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, | |||
2452 | seq_puts(f, "FLOCK ADVISORY "); | 2476 | seq_puts(f, "FLOCK ADVISORY "); |
2453 | } | 2477 | } |
2454 | } else if (IS_LEASE(fl)) { | 2478 | } else if (IS_LEASE(fl)) { |
2455 | seq_puts(f, "LEASE "); | 2479 | if (fl->fl_flags & FL_DELEG) |
2480 | seq_puts(f, "DELEG "); | ||
2481 | else | ||
2482 | seq_puts(f, "LEASE "); | ||
2483 | |||
2456 | if (lease_breaking(fl)) | 2484 | if (lease_breaking(fl)) |
2457 | seq_puts(f, "BREAKING "); | 2485 | seq_puts(f, "BREAKING "); |
2458 | else if (fl->fl_file) | 2486 | else if (fl->fl_file) |