Blame view
fs/nfs/callback.c
9.74 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 |
/* * linux/fs/nfs/callback.c * * Copyright (C) 2004 Trond Myklebust * * NFSv4 callback handling */ |
1da177e4c Linux-2.6.12-rc2 |
8 9 10 |
#include <linux/completion.h> #include <linux/ip.h> #include <linux/module.h> |
1da177e4c Linux-2.6.12-rc2 |
11 12 13 |
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svcsock.h> #include <linux/nfs_fs.h> |
353ab6e97 [PATCH] sem2mutex... |
14 |
#include <linux/mutex.h> |
831441862 Freezer: make ker... |
15 |
#include <linux/freezer.h> |
a277e33cb NFS: convert nfs4... |
16 |
#include <linux/kthread.h> |
945b34a77 rpc: allow gss ca... |
17 |
#include <linux/sunrpc/svcauth_gss.h> |
a43cde94f nfs41: Implement ... |
18 |
#include <linux/sunrpc/bc_xprt.h> |
14c850212 [INET_SOCK]: Move... |
19 20 |
#include <net/inet_sock.h> |
4ce79717c [PATCH] NFS: Head... |
21 |
#include "nfs4_fs.h" |
1da177e4c Linux-2.6.12-rc2 |
22 |
#include "callback.h" |
24c8dbbb5 NFS: Generalise t... |
23 |
#include "internal.h" |
1da177e4c Linux-2.6.12-rc2 |
24 25 26 27 28 |
#define NFSDBG_FACILITY NFSDBG_CALLBACK struct nfs_callback_data { unsigned int users; |
a43cde94f nfs41: Implement ... |
29 |
struct svc_serv *serv; |
5afc597c5 nfs4: fix potenti... |
30 |
struct svc_rqst *rqst; |
a277e33cb NFS: convert nfs4... |
31 |
struct task_struct *task; |
1da177e4c Linux-2.6.12-rc2 |
32 |
}; |
e82dc22da nfs41: Allow NFSv... |
33 |
static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; |
353ab6e97 [PATCH] sem2mutex... |
34 |
static DEFINE_MUTEX(nfs_callback_mutex); |
1da177e4c Linux-2.6.12-rc2 |
35 |
static struct svc_program nfs4_callback_program; |
a72b44222 NFSv4: Allow user... |
36 |
unsigned int nfs_callback_set_tcpport; |
1da177e4c Linux-2.6.12-rc2 |
37 |
unsigned short nfs_callback_tcpport; |
f738f5170 NFS: Start PF_INE... |
38 |
unsigned short nfs_callback_tcpport6; |
c140aa913 NFSv4: Clean up t... |
39 |
#define NFS_CALLBACK_MAXPORTNR (65535U) |
7d4e2747a NFS: Fix up split... |
40 |
|
9bbb9e5a3 param: use ops in... |
41 |
static int param_set_portnr(const char *val, const struct kernel_param *kp) |
7d4e2747a NFS: Fix up split... |
42 |
{ |
c140aa913 NFSv4: Clean up t... |
43 44 45 46 47 48 49 |
unsigned long num; int ret; if (!val) return -EINVAL; ret = strict_strtoul(val, 0, &num); if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR) |
7d4e2747a NFS: Fix up split... |
50 |
return -EINVAL; |
c140aa913 NFSv4: Clean up t... |
51 |
*((unsigned int *)kp->arg) = num; |
7d4e2747a NFS: Fix up split... |
52 53 |
return 0; } |
9bbb9e5a3 param: use ops in... |
54 55 56 57 |
static struct kernel_param_ops param_ops_portnr = { .set = param_set_portnr, .get = param_get_uint, }; |
c140aa913 NFSv4: Clean up t... |
58 59 60 |
#define param_check_portnr(name, p) __param_check(name, p, unsigned int); module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644); |
1da177e4c Linux-2.6.12-rc2 |
61 62 |
/* |
e82dc22da nfs41: Allow NFSv... |
63 |
* This is the NFSv4 callback kernel thread. |
1da177e4c Linux-2.6.12-rc2 |
64 |
*/ |
a277e33cb NFS: convert nfs4... |
65 |
static int |
714685137 nfs41: minorversi... |
66 |
nfs4_callback_svc(void *vrqstp) |
1da177e4c Linux-2.6.12-rc2 |
67 |
{ |
06e02d66f NFS: don't let nf... |
68 |
int err, preverr = 0; |
a277e33cb NFS: convert nfs4... |
69 |
struct svc_rqst *rqstp = vrqstp; |
1da177e4c Linux-2.6.12-rc2 |
70 |
|
831441862 Freezer: make ker... |
71 |
set_freezable(); |
1da177e4c Linux-2.6.12-rc2 |
72 |
|
a277e33cb NFS: convert nfs4... |
73 |
while (!kthread_should_stop()) { |
1da177e4c Linux-2.6.12-rc2 |
74 75 76 |
/* * Listen for a request on the socket */ |
6fb2b47fa [PATCH] knfsd: Dr... |
77 |
err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT); |
06e02d66f NFS: don't let nf... |
78 79 |
if (err == -EAGAIN || err == -EINTR) { preverr = err; |
1da177e4c Linux-2.6.12-rc2 |
80 |
continue; |
06e02d66f NFS: don't let nf... |
81 |
} |
1da177e4c Linux-2.6.12-rc2 |
82 |
if (err < 0) { |
06e02d66f NFS: don't let nf... |
83 84 85 86 87 88 89 90 |
if (err != preverr) { printk(KERN_WARNING "%s: unexpected error " "from svc_recv (%d) ", __func__, err); preverr = err; } schedule_timeout_uninterruptible(HZ); continue; |
1da177e4c Linux-2.6.12-rc2 |
91 |
} |
06e02d66f NFS: don't let nf... |
92 |
preverr = err; |
6fb2b47fa [PATCH] knfsd: Dr... |
93 |
svc_process(rqstp); |
1da177e4c Linux-2.6.12-rc2 |
94 |
} |
a277e33cb NFS: convert nfs4... |
95 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
96 97 98 |
} /* |
714685137 nfs41: minorversi... |
99 |
* Prepare to bring up the NFSv4 callback service |
1da177e4c Linux-2.6.12-rc2 |
100 |
*/ |
714685137 nfs41: minorversi... |
101 102 |
struct svc_rqst * nfs4_callback_up(struct svc_serv *serv) |
1da177e4c Linux-2.6.12-rc2 |
103 |
{ |
714685137 nfs41: minorversi... |
104 |
int ret; |
482fb94e1 [PATCH] knfsd: SU... |
105 |
|
fc5d00b04 sunrpc: Add net a... |
106 |
ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET, |
9652ada3f SUNRPC: Change sv... |
107 |
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); |
482fb94e1 [PATCH] knfsd: SU... |
108 |
if (ret <= 0) |
8e60029f4 NFS: fix referenc... |
109 |
goto out_err; |
482fb94e1 [PATCH] knfsd: SU... |
110 |
nfs_callback_tcpport = ret; |
18de97353 NFS: Enable NFSv4... |
111 112 |
dprintk("NFS: Callback listener port = %u (af %u) ", |
26298caac NFS: Revert creat... |
113 |
nfs_callback_tcpport, PF_INET); |
482fb94e1 [PATCH] knfsd: SU... |
114 |
|
fc5d00b04 sunrpc: Add net a... |
115 |
ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6, |
f738f5170 NFS: Start PF_INE... |
116 117 118 119 120 121 |
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); if (ret > 0) { nfs_callback_tcpport6 = ret; dprintk("NFS: Callback listener port = %u (af %u) ", nfs_callback_tcpport6, PF_INET6); |
18fc31641 NFS: Fix false er... |
122 123 124 |
} else if (ret == -EAFNOSUPPORT) ret = 0; else |
f738f5170 NFS: Start PF_INE... |
125 |
goto out_err; |
f738f5170 NFS: Start PF_INE... |
126 |
|
11fd165c6 sunrpc: use bette... |
127 |
return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); |
714685137 nfs41: minorversi... |
128 129 130 131 132 133 |
out_err: if (ret == 0) ret = -ENOMEM; return ERR_PTR(ret); } |
a43cde94f nfs41: Implement ... |
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
#if defined(CONFIG_NFS_V4_1) /* * The callback service for NFSv4.1 callbacks */ static int nfs41_callback_svc(void *vrqstp) { struct svc_rqst *rqstp = vrqstp; struct svc_serv *serv = rqstp->rq_server; struct rpc_rqst *req; int error; DEFINE_WAIT(wq); set_freezable(); |
a43cde94f nfs41: Implement ... |
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
while (!kthread_should_stop()) { prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); spin_lock_bh(&serv->sv_cb_lock); if (!list_empty(&serv->sv_cb_list)) { req = list_first_entry(&serv->sv_cb_list, struct rpc_rqst, rq_bc_list); list_del(&req->rq_bc_list); spin_unlock_bh(&serv->sv_cb_lock); dprintk("Invoking bc_svc_process() "); error = bc_svc_process(serv, req, rqstp); dprintk("bc_svc_process() returned w/ error code= %d ", error); } else { spin_unlock_bh(&serv->sv_cb_lock); schedule(); } finish_wait(&serv->sv_cb_waitq, &wq); } |
a43cde94f nfs41: Implement ... |
168 169 170 171 172 173 174 175 176 |
return 0; } /* * Bring up the NFSv4.1 callback service */ struct svc_rqst * nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) { |
01c9a0bc6 NFS use svc_creat... |
177 178 |
struct svc_rqst *rqstp; int ret; |
8f9752423 nfs41: create a s... |
179 |
|
01c9a0bc6 NFS use svc_creat... |
180 181 182 183 184 185 186 187 188 |
/* * Create an svc_sock for the back channel service that shares the * fore channel connection. * Returns the input port (0) and sets the svc_serv bc_xprt on success */ ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0, SVC_SOCK_ANONYMOUS); if (ret < 0) { rqstp = ERR_PTR(ret); |
8f9752423 nfs41: create a s... |
189 |
goto out; |
01c9a0bc6 NFS use svc_creat... |
190 |
} |
8f9752423 nfs41: create a s... |
191 |
|
a43cde94f nfs41: Implement ... |
192 193 194 195 196 197 198 199 200 |
/* * Save the svc_serv in the transport so that it can * be referenced when the session backchannel is initialized */ xprt->bc_serv = serv; INIT_LIST_HEAD(&serv->sv_cb_list); spin_lock_init(&serv->sv_cb_lock); init_waitqueue_head(&serv->sv_cb_waitq); |
11fd165c6 sunrpc: use bette... |
201 |
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); |
01c9a0bc6 NFS use svc_creat... |
202 |
if (IS_ERR(rqstp)) { |
4a19de0f4 NFS rename client... |
203 204 |
svc_xprt_put(serv->sv_bc_xprt); serv->sv_bc_xprt = NULL; |
01c9a0bc6 NFS use svc_creat... |
205 |
} |
8f9752423 nfs41: create a s... |
206 |
out: |
01c9a0bc6 NFS use svc_creat... |
207 208 209 |
dprintk("--> %s return %ld ", __func__, IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0); |
8f9752423 nfs41: create a s... |
210 |
return rqstp; |
a43cde94f nfs41: Implement ... |
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
} static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, struct svc_serv *serv, struct rpc_xprt *xprt, struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) { if (minorversion) { *rqstpp = nfs41_callback_up(serv, xprt); *callback_svc = nfs41_callback_svc; } return minorversion; } static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct nfs_callback_data *cb_info) { if (minorversion) xprt->bc_serv = cb_info->serv; } #else static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, struct svc_serv *serv, struct rpc_xprt *xprt, struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) { return 0; } static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, struct nfs_callback_data *cb_info) { } #endif /* CONFIG_NFS_V4_1 */ |
714685137 nfs41: minorversi... |
243 244 245 246 247 248 249 250 |
/* * Bring up the callback thread if it is not already up. */ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) { struct svc_serv *serv = NULL; struct svc_rqst *rqstp; int (*callback_svc)(void *vrqstp); |
e82dc22da nfs41: Allow NFSv... |
251 |
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
714685137 nfs41: minorversi... |
252 253 |
char svc_name[12]; int ret = 0; |
a43cde94f nfs41: Implement ... |
254 |
int minorversion_setup; |
714685137 nfs41: minorversi... |
255 256 |
mutex_lock(&nfs_callback_mutex); |
e82dc22da nfs41: Allow NFSv... |
257 258 |
if (cb_info->users++ || cb_info->task != NULL) { nfs_callback_bc_serv(minorversion, xprt, cb_info); |
714685137 nfs41: minorversi... |
259 |
goto out; |
a43cde94f nfs41: Implement ... |
260 |
} |
714685137 nfs41: minorversi... |
261 262 263 264 265 |
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); if (!serv) { ret = -ENOMEM; goto out_err; } |
a43cde94f nfs41: Implement ... |
266 267 268 269 |
minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, serv, xprt, &rqstp, &callback_svc); if (!minorversion_setup) { /* v4.0 callback setup */ |
714685137 nfs41: minorversi... |
270 271 |
rqstp = nfs4_callback_up(serv); callback_svc = nfs4_callback_svc; |
714685137 nfs41: minorversi... |
272 273 274 275 |
} if (IS_ERR(rqstp)) { ret = PTR_ERR(rqstp); |
8e60029f4 NFS: fix referenc... |
276 |
goto out_err; |
a277e33cb NFS: convert nfs4... |
277 278 279 |
} svc_sock_update_bufs(serv); |
a277e33cb NFS: convert nfs4... |
280 |
|
714685137 nfs41: minorversi... |
281 |
sprintf(svc_name, "nfsv4.%u-svc", minorversion); |
e82dc22da nfs41: Allow NFSv... |
282 283 284 285 286 287 288 289 |
cb_info->serv = serv; cb_info->rqst = rqstp; cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); if (IS_ERR(cb_info->task)) { ret = PTR_ERR(cb_info->task); svc_exit_thread(cb_info->rqst); cb_info->rqst = NULL; cb_info->task = NULL; |
a277e33cb NFS: convert nfs4... |
290 291 |
goto out_err; } |
1da177e4c Linux-2.6.12-rc2 |
292 |
out: |
8e60029f4 NFS: fix referenc... |
293 294 |
/* * svc_create creates the svc_serv with sv_nrthreads == 1, and then |
a277e33cb NFS: convert nfs4... |
295 |
* svc_prepare_thread increments that. So we need to call svc_destroy |
8e60029f4 NFS: fix referenc... |
296 297 298 299 300 |
* on both success and failure so that the refcount is 1 when the * thread exits. */ if (serv) svc_destroy(serv); |
353ab6e97 [PATCH] sem2mutex... |
301 |
mutex_unlock(&nfs_callback_mutex); |
1da177e4c Linux-2.6.12-rc2 |
302 |
return ret; |
8e60029f4 NFS: fix referenc... |
303 |
out_err: |
18de97353 NFS: Enable NFSv4... |
304 305 306 |
dprintk("NFS: Couldn't create callback socket or server thread; " "err = %d ", ret); |
e82dc22da nfs41: Allow NFSv... |
307 |
cb_info->users--; |
1da177e4c Linux-2.6.12-rc2 |
308 309 310 311 |
goto out; } /* |
5afc597c5 nfs4: fix potenti... |
312 |
* Kill the callback thread if it's no longer being used. |
1da177e4c Linux-2.6.12-rc2 |
313 |
*/ |
e82dc22da nfs41: Allow NFSv... |
314 |
void nfs_callback_down(int minorversion) |
1da177e4c Linux-2.6.12-rc2 |
315 |
{ |
e82dc22da nfs41: Allow NFSv... |
316 |
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; |
353ab6e97 [PATCH] sem2mutex... |
317 |
mutex_lock(&nfs_callback_mutex); |
e82dc22da nfs41: Allow NFSv... |
318 319 320 321 322 323 324 |
cb_info->users--; if (cb_info->users == 0 && cb_info->task != NULL) { kthread_stop(cb_info->task); svc_exit_thread(cb_info->rqst); cb_info->serv = NULL; cb_info->rqst = NULL; cb_info->task = NULL; |
5afc597c5 nfs4: fix potenti... |
325 |
} |
353ab6e97 [PATCH] sem2mutex... |
326 |
mutex_unlock(&nfs_callback_mutex); |
1da177e4c Linux-2.6.12-rc2 |
327 |
} |
778be232a NFS do not find c... |
328 329 330 |
/* Boolean check of RPC_AUTH_GSS principal */ int check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) |
945b34a77 rpc: allow gss ca... |
331 332 333 |
{ struct rpc_clnt *r = clp->cl_rpcclient; char *p = svc_gss_principal(rqstp); |
778be232a NFS do not find c... |
334 335 |
if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) return 1; |
ece0de633 NFS RPC_AUTH_GSS ... |
336 337 |
/* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ if (clp->cl_minorversion != 0) |
778be232a NFS do not find c... |
338 |
return 0; |
945b34a77 rpc: allow gss ca... |
339 340 341 342 343 |
/* * It might just be a normal user principal, in which case * userspace won't bother to tell us the name at all. */ if (p == NULL) |
778be232a NFS do not find c... |
344 |
return 0; |
945b34a77 rpc: allow gss ca... |
345 346 347 348 |
/* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ if (memcmp(p, "nfs@", 4) != 0) |
778be232a NFS do not find c... |
349 |
return 0; |
945b34a77 rpc: allow gss ca... |
350 351 |
p += 4; if (strcmp(p, r->cl_server) != 0) |
778be232a NFS do not find c... |
352 353 |
return 0; return 1; |
945b34a77 rpc: allow gss ca... |
354 |
} |
778be232a NFS do not find c... |
355 356 357 358 359 360 361 362 363 |
/* * pg_authenticate method for nfsv4 callback threads. * * The authflavor has been negotiated, so an incorrect flavor is a server * bug. Drop packets with incorrect authflavor. * * All other checking done after NFS decoding where the nfs_client can be * found in nfs4_callback_compound */ |
1da177e4c Linux-2.6.12-rc2 |
364 365 |
static int nfs_callback_authenticate(struct svc_rqst *rqstp) { |
1da177e4c Linux-2.6.12-rc2 |
366 |
switch (rqstp->rq_authop->flavour) { |
778be232a NFS do not find c... |
367 368 369 370 371 372 373 374 |
case RPC_AUTH_NULL: if (rqstp->rq_proc != CB_NULL) return SVC_DROP; break; case RPC_AUTH_GSS: /* No RPC_AUTH_GSS support yet in NFSv4.1 */ if (svc_is_backchannel(rqstp)) return SVC_DROP; |
1da177e4c Linux-2.6.12-rc2 |
375 |
} |
778be232a NFS do not find c... |
376 |
return SVC_OK; |
1da177e4c Linux-2.6.12-rc2 |
377 378 379 380 381 |
} /* * Define NFS4 callback program */ |
1da177e4c Linux-2.6.12-rc2 |
382 383 |
static struct svc_version *nfs4_callback_version[] = { [1] = &nfs4_callback_version1, |
07bccc2dd nfs41: add suppor... |
384 |
[4] = &nfs4_callback_version4, |
1da177e4c Linux-2.6.12-rc2 |
385 386 387 388 389 390 391 392 393 394 395 396 397 |
}; static struct svc_stat nfs4_callback_stats; static struct svc_program nfs4_callback_program = { .pg_prog = NFS4_CALLBACK, /* RPC service number */ .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ .pg_vers = nfs4_callback_version, /* version table */ .pg_name = "NFSv4 callback", /* service name */ .pg_class = "nfs", /* authentication class */ .pg_stats = &nfs4_callback_stats, .pg_authenticate = nfs_callback_authenticate, }; |