svc.c revision 261054
1177633Sdfr/*	$NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $	*/
2177633Sdfr
3261046Smav/*-
4261046Smav * Copyright (c) 2009, Sun Microsystems, Inc.
5261046Smav * All rights reserved.
6177633Sdfr *
7261046Smav * Redistribution and use in source and binary forms, with or without
8261046Smav * modification, are permitted provided that the following conditions are met:
9261046Smav * - Redistributions of source code must retain the above copyright notice,
10261046Smav *   this list of conditions and the following disclaimer.
11261046Smav * - Redistributions in binary form must reproduce the above copyright notice,
12261046Smav *   this list of conditions and the following disclaimer in the documentation
13261046Smav *   and/or other materials provided with the distribution.
14261046Smav * - Neither the name of Sun Microsystems, Inc. nor the names of its
15261046Smav *   contributors may be used to endorse or promote products derived
16261046Smav *   from this software without specific prior written permission.
17261046Smav *
18261046Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19261046Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20261046Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21261046Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22261046Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23261046Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24261046Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25261046Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26261046Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27261046Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28261046Smav * POSSIBILITY OF SUCH DAMAGE.
29177633Sdfr */
30177633Sdfr
31177633Sdfr#if defined(LIBC_SCCS) && !defined(lint)
32177633Sdfrstatic char *sccsid2 = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";
33177633Sdfrstatic char *sccsid = "@(#)svc.c	2.4 88/08/11 4.0 RPCSRC";
34177633Sdfr#endif
35177633Sdfr#include <sys/cdefs.h>
36177633Sdfr__FBSDID("$FreeBSD: stable/10/sys/rpc/svc.c 261054 2014-01-22 23:52:20Z mav $");
37177633Sdfr
38177633Sdfr/*
39177633Sdfr * svc.c, Server-side remote procedure call interface.
40177633Sdfr *
41177633Sdfr * There are two sets of procedures here.  The xprt routines are
42177633Sdfr * for handling transport handles.  The svc routines handle the
43177633Sdfr * list of service routines.
44177633Sdfr *
45177633Sdfr * Copyright (C) 1984, Sun Microsystems, Inc.
46177633Sdfr */
47177633Sdfr
48177633Sdfr#include <sys/param.h>
49177633Sdfr#include <sys/lock.h>
50177633Sdfr#include <sys/kernel.h>
51184588Sdfr#include <sys/kthread.h>
52177633Sdfr#include <sys/malloc.h>
53184588Sdfr#include <sys/mbuf.h>
54177633Sdfr#include <sys/mutex.h>
55184588Sdfr#include <sys/proc.h>
56177633Sdfr#include <sys/queue.h>
57184588Sdfr#include <sys/socketvar.h>
58177633Sdfr#include <sys/systm.h>
59177633Sdfr#include <sys/ucred.h>
60177633Sdfr
61177633Sdfr#include <rpc/rpc.h>
62177633Sdfr#include <rpc/rpcb_clnt.h>
63184588Sdfr#include <rpc/replay.h>
64177633Sdfr
65177685Sdfr#include <rpc/rpc_com.h>
66177633Sdfr
67177633Sdfr#define SVC_VERSQUIET 0x0001		/* keep quiet about vers mismatch */
68184588Sdfr#define version_keepquiet(xp) (SVC_EXT(xp)->xp_flags & SVC_VERSQUIET)
69177633Sdfr
70177633Sdfrstatic struct svc_callout *svc_find(SVCPOOL *pool, rpcprog_t, rpcvers_t,
71177633Sdfr    char *);
72184588Sdfrstatic void svc_new_thread(SVCPOOL *pool);
73184588Sdfrstatic void xprt_unregister_locked(SVCXPRT *xprt);
74261054Smavstatic void svc_change_space_used(SVCPOOL *pool, int delta);
75261054Smavstatic bool_t svc_request_space_available(SVCPOOL *pool);
76177633Sdfr
77177633Sdfr/* ***************  SVCXPRT related stuff **************** */
78177633Sdfr
79184588Sdfrstatic int svcpool_minthread_sysctl(SYSCTL_HANDLER_ARGS);
80184588Sdfrstatic int svcpool_maxthread_sysctl(SYSCTL_HANDLER_ARGS);
81184588Sdfr
82177633SdfrSVCPOOL*
83184588Sdfrsvcpool_create(const char *name, struct sysctl_oid_list *sysctl_base)
84177633Sdfr{
85177633Sdfr	SVCPOOL *pool;
86177633Sdfr
87177633Sdfr	pool = malloc(sizeof(SVCPOOL), M_RPC, M_WAITOK|M_ZERO);
88177633Sdfr
89177633Sdfr	mtx_init(&pool->sp_lock, "sp_lock", NULL, MTX_DEF);
90184588Sdfr	pool->sp_name = name;
91184588Sdfr	pool->sp_state = SVCPOOL_INIT;
92184588Sdfr	pool->sp_proc = NULL;
93177633Sdfr	TAILQ_INIT(&pool->sp_xlist);
94177633Sdfr	TAILQ_INIT(&pool->sp_active);
95177633Sdfr	TAILQ_INIT(&pool->sp_callouts);
96184588Sdfr	LIST_INIT(&pool->sp_threads);
97184588Sdfr	LIST_INIT(&pool->sp_idlethreads);
98184588Sdfr	pool->sp_minthreads = 1;
99184588Sdfr	pool->sp_maxthreads = 1;
100184588Sdfr	pool->sp_threadcount = 0;
101177633Sdfr
102184588Sdfr	/*
103184588Sdfr	 * Don't use more than a quarter of mbuf clusters or more than
104184588Sdfr	 * 45Mb buffering requests.
105184588Sdfr	 */
106184588Sdfr	pool->sp_space_high = nmbclusters * MCLBYTES / 4;
107184588Sdfr	if (pool->sp_space_high > 45 << 20)
108184588Sdfr		pool->sp_space_high = 45 << 20;
109184588Sdfr	pool->sp_space_low = 2 * pool->sp_space_high / 3;
110184588Sdfr
111184588Sdfr	sysctl_ctx_init(&pool->sp_sysctl);
112184588Sdfr	if (sysctl_base) {
113184588Sdfr		SYSCTL_ADD_PROC(&pool->sp_sysctl, sysctl_base, OID_AUTO,
114184588Sdfr		    "minthreads", CTLTYPE_INT | CTLFLAG_RW,
115184588Sdfr		    pool, 0, svcpool_minthread_sysctl, "I", "");
116184588Sdfr		SYSCTL_ADD_PROC(&pool->sp_sysctl, sysctl_base, OID_AUTO,
117184588Sdfr		    "maxthreads", CTLTYPE_INT | CTLFLAG_RW,
118184588Sdfr		    pool, 0, svcpool_maxthread_sysctl, "I", "");
119184588Sdfr		SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
120184588Sdfr		    "threads", CTLFLAG_RD, &pool->sp_threadcount, 0, "");
121184588Sdfr
122184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
123184588Sdfr		    "request_space_used", CTLFLAG_RD,
124184588Sdfr		    &pool->sp_space_used, 0,
125184588Sdfr		    "Space in parsed but not handled requests.");
126184588Sdfr
127184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
128184588Sdfr		    "request_space_used_highest", CTLFLAG_RD,
129184588Sdfr		    &pool->sp_space_used_highest, 0,
130184588Sdfr		    "Highest space used since reboot.");
131184588Sdfr
132184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
133184588Sdfr		    "request_space_high", CTLFLAG_RW,
134184588Sdfr		    &pool->sp_space_high, 0,
135184588Sdfr		    "Maximum space in parsed but not handled requests.");
136184588Sdfr
137184588Sdfr		SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
138184588Sdfr		    "request_space_low", CTLFLAG_RW,
139184588Sdfr		    &pool->sp_space_low, 0,
140184588Sdfr		    "Low water mark for request space.");
141184588Sdfr
142217326Smdf		SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
143184588Sdfr		    "request_space_throttled", CTLFLAG_RD,
144184588Sdfr		    &pool->sp_space_throttled, 0,
145184588Sdfr		    "Whether nfs requests are currently throttled");
146184588Sdfr
147217326Smdf		SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
148184588Sdfr		    "request_space_throttle_count", CTLFLAG_RD,
149184588Sdfr		    &pool->sp_space_throttle_count, 0,
150184588Sdfr		    "Count of times throttling based on request space has occurred");
151184588Sdfr	}
152184588Sdfr
153177633Sdfr	return pool;
154177633Sdfr}
155177633Sdfr
156177633Sdfrvoid
157177633Sdfrsvcpool_destroy(SVCPOOL *pool)
158177633Sdfr{
159184588Sdfr	SVCXPRT *xprt, *nxprt;
160177633Sdfr	struct svc_callout *s;
161184588Sdfr	struct svcxprt_list cleanup;
162177633Sdfr
163184588Sdfr	TAILQ_INIT(&cleanup);
164177633Sdfr	mtx_lock(&pool->sp_lock);
165177633Sdfr
166177633Sdfr	while (TAILQ_FIRST(&pool->sp_xlist)) {
167177633Sdfr		xprt = TAILQ_FIRST(&pool->sp_xlist);
168184588Sdfr		xprt_unregister_locked(xprt);
169184588Sdfr		TAILQ_INSERT_TAIL(&cleanup, xprt, xp_link);
170177633Sdfr	}
171177633Sdfr
172177633Sdfr	while (TAILQ_FIRST(&pool->sp_callouts)) {
173177633Sdfr		s = TAILQ_FIRST(&pool->sp_callouts);
174177633Sdfr		mtx_unlock(&pool->sp_lock);
175177633Sdfr		svc_unreg(pool, s->sc_prog, s->sc_vers);
176177633Sdfr		mtx_lock(&pool->sp_lock);
177177633Sdfr	}
178193603Srmacklem	mtx_unlock(&pool->sp_lock);
179177633Sdfr
180184588Sdfr	TAILQ_FOREACH_SAFE(xprt, &cleanup, xp_link, nxprt) {
181184588Sdfr		SVC_RELEASE(xprt);
182184588Sdfr	}
183184588Sdfr
184193436Srmacklem	mtx_destroy(&pool->sp_lock);
185193436Srmacklem
186184588Sdfr	if (pool->sp_rcache)
187184588Sdfr		replay_freecache(pool->sp_rcache);
188184588Sdfr
189184588Sdfr	sysctl_ctx_free(&pool->sp_sysctl);
190177633Sdfr	free(pool, M_RPC);
191177633Sdfr}
192177633Sdfr
193184588Sdfrstatic bool_t
194184588Sdfrsvcpool_active(SVCPOOL *pool)
195184588Sdfr{
196184588Sdfr	enum svcpool_state state = pool->sp_state;
197184588Sdfr
198184588Sdfr	if (state == SVCPOOL_INIT || state == SVCPOOL_CLOSING)
199184588Sdfr		return (FALSE);
200184588Sdfr	return (TRUE);
201184588Sdfr}
202184588Sdfr
203177633Sdfr/*
204184588Sdfr * Sysctl handler to set the minimum thread count on a pool
205184588Sdfr */
206184588Sdfrstatic int
207184588Sdfrsvcpool_minthread_sysctl(SYSCTL_HANDLER_ARGS)
208184588Sdfr{
209184588Sdfr	SVCPOOL *pool;
210184588Sdfr	int newminthreads, error, n;
211184588Sdfr
212184588Sdfr	pool = oidp->oid_arg1;
213184588Sdfr	newminthreads = pool->sp_minthreads;
214184588Sdfr	error = sysctl_handle_int(oidp, &newminthreads, 0, req);
215184588Sdfr	if (error == 0 && newminthreads != pool->sp_minthreads) {
216184588Sdfr		if (newminthreads > pool->sp_maxthreads)
217184588Sdfr			return (EINVAL);
218184588Sdfr		mtx_lock(&pool->sp_lock);
219184588Sdfr		if (newminthreads > pool->sp_minthreads
220184588Sdfr		    && svcpool_active(pool)) {
221184588Sdfr			/*
222184588Sdfr			 * If the pool is running and we are
223184588Sdfr			 * increasing, create some more threads now.
224184588Sdfr			 */
225184588Sdfr			n = newminthreads - pool->sp_threadcount;
226184588Sdfr			if (n > 0) {
227184588Sdfr				mtx_unlock(&pool->sp_lock);
228184588Sdfr				while (n--)
229184588Sdfr					svc_new_thread(pool);
230184588Sdfr				mtx_lock(&pool->sp_lock);
231184588Sdfr			}
232184588Sdfr		}
233184588Sdfr		pool->sp_minthreads = newminthreads;
234184588Sdfr		mtx_unlock(&pool->sp_lock);
235184588Sdfr	}
236184588Sdfr	return (error);
237184588Sdfr}
238184588Sdfr
239184588Sdfr/*
240184588Sdfr * Sysctl handler to set the maximum thread count on a pool
241184588Sdfr */
242184588Sdfrstatic int
243184588Sdfrsvcpool_maxthread_sysctl(SYSCTL_HANDLER_ARGS)
244184588Sdfr{
245184588Sdfr	SVCPOOL *pool;
246184588Sdfr	SVCTHREAD *st;
247184588Sdfr	int newmaxthreads, error;
248184588Sdfr
249184588Sdfr	pool = oidp->oid_arg1;
250184588Sdfr	newmaxthreads = pool->sp_maxthreads;
251184588Sdfr	error = sysctl_handle_int(oidp, &newmaxthreads, 0, req);
252184588Sdfr	if (error == 0 && newmaxthreads != pool->sp_maxthreads) {
253184588Sdfr		if (newmaxthreads < pool->sp_minthreads)
254184588Sdfr			return (EINVAL);
255184588Sdfr		mtx_lock(&pool->sp_lock);
256184588Sdfr		if (newmaxthreads < pool->sp_maxthreads
257184588Sdfr		    && svcpool_active(pool)) {
258184588Sdfr			/*
259184588Sdfr			 * If the pool is running and we are
260184588Sdfr			 * decreasing, wake up some idle threads to
261184588Sdfr			 * encourage them to exit.
262184588Sdfr			 */
263184588Sdfr			LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink)
264184588Sdfr				cv_signal(&st->st_cond);
265184588Sdfr		}
266184588Sdfr		pool->sp_maxthreads = newmaxthreads;
267184588Sdfr		mtx_unlock(&pool->sp_lock);
268184588Sdfr	}
269184588Sdfr	return (error);
270184588Sdfr}
271184588Sdfr
272184588Sdfr/*
273177633Sdfr * Activate a transport handle.
274177633Sdfr */
275177633Sdfrvoid
276177633Sdfrxprt_register(SVCXPRT *xprt)
277177633Sdfr{
278177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
279177633Sdfr
280194407Srmacklem	SVC_ACQUIRE(xprt);
281177633Sdfr	mtx_lock(&pool->sp_lock);
282177633Sdfr	xprt->xp_registered = TRUE;
283177633Sdfr	xprt->xp_active = FALSE;
284177633Sdfr	TAILQ_INSERT_TAIL(&pool->sp_xlist, xprt, xp_link);
285177633Sdfr	mtx_unlock(&pool->sp_lock);
286177633Sdfr}
287177633Sdfr
288177633Sdfr/*
289184588Sdfr * De-activate a transport handle. Note: the locked version doesn't
290184588Sdfr * release the transport - caller must do that after dropping the pool
291184588Sdfr * lock.
292177633Sdfr */
293177633Sdfrstatic void
294184588Sdfrxprt_unregister_locked(SVCXPRT *xprt)
295177633Sdfr{
296177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
297177633Sdfr
298261048Smav	mtx_assert(&pool->sp_lock, MA_OWNED);
299193649Srmacklem	KASSERT(xprt->xp_registered == TRUE,
300193649Srmacklem	    ("xprt_unregister_locked: not registered"));
301261048Smav	xprt_inactive_locked(xprt);
302177633Sdfr	TAILQ_REMOVE(&pool->sp_xlist, xprt, xp_link);
303177633Sdfr	xprt->xp_registered = FALSE;
304184588Sdfr}
305177633Sdfr
306184588Sdfrvoid
307184588Sdfrxprt_unregister(SVCXPRT *xprt)
308184588Sdfr{
309184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
310184588Sdfr
311184588Sdfr	mtx_lock(&pool->sp_lock);
312193649Srmacklem	if (xprt->xp_registered == FALSE) {
313193649Srmacklem		/* Already unregistered by another thread */
314193649Srmacklem		mtx_unlock(&pool->sp_lock);
315193649Srmacklem		return;
316193649Srmacklem	}
317184588Sdfr	xprt_unregister_locked(xprt);
318184588Sdfr	mtx_unlock(&pool->sp_lock);
319184588Sdfr
320184588Sdfr	SVC_RELEASE(xprt);
321177633Sdfr}
322177633Sdfr
323261048Smav/*
324261048Smav * Attempt to assign a service thread to this transport.
325261048Smav */
326261048Smavstatic int
327184588Sdfrxprt_assignthread(SVCXPRT *xprt)
328184588Sdfr{
329184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
330184588Sdfr	SVCTHREAD *st;
331184588Sdfr
332261048Smav	mtx_assert(&pool->sp_lock, MA_OWNED);
333261048Smav	st = LIST_FIRST(&pool->sp_idlethreads);
334184588Sdfr	if (st) {
335261048Smav		LIST_REMOVE(st, st_ilink);
336261048Smav		st->st_idle = FALSE;
337184588Sdfr		SVC_ACQUIRE(xprt);
338184588Sdfr		xprt->xp_thread = st;
339184588Sdfr		st->st_xprt = xprt;
340184588Sdfr		cv_signal(&st->st_cond);
341261048Smav		return (TRUE);
342184588Sdfr	} else {
343184588Sdfr		/*
344184588Sdfr		 * See if we can create a new thread. The
345184588Sdfr		 * actual thread creation happens in
346184588Sdfr		 * svc_run_internal because our locking state
347184588Sdfr		 * is poorly defined (we are typically called
348184588Sdfr		 * from a socket upcall). Don't create more
349184588Sdfr		 * than one thread per second.
350184588Sdfr		 */
351184588Sdfr		if (pool->sp_state == SVCPOOL_ACTIVE
352184588Sdfr		    && pool->sp_lastcreatetime < time_uptime
353184588Sdfr		    && pool->sp_threadcount < pool->sp_maxthreads) {
354184588Sdfr			pool->sp_state = SVCPOOL_THREADWANTED;
355184588Sdfr		}
356184588Sdfr	}
357261048Smav	return (FALSE);
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		xprt->xp_active = TRUE;
377261048Smav		if (xprt->xp_thread == NULL) {
378261054Smav			if (!svc_request_space_available(pool) ||
379261054Smav			    !xprt_assignthread(xprt))
380261048Smav				TAILQ_INSERT_TAIL(&pool->sp_active, xprt,
381261048Smav				    xp_alink);
382261048Smav		}
383177633Sdfr	}
384177633Sdfr
385177633Sdfr	mtx_unlock(&pool->sp_lock);
386177633Sdfr}
387177633Sdfr
388177633Sdfrvoid
389184588Sdfrxprt_inactive_locked(SVCXPRT *xprt)
390177633Sdfr{
391177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
392177633Sdfr
393261048Smav	mtx_assert(&pool->sp_lock, MA_OWNED);
394177633Sdfr	if (xprt->xp_active) {
395261048Smav		if (xprt->xp_thread == NULL)
396261048Smav			TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
397177633Sdfr		xprt->xp_active = FALSE;
398177633Sdfr	}
399184588Sdfr}
400177633Sdfr
401184588Sdfrvoid
402184588Sdfrxprt_inactive(SVCXPRT *xprt)
403184588Sdfr{
404184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
405184588Sdfr
406184588Sdfr	mtx_lock(&pool->sp_lock);
407184588Sdfr	xprt_inactive_locked(xprt);
408177633Sdfr	mtx_unlock(&pool->sp_lock);
409177633Sdfr}
410177633Sdfr
411177633Sdfr/*
412261053Smav * Variant of xprt_inactive() for use only when sure that port is
413261053Smav * assigned to thread. For example, withing receive handlers.
414261053Smav */
415261053Smavvoid
416261053Smavxprt_inactive_self(SVCXPRT *xprt)
417261053Smav{
418261053Smav
419261053Smav	KASSERT(xprt->xp_thread != NULL,
420261053Smav	    ("xprt_inactive_self(%p) with NULL xp_thread", xprt));
421261053Smav	xprt->xp_active = FALSE;
422261053Smav}
423261053Smav
424261053Smav/*
425177633Sdfr * Add a service program to the callout list.
426177633Sdfr * The dispatch routine will be called when a rpc request for this
427177633Sdfr * program number comes in.
428177633Sdfr */
429177633Sdfrbool_t
430177633Sdfrsvc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
431177633Sdfr    void (*dispatch)(struct svc_req *, SVCXPRT *),
432177633Sdfr    const struct netconfig *nconf)
433177633Sdfr{
434177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
435177633Sdfr	struct svc_callout *s;
436177633Sdfr	char *netid = NULL;
437177633Sdfr	int flag = 0;
438177633Sdfr
439177633Sdfr/* VARIABLES PROTECTED BY svc_lock: s, svc_head */
440177633Sdfr
441177633Sdfr	if (xprt->xp_netid) {
442177633Sdfr		netid = strdup(xprt->xp_netid, M_RPC);
443177633Sdfr		flag = 1;
444177633Sdfr	} else if (nconf && nconf->nc_netid) {
445177633Sdfr		netid = strdup(nconf->nc_netid, M_RPC);
446177633Sdfr		flag = 1;
447177633Sdfr	} /* must have been created with svc_raw_create */
448177633Sdfr	if ((netid == NULL) && (flag == 1)) {
449177633Sdfr		return (FALSE);
450177633Sdfr	}
451177633Sdfr
452177633Sdfr	mtx_lock(&pool->sp_lock);
453177633Sdfr	if ((s = svc_find(pool, prog, vers, netid)) != NULL) {
454177633Sdfr		if (netid)
455177633Sdfr			free(netid, M_RPC);
456177633Sdfr		if (s->sc_dispatch == dispatch)
457177633Sdfr			goto rpcb_it; /* he is registering another xptr */
458177633Sdfr		mtx_unlock(&pool->sp_lock);
459177633Sdfr		return (FALSE);
460177633Sdfr	}
461177633Sdfr	s = malloc(sizeof (struct svc_callout), M_RPC, M_NOWAIT);
462177633Sdfr	if (s == NULL) {
463177633Sdfr		if (netid)
464177633Sdfr			free(netid, M_RPC);
465177633Sdfr		mtx_unlock(&pool->sp_lock);
466177633Sdfr		return (FALSE);
467177633Sdfr	}
468177633Sdfr
469177633Sdfr	s->sc_prog = prog;
470177633Sdfr	s->sc_vers = vers;
471177633Sdfr	s->sc_dispatch = dispatch;
472177633Sdfr	s->sc_netid = netid;
473177633Sdfr	TAILQ_INSERT_TAIL(&pool->sp_callouts, s, sc_link);
474177633Sdfr
475177633Sdfr	if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
476177633Sdfr		((SVCXPRT *) xprt)->xp_netid = strdup(netid, M_RPC);
477177633Sdfr
478177633Sdfrrpcb_it:
479177633Sdfr	mtx_unlock(&pool->sp_lock);
480177633Sdfr	/* now register the information with the local binder service */
481177633Sdfr	if (nconf) {
482177633Sdfr		bool_t dummy;
483177633Sdfr		struct netconfig tnc;
484184588Sdfr		struct netbuf nb;
485177633Sdfr		tnc = *nconf;
486184588Sdfr		nb.buf = &xprt->xp_ltaddr;
487184588Sdfr		nb.len = xprt->xp_ltaddr.ss_len;
488184588Sdfr		dummy = rpcb_set(prog, vers, &tnc, &nb);
489177633Sdfr		return (dummy);
490177633Sdfr	}
491177633Sdfr	return (TRUE);
492177633Sdfr}
493177633Sdfr
494177633Sdfr/*
495177633Sdfr * Remove a service program from the callout list.
496177633Sdfr */
497177633Sdfrvoid
498177633Sdfrsvc_unreg(SVCPOOL *pool, const rpcprog_t prog, const rpcvers_t vers)
499177633Sdfr{
500177633Sdfr	struct svc_callout *s;
501177633Sdfr
502177633Sdfr	/* unregister the information anyway */
503177633Sdfr	(void) rpcb_unset(prog, vers, NULL);
504177633Sdfr	mtx_lock(&pool->sp_lock);
505177633Sdfr	while ((s = svc_find(pool, prog, vers, NULL)) != NULL) {
506177633Sdfr		TAILQ_REMOVE(&pool->sp_callouts, s, sc_link);
507177633Sdfr		if (s->sc_netid)
508177633Sdfr			mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
509177633Sdfr		mem_free(s, sizeof (struct svc_callout));
510177633Sdfr	}
511177633Sdfr	mtx_unlock(&pool->sp_lock);
512177633Sdfr}
513177633Sdfr
514177633Sdfr/* ********************** CALLOUT list related stuff ************* */
515177633Sdfr
516177633Sdfr/*
517177633Sdfr * Search the callout list for a program number, return the callout
518177633Sdfr * struct.
519177633Sdfr */
520177633Sdfrstatic struct svc_callout *
521177633Sdfrsvc_find(SVCPOOL *pool, rpcprog_t prog, rpcvers_t vers, char *netid)
522177633Sdfr{
523177633Sdfr	struct svc_callout *s;
524177633Sdfr
525177633Sdfr	mtx_assert(&pool->sp_lock, MA_OWNED);
526177633Sdfr	TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
527177633Sdfr		if (s->sc_prog == prog && s->sc_vers == vers
528177633Sdfr		    && (netid == NULL || s->sc_netid == NULL ||
529177633Sdfr			strcmp(netid, s->sc_netid) == 0))
530177633Sdfr			break;
531177633Sdfr	}
532177633Sdfr
533177633Sdfr	return (s);
534177633Sdfr}
535177633Sdfr
536177633Sdfr/* ******************* REPLY GENERATION ROUTINES  ************ */
537177633Sdfr
538184588Sdfrstatic bool_t
539184588Sdfrsvc_sendreply_common(struct svc_req *rqstp, struct rpc_msg *rply,
540184588Sdfr    struct mbuf *body)
541184588Sdfr{
542184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
543184588Sdfr	bool_t ok;
544184588Sdfr
545184588Sdfr	if (rqstp->rq_args) {
546184588Sdfr		m_freem(rqstp->rq_args);
547184588Sdfr		rqstp->rq_args = NULL;
548184588Sdfr	}
549184588Sdfr
550184588Sdfr	if (xprt->xp_pool->sp_rcache)
551184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
552184588Sdfr		    rply, svc_getrpccaller(rqstp), body);
553184588Sdfr
554184588Sdfr	if (!SVCAUTH_WRAP(&rqstp->rq_auth, &body))
555184588Sdfr		return (FALSE);
556184588Sdfr
557184588Sdfr	ok = SVC_REPLY(xprt, rply, rqstp->rq_addr, body);
558184588Sdfr	if (rqstp->rq_addr) {
559184588Sdfr		free(rqstp->rq_addr, M_SONAME);
560184588Sdfr		rqstp->rq_addr = NULL;
561184588Sdfr	}
562184588Sdfr
563184588Sdfr	return (ok);
564184588Sdfr}
565184588Sdfr
566177633Sdfr/*
567177633Sdfr * Send a reply to an rpc request
568177633Sdfr */
569177633Sdfrbool_t
570184588Sdfrsvc_sendreply(struct svc_req *rqstp, xdrproc_t xdr_results, void * xdr_location)
571177633Sdfr{
572177633Sdfr	struct rpc_msg rply;
573184588Sdfr	struct mbuf *m;
574184588Sdfr	XDR xdrs;
575184588Sdfr	bool_t ok;
576177633Sdfr
577184588Sdfr	rply.rm_xid = rqstp->rq_xid;
578177633Sdfr	rply.rm_direction = REPLY;
579177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
580184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
581177633Sdfr	rply.acpted_rply.ar_stat = SUCCESS;
582184588Sdfr	rply.acpted_rply.ar_results.where = NULL;
583184588Sdfr	rply.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
584177633Sdfr
585248195Sglebius	m = m_getcl(M_WAITOK, MT_DATA, 0);
586184588Sdfr	xdrmbuf_create(&xdrs, m, XDR_ENCODE);
587184588Sdfr	ok = xdr_results(&xdrs, xdr_location);
588184588Sdfr	XDR_DESTROY(&xdrs);
589184588Sdfr
590184588Sdfr	if (ok) {
591184588Sdfr		return (svc_sendreply_common(rqstp, &rply, m));
592184588Sdfr	} else {
593184588Sdfr		m_freem(m);
594184588Sdfr		return (FALSE);
595184588Sdfr	}
596177633Sdfr}
597177633Sdfr
598184588Sdfrbool_t
599184588Sdfrsvc_sendreply_mbuf(struct svc_req *rqstp, struct mbuf *m)
600184588Sdfr{
601184588Sdfr	struct rpc_msg rply;
602184588Sdfr
603184588Sdfr	rply.rm_xid = rqstp->rq_xid;
604184588Sdfr	rply.rm_direction = REPLY;
605184588Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
606184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
607184588Sdfr	rply.acpted_rply.ar_stat = SUCCESS;
608184588Sdfr	rply.acpted_rply.ar_results.where = NULL;
609184588Sdfr	rply.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
610184588Sdfr
611184588Sdfr	return (svc_sendreply_common(rqstp, &rply, m));
612184588Sdfr}
613184588Sdfr
614177633Sdfr/*
615177633Sdfr * No procedure error reply
616177633Sdfr */
617177633Sdfrvoid
618184588Sdfrsvcerr_noproc(struct svc_req *rqstp)
619177633Sdfr{
620184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
621177633Sdfr	struct rpc_msg rply;
622177633Sdfr
623184588Sdfr	rply.rm_xid = rqstp->rq_xid;
624177633Sdfr	rply.rm_direction = REPLY;
625177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
626184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
627177633Sdfr	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
628177633Sdfr
629184588Sdfr	if (xprt->xp_pool->sp_rcache)
630184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
631184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
632184588Sdfr
633184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
634177633Sdfr}
635177633Sdfr
636177633Sdfr/*
637177633Sdfr * Can't decode args error reply
638177633Sdfr */
639177633Sdfrvoid
640184588Sdfrsvcerr_decode(struct svc_req *rqstp)
641177633Sdfr{
642184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
643177633Sdfr	struct rpc_msg rply;
644177633Sdfr
645184588Sdfr	rply.rm_xid = rqstp->rq_xid;
646177633Sdfr	rply.rm_direction = REPLY;
647177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
648184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
649177633Sdfr	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
650177633Sdfr
651184588Sdfr	if (xprt->xp_pool->sp_rcache)
652184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
653184588Sdfr		    &rply, (struct sockaddr *) &xprt->xp_rtaddr, NULL);
654184588Sdfr
655184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
656177633Sdfr}
657177633Sdfr
658177633Sdfr/*
659177633Sdfr * Some system error
660177633Sdfr */
661177633Sdfrvoid
662184588Sdfrsvcerr_systemerr(struct svc_req *rqstp)
663177633Sdfr{
664184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
665177633Sdfr	struct rpc_msg rply;
666177633Sdfr
667184588Sdfr	rply.rm_xid = rqstp->rq_xid;
668177633Sdfr	rply.rm_direction = REPLY;
669177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
670184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
671177633Sdfr	rply.acpted_rply.ar_stat = SYSTEM_ERR;
672177633Sdfr
673184588Sdfr	if (xprt->xp_pool->sp_rcache)
674184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
675184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
676184588Sdfr
677184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
678177633Sdfr}
679177633Sdfr
680177633Sdfr/*
681177633Sdfr * Authentication error reply
682177633Sdfr */
683177633Sdfrvoid
684184588Sdfrsvcerr_auth(struct svc_req *rqstp, enum auth_stat why)
685177633Sdfr{
686184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
687177633Sdfr	struct rpc_msg rply;
688177633Sdfr
689184588Sdfr	rply.rm_xid = rqstp->rq_xid;
690177633Sdfr	rply.rm_direction = REPLY;
691177633Sdfr	rply.rm_reply.rp_stat = MSG_DENIED;
692177633Sdfr	rply.rjcted_rply.rj_stat = AUTH_ERROR;
693177633Sdfr	rply.rjcted_rply.rj_why = why;
694177633Sdfr
695184588Sdfr	if (xprt->xp_pool->sp_rcache)
696184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
697184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
698184588Sdfr
699184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
700177633Sdfr}
701177633Sdfr
702177633Sdfr/*
703177633Sdfr * Auth too weak error reply
704177633Sdfr */
705177633Sdfrvoid
706184588Sdfrsvcerr_weakauth(struct svc_req *rqstp)
707177633Sdfr{
708177633Sdfr
709184588Sdfr	svcerr_auth(rqstp, AUTH_TOOWEAK);
710177633Sdfr}
711177633Sdfr
712177633Sdfr/*
713177633Sdfr * Program unavailable error reply
714177633Sdfr */
715177633Sdfrvoid
716184588Sdfrsvcerr_noprog(struct svc_req *rqstp)
717177633Sdfr{
718184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
719177633Sdfr	struct rpc_msg rply;
720177633Sdfr
721184588Sdfr	rply.rm_xid = rqstp->rq_xid;
722177633Sdfr	rply.rm_direction = REPLY;
723177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
724184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
725177633Sdfr	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
726177633Sdfr
727184588Sdfr	if (xprt->xp_pool->sp_rcache)
728184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
729184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
730184588Sdfr
731184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
732177633Sdfr}
733177633Sdfr
734177633Sdfr/*
735177633Sdfr * Program version mismatch error reply
736177633Sdfr */
737177633Sdfrvoid
738184588Sdfrsvcerr_progvers(struct svc_req *rqstp, rpcvers_t low_vers, rpcvers_t high_vers)
739177633Sdfr{
740184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
741177633Sdfr	struct rpc_msg rply;
742177633Sdfr
743184588Sdfr	rply.rm_xid = rqstp->rq_xid;
744177633Sdfr	rply.rm_direction = REPLY;
745177633Sdfr	rply.rm_reply.rp_stat = MSG_ACCEPTED;
746184588Sdfr	rply.acpted_rply.ar_verf = rqstp->rq_verf;
747177633Sdfr	rply.acpted_rply.ar_stat = PROG_MISMATCH;
748177633Sdfr	rply.acpted_rply.ar_vers.low = (uint32_t)low_vers;
749177633Sdfr	rply.acpted_rply.ar_vers.high = (uint32_t)high_vers;
750177633Sdfr
751184588Sdfr	if (xprt->xp_pool->sp_rcache)
752184588Sdfr		replay_setreply(xprt->xp_pool->sp_rcache,
753184588Sdfr		    &rply, svc_getrpccaller(rqstp), NULL);
754184588Sdfr
755184588Sdfr	svc_sendreply_common(rqstp, &rply, NULL);
756177633Sdfr}
757177633Sdfr
758184588Sdfr/*
759184588Sdfr * Allocate a new server transport structure. All fields are
760184588Sdfr * initialized to zero and xp_p3 is initialized to point at an
761184588Sdfr * extension structure to hold various flags and authentication
762184588Sdfr * parameters.
763184588Sdfr */
764184588SdfrSVCXPRT *
765184588Sdfrsvc_xprt_alloc()
766184588Sdfr{
767184588Sdfr	SVCXPRT *xprt;
768184588Sdfr	SVCXPRT_EXT *ext;
769184588Sdfr
770184588Sdfr	xprt = mem_alloc(sizeof(SVCXPRT));
771184588Sdfr	memset(xprt, 0, sizeof(SVCXPRT));
772184588Sdfr	ext = mem_alloc(sizeof(SVCXPRT_EXT));
773184588Sdfr	memset(ext, 0, sizeof(SVCXPRT_EXT));
774184588Sdfr	xprt->xp_p3 = ext;
775184588Sdfr	refcount_init(&xprt->xp_refs, 1);
776184588Sdfr
777184588Sdfr	return (xprt);
778184588Sdfr}
779184588Sdfr
780184588Sdfr/*
781184588Sdfr * Free a server transport structure.
782184588Sdfr */
783184588Sdfrvoid
784184588Sdfrsvc_xprt_free(xprt)
785184588Sdfr	SVCXPRT *xprt;
786184588Sdfr{
787184588Sdfr
788184588Sdfr	mem_free(xprt->xp_p3, sizeof(SVCXPRT_EXT));
789184588Sdfr	mem_free(xprt, sizeof(SVCXPRT));
790184588Sdfr}
791184588Sdfr
792177633Sdfr/* ******************* SERVER INPUT STUFF ******************* */
793177633Sdfr
794177633Sdfr/*
795184588Sdfr * Read RPC requests from a transport and queue them to be
796184588Sdfr * executed. We handle authentication and replay cache replies here.
797184588Sdfr * Actually dispatching the RPC is deferred till svc_executereq.
798177633Sdfr */
799184588Sdfrstatic enum xprt_stat
800184588Sdfrsvc_getreq(SVCXPRT *xprt, struct svc_req **rqstp_ret)
801177633Sdfr{
802177633Sdfr	SVCPOOL *pool = xprt->xp_pool;
803184588Sdfr	struct svc_req *r;
804177633Sdfr	struct rpc_msg msg;
805184588Sdfr	struct mbuf *args;
806177633Sdfr	enum xprt_stat stat;
807177633Sdfr
808177633Sdfr	/* now receive msgs from xprtprt (support batch calls) */
809184588Sdfr	r = malloc(sizeof(*r), M_RPC, M_WAITOK|M_ZERO);
810177633Sdfr
811184588Sdfr	msg.rm_call.cb_cred.oa_base = r->rq_credarea;
812184588Sdfr	msg.rm_call.cb_verf.oa_base = &r->rq_credarea[MAX_AUTH_BYTES];
813184588Sdfr	r->rq_clntcred = &r->rq_credarea[2*MAX_AUTH_BYTES];
814184588Sdfr	if (SVC_RECV(xprt, &msg, &r->rq_addr, &args)) {
815184588Sdfr		enum auth_stat why;
816177633Sdfr
817184588Sdfr		/*
818184588Sdfr		 * Handle replays and authenticate before queuing the
819184588Sdfr		 * request to be executed.
820184588Sdfr		 */
821184588Sdfr		SVC_ACQUIRE(xprt);
822184588Sdfr		r->rq_xprt = xprt;
823184588Sdfr		if (pool->sp_rcache) {
824184588Sdfr			struct rpc_msg repmsg;
825184588Sdfr			struct mbuf *repbody;
826184588Sdfr			enum replay_state rs;
827184588Sdfr			rs = replay_find(pool->sp_rcache, &msg,
828184588Sdfr			    svc_getrpccaller(r), &repmsg, &repbody);
829184588Sdfr			switch (rs) {
830184588Sdfr			case RS_NEW:
831184588Sdfr				break;
832184588Sdfr			case RS_DONE:
833184588Sdfr				SVC_REPLY(xprt, &repmsg, r->rq_addr,
834184588Sdfr				    repbody);
835184588Sdfr				if (r->rq_addr) {
836184588Sdfr					free(r->rq_addr, M_SONAME);
837184588Sdfr					r->rq_addr = NULL;
838184588Sdfr				}
839205562Srmacklem				m_freem(args);
840177633Sdfr				goto call_done;
841184588Sdfr
842184588Sdfr			default:
843205562Srmacklem				m_freem(args);
844184588Sdfr				goto call_done;
845177633Sdfr			}
846184588Sdfr		}
847184588Sdfr
848184588Sdfr		r->rq_xid = msg.rm_xid;
849184588Sdfr		r->rq_prog = msg.rm_call.cb_prog;
850184588Sdfr		r->rq_vers = msg.rm_call.cb_vers;
851184588Sdfr		r->rq_proc = msg.rm_call.cb_proc;
852184588Sdfr		r->rq_size = sizeof(*r) + m_length(args, NULL);
853184588Sdfr		r->rq_args = args;
854184588Sdfr		if ((why = _authenticate(r, &msg)) != AUTH_OK) {
855177633Sdfr			/*
856184588Sdfr			 * RPCSEC_GSS uses this return code
857184588Sdfr			 * for requests that form part of its
858184588Sdfr			 * context establishment protocol and
859184588Sdfr			 * should not be dispatched to the
860184588Sdfr			 * application.
861177633Sdfr			 */
862184588Sdfr			if (why != RPCSEC_GSS_NODISPATCH)
863184588Sdfr				svcerr_auth(r, why);
864184588Sdfr			goto call_done;
865177633Sdfr		}
866184588Sdfr
867184588Sdfr		if (!SVCAUTH_UNWRAP(&r->rq_auth, &r->rq_args)) {
868184588Sdfr			svcerr_decode(r);
869184588Sdfr			goto call_done;
870184588Sdfr		}
871184588Sdfr
872177633Sdfr		/*
873184588Sdfr		 * Everything checks out, return request to caller.
874177633Sdfr		 */
875184588Sdfr		*rqstp_ret = r;
876184588Sdfr		r = NULL;
877184588Sdfr	}
878177633Sdfrcall_done:
879184588Sdfr	if (r) {
880184588Sdfr		svc_freereq(r);
881184588Sdfr		r = NULL;
882184588Sdfr	}
883184588Sdfr	if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
884184588Sdfr		xprt_unregister(xprt);
885184588Sdfr	}
886184588Sdfr
887184588Sdfr	return (stat);
888184588Sdfr}
889184588Sdfr
890184588Sdfrstatic void
891184588Sdfrsvc_executereq(struct svc_req *rqstp)
892184588Sdfr{
893184588Sdfr	SVCXPRT *xprt = rqstp->rq_xprt;
894184588Sdfr	SVCPOOL *pool = xprt->xp_pool;
895184588Sdfr	int prog_found;
896184588Sdfr	rpcvers_t low_vers;
897184588Sdfr	rpcvers_t high_vers;
898184588Sdfr	struct svc_callout *s;
899184588Sdfr
900184588Sdfr	/* now match message with a registered service*/
901184588Sdfr	prog_found = FALSE;
902184588Sdfr	low_vers = (rpcvers_t) -1L;
903184588Sdfr	high_vers = (rpcvers_t) 0L;
904184588Sdfr	TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
905184588Sdfr		if (s->sc_prog == rqstp->rq_prog) {
906184588Sdfr			if (s->sc_vers == rqstp->rq_vers) {
907184588Sdfr				/*
908184588Sdfr				 * We hand ownership of r to the
909184588Sdfr				 * dispatch method - they must call
910184588Sdfr				 * svc_freereq.
911184588Sdfr				 */
912184588Sdfr				(*s->sc_dispatch)(rqstp, xprt);
913184588Sdfr				return;
914184588Sdfr			}  /* found correct version */
915184588Sdfr			prog_found = TRUE;
916184588Sdfr			if (s->sc_vers < low_vers)
917184588Sdfr				low_vers = s->sc_vers;
918184588Sdfr			if (s->sc_vers > high_vers)
919184588Sdfr				high_vers = s->sc_vers;
920184588Sdfr		}   /* found correct program */
921184588Sdfr	}
922184588Sdfr
923184588Sdfr	/*
924184588Sdfr	 * if we got here, the program or version
925184588Sdfr	 * is not served ...
926184588Sdfr	 */
927184588Sdfr	if (prog_found)
928184588Sdfr		svcerr_progvers(rqstp, low_vers, high_vers);
929184588Sdfr	else
930184588Sdfr		svcerr_noprog(rqstp);
931184588Sdfr
932184588Sdfr	svc_freereq(rqstp);
933184588Sdfr}
934184588Sdfr
935184588Sdfrstatic void
936184588Sdfrsvc_checkidle(SVCPOOL *pool)
937184588Sdfr{
938184588Sdfr	SVCXPRT *xprt, *nxprt;
939184588Sdfr	time_t timo;
940184588Sdfr	struct svcxprt_list cleanup;
941184588Sdfr
942184588Sdfr	TAILQ_INIT(&cleanup);
943184588Sdfr	TAILQ_FOREACH_SAFE(xprt, &pool->sp_xlist, xp_link, nxprt) {
944184588Sdfr		/*
945184588Sdfr		 * Only some transports have idle timers. Don't time
946184588Sdfr		 * something out which is just waking up.
947184588Sdfr		 */
948184588Sdfr		if (!xprt->xp_idletimeout || xprt->xp_thread)
949184588Sdfr			continue;
950184588Sdfr
951184588Sdfr		timo = xprt->xp_lastactive + xprt->xp_idletimeout;
952184588Sdfr		if (time_uptime > timo) {
953184588Sdfr			xprt_unregister_locked(xprt);
954184588Sdfr			TAILQ_INSERT_TAIL(&cleanup, xprt, xp_link);
955177633Sdfr		}
956184588Sdfr	}
957184588Sdfr
958184588Sdfr	mtx_unlock(&pool->sp_lock);
959184588Sdfr	TAILQ_FOREACH_SAFE(xprt, &cleanup, xp_link, nxprt) {
960184588Sdfr		SVC_RELEASE(xprt);
961184588Sdfr	}
962184588Sdfr	mtx_lock(&pool->sp_lock);
963184588Sdfr
964177633Sdfr}
965177633Sdfr
966184588Sdfrstatic void
967184588Sdfrsvc_assign_waiting_sockets(SVCPOOL *pool)
968177633Sdfr{
969177633Sdfr	SVCXPRT *xprt;
970184588Sdfr
971261054Smav	mtx_lock(&pool->sp_lock);
972261048Smav	while ((xprt = TAILQ_FIRST(&pool->sp_active)) != NULL) {
973261048Smav		if (xprt_assignthread(xprt))
974261048Smav			TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
975261048Smav		else
976261048Smav			break;
977184588Sdfr	}
978261054Smav	mtx_unlock(&pool->sp_lock);
979184588Sdfr}
980184588Sdfr
981261054Smavstatic void
982261054Smavsvc_change_space_used(SVCPOOL *pool, int delta)
983184588Sdfr{
984261054Smav	unsigned int value;
985184588Sdfr
986261054Smav	value = atomic_fetchadd_int(&pool->sp_space_used, delta) + delta;
987261054Smav	if (delta > 0) {
988261054Smav		if (value >= pool->sp_space_high && !pool->sp_space_throttled) {
989261054Smav			pool->sp_space_throttled = TRUE;
990261054Smav			pool->sp_space_throttle_count++;
991261054Smav		}
992261054Smav		if (value > pool->sp_space_used_highest)
993261054Smav			pool->sp_space_used_highest = value;
994261054Smav	} else {
995261054Smav		if (value < pool->sp_space_low && pool->sp_space_throttled) {
996184588Sdfr			pool->sp_space_throttled = FALSE;
997184588Sdfr			svc_assign_waiting_sockets(pool);
998184588Sdfr		}
999184588Sdfr	}
1000184588Sdfr}
1001184588Sdfr
1002261054Smavstatic bool_t
1003261054Smavsvc_request_space_available(SVCPOOL *pool)
1004261054Smav{
1005261054Smav
1006261054Smav	if (pool->sp_space_throttled)
1007261054Smav		return (FALSE);
1008261054Smav	return (TRUE);
1009261054Smav}
1010261054Smav
1011184588Sdfrstatic void
1012184588Sdfrsvc_run_internal(SVCPOOL *pool, bool_t ismaster)
1013184588Sdfr{
1014261054Smav	struct svc_reqlist reqs;
1015184588Sdfr	SVCTHREAD *st, *stpref;
1016184588Sdfr	SVCXPRT *xprt;
1017184588Sdfr	enum xprt_stat stat;
1018184588Sdfr	struct svc_req *rqstp;
1019261054Smav	size_t sz;
1020177633Sdfr	int error;
1021177633Sdfr
1022184588Sdfr	st = mem_alloc(sizeof(*st));
1023261054Smav	st->st_pool = pool;
1024184588Sdfr	st->st_xprt = NULL;
1025184588Sdfr	STAILQ_INIT(&st->st_reqs);
1026184588Sdfr	cv_init(&st->st_cond, "rpcsvc");
1027261054Smav	STAILQ_INIT(&reqs);
1028184588Sdfr
1029177633Sdfr	mtx_lock(&pool->sp_lock);
1030184588Sdfr	LIST_INSERT_HEAD(&pool->sp_threads, st, st_link);
1031177633Sdfr
1032184588Sdfr	/*
1033184588Sdfr	 * If we are a new thread which was spawned to cope with
1034184588Sdfr	 * increased load, set the state back to SVCPOOL_ACTIVE.
1035184588Sdfr	 */
1036184588Sdfr	if (pool->sp_state == SVCPOOL_THREADSTARTING)
1037184588Sdfr		pool->sp_state = SVCPOOL_ACTIVE;
1038177633Sdfr
1039184588Sdfr	while (pool->sp_state != SVCPOOL_CLOSING) {
1040184588Sdfr		/*
1041261045Smav		 * Create new thread if requested.
1042261045Smav		 */
1043261045Smav		if (pool->sp_state == SVCPOOL_THREADWANTED) {
1044261045Smav			pool->sp_state = SVCPOOL_THREADSTARTING;
1045261045Smav			pool->sp_lastcreatetime = time_uptime;
1046261045Smav			mtx_unlock(&pool->sp_lock);
1047261045Smav			svc_new_thread(pool);
1048261045Smav			mtx_lock(&pool->sp_lock);
1049261045Smav			continue;
1050261045Smav		}
1051261045Smav
1052261045Smav		/*
1053184588Sdfr		 * Check for idle transports once per second.
1054184588Sdfr		 */
1055184588Sdfr		if (time_uptime > pool->sp_lastidlecheck) {
1056184588Sdfr			pool->sp_lastidlecheck = time_uptime;
1057184588Sdfr			svc_checkidle(pool);
1058184588Sdfr		}
1059184588Sdfr
1060184588Sdfr		xprt = st->st_xprt;
1061184588Sdfr		if (!xprt && STAILQ_EMPTY(&st->st_reqs)) {
1062184588Sdfr			/*
1063184588Sdfr			 * Enforce maxthreads count.
1064184588Sdfr			 */
1065184588Sdfr			if (pool->sp_threadcount > pool->sp_maxthreads)
1066177633Sdfr				break;
1067184588Sdfr
1068184588Sdfr			/*
1069184588Sdfr			 * Before sleeping, see if we can find an
1070184588Sdfr			 * active transport which isn't being serviced
1071184588Sdfr			 * by a thread.
1072184588Sdfr			 */
1073261048Smav			if (svc_request_space_available(pool) &&
1074261048Smav			    (xprt = TAILQ_FIRST(&pool->sp_active)) != NULL) {
1075261048Smav				TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
1076261048Smav				SVC_ACQUIRE(xprt);
1077261048Smav				xprt->xp_thread = st;
1078261048Smav				st->st_xprt = xprt;
1079261048Smav				continue;
1080184588Sdfr			}
1081184588Sdfr
1082184588Sdfr			LIST_INSERT_HEAD(&pool->sp_idlethreads, st, st_ilink);
1083261048Smav			st->st_idle = TRUE;
1084261045Smav			if (ismaster || (!ismaster &&
1085261045Smav			    pool->sp_threadcount > pool->sp_minthreads))
1086261045Smav				error = cv_timedwait_sig(&st->st_cond,
1087261045Smav				    &pool->sp_lock, 5 * hz);
1088261045Smav			else
1089261045Smav				error = cv_wait_sig(&st->st_cond,
1090261045Smav				    &pool->sp_lock);
1091261048Smav			if (st->st_idle) {
1092261048Smav				LIST_REMOVE(st, st_ilink);
1093261048Smav				st->st_idle = FALSE;
1094261048Smav			}
1095184588Sdfr
1096184588Sdfr			/*
1097184588Sdfr			 * Reduce worker thread count when idle.
1098184588Sdfr			 */
1099184588Sdfr			if (error == EWOULDBLOCK) {
1100184588Sdfr				if (!ismaster
1101184588Sdfr				    && (pool->sp_threadcount
1102184588Sdfr					> pool->sp_minthreads)
1103184588Sdfr					&& !st->st_xprt
1104184588Sdfr					&& STAILQ_EMPTY(&st->st_reqs))
1105184588Sdfr					break;
1106261045Smav			} else if (error) {
1107184588Sdfr				mtx_unlock(&pool->sp_lock);
1108261045Smav				svc_exit(pool);
1109184588Sdfr				mtx_lock(&pool->sp_lock);
1110261045Smav				break;
1111184588Sdfr			}
1112177633Sdfr			continue;
1113177633Sdfr		}
1114177633Sdfr
1115184588Sdfr		if (xprt) {
1116184588Sdfr			/*
1117184588Sdfr			 * Drain the transport socket and queue up any
1118184588Sdfr			 * RPCs.
1119184588Sdfr			 */
1120184588Sdfr			xprt->xp_lastactive = time_uptime;
1121184588Sdfr			do {
1122261054Smav				mtx_unlock(&pool->sp_lock);
1123184588Sdfr				if (!svc_request_space_available(pool))
1124184588Sdfr					break;
1125184588Sdfr				rqstp = NULL;
1126184588Sdfr				stat = svc_getreq(xprt, &rqstp);
1127184588Sdfr				if (rqstp) {
1128261054Smav					svc_change_space_used(pool, rqstp->rq_size);
1129184588Sdfr					/*
1130184588Sdfr					 * See if the application has
1131184588Sdfr					 * a preference for some other
1132184588Sdfr					 * thread.
1133184588Sdfr					 */
1134184588Sdfr					stpref = st;
1135184588Sdfr					if (pool->sp_assign)
1136184588Sdfr						stpref = pool->sp_assign(st,
1137184588Sdfr						    rqstp);
1138261054Smav					else
1139261054Smav						mtx_lock(&pool->sp_lock);
1140184588Sdfr
1141184588Sdfr					rqstp->rq_thread = stpref;
1142184588Sdfr					STAILQ_INSERT_TAIL(&stpref->st_reqs,
1143184588Sdfr					    rqstp, rq_link);
1144184588Sdfr
1145184588Sdfr					/*
1146184588Sdfr					 * If we assigned the request
1147184588Sdfr					 * to another thread, make
1148184588Sdfr					 * sure its awake and continue
1149184588Sdfr					 * reading from the
1150184588Sdfr					 * socket. Otherwise, try to
1151184588Sdfr					 * find some other thread to
1152184588Sdfr					 * read from the socket and
1153184588Sdfr					 * execute the request
1154184588Sdfr					 * immediately.
1155184588Sdfr					 */
1156261048Smav					if (stpref == st)
1157261048Smav						break;
1158261048Smav					if (stpref->st_idle) {
1159261048Smav						LIST_REMOVE(stpref, st_ilink);
1160261048Smav						stpref->st_idle = FALSE;
1161184588Sdfr						cv_signal(&stpref->st_cond);
1162184588Sdfr					}
1163261054Smav				} else
1164261054Smav					mtx_lock(&pool->sp_lock);
1165184588Sdfr			} while (stat == XPRT_MOREREQS
1166184588Sdfr			    && pool->sp_state != SVCPOOL_CLOSING);
1167184588Sdfr
1168184588Sdfr			/*
1169184588Sdfr			 * Move this transport to the end of the
1170184588Sdfr			 * active list to ensure fairness when
1171184588Sdfr			 * multiple transports are active. If this was
1172184588Sdfr			 * the last queued request, svc_getreq will
1173184588Sdfr			 * end up calling xprt_inactive to remove from
1174184588Sdfr			 * the active list.
1175184588Sdfr			 */
1176184588Sdfr			xprt->xp_thread = NULL;
1177184588Sdfr			st->st_xprt = NULL;
1178184588Sdfr			if (xprt->xp_active) {
1179261054Smav				if (!svc_request_space_available(pool) ||
1180261054Smav				    !xprt_assignthread(xprt))
1181261048Smav					TAILQ_INSERT_TAIL(&pool->sp_active,
1182261048Smav					    xprt, xp_alink);
1183184588Sdfr			}
1184261054Smav			STAILQ_CONCAT(&reqs, &st->st_reqs);
1185184588Sdfr			mtx_unlock(&pool->sp_lock);
1186184588Sdfr			SVC_RELEASE(xprt);
1187261054Smav		} else {
1188261054Smav			STAILQ_CONCAT(&reqs, &st->st_reqs);
1189261054Smav			mtx_unlock(&pool->sp_lock);
1190184588Sdfr		}
1191184588Sdfr
1192177633Sdfr		/*
1193184588Sdfr		 * Execute what we have queued.
1194177633Sdfr		 */
1195261054Smav		sz = 0;
1196261054Smav		while ((rqstp = STAILQ_FIRST(&reqs)) != NULL) {
1197261054Smav			STAILQ_REMOVE_HEAD(&reqs, rq_link);
1198261054Smav			sz += rqstp->rq_size;
1199184588Sdfr			svc_executereq(rqstp);
1200184588Sdfr		}
1201261054Smav		svc_change_space_used(pool, -sz);
1202261054Smav		mtx_lock(&pool->sp_lock);
1203184588Sdfr	}
1204177633Sdfr
1205184588Sdfr	if (st->st_xprt) {
1206184588Sdfr		xprt = st->st_xprt;
1207184588Sdfr		st->st_xprt = NULL;
1208184588Sdfr		SVC_RELEASE(xprt);
1209177633Sdfr	}
1210177633Sdfr
1211184588Sdfr	KASSERT(STAILQ_EMPTY(&st->st_reqs), ("stray reqs on exit"));
1212184588Sdfr	LIST_REMOVE(st, st_link);
1213184588Sdfr	pool->sp_threadcount--;
1214184588Sdfr
1215177633Sdfr	mtx_unlock(&pool->sp_lock);
1216184588Sdfr
1217184588Sdfr	cv_destroy(&st->st_cond);
1218184588Sdfr	mem_free(st, sizeof(*st));
1219184588Sdfr
1220184588Sdfr	if (!ismaster)
1221184588Sdfr		wakeup(pool);
1222177633Sdfr}
1223177633Sdfr
1224184588Sdfrstatic void
1225184588Sdfrsvc_thread_start(void *arg)
1226184588Sdfr{
1227184588Sdfr
1228184588Sdfr	svc_run_internal((SVCPOOL *) arg, FALSE);
1229184588Sdfr	kthread_exit();
1230184588Sdfr}
1231184588Sdfr
1232184588Sdfrstatic void
1233184588Sdfrsvc_new_thread(SVCPOOL *pool)
1234184588Sdfr{
1235184588Sdfr	struct thread *td;
1236184588Sdfr
1237184588Sdfr	pool->sp_threadcount++;
1238184588Sdfr	kthread_add(svc_thread_start, pool,
1239184588Sdfr	    pool->sp_proc, &td, 0, 0,
1240184588Sdfr	    "%s: service", pool->sp_name);
1241184588Sdfr}
1242184588Sdfr
1243177633Sdfrvoid
1244184588Sdfrsvc_run(SVCPOOL *pool)
1245184588Sdfr{
1246184588Sdfr	int i;
1247184588Sdfr	struct proc *p;
1248184588Sdfr	struct thread *td;
1249184588Sdfr
1250184588Sdfr	p = curproc;
1251184588Sdfr	td = curthread;
1252184588Sdfr	snprintf(td->td_name, sizeof(td->td_name),
1253184588Sdfr	    "%s: master", pool->sp_name);
1254184588Sdfr	pool->sp_state = SVCPOOL_ACTIVE;
1255184588Sdfr	pool->sp_proc = p;
1256184588Sdfr	pool->sp_lastcreatetime = time_uptime;
1257184588Sdfr	pool->sp_threadcount = 1;
1258184588Sdfr
1259184588Sdfr	for (i = 1; i < pool->sp_minthreads; i++) {
1260184588Sdfr		svc_new_thread(pool);
1261184588Sdfr	}
1262184588Sdfr
1263184588Sdfr	svc_run_internal(pool, TRUE);
1264184588Sdfr
1265184588Sdfr	mtx_lock(&pool->sp_lock);
1266184588Sdfr	while (pool->sp_threadcount > 0)
1267184588Sdfr		msleep(pool, &pool->sp_lock, 0, "svcexit", 0);
1268184588Sdfr	mtx_unlock(&pool->sp_lock);
1269184588Sdfr}
1270184588Sdfr
1271184588Sdfrvoid
1272177633Sdfrsvc_exit(SVCPOOL *pool)
1273177633Sdfr{
1274184588Sdfr	SVCTHREAD *st;
1275184588Sdfr
1276177633Sdfr	mtx_lock(&pool->sp_lock);
1277184588Sdfr
1278261045Smav	if (pool->sp_state != SVCPOOL_CLOSING) {
1279261045Smav		pool->sp_state = SVCPOOL_CLOSING;
1280261045Smav		LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink)
1281261045Smav			cv_signal(&st->st_cond);
1282261045Smav	}
1283184588Sdfr
1284177633Sdfr	mtx_unlock(&pool->sp_lock);
1285177633Sdfr}
1286184588Sdfr
1287184588Sdfrbool_t
1288184588Sdfrsvc_getargs(struct svc_req *rqstp, xdrproc_t xargs, void *args)
1289184588Sdfr{
1290184588Sdfr	struct mbuf *m;
1291184588Sdfr	XDR xdrs;
1292184588Sdfr	bool_t stat;
1293184588Sdfr
1294184588Sdfr	m = rqstp->rq_args;
1295184588Sdfr	rqstp->rq_args = NULL;
1296184588Sdfr
1297184588Sdfr	xdrmbuf_create(&xdrs, m, XDR_DECODE);
1298184588Sdfr	stat = xargs(&xdrs, args);
1299184588Sdfr	XDR_DESTROY(&xdrs);
1300184588Sdfr
1301184588Sdfr	return (stat);
1302184588Sdfr}
1303184588Sdfr
1304184588Sdfrbool_t
1305184588Sdfrsvc_freeargs(struct svc_req *rqstp, xdrproc_t xargs, void *args)
1306184588Sdfr{
1307184588Sdfr	XDR xdrs;
1308184588Sdfr
1309184588Sdfr	if (rqstp->rq_addr) {
1310184588Sdfr		free(rqstp->rq_addr, M_SONAME);
1311184588Sdfr		rqstp->rq_addr = NULL;
1312184588Sdfr	}
1313184588Sdfr
1314184588Sdfr	xdrs.x_op = XDR_FREE;
1315184588Sdfr	return (xargs(&xdrs, args));
1316184588Sdfr}
1317184588Sdfr
1318184588Sdfrvoid
1319184588Sdfrsvc_freereq(struct svc_req *rqstp)
1320184588Sdfr{
1321184588Sdfr	SVCTHREAD *st;
1322184588Sdfr	SVCPOOL *pool;
1323184588Sdfr
1324184588Sdfr	st = rqstp->rq_thread;
1325184588Sdfr	if (st) {
1326261054Smav		pool = st->st_pool;
1327184588Sdfr		if (pool->sp_done)
1328184588Sdfr			pool->sp_done(st, rqstp);
1329184588Sdfr	}
1330184588Sdfr
1331184588Sdfr	if (rqstp->rq_auth.svc_ah_ops)
1332184588Sdfr		SVCAUTH_RELEASE(&rqstp->rq_auth);
1333184588Sdfr
1334184588Sdfr	if (rqstp->rq_xprt) {
1335184588Sdfr		SVC_RELEASE(rqstp->rq_xprt);
1336184588Sdfr	}
1337184588Sdfr
1338184588Sdfr	if (rqstp->rq_addr)
1339184588Sdfr		free(rqstp->rq_addr, M_SONAME);
1340184588Sdfr
1341184588Sdfr	if (rqstp->rq_args)
1342184588Sdfr		m_freem(rqstp->rq_args);
1343184588Sdfr
1344184588Sdfr	free(rqstp, M_RPC);
1345184588Sdfr}
1346