/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
 * Portions of this source code were derived from Berkeley
 * 4.3 BSD under license from the Regents of the University of
 * California.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * This is the rpc server side idle loop
 * Wait for input, call server program.
 */
#include "mt.h"
#include "rpc_mt.h"
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <rpc/rpc.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <syslog.h>
#include <thread.h>
#include <assert.h>
#include <libintl.h>
#include <values.h>

extern const char __nsl_dom[];

extern int __rpc_compress_pollfd(int, pollfd_t *, pollfd_t *);
extern bool_t __is_a_userfd(int P_fd);
extern void   __destroy_userfd();
extern void clear_pollfd(int);
extern void set_pollfd(int /* fd */, short /* events */);
extern void svc_getreq_poll();
extern void (*__proc_cleanup_cb)();

static void start_threads();
static void create_pipe();
static void clear_pipe();
static int select_next_pollfd();
static SVCXPRT *make_xprt_copy();
static void _svc_run_mt();
static void _svc_run();

int _svc_prog_dispatch();
static void _svc_done_private();

extern rwlock_t svc_fd_lock;
extern mutex_t	svc_door_mutex;
extern cond_t	svc_door_waitcv;
extern int	svc_ndoorfds;
extern void	__svc_cleanup_door_xprts();
extern void	__svc_free_xprtlist();
extern void	__svc_getreq_user(struct pollfd *);

/*
 * Maximum fragment size allowed for connection oriented connections.
 * Zero means that no maximum size limit has been requested.
 */
int __rpc_connmaxrec = 0;

/* Inter-Record Timeout in secs for non-blocked connection RPC */
int __rpc_irtimeout = 35;

/*
 * Request exclusive access to tcp and udp non-priv ports bound with a
 * wildcard addr.
 */
bool_t __rpc_tp_exclbind = FALSE;

/*
 * XXX - eventually, all mutexes and their initializations static
 */

/*
 * Variables used for MT
 */
int svc_mt_mode;		/* multi-threading mode */

int svc_pipe[2];	/* pipe for breaking out of poll: read(0), write(1) */

/* BEGIN PROTECTED BY svc_mutex */

static int svc_thr_max = 16;	/* default maximum number of threads allowed */

static int svc_thr_total;	/* current number of threads */

static int svc_thr_active;	/* current number of threads active */

/* circular array of file descriptors with pending data */

#define	CIRCULAR_BUFSIZE	1024

static int svc_pending_fds[CIRCULAR_BUFSIZE+1];	/* fds with pending data */

static int svc_next_pending;			/* next one to be processed */

static int svc_last_pending;			/* last one in list */

static int svc_total_pending;			/* total in list */

static int svc_thr_total_creates;	/* total created - stats */

static int svc_thr_total_create_errors;	/* total create errors - stats */

static int svc_waiters;		/* number of waiting threads */

/* END PROTECTED BY svc_mutex */

/* BEGIN PROTECTED BY svc_fd_lock: */

int svc_nfds;		/* total number of active file descriptors */

int svc_nfds_set;	/* total number of fd bits set in svc_fdset */

int svc_max_fd = 0;	/* largest active file descriptor */

int svc_npollfds;	/* total number of active pollfds */

int svc_npollfds_set;	/* total number of pollfd set in svc_pollfd */

int svc_max_pollfd;	/* largest active pollfd so far */

int svc_pollfd_allocd;  /* number of pollfd structures allocated */

/* END PROTECTED BY svc_fd_lock: */

/* BEGIN PROTECTED BY svc_thr_mutex */

#define	POLLSET_EXTEND	256
static int svc_pollset_allocd;
static struct pollfd *svc_pollset;
				/*
				 * array of file descriptors currently active
				 */
static int svc_polled;		/* no of fds polled in last poll() - input */

static int svc_pollfds;		/* no of active fds in last poll() - output */

static int svc_next_pollfd;	/* next fd  to processin svc_pollset */

bool_t svc_polling;		/* true if a thread is polling */

/* END PROTECTED BY svc_thr_mutex */

/* BEGIN PROTECTED BY svc_exit_mutex */

static bool_t svc_exit_done = TRUE;

/* END PROTECTED BY svc_exit_mutex */

