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