diff options
Diffstat (limited to 'fs/nfs/callback.c')
-rw-r--r-- | fs/nfs/callback.c | 112 |
1 files changed, 108 insertions, 4 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 4b1313eda6f5..470928898063 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c | |||
@@ -17,6 +17,9 @@ | |||
17 | #include <linux/freezer.h> | 17 | #include <linux/freezer.h> |
18 | #include <linux/kthread.h> | 18 | #include <linux/kthread.h> |
19 | #include <linux/sunrpc/svcauth_gss.h> | 19 | #include <linux/sunrpc/svcauth_gss.h> |
20 | #if defined(CONFIG_NFS_V4_1) | ||
21 | #include <linux/sunrpc/bc_xprt.h> | ||
22 | #endif | ||
20 | 23 | ||
21 | #include <net/inet_sock.h> | 24 | #include <net/inet_sock.h> |
22 | 25 | ||
@@ -28,6 +31,7 @@ | |||
28 | 31 | ||
29 | struct nfs_callback_data { | 32 | struct nfs_callback_data { |
30 | unsigned int users; | 33 | unsigned int users; |
34 | struct svc_serv *serv; | ||
31 | struct svc_rqst *rqst; | 35 | struct svc_rqst *rqst; |
32 | struct task_struct *task; | 36 | struct task_struct *task; |
33 | }; | 37 | }; |
@@ -131,6 +135,99 @@ out_err: | |||
131 | return ERR_PTR(ret); | 135 | return ERR_PTR(ret); |
132 | } | 136 | } |
133 | 137 | ||
138 | #if defined(CONFIG_NFS_V4_1) | ||
139 | /* | ||
140 | * The callback service for NFSv4.1 callbacks | ||
141 | */ | ||
142 | static int | ||
143 | nfs41_callback_svc(void *vrqstp) | ||
144 | { | ||
145 | struct svc_rqst *rqstp = vrqstp; | ||
146 | struct svc_serv *serv = rqstp->rq_server; | ||
147 | struct rpc_rqst *req; | ||
148 | int error; | ||
149 | DEFINE_WAIT(wq); | ||
150 | |||
151 | set_freezable(); | ||
152 | |||
153 | /* | ||
154 | * FIXME: do we really need to run this under the BKL? If so, please | ||
155 | * add a comment about what it's intended to protect. | ||
156 | */ | ||
157 | lock_kernel(); | ||
158 | while (!kthread_should_stop()) { | ||
159 | prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); | ||
160 | spin_lock_bh(&serv->sv_cb_lock); | ||
161 | if (!list_empty(&serv->sv_cb_list)) { | ||
162 | req = list_first_entry(&serv->sv_cb_list, | ||
163 | struct rpc_rqst, rq_bc_list); | ||
164 | list_del(&req->rq_bc_list); | ||
165 | spin_unlock_bh(&serv->sv_cb_lock); | ||
166 | dprintk("Invoking bc_svc_process()\n"); | ||
167 | error = bc_svc_process(serv, req, rqstp); | ||
168 | dprintk("bc_svc_process() returned w/ error code= %d\n", | ||
169 | error); | ||
170 | } else { | ||
171 | spin_unlock_bh(&serv->sv_cb_lock); | ||
172 | schedule(); | ||
173 | } | ||
174 | finish_wait(&serv->sv_cb_waitq, &wq); | ||
175 | } | ||
176 | unlock_kernel(); | ||
177 | nfs_callback_info.task = NULL; | ||
178 | svc_exit_thread(rqstp); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Bring up the NFSv4.1 callback service | ||
184 | */ | ||
185 | struct svc_rqst * | ||
186 | nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) | ||
187 | { | ||
188 | /* | ||
189 | * Save the svc_serv in the transport so that it can | ||
190 | * be referenced when the session backchannel is initialized | ||
191 | */ | ||
192 | xprt->bc_serv = serv; | ||
193 | |||
194 | INIT_LIST_HEAD(&serv->sv_cb_list); | ||
195 | spin_lock_init(&serv->sv_cb_lock); | ||
196 | init_waitqueue_head(&serv->sv_cb_waitq); | ||
197 | return svc_prepare_thread(serv, &serv->sv_pools[0]); | ||
198 | } | ||
199 | |||
200 | static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, | ||
201 | struct svc_serv *serv, struct rpc_xprt *xprt, | ||
202 | struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) | ||
203 | { | ||
204 | if (minorversion) { | ||
205 | *rqstpp = nfs41_callback_up(serv, xprt); | ||
206 | *callback_svc = nfs41_callback_svc; | ||
207 | } | ||
208 | return minorversion; | ||
209 | } | ||
210 | |||
211 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, | ||
212 | struct nfs_callback_data *cb_info) | ||
213 | { | ||
214 | if (minorversion) | ||
215 | xprt->bc_serv = cb_info->serv; | ||
216 | } | ||
217 | #else | ||
218 | static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, | ||
219 | struct svc_serv *serv, struct rpc_xprt *xprt, | ||
220 | struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) | ||
221 | { | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, | ||
226 | struct nfs_callback_data *cb_info) | ||
227 | { | ||
228 | } | ||
229 | #endif /* CONFIG_NFS_V4_1 */ | ||
230 | |||
134 | /* | 231 | /* |
135 | * Bring up the callback thread if it is not already up. | 232 | * Bring up the callback thread if it is not already up. |
136 | */ | 233 | */ |
@@ -141,21 +238,25 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) | |||
141 | int (*callback_svc)(void *vrqstp); | 238 | int (*callback_svc)(void *vrqstp); |
142 | char svc_name[12]; | 239 | char svc_name[12]; |
143 | int ret = 0; | 240 | int ret = 0; |
241 | int minorversion_setup; | ||
144 | 242 | ||
145 | mutex_lock(&nfs_callback_mutex); | 243 | mutex_lock(&nfs_callback_mutex); |
146 | if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) | 244 | if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) { |
245 | nfs_callback_bc_serv(minorversion, xprt, &nfs_callback_info); | ||
147 | goto out; | 246 | goto out; |
247 | } | ||
148 | serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); | 248 | serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); |
149 | if (!serv) { | 249 | if (!serv) { |
150 | ret = -ENOMEM; | 250 | ret = -ENOMEM; |
151 | goto out_err; | 251 | goto out_err; |
152 | } | 252 | } |
153 | 253 | ||
154 | if (!minorversion) { | 254 | minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, |
255 | serv, xprt, &rqstp, &callback_svc); | ||
256 | if (!minorversion_setup) { | ||
257 | /* v4.0 callback setup */ | ||
155 | rqstp = nfs4_callback_up(serv); | 258 | rqstp = nfs4_callback_up(serv); |
156 | callback_svc = nfs4_callback_svc; | 259 | callback_svc = nfs4_callback_svc; |
157 | } else { | ||
158 | BUG(); /* for now */ | ||
159 | } | 260 | } |
160 | 261 | ||
161 | if (IS_ERR(rqstp)) { | 262 | if (IS_ERR(rqstp)) { |
@@ -166,6 +267,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) | |||
166 | svc_sock_update_bufs(serv); | 267 | svc_sock_update_bufs(serv); |
167 | 268 | ||
168 | sprintf(svc_name, "nfsv4.%u-svc", minorversion); | 269 | sprintf(svc_name, "nfsv4.%u-svc", minorversion); |
270 | nfs_callback_info.serv = serv; | ||
169 | nfs_callback_info.rqst = rqstp; | 271 | nfs_callback_info.rqst = rqstp; |
170 | nfs_callback_info.task = kthread_run(callback_svc, | 272 | nfs_callback_info.task = kthread_run(callback_svc, |
171 | nfs_callback_info.rqst, | 273 | nfs_callback_info.rqst, |
@@ -173,6 +275,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) | |||
173 | if (IS_ERR(nfs_callback_info.task)) { | 275 | if (IS_ERR(nfs_callback_info.task)) { |
174 | ret = PTR_ERR(nfs_callback_info.task); | 276 | ret = PTR_ERR(nfs_callback_info.task); |
175 | svc_exit_thread(nfs_callback_info.rqst); | 277 | svc_exit_thread(nfs_callback_info.rqst); |
278 | nfs_callback_info.serv = NULL; | ||
176 | nfs_callback_info.rqst = NULL; | 279 | nfs_callback_info.rqst = NULL; |
177 | nfs_callback_info.task = NULL; | 280 | nfs_callback_info.task = NULL; |
178 | goto out_err; | 281 | goto out_err; |
@@ -205,6 +308,7 @@ void nfs_callback_down(void) | |||
205 | if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { | 308 | if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { |
206 | kthread_stop(nfs_callback_info.task); | 309 | kthread_stop(nfs_callback_info.task); |
207 | svc_exit_thread(nfs_callback_info.rqst); | 310 | svc_exit_thread(nfs_callback_info.rqst); |
311 | nfs_callback_info.serv = NULL; | ||
208 | nfs_callback_info.rqst = NULL; | 312 | nfs_callback_info.rqst = NULL; |
209 | nfs_callback_info.task = NULL; | 313 | nfs_callback_info.task = NULL; |
210 | } | 314 | } |