/*
 * Warlock section
 */

/* VARIABLES PROTECTED BY svc_mutex:
	svc_thr_total, svc_thr_active, svc_pending_fds, svc_next_pending,
	svc_last_pending, svc_total_pending, svc_thr_total_creates,
	svc_thr_total_create_errors,
	svcxprt_list_t::next, svcxprt_ext_t::my_xlist,
	svc_thr_max, svc_waiters
 */

/* VARIABLES PROTECTED BY svc_fd_lock:
	svc_xports, svc_fdset, svc_nfds, svc_nfds_set, svc_max_fd,
	svc_pollfd, svc_npollfds, svc_npollfds_set, svc_max_pollfd
 */

/* VARIABLES PROTECTED BY svc_thr_mutex:
	svc_pollset, svc_pollfds, svc_next_pollfd, svc_polling
	svc_pollset_allocd, svc_polled
 */

/* VARIABLES PROTECTED BY svc_exit_mutex:
	svc_exit_done
 */

/* VARIABLES READABLE WITHOUT LOCK:
	svc_thr_total, svc_thr_active, svc_thr_total_creates,
	svc_thr_total_create_errors,
	svc_xports, svc_nfds, svc_nfds_set, svc_max_fd,
	svc_npollfds, svc_npollfds_set, svc_max_pollfd,
	svc_pollfds, svc_next_pollfd, svc_exit_done, svc_polling,
	svc_thr_max, svc_waiters
 */

/* VARIABLES PROTECTED BY "program_logic":
	rpc_msg::, svc_req::, svcxprt_ext_t::flags, svc_mt_mode,
	svcxprt_ext_t::parent
 */

/* LOCK ORDER:
	svc_exit_mutex, svc_thr_mutex, svc_mutex, svc_fd_lock
 */


void
svc_run(void)
{
	/* NO OTHER THREADS ARE RUNNING */

	svc_exit_done = FALSE;

	while ((svc_npollfds > 0 || svc_ndoorfds > 0) && !svc_exit_done) {
		if (svc_npollfds > 0) {
			switch (svc_mt_mode) {
			case RPC_SVC_MT_NONE:
				_svc_run();
				break;
			default:
				_svc_run_mt();
				break;
			}
			continue;
		}

		(void) mutex_lock(&svc_door_mutex);
		if (svc_ndoorfds > 0)
			(void) cond_wait(&svc_door_waitcv, &svc_door_mutex);
		(void) mutex_unlock(&svc_door_mutex);
	}
}


/*
 *	This function causes svc_run() to exit by destroying all
 *	service handles.
 */
void
svc_exit(void)
{
	SVCXPRT	*xprt;
	int fd;
	char dummy;

	/* NO LOCKS HELD */

	(void) mutex_lock(&svc_exit_mutex);
	if (svc_exit_done) {
		(void) mutex_unlock(&svc_exit_mutex);
		return;
	}
	svc_exit_done = TRUE;
	for (fd = 0; fd < svc_max_pollfd; fd++) {
		xprt = svc_xports[fd];
		if (xprt) {
			SVC_DESTROY(xprt);
		}
	}
	__svc_free_xprtlist();
	__svc_cleanup_door_xprts();
	(void) mutex_unlock(&svc_exit_mutex);

	if (svc_mt_mode != RPC_SVC_MT_NONE) {
		(void) mutex_lock(&svc_mutex);
		(void) cond_broadcast(&svc_thr_fdwait);
		(void) mutex_unlock(&svc_mutex);

		(void) write(svc_pipe[1], &dummy, sizeof (dummy));
	}

	(void) mutex_lock(&svc_door_mutex);
	(void) cond_signal(&svc_door_waitcv);	/* wake up door dispatching */
	(void) mutex_unlock(&svc_door_mutex);

	/* destroy reactor information if any */
	__destroy_userfd();
}


/*
 * this funtion is called with svc_fd_lock and svc_thr_mutex
 */

static int
alloc_pollset(int npollfds)
{
	if (npollfds > svc_pollset_allocd) {
		pollfd_t *tmp;
		do {
			svc_pollset_allocd += POLLSET_EXTEND;
		} while (npollfds > svc_pollset_allocd);
		tmp = realloc(svc_pollset,
				sizeof (pollfd_t) * svc_pollset_allocd);
		if (tmp == NULL) {
			syslog(LOG_ERR, "alloc_pollset: out of memory");
			return (-1);
		}
		svc_pollset = tmp;
	}
	return (0);
}

