aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/callback.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/callback.c')
-rw-r--r--fs/nfs/callback.c214
1 files changed, 179 insertions, 35 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index a886e692ddd0..e69b8f61189e 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,11 +31,12 @@
28 31
29struct nfs_callback_data { 32struct 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};
34 38
35static struct nfs_callback_data nfs_callback_info; 39static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
36static DEFINE_MUTEX(nfs_callback_mutex); 40static DEFINE_MUTEX(nfs_callback_mutex);
37static struct svc_program nfs4_callback_program; 41static struct svc_program nfs4_callback_program;
38 42
@@ -56,10 +60,10 @@ module_param_call(callback_tcpport, param_set_port, param_get_int,
56 &nfs_callback_set_tcpport, 0644); 60 &nfs_callback_set_tcpport, 0644);
57 61
58/* 62/*
59 * This is the callback kernel thread. 63 * This is the NFSv4 callback kernel thread.
60 */ 64 */
61static int 65static int
62nfs_callback_svc(void *vrqstp) 66nfs4_callback_svc(void *vrqstp)
63{ 67{
64 int err, preverr = 0; 68 int err, preverr = 0;
65 struct svc_rqst *rqstp = vrqstp; 69 struct svc_rqst *rqstp = vrqstp;
@@ -97,20 +101,12 @@ nfs_callback_svc(void *vrqstp)
97} 101}
98 102
99/* 103/*
100 * Bring up the callback thread if it is not already up. 104 * Prepare to bring up the NFSv4 callback service
101 */ 105 */
102int nfs_callback_up(void) 106struct svc_rqst *
107nfs4_callback_up(struct svc_serv *serv)
103{ 108{
104 struct svc_serv *serv = NULL; 109 int ret;
105 int ret = 0;
106
107 mutex_lock(&nfs_callback_mutex);
108 if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
109 goto out;
110 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
111 ret = -ENOMEM;
112 if (!serv)
113 goto out_err;
114 110
115 ret = svc_create_xprt(serv, "tcp", PF_INET, 111 ret = svc_create_xprt(serv, "tcp", PF_INET,
116 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); 112 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
@@ -131,23 +127,168 @@ int nfs_callback_up(void)
131 goto out_err; 127 goto out_err;
132#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ 128#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
133 129
134 nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); 130 return svc_prepare_thread(serv, &serv->sv_pools[0]);
135 if (IS_ERR(nfs_callback_info.rqst)) { 131
136 ret = PTR_ERR(nfs_callback_info.rqst); 132out_err:
137 nfs_callback_info.rqst = NULL; 133 if (ret == 0)
134 ret = -ENOMEM;
135 return ERR_PTR(ret);
136}
137
138#if defined(CONFIG_NFS_V4_1)
139/*
140 * The callback service for NFSv4.1 callbacks
141 */
142static int
143nfs41_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 return 0;
178}
179
180/*
181 * Bring up the NFSv4.1 callback service
182 */
183struct svc_rqst *
184nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
185{
186 struct svc_xprt *bc_xprt;
187 struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
188
189 dprintk("--> %s\n", __func__);
190 /* Create a svc_sock for the service */
191 bc_xprt = svc_sock_create(serv, xprt->prot);
192 if (!bc_xprt)
193 goto out;
194
195 /*
196 * Save the svc_serv in the transport so that it can
197 * be referenced when the session backchannel is initialized
198 */
199 serv->bc_xprt = bc_xprt;
200 xprt->bc_serv = serv;
201
202 INIT_LIST_HEAD(&serv->sv_cb_list);
203 spin_lock_init(&serv->sv_cb_lock);
204 init_waitqueue_head(&serv->sv_cb_waitq);
205 rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
206 if (IS_ERR(rqstp))
207 svc_sock_destroy(bc_xprt);
208out:
209 dprintk("--> %s return %p\n", __func__, rqstp);
210 return rqstp;
211}
212
213static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
214 struct svc_serv *serv, struct rpc_xprt *xprt,
215 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
216{
217 if (minorversion) {
218 *rqstpp = nfs41_callback_up(serv, xprt);
219 *callback_svc = nfs41_callback_svc;
220 }
221 return minorversion;
222}
223
224static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
225 struct nfs_callback_data *cb_info)
226{
227 if (minorversion)
228 xprt->bc_serv = cb_info->serv;
229}
230#else
231static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
232 struct svc_serv *serv, struct rpc_xprt *xprt,
233 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
234{
235 return 0;
236}
237
238static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
239 struct nfs_callback_data *cb_info)
240{
241}
242#endif /* CONFIG_NFS_V4_1 */
243
244/*
245 * Bring up the callback thread if it is not already up.
246 */
247int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
248{
249 struct svc_serv *serv = NULL;
250 struct svc_rqst *rqstp;
251 int (*callback_svc)(void *vrqstp);
252 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
253 char svc_name[12];
254 int ret = 0;
255 int minorversion_setup;
256
257 mutex_lock(&nfs_callback_mutex);
258 if (cb_info->users++ || cb_info->task != NULL) {
259 nfs_callback_bc_serv(minorversion, xprt, cb_info);
260 goto out;
261 }
262 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
263 if (!serv) {
264 ret = -ENOMEM;
265 goto out_err;
266 }
267
268 minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion,
269 serv, xprt, &rqstp, &callback_svc);
270 if (!minorversion_setup) {
271 /* v4.0 callback setup */
272 rqstp = nfs4_callback_up(serv);
273 callback_svc = nfs4_callback_svc;
274 }
275
276 if (IS_ERR(rqstp)) {
277 ret = PTR_ERR(rqstp);
138 goto out_err; 278 goto out_err;
139 } 279 }
140 280
141 svc_sock_update_bufs(serv); 281 svc_sock_update_bufs(serv);
142 282
143 nfs_callback_info.task = kthread_run(nfs_callback_svc, 283 sprintf(svc_name, "nfsv4.%u-svc", minorversion);
144 nfs_callback_info.rqst, 284 cb_info->serv = serv;
145 "nfsv4-svc"); 285 cb_info->rqst = rqstp;
146 if (IS_ERR(nfs_callback_info.task)) { 286 cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
147 ret = PTR_ERR(nfs_callback_info.task); 287 if (IS_ERR(cb_info->task)) {
148 svc_exit_thread(nfs_callback_info.rqst); 288 ret = PTR_ERR(cb_info->task);
149 nfs_callback_info.rqst = NULL; 289 svc_exit_thread(cb_info->rqst);
150 nfs_callback_info.task = NULL; 290 cb_info->rqst = NULL;
291 cb_info->task = NULL;
151 goto out_err; 292 goto out_err;
152 } 293 }
153out: 294out:
@@ -164,22 +305,25 @@ out:
164out_err: 305out_err:
165 dprintk("NFS: Couldn't create callback socket or server thread; " 306 dprintk("NFS: Couldn't create callback socket or server thread; "
166 "err = %d\n", ret); 307 "err = %d\n", ret);
167 nfs_callback_info.users--; 308 cb_info->users--;
168 goto out; 309 goto out;
169} 310}
170 311
171/* 312/*
172 * Kill the callback thread if it's no longer being used. 313 * Kill the callback thread if it's no longer being used.
173 */ 314 */
174void nfs_callback_down(void) 315void nfs_callback_down(int minorversion)
175{ 316{
317 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
318
176 mutex_lock(&nfs_callback_mutex); 319 mutex_lock(&nfs_callback_mutex);
177 nfs_callback_info.users--; 320 cb_info->users--;
178 if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { 321 if (cb_info->users == 0 && cb_info->task != NULL) {
179 kthread_stop(nfs_callback_info.task); 322 kthread_stop(cb_info->task);
180 svc_exit_thread(nfs_callback_info.rqst); 323 svc_exit_thread(cb_info->rqst);
181 nfs_callback_info.rqst = NULL; 324 cb_info->serv = NULL;
182 nfs_callback_info.task = NULL; 325 cb_info->rqst = NULL;
326 cb_info->task = NULL;
183 } 327 }
184 mutex_unlock(&nfs_callback_mutex); 328 mutex_unlock(&nfs_callback_mutex);
185} 329}