xref: /onnv-gate/usr/src/uts/common/rpc/svc.c (revision 390)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
22*390Sraf 
230Sstevel@tonic-gate /*
24*390Sraf  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985,  1986, 1987, 1988, 1989 AT&T	*/
330Sstevel@tonic-gate /*	  All Rights Reserved  	*/
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /*
360Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
370Sstevel@tonic-gate  * under license from the Regents of the University of California.
380Sstevel@tonic-gate  */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * Server-side remote procedure call interface.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * Master transport handle (SVCMASTERXPRT).
460Sstevel@tonic-gate  *   The master transport handle structure is shared among service
470Sstevel@tonic-gate  *   threads processing events on the transport. Some fields in the
480Sstevel@tonic-gate  *   master structure are protected by locks
490Sstevel@tonic-gate  *   - xp_req_lock protects the request queue:
500Sstevel@tonic-gate  *	xp_req_head, xp_req_tail
510Sstevel@tonic-gate  *   - xp_thread_lock protects the thread (clone) counts
520Sstevel@tonic-gate  *	xp_threads, xp_detached_threads, xp_wq
530Sstevel@tonic-gate  *   Each master transport is registered to exactly one thread pool.
540Sstevel@tonic-gate  *
550Sstevel@tonic-gate  * Clone transport handle (SVCXPRT)
560Sstevel@tonic-gate  *   The clone transport handle structure is a per-service-thread handle
570Sstevel@tonic-gate  *   to the transport. The structure carries all the fields/buffers used
580Sstevel@tonic-gate  *   for request processing. A service thread or, in other words, a clone
590Sstevel@tonic-gate  *   structure, can be linked to an arbitrary master structure to process
600Sstevel@tonic-gate  *   requests on this transport. The master handle keeps track of reference
610Sstevel@tonic-gate  *   counts of threads (clones) linked to it. A service thread can switch
620Sstevel@tonic-gate  *   to another transport by unlinking its clone handle from the current
630Sstevel@tonic-gate  *   transport and linking to a new one. Switching is relatively inexpensive
640Sstevel@tonic-gate  *   but it involves locking (master's xprt->xp_thread_lock).
650Sstevel@tonic-gate  *
660Sstevel@tonic-gate  * Pools.
670Sstevel@tonic-gate  *   A pool represents a kernel RPC service (NFS, Lock Manager, etc.).
680Sstevel@tonic-gate  *   Transports related to the service are registered to the service pool.
690Sstevel@tonic-gate  *   Service threads can switch between different transports in the pool.
700Sstevel@tonic-gate  *   Thus, each service has its own pool of service threads. The maximum
710Sstevel@tonic-gate  *   number of threads in a pool is pool->p_maxthreads. This limit allows
720Sstevel@tonic-gate  *   to restrict resource usage by the service. Some fields are protected
730Sstevel@tonic-gate  *   by locks:
740Sstevel@tonic-gate  *   - p_req_lock protects several counts and flags:
750Sstevel@tonic-gate  *	p_reqs, p_walkers, p_asleep, p_drowsy, p_req_cv
760Sstevel@tonic-gate  *   - p_thread_lock governs other thread counts:
770Sstevel@tonic-gate  *	p_threads, p_detached_threads, p_reserved_threads, p_closing
780Sstevel@tonic-gate  *
790Sstevel@tonic-gate  *   In addition, each pool contains a doubly-linked list of transports,
800Sstevel@tonic-gate  *   an `xprt-ready' queue and a creator thread (see below). Threads in
810Sstevel@tonic-gate  *   the pool share some other parameters such as stack size and
820Sstevel@tonic-gate  *   polling timeout.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  *   Pools are initialized through the svc_pool_create() function called from
850Sstevel@tonic-gate  *   the nfssys() system call. However, thread creation must be done by
860Sstevel@tonic-gate  *   the userland agent. This is done by using SVCPOOL_WAIT and
870Sstevel@tonic-gate  *   SVCPOOL_RUN arguments to nfssys(), which call svc_wait() and
880Sstevel@tonic-gate  *   svc_do_run(), respectively. Once the pool has been initialized,
890Sstevel@tonic-gate  *   the userland process must set up a 'creator' thread. This thread
900Sstevel@tonic-gate  *   should park itself in the kernel by calling svc_wait(). If
910Sstevel@tonic-gate  *   svc_wait() returns successfully, it should fork off a new worker
920Sstevel@tonic-gate  *   thread, which then calls svc_do_run() in order to get work. When
930Sstevel@tonic-gate  *   that thread is complete, svc_do_run() will return, and the user
940Sstevel@tonic-gate  *   program should call thr_exit().
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  *   When we try to register a new pool and there is an old pool with
970Sstevel@tonic-gate  *   the same id in the doubly linked pool list (this happens when we kill
980Sstevel@tonic-gate  *   and restart nfsd or lockd), then we unlink the old pool from the list
990Sstevel@tonic-gate  *   and mark its state as `closing'. After that the transports can still
1000Sstevel@tonic-gate  *   process requests but new transports won't be registered. When all the
1010Sstevel@tonic-gate  *   transports and service threads associated with the pool are gone the
1020Sstevel@tonic-gate  *   creator thread (see below) will clean up the pool structure and exit.
1030Sstevel@tonic-gate  *
1040Sstevel@tonic-gate  * svc_queuereq() and svc_run().
1050Sstevel@tonic-gate  *   The kernel RPC server is interrupt driven. The svc_queuereq() interrupt
1060Sstevel@tonic-gate  *   routine is called to deliver an RPC request. The service threads
1070Sstevel@tonic-gate  *   loop in svc_run(). The interrupt function queues a request on the
1080Sstevel@tonic-gate  *   transport's queue and it makes sure that the request is serviced.
1090Sstevel@tonic-gate  *   It may either wake up one of sleeping threads, or ask for a new thread
1100Sstevel@tonic-gate  *   to be created, or, if the previous request is just being picked up, do
1110Sstevel@tonic-gate  *   nothing. In the last case the service thread that is picking up the
1120Sstevel@tonic-gate  *   previous request will wake up or create the next thread. After a service
1130Sstevel@tonic-gate  *   thread processes a request and sends a reply it returns to svc_run()
1140Sstevel@tonic-gate  *   and svc_run() calls svc_poll() to find new input.
1150Sstevel@tonic-gate  *
1160Sstevel@tonic-gate  *   There is an "inconsistent" but "safe" optimization in the
1170Sstevel@tonic-gate  *   svc_queuereq() code. The request is queued under the transport's
1180Sstevel@tonic-gate  *   request lock, while the `pending-requests' count is incremented
1190Sstevel@tonic-gate  *   independently under the pool request lock. Thus, a request can be picked
1200Sstevel@tonic-gate  *   up by a service thread before the counter is incremented. It may also
1210Sstevel@tonic-gate  *   happen that the service thread will win the race condition on the pool
1220Sstevel@tonic-gate  *   lock and it will decrement the count even before the interrupt thread
1230Sstevel@tonic-gate  *   increments it (so the count can be temporarily negative).
1240Sstevel@tonic-gate  *
1250Sstevel@tonic-gate  * svc_poll().
1260Sstevel@tonic-gate  *   In order to avoid unnecessary locking, which causes performance
1270Sstevel@tonic-gate  *   problems, we always look for a pending request on the current transport.
1280Sstevel@tonic-gate  *   If there is none we take a hint from the pool's `xprt-ready' queue.
1290Sstevel@tonic-gate  *   If the queue had an overflow we switch to the `drain' mode checking
1300Sstevel@tonic-gate  *   each transport  in the pool's transport list. Once we find a
1310Sstevel@tonic-gate  *   master transport handle with a pending request we latch the request
1320Sstevel@tonic-gate  *   lock on this transport and return to svc_run(). If the request
1330Sstevel@tonic-gate  *   belongs to a transport different than the one the service thread is
1340Sstevel@tonic-gate  *   linked to we need to unlink and link again.
1350Sstevel@tonic-gate  *
1360Sstevel@tonic-gate  *   A service thread goes asleep when there are no pending
1370Sstevel@tonic-gate  *   requests on the transports registered on the pool's transports.
1380Sstevel@tonic-gate  *   All the pool's threads sleep on the same condition variable.
1390Sstevel@tonic-gate  *   If a thread has been sleeping for too long period of time
1400Sstevel@tonic-gate  *   (by default 5 seconds) it wakes up and exits.  Also when a transport
1410Sstevel@tonic-gate  *   is closing sleeping threads wake up to unlink from this transport.
1420Sstevel@tonic-gate  *
1430Sstevel@tonic-gate  * The `xprt-ready' queue.
1440Sstevel@tonic-gate  *   If a service thread finds no request on a transport it is currently linked
1450Sstevel@tonic-gate  *   to it will find another transport with a pending request. To make
1460Sstevel@tonic-gate  *   this search more efficient each pool has an `xprt-ready' queue.
1470Sstevel@tonic-gate  *   The queue is a FIFO. When the interrupt routine queues a request it also
1480Sstevel@tonic-gate  *   inserts a pointer to the transport into the `xprt-ready' queue. A
1490Sstevel@tonic-gate  *   thread looking for a transport with a pending request can pop up a
1500Sstevel@tonic-gate  *   transport and check for a request. The request can be already gone
1510Sstevel@tonic-gate  *   since it could be taken by a thread linked to that transport. In such a
1520Sstevel@tonic-gate  *   case we try the next hint. The `xprt-ready' queue has fixed size (by
1530Sstevel@tonic-gate  *   default 256 nodes). If it overflows svc_poll() has to switch to the
1540Sstevel@tonic-gate  *   less efficient but safe `drain' mode and walk through the pool's
1550Sstevel@tonic-gate  *   transport list.
1560Sstevel@tonic-gate  *
1570Sstevel@tonic-gate  *   Both the svc_poll() loop and the `xprt-ready' queue are optimized
1580Sstevel@tonic-gate  *   for the peak load case that is for the situation when the queue is not
1590Sstevel@tonic-gate  *   empty, there are all the time few pending requests, and a service
1600Sstevel@tonic-gate  *   thread which has just processed a request does not go asleep but picks
1610Sstevel@tonic-gate  *   up immediately the next request.
1620Sstevel@tonic-gate  *
1630Sstevel@tonic-gate  * Thread creator.
1640Sstevel@tonic-gate  *   Each pool has a thread creator associated with it. The creator thread
1650Sstevel@tonic-gate  *   sleeps on a condition variable and waits for a signal to create a
1660Sstevel@tonic-gate  *   service thread. The actual thread creation is done in userland by
1670Sstevel@tonic-gate  *   the method described in "Pools" above.
1680Sstevel@tonic-gate  *
1690Sstevel@tonic-gate  *   Signaling threads should turn on the `creator signaled' flag, and
1700Sstevel@tonic-gate  *   can avoid sending signals when the flag is on. The flag is cleared
1710Sstevel@tonic-gate  *   when the thread is created.
1720Sstevel@tonic-gate  *
1730Sstevel@tonic-gate  *   When the pool is in closing state (ie it has been already unregistered
1740Sstevel@tonic-gate  *   from the pool list) the last thread on the last transport in the pool
1750Sstevel@tonic-gate  *   should turn the p_creator_exit flag on. The creator thread will
1760Sstevel@tonic-gate  *   clean up the pool structure and exit.
1770Sstevel@tonic-gate  *
1780Sstevel@tonic-gate  * Thread reservation; Detaching service threads.
1790Sstevel@tonic-gate  *   A service thread can detach itself to block for an extended amount
1800Sstevel@tonic-gate  *   of time. However, to keep the service active we need to guarantee
1810Sstevel@tonic-gate  *   at least pool->p_redline non-detached threads that can process incoming
1820Sstevel@tonic-gate  *   requests. This, the maximum number of detached and reserved threads is
1830Sstevel@tonic-gate  *   p->p_maxthreads - p->p_redline. A service thread should first acquire
1840Sstevel@tonic-gate  *   a reservation, and if the reservation was granted it can detach itself.
1850Sstevel@tonic-gate  *   If a reservation was granted but the thread does not detach itself
1860Sstevel@tonic-gate  *   it should cancel the reservation before it returns to svc_run().
1870Sstevel@tonic-gate  */
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate #include <sys/param.h>
1900Sstevel@tonic-gate #include <sys/types.h>
1910Sstevel@tonic-gate #include <rpc/types.h>
1920Sstevel@tonic-gate #include <sys/socket.h>
1930Sstevel@tonic-gate #include <sys/time.h>
1940Sstevel@tonic-gate #include <sys/tiuser.h>
1950Sstevel@tonic-gate #include <sys/t_kuser.h>
1960Sstevel@tonic-gate #include <netinet/in.h>
1970Sstevel@tonic-gate #include <rpc/xdr.h>
1980Sstevel@tonic-gate #include <rpc/auth.h>
1990Sstevel@tonic-gate #include <rpc/clnt.h>
2000Sstevel@tonic-gate #include <rpc/rpc_msg.h>
2010Sstevel@tonic-gate #include <rpc/svc.h>
2020Sstevel@tonic-gate #include <sys/proc.h>
2030Sstevel@tonic-gate #include <sys/user.h>
2040Sstevel@tonic-gate #include <sys/stream.h>
2050Sstevel@tonic-gate #include <sys/strsubr.h>
2060Sstevel@tonic-gate #include <sys/tihdr.h>
2070Sstevel@tonic-gate #include <sys/debug.h>
2080Sstevel@tonic-gate #include <sys/cmn_err.h>
2090Sstevel@tonic-gate #include <sys/file.h>
2100Sstevel@tonic-gate #include <sys/systm.h>
2110Sstevel@tonic-gate #include <sys/callb.h>
2120Sstevel@tonic-gate #include <sys/vtrace.h>
2130Sstevel@tonic-gate #include <sys/zone.h>
2140Sstevel@tonic-gate #include <nfs/nfs.h>
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate #define	RQCRED_SIZE	400	/* this size is excessive */
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate /*
2190Sstevel@tonic-gate  * Defines for svc_poll()
2200Sstevel@tonic-gate  */
2210Sstevel@tonic-gate #define	SVC_EXPRTGONE ((SVCMASTERXPRT *)1)	/* Transport is closing */
2220Sstevel@tonic-gate #define	SVC_ETIMEDOUT ((SVCMASTERXPRT *)2)	/* Timeout */
2230Sstevel@tonic-gate #define	SVC_EINTR ((SVCMASTERXPRT *)3)		/* Interrupted by signal */
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate /*
2260Sstevel@tonic-gate  * Default stack size for service threads.
2270Sstevel@tonic-gate  */
2280Sstevel@tonic-gate #define	DEFAULT_SVC_RUN_STKSIZE		(0)	/* default kernel stack */
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate int    svc_default_stksize = DEFAULT_SVC_RUN_STKSIZE;
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate /*
2330Sstevel@tonic-gate  * Default polling timeout for service threads.
2340Sstevel@tonic-gate  * Multiplied by hz when used.
2350Sstevel@tonic-gate  */
2360Sstevel@tonic-gate #define	DEFAULT_SVC_POLL_TIMEOUT	(5)	/* seconds */
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate clock_t svc_default_timeout = DEFAULT_SVC_POLL_TIMEOUT;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate /*
2410Sstevel@tonic-gate  * Size of the `xprt-ready' queue.
2420Sstevel@tonic-gate  */
2430Sstevel@tonic-gate #define	DEFAULT_SVC_QSIZE		(256)	/* qnodes */
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate size_t svc_default_qsize = DEFAULT_SVC_QSIZE;
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate /*
2480Sstevel@tonic-gate  * Default limit for the number of service threads.
2490Sstevel@tonic-gate  */
2500Sstevel@tonic-gate #define	DEFAULT_SVC_MAXTHREADS		(INT16_MAX)
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate int    svc_default_maxthreads = DEFAULT_SVC_MAXTHREADS;
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate  * Maximum number of requests from the same transport (in `drain' mode).
2560Sstevel@tonic-gate  */
2570Sstevel@tonic-gate #define	DEFAULT_SVC_MAX_SAME_XPRT	(8)
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate int    svc_default_max_same_xprt = DEFAULT_SVC_MAX_SAME_XPRT;
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate /*
2630Sstevel@tonic-gate  * Default `Redline' of non-detached threads.
2640Sstevel@tonic-gate  * Total number of detached and reserved threads in an RPC server
2650Sstevel@tonic-gate  * thread pool is limited to pool->p_maxthreads - svc_redline.
2660Sstevel@tonic-gate  */
2670Sstevel@tonic-gate #define	DEFAULT_SVC_REDLINE		(1)
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate int    svc_default_redline = DEFAULT_SVC_REDLINE;
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate /*
2720Sstevel@tonic-gate  * A node for the `xprt-ready' queue.
2730Sstevel@tonic-gate  * See below.
2740Sstevel@tonic-gate  */
2750Sstevel@tonic-gate struct __svcxprt_qnode {
2760Sstevel@tonic-gate 	__SVCXPRT_QNODE	*q_next;
2770Sstevel@tonic-gate 	SVCMASTERXPRT	*q_xprt;
2780Sstevel@tonic-gate };
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate /*
2810Sstevel@tonic-gate  * Global SVC variables (private).
2820Sstevel@tonic-gate  */
2830Sstevel@tonic-gate struct svc_globals {
2840Sstevel@tonic-gate 	SVCPOOL		*svc_pools;
2850Sstevel@tonic-gate 	kmutex_t	svc_plock;
2860Sstevel@tonic-gate };
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate /*
2890Sstevel@tonic-gate  * Debug variable to check for rdma based
2900Sstevel@tonic-gate  * transport startup and cleanup. Contorlled
2910Sstevel@tonic-gate  * through /etc/system. Off by default.
2920Sstevel@tonic-gate  */
2930Sstevel@tonic-gate int rdma_check = 0;
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate  * Authentication parameters list.
2970Sstevel@tonic-gate  */
2980Sstevel@tonic-gate static caddr_t rqcred_head;
2990Sstevel@tonic-gate static kmutex_t rqcred_lock;
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate /*
3020Sstevel@tonic-gate  * Pointers to transport specific `rele' routines in rpcmod (set from rpcmod).
3030Sstevel@tonic-gate  */
3040Sstevel@tonic-gate void	(*rpc_rele)(queue_t *, mblk_t *) = NULL;
3050Sstevel@tonic-gate void	(*mir_rele)(queue_t *, mblk_t *) = NULL;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate /* ARGSUSED */
3080Sstevel@tonic-gate void
3090Sstevel@tonic-gate rpc_rdma_rele(queue_t *q, mblk_t *mp)
3100Sstevel@tonic-gate {
3110Sstevel@tonic-gate }
3120Sstevel@tonic-gate void    (*rdma_rele)(queue_t *, mblk_t *) = rpc_rdma_rele;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate /*
3160Sstevel@tonic-gate  * This macro picks which `rele' routine to use, based on the transport type.
3170Sstevel@tonic-gate  */
3180Sstevel@tonic-gate #define	RELE_PROC(xprt) \
3190Sstevel@tonic-gate 	((xprt)->xp_type == T_RDMA ? rdma_rele : \
3200Sstevel@tonic-gate 	(((xprt)->xp_type == T_CLTS) ? rpc_rele : mir_rele))
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate /*
3230Sstevel@tonic-gate  * If true, then keep quiet about version mismatch.
3240Sstevel@tonic-gate  * This macro is for broadcast RPC only. We have no broadcast RPC in
3250Sstevel@tonic-gate  * kernel now but one may define a flag in the transport structure
3260Sstevel@tonic-gate  * and redefine this macro.
3270Sstevel@tonic-gate  */
3280Sstevel@tonic-gate #define	version_keepquiet(xprt)	(FALSE)
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate  * ZSD key used to retrieve zone-specific svc globals
3320Sstevel@tonic-gate  */
3330Sstevel@tonic-gate static zone_key_t svc_zone_key;
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate static void svc_callout_free(SVCMASTERXPRT *);
3360Sstevel@tonic-gate static void svc_xprt_qinit(SVCPOOL *, size_t);
3370Sstevel@tonic-gate static void svc_xprt_qdestroy(SVCPOOL *);
3380Sstevel@tonic-gate static void svc_thread_creator(SVCPOOL *);
3390Sstevel@tonic-gate static void svc_creator_signal(SVCPOOL *);
3400Sstevel@tonic-gate static void svc_creator_signalexit(SVCPOOL *);
3410Sstevel@tonic-gate static void svc_pool_unregister(struct svc_globals *, SVCPOOL *);
3420Sstevel@tonic-gate static int svc_run(SVCPOOL *);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate /* ARGSUSED */
3450Sstevel@tonic-gate static void *
3460Sstevel@tonic-gate svc_zoneinit(zoneid_t zoneid)
3470Sstevel@tonic-gate {
3480Sstevel@tonic-gate 	struct svc_globals *svc;
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	svc = kmem_alloc(sizeof (*svc), KM_SLEEP);
3510Sstevel@tonic-gate 	mutex_init(&svc->svc_plock, NULL, MUTEX_DEFAULT, NULL);
3520Sstevel@tonic-gate 	svc->svc_pools = NULL;
3530Sstevel@tonic-gate 	return (svc);
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate /* ARGSUSED */
3570Sstevel@tonic-gate static void
3580Sstevel@tonic-gate svc_zoneshutdown(zoneid_t zoneid, void *arg)
3590Sstevel@tonic-gate {
3600Sstevel@tonic-gate 	struct svc_globals *svc = arg;
3610Sstevel@tonic-gate 	SVCPOOL *pool;
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	mutex_enter(&svc->svc_plock);
3640Sstevel@tonic-gate 	while ((pool = svc->svc_pools) != NULL) {
3650Sstevel@tonic-gate 		svc_pool_unregister(svc, pool);
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 	mutex_exit(&svc->svc_plock);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate /* ARGSUSED */
3710Sstevel@tonic-gate static void
3720Sstevel@tonic-gate svc_zonefini(zoneid_t zoneid, void *arg)
3730Sstevel@tonic-gate {
3740Sstevel@tonic-gate 	struct svc_globals *svc = arg;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	ASSERT(svc->svc_pools == NULL);
3770Sstevel@tonic-gate 	mutex_destroy(&svc->svc_plock);
3780Sstevel@tonic-gate 	kmem_free(svc, sizeof (*svc));
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate  * Global SVC init routine.
3830Sstevel@tonic-gate  * Initialize global generic and transport type specific structures
3840Sstevel@tonic-gate  * used by the kernel RPC server side. This routine is called only
3850Sstevel@tonic-gate  * once when the module is being loaded.
3860Sstevel@tonic-gate  */
3870Sstevel@tonic-gate void
3880Sstevel@tonic-gate svc_init()
3890Sstevel@tonic-gate {
3900Sstevel@tonic-gate 	zone_key_create(&svc_zone_key, svc_zoneinit, svc_zoneshutdown,
3910Sstevel@tonic-gate 	    svc_zonefini);
3920Sstevel@tonic-gate 	svc_cots_init();
3930Sstevel@tonic-gate 	svc_clts_init();
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate  * Destroy the SVCPOOL structure.
3980Sstevel@tonic-gate  */
3990Sstevel@tonic-gate static void
4000Sstevel@tonic-gate svc_pool_cleanup(SVCPOOL *pool)
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 	ASSERT(pool->p_threads + pool->p_detached_threads == 0);
4030Sstevel@tonic-gate 	ASSERT(pool->p_lcount == 0);
4040Sstevel@tonic-gate 	ASSERT(pool->p_closing);
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	/*
4070Sstevel@tonic-gate 	 * Call the user supplied shutdown function.  This is done
4080Sstevel@tonic-gate 	 * here so the user of the pool will be able to cleanup
4090Sstevel@tonic-gate 	 * service related resources.
4100Sstevel@tonic-gate 	 */
4110Sstevel@tonic-gate 	if (pool->p_shutdown != NULL)
4120Sstevel@tonic-gate 		(pool->p_shutdown)();
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	/* Destroy `xprt-ready' queue */
4150Sstevel@tonic-gate 	svc_xprt_qdestroy(pool);
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	/* Destroy transport list */
4180Sstevel@tonic-gate 	rw_destroy(&pool->p_lrwlock);
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 	/* Destroy locks and condition variables */
4210Sstevel@tonic-gate 	mutex_destroy(&pool->p_thread_lock);
4220Sstevel@tonic-gate 	mutex_destroy(&pool->p_req_lock);
4230Sstevel@tonic-gate 	cv_destroy(&pool->p_req_cv);
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	/* Destroy creator's locks and condition variables */
4260Sstevel@tonic-gate 	mutex_destroy(&pool->p_creator_lock);
4270Sstevel@tonic-gate 	cv_destroy(&pool->p_creator_cv);
4280Sstevel@tonic-gate 	mutex_destroy(&pool->p_user_lock);
4290Sstevel@tonic-gate 	cv_destroy(&pool->p_user_cv);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	/* Free pool structure */
4320Sstevel@tonic-gate 	kmem_free(pool, sizeof (SVCPOOL));
4330Sstevel@tonic-gate }
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate  * If all the transports and service threads are already gone
4370Sstevel@tonic-gate  * signal the creator thread to clean up and exit.
4380Sstevel@tonic-gate  */
4390Sstevel@tonic-gate static bool_t
4400Sstevel@tonic-gate svc_pool_tryexit(SVCPOOL *pool)
4410Sstevel@tonic-gate {
4420Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pool->p_thread_lock));
4430Sstevel@tonic-gate 	ASSERT(pool->p_closing);
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	if (pool->p_threads + pool->p_detached_threads == 0) {
4460Sstevel@tonic-gate 		rw_enter(&pool->p_lrwlock, RW_READER);
4470Sstevel@tonic-gate 		if (pool->p_lcount == 0) {
4480Sstevel@tonic-gate 			/*
4490Sstevel@tonic-gate 			 * Release the locks before sending a signal.
4500Sstevel@tonic-gate 			 */
4510Sstevel@tonic-gate 			rw_exit(&pool->p_lrwlock);
4520Sstevel@tonic-gate 			mutex_exit(&pool->p_thread_lock);
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 			/*
4550Sstevel@tonic-gate 			 * Notify the creator thread to clean up and exit
4560Sstevel@tonic-gate 			 *
4570Sstevel@tonic-gate 			 * NOTICE: No references to the pool beyond this point!
4580Sstevel@tonic-gate 			 *		   The pool is being destroyed.
4590Sstevel@tonic-gate 			 */
4600Sstevel@tonic-gate 			ASSERT(!MUTEX_HELD(&pool->p_thread_lock));
4610Sstevel@tonic-gate 			svc_creator_signalexit(pool);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 			return (TRUE);
4640Sstevel@tonic-gate 		}
4650Sstevel@tonic-gate 		rw_exit(&pool->p_lrwlock);
4660Sstevel@tonic-gate 	}
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pool->p_thread_lock));
4690Sstevel@tonic-gate 	return (FALSE);
4700Sstevel@tonic-gate }
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate /*
4730Sstevel@tonic-gate  * Find a pool with a given id.
4740Sstevel@tonic-gate  */
4750Sstevel@tonic-gate static SVCPOOL *
4760Sstevel@tonic-gate svc_pool_find(struct svc_globals *svc, int id)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	SVCPOOL *pool;
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&svc->svc_plock));
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	/*
4830Sstevel@tonic-gate 	 * Search the list for a pool with a matching id
4840Sstevel@tonic-gate 	 * and register the transport handle with that pool.
4850Sstevel@tonic-gate 	 */
4860Sstevel@tonic-gate 	for (pool = svc->svc_pools; pool; pool = pool->p_next)
4870Sstevel@tonic-gate 		if (pool->p_id == id)
4880Sstevel@tonic-gate 			return (pool);
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	return (NULL);
4910Sstevel@tonic-gate }
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate /*
4940Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
4950Sstevel@tonic-gate  * svc_do_run
4960Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
4970Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
4980Sstevel@tonic-gate  */
4990Sstevel@tonic-gate int
5000Sstevel@tonic-gate svc_do_run(int id)
5010Sstevel@tonic-gate {
5020Sstevel@tonic-gate 	SVCPOOL *pool;
5030Sstevel@tonic-gate 	int err = 0;
5040Sstevel@tonic-gate 	struct svc_globals *svc;
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 	svc = zone_getspecific(svc_zone_key, curproc->p_zone);
5070Sstevel@tonic-gate 	mutex_enter(&svc->svc_plock);
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	pool = svc_pool_find(svc, id);
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	mutex_exit(&svc->svc_plock);
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	if (pool == NULL)
5140Sstevel@tonic-gate 		return (ENOENT);
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	/*
5170Sstevel@tonic-gate 	 * Increment counter of pool threads now
5180Sstevel@tonic-gate 	 * that a thread has been created.
5190Sstevel@tonic-gate 	 */
5200Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
5210Sstevel@tonic-gate 	pool->p_threads++;
5220Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	/* Give work to the new thread. */
5250Sstevel@tonic-gate 	err = svc_run(pool);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	return (err);
5280Sstevel@tonic-gate }
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate /*
5310Sstevel@tonic-gate  * Unregister a pool from the pool list.
5320Sstevel@tonic-gate  * Set the closing state. If all the transports and service threads
5330Sstevel@tonic-gate  * are already gone signal the creator thread to clean up and exit.
5340Sstevel@tonic-gate  */
5350Sstevel@tonic-gate static void
5360Sstevel@tonic-gate svc_pool_unregister(struct svc_globals *svc, SVCPOOL *pool)
5370Sstevel@tonic-gate {
5380Sstevel@tonic-gate 	SVCPOOL *next = pool->p_next;
5390Sstevel@tonic-gate 	SVCPOOL *prev = pool->p_prev;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&svc->svc_plock));
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	/* Remove from the list */
5440Sstevel@tonic-gate 	if (pool == svc->svc_pools)
5450Sstevel@tonic-gate 		svc->svc_pools = next;
5460Sstevel@tonic-gate 	if (next)
5470Sstevel@tonic-gate 		next->p_prev = prev;
5480Sstevel@tonic-gate 	if (prev)
5490Sstevel@tonic-gate 		prev->p_next = next;
5500Sstevel@tonic-gate 	pool->p_next = pool->p_prev = NULL;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	/*
5530Sstevel@tonic-gate 	 * Offline the pool. Mark the pool as closing.
5540Sstevel@tonic-gate 	 * If there are no transports in this pool notify
5550Sstevel@tonic-gate 	 * the creator thread to clean it up and exit.
5560Sstevel@tonic-gate 	 */
5570Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
5580Sstevel@tonic-gate 	if (pool->p_offline != NULL)
5590Sstevel@tonic-gate 		(pool->p_offline)();
5600Sstevel@tonic-gate 	pool->p_closing = TRUE;
5610Sstevel@tonic-gate 	if (svc_pool_tryexit(pool))
5620Sstevel@tonic-gate 		return;
5630Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate /*
5670Sstevel@tonic-gate  * Register a pool with a given id in the global doubly linked pool list.
5680Sstevel@tonic-gate  * - if there is a pool with the same id in the list then unregister it
5690Sstevel@tonic-gate  * - insert the new pool into the list.
5700Sstevel@tonic-gate  */
5710Sstevel@tonic-gate static void
5720Sstevel@tonic-gate svc_pool_register(struct svc_globals *svc, SVCPOOL *pool, int id)
5730Sstevel@tonic-gate {
5740Sstevel@tonic-gate 	SVCPOOL *old_pool;
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 	/*
5770Sstevel@tonic-gate 	 * If there is a pool with the same id then remove it from
5780Sstevel@tonic-gate 	 * the list and mark the pool as closing.
5790Sstevel@tonic-gate 	 */
5800Sstevel@tonic-gate 	mutex_enter(&svc->svc_plock);
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	if (old_pool = svc_pool_find(svc, id))
5830Sstevel@tonic-gate 		svc_pool_unregister(svc, old_pool);
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	/* Insert into the doubly linked list */
5860Sstevel@tonic-gate 	pool->p_id = id;
5870Sstevel@tonic-gate 	pool->p_next = svc->svc_pools;
5880Sstevel@tonic-gate 	pool->p_prev = NULL;
5890Sstevel@tonic-gate 	if (svc->svc_pools)
5900Sstevel@tonic-gate 		svc->svc_pools->p_prev = pool;
5910Sstevel@tonic-gate 	svc->svc_pools = pool;
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	mutex_exit(&svc->svc_plock);
5940Sstevel@tonic-gate }
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate /*
5970Sstevel@tonic-gate  * Initialize a newly created pool structure
5980Sstevel@tonic-gate  */
5990Sstevel@tonic-gate static int
6000Sstevel@tonic-gate svc_pool_init(SVCPOOL *pool, uint_t maxthreads, uint_t redline,
6010Sstevel@tonic-gate 	uint_t qsize, uint_t timeout, uint_t stksize, uint_t max_same_xprt)
6020Sstevel@tonic-gate {
6030Sstevel@tonic-gate 	klwp_t *lwp = ttolwp(curthread);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	ASSERT(pool);
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if (maxthreads == 0)
6080Sstevel@tonic-gate 		maxthreads = svc_default_maxthreads;
6090Sstevel@tonic-gate 	if (redline == 0)
6100Sstevel@tonic-gate 		redline = svc_default_redline;
6110Sstevel@tonic-gate 	if (qsize == 0)
6120Sstevel@tonic-gate 		qsize = svc_default_qsize;
6130Sstevel@tonic-gate 	if (timeout == 0)
6140Sstevel@tonic-gate 		timeout = svc_default_timeout;
6150Sstevel@tonic-gate 	if (stksize == 0)
6160Sstevel@tonic-gate 		stksize = svc_default_stksize;
6170Sstevel@tonic-gate 	if (max_same_xprt == 0)
6180Sstevel@tonic-gate 		max_same_xprt = svc_default_max_same_xprt;
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	if (maxthreads < redline)
6210Sstevel@tonic-gate 		return (EINVAL);
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	/* Allocate and initialize the `xprt-ready' queue */
6240Sstevel@tonic-gate 	svc_xprt_qinit(pool, qsize);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	/* Initialize doubly-linked xprt list */
6270Sstevel@tonic-gate 	rw_init(&pool->p_lrwlock, NULL, RW_DEFAULT, NULL);
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	/*
6300Sstevel@tonic-gate 	 * Setting lwp_childstksz on the current lwp so that
6310Sstevel@tonic-gate 	 * descendants of this lwp get the modified stacksize, if
6320Sstevel@tonic-gate 	 * it is defined. It is important that either this lwp or
6330Sstevel@tonic-gate 	 * one of its descendants do the actual servicepool thread
6340Sstevel@tonic-gate 	 * creation to maintain the stacksize inheritance.
6350Sstevel@tonic-gate 	 */
6360Sstevel@tonic-gate 	if (lwp != NULL)
6370Sstevel@tonic-gate 		lwp->lwp_childstksz = stksize;
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	/* Initialize thread limits, locks and condition variables */
6400Sstevel@tonic-gate 	pool->p_maxthreads = maxthreads;
6410Sstevel@tonic-gate 	pool->p_redline = redline;
6420Sstevel@tonic-gate 	pool->p_timeout = timeout * hz;
6430Sstevel@tonic-gate 	pool->p_stksize = stksize;
6440Sstevel@tonic-gate 	pool->p_max_same_xprt = max_same_xprt;
6450Sstevel@tonic-gate 	mutex_init(&pool->p_thread_lock, NULL, MUTEX_DEFAULT, NULL);
6460Sstevel@tonic-gate 	mutex_init(&pool->p_req_lock, NULL, MUTEX_DEFAULT, NULL);
6470Sstevel@tonic-gate 	cv_init(&pool->p_req_cv, NULL, CV_DEFAULT, NULL);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	/* Initialize userland creator */
6500Sstevel@tonic-gate 	pool->p_user_exit = FALSE;
6510Sstevel@tonic-gate 	pool->p_signal_create_thread = FALSE;
6520Sstevel@tonic-gate 	pool->p_user_waiting = FALSE;
6530Sstevel@tonic-gate 	mutex_init(&pool->p_user_lock, NULL, MUTEX_DEFAULT, NULL);
6540Sstevel@tonic-gate 	cv_init(&pool->p_user_cv, NULL, CV_DEFAULT, NULL);
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	/* Initialize the creator and start the creator thread */
6570Sstevel@tonic-gate 	pool->p_creator_exit = FALSE;
6580Sstevel@tonic-gate 	mutex_init(&pool->p_creator_lock, NULL, MUTEX_DEFAULT, NULL);
6590Sstevel@tonic-gate 	cv_init(&pool->p_creator_cv, NULL, CV_DEFAULT, NULL);
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	(void) zthread_create(NULL, pool->p_stksize, svc_thread_creator,
6620Sstevel@tonic-gate 	    pool, 0, minclsyspri);
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	return (0);
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
6690Sstevel@tonic-gate  * svc_pool_create
6700Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
6710Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
6720Sstevel@tonic-gate  *
6730Sstevel@tonic-gate  * Create an kernel RPC server-side thread/transport pool.
6740Sstevel@tonic-gate  *
6750Sstevel@tonic-gate  * This is public interface for creation of a server RPC thread pool
6760Sstevel@tonic-gate  * for a given service provider. Transports registered with the pool's id
6770Sstevel@tonic-gate  * will be served by a pool's threads. This function is called from the
6780Sstevel@tonic-gate  * nfssys() system call.
6790Sstevel@tonic-gate  */
6800Sstevel@tonic-gate int
6810Sstevel@tonic-gate svc_pool_create(struct svcpool_args *args)
6820Sstevel@tonic-gate {
6830Sstevel@tonic-gate 	SVCPOOL *pool;
6840Sstevel@tonic-gate 	int error;
6850Sstevel@tonic-gate 	struct svc_globals *svc;
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	/*
6880Sstevel@tonic-gate 	 * Caller should check credentials in a way appropriate
6890Sstevel@tonic-gate 	 * in the context of the call.
6900Sstevel@tonic-gate 	 */
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 	svc = zone_getspecific(svc_zone_key, curproc->p_zone);
6930Sstevel@tonic-gate 	/* Allocate a new pool */
6940Sstevel@tonic-gate 	pool = kmem_zalloc(sizeof (SVCPOOL), KM_SLEEP);
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 	/*
6970Sstevel@tonic-gate 	 * Initialize the pool structure and create a creator thread.
6980Sstevel@tonic-gate 	 */
6990Sstevel@tonic-gate 	error = svc_pool_init(pool, args->maxthreads, args->redline,
7000Sstevel@tonic-gate 	    args->qsize, args->timeout, args->stksize, args->max_same_xprt);
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	if (error) {
7030Sstevel@tonic-gate 		kmem_free(pool, sizeof (SVCPOOL));
7040Sstevel@tonic-gate 		return (error);
7050Sstevel@tonic-gate 	}
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	/* Register the pool with the global pool list */
7080Sstevel@tonic-gate 	svc_pool_register(svc, pool, args->id);
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	return (0);
7110Sstevel@tonic-gate }
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate int
7140Sstevel@tonic-gate svc_pool_control(int id, int cmd, void *arg)
7150Sstevel@tonic-gate {
7160Sstevel@tonic-gate 	SVCPOOL *pool;
7170Sstevel@tonic-gate 	struct svc_globals *svc;
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 	svc = zone_getspecific(svc_zone_key, curproc->p_zone);
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 	switch (cmd) {
7220Sstevel@tonic-gate 	case SVCPSET_SHUTDOWN_PROC:
7230Sstevel@tonic-gate 		/*
7240Sstevel@tonic-gate 		 * Search the list for a pool with a matching id
7250Sstevel@tonic-gate 		 * and register the transport handle with that pool.
7260Sstevel@tonic-gate 		 */
7270Sstevel@tonic-gate 		mutex_enter(&svc->svc_plock);
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 		if ((pool = svc_pool_find(svc, id)) == NULL) {
7300Sstevel@tonic-gate 			mutex_exit(&svc->svc_plock);
7310Sstevel@tonic-gate 			return (ENOENT);
7320Sstevel@tonic-gate 		}
7330Sstevel@tonic-gate 		/*
7340Sstevel@tonic-gate 		 * Grab the transport list lock before releasing the
7350Sstevel@tonic-gate 		 * pool list lock
7360Sstevel@tonic-gate 		 */
7370Sstevel@tonic-gate 		rw_enter(&pool->p_lrwlock, RW_WRITER);
7380Sstevel@tonic-gate 		mutex_exit(&svc->svc_plock);
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 		pool->p_shutdown = *((void (*)())arg);
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 		rw_exit(&pool->p_lrwlock);
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 		return (0);
7450Sstevel@tonic-gate 	case SVCPSET_UNREGISTER_PROC:
7460Sstevel@tonic-gate 		/*
7470Sstevel@tonic-gate 		 * Search the list for a pool with a matching id
7480Sstevel@tonic-gate 		 * and register the unregister callback handle with that pool.
7490Sstevel@tonic-gate 		 */
7500Sstevel@tonic-gate 		mutex_enter(&svc->svc_plock);
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 		if ((pool = svc_pool_find(svc, id)) == NULL) {
7530Sstevel@tonic-gate 			mutex_exit(&svc->svc_plock);
7540Sstevel@tonic-gate 			return (ENOENT);
7550Sstevel@tonic-gate 		}
7560Sstevel@tonic-gate 		/*
7570Sstevel@tonic-gate 		 * Grab the transport list lock before releasing the
7580Sstevel@tonic-gate 		 * pool list lock
7590Sstevel@tonic-gate 		 */
7600Sstevel@tonic-gate 		rw_enter(&pool->p_lrwlock, RW_WRITER);
7610Sstevel@tonic-gate 		mutex_exit(&svc->svc_plock);
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 		pool->p_offline = *((void (*)())arg);
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 		rw_exit(&pool->p_lrwlock);
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 		return (0);
7680Sstevel@tonic-gate 	default:
7690Sstevel@tonic-gate 		return (EINVAL);
7700Sstevel@tonic-gate 	}
7710Sstevel@tonic-gate }
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate /*
7740Sstevel@tonic-gate  * Pool's transport list manipulation routines.
7750Sstevel@tonic-gate  * - svc_xprt_register()
7760Sstevel@tonic-gate  * - svc_xprt_unregister()
7770Sstevel@tonic-gate  *
7780Sstevel@tonic-gate  * svc_xprt_register() is called from svc_tli_kcreate() to
7790Sstevel@tonic-gate  * insert a new master transport handle into the doubly linked
7800Sstevel@tonic-gate  * list of server transport handles (one list per pool).
7810Sstevel@tonic-gate  *
7820Sstevel@tonic-gate  * The list is used by svc_poll(), when it operates in `drain'
7830Sstevel@tonic-gate  * mode, to search for a next transport with a pending request.
7840Sstevel@tonic-gate  */
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate int
7870Sstevel@tonic-gate svc_xprt_register(SVCMASTERXPRT *xprt, int id)
7880Sstevel@tonic-gate {
7890Sstevel@tonic-gate 	SVCMASTERXPRT *prev, *next;
7900Sstevel@tonic-gate 	SVCPOOL *pool;
7910Sstevel@tonic-gate 	struct svc_globals *svc;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	svc = zone_getspecific(svc_zone_key, curproc->p_zone);
7940Sstevel@tonic-gate 	/*
7950Sstevel@tonic-gate 	 * Search the list for a pool with a matching id
7960Sstevel@tonic-gate 	 * and register the transport handle with that pool.
7970Sstevel@tonic-gate 	 */
7980Sstevel@tonic-gate 	mutex_enter(&svc->svc_plock);
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	if ((pool = svc_pool_find(svc, id)) == NULL) {
8010Sstevel@tonic-gate 		mutex_exit(&svc->svc_plock);
8020Sstevel@tonic-gate 		return (ENOENT);
8030Sstevel@tonic-gate 	}
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	/* Grab the transport list lock before releasing the pool list lock */
8060Sstevel@tonic-gate 	rw_enter(&pool->p_lrwlock, RW_WRITER);
8070Sstevel@tonic-gate 	mutex_exit(&svc->svc_plock);
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	/* Don't register new transports when the pool is in closing state */
8100Sstevel@tonic-gate 	if (pool->p_closing) {
8110Sstevel@tonic-gate 		rw_exit(&pool->p_lrwlock);
8120Sstevel@tonic-gate 		return (EBUSY);
8130Sstevel@tonic-gate 	}
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 	/*
8160Sstevel@tonic-gate 	 * Initialize xp_pool to point to the pool.
8170Sstevel@tonic-gate 	 * We don't want to go through the pool list every time.
8180Sstevel@tonic-gate 	 */
8190Sstevel@tonic-gate 	xprt->xp_pool = pool;
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	/*
8220Sstevel@tonic-gate 	 * Insert a transport handle into the list.
8230Sstevel@tonic-gate 	 * The list head points to the most recently inserted transport.
8240Sstevel@tonic-gate 	 */
8250Sstevel@tonic-gate 	if (pool->p_lhead == NULL)
8260Sstevel@tonic-gate 		pool->p_lhead = xprt->xp_prev = xprt->xp_next = xprt;
8270Sstevel@tonic-gate 	else {
8280Sstevel@tonic-gate 		next = pool->p_lhead;
8290Sstevel@tonic-gate 		prev = pool->p_lhead->xp_prev;
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 		xprt->xp_next = next;
8320Sstevel@tonic-gate 		xprt->xp_prev = prev;
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 		pool->p_lhead = prev->xp_next = next->xp_prev = xprt;
8350Sstevel@tonic-gate 	}
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 	/* Increment the transports count */
8380Sstevel@tonic-gate 	pool->p_lcount++;
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	rw_exit(&pool->p_lrwlock);
8410Sstevel@tonic-gate 	return (0);
8420Sstevel@tonic-gate }
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate /*
8450Sstevel@tonic-gate  * Called from svc_xprt_cleanup() to remove a master transport handle
8460Sstevel@tonic-gate  * from the pool's list of server transports (when a transport is
8470Sstevel@tonic-gate  * being destroyed).
8480Sstevel@tonic-gate  */
8490Sstevel@tonic-gate void
8500Sstevel@tonic-gate svc_xprt_unregister(SVCMASTERXPRT *xprt)
8510Sstevel@tonic-gate {
8520Sstevel@tonic-gate 	SVCPOOL *pool = xprt->xp_pool;
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	/*
8550Sstevel@tonic-gate 	 * Unlink xprt from the list.
8560Sstevel@tonic-gate 	 * If the list head points to this xprt then move it
8570Sstevel@tonic-gate 	 * to the next xprt or reset to NULL if this is the last
8580Sstevel@tonic-gate 	 * xprt in the list.
8590Sstevel@tonic-gate 	 */
8600Sstevel@tonic-gate 	rw_enter(&pool->p_lrwlock, RW_WRITER);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	if (xprt == xprt->xp_next)
8630Sstevel@tonic-gate 		pool->p_lhead = NULL;
8640Sstevel@tonic-gate 	else {
8650Sstevel@tonic-gate 		SVCMASTERXPRT *next = xprt->xp_next;
8660Sstevel@tonic-gate 		SVCMASTERXPRT *prev = xprt->xp_prev;
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 		next->xp_prev = prev;
8690Sstevel@tonic-gate 		prev->xp_next = next;
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 		if (pool->p_lhead == xprt)
8720Sstevel@tonic-gate 			pool->p_lhead = next;
8730Sstevel@tonic-gate 	}
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	xprt->xp_next = xprt->xp_prev = NULL;
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	/* Decrement list count */
8780Sstevel@tonic-gate 	pool->p_lcount--;
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	rw_exit(&pool->p_lrwlock);
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate static void
8840Sstevel@tonic-gate svc_xprt_qdestroy(SVCPOOL *pool)
8850Sstevel@tonic-gate {
8860Sstevel@tonic-gate 	mutex_destroy(&pool->p_qend_lock);
8870Sstevel@tonic-gate 	kmem_free(pool->p_qbody, pool->p_qsize * sizeof (__SVCXPRT_QNODE));
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate /*
8910Sstevel@tonic-gate  * Initialize an `xprt-ready' queue for a given pool.
8920Sstevel@tonic-gate  */
8930Sstevel@tonic-gate static void
8940Sstevel@tonic-gate svc_xprt_qinit(SVCPOOL *pool, size_t qsize)
8950Sstevel@tonic-gate {
8960Sstevel@tonic-gate 	int i;
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	pool->p_qsize = qsize;
8990Sstevel@tonic-gate 	pool->p_qbody = kmem_zalloc(pool->p_qsize * sizeof (__SVCXPRT_QNODE),
9000Sstevel@tonic-gate 	    KM_SLEEP);
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	for (i = 0; i < pool->p_qsize - 1; i++)
9030Sstevel@tonic-gate 		pool->p_qbody[i].q_next = &(pool->p_qbody[i+1]);
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	pool->p_qbody[pool->p_qsize-1].q_next = &(pool->p_qbody[0]);
9060Sstevel@tonic-gate 	pool->p_qtop = &(pool->p_qbody[0]);
9070Sstevel@tonic-gate 	pool->p_qend = &(pool->p_qbody[0]);
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	mutex_init(&pool->p_qend_lock, NULL, MUTEX_DEFAULT, NULL);
9100Sstevel@tonic-gate }
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate /*
9130Sstevel@tonic-gate  * Called from the svc_queuereq() interrupt routine to queue
9140Sstevel@tonic-gate  * a hint for svc_poll() which transport has a pending request.
9150Sstevel@tonic-gate  * - insert a pointer to xprt into the xprt-ready queue (FIFO)
9160Sstevel@tonic-gate  * - if the xprt-ready queue is full turn the overflow flag on.
9170Sstevel@tonic-gate  *
9180Sstevel@tonic-gate  * NOTICE: pool->p_qtop is protected by the the pool's request lock
9190Sstevel@tonic-gate  * and the caller (svc_queuereq()) must hold the lock.
9200Sstevel@tonic-gate  */
9210Sstevel@tonic-gate static void
9220Sstevel@tonic-gate svc_xprt_qput(SVCPOOL *pool, SVCMASTERXPRT *xprt)
9230Sstevel@tonic-gate {
9240Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pool->p_req_lock));
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	/* If the overflow flag is there is nothing we can do */
9270Sstevel@tonic-gate 	if (pool->p_qoverflow)
9280Sstevel@tonic-gate 		return;
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	/* If the queue is full turn the overflow flag on and exit */
9310Sstevel@tonic-gate 	if (pool->p_qtop->q_next == pool->p_qend) {
9320Sstevel@tonic-gate 		mutex_enter(&pool->p_qend_lock);
9330Sstevel@tonic-gate 		if (pool->p_qtop->q_next == pool->p_qend) {
9340Sstevel@tonic-gate 			pool->p_qoverflow = TRUE;
9350Sstevel@tonic-gate 			mutex_exit(&pool->p_qend_lock);
9360Sstevel@tonic-gate 			return;
9370Sstevel@tonic-gate 		}
9380Sstevel@tonic-gate 		mutex_exit(&pool->p_qend_lock);
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	/* Insert a hint and move pool->p_qtop */
9420Sstevel@tonic-gate 	pool->p_qtop->q_xprt = xprt;
9430Sstevel@tonic-gate 	pool->p_qtop = pool->p_qtop->q_next;
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate /*
9470Sstevel@tonic-gate  * Called from svc_poll() to get a hint which transport has a
9480Sstevel@tonic-gate  * pending request. Returns a pointer to a transport or NULL if the
9490Sstevel@tonic-gate  * `xprt-ready' queue is empty.
9500Sstevel@tonic-gate  *
9510Sstevel@tonic-gate  * Since we do not acquire the pool's request lock while checking if
9520Sstevel@tonic-gate  * the queue is empty we may miss a request that is just being delivered.
9530Sstevel@tonic-gate  * However this is ok since svc_poll() will retry again until the
9540Sstevel@tonic-gate  * count indicates that there are pending requests for this pool.
9550Sstevel@tonic-gate  */
9560Sstevel@tonic-gate static SVCMASTERXPRT *
9570Sstevel@tonic-gate svc_xprt_qget(SVCPOOL *pool)
9580Sstevel@tonic-gate {
9590Sstevel@tonic-gate 	SVCMASTERXPRT *xprt;
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 	mutex_enter(&pool->p_qend_lock);
9620Sstevel@tonic-gate 	do {
9630Sstevel@tonic-gate 		/*
9640Sstevel@tonic-gate 		 * If the queue is empty return NULL.
9650Sstevel@tonic-gate 		 * Since we do not acquire the pool's request lock which
9660Sstevel@tonic-gate 		 * protects pool->p_qtop this is not exact check. However,
9670Sstevel@tonic-gate 		 * this is safe - if we miss a request here svc_poll()
9680Sstevel@tonic-gate 		 * will retry again.
9690Sstevel@tonic-gate 		 */
9700Sstevel@tonic-gate 		if (pool->p_qend == pool->p_qtop) {
9710Sstevel@tonic-gate 			mutex_exit(&pool->p_qend_lock);
9720Sstevel@tonic-gate 			return (NULL);
9730Sstevel@tonic-gate 		}
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 		/* Get a hint and move pool->p_qend */
9760Sstevel@tonic-gate 		xprt = pool->p_qend->q_xprt;
9770Sstevel@tonic-gate 		pool->p_qend = pool->p_qend->q_next;
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 		/* Skip fields deleted by svc_xprt_qdelete()	 */
9800Sstevel@tonic-gate 	} while (xprt == NULL);
9810Sstevel@tonic-gate 	mutex_exit(&pool->p_qend_lock);
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	return (xprt);
9840Sstevel@tonic-gate }
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate /*
9870Sstevel@tonic-gate  * Reset an overflow in the xprt-ready queue after
9880Sstevel@tonic-gate  * all the pending requests has been drained.
9890Sstevel@tonic-gate  * This switches svc_poll back to getting hints from the
9900Sstevel@tonic-gate  * xprt-ready queue.
9910Sstevel@tonic-gate  *
9920Sstevel@tonic-gate  * NOTICE: pool->p_qtop is protected by the the pool's request lock
9930Sstevel@tonic-gate  * and the caller (svc_poll()) must hold the lock.
9940Sstevel@tonic-gate  */
9950Sstevel@tonic-gate static void
9960Sstevel@tonic-gate svc_xprt_qreset(SVCPOOL *pool)
9970Sstevel@tonic-gate {
9980Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pool->p_req_lock));
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 	pool->p_qend = pool->p_qtop;
10010Sstevel@tonic-gate 	pool->p_qoverflow = FALSE;
10020Sstevel@tonic-gate }
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate /*
10050Sstevel@tonic-gate  * Delete all the references to a transport handle that
10060Sstevel@tonic-gate  * is being destroyed from the xprt-ready queue.
10070Sstevel@tonic-gate  * Deleted pointers are replaced with NULLs.
10080Sstevel@tonic-gate  */
10090Sstevel@tonic-gate static void
10100Sstevel@tonic-gate svc_xprt_qdelete(SVCPOOL *pool, SVCMASTERXPRT *xprt)
10110Sstevel@tonic-gate {
10120Sstevel@tonic-gate 	__SVCXPRT_QNODE *q = pool->p_qend;
10130Sstevel@tonic-gate 	__SVCXPRT_QNODE *qtop = pool->p_qtop;
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	/*
10160Sstevel@tonic-gate 	 * Delete all the references to xprt between the current
10170Sstevel@tonic-gate 	 * position of pool->p_qend and current pool->p_qtop.
10180Sstevel@tonic-gate 	 */
10190Sstevel@tonic-gate 	for (;;) {
10200Sstevel@tonic-gate 		if (q->q_xprt == xprt)
10210Sstevel@tonic-gate 			q->q_xprt = NULL;
10220Sstevel@tonic-gate 		if (q == qtop)
10230Sstevel@tonic-gate 			return;
10240Sstevel@tonic-gate 		q = q->q_next;
10250Sstevel@tonic-gate 	}
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate /*
10290Sstevel@tonic-gate  * Destructor for a master server transport handle.
10300Sstevel@tonic-gate  * - if there are no more non-detached threads linked to this transport
10310Sstevel@tonic-gate  *   then, if requested, call xp_closeproc (we don't wait for detached
10320Sstevel@tonic-gate  *   threads linked to this transport to complete).
10330Sstevel@tonic-gate  * - if there are no more threads linked to this
10340Sstevel@tonic-gate  *   transport then
10350Sstevel@tonic-gate  *   a) remove references to this transport from the xprt-ready queue
10360Sstevel@tonic-gate  *   b) remove a reference to this transport from the pool's transport list
10370Sstevel@tonic-gate  *   c) call a transport specific `destroy' function
10380Sstevel@tonic-gate  *   d) cancel remaining thread reservations.
10390Sstevel@tonic-gate  *
10400Sstevel@tonic-gate  * NOTICE: Caller must hold the transport's thread lock.
10410Sstevel@tonic-gate  */
10420Sstevel@tonic-gate static void
10430Sstevel@tonic-gate svc_xprt_cleanup(SVCMASTERXPRT *xprt, bool_t detached)
10440Sstevel@tonic-gate {
10450Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&xprt->xp_thread_lock));
10460Sstevel@tonic-gate 	ASSERT(xprt->xp_wq == NULL);
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 	/*
10490Sstevel@tonic-gate 	 * If called from the last non-detached thread
10500Sstevel@tonic-gate 	 * it should call the closeproc on this transport.
10510Sstevel@tonic-gate 	 */
10520Sstevel@tonic-gate 	if (!detached && xprt->xp_threads == 0 && xprt->xp_closeproc) {
10530Sstevel@tonic-gate 		(*(xprt->xp_closeproc)) (xprt);
10540Sstevel@tonic-gate 	}
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 	if (xprt->xp_threads + xprt->xp_detached_threads > 0)
10570Sstevel@tonic-gate 		mutex_exit(&xprt->xp_thread_lock);
10580Sstevel@tonic-gate 	else {
10590Sstevel@tonic-gate 		/* Remove references to xprt from the `xprt-ready' queue */
10600Sstevel@tonic-gate 		svc_xprt_qdelete(xprt->xp_pool, xprt);
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 		/* Unregister xprt from the pool's transport list */
10630Sstevel@tonic-gate 		svc_xprt_unregister(xprt);
10640Sstevel@tonic-gate 		svc_callout_free(xprt);
10650Sstevel@tonic-gate 		SVC_DESTROY(xprt);
10660Sstevel@tonic-gate 	}
10670Sstevel@tonic-gate }
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate /*
10700Sstevel@tonic-gate  * Find a dispatch routine for a given prog/vers pair.
10710Sstevel@tonic-gate  * This function is called from svc_getreq() to search the callout
10720Sstevel@tonic-gate  * table for an entry with a matching RPC program number `prog'
10730Sstevel@tonic-gate  * and a version range that covers `vers'.
10740Sstevel@tonic-gate  * - if it finds a matching entry it returns pointer to the dispatch routine
10750Sstevel@tonic-gate  * - otherwise it returns NULL and, if `minp' or `maxp' are not NULL,
10760Sstevel@tonic-gate  *   fills them with, respectively, lowest version and highest version
10770Sstevel@tonic-gate  *   supported for the program `prog'
10780Sstevel@tonic-gate  */
10790Sstevel@tonic-gate static SVC_DISPATCH *
10800Sstevel@tonic-gate svc_callout_find(SVCXPRT *xprt, rpcprog_t prog, rpcvers_t vers,
10810Sstevel@tonic-gate     rpcvers_t *vers_min, rpcvers_t *vers_max)
10820Sstevel@tonic-gate {
10830Sstevel@tonic-gate 	SVC_CALLOUT_TABLE *sct = xprt->xp_sct;
10840Sstevel@tonic-gate 	int i;
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate 	*vers_min = ~(rpcvers_t)0;
10870Sstevel@tonic-gate 	*vers_max = 0;
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	for (i = 0; i < sct->sct_size; i++) {
10900Sstevel@tonic-gate 		SVC_CALLOUT *sc = &sct->sct_sc[i];
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 		if (prog == sc->sc_prog) {
10930Sstevel@tonic-gate 			if (vers >= sc->sc_versmin && vers <= sc->sc_versmax)
10940Sstevel@tonic-gate 				return (sc->sc_dispatch);
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 			if (*vers_max < sc->sc_versmax)
10970Sstevel@tonic-gate 				*vers_max = sc->sc_versmax;
10980Sstevel@tonic-gate 			if (*vers_min > sc->sc_versmin)
10990Sstevel@tonic-gate 				*vers_min = sc->sc_versmin;
11000Sstevel@tonic-gate 		}
11010Sstevel@tonic-gate 	}
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	return (NULL);
11040Sstevel@tonic-gate }
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate /*
11070Sstevel@tonic-gate  * Optionally free callout table allocated for this transport by
11080Sstevel@tonic-gate  * the service provider.
11090Sstevel@tonic-gate  */
11100Sstevel@tonic-gate static void
11110Sstevel@tonic-gate svc_callout_free(SVCMASTERXPRT *xprt)
11120Sstevel@tonic-gate {
11130Sstevel@tonic-gate 	SVC_CALLOUT_TABLE *sct = xprt->xp_sct;
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	if (sct->sct_free) {
11160Sstevel@tonic-gate 		kmem_free(sct->sct_sc, sct->sct_size * sizeof (SVC_CALLOUT));
11170Sstevel@tonic-gate 		kmem_free(sct, sizeof (SVC_CALLOUT_TABLE));
11180Sstevel@tonic-gate 	}
11190Sstevel@tonic-gate }
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate /*
11220Sstevel@tonic-gate  * Send a reply to an RPC request
11230Sstevel@tonic-gate  *
11240Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
11250Sstevel@tonic-gate  * svc_sendreply
11260Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
11270Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
11280Sstevel@tonic-gate  */
11290Sstevel@tonic-gate bool_t
11300Sstevel@tonic-gate svc_sendreply(const SVCXPRT *clone_xprt, const xdrproc_t xdr_results,
11310Sstevel@tonic-gate     const caddr_t xdr_location)
11320Sstevel@tonic-gate {
11330Sstevel@tonic-gate 	struct rpc_msg rply;
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	rply.rm_direction = REPLY;
11360Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
11370Sstevel@tonic-gate 	rply.acpted_rply.ar_verf = clone_xprt->xp_verf;
11380Sstevel@tonic-gate 	rply.acpted_rply.ar_stat = SUCCESS;
11390Sstevel@tonic-gate 	rply.acpted_rply.ar_results.where = xdr_location;
11400Sstevel@tonic-gate 	rply.acpted_rply.ar_results.proc = xdr_results;
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	return (SVC_REPLY((SVCXPRT *)clone_xprt, &rply));
11430Sstevel@tonic-gate }
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate /*
11460Sstevel@tonic-gate  * No procedure error reply
11470Sstevel@tonic-gate  *
11480Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
11490Sstevel@tonic-gate  * svcerr_noproc
11500Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
11510Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
11520Sstevel@tonic-gate  */
11530Sstevel@tonic-gate void
11540Sstevel@tonic-gate svcerr_noproc(const SVCXPRT *clone_xprt)
11550Sstevel@tonic-gate {
11560Sstevel@tonic-gate 	struct rpc_msg rply;
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	rply.rm_direction = REPLY;
11590Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
11600Sstevel@tonic-gate 	rply.acpted_rply.ar_verf = clone_xprt->xp_verf;
11610Sstevel@tonic-gate 	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
11620Sstevel@tonic-gate 	SVC_FREERES((SVCXPRT *)clone_xprt);
11630Sstevel@tonic-gate 	SVC_REPLY((SVCXPRT *)clone_xprt, &rply);
11640Sstevel@tonic-gate }
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate /*
11670Sstevel@tonic-gate  * Can't decode arguments error reply
11680Sstevel@tonic-gate  *
11690Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
11700Sstevel@tonic-gate  * svcerr_decode
11710Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
11720Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
11730Sstevel@tonic-gate  */
11740Sstevel@tonic-gate void
11750Sstevel@tonic-gate svcerr_decode(const SVCXPRT *clone_xprt)
11760Sstevel@tonic-gate {
11770Sstevel@tonic-gate 	struct rpc_msg rply;
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 	rply.rm_direction = REPLY;
11800Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
11810Sstevel@tonic-gate 	rply.acpted_rply.ar_verf = clone_xprt->xp_verf;
11820Sstevel@tonic-gate 	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
11830Sstevel@tonic-gate 	SVC_FREERES((SVCXPRT *)clone_xprt);
11840Sstevel@tonic-gate 	SVC_REPLY((SVCXPRT *)clone_xprt, &rply);
11850Sstevel@tonic-gate }
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate /*
11880Sstevel@tonic-gate  * Some system error
11890Sstevel@tonic-gate  */
11900Sstevel@tonic-gate void
11910Sstevel@tonic-gate svcerr_systemerr(const SVCXPRT *clone_xprt)
11920Sstevel@tonic-gate {
11930Sstevel@tonic-gate 	struct rpc_msg rply;
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 	rply.rm_direction = REPLY;
11960Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
11970Sstevel@tonic-gate 	rply.acpted_rply.ar_verf = clone_xprt->xp_verf;
11980Sstevel@tonic-gate 	rply.acpted_rply.ar_stat = SYSTEM_ERR;
11990Sstevel@tonic-gate 	SVC_FREERES((SVCXPRT *)clone_xprt);
12000Sstevel@tonic-gate 	SVC_REPLY((SVCXPRT *)clone_xprt, &rply);
12010Sstevel@tonic-gate }
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate /*
12040Sstevel@tonic-gate  * Authentication error reply
12050Sstevel@tonic-gate  */
12060Sstevel@tonic-gate void
12070Sstevel@tonic-gate svcerr_auth(const SVCXPRT *clone_xprt, const enum auth_stat why)
12080Sstevel@tonic-gate {
12090Sstevel@tonic-gate 	struct rpc_msg rply;
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 	rply.rm_direction = REPLY;
12120Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_DENIED;
12130Sstevel@tonic-gate 	rply.rjcted_rply.rj_stat = AUTH_ERROR;
12140Sstevel@tonic-gate 	rply.rjcted_rply.rj_why = why;
12150Sstevel@tonic-gate 	SVC_FREERES((SVCXPRT *)clone_xprt);
12160Sstevel@tonic-gate 	SVC_REPLY((SVCXPRT *)clone_xprt, &rply);
12170Sstevel@tonic-gate }
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate /*
12200Sstevel@tonic-gate  * Authentication too weak error reply
12210Sstevel@tonic-gate  */
12220Sstevel@tonic-gate void
12230Sstevel@tonic-gate svcerr_weakauth(const SVCXPRT *clone_xprt)
12240Sstevel@tonic-gate {
12250Sstevel@tonic-gate 	svcerr_auth((SVCXPRT *)clone_xprt, AUTH_TOOWEAK);
12260Sstevel@tonic-gate }
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate /*
12290Sstevel@tonic-gate  * Program unavailable error reply
12300Sstevel@tonic-gate  *
12310Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
12320Sstevel@tonic-gate  * svcerr_noprog
12330Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
12340Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
12350Sstevel@tonic-gate  */
12360Sstevel@tonic-gate void
12370Sstevel@tonic-gate svcerr_noprog(const SVCXPRT *clone_xprt)
12380Sstevel@tonic-gate {
12390Sstevel@tonic-gate 	struct rpc_msg rply;
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 	rply.rm_direction = REPLY;
12420Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
12430Sstevel@tonic-gate 	rply.acpted_rply.ar_verf = clone_xprt->xp_verf;
12440Sstevel@tonic-gate 	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
12450Sstevel@tonic-gate 	SVC_FREERES((SVCXPRT *)clone_xprt);
12460Sstevel@tonic-gate 	SVC_REPLY((SVCXPRT *)clone_xprt, &rply);
12470Sstevel@tonic-gate }
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate /*
12500Sstevel@tonic-gate  * Program version mismatch error reply
12510Sstevel@tonic-gate  *
12520Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
12530Sstevel@tonic-gate  * svcerr_progvers
12540Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
12550Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
12560Sstevel@tonic-gate  */
12570Sstevel@tonic-gate void
12580Sstevel@tonic-gate svcerr_progvers(const SVCXPRT *clone_xprt,
12590Sstevel@tonic-gate     const rpcvers_t low_vers, const rpcvers_t high_vers)
12600Sstevel@tonic-gate {
12610Sstevel@tonic-gate 	struct rpc_msg rply;
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	rply.rm_direction = REPLY;
12640Sstevel@tonic-gate 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
12650Sstevel@tonic-gate 	rply.acpted_rply.ar_verf = clone_xprt->xp_verf;
12660Sstevel@tonic-gate 	rply.acpted_rply.ar_stat = PROG_MISMATCH;
12670Sstevel@tonic-gate 	rply.acpted_rply.ar_vers.low = low_vers;
12680Sstevel@tonic-gate 	rply.acpted_rply.ar_vers.high = high_vers;
12690Sstevel@tonic-gate 	SVC_FREERES((SVCXPRT *)clone_xprt);
12700Sstevel@tonic-gate 	SVC_REPLY((SVCXPRT *)clone_xprt, &rply);
12710Sstevel@tonic-gate }
12720Sstevel@tonic-gate 
12730Sstevel@tonic-gate /*
12740Sstevel@tonic-gate  * Get server side input from some transport.
12750Sstevel@tonic-gate  *
12760Sstevel@tonic-gate  * Statement of authentication parameters management:
12770Sstevel@tonic-gate  * This function owns and manages all authentication parameters, specifically
12780Sstevel@tonic-gate  * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
12790Sstevel@tonic-gate  * the "cooked" credentials (rqst->rq_clntcred).
12800Sstevel@tonic-gate  * However, this function does not know the structure of the cooked
12810Sstevel@tonic-gate  * credentials, so it make the following assumptions:
12820Sstevel@tonic-gate  *   a) the structure is contiguous (no pointers), and
12830Sstevel@tonic-gate  *   b) the cred structure size does not exceed RQCRED_SIZE bytes.
12840Sstevel@tonic-gate  * In all events, all three parameters are freed upon exit from this routine.
12850Sstevel@tonic-gate  * The storage is trivially managed on the call stack in user land, but
12860Sstevel@tonic-gate  * is malloced in kernel land.
12870Sstevel@tonic-gate  *
12880Sstevel@tonic-gate  * Note: the xprt's xp_svc_lock is not held while the service's dispatch
12890Sstevel@tonic-gate  * routine is running.	If we decide to implement svc_unregister(), we'll
12900Sstevel@tonic-gate  * need to decide whether it's okay for a thread to unregister a service
12910Sstevel@tonic-gate  * while a request is being processed.	If we decide that this is a
12920Sstevel@tonic-gate  * problem, we can probably use some sort of reference counting scheme to
12930Sstevel@tonic-gate  * keep the callout entry from going away until the request has completed.
12940Sstevel@tonic-gate  */
12950Sstevel@tonic-gate static void
12960Sstevel@tonic-gate svc_getreq(
12970Sstevel@tonic-gate 	SVCXPRT *clone_xprt,	/* clone transport handle */
12980Sstevel@tonic-gate 	mblk_t *mp)
12990Sstevel@tonic-gate {
13000Sstevel@tonic-gate 	struct rpc_msg msg;
13010Sstevel@tonic-gate 	struct svc_req r;
13020Sstevel@tonic-gate 	char  *cred_area;	/* too big to allocate on call stack */
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	TRACE_0(TR_FAC_KRPC, TR_SVC_GETREQ_START,
13050Sstevel@tonic-gate 	    "svc_getreq_start:");
13060Sstevel@tonic-gate 
13070Sstevel@tonic-gate 	ASSERT(clone_xprt->xp_master != NULL);
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 	/*
13100Sstevel@tonic-gate 	 * Firstly, allocate the authentication parameters' storage
13110Sstevel@tonic-gate 	 */
13120Sstevel@tonic-gate 	mutex_enter(&rqcred_lock);
13130Sstevel@tonic-gate 	if (rqcred_head) {
13140Sstevel@tonic-gate 		cred_area = rqcred_head;
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate 		/* LINTED pointer alignment */
13170Sstevel@tonic-gate 		rqcred_head = *(caddr_t *)rqcred_head;
13180Sstevel@tonic-gate 		mutex_exit(&rqcred_lock);
13190Sstevel@tonic-gate 	} else {
13200Sstevel@tonic-gate 		mutex_exit(&rqcred_lock);
13210Sstevel@tonic-gate 		cred_area = kmem_alloc(2 * MAX_AUTH_BYTES + RQCRED_SIZE,
13220Sstevel@tonic-gate 		    KM_SLEEP);
13230Sstevel@tonic-gate 	}
13240Sstevel@tonic-gate 	msg.rm_call.cb_cred.oa_base = cred_area;
13250Sstevel@tonic-gate 	msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
13260Sstevel@tonic-gate 	r.rq_clntcred = &(cred_area[2 * MAX_AUTH_BYTES]);
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate 	/*
13290Sstevel@tonic-gate 	 * Now receive a message from the transport.
13300Sstevel@tonic-gate 	 */
13310Sstevel@tonic-gate 	if (SVC_RECV(clone_xprt, mp, &msg)) {
13320Sstevel@tonic-gate 		void (*dispatchroutine) (struct svc_req *, SVCXPRT *);
13330Sstevel@tonic-gate 		rpcvers_t vers_min;
13340Sstevel@tonic-gate 		rpcvers_t vers_max;
13350Sstevel@tonic-gate 		bool_t no_dispatch;
13360Sstevel@tonic-gate 		enum auth_stat why;
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 		/*
13390Sstevel@tonic-gate 		 * Find the registered program and call its
13400Sstevel@tonic-gate 		 * dispatch routine.
13410Sstevel@tonic-gate 		 */
13420Sstevel@tonic-gate 		r.rq_xprt = clone_xprt;
13430Sstevel@tonic-gate 		r.rq_prog = msg.rm_call.cb_prog;
13440Sstevel@tonic-gate 		r.rq_vers = msg.rm_call.cb_vers;
13450Sstevel@tonic-gate 		r.rq_proc = msg.rm_call.cb_proc;
13460Sstevel@tonic-gate 		r.rq_cred = msg.rm_call.cb_cred;
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 		/*
13490Sstevel@tonic-gate 		 * First authenticate the message.
13500Sstevel@tonic-gate 		 */
13510Sstevel@tonic-gate 		TRACE_0(TR_FAC_KRPC, TR_SVC_GETREQ_AUTH_START,
13520Sstevel@tonic-gate 		    "svc_getreq_auth_start:");
13530Sstevel@tonic-gate 		if ((why = sec_svc_msg(&r, &msg, &no_dispatch)) != AUTH_OK) {
13540Sstevel@tonic-gate 			TRACE_1(TR_FAC_KRPC, TR_SVC_GETREQ_AUTH_END,
13550Sstevel@tonic-gate 			    "svc_getreq_auth_end:(%S)", "failed");
13560Sstevel@tonic-gate 			svcerr_auth(clone_xprt, why);
13570Sstevel@tonic-gate 			/*
13580Sstevel@tonic-gate 			 * Free the arguments.
13590Sstevel@tonic-gate 			 */
13600Sstevel@tonic-gate 			(void) SVC_FREEARGS(clone_xprt, NULL, NULL);
13610Sstevel@tonic-gate 		} else if (no_dispatch) {
13620Sstevel@tonic-gate 			/*
13630Sstevel@tonic-gate 			 * XXX - when bug id 4053736 is done, remove
13640Sstevel@tonic-gate 			 * the SVC_FREEARGS() call.
13650Sstevel@tonic-gate 			 */
13660Sstevel@tonic-gate 			(void) SVC_FREEARGS(clone_xprt, NULL, NULL);
13670Sstevel@tonic-gate 		} else {
13680Sstevel@tonic-gate 			TRACE_1(TR_FAC_KRPC, TR_SVC_GETREQ_AUTH_END,
13690Sstevel@tonic-gate 			    "svc_getreq_auth_end:(%S)", "good");
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate 			dispatchroutine = svc_callout_find(clone_xprt,
13720Sstevel@tonic-gate 			    r.rq_prog, r.rq_vers, &vers_min, &vers_max);
13730Sstevel@tonic-gate 
13740Sstevel@tonic-gate 			if (dispatchroutine) {
13750Sstevel@tonic-gate 				(*dispatchroutine) (&r, clone_xprt);
13760Sstevel@tonic-gate 			} else {
13770Sstevel@tonic-gate 				/*
13780Sstevel@tonic-gate 				 * If we got here, the program or version
13790Sstevel@tonic-gate 				 * is not served ...
13800Sstevel@tonic-gate 				 */
13810Sstevel@tonic-gate 				if (vers_max == 0 ||
13820Sstevel@tonic-gate 				    version_keepquiet(clone_xprt))
13830Sstevel@tonic-gate 					svcerr_noprog(clone_xprt);
13840Sstevel@tonic-gate 				else
13850Sstevel@tonic-gate 					svcerr_progvers(clone_xprt, vers_min,
13860Sstevel@tonic-gate 					    vers_max);
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 				/*
13890Sstevel@tonic-gate 				 * Free the arguments. For successful calls
13900Sstevel@tonic-gate 				 * this is done by the dispatch routine.
13910Sstevel@tonic-gate 				 */
13920Sstevel@tonic-gate 				(void) SVC_FREEARGS(clone_xprt, NULL, NULL);
13930Sstevel@tonic-gate 				/* Fall through to ... */
13940Sstevel@tonic-gate 			}
13950Sstevel@tonic-gate 			/*
13960Sstevel@tonic-gate 			 * Call cleanup procedure for RPCSEC_GSS.
13970Sstevel@tonic-gate 			 * This is a hack since there is currently no
13980Sstevel@tonic-gate 			 * op, such as SVC_CLEANAUTH. rpc_gss_cleanup
13990Sstevel@tonic-gate 			 * should only be called for a non null proc.
14000Sstevel@tonic-gate 			 * Null procs in RPC GSS are overloaded to
14010Sstevel@tonic-gate 			 * provide context setup and control. The main
14020Sstevel@tonic-gate 			 * purpose of rpc_gss_cleanup is to decrement the
14030Sstevel@tonic-gate 			 * reference count associated with the cached
14040Sstevel@tonic-gate 			 * GSS security context. We should never get here
14050Sstevel@tonic-gate 			 * for an RPCSEC_GSS null proc since *no_dispatch
14060Sstevel@tonic-gate 			 * would have been set to true from sec_svc_msg above.
14070Sstevel@tonic-gate 			 */
14080Sstevel@tonic-gate 			if (r.rq_cred.oa_flavor == RPCSEC_GSS)
14090Sstevel@tonic-gate 				rpc_gss_cleanup(clone_xprt);
14100Sstevel@tonic-gate 		}
14110Sstevel@tonic-gate 	}
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	/*
14140Sstevel@tonic-gate 	 * Free authentication parameters' storage
14150Sstevel@tonic-gate 	 */
14160Sstevel@tonic-gate 	mutex_enter(&rqcred_lock);
14170Sstevel@tonic-gate 	/* LINTED pointer alignment */
14180Sstevel@tonic-gate 	*(caddr_t *)cred_area = rqcred_head;
14190Sstevel@tonic-gate 	rqcred_head = cred_area;
14200Sstevel@tonic-gate 	mutex_exit(&rqcred_lock);
14210Sstevel@tonic-gate }
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate /*
14240Sstevel@tonic-gate  * Allocate new clone transport handle.
14250Sstevel@tonic-gate  */
14260Sstevel@tonic-gate static SVCXPRT *
14270Sstevel@tonic-gate svc_clone_init(void)
14280Sstevel@tonic-gate {
14290Sstevel@tonic-gate 	SVCXPRT *clone_xprt;
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 	clone_xprt = kmem_zalloc(sizeof (SVCXPRT), KM_SLEEP);
14320Sstevel@tonic-gate 	clone_xprt->xp_cred = crget();
14330Sstevel@tonic-gate 	return (clone_xprt);
14340Sstevel@tonic-gate }
14350Sstevel@tonic-gate 
14360Sstevel@tonic-gate /*
14370Sstevel@tonic-gate  * Free memory allocated by svc_clone_init.
14380Sstevel@tonic-gate  */
14390Sstevel@tonic-gate static void
14400Sstevel@tonic-gate svc_clone_free(SVCXPRT *clone_xprt)
14410Sstevel@tonic-gate {
14420Sstevel@tonic-gate 	/* Fre credentials from crget() */
14430Sstevel@tonic-gate 	if (clone_xprt->xp_cred)
14440Sstevel@tonic-gate 		crfree(clone_xprt->xp_cred);
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 	kmem_free(clone_xprt, sizeof (SVCXPRT));
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate /*
14500Sstevel@tonic-gate  * Link a per-thread clone transport handle to a master
14510Sstevel@tonic-gate  * - increment a thread reference count on the master
14520Sstevel@tonic-gate  * - copy some of the master's fields to the clone
14530Sstevel@tonic-gate  * - call a transport specific clone routine.
14540Sstevel@tonic-gate  */
14550Sstevel@tonic-gate static void
14560Sstevel@tonic-gate svc_clone_link(SVCMASTERXPRT *xprt, SVCXPRT *clone_xprt)
14570Sstevel@tonic-gate {
14580Sstevel@tonic-gate 	cred_t *cred = clone_xprt->xp_cred;
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate 	ASSERT(cred);
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	/*
14630Sstevel@tonic-gate 	 * Bump up master's thread count.
14640Sstevel@tonic-gate 	 * Linking a per-thread clone transport handle to a master
14650Sstevel@tonic-gate 	 * associates a service thread with the master.
14660Sstevel@tonic-gate 	 */
14670Sstevel@tonic-gate 	mutex_enter(&xprt->xp_thread_lock);
14680Sstevel@tonic-gate 	xprt->xp_threads++;
14690Sstevel@tonic-gate 	mutex_exit(&xprt->xp_thread_lock);
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate 	/* Clear everything */
14720Sstevel@tonic-gate 	bzero(clone_xprt, sizeof (SVCXPRT));
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate 	/* Set pointer to the master transport stucture */
14750Sstevel@tonic-gate 	clone_xprt->xp_master = xprt;
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate 	/* Structure copy of all the common fields */
14780Sstevel@tonic-gate 	clone_xprt->xp_xpc = xprt->xp_xpc;
14790Sstevel@tonic-gate 
14800Sstevel@tonic-gate 	/* Restore per-thread fields (xp_cred) */
14810Sstevel@tonic-gate 	clone_xprt->xp_cred = cred;
14820Sstevel@tonic-gate 
14830Sstevel@tonic-gate 	/*
14840Sstevel@tonic-gate 	 * NOTICE: There is no transport-type specific code now.
14850Sstevel@tonic-gate 	 *	   If you want to add a transport-type specific cloning code
14860Sstevel@tonic-gate 	 *	   add one more operation (e.g. xp_clone()) to svc_ops,
14870Sstevel@tonic-gate 	 *	   implement it for each transport type, and call it here
14880Sstevel@tonic-gate 	 *	   through an appropriate macro (e.g. SVC_CLONE()).
14890Sstevel@tonic-gate 	 */
14900Sstevel@tonic-gate }
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate /*
14930Sstevel@tonic-gate  * Unlink a non-detached clone transport handle from a master
14940Sstevel@tonic-gate  * - decrement a thread reference count on the master
14950Sstevel@tonic-gate  * - if the transport is closing (xp_wq is NULL) call svc_xprt_cleanup();
14960Sstevel@tonic-gate  *   if this is the last non-detached/absolute thread on this transport
14970Sstevel@tonic-gate  *   then it will close/destroy the transport
14980Sstevel@tonic-gate  * - call transport specific function to destroy the clone handle
14990Sstevel@tonic-gate  * - clear xp_master to avoid recursion.
15000Sstevel@tonic-gate  */
15010Sstevel@tonic-gate static void
15020Sstevel@tonic-gate svc_clone_unlink(SVCXPRT *clone_xprt)
15030Sstevel@tonic-gate {
15040Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = clone_xprt->xp_master;
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	/* This cannot be a detached thread */
15070Sstevel@tonic-gate 	ASSERT(!clone_xprt->xp_detached);
15080Sstevel@tonic-gate 	ASSERT(xprt->xp_threads > 0);
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate 	/* Decrement a reference count on the transport */
15110Sstevel@tonic-gate 	mutex_enter(&xprt->xp_thread_lock);
15120Sstevel@tonic-gate 	xprt->xp_threads--;
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 	/* svc_xprt_cleanup() unlocks xp_thread_lock or destroys xprt */
15150Sstevel@tonic-gate 	if (xprt->xp_wq)
15160Sstevel@tonic-gate 		mutex_exit(&xprt->xp_thread_lock);
15170Sstevel@tonic-gate 	else
15180Sstevel@tonic-gate 		svc_xprt_cleanup(xprt, FALSE);
15190Sstevel@tonic-gate 
15200Sstevel@tonic-gate 	/* Call a transport specific clone `destroy' function */
15210Sstevel@tonic-gate 	SVC_CLONE_DESTROY(clone_xprt);
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate 	/* Clear xp_master */
15240Sstevel@tonic-gate 	clone_xprt->xp_master = NULL;
15250Sstevel@tonic-gate }
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate /*
15280Sstevel@tonic-gate  * Unlink a detached clone transport handle from a master
15290Sstevel@tonic-gate  * - decrement the thread count on the master
15300Sstevel@tonic-gate  * - if the transport is closing (xp_wq is NULL) call svc_xprt_cleanup();
15310Sstevel@tonic-gate  *   if this is the last thread on this transport then it will destroy
15320Sstevel@tonic-gate  *   the transport.
15330Sstevel@tonic-gate  * - call a transport specific function to destroy the clone handle
15340Sstevel@tonic-gate  * - clear xp_master to avoid recursion.
15350Sstevel@tonic-gate  */
15360Sstevel@tonic-gate static void
15370Sstevel@tonic-gate svc_clone_unlinkdetached(SVCXPRT *clone_xprt)
15380Sstevel@tonic-gate {
15390Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = clone_xprt->xp_master;
15400Sstevel@tonic-gate 
15410Sstevel@tonic-gate 	/* This must be a detached thread */
15420Sstevel@tonic-gate 	ASSERT(clone_xprt->xp_detached);
15430Sstevel@tonic-gate 	ASSERT(xprt->xp_detached_threads > 0);
15440Sstevel@tonic-gate 	ASSERT(xprt->xp_threads + xprt->xp_detached_threads > 0);
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	/* Grab xprt->xp_thread_lock and decrement link counts */
15470Sstevel@tonic-gate 	mutex_enter(&xprt->xp_thread_lock);
15480Sstevel@tonic-gate 	xprt->xp_detached_threads--;
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate 	/* svc_xprt_cleanup() unlocks xp_thread_lock or destroys xprt */
15510Sstevel@tonic-gate 	if (xprt->xp_wq)
15520Sstevel@tonic-gate 		mutex_exit(&xprt->xp_thread_lock);
15530Sstevel@tonic-gate 	else
15540Sstevel@tonic-gate 		svc_xprt_cleanup(xprt, TRUE);
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 	/* Call transport specific clone `destroy' function */
15570Sstevel@tonic-gate 	SVC_CLONE_DESTROY(clone_xprt);
15580Sstevel@tonic-gate 
15590Sstevel@tonic-gate 	/* Clear xp_master */
15600Sstevel@tonic-gate 	clone_xprt->xp_master = NULL;
15610Sstevel@tonic-gate }
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate /*
15640Sstevel@tonic-gate  * Try to exit a non-detached service thread
15650Sstevel@tonic-gate  * - check if there are enough threads left
15660Sstevel@tonic-gate  * - if this thread (ie its clone transport handle) are linked
15670Sstevel@tonic-gate  *   to a master transport then unlink it
15680Sstevel@tonic-gate  * - free the clone structure
15690Sstevel@tonic-gate  * - return to userland for thread exit
15700Sstevel@tonic-gate  *
15710Sstevel@tonic-gate  * If this is the last non-detached or the last thread on this
15720Sstevel@tonic-gate  * transport then the call to svc_clone_unlink() will, respectively,
15730Sstevel@tonic-gate  * close and/or destroy the transport.
15740Sstevel@tonic-gate  */
15750Sstevel@tonic-gate static void
15760Sstevel@tonic-gate svc_thread_exit(SVCPOOL *pool, SVCXPRT *clone_xprt)
15770Sstevel@tonic-gate {
15780Sstevel@tonic-gate 	if (clone_xprt->xp_master)
15790Sstevel@tonic-gate 		svc_clone_unlink(clone_xprt);
15800Sstevel@tonic-gate 	svc_clone_free(clone_xprt);
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
15830Sstevel@tonic-gate 	pool->p_threads--;
15840Sstevel@tonic-gate 	if (pool->p_closing && svc_pool_tryexit(pool))
15850Sstevel@tonic-gate 		/* return -  thread exit will be handled at user level */
15860Sstevel@tonic-gate 		return;
15870Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	/* return -  thread exit will be handled at user level */
15900Sstevel@tonic-gate }
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate /*
15930Sstevel@tonic-gate  * Exit a detached service thread that returned to svc_run
15940Sstevel@tonic-gate  * - decrement the `detached thread' count for the pool
15950Sstevel@tonic-gate  * - unlink the detached clone transport handle from the master
15960Sstevel@tonic-gate  * - free the clone structure
15970Sstevel@tonic-gate  * - return to userland for thread exit
15980Sstevel@tonic-gate  *
15990Sstevel@tonic-gate  * If this is the last thread on this transport then the call
16000Sstevel@tonic-gate  * to svc_clone_unlinkdetached() will destroy the transport.
16010Sstevel@tonic-gate  */
16020Sstevel@tonic-gate static void
16030Sstevel@tonic-gate svc_thread_exitdetached(SVCPOOL *pool, SVCXPRT *clone_xprt)
16040Sstevel@tonic-gate {
16050Sstevel@tonic-gate 	/* This must be a detached thread */
16060Sstevel@tonic-gate 	ASSERT(clone_xprt->xp_master);
16070Sstevel@tonic-gate 	ASSERT(clone_xprt->xp_detached);
16080Sstevel@tonic-gate 	ASSERT(!MUTEX_HELD(&pool->p_thread_lock));
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate 	svc_clone_unlinkdetached(clone_xprt);
16110Sstevel@tonic-gate 	svc_clone_free(clone_xprt);
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	ASSERT(pool->p_reserved_threads >= 0);
16160Sstevel@tonic-gate 	ASSERT(pool->p_detached_threads > 0);
16170Sstevel@tonic-gate 
16180Sstevel@tonic-gate 	pool->p_detached_threads--;
16190Sstevel@tonic-gate 	if (pool->p_closing && svc_pool_tryexit(pool))
16200Sstevel@tonic-gate 		/* return -  thread exit will be handled at user level */
16210Sstevel@tonic-gate 		return;
16220Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	/* return -  thread exit will be handled at user level */
16250Sstevel@tonic-gate }
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate /*
16280Sstevel@tonic-gate  * PSARC 2003/523 Contract Private Interface
16290Sstevel@tonic-gate  * svc_wait
16300Sstevel@tonic-gate  * Changes must be reviewed by Solaris File Sharing
16310Sstevel@tonic-gate  * Changes must be communicated to contract-2003-523@sun.com
16320Sstevel@tonic-gate  */
16330Sstevel@tonic-gate int
16340Sstevel@tonic-gate svc_wait(int id)
16350Sstevel@tonic-gate {
16360Sstevel@tonic-gate 	SVCPOOL *pool;
16370Sstevel@tonic-gate 	int	err = 0;
16380Sstevel@tonic-gate 	struct svc_globals *svc;
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	svc = zone_getspecific(svc_zone_key, curproc->p_zone);
16410Sstevel@tonic-gate 	mutex_enter(&svc->svc_plock);
16420Sstevel@tonic-gate 	pool = svc_pool_find(svc, id);
16430Sstevel@tonic-gate 	mutex_exit(&svc->svc_plock);
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate 	if (pool == NULL)
16460Sstevel@tonic-gate 		return (ENOENT);
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	mutex_enter(&pool->p_user_lock);
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 	/* Check if there's already a user thread waiting on this pool */
16510Sstevel@tonic-gate 	if (pool->p_user_waiting) {
16520Sstevel@tonic-gate 		mutex_exit(&pool->p_user_lock);
16530Sstevel@tonic-gate 		return (EBUSY);
16540Sstevel@tonic-gate 	}
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 	pool->p_user_waiting = TRUE;
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 	/* Go to sleep, waiting for the signaled flag. */
16590Sstevel@tonic-gate 	while (!pool->p_signal_create_thread && !pool->p_user_exit) {
16600Sstevel@tonic-gate 		if (cv_wait_sig(&pool->p_user_cv, &pool->p_user_lock) == 0) {
16610Sstevel@tonic-gate 			/* Interrupted, return to handle exit or signal */
16620Sstevel@tonic-gate 			pool->p_user_waiting = FALSE;
16630Sstevel@tonic-gate 			pool->p_signal_create_thread = FALSE;
16640Sstevel@tonic-gate 			mutex_exit(&pool->p_user_lock);
16650Sstevel@tonic-gate 
16660Sstevel@tonic-gate 			/*
16670Sstevel@tonic-gate 			 * Thread has been interrupted and therefore
16680Sstevel@tonic-gate 			 * the service daemon is leaving as well so
16690Sstevel@tonic-gate 			 * let's go ahead and remove the service
16700Sstevel@tonic-gate 			 * pool at this time.
16710Sstevel@tonic-gate 			 */
16720Sstevel@tonic-gate 			mutex_enter(&svc->svc_plock);
16730Sstevel@tonic-gate 			svc_pool_unregister(svc, pool);
16740Sstevel@tonic-gate 			mutex_exit(&svc->svc_plock);
16750Sstevel@tonic-gate 
16760Sstevel@tonic-gate 			return (EINTR);
16770Sstevel@tonic-gate 		}
16780Sstevel@tonic-gate 	}
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	pool->p_signal_create_thread = FALSE;
16810Sstevel@tonic-gate 	pool->p_user_waiting = FALSE;
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 	/*
16840Sstevel@tonic-gate 	 * About to exit the service pool. Set return value
16850Sstevel@tonic-gate 	 * to let the userland code know our intent. Signal
16860Sstevel@tonic-gate 	 * svc_thread_creator() so that it can clean up the
16870Sstevel@tonic-gate 	 * pool structure.
16880Sstevel@tonic-gate 	 */
16890Sstevel@tonic-gate 	if (pool->p_user_exit) {
16900Sstevel@tonic-gate 		err = ECANCELED;
16910Sstevel@tonic-gate 		cv_signal(&pool->p_user_cv);
16920Sstevel@tonic-gate 	}
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	mutex_exit(&pool->p_user_lock);
16950Sstevel@tonic-gate 
16960Sstevel@tonic-gate 	/* Return to userland with error code, for possible thread creation. */
16970Sstevel@tonic-gate 	return (err);
16980Sstevel@tonic-gate }
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate /*
17010Sstevel@tonic-gate  * `Service threads' creator thread.
17020Sstevel@tonic-gate  * The creator thread waits for a signal to create new thread.
17030Sstevel@tonic-gate  */
17040Sstevel@tonic-gate static void
17050Sstevel@tonic-gate svc_thread_creator(SVCPOOL *pool)
17060Sstevel@tonic-gate {
17070Sstevel@tonic-gate 	callb_cpr_t cpr_info;	/* CPR info for the creator thread */
17080Sstevel@tonic-gate 
17090Sstevel@tonic-gate 	CALLB_CPR_INIT(&cpr_info, &pool->p_creator_lock, callb_generic_cpr,
17100Sstevel@tonic-gate 	    "svc_thread_creator");
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate 	for (;;) {
17130Sstevel@tonic-gate 		mutex_enter(&pool->p_creator_lock);
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate 		/* Check if someone set the exit flag */
17160Sstevel@tonic-gate 		if (pool->p_creator_exit)
17170Sstevel@tonic-gate 			break;
17180Sstevel@tonic-gate 
17190Sstevel@tonic-gate 		/* Clear the `signaled' flag and go asleep */
17200Sstevel@tonic-gate 		pool->p_creator_signaled = FALSE;
17210Sstevel@tonic-gate 
17220Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cpr_info);
17230Sstevel@tonic-gate 		cv_wait(&pool->p_creator_cv, &pool->p_creator_lock);
17240Sstevel@tonic-gate 		CALLB_CPR_SAFE_END(&cpr_info, &pool->p_creator_lock);
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 		/* Check if someone signaled to exit */
17270Sstevel@tonic-gate 		if (pool->p_creator_exit)
17280Sstevel@tonic-gate 			break;
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 		mutex_exit(&pool->p_creator_lock);
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 		mutex_enter(&pool->p_thread_lock);
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 		/*
17350Sstevel@tonic-gate 		 * When the pool is in closing state and all the transports
17360Sstevel@tonic-gate 		 * are gone the creator should not create any new threads.
17370Sstevel@tonic-gate 		 */
17380Sstevel@tonic-gate 		if (pool->p_closing) {
17390Sstevel@tonic-gate 			rw_enter(&pool->p_lrwlock, RW_READER);
17400Sstevel@tonic-gate 			if (pool->p_lcount == 0) {
17410Sstevel@tonic-gate 				rw_exit(&pool->p_lrwlock);
17420Sstevel@tonic-gate 				mutex_exit(&pool->p_thread_lock);
17430Sstevel@tonic-gate 				continue;
17440Sstevel@tonic-gate 			}
17450Sstevel@tonic-gate 			rw_exit(&pool->p_lrwlock);
17460Sstevel@tonic-gate 		}
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate 		/*
17490Sstevel@tonic-gate 		 * Create a new service thread now.
17500Sstevel@tonic-gate 		 */
17510Sstevel@tonic-gate 		ASSERT(pool->p_reserved_threads >= 0);
17520Sstevel@tonic-gate 		ASSERT(pool->p_detached_threads >= 0);
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 		if (pool->p_threads + pool->p_detached_threads <
17550Sstevel@tonic-gate 		    pool->p_maxthreads) {
17560Sstevel@tonic-gate 			/*
17570Sstevel@tonic-gate 			 * Signal the service pool wait thread
17580Sstevel@tonic-gate 			 * only if it hasn't already been signaled.
17590Sstevel@tonic-gate 			 */
17600Sstevel@tonic-gate 			mutex_enter(&pool->p_user_lock);
17610Sstevel@tonic-gate 			if (pool->p_signal_create_thread == FALSE) {
17620Sstevel@tonic-gate 				pool->p_signal_create_thread = TRUE;
17630Sstevel@tonic-gate 				cv_signal(&pool->p_user_cv);
17640Sstevel@tonic-gate 			}
17650Sstevel@tonic-gate 			mutex_exit(&pool->p_user_lock);
17660Sstevel@tonic-gate 
17670Sstevel@tonic-gate 		}
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 		mutex_exit(&pool->p_thread_lock);
17700Sstevel@tonic-gate 	}
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate 	/*
17730Sstevel@tonic-gate 	 * Pool is closed. Cleanup and exit.
17740Sstevel@tonic-gate 	 */
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate 	/* Signal userland creator thread that it can stop now. */
17770Sstevel@tonic-gate 	mutex_enter(&pool->p_user_lock);
17780Sstevel@tonic-gate 	pool->p_user_exit = TRUE;
17790Sstevel@tonic-gate 	cv_broadcast(&pool->p_user_cv);
17800Sstevel@tonic-gate 	mutex_exit(&pool->p_user_lock);
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 	/* Wait for svc_wait() to be done with the pool */
17830Sstevel@tonic-gate 	mutex_enter(&pool->p_user_lock);
17840Sstevel@tonic-gate 	while (pool->p_user_waiting) {
17850Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cpr_info);
17860Sstevel@tonic-gate 		cv_wait(&pool->p_user_cv, &pool->p_user_lock);
17870Sstevel@tonic-gate 		CALLB_CPR_SAFE_END(&cpr_info, &pool->p_creator_lock);
17880Sstevel@tonic-gate 	}
17890Sstevel@tonic-gate 	mutex_exit(&pool->p_user_lock);
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cpr_info);
17920Sstevel@tonic-gate 	svc_pool_cleanup(pool);
17930Sstevel@tonic-gate 	zthread_exit();
17940Sstevel@tonic-gate }
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate /*
17970Sstevel@tonic-gate  * If the creator thread  is idle signal it to create
17980Sstevel@tonic-gate  * a new service thread.
17990Sstevel@tonic-gate  */
18000Sstevel@tonic-gate static void
18010Sstevel@tonic-gate svc_creator_signal(SVCPOOL *pool)
18020Sstevel@tonic-gate {
18030Sstevel@tonic-gate 	mutex_enter(&pool->p_creator_lock);
18040Sstevel@tonic-gate 	if (pool->p_creator_signaled == FALSE) {
18050Sstevel@tonic-gate 		pool->p_creator_signaled = TRUE;
18060Sstevel@tonic-gate 		cv_signal(&pool->p_creator_cv);
18070Sstevel@tonic-gate 	}
18080Sstevel@tonic-gate 	mutex_exit(&pool->p_creator_lock);
18090Sstevel@tonic-gate }
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate /*
18120Sstevel@tonic-gate  * Notify the creator thread to clean up and exit.
18130Sstevel@tonic-gate  */
18140Sstevel@tonic-gate static void
18150Sstevel@tonic-gate svc_creator_signalexit(SVCPOOL *pool)
18160Sstevel@tonic-gate {
18170Sstevel@tonic-gate 	mutex_enter(&pool->p_creator_lock);
18180Sstevel@tonic-gate 	pool->p_creator_exit = TRUE;
18190Sstevel@tonic-gate 	cv_signal(&pool->p_creator_cv);
18200Sstevel@tonic-gate 	mutex_exit(&pool->p_creator_lock);
18210Sstevel@tonic-gate }
18220Sstevel@tonic-gate 
18230Sstevel@tonic-gate /*
18240Sstevel@tonic-gate  * Polling part of the svc_run().
18250Sstevel@tonic-gate  * - search for a transport with a pending request
18260Sstevel@tonic-gate  * - when one is found then latch the request lock and return to svc_run()
18270Sstevel@tonic-gate  * - if there is no request go asleep and wait for a signal
18280Sstevel@tonic-gate  * - handle two exceptions:
18290Sstevel@tonic-gate  *   a) current transport is closing
18300Sstevel@tonic-gate  *   b) timeout waiting for a new request
18310Sstevel@tonic-gate  *   in both cases return to svc_run()
18320Sstevel@tonic-gate  */
18330Sstevel@tonic-gate static SVCMASTERXPRT *
18340Sstevel@tonic-gate svc_poll(SVCPOOL *pool, SVCMASTERXPRT *xprt, SVCXPRT *clone_xprt)
18350Sstevel@tonic-gate {
18360Sstevel@tonic-gate 	/*
18370Sstevel@tonic-gate 	 * Main loop iterates until
18380Sstevel@tonic-gate 	 * a) we find a pending request,
18390Sstevel@tonic-gate 	 * b) detect that the current transport is closing
18400Sstevel@tonic-gate 	 * c) time out waiting for a new request.
18410Sstevel@tonic-gate 	 */
18420Sstevel@tonic-gate 	for (;;) {
18430Sstevel@tonic-gate 		SVCMASTERXPRT *next;
18440Sstevel@tonic-gate 		clock_t timeleft;
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate 		/*
18470Sstevel@tonic-gate 		 * Step 1.
18480Sstevel@tonic-gate 		 * Check if there is a pending request on the current
18490Sstevel@tonic-gate 		 * transport handle so that we can avoid cloning.
18500Sstevel@tonic-gate 		 * If so then decrement the `pending-request' count for
18510Sstevel@tonic-gate 		 * the pool and return to svc_run().
18520Sstevel@tonic-gate 		 *
18530Sstevel@tonic-gate 		 * We need to prevent a potential starvation. When
18540Sstevel@tonic-gate 		 * a selected transport has all pending requests coming in
18550Sstevel@tonic-gate 		 * all the time then the service threads will never switch to
18560Sstevel@tonic-gate 		 * another transport. With a limited number of service
18570Sstevel@tonic-gate 		 * threads some transports may be never serviced.
18580Sstevel@tonic-gate 		 * To prevent such a scenario we pick up at most
18590Sstevel@tonic-gate 		 * pool->p_max_same_xprt requests from the same transport
18600Sstevel@tonic-gate 		 * and then take a hint from the xprt-ready queue or walk
18610Sstevel@tonic-gate 		 * the transport list.
18620Sstevel@tonic-gate 		 */
18630Sstevel@tonic-gate 		if (xprt && xprt->xp_req_head && (!pool->p_qoverflow ||
18640Sstevel@tonic-gate 		    clone_xprt->xp_same_xprt++ < pool->p_max_same_xprt)) {
18650Sstevel@tonic-gate 			mutex_enter(&xprt->xp_req_lock);
18660Sstevel@tonic-gate 			if (xprt->xp_req_head) {
18670Sstevel@tonic-gate 				mutex_enter(&pool->p_req_lock);
18680Sstevel@tonic-gate 				pool->p_reqs--;
18690Sstevel@tonic-gate 				mutex_exit(&pool->p_req_lock);
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 				return (xprt);
18720Sstevel@tonic-gate 			}
18730Sstevel@tonic-gate 			mutex_exit(&xprt->xp_req_lock);
18740Sstevel@tonic-gate 		}
18750Sstevel@tonic-gate 		clone_xprt->xp_same_xprt = 0;
18760Sstevel@tonic-gate 
18770Sstevel@tonic-gate 		/*
18780Sstevel@tonic-gate 		 * Step 2.
18790Sstevel@tonic-gate 		 * If there is no request on the current transport try to
18800Sstevel@tonic-gate 		 * find another transport with a pending request.
18810Sstevel@tonic-gate 		 */
18820Sstevel@tonic-gate 		mutex_enter(&pool->p_req_lock);
18830Sstevel@tonic-gate 		pool->p_walkers++;
18840Sstevel@tonic-gate 		mutex_exit(&pool->p_req_lock);
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 		/*
18870Sstevel@tonic-gate 		 * Make sure that transports will not be destroyed just
18880Sstevel@tonic-gate 		 * while we are checking them.
18890Sstevel@tonic-gate 		 */
18900Sstevel@tonic-gate 		rw_enter(&pool->p_lrwlock, RW_READER);
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 		for (;;) {
18930Sstevel@tonic-gate 			SVCMASTERXPRT *hint;
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 			/*
18960Sstevel@tonic-gate 			 * Get the next transport from the xprt-ready queue.
18970Sstevel@tonic-gate 			 * This is a hint. There is no guarantee that the
18980Sstevel@tonic-gate 			 * transport still has a pending request since it
18990Sstevel@tonic-gate 			 * could be picked up by another thread in step 1.
19000Sstevel@tonic-gate 			 *
19010Sstevel@tonic-gate 			 * If the transport has a pending request then keep
19020Sstevel@tonic-gate 			 * it locked. Decrement the `pending-requests' for
19030Sstevel@tonic-gate 			 * the pool and `walking-threads' counts, and return
19040Sstevel@tonic-gate 			 * to svc_run().
19050Sstevel@tonic-gate 			 */
19060Sstevel@tonic-gate 			hint = svc_xprt_qget(pool);
19070Sstevel@tonic-gate 
19080Sstevel@tonic-gate 			if (hint && hint->xp_req_head) {
19090Sstevel@tonic-gate 				mutex_enter(&hint->xp_req_lock);
19100Sstevel@tonic-gate 				if (hint->xp_req_head) {
19110Sstevel@tonic-gate 					rw_exit(&pool->p_lrwlock);
19120Sstevel@tonic-gate 
19130Sstevel@tonic-gate 					mutex_enter(&pool->p_req_lock);
19140Sstevel@tonic-gate 					pool->p_reqs--;
19150Sstevel@tonic-gate 					pool->p_walkers--;
19160Sstevel@tonic-gate 					mutex_exit(&pool->p_req_lock);
19170Sstevel@tonic-gate 
19180Sstevel@tonic-gate 					return (hint);
19190Sstevel@tonic-gate 				}
19200Sstevel@tonic-gate 				mutex_exit(&hint->xp_req_lock);
19210Sstevel@tonic-gate 			}
19220Sstevel@tonic-gate 
19230Sstevel@tonic-gate 			/*
19240Sstevel@tonic-gate 			 * If there was no hint in the xprt-ready queue then
19250Sstevel@tonic-gate 			 * - if there is less pending requests than polling
19260Sstevel@tonic-gate 			 *   threads go asleep
19270Sstevel@tonic-gate 			 * - otherwise check if there was an overflow in the
19280Sstevel@tonic-gate 			 *   xprt-ready queue; if so, then we need to break
19290Sstevel@tonic-gate 			 *   the `drain' mode
19300Sstevel@tonic-gate 			 */
19310Sstevel@tonic-gate 			if (hint == NULL) {
19320Sstevel@tonic-gate 				if (pool->p_reqs < pool->p_walkers) {
19330Sstevel@tonic-gate 					mutex_enter(&pool->p_req_lock);
19340Sstevel@tonic-gate 					if (pool->p_reqs < pool->p_walkers)
19350Sstevel@tonic-gate 						goto sleep;
19360Sstevel@tonic-gate 					mutex_exit(&pool->p_req_lock);
19370Sstevel@tonic-gate 				}
19380Sstevel@tonic-gate 				if (pool->p_qoverflow) {
19390Sstevel@tonic-gate 					break;
19400Sstevel@tonic-gate 				}
19410Sstevel@tonic-gate 			}
19420Sstevel@tonic-gate 		}
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 		/*
19450Sstevel@tonic-gate 		 * If there was an overflow in the xprt-ready queue then we
19460Sstevel@tonic-gate 		 * need to switch to the `drain' mode, i.e. walk through the
19470Sstevel@tonic-gate 		 * pool's transport list and search for a transport with a
19480Sstevel@tonic-gate 		 * pending request. If we manage to drain all the pending
19490Sstevel@tonic-gate 		 * requests then we can clear the overflow flag. This will
19500Sstevel@tonic-gate 		 * switch svc_poll() back to taking hints from the xprt-ready
19510Sstevel@tonic-gate 		 * queue (which is generally more efficient).
19520Sstevel@tonic-gate 		 *
19530Sstevel@tonic-gate 		 * If there are no registered transports simply go asleep.
19540Sstevel@tonic-gate 		 */
19550Sstevel@tonic-gate 		if (xprt == NULL && pool->p_lhead == NULL) {
19560Sstevel@tonic-gate 			mutex_enter(&pool->p_req_lock);
19570Sstevel@tonic-gate 			goto sleep;
19580Sstevel@tonic-gate 		}
19590Sstevel@tonic-gate 
19600Sstevel@tonic-gate 		/*
19610Sstevel@tonic-gate 		 * `Walk' through the pool's list of master server
19620Sstevel@tonic-gate 		 * transport handles. Continue to loop until there are less
19630Sstevel@tonic-gate 		 * looping threads then pending requests.
19640Sstevel@tonic-gate 		 */
19650Sstevel@tonic-gate 		next = xprt ? xprt->xp_next : pool->p_lhead;
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 		for (;;) {
19680Sstevel@tonic-gate 			/*
19690Sstevel@tonic-gate 			 * Check if there is a request on this transport.
19700Sstevel@tonic-gate 			 *
19710Sstevel@tonic-gate 			 * Since blocking on a locked mutex is very expensive
19720Sstevel@tonic-gate 			 * check for a request without a lock first. If we miss
19730Sstevel@tonic-gate 			 * a request that is just being delivered but this will
19740Sstevel@tonic-gate 			 * cost at most one full walk through the list.
19750Sstevel@tonic-gate 			 */
19760Sstevel@tonic-gate 			if (next->xp_req_head) {
19770Sstevel@tonic-gate 				/*
19780Sstevel@tonic-gate 				 * Check again, now with a lock.
19790Sstevel@tonic-gate 				 */
19800Sstevel@tonic-gate 				mutex_enter(&next->xp_req_lock);
19810Sstevel@tonic-gate 				if (next->xp_req_head) {
19820Sstevel@tonic-gate 					rw_exit(&pool->p_lrwlock);
19830Sstevel@tonic-gate 
19840Sstevel@tonic-gate 					mutex_enter(&pool->p_req_lock);
19850Sstevel@tonic-gate 					pool->p_reqs--;
19860Sstevel@tonic-gate 					pool->p_walkers--;
19870Sstevel@tonic-gate 					mutex_exit(&pool->p_req_lock);
19880Sstevel@tonic-gate 
19890Sstevel@tonic-gate 					return (next);
19900Sstevel@tonic-gate 				}
19910Sstevel@tonic-gate 				mutex_exit(&next->xp_req_lock);
19920Sstevel@tonic-gate 			}
19930Sstevel@tonic-gate 
19940Sstevel@tonic-gate 			/*
19950Sstevel@tonic-gate 			 * Continue to `walk' through the pool's
19960Sstevel@tonic-gate 			 * transport list until there is less requests
19970Sstevel@tonic-gate 			 * than walkers. Check this condition without
19980Sstevel@tonic-gate 			 * a lock first to avoid contention on a mutex.
19990Sstevel@tonic-gate 			 */
20000Sstevel@tonic-gate 			if (pool->p_reqs < pool->p_walkers) {
20010Sstevel@tonic-gate 				/*
20020Sstevel@tonic-gate 				 * Check again, now with the lock.
20030Sstevel@tonic-gate 				 * If all the pending requests have been
20040Sstevel@tonic-gate 				 * picked up than clear the overflow flag.
20050Sstevel@tonic-gate 				 */
20060Sstevel@tonic-gate 				mutex_enter(&pool->p_req_lock);
20070Sstevel@tonic-gate 				if (pool->p_reqs <= 0)
20080Sstevel@tonic-gate 					svc_xprt_qreset(pool);
20090Sstevel@tonic-gate 				if (pool->p_reqs < pool->p_walkers)
20100Sstevel@tonic-gate 					break;	/* goto sleep */
20110Sstevel@tonic-gate 				mutex_exit(&pool->p_req_lock);
20120Sstevel@tonic-gate 			}
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 			next = next->xp_next;
20150Sstevel@tonic-gate 		}
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate 	sleep:
20180Sstevel@tonic-gate 		/*
20190Sstevel@tonic-gate 		 * No work to do. Stop the `walk' and go asleep.
20200Sstevel@tonic-gate 		 * Decrement the `walking-threads' count for the pool.
20210Sstevel@tonic-gate 		 */
20220Sstevel@tonic-gate 		pool->p_walkers--;
20230Sstevel@tonic-gate 		rw_exit(&pool->p_lrwlock);
20240Sstevel@tonic-gate 
20250Sstevel@tonic-gate 		/*
20260Sstevel@tonic-gate 		 * Count us as asleep, mark this thread as safe
20270Sstevel@tonic-gate 		 * for suspend and wait for a request.
20280Sstevel@tonic-gate 		 */
20290Sstevel@tonic-gate 		pool->p_asleep++;
20300Sstevel@tonic-gate 		timeleft = cv_timedwait_sig(&pool->p_req_cv, &pool->p_req_lock,
20310Sstevel@tonic-gate 		    pool->p_timeout + lbolt);
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate 		/*
20340Sstevel@tonic-gate 		 * If the drowsy flag is on this means that
20350Sstevel@tonic-gate 		 * someone has signaled a wakeup. In such a case
20360Sstevel@tonic-gate 		 * the `asleep-threads' count has already updated
20370Sstevel@tonic-gate 		 * so just clear the flag.
20380Sstevel@tonic-gate 		 *
20390Sstevel@tonic-gate 		 * If the drowsy flag is off then we need to update
20400Sstevel@tonic-gate 		 * the `asleep-threads' count.
20410Sstevel@tonic-gate 		 */
20420Sstevel@tonic-gate 		if (pool->p_drowsy) {
20430Sstevel@tonic-gate 			pool->p_drowsy = FALSE;
20440Sstevel@tonic-gate 			/*
20450Sstevel@tonic-gate 			 * If the thread is here because it timedout,
20460Sstevel@tonic-gate 			 * instead of returning SVC_ETIMEDOUT, it is
20470Sstevel@tonic-gate 			 * time to do some more work.
20480Sstevel@tonic-gate 			 */
20490Sstevel@tonic-gate 			if (timeleft == -1)
20500Sstevel@tonic-gate 				timeleft = 1;
20510Sstevel@tonic-gate 		} else {
20520Sstevel@tonic-gate 			pool->p_asleep--;
20530Sstevel@tonic-gate 		}
20540Sstevel@tonic-gate 		mutex_exit(&pool->p_req_lock);
20550Sstevel@tonic-gate 
20560Sstevel@tonic-gate 		/*
20570Sstevel@tonic-gate 		 * If we received a signal while waiting for a
20580Sstevel@tonic-gate 		 * request, inform svc_run(), so that we can return
20590Sstevel@tonic-gate 		 * to user level and restart the call.
20600Sstevel@tonic-gate 		 */
20610Sstevel@tonic-gate 		if (timeleft == 0)
20620Sstevel@tonic-gate 			return (SVC_EINTR);
20630Sstevel@tonic-gate 
20640Sstevel@tonic-gate 		/*
20650Sstevel@tonic-gate 		 * If the current transport is gone then notify
20660Sstevel@tonic-gate 		 * svc_run() to unlink from it.
20670Sstevel@tonic-gate 		 */
20680Sstevel@tonic-gate 		if (xprt && xprt->xp_wq == NULL)
20690Sstevel@tonic-gate 			return (SVC_EXPRTGONE);
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 		/*
20720Sstevel@tonic-gate 		 * If we have timed out waiting for a request inform
20730Sstevel@tonic-gate 		 * svc_run() that we probably don't need this thread.
20740Sstevel@tonic-gate 		 */
20750Sstevel@tonic-gate 		if (timeleft == -1)
20760Sstevel@tonic-gate 			return (SVC_ETIMEDOUT);
20770Sstevel@tonic-gate 	}
20780Sstevel@tonic-gate }
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate /*
20810Sstevel@tonic-gate  * Main loop of the kernel RPC server
20820Sstevel@tonic-gate  * - wait for input (find a transport with a pending request).
20830Sstevel@tonic-gate  * - dequeue the request
20840Sstevel@tonic-gate  * - call a registered server routine to process the requests
20850Sstevel@tonic-gate  *
20860Sstevel@tonic-gate  * There can many threads running concurrently in this loop
20870Sstevel@tonic-gate  * on the same or on different transports.
20880Sstevel@tonic-gate  */
20890Sstevel@tonic-gate static int
20900Sstevel@tonic-gate svc_run(SVCPOOL *pool)
20910Sstevel@tonic-gate {
20920Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = NULL;	/* master transport handle  */
20930Sstevel@tonic-gate 	SVCXPRT *clone_xprt;	/* clone for this thread    */
20940Sstevel@tonic-gate 	struct svc_globals *svc;
20950Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate 	/* Allocate a clone transport handle for this thread */
20980Sstevel@tonic-gate 	clone_xprt = svc_clone_init();
20990Sstevel@tonic-gate 
21000Sstevel@tonic-gate 	/*
21010Sstevel@tonic-gate 	 * The loop iterates until the thread becomes
21020Sstevel@tonic-gate 	 * idle too long or the transport is gone.
21030Sstevel@tonic-gate 	 */
21040Sstevel@tonic-gate 	for (;;) {
21050Sstevel@tonic-gate 		SVCMASTERXPRT *next;
21060Sstevel@tonic-gate 		mblk_t *mp;
21070Sstevel@tonic-gate 
21080Sstevel@tonic-gate 		TRACE_0(TR_FAC_KRPC, TR_SVC_RUN, "svc_run");
21090Sstevel@tonic-gate 
21100Sstevel@tonic-gate 		/*
21110Sstevel@tonic-gate 		 * If the process is exiting/killed, return
21120Sstevel@tonic-gate 		 * immediately without processing any more
21130Sstevel@tonic-gate 		 * requests.
21140Sstevel@tonic-gate 		 */
2115*390Sraf 		if (p->p_flag & (SEXITING | SKILLED)) {
21160Sstevel@tonic-gate 			svc_thread_exit(pool, clone_xprt);
21170Sstevel@tonic-gate 
21180Sstevel@tonic-gate 			/*
21190Sstevel@tonic-gate 			 * Thread has been interrupted and therefore
21200Sstevel@tonic-gate 			 * the service daemon is leaving as well so
21210Sstevel@tonic-gate 			 * let's go ahead and remove the service
21220Sstevel@tonic-gate 			 * pool at this time.
21230Sstevel@tonic-gate 			 */
21240Sstevel@tonic-gate 			svc = zone_getspecific(svc_zone_key, curproc->p_zone);
21250Sstevel@tonic-gate 			mutex_enter(&svc->svc_plock);
21260Sstevel@tonic-gate 			svc_pool_unregister(svc, pool);
21270Sstevel@tonic-gate 			mutex_exit(&svc->svc_plock);
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 			return (0);
21300Sstevel@tonic-gate 		}
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate 		/* Find a transport with a pending request */
21330Sstevel@tonic-gate 		next = svc_poll(pool, xprt, clone_xprt);
21340Sstevel@tonic-gate 
21350Sstevel@tonic-gate 		/*
21360Sstevel@tonic-gate 		 * If svc_poll() finds a transport with a request
21370Sstevel@tonic-gate 		 * it latches xp_req_lock on it. Therefore we need
21380Sstevel@tonic-gate 		 * to dequeue the request and release the lock as
21390Sstevel@tonic-gate 		 * soon as possible.
21400Sstevel@tonic-gate 		 */
21410Sstevel@tonic-gate 		ASSERT(next != NULL &&
21420Sstevel@tonic-gate 		    (next == SVC_EXPRTGONE ||
21430Sstevel@tonic-gate 		    next == SVC_ETIMEDOUT ||
21440Sstevel@tonic-gate 		    next == SVC_EINTR ||
21450Sstevel@tonic-gate 		    MUTEX_HELD(&next->xp_req_lock)));
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 		/* Ooops! Current transport is closing. Unlink now */
21480Sstevel@tonic-gate 		if (next == SVC_EXPRTGONE) {
21490Sstevel@tonic-gate 			svc_clone_unlink(clone_xprt);
21500Sstevel@tonic-gate 			xprt = NULL;
21510Sstevel@tonic-gate 			continue;
21520Sstevel@tonic-gate 		}
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate 		/* Ooops! Timeout while waiting for a request. Exit */
21550Sstevel@tonic-gate 		if (next == SVC_ETIMEDOUT) {
21560Sstevel@tonic-gate 			svc_thread_exit(pool, clone_xprt);
21570Sstevel@tonic-gate 			return (0);
21580Sstevel@tonic-gate 		}
21590Sstevel@tonic-gate 
21600Sstevel@tonic-gate 		/*
21610Sstevel@tonic-gate 		 * Interrupted by a signal while waiting for a
21620Sstevel@tonic-gate 		 * request. Return to userspace and restart.
21630Sstevel@tonic-gate 		 */
21640Sstevel@tonic-gate 		if (next == SVC_EINTR) {
21650Sstevel@tonic-gate 			svc_thread_exit(pool, clone_xprt);
21660Sstevel@tonic-gate 
21670Sstevel@tonic-gate 			/*
21680Sstevel@tonic-gate 			 * Thread has been interrupted and therefore
21690Sstevel@tonic-gate 			 * the service daemon is leaving as well so
21700Sstevel@tonic-gate 			 * let's go ahead and remove the service
21710Sstevel@tonic-gate 			 * pool at this time.
21720Sstevel@tonic-gate 			 */
21730Sstevel@tonic-gate 			svc = zone_getspecific(svc_zone_key, curproc->p_zone);
21740Sstevel@tonic-gate 			mutex_enter(&svc->svc_plock);
21750Sstevel@tonic-gate 			svc_pool_unregister(svc, pool);
21760Sstevel@tonic-gate 			mutex_exit(&svc->svc_plock);
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 			return (EINTR);
21790Sstevel@tonic-gate 		}
21800Sstevel@tonic-gate 
21810Sstevel@tonic-gate 		/*
21820Sstevel@tonic-gate 		 * De-queue the request and release the request lock
21830Sstevel@tonic-gate 		 * on this transport (latched by svc_poll()).
21840Sstevel@tonic-gate 		 */
21850Sstevel@tonic-gate 		mp = next->xp_req_head;
21860Sstevel@tonic-gate 		next->xp_req_head = mp->b_next;
21870Sstevel@tonic-gate 		mp->b_next = (mblk_t *)0;
21880Sstevel@tonic-gate 
21890Sstevel@tonic-gate 		TRACE_2(TR_FAC_KRPC, TR_NFSFP_QUE_REQ_DEQ,
21900Sstevel@tonic-gate 		    "rpc_que_req_deq:pool %p mp %p", pool, mp);
21910Sstevel@tonic-gate 		mutex_exit(&next->xp_req_lock);
21920Sstevel@tonic-gate 
21930Sstevel@tonic-gate 		/*
21940Sstevel@tonic-gate 		 * If this is a new request on a current transport then
21950Sstevel@tonic-gate 		 * the clone structure is already properly initialized.
21960Sstevel@tonic-gate 		 * Otherwise, if the request is on a different transport,
21970Sstevel@tonic-gate 		 * unlink from the current master and link to
21980Sstevel@tonic-gate 		 * the one we got a request on.
21990Sstevel@tonic-gate 		 */
22000Sstevel@tonic-gate 		if (next != xprt) {
22010Sstevel@tonic-gate 			if (xprt)
22020Sstevel@tonic-gate 				svc_clone_unlink(clone_xprt);
22030Sstevel@tonic-gate 			svc_clone_link(next, clone_xprt);
22040Sstevel@tonic-gate 			xprt = next;
22050Sstevel@tonic-gate 		}
22060Sstevel@tonic-gate 
22070Sstevel@tonic-gate 		/*
22080Sstevel@tonic-gate 		 * If there are more requests and req_cv hasn't
22090Sstevel@tonic-gate 		 * been signaled yet then wake up one more thread now.
22100Sstevel@tonic-gate 		 *
22110Sstevel@tonic-gate 		 * We avoid signaling req_cv until the most recently
22120Sstevel@tonic-gate 		 * signaled thread wakes up and gets CPU to clear
22130Sstevel@tonic-gate 		 * the `drowsy' flag.
22140Sstevel@tonic-gate 		 */
22150Sstevel@tonic-gate 		if (!(pool->p_drowsy || pool->p_reqs <= pool->p_walkers ||
22160Sstevel@tonic-gate 		    pool->p_asleep == 0)) {
22170Sstevel@tonic-gate 			mutex_enter(&pool->p_req_lock);
22180Sstevel@tonic-gate 
22190Sstevel@tonic-gate 			if (pool->p_drowsy || pool->p_reqs <= pool->p_walkers ||
22200Sstevel@tonic-gate 			    pool->p_asleep == 0)
22210Sstevel@tonic-gate 				mutex_exit(&pool->p_req_lock);
22220Sstevel@tonic-gate 			else {
22230Sstevel@tonic-gate 				pool->p_asleep--;
22240Sstevel@tonic-gate 				pool->p_drowsy = TRUE;
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate 				cv_signal(&pool->p_req_cv);
22270Sstevel@tonic-gate 				mutex_exit(&pool->p_req_lock);
22280Sstevel@tonic-gate 			}
22290Sstevel@tonic-gate 		}
22300Sstevel@tonic-gate 
22310Sstevel@tonic-gate 		/*
22320Sstevel@tonic-gate 		 * If there are no asleep/signaled threads, we are
22330Sstevel@tonic-gate 		 * still below pool->p_maxthreads limit, and no thread is
22340Sstevel@tonic-gate 		 * currently being created then signal the creator
22350Sstevel@tonic-gate 		 * for one more service thread.
22360Sstevel@tonic-gate 		 *
22370Sstevel@tonic-gate 		 * The asleep and drowsy checks are not protected
22380Sstevel@tonic-gate 		 * by a lock since it hurts performance and a wrong
22390Sstevel@tonic-gate 		 * decision is not essential.
22400Sstevel@tonic-gate 		 */
22410Sstevel@tonic-gate 		if (pool->p_asleep == 0 && !pool->p_drowsy &&
22420Sstevel@tonic-gate 		    pool->p_threads + pool->p_detached_threads <
22430Sstevel@tonic-gate 		    pool->p_maxthreads)
22440Sstevel@tonic-gate 			svc_creator_signal(pool);
22450Sstevel@tonic-gate 
22460Sstevel@tonic-gate 		/*
22470Sstevel@tonic-gate 		 * Process the request.
22480Sstevel@tonic-gate 		 */
22490Sstevel@tonic-gate 		svc_getreq(clone_xprt, mp);
22500Sstevel@tonic-gate 
22510Sstevel@tonic-gate 		/* If thread had a reservation it should have been canceled */
22520Sstevel@tonic-gate 		ASSERT(!clone_xprt->xp_reserved);
22530Sstevel@tonic-gate 
22540Sstevel@tonic-gate 		/*
22550Sstevel@tonic-gate 		 * If the clone is marked detached then exit.
22560Sstevel@tonic-gate 		 * The rpcmod slot has already been released
22570Sstevel@tonic-gate 		 * when we detached this thread.
22580Sstevel@tonic-gate 		 */
22590Sstevel@tonic-gate 		if (clone_xprt->xp_detached) {
22600Sstevel@tonic-gate 			svc_thread_exitdetached(pool, clone_xprt);
22610Sstevel@tonic-gate 			return (0);
22620Sstevel@tonic-gate 		}
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 		/*
22650Sstevel@tonic-gate 		 * Release our reference on the rpcmod
22660Sstevel@tonic-gate 		 * slot attached to xp_wq->q_ptr.
22670Sstevel@tonic-gate 		 */
22680Sstevel@tonic-gate 		(*RELE_PROC(xprt)) (clone_xprt->xp_wq, NULL);
22690Sstevel@tonic-gate 	}
22700Sstevel@tonic-gate 	/* NOTREACHED */
22710Sstevel@tonic-gate }
22720Sstevel@tonic-gate 
22730Sstevel@tonic-gate /*
22740Sstevel@tonic-gate  * Flush any pending requests for the queue and
22750Sstevel@tonic-gate  * and free the associated mblks.
22760Sstevel@tonic-gate  */
22770Sstevel@tonic-gate void
22780Sstevel@tonic-gate svc_queueclean(queue_t *q)
22790Sstevel@tonic-gate {
22800Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = ((void **) q->q_ptr)[0];
22810Sstevel@tonic-gate 	mblk_t *mp;
22820Sstevel@tonic-gate 
22830Sstevel@tonic-gate 	/*
22840Sstevel@tonic-gate 	 * clean up the requests
22850Sstevel@tonic-gate 	 */
22860Sstevel@tonic-gate 	mutex_enter(&xprt->xp_req_lock);
22870Sstevel@tonic-gate 	while ((mp = xprt->xp_req_head) != NULL) {
22880Sstevel@tonic-gate 		xprt->xp_req_head = mp->b_next;
22890Sstevel@tonic-gate 		mp->b_next = (mblk_t *)0;
22900Sstevel@tonic-gate 		(*RELE_PROC(xprt)) (xprt->xp_wq, mp);
22910Sstevel@tonic-gate 	}
22920Sstevel@tonic-gate 	mutex_exit(&xprt->xp_req_lock);
22930Sstevel@tonic-gate }
22940Sstevel@tonic-gate 
22950Sstevel@tonic-gate /*
22960Sstevel@tonic-gate  * This routine is called by rpcmod to inform kernel RPC that a
22970Sstevel@tonic-gate  * queue is closing. It is called after all the requests have been
22980Sstevel@tonic-gate  * picked up (that is after all the slots on the queue have
22990Sstevel@tonic-gate  * been released by kernel RPC). It is also guaranteed that no more
23000Sstevel@tonic-gate  * request will be delivered on this transport.
23010Sstevel@tonic-gate  *
23020Sstevel@tonic-gate  * - clear xp_wq to mark the master server transport handle as closing
23030Sstevel@tonic-gate  * - if there are no more threads on this transport close/destroy it
23040Sstevel@tonic-gate  * - otherwise, broadcast threads sleeping in svc_poll(); the last
23050Sstevel@tonic-gate  *   thread will close/destroy the transport.
23060Sstevel@tonic-gate  */
23070Sstevel@tonic-gate void
23080Sstevel@tonic-gate svc_queueclose(queue_t *q)
23090Sstevel@tonic-gate {
23100Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = ((void **) q->q_ptr)[0];
23110Sstevel@tonic-gate 
23120Sstevel@tonic-gate 	if (xprt == NULL) {
23130Sstevel@tonic-gate 		/*
23140Sstevel@tonic-gate 		 * If there is no master xprt associated with this stream,
23150Sstevel@tonic-gate 		 * then there is nothing to do.  This happens regularly
23160Sstevel@tonic-gate 		 * with connection-oriented listening streams created by
23170Sstevel@tonic-gate 		 * nfsd.
23180Sstevel@tonic-gate 		 */
23190Sstevel@tonic-gate 		return;
23200Sstevel@tonic-gate 	}
23210Sstevel@tonic-gate 
23220Sstevel@tonic-gate 	mutex_enter(&xprt->xp_thread_lock);
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 	ASSERT(xprt->xp_req_head == NULL);
23250Sstevel@tonic-gate 	ASSERT(xprt->xp_wq != NULL);
23260Sstevel@tonic-gate 
23270Sstevel@tonic-gate 	xprt->xp_wq = NULL;
23280Sstevel@tonic-gate 
23290Sstevel@tonic-gate 	if (xprt->xp_threads == 0) {
23300Sstevel@tonic-gate 		SVCPOOL *pool = xprt->xp_pool;
23310Sstevel@tonic-gate 
23320Sstevel@tonic-gate 		/*
23330Sstevel@tonic-gate 		 * svc_xprt_cleanup() destroys the transport
23340Sstevel@tonic-gate 		 * or releases the transport thread lock
23350Sstevel@tonic-gate 		 */
23360Sstevel@tonic-gate 		svc_xprt_cleanup(xprt, FALSE);
23370Sstevel@tonic-gate 
23380Sstevel@tonic-gate 		mutex_enter(&pool->p_thread_lock);
23390Sstevel@tonic-gate 
23400Sstevel@tonic-gate 		/*
23410Sstevel@tonic-gate 		 * If the pool is in closing state and this was
23420Sstevel@tonic-gate 		 * the last transport in the pool then signal the creator
23430Sstevel@tonic-gate 		 * thread to clean up and exit.
23440Sstevel@tonic-gate 		 */
23450Sstevel@tonic-gate 		if (pool->p_closing && svc_pool_tryexit(pool)) {
23460Sstevel@tonic-gate 			return;
23470Sstevel@tonic-gate 		}
23480Sstevel@tonic-gate 		mutex_exit(&pool->p_thread_lock);
23490Sstevel@tonic-gate 	} else {
23500Sstevel@tonic-gate 		/*
23510Sstevel@tonic-gate 		 * Wakeup threads sleeping in svc_poll() so that they
23520Sstevel@tonic-gate 		 * unlink from the transport
23530Sstevel@tonic-gate 		 */
23540Sstevel@tonic-gate 		mutex_enter(&xprt->xp_pool->p_req_lock);
23550Sstevel@tonic-gate 		cv_broadcast(&xprt->xp_pool->p_req_cv);
23560Sstevel@tonic-gate 		mutex_exit(&xprt->xp_pool->p_req_lock);
23570Sstevel@tonic-gate 
23580Sstevel@tonic-gate 		/*
23590Sstevel@tonic-gate 		 *  NOTICE: No references to the master transport structure
23600Sstevel@tonic-gate 		 *	    beyond this point!
23610Sstevel@tonic-gate 		 */
23620Sstevel@tonic-gate 		mutex_exit(&xprt->xp_thread_lock);
23630Sstevel@tonic-gate 	}
23640Sstevel@tonic-gate }
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate /*
23670Sstevel@tonic-gate  * Interrupt `request delivery' routine called from rpcmod
23680Sstevel@tonic-gate  * - put a request at the tail of the transport request queue
23690Sstevel@tonic-gate  * - insert a hint for svc_poll() into the xprt-ready queue
23700Sstevel@tonic-gate  * - increment the `pending-requests' count for the pool
23710Sstevel@tonic-gate  * - wake up a thread sleeping in svc_poll() if necessary
23720Sstevel@tonic-gate  * - if all the threads are running ask the creator for a new one.
23730Sstevel@tonic-gate  */
23740Sstevel@tonic-gate void
23750Sstevel@tonic-gate svc_queuereq(queue_t *q, mblk_t *mp)
23760Sstevel@tonic-gate {
23770Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = ((void **) q->q_ptr)[0];
23780Sstevel@tonic-gate 	SVCPOOL *pool = xprt->xp_pool;
23790Sstevel@tonic-gate 
23800Sstevel@tonic-gate 	TRACE_0(TR_FAC_KRPC, TR_SVC_QUEUEREQ_START, "svc_queuereq_start");
23810Sstevel@tonic-gate 
23820Sstevel@tonic-gate 	/*
23830Sstevel@tonic-gate 	 * Step 1.
23840Sstevel@tonic-gate 	 * Grab the transport's request lock and put
23850Sstevel@tonic-gate 	 * the request at the tail of the transport's
23860Sstevel@tonic-gate 	 * request queue.
23870Sstevel@tonic-gate 	 */
23880Sstevel@tonic-gate 	mutex_enter(&xprt->xp_req_lock);
23890Sstevel@tonic-gate 	if (xprt->xp_req_head == NULL)
23900Sstevel@tonic-gate 		xprt->xp_req_head = mp;
23910Sstevel@tonic-gate 	else
23920Sstevel@tonic-gate 		xprt->xp_req_tail->b_next = mp;
23930Sstevel@tonic-gate 	xprt->xp_req_tail = mp;
23940Sstevel@tonic-gate 
23950Sstevel@tonic-gate 	mutex_exit(&xprt->xp_req_lock);
23960Sstevel@tonic-gate 
23970Sstevel@tonic-gate 	/*
23980Sstevel@tonic-gate 	 * Step 2.
23990Sstevel@tonic-gate 	 * Grab the pool request lock, insert a hint into
24000Sstevel@tonic-gate 	 * the xprt-ready queue, increment `pending-requests'
24010Sstevel@tonic-gate 	 * count for the pool, and wake up a thread sleeping
24020Sstevel@tonic-gate 	 * in svc_poll() if necessary.
24030Sstevel@tonic-gate 	 */
24040Sstevel@tonic-gate 	mutex_enter(&pool->p_req_lock);
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 	/* Insert pointer to this transport into the xprt-ready queue */
24070Sstevel@tonic-gate 	svc_xprt_qput(pool, xprt);
24080Sstevel@tonic-gate 
24090Sstevel@tonic-gate 	/* Increment the `pending-requests' count for the pool */
24100Sstevel@tonic-gate 	pool->p_reqs++;
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	TRACE_2(TR_FAC_KRPC, TR_NFSFP_QUE_REQ_ENQ,
24130Sstevel@tonic-gate 	    "rpc_que_req_enq:pool %p mp %p", pool, mp);
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate 	/*
24160Sstevel@tonic-gate 	 * If there are more requests and req_cv hasn't
24170Sstevel@tonic-gate 	 * been signaled yet then wake up one more thread now.
24180Sstevel@tonic-gate 	 *
24190Sstevel@tonic-gate 	 * We avoid signaling req_cv until the most recently
24200Sstevel@tonic-gate 	 * signaled thread wakes up and gets CPU to clear
24210Sstevel@tonic-gate 	 * the `drowsy' flag.
24220Sstevel@tonic-gate 	 */
24230Sstevel@tonic-gate 	if (pool->p_drowsy || pool->p_reqs <= pool->p_walkers ||
24240Sstevel@tonic-gate 	    pool->p_asleep == 0) {
24250Sstevel@tonic-gate 		mutex_exit(&pool->p_req_lock);
24260Sstevel@tonic-gate 	} else {
24270Sstevel@tonic-gate 		pool->p_drowsy = TRUE;
24280Sstevel@tonic-gate 		pool->p_asleep--;
24290Sstevel@tonic-gate 
24300Sstevel@tonic-gate 		/*
24310Sstevel@tonic-gate 		 * Signal wakeup and drop the request lock.
24320Sstevel@tonic-gate 		 */
24330Sstevel@tonic-gate 		cv_signal(&pool->p_req_cv);
24340Sstevel@tonic-gate 		mutex_exit(&pool->p_req_lock);
24350Sstevel@tonic-gate 	}
24360Sstevel@tonic-gate 
24370Sstevel@tonic-gate 	/*
24380Sstevel@tonic-gate 	 * Step 3.
24390Sstevel@tonic-gate 	 * If there are no asleep/signaled threads, we are
24400Sstevel@tonic-gate 	 * still below pool->p_maxthreads limit, and no thread is
24410Sstevel@tonic-gate 	 * currently being created then signal the creator
24420Sstevel@tonic-gate 	 * for one more service thread.
24430Sstevel@tonic-gate 	 *
24440Sstevel@tonic-gate 	 * The asleep and drowsy checks are not not protected
24450Sstevel@tonic-gate 	 * by a lock since it hurts performance and a wrong
24460Sstevel@tonic-gate 	 * decision is not essential.
24470Sstevel@tonic-gate 	 */
24480Sstevel@tonic-gate 	if (pool->p_asleep == 0 && !pool->p_drowsy &&
24490Sstevel@tonic-gate 		pool->p_threads + pool->p_detached_threads < pool->p_maxthreads)
24500Sstevel@tonic-gate 		svc_creator_signal(pool);
24510Sstevel@tonic-gate 
24520Sstevel@tonic-gate 	TRACE_1(TR_FAC_KRPC, TR_SVC_QUEUEREQ_END,
24530Sstevel@tonic-gate 	    "svc_queuereq_end:(%S)", "end");
24540Sstevel@tonic-gate }
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate /*
24570Sstevel@tonic-gate  * Reserve a service thread so that it can be detached later.
24580Sstevel@tonic-gate  * This reservation is required to make sure that when it tries to
24590Sstevel@tonic-gate  * detach itself the total number of detached threads does not exceed
24600Sstevel@tonic-gate  * pool->p_maxthreads - pool->p_redline (i.e. that we can have
24610Sstevel@tonic-gate  * up to pool->p_redline non-detached threads).
24620Sstevel@tonic-gate  *
24630Sstevel@tonic-gate  * If the thread does not detach itself later, it should cancel the
24640Sstevel@tonic-gate  * reservation before returning to svc_run().
24650Sstevel@tonic-gate  *
24660Sstevel@tonic-gate  * - check if there is room for more reserved/detached threads
24670Sstevel@tonic-gate  * - if so, then increment the `reserved threads' count for the pool
24680Sstevel@tonic-gate  * - mark the thread as reserved (setting the flag in the clone transport
24690Sstevel@tonic-gate  *   handle for this thread
24700Sstevel@tonic-gate  * - returns 1 if the reservation succeeded, 0 if it failed.
24710Sstevel@tonic-gate  */
24720Sstevel@tonic-gate int
24730Sstevel@tonic-gate svc_reserve_thread(SVCXPRT *clone_xprt)
24740Sstevel@tonic-gate {
24750Sstevel@tonic-gate 	SVCPOOL *pool = clone_xprt->xp_master->xp_pool;
24760Sstevel@tonic-gate 
24770Sstevel@tonic-gate 	/* Recursive reservations are not allowed */
24780Sstevel@tonic-gate 	ASSERT(!clone_xprt->xp_reserved);
24790Sstevel@tonic-gate 	ASSERT(!clone_xprt->xp_detached);
24800Sstevel@tonic-gate 
24810Sstevel@tonic-gate 	/* Check pool counts if there is room for reservation */
24820Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
24830Sstevel@tonic-gate 	if (pool->p_reserved_threads + pool->p_detached_threads >=
24840Sstevel@tonic-gate 		pool->p_maxthreads - pool->p_redline) {
24850Sstevel@tonic-gate 		mutex_exit(&pool->p_thread_lock);
24860Sstevel@tonic-gate 		return (0);
24870Sstevel@tonic-gate 	}
24880Sstevel@tonic-gate 	pool->p_reserved_threads++;
24890Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
24900Sstevel@tonic-gate 
24910Sstevel@tonic-gate 	/* Mark the thread (clone handle) as reserved */
24920Sstevel@tonic-gate 	clone_xprt->xp_reserved = TRUE;
24930Sstevel@tonic-gate 
24940Sstevel@tonic-gate 	return (1);
24950Sstevel@tonic-gate }
24960Sstevel@tonic-gate 
24970Sstevel@tonic-gate /*
24980Sstevel@tonic-gate  * Cancel a reservation for a thread.
24990Sstevel@tonic-gate  * - decrement the `reserved threads' count for the pool
25000Sstevel@tonic-gate  * - clear the flag in the clone transport handle for this thread.
25010Sstevel@tonic-gate  */
25020Sstevel@tonic-gate void
25030Sstevel@tonic-gate svc_unreserve_thread(SVCXPRT *clone_xprt)
25040Sstevel@tonic-gate {
25050Sstevel@tonic-gate 	SVCPOOL *pool = clone_xprt->xp_master->xp_pool;
25060Sstevel@tonic-gate 
25070Sstevel@tonic-gate 	/* Thread must have a reservation */
25080Sstevel@tonic-gate 	ASSERT(clone_xprt->xp_reserved);
25090Sstevel@tonic-gate 	ASSERT(!clone_xprt->xp_detached);
25100Sstevel@tonic-gate 
25110Sstevel@tonic-gate 	/* Decrement global count */
25120Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
25130Sstevel@tonic-gate 	pool->p_reserved_threads--;
25140Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
25150Sstevel@tonic-gate 
25160Sstevel@tonic-gate 	/* Clear reservation flag */
25170Sstevel@tonic-gate 	clone_xprt->xp_reserved = FALSE;
25180Sstevel@tonic-gate }
25190Sstevel@tonic-gate 
25200Sstevel@tonic-gate /*
25210Sstevel@tonic-gate  * Detach a thread from its transport, so that it can block for an
25220Sstevel@tonic-gate  * extended time.  Because the transport can be closed after the thread is
25230Sstevel@tonic-gate  * detached, the thread should have already sent off a reply if it was
25240Sstevel@tonic-gate  * going to send one.
25250Sstevel@tonic-gate  *
25260Sstevel@tonic-gate  * - decrement `non-detached threads' count and increment `detached threads'
25270Sstevel@tonic-gate  *   counts for the transport
25280Sstevel@tonic-gate  * - decrement the  `non-detached threads' and `reserved threads'
25290Sstevel@tonic-gate  *   counts and increment the `detached threads' count for the pool
25300Sstevel@tonic-gate  * - release the rpcmod slot
25310Sstevel@tonic-gate  * - mark the clone (thread) as detached.
25320Sstevel@tonic-gate  *
25330Sstevel@tonic-gate  * No need to return a pointer to the thread's CPR information, since
25340Sstevel@tonic-gate  * the thread has a userland identity.
25350Sstevel@tonic-gate  *
25360Sstevel@tonic-gate  * NOTICE: a thread must not detach itself without making a prior reservation
25370Sstevel@tonic-gate  *	   through svc_thread_reserve().
25380Sstevel@tonic-gate  */
25390Sstevel@tonic-gate callb_cpr_t *
25400Sstevel@tonic-gate svc_detach_thread(SVCXPRT *clone_xprt)
25410Sstevel@tonic-gate {
25420Sstevel@tonic-gate 	SVCMASTERXPRT *xprt = clone_xprt->xp_master;
25430Sstevel@tonic-gate 	SVCPOOL *pool = xprt->xp_pool;
25440Sstevel@tonic-gate 
25450Sstevel@tonic-gate 	/* Thread must have a reservation */
25460Sstevel@tonic-gate 	ASSERT(clone_xprt->xp_reserved);
25470Sstevel@tonic-gate 	ASSERT(!clone_xprt->xp_detached);
25480Sstevel@tonic-gate 
25490Sstevel@tonic-gate 	/* Bookkeeping for this transport */
25500Sstevel@tonic-gate 	mutex_enter(&xprt->xp_thread_lock);
25510Sstevel@tonic-gate 	xprt->xp_threads--;
25520Sstevel@tonic-gate 	xprt->xp_detached_threads++;
25530Sstevel@tonic-gate 	mutex_exit(&xprt->xp_thread_lock);
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 	/* Bookkeeping for the pool */
25560Sstevel@tonic-gate 	mutex_enter(&pool->p_thread_lock);
25570Sstevel@tonic-gate 	pool->p_threads--;
25580Sstevel@tonic-gate 	pool->p_reserved_threads--;
25590Sstevel@tonic-gate 	pool->p_detached_threads++;
25600Sstevel@tonic-gate 	mutex_exit(&pool->p_thread_lock);
25610Sstevel@tonic-gate 
25620Sstevel@tonic-gate 	/* Release an rpcmod slot for this request */
25630Sstevel@tonic-gate 	(*RELE_PROC(xprt)) (clone_xprt->xp_wq, NULL);
25640Sstevel@tonic-gate 
25650Sstevel@tonic-gate 	/* Mark the clone (thread) as detached */
25660Sstevel@tonic-gate 	clone_xprt->xp_reserved = FALSE;
25670Sstevel@tonic-gate 	clone_xprt->xp_detached = TRUE;
25680Sstevel@tonic-gate 
25690Sstevel@tonic-gate 	return (NULL);
25700Sstevel@tonic-gate }
25710Sstevel@tonic-gate 
25720Sstevel@tonic-gate /*
25730Sstevel@tonic-gate  * This routine is responsible for extracting RDMA plugin master XPRT,
25740Sstevel@tonic-gate  * unregister from the SVCPOOL and initiate plugin specific cleanup.
25750Sstevel@tonic-gate  * It is passed a list/group of rdma transports as records which are
25760Sstevel@tonic-gate  * active in a given registered or unregistered kRPC thread pool. Its shuts
25770Sstevel@tonic-gate  * all active rdma transports in that pool. If the thread active on the trasport
25780Sstevel@tonic-gate  * happens to be last thread for that pool, it will signal the creater thread
25790Sstevel@tonic-gate  * to cleanup the pool and destroy the xprt in svc_queueclose()
25800Sstevel@tonic-gate  */
25810Sstevel@tonic-gate void
25820Sstevel@tonic-gate rdma_stop(rdma_xprt_group_t rdma_xprts)
25830Sstevel@tonic-gate {
25840Sstevel@tonic-gate 	SVCMASTERXPRT *xprt;
25850Sstevel@tonic-gate 	rdma_xprt_record_t *curr_rec;
25860Sstevel@tonic-gate 	queue_t *q;
25870Sstevel@tonic-gate 	mblk_t *mp;
25880Sstevel@tonic-gate 	int i;
25890Sstevel@tonic-gate 
25900Sstevel@tonic-gate 	if (rdma_xprts.rtg_count == 0)
25910Sstevel@tonic-gate 		return;
25920Sstevel@tonic-gate 
25930Sstevel@tonic-gate 	for (i = 0; i < rdma_xprts.rtg_count; i++) {
25940Sstevel@tonic-gate 		curr_rec = rdma_xprts.rtg_listhead;
25950Sstevel@tonic-gate 		rdma_xprts.rtg_listhead = curr_rec->rtr_next;
25960Sstevel@tonic-gate 		curr_rec->rtr_next = NULL;
25970Sstevel@tonic-gate 		xprt = curr_rec->rtr_xprt_ptr;
25980Sstevel@tonic-gate 		q = xprt->xp_wq;
25990Sstevel@tonic-gate 		svc_rdma_kstop(xprt);
26000Sstevel@tonic-gate 
26010Sstevel@tonic-gate 		mutex_enter(&xprt->xp_req_lock);
26020Sstevel@tonic-gate 		while ((mp = xprt->xp_req_head) != NULL) {
26030Sstevel@tonic-gate 			xprt->xp_req_head = mp->b_next;
26040Sstevel@tonic-gate 			mp->b_next = (mblk_t *)0;
26050Sstevel@tonic-gate 			if (mp)
26060Sstevel@tonic-gate 				freemsg(mp);
26070Sstevel@tonic-gate 		}
26080Sstevel@tonic-gate 		mutex_exit(&xprt->xp_req_lock);
26090Sstevel@tonic-gate 		svc_queueclose(q);
26100Sstevel@tonic-gate #ifdef	DEBUG
26110Sstevel@tonic-gate 		if (rdma_check)
26120Sstevel@tonic-gate 			cmn_err(CE_NOTE, "rdma_stop: Exited svc_queueclose\n");
26130Sstevel@tonic-gate #endif
26140Sstevel@tonic-gate 		/*
26150Sstevel@tonic-gate 		 * Free the rdma transport record for the expunged rdma
26160Sstevel@tonic-gate 		 * based master transport handle.
26170Sstevel@tonic-gate 		 */
26180Sstevel@tonic-gate 		kmem_free(curr_rec, sizeof (rdma_xprt_record_t));
26190Sstevel@tonic-gate 		if (!rdma_xprts.rtg_listhead)
26200Sstevel@tonic-gate 			break;
26210Sstevel@tonic-gate 	}
26220Sstevel@tonic-gate }
2623