Merge branch 'nfs-for-2.6.32'
[pandora-kernel.git] / fs / nfs / callback.c
1 /*
2  * linux/fs/nfs/callback.c
3  *
4  * Copyright (C) 2004 Trond Myklebust
5  *
6  * NFSv4 callback handling
7  */
8
9 #include <linux/completion.h>
10 #include <linux/ip.h>
11 #include <linux/module.h>
12 #include <linux/smp_lock.h>
13 #include <linux/sunrpc/svc.h>
14 #include <linux/sunrpc/svcsock.h>
15 #include <linux/nfs_fs.h>
16 #include <linux/mutex.h>
17 #include <linux/freezer.h>
18 #include <linux/kthread.h>
19 #include <linux/sunrpc/svcauth_gss.h>
20 #if defined(CONFIG_NFS_V4_1)
21 #include <linux/sunrpc/bc_xprt.h>
22 #endif
23
24 #include <net/inet_sock.h>
25
26 #include "nfs4_fs.h"
27 #include "callback.h"
28 #include "internal.h"
29
30 #define NFSDBG_FACILITY NFSDBG_CALLBACK
31
32 struct nfs_callback_data {
33         unsigned int users;
34         struct svc_serv *serv;
35         struct svc_rqst *rqst;
36         struct task_struct *task;
37 };
38
39 static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
40 static DEFINE_MUTEX(nfs_callback_mutex);
41 static struct svc_program nfs4_callback_program;
42
43 unsigned int nfs_callback_set_tcpport;
44 unsigned short nfs_callback_tcpport;
45 unsigned short nfs_callback_tcpport6;
46 #define NFS_CALLBACK_MAXPORTNR (65535U)
47
48 static int param_set_portnr(const char *val, struct kernel_param *kp)
49 {
50         unsigned long num;
51         int ret;
52
53         if (!val)
54                 return -EINVAL;
55         ret = strict_strtoul(val, 0, &num);
56         if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
57                 return -EINVAL;
58         *((unsigned int *)kp->arg) = num;
59         return 0;
60 }
61
62 static int param_get_portnr(char *buffer, struct kernel_param *kp)
63 {
64         return param_get_uint(buffer, kp);
65 }
66 #define param_check_portnr(name, p) __param_check(name, p, unsigned int);
67
68 module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
69
70 /*
71  * This is the NFSv4 callback kernel thread.
72  */
73 static int
74 nfs4_callback_svc(void *vrqstp)
75 {
76         int err, preverr = 0;
77         struct svc_rqst *rqstp = vrqstp;
78
79         set_freezable();
80
81         /*
82          * FIXME: do we really need to run this under the BKL? If so, please
83          * add a comment about what it's intended to protect.
84          */
85         lock_kernel();
86         while (!kthread_should_stop()) {
87                 /*
88                  * Listen for a request on the socket
89                  */
90                 err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
91                 if (err == -EAGAIN || err == -EINTR) {
92                         preverr = err;
93                         continue;
94                 }
95                 if (err < 0) {
96                         if (err != preverr) {
97                                 printk(KERN_WARNING "%s: unexpected error "
98                                         "from svc_recv (%d)\n", __func__, err);
99                                 preverr = err;
100                         }
101                         schedule_timeout_uninterruptible(HZ);
102                         continue;
103                 }
104                 preverr = err;
105                 svc_process(rqstp);
106         }
107         unlock_kernel();
108         return 0;
109 }
110
111 /*
112  * Prepare to bring up the NFSv4 callback service
113  */
114 struct svc_rqst *
115 nfs4_callback_up(struct svc_serv *serv)
116 {
117         int ret;
118
119         ret = svc_create_xprt(serv, "tcp", PF_INET,
120                                 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
121         if (ret <= 0)
122                 goto out_err;
123         nfs_callback_tcpport = ret;
124         dprintk("NFS: Callback listener port = %u (af %u)\n",
125                         nfs_callback_tcpport, PF_INET);
126
127 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
128         ret = svc_create_xprt(serv, "tcp", PF_INET6,
129                                 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
130         if (ret > 0) {
131                 nfs_callback_tcpport6 = ret;
132                 dprintk("NFS: Callback listener port = %u (af %u)\n",
133                                 nfs_callback_tcpport6, PF_INET6);
134         } else if (ret == -EAFNOSUPPORT)
135                 ret = 0;
136         else
137                 goto out_err;
138 #endif  /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
139
140         return svc_prepare_thread(serv, &serv->sv_pools[0]);
141
142 out_err:
143         if (ret == 0)
144                 ret = -ENOMEM;
145         return ERR_PTR(ret);
146 }
147
148 #if defined(CONFIG_NFS_V4_1)
149 /*
150  * The callback service for NFSv4.1 callbacks
151  */
152 static int
153 nfs41_callback_svc(void *vrqstp)
154 {
155         struct svc_rqst *rqstp = vrqstp;
156         struct svc_serv *serv = rqstp->rq_server;
157         struct rpc_rqst *req;
158         int error;
159         DEFINE_WAIT(wq);
160
161         set_freezable();
162
163         /*
164          * FIXME: do we really need to run this under the BKL? If so, please
165          * add a comment about what it's intended to protect.
166          */
167         lock_kernel();
168         while (!kthread_should_stop()) {
169                 prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
170                 spin_lock_bh(&serv->sv_cb_lock);
171                 if (!list_empty(&serv->sv_cb_list)) {
172                         req = list_first_entry(&serv->sv_cb_list,
173                                         struct rpc_rqst, rq_bc_list);
174                         list_del(&req->rq_bc_list);
175                         spin_unlock_bh(&serv->sv_cb_lock);
176                         dprintk("Invoking bc_svc_process()\n");
177                         error = bc_svc_process(serv, req, rqstp);
178                         dprintk("bc_svc_process() returned w/ error code= %d\n",
179                                 error);
180                 } else {
181                         spin_unlock_bh(&serv->sv_cb_lock);
182                         schedule();
183                 }
184                 finish_wait(&serv->sv_cb_waitq, &wq);
185         }
186         unlock_kernel();
187         return 0;
188 }
189
190 /*
191  * Bring up the NFSv4.1 callback service
192  */
193 struct svc_rqst *
194 nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
195 {
196         struct svc_xprt *bc_xprt;
197         struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
198
199         dprintk("--> %s\n", __func__);
200         /* Create a svc_sock for the service */
201         bc_xprt = svc_sock_create(serv, xprt->prot);
202         if (!bc_xprt)
203                 goto out;
204
205         /*
206          * Save the svc_serv in the transport so that it can
207          * be referenced when the session backchannel is initialized
208          */
209         serv->bc_xprt = bc_xprt;
210         xprt->bc_serv = serv;
211
212         INIT_LIST_HEAD(&serv->sv_cb_list);
213         spin_lock_init(&serv->sv_cb_lock);
214         init_waitqueue_head(&serv->sv_cb_waitq);
215         rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
216         if (IS_ERR(rqstp))
217                 svc_sock_destroy(bc_xprt);
218 out:
219         dprintk("--> %s return %p\n", __func__, rqstp);
220         return rqstp;
221 }
222
223 static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
224                 struct svc_serv *serv, struct rpc_xprt *xprt,
225                 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
226 {
227         if (minorversion) {
228                 *rqstpp = nfs41_callback_up(serv, xprt);
229                 *callback_svc = nfs41_callback_svc;
230         }
231         return minorversion;
232 }
233
234 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
235                 struct nfs_callback_data *cb_info)
236 {
237         if (minorversion)
238                 xprt->bc_serv = cb_info->serv;
239 }
240 #else
241 static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
242                 struct svc_serv *serv, struct rpc_xprt *xprt,
243                 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
244 {
245         return 0;
246 }
247
248 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
249                 struct nfs_callback_data *cb_info)
250 {
251 }
252 #endif /* CONFIG_NFS_V4_1 */
253
254 /*
255  * Bring up the callback thread if it is not already up.
256  */
257 int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
258 {
259         struct svc_serv *serv = NULL;
260         struct svc_rqst *rqstp;
261         int (*callback_svc)(void *vrqstp);
262         struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
263         char svc_name[12];
264         int ret = 0;
265         int minorversion_setup;
266
267         mutex_lock(&nfs_callback_mutex);
268         if (cb_info->users++ || cb_info->task != NULL) {
269                 nfs_callback_bc_serv(minorversion, xprt, cb_info);
270                 goto out;
271         }
272         serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
273         if (!serv) {
274                 ret = -ENOMEM;
275                 goto out_err;
276         }
277
278         minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
279                                         serv, xprt, &rqstp, &callback_svc);
280         if (!minorversion_setup) {
281                 /* v4.0 callback setup */
282                 rqstp = nfs4_callback_up(serv);
283                 callback_svc = nfs4_callback_svc;
284         }
285
286         if (IS_ERR(rqstp)) {
287                 ret = PTR_ERR(rqstp);
288                 goto out_err;
289         }
290
291         svc_sock_update_bufs(serv);
292
293         sprintf(svc_name, "nfsv4.%u-svc", minorversion);
294         cb_info->serv = serv;
295         cb_info->rqst = rqstp;
296         cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
297         if (IS_ERR(cb_info->task)) {
298                 ret = PTR_ERR(cb_info->task);
299                 svc_exit_thread(cb_info->rqst);
300                 cb_info->rqst = NULL;
301                 cb_info->task = NULL;
302                 goto out_err;
303         }
304 out:
305         /*
306          * svc_create creates the svc_serv with sv_nrthreads == 1, and then
307          * svc_prepare_thread increments that. So we need to call svc_destroy
308          * on both success and failure so that the refcount is 1 when the
309          * thread exits.
310          */
311         if (serv)
312                 svc_destroy(serv);
313         mutex_unlock(&nfs_callback_mutex);
314         return ret;
315 out_err:
316         dprintk("NFS: Couldn't create callback socket or server thread; "
317                 "err = %d\n", ret);
318         cb_info->users--;
319         goto out;
320 }
321
322 /*
323  * Kill the callback thread if it's no longer being used.
324  */
325 void nfs_callback_down(int minorversion)
326 {
327         struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
328
329         mutex_lock(&nfs_callback_mutex);
330         cb_info->users--;
331         if (cb_info->users == 0 && cb_info->task != NULL) {
332                 kthread_stop(cb_info->task);
333                 svc_exit_thread(cb_info->rqst);
334                 cb_info->serv = NULL;
335                 cb_info->rqst = NULL;
336                 cb_info->task = NULL;
337         }
338         mutex_unlock(&nfs_callback_mutex);
339 }
340
341 static int check_gss_callback_principal(struct nfs_client *clp,
342                                         struct svc_rqst *rqstp)
343 {
344         struct rpc_clnt *r = clp->cl_rpcclient;
345         char *p = svc_gss_principal(rqstp);
346
347         /*
348          * It might just be a normal user principal, in which case
349          * userspace won't bother to tell us the name at all.
350          */
351         if (p == NULL)
352                 return SVC_DENIED;
353
354         /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
355
356         if (memcmp(p, "nfs@", 4) != 0)
357                 return SVC_DENIED;
358         p += 4;
359         if (strcmp(p, r->cl_server) != 0)
360                 return SVC_DENIED;
361         return SVC_OK;
362 }
363
364 static int nfs_callback_authenticate(struct svc_rqst *rqstp)
365 {
366         struct nfs_client *clp;
367         RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
368         int ret = SVC_OK;
369
370         /* Don't talk to strangers */
371         clp = nfs_find_client(svc_addr(rqstp), 4);
372         if (clp == NULL)
373                 return SVC_DROP;
374
375         dprintk("%s: %s NFSv4 callback!\n", __func__,
376                         svc_print_addr(rqstp, buf, sizeof(buf)));
377
378         switch (rqstp->rq_authop->flavour) {
379                 case RPC_AUTH_NULL:
380                         if (rqstp->rq_proc != CB_NULL)
381                                 ret = SVC_DENIED;
382                         break;
383                 case RPC_AUTH_UNIX:
384                         break;
385                 case RPC_AUTH_GSS:
386                         ret = check_gss_callback_principal(clp, rqstp);
387                         break;
388                 default:
389                         ret = SVC_DENIED;
390         }
391         nfs_put_client(clp);
392         return ret;
393 }
394
395 /*
396  * Define NFS4 callback program
397  */
398 static struct svc_version *nfs4_callback_version[] = {
399         [1] = &nfs4_callback_version1,
400 };
401
402 static struct svc_stat nfs4_callback_stats;
403
404 static struct svc_program nfs4_callback_program = {
405         .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
406         .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
407         .pg_vers = nfs4_callback_version,               /* version table */
408         .pg_name = "NFSv4 callback",                    /* service name */
409         .pg_class = "nfs",                              /* authentication class */
410         .pg_stats = &nfs4_callback_stats,
411         .pg_authenticate = nfs_callback_authenticate,
412 };