diff options
author | Jeff Layton <jlayton@redhat.com> | 2008-06-11 10:03:11 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-07-09 12:09:32 -0400 |
commit | 5afc597c5f0bd184457e49b9a330fcb37b69db11 (patch) | |
tree | f84cd40755de219095c376a5f96ba69da80e26f5 /fs | |
parent | ee84dfc45467fd8e5ce04fa2813d98e0aebe465c (diff) |
nfs4: fix potential race with rapid nfs_callback_up/down cycle
If the nfsv4 callback thread is rapidly brought up and down, it's
possible that nfs_callback_svc might never get a chance to run. If
this happens, the cleanup at thread exit might never occur, throwing
the refcounting off and nfs_callback_info in an incorrect state.
Move the clean functions into nfs_callback_down. Also change the
nfs_callback_info struct to track the svc_rqst rather than svc_serv
since we need to know that to call svc_exit_thread.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/callback.c | 30 |
1 files changed, 16 insertions, 14 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 9e713d2d5d74..f447f4b4476c 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c | |||
@@ -27,7 +27,7 @@ | |||
27 | 27 | ||
28 | struct nfs_callback_data { | 28 | struct nfs_callback_data { |
29 | unsigned int users; | 29 | unsigned int users; |
30 | struct svc_serv *serv; | 30 | struct svc_rqst *rqst; |
31 | struct task_struct *task; | 31 | struct task_struct *task; |
32 | }; | 32 | }; |
33 | 33 | ||
@@ -91,18 +91,15 @@ nfs_callback_svc(void *vrqstp) | |||
91 | svc_process(rqstp); | 91 | svc_process(rqstp); |
92 | } | 92 | } |
93 | unlock_kernel(); | 93 | unlock_kernel(); |
94 | nfs_callback_info.task = NULL; | ||
95 | svc_exit_thread(rqstp); | ||
96 | return 0; | 94 | return 0; |
97 | } | 95 | } |
98 | 96 | ||
99 | /* | 97 | /* |
100 | * Bring up the server process if it is not already up. | 98 | * Bring up the callback thread if it is not already up. |
101 | */ | 99 | */ |
102 | int nfs_callback_up(void) | 100 | int nfs_callback_up(void) |
103 | { | 101 | { |
104 | struct svc_serv *serv = NULL; | 102 | struct svc_serv *serv = NULL; |
105 | struct svc_rqst *rqstp; | ||
106 | int ret = 0; | 103 | int ret = 0; |
107 | 104 | ||
108 | mutex_lock(&nfs_callback_mutex); | 105 | mutex_lock(&nfs_callback_mutex); |
@@ -120,22 +117,23 @@ int nfs_callback_up(void) | |||
120 | nfs_callback_tcpport = ret; | 117 | nfs_callback_tcpport = ret; |
121 | dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); | 118 | dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); |
122 | 119 | ||
123 | rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); | 120 | nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); |
124 | if (IS_ERR(rqstp)) { | 121 | if (IS_ERR(nfs_callback_info.rqst)) { |
125 | ret = PTR_ERR(rqstp); | 122 | ret = PTR_ERR(nfs_callback_info.rqst); |
123 | nfs_callback_info.rqst = NULL; | ||
126 | goto out_err; | 124 | goto out_err; |
127 | } | 125 | } |
128 | 126 | ||
129 | svc_sock_update_bufs(serv); | 127 | svc_sock_update_bufs(serv); |
130 | nfs_callback_info.serv = serv; | ||
131 | 128 | ||
132 | nfs_callback_info.task = kthread_run(nfs_callback_svc, rqstp, | 129 | nfs_callback_info.task = kthread_run(nfs_callback_svc, |
130 | nfs_callback_info.rqst, | ||
133 | "nfsv4-svc"); | 131 | "nfsv4-svc"); |
134 | if (IS_ERR(nfs_callback_info.task)) { | 132 | if (IS_ERR(nfs_callback_info.task)) { |
135 | ret = PTR_ERR(nfs_callback_info.task); | 133 | ret = PTR_ERR(nfs_callback_info.task); |
136 | nfs_callback_info.serv = NULL; | 134 | svc_exit_thread(nfs_callback_info.rqst); |
135 | nfs_callback_info.rqst = NULL; | ||
137 | nfs_callback_info.task = NULL; | 136 | nfs_callback_info.task = NULL; |
138 | svc_exit_thread(rqstp); | ||
139 | goto out_err; | 137 | goto out_err; |
140 | } | 138 | } |
141 | out: | 139 | out: |
@@ -157,14 +155,18 @@ out_err: | |||
157 | } | 155 | } |
158 | 156 | ||
159 | /* | 157 | /* |
160 | * Kill the server process if it is not already down. | 158 | * Kill the callback thread if it's no longer being used. |
161 | */ | 159 | */ |
162 | void nfs_callback_down(void) | 160 | void nfs_callback_down(void) |
163 | { | 161 | { |
164 | mutex_lock(&nfs_callback_mutex); | 162 | mutex_lock(&nfs_callback_mutex); |
165 | nfs_callback_info.users--; | 163 | nfs_callback_info.users--; |
166 | if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) | 164 | if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { |
167 | kthread_stop(nfs_callback_info.task); | 165 | kthread_stop(nfs_callback_info.task); |
166 | svc_exit_thread(nfs_callback_info.rqst); | ||
167 | nfs_callback_info.rqst = NULL; | ||
168 | nfs_callback_info.task = NULL; | ||
169 | } | ||
168 | mutex_unlock(&nfs_callback_mutex); | 170 | mutex_unlock(&nfs_callback_mutex); |
169 | } | 171 | } |
170 | 172 | ||