svc_run.c revision 61961e0f20c7637a3846bb39786bb9dffa91dfb9
/*
* 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
* 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 <errno.h>
#include <syslog.h>
#include <thread.h>
#include <assert.h>
#include <libintl.h>
#include <values.h>
extern const char __nsl_dom[];
extern void __destroy_userfd();
extern void clear_pollfd(int);
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.
*/
/*
* XXX - eventually, all mutexes and their initializations static
*/
/*
* Variables used for MT
*/
int svc_mt_mode; /* multi-threading mode */
/* BEGIN PROTECTED BY svc_mutex */
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_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 */
/* END PROTECTED BY svc_thr_mutex */
/* BEGIN PROTECTED BY svc_exit_mutex */
/* 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 */
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) mutex_unlock(&svc_door_mutex);
}
}
/*
* This function causes svc_run() to exit by destroying all
* service handles.
*/
void
svc_exit(void)
{
int fd;
char dummy;
/* NO LOCKS HELD */
(void) mutex_lock(&svc_exit_mutex);
if (svc_exit_done) {
(void) mutex_unlock(&svc_exit_mutex);
return;
}
if (xprt) {
}
}
(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) mutex_lock(&svc_door_mutex);
(void) mutex_unlock(&svc_door_mutex);
/* destroy reactor information if any */
}
/*
* this funtion is called with svc_fd_lock and svc_thr_mutex
*/
static int
alloc_pollset(int npollfds)
{
if (npollfds > svc_pollset_allocd) {
do {
} while (npollfds > svc_pollset_allocd);
sizeof (pollfd_t) * svc_pollset_allocd);
return (-1);
}
svc_pollset = tmp;
}
return (0);
}
extern int _sigemptyset(sigset_t *);
extern int _sigaddset(sigset_t *, int);
static void
_svc_run(void)
{
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);
while (!svc_exit_done) {
/*
* Check whether there is any server fd on which we may want
* to wait.
*/
(void) rw_rdlock(&svc_fd_lock);
break;
(void) rw_unlock(&svc_fd_lock);
if (npollfds == 0)
break; /* None waiting, hence return */
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:
}
}
}
/*
* 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,
};
static void
_svc_run_mt(void)
{
int npollfds;
int n_new;
/*
* 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_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);
myfd = -1;
/*
* Check if there are any descriptors with data pending.
*/
if (svc_total_pending > 0) {
if (svc_next_pending > CIRCULAR_BUFSIZE)
svc_next_pending = 0;
}
/*
* Get the next active file descriptor to process.
*/
/*
* 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 ||
(void) rw_unlock(&svc_fd_lock);
svc_polling = FALSE;
(void) mutex_unlock(&svc_mutex);
(void) mutex_unlock(&svc_thr_mutex);
if (!main_thread) {
/* NOTREACHED */
}
break;
}
(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) {
(void) mutex_unlock(&svc_mutex);
/* 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) {
(void) mutex_unlock(&svc_mutex);
if (!main_thread)
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.
*/
do {
int i, j;
(void) mutex_unlock(&svc_mutex);
(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++) {
/* 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.
*/
clear_pipe();
n_polled--;
}
svc_next_pollfd = 0;
/*
* Check exit flag.
*/
if (svc_exit_done) {
(void) mutex_unlock(&svc_mutex);
(void) mutex_unlock(&svc_thr_mutex);
if (!main_thread) {
/* 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) {
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 &&
if (n_new > 0)
}
/*
* 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.
*/
if (parent_xprt == NULL) {
/* Check if it is not a user FD */
goto continue_with_locks;
}
/* LINTED pointer alignment */
goto continue_with_locks;
/*
* Make a copy of parent xprt, update svc_fdset.
*/
goto continue_with_locks;
/*
* Keep track of active threads in automatic mode.
*/
if (svc_mt_mode == RPC_SVC_MT_AUTO)
/*
* Release mutexes so other threads can get going.
*/
(void) mutex_unlock(&svc_mutex);
(void) mutex_unlock(&svc_thr_mutex);
/*
* Process request.
*/
{
struct svc_req *r;
char *cred_area;
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
&(cred_area[MAX_AUTH_BYTES]);
/*
* receive RPC message
*/
if (svc_mt_mode != RPC_SVC_MT_NONE)
/* LINTED pointer alignment */
/*
* Call cleanup procedure if set.
*/
if (__proc_cleanup_cb != NULL)
(*__proc_cleanup_cb)(xprt);
} else
/*
* Finish up, if automatic mode, or not dispatched.
*/
/* LINTED pointer alignment */
(void) mutex_lock(&svc_mutex);
if (svc_mt_mode == RPC_SVC_MT_AUTO) {
/*
* not active any more
*/
/*
* 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)) {
if (svc_thr_total ==
svc_waiters) {
(void) cond_broadcast(
}
(void) mutex_unlock(&svc_mutex);
/* NOTREACHED */
}
}
(void) mutex_unlock(&svc_mutex);
}
}
}
}
/*
* start_threads() - Start specified number of threads.
*/
static void
start_threads(int num_threads)
{
int i;
for (i = 0; i < num_threads; i++) {
THR_DETACHED, NULL) == 0) {
} else {
}
}
}
/*
* create_pipe() - create pipe for breaking out of poll.
*/
static void
create_pipe(void)
{
"RPC: svc could not create pipe - exiting"));
exit(1);
}
"RPC: svc pipe error - exiting"));
exit(1);
}
"RPC: svc pipe error - exiting"));
exit(1);
}
}
/*
* clear_pipe() - Empty data in pipe.
*/
static void
clear_pipe(void)
{
char buf[16];
int i;
do {
} while (i == sizeof (buf));
}
/*
* select_next_pollfd() - Select the next active fd in svc_pollset.
*/
static int
{
int i;
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;
*pollfdIndex = i;
return (0);
}
}
svc_next_pollfd = svc_pollfds = 0;
*fd = -1;
return (-1);
}
/*
* make_xprt_copy() - make a copy of the parent xprt.
* Clear fd bit in svc_fdset.
*/
static SVCXPRT *
{
/* LINTED pointer alignment */
if (xret) {
/* LINTED pointer alignment */
} else
if (xprt) {
/* LINTED pointer alignment */
(void) rw_wrlock(&svc_fd_lock);
(void) rw_unlock(&svc_fd_lock);
}
return (xprt);
}
/*
* _svc_done_private() - return copies to library.
*/
static void
{
/* LINTED pointer alignment */
return;
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/*
* 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 */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
(SVC_FAILED | SVC_DEFUNCT));
/* LINTED pointer alignment */
}
}
void
{
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 */
return;
/* LINTED pointer alignment */
(void) mutex_lock(&svc_mutex);
(void) mutex_unlock(&svc_mutex);
}
/*
* Mark argument completion. Release file descriptor.
*/
void
{
char dummy;
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
return;
/* LINTED pointer alignment */
if (stat == XPRT_MOREREQS) {
(void) mutex_lock(&svc_mutex);
if (svc_last_pending > CIRCULAR_BUFSIZE)
svc_last_pending = 0;
(void) mutex_unlock(&svc_mutex);
} else {
/*
* connection failed
*/
return;
}
} else {
(void) rw_wrlock(&svc_fd_lock);
(void) rw_unlock(&svc_fd_lock);
}
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)
}
int
__rpc_legal_connmaxrec(int suggested) {
if (suggested == -1) {
/* Supply default */
} else if (suggested < 0) {
return (-1);
} else if (suggested > 0) {
/* Round down to multiple of BYTES_PER_XDR_UNIT */
/* If possible, allow for two fragment headers */
/* Allow for two fragment headers */
} else {
}
return (-1);
}
}
return (suggested);
}
{
int tmp;
extern int __rpc_minfd;
switch (op) {
case RPC_SVC_MTMODE_SET:
tmp != RPC_SVC_MT_USER)
return (FALSE);
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:
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) {
return (TRUE);
}
return (FALSE);
case __RPC_CLNT_MINFD_SET:
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:
if (tmp >= 0) {
return (TRUE);
} else {
return (FALSE);
}
case RPC_SVC_CONNMAXREC_GET:
*((int *)info) = __rpc_connmaxrec;
return (TRUE);
case RPC_SVC_IRTIMEOUT_SET:
if (tmp >= 0) {
return (TRUE);
} else {
return (FALSE);
}
/*
* be used before an RPC daemon goes mt-hot.
*/
case __RPC_SVC_EXCLBIND_SET:
if (info) {
return (TRUE);
}
return (FALSE);
case __RPC_SVC_EXCLBIND_GET:
if (info) {
return (TRUE);
}
return (FALSE);
default:
return (FALSE);
}
}