svc_dg.c revision 1.14.2.1       1  1.14.2.1       tls /*	$NetBSD: svc_dg.c,v 1.14.2.1 2013/06/23 06:21:05 tls Exp $	*/
      2       1.1      fvdl 
      3       1.1      fvdl /*
      4  1.14.2.1       tls  * Copyright (c) 2010, Oracle America, Inc.
      5  1.14.2.1       tls  *
      6  1.14.2.1       tls  * Redistribution and use in source and binary forms, with or without
      7  1.14.2.1       tls  * modification, are permitted provided that the following conditions are
      8  1.14.2.1       tls  * met:
      9  1.14.2.1       tls  *
     10  1.14.2.1       tls  *     * Redistributions of source code must retain the above copyright
     11  1.14.2.1       tls  *       notice, this list of conditions and the following disclaimer.
     12  1.14.2.1       tls  *     * Redistributions in binary form must reproduce the above
     13  1.14.2.1       tls  *       copyright notice, this list of conditions and the following
     14  1.14.2.1       tls  *       disclaimer in the documentation and/or other materials
     15  1.14.2.1       tls  *       provided with the distribution.
     16  1.14.2.1       tls  *     * Neither the name of the "Oracle America, Inc." nor the names of its
     17  1.14.2.1       tls  *       contributors may be used to endorse or promote products derived
     18  1.14.2.1       tls  *       from this software without specific prior written permission.
     19  1.14.2.1       tls  *
     20  1.14.2.1       tls  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  1.14.2.1       tls  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  1.14.2.1       tls  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     23  1.14.2.1       tls  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     24  1.14.2.1       tls  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     25  1.14.2.1       tls  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  1.14.2.1       tls  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     27  1.14.2.1       tls  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28  1.14.2.1       tls  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     29  1.14.2.1       tls  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     30  1.14.2.1       tls  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     31  1.14.2.1       tls  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32       1.1      fvdl  */
     33       1.1      fvdl 
     34       1.1      fvdl /*
     35       1.1      fvdl  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
     36       1.1      fvdl  */
     37       1.1      fvdl 
     38       1.1      fvdl /* #ident	"@(#)svc_dg.c	1.17	94/04/24 SMI" */
     39       1.1      fvdl 
     40       1.1      fvdl 
     41       1.1      fvdl /*
     42       1.1      fvdl  * svc_dg.c, Server side for connectionless RPC.
     43       1.1      fvdl  *
     44       1.1      fvdl  * Does some caching in the hopes of achieving execute-at-most-once semantics.
     45       1.1      fvdl  */
     46      1.10    itojun 
     47      1.10    itojun #include <sys/cdefs.h>
     48      1.10    itojun #if defined(LIBC_SCCS) && !defined(lint)
     49  1.14.2.1       tls __RCSID("$NetBSD: svc_dg.c,v 1.14.2.1 2013/06/23 06:21:05 tls Exp $");
     50      1.10    itojun #endif
     51       1.1      fvdl 
     52       1.1      fvdl #include "namespace.h"
     53       1.1      fvdl #include "reentrant.h"
     54       1.1      fvdl #include <sys/types.h>
     55       1.1      fvdl #include <sys/socket.h>
     56       1.1      fvdl #include <rpc/rpc.h>
     57       1.6     lukem #include <assert.h>
     58       1.1      fvdl #include <errno.h>
     59       1.1      fvdl #include <unistd.h>
     60       1.1      fvdl #include <stdio.h>
     61       1.1      fvdl #include <stdlib.h>
     62       1.2   thorpej #include <string.h>
     63       1.1      fvdl #ifdef RPC_CACHE_DEBUG
     64       1.1      fvdl #include <netconfig.h>
     65       1.1      fvdl #include <netdir.h>
     66       1.1      fvdl #endif
     67       1.1      fvdl #include <err.h>
     68       1.1      fvdl 
     69  1.14.2.1       tls #include "svc_fdset.h"
     70       1.7      fvdl #include "rpc_internal.h"
     71       1.1      fvdl #include "svc_dg.h"
     72       1.1      fvdl 
     73       1.1      fvdl #define	su_data(xprt)	((struct svc_dg_data *)(xprt->xp_p2))
     74       1.1      fvdl #define	rpc_buffer(xprt) ((xprt)->xp_p1)
     75       1.1      fvdl 
     76       1.1      fvdl #ifdef __weak_alias
     77       1.1      fvdl __weak_alias(svc_dg_create,_svc_dg_create)
     78       1.1      fvdl #endif
     79       1.1      fvdl 
     80       1.1      fvdl #ifndef MAX
     81       1.1      fvdl #define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
     82       1.1      fvdl #endif
     83       1.1      fvdl 
     84      1.14      matt static void svc_dg_ops(SVCXPRT *);
     85      1.14      matt static enum xprt_stat svc_dg_stat(SVCXPRT *);
     86      1.14      matt static bool_t svc_dg_recv(SVCXPRT *, struct rpc_msg *);
     87      1.14      matt static bool_t svc_dg_reply(SVCXPRT *, struct rpc_msg *);
     88      1.14      matt static bool_t svc_dg_getargs(SVCXPRT *, xdrproc_t, caddr_t);
     89      1.14      matt static bool_t svc_dg_freeargs(SVCXPRT *, xdrproc_t, caddr_t);
     90      1.14      matt static void svc_dg_destroy(SVCXPRT *);
     91      1.14      matt static bool_t svc_dg_control(SVCXPRT *, const u_int, void *);
     92      1.14      matt static int cache_get(SVCXPRT *, struct rpc_msg *, char **, size_t *);
     93      1.14      matt static void cache_set(SVCXPRT *, size_t);
     94       1.1      fvdl 
     95       1.1      fvdl /*
     96       1.1      fvdl  * Usage:
     97       1.1      fvdl  *	xprt = svc_dg_create(sock, sendsize, recvsize);
     98       1.1      fvdl  * Does other connectionless specific initializations.
     99       1.1      fvdl  * Once *xprt is initialized, it is registered.
    100       1.1      fvdl  * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable
    101       1.1      fvdl  * system defaults are chosen.
    102       1.1      fvdl  * The routines returns NULL if a problem occurred.
    103       1.1      fvdl  */
    104       1.1      fvdl static const char svc_dg_str[] = "svc_dg_create: %s";
    105       1.1      fvdl static const char svc_dg_err1[] = "could not get transport information";
    106       1.1      fvdl static const char svc_dg_err2[] = " transport does not support data transfer";
    107       1.1      fvdl static const char __no_mem_str[] = "out of memory";
    108       1.1      fvdl 
    109       1.1      fvdl SVCXPRT *
    110      1.14      matt svc_dg_create(int fd, u_int sendsize, u_int recvsize)
    111       1.1      fvdl {
    112       1.1      fvdl 	SVCXPRT *xprt;
    113       1.1      fvdl 	struct svc_dg_data *su = NULL;
    114       1.1      fvdl 	struct __rpc_sockinfo si;
    115       1.1      fvdl 	struct sockaddr_storage ss;
    116       1.1      fvdl 	socklen_t slen;
    117       1.1      fvdl 
    118       1.1      fvdl 	if (!__rpc_fd2sockinfo(fd, &si)) {
    119       1.1      fvdl 		warnx(svc_dg_str, svc_dg_err1);
    120       1.4  christos 		return (NULL);
    121       1.1      fvdl 	}
    122       1.1      fvdl 	/*
    123       1.1      fvdl 	 * Find the receive and the send size
    124       1.1      fvdl 	 */
    125       1.1      fvdl 	sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
    126       1.1      fvdl 	recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
    127       1.1      fvdl 	if ((sendsize == 0) || (recvsize == 0)) {
    128       1.1      fvdl 		warnx(svc_dg_str, svc_dg_err2);
    129       1.4  christos 		return (NULL);
    130       1.1      fvdl 	}
    131       1.1      fvdl 
    132       1.4  christos 	xprt = mem_alloc(sizeof (SVCXPRT));
    133       1.1      fvdl 	if (xprt == NULL)
    134  1.14.2.1       tls 		goto outofmem;
    135       1.4  christos 	memset(xprt, 0, sizeof (SVCXPRT));
    136       1.1      fvdl 
    137       1.4  christos 	su = mem_alloc(sizeof (*su));
    138       1.1      fvdl 	if (su == NULL)
    139  1.14.2.1       tls 		goto outofmem;
    140       1.1      fvdl 	su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4;
    141      1.11      yamt 	if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL)
    142  1.14.2.1       tls 		goto outofmem;
    143      1.13  christos 	_DIAGASSERT(__type_fit(u_int, su->su_iosz));
    144      1.13  christos 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), (u_int)su->su_iosz,
    145       1.1      fvdl 		XDR_DECODE);
    146       1.1      fvdl 	su->su_cache = NULL;
    147       1.1      fvdl 	xprt->xp_fd = fd;
    148       1.4  christos 	xprt->xp_p2 = (caddr_t)(void *)su;
    149       1.1      fvdl 	xprt->xp_verf.oa_base = su->su_verfbody;
    150       1.1      fvdl 	svc_dg_ops(xprt);
    151       1.1      fvdl 	xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
    152       1.1      fvdl 
    153       1.1      fvdl 	slen = sizeof ss;
    154       1.4  christos 	if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0)
    155       1.1      fvdl 		goto freedata;
    156       1.1      fvdl 	xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
    157       1.1      fvdl 	xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage);
    158       1.1      fvdl 	xprt->xp_ltaddr.len = slen;
    159       1.1      fvdl 	memcpy(xprt->xp_ltaddr.buf, &ss, slen);
    160       1.1      fvdl 
    161  1.14.2.1       tls 	if (!xprt_register(xprt))
    162  1.14.2.1       tls 		goto freedata;
    163       1.1      fvdl 	return (xprt);
    164  1.14.2.1       tls 
    165  1.14.2.1       tls outofmem:
    166       1.1      fvdl 	(void) warnx(svc_dg_str, __no_mem_str);
    167  1.14.2.1       tls freedata:
    168       1.1      fvdl 	if (xprt) {
    169       1.1      fvdl 		if (su)
    170       1.4  christos 			(void) mem_free(su, sizeof (*su));
    171       1.4  christos 		(void) mem_free(xprt, sizeof (SVCXPRT));
    172       1.1      fvdl 	}
    173       1.4  christos 	return (NULL);
    174       1.1      fvdl }
    175       1.1      fvdl 
    176       1.4  christos /*ARGSUSED*/
    177       1.1      fvdl static enum xprt_stat
    178      1.14      matt svc_dg_stat(SVCXPRT *xprt)
    179       1.1      fvdl {
    180       1.1      fvdl 	return (XPRT_IDLE);
    181       1.1      fvdl }
    182       1.1      fvdl 
    183       1.1      fvdl static bool_t
    184      1.14      matt svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
    185       1.1      fvdl {
    186       1.6     lukem 	struct svc_dg_data *su;
    187       1.6     lukem 	XDR *xdrs;
    188       1.1      fvdl 	char *reply;
    189       1.1      fvdl 	struct sockaddr_storage ss;
    190       1.1      fvdl 	socklen_t alen;
    191       1.1      fvdl 	size_t replylen;
    192       1.8   thorpej 	ssize_t rlen;
    193       1.1      fvdl 
    194       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    195       1.6     lukem 	_DIAGASSERT(msg != NULL);
    196       1.6     lukem 
    197       1.6     lukem 	su = su_data(xprt);
    198       1.6     lukem 	xdrs = &(su->su_xdrs);
    199       1.6     lukem 
    200       1.1      fvdl again:
    201       1.1      fvdl 	alen = sizeof (struct sockaddr_storage);
    202       1.1      fvdl 	rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0,
    203       1.4  christos 	    (struct sockaddr *)(void *)&ss, &alen);
    204       1.1      fvdl 	if (rlen == -1 && errno == EINTR)
    205       1.1      fvdl 		goto again;
    206       1.8   thorpej 	if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t))))
    207       1.1      fvdl 		return (FALSE);
    208       1.3      fvdl 	if (xprt->xp_rtaddr.len < alen) {
    209       1.3      fvdl 		if (xprt->xp_rtaddr.len != 0)
    210       1.3      fvdl 			mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.len);
    211       1.3      fvdl 		xprt->xp_rtaddr.buf = mem_alloc(alen);
    212       1.3      fvdl 		xprt->xp_rtaddr.len = alen;
    213       1.3      fvdl 	}
    214       1.1      fvdl 	memcpy(xprt->xp_rtaddr.buf, &ss, alen);
    215       1.1      fvdl #ifdef PORTMAP
    216       1.1      fvdl 	if (ss.ss_family == AF_INET) {
    217       1.1      fvdl 		xprt->xp_raddr = *(struct sockaddr_in *)xprt->xp_rtaddr.buf;
    218       1.1      fvdl 		xprt->xp_addrlen = sizeof (struct sockaddr_in);
    219       1.1      fvdl 	}
    220       1.1      fvdl #endif
    221       1.1      fvdl 	xdrs->x_op = XDR_DECODE;
    222       1.1      fvdl 	XDR_SETPOS(xdrs, 0);
    223       1.1      fvdl 	if (! xdr_callmsg(xdrs, msg)) {
    224       1.1      fvdl 		return (FALSE);
    225       1.1      fvdl 	}
    226       1.1      fvdl 	su->su_xid = msg->rm_xid;
    227       1.1      fvdl 	if (su->su_cache != NULL) {
    228       1.1      fvdl 		if (cache_get(xprt, msg, &reply, &replylen)) {
    229       1.1      fvdl 			(void)sendto(xprt->xp_fd, reply, replylen, 0,
    230       1.4  christos 			    (struct sockaddr *)(void *)&ss, alen);
    231       1.1      fvdl 			return (FALSE);
    232       1.1      fvdl 		}
    233       1.1      fvdl 	}
    234       1.1      fvdl 	return (TRUE);
    235       1.1      fvdl }
    236       1.1      fvdl 
    237       1.1      fvdl static bool_t
    238      1.14      matt svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg)
    239       1.1      fvdl {
    240       1.6     lukem 	struct svc_dg_data *su;
    241       1.6     lukem 	XDR *xdrs;
    242       1.1      fvdl 	bool_t stat = FALSE;
    243       1.1      fvdl 	size_t slen;
    244       1.1      fvdl 
    245       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    246       1.6     lukem 	_DIAGASSERT(msg != NULL);
    247       1.6     lukem 
    248       1.6     lukem 	su = su_data(xprt);
    249       1.6     lukem 	xdrs = &(su->su_xdrs);
    250       1.6     lukem 
    251       1.1      fvdl 	xdrs->x_op = XDR_ENCODE;
    252       1.1      fvdl 	XDR_SETPOS(xdrs, 0);
    253       1.1      fvdl 	msg->rm_xid = su->su_xid;
    254       1.1      fvdl 	if (xdr_replymsg(xdrs, msg)) {
    255       1.1      fvdl 		slen = XDR_GETPOS(xdrs);
    256       1.1      fvdl 		if (sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0,
    257       1.1      fvdl 		    (struct sockaddr *)xprt->xp_rtaddr.buf,
    258       1.8   thorpej 		    (socklen_t)xprt->xp_rtaddr.len) == (ssize_t) slen) {
    259       1.1      fvdl 			stat = TRUE;
    260       1.4  christos 			if (su->su_cache)
    261       1.1      fvdl 				cache_set(xprt, slen);
    262       1.1      fvdl 		}
    263       1.1      fvdl 	}
    264       1.1      fvdl 	return (stat);
    265       1.1      fvdl }
    266       1.1      fvdl 
    267       1.1      fvdl static bool_t
    268      1.14      matt svc_dg_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
    269       1.1      fvdl {
    270       1.1      fvdl 	return (*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr);
    271       1.1      fvdl }
    272       1.1      fvdl 
    273       1.1      fvdl static bool_t
    274      1.14      matt svc_dg_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
    275       1.1      fvdl {
    276       1.6     lukem 	XDR *xdrs;
    277       1.1      fvdl 
    278       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    279       1.6     lukem 
    280       1.6     lukem 	xdrs = &(su_data(xprt)->su_xdrs);
    281       1.1      fvdl 	xdrs->x_op = XDR_FREE;
    282       1.1      fvdl 	return (*xdr_args)(xdrs, args_ptr);
    283       1.1      fvdl }
    284       1.1      fvdl 
    285       1.1      fvdl static void
    286      1.14      matt svc_dg_destroy(SVCXPRT *xprt)
    287       1.1      fvdl {
    288       1.6     lukem 	struct svc_dg_data *su;
    289       1.6     lukem 
    290       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    291       1.6     lukem 
    292       1.6     lukem 	su = su_data(xprt);
    293       1.1      fvdl 
    294       1.1      fvdl 	xprt_unregister(xprt);
    295       1.1      fvdl 	if (xprt->xp_fd != -1)
    296       1.1      fvdl 		(void)close(xprt->xp_fd);
    297       1.1      fvdl 	XDR_DESTROY(&(su->su_xdrs));
    298       1.1      fvdl 	(void) mem_free(rpc_buffer(xprt), su->su_iosz);
    299       1.4  christos 	(void) mem_free(su, sizeof (*su));
    300       1.1      fvdl 	if (xprt->xp_rtaddr.buf)
    301       1.1      fvdl 		(void) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
    302       1.1      fvdl 	if (xprt->xp_ltaddr.buf)
    303       1.1      fvdl 		(void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
    304       1.1      fvdl 	if (xprt->xp_tp)
    305       1.1      fvdl 		(void) free(xprt->xp_tp);
    306       1.4  christos 	(void) mem_free(xprt, sizeof (SVCXPRT));
    307       1.1      fvdl }
    308       1.1      fvdl 
    309       1.1      fvdl static bool_t
    310       1.4  christos /*ARGSUSED*/
    311      1.14      matt svc_dg_control(SVCXPRT *xprt, const u_int rq, void *in)
    312       1.1      fvdl {
    313       1.1      fvdl 	return (FALSE);
    314       1.1      fvdl }
    315       1.1      fvdl 
    316       1.1      fvdl static void
    317      1.14      matt svc_dg_ops(SVCXPRT *xprt)
    318       1.1      fvdl {
    319       1.1      fvdl 	static struct xp_ops ops;
    320       1.1      fvdl 	static struct xp_ops2 ops2;
    321       1.9   thorpej #ifdef _REENTRANT
    322       1.1      fvdl 	extern mutex_t ops_lock;
    323       1.1      fvdl #endif
    324       1.1      fvdl 
    325       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    326       1.6     lukem 
    327       1.1      fvdl /* VARIABLES PROTECTED BY ops_lock: ops */
    328       1.1      fvdl 
    329       1.1      fvdl 	mutex_lock(&ops_lock);
    330       1.1      fvdl 	if (ops.xp_recv == NULL) {
    331       1.1      fvdl 		ops.xp_recv = svc_dg_recv;
    332       1.1      fvdl 		ops.xp_stat = svc_dg_stat;
    333       1.1      fvdl 		ops.xp_getargs = svc_dg_getargs;
    334       1.1      fvdl 		ops.xp_reply = svc_dg_reply;
    335       1.1      fvdl 		ops.xp_freeargs = svc_dg_freeargs;
    336       1.1      fvdl 		ops.xp_destroy = svc_dg_destroy;
    337       1.1      fvdl 		ops2.xp_control = svc_dg_control;
    338       1.1      fvdl 	}
    339       1.1      fvdl 	xprt->xp_ops = &ops;
    340       1.1      fvdl 	xprt->xp_ops2 = &ops2;
    341       1.1      fvdl 	mutex_unlock(&ops_lock);
    342       1.1      fvdl }
    343       1.1      fvdl 
    344       1.1      fvdl /*  The CACHING COMPONENT */
    345       1.1      fvdl 
    346       1.1      fvdl /*
    347       1.1      fvdl  * Could have been a separate file, but some part of it depends upon the
    348       1.1      fvdl  * private structure of the client handle.
    349       1.1      fvdl  *
    350       1.1      fvdl  * Fifo cache for cl server
    351       1.1      fvdl  * Copies pointers to reply buffers into fifo cache
    352       1.1      fvdl  * Buffers are sent again if retransmissions are detected.
    353       1.1      fvdl  */
    354       1.1      fvdl 
    355       1.1      fvdl #define	SPARSENESS 4	/* 75% sparse */
    356       1.1      fvdl 
    357       1.1      fvdl #define	ALLOC(type, size)	\
    358      1.12  christos 	mem_alloc((sizeof (type) * (size)))
    359       1.1      fvdl 
    360       1.1      fvdl #define	MEMZERO(addr, type, size)	 \
    361       1.4  christos 	(void) memset((void *) (addr), 0, sizeof (type) * (int) (size))
    362       1.1      fvdl 
    363       1.1      fvdl #define	FREE(addr, type, size)	\
    364       1.4  christos 	mem_free((addr), (sizeof (type) * (size)))
    365       1.1      fvdl 
    366       1.1      fvdl /*
    367       1.1      fvdl  * An entry in the cache
    368       1.1      fvdl  */
    369       1.1      fvdl typedef struct cache_node *cache_ptr;
    370       1.1      fvdl struct cache_node {
    371       1.1      fvdl 	/*
    372       1.1      fvdl 	 * Index into cache is xid, proc, vers, prog and address
    373       1.1      fvdl 	 */
    374       1.1      fvdl 	u_int32_t cache_xid;
    375       1.1      fvdl 	rpcproc_t cache_proc;
    376       1.1      fvdl 	rpcvers_t cache_vers;
    377       1.1      fvdl 	rpcprog_t cache_prog;
    378       1.1      fvdl 	struct netbuf cache_addr;
    379       1.1      fvdl 	/*
    380       1.1      fvdl 	 * The cached reply and length
    381       1.1      fvdl 	 */
    382       1.1      fvdl 	char *cache_reply;
    383       1.1      fvdl 	size_t cache_replylen;
    384       1.1      fvdl 	/*
    385       1.1      fvdl 	 * Next node on the list, if there is a collision
    386       1.1      fvdl 	 */
    387       1.1      fvdl 	cache_ptr cache_next;
    388       1.1      fvdl };
    389       1.1      fvdl 
    390       1.1      fvdl /*
    391       1.1      fvdl  * The entire cache
    392       1.1      fvdl  */
    393       1.1      fvdl struct cl_cache {
    394       1.1      fvdl 	u_int uc_size;		/* size of cache */
    395       1.1      fvdl 	cache_ptr *uc_entries;	/* hash table of entries in cache */
    396       1.1      fvdl 	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
    397       1.1      fvdl 	u_int uc_nextvictim;	/* points to next victim in fifo list */
    398       1.1      fvdl 	rpcprog_t uc_prog;	/* saved program number */
    399       1.1      fvdl 	rpcvers_t uc_vers;	/* saved version number */
    400       1.1      fvdl 	rpcproc_t uc_proc;	/* saved procedure number */
    401       1.1      fvdl };
    402       1.1      fvdl 
    403       1.1      fvdl 
    404       1.1      fvdl /*
    405       1.1      fvdl  * the hashing function
    406       1.1      fvdl  */
    407       1.1      fvdl #define	CACHE_LOC(transp, xid)	\
    408       1.1      fvdl 	(xid % (SPARSENESS * ((struct cl_cache *) \
    409       1.1      fvdl 		su_data(transp)->su_cache)->uc_size))
    410       1.1      fvdl 
    411       1.9   thorpej #ifdef _REENTRANT
    412       1.1      fvdl extern mutex_t	dupreq_lock;
    413       1.1      fvdl #endif
    414       1.1      fvdl 
    415       1.1      fvdl /*
    416       1.1      fvdl  * Enable use of the cache. Returns 1 on success, 0 on failure.
    417       1.1      fvdl  * Note: there is no disable.
    418       1.1      fvdl  */
    419       1.1      fvdl static const char cache_enable_str[] = "svc_enablecache: %s %s";
    420       1.1      fvdl static const char alloc_err[] = "could not allocate cache ";
    421       1.1      fvdl static const char enable_err[] = "cache already enabled";
    422       1.1      fvdl 
    423       1.1      fvdl int
    424      1.14      matt svc_dg_enablecache(SVCXPRT *transp, u_int size)
    425       1.1      fvdl {
    426       1.6     lukem 	struct svc_dg_data *su;
    427       1.1      fvdl 	struct cl_cache *uc;
    428       1.1      fvdl 
    429       1.6     lukem 	_DIAGASSERT(transp != NULL);
    430       1.6     lukem 
    431       1.6     lukem 	su = su_data(transp);
    432       1.6     lukem 
    433       1.1      fvdl 	mutex_lock(&dupreq_lock);
    434       1.1      fvdl 	if (su->su_cache != NULL) {
    435       1.1      fvdl 		(void) warnx(cache_enable_str, enable_err, " ");
    436       1.1      fvdl 		mutex_unlock(&dupreq_lock);
    437       1.1      fvdl 		return (0);
    438       1.1      fvdl 	}
    439       1.1      fvdl 	uc = ALLOC(struct cl_cache, 1);
    440       1.1      fvdl 	if (uc == NULL) {
    441       1.1      fvdl 		warnx(cache_enable_str, alloc_err, " ");
    442       1.1      fvdl 		mutex_unlock(&dupreq_lock);
    443       1.1      fvdl 		return (0);
    444       1.1      fvdl 	}
    445       1.1      fvdl 	uc->uc_size = size;
    446       1.1      fvdl 	uc->uc_nextvictim = 0;
    447       1.1      fvdl 	uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
    448       1.1      fvdl 	if (uc->uc_entries == NULL) {
    449       1.1      fvdl 		warnx(cache_enable_str, alloc_err, "data");
    450       1.1      fvdl 		FREE(uc, struct cl_cache, 1);
    451       1.1      fvdl 		mutex_unlock(&dupreq_lock);
    452       1.1      fvdl 		return (0);
    453       1.1      fvdl 	}
    454       1.1      fvdl 	MEMZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
    455       1.1      fvdl 	uc->uc_fifo = ALLOC(cache_ptr, size);
    456       1.1      fvdl 	if (uc->uc_fifo == NULL) {
    457       1.1      fvdl 		warnx(cache_enable_str, alloc_err, "fifo");
    458       1.1      fvdl 		FREE(uc->uc_entries, cache_ptr, size * SPARSENESS);
    459       1.1      fvdl 		FREE(uc, struct cl_cache, 1);
    460       1.1      fvdl 		mutex_unlock(&dupreq_lock);
    461       1.1      fvdl 		return (0);
    462       1.1      fvdl 	}
    463       1.1      fvdl 	MEMZERO(uc->uc_fifo, cache_ptr, size);
    464       1.4  christos 	su->su_cache = (char *)(void *)uc;
    465       1.1      fvdl 	mutex_unlock(&dupreq_lock);
    466       1.1      fvdl 	return (1);
    467       1.1      fvdl }
    468       1.1      fvdl 
    469       1.1      fvdl /*
    470       1.1      fvdl  * Set an entry in the cache.  It assumes that the uc entry is set from
    471       1.1      fvdl  * the earlier call to cache_get() for the same procedure.  This will always
    472       1.1      fvdl  * happen because cache_get() is calle by svc_dg_recv and cache_set() is called
    473       1.1      fvdl  * by svc_dg_reply().  All this hoopla because the right RPC parameters are
    474       1.1      fvdl  * not available at svc_dg_reply time.
    475       1.1      fvdl  */
    476       1.1      fvdl 
    477       1.1      fvdl static const char cache_set_str[] = "cache_set: %s";
    478       1.1      fvdl static const char cache_set_err1[] = "victim not found";
    479       1.1      fvdl static const char cache_set_err2[] = "victim alloc failed";
    480       1.1      fvdl static const char cache_set_err3[] = "could not allocate new rpc buffer";
    481       1.1      fvdl 
    482       1.1      fvdl static void
    483      1.14      matt cache_set(SVCXPRT *xprt, size_t replylen)
    484       1.1      fvdl {
    485       1.4  christos 	cache_ptr victim;
    486       1.4  christos 	cache_ptr *vicp;
    487       1.6     lukem 	struct svc_dg_data *su;
    488       1.6     lukem 	struct cl_cache *uc;
    489       1.1      fvdl 	u_int loc;
    490       1.1      fvdl 	char *newbuf;
    491       1.1      fvdl #ifdef RPC_CACHE_DEBUG
    492       1.1      fvdl 	struct netconfig *nconf;
    493       1.1      fvdl 	char *uaddr;
    494       1.1      fvdl #endif
    495       1.1      fvdl 
    496       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    497       1.6     lukem 
    498       1.6     lukem 	su = su_data(xprt);
    499       1.6     lukem 	uc = (struct cl_cache *) su->su_cache;
    500       1.6     lukem 
    501       1.1      fvdl 	mutex_lock(&dupreq_lock);
    502       1.1      fvdl 	/*
    503       1.1      fvdl 	 * Find space for the new entry, either by
    504       1.1      fvdl 	 * reusing an old entry, or by mallocing a new one
    505       1.1      fvdl 	 */
    506       1.1      fvdl 	victim = uc->uc_fifo[uc->uc_nextvictim];
    507       1.1      fvdl 	if (victim != NULL) {
    508       1.1      fvdl 		loc = CACHE_LOC(xprt, victim->cache_xid);
    509       1.1      fvdl 		for (vicp = &uc->uc_entries[loc];
    510       1.1      fvdl 			*vicp != NULL && *vicp != victim;
    511       1.1      fvdl 			vicp = &(*vicp)->cache_next)
    512       1.1      fvdl 			;
    513       1.1      fvdl 		if (*vicp == NULL) {
    514       1.1      fvdl 			warnx(cache_set_str, cache_set_err1);
    515       1.1      fvdl 			mutex_unlock(&dupreq_lock);
    516       1.1      fvdl 			return;
    517       1.1      fvdl 		}
    518       1.1      fvdl 		*vicp = victim->cache_next;	/* remove from cache */
    519       1.1      fvdl 		newbuf = victim->cache_reply;
    520       1.1      fvdl 	} else {
    521       1.1      fvdl 		victim = ALLOC(struct cache_node, 1);
    522       1.1      fvdl 		if (victim == NULL) {
    523       1.1      fvdl 			warnx(cache_set_str, cache_set_err2);
    524       1.1      fvdl 			mutex_unlock(&dupreq_lock);
    525       1.1      fvdl 			return;
    526       1.1      fvdl 		}
    527       1.4  christos 		newbuf = mem_alloc(su->su_iosz);
    528       1.1      fvdl 		if (newbuf == NULL) {
    529       1.1      fvdl 			warnx(cache_set_str, cache_set_err3);
    530       1.1      fvdl 			FREE(victim, struct cache_node, 1);
    531       1.1      fvdl 			mutex_unlock(&dupreq_lock);
    532       1.1      fvdl 			return;
    533       1.1      fvdl 		}
    534       1.1      fvdl 	}
    535       1.1      fvdl 
    536       1.1      fvdl 	/*
    537       1.1      fvdl 	 * Store it away
    538       1.1      fvdl 	 */
    539       1.1      fvdl #ifdef RPC_CACHE_DEBUG
    540       1.1      fvdl 	if (nconf = getnetconfigent(xprt->xp_netid)) {
    541       1.1      fvdl 		uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
    542       1.1      fvdl 		freenetconfigent(nconf);
    543       1.1      fvdl 		printf(
    544       1.1      fvdl 	"cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
    545       1.1      fvdl 			su->su_xid, uc->uc_prog, uc->uc_vers,
    546       1.1      fvdl 			uc->uc_proc, uaddr);
    547       1.1      fvdl 		free(uaddr);
    548       1.1      fvdl 	}
    549       1.1      fvdl #endif
    550       1.1      fvdl 	victim->cache_replylen = replylen;
    551       1.1      fvdl 	victim->cache_reply = rpc_buffer(xprt);
    552       1.1      fvdl 	rpc_buffer(xprt) = newbuf;
    553      1.13  christos 	_DIAGASSERT(__type_fit(u_int, su->su_iosz));
    554      1.13  christos 	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), (u_int)su->su_iosz,
    555      1.13  christos 	    XDR_ENCODE);
    556       1.1      fvdl 	victim->cache_xid = su->su_xid;
    557       1.1      fvdl 	victim->cache_proc = uc->uc_proc;
    558       1.1      fvdl 	victim->cache_vers = uc->uc_vers;
    559       1.1      fvdl 	victim->cache_prog = uc->uc_prog;
    560       1.1      fvdl 	victim->cache_addr = xprt->xp_rtaddr;
    561       1.1      fvdl 	victim->cache_addr.buf = ALLOC(char, xprt->xp_rtaddr.len);
    562       1.1      fvdl 	(void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf,
    563       1.4  christos 	    (size_t)xprt->xp_rtaddr.len);
    564       1.1      fvdl 	loc = CACHE_LOC(xprt, victim->cache_xid);
    565       1.1      fvdl 	victim->cache_next = uc->uc_entries[loc];
    566       1.1      fvdl 	uc->uc_entries[loc] = victim;
    567       1.1      fvdl 	uc->uc_fifo[uc->uc_nextvictim++] = victim;
    568       1.1      fvdl 	uc->uc_nextvictim %= uc->uc_size;
    569       1.1      fvdl 	mutex_unlock(&dupreq_lock);
    570       1.1      fvdl }
    571       1.1      fvdl 
    572       1.1      fvdl /*
    573       1.1      fvdl  * Try to get an entry from the cache
    574       1.1      fvdl  * return 1 if found, 0 if not found and set the stage for cache_set()
    575       1.1      fvdl  */
    576       1.1      fvdl static int
    577      1.14      matt cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, size_t *replylenp)
    578       1.1      fvdl {
    579       1.1      fvdl 	u_int loc;
    580       1.4  christos 	cache_ptr ent;
    581       1.6     lukem 	struct svc_dg_data *su;
    582       1.6     lukem 	struct cl_cache *uc;
    583       1.1      fvdl #ifdef RPC_CACHE_DEBUG
    584       1.1      fvdl 	struct netconfig *nconf;
    585       1.1      fvdl 	char *uaddr;
    586       1.1      fvdl #endif
    587       1.6     lukem 
    588       1.6     lukem 	_DIAGASSERT(xprt != NULL);
    589       1.6     lukem 	_DIAGASSERT(msg != NULL);
    590       1.6     lukem 	_DIAGASSERT(replyp != NULL);
    591       1.6     lukem 	_DIAGASSERT(replylenp != NULL);
    592       1.6     lukem 
    593       1.6     lukem 	su = su_data(xprt);
    594       1.6     lukem 	uc = (struct cl_cache *) su->su_cache;
    595       1.1      fvdl 
    596       1.1      fvdl 	mutex_lock(&dupreq_lock);
    597       1.1      fvdl 	loc = CACHE_LOC(xprt, su->su_xid);
    598       1.1      fvdl 	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
    599       1.1      fvdl 		if (ent->cache_xid == su->su_xid &&
    600       1.1      fvdl 			ent->cache_proc == msg->rm_call.cb_proc &&
    601       1.1      fvdl 			ent->cache_vers == msg->rm_call.cb_vers &&
    602       1.1      fvdl 			ent->cache_prog == msg->rm_call.cb_prog &&
    603       1.1      fvdl 			ent->cache_addr.len == xprt->xp_rtaddr.len &&
    604       1.1      fvdl 			(memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf,
    605       1.1      fvdl 				xprt->xp_rtaddr.len) == 0)) {
    606       1.1      fvdl #ifdef RPC_CACHE_DEBUG
    607       1.1      fvdl 			if (nconf = getnetconfigent(xprt->xp_netid)) {
    608       1.1      fvdl 				uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
    609       1.1      fvdl 				freenetconfigent(nconf);
    610       1.1      fvdl 				printf(
    611       1.1      fvdl 	"cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
    612       1.1      fvdl 					su->su_xid, msg->rm_call.cb_prog,
    613       1.1      fvdl 					msg->rm_call.cb_vers,
    614       1.1      fvdl 					msg->rm_call.cb_proc, uaddr);
    615       1.1      fvdl 				free(uaddr);
    616       1.1      fvdl 			}
    617       1.1      fvdl #endif
    618       1.1      fvdl 			*replyp = ent->cache_reply;
    619       1.1      fvdl 			*replylenp = ent->cache_replylen;
    620       1.1      fvdl 			mutex_unlock(&dupreq_lock);
    621       1.1      fvdl 			return (1);
    622       1.1      fvdl 		}
    623       1.1      fvdl 	}
    624       1.1      fvdl 	/*
    625       1.1      fvdl 	 * Failed to find entry
    626       1.1      fvdl 	 * Remember a few things so we can do a set later
    627       1.1      fvdl 	 */
    628       1.1      fvdl 	uc->uc_proc = msg->rm_call.cb_proc;
    629       1.1      fvdl 	uc->uc_vers = msg->rm_call.cb_vers;
    630       1.1      fvdl 	uc->uc_prog = msg->rm_call.cb_prog;
    631       1.1      fvdl 	mutex_unlock(&dupreq_lock);
    632       1.1      fvdl 	return (0);
    633       1.1      fvdl }
    634