]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/net/sunrpc/svc.c | |
3 | * | |
4 | * High-level RPC service routines | |
5 | * | |
6 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | |
7 | */ | |
8 | ||
9 | #include <linux/linkage.h> | |
10 | #include <linux/sched.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/net.h> | |
13 | #include <linux/in.h> | |
14 | #include <linux/mm.h> | |
a7455442 GB |
15 | #include <linux/interrupt.h> |
16 | #include <linux/module.h> | |
1da177e4 LT |
17 | |
18 | #include <linux/sunrpc/types.h> | |
19 | #include <linux/sunrpc/xdr.h> | |
20 | #include <linux/sunrpc/stats.h> | |
21 | #include <linux/sunrpc/svcsock.h> | |
22 | #include <linux/sunrpc/clnt.h> | |
23 | ||
24 | #define RPCDBG_FACILITY RPCDBG_SVCDSP | |
25 | #define RPC_PARANOIA 1 | |
26 | ||
27 | /* | |
28 | * Create an RPC service | |
29 | */ | |
a7455442 GB |
30 | static struct svc_serv * |
31 | __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, | |
bc591ccf | 32 | void (*shutdown)(struct svc_serv *serv)) |
1da177e4 LT |
33 | { |
34 | struct svc_serv *serv; | |
35 | int vers; | |
36 | unsigned int xdrsize; | |
3262c816 | 37 | unsigned int i; |
1da177e4 | 38 | |
0da974f4 | 39 | if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL))) |
1da177e4 | 40 | return NULL; |
9ba02638 | 41 | serv->sv_name = prog->pg_name; |
1da177e4 LT |
42 | serv->sv_program = prog; |
43 | serv->sv_nrthreads = 1; | |
44 | serv->sv_stats = prog->pg_stats; | |
45 | serv->sv_bufsz = bufsize? bufsize : 4096; | |
bc591ccf | 46 | serv->sv_shutdown = shutdown; |
1da177e4 | 47 | xdrsize = 0; |
9ba02638 AG |
48 | while (prog) { |
49 | prog->pg_lovers = prog->pg_nvers-1; | |
50 | for (vers=0; vers<prog->pg_nvers ; vers++) | |
51 | if (prog->pg_vers[vers]) { | |
52 | prog->pg_hivers = vers; | |
53 | if (prog->pg_lovers > vers) | |
54 | prog->pg_lovers = vers; | |
55 | if (prog->pg_vers[vers]->vs_xdrsize > xdrsize) | |
56 | xdrsize = prog->pg_vers[vers]->vs_xdrsize; | |
57 | } | |
58 | prog = prog->pg_next; | |
59 | } | |
1da177e4 | 60 | serv->sv_xdrsize = xdrsize; |
1da177e4 LT |
61 | INIT_LIST_HEAD(&serv->sv_tempsocks); |
62 | INIT_LIST_HEAD(&serv->sv_permsocks); | |
36bdfc8b | 63 | init_timer(&serv->sv_temptimer); |
1da177e4 LT |
64 | spin_lock_init(&serv->sv_lock); |
65 | ||
a7455442 | 66 | serv->sv_nrpools = npools; |
3262c816 GB |
67 | serv->sv_pools = |
68 | kcalloc(sizeof(struct svc_pool), serv->sv_nrpools, | |
69 | GFP_KERNEL); | |
70 | if (!serv->sv_pools) { | |
71 | kfree(serv); | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | for (i = 0; i < serv->sv_nrpools; i++) { | |
76 | struct svc_pool *pool = &serv->sv_pools[i]; | |
77 | ||
78 | dprintk("initialising pool %u for %s\n", | |
79 | i, serv->sv_name); | |
80 | ||
81 | pool->sp_id = i; | |
82 | INIT_LIST_HEAD(&pool->sp_threads); | |
83 | INIT_LIST_HEAD(&pool->sp_sockets); | |
a7455442 | 84 | INIT_LIST_HEAD(&pool->sp_all_threads); |
3262c816 GB |
85 | spin_lock_init(&pool->sp_lock); |
86 | } | |
87 | ||
88 | ||
1da177e4 LT |
89 | /* Remove any stale portmap registrations */ |
90 | svc_register(serv, 0, 0); | |
91 | ||
92 | return serv; | |
93 | } | |
94 | ||
a7455442 GB |
95 | struct svc_serv * |
96 | svc_create(struct svc_program *prog, unsigned int bufsize, | |
97 | void (*shutdown)(struct svc_serv *serv)) | |
98 | { | |
99 | return __svc_create(prog, bufsize, /*npools*/1, shutdown); | |
100 | } | |
101 | ||
102 | struct svc_serv * | |
103 | svc_create_pooled(struct svc_program *prog, unsigned int bufsize, | |
104 | void (*shutdown)(struct svc_serv *serv), | |
105 | svc_thread_fn func, int sig, struct module *mod) | |
106 | { | |
107 | struct svc_serv *serv; | |
108 | ||
109 | serv = __svc_create(prog, bufsize, /*npools*/1, shutdown); | |
110 | ||
111 | if (serv != NULL) { | |
112 | serv->sv_function = func; | |
113 | serv->sv_kill_signal = sig; | |
114 | serv->sv_module = mod; | |
115 | } | |
116 | ||
117 | return serv; | |
118 | } | |
119 | ||
1da177e4 | 120 | /* |
3262c816 | 121 | * Destroy an RPC service. Should be called with the BKL held |
1da177e4 LT |
122 | */ |
123 | void | |
124 | svc_destroy(struct svc_serv *serv) | |
125 | { | |
126 | struct svc_sock *svsk; | |
127 | ||
128 | dprintk("RPC: svc_destroy(%s, %d)\n", | |
129 | serv->sv_program->pg_name, | |
130 | serv->sv_nrthreads); | |
131 | ||
132 | if (serv->sv_nrthreads) { | |
133 | if (--(serv->sv_nrthreads) != 0) { | |
134 | svc_sock_update_bufs(serv); | |
135 | return; | |
136 | } | |
137 | } else | |
138 | printk("svc_destroy: no threads for serv=%p!\n", serv); | |
139 | ||
36bdfc8b GB |
140 | del_timer_sync(&serv->sv_temptimer); |
141 | ||
1da177e4 LT |
142 | while (!list_empty(&serv->sv_tempsocks)) { |
143 | svsk = list_entry(serv->sv_tempsocks.next, | |
144 | struct svc_sock, | |
145 | sk_list); | |
146 | svc_delete_socket(svsk); | |
147 | } | |
bc591ccf N |
148 | if (serv->sv_shutdown) |
149 | serv->sv_shutdown(serv); | |
150 | ||
1da177e4 LT |
151 | while (!list_empty(&serv->sv_permsocks)) { |
152 | svsk = list_entry(serv->sv_permsocks.next, | |
153 | struct svc_sock, | |
154 | sk_list); | |
155 | svc_delete_socket(svsk); | |
156 | } | |
157 | ||
158 | cache_clean_deferred(serv); | |
159 | ||
160 | /* Unregister service with the portmapper */ | |
161 | svc_register(serv, 0, 0); | |
3262c816 | 162 | kfree(serv->sv_pools); |
1da177e4 LT |
163 | kfree(serv); |
164 | } | |
165 | ||
166 | /* | |
167 | * Allocate an RPC server's buffer space. | |
168 | * We allocate pages and place them in rq_argpages. | |
169 | */ | |
170 | static int | |
171 | svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) | |
172 | { | |
173 | int pages; | |
174 | int arghi; | |
175 | ||
176 | if (size > RPCSVC_MAXPAYLOAD) | |
177 | size = RPCSVC_MAXPAYLOAD; | |
178 | pages = 2 + (size+ PAGE_SIZE -1) / PAGE_SIZE; | |
179 | rqstp->rq_argused = 0; | |
180 | rqstp->rq_resused = 0; | |
181 | arghi = 0; | |
09a62660 | 182 | BUG_ON(pages > RPCSVC_MAXPAGES); |
1da177e4 LT |
183 | while (pages) { |
184 | struct page *p = alloc_page(GFP_KERNEL); | |
185 | if (!p) | |
186 | break; | |
187 | rqstp->rq_argpages[arghi++] = p; | |
188 | pages--; | |
189 | } | |
190 | rqstp->rq_arghi = arghi; | |
191 | return ! pages; | |
192 | } | |
193 | ||
194 | /* | |
195 | * Release an RPC server buffer | |
196 | */ | |
197 | static void | |
198 | svc_release_buffer(struct svc_rqst *rqstp) | |
199 | { | |
200 | while (rqstp->rq_arghi) | |
201 | put_page(rqstp->rq_argpages[--rqstp->rq_arghi]); | |
202 | while (rqstp->rq_resused) { | |
203 | if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) | |
204 | continue; | |
205 | put_page(rqstp->rq_respages[rqstp->rq_resused]); | |
206 | } | |
207 | rqstp->rq_argused = 0; | |
208 | } | |
209 | ||
210 | /* | |
3262c816 | 211 | * Create a thread in the given pool. Caller must hold BKL. |
1da177e4 | 212 | */ |
3262c816 GB |
213 | static int |
214 | __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, | |
215 | struct svc_pool *pool) | |
1da177e4 LT |
216 | { |
217 | struct svc_rqst *rqstp; | |
218 | int error = -ENOMEM; | |
219 | ||
0da974f4 | 220 | rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL); |
1da177e4 LT |
221 | if (!rqstp) |
222 | goto out; | |
223 | ||
1da177e4 LT |
224 | init_waitqueue_head(&rqstp->rq_wait); |
225 | ||
12fe2c58 JJ |
226 | if (!(rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) |
227 | || !(rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) | |
1da177e4 LT |
228 | || !svc_init_buffer(rqstp, serv->sv_bufsz)) |
229 | goto out_thread; | |
230 | ||
231 | serv->sv_nrthreads++; | |
3262c816 GB |
232 | spin_lock_bh(&pool->sp_lock); |
233 | pool->sp_nrthreads++; | |
a7455442 | 234 | list_add(&rqstp->rq_all, &pool->sp_all_threads); |
3262c816 | 235 | spin_unlock_bh(&pool->sp_lock); |
1da177e4 | 236 | rqstp->rq_server = serv; |
3262c816 | 237 | rqstp->rq_pool = pool; |
1da177e4 LT |
238 | error = kernel_thread((int (*)(void *)) func, rqstp, 0); |
239 | if (error < 0) | |
240 | goto out_thread; | |
241 | svc_sock_update_bufs(serv); | |
242 | error = 0; | |
243 | out: | |
244 | return error; | |
245 | ||
246 | out_thread: | |
247 | svc_exit_thread(rqstp); | |
248 | goto out; | |
249 | } | |
250 | ||
251 | /* | |
3262c816 GB |
252 | * Create a thread in the default pool. Caller must hold BKL. |
253 | */ | |
254 | int | |
255 | svc_create_thread(svc_thread_fn func, struct svc_serv *serv) | |
256 | { | |
257 | return __svc_create_thread(func, serv, &serv->sv_pools[0]); | |
258 | } | |
259 | ||
a7455442 GB |
260 | /* |
261 | * Choose a pool in which to create a new thread, for svc_set_num_threads | |
262 | */ | |
263 | static inline struct svc_pool * | |
264 | choose_pool(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) | |
265 | { | |
266 | if (pool != NULL) | |
267 | return pool; | |
268 | ||
269 | return &serv->sv_pools[(*state)++ % serv->sv_nrpools]; | |
270 | } | |
271 | ||
272 | /* | |
273 | * Choose a thread to kill, for svc_set_num_threads | |
274 | */ | |
275 | static inline struct task_struct * | |
276 | choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) | |
277 | { | |
278 | unsigned int i; | |
279 | struct task_struct *task = NULL; | |
280 | ||
281 | if (pool != NULL) { | |
282 | spin_lock_bh(&pool->sp_lock); | |
283 | } else { | |
284 | /* choose a pool in round-robin fashion */ | |
285 | for (i = 0; i < serv->sv_nrpools; i++) { | |
286 | pool = &serv->sv_pools[--(*state) % serv->sv_nrpools]; | |
287 | spin_lock_bh(&pool->sp_lock); | |
288 | if (!list_empty(&pool->sp_all_threads)) | |
289 | goto found_pool; | |
290 | spin_unlock_bh(&pool->sp_lock); | |
291 | } | |
292 | return NULL; | |
293 | } | |
294 | ||
295 | found_pool: | |
296 | if (!list_empty(&pool->sp_all_threads)) { | |
297 | struct svc_rqst *rqstp; | |
298 | ||
299 | /* | |
300 | * Remove from the pool->sp_all_threads list | |
301 | * so we don't try to kill it again. | |
302 | */ | |
303 | rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all); | |
304 | list_del_init(&rqstp->rq_all); | |
305 | task = rqstp->rq_task; | |
306 | } | |
307 | spin_unlock_bh(&pool->sp_lock); | |
308 | ||
309 | return task; | |
310 | } | |
311 | ||
312 | /* | |
313 | * Create or destroy enough new threads to make the number | |
314 | * of threads the given number. If `pool' is non-NULL, applies | |
315 | * only to threads in that pool, otherwise round-robins between | |
316 | * all pools. Must be called with a svc_get() reference and | |
317 | * the BKL held. | |
318 | * | |
319 | * Destroying threads relies on the service threads filling in | |
320 | * rqstp->rq_task, which only the nfs ones do. Assumes the serv | |
321 | * has been created using svc_create_pooled(). | |
322 | * | |
323 | * Based on code that used to be in nfsd_svc() but tweaked | |
324 | * to be pool-aware. | |
325 | */ | |
326 | int | |
327 | svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) | |
328 | { | |
329 | struct task_struct *victim; | |
330 | int error = 0; | |
331 | unsigned int state = serv->sv_nrthreads-1; | |
332 | ||
333 | if (pool == NULL) { | |
334 | /* The -1 assumes caller has done a svc_get() */ | |
335 | nrservs -= (serv->sv_nrthreads-1); | |
336 | } else { | |
337 | spin_lock_bh(&pool->sp_lock); | |
338 | nrservs -= pool->sp_nrthreads; | |
339 | spin_unlock_bh(&pool->sp_lock); | |
340 | } | |
341 | ||
342 | /* create new threads */ | |
343 | while (nrservs > 0) { | |
344 | nrservs--; | |
345 | __module_get(serv->sv_module); | |
346 | error = __svc_create_thread(serv->sv_function, serv, | |
347 | choose_pool(serv, pool, &state)); | |
348 | if (error < 0) { | |
349 | module_put(serv->sv_module); | |
350 | break; | |
351 | } | |
352 | } | |
353 | /* destroy old threads */ | |
354 | while (nrservs < 0 && | |
355 | (victim = choose_victim(serv, pool, &state)) != NULL) { | |
356 | send_sig(serv->sv_kill_signal, victim, 1); | |
357 | nrservs++; | |
358 | } | |
359 | ||
360 | return error; | |
361 | } | |
362 | ||
3262c816 GB |
363 | /* |
364 | * Called from a server thread as it's exiting. Caller must hold BKL. | |
1da177e4 LT |
365 | */ |
366 | void | |
367 | svc_exit_thread(struct svc_rqst *rqstp) | |
368 | { | |
369 | struct svc_serv *serv = rqstp->rq_server; | |
3262c816 | 370 | struct svc_pool *pool = rqstp->rq_pool; |
1da177e4 LT |
371 | |
372 | svc_release_buffer(rqstp); | |
a51482bd JJ |
373 | kfree(rqstp->rq_resp); |
374 | kfree(rqstp->rq_argp); | |
375 | kfree(rqstp->rq_auth_data); | |
3262c816 GB |
376 | |
377 | spin_lock_bh(&pool->sp_lock); | |
378 | pool->sp_nrthreads--; | |
a7455442 | 379 | list_del(&rqstp->rq_all); |
3262c816 GB |
380 | spin_unlock_bh(&pool->sp_lock); |
381 | ||
1da177e4 LT |
382 | kfree(rqstp); |
383 | ||
384 | /* Release the server */ | |
385 | if (serv) | |
386 | svc_destroy(serv); | |
387 | } | |
388 | ||
389 | /* | |
390 | * Register an RPC service with the local portmapper. | |
391 | * To unregister a service, call this routine with | |
392 | * proto and port == 0. | |
393 | */ | |
394 | int | |
395 | svc_register(struct svc_serv *serv, int proto, unsigned short port) | |
396 | { | |
397 | struct svc_program *progp; | |
398 | unsigned long flags; | |
399 | int i, error = 0, dummy; | |
400 | ||
401 | progp = serv->sv_program; | |
402 | ||
403 | dprintk("RPC: svc_register(%s, %s, %d)\n", | |
404 | progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port); | |
405 | ||
406 | if (!port) | |
407 | clear_thread_flag(TIF_SIGPENDING); | |
408 | ||
409 | for (i = 0; i < progp->pg_nvers; i++) { | |
410 | if (progp->pg_vers[i] == NULL) | |
411 | continue; | |
412 | error = rpc_register(progp->pg_prog, i, proto, port, &dummy); | |
413 | if (error < 0) | |
414 | break; | |
415 | if (port && !dummy) { | |
416 | error = -EACCES; | |
417 | break; | |
418 | } | |
419 | } | |
420 | ||
421 | if (!port) { | |
422 | spin_lock_irqsave(¤t->sighand->siglock, flags); | |
423 | recalc_sigpending(); | |
424 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | |
425 | } | |
426 | ||
427 | return error; | |
428 | } | |
429 | ||
430 | /* | |
431 | * Process the RPC request. | |
432 | */ | |
433 | int | |
6fb2b47f | 434 | svc_process(struct svc_rqst *rqstp) |
1da177e4 LT |
435 | { |
436 | struct svc_program *progp; | |
437 | struct svc_version *versp = NULL; /* compiler food */ | |
438 | struct svc_procedure *procp = NULL; | |
439 | struct kvec * argv = &rqstp->rq_arg.head[0]; | |
440 | struct kvec * resv = &rqstp->rq_res.head[0]; | |
6fb2b47f | 441 | struct svc_serv *serv = rqstp->rq_server; |
1da177e4 | 442 | kxdrproc_t xdr; |
d8ed029d AD |
443 | __be32 *statp; |
444 | u32 dir, prog, vers, proc; | |
445 | __be32 auth_stat, rpc_stat; | |
1da177e4 | 446 | int auth_res; |
d8ed029d | 447 | __be32 *accept_statp; |
1da177e4 LT |
448 | |
449 | rpc_stat = rpc_success; | |
450 | ||
451 | if (argv->iov_len < 6*4) | |
452 | goto err_short_len; | |
453 | ||
454 | /* setup response xdr_buf. | |
455 | * Initially it has just one page | |
456 | */ | |
457 | svc_take_page(rqstp); /* must succeed */ | |
458 | resv->iov_base = page_address(rqstp->rq_respages[0]); | |
459 | resv->iov_len = 0; | |
460 | rqstp->rq_res.pages = rqstp->rq_respages+1; | |
461 | rqstp->rq_res.len = 0; | |
462 | rqstp->rq_res.page_base = 0; | |
463 | rqstp->rq_res.page_len = 0; | |
334ccfd5 | 464 | rqstp->rq_res.buflen = PAGE_SIZE; |
7c9fdcfb | 465 | rqstp->rq_res.tail[0].iov_base = NULL; |
1da177e4 | 466 | rqstp->rq_res.tail[0].iov_len = 0; |
5c04c46a BF |
467 | /* Will be turned off only in gss privacy case: */ |
468 | rqstp->rq_sendfile_ok = 1; | |
1da177e4 LT |
469 | /* tcp needs a space for the record length... */ |
470 | if (rqstp->rq_prot == IPPROTO_TCP) | |
76994313 | 471 | svc_putnl(resv, 0); |
1da177e4 LT |
472 | |
473 | rqstp->rq_xid = svc_getu32(argv); | |
474 | svc_putu32(resv, rqstp->rq_xid); | |
475 | ||
76994313 AD |
476 | dir = svc_getnl(argv); |
477 | vers = svc_getnl(argv); | |
1da177e4 LT |
478 | |
479 | /* First words of reply: */ | |
76994313 | 480 | svc_putnl(resv, 1); /* REPLY */ |
1da177e4 LT |
481 | |
482 | if (dir != 0) /* direction != CALL */ | |
483 | goto err_bad_dir; | |
484 | if (vers != 2) /* RPC version number */ | |
485 | goto err_bad_rpc; | |
486 | ||
487 | /* Save position in case we later decide to reject: */ | |
488 | accept_statp = resv->iov_base + resv->iov_len; | |
489 | ||
76994313 | 490 | svc_putnl(resv, 0); /* ACCEPT */ |
1da177e4 | 491 | |
76994313 AD |
492 | rqstp->rq_prog = prog = svc_getnl(argv); /* program number */ |
493 | rqstp->rq_vers = vers = svc_getnl(argv); /* version number */ | |
494 | rqstp->rq_proc = proc = svc_getnl(argv); /* procedure number */ | |
1da177e4 LT |
495 | |
496 | progp = serv->sv_program; | |
80d188a6 N |
497 | |
498 | for (progp = serv->sv_program; progp; progp = progp->pg_next) | |
499 | if (prog == progp->pg_prog) | |
500 | break; | |
501 | ||
1da177e4 LT |
502 | /* |
503 | * Decode auth data, and add verifier to reply buffer. | |
504 | * We do this before anything else in order to get a decent | |
505 | * auth verifier. | |
506 | */ | |
507 | auth_res = svc_authenticate(rqstp, &auth_stat); | |
508 | /* Also give the program a chance to reject this call: */ | |
80d188a6 | 509 | if (auth_res == SVC_OK && progp) { |
1da177e4 LT |
510 | auth_stat = rpc_autherr_badcred; |
511 | auth_res = progp->pg_authenticate(rqstp); | |
512 | } | |
513 | switch (auth_res) { | |
514 | case SVC_OK: | |
515 | break; | |
516 | case SVC_GARBAGE: | |
517 | rpc_stat = rpc_garbage_args; | |
518 | goto err_bad; | |
519 | case SVC_SYSERR: | |
520 | rpc_stat = rpc_system_err; | |
521 | goto err_bad; | |
522 | case SVC_DENIED: | |
523 | goto err_bad_auth; | |
524 | case SVC_DROP: | |
525 | goto dropit; | |
526 | case SVC_COMPLETE: | |
527 | goto sendit; | |
528 | } | |
80d188a6 | 529 | |
9ba02638 | 530 | if (progp == NULL) |
1da177e4 LT |
531 | goto err_bad_prog; |
532 | ||
533 | if (vers >= progp->pg_nvers || | |
534 | !(versp = progp->pg_vers[vers])) | |
535 | goto err_bad_vers; | |
536 | ||
537 | procp = versp->vs_proc + proc; | |
538 | if (proc >= versp->vs_nproc || !procp->pc_func) | |
539 | goto err_bad_proc; | |
540 | rqstp->rq_server = serv; | |
541 | rqstp->rq_procinfo = procp; | |
542 | ||
543 | /* Syntactic check complete */ | |
544 | serv->sv_stats->rpccnt++; | |
545 | ||
546 | /* Build the reply header. */ | |
547 | statp = resv->iov_base +resv->iov_len; | |
76994313 | 548 | svc_putnl(resv, RPC_SUCCESS); |
1da177e4 LT |
549 | |
550 | /* Bump per-procedure stats counter */ | |
551 | procp->pc_count++; | |
552 | ||
553 | /* Initialize storage for argp and resp */ | |
554 | memset(rqstp->rq_argp, 0, procp->pc_argsize); | |
555 | memset(rqstp->rq_resp, 0, procp->pc_ressize); | |
556 | ||
557 | /* un-reserve some of the out-queue now that we have a | |
558 | * better idea of reply size | |
559 | */ | |
560 | if (procp->pc_xdrressize) | |
561 | svc_reserve(rqstp, procp->pc_xdrressize<<2); | |
562 | ||
563 | /* Call the function that processes the request. */ | |
564 | if (!versp->vs_dispatch) { | |
565 | /* Decode arguments */ | |
566 | xdr = procp->pc_decode; | |
567 | if (xdr && !xdr(rqstp, argv->iov_base, rqstp->rq_argp)) | |
568 | goto err_garbage; | |
569 | ||
570 | *statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); | |
571 | ||
572 | /* Encode reply */ | |
573 | if (*statp == rpc_success && (xdr = procp->pc_encode) | |
574 | && !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) { | |
575 | dprintk("svc: failed to encode reply\n"); | |
576 | /* serv->sv_stats->rpcsystemerr++; */ | |
577 | *statp = rpc_system_err; | |
578 | } | |
579 | } else { | |
580 | dprintk("svc: calling dispatcher\n"); | |
581 | if (!versp->vs_dispatch(rqstp, statp)) { | |
582 | /* Release reply info */ | |
583 | if (procp->pc_release) | |
584 | procp->pc_release(rqstp, NULL, rqstp->rq_resp); | |
585 | goto dropit; | |
586 | } | |
587 | } | |
588 | ||
589 | /* Check RPC status result */ | |
590 | if (*statp != rpc_success) | |
591 | resv->iov_len = ((void*)statp) - resv->iov_base + 4; | |
592 | ||
593 | /* Release reply info */ | |
594 | if (procp->pc_release) | |
595 | procp->pc_release(rqstp, NULL, rqstp->rq_resp); | |
596 | ||
597 | if (procp->pc_encode == NULL) | |
598 | goto dropit; | |
599 | ||
600 | sendit: | |
601 | if (svc_authorise(rqstp)) | |
602 | goto dropit; | |
603 | return svc_send(rqstp); | |
604 | ||
605 | dropit: | |
606 | svc_authorise(rqstp); /* doesn't hurt to call this twice */ | |
607 | dprintk("svc: svc_process dropit\n"); | |
608 | svc_drop(rqstp); | |
609 | return 0; | |
610 | ||
611 | err_short_len: | |
612 | #ifdef RPC_PARANOIA | |
613 | printk("svc: short len %Zd, dropping request\n", argv->iov_len); | |
614 | #endif | |
615 | goto dropit; /* drop request */ | |
616 | ||
617 | err_bad_dir: | |
618 | #ifdef RPC_PARANOIA | |
619 | printk("svc: bad direction %d, dropping request\n", dir); | |
620 | #endif | |
621 | serv->sv_stats->rpcbadfmt++; | |
622 | goto dropit; /* drop request */ | |
623 | ||
624 | err_bad_rpc: | |
625 | serv->sv_stats->rpcbadfmt++; | |
76994313 AD |
626 | svc_putnl(resv, 1); /* REJECT */ |
627 | svc_putnl(resv, 0); /* RPC_MISMATCH */ | |
628 | svc_putnl(resv, 2); /* Only RPCv2 supported */ | |
629 | svc_putnl(resv, 2); | |
1da177e4 LT |
630 | goto sendit; |
631 | ||
632 | err_bad_auth: | |
633 | dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat)); | |
634 | serv->sv_stats->rpcbadauth++; | |
635 | /* Restore write pointer to location of accept status: */ | |
636 | xdr_ressize_check(rqstp, accept_statp); | |
76994313 AD |
637 | svc_putnl(resv, 1); /* REJECT */ |
638 | svc_putnl(resv, 1); /* AUTH_ERROR */ | |
639 | svc_putnl(resv, ntohl(auth_stat)); /* status */ | |
1da177e4 LT |
640 | goto sendit; |
641 | ||
642 | err_bad_prog: | |
9ba02638 | 643 | dprintk("svc: unknown program %d\n", prog); |
1da177e4 | 644 | serv->sv_stats->rpcbadfmt++; |
76994313 | 645 | svc_putnl(resv, RPC_PROG_UNAVAIL); |
1da177e4 LT |
646 | goto sendit; |
647 | ||
648 | err_bad_vers: | |
649 | #ifdef RPC_PARANOIA | |
650 | printk("svc: unknown version (%d)\n", vers); | |
651 | #endif | |
652 | serv->sv_stats->rpcbadfmt++; | |
76994313 AD |
653 | svc_putnl(resv, RPC_PROG_MISMATCH); |
654 | svc_putnl(resv, progp->pg_lovers); | |
655 | svc_putnl(resv, progp->pg_hivers); | |
1da177e4 LT |
656 | goto sendit; |
657 | ||
658 | err_bad_proc: | |
659 | #ifdef RPC_PARANOIA | |
660 | printk("svc: unknown procedure (%d)\n", proc); | |
661 | #endif | |
662 | serv->sv_stats->rpcbadfmt++; | |
76994313 | 663 | svc_putnl(resv, RPC_PROC_UNAVAIL); |
1da177e4 LT |
664 | goto sendit; |
665 | ||
666 | err_garbage: | |
667 | #ifdef RPC_PARANOIA | |
668 | printk("svc: failed to decode args\n"); | |
669 | #endif | |
670 | rpc_stat = rpc_garbage_args; | |
671 | err_bad: | |
672 | serv->sv_stats->rpcbadfmt++; | |
76994313 | 673 | svc_putnl(resv, ntohl(rpc_stat)); |
1da177e4 LT |
674 | goto sendit; |
675 | } |