xref: /openbmc/linux/net/sunrpc/svc.c (revision bfd241600a3b0db4fe43c859f1460d0a958d924a)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/net/sunrpc/svc.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * High-level RPC service routines
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7*bfd24160SGreg Banks  *
8*bfd24160SGreg Banks  * Multiple threads pools and NUMAisation
9*bfd24160SGreg Banks  * Copyright (c) 2006 Silicon Graphics, Inc.
10*bfd24160SGreg Banks  * by Greg Banks <gnb@melbourne.sgi.com>
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/linkage.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/net.h>
171da177e4SLinus Torvalds #include <linux/in.h>
181da177e4SLinus Torvalds #include <linux/mm.h>
19a7455442SGreg Banks #include <linux/interrupt.h>
20a7455442SGreg Banks #include <linux/module.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <linux/sunrpc/types.h>
231da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h>
241da177e4SLinus Torvalds #include <linux/sunrpc/stats.h>
251da177e4SLinus Torvalds #include <linux/sunrpc/svcsock.h>
261da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #define RPCDBG_FACILITY	RPCDBG_SVCDSP
291da177e4SLinus Torvalds #define RPC_PARANOIA 1
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds /*
32*bfd24160SGreg Banks  * Mode for mapping cpus to pools.
33*bfd24160SGreg Banks  */
34*bfd24160SGreg Banks enum {
35*bfd24160SGreg Banks 	SVC_POOL_NONE = -1,	/* uninitialised, choose one of the others */
36*bfd24160SGreg Banks 	SVC_POOL_GLOBAL,	/* no mapping, just a single global pool
37*bfd24160SGreg Banks 				 * (legacy & UP mode) */
38*bfd24160SGreg Banks 	SVC_POOL_PERCPU,	/* one pool per cpu */
39*bfd24160SGreg Banks 	SVC_POOL_PERNODE	/* one pool per numa node */
40*bfd24160SGreg Banks };
41*bfd24160SGreg Banks 
42*bfd24160SGreg Banks /*
43*bfd24160SGreg Banks  * Structure for mapping cpus to pools and vice versa.
44*bfd24160SGreg Banks  * Setup once during sunrpc initialisation.
45*bfd24160SGreg Banks  */
46*bfd24160SGreg Banks static struct svc_pool_map {
47*bfd24160SGreg Banks 	int mode;			/* Note: int not enum to avoid
48*bfd24160SGreg Banks 					 * warnings about "enumeration value
49*bfd24160SGreg Banks 					 * not handled in switch" */
50*bfd24160SGreg Banks 	unsigned int npools;
51*bfd24160SGreg Banks 	unsigned int *pool_to;		/* maps pool id to cpu or node */
52*bfd24160SGreg Banks 	unsigned int *to_pool;		/* maps cpu or node to pool id */
53*bfd24160SGreg Banks } svc_pool_map = {
54*bfd24160SGreg Banks 	.mode = SVC_POOL_NONE
55*bfd24160SGreg Banks };
56*bfd24160SGreg Banks 
57*bfd24160SGreg Banks 
58*bfd24160SGreg Banks /*
59*bfd24160SGreg Banks  * Detect best pool mapping mode heuristically,
60*bfd24160SGreg Banks  * according to the machine's topology.
61*bfd24160SGreg Banks  */
62*bfd24160SGreg Banks static int
63*bfd24160SGreg Banks svc_pool_map_choose_mode(void)
64*bfd24160SGreg Banks {
65*bfd24160SGreg Banks 	unsigned int node;
66*bfd24160SGreg Banks 
67*bfd24160SGreg Banks 	if (num_online_nodes() > 1) {
68*bfd24160SGreg Banks 		/*
69*bfd24160SGreg Banks 		 * Actually have multiple NUMA nodes,
70*bfd24160SGreg Banks 		 * so split pools on NUMA node boundaries
71*bfd24160SGreg Banks 		 */
72*bfd24160SGreg Banks 		return SVC_POOL_PERNODE;
73*bfd24160SGreg Banks 	}
74*bfd24160SGreg Banks 
75*bfd24160SGreg Banks 	node = any_online_node(node_online_map);
76*bfd24160SGreg Banks 	if (nr_cpus_node(node) > 2) {
77*bfd24160SGreg Banks 		/*
78*bfd24160SGreg Banks 		 * Non-trivial SMP, or CONFIG_NUMA on
79*bfd24160SGreg Banks 		 * non-NUMA hardware, e.g. with a generic
80*bfd24160SGreg Banks 		 * x86_64 kernel on Xeons.  In this case we
81*bfd24160SGreg Banks 		 * want to divide the pools on cpu boundaries.
82*bfd24160SGreg Banks 		 */
83*bfd24160SGreg Banks 		return SVC_POOL_PERCPU;
84*bfd24160SGreg Banks 	}
85*bfd24160SGreg Banks 
86*bfd24160SGreg Banks 	/* default: one global pool */
87*bfd24160SGreg Banks 	return SVC_POOL_GLOBAL;
88*bfd24160SGreg Banks }
89*bfd24160SGreg Banks 
90*bfd24160SGreg Banks /*
91*bfd24160SGreg Banks  * Allocate the to_pool[] and pool_to[] arrays.
92*bfd24160SGreg Banks  * Returns 0 on success or an errno.
93*bfd24160SGreg Banks  */
94*bfd24160SGreg Banks static int
95*bfd24160SGreg Banks svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools)
96*bfd24160SGreg Banks {
97*bfd24160SGreg Banks 	m->to_pool = kcalloc(maxpools, sizeof(unsigned int), GFP_KERNEL);
98*bfd24160SGreg Banks 	if (!m->to_pool)
99*bfd24160SGreg Banks 		goto fail;
100*bfd24160SGreg Banks 	m->pool_to = kcalloc(maxpools, sizeof(unsigned int), GFP_KERNEL);
101*bfd24160SGreg Banks 	if (!m->pool_to)
102*bfd24160SGreg Banks 		goto fail_free;
103*bfd24160SGreg Banks 
104*bfd24160SGreg Banks 	return 0;
105*bfd24160SGreg Banks 
106*bfd24160SGreg Banks fail_free:
107*bfd24160SGreg Banks 	kfree(m->to_pool);
108*bfd24160SGreg Banks fail:
109*bfd24160SGreg Banks 	return -ENOMEM;
110*bfd24160SGreg Banks }
111*bfd24160SGreg Banks 
112*bfd24160SGreg Banks /*
113*bfd24160SGreg Banks  * Initialise the pool map for SVC_POOL_PERCPU mode.
114*bfd24160SGreg Banks  * Returns number of pools or <0 on error.
115*bfd24160SGreg Banks  */
116*bfd24160SGreg Banks static int
117*bfd24160SGreg Banks svc_pool_map_init_percpu(struct svc_pool_map *m)
118*bfd24160SGreg Banks {
119*bfd24160SGreg Banks 	unsigned int maxpools = highest_possible_processor_id()+1;
120*bfd24160SGreg Banks 	unsigned int pidx = 0;
121*bfd24160SGreg Banks 	unsigned int cpu;
122*bfd24160SGreg Banks 	int err;
123*bfd24160SGreg Banks 
124*bfd24160SGreg Banks 	err = svc_pool_map_alloc_arrays(m, maxpools);
125*bfd24160SGreg Banks 	if (err)
126*bfd24160SGreg Banks 		return err;
127*bfd24160SGreg Banks 
128*bfd24160SGreg Banks 	for_each_online_cpu(cpu) {
129*bfd24160SGreg Banks 		BUG_ON(pidx > maxpools);
130*bfd24160SGreg Banks 		m->to_pool[cpu] = pidx;
131*bfd24160SGreg Banks 		m->pool_to[pidx] = cpu;
132*bfd24160SGreg Banks 		pidx++;
133*bfd24160SGreg Banks 	}
134*bfd24160SGreg Banks 	/* cpus brought online later all get mapped to pool0, sorry */
135*bfd24160SGreg Banks 
136*bfd24160SGreg Banks 	return pidx;
137*bfd24160SGreg Banks };
138*bfd24160SGreg Banks 
139*bfd24160SGreg Banks 
140*bfd24160SGreg Banks /*
141*bfd24160SGreg Banks  * Initialise the pool map for SVC_POOL_PERNODE mode.
142*bfd24160SGreg Banks  * Returns number of pools or <0 on error.
143*bfd24160SGreg Banks  */
144*bfd24160SGreg Banks static int
145*bfd24160SGreg Banks svc_pool_map_init_pernode(struct svc_pool_map *m)
146*bfd24160SGreg Banks {
147*bfd24160SGreg Banks 	unsigned int maxpools = highest_possible_node_id()+1;
148*bfd24160SGreg Banks 	unsigned int pidx = 0;
149*bfd24160SGreg Banks 	unsigned int node;
150*bfd24160SGreg Banks 	int err;
151*bfd24160SGreg Banks 
152*bfd24160SGreg Banks 	err = svc_pool_map_alloc_arrays(m, maxpools);
153*bfd24160SGreg Banks 	if (err)
154*bfd24160SGreg Banks 		return err;
155*bfd24160SGreg Banks 
156*bfd24160SGreg Banks 	for_each_node_with_cpus(node) {
157*bfd24160SGreg Banks 		/* some architectures (e.g. SN2) have cpuless nodes */
158*bfd24160SGreg Banks 		BUG_ON(pidx > maxpools);
159*bfd24160SGreg Banks 		m->to_pool[node] = pidx;
160*bfd24160SGreg Banks 		m->pool_to[pidx] = node;
161*bfd24160SGreg Banks 		pidx++;
162*bfd24160SGreg Banks 	}
163*bfd24160SGreg Banks 	/* nodes brought online later all get mapped to pool0, sorry */
164*bfd24160SGreg Banks 
165*bfd24160SGreg Banks 	return pidx;
166*bfd24160SGreg Banks }
167*bfd24160SGreg Banks 
168*bfd24160SGreg Banks 
169*bfd24160SGreg Banks /*
170*bfd24160SGreg Banks  * Build the global map of cpus to pools and vice versa.
171*bfd24160SGreg Banks  */
172*bfd24160SGreg Banks static unsigned int
173*bfd24160SGreg Banks svc_pool_map_init(void)
174*bfd24160SGreg Banks {
175*bfd24160SGreg Banks 	struct svc_pool_map *m = &svc_pool_map;
176*bfd24160SGreg Banks 	int npools = -1;
177*bfd24160SGreg Banks 
178*bfd24160SGreg Banks 	if (m->mode != SVC_POOL_NONE)
179*bfd24160SGreg Banks 		return m->npools;
180*bfd24160SGreg Banks 
181*bfd24160SGreg Banks 	m->mode = svc_pool_map_choose_mode();
182*bfd24160SGreg Banks 
183*bfd24160SGreg Banks 	switch (m->mode) {
184*bfd24160SGreg Banks 	case SVC_POOL_PERCPU:
185*bfd24160SGreg Banks 		npools = svc_pool_map_init_percpu(m);
186*bfd24160SGreg Banks 		break;
187*bfd24160SGreg Banks 	case SVC_POOL_PERNODE:
188*bfd24160SGreg Banks 		npools = svc_pool_map_init_pernode(m);
189*bfd24160SGreg Banks 		break;
190*bfd24160SGreg Banks 	}
191*bfd24160SGreg Banks 
192*bfd24160SGreg Banks 	if (npools < 0) {
193*bfd24160SGreg Banks 		/* default, or memory allocation failure */
194*bfd24160SGreg Banks 		npools = 1;
195*bfd24160SGreg Banks 		m->mode = SVC_POOL_GLOBAL;
196*bfd24160SGreg Banks 	}
197*bfd24160SGreg Banks 	m->npools = npools;
198*bfd24160SGreg Banks 
199*bfd24160SGreg Banks 	return m->npools;
200*bfd24160SGreg Banks }
201*bfd24160SGreg Banks 
202*bfd24160SGreg Banks /*
203*bfd24160SGreg Banks  * Set the current thread's cpus_allowed mask so that it
204*bfd24160SGreg Banks  * will only run on cpus in the given pool.
205*bfd24160SGreg Banks  *
206*bfd24160SGreg Banks  * Returns 1 and fills in oldmask iff a cpumask was applied.
207*bfd24160SGreg Banks  */
208*bfd24160SGreg Banks static inline int
209*bfd24160SGreg Banks svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask)
210*bfd24160SGreg Banks {
211*bfd24160SGreg Banks 	struct svc_pool_map *m = &svc_pool_map;
212*bfd24160SGreg Banks 	unsigned int node; /* or cpu */
213*bfd24160SGreg Banks 
214*bfd24160SGreg Banks 	/*
215*bfd24160SGreg Banks 	 * The caller checks for sv_nrpools > 1, which
216*bfd24160SGreg Banks 	 * implies that we've been initialized and the
217*bfd24160SGreg Banks 	 * map mode is not NONE.
218*bfd24160SGreg Banks 	 */
219*bfd24160SGreg Banks 	BUG_ON(m->mode == SVC_POOL_NONE);
220*bfd24160SGreg Banks 
221*bfd24160SGreg Banks 	switch (m->mode)
222*bfd24160SGreg Banks 	{
223*bfd24160SGreg Banks 	default:
224*bfd24160SGreg Banks 		return 0;
225*bfd24160SGreg Banks 	case SVC_POOL_PERCPU:
226*bfd24160SGreg Banks 		node = m->pool_to[pidx];
227*bfd24160SGreg Banks 		*oldmask = current->cpus_allowed;
228*bfd24160SGreg Banks 		set_cpus_allowed(current, cpumask_of_cpu(node));
229*bfd24160SGreg Banks 		return 1;
230*bfd24160SGreg Banks 	case SVC_POOL_PERNODE:
231*bfd24160SGreg Banks 		node = m->pool_to[pidx];
232*bfd24160SGreg Banks 		*oldmask = current->cpus_allowed;
233*bfd24160SGreg Banks 		set_cpus_allowed(current, node_to_cpumask(node));
234*bfd24160SGreg Banks 		return 1;
235*bfd24160SGreg Banks 	}
236*bfd24160SGreg Banks }
237*bfd24160SGreg Banks 
238*bfd24160SGreg Banks /*
239*bfd24160SGreg Banks  * Use the mapping mode to choose a pool for a given CPU.
240*bfd24160SGreg Banks  * Used when enqueueing an incoming RPC.  Always returns
241*bfd24160SGreg Banks  * a non-NULL pool pointer.
242*bfd24160SGreg Banks  */
243*bfd24160SGreg Banks struct svc_pool *
244*bfd24160SGreg Banks svc_pool_for_cpu(struct svc_serv *serv, int cpu)
245*bfd24160SGreg Banks {
246*bfd24160SGreg Banks 	struct svc_pool_map *m = &svc_pool_map;
247*bfd24160SGreg Banks 	unsigned int pidx = 0;
248*bfd24160SGreg Banks 
249*bfd24160SGreg Banks 	/*
250*bfd24160SGreg Banks 	 * SVC_POOL_NONE happens in a pure client when
251*bfd24160SGreg Banks 	 * lockd is brought up, so silently treat it the
252*bfd24160SGreg Banks 	 * same as SVC_POOL_GLOBAL.
253*bfd24160SGreg Banks 	 */
254*bfd24160SGreg Banks 
255*bfd24160SGreg Banks 	switch (m->mode) {
256*bfd24160SGreg Banks 	case SVC_POOL_PERCPU:
257*bfd24160SGreg Banks 		pidx = m->to_pool[cpu];
258*bfd24160SGreg Banks 		break;
259*bfd24160SGreg Banks 	case SVC_POOL_PERNODE:
260*bfd24160SGreg Banks 		pidx = m->to_pool[cpu_to_node(cpu)];
261*bfd24160SGreg Banks 		break;
262*bfd24160SGreg Banks 	}
263*bfd24160SGreg Banks 	return &serv->sv_pools[pidx % serv->sv_nrpools];
264*bfd24160SGreg Banks }
265*bfd24160SGreg Banks 
266*bfd24160SGreg Banks 
267*bfd24160SGreg Banks /*
2681da177e4SLinus Torvalds  * Create an RPC service
2691da177e4SLinus Torvalds  */
270a7455442SGreg Banks static struct svc_serv *
271a7455442SGreg Banks __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
272bc591ccfSNeilBrown 	   void (*shutdown)(struct svc_serv *serv))
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	struct svc_serv	*serv;
2751da177e4SLinus Torvalds 	int vers;
2761da177e4SLinus Torvalds 	unsigned int xdrsize;
2773262c816SGreg Banks 	unsigned int i;
2781da177e4SLinus Torvalds 
2790da974f4SPanagiotis Issaris 	if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
2801da177e4SLinus Torvalds 		return NULL;
2819ba02638SAndreas Gruenbacher 	serv->sv_name      = prog->pg_name;
2821da177e4SLinus Torvalds 	serv->sv_program   = prog;
2831da177e4SLinus Torvalds 	serv->sv_nrthreads = 1;
2841da177e4SLinus Torvalds 	serv->sv_stats     = prog->pg_stats;
2851da177e4SLinus Torvalds 	serv->sv_bufsz	   = bufsize? bufsize : 4096;
286bc591ccfSNeilBrown 	serv->sv_shutdown  = shutdown;
2871da177e4SLinus Torvalds 	xdrsize = 0;
2889ba02638SAndreas Gruenbacher 	while (prog) {
2899ba02638SAndreas Gruenbacher 		prog->pg_lovers = prog->pg_nvers-1;
2901da177e4SLinus Torvalds 		for (vers=0; vers<prog->pg_nvers ; vers++)
2911da177e4SLinus Torvalds 			if (prog->pg_vers[vers]) {
2921da177e4SLinus Torvalds 				prog->pg_hivers = vers;
2931da177e4SLinus Torvalds 				if (prog->pg_lovers > vers)
2941da177e4SLinus Torvalds 					prog->pg_lovers = vers;
2951da177e4SLinus Torvalds 				if (prog->pg_vers[vers]->vs_xdrsize > xdrsize)
2961da177e4SLinus Torvalds 					xdrsize = prog->pg_vers[vers]->vs_xdrsize;
2971da177e4SLinus Torvalds 			}
2989ba02638SAndreas Gruenbacher 		prog = prog->pg_next;
2999ba02638SAndreas Gruenbacher 	}
3001da177e4SLinus Torvalds 	serv->sv_xdrsize   = xdrsize;
3011da177e4SLinus Torvalds 	INIT_LIST_HEAD(&serv->sv_tempsocks);
3021da177e4SLinus Torvalds 	INIT_LIST_HEAD(&serv->sv_permsocks);
30336bdfc8bSGreg Banks 	init_timer(&serv->sv_temptimer);
3041da177e4SLinus Torvalds 	spin_lock_init(&serv->sv_lock);
3051da177e4SLinus Torvalds 
306a7455442SGreg Banks 	serv->sv_nrpools = npools;
3073262c816SGreg Banks 	serv->sv_pools =
3083262c816SGreg Banks 		kcalloc(sizeof(struct svc_pool), serv->sv_nrpools,
3093262c816SGreg Banks 			GFP_KERNEL);
3103262c816SGreg Banks 	if (!serv->sv_pools) {
3113262c816SGreg Banks 		kfree(serv);
3123262c816SGreg Banks 		return NULL;
3133262c816SGreg Banks 	}
3143262c816SGreg Banks 
3153262c816SGreg Banks 	for (i = 0; i < serv->sv_nrpools; i++) {
3163262c816SGreg Banks 		struct svc_pool *pool = &serv->sv_pools[i];
3173262c816SGreg Banks 
3183262c816SGreg Banks 		dprintk("initialising pool %u for %s\n",
3193262c816SGreg Banks 				i, serv->sv_name);
3203262c816SGreg Banks 
3213262c816SGreg Banks 		pool->sp_id = i;
3223262c816SGreg Banks 		INIT_LIST_HEAD(&pool->sp_threads);
3233262c816SGreg Banks 		INIT_LIST_HEAD(&pool->sp_sockets);
324a7455442SGreg Banks 		INIT_LIST_HEAD(&pool->sp_all_threads);
3253262c816SGreg Banks 		spin_lock_init(&pool->sp_lock);
3263262c816SGreg Banks 	}
3273262c816SGreg Banks 
3283262c816SGreg Banks 
3291da177e4SLinus Torvalds 	/* Remove any stale portmap registrations */
3301da177e4SLinus Torvalds 	svc_register(serv, 0, 0);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	return serv;
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds 
335a7455442SGreg Banks struct svc_serv *
336a7455442SGreg Banks svc_create(struct svc_program *prog, unsigned int bufsize,
337a7455442SGreg Banks 		void (*shutdown)(struct svc_serv *serv))
338a7455442SGreg Banks {
339a7455442SGreg Banks 	return __svc_create(prog, bufsize, /*npools*/1, shutdown);
340a7455442SGreg Banks }
341a7455442SGreg Banks 
342a7455442SGreg Banks struct svc_serv *
343a7455442SGreg Banks svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
344a7455442SGreg Banks 		void (*shutdown)(struct svc_serv *serv),
345a7455442SGreg Banks 		  svc_thread_fn func, int sig, struct module *mod)
346a7455442SGreg Banks {
347a7455442SGreg Banks 	struct svc_serv *serv;
348*bfd24160SGreg Banks 	unsigned int npools = svc_pool_map_init();
349a7455442SGreg Banks 
350*bfd24160SGreg Banks 	serv = __svc_create(prog, bufsize, npools, shutdown);
351a7455442SGreg Banks 
352a7455442SGreg Banks 	if (serv != NULL) {
353a7455442SGreg Banks 		serv->sv_function = func;
354a7455442SGreg Banks 		serv->sv_kill_signal = sig;
355a7455442SGreg Banks 		serv->sv_module = mod;
356a7455442SGreg Banks 	}
357a7455442SGreg Banks 
358a7455442SGreg Banks 	return serv;
359a7455442SGreg Banks }
360a7455442SGreg Banks 
3611da177e4SLinus Torvalds /*
3623262c816SGreg Banks  * Destroy an RPC service.  Should be called with the BKL held
3631da177e4SLinus Torvalds  */
3641da177e4SLinus Torvalds void
3651da177e4SLinus Torvalds svc_destroy(struct svc_serv *serv)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds 	struct svc_sock	*svsk;
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	dprintk("RPC: svc_destroy(%s, %d)\n",
3701da177e4SLinus Torvalds 				serv->sv_program->pg_name,
3711da177e4SLinus Torvalds 				serv->sv_nrthreads);
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	if (serv->sv_nrthreads) {
3741da177e4SLinus Torvalds 		if (--(serv->sv_nrthreads) != 0) {
3751da177e4SLinus Torvalds 			svc_sock_update_bufs(serv);
3761da177e4SLinus Torvalds 			return;
3771da177e4SLinus Torvalds 		}
3781da177e4SLinus Torvalds 	} else
3791da177e4SLinus Torvalds 		printk("svc_destroy: no threads for serv=%p!\n", serv);
3801da177e4SLinus Torvalds 
38136bdfc8bSGreg Banks 	del_timer_sync(&serv->sv_temptimer);
38236bdfc8bSGreg Banks 
3831da177e4SLinus Torvalds 	while (!list_empty(&serv->sv_tempsocks)) {
3841da177e4SLinus Torvalds 		svsk = list_entry(serv->sv_tempsocks.next,
3851da177e4SLinus Torvalds 				  struct svc_sock,
3861da177e4SLinus Torvalds 				  sk_list);
3871da177e4SLinus Torvalds 		svc_delete_socket(svsk);
3881da177e4SLinus Torvalds 	}
389bc591ccfSNeilBrown 	if (serv->sv_shutdown)
390bc591ccfSNeilBrown 		serv->sv_shutdown(serv);
391bc591ccfSNeilBrown 
3921da177e4SLinus Torvalds 	while (!list_empty(&serv->sv_permsocks)) {
3931da177e4SLinus Torvalds 		svsk = list_entry(serv->sv_permsocks.next,
3941da177e4SLinus Torvalds 				  struct svc_sock,
3951da177e4SLinus Torvalds 				  sk_list);
3961da177e4SLinus Torvalds 		svc_delete_socket(svsk);
3971da177e4SLinus Torvalds 	}
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	cache_clean_deferred(serv);
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	/* Unregister service with the portmapper */
4021da177e4SLinus Torvalds 	svc_register(serv, 0, 0);
4033262c816SGreg Banks 	kfree(serv->sv_pools);
4041da177e4SLinus Torvalds 	kfree(serv);
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds /*
4081da177e4SLinus Torvalds  * Allocate an RPC server's buffer space.
4091da177e4SLinus Torvalds  * We allocate pages and place them in rq_argpages.
4101da177e4SLinus Torvalds  */
4111da177e4SLinus Torvalds static int
4121da177e4SLinus Torvalds svc_init_buffer(struct svc_rqst *rqstp, unsigned int size)
4131da177e4SLinus Torvalds {
4141da177e4SLinus Torvalds 	int pages;
4151da177e4SLinus Torvalds 	int arghi;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	if (size > RPCSVC_MAXPAYLOAD)
4181da177e4SLinus Torvalds 		size = RPCSVC_MAXPAYLOAD;
4191da177e4SLinus Torvalds 	pages = 2 + (size+ PAGE_SIZE -1) / PAGE_SIZE;
4201da177e4SLinus Torvalds 	rqstp->rq_argused = 0;
4211da177e4SLinus Torvalds 	rqstp->rq_resused = 0;
4221da177e4SLinus Torvalds 	arghi = 0;
42309a62660SKris Katterjohn 	BUG_ON(pages > RPCSVC_MAXPAGES);
4241da177e4SLinus Torvalds 	while (pages) {
4251da177e4SLinus Torvalds 		struct page *p = alloc_page(GFP_KERNEL);
4261da177e4SLinus Torvalds 		if (!p)
4271da177e4SLinus Torvalds 			break;
4281da177e4SLinus Torvalds 		rqstp->rq_argpages[arghi++] = p;
4291da177e4SLinus Torvalds 		pages--;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 	rqstp->rq_arghi = arghi;
4321da177e4SLinus Torvalds 	return ! pages;
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds /*
4361da177e4SLinus Torvalds  * Release an RPC server buffer
4371da177e4SLinus Torvalds  */
4381da177e4SLinus Torvalds static void
4391da177e4SLinus Torvalds svc_release_buffer(struct svc_rqst *rqstp)
4401da177e4SLinus Torvalds {
4411da177e4SLinus Torvalds 	while (rqstp->rq_arghi)
4421da177e4SLinus Torvalds 		put_page(rqstp->rq_argpages[--rqstp->rq_arghi]);
4431da177e4SLinus Torvalds 	while (rqstp->rq_resused) {
4441da177e4SLinus Torvalds 		if (rqstp->rq_respages[--rqstp->rq_resused] == NULL)
4451da177e4SLinus Torvalds 			continue;
4461da177e4SLinus Torvalds 		put_page(rqstp->rq_respages[rqstp->rq_resused]);
4471da177e4SLinus Torvalds 	}
4481da177e4SLinus Torvalds 	rqstp->rq_argused = 0;
4491da177e4SLinus Torvalds }
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds /*
4523262c816SGreg Banks  * Create a thread in the given pool.  Caller must hold BKL.
453*bfd24160SGreg Banks  * On a NUMA or SMP machine, with a multi-pool serv, the thread
454*bfd24160SGreg Banks  * will be restricted to run on the cpus belonging to the pool.
4551da177e4SLinus Torvalds  */
4563262c816SGreg Banks static int
4573262c816SGreg Banks __svc_create_thread(svc_thread_fn func, struct svc_serv *serv,
4583262c816SGreg Banks 		    struct svc_pool *pool)
4591da177e4SLinus Torvalds {
4601da177e4SLinus Torvalds 	struct svc_rqst	*rqstp;
4611da177e4SLinus Torvalds 	int		error = -ENOMEM;
462*bfd24160SGreg Banks 	int		have_oldmask = 0;
463*bfd24160SGreg Banks 	cpumask_t	oldmask;
4641da177e4SLinus Torvalds 
4650da974f4SPanagiotis Issaris 	rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL);
4661da177e4SLinus Torvalds 	if (!rqstp)
4671da177e4SLinus Torvalds 		goto out;
4681da177e4SLinus Torvalds 
4691da177e4SLinus Torvalds 	init_waitqueue_head(&rqstp->rq_wait);
4701da177e4SLinus Torvalds 
47112fe2c58SJesper Juhl 	if (!(rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL))
47212fe2c58SJesper Juhl 	 || !(rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL))
4731da177e4SLinus Torvalds 	 || !svc_init_buffer(rqstp, serv->sv_bufsz))
4741da177e4SLinus Torvalds 		goto out_thread;
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 	serv->sv_nrthreads++;
4773262c816SGreg Banks 	spin_lock_bh(&pool->sp_lock);
4783262c816SGreg Banks 	pool->sp_nrthreads++;
479a7455442SGreg Banks 	list_add(&rqstp->rq_all, &pool->sp_all_threads);
4803262c816SGreg Banks 	spin_unlock_bh(&pool->sp_lock);
4811da177e4SLinus Torvalds 	rqstp->rq_server = serv;
4823262c816SGreg Banks 	rqstp->rq_pool = pool;
483*bfd24160SGreg Banks 
484*bfd24160SGreg Banks 	if (serv->sv_nrpools > 1)
485*bfd24160SGreg Banks 		have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask);
486*bfd24160SGreg Banks 
4871da177e4SLinus Torvalds 	error = kernel_thread((int (*)(void *)) func, rqstp, 0);
488*bfd24160SGreg Banks 
489*bfd24160SGreg Banks 	if (have_oldmask)
490*bfd24160SGreg Banks 		set_cpus_allowed(current, oldmask);
491*bfd24160SGreg Banks 
4921da177e4SLinus Torvalds 	if (error < 0)
4931da177e4SLinus Torvalds 		goto out_thread;
4941da177e4SLinus Torvalds 	svc_sock_update_bufs(serv);
4951da177e4SLinus Torvalds 	error = 0;
4961da177e4SLinus Torvalds out:
4971da177e4SLinus Torvalds 	return error;
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds out_thread:
5001da177e4SLinus Torvalds 	svc_exit_thread(rqstp);
5011da177e4SLinus Torvalds 	goto out;
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds /*
5053262c816SGreg Banks  * Create a thread in the default pool.  Caller must hold BKL.
5063262c816SGreg Banks  */
5073262c816SGreg Banks int
5083262c816SGreg Banks svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
5093262c816SGreg Banks {
5103262c816SGreg Banks 	return __svc_create_thread(func, serv, &serv->sv_pools[0]);
5113262c816SGreg Banks }
5123262c816SGreg Banks 
5133262c816SGreg Banks /*
514a7455442SGreg Banks  * Choose a pool in which to create a new thread, for svc_set_num_threads
515a7455442SGreg Banks  */
516a7455442SGreg Banks static inline struct svc_pool *
517a7455442SGreg Banks choose_pool(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
518a7455442SGreg Banks {
519a7455442SGreg Banks 	if (pool != NULL)
520a7455442SGreg Banks 		return pool;
521a7455442SGreg Banks 
522a7455442SGreg Banks  	return &serv->sv_pools[(*state)++ % serv->sv_nrpools];
523a7455442SGreg Banks }
524a7455442SGreg Banks 
525a7455442SGreg Banks /*
526a7455442SGreg Banks  * Choose a thread to kill, for svc_set_num_threads
527a7455442SGreg Banks  */
528a7455442SGreg Banks static inline struct task_struct *
529a7455442SGreg Banks choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
530a7455442SGreg Banks {
531a7455442SGreg Banks 	unsigned int i;
532a7455442SGreg Banks 	struct task_struct *task = NULL;
533a7455442SGreg Banks 
534a7455442SGreg Banks 	if (pool != NULL) {
535a7455442SGreg Banks 		spin_lock_bh(&pool->sp_lock);
536a7455442SGreg Banks 	} else {
537a7455442SGreg Banks 		/* choose a pool in round-robin fashion */
538a7455442SGreg Banks  		for (i = 0; i < serv->sv_nrpools; i++) {
539a7455442SGreg Banks  			pool = &serv->sv_pools[--(*state) % serv->sv_nrpools];
540a7455442SGreg Banks 			spin_lock_bh(&pool->sp_lock);
541a7455442SGreg Banks  			if (!list_empty(&pool->sp_all_threads))
542a7455442SGreg Banks  				goto found_pool;
543a7455442SGreg Banks 			spin_unlock_bh(&pool->sp_lock);
544a7455442SGreg Banks  		}
545a7455442SGreg Banks 		return NULL;
546a7455442SGreg Banks 	}
547a7455442SGreg Banks 
548a7455442SGreg Banks found_pool:
549a7455442SGreg Banks 	if (!list_empty(&pool->sp_all_threads)) {
550a7455442SGreg Banks 		struct svc_rqst *rqstp;
551a7455442SGreg Banks 
552a7455442SGreg Banks 		/*
553a7455442SGreg Banks 		 * Remove from the pool->sp_all_threads list
554a7455442SGreg Banks 		 * so we don't try to kill it again.
555a7455442SGreg Banks 		 */
556a7455442SGreg Banks 		rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all);
557a7455442SGreg Banks 		list_del_init(&rqstp->rq_all);
558a7455442SGreg Banks 		task = rqstp->rq_task;
559a7455442SGreg Banks     	}
560a7455442SGreg Banks 	spin_unlock_bh(&pool->sp_lock);
561a7455442SGreg Banks 
562a7455442SGreg Banks 	return task;
563a7455442SGreg Banks }
564a7455442SGreg Banks 
565a7455442SGreg Banks /*
566a7455442SGreg Banks  * Create or destroy enough new threads to make the number
567a7455442SGreg Banks  * of threads the given number.  If `pool' is non-NULL, applies
568a7455442SGreg Banks  * only to threads in that pool, otherwise round-robins between
569a7455442SGreg Banks  * all pools.  Must be called with a svc_get() reference and
570a7455442SGreg Banks  * the BKL held.
571a7455442SGreg Banks  *
572a7455442SGreg Banks  * Destroying threads relies on the service threads filling in
573a7455442SGreg Banks  * rqstp->rq_task, which only the nfs ones do.  Assumes the serv
574a7455442SGreg Banks  * has been created using svc_create_pooled().
575a7455442SGreg Banks  *
576a7455442SGreg Banks  * Based on code that used to be in nfsd_svc() but tweaked
577a7455442SGreg Banks  * to be pool-aware.
578a7455442SGreg Banks  */
579a7455442SGreg Banks int
580a7455442SGreg Banks svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
581a7455442SGreg Banks {
582a7455442SGreg Banks 	struct task_struct *victim;
583a7455442SGreg Banks 	int error = 0;
584a7455442SGreg Banks 	unsigned int state = serv->sv_nrthreads-1;
585a7455442SGreg Banks 
586a7455442SGreg Banks 	if (pool == NULL) {
587a7455442SGreg Banks 		/* The -1 assumes caller has done a svc_get() */
588a7455442SGreg Banks 		nrservs -= (serv->sv_nrthreads-1);
589a7455442SGreg Banks 	} else {
590a7455442SGreg Banks 		spin_lock_bh(&pool->sp_lock);
591a7455442SGreg Banks 		nrservs -= pool->sp_nrthreads;
592a7455442SGreg Banks 		spin_unlock_bh(&pool->sp_lock);
593a7455442SGreg Banks 	}
594a7455442SGreg Banks 
595a7455442SGreg Banks 	/* create new threads */
596a7455442SGreg Banks 	while (nrservs > 0) {
597a7455442SGreg Banks 		nrservs--;
598a7455442SGreg Banks 		__module_get(serv->sv_module);
599a7455442SGreg Banks 		error = __svc_create_thread(serv->sv_function, serv,
600a7455442SGreg Banks 					    choose_pool(serv, pool, &state));
601a7455442SGreg Banks 		if (error < 0) {
602a7455442SGreg Banks 			module_put(serv->sv_module);
603a7455442SGreg Banks 			break;
604a7455442SGreg Banks 		}
605a7455442SGreg Banks 	}
606a7455442SGreg Banks 	/* destroy old threads */
607a7455442SGreg Banks 	while (nrservs < 0 &&
608a7455442SGreg Banks 	       (victim = choose_victim(serv, pool, &state)) != NULL) {
609a7455442SGreg Banks 		send_sig(serv->sv_kill_signal, victim, 1);
610a7455442SGreg Banks 		nrservs++;
611a7455442SGreg Banks 	}
612a7455442SGreg Banks 
613a7455442SGreg Banks 	return error;
614a7455442SGreg Banks }
615a7455442SGreg Banks 
616a7455442SGreg Banks /*
6173262c816SGreg Banks  * Called from a server thread as it's exiting.  Caller must hold BKL.
6181da177e4SLinus Torvalds  */
6191da177e4SLinus Torvalds void
6201da177e4SLinus Torvalds svc_exit_thread(struct svc_rqst *rqstp)
6211da177e4SLinus Torvalds {
6221da177e4SLinus Torvalds 	struct svc_serv	*serv = rqstp->rq_server;
6233262c816SGreg Banks 	struct svc_pool	*pool = rqstp->rq_pool;
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 	svc_release_buffer(rqstp);
6261da177e4SLinus Torvalds 	kfree(rqstp->rq_resp);
6271da177e4SLinus Torvalds 	kfree(rqstp->rq_argp);
6281da177e4SLinus Torvalds 	kfree(rqstp->rq_auth_data);
6293262c816SGreg Banks 
6303262c816SGreg Banks 	spin_lock_bh(&pool->sp_lock);
6313262c816SGreg Banks 	pool->sp_nrthreads--;
632a7455442SGreg Banks 	list_del(&rqstp->rq_all);
6333262c816SGreg Banks 	spin_unlock_bh(&pool->sp_lock);
6343262c816SGreg Banks 
6351da177e4SLinus Torvalds 	kfree(rqstp);
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	/* Release the server */
6381da177e4SLinus Torvalds 	if (serv)
6391da177e4SLinus Torvalds 		svc_destroy(serv);
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds /*
6431da177e4SLinus Torvalds  * Register an RPC service with the local portmapper.
6441da177e4SLinus Torvalds  * To unregister a service, call this routine with
6451da177e4SLinus Torvalds  * proto and port == 0.
6461da177e4SLinus Torvalds  */
6471da177e4SLinus Torvalds int
6481da177e4SLinus Torvalds svc_register(struct svc_serv *serv, int proto, unsigned short port)
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	struct svc_program	*progp;
6511da177e4SLinus Torvalds 	unsigned long		flags;
6521da177e4SLinus Torvalds 	int			i, error = 0, dummy;
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 	progp = serv->sv_program;
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 	dprintk("RPC: svc_register(%s, %s, %d)\n",
6571da177e4SLinus Torvalds 		progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port);
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	if (!port)
6601da177e4SLinus Torvalds 		clear_thread_flag(TIF_SIGPENDING);
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	for (i = 0; i < progp->pg_nvers; i++) {
6631da177e4SLinus Torvalds 		if (progp->pg_vers[i] == NULL)
6641da177e4SLinus Torvalds 			continue;
6651da177e4SLinus Torvalds 		error = rpc_register(progp->pg_prog, i, proto, port, &dummy);
6661da177e4SLinus Torvalds 		if (error < 0)
6671da177e4SLinus Torvalds 			break;
6681da177e4SLinus Torvalds 		if (port && !dummy) {
6691da177e4SLinus Torvalds 			error = -EACCES;
6701da177e4SLinus Torvalds 			break;
6711da177e4SLinus Torvalds 		}
6721da177e4SLinus Torvalds 	}
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 	if (!port) {
6751da177e4SLinus Torvalds 		spin_lock_irqsave(&current->sighand->siglock, flags);
6761da177e4SLinus Torvalds 		recalc_sigpending();
6771da177e4SLinus Torvalds 		spin_unlock_irqrestore(&current->sighand->siglock, flags);
6781da177e4SLinus Torvalds 	}
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 	return error;
6811da177e4SLinus Torvalds }
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds /*
6841da177e4SLinus Torvalds  * Process the RPC request.
6851da177e4SLinus Torvalds  */
6861da177e4SLinus Torvalds int
6876fb2b47fSNeilBrown svc_process(struct svc_rqst *rqstp)
6881da177e4SLinus Torvalds {
6891da177e4SLinus Torvalds 	struct svc_program	*progp;
6901da177e4SLinus Torvalds 	struct svc_version	*versp = NULL;	/* compiler food */
6911da177e4SLinus Torvalds 	struct svc_procedure	*procp = NULL;
6921da177e4SLinus Torvalds 	struct kvec *		argv = &rqstp->rq_arg.head[0];
6931da177e4SLinus Torvalds 	struct kvec *		resv = &rqstp->rq_res.head[0];
6946fb2b47fSNeilBrown 	struct svc_serv		*serv = rqstp->rq_server;
6951da177e4SLinus Torvalds 	kxdrproc_t		xdr;
696d8ed029dSAlexey Dobriyan 	__be32			*statp;
697d8ed029dSAlexey Dobriyan 	u32			dir, prog, vers, proc;
698d8ed029dSAlexey Dobriyan 	__be32			auth_stat, rpc_stat;
6991da177e4SLinus Torvalds 	int			auth_res;
700d8ed029dSAlexey Dobriyan 	__be32			*accept_statp;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	rpc_stat = rpc_success;
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	if (argv->iov_len < 6*4)
7051da177e4SLinus Torvalds 		goto err_short_len;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	/* setup response xdr_buf.
7081da177e4SLinus Torvalds 	 * Initially it has just one page
7091da177e4SLinus Torvalds 	 */
7101da177e4SLinus Torvalds 	svc_take_page(rqstp); /* must succeed */
7111da177e4SLinus Torvalds 	resv->iov_base = page_address(rqstp->rq_respages[0]);
7121da177e4SLinus Torvalds 	resv->iov_len = 0;
7131da177e4SLinus Torvalds 	rqstp->rq_res.pages = rqstp->rq_respages+1;
7141da177e4SLinus Torvalds 	rqstp->rq_res.len = 0;
7151da177e4SLinus Torvalds 	rqstp->rq_res.page_base = 0;
7161da177e4SLinus Torvalds 	rqstp->rq_res.page_len = 0;
717334ccfd5STrond Myklebust 	rqstp->rq_res.buflen = PAGE_SIZE;
7187c9fdcfbSJ. Bruce Fields 	rqstp->rq_res.tail[0].iov_base = NULL;
7191da177e4SLinus Torvalds 	rqstp->rq_res.tail[0].iov_len = 0;
7205c04c46aSJ. Bruce Fields 	/* Will be turned off only in gss privacy case: */
7215c04c46aSJ. Bruce Fields 	rqstp->rq_sendfile_ok = 1;
7221da177e4SLinus Torvalds 	/* tcp needs a space for the record length... */
7231da177e4SLinus Torvalds 	if (rqstp->rq_prot == IPPROTO_TCP)
72476994313SAlexey Dobriyan 		svc_putnl(resv, 0);
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	rqstp->rq_xid = svc_getu32(argv);
7271da177e4SLinus Torvalds 	svc_putu32(resv, rqstp->rq_xid);
7281da177e4SLinus Torvalds 
72976994313SAlexey Dobriyan 	dir  = svc_getnl(argv);
73076994313SAlexey Dobriyan 	vers = svc_getnl(argv);
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds 	/* First words of reply: */
73376994313SAlexey Dobriyan 	svc_putnl(resv, 1);		/* REPLY */
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 	if (dir != 0)		/* direction != CALL */
7361da177e4SLinus Torvalds 		goto err_bad_dir;
7371da177e4SLinus Torvalds 	if (vers != 2)		/* RPC version number */
7381da177e4SLinus Torvalds 		goto err_bad_rpc;
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 	/* Save position in case we later decide to reject: */
7411da177e4SLinus Torvalds 	accept_statp = resv->iov_base + resv->iov_len;
7421da177e4SLinus Torvalds 
74376994313SAlexey Dobriyan 	svc_putnl(resv, 0);		/* ACCEPT */
7441da177e4SLinus Torvalds 
74576994313SAlexey Dobriyan 	rqstp->rq_prog = prog = svc_getnl(argv);	/* program number */
74676994313SAlexey Dobriyan 	rqstp->rq_vers = vers = svc_getnl(argv);	/* version number */
74776994313SAlexey Dobriyan 	rqstp->rq_proc = proc = svc_getnl(argv);	/* procedure number */
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 	progp = serv->sv_program;
75080d188a6SNeilBrown 
75180d188a6SNeilBrown 	for (progp = serv->sv_program; progp; progp = progp->pg_next)
75280d188a6SNeilBrown 		if (prog == progp->pg_prog)
75380d188a6SNeilBrown 			break;
75480d188a6SNeilBrown 
7551da177e4SLinus Torvalds 	/*
7561da177e4SLinus Torvalds 	 * Decode auth data, and add verifier to reply buffer.
7571da177e4SLinus Torvalds 	 * We do this before anything else in order to get a decent
7581da177e4SLinus Torvalds 	 * auth verifier.
7591da177e4SLinus Torvalds 	 */
7601da177e4SLinus Torvalds 	auth_res = svc_authenticate(rqstp, &auth_stat);
7611da177e4SLinus Torvalds 	/* Also give the program a chance to reject this call: */
76280d188a6SNeilBrown 	if (auth_res == SVC_OK && progp) {
7631da177e4SLinus Torvalds 		auth_stat = rpc_autherr_badcred;
7641da177e4SLinus Torvalds 		auth_res = progp->pg_authenticate(rqstp);
7651da177e4SLinus Torvalds 	}
7661da177e4SLinus Torvalds 	switch (auth_res) {
7671da177e4SLinus Torvalds 	case SVC_OK:
7681da177e4SLinus Torvalds 		break;
7691da177e4SLinus Torvalds 	case SVC_GARBAGE:
7701da177e4SLinus Torvalds 		rpc_stat = rpc_garbage_args;
7711da177e4SLinus Torvalds 		goto err_bad;
7721da177e4SLinus Torvalds 	case SVC_SYSERR:
7731da177e4SLinus Torvalds 		rpc_stat = rpc_system_err;
7741da177e4SLinus Torvalds 		goto err_bad;
7751da177e4SLinus Torvalds 	case SVC_DENIED:
7761da177e4SLinus Torvalds 		goto err_bad_auth;
7771da177e4SLinus Torvalds 	case SVC_DROP:
7781da177e4SLinus Torvalds 		goto dropit;
7791da177e4SLinus Torvalds 	case SVC_COMPLETE:
7801da177e4SLinus Torvalds 		goto sendit;
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 
7839ba02638SAndreas Gruenbacher 	if (progp == NULL)
7841da177e4SLinus Torvalds 		goto err_bad_prog;
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds 	if (vers >= progp->pg_nvers ||
7871da177e4SLinus Torvalds 	  !(versp = progp->pg_vers[vers]))
7881da177e4SLinus Torvalds 		goto err_bad_vers;
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 	procp = versp->vs_proc + proc;
7911da177e4SLinus Torvalds 	if (proc >= versp->vs_nproc || !procp->pc_func)
7921da177e4SLinus Torvalds 		goto err_bad_proc;
7931da177e4SLinus Torvalds 	rqstp->rq_server   = serv;
7941da177e4SLinus Torvalds 	rqstp->rq_procinfo = procp;
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 	/* Syntactic check complete */
7971da177e4SLinus Torvalds 	serv->sv_stats->rpccnt++;
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds 	/* Build the reply header. */
8001da177e4SLinus Torvalds 	statp = resv->iov_base +resv->iov_len;
80176994313SAlexey Dobriyan 	svc_putnl(resv, RPC_SUCCESS);
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds 	/* Bump per-procedure stats counter */
8041da177e4SLinus Torvalds 	procp->pc_count++;
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds 	/* Initialize storage for argp and resp */
8071da177e4SLinus Torvalds 	memset(rqstp->rq_argp, 0, procp->pc_argsize);
8081da177e4SLinus Torvalds 	memset(rqstp->rq_resp, 0, procp->pc_ressize);
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 	/* un-reserve some of the out-queue now that we have a
8111da177e4SLinus Torvalds 	 * better idea of reply size
8121da177e4SLinus Torvalds 	 */
8131da177e4SLinus Torvalds 	if (procp->pc_xdrressize)
8141da177e4SLinus Torvalds 		svc_reserve(rqstp, procp->pc_xdrressize<<2);
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds 	/* Call the function that processes the request. */
8171da177e4SLinus Torvalds 	if (!versp->vs_dispatch) {
8181da177e4SLinus Torvalds 		/* Decode arguments */
8191da177e4SLinus Torvalds 		xdr = procp->pc_decode;
8201da177e4SLinus Torvalds 		if (xdr && !xdr(rqstp, argv->iov_base, rqstp->rq_argp))
8211da177e4SLinus Torvalds 			goto err_garbage;
8221da177e4SLinus Torvalds 
8231da177e4SLinus Torvalds 		*statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 		/* Encode reply */
8261da177e4SLinus Torvalds 		if (*statp == rpc_success && (xdr = procp->pc_encode)
8271da177e4SLinus Torvalds 		 && !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) {
8281da177e4SLinus Torvalds 			dprintk("svc: failed to encode reply\n");
8291da177e4SLinus Torvalds 			/* serv->sv_stats->rpcsystemerr++; */
8301da177e4SLinus Torvalds 			*statp = rpc_system_err;
8311da177e4SLinus Torvalds 		}
8321da177e4SLinus Torvalds 	} else {
8331da177e4SLinus Torvalds 		dprintk("svc: calling dispatcher\n");
8341da177e4SLinus Torvalds 		if (!versp->vs_dispatch(rqstp, statp)) {
8351da177e4SLinus Torvalds 			/* Release reply info */
8361da177e4SLinus Torvalds 			if (procp->pc_release)
8371da177e4SLinus Torvalds 				procp->pc_release(rqstp, NULL, rqstp->rq_resp);
8381da177e4SLinus Torvalds 			goto dropit;
8391da177e4SLinus Torvalds 		}
8401da177e4SLinus Torvalds 	}
8411da177e4SLinus Torvalds 
8421da177e4SLinus Torvalds 	/* Check RPC status result */
8431da177e4SLinus Torvalds 	if (*statp != rpc_success)
8441da177e4SLinus Torvalds 		resv->iov_len = ((void*)statp)  - resv->iov_base + 4;
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds 	/* Release reply info */
8471da177e4SLinus Torvalds 	if (procp->pc_release)
8481da177e4SLinus Torvalds 		procp->pc_release(rqstp, NULL, rqstp->rq_resp);
8491da177e4SLinus Torvalds 
8501da177e4SLinus Torvalds 	if (procp->pc_encode == NULL)
8511da177e4SLinus Torvalds 		goto dropit;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds  sendit:
8541da177e4SLinus Torvalds 	if (svc_authorise(rqstp))
8551da177e4SLinus Torvalds 		goto dropit;
8561da177e4SLinus Torvalds 	return svc_send(rqstp);
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds  dropit:
8591da177e4SLinus Torvalds 	svc_authorise(rqstp);	/* doesn't hurt to call this twice */
8601da177e4SLinus Torvalds 	dprintk("svc: svc_process dropit\n");
8611da177e4SLinus Torvalds 	svc_drop(rqstp);
8621da177e4SLinus Torvalds 	return 0;
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds err_short_len:
8651da177e4SLinus Torvalds #ifdef RPC_PARANOIA
8661da177e4SLinus Torvalds 	printk("svc: short len %Zd, dropping request\n", argv->iov_len);
8671da177e4SLinus Torvalds #endif
8681da177e4SLinus Torvalds 	goto dropit;			/* drop request */
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds err_bad_dir:
8711da177e4SLinus Torvalds #ifdef RPC_PARANOIA
8721da177e4SLinus Torvalds 	printk("svc: bad direction %d, dropping request\n", dir);
8731da177e4SLinus Torvalds #endif
8741da177e4SLinus Torvalds 	serv->sv_stats->rpcbadfmt++;
8751da177e4SLinus Torvalds 	goto dropit;			/* drop request */
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds err_bad_rpc:
8781da177e4SLinus Torvalds 	serv->sv_stats->rpcbadfmt++;
87976994313SAlexey Dobriyan 	svc_putnl(resv, 1);	/* REJECT */
88076994313SAlexey Dobriyan 	svc_putnl(resv, 0);	/* RPC_MISMATCH */
88176994313SAlexey Dobriyan 	svc_putnl(resv, 2);	/* Only RPCv2 supported */
88276994313SAlexey Dobriyan 	svc_putnl(resv, 2);
8831da177e4SLinus Torvalds 	goto sendit;
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds err_bad_auth:
8861da177e4SLinus Torvalds 	dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat));
8871da177e4SLinus Torvalds 	serv->sv_stats->rpcbadauth++;
8881da177e4SLinus Torvalds 	/* Restore write pointer to location of accept status: */
8891da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, accept_statp);
89076994313SAlexey Dobriyan 	svc_putnl(resv, 1);	/* REJECT */
89176994313SAlexey Dobriyan 	svc_putnl(resv, 1);	/* AUTH_ERROR */
89276994313SAlexey Dobriyan 	svc_putnl(resv, ntohl(auth_stat));	/* status */
8931da177e4SLinus Torvalds 	goto sendit;
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds err_bad_prog:
8969ba02638SAndreas Gruenbacher 	dprintk("svc: unknown program %d\n", prog);
8971da177e4SLinus Torvalds 	serv->sv_stats->rpcbadfmt++;
89876994313SAlexey Dobriyan 	svc_putnl(resv, RPC_PROG_UNAVAIL);
8991da177e4SLinus Torvalds 	goto sendit;
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds err_bad_vers:
9021da177e4SLinus Torvalds #ifdef RPC_PARANOIA
9031da177e4SLinus Torvalds 	printk("svc: unknown version (%d)\n", vers);
9041da177e4SLinus Torvalds #endif
9051da177e4SLinus Torvalds 	serv->sv_stats->rpcbadfmt++;
90676994313SAlexey Dobriyan 	svc_putnl(resv, RPC_PROG_MISMATCH);
90776994313SAlexey Dobriyan 	svc_putnl(resv, progp->pg_lovers);
90876994313SAlexey Dobriyan 	svc_putnl(resv, progp->pg_hivers);
9091da177e4SLinus Torvalds 	goto sendit;
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds err_bad_proc:
9121da177e4SLinus Torvalds #ifdef RPC_PARANOIA
9131da177e4SLinus Torvalds 	printk("svc: unknown procedure (%d)\n", proc);
9141da177e4SLinus Torvalds #endif
9151da177e4SLinus Torvalds 	serv->sv_stats->rpcbadfmt++;
91676994313SAlexey Dobriyan 	svc_putnl(resv, RPC_PROC_UNAVAIL);
9171da177e4SLinus Torvalds 	goto sendit;
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds err_garbage:
9201da177e4SLinus Torvalds #ifdef RPC_PARANOIA
9211da177e4SLinus Torvalds 	printk("svc: failed to decode args\n");
9221da177e4SLinus Torvalds #endif
9231da177e4SLinus Torvalds 	rpc_stat = rpc_garbage_args;
9241da177e4SLinus Torvalds err_bad:
9251da177e4SLinus Torvalds 	serv->sv_stats->rpcbadfmt++;
92676994313SAlexey Dobriyan 	svc_putnl(resv, ntohl(rpc_stat));
9271da177e4SLinus Torvalds 	goto sendit;
9281da177e4SLinus Torvalds }
929