Skip to content

Commit ed9814d

Browse files
author
Jeff Layton
committed
locks: defer freeing locks in locks_delete_lock until after i_lock has been dropped
In commit 72f98e7 (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 <[email protected]> Signed-off-by: Jeff Layton <[email protected]>
1 parent b84d49f commit ed9814d

File tree

1 file changed

+30
-8
lines changed

1 file changed

+30
-8
lines changed

fs/locks.c

+30-8
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,18 @@ void locks_free_lock(struct file_lock *fl)
247247
}
248248
EXPORT_SYMBOL(locks_free_lock);
249249

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+
250262
void locks_init_lock(struct file_lock *fl)
251263
{
252264
memset(fl, 0, sizeof(struct file_lock));
@@ -651,12 +663,16 @@ static void locks_unlink_lock(struct file_lock **thisfl_p)
651663
*
652664
* Must be called with i_lock held!
653665
*/
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)
655668
{
656669
struct file_lock *fl = *thisfl_p;
657670

658671
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);
660676
}
661677

662678
/* 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)
812828
struct inode * inode = file_inode(filp);
813829
int error = 0;
814830
int found = 0;
831+
LIST_HEAD(dispose);
815832

816833
if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) {
817834
new_fl = locks_alloc_lock();
@@ -834,7 +851,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
834851
if (request->fl_type == fl->fl_type)
835852
goto out;
836853
found = 1;
837-
locks_delete_lock(before);
854+
locks_delete_lock(before, &dispose);
838855
break;
839856
}
840857

@@ -881,6 +898,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
881898
spin_unlock(&inode->i_lock);
882899
if (new_fl)
883900
locks_free_lock(new_fl);
901+
locks_dispose_list(&dispose);
884902
return error;
885903
}
886904

@@ -894,6 +912,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
894912
struct file_lock **before;
895913
int error;
896914
bool added = false;
915+
LIST_HEAD(dispose);
897916

898917
/*
899918
* 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
9891008
else
9901009
request->fl_end = fl->fl_end;
9911010
if (added) {
992-
locks_delete_lock(before);
1011+
locks_delete_lock(before, &dispose);
9931012
continue;
9941013
}
9951014
request = fl;
@@ -1019,7 +1038,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
10191038
* one (This may happen several times).
10201039
*/
10211040
if (added) {
1022-
locks_delete_lock(before);
1041+
locks_delete_lock(before, &dispose);
10231042
continue;
10241043
}
10251044
/*
@@ -1035,7 +1054,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
10351054
locks_copy_lock(new_fl, request);
10361055
request = new_fl;
10371056
new_fl = NULL;
1038-
locks_delete_lock(before);
1057+
locks_delete_lock(before, &dispose);
10391058
locks_insert_lock(before, request);
10401059
added = true;
10411060
}
@@ -1097,6 +1116,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
10971116
locks_free_lock(new_fl);
10981117
if (new_fl2)
10991118
locks_free_lock(new_fl2);
1119+
locks_dispose_list(&dispose);
11001120
return error;
11011121
}
11021122

@@ -1272,7 +1292,7 @@ int lease_modify(struct file_lock **before, int arg)
12721292
printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
12731293
fl->fl_fasync = NULL;
12741294
}
1275-
locks_delete_lock(before);
1295+
locks_delete_lock(before, NULL);
12761296
}
12771297
return 0;
12781298
}
@@ -2324,6 +2344,7 @@ void locks_remove_file(struct file *filp)
23242344
struct inode * inode = file_inode(filp);
23252345
struct file_lock *fl;
23262346
struct file_lock **before;
2347+
LIST_HEAD(dispose);
23272348

23282349
if (!inode->i_flock)
23292350
return;
@@ -2369,12 +2390,13 @@ void locks_remove_file(struct file *filp)
23692390
fl->fl_type, fl->fl_flags,
23702391
fl->fl_start, fl->fl_end);
23712392

2372-
locks_delete_lock(before);
2393+
locks_delete_lock(before, &dispose);
23732394
continue;
23742395
}
23752396
before = &fl->fl_next;
23762397
}
23772398
spin_unlock(&inode->i_lock);
2399+
locks_dispose_list(&dispose);
23782400
}
23792401

23802402
/**

0 commit comments

Comments
 (0)