/*
* 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
* 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 1993 OpenVision Technologies, 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.
*/
/*
* svc.c, Server-side remote procedure call interface.
*
* There are two sets of procedures here. The xprt routines are
* for handling transport handles. The svc routines handle the
* list of service routines.
*
*/
#include "mt.h"
#include "rpc_mt.h"
#include <assert.h>
#include <errno.h>
#include <stropts.h>
#ifdef PORTMAP
#include <rpc/pmap_clnt.h>
#endif
#include <netconfig.h>
#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
extern bool_t __svc_get_door_cred();
extern bool_t __rpc_get_local_cred();
MAX_MACHINE_NAME + 1 + \
sizeof (struct authsys_parms))
/*
* The services list
* Each entry represents a set of procedures (an rpc program).
* The dispatch routine takes request structs and runs the
* appropriate procedure.
*/
static struct svc_callout {
char *sc_netid;
void (*sc_dispatch)();
} *svc_head;
static struct svc_callout *svc_find();
int _svc_prog_dispatch();
void svc_getreq_common();
char *strdup();
extern mutex_t svc_door_mutex;
extern cond_t svc_door_waitcv;
extern int svc_ndoorfds;
extern SVCXPRT_LIST *_svc_xprtlist;
extern mutex_t xprtlist_lock;
extern void __svc_rm_from_xlist();
extern fd_set _new_svc_fdset;
/*
* If the allocated array of reactor is too small, this value is used as a
* margin. This reduces the number of allocations.
*/
static void remove_pollfd(int fd);
static void __svc_remove_input_of_fd(int fd);
/*
* Data used to handle reactor:
* - one file descriptor we listen to,
* - one callback we call if the fd pops,
* - and a cookie passed as a parameter to the callback.
*
* The structure is an array indexed on the file descriptor. Each entry is
* pointing to the first element of a double-linked list of callback.
* only one callback may be associated to a couple (fd, event).
*/
struct _svc_user_fd_head;
typedef struct {
typedef struct _svc_user_fd_node {
/* The lnk field must be the first field. */
int fd;
unsigned int events;
void* cookie;
typedef struct _svc_user_fd_head {
/* The lnk field must be the first field. */
/* Define some macros to manage the linked list. */
#define LIST_CLR(l) \
/* Array of defined reactor - indexed on file descriptor */
/* current size of file descriptor */
static int svc_nuserfds = 0;
/* Mutex to ensure MT safe operations for user fds callbacks. */
/*
* This structure is used to have constant time alogrithms. There is an array
* of this structure as large as svc_nuserfds. When the user is registering a
* new callback, the address of the created structure is stored in a cell of
* this array. The address of this cell is the returned unique identifier.
*
* On removing, the id is given by the user, then we know if this cell is
* filled or not (with free). If it is free, we return an error. Otherwise,
* we can free the structure pointed by fd_node.
*
* On insertion, we use the linked list created by (first_free,
* next_free). In this way with a constant time computation, we can give a
* correct index to the user.
*/
typedef struct _svc_management_user_fd {
union {
} data;
/* index to the first free elem */
/* the size of this array is the same as svc_nuserfds */
/* current size of user_fd_mgt_array */
static int svc_nmgtuserfds = 0;
/* Define some macros to access data associated to registration ids. */
#ifndef POLLSTANDARD
#define POLLSTANDARD \
#endif
/*
* To free an Id, we set the cell as free and insert its address in the list
* of free cell.
*/
static void
{
first_free = id;
}
/*
* To get a free cell, we just have to take it from the free linked list and
* set the flag to "not free". This function also allocates new memory if
* necessary
*/
static svc_input_id_t
{
if (selected_index == -1) {
/* Allocate new entries */
int i;
* sizeof (_svc_management_user_fd));
if (user_fd_mgt_array == NULL) {
return ((svc_input_id_t)-1);
}
selected_index = (int)first_free;
}
return ((svc_input_id_t)selected_index);
}
/*
* Access to a pollfd treatment. Scan all the associated callbacks that have
* at least one bit in their mask that masks a received event.
*
* If event POLLNVAL is received, we check that one callback processes it, if
* not, then remove the file descriptor from the poll. If there is one, let
* the user do the work.
*/
void
{
(void) mutex_lock(&svc_userfds_lock);
(void) mutex_unlock(&svc_userfds_lock);
return;
}
/* check if at least one mask fits */
(void) mutex_unlock(&svc_userfds_lock);
return;
}
/*
* If one of the received events maps the ones the node listens
* to
*/
invalHandled = TRUE;
}
/*
* The lock must be released before calling the
* user function, as this function can call
* svc_remove_input() for example.
*/
(void) mutex_unlock(&svc_userfds_lock);
/*
* Do not use the node structure anymore, as it
* could have been deallocated by the previous
* callback.
*/
(void) mutex_lock(&svc_userfds_lock);
}
}
}
(void) mutex_unlock(&svc_userfds_lock);
}
/*
* Check if a file descriptor is associated with a user reactor.
* To do this, just check that the array indexed on fd has a non-void linked
* list (ie. first element is not NULL)
*/
{
/* Checks argument */
return (FALSE);
}
/* free everything concerning user fd */
/* used in svc_run.c => no static */
void
__destroy_userfd(void)
{
int one_fd;
/* Clean user fd */
if (svc_userfds != NULL) {
while ((_svc_user_link *) node
}
}
svc_userfds = NULL;
svc_nuserfds = 0;
}
}
/*
* Remove all the callback associated with a fd => useful when the fd is
* closed for instance
*/
static void
{
return;
while ((_svc_user_link *) one_node
}
}
/*
* Allow user to add an fd in the poll list. If it does not succeed, return
* -1. Otherwise, return a svc_id
*/
{
if (user_fd < 0) {
return ((svc_input_id_t)-1);
}
if ((events == 0x0000) ||
return ((svc_input_id_t)-1);
}
(void) mutex_lock(&svc_userfds_lock);
if ((user_fd < svc_nuserfds) &&
/* Already registrated call-back */
(void) mutex_unlock(&svc_userfds_lock);
return ((svc_input_id_t)-1);
}
/* Handle memory allocation. */
if (user_fd >= svc_nuserfds) {
int i;
svc_userfds = (_svc_user_fd_head *)
svc_nuserfds * sizeof (_svc_user_fd_head));
if (svc_userfds == NULL) {
(void) mutex_unlock(&svc_userfds_lock);
return ((svc_input_id_t)-1);
}
for (i = oldSize; i < svc_nuserfds; i++) {
LIST_CLR(svc_userfds[i]);
svc_userfds[i].mask = 0;
}
}
(void) mutex_unlock(&svc_userfds_lock);
return ((svc_input_id_t)-1);
}
/* create a new node */
(void) _svc_attribute_new_id(new_node);
/* Add the new element at the beginning of the list. */
}
/* refresh global mask for this file desciptor */
/* refresh mask for the poll */
(void) mutex_unlock(&svc_userfds_lock);
}
int
{
(void) mutex_lock(&svc_userfds_lock);
/* Immediately update data for id management */
is_free_id(id)) {
(void) mutex_unlock(&svc_userfds_lock);
return (-1);
}
/* Remove this node from the list. */
/* Remove the node flags from the global mask */
}
/* <=> CLEAN NEEDED TO SHRINK MEMORY USAGE */
(void) mutex_unlock(&svc_userfds_lock);
return (0);
}
/*
* Provides default service-side functions for authentication flavors
* that do not use all the fields in struct svc_auth_ops.
*/
/*ARGSUSED*/
static int
{
}
};
/*
* Return pointer to server authentication structure.
*/
SVCAUTH *
{
/* LINTED pointer alignment */
return (&SVC_XP_AUTH(xprt));
}
/*
* A callback routine to cleanup after a procedure is executed.
*/
void *
{
__proc_cleanup_cb = (void (*)())cb;
return (tmp);
}
/* *************** SVCXPRT related stuff **************** */
/*
* Add fd to svc_pollfd
*/
static void
{
if (fd < FD_SETSIZE) {
#if !defined(_LP64)
#endif
svc_nfds++;
svc_nfds_set++;
if (fd >= svc_max_fd)
}
if (fd >= svc_max_pollfd)
if (svc_max_pollfd > svc_pollfd_allocd) {
int i = svc_pollfd_allocd;
do {
} while (svc_max_pollfd > svc_pollfd_allocd);
sizeof (pollfd_t) * svc_pollfd_allocd);
svc_pollfd = tmp;
for (; i < svc_pollfd_allocd; i++)
POLLFD_CLR(i, tmp);
} else {
/*
* give an error message; undo fdset setting
* above; reset the pollfd_shrinking flag.
* because of this poll will not be done
* on these fds.
*/
if (fd < FD_SETSIZE) {
#if !defined(_LP64)
#endif
svc_nfds--;
svc_nfds_set--;
svc_max_fd--;
}
pollfd_shrinking = 0;
_exit(1);
}
}
svc_npollfds++;
}
/*
* the fd is still active but only the bit in fdset is cleared.
* do not subtract svc_nfds or svc_npollfds
*/
void
{
#if !defined(_LP64)
#endif
svc_nfds_set--;
}
}
}
/*
* sets the bit in fdset for an active fd so that poll() is done for that
*/
void
{
if (fd < FD_SETSIZE) {
#if !defined(_LP64)
#endif
svc_nfds_set++;
}
if (fd < svc_pollfd_allocd) {
}
}
/*
* remove a svc_pollfd entry; it does not shrink the memory
*/
static void
{
svc_max_fd--;
svc_nfds--;
svc_npollfds--;
}
/*
* delete a svc_pollfd entry; it shrinks the memory
* use remove_pollfd if you do not want to shrink
*/
static void
{
if (pollfd_shrinking && svc_max_pollfd <
(svc_pollfd_allocd - POLLFD_SHRINK)) {
do {
sizeof (pollfd_t) * svc_pollfd_allocd);
if (svc_pollfd == NULL) {
_exit(1);
}
}
}
/*
* Activate a transport handle.
*/
void
{
#ifdef CALLBACK
extern void (*_svc_getreqset_proc)();
#endif
/* VARIABLES PROTECTED BY svc_fd_lock: svc_xports, svc_fdset */
(void) rw_wrlock(&svc_fd_lock);
if (svc_xports == NULL) {
/* allocate some small amount first */
if (svc_xports == NULL) {
_exit(1);
}
#ifdef CALLBACK
/*
* XXX: This code does not keep track of the server state.
*
* This provides for callback support. When a client
* recv's a call from another client on the server fd's,
* it calls _svc_getreqset_proc() which would return
* after serving all the server requests. Also look under
*/
#endif
}
while (fd >= nsvc_xports) {
/* time to expand svc_xprts */
_exit(1);
}
sizeof (SVCXPRT *) * FD_INCREMENT);
}
if (svc_polling) {
char dummy;
/*
* This happens only in one of the MT modes.
* Wake up poller.
*/
}
/*
* If already dispatching door based services, start
* dispatching TLI based services now.
*/
(void) mutex_lock(&svc_door_mutex);
if (svc_ndoorfds > 0)
(void) cond_signal(&svc_door_waitcv);
(void) mutex_unlock(&svc_door_mutex);
/* allocate initial chunk */
else {
_exit(1);
}
}
(void) rw_unlock(&svc_fd_lock);
}
/*
* De-activate a transport handle.
*/
void
{
if (lock_not_held)
(void) rw_wrlock(&svc_fd_lock);
}
if (lock_not_held)
(void) rw_unlock(&svc_fd_lock);
}
void
{
}
/* ********************** CALLOUT list related stuff ************* */
/*
* Add a service program to the callout list.
* The dispatch routine will be called when a rpc request for this
* program number comes in.
*/
{
int flag = 0;
/* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
flag = 1;
flag = 1;
!= NULL) {
flag = 1;
} /* must have been created with svc_raw_create */
return (FALSE);
if (netid)
if (s->sc_dispatch == dispatch)
goto rpcb_it; /* he is registering another xptr */
return (FALSE);
}
s = malloc(sizeof (struct svc_callout));
if (s == NULL) {
if (netid)
return (FALSE);
}
s->sc_dispatch = dispatch;
/*
* The ordering of transports is such that the most frequently used
* one appears first. So add the new entry to the end of the list.
*/
;
*s2 = s;
free(s);
return (FALSE);
}
/* now register the information with the local binder service */
if (nconf)
return (TRUE);
/*NOTREACHED*/
}
/*
* Remove a service program from the callout list.
*/
void
{
struct svc_callout *s;
/* unregister the information anyway */
} else {
}
if (s->sc_netid)
free(s);
}
}
#ifdef PORTMAP
/*
* Add a service program to the callout list.
* The dispatch routine will be called when a rpc request for this
* program number comes in.
* For version 2 portmappers.
*/
{
struct svc_callout *s;
int flag = 0;
flag = 1;
/* fill in missing netid field in SVCXPRT */
flag = 1;
} /* must be svc_raw_create */
return (FALSE);
if (netid)
if (s->sc_dispatch == dispatch)
goto pmap_it; /* he is registering another xptr */
return (FALSE);
}
s = malloc(sizeof (struct svc_callout));
if (s == (struct svc_callout *)0) {
if (netid)
return (FALSE);
}
s->sc_dispatch = dispatch;
svc_head = s;
free(s);
return (FALSE);
}
/* now register the information with the local binder service */
if (protocol)
return (TRUE);
}
/*
* Remove a service program from the callout list.
* For version 2 portmappers.
*/
void
{
struct svc_callout *s;
} else {
}
if (s->sc_netid)
free(s);
/* unregister the information with the local binder service */
}
}
#endif /* PORTMAP */
/*
* Search the callout list for a program number, return the callout
* struct.
* Also check for transport as well. Many routines such as svc_unreg
* dont give any corresponding transport, so dont check for transport if
* netid == NULL
*/
static struct svc_callout *
{
struct svc_callout *s, *p;
/* WRITE LOCK HELD ON ENTRY: svc_lock */
/* assert(RW_WRITE_HELD(&svc_lock)); */
p = NULL_SVC;
break;
p = s;
}
*prev = p;
return (s);
}
/* ******************* REPLY GENERATION ROUTINES ************ */
/*
* Send a reply to an rpc request
*/
const caddr_t xdr_location)
{
}
/*
* No procedure error reply
*/
void
{
}
/*
* Can't decode args error reply
*/
void
{
}
/*
* Some system error
*/
void
{
}
/*
* Tell RPC package to not complain about version errors to the client. This
* is useful when revving broadcast protocols that sit on a fixed address.
* There is really one (or should be only one) example of this kind of
* protocol: the portmapper (or rpc binder).
*/
void
{
/* LINTED pointer alignment */
}
void
{
/* LINTED pointer alignment */
}
void
{
}
int
{
/* LINTED pointer alignment */
}
/*
* Authentication error reply
*/
void
{
}
/*
* Auth too weak error reply
*/
void
{
}
/*
* Program unavailable error reply
*/
void
{
}
/*
* Program version mismatch error reply
*/
void
{
}
/* ******************* SERVER INPUT STUFF ******************* */
/*
* Get server side input from some transport.
*
* Statement of authentication parameters management:
* This function owns and manages all authentication parameters, specifically
* the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
* the "cooked" credentials (rqst->rq_clntcred).
* However, this function does not know the structure of the cooked
* credentials, so it make the following assumptions:
* a) the structure is contiguous (no pointers), and
* b) the cred structure size does not exceed RQCRED_SIZE bytes.
* In all events, all three parameters are freed upon exit from this routine.
* The storage is trivially management on the call stack in user land, but
* is mallocated in kernel land.
*/
void
{
}
void
{
int i;
for (i = 0; i < svc_max_fd; i++) {
/* fd has input waiting */
}
}
void
{
int i;
int fds_found;
if (p->revents) {
/* fd has input waiting */
fds_found++;
/*
* We assume that this function is only called
* via someone select()ing from svc_fdset or
* poll()ing from svc_pollset[]. Thus it's safe
* to handle the POLLNVAL event by simply turning
* the corresponding bit off in svc_fdset. The
* svc_pollset[] array is derived from svc_fdset
* and so will also be updated eventually.
*
* XXX Should we do an xprt_unregister() instead?
*/
/* Handle user callback */
(void) rw_rdlock(&svc_fd_lock);
(void) rw_unlock(&svc_fd_lock);
} else {
(void) rw_wrlock(&svc_fd_lock);
(void) rw_unlock(&svc_fd_lock);
} else {
svc_getreq_common(p->fd);
}
}
}
}
}
void
{
struct svc_req *r;
char *cred_area;
(void) rw_rdlock(&svc_fd_lock);
/* HANDLE USER CALLBACK */
(void) rw_unlock(&svc_fd_lock);
return;
}
/*
* The transport associated with this fd could have been
* removed from svc_timeout_nonblock_xprt_and_LRU, for instance.
* This can happen if two or more fds get read events and are
* passed to svc_getreq_poll/set, the first fd is seviced by
* the dispatch routine and cleans up any dead transports. If
* one of the dead transports removed is the other fd that
* had a read event then svc_getreq_common() will be called with no
* xprt associated with the fd that had the original read event.
*/
(void) rw_unlock(&svc_fd_lock);
return;
}
(void) rw_unlock(&svc_fd_lock);
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* receive msgs from xprtprt (support batch calls) */
do {
/*
* Check if the xprt has been disconnected in a recursive call
* in the service dispatch routine. If so, then break
*/
(void) rw_rdlock(&svc_fd_lock);
(void) rw_unlock(&svc_fd_lock);
break;
}
(void) rw_unlock(&svc_fd_lock);
/*
* Call cleanup procedure if set.
*/
(*__proc_cleanup_cb)(xprt);
break;
}
} while (stat == XPRT_MOREREQS);
}
int
{
struct svc_callout *s;
int prog_found;
void (*disp_fn)();
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* first authenticate the message */
/* Check for null flavor and bypass these calls if possible */
} else {
&no_dispatch)) != AUTH_OK) {
return (0);
}
if (no_dispatch)
return (0);
}
/* match message with a registered service */
prog_found = FALSE;
high_vers = 0;
prog_found = TRUE;
s->sc_netid) == 0)) {
disp_fn = (*s->sc_dispatch);
return (1);
}
prog_found = FALSE;
}
} /* found correct program */
}
/*
* if we got here, the program or version
* is not served ...
*/
if (prog_found) {
/* LINTED pointer alignment */
if (!version_keepquiet(xprt))
} else {
}
return (0);
}
/* ******************* SVCXPRT allocation and deallocation ***************** */
/*
* svc_xprt_alloc() - allocate a service transport handle
*/
SVCXPRT *
svc_xprt_alloc(void)
{
goto err_exit;
goto err_exit;
goto err_exit;
goto err_exit;
goto err_exit;
goto err_exit;
/* LINTED pointer alignment */
return (xprt);
return (NULL);
}
/*
* svc_xprt_free() - free a service handle
*/
void
{
/* LINTED pointer alignment */
if (xprt)
if (xt)
if (my_xlist)
if (msg)
if (req)
if (cred_area)
}
/*
* svc_xprt_destroy() - free parent and child xprt list
*/
void
{
int type;
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
/* LINTED pointer alignment */
switch (type) {
case SVC_DGRAM:
break;
case SVC_RENDEZVOUS:
break;
case SVC_CONNECTION:
break;
case SVC_DOOR:
break;
}
}
}
/*
* svc_copy() - make a copy of parent
*/
SVCXPRT *
{
/* LINTED pointer alignment */
case SVC_DGRAM:
return (svc_dg_xprtcopy(xprt));
case SVC_RENDEZVOUS:
return (svc_vc_xprtcopy(xprt));
case SVC_CONNECTION:
return (svc_fd_xprtcopy(xprt));
}
return (NULL);
}
/*
* _svc_destroy_private() - private SVC_DESTROY interface
*/
void
{
/* LINTED pointer alignment */
case SVC_DGRAM:
break;
case SVC_RENDEZVOUS:
case SVC_CONNECTION:
break;
}
}
/*
* svc_get_local_cred() - fetch local user credentials. This always
* works over doors based transports. For local transports, this
* does not yield correct results unless the __rpc_negotiate_uid()
* call has been invoked to enable this feature.
*/
{
/* LINTED pointer alignment */
}
/* ******************* DUPLICATE ENTRY HANDLING ROUTINES ************** */
/*
* the dup cacheing routines below provide a cache of received
* transactions. rpc service routines can use this to detect
* retransmissions and re-send a non-failure response. Uses a
* lru scheme to find entries to get rid of entries in the cache,
* though only DUP_DONE entries are placed on the lru list.
* the routines were written towards development of a generic
* SVC_DUP() interface, which can be expanded to encompass the
* svc_dg_enablecache() routines as well. the cache is currently
* private to the automounter.
*/
/* dupcache header contains xprt specific information */
struct dupcache {
int dc_buckets;
int dc_maxsz;
int dc_basis;
};
/*
* private duplicate cache request routines
*/
#ifdef DUP_DEBUG
static void __svc_dupcache_debug(struct dupcache *);
#endif /* DUP_DEBUG */
/* default parameters for the dupcache */
/*
* __svc_dupcache_init(void *condition, int basis, char *xprt_cache)
* initialize the duprequest cache and assign it to the xprt_cache
* Use default values depending on the cache condition and basis.
* return TRUE on success and FALSE on failure
*/
{
int i;
(void) mutex_lock(&initdc_lock);
(void) mutex_unlock(&initdc_lock);
"__svc_dupcache_init: multiply defined dup cache");
return (FALSE);
}
switch (basis) {
case DUPCACHE_FIXEDTIME:
(void) mutex_unlock(&initdc_lock);
"__svc_dupcache_init: memory alloc failed");
return (FALSE);
}
else
sizeof (struct dupreq *));
(void) mutex_unlock(&initdc_lock);
"__svc_dupcache_init: memory alloc failed");
return (FALSE);
}
for (i = 0; i < DUPCACHE_BUCKETS; i++)
*xprt_cache = (char *)dc;
break;
default:
(void) mutex_unlock(&initdc_lock);
"__svc_dupcache_init: undefined dup cache basis");
return (FALSE);
}
(void) mutex_unlock(&initdc_lock);
return (TRUE);
}
/*
* __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
* char *xprt_cache)
* searches the request cache. Creates an entry and returns DUP_NEW if
* the request is not found in the cache. If it is found, then it
* returns the state of the request (in progress, drop, or done) and
* also allocates, and passes back results to the user (if any) in
* resp_buf, and its length in resp_bufsz. DUP_ERROR is returned on error.
*/
int
char *xprt_cache)
{
int rc;
/* LINTED pointer alignment */
return (DUP_ERROR);
}
/* get the xid of the request */
return (DUP_ERROR);
}
return (rc);
return (DUP_ERROR);
== DUP_ERROR)
return (rc);
return (DUP_NEW);
}
/*
* __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf,
* uint_t *resp_bufsz,truct dupcache *dc, uint32_t drxid,
* uint32_t drhash)
* Checks to see whether an entry already exists in the cache. If it does
* copy back into the resp_buf, if appropriate. Return the status of
* the request, or DUP_NEW if the entry is not in the cache
*/
static int
{
/* sanity check */
"\n__svc_dupdone: hashing error");
return (DUP_ERROR);
}
/*
* return results for requests on lru list, if
* appropriate requests must be DUP_DROP or DUP_DONE
* to have a result. A NULL buffer in the cache
* implies no results were sent during dupdone.
* A NULL buffer in the call implies not interested
* in results.
*/
"__svc_dupcache_check: malloc failed");
return (DUP_ERROR);
}
} else {
/* no result */
if (resp_buf)
if (resp_bufsz)
*resp_bufsz = 0;
}
}
}
return (DUP_NEW);
}
/*
* __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
* Return a victim dupreq entry to the caller, depending on cache policy.
*/
static struct dupreq *
{
case DUPCACHE_FIXEDTIME:
/*
* The hash policy is to free up a bit of the hash
* table before allocating a new entry as the victim.
* Freeing up the hash table each time should split
* the cost of keeping the hash table clean among threads.
* Note that only DONE or DROPPED entries are on the lru
* list but we do a sanity check anyway.
*/
/* clean and then free the entry */
/*
* The LRU list can't contain an
* entry where the status is other than
* DUP_DONE or DUP_DROP.
*/
"__svc_dupcache_victim: bad victim");
#ifdef DUP_DEBUG
/*
* print the cache info, since we already
* hold the writers lock, we shall continue
* calling __svc_dupcache_debug()
*/
#endif /* DUP_DEBUG */
return (NULL);
}
/* free buffers */
}
}
/* unhash the entry */
if (dr->dr_prevchain)
/* modify the lru pointers */
} else {
}
}
/*
* Allocate and return new clean entry as victim
*/
"__svc_dupcache_victim: malloc failed");
return (NULL);
}
return (dr);
default:
"__svc_dupcache_victim: undefined dup cache_basis");
return (NULL);
}
}
/*
* __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
* struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
* build new duprequest entry and then insert into the cache
*/
static int
{
return (DUP_ERROR);
}
/* place entry at head of hash table */
return (DUP_NEW);
}
/*
* __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
* int status, char *xprt_cache)
* Marks the request done (DUP_DONE or DUP_DROP) and stores the response.
* Only DONE and DROP requests can be marked as done. Sets the lru pointers
* to make the entry the most recently used. Returns DUP_ERROR or status.
*/
int
int status, char *xprt_cache)
{
int rc;
/* LINTED pointer alignment */
return (DUP_ERROR);
}
return (DUP_ERROR);
}
/* find the xid of the entry in the cache */
return (DUP_ERROR);
}
/* update the status of the entry and result buffers, if required */
return (DUP_ERROR);
}
return (rc);
}
/*
* __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf,
* uint_t resp_bufsz, int status, struct dupcache *dc, uint32_t drxid,
* uint32_t drhash)
* Check if entry exists in the dupcacache. If it does, update its status
* and time and also its buffer, if appropriate. Its possible, but unlikely
* for DONE requests to not exist in the cache. Return DUP_ERROR or status.
*/
static int
{
/* sanity check */
"\n__svc_dupdone: hashing error");
return (DUP_ERROR);
}
/* store the results if bufer is not NULL */
"__svc_dupdone: malloc failed");
return (DUP_ERROR);
}
(uint_t)resp_bufsz);
}
/* update status and done time */
/* move the entry to the mru position */
} else {
}
return (status);
}
}
return (DUP_ERROR);
}
#ifdef DUP_DEBUG
/*
* __svc_dupcache_debug(struct dupcache *dc)
* print out the hash table stuff
*
* This function requires the caller to hold the reader
* or writer version of the duplicate request cache lock (dc_lock).
*/
static void
{
int i;
for (i = 0; i < dc->dc_buckets; i++) {
if (!bval) { /* ensures bucket printed only once */
}
}
}
}
}
}
#endif /* DUP_DEBUG */