Skip to content

Commit dcf23ac

Browse files
committed
locks: reinstate locks_delete_block optimization
There is measurable performance impact in some synthetic tests due to commit 6d390e4 (locks: fix a potential use-after-free problem when wakeup a waiter). Fix the race condition instead by clearing the fl_blocker pointer after the wake_up, using explicit acquire/release semantics. This does mean that we can no longer use the clearing of fl_blocker as the wait condition, so switch the waiters over to checking whether the fl_blocked_member list_head is empty. Reviewed-by: yangerkun <[email protected]> Reviewed-by: NeilBrown <[email protected]> Fixes: 6d390e4 (locks: fix a potential use-after-free problem when wakeup a waiter) Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5076190 commit dcf23ac

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

fs/cifs/file.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,8 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
11691169
rc = posix_lock_file(file, flock, NULL);
11701170
up_write(&cinode->lock_sem);
11711171
if (rc == FILE_LOCK_DEFERRED) {
1172-
rc = wait_event_interruptible(flock->fl_wait, !flock->fl_blocker);
1172+
rc = wait_event_interruptible(flock->fl_wait,
1173+
list_empty(&flock->fl_blocked_member));
11731174
if (!rc)
11741175
goto try_again;
11751176
locks_delete_block(flock);

fs/locks.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,6 @@ static void __locks_delete_block(struct file_lock *waiter)
725725
{
726726
locks_delete_global_blocked(waiter);
727727
list_del_init(&waiter->fl_blocked_member);
728-
waiter->fl_blocker = NULL;
729728
}
730729

731730
static void __locks_wake_up_blocks(struct file_lock *blocker)
@@ -740,6 +739,13 @@ static void __locks_wake_up_blocks(struct file_lock *blocker)
740739
waiter->fl_lmops->lm_notify(waiter);
741740
else
742741
wake_up(&waiter->fl_wait);
742+
743+
/*
744+
* The setting of fl_blocker to NULL marks the "done"
745+
* point in deleting a block. Paired with acquire at the top
746+
* of locks_delete_block().
747+
*/
748+
smp_store_release(&waiter->fl_blocker, NULL);
743749
}
744750
}
745751

@@ -753,11 +759,42 @@ int locks_delete_block(struct file_lock *waiter)
753759
{
754760
int status = -ENOENT;
755761

762+
/*
763+
* If fl_blocker is NULL, it won't be set again as this thread "owns"
764+
* the lock and is the only one that might try to claim the lock.
765+
*
766+
* We use acquire/release to manage fl_blocker so that we can
767+
* optimize away taking the blocked_lock_lock in many cases.
768+
*
769+
* The smp_load_acquire guarantees two things:
770+
*
771+
* 1/ that fl_blocked_requests can be tested locklessly. If something
772+
* was recently added to that list it must have been in a locked region
773+
* *before* the locked region when fl_blocker was set to NULL.
774+
*
775+
* 2/ that no other thread is accessing 'waiter', so it is safe to free
776+
* it. __locks_wake_up_blocks is careful not to touch waiter after
777+
* fl_blocker is released.
778+
*
779+
* If a lockless check of fl_blocker shows it to be NULL, we know that
780+
* no new locks can be inserted into its fl_blocked_requests list, and
781+
* can avoid doing anything further if the list is empty.
782+
*/
783+
if (!smp_load_acquire(&waiter->fl_blocker) &&
784+
list_empty(&waiter->fl_blocked_requests))
785+
return status;
786+
756787
spin_lock(&blocked_lock_lock);
757788
if (waiter->fl_blocker)
758789
status = 0;
759790
__locks_wake_up_blocks(waiter);
760791
__locks_delete_block(waiter);
792+
793+
/*
794+
* The setting of fl_blocker to NULL marks the "done" point in deleting
795+
* a block. Paired with acquire at the top of this function.
796+
*/
797+
smp_store_release(&waiter->fl_blocker, NULL);
761798
spin_unlock(&blocked_lock_lock);
762799
return status;
763800
}
@@ -1350,7 +1387,8 @@ static int posix_lock_inode_wait(struct inode *inode, struct file_lock *fl)
13501387
error = posix_lock_inode(inode, fl, NULL);
13511388
if (error != FILE_LOCK_DEFERRED)
13521389
break;
1353-
error = wait_event_interruptible(fl->fl_wait, !fl->fl_blocker);
1390+
error = wait_event_interruptible(fl->fl_wait,
1391+
list_empty(&fl->fl_blocked_member));
13541392
if (error)
13551393
break;
13561394
}
@@ -1435,7 +1473,8 @@ int locks_mandatory_area(struct inode *inode, struct file *filp, loff_t start,
14351473
error = posix_lock_inode(inode, &fl, NULL);
14361474
if (error != FILE_LOCK_DEFERRED)
14371475
break;
1438-
error = wait_event_interruptible(fl.fl_wait, !fl.fl_blocker);
1476+
error = wait_event_interruptible(fl.fl_wait,
1477+
list_empty(&fl.fl_blocked_member));
14391478
if (!error) {
14401479
/*
14411480
* If we've been sleeping someone might have
@@ -1638,7 +1677,8 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
16381677

16391678
locks_dispose_list(&dispose);
16401679
error = wait_event_interruptible_timeout(new_fl->fl_wait,
1641-
!new_fl->fl_blocker, break_time);
1680+
list_empty(&new_fl->fl_blocked_member),
1681+
break_time);
16421682

16431683
percpu_down_read(&file_rwsem);
16441684
spin_lock(&ctx->flc_lock);
@@ -2122,7 +2162,8 @@ static int flock_lock_inode_wait(struct inode *inode, struct file_lock *fl)
21222162
error = flock_lock_inode(inode, fl);
21232163
if (error != FILE_LOCK_DEFERRED)
21242164
break;
2125-
error = wait_event_interruptible(fl->fl_wait, !fl->fl_blocker);
2165+
error = wait_event_interruptible(fl->fl_wait,
2166+
list_empty(&fl->fl_blocked_member));
21262167
if (error)
21272168
break;
21282169
}
@@ -2399,7 +2440,8 @@ static int do_lock_file_wait(struct file *filp, unsigned int cmd,
23992440
error = vfs_lock_file(filp, cmd, fl, NULL);
24002441
if (error != FILE_LOCK_DEFERRED)
24012442
break;
2402-
error = wait_event_interruptible(fl->fl_wait, !fl->fl_blocker);
2443+
error = wait_event_interruptible(fl->fl_wait,
2444+
list_empty(&fl->fl_blocked_member));
24032445
if (error)
24042446
break;
24052447
}

0 commit comments

Comments
 (0)