diff --git a/fs/locks.c b/fs/locks.c index 1260c265ba62d24443816b7d8a6f59be50810f88..f2fa1465046bcd28e77113b0db3619f770d24a32 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -710,6 +710,20 @@ static void __locks_wake_up_blocks(struct file_lock *blocker) static void locks_delete_block(struct file_lock *waiter) { + /* + * If fl_blocker is NULL, it won't be set again as this thread + * "owns" the lock and is the only one that might try to claim + * the lock. So it is safe to test fl_blocker locklessly. + * Also if fl_blocker is NULL, this waiter is not listed on + * fl_blocked_requests for some lock, so no other request can + * be added to the list of fl_blocked_requests for this + * request. So if fl_blocker is NULL, it is safe to + * locklessly check if fl_blocked_requests is empty. If both + * of these checks succeed, there is no need to take the lock. + */ + if (waiter->fl_blocker == NULL && + list_empty(&waiter->fl_blocked_requests)) + return; spin_lock(&blocked_lock_lock); __locks_wake_up_blocks(waiter); __locks_delete_block(waiter); @@ -1279,12 +1293,10 @@ static int posix_lock_inode_wait(struct inode *inode, struct file_lock *fl) if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_blocker); - if (!error) - continue; - - locks_delete_block(fl); - break; + if (error) + break; } + locks_delete_block(fl); return error; } @@ -1375,9 +1387,9 @@ int locks_mandatory_area(struct inode *inode, struct file *filp, loff_t start, continue; } - locks_delete_block(&fl); break; } + locks_delete_block(&fl); return error; } @@ -1973,12 +1985,10 @@ static int flock_lock_inode_wait(struct inode *inode, struct file_lock *fl) if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_blocker); - if (!error) - continue; - - locks_delete_block(fl); - break; + if (error) + break; } + locks_delete_block(fl); return error; } @@ -2252,12 +2262,10 @@ static int do_lock_file_wait(struct file *filp, unsigned int cmd, if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_blocker); - if (!error) - continue; - - locks_delete_block(fl); - break; + if (error) + break; } + locks_delete_block(fl); return error; }