extern int _sigemptyset(sigset_t *);
extern int _sigaddset(sigset_t *, int);
extern int _sigprocmask(int, const sigset_t *, sigset_t *);

static void
_svc_run(void)
{
	sigset_t set, oldset;
	int npollfds;
	int i;

	/*
	 * Block SIGALRM while doing work.  Unblock it while doing poll().
	 * This is so that services like rpc.rstatd can cause the poll()
	 * to be interrupted due to alarm() but that we don't end up in
	 * an MT-unsafe signal handler at an inopportune time.
	 */
	(void) _sigemptyset(&set);
	(void) _sigaddset(&set, SIGALRM);
	(void) _sigprocmask(SIG_BLOCK, &set, &oldset);
	while (!svc_exit_done) {
		/*
		 * Check whether there is any server fd on which we may want
		 * to wait.
		 */
		(void) rw_rdlock(&svc_fd_lock);
		if (alloc_pollset(svc_npollfds) == -1)
			break;
		npollfds = __rpc_compress_pollfd(svc_max_pollfd,
			svc_pollfd, svc_pollset);
		(void) rw_unlock(&svc_fd_lock);
		if (npollfds == 0)
			break;	/* None waiting, hence return */

		(void) _sigprocmask(SIG_SETMASK, &oldset, NULL);
		i = poll(svc_pollset, npollfds, -1);
		(void) _sigprocmask(SIG_BLOCK, &set, &oldset);
		switch (i) {
		case -1:
			/*
			 * We ignore all errors, continuing with the assumption
			 * that it was set by the signal handlers (or any
			 * other outside event) and not caused by poll().
			 */
		case 0:
			continue;
		default:
			svc_getreq_poll(svc_pollset, i);
		}
	}
	(void) _sigprocmask(SIG_SETMASK, &oldset, NULL);
}

/*
 * In _svc_run_mt, myfd is linked with mypollfd
 * svc_pollset[mypollfd].fd == myfd
 * However, in some cases, the link can not be made, thus we define the
 * following values for these special cases
 */
enum {
	INVALID_POLLFD	= -200,
	FD_FROM_PENDING
};

