Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.8
      1 /*	$NetBSD: nfs_srvcache.c,v 1.8 1994/06/29 06:42:19 cgd Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1989, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Rick Macklem at The University of Guelph.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  *
     38  *	@(#)nfs_srvcache.c	8.1 (Berkeley) 6/10/93
     39  */
     40 
     41 /*
     42  * Reference: Chet Juszczak, "Improving the Performance and Correctness
     43  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
     44  *		pages 53-63. San Diego, February 1989.
     45  */
     46 #include <sys/param.h>
     47 #include <sys/vnode.h>
     48 #include <sys/mount.h>
     49 #include <sys/kernel.h>
     50 #include <sys/systm.h>
     51 #include <sys/proc.h>
     52 #include <sys/mbuf.h>
     53 #include <sys/malloc.h>
     54 #include <sys/socket.h>
     55 #include <sys/socketvar.h>
     56 
     57 #include <netinet/in.h>
     58 #ifdef ISO
     59 #include <netiso/iso.h>
     60 #endif
     61 #include <nfs/nfsm_subs.h>
     62 #include <nfs/rpcv2.h>
     63 #include <nfs/nfsv2.h>
     64 #include <nfs/nfs.h>
     65 #include <nfs/nfsrvcache.h>
     66 #include <nfs/nqnfs.h>
     67 
     68 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
     69 
     70 #define	NFSRCHASH(xid)		(((xid) + ((xid) >> 24)) & rheadhash)
     71 static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
     72 static struct nfsrvcache **rheadhtbl;
     73 static u_long rheadhash;
     74 
     75 #define TRUE	1
     76 #define	FALSE	0
     77 
     78 #define	NETFAMILY(rp) \
     79 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
     80 
     81 /*
     82  * Static array that defines which nfs rpc's are nonidempotent
     83  */
     84 int nonidempotent[NFS_NPROCS] = {
     85 	FALSE,
     86 	FALSE,
     87 	TRUE,
     88 	FALSE,
     89 	FALSE,
     90 	FALSE,
     91 	FALSE,
     92 	FALSE,
     93 	TRUE,
     94 	TRUE,
     95 	TRUE,
     96 	TRUE,
     97 	TRUE,
     98 	TRUE,
     99 	TRUE,
    100 	TRUE,
    101 	FALSE,
    102 	FALSE,
    103 	FALSE,
    104 	FALSE,
    105 	FALSE,
    106 	FALSE,
    107 	FALSE,
    108 };
    109 
    110 /* True iff the rpc reply is an nfs status ONLY! */
    111 static int repliesstatus[NFS_NPROCS] = {
    112 	FALSE,
    113 	FALSE,
    114 	FALSE,
    115 	FALSE,
    116 	FALSE,
    117 	FALSE,
    118 	FALSE,
    119 	FALSE,
    120 	FALSE,
    121 	FALSE,
    122 	TRUE,
    123 	TRUE,
    124 	TRUE,
    125 	TRUE,
    126 	FALSE,
    127 	TRUE,
    128 	FALSE,
    129 	FALSE,
    130 	FALSE,
    131 	FALSE,
    132 	FALSE,
    133 	FALSE,
    134 	TRUE,
    135 };
    136 
    137 /*
    138  * Initialize the server request cache list
    139  */
    140 nfsrv_initcache()
    141 {
    142 
    143 	rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
    144 }
    145 
    146 /*
    147  * Look for the request in the cache
    148  * If found then
    149  *    return action and optionally reply
    150  * else
    151  *    insert it in the cache
    152  *
    153  * The rules are as follows:
    154  * - if in progress, return DROP request
    155  * - if completed within DELAY of the current time, return DROP it
    156  * - if completed a longer time ago return REPLY if the reply was cached or
    157  *   return DOIT
    158  * Update/add new request at end of lru list
    159  */
    160 nfsrv_getcache(nam, nd, repp)
    161 	struct mbuf *nam;
    162 	register struct nfsd *nd;
    163 	struct mbuf **repp;
    164 {
    165 	register struct nfsrvcache *rp, *rq, **rpp;
    166 	struct mbuf *mb;
    167 	struct sockaddr_in *saddr;
    168 	caddr_t bpos;
    169 	int ret;
    170 
    171 	if (nd->nd_nqlflag != NQL_NOVAL)
    172 		return (RC_DOIT);
    173 	rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
    174 loop:
    175 	for (rp = *rpp; rp; rp = rp->rc_forw) {
    176 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    177 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
    178 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    179 				rp->rc_flag |= RC_WANTED;
    180 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    181 				goto loop;
    182 			}
    183 			rp->rc_flag |= RC_LOCKED;
    184 			/* If not at end of LRU chain, move it there */
    185 			if (rp->rc_next) {
    186 				/* remove from LRU chain */
    187 				*rp->rc_prev = rp->rc_next;
    188 				rp->rc_next->rc_prev = rp->rc_prev;
    189 				/* and replace at end of it */
    190 				rp->rc_next = NULL;
    191 				rp->rc_prev = nfsrvlrutail;
    192 				*nfsrvlrutail = rp;
    193 				nfsrvlrutail = &rp->rc_next;
    194 			}
    195 			if (rp->rc_state == RC_UNUSED)
    196 				panic("nfsrv cache");
    197 			if (rp->rc_state == RC_INPROG) {
    198 				nfsstats.srvcache_inproghits++;
    199 				ret = RC_DROPIT;
    200 			} else if (rp->rc_flag & RC_REPSTATUS) {
    201 				nfsstats.srvcache_nonidemdonehits++;
    202 				nfs_rephead(0, nd, rp->rc_status,
    203 				   0, (u_quad_t *)0, repp, &mb, &bpos);
    204 				ret = RC_REPLY;
    205 			} else if (rp->rc_flag & RC_REPMBUF) {
    206 				nfsstats.srvcache_nonidemdonehits++;
    207 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
    208 						M_WAIT);
    209 				ret = RC_REPLY;
    210 			} else {
    211 				nfsstats.srvcache_idemdonehits++;
    212 				rp->rc_state = RC_INPROG;
    213 				ret = RC_DOIT;
    214 			}
    215 			rp->rc_flag &= ~RC_LOCKED;
    216 			if (rp->rc_flag & RC_WANTED) {
    217 				rp->rc_flag &= ~RC_WANTED;
    218 				wakeup((caddr_t)rp);
    219 			}
    220 			return (ret);
    221 		}
    222 	}
    223 	nfsstats.srvcache_misses++;
    224 	if (numnfsrvcache < desirednfsrvcache) {
    225 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
    226 		    M_NFSD, M_WAITOK);
    227 		bzero((char *)rp, sizeof *rp);
    228 		numnfsrvcache++;
    229 		rp->rc_flag = RC_LOCKED;
    230 	} else {
    231 		rp = nfsrvlruhead;
    232 		while ((rp->rc_flag & RC_LOCKED) != 0) {
    233 			rp->rc_flag |= RC_WANTED;
    234 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    235 			rp = nfsrvlruhead;
    236 		}
    237 		rp->rc_flag |= RC_LOCKED;
    238 		/* remove from hash chain */
    239 		if (rq = rp->rc_forw)
    240 			rq->rc_back = rp->rc_back;
    241 		*rp->rc_back = rq;
    242 		/* remove from LRU chain */
    243 		*rp->rc_prev = rp->rc_next;
    244 		rp->rc_next->rc_prev = rp->rc_prev;
    245 		if (rp->rc_flag & RC_REPMBUF)
    246 			m_freem(rp->rc_reply);
    247 		if (rp->rc_flag & RC_NAM)
    248 			MFREE(rp->rc_nam, mb);
    249 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
    250 	}
    251 	/* place at end of LRU list */
    252 	rp->rc_next = NULL;
    253 	rp->rc_prev = nfsrvlrutail;
    254 	*nfsrvlrutail = rp;
    255 	nfsrvlrutail = &rp->rc_next;
    256 	rp->rc_state = RC_INPROG;
    257 	rp->rc_xid = nd->nd_retxid;
    258 	saddr = mtod(nam, struct sockaddr_in *);
    259 	switch (saddr->sin_family) {
    260 	case AF_INET:
    261 		rp->rc_flag |= RC_INETADDR;
    262 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
    263 		break;
    264 	case AF_ISO:
    265 	default:
    266 		rp->rc_flag |= RC_NAM;
    267 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
    268 		break;
    269 	};
    270 	rp->rc_proc = nd->nd_procnum;
    271 	/* insert into hash chain */
    272 	if (rq = *rpp)
    273 		rq->rc_back = &rp->rc_forw;
    274 	rp->rc_forw = rq;
    275 	rp->rc_back = rpp;
    276 	*rpp = rp;
    277 	rp->rc_flag &= ~RC_LOCKED;
    278 	if (rp->rc_flag & RC_WANTED) {
    279 		rp->rc_flag &= ~RC_WANTED;
    280 		wakeup((caddr_t)rp);
    281 	}
    282 	return (RC_DOIT);
    283 }
    284 
    285 /*
    286  * Update a request cache entry after the rpc has been done
    287  */
    288 void
    289 nfsrv_updatecache(nam, nd, repvalid, repmbuf)
    290 	struct mbuf *nam;
    291 	register struct nfsd *nd;
    292 	int repvalid;
    293 	struct mbuf *repmbuf;
    294 {
    295 	register struct nfsrvcache *rp;
    296 
    297 	if (nd->nd_nqlflag != NQL_NOVAL)
    298 		return;
    299 loop:
    300 	for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
    301 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    302 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
    303 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    304 				rp->rc_flag |= RC_WANTED;
    305 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    306 				goto loop;
    307 			}
    308 			rp->rc_flag |= RC_LOCKED;
    309 			rp->rc_state = RC_DONE;
    310 			/*
    311 			 * If we have a valid reply update status and save
    312 			 * the reply for non-idempotent rpc's.
    313 			 */
    314 			if (repvalid && nonidempotent[nd->nd_procnum]) {
    315 				if (repliesstatus[nd->nd_procnum]) {
    316 					rp->rc_status = nd->nd_repstat;
    317 					rp->rc_flag |= RC_REPSTATUS;
    318 				} else {
    319 					rp->rc_reply = m_copym(repmbuf,
    320 						0, M_COPYALL, M_WAIT);
    321 					rp->rc_flag |= RC_REPMBUF;
    322 				}
    323 			}
    324 			rp->rc_flag &= ~RC_LOCKED;
    325 			if (rp->rc_flag & RC_WANTED) {
    326 				rp->rc_flag &= ~RC_WANTED;
    327 				wakeup((caddr_t)rp);
    328 			}
    329 			return;
    330 		}
    331 	}
    332 }
    333 
    334 /*
    335  * Clean out the cache. Called when the last nfsd terminates.
    336  */
    337 void
    338 nfsrv_cleancache()
    339 {
    340 	register struct nfsrvcache *rp, *nextrp;
    341 
    342 	for (rp = nfsrvlruhead; rp; rp = nextrp) {
    343 		nextrp = rp->rc_next;
    344 		free(rp, M_NFSD);
    345 	}
    346 	bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
    347 	nfsrvlruhead = NULL;
    348 	nfsrvlrutail = &nfsrvlruhead;
    349 	numnfsrvcache = 0;
    350 }
    351