aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/callback.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2008-06-11 10:03:11 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-07-09 12:09:32 -0400
commit5afc597c5f0bd184457e49b9a330fcb37b69db11 (patch)
treef84cd40755de219095c376a5f96ba69da80e26f5 /fs/nfs/callback.c
parentee84dfc45467fd8e5ce04fa2813d98e0aebe465c (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/nfs/callback.c')
-rw-r--r--fs/nfs/callback.c30
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
28struct nfs_callback_data { 28struct 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 */
102int nfs_callback_up(void) 100int 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 }
141out: 139out:
@@ -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 */
162void nfs_callback_down(void) 160void 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