static void
_svc_run_mt(void)
{
	int npollfds;
	int n_polled, dispatch;

	static bool_t first_time = TRUE;
	bool_t main_thread = FALSE;
	int n_new;
	int myfd, mypollfd;
	SVCXPRT *parent_xprt, *xprt;

	/*
	 * Server is multi-threaded.  Do "first time" initializations.
	 * Since only one thread exists in the beginning, there's no
	 * need for mutex protection for first time initializations.
	 */
	if (first_time) {
		first_time = FALSE;
		main_thread = TRUE;
		svc_thr_total = 1;	/* this thread */
		svc_next_pending = svc_last_pending = 0;

		/*
		 * Create a pipe for waking up the poll, if new
		 * descriptors have been added to svc_fdset.
		 */
		create_pipe();
	}

	/* OTHER THREADS ARE RUNNING */

	if (svc_exit_done)
		return;

	for (;;) {
		/*
		 * svc_thr_mutex prevents more than one thread from
		 * trying to select a descriptor to process further.
		 * svc_thr_mutex is unlocked after a thread selects
		 * a descriptor on which to receive data.  If there are
		 * no such descriptors, the thread will poll with
		 * svc_thr_mutex locked, after unlocking all other
		 * locks.  This prevents more than one thread from
		 * trying to poll at the same time.
		 */
		(void) mutex_lock(&svc_thr_mutex);
		(void) mutex_lock(&svc_mutex);
continue_with_locks:
		myfd = -1;
		mypollfd = INVALID_POLLFD;

		/*
		 * Check if there are any descriptors with data pending.
		 */
		if (svc_total_pending > 0) {
			myfd = svc_pending_fds[svc_next_pending++];
			mypollfd = FD_FROM_PENDING;
			if (svc_next_pending > CIRCULAR_BUFSIZE)
				svc_next_pending = 0;
			svc_total_pending--;
		}

		/*
		 * Get the next active file descriptor to process.
		 */
		if (myfd == -1 && svc_pollfds == 0) {
			/*
			 * svc_pollset is empty; do polling
			 */
			svc_polling = TRUE;

			/*
			 * if there are no file descriptors, return
			 */
			(void) rw_rdlock(&svc_fd_lock);
			if (svc_npollfds == 0 ||
					alloc_pollset(svc_npollfds + 1) == -1) {
				(void) rw_unlock(&svc_fd_lock);
				svc_polling = FALSE;
				svc_thr_total--;
				(void) mutex_unlock(&svc_mutex);
				(void) mutex_unlock(&svc_thr_mutex);
				if (!main_thread) {
					thr_exit(NULL);
					/* NOTREACHED */
				}
				break;
			}

			npollfds = __rpc_compress_pollfd(svc_max_pollfd,
					svc_pollfd, svc_pollset);
			(void) rw_unlock(&svc_fd_lock);

			if (npollfds == 0) {
				/*
				 * There are file descriptors, but none of them
				 * are available for polling.  If this is the
				 * main thread, or if no thread is waiting,
				 * wait on condition variable, otherwise exit.
				 */
				svc_polling = FALSE;
				(void) mutex_unlock(&svc_thr_mutex);
				if ((!main_thread) && svc_waiters > 0) {
					svc_thr_total--;
					(void) mutex_unlock(&svc_mutex);
					thr_exit(NULL);
					/* NOTREACHED */
				}

				while (svc_npollfds_set == 0 &&
					svc_pollfds == 0 &&
					svc_total_pending == 0 &&
							!svc_exit_done) {
					svc_waiters++;
					(void) cond_wait(&svc_thr_fdwait,
								&svc_mutex);
					svc_waiters--;
				}

				/*
				 * Check exit flag.  If this is not the main
				 * thread, exit.
				 */
				if (svc_exit_done) {
					svc_thr_total--;
					(void) mutex_unlock(&svc_mutex);
					if (!main_thread)
						thr_exit(NULL);
					break;
				}

				(void) mutex_unlock(&svc_mutex);
				continue;
			}

			/*
			 * We're ready to poll.  Always set svc_pipe[0]
			 * as the last one, since the poll will occasionally
			 * need to be interrupted.  Release svc_mutex for
			 * the duration of the poll, but hold on to
			 * svc_thr_mutex, as we don't want any other thread
			 * to do the same.
			 */
			svc_pollset[npollfds].fd = svc_pipe[0];
			svc_pollset[npollfds].events = MASKVAL;

			do {
				int i, j;

				(void) mutex_unlock(&svc_mutex);
				n_polled = poll(svc_pollset, npollfds + 1, -1);
				(void) mutex_lock(&svc_mutex);
				if (n_polled <= 0)
					continue;

				/*
				 * Check if information returned indicates one
				 * or more closed fd's; find and remove any such
				 * information
				 */
				for (i = 0; i <= npollfds; i++) {
					if (svc_pollset[i].revents & POLLNVAL) {
						/* Overwrite svc_pollset[i] */
						for (j = i; j < npollfds; j++)
							svc_pollset[j] =
							    svc_pollset[j + 1];
						(void) memset(&svc_pollset[j],
						    0, sizeof (struct pollfd));
						npollfds--;
						n_polled--;
						i--;
					}
				}
			} while (n_polled <= 0);
			svc_polling = FALSE;

			/*
			 * If there's data in the pipe, clear it.
			 */
			if (svc_pollset[npollfds].revents) {
				clear_pipe();
				n_polled--;
				svc_pollset[npollfds].revents = 0;
			}
			svc_polled = npollfds;
			svc_pollfds = n_polled;
			svc_next_pollfd = 0;

			/*
			 * Check exit flag.
			 */
			if (svc_exit_done) {
				svc_thr_total--;
				(void) mutex_unlock(&svc_mutex);
				(void) mutex_unlock(&svc_thr_mutex);
				if (!main_thread) {
					thr_exit(NULL);
					/* NOTREACHED */
				}
				break;
			}

			/*
			 * If no descriptor is active, continue.
			 */
			if (svc_pollfds == 0)
				goto continue_with_locks;
		}

		/*
		 * If a file descriptor has already not been selected,
		 * choose a file descriptor.
		 * svc_pollfds and svc_next_pollfd are updated.
		 */
		if (myfd == -1) {
			if (select_next_pollfd(&myfd, &mypollfd) == -1)
				goto continue_with_locks;
		}

		/*
		 * Check to see if new threads need to be started.
		 * Count of threads that could be gainfully employed is
		 * obtained as follows:
		 *	- count 1 for poller
		 *	- count 1 for this request
		 *	- count active file descriptors (svc_pollfds)
		 *	- count pending file descriptors
		 *
		 * (svc_thr_total - svc_thr_active) are already available.
		 * This thread is one of the available threads.
		 *
		 * Number of new threads should not exceed
		 *	(svc_thr_max - svc_thr_total).
		 */
		if (svc_thr_total < svc_thr_max &&
			    svc_mt_mode == RPC_SVC_MT_AUTO && !svc_exit_done) {
			n_new = 1 + 1 + svc_pollfds + svc_total_pending -
					(svc_thr_total - svc_thr_active);
			if (n_new > (svc_thr_max - svc_thr_total))
				n_new = svc_thr_max - svc_thr_total;
			if (n_new > 0)
				start_threads(n_new);
		}

		/*
		 * Get parent xprt.  It is possible for the parent service
		 * handle to be destroyed by now, due to a race condition.
		 * Check for this, and if so, log a warning and go on.
		 */
		parent_xprt = svc_xports[myfd];
		if (parent_xprt == NULL) {
			/* Check if it is not a user FD */
			if (__is_a_userfd(myfd) == TRUE)
				__svc_getreq_user(&(svc_pollset[mypollfd]));
			goto continue_with_locks;
		}
/* LINTED pointer alignment */
		if (svc_defunct(parent_xprt) || svc_failed(parent_xprt))
			goto continue_with_locks;

		/*
		 * Make a copy of parent xprt, update svc_fdset.
		 */
		if ((xprt = make_xprt_copy(parent_xprt)) == NULL)
			goto continue_with_locks;

		/*
		 * Keep track of active threads in automatic mode.
		 */
		if (svc_mt_mode == RPC_SVC_MT_AUTO)
			svc_thr_active++;

		/*
		 * Release mutexes so other threads can get going.
		 */
		(void) mutex_unlock(&svc_mutex);
		(void) mutex_unlock(&svc_thr_mutex);

		/*
		 * Process request.
		 */
		{
			struct rpc_msg *msg;
			struct svc_req *r;
			char *cred_area;

/* LINTED pointer alignment */
			msg = SVCEXT(xprt)->msg;
/* LINTED pointer alignment */
			r = SVCEXT(xprt)->req;
/* LINTED pointer alignment */
			cred_area = SVCEXT(xprt)->cred_area;


			msg->rm_call.cb_cred.oa_base = cred_area;
			msg->rm_call.cb_verf.oa_base =
						&(cred_area[MAX_AUTH_BYTES]);
			r->rq_clntcred = &(cred_area[2 * MAX_AUTH_BYTES]);

			/*
			 * receive RPC message
			 */
			if ((dispatch = SVC_RECV(xprt, msg))) {
				if (svc_mt_mode != RPC_SVC_MT_NONE)
/* LINTED pointer alignment */
					svc_flags(xprt) |= SVC_ARGS_CHECK;
				dispatch = _svc_prog_dispatch(xprt, msg, r);

				/*
				 * Call cleanup procedure if set.
				 */
				if (__proc_cleanup_cb != NULL)
					(*__proc_cleanup_cb)(xprt);
			} else
				svc_args_done(xprt);

			/*
			 * Finish up, if automatic mode, or not dispatched.
			 */
			if (svc_mt_mode == RPC_SVC_MT_AUTO || !dispatch) {
/* LINTED pointer alignment */
				if (svc_flags(xprt) & SVC_ARGS_CHECK)
					svc_args_done(xprt);
				(void) mutex_lock(&svc_mutex);
				_svc_done_private(xprt);
				if (svc_mt_mode == RPC_SVC_MT_AUTO) {
					/*
					 * not active any more
					 */
					svc_thr_active--;

					/*
					 * If not main thread, exit unless
					 * there's some immediate work.
					 */
					if (!main_thread &&
						    svc_pollfds <= 0 &&
						    svc_total_pending <= 0 &&
						    (svc_polling ||
							svc_waiters > 0)) {
						svc_thr_total--;
						if (svc_thr_total ==
						    svc_waiters) {
							(void) cond_broadcast(
							    &svc_thr_fdwait);
						}
						(void) mutex_unlock(&svc_mutex);
						thr_exit(NULL);
						/* NOTREACHED */
					}
				}
				(void) mutex_unlock(&svc_mutex);
			}
		}

	}
}


