]>
Commit | Line | Data |
---|---|---|
5c6958b0 GKH |
1 | From 52b042ab9948cc367b61f9ca9c18603aa7813c3a Mon Sep 17 00:00:00 2001 |
2 | From: Yihao Wu <wuyihao@linux.alibaba.com> | |
3 | Date: Wed, 22 May 2019 01:57:10 +0800 | |
4 | Subject: NFSv4.1: Again fix a race where CB_NOTIFY_LOCK fails to wake a waiter | |
5 | ||
6 | From: Yihao Wu <wuyihao@linux.alibaba.com> | |
7 | ||
8 | commit 52b042ab9948cc367b61f9ca9c18603aa7813c3a upstream. | |
9 | ||
10 | Commit b7dbcc0e433f "NFSv4.1: Fix a race where CB_NOTIFY_LOCK fails to wake a waiter" | |
11 | found this bug. However it didn't fix it. | |
12 | ||
13 | This commit replaces schedule_timeout() with wait_woken() and | |
14 | default_wake_function() with woken_wake_function() in function | |
15 | nfs4_retry_setlk() and nfs4_wake_lock_waiter(). wait_woken() uses | |
16 | memory barriers in its implementation to avoid potential race condition | |
17 | when putting a process into sleeping state and then waking it up. | |
18 | ||
19 | Fixes: a1d617d8f134 ("nfs: allow blocking locks to be awoken by lock callbacks") | |
20 | Cc: stable@vger.kernel.org #4.9+ | |
21 | Signed-off-by: Yihao Wu <wuyihao@linux.alibaba.com> | |
22 | Reviewed-by: Jeff Layton <jlayton@kernel.org> | |
23 | Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> | |
24 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
25 | ||
26 | --- | |
27 | fs/nfs/nfs4proc.c | 24 +++++++----------------- | |
28 | 1 file changed, 7 insertions(+), 17 deletions(-) | |
29 | ||
30 | --- a/fs/nfs/nfs4proc.c | |
31 | +++ b/fs/nfs/nfs4proc.c | |
32 | @@ -6850,7 +6850,6 @@ struct nfs4_lock_waiter { | |
33 | struct task_struct *task; | |
34 | struct inode *inode; | |
35 | struct nfs_lowner *owner; | |
36 | - bool notified; | |
37 | }; | |
38 | ||
39 | static int | |
40 | @@ -6872,13 +6871,13 @@ nfs4_wake_lock_waiter(wait_queue_entry_t | |
41 | /* Make sure it's for the right inode */ | |
42 | if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh)) | |
43 | return 0; | |
44 | - | |
45 | - waiter->notified = true; | |
46 | } | |
47 | ||
48 | /* override "private" so we can use default_wake_function */ | |
49 | wait->private = waiter->task; | |
50 | - ret = autoremove_wake_function(wait, mode, flags, key); | |
51 | + ret = woken_wake_function(wait, mode, flags, key); | |
52 | + if (ret) | |
53 | + list_del_init(&wait->entry); | |
54 | wait->private = waiter; | |
55 | return ret; | |
56 | } | |
57 | @@ -6887,7 +6886,6 @@ static int | |
58 | nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) | |
59 | { | |
60 | int status = -ERESTARTSYS; | |
61 | - unsigned long flags; | |
62 | struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner; | |
63 | struct nfs_server *server = NFS_SERVER(state->inode); | |
64 | struct nfs_client *clp = server->nfs_client; | |
65 | @@ -6897,8 +6895,7 @@ nfs4_retry_setlk(struct nfs4_state *stat | |
66 | .s_dev = server->s_dev }; | |
67 | struct nfs4_lock_waiter waiter = { .task = current, | |
68 | .inode = state->inode, | |
69 | - .owner = &owner, | |
70 | - .notified = false }; | |
71 | + .owner = &owner}; | |
72 | wait_queue_entry_t wait; | |
73 | ||
74 | /* Don't bother with waitqueue if we don't expect a callback */ | |
75 | @@ -6911,21 +6908,14 @@ nfs4_retry_setlk(struct nfs4_state *stat | |
76 | add_wait_queue(q, &wait); | |
77 | ||
78 | while(!signalled()) { | |
79 | - waiter.notified = false; | |
80 | status = nfs4_proc_setlk(state, cmd, request); | |
81 | if ((status != -EAGAIN) || IS_SETLK(cmd)) | |
82 | break; | |
83 | ||
84 | status = -ERESTARTSYS; | |
85 | - spin_lock_irqsave(&q->lock, flags); | |
86 | - if (waiter.notified) { | |
87 | - spin_unlock_irqrestore(&q->lock, flags); | |
88 | - continue; | |
89 | - } | |
90 | - set_current_state(TASK_INTERRUPTIBLE); | |
91 | - spin_unlock_irqrestore(&q->lock, flags); | |
92 | - | |
93 | - freezable_schedule_timeout(NFS4_LOCK_MAXTIMEOUT); | |
94 | + freezer_do_not_count(); | |
95 | + wait_woken(&wait, TASK_INTERRUPTIBLE, NFS4_LOCK_MAXTIMEOUT); | |
96 | + freezer_count(); | |
97 | } | |
98 | ||
99 | finish_wait(q, &wait); |