root/usr/src/lib/libnsl/rpc/svc_run.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 */

/* 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.
 */

/*
 * 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;

/*
 * Maximum number of outstanding connection indications (listen backlog).
 */
static int __svc_lstnbklog = 64;        /* Chosen Arbitrarily */

/*
 * 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 */

/* 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);
}

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;

        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_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);

        case __RPC_SVC_LSTNBKLOG_SET:
                tmp = *(int *)info;
                if (tmp > 0) {
                        __svc_lstnbklog = tmp;
                        return (TRUE);
                }
                return (FALSE);
        case __RPC_SVC_LSTNBKLOG_GET:
                *(int *)info = __svc_lstnbklog;
                return (TRUE);

        default:
                return (FALSE);
        }
}