/*
 * start_threads() - Start specified number of threads.
 */
static void
start_threads(int num_threads)
{
	int		i;

	assert(MUTEX_HELD(&svc_mutex));

	for (i = 0; i < num_threads; i++) {
		if (thr_create(NULL, 0, (void *(*)(void *))_svc_run_mt, NULL,
		    THR_DETACHED, NULL) == 0) {
			svc_thr_total++;
			svc_thr_total_creates++;
		} else {
			svc_thr_total_create_errors++;
		}
	}
}


/*
 * create_pipe() - create pipe for breaking out of poll.
 */
static void
create_pipe(void)
{
	if (pipe(svc_pipe) == -1) {
		syslog(LOG_ERR, dgettext(__nsl_dom,
				"RPC: svc could not create pipe - exiting"));
		exit(1);
	}
	if (_fcntl(svc_pipe[0], F_SETFL, O_NONBLOCK) == -1) {
		syslog(LOG_ERR, dgettext(__nsl_dom,
					"RPC: svc pipe error - exiting"));
		exit(1);
	}
	if (_fcntl(svc_pipe[1], F_SETFL, O_NONBLOCK) == -1) {
		syslog(LOG_ERR, dgettext(__nsl_dom,
					"RPC: svc pipe error - exiting"));
		exit(1);
	}
}


