4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 * Copyright 2017 Joyent Inc
29 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
31 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
34 * Portions of this source code were derived from Berkeley
35 * 4.3 BSD under license from the Regents of the University of
40 * svc.c, Server-side remote procedure call interface.
42 * There are two sets of procedures here. The xprt routines are
43 * for handling transport handles. The svc routines handle the
44 * list of service routines.
52 #include <sys/types.h>
57 #include <rpc/pmap_clnt.h>
59 #include <netconfig.h>
66 extern bool_t
__svc_get_door_cred();
67 extern bool_t
__rpc_get_local_cred();
70 static int nsvc_xports
; /* total number of svc_xports allocated */
72 XDR
**svc_xdrs
; /* common XDR receive area */
73 int nsvc_xdrs
; /* total number of svc_xdrs allocated */
75 int __rpc_use_pollfd_done
; /* to unlimit the number of connections */
79 * Each entry represents a set of procedures (an rpc program).
80 * The dispatch routine takes request structs and runs the
81 * appropriate procedure.
83 static struct svc_callout
{
84 struct svc_callout
*sc_next
;
88 void (*sc_dispatch
)();
90 extern rwlock_t svc_lock
;
92 static struct svc_callout
*svc_find();
93 int _svc_prog_dispatch();
94 void svc_getreq_common();
97 extern mutex_t svc_door_mutex
;
98 extern cond_t svc_door_waitcv
;
99 extern int svc_ndoorfds
;
100 extern SVCXPRT_LIST
*_svc_xprtlist
;
101 extern mutex_t xprtlist_lock
;
102 extern void __svc_rm_from_xlist();
105 extern fd_set _new_svc_fdset
;
109 * If the allocated array of reactor is too small, this value is used as a
110 * margin. This reduces the number of allocations.
112 #define USER_FD_INCREMENT 5
114 static void add_pollfd(int fd
, short events
);
115 static void remove_pollfd(int fd
);
116 static void __svc_remove_input_of_fd(int fd
);
119 * Data used to handle reactor:
120 * - one file descriptor we listen to,
121 * - one callback we call if the fd pops,
122 * - and a cookie passed as a parameter to the callback.
124 * The structure is an array indexed on the file descriptor. Each entry is
125 * pointing to the first element of a double-linked list of callback.
126 * only one callback may be associated to a couple (fd, event).
129 struct _svc_user_fd_head
;
132 struct _svc_user_fd_node
*next
;
133 struct _svc_user_fd_node
*previous
;
136 typedef struct _svc_user_fd_node
{
141 svc_callback_t callback
;
145 typedef struct _svc_user_fd_head
{
146 struct _svc_user_fd_node
*list
;
147 unsigned int mask
; /* logical OR of all sub-masks */
151 /* Array of defined reactor - indexed on file descriptor */
152 static _svc_user_fd_head
*svc_userfds
= NULL
;
154 /* current size of file descriptor */
155 static int svc_nuserfds
= 0;
157 /* Mutex to ensure MT safe operations for user fds callbacks. */
158 static mutex_t svc_userfds_lock
= DEFAULTMUTEX
;
162 * This structure is used to have constant time alogrithms. There is an array
163 * of this structure as large as svc_nuserfds. When the user is registering a
164 * new callback, the address of the created structure is stored in a cell of
165 * this array. The address of this cell is the returned unique identifier.
167 * On removing, the id is given by the user, then we know if this cell is
168 * filled or not (with free). If it is free, we return an error. Otherwise,
169 * we can free the structure pointed by fd_node.
171 * On insertion, we use the linked list created by (first_free,
172 * next_free). In this way with a constant time computation, we can give a
173 * correct index to the user.
176 typedef struct _svc_management_user_fd
{
179 svc_input_id_t next_free
;
180 _svc_user_fd_node
*fd_node
;
182 } _svc_management_user_fd
;
184 /* index to the first free elem */
185 static svc_input_id_t first_free
= (svc_input_id_t
)-1;
186 /* the size of this array is the same as svc_nuserfds */
187 static _svc_management_user_fd
* user_fd_mgt_array
= NULL
;
189 /* current size of user_fd_mgt_array */
190 static int svc_nmgtuserfds
= 0;
193 /* Define some macros to access data associated to registration ids. */
194 #define node_from_id(id) (user_fd_mgt_array[(int)id].data.fd_node)
195 #define is_free_id(id) (user_fd_mgt_array[(int)id].free)
198 #define POLLSTANDARD \
199 (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| \
200 POLLWRBAND|POLLERR|POLLHUP|POLLNVAL)
204 * To free an Id, we set the cell as free and insert its address in the list
209 _svc_free_id(const svc_input_id_t id
)
211 assert(((int)id
>= 0) && ((int)id
< svc_nmgtuserfds
));
212 user_fd_mgt_array
[(int)id
].free
= TRUE
;
213 user_fd_mgt_array
[(int)id
].data
.next_free
= first_free
;
218 * To get a free cell, we just have to take it from the free linked list and
219 * set the flag to "not free". This function also allocates new memory if
222 static svc_input_id_t
223 _svc_attribute_new_id(_svc_user_fd_node
*node
)
225 int selected_index
= (int)first_free
;
226 assert(node
!= NULL
);
228 if (selected_index
== -1) {
229 /* Allocate new entries */
230 int L_inOldSize
= svc_nmgtuserfds
;
232 _svc_management_user_fd
*tmp
;
234 svc_nmgtuserfds
+= USER_FD_INCREMENT
;
236 tmp
= reallocarray(user_fd_mgt_array
, svc_nmgtuserfds
,
237 sizeof (_svc_management_user_fd
));
240 syslog(LOG_ERR
, "_svc_attribute_new_id: out of memory");
241 svc_nmgtuserfds
= L_inOldSize
;
243 return ((svc_input_id_t
)-1);
246 user_fd_mgt_array
= tmp
;
248 for (i
= svc_nmgtuserfds
- 1; i
>= L_inOldSize
; i
--)
249 _svc_free_id((svc_input_id_t
)i
);
250 selected_index
= (int)first_free
;
253 node
->id
= (svc_input_id_t
)selected_index
;
254 first_free
= user_fd_mgt_array
[selected_index
].data
.next_free
;
256 user_fd_mgt_array
[selected_index
].data
.fd_node
= node
;
257 user_fd_mgt_array
[selected_index
].free
= FALSE
;
259 return ((svc_input_id_t
)selected_index
);
263 * Access to a pollfd treatment. Scan all the associated callbacks that have
264 * at least one bit in their mask that masks a received event.
266 * If event POLLNVAL is received, we check that one callback processes it, if
267 * not, then remove the file descriptor from the poll. If there is one, let
268 * the user do the work.
271 __svc_getreq_user(struct pollfd
*pfd
)
274 short revents
= pfd
->revents
;
275 bool_t invalHandled
= FALSE
;
276 _svc_user_fd_node
*node
;
278 (void) mutex_lock(&svc_userfds_lock
);
280 if ((fd
< 0) || (fd
>= svc_nuserfds
)) {
281 (void) mutex_unlock(&svc_userfds_lock
);
285 node
= svc_userfds
[fd
].list
;
287 /* check if at least one mask fits */
288 if (0 == (revents
& svc_userfds
[fd
].mask
)) {
289 (void) mutex_unlock(&svc_userfds_lock
);
293 while ((svc_userfds
[fd
].mask
!= 0) && (node
!= NULL
)) {
295 * If one of the received events maps the ones the node listens
298 _svc_user_fd_node
*next
= node
->lnk
.next
;
300 if (node
->callback
!= NULL
) {
301 if (node
->events
& revents
) {
302 if (revents
& POLLNVAL
) {
307 * The lock must be released before calling the
308 * user function, as this function can call
309 * svc_remove_input() for example.
311 (void) mutex_unlock(&svc_userfds_lock
);
312 node
->callback(node
->id
, node
->fd
,
313 node
->events
& revents
, node
->cookie
);
315 * Do not use the node structure anymore, as it
316 * could have been deallocated by the previous
319 (void) mutex_lock(&svc_userfds_lock
);
325 if ((revents
& POLLNVAL
) && !invalHandled
)
326 __svc_remove_input_of_fd(fd
);
327 (void) mutex_unlock(&svc_userfds_lock
);
332 * Check if a file descriptor is associated with a user reactor.
333 * To do this, just check that the array indexed on fd has a non-void linked
334 * list (ie. first element is not NULL)
337 __is_a_userfd(int fd
)
339 /* Checks argument */
340 if ((fd
< 0) || (fd
>= svc_nuserfds
))
342 return ((svc_userfds
[fd
].mask
== 0x0000)? FALSE
:TRUE
);
345 /* free everything concerning user fd */
346 /* used in svc_run.c => no static */
349 __destroy_userfd(void)
353 if (svc_userfds
!= NULL
) {
354 for (one_fd
= 0; one_fd
< svc_nuserfds
; one_fd
++) {
355 _svc_user_fd_node
*node
;
357 node
= svc_userfds
[one_fd
].list
;
358 while (node
!= NULL
) {
359 _svc_user_fd_node
*tmp
= node
;
360 _svc_free_id(node
->id
);
361 node
= node
->lnk
.next
;
366 free(user_fd_mgt_array
);
367 user_fd_mgt_array
= NULL
;
368 first_free
= (svc_input_id_t
)-1;
377 * Remove all the callback associated with a fd => useful when the fd is
378 * closed for instance
381 __svc_remove_input_of_fd(int fd
)
383 _svc_user_fd_node
**pnode
;
384 _svc_user_fd_node
*tmp
;
386 if ((fd
< 0) || (fd
>= svc_nuserfds
))
389 pnode
= &svc_userfds
[fd
].list
;
390 while ((tmp
= *pnode
) != NULL
) {
391 *pnode
= tmp
->lnk
.next
;
393 _svc_free_id(tmp
->id
);
397 svc_userfds
[fd
].mask
= 0;
401 * Allow user to add an fd in the poll list. If it does not succeed, return
402 * -1. Otherwise, return a svc_id
406 svc_add_input(int user_fd
, unsigned int events
,
407 svc_callback_t user_callback
, void *cookie
)
409 _svc_user_fd_node
*new_node
;
413 return ((svc_input_id_t
)-1);
416 if ((events
== 0x0000) ||
417 (events
& ~(POLLIN
|POLLPRI
|POLLOUT
|POLLRDNORM
|POLLRDBAND
|\
418 POLLWRBAND
|POLLERR
|POLLHUP
|POLLNVAL
))) {
420 return ((svc_input_id_t
)-1);
423 (void) mutex_lock(&svc_userfds_lock
);
425 if ((user_fd
< svc_nuserfds
) &&
426 (svc_userfds
[user_fd
].mask
& events
) != 0) {
427 /* Already registrated call-back */
429 (void) mutex_unlock(&svc_userfds_lock
);
430 return ((svc_input_id_t
)-1);
433 /* Handle memory allocation. */
434 if (user_fd
>= svc_nuserfds
) {
435 int oldSize
= svc_nuserfds
;
437 _svc_user_fd_head
*tmp
;
439 svc_nuserfds
= (user_fd
+ 1) + USER_FD_INCREMENT
;
441 tmp
= reallocarray(svc_userfds
, svc_nuserfds
,
442 sizeof (_svc_user_fd_head
));
445 syslog(LOG_ERR
, "svc_add_input: out of memory");
446 svc_nuserfds
= oldSize
;
448 (void) mutex_unlock(&svc_userfds_lock
);
449 return ((svc_input_id_t
)-1);
454 for (i
= oldSize
; i
< svc_nuserfds
; i
++) {
455 svc_userfds
[i
].list
= NULL
;
456 svc_userfds
[i
].mask
= 0;
460 new_node
= malloc(sizeof (_svc_user_fd_node
));
461 if (new_node
== NULL
) {
462 syslog(LOG_ERR
, "svc_add_input: out of memory");
464 (void) mutex_unlock(&svc_userfds_lock
);
465 return ((svc_input_id_t
)-1);
468 /* create a new node */
469 new_node
->fd
= user_fd
;
470 new_node
->events
= events
;
471 new_node
->callback
= user_callback
;
472 new_node
->cookie
= cookie
;
474 if (_svc_attribute_new_id(new_node
) == -1) {
475 (void) mutex_unlock(&svc_userfds_lock
);
477 return ((svc_input_id_t
)-1);
480 /* Add the new element at the beginning of the list. */
481 if (svc_userfds
[user_fd
].list
!= NULL
)
482 svc_userfds
[user_fd
].list
->lnk
.previous
= new_node
;
483 new_node
->lnk
.next
= svc_userfds
[user_fd
].list
;
484 new_node
->lnk
.previous
= NULL
;
486 svc_userfds
[user_fd
].list
= new_node
;
488 /* refresh global mask for this file desciptor */
489 svc_userfds
[user_fd
].mask
|= events
;
491 /* refresh mask for the poll */
492 add_pollfd(user_fd
, (svc_userfds
[user_fd
].mask
));
494 (void) mutex_unlock(&svc_userfds_lock
);
495 return (new_node
->id
);
499 svc_remove_input(svc_input_id_t id
)
501 _svc_user_fd_node
* node
;
502 _svc_user_fd_node
* next
;
503 _svc_user_fd_node
* previous
;
504 int fd
; /* caching optim */
506 (void) mutex_lock(&svc_userfds_lock
);
508 /* Immediately update data for id management */
509 if (user_fd_mgt_array
== NULL
|| id
>= svc_nmgtuserfds
||
512 (void) mutex_unlock(&svc_userfds_lock
);
516 node
= node_from_id(id
);
517 assert(node
!= NULL
);
520 next
= node
->lnk
.next
;
521 previous
= node
->lnk
.previous
;
522 fd
= node
->fd
; /* caching optim */
524 /* Remove this node from the list. */
525 if (previous
!= NULL
) {
526 previous
->lnk
.next
= next
;
528 assert(svc_userfds
[fd
].list
== node
);
529 svc_userfds
[fd
].list
= next
;
532 next
->lnk
.previous
= previous
;
534 /* Remove the node flags from the global mask */
535 svc_userfds
[fd
].mask
^= node
->events
;
538 if (svc_userfds
[fd
].mask
== 0) {
539 assert(svc_userfds
[fd
].list
== NULL
);
542 assert(svc_userfds
[fd
].list
!= NULL
);
544 /* <=> CLEAN NEEDED TO SHRINK MEMORY USAGE */
546 (void) mutex_unlock(&svc_userfds_lock
);
551 * Provides default service-side functions for authentication flavors
552 * that do not use all the fields in struct svc_auth_ops.
557 authany_wrap(AUTH
*auth
, XDR
*xdrs
, xdrproc_t xfunc
, caddr_t xwhere
)
559 return (*xfunc
)(xdrs
, xwhere
);
562 struct svc_auth_ops svc_auth_any_ops
= {
568 * Return pointer to server authentication structure.
571 __svc_get_svcauth(SVCXPRT
*xprt
)
573 /* LINTED pointer alignment */
574 return (&SVC_XP_AUTH(xprt
));
578 * A callback routine to cleanup after a procedure is executed.
580 void (*__proc_cleanup_cb
)() = NULL
;
583 __svc_set_proc_cleanup_cb(void *cb
)
585 void *tmp
= (void *)__proc_cleanup_cb
;
587 __proc_cleanup_cb
= (void (*)())cb
;
591 /* *************** SVCXPRT related stuff **************** */
594 static int pollfd_shrinking
= 1;
598 * Add fd to svc_pollfd
601 add_pollfd(int fd
, short events
)
603 if (fd
< FD_SETSIZE
) {
604 FD_SET(fd
, &svc_fdset
);
606 FD_SET(fd
, &_new_svc_fdset
);
610 if (fd
>= svc_max_fd
)
613 if (fd
>= svc_max_pollfd
)
614 svc_max_pollfd
= fd
+ 1;
615 if (svc_max_pollfd
> svc_pollfd_allocd
) {
616 int i
= svc_pollfd_allocd
;
619 svc_pollfd_allocd
+= POLLFD_EXTEND
;
620 } while (svc_max_pollfd
> svc_pollfd_allocd
);
621 tmp
= reallocarray(svc_pollfd
, svc_pollfd_allocd
,
625 for (; i
< svc_pollfd_allocd
; i
++)
629 * give an error message; undo fdset setting
630 * above; reset the pollfd_shrinking flag.
631 * because of this poll will not be done
634 if (fd
< FD_SETSIZE
) {
635 FD_CLR(fd
, &svc_fdset
);
637 FD_CLR(fd
, &_new_svc_fdset
);
641 if (fd
== (svc_max_fd
- 1))
644 if (fd
== (svc_max_pollfd
- 1))
646 pollfd_shrinking
= 0;
647 syslog(LOG_ERR
, "add_pollfd: out of memory");
651 svc_pollfd
[fd
].fd
= fd
;
652 svc_pollfd
[fd
].events
= events
;
658 * the fd is still active but only the bit in fdset is cleared.
659 * do not subtract svc_nfds or svc_npollfds
664 if (fd
< FD_SETSIZE
&& FD_ISSET(fd
, &svc_fdset
)) {
665 FD_CLR(fd
, &svc_fdset
);
667 FD_CLR(fd
, &_new_svc_fdset
);
671 if (fd
< svc_pollfd_allocd
&& POLLFD_ISSET(fd
, svc_pollfd
)) {
672 POLLFD_CLR(fd
, svc_pollfd
);
678 * sets the bit in fdset for an active fd so that poll() is done for that
681 set_pollfd(int fd
, short events
)
683 if (fd
< FD_SETSIZE
) {
684 FD_SET(fd
, &svc_fdset
);
686 FD_SET(fd
, &_new_svc_fdset
);
690 if (fd
< svc_pollfd_allocd
) {
691 svc_pollfd
[fd
].fd
= fd
;
692 svc_pollfd
[fd
].events
= events
;
698 * remove a svc_pollfd entry; it does not shrink the memory
701 remove_pollfd(int fd
)
704 if (fd
== (svc_max_fd
- 1))
707 if (fd
== (svc_max_pollfd
- 1))
713 * delete a svc_pollfd entry; it shrinks the memory
714 * use remove_pollfd if you do not want to shrink
717 delete_pollfd(int fd
)
720 if (pollfd_shrinking
&& svc_max_pollfd
<
721 (svc_pollfd_allocd
- POLLFD_SHRINK
)) {
723 svc_pollfd_allocd
-= POLLFD_SHRINK
;
724 } while (svc_max_pollfd
< (svc_pollfd_allocd
- POLLFD_SHRINK
));
725 svc_pollfd
= reallocarray(svc_pollfd
, svc_pollfd_allocd
,
727 if (svc_pollfd
== NULL
) {
728 syslog(LOG_ERR
, "delete_pollfd: out of memory");
736 * Activate a transport handle.
739 xprt_register(const SVCXPRT
*xprt
)
741 int fd
= xprt
->xp_fd
;
743 extern void (*_svc_getreqset_proc
)();
745 /* VARIABLES PROTECTED BY svc_fd_lock: svc_xports, svc_fdset */
747 (void) rw_wrlock(&svc_fd_lock
);
748 if (svc_xports
== NULL
) {
749 /* allocate some small amount first */
750 svc_xports
= calloc(FD_INCREMENT
, sizeof (SVCXPRT
*));
751 if (svc_xports
== NULL
) {
752 syslog(LOG_ERR
, "xprt_register: out of memory");
755 nsvc_xports
= FD_INCREMENT
;
759 * XXX: This code does not keep track of the server state.
761 * This provides for callback support. When a client
762 * recv's a call from another client on the server fd's,
763 * it calls _svc_getreqset_proc() which would return
764 * after serving all the server requests. Also look under
765 * clnt_dg.c and clnt_vc.c (clnt_call part of it)
767 _svc_getreqset_proc
= svc_getreq_poll
;
771 while (fd
>= nsvc_xports
) {
772 SVCXPRT
**tmp_xprts
= svc_xports
;
774 /* time to expand svc_xprts */
775 tmp_xprts
= reallocarray(svc_xports
,
776 nsvc_xports
+ FD_INCREMENT
, sizeof (SVCXPRT
*));
777 if (tmp_xprts
== NULL
) {
778 syslog(LOG_ERR
, "xprt_register : out of memory.");
782 svc_xports
= tmp_xprts
;
783 (void) memset(&svc_xports
[nsvc_xports
], 0,
784 sizeof (SVCXPRT
*) * FD_INCREMENT
);
785 nsvc_xports
+= FD_INCREMENT
;
788 svc_xports
[fd
] = (SVCXPRT
*)xprt
;
790 add_pollfd(fd
, MASKVAL
);
796 * This happens only in one of the MT modes.
799 (void) write(svc_pipe
[1], &dummy
, sizeof (dummy
));
802 * If already dispatching door based services, start
803 * dispatching TLI based services now.
805 (void) mutex_lock(&svc_door_mutex
);
806 if (svc_ndoorfds
> 0)
807 (void) cond_signal(&svc_door_waitcv
);
808 (void) mutex_unlock(&svc_door_mutex
);
810 if (svc_xdrs
== NULL
) {
811 /* allocate initial chunk */
812 svc_xdrs
= calloc(FD_INCREMENT
, sizeof (XDR
*));
813 if (svc_xdrs
!= NULL
)
814 nsvc_xdrs
= FD_INCREMENT
;
816 syslog(LOG_ERR
, "xprt_register : out of memory.");
820 (void) rw_unlock(&svc_fd_lock
);
824 * De-activate a transport handle.
827 __xprt_unregister_private(const SVCXPRT
*xprt
, bool_t lock_not_held
)
829 int fd
= xprt
->xp_fd
;
832 (void) rw_wrlock(&svc_fd_lock
);
833 if ((fd
< nsvc_xports
) && (svc_xports
[fd
] == xprt
)) {
834 svc_xports
[fd
] = NULL
;
838 (void) rw_unlock(&svc_fd_lock
);
839 __svc_rm_from_xlist(&_svc_xprtlist
, xprt
, &xprtlist_lock
);
843 xprt_unregister(const SVCXPRT
*xprt
)
845 __xprt_unregister_private(xprt
, TRUE
);
848 /* ********************** CALLOUT list related stuff ************* */
851 * Add a service program to the callout list.
852 * The dispatch routine will be called when a rpc request for this
853 * program number comes in.
856 svc_reg(const SVCXPRT
*xprt
, const rpcprog_t prog
, const rpcvers_t vers
,
857 void (*dispatch
)(), const struct netconfig
*nconf
)
859 struct svc_callout
*prev
;
860 struct svc_callout
*s
, **s2
;
861 struct netconfig
*tnconf
;
865 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
867 if (xprt
->xp_netid
) {
868 netid
= strdup(xprt
->xp_netid
);
870 } else if (nconf
&& nconf
->nc_netid
) {
871 netid
= strdup(nconf
->nc_netid
);
873 } else if ((tnconf
= __rpcfd_to_nconf(xprt
->xp_fd
, xprt
->xp_type
))
875 netid
= strdup(tnconf
->nc_netid
);
877 freenetconfigent(tnconf
);
878 } /* must have been created with svc_raw_create */
879 if ((netid
== NULL
) && (flag
== 1))
882 (void) rw_wrlock(&svc_lock
);
883 if ((s
= svc_find(prog
, vers
, &prev
, netid
)) != NULL
) {
885 if (s
->sc_dispatch
== dispatch
)
886 goto rpcb_it
; /* it is registering another xptr */
887 (void) rw_unlock(&svc_lock
);
890 s
= malloc(sizeof (struct svc_callout
));
893 (void) rw_unlock(&svc_lock
);
899 s
->sc_dispatch
= dispatch
;
904 * The ordering of transports is such that the most frequently used
905 * one appears first. So add the new entry to the end of the list.
907 for (s2
= &svc_head
; *s2
!= NULL
; s2
= &(*s2
)->sc_next
)
911 if ((xprt
->xp_netid
== NULL
) && (flag
== 1) && netid
)
912 if ((((SVCXPRT
*)xprt
)->xp_netid
= strdup(netid
)) == NULL
) {
913 syslog(LOG_ERR
, "svc_reg : strdup failed.");
917 (void) rw_unlock(&svc_lock
);
922 (void) rw_unlock(&svc_lock
);
924 /* now register the information with the local binder service */
926 return (rpcb_set(prog
, vers
, nconf
, &xprt
->xp_ltaddr
));
932 * Remove a service program from the callout list.
935 svc_unreg(const rpcprog_t prog
, const rpcvers_t vers
)
937 struct svc_callout
*prev
;
938 struct svc_callout
*s
;
940 /* unregister the information anyway */
941 (void) rpcb_unset(prog
, vers
, NULL
);
943 (void) rw_wrlock(&svc_lock
);
944 while ((s
= svc_find(prog
, vers
, &prev
, NULL
)) != NULL
) {
946 svc_head
= s
->sc_next
;
948 prev
->sc_next
= s
->sc_next
;
954 (void) rw_unlock(&svc_lock
);
958 * Add a service program to the callout list.
959 * The dispatch routine will be called when a rpc request for this
960 * program number comes in.
961 * For version 2 portmappers.
964 svc_register(SVCXPRT
*xprt
, rpcprog_t prog
, rpcvers_t vers
,
965 void (*dispatch
)(), int protocol
)
967 struct svc_callout
*prev
;
968 struct svc_callout
*s
;
969 struct netconfig
*nconf
;
973 if (xprt
->xp_netid
) {
974 netid
= strdup(xprt
->xp_netid
);
976 } else if ((ioctl(xprt
->xp_fd
, I_FIND
, "timod") > 0) && ((nconf
=
977 __rpcfd_to_nconf(xprt
->xp_fd
, xprt
->xp_type
)) != NULL
)) {
978 /* fill in missing netid field in SVCXPRT */
979 netid
= strdup(nconf
->nc_netid
);
981 freenetconfigent(nconf
);
982 } /* must be svc_raw_create */
984 if ((netid
== NULL
) && (flag
== 1))
987 (void) rw_wrlock(&svc_lock
);
988 if ((s
= svc_find(prog
, vers
, &prev
, netid
)) != NULL
) {
990 if (s
->sc_dispatch
== dispatch
)
991 goto pmap_it
; /* it is registering another xptr */
992 (void) rw_unlock(&svc_lock
);
995 s
= malloc(sizeof (struct svc_callout
));
998 (void) rw_unlock(&svc_lock
);
1003 s
->sc_dispatch
= dispatch
;
1004 s
->sc_netid
= netid
;
1005 s
->sc_next
= svc_head
;
1008 if ((xprt
->xp_netid
== NULL
) && (flag
== 1) && netid
)
1009 if ((xprt
->xp_netid
= strdup(netid
)) == NULL
) {
1010 syslog(LOG_ERR
, "svc_register : strdup failed.");
1012 svc_head
= s
->sc_next
;
1014 (void) rw_unlock(&svc_lock
);
1019 (void) rw_unlock(&svc_lock
);
1020 /* now register the information with the local binder service */
1022 return (pmap_set(prog
, vers
, protocol
, xprt
->xp_port
));
1027 * Remove a service program from the callout list.
1028 * For version 2 portmappers.
1031 svc_unregister(rpcprog_t prog
, rpcvers_t vers
)
1033 struct svc_callout
*prev
;
1034 struct svc_callout
*s
;
1036 (void) rw_wrlock(&svc_lock
);
1037 while ((s
= svc_find(prog
, vers
, &prev
, NULL
)) != NULL
) {
1039 svc_head
= s
->sc_next
;
1041 prev
->sc_next
= s
->sc_next
;
1046 /* unregister the information with the local binder service */
1047 (void) pmap_unset(prog
, vers
);
1049 (void) rw_unlock(&svc_lock
);
1053 * Search the callout list for a program number, return the callout
1055 * Also check for transport as well. Many routines such as svc_unreg
1056 * dont give any corresponding transport, so dont check for transport if
1059 static struct svc_callout
*
1060 svc_find(rpcprog_t prog
, rpcvers_t vers
, struct svc_callout
**prev
, char *netid
)
1062 struct svc_callout
*s
, *p
;
1064 /* WRITE LOCK HELD ON ENTRY: svc_lock */
1066 /* assert(RW_WRITE_HELD(&svc_lock)); */
1068 for (s
= svc_head
; s
!= NULL
; s
= s
->sc_next
) {
1069 if (((s
->sc_prog
== prog
) && (s
->sc_vers
== vers
)) &&
1070 ((netid
== NULL
) || (s
->sc_netid
== NULL
) ||
1071 (strcmp(netid
, s
->sc_netid
) == 0)))
1080 /* ******************* REPLY GENERATION ROUTINES ************ */
1083 * Send a reply to an rpc request
1086 svc_sendreply(const SVCXPRT
*xprt
, const xdrproc_t xdr_results
,
1087 const caddr_t xdr_location
)
1089 struct rpc_msg rply
;
1091 rply
.rm_direction
= REPLY
;
1092 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
1093 rply
.acpted_rply
.ar_verf
= xprt
->xp_verf
;
1094 rply
.acpted_rply
.ar_stat
= SUCCESS
;
1095 rply
.acpted_rply
.ar_results
.where
= xdr_location
;
1096 rply
.acpted_rply
.ar_results
.proc
= xdr_results
;
1097 return (SVC_REPLY((SVCXPRT
*)xprt
, &rply
));
1101 * No procedure error reply
1104 svcerr_noproc(const SVCXPRT
*xprt
)
1106 struct rpc_msg rply
;
1108 rply
.rm_direction
= REPLY
;
1109 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
1110 rply
.acpted_rply
.ar_verf
= xprt
->xp_verf
;
1111 rply
.acpted_rply
.ar_stat
= PROC_UNAVAIL
;
1112 SVC_REPLY((SVCXPRT
*)xprt
, &rply
);
1116 * Can't decode args error reply
1119 svcerr_decode(const SVCXPRT
*xprt
)
1121 struct rpc_msg rply
;
1123 rply
.rm_direction
= REPLY
;
1124 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
1125 rply
.acpted_rply
.ar_verf
= xprt
->xp_verf
;
1126 rply
.acpted_rply
.ar_stat
= GARBAGE_ARGS
;
1127 SVC_REPLY((SVCXPRT
*)xprt
, &rply
);
1134 svcerr_systemerr(const SVCXPRT
*xprt
)
1136 struct rpc_msg rply
;
1138 rply
.rm_direction
= REPLY
;
1139 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
1140 rply
.acpted_rply
.ar_verf
= xprt
->xp_verf
;
1141 rply
.acpted_rply
.ar_stat
= SYSTEM_ERR
;
1142 SVC_REPLY((SVCXPRT
*)xprt
, &rply
);
1146 * Tell RPC package to not complain about version errors to the client. This
1147 * is useful when revving broadcast protocols that sit on a fixed address.
1148 * There is really one (or should be only one) example of this kind of
1149 * protocol: the portmapper (or rpc binder).
1152 __svc_versquiet_on(const SVCXPRT
*xprt
)
1154 /* LINTED pointer alignment */
1155 svc_flags(xprt
) |= SVC_VERSQUIET
;
1159 __svc_versquiet_off(const SVCXPRT
*xprt
)
1161 /* LINTED pointer alignment */
1162 svc_flags(xprt
) &= ~SVC_VERSQUIET
;
1166 svc_versquiet(const SVCXPRT
*xprt
)
1168 __svc_versquiet_on(xprt
);
1172 __svc_versquiet_get(const SVCXPRT
*xprt
)
1174 /* LINTED pointer alignment */
1175 return (svc_flags(xprt
) & SVC_VERSQUIET
);
1179 * Authentication error reply
1182 svcerr_auth(const SVCXPRT
*xprt
, const enum auth_stat why
)
1184 struct rpc_msg rply
;
1186 rply
.rm_direction
= REPLY
;
1187 rply
.rm_reply
.rp_stat
= MSG_DENIED
;
1188 rply
.rjcted_rply
.rj_stat
= AUTH_ERROR
;
1189 rply
.rjcted_rply
.rj_why
= why
;
1190 SVC_REPLY((SVCXPRT
*)xprt
, &rply
);
1194 * Auth too weak error reply
1197 svcerr_weakauth(const SVCXPRT
*xprt
)
1199 svcerr_auth(xprt
, AUTH_TOOWEAK
);
1203 * Program unavailable error reply
1206 svcerr_noprog(const SVCXPRT
*xprt
)
1208 struct rpc_msg rply
;
1210 rply
.rm_direction
= REPLY
;
1211 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
1212 rply
.acpted_rply
.ar_verf
= xprt
->xp_verf
;
1213 rply
.acpted_rply
.ar_stat
= PROG_UNAVAIL
;
1214 SVC_REPLY((SVCXPRT
*)xprt
, &rply
);
1218 * Program version mismatch error reply
1221 svcerr_progvers(const SVCXPRT
*xprt
, const rpcvers_t low_vers
,
1222 const rpcvers_t high_vers
)
1224 struct rpc_msg rply
;
1226 rply
.rm_direction
= REPLY
;
1227 rply
.rm_reply
.rp_stat
= MSG_ACCEPTED
;
1228 rply
.acpted_rply
.ar_verf
= xprt
->xp_verf
;
1229 rply
.acpted_rply
.ar_stat
= PROG_MISMATCH
;
1230 rply
.acpted_rply
.ar_vers
.low
= low_vers
;
1231 rply
.acpted_rply
.ar_vers
.high
= high_vers
;
1232 SVC_REPLY((SVCXPRT
*)xprt
, &rply
);
1235 /* ******************* SERVER INPUT STUFF ******************* */
1238 * Get server side input from some transport.
1240 * Statement of authentication parameters management:
1241 * This function owns and manages all authentication parameters, specifically
1242 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
1243 * the "cooked" credentials (rqst->rq_clntcred).
1244 * However, this function does not know the structure of the cooked
1245 * credentials, so it make the following assumptions:
1246 * a) the structure is contiguous (no pointers), and
1247 * b) the cred structure size does not exceed RQCRED_SIZE bytes.
1248 * In all events, all three parameters are freed upon exit from this routine.
1249 * The storage is trivially management on the call stack in user land, but
1250 * is mallocated in kernel land.
1254 svc_getreq(int rdfds
)
1259 readfds
.fds_bits
[0] = rdfds
;
1260 svc_getreqset(&readfds
);
1264 svc_getreqset(fd_set
*readfds
)
1268 for (i
= 0; i
< svc_max_fd
; i
++) {
1269 /* fd has input waiting */
1270 if (FD_ISSET(i
, readfds
))
1271 svc_getreq_common(i
);
1276 svc_getreq_poll(struct pollfd
*pfdp
, const int pollretval
)
1281 for (i
= fds_found
= 0; fds_found
< pollretval
; i
++) {
1282 struct pollfd
*p
= &pfdp
[i
];
1285 /* fd has input waiting */
1288 * We assume that this function is only called
1289 * via someone select()ing from svc_fdset or
1290 * poll()ing from svc_pollset[]. Thus it's safe
1291 * to handle the POLLNVAL event by simply turning
1292 * the corresponding bit off in svc_fdset. The
1293 * svc_pollset[] array is derived from svc_fdset
1294 * and so will also be updated eventually.
1296 * XXX Should we do an xprt_unregister() instead?
1298 /* Handle user callback */
1299 if (__is_a_userfd(p
->fd
) == TRUE
) {
1300 (void) rw_rdlock(&svc_fd_lock
);
1301 __svc_getreq_user(p
);
1302 (void) rw_unlock(&svc_fd_lock
);
1304 if (p
->revents
& POLLNVAL
) {
1305 (void) rw_wrlock(&svc_fd_lock
);
1306 remove_pollfd(p
->fd
); /* XXX */
1307 (void) rw_unlock(&svc_fd_lock
);
1309 svc_getreq_common(p
->fd
);
1317 svc_getreq_common(const int fd
)
1320 enum xprt_stat stat
;
1321 struct rpc_msg
*msg
;
1325 (void) rw_rdlock(&svc_fd_lock
);
1327 /* HANDLE USER CALLBACK */
1328 if (__is_a_userfd(fd
) == TRUE
) {
1329 struct pollfd virtual_fd
;
1331 virtual_fd
.events
= virtual_fd
.revents
= (short)0xFFFF;
1333 __svc_getreq_user(&virtual_fd
);
1334 (void) rw_unlock(&svc_fd_lock
);
1339 * The transport associated with this fd could have been
1340 * removed from svc_timeout_nonblock_xprt_and_LRU, for instance.
1341 * This can happen if two or more fds get read events and are
1342 * passed to svc_getreq_poll/set, the first fd is seviced by
1343 * the dispatch routine and cleans up any dead transports. If
1344 * one of the dead transports removed is the other fd that
1345 * had a read event then svc_getreq_common() will be called with no
1346 * xprt associated with the fd that had the original read event.
1348 if ((fd
>= nsvc_xports
) || (xprt
= svc_xports
[fd
]) == NULL
) {
1349 (void) rw_unlock(&svc_fd_lock
);
1352 (void) rw_unlock(&svc_fd_lock
);
1353 /* LINTED pointer alignment */
1354 msg
= SVCEXT(xprt
)->msg
;
1355 /* LINTED pointer alignment */
1356 r
= SVCEXT(xprt
)->req
;
1357 /* LINTED pointer alignment */
1358 cred_area
= SVCEXT(xprt
)->cred_area
;
1359 msg
->rm_call
.cb_cred
.oa_base
= cred_area
;
1360 msg
->rm_call
.cb_verf
.oa_base
= &(cred_area
[MAX_AUTH_BYTES
]);
1361 r
->rq_clntcred
= &(cred_area
[2 * MAX_AUTH_BYTES
]);
1363 /* receive msgs from xprtprt (support batch calls) */
1367 if (dispatch
= SVC_RECV(xprt
, msg
))
1368 (void) _svc_prog_dispatch(xprt
, msg
, r
);
1370 * Check if the xprt has been disconnected in a recursive call
1371 * in the service dispatch routine. If so, then break
1373 (void) rw_rdlock(&svc_fd_lock
);
1374 if (xprt
!= svc_xports
[fd
]) {
1375 (void) rw_unlock(&svc_fd_lock
);
1378 (void) rw_unlock(&svc_fd_lock
);
1381 * Call cleanup procedure if set.
1383 if (__proc_cleanup_cb
!= NULL
&& dispatch
)
1384 (*__proc_cleanup_cb
)(xprt
);
1386 if ((stat
= SVC_STAT(xprt
)) == XPRT_DIED
) {
1390 } while (stat
== XPRT_MOREREQS
);
1394 _svc_prog_dispatch(SVCXPRT
*xprt
, struct rpc_msg
*msg
, struct svc_req
*r
)
1396 struct svc_callout
*s
;
1400 rpcvers_t high_vers
;
1404 r
->rq_prog
= msg
->rm_call
.cb_prog
;
1405 r
->rq_vers
= msg
->rm_call
.cb_vers
;
1406 r
->rq_proc
= msg
->rm_call
.cb_proc
;
1407 r
->rq_cred
= msg
->rm_call
.cb_cred
;
1408 /* LINTED pointer alignment */
1409 SVC_XP_AUTH(r
->rq_xprt
).svc_ah_ops
= svc_auth_any_ops
;
1410 /* LINTED pointer alignment */
1411 SVC_XP_AUTH(r
->rq_xprt
).svc_ah_private
= NULL
;
1413 /* first authenticate the message */
1414 /* Check for null flavor and bypass these calls if possible */
1416 if (msg
->rm_call
.cb_cred
.oa_flavor
== AUTH_NULL
) {
1417 r
->rq_xprt
->xp_verf
.oa_flavor
= _null_auth
.oa_flavor
;
1418 r
->rq_xprt
->xp_verf
.oa_length
= 0;
1422 if ((why
= __gss_authenticate(r
, msg
,
1423 &no_dispatch
)) != AUTH_OK
) {
1424 svcerr_auth(xprt
, why
);
1430 /* match message with a registered service */
1432 low_vers
= (rpcvers_t
)(0 - 1);
1434 (void) rw_rdlock(&svc_lock
);
1435 for (s
= svc_head
; s
!= NULL
; s
= s
->sc_next
) {
1436 if (s
->sc_prog
== r
->rq_prog
) {
1438 if (s
->sc_vers
== r
->rq_vers
) {
1439 if ((xprt
->xp_netid
== NULL
) ||
1440 (s
->sc_netid
== NULL
) ||
1441 (strcmp(xprt
->xp_netid
,
1442 s
->sc_netid
) == 0)) {
1443 disp_fn
= (*s
->sc_dispatch
);
1444 (void) rw_unlock(&svc_lock
);
1450 if (s
->sc_vers
< low_vers
)
1451 low_vers
= s
->sc_vers
;
1452 if (s
->sc_vers
> high_vers
)
1453 high_vers
= s
->sc_vers
;
1454 } /* found correct program */
1456 (void) rw_unlock(&svc_lock
);
1459 * if we got here, the program or version
1463 /* LINTED pointer alignment */
1464 if (!version_keepquiet(xprt
))
1465 svcerr_progvers(xprt
, low_vers
, high_vers
);
1467 svcerr_noprog(xprt
);
1472 /* ******************* SVCXPRT allocation and deallocation ***************** */
1475 * svc_xprt_alloc() - allocate a service transport handle
1478 svc_xprt_alloc(void)
1480 SVCXPRT
*xprt
= NULL
;
1481 SVCXPRT_EXT
*xt
= NULL
;
1482 SVCXPRT_LIST
*xlist
= NULL
;
1483 struct rpc_msg
*msg
= NULL
;
1484 struct svc_req
*req
= NULL
;
1485 char *cred_area
= NULL
;
1487 if ((xprt
= calloc(1, sizeof (SVCXPRT
))) == NULL
)
1490 if ((xt
= calloc(1, sizeof (SVCXPRT_EXT
))) == NULL
)
1492 xprt
->xp_p3
= (caddr_t
)xt
; /* SVCEXT(xprt) = xt */
1494 if ((xlist
= calloc(1, sizeof (SVCXPRT_LIST
))) == NULL
)
1496 xt
->my_xlist
= xlist
;
1499 if ((msg
= malloc(sizeof (struct rpc_msg
))) == NULL
)
1503 if ((req
= malloc(sizeof (struct svc_req
))) == NULL
)
1507 if ((cred_area
= malloc(2*MAX_AUTH_BYTES
+ RQCRED_SIZE
)) == NULL
)
1509 xt
->cred_area
= cred_area
;
1511 /* LINTED pointer alignment */
1512 (void) mutex_init(&svc_send_mutex(xprt
), USYNC_THREAD
, NULL
);
1516 svc_xprt_free(xprt
);
1522 * svc_xprt_free() - free a service handle
1525 svc_xprt_free(SVCXPRT
*xprt
)
1527 /* LINTED pointer alignment */
1528 SVCXPRT_EXT
*xt
= xprt
? SVCEXT(xprt
) : NULL
;
1529 SVCXPRT_LIST
*my_xlist
= xt
? xt
->my_xlist
: NULL
;
1530 struct rpc_msg
*msg
= xt
? xt
->msg
: NULL
;
1531 struct svc_req
*req
= xt
? xt
->req
: NULL
;
1532 char *cred_area
= xt
? xt
->cred_area
: NULL
;
1544 * svc_xprt_destroy() - free parent and child xprt list
1547 svc_xprt_destroy(SVCXPRT
*xprt
)
1549 SVCXPRT_LIST
*xlist
, *xnext
= NULL
;
1552 /* LINTED pointer alignment */
1553 if (SVCEXT(xprt
)->parent
)
1554 /* LINTED pointer alignment */
1555 xprt
= SVCEXT(xprt
)->parent
;
1556 /* LINTED pointer alignment */
1557 type
= svc_type(xprt
);
1558 /* LINTED pointer alignment */
1559 for (xlist
= SVCEXT(xprt
)->my_xlist
; xlist
!= NULL
; xlist
= xnext
) {
1560 xnext
= xlist
->next
;
1564 svc_dg_xprtfree(xprt
);
1566 case SVC_RENDEZVOUS
:
1567 svc_vc_xprtfree(xprt
);
1569 case SVC_CONNECTION
:
1570 svc_fd_xprtfree(xprt
);
1573 svc_door_xprtfree(xprt
);
1581 * svc_copy() - make a copy of parent
1584 svc_copy(SVCXPRT
*xprt
)
1586 /* LINTED pointer alignment */
1587 switch (svc_type(xprt
)) {
1589 return (svc_dg_xprtcopy(xprt
));
1590 case SVC_RENDEZVOUS
:
1591 return (svc_vc_xprtcopy(xprt
));
1592 case SVC_CONNECTION
:
1593 return (svc_fd_xprtcopy(xprt
));
1600 * _svc_destroy_private() - private SVC_DESTROY interface
1603 _svc_destroy_private(SVCXPRT
*xprt
)
1605 /* LINTED pointer alignment */
1606 switch (svc_type(xprt
)) {
1608 _svc_dg_destroy_private(xprt
);
1610 case SVC_RENDEZVOUS
:
1611 case SVC_CONNECTION
:
1612 _svc_vc_destroy_private(xprt
, TRUE
);
1618 * svc_get_local_cred() - fetch local user credentials. This always
1619 * works over doors based transports. For local transports, this
1620 * does not yield correct results unless the __rpc_negotiate_uid()
1621 * call has been invoked to enable this feature.
1624 svc_get_local_cred(SVCXPRT
*xprt
, svc_local_cred_t
*lcred
)
1626 /* LINTED pointer alignment */
1627 if (svc_type(xprt
) == SVC_DOOR
)
1628 return (__svc_get_door_cred(xprt
, lcred
));
1629 return (__rpc_get_local_cred(xprt
, lcred
));
1633 /* ******************* DUPLICATE ENTRY HANDLING ROUTINES ************** */
1636 * the dup cacheing routines below provide a cache of received
1637 * transactions. rpc service routines can use this to detect
1638 * retransmissions and re-send a non-failure response. Uses a
1639 * lru scheme to find entries to get rid of entries in the cache,
1640 * though only DUP_DONE entries are placed on the lru list.
1641 * the routines were written towards development of a generic
1642 * SVC_DUP() interface, which can be expanded to encompass the
1643 * svc_dg_enablecache() routines as well. the cache is currently
1644 * private to the automounter.
1648 /* dupcache header contains xprt specific information */
1655 struct dupreq
*dc_mru
;
1656 struct dupreq
**dc_hashtbl
;
1660 * private duplicate cache request routines
1662 static int __svc_dupcache_check(struct svc_req
*, caddr_t
*, uint_t
*,
1663 struct dupcache
*, uint32_t, uint32_t);
1664 static struct dupreq
*__svc_dupcache_victim(struct dupcache
*, time_t);
1665 static int __svc_dupcache_enter(struct svc_req
*, struct dupreq
*,
1666 struct dupcache
*, uint32_t, uint32_t, time_t);
1667 static int __svc_dupcache_update(struct svc_req
*, caddr_t
, uint_t
, int,
1668 struct dupcache
*, uint32_t, uint32_t);
1670 static void __svc_dupcache_debug(struct dupcache
*);
1671 #endif /* DUP_DEBUG */
1673 /* default parameters for the dupcache */
1674 #define DUPCACHE_BUCKETS 257
1675 #define DUPCACHE_TIME 900
1676 #define DUPCACHE_MAXSZ INT_MAX
1679 * __svc_dupcache_init(void *condition, int basis, char *xprt_cache)
1680 * initialize the duprequest cache and assign it to the xprt_cache
1681 * Use default values depending on the cache condition and basis.
1682 * return TRUE on success and FALSE on failure
1685 __svc_dupcache_init(void *condition
, int basis
, char **xprt_cache
)
1687 static mutex_t initdc_lock
= DEFAULTMUTEX
;
1689 struct dupcache
*dc
;
1691 (void) mutex_lock(&initdc_lock
);
1692 if (*xprt_cache
!= NULL
) { /* do only once per xprt */
1693 (void) mutex_unlock(&initdc_lock
);
1695 "__svc_dupcache_init: multiply defined dup cache");
1700 case DUPCACHE_FIXEDTIME
:
1701 dc
= malloc(sizeof (struct dupcache
));
1703 (void) mutex_unlock(&initdc_lock
);
1705 "__svc_dupcache_init: memory alloc failed");
1708 (void) rwlock_init(&(dc
->dc_lock
), USYNC_THREAD
, NULL
);
1709 if (condition
!= NULL
)
1710 dc
->dc_time
= *((time_t *)condition
);
1712 dc
->dc_time
= DUPCACHE_TIME
;
1713 dc
->dc_buckets
= DUPCACHE_BUCKETS
;
1714 dc
->dc_maxsz
= DUPCACHE_MAXSZ
;
1715 dc
->dc_basis
= basis
;
1717 dc
->dc_hashtbl
= malloc(dc
->dc_buckets
*
1718 sizeof (struct dupreq
*));
1719 if (dc
->dc_hashtbl
== NULL
) {
1721 (void) mutex_unlock(&initdc_lock
);
1723 "__svc_dupcache_init: memory alloc failed");
1726 for (i
= 0; i
< DUPCACHE_BUCKETS
; i
++)
1727 dc
->dc_hashtbl
[i
] = NULL
;
1728 *xprt_cache
= (char *)dc
;
1731 (void) mutex_unlock(&initdc_lock
);
1733 "__svc_dupcache_init: undefined dup cache basis");
1737 (void) mutex_unlock(&initdc_lock
);
1743 * __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1745 * searches the request cache. Creates an entry and returns DUP_NEW if
1746 * the request is not found in the cache. If it is found, then it
1747 * returns the state of the request (in progress, drop, or done) and
1748 * also allocates, and passes back results to the user (if any) in
1749 * resp_buf, and its length in resp_bufsz. DUP_ERROR is returned on error.
1752 __svc_dup(struct svc_req
*req
, caddr_t
*resp_buf
, uint_t
*resp_bufsz
,
1755 uint32_t drxid
, drhash
;
1757 struct dupreq
*dr
= NULL
;
1758 time_t timenow
= time(NULL
);
1760 /* LINTED pointer alignment */
1761 struct dupcache
*dc
= (struct dupcache
*)xprt_cache
;
1764 syslog(LOG_ERR
, "__svc_dup: undefined cache");
1768 /* get the xid of the request */
1769 if (SVC_CONTROL(req
->rq_xprt
, SVCGET_XID
, (void*)&drxid
) == FALSE
) {
1770 syslog(LOG_ERR
, "__svc_dup: xid error");
1773 drhash
= drxid
% dc
->dc_buckets
;
1775 if ((rc
= __svc_dupcache_check(req
, resp_buf
, resp_bufsz
, dc
, drxid
,
1776 drhash
)) != DUP_NEW
)
1779 if ((dr
= __svc_dupcache_victim(dc
, timenow
)) == NULL
)
1782 if ((rc
= __svc_dupcache_enter(req
, dr
, dc
, drxid
, drhash
, timenow
))
1792 * __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf,
1793 * uint_t *resp_bufsz,truct dupcache *dc, uint32_t drxid,
1795 * Checks to see whether an entry already exists in the cache. If it does
1796 * copy back into the resp_buf, if appropriate. Return the status of
1797 * the request, or DUP_NEW if the entry is not in the cache
1800 __svc_dupcache_check(struct svc_req
*req
, caddr_t
*resp_buf
, uint_t
*resp_bufsz
,
1801 struct dupcache
*dc
, uint32_t drxid
, uint32_t drhash
)
1803 struct dupreq
*dr
= NULL
;
1805 (void) rw_rdlock(&(dc
->dc_lock
));
1806 dr
= dc
->dc_hashtbl
[drhash
];
1807 while (dr
!= NULL
) {
1808 if (dr
->dr_xid
== drxid
&&
1809 dr
->dr_proc
== req
->rq_proc
&&
1810 dr
->dr_prog
== req
->rq_prog
&&
1811 dr
->dr_vers
== req
->rq_vers
&&
1812 dr
->dr_addr
.len
== req
->rq_xprt
->xp_rtaddr
.len
&&
1813 memcmp(dr
->dr_addr
.buf
, req
->rq_xprt
->xp_rtaddr
.buf
,
1814 dr
->dr_addr
.len
) == 0) { /* entry found */
1815 if (dr
->dr_hash
!= drhash
) {
1817 (void) rw_unlock((&dc
->dc_lock
));
1819 "\n__svc_dupdone: hashing error");
1824 * return results for requests on lru list, if
1825 * appropriate requests must be DUP_DROP or DUP_DONE
1826 * to have a result. A NULL buffer in the cache
1827 * implies no results were sent during dupdone.
1828 * A NULL buffer in the call implies not interested
1831 if (((dr
->dr_status
== DUP_DONE
) ||
1832 (dr
->dr_status
== DUP_DROP
)) &&
1834 dr
->dr_resp
.buf
!= NULL
) {
1835 *resp_buf
= malloc(dr
->dr_resp
.len
);
1836 if (*resp_buf
== NULL
) {
1838 "__svc_dupcache_check: malloc failed");
1839 (void) rw_unlock(&(dc
->dc_lock
));
1842 (void) memset(*resp_buf
, 0, dr
->dr_resp
.len
);
1843 (void) memcpy(*resp_buf
, dr
->dr_resp
.buf
,
1845 *resp_bufsz
= dr
->dr_resp
.len
;
1853 (void) rw_unlock(&(dc
->dc_lock
));
1854 return (dr
->dr_status
);
1858 (void) rw_unlock(&(dc
->dc_lock
));
1863 * __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1864 * Return a victim dupreq entry to the caller, depending on cache policy.
1866 static struct dupreq
*
1867 __svc_dupcache_victim(struct dupcache
*dc
, time_t timenow
)
1869 struct dupreq
*dr
= NULL
;
1871 switch (dc
->dc_basis
) {
1872 case DUPCACHE_FIXEDTIME
:
1874 * The hash policy is to free up a bit of the hash
1875 * table before allocating a new entry as the victim.
1876 * Freeing up the hash table each time should split
1877 * the cost of keeping the hash table clean among threads.
1878 * Note that only DONE or DROPPED entries are on the lru
1879 * list but we do a sanity check anyway.
1881 (void) rw_wrlock(&(dc
->dc_lock
));
1882 while ((dc
->dc_mru
) && (dr
= dc
->dc_mru
->dr_next
) &&
1883 ((timenow
- dr
->dr_time
) > dc
->dc_time
)) {
1884 /* clean and then free the entry */
1885 if (dr
->dr_status
!= DUP_DONE
&&
1886 dr
->dr_status
!= DUP_DROP
) {
1888 * The LRU list can't contain an
1889 * entry where the status is other than
1890 * DUP_DONE or DUP_DROP.
1893 "__svc_dupcache_victim: bad victim");
1896 * Need to hold the reader/writers lock to
1897 * print the cache info, since we already
1898 * hold the writers lock, we shall continue
1899 * calling __svc_dupcache_debug()
1901 __svc_dupcache_debug(dc
);
1902 #endif /* DUP_DEBUG */
1903 (void) rw_unlock(&(dc
->dc_lock
));
1907 if (dr
->dr_resp
.buf
) {
1908 free(dr
->dr_resp
.buf
);
1909 dr
->dr_resp
.buf
= NULL
;
1911 if (dr
->dr_addr
.buf
) {
1912 free(dr
->dr_addr
.buf
);
1913 dr
->dr_addr
.buf
= NULL
;
1916 /* unhash the entry */
1918 dr
->dr_chain
->dr_prevchain
= dr
->dr_prevchain
;
1919 if (dr
->dr_prevchain
)
1920 dr
->dr_prevchain
->dr_chain
= dr
->dr_chain
;
1921 if (dc
->dc_hashtbl
[dr
->dr_hash
] == dr
)
1922 dc
->dc_hashtbl
[dr
->dr_hash
] = dr
->dr_chain
;
1924 /* modify the lru pointers */
1925 if (dc
->dc_mru
== dr
) {
1928 dc
->dc_mru
->dr_next
= dr
->dr_next
;
1929 dr
->dr_next
->dr_prev
= dc
->dc_mru
;
1934 (void) rw_unlock(&(dc
->dc_lock
));
1937 * Allocate and return new clean entry as victim
1939 if ((dr
= malloc(sizeof (*dr
))) == NULL
) {
1941 "__svc_dupcache_victim: malloc failed");
1944 (void) memset(dr
, 0, sizeof (*dr
));
1948 "__svc_dupcache_victim: undefined dup cache_basis");
1954 * __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1955 * struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1956 * build new duprequest entry and then insert into the cache
1959 __svc_dupcache_enter(struct svc_req
*req
, struct dupreq
*dr
,
1960 struct dupcache
*dc
, uint32_t drxid
, uint32_t drhash
, time_t timenow
)
1963 dr
->dr_prog
= req
->rq_prog
;
1964 dr
->dr_vers
= req
->rq_vers
;
1965 dr
->dr_proc
= req
->rq_proc
;
1966 dr
->dr_addr
.maxlen
= req
->rq_xprt
->xp_rtaddr
.len
;
1967 dr
->dr_addr
.len
= dr
->dr_addr
.maxlen
;
1968 if ((dr
->dr_addr
.buf
= malloc(dr
->dr_addr
.maxlen
)) == NULL
) {
1969 syslog(LOG_ERR
, "__svc_dupcache_enter: malloc failed");
1973 (void) memset(dr
->dr_addr
.buf
, 0, dr
->dr_addr
.len
);
1974 (void) memcpy(dr
->dr_addr
.buf
, req
->rq_xprt
->xp_rtaddr
.buf
,
1976 dr
->dr_resp
.buf
= NULL
;
1977 dr
->dr_resp
.maxlen
= 0;
1978 dr
->dr_resp
.len
= 0;
1979 dr
->dr_status
= DUP_INPROGRESS
;
1980 dr
->dr_time
= timenow
;
1981 dr
->dr_hash
= drhash
; /* needed for efficient victim cleanup */
1983 /* place entry at head of hash table */
1984 (void) rw_wrlock(&(dc
->dc_lock
));
1985 dr
->dr_chain
= dc
->dc_hashtbl
[drhash
];
1986 dr
->dr_prevchain
= NULL
;
1987 if (dc
->dc_hashtbl
[drhash
] != NULL
)
1988 dc
->dc_hashtbl
[drhash
]->dr_prevchain
= dr
;
1989 dc
->dc_hashtbl
[drhash
] = dr
;
1990 (void) rw_unlock(&(dc
->dc_lock
));
1995 * __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
1996 * int status, char *xprt_cache)
1997 * Marks the request done (DUP_DONE or DUP_DROP) and stores the response.
1998 * Only DONE and DROP requests can be marked as done. Sets the lru pointers
1999 * to make the entry the most recently used. Returns DUP_ERROR or status.
2002 __svc_dupdone(struct svc_req
*req
, caddr_t resp_buf
, uint_t resp_bufsz
,
2003 int status
, char *xprt_cache
)
2005 uint32_t drxid
, drhash
;
2008 /* LINTED pointer alignment */
2009 struct dupcache
*dc
= (struct dupcache
*)xprt_cache
;
2012 syslog(LOG_ERR
, "__svc_dupdone: undefined cache");
2016 if (status
!= DUP_DONE
&& status
!= DUP_DROP
) {
2017 syslog(LOG_ERR
, "__svc_dupdone: invalid dupdone status");
2018 syslog(LOG_ERR
, " must be DUP_DONE or DUP_DROP");
2022 /* find the xid of the entry in the cache */
2023 if (SVC_CONTROL(req
->rq_xprt
, SVCGET_XID
, (void*)&drxid
) == FALSE
) {
2024 syslog(LOG_ERR
, "__svc_dup: xid error");
2027 drhash
= drxid
% dc
->dc_buckets
;
2029 /* update the status of the entry and result buffers, if required */
2030 if ((rc
= __svc_dupcache_update(req
, resp_buf
, resp_bufsz
, status
,
2031 dc
, drxid
, drhash
)) == DUP_ERROR
) {
2032 syslog(LOG_ERR
, "__svc_dupdone: cache entry error");
2040 * __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf,
2041 * uint_t resp_bufsz, int status, struct dupcache *dc, uint32_t drxid,
2043 * Check if entry exists in the dupcacache. If it does, update its status
2044 * and time and also its buffer, if appropriate. Its possible, but unlikely
2045 * for DONE requests to not exist in the cache. Return DUP_ERROR or status.
2048 __svc_dupcache_update(struct svc_req
*req
, caddr_t resp_buf
, uint_t resp_bufsz
,
2049 int status
, struct dupcache
*dc
, uint32_t drxid
, uint32_t drhash
)
2051 struct dupreq
*dr
= NULL
;
2052 time_t timenow
= time(NULL
);
2054 (void) rw_wrlock(&(dc
->dc_lock
));
2055 dr
= dc
->dc_hashtbl
[drhash
];
2056 while (dr
!= NULL
) {
2057 if (dr
->dr_xid
== drxid
&&
2058 dr
->dr_proc
== req
->rq_proc
&&
2059 dr
->dr_prog
== req
->rq_prog
&&
2060 dr
->dr_vers
== req
->rq_vers
&&
2061 dr
->dr_addr
.len
== req
->rq_xprt
->xp_rtaddr
.len
&&
2062 memcmp(dr
->dr_addr
.buf
, req
->rq_xprt
->xp_rtaddr
.buf
,
2063 dr
->dr_addr
.len
) == 0) { /* entry found */
2064 if (dr
->dr_hash
!= drhash
) {
2066 (void) rw_unlock(&(dc
->dc_lock
));
2068 "\n__svc_dupdone: hashing error");
2072 /* store the results if bufer is not NULL */
2073 if (resp_buf
!= NULL
) {
2074 if ((dr
->dr_resp
.buf
=
2075 malloc(resp_bufsz
)) == NULL
) {
2076 (void) rw_unlock(&(dc
->dc_lock
));
2078 "__svc_dupdone: malloc failed");
2081 (void) memset(dr
->dr_resp
.buf
, 0, resp_bufsz
);
2082 (void) memcpy(dr
->dr_resp
.buf
, resp_buf
,
2083 (uint_t
)resp_bufsz
);
2084 dr
->dr_resp
.len
= resp_bufsz
;
2087 /* update status and done time */
2088 dr
->dr_status
= status
;
2089 dr
->dr_time
= timenow
;
2091 /* move the entry to the mru position */
2092 if (dc
->dc_mru
== NULL
) {
2096 dr
->dr_next
= dc
->dc_mru
->dr_next
;
2097 dc
->dc_mru
->dr_next
->dr_prev
= dr
;
2098 dr
->dr_prev
= dc
->dc_mru
;
2099 dc
->dc_mru
->dr_next
= dr
;
2103 (void) rw_unlock(&(dc
->dc_lock
));
2108 (void) rw_unlock(&(dc
->dc_lock
));
2109 syslog(LOG_ERR
, "__svc_dupdone: entry not in dup cache");
2115 * __svc_dupcache_debug(struct dupcache *dc)
2116 * print out the hash table stuff
2118 * This function requires the caller to hold the reader
2119 * or writer version of the duplicate request cache lock (dc_lock).
2122 __svc_dupcache_debug(struct dupcache
*dc
)
2124 struct dupreq
*dr
= NULL
;
2128 fprintf(stderr
, " HASHTABLE\n");
2129 for (i
= 0; i
< dc
->dc_buckets
; i
++) {
2131 dr
= dc
->dc_hashtbl
[i
];
2132 while (dr
!= NULL
) {
2133 if (!bval
) { /* ensures bucket printed only once */
2134 fprintf(stderr
, " bucket : %d\n", i
);
2137 fprintf(stderr
, "\txid: %u status: %d time: %ld",
2138 dr
->dr_xid
, dr
->dr_status
, dr
->dr_time
);
2139 fprintf(stderr
, " dr: %x chain: %x prevchain: %x\n",
2140 dr
, dr
->dr_chain
, dr
->dr_prevchain
);
2145 fprintf(stderr
, " LRU\n");
2147 dr
= dc
->dc_mru
->dr_next
; /* lru */
2148 while (dr
!= dc
->dc_mru
) {
2149 fprintf(stderr
, "\txid: %u status : %d time : %ld",
2150 dr
->dr_xid
, dr
->dr_status
, dr
->dr_time
);
2151 fprintf(stderr
, " dr: %x next: %x prev: %x\n",
2152 dr
, dr
->dr_next
, dr
->dr_prev
);
2155 fprintf(stderr
, "\txid: %u status: %d time: %ld",
2156 dr
->dr_xid
, dr
->dr_status
, dr
->dr_time
);
2157 fprintf(stderr
, " dr: %x next: %x prev: %x\n",
2158 dr
, dr
->dr_next
, dr
->dr_prev
);
2161 #endif /* DUP_DEBUG */