mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
NFSv4.1: Again fix a race where CB_NOTIFY_LOCK fails to wake a waiter
Commitb7dbcc0e43
"NFSv4.1: Fix a race where CB_NOTIFY_LOCK fails to wake a waiter" found this bug. However it didn't fix it. This commit replaces schedule_timeout() with wait_woken() and default_wake_function() with woken_wake_function() in function nfs4_retry_setlk() and nfs4_wake_lock_waiter(). wait_woken() uses memory barriers in its implementation to avoid potential race condition when putting a process into sleeping state and then waking it up. Fixes:a1d617d8f1
("nfs: allow blocking locks to be awoken by lock callbacks") Cc: stable@vger.kernel.org #4.9+ Signed-off-by: Yihao Wu <wuyihao@linux.alibaba.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
7987b694ad
commit
52b042ab99
@ -6932,7 +6932,6 @@ struct nfs4_lock_waiter {
|
||||
struct task_struct *task;
|
||||
struct inode *inode;
|
||||
struct nfs_lowner *owner;
|
||||
bool notified;
|
||||
};
|
||||
|
||||
static int
|
||||
@ -6954,13 +6953,13 @@ nfs4_wake_lock_waiter(wait_queue_entry_t *wait, unsigned int mode, int flags, vo
|
||||
/* Make sure it's for the right inode */
|
||||
if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh))
|
||||
return 0;
|
||||
|
||||
waiter->notified = true;
|
||||
}
|
||||
|
||||
/* override "private" so we can use default_wake_function */
|
||||
wait->private = waiter->task;
|
||||
ret = autoremove_wake_function(wait, mode, flags, key);
|
||||
ret = woken_wake_function(wait, mode, flags, key);
|
||||
if (ret)
|
||||
list_del_init(&wait->entry);
|
||||
wait->private = waiter;
|
||||
return ret;
|
||||
}
|
||||
@ -6969,7 +6968,6 @@ static int
|
||||
nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
||||
{
|
||||
int status = -ERESTARTSYS;
|
||||
unsigned long flags;
|
||||
struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner;
|
||||
struct nfs_server *server = NFS_SERVER(state->inode);
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
@ -6979,8 +6977,7 @@ nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
||||
.s_dev = server->s_dev };
|
||||
struct nfs4_lock_waiter waiter = { .task = current,
|
||||
.inode = state->inode,
|
||||
.owner = &owner,
|
||||
.notified = false };
|
||||
.owner = &owner};
|
||||
wait_queue_entry_t wait;
|
||||
|
||||
/* Don't bother with waitqueue if we don't expect a callback */
|
||||
@ -6993,21 +6990,14 @@ nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
||||
add_wait_queue(q, &wait);
|
||||
|
||||
while(!signalled()) {
|
||||
waiter.notified = false;
|
||||
status = nfs4_proc_setlk(state, cmd, request);
|
||||
if ((status != -EAGAIN) || IS_SETLK(cmd))
|
||||
break;
|
||||
|
||||
status = -ERESTARTSYS;
|
||||
spin_lock_irqsave(&q->lock, flags);
|
||||
if (waiter.notified) {
|
||||
spin_unlock_irqrestore(&q->lock, flags);
|
||||
continue;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
spin_unlock_irqrestore(&q->lock, flags);
|
||||
|
||||
freezable_schedule_timeout(NFS4_LOCK_MAXTIMEOUT);
|
||||
freezer_do_not_count();
|
||||
wait_woken(&wait, TASK_INTERRUPTIBLE, NFS4_LOCK_MAXTIMEOUT);
|
||||
freezer_count();
|
||||
}
|
||||
|
||||
finish_wait(q, &wait);
|
||||
|
Loading…
Reference in New Issue
Block a user