/*
 * clear_pipe() - Empty data in pipe.
 */
static void
clear_pipe(void)
{
	char	buf[16];
	int	i;

	do {
		i = read(svc_pipe[0], buf, sizeof (buf));
	} while (i == sizeof (buf));
}


/*
 * select_next_pollfd() - Select the next active fd in svc_pollset.
 */
static int
select_next_pollfd(int *fd, int *pollfdIndex)
{
	int i;

	assert(MUTEX_HELD(&svc_thr_mutex));
	assert(MUTEX_HELD(&svc_mutex));

	for (i = svc_next_pollfd; svc_pollfds > 0 && i < svc_polled;
							i++) {
		if (svc_pollset[i].revents) {
			svc_pollfds--;
			/*
			 * No more special case for POLLNVAL, because it may
			 * be linked with a user file descriptot callback
			 */
			svc_next_pollfd = i + 1;

			*fd = svc_pollset[i].fd;
			*pollfdIndex = i;

			return (0);
		}
	}
	svc_next_pollfd = svc_pollfds = 0;
	*fd = -1;
	*pollfdIndex = INVALID_POLLFD;
	return (-1);
}


/*
 * make_xprt_copy() - make a copy of the parent xprt.
 * Clear fd bit in svc_fdset.
 */
static SVCXPRT *
make_xprt_copy(SVCXPRT *parent)
{
/* LINTED pointer alignment */
	SVCXPRT_LIST	*xlist = SVCEXT(parent)->my_xlist;
	SVCXPRT_LIST	*xret;
	SVCXPRT		*xprt;
	int		fd = parent->xp_fd;

	assert(MUTEX_HELD(&svc_mutex));

	xret = xlist->next;
	if (xret) {
		xlist->next = xret->next;
		xret->next = NULL;
		xprt = xret->xprt;
/* LINTED pointer alignment */
		svc_flags(xprt) = svc_flags(parent);
	} else
		xprt = svc_copy(parent);

	if (xprt) {
/* LINTED pointer alignment */
		SVCEXT(parent)->refcnt++;
		(void) rw_wrlock(&svc_fd_lock);
		clear_pollfd(fd);
		(void) rw_unlock(&svc_fd_lock);
	}
	return (xprt);
}

