Skip to content

Commit 5946c43

Browse files
NeilBrownjtlayton
authored andcommitted
fs/locks: allow a lock request to block other requests.
Currently, a lock can block pending requests, but all pending requests are equal. If lots of pending requests are mutually exclusive, this means they will all be woken up and all but one will fail. This can hurt performance. So we will allow pending requests to block other requests. Only the first request will be woken, and it will wake the others. This patch doesn't implement this fully, but prepares the way. - It acknowledges that a request might be blocking other requests, and when the request is converted to a lock, those blocked requests are moved across. - When a request is requeued or discarded, all blocked requests are woken. - When deadlock-detection looks for the lock which blocks a given request, we follow the chain of ->fl_blocker all the way to the top. Tested-by: kernel test robot <[email protected]> Signed-off-by: NeilBrown <[email protected]> Reviewed-by: J. Bruce Fields <[email protected]> Signed-off-by: Jeff Layton <[email protected]>
1 parent d6367d6 commit 5946c43

File tree

1 file changed

+37
-6
lines changed

1 file changed

+37
-6
lines changed

fs/locks.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,24 @@ void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
402402

403403
EXPORT_SYMBOL(locks_copy_lock);
404404

405+
static void locks_move_blocks(struct file_lock *new, struct file_lock *fl)
406+
{
407+
struct file_lock *f;
408+
409+
/*
410+
* As ctx->flc_lock is held, new requests cannot be added to
411+
* ->fl_blocked_requests, so we don't need a lock to check if it
412+
* is empty.
413+
*/
414+
if (list_empty(&fl->fl_blocked_requests))
415+
return;
416+
spin_lock(&blocked_lock_lock);
417+
list_splice_init(&fl->fl_blocked_requests, &new->fl_blocked_requests);
418+
list_for_each_entry(f, &fl->fl_blocked_requests, fl_blocked_member)
419+
f->fl_blocker = new;
420+
spin_unlock(&blocked_lock_lock);
421+
}
422+
405423
static inline int flock_translate_cmd(int cmd) {
406424
if (cmd & LOCK_MAND)
407425
return cmd & (LOCK_MAND | LOCK_RW);
@@ -693,6 +711,7 @@ static void __locks_wake_up_blocks(struct file_lock *blocker)
693711
static void locks_delete_block(struct file_lock *waiter)
694712
{
695713
spin_lock(&blocked_lock_lock);
714+
__locks_wake_up_blocks(waiter);
696715
__locks_delete_block(waiter);
697716
spin_unlock(&blocked_lock_lock);
698717
}
@@ -716,6 +735,12 @@ static void __locks_insert_block(struct file_lock *blocker,
716735
list_add_tail(&waiter->fl_blocked_member, &blocker->fl_blocked_requests);
717736
if (IS_POSIX(blocker) && !IS_OFDLCK(blocker))
718737
locks_insert_global_blocked(waiter);
738+
739+
/* The requests in waiter->fl_blocked are known to conflict with
740+
* waiter, but might not conflict with blocker, or the requests
741+
* and lock which block it. So they all need to be woken.
742+
*/
743+
__locks_wake_up_blocks(waiter);
719744
}
720745

721746
/* Must be called with flc_lock held. */
@@ -888,8 +913,11 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl)
888913
struct file_lock *fl;
889914

890915
hash_for_each_possible(blocked_hash, fl, fl_link, posix_owner_key(block_fl)) {
891-
if (posix_same_owner(fl, block_fl))
892-
return fl->fl_blocker;
916+
if (posix_same_owner(fl, block_fl)) {
917+
while (fl->fl_blocker)
918+
fl = fl->fl_blocker;
919+
return fl;
920+
}
893921
}
894922
return NULL;
895923
}
@@ -982,6 +1010,7 @@ static int flock_lock_inode(struct inode *inode, struct file_lock *request)
9821010
if (request->fl_flags & FL_ACCESS)
9831011
goto out;
9841012
locks_copy_lock(new_fl, request);
1013+
locks_move_blocks(new_fl, request);
9851014
locks_insert_lock_ctx(new_fl, &ctx->flc_flock);
9861015
new_fl = NULL;
9871016
error = 0;
@@ -1175,6 +1204,7 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
11751204
goto out;
11761205
}
11771206
locks_copy_lock(new_fl, request);
1207+
locks_move_blocks(new_fl, request);
11781208
locks_insert_lock_ctx(new_fl, &fl->fl_list);
11791209
fl = new_fl;
11801210
new_fl = NULL;
@@ -2586,13 +2616,14 @@ void locks_remove_file(struct file *filp)
25862616
int
25872617
posix_unblock_lock(struct file_lock *waiter)
25882618
{
2589-
int status = 0;
2619+
int status = -ENOENT;
25902620

25912621
spin_lock(&blocked_lock_lock);
2592-
if (waiter->fl_blocker)
2622+
if (waiter->fl_blocker) {
2623+
__locks_wake_up_blocks(waiter);
25932624
__locks_delete_block(waiter);
2594-
else
2595-
status = -ENOENT;
2625+
status = 0;
2626+
}
25962627
spin_unlock(&blocked_lock_lock);
25972628
return status;
25982629
}

0 commit comments

Comments
 (0)