svc.c revision 248195
1177633Sdfr/*	$NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $	*/
2177633Sdfr
3177633Sdfr/*
4177633Sdfr * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5177633Sdfr * unrestricted use provided that this legend is included on all tape
6177633Sdfr * media and as a part of the software program in whole or part.  Users
7177633Sdfr * may copy or modify Sun RPC without charge, but are not authorized
8177633Sdfr * to license or distribute it to anyone else except as part of a product or
9177633Sdfr * program developed by the user.
10177633Sdfr *
11177633Sdfr * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12177633Sdfr * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13177633Sdfr * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14177633Sdfr *
15177633Sdfr * Sun RPC is provided with no support and without any obligation on the
16177633Sdfr * part of Sun Microsystems, Inc. to assist in its use, correction,
17177633Sdfr * modification or enhancement.
18177633Sdfr *
19177633Sdfr * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20177633Sdfr * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21177633Sdfr * OR ANY PART THEREOF.
22177633Sdfr *
23177633Sdfr * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24177633Sdfr * or profits or other special, indirect and consequential damages, even if
25177633Sdfr * Sun has been advised of the possibility of such damages.
26177633Sdfr *
27177633Sdfr * Sun Microsystems, Inc.
28177633Sdfr * 2550 Garcia Avenue
29177633Sdfr * Mountain View, California  94043
30177633Sdfr */
31177633Sdfr
32177633Sdfr#if defined(LIBC_SCCS) && !defined(lint)
33177633Sdfrstatic char *sccsid2 = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";
34177633Sdfrstatic char *sccsid = "@(#)svc.c	2.4 88/08/11 4.0 RPCSRC";
35177633Sdfr#endif
36177633Sdfr#include <sys/cdefs.h>
37177633Sdfr__FBSDID("$FreeBSD: head/sys/rpc/svc.c 248195 2013-03-12 12:17:19Z glebius $");
38177633Sdfr
39177633Sdfr/*
40177633Sdfr * svc.c, Server-side remote procedure call interface.
41177633Sdfr *
42177633Sdfr * There are two sets of procedures here.  The xprt routines are
43177633Sdfr * for handling transport handles.  The svc routines handle the
44177633Sdfr * list of service routines.
45177633Sdfr *
46177633Sdfr * Copyright (C) 1984, Sun Microsystems, Inc.
47177633Sdfr */
48177633Sdfr
49177633Sdfr#include <sys/param.h>
50177633Sdfr#include <sys/lock.h>
51177633Sdfr#include <sys/kernel.h>
52184588Sdfr#include <sys/kthread.h>
53177633Sdfr#include <sys/malloc.h>
54184588Sdfr#include <sys/mbuf.h>
55177633Sdfr#include <sys/mutex.h>
56184588Sdfr#include <sys/proc.h>
57177633Sdfr#include <sys/queue.h>
58184588Sdfr#include <sys/socketvar.h>
59177633Sdfr#include <sys/systm.h>
60177633Sdfr#include <sys/ucred.h>
61177633Sdfr
62177633Sdfr#include <rpc/rpc.h>
63177633Sdfr#include <rpc/rpcb_clnt.h>
64184588Sdfr#include <rpc/replay.h>
65177633Sdfr
66177685Sdfr#include <rpc/rpc_com.h>
67177633Sdfr
68177633Sdfr#define SVC_VERSQUIET 0x0001		/* keep quiet about vers mismatch */
69184588Sdfr#define version_keepquiet(xp) (SVC_EXT(xp)->xp_flags & SVC_VERSQUIET)
70177633Sdfr
71177633Sdfrstatic struct svc_callout *svc_find(SVCPOOL *pool, rpcprog_t, rpcvers_t,
72177633Sdfr    char *);
73184588Sdfrstatic void svc_new_thread(SVCPOOL *pool);
74184588Sdfrstatic void xprt_unregister_locked(SVCXPRT *xprt);
75177633Sdfr
76177633Sdfr/* ***************  SVCXPRT related stuff **************** */
77177633Sdfr
78184588Sdfrstatic int svcpool_minthread_sysctl(SYSCTL_HANDLER_ARGS);
79184588Sdfrstatic int svcpool_maxthread_sysctl(SYSCTL_HANDLER_ARGS);
80184588Sdfr
81177633SdfrSVCPOOL*
82184588Sdfrsvcpool_create(const char *name, struct sysctl_oid_list *sysctl_base)
83177633Sdfr{
84177633Sdfr	SVCPOOL *pool;
85177633Sdfr
86177633Sdfr	pool = malloc(sizeof(SVCPOOL), M_RPC, M_WAITOK|M_ZERO);
87177633Sdfr
88177633Sdfr	mtx_init(&pool->sp_lock, "sp_lock", NULL, MTX_DEF);
89184588Sdfr	pool->sp_name = name;
90184588Sdfr	pool->sp_state = SVCPOOL_INIT;
91184588Sdfr	pool->sp_proc = NULL;
92177633Sdfr	TAILQ_INIT(&pool->sp_xlist);
93177633Sdfr	TAILQ_INIT(&pool->sp_active);
94177633Sdfr	TAILQ_INIT(&pool->sp_callouts);
95184588Sdfr	LIST_INIT(&pool->sp_threads);
96184588Sdfr	LIST_INIT(&pool->sp_idlethreads);
97184588Sdfr	pool->sp_minthreads = 1;
98184588Sdfr	pool->sp_maxthreads = 1;
99184588Sdfr	pool->sp_threadcount = 0;
100177633Sdfr
101184588Sdfr	/*
102184588Sdfr	 * Don't use more than a quarter of mbuf clusters or more than
103184588Sdfr	 * 45Mb buffering requests.
104184588Sdfr	 */
105184588Sdfr	pool->sp_space_high = nmbclusters * MCLBYTES / 4;
106184588Sdfr	if (pool->sp_space_high > 45 << 20)
107184588Sdfr		pool->sp_space_high = 45 << 20;
108184588Sdfr	pool->sp_space_low = 2 * pool->sp_space_high / 3;
109184588Sdfr
110184588Sdfr	sysctl_ctx_init(&pool->sp_sysctl);
111184588Sdfr	if (sysctl_base) {
112184588Sdfr		SYSCTL_ADD_PROC(&pool->sp_sysctl, sysctl_base, OID_AUTO,
113184588Sdfr		    "minthreads", CTLTYPE_INT | CTLFLAG_RW,
114184588Sdfr		    pool, 0, svcpool_minthread_sysctl, "I", "");
115184588Sdfr		SYSCTL_ADD_PROC(&pool->sp_sysctl, sysctl_base, OID_AUTO,
116184588Sdfr		    "maxthreads", CTLTYPE_INT | CTLFLAG_RW,
117184588Sdfr		    pool, 0, svcpool_maxthread_sysctl, "I", "");
118184588Sdfr		SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
119184588Sdfr		    "threads", CTLFLAG_RD, &pool->sp_threadcount, 0, "");
120184588Sdfr
121184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
122184588Sdfr		    "request_space_used", CTLFLAG_RD,
123184588Sdfr		    &pool->sp_space_used, 0,
124184588Sdfr		    "Space in parsed but not handled requests.");
125184588Sdfr
126184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
127184588Sdfr		    "request_space_used_highest", CTLFLAG_RD,
128184588Sdfr		    &pool->sp_space_used_highest, 0,
129184588Sdfr		    "Highest space used since reboot.");
130184588Sdfr
131184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
132184588Sdfr		    "request_space_high", CTLFLAG_RW,
133184588Sdfr		    &pool->sp_space_high, 0,
134184588Sdfr		    "Maximum space in parsed but not handled requests.");
135184588Sdfr
136184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
137184588Sdfr		    "request_space_low", CTLFLAG_RW,
138184588Sdfr		    &pool->sp_space_low, 0,
139184588Sdfr		    "Low water mark for request space.");
140184588Sdfr
141217326Smdf		SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
142184588Sdfr		    "request_space_throttled", CTLFLAG_RD,
143184588Sdfr		    &pool->sp_space_throttled, 0,
144184588Sdfr		    "Whether nfs requests are currently throttled");
145184588Sdfr
146217326Smdf		SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
147184588Sdfr		    "request_space_throttle_count", CTLFLAG_RD,
148184588Sdfr		    &pool->sp_space_throttle_count, 0,
149184588Sdfr		    "Count of times throttling based on request space has occurred");
150184588Sdfr	}
151184588Sdfr
152177633Sdfr	return pool;
153177633Sdfr}
154177633Sdfr
155177633Sdfrvoid
156177633Sdfrsvcpool_destroy(SVCPOOL *pool)
157177633Sdfr{
158184588Sdfr	SVCXPRT *xprt, *nxprt;
159177633Sdfr	struct svc_callout *s;
160184588Sdfr	struct svcxprt_list cleanup;
161177633Sdfr
162184588Sdfr	TAILQ_INIT(&cleanup);
163177633Sdfr	mtx_lock(&pool->sp_lock);
164177633Sdfr
165177633Sdfr	while (TAILQ_FIRST(&pool->sp_xlist)) {
166177633Sdfr		xprt = TAILQ_FIRST(&pool->sp_xlist);
167184588Sdfr		xprt_unregister_locked(xprt);
168184588Sdfr		TAILQ_INSERT_TAIL(&cleanup, xprt, xp_link);
169177633Sdfr	}
170177633Sdfr
171177633Sdfr	while (TAILQ_FIRST(&pool->sp_callouts)) {
172177633Sdfr		s = TAILQ_FIRST(&pool->sp_callouts);
173177633Sdfr		mtx_unlock(&pool->sp_lock);
174177633Sdfr		svc_unreg(pool, s->sc_prog, s->sc_vers);
175177633Sdfr		mtx_lock(&pool->sp_lock);
176177633Sdfr	}
177193603Srmacklem	mtx_unlock(&pool->sp_lock);
178177633Sdfr
179184588Sdfr	TAILQ_FOREACH_SAFE(xprt, &cleanup, xp_link, nxprt) {
180184588Sdfr		SVC_RELEASE(xprt);
181184588Sdfr	}
182184588Sdfr
183193436Srmacklem	mtx_destroy(&pool->sp_lock);
184193436Srmacklem
185184588Sdfr	if (pool->sp_rcache)
186184588Sdfr		replay_freecache(pool->sp_rcache);
187184588Sdfr
188184588Sdfr	sysctl_ctx_free(&pool->sp_sysctl);
189177633Sdfr	free(pool, M_RPC);
190177633Sdfr}
191177633Sdfr
192184588Sdfrstatic bool_t
193184588Sdfrsvcpool_active(SVCPOOL *pool)
194184588Sdfr{
195184588Sdfr	enum svcpool_state state = pool->sp_state;
196184588Sdfr
197184588Sdfr	if (state == SVCPOOL_INIT || state == SVCPOOL_CLOSING)
198184588Sdfr		return (FALSE);
199184588Sdfr	return (TRUE);
200184588Sdfr}
201184588Sdfr
202177633Sdfr/*
203184588Sdfr * Sysctl handler to set the minimum thread count on a pool
204184588Sdfr */
205184588Sdfrstatic int
206184588Sdfrsvcpool_minthread_sysctl(SYSCTL_HANDLER_ARGS)
207184588Sdfr{
208184588Sdfr	SVCPOOL *pool;
209184588Sdfr	int newminthreads, error, n;
210184588Sdfr
211184588Sdfr	pool = oidp->oid_arg1;
212184588Sdfr	newminthreads = pool->sp_minthreads;
213184588Sdfr	error = sysctl_handle_int(oidp, &newminthreads, 0, req);
214184588Sdfr	if (error == 0 && newminthreads != pool->sp_minthreads) {
215184588Sdfr		if (newminthreads > pool->sp_maxthreads)
216184588Sdfr			return (EINVAL);
217184588Sdfr		mtx_lock(&pool->sp_lock);
218184588Sdfr		if (newminthreads > pool->sp_minthreads
219184588Sdfr		    && svcpool_active(pool)) {
220184588Sdfr			/*
221184588Sdfr			 * If the pool is running and we are
222184588Sdfr			 * increasing, create some more threads now.
223184588Sdfr			 */
224184588Sdfr			n = newminthreads - pool->sp_threadcount;
225184588Sdfr			if (n > 0) {
226184588Sdfr				mtx_unlock(&pool->sp_lock);
227184588Sdfr				while (n--)
228184588Sdfr					svc_new_thread(pool);
229184588Sdfr				mtx_lock(&pool->sp_lock);
230184588Sdfr			}
231184588Sdfr		}
232184588Sdfr		pool->sp_minthreads = newminthreads;
233184588Sdfr		mtx_unlock(&pool->sp_lock);
234184588Sdfr	}
235184588Sdfr	return (error);
236184588Sdfr}
237184588Sdfr
238184588Sdfr/*
239184588Sdfr * Sysctl handler to set the maximum thread count on a pool
240184588Sdfr */
241184588Sdfrstatic int
242184588Sdfrsvcpool_maxthread_sysctl(SYSCTL_HANDLER_ARGS)
243184588Sdfr{
244184588Sdfr	SVCPOOL *pool;
245184588Sdfr	SVCTHREAD *st;
246184588Sdfr	int newmaxthreads, error;
247184588Sdfr
248184588Sdfr	pool = oidp->oid_arg1;
249184588Sdfr	newmaxthreads = pool->sp_maxthreads;
250184588Sdfr	error = sysctl_handle_int(oidp, &newmaxthreads, 0, req);
251184588Sdfr	if (error == 0 && newmaxthreads != pool->sp_maxthreads) {
252184588Sdfr		if (newmaxthreads < pool->sp_minthreads)
253184588Sdfr			return (EINVAL);
254184588Sdfr		mtx_lock(&pool->sp_lock);
255184588Sdfr		if (newmaxthreads < pool->sp_maxthreads
256184588Sdfr		    && svcpool_active(pool)) {
257184588Sdfr			/*
258184588Sdfr			 * If the pool is running and we are
259184588Sdfr			 * decreasing, wake up some idle threads to
260184588Sdfr			 * encourage them to exit.
261184588Sdfr			 */
262184588Sdfr			LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink)
263184588Sdfr				cv_signal(&st->st_cond);
264184588Sdfr		}
265184588Sdfr		pool->sp_maxthreads = newmaxthreads;
266184588Sdfr		mtx_unlock(&pool->sp_lock);
267184588Sdfr	}
268184588Sdfr	return (error);
269184588Sdfr}
270184588Sdfr
271184588Sdfr/*
272177633Sdfr * Activate a transport handle.
273177633Sdfr */
274177633Sdfrvoid
275177633Sdfrxprt_register(SVCXPRT *xprt)
276177633Sdfr{
277177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
278177633Sdfr
279194407Srmacklem	SVC_ACQUIRE(xprt);
280177633Sdfr	mtx_lock(&pool->sp_lock);
281177633Sdfr	xprt->xp_registered = TRUE;
282177633Sdfr	xprt->xp_active = FALSE;
283177633Sdfr	TAILQ_INSERT_TAIL(&pool->sp_xlist, xprt, xp_link);
284177633Sdfr	mtx_unlock(&pool->sp_lock);
285177633Sdfr}
286177633Sdfr
287177633Sdfr/*
288184588Sdfr * De-activate a transport handle. Note: the locked version doesn't
289184588Sdfr * release the transport - caller must do that after dropping the pool
290184588Sdfr * lock.
291177633Sdfr */
292177633Sdfrstatic void
293184588Sdfrxprt_unregister_locked(SVCXPRT *xprt)
294177633Sdfr{
295177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
296177633Sdfr
297193649Srmacklem	KASSERT(xprt->xp_registered == TRUE,
298193649Srmacklem	    ("xprt_unregister_locked: not registered"));
299177633Sdfr	if (xprt->xp_active) {
300177633Sdfr		TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
301177633Sdfr		xprt->xp_active = FALSE;
302177633Sdfr	}
303177633Sdfr	TAILQ_REMOVE(&pool->sp_xlist, xprt, xp_link);
304177633Sdfr	xprt->xp_registered = FALSE;
305184588Sdfr}
306177633Sdfr
307184588Sdfrvoid
308184588Sdfrxprt_unregister(SVCXPRT *xprt)
309184588Sdfr{
310184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
311184588Sdfr
312184588Sdfr	mtx_lock(&pool->sp_lock);
313193649Srmacklem	if (xprt->xp_registered == FALSE) {
314193649Srmacklem		/* Already unregistered by another thread */
315193649Srmacklem		mtx_unlock(&pool->sp_lock);
316193649Srmacklem		return;
317193649Srmacklem	}
318184588Sdfr	xprt_unregister_locked(xprt);
319184588Sdfr	mtx_unlock(&pool->sp_lock);
320184588Sdfr
321184588Sdfr	SVC_RELEASE(xprt);
322177633Sdfr}
323177633Sdfr
324184588Sdfrstatic void
325184588Sdfrxprt_assignthread(SVCXPRT *xprt)
326184588Sdfr{
327184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
328184588Sdfr	SVCTHREAD *st;
329184588Sdfr
330184588Sdfr	/*
331184588Sdfr	 * Attempt to assign a service thread to this
332184588Sdfr	 * transport.
333184588Sdfr	 */
334184588Sdfr	LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink) {
335184588Sdfr		if (st->st_xprt == NULL && STAILQ_EMPTY(&st->st_reqs))
336184588Sdfr			break;
337184588Sdfr	}
338184588Sdfr	if (st) {
339184588Sdfr		SVC_ACQUIRE(xprt);
340184588Sdfr		xprt->xp_thread = st;
341184588Sdfr		st->st_xprt = xprt;
342184588Sdfr		cv_signal(&st->st_cond);
343184588Sdfr	} else {
344184588Sdfr		/*
345184588Sdfr		 * See if we can create a new thread. The
346184588Sdfr		 * actual thread creation happens in
347184588Sdfr		 * svc_run_internal because our locking state
348184588Sdfr		 * is poorly defined (we are typically called
349184588Sdfr		 * from a socket upcall). Don't create more
350184588Sdfr		 * than one thread per second.
351184588Sdfr		 */
352184588Sdfr		if (pool->sp_state == SVCPOOL_ACTIVE
353184588Sdfr		    && pool->sp_lastcreatetime < time_uptime
354184588Sdfr		    && pool->sp_threadcount < pool->sp_maxthreads) {
355184588Sdfr			pool->sp_state = SVCPOOL_THREADWANTED;
356184588Sdfr		}
357184588Sdfr	}
358184588Sdfr}
359184588Sdfr
360177633Sdfrvoid
361177633Sdfrxprt_active(SVCXPRT *xprt)
362177633Sdfr{
363177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
364177633Sdfr
365193436Srmacklem	mtx_lock(&pool->sp_lock);
366193436Srmacklem
367184588Sdfr	if (!xprt->xp_registered) {
368184588Sdfr		/*
369184588Sdfr		 * Race with xprt_unregister - we lose.
370184588Sdfr		 */
371193436Srmacklem		mtx_unlock(&pool->sp_lock);
372184588Sdfr		return;
373184588Sdfr	}
374184588Sdfr
375177633Sdfr	if (!xprt->xp_active) {
376177633Sdfr		TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink);
377177633Sdfr		xprt->xp_active = TRUE;
378184588Sdfr		xprt_assignthread(xprt);
379177633Sdfr	}
380177633Sdfr
381177633Sdfr	mtx_unlock(&pool->sp_lock);
382177633Sdfr}
383177633Sdfr
384177633Sdfrvoid
385184588Sdfrxprt_inactive_locked(SVCXPRT *xprt)
386177633Sdfr{
387177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
388177633Sdfr
389177633Sdfr	if (xprt->xp_active) {
390177633Sdfr		TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
391177633Sdfr		xprt->xp_active = FALSE;
392177633Sdfr	}
393184588Sdfr}
394177633Sdfr
395184588Sdfrvoid
396184588Sdfrxprt_inactive(SVCXPRT *xprt)
397184588Sdfr{
398184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
399184588Sdfr
400184588Sdfr	mtx_lock(&pool->sp_lock);
401184588Sdfr	xprt_inactive_locked(xprt);
402177633Sdfr	mtx_unlock(&pool->sp_lock);
403177633Sdfr}
404177633Sdfr
405177633Sdfr/*
406177633Sdfr * Add a service program to the callout list.
407177633Sdfr * The dispatch routine will be called when a rpc request for this
408177633Sdfr * program number comes in.
409177633Sdfr */
410177633Sdfrbool_t
411177633Sdfrsvc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
412177633Sdfr    void (*dispatch)(struct svc_req *, SVCXPRT *),
413177633Sdfr    const struct netconfig *nconf)
414177633Sdfr{
415177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
416177633Sdfr	struct svc_callout *s;
417177633Sdfr	char *netid = NULL;
418177633Sdfr	int flag = 0;
419177633Sdfr
420177633Sdfr/* VARIABLES PROTECTED BY svc_lock: s, svc_head */
421177633Sdfr
422177633Sdfr	if (xprt->xp_netid) {
423177633Sdfr		netid = strdup(xprt->xp_netid, M_RPC);
424177633Sdfr		flag = 1;
425177633Sdfr	} else if (nconf && nconf->nc_netid) {
426177633Sdfr		netid = strdup(nconf->nc_netid, M_RPC);
427177633Sdfr		flag = 1;
428177633Sdfr	} /* must have been created with svc_raw_create */
429177633Sdfr	if ((netid == NULL) && (flag == 1)) {
430177633Sdfr		return (FALSE);
431177633Sdfr	}
432177633Sdfr
433177633Sdfr	mtx_lock(&pool->sp_lock);
434177633Sdfr	if ((s = svc_find(pool, prog, vers, netid)) != NULL) {
435177633Sdfr		if (netid)
436177633Sdfr			free(netid, M_RPC);
437177633Sdfr		if (s->sc_dispatch == dispatch)
438177633Sdfr			goto rpcb_it; /* he is registering another xptr */
439177633Sdfr		mtx_unlock(&pool->sp_lock);
440177633Sdfr		return (FALSE);
441177633Sdfr	}
442177633Sdfr	s = malloc(sizeof (struct svc_callout), M_RPC, M_NOWAIT);
443177633Sdfr	if (s == NULL) {
444177633Sdfr		if (netid)
445177633Sdfr			free(netid, M_RPC);
446177633Sdfr		mtx_unlock(&pool->sp_lock);
447177633Sdfr		return (FALSE);
448177633Sdfr	}
449177633Sdfr
450177633Sdfr	s->sc_prog = prog;
451177633Sdfr	s->sc_vers = vers;
452177633Sdfr	s->sc_dispatch = dispatch;
453177633Sdfr	s->sc_netid = netid;
454177633Sdfr	TAILQ_INSERT_TAIL(&pool->sp_callouts, s, sc_link);
455177633Sdfr
456177633Sdfr	if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
457177633Sdfr		((SVCXPRT *) xprt)->xp_netid = strdup(netid, M_RPC);
458177633Sdfr
459177633Sdfrrpcb_it:
460177633Sdfr	mtx_unlock(&pool->sp_lock);
461177633Sdfr	/* now register the information with the local binder service */
462177633Sdfr	if (nconf) {
463177633Sdfr		bool_t dummy;
464177633Sdfr		struct netconfig tnc;
465184588Sdfr		struct netbuf nb;
466177633Sdfr		tnc = *nconf;
467184588Sdfr		nb.buf = &xprt->xp_ltaddr;
468184588Sdfr		nb.len = xprt->xp_ltaddr.ss_len;
469184588Sdfr		dummy = rpcb_set(prog, vers, &tnc, &nb);
470177633Sdfr		return (dummy);
471177633Sdfr	}
472177633Sdfr	return (TRUE);
473177633Sdfr}
474177633Sdfr
475177633Sdfr/*
476177633Sdfr * Remove a service program from the callout list.
477177633Sdfr */
478177633Sdfrvoid
479177633Sdfrsvc_unreg(SVCPOOL *pool, const rpcprog_t prog, const rpcvers_t vers)
480177633Sdfr{
481177633Sdfr	struct svc_callout *s;
482177633Sdfr
483177633Sdfr	/* unregister the information anyway */
484177633Sdfr	(void) rpcb_unset(prog, vers, NULL);
485177633Sdfr	mtx_lock(&pool->sp_lock);
486177633Sdfr	while ((s = svc_find(pool, prog, vers, NULL)) != NULL) {
487177633Sdfr		TAILQ_REMOVE(&pool->sp_callouts, s, sc_link);
488177633Sdfr		if (s->sc_netid)
489177633Sdfr			mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
490177633Sdfr		mem_free(s, sizeof (struct svc_callout));
491177633Sdfr	}
492177633Sdfr	mtx_unlock(&pool->sp_lock);
493177633Sdfr}
494177633Sdfr
495177633Sdfr/* ********************** CALLOUT list related stuff ************* */
496177633Sdfr
497177633Sdfr/*
498177633Sdfr * Search the callout list for a program number, return the callout
499177633Sdfr * struct.
500177633Sdfr */
501177633Sdfrstatic struct svc_callout *
502177633Sdfrsvc_find(SVCPOOL *pool, rpcprog_t prog, rpcvers_t vers, char *netid)
503177633Sdfr{
504177633Sdfr	struct svc_callout *s;
505177633Sdfr
506177633Sdfr	mtx_assert(&pool->sp_lock, MA_OWNED);
507177633Sdfr	TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
508177633Sdfr		if (s->sc_prog == prog && s->sc_vers == vers
509177633Sdfr		    && (netid == NULL || s->sc_netid == NULL ||
510177633Sdfr			strcmp(netid, s->sc_netid) == 0))
511177633Sdfr			break;
512177633Sdfr	}
513177633Sdfr
514177633Sdfr	return (s);
515177633Sdfr}
516177633Sdfr
517177633Sdfr/* ******************* REPLY GENERATION ROUTINES  ************ */
518177633Sdfr
519184588Sdfrstatic bool_t
520184588Sdfrsvc_sendreply_common(struct svc_req *rqstp, struct rpc_msg *rply,
521184588Sdfr    struct mbuf *body)
522184588Sdfr{
523184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
524184588Sdfr	bool_t ok;
525184588Sdfr
526184588Sdfr	if (rqstp->rq_args) {
527184588Sdfr		m_freem(rqstp->rq_args);
528184588Sdfr		rqstp->rq_args = NULL;
529184588Sdfr	}
530184588Sdfr
531184588Sdfr	if (xprt->xp_pool->sp_rcache)
532184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
533184588Sdfr		    rply, svc_getrpccaller(rqstp), body);
534184588Sdfr
535184588Sdfr	if (!SVCAUTH_WRAP(&rqstp->rq_auth, &body))
536184588Sdfr		return (FALSE);
537184588Sdfr
538184588Sdfr	ok = SVC_REPLY(xprt, rply, rqstp->rq_addr, body);
539184588Sdfr	if (rqstp->rq_addr) {
540184588Sdfr		free(rqstp->rq_addr, M_SONAME);
541184588Sdfr		rqstp->rq_addr = NULL;
542184588Sdfr	}
543184588Sdfr
544184588Sdfr	return (ok);
545184588Sdfr}
546184588Sdfr
547177633Sdfr/*
548177633Sdfr * Send a reply to an rpc request
549177633Sdfr */
550177633Sdfrbool_t
551184588Sdfrsvc_sendreply(struct svc_req *rqstp, xdrproc_t xdr_results, void * xdr_location)
552177633Sdfr{
553177633Sdfr	struct rpc_msg rply;
554184588Sdfr	struct mbuf *m;
555184588Sdfr	XDR xdrs;
556184588Sdfr	bool_t ok;
557177633Sdfr
558184588Sdfr	rply.rm_xid = rqstp->rq_xid;
559177633Sdfr	rply.rm_direction = REPLY;
560177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
561184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
562177633Sdfr	rply.acpted_rply.ar_stat = SUCCESS;
563184588Sdfr	rply.acpted_rply.ar_results.where = NULL;
564184588Sdfr	rply.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
565177633Sdfr
566248195Sglebius	m = m_getcl(M_WAITOK, MT_DATA, 0);
567184588Sdfr	xdrmbuf_create(&xdrs, m, XDR_ENCODE);
568184588Sdfr	ok = xdr_results(&xdrs, xdr_location);
569184588Sdfr	XDR_DESTROY(&xdrs);
570184588Sdfr
571184588Sdfr	if (ok) {
572184588Sdfr		return (svc_sendreply_common(rqstp, &rply, m));
573184588Sdfr	} else {
574184588Sdfr		m_freem(m);
575184588Sdfr		return (FALSE);
576184588Sdfr	}
577177633Sdfr}
578177633Sdfr
579184588Sdfrbool_t
580184588Sdfrsvc_sendreply_mbuf(struct svc_req *rqstp, struct mbuf *m)
581184588Sdfr{
582184588Sdfr	struct rpc_msg rply;
583184588Sdfr
584184588Sdfr	rply.rm_xid = rqstp->rq_xid;
585184588Sdfr	rply.rm_direction = REPLY;
586184588Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
587184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
588184588Sdfr	rply.acpted_rply.ar_stat = SUCCESS;
589184588Sdfr	rply.acpted_rply.ar_results.where = NULL;
590184588Sdfr	rply.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
591184588Sdfr
592184588Sdfr	return (svc_sendreply_common(rqstp, &rply, m));
593184588Sdfr}
594184588Sdfr
595177633Sdfr/*
596177633Sdfr * No procedure error reply
597177633Sdfr */
598177633Sdfrvoid
599184588Sdfrsvcerr_noproc(struct svc_req *rqstp)
600177633Sdfr{
601184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
602177633Sdfr	struct rpc_msg rply;
603177633Sdfr
604184588Sdfr	rply.rm_xid = rqstp->rq_xid;
605177633Sdfr	rply.rm_direction = REPLY;
606177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
607184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
608177633Sdfr	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
609177633Sdfr
610184588Sdfr	if (xprt->xp_pool->sp_rcache)
611184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
612184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
613184588Sdfr
614184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
615177633Sdfr}
616177633Sdfr
617177633Sdfr/*
618177633Sdfr * Can't decode args error reply
619177633Sdfr */
620177633Sdfrvoid
621184588Sdfrsvcerr_decode(struct svc_req *rqstp)
622177633Sdfr{
623184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
624177633Sdfr	struct rpc_msg rply;
625177633Sdfr
626184588Sdfr	rply.rm_xid = rqstp->rq_xid;
627177633Sdfr	rply.rm_direction = REPLY;
628177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
629184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
630177633Sdfr	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
631177633Sdfr
632184588Sdfr	if (xprt->xp_pool->sp_rcache)
633184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
634184588Sdfr		    &rply, (struct sockaddr *) &xprt->xp_rtaddr, NULL);
635184588Sdfr
636184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
637177633Sdfr}
638177633Sdfr
639177633Sdfr/*
640177633Sdfr * Some system error
641177633Sdfr */
642177633Sdfrvoid
643184588Sdfrsvcerr_systemerr(struct svc_req *rqstp)
644177633Sdfr{
645184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
646177633Sdfr	struct rpc_msg rply;
647177633Sdfr
648184588Sdfr	rply.rm_xid = rqstp->rq_xid;
649177633Sdfr	rply.rm_direction = REPLY;
650177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
651184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
652177633Sdfr	rply.acpted_rply.ar_stat = SYSTEM_ERR;
653177633Sdfr
654184588Sdfr	if (xprt->xp_pool->sp_rcache)
655184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
656184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
657184588Sdfr
658184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
659177633Sdfr}
660177633Sdfr
661177633Sdfr/*
662177633Sdfr * Authentication error reply
663177633Sdfr */
664177633Sdfrvoid
665184588Sdfrsvcerr_auth(struct svc_req *rqstp, enum auth_stat why)
666177633Sdfr{
667184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
668177633Sdfr	struct rpc_msg rply;
669177633Sdfr
670184588Sdfr	rply.rm_xid = rqstp->rq_xid;
671177633Sdfr	rply.rm_direction = REPLY;
672177633Sdfr	rply.rm_reply.rp_stat = MSG_DENIED;
673177633Sdfr	rply.rjcted_rply.rj_stat = AUTH_ERROR;
674177633Sdfr	rply.rjcted_rply.rj_why = why;
675177633Sdfr
676184588Sdfr	if (xprt->xp_pool->sp_rcache)
677184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
678184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
679184588Sdfr
680184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
681177633Sdfr}
682177633Sdfr
683177633Sdfr/*
684177633Sdfr * Auth too weak error reply
685177633Sdfr */
686177633Sdfrvoid
687184588Sdfrsvcerr_weakauth(struct svc_req *rqstp)
688177633Sdfr{
689177633Sdfr
690184588Sdfr	svcerr_auth(rqstp, AUTH_TOOWEAK);
691177633Sdfr}
692177633Sdfr
693177633Sdfr/*
694177633Sdfr * Program unavailable error reply
695177633Sdfr */
696177633Sdfrvoid
697184588Sdfrsvcerr_noprog(struct svc_req *rqstp)
698177633Sdfr{
699184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
700177633Sdfr	struct rpc_msg rply;
701177633Sdfr
702184588Sdfr	rply.rm_xid = rqstp->rq_xid;
703177633Sdfr	rply.rm_direction = REPLY;
704177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
705184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
706177633Sdfr	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
707177633Sdfr
708184588Sdfr	if (xprt->xp_pool->sp_rcache)
709184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
710184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
711184588Sdfr
712184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
713177633Sdfr}
714177633Sdfr
715177633Sdfr/*
716177633Sdfr * Program version mismatch error reply
717177633Sdfr */
718177633Sdfrvoid
719184588Sdfrsvcerr_progvers(struct svc_req *rqstp, rpcvers_t low_vers, rpcvers_t high_vers)
720177633Sdfr{
721184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
722177633Sdfr	struct rpc_msg rply;
723177633Sdfr
724184588Sdfr	rply.rm_xid = rqstp->rq_xid;
725177633Sdfr	rply.rm_direction = REPLY;
726177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
727184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
728177633Sdfr	rply.acpted_rply.ar_stat = PROG_MISMATCH;
729177633Sdfr	rply.acpted_rply.ar_vers.low = (uint32_t)low_vers;
730177633Sdfr	rply.acpted_rply.ar_vers.high = (uint32_t)high_vers;
731177633Sdfr
732184588Sdfr	if (xprt->xp_pool->sp_rcache)
733184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
734184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
735184588Sdfr
736184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
737177633Sdfr}
738177633Sdfr
739184588Sdfr/*
740184588Sdfr * Allocate a new server transport structure. All fields are
741184588Sdfr * initialized to zero and xp_p3 is initialized to point at an
742184588Sdfr * extension structure to hold various flags and authentication
743184588Sdfr * parameters.
744184588Sdfr */
745184588SdfrSVCXPRT *
746184588Sdfrsvc_xprt_alloc()
747184588Sdfr{
748184588Sdfr	SVCXPRT *xprt;
749184588Sdfr	SVCXPRT_EXT *ext;
750184588Sdfr
751184588Sdfr	xprt = mem_alloc(sizeof(SVCXPRT));
752184588Sdfr	memset(xprt, 0, sizeof(SVCXPRT));
753184588Sdfr	ext = mem_alloc(sizeof(SVCXPRT_EXT));
754184588Sdfr	memset(ext, 0, sizeof(SVCXPRT_EXT));
755184588Sdfr	xprt->xp_p3 = ext;
756184588Sdfr	refcount_init(&xprt->xp_refs, 1);
757184588Sdfr
758184588Sdfr	return (xprt);
759184588Sdfr}
760184588Sdfr
761184588Sdfr/*
762184588Sdfr * Free a server transport structure.
763184588Sdfr */
764184588Sdfrvoid
765184588Sdfrsvc_xprt_free(xprt)
766184588Sdfr	SVCXPRT *xprt;
767184588Sdfr{
768184588Sdfr
769184588Sdfr	mem_free(xprt->xp_p3, sizeof(SVCXPRT_EXT));
770184588Sdfr	mem_free(xprt, sizeof(SVCXPRT));
771184588Sdfr}
772184588Sdfr
773177633Sdfr/* ******************* SERVER INPUT STUFF ******************* */
774177633Sdfr
775177633Sdfr/*
776184588Sdfr * Read RPC requests from a transport and queue them to be
777184588Sdfr * executed. We handle authentication and replay cache replies here.
778184588Sdfr * Actually dispatching the RPC is deferred till svc_executereq.
779177633Sdfr */
780184588Sdfrstatic enum xprt_stat
781184588Sdfrsvc_getreq(SVCXPRT *xprt, struct svc_req **rqstp_ret)
782177633Sdfr{
783177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
784184588Sdfr	struct svc_req *r;
785177633Sdfr	struct rpc_msg msg;
786184588Sdfr	struct mbuf *args;
787177633Sdfr	enum xprt_stat stat;
788177633Sdfr
789177633Sdfr	/* now receive msgs from xprtprt (support batch calls) */
790184588Sdfr	r = malloc(sizeof(*r), M_RPC, M_WAITOK|M_ZERO);
791177633Sdfr
792184588Sdfr	msg.rm_call.cb_cred.oa_base = r->rq_credarea;
793184588Sdfr	msg.rm_call.cb_verf.oa_base = &r->rq_credarea[MAX_AUTH_BYTES];
794184588Sdfr	r->rq_clntcred = &r->rq_credarea[2*MAX_AUTH_BYTES];
795184588Sdfr	if (SVC_RECV(xprt, &msg, &r->rq_addr, &args)) {
796184588Sdfr		enum auth_stat why;
797177633Sdfr
798184588Sdfr		/*
799184588Sdfr		 * Handle replays and authenticate before queuing the
800184588Sdfr		 * request to be executed.
801184588Sdfr		 */
802184588Sdfr		SVC_ACQUIRE(xprt);
803184588Sdfr		r->rq_xprt = xprt;
804184588Sdfr		if (pool->sp_rcache) {
805184588Sdfr			struct rpc_msg repmsg;
806184588Sdfr			struct mbuf *repbody;
807184588Sdfr			enum replay_state rs;
808184588Sdfr			rs = replay_find(pool->sp_rcache, &msg,
809184588Sdfr			    svc_getrpccaller(r), &repmsg, &repbody);
810184588Sdfr			switch (rs) {
811184588Sdfr			case RS_NEW:
812184588Sdfr				break;
813184588Sdfr			case RS_DONE:
814184588Sdfr				SVC_REPLY(xprt, &repmsg, r->rq_addr,
815184588Sdfr				    repbody);
816184588Sdfr				if (r->rq_addr) {
817184588Sdfr					free(r->rq_addr, M_SONAME);
818184588Sdfr					r->rq_addr = NULL;
819184588Sdfr				}
820205562Srmacklem				m_freem(args);
821177633Sdfr				goto call_done;
822184588Sdfr
823184588Sdfr			default:
824205562Srmacklem				m_freem(args);
825184588Sdfr				goto call_done;
826177633Sdfr			}
827184588Sdfr		}
828184588Sdfr
829184588Sdfr		r->rq_xid = msg.rm_xid;
830184588Sdfr		r->rq_prog = msg.rm_call.cb_prog;
831184588Sdfr		r->rq_vers = msg.rm_call.cb_vers;
832184588Sdfr		r->rq_proc = msg.rm_call.cb_proc;
833184588Sdfr		r->rq_size = sizeof(*r) + m_length(args, NULL);
834184588Sdfr		r->rq_args = args;
835184588Sdfr		if ((why = _authenticate(r, &msg)) != AUTH_OK) {
836177633Sdfr			/*
837184588Sdfr			 * RPCSEC_GSS uses this return code
838184588Sdfr			 * for requests that form part of its
839184588Sdfr			 * context establishment protocol and
840184588Sdfr			 * should not be dispatched to the
841184588Sdfr			 * application.
842177633Sdfr			 */
843184588Sdfr			if (why != RPCSEC_GSS_NODISPATCH)
844184588Sdfr				svcerr_auth(r, why);
845184588Sdfr			goto call_done;
846177633Sdfr		}
847184588Sdfr
848184588Sdfr		if (!SVCAUTH_UNWRAP(&r->rq_auth, &r->rq_args)) {
849184588Sdfr			svcerr_decode(r);
850184588Sdfr			goto call_done;
851184588Sdfr		}
852184588Sdfr
853177633Sdfr		/*
854184588Sdfr		 * Everything checks out, return request to caller.
855177633Sdfr		 */
856184588Sdfr		*rqstp_ret = r;
857184588Sdfr		r = NULL;
858184588Sdfr	}
859177633Sdfrcall_done:
860184588Sdfr	if (r) {
861184588Sdfr		svc_freereq(r);
862184588Sdfr		r = NULL;
863184588Sdfr	}
864184588Sdfr	if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
865184588Sdfr		xprt_unregister(xprt);
866184588Sdfr	}
867184588Sdfr
868184588Sdfr	return (stat);
869184588Sdfr}
870184588Sdfr
871184588Sdfrstatic void
872184588Sdfrsvc_executereq(struct svc_req *rqstp)
873184588Sdfr{
874184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
875184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
876184588Sdfr	int prog_found;
877184588Sdfr	rpcvers_t low_vers;
878184588Sdfr	rpcvers_t high_vers;
879184588Sdfr	struct svc_callout *s;
880184588Sdfr
881184588Sdfr	/* now match message with a registered service*/
882184588Sdfr	prog_found = FALSE;
883184588Sdfr	low_vers = (rpcvers_t) -1L;
884184588Sdfr	high_vers = (rpcvers_t) 0L;
885184588Sdfr	TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
886184588Sdfr		if (s->sc_prog == rqstp->rq_prog) {
887184588Sdfr			if (s->sc_vers == rqstp->rq_vers) {
888184588Sdfr				/*
889184588Sdfr				 * We hand ownership of r to the
890184588Sdfr				 * dispatch method - they must call
891184588Sdfr				 * svc_freereq.
892184588Sdfr				 */
893184588Sdfr				(*s->sc_dispatch)(rqstp, xprt);
894184588Sdfr				return;
895184588Sdfr			}  /* found correct version */
896184588Sdfr			prog_found = TRUE;
897184588Sdfr			if (s->sc_vers < low_vers)
898184588Sdfr				low_vers = s->sc_vers;
899184588Sdfr			if (s->sc_vers > high_vers)
900184588Sdfr				high_vers = s->sc_vers;
901184588Sdfr		}   /* found correct program */
902184588Sdfr	}
903184588Sdfr
904184588Sdfr	/*
905184588Sdfr	 * if we got here, the program or version
906184588Sdfr	 * is not served ...
907184588Sdfr	 */
908184588Sdfr	if (prog_found)
909184588Sdfr		svcerr_progvers(rqstp, low_vers, high_vers);
910184588Sdfr	else
911184588Sdfr		svcerr_noprog(rqstp);
912184588Sdfr
913184588Sdfr	svc_freereq(rqstp);
914184588Sdfr}
915184588Sdfr
916184588Sdfrstatic void
917184588Sdfrsvc_checkidle(SVCPOOL *pool)
918184588Sdfr{
919184588Sdfr	SVCXPRT *xprt, *nxprt;
920184588Sdfr	time_t timo;
921184588Sdfr	struct svcxprt_list cleanup;
922184588Sdfr
923184588Sdfr	TAILQ_INIT(&cleanup);
924184588Sdfr	TAILQ_FOREACH_SAFE(xprt, &pool->sp_xlist, xp_link, nxprt) {
925184588Sdfr		/*
926184588Sdfr		 * Only some transports have idle timers. Don't time
927184588Sdfr		 * something out which is just waking up.
928184588Sdfr		 */
929184588Sdfr		if (!xprt->xp_idletimeout || xprt->xp_thread)
930184588Sdfr			continue;
931184588Sdfr
932184588Sdfr		timo = xprt->xp_lastactive + xprt->xp_idletimeout;
933184588Sdfr		if (time_uptime > timo) {
934184588Sdfr			xprt_unregister_locked(xprt);
935184588Sdfr			TAILQ_INSERT_TAIL(&cleanup, xprt, xp_link);
936177633Sdfr		}
937184588Sdfr	}
938184588Sdfr
939184588Sdfr	mtx_unlock(&pool->sp_lock);
940184588Sdfr	TAILQ_FOREACH_SAFE(xprt, &cleanup, xp_link, nxprt) {
941184588Sdfr		SVC_RELEASE(xprt);
942184588Sdfr	}
943184588Sdfr	mtx_lock(&pool->sp_lock);
944184588Sdfr
945177633Sdfr}
946177633Sdfr
947184588Sdfrstatic void
948184588Sdfrsvc_assign_waiting_sockets(SVCPOOL *pool)
949177633Sdfr{
950177633Sdfr	SVCXPRT *xprt;
951184588Sdfr
952184588Sdfr	TAILQ_FOREACH(xprt, &pool->sp_active, xp_alink) {
953184588Sdfr		if (!xprt->xp_thread) {
954184588Sdfr			xprt_assignthread(xprt);
955184588Sdfr		}
956184588Sdfr	}
957184588Sdfr}
958184588Sdfr
959184588Sdfrstatic bool_t
960184588Sdfrsvc_request_space_available(SVCPOOL *pool)
961184588Sdfr{
962184588Sdfr
963184588Sdfr	mtx_assert(&pool->sp_lock, MA_OWNED);
964184588Sdfr
965184588Sdfr	if (pool->sp_space_throttled) {
966184588Sdfr		/*
967184588Sdfr		 * Below the low-water yet? If so, assign any waiting sockets.
968184588Sdfr		 */
969184588Sdfr		if (pool->sp_space_used < pool->sp_space_low) {
970184588Sdfr			pool->sp_space_throttled = FALSE;
971184588Sdfr			svc_assign_waiting_sockets(pool);
972184588Sdfr			return TRUE;
973184588Sdfr		}
974184588Sdfr
975184588Sdfr		return FALSE;
976184588Sdfr	} else {
977184588Sdfr		if (pool->sp_space_used
978184588Sdfr		    >= pool->sp_space_high) {
979184588Sdfr			pool->sp_space_throttled = TRUE;
980184588Sdfr			pool->sp_space_throttle_count++;
981184588Sdfr			return FALSE;
982184588Sdfr		}
983184588Sdfr
984184588Sdfr		return TRUE;
985184588Sdfr	}
986184588Sdfr}
987184588Sdfr
988184588Sdfrstatic void
989184588Sdfrsvc_run_internal(SVCPOOL *pool, bool_t ismaster)
990184588Sdfr{
991184588Sdfr	SVCTHREAD *st, *stpref;
992184588Sdfr	SVCXPRT *xprt;
993184588Sdfr	enum xprt_stat stat;
994184588Sdfr	struct svc_req *rqstp;
995177633Sdfr	int error;
996177633Sdfr
997184588Sdfr	st = mem_alloc(sizeof(*st));
998184588Sdfr	st->st_xprt = NULL;
999184588Sdfr	STAILQ_INIT(&st->st_reqs);
1000184588Sdfr	cv_init(&st->st_cond, "rpcsvc");
1001184588Sdfr
1002177633Sdfr	mtx_lock(&pool->sp_lock);
1003184588Sdfr	LIST_INSERT_HEAD(&pool->sp_threads, st, st_link);
1004177633Sdfr
1005184588Sdfr	/*
1006184588Sdfr	 * If we are a new thread which was spawned to cope with
1007184588Sdfr	 * increased load, set the state back to SVCPOOL_ACTIVE.
1008184588Sdfr	 */
1009184588Sdfr	if (pool->sp_state == SVCPOOL_THREADSTARTING)
1010184588Sdfr		pool->sp_state = SVCPOOL_ACTIVE;
1011177633Sdfr
1012184588Sdfr	while (pool->sp_state != SVCPOOL_CLOSING) {
1013184588Sdfr		/*
1014184588Sdfr		 * Check for idle transports once per second.
1015184588Sdfr		 */
1016184588Sdfr		if (time_uptime > pool->sp_lastidlecheck) {
1017184588Sdfr			pool->sp_lastidlecheck = time_uptime;
1018184588Sdfr			svc_checkidle(pool);
1019184588Sdfr		}
1020184588Sdfr
1021184588Sdfr		xprt = st->st_xprt;
1022184588Sdfr		if (!xprt && STAILQ_EMPTY(&st->st_reqs)) {
1023184588Sdfr			/*
1024184588Sdfr			 * Enforce maxthreads count.
1025184588Sdfr			 */
1026184588Sdfr			if (pool->sp_threadcount > pool->sp_maxthreads)
1027177633Sdfr				break;
1028184588Sdfr
1029184588Sdfr			/*
1030184588Sdfr			 * Before sleeping, see if we can find an
1031184588Sdfr			 * active transport which isn't being serviced
1032184588Sdfr			 * by a thread.
1033184588Sdfr			 */
1034184588Sdfr			if (svc_request_space_available(pool)) {
1035184588Sdfr				TAILQ_FOREACH(xprt, &pool->sp_active,
1036184588Sdfr				    xp_alink) {
1037184588Sdfr					if (!xprt->xp_thread) {
1038184588Sdfr						SVC_ACQUIRE(xprt);
1039184588Sdfr						xprt->xp_thread = st;
1040184588Sdfr						st->st_xprt = xprt;
1041184588Sdfr						break;
1042184588Sdfr					}
1043184588Sdfr				}
1044184588Sdfr			}
1045184588Sdfr			if (st->st_xprt)
1046184588Sdfr				continue;
1047184588Sdfr
1048184588Sdfr			LIST_INSERT_HEAD(&pool->sp_idlethreads, st, st_ilink);
1049184588Sdfr			error = cv_timedwait_sig(&st->st_cond, &pool->sp_lock,
1050184588Sdfr				5 * hz);
1051184588Sdfr			LIST_REMOVE(st, st_ilink);
1052184588Sdfr
1053184588Sdfr			/*
1054184588Sdfr			 * Reduce worker thread count when idle.
1055184588Sdfr			 */
1056184588Sdfr			if (error == EWOULDBLOCK) {
1057184588Sdfr				if (!ismaster
1058184588Sdfr				    && (pool->sp_threadcount
1059184588Sdfr					> pool->sp_minthreads)
1060184588Sdfr					&& !st->st_xprt
1061184588Sdfr					&& STAILQ_EMPTY(&st->st_reqs))
1062184588Sdfr					break;
1063184588Sdfr			}
1064184588Sdfr			if (error == EWOULDBLOCK)
1065184588Sdfr				continue;
1066184588Sdfr			if (error) {
1067184588Sdfr				if (pool->sp_state != SVCPOOL_CLOSING) {
1068184588Sdfr					mtx_unlock(&pool->sp_lock);
1069184588Sdfr					svc_exit(pool);
1070184588Sdfr					mtx_lock(&pool->sp_lock);
1071184588Sdfr				}
1072184588Sdfr				break;
1073184588Sdfr			}
1074184588Sdfr
1075184588Sdfr			if (pool->sp_state == SVCPOOL_THREADWANTED) {
1076184588Sdfr				pool->sp_state = SVCPOOL_THREADSTARTING;
1077184588Sdfr				pool->sp_lastcreatetime = time_uptime;
1078184588Sdfr				mtx_unlock(&pool->sp_lock);
1079184588Sdfr				svc_new_thread(pool);
1080184588Sdfr				mtx_lock(&pool->sp_lock);
1081184588Sdfr			}
1082177633Sdfr			continue;
1083177633Sdfr		}
1084177633Sdfr
1085184588Sdfr		if (xprt) {
1086184588Sdfr			/*
1087184588Sdfr			 * Drain the transport socket and queue up any
1088184588Sdfr			 * RPCs.
1089184588Sdfr			 */
1090184588Sdfr			xprt->xp_lastactive = time_uptime;
1091184588Sdfr			stat = XPRT_IDLE;
1092184588Sdfr			do {
1093184588Sdfr				if (!svc_request_space_available(pool))
1094184588Sdfr					break;
1095184588Sdfr				rqstp = NULL;
1096184588Sdfr				mtx_unlock(&pool->sp_lock);
1097184588Sdfr				stat = svc_getreq(xprt, &rqstp);
1098184588Sdfr				mtx_lock(&pool->sp_lock);
1099184588Sdfr				if (rqstp) {
1100184588Sdfr					/*
1101184588Sdfr					 * See if the application has
1102184588Sdfr					 * a preference for some other
1103184588Sdfr					 * thread.
1104184588Sdfr					 */
1105184588Sdfr					stpref = st;
1106184588Sdfr					if (pool->sp_assign)
1107184588Sdfr						stpref = pool->sp_assign(st,
1108184588Sdfr						    rqstp);
1109184588Sdfr
1110184588Sdfr					pool->sp_space_used +=
1111184588Sdfr						rqstp->rq_size;
1112184588Sdfr					if (pool->sp_space_used
1113184588Sdfr					    > pool->sp_space_used_highest)
1114184588Sdfr						pool->sp_space_used_highest =
1115184588Sdfr							pool->sp_space_used;
1116184588Sdfr					rqstp->rq_thread = stpref;
1117184588Sdfr					STAILQ_INSERT_TAIL(&stpref->st_reqs,
1118184588Sdfr					    rqstp, rq_link);
1119184588Sdfr					stpref->st_reqcount++;
1120184588Sdfr
1121184588Sdfr					/*
1122184588Sdfr					 * If we assigned the request
1123184588Sdfr					 * to another thread, make
1124184588Sdfr					 * sure its awake and continue
1125184588Sdfr					 * reading from the
1126184588Sdfr					 * socket. Otherwise, try to
1127184588Sdfr					 * find some other thread to
1128184588Sdfr					 * read from the socket and
1129184588Sdfr					 * execute the request
1130184588Sdfr					 * immediately.
1131184588Sdfr					 */
1132184588Sdfr					if (stpref != st) {
1133184588Sdfr						cv_signal(&stpref->st_cond);
1134184588Sdfr						continue;
1135184588Sdfr					} else {
1136184588Sdfr						break;
1137184588Sdfr					}
1138184588Sdfr				}
1139184588Sdfr			} while (stat == XPRT_MOREREQS
1140184588Sdfr			    && pool->sp_state != SVCPOOL_CLOSING);
1141184588Sdfr
1142184588Sdfr			/*
1143184588Sdfr			 * Move this transport to the end of the
1144184588Sdfr			 * active list to ensure fairness when
1145184588Sdfr			 * multiple transports are active. If this was
1146184588Sdfr			 * the last queued request, svc_getreq will
1147184588Sdfr			 * end up calling xprt_inactive to remove from
1148184588Sdfr			 * the active list.
1149184588Sdfr			 */
1150184588Sdfr			xprt->xp_thread = NULL;
1151184588Sdfr			st->st_xprt = NULL;
1152184588Sdfr			if (xprt->xp_active) {
1153184588Sdfr				xprt_assignthread(xprt);
1154184588Sdfr				TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
1155184588Sdfr				TAILQ_INSERT_TAIL(&pool->sp_active, xprt,
1156184588Sdfr				    xp_alink);
1157184588Sdfr			}
1158184588Sdfr			mtx_unlock(&pool->sp_lock);
1159184588Sdfr			SVC_RELEASE(xprt);
1160184588Sdfr			mtx_lock(&pool->sp_lock);
1161184588Sdfr		}
1162184588Sdfr
1163177633Sdfr		/*
1164184588Sdfr		 * Execute what we have queued.
1165177633Sdfr		 */
1166184588Sdfr		while ((rqstp = STAILQ_FIRST(&st->st_reqs)) != NULL) {
1167184588Sdfr			size_t sz = rqstp->rq_size;
1168184588Sdfr			mtx_unlock(&pool->sp_lock);
1169184588Sdfr			svc_executereq(rqstp);
1170184588Sdfr			mtx_lock(&pool->sp_lock);
1171184588Sdfr			pool->sp_space_used -= sz;
1172184588Sdfr		}
1173184588Sdfr	}
1174177633Sdfr
1175184588Sdfr	if (st->st_xprt) {
1176184588Sdfr		xprt = st->st_xprt;
1177184588Sdfr		st->st_xprt = NULL;
1178184588Sdfr		SVC_RELEASE(xprt);
1179177633Sdfr	}
1180177633Sdfr
1181184588Sdfr	KASSERT(STAILQ_EMPTY(&st->st_reqs), ("stray reqs on exit"));
1182184588Sdfr	LIST_REMOVE(st, st_link);
1183184588Sdfr	pool->sp_threadcount--;
1184184588Sdfr
1185177633Sdfr	mtx_unlock(&pool->sp_lock);
1186184588Sdfr
1187184588Sdfr	cv_destroy(&st->st_cond);
1188184588Sdfr	mem_free(st, sizeof(*st));
1189184588Sdfr
1190184588Sdfr	if (!ismaster)
1191184588Sdfr		wakeup(pool);
1192177633Sdfr}
1193177633Sdfr
1194184588Sdfrstatic void
1195184588Sdfrsvc_thread_start(void *arg)
1196184588Sdfr{
1197184588Sdfr
1198184588Sdfr	svc_run_internal((SVCPOOL *) arg, FALSE);
1199184588Sdfr	kthread_exit();
1200184588Sdfr}
1201184588Sdfr
1202184588Sdfrstatic void
1203184588Sdfrsvc_new_thread(SVCPOOL *pool)
1204184588Sdfr{
1205184588Sdfr	struct thread *td;
1206184588Sdfr
1207184588Sdfr	pool->sp_threadcount++;
1208184588Sdfr	kthread_add(svc_thread_start, pool,
1209184588Sdfr	    pool->sp_proc, &td, 0, 0,
1210184588Sdfr	    "%s: service", pool->sp_name);
1211184588Sdfr}
1212184588Sdfr
1213177633Sdfrvoid
1214184588Sdfrsvc_run(SVCPOOL *pool)
1215184588Sdfr{
1216184588Sdfr	int i;
1217184588Sdfr	struct proc *p;
1218184588Sdfr	struct thread *td;
1219184588Sdfr
1220184588Sdfr	p = curproc;
1221184588Sdfr	td = curthread;
1222184588Sdfr	snprintf(td->td_name, sizeof(td->td_name),
1223184588Sdfr	    "%s: master", pool->sp_name);
1224184588Sdfr	pool->sp_state = SVCPOOL_ACTIVE;
1225184588Sdfr	pool->sp_proc = p;
1226184588Sdfr	pool->sp_lastcreatetime = time_uptime;
1227184588Sdfr	pool->sp_threadcount = 1;
1228184588Sdfr
1229184588Sdfr	for (i = 1; i < pool->sp_minthreads; i++) {
1230184588Sdfr		svc_new_thread(pool);
1231184588Sdfr	}
1232184588Sdfr
1233184588Sdfr	svc_run_internal(pool, TRUE);
1234184588Sdfr
1235184588Sdfr	mtx_lock(&pool->sp_lock);
1236184588Sdfr	while (pool->sp_threadcount > 0)
1237184588Sdfr		msleep(pool, &pool->sp_lock, 0, "svcexit", 0);
1238184588Sdfr	mtx_unlock(&pool->sp_lock);
1239184588Sdfr}
1240184588Sdfr
1241184588Sdfrvoid
1242177633Sdfrsvc_exit(SVCPOOL *pool)
1243177633Sdfr{
1244184588Sdfr	SVCTHREAD *st;
1245184588Sdfr
1246177633Sdfr	mtx_lock(&pool->sp_lock);
1247184588Sdfr
1248184588Sdfr	pool->sp_state = SVCPOOL_CLOSING;
1249184588Sdfr	LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink)
1250184588Sdfr		cv_signal(&st->st_cond);
1251184588Sdfr
1252177633Sdfr	mtx_unlock(&pool->sp_lock);
1253177633Sdfr}
1254184588Sdfr
1255184588Sdfrbool_t
1256184588Sdfrsvc_getargs(struct svc_req *rqstp, xdrproc_t xargs, void *args)
1257184588Sdfr{
1258184588Sdfr	struct mbuf *m;
1259184588Sdfr	XDR xdrs;
1260184588Sdfr	bool_t stat;
1261184588Sdfr
1262184588Sdfr	m = rqstp->rq_args;
1263184588Sdfr	rqstp->rq_args = NULL;
1264184588Sdfr
1265184588Sdfr	xdrmbuf_create(&xdrs, m, XDR_DECODE);
1266184588Sdfr	stat = xargs(&xdrs, args);
1267184588Sdfr	XDR_DESTROY(&xdrs);
1268184588Sdfr
1269184588Sdfr	return (stat);
1270184588Sdfr}
1271184588Sdfr
1272184588Sdfrbool_t
1273184588Sdfrsvc_freeargs(struct svc_req *rqstp, xdrproc_t xargs, void *args)
1274184588Sdfr{
1275184588Sdfr	XDR xdrs;
1276184588Sdfr
1277184588Sdfr	if (rqstp->rq_addr) {
1278184588Sdfr		free(rqstp->rq_addr, M_SONAME);
1279184588Sdfr		rqstp->rq_addr = NULL;
1280184588Sdfr	}
1281184588Sdfr
1282184588Sdfr	xdrs.x_op = XDR_FREE;
1283184588Sdfr	return (xargs(&xdrs, args));
1284184588Sdfr}
1285184588Sdfr
1286184588Sdfrvoid
1287184588Sdfrsvc_freereq(struct svc_req *rqstp)
1288184588Sdfr{
1289184588Sdfr	SVCTHREAD *st;
1290184588Sdfr	SVCXPRT *xprt;
1291184588Sdfr	SVCPOOL *pool;
1292184588Sdfr
1293184588Sdfr	st = rqstp->rq_thread;
1294184588Sdfr	xprt = rqstp->rq_xprt;
1295184588Sdfr	if (xprt)
1296184588Sdfr		pool = xprt->xp_pool;
1297184588Sdfr	else
1298184588Sdfr		pool = NULL;
1299184588Sdfr	if (st) {
1300184588Sdfr		mtx_lock(&pool->sp_lock);
1301184588Sdfr		KASSERT(rqstp == STAILQ_FIRST(&st->st_reqs),
1302184588Sdfr		    ("Freeing request out of order"));
1303184588Sdfr		STAILQ_REMOVE_HEAD(&st->st_reqs, rq_link);
1304184588Sdfr		st->st_reqcount--;
1305184588Sdfr		if (pool->sp_done)
1306184588Sdfr			pool->sp_done(st, rqstp);
1307184588Sdfr		mtx_unlock(&pool->sp_lock);
1308184588Sdfr	}
1309184588Sdfr
1310184588Sdfr	if (rqstp->rq_auth.svc_ah_ops)
1311184588Sdfr		SVCAUTH_RELEASE(&rqstp->rq_auth);
1312184588Sdfr
1313184588Sdfr	if (rqstp->rq_xprt) {
1314184588Sdfr		SVC_RELEASE(rqstp->rq_xprt);
1315184588Sdfr	}
1316184588Sdfr
1317184588Sdfr	if (rqstp->rq_addr)
1318184588Sdfr		free(rqstp->rq_addr, M_SONAME);
1319184588Sdfr
1320184588Sdfr	if (rqstp->rq_args)
1321184588Sdfr		m_freem(rqstp->rq_args);
1322184588Sdfr
1323184588Sdfr	free(rqstp, M_RPC);
1324184588Sdfr}
1325