/*
 * _svc_done_private() - return copies to library.
 */
static void
_svc_done_private(SVCXPRT *xprt)
{
	SVCXPRT		*parent;
	SVCXPRT_LIST	*xhead, *xlist;

	assert(MUTEX_HELD(&svc_mutex));

/* LINTED pointer alignment */
	if ((parent = SVCEXT(xprt)->parent) == NULL)
		return;

/* LINTED pointer alignment */
	xhead = SVCEXT(parent)->my_xlist;
/* LINTED pointer alignment */
	xlist = SVCEXT(xprt)->my_xlist;
	xlist->next = xhead->next;
	xhead->next = xlist;

/* LINTED pointer alignment */
	SVCEXT(parent)->refcnt--;

	/*
	 * Propagate any error flags.  This is done in both directions to
	 * ensure that if one child gets an error, everyone will see it
	 * (even if there are multiple outstanding children) and the
	 * transport will get closed.
	 */
/* LINTED pointer alignment */
	svc_flags(xprt) |= svc_flags(parent);
/* LINTED pointer alignment */
	if (svc_failed(xprt) || svc_defunct(xprt)) {
/* LINTED pointer alignment */
		svc_flags(parent) |= (svc_flags(xprt) &
				(SVC_FAILED | SVC_DEFUNCT));
/* LINTED pointer alignment */
		if (SVCEXT(parent)->refcnt == 0)
			_svc_destroy_private(xprt);
	}
}

void
svc_done(SVCXPRT *xprt)
{
	if (svc_mt_mode != RPC_SVC_MT_USER)
		return;

	/*
	 * Make sure file descriptor is released in user mode.
	 * If the xprt is a door, do nothing: this work is performed by
	 * svc_door.c's return_xprt_copy() routine, which is basically a
	 * door-specific copy of _svc_done_private().
	 */
/* LINTED pointer alignment */
	if (svc_type(xprt) == SVC_DOOR)
		return;

/* LINTED pointer alignment */
	if (svc_flags(xprt) & SVC_ARGS_CHECK)
		svc_args_done(xprt);

	(void) mutex_lock(&svc_mutex);
	_svc_done_private(xprt);
	(void) mutex_unlock(&svc_mutex);
}


/*
 * Mark argument completion.  Release file descriptor.
 */
void
svc_args_done(SVCXPRT *xprt)
{
	char	dummy;
/* LINTED pointer alignment */
	SVCXPRT	*parent = SVCEXT(xprt)->parent;
	bool_t	wake_up_poller;
	enum	xprt_stat stat;

/* LINTED pointer alignment */
	svc_flags(xprt) |= svc_flags(parent);
/* LINTED pointer alignment */
	svc_flags(xprt) &= ~SVC_ARGS_CHECK;
/* LINTED pointer alignment */
	if (svc_failed(xprt) || svc_defunct(parent))
		return;

/* LINTED pointer alignment */
	if (svc_type(xprt) == SVC_CONNECTION &&
				(stat = SVC_STAT(xprt)) != XPRT_IDLE) {
		if (stat == XPRT_MOREREQS) {
			(void) mutex_lock(&svc_mutex);
			svc_pending_fds[svc_last_pending++] = xprt->xp_fd;
			if (svc_last_pending > CIRCULAR_BUFSIZE)
				svc_last_pending = 0;
			svc_total_pending++;
			(void) mutex_unlock(&svc_mutex);
			wake_up_poller = FALSE;
		} else {
			/*
			 * connection failed
			 */
			return;
		}
	} else {
		(void) rw_wrlock(&svc_fd_lock);
		set_pollfd(xprt->xp_fd, MASKVAL);
		(void) rw_unlock(&svc_fd_lock);
		wake_up_poller = TRUE;
	}

	if (!wake_up_poller || !svc_polling) {
		/*
		 * Wake up any waiting threads.
		 */
		(void) mutex_lock(&svc_mutex);
		if (svc_waiters > 0) {
			(void) cond_broadcast(&svc_thr_fdwait);
			(void) mutex_unlock(&svc_mutex);
			return;
		}
		(void) mutex_unlock(&svc_mutex);
	}

	/*
	 * Wake up any polling thread.
	 */
	if (svc_polling)
		(void) write(svc_pipe[1], &dummy, sizeof (dummy));
}


