diff options
author | Jeff Layton <jlayton@primarydata.com> | 2014-08-11 14:20:31 -0400 |
---|---|---|
committer | Jeff Layton <jlayton@primarydata.com> | 2014-08-14 10:07:47 -0400 |
commit | ed9814d85810c27670987b40c77e8a07105838fe (patch) | |
tree | 445d69a6adf6a9f9aaedd118f714c07abc96ba1b /fs | |
parent | b84d49f9440b2b039828f3eb114e4bd4ebeb0c54 (diff) |
locks: defer freeing locks in locks_delete_lock until after i_lock has been dropped
In commit 72f98e72551fa (locks: turn lock_flocks into a spinlock), we
moved from using the BKL to a global spinlock. With this change, we lost
the ability to block in the fl_release_private operation.
This is problematic for NFS (and probably some other filesystems as
well). Add a new list_head argument to locks_delete_lock. If that
argument is non-NULL, then queue any locks that we want to free to the
list instead of freeing them.
Then, add a new locks_dispose_list function that will walk such a list
and call locks_free_lock on them after the i_lock has been dropped.
Finally, change all of the callers of locks_delete_lock to pass in a
list_head, except for lease_modify. That function can be called long
after the i_lock has been acquired. Deferring the freeing of a lease
after unlocking it in that function is non-trivial until we overhaul
some of the spinlocking in the lease code.
Currently though, no filesystem that sets fl_release_private supports
leases, so this is not currently a problem. We'll eventually want to
make the same change in the lease code, but it needs a lot more work
before we can reasonably do so.
Acked-by: J. Bruce Fields <bfields@fieldses.org>
Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/locks.c | 38 |
1 files changed, 30 insertions, 8 deletions
diff --git a/fs/locks.c b/fs/locks.c index 7dd4defb4d8d..4ce087cca501 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)); |
@@ -651,12 +663,16 @@ static void locks_unlink_lock(struct file_lock **thisfl_p) | |||
651 | * | 663 | * |
652 | * Must be called with i_lock held! | 664 | * Must be called with i_lock held! |
653 | */ | 665 | */ |
654 | 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) | ||
655 | { | 668 | { |
656 | struct file_lock *fl = *thisfl_p; | 669 | struct file_lock *fl = *thisfl_p; |
657 | 670 | ||
658 | locks_unlink_lock(thisfl_p); | 671 | locks_unlink_lock(thisfl_p); |
659 | locks_free_lock(fl); | 672 | if (dispose) |
673 | list_add(&fl->fl_block, dispose); | ||
674 | else | ||
675 | locks_free_lock(fl); | ||
660 | } | 676 | } |
661 | 677 | ||
662 | /* Determine if lock sys_fl blocks lock caller_fl. Common functionality | 678 | /* Determine if lock sys_fl blocks lock caller_fl. Common functionality |
@@ -812,6 +828,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
812 | struct inode * inode = file_inode(filp); | 828 | struct inode * inode = file_inode(filp); |
813 | int error = 0; | 829 | int error = 0; |
814 | int found = 0; | 830 | int found = 0; |
831 | LIST_HEAD(dispose); | ||
815 | 832 | ||
816 | if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { | 833 | if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { |
817 | new_fl = locks_alloc_lock(); | 834 | new_fl = locks_alloc_lock(); |
@@ -834,7 +851,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
834 | if (request->fl_type == fl->fl_type) | 851 | if (request->fl_type == fl->fl_type) |
835 | goto out; | 852 | goto out; |
836 | found = 1; | 853 | found = 1; |
837 | locks_delete_lock(before); | 854 | locks_delete_lock(before, &dispose); |
838 | break; | 855 | break; |
839 | } | 856 | } |
840 | 857 | ||
@@ -881,6 +898,7 @@ out: | |||
881 | spin_unlock(&inode->i_lock); | 898 | spin_unlock(&inode->i_lock); |
882 | if (new_fl) | 899 | if (new_fl) |
883 | locks_free_lock(new_fl); | 900 | locks_free_lock(new_fl); |
901 | locks_dispose_list(&dispose); | ||
884 | return error; | 902 | return error; |
885 | } | 903 | } |
886 | 904 | ||
@@ -894,6 +912,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
894 | struct file_lock **before; | 912 | struct file_lock **before; |
895 | int error; | 913 | int error; |
896 | bool added = false; | 914 | bool added = false; |
915 | LIST_HEAD(dispose); | ||
897 | 916 | ||
898 | /* | 917 | /* |
899 | * We may need two file_lock structures for this operation, | 918 | * We may need two file_lock structures for this operation, |
@@ -989,7 +1008,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
989 | else | 1008 | else |
990 | request->fl_end = fl->fl_end; | 1009 | request->fl_end = fl->fl_end; |
991 | if (added) { | 1010 | if (added) { |
992 | locks_delete_lock(before); | 1011 | locks_delete_lock(before, &dispose); |
993 | continue; | 1012 | continue; |
994 | } | 1013 | } |
995 | request = fl; | 1014 | request = fl; |
@@ -1019,7 +1038,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1019 | * one (This may happen several times). | 1038 | * one (This may happen several times). |
1020 | */ | 1039 | */ |
1021 | if (added) { | 1040 | if (added) { |
1022 | locks_delete_lock(before); | 1041 | locks_delete_lock(before, &dispose); |
1023 | continue; | 1042 | continue; |
1024 | } | 1043 | } |
1025 | /* | 1044 | /* |
@@ -1035,7 +1054,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1035 | locks_copy_lock(new_fl, request); | 1054 | locks_copy_lock(new_fl, request); |
1036 | request = new_fl; | 1055 | request = new_fl; |
1037 | new_fl = NULL; | 1056 | new_fl = NULL; |
1038 | locks_delete_lock(before); | 1057 | locks_delete_lock(before, &dispose); |
1039 | locks_insert_lock(before, request); | 1058 | locks_insert_lock(before, request); |
1040 | added = true; | 1059 | added = true; |
1041 | } | 1060 | } |
@@ -1097,6 +1116,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
1097 | locks_free_lock(new_fl); | 1116 | locks_free_lock(new_fl); |
1098 | if (new_fl2) | 1117 | if (new_fl2) |
1099 | locks_free_lock(new_fl2); | 1118 | locks_free_lock(new_fl2); |
1119 | locks_dispose_list(&dispose); | ||
1100 | return error; | 1120 | return error; |
1101 | } | 1121 | } |
1102 | 1122 | ||
@@ -1272,7 +1292,7 @@ int lease_modify(struct file_lock **before, int arg) | |||
1272 | 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); |
1273 | fl->fl_fasync = NULL; | 1293 | fl->fl_fasync = NULL; |
1274 | } | 1294 | } |
1275 | locks_delete_lock(before); | 1295 | locks_delete_lock(before, NULL); |
1276 | } | 1296 | } |
1277 | return 0; | 1297 | return 0; |
1278 | } | 1298 | } |
@@ -2324,6 +2344,7 @@ void locks_remove_file(struct file *filp) | |||
2324 | struct inode * inode = file_inode(filp); | 2344 | struct inode * inode = file_inode(filp); |
2325 | struct file_lock *fl; | 2345 | struct file_lock *fl; |
2326 | struct file_lock **before; | 2346 | struct file_lock **before; |
2347 | LIST_HEAD(dispose); | ||
2327 | 2348 | ||
2328 | if (!inode->i_flock) | 2349 | if (!inode->i_flock) |
2329 | return; | 2350 | return; |
@@ -2369,12 +2390,13 @@ void locks_remove_file(struct file *filp) | |||
2369 | fl->fl_type, fl->fl_flags, | 2390 | fl->fl_type, fl->fl_flags, |
2370 | fl->fl_start, fl->fl_end); | 2391 | fl->fl_start, fl->fl_end); |
2371 | 2392 | ||
2372 | locks_delete_lock(before); | 2393 | locks_delete_lock(before, &dispose); |
2373 | continue; | 2394 | continue; |
2374 | } | 2395 | } |
2375 | before = &fl->fl_next; | 2396 | before = &fl->fl_next; |
2376 | } | 2397 | } |
2377 | spin_unlock(&inode->i_lock); | 2398 | spin_unlock(&inode->i_lock); |
2399 | locks_dispose_list(&dispose); | ||
2378 | } | 2400 | } |
2379 | 2401 | ||
2380 | /** | 2402 | /** |