int
__rpc_legal_connmaxrec(int suggested) {
	if (suggested == -1) {
		/* Supply default */
		return (RPC_MAXDATASIZE + 2*sizeof (uint32_t));
	} else if (suggested < 0) {
		return (-1);
	} else if (suggested > 0) {
		/* Round down to multiple of BYTES_PER_XDR_UNIT */
		suggested -= suggested % BYTES_PER_XDR_UNIT;
		/* If possible, allow for two fragment headers */
		if (suggested < MAXINT-(2*sizeof (uint32_t))) {
			/* Allow for two fragment headers */
			suggested += 2 * sizeof (uint32_t);
		} else {
			suggested = MAXINT;
		}
		if (suggested < sizeof (struct rpc_msg)) {
			return (-1);
		}
	}
	return (suggested);
}


bool_t
rpc_control(int op, void *info)
{
	int		tmp;
	extern int	__rpc_minfd;

	switch (op) {
	case RPC_SVC_MTMODE_SET:
		tmp = *((int *)info);
		if (tmp != RPC_SVC_MT_NONE && tmp != RPC_SVC_MT_AUTO &&
						tmp != RPC_SVC_MT_USER)
			return (FALSE);
		if (svc_mt_mode != RPC_SVC_MT_NONE && svc_mt_mode != tmp)
			return (FALSE);
		svc_mt_mode = tmp;
		return (TRUE);
	case RPC_SVC_MTMODE_GET:
		*((int *)info) = svc_mt_mode;
		return (TRUE);
	case RPC_SVC_THRMAX_SET:
		if ((tmp = *((int *)info)) < 1)
			return (FALSE);
		(void) mutex_lock(&svc_mutex);
		svc_thr_max = tmp;
		(void) mutex_unlock(&svc_mutex);
		return (TRUE);
	case RPC_SVC_THRMAX_GET:
		*((int *)info) = svc_thr_max;
		return (TRUE);
	case RPC_SVC_THRTOTAL_GET:
		*((int *)info) = svc_thr_total;
		return (TRUE);
	case RPC_SVC_THRCREATES_GET:
		*((int *)info) = svc_thr_total_creates;
		return (TRUE);
	case RPC_SVC_THRERRORS_GET:
		*((int *)info) = svc_thr_total_create_errors;
		return (TRUE);
	case RPC_SVC_USE_POLLFD:
		if (*((int *)info) && !__rpc_use_pollfd_done) {
			__rpc_use_pollfd_done = 1;
			return (TRUE);
		}
		return (FALSE);
	case __RPC_CLNT_MINFD_SET:
		tmp = *((int *)info);
		if (tmp < 0)
			return (FALSE);
		__rpc_minfd = tmp;
		return (TRUE);
	case __RPC_CLNT_MINFD_GET:
		*((int *)info) = __rpc_minfd;
		return (TRUE);
	case RPC_SVC_CONNMAXREC_SET:
		tmp = __rpc_legal_connmaxrec(*(int *)info);
		if (tmp >= 0) {
			__rpc_connmaxrec = tmp;
			return (TRUE);
		} else {
			return (FALSE);
		}
	case RPC_SVC_CONNMAXREC_GET:
		*((int *)info) = __rpc_connmaxrec;
		return (TRUE);
	case RPC_SVC_IRTIMEOUT_SET:
		tmp = *((int *)info);
		if (tmp >= 0) {
			__rpc_irtimeout = tmp;
			return (TRUE);
		} else {
			return (FALSE);
		}
	/*
	 * No mutex necessary as _EXCLBIND_SET will/should only
	 * be used before an RPC daemon goes mt-hot.
	 */
	case __RPC_SVC_EXCLBIND_SET:
		if (info) {
			__rpc_tp_exclbind = *((bool_t *)info);
			return (TRUE);
		}
		return (FALSE);
	case __RPC_SVC_EXCLBIND_GET:
		if (info) {
			*((bool_t *)info) = __rpc_tp_exclbind;
			return (TRUE);
		}
		return (FALSE);

	default:
		return (FALSE);
	}
}