Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.1.1.2
      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  *	@(#)nfs_srvcache.c	8.1 (Berkeley) 6/10/93
     37  */
     38 
     39 /*
     40  * Reference: Chet Juszczak, "Improving the Performance and Correctness
     41  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
     42  *		pages 53-63. San Diego, February 1989.
     43  */
     44 #include <sys/param.h>
     45 #include <sys/vnode.h>
     46 #include <sys/mount.h>
     47 #include <sys/kernel.h>
     48 #include <sys/systm.h>
     49 #include <sys/proc.h>
     50 #include <sys/mbuf.h>
     51 #include <sys/malloc.h>
     52 #include <sys/socket.h>
     53 #include <sys/socketvar.h>
     54 
     55 #include <netinet/in.h>
     56 #ifdef ISO
     57 #include <netiso/iso.h>
     58 #endif
     59 #include <nfs/nfsm_subs.h>
     60 #include <nfs/rpcv2.h>
     61 #include <nfs/nfsv2.h>
     62 #include <nfs/nfs.h>
     63 #include <nfs/nfsrvcache.h>
     64 #include <nfs/nqnfs.h>
     65 
     66 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
     67 
     68 #define	NFSRCHASH(xid)		(((xid) + ((xid) >> 24)) & rheadhash)
     69 static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
     70 static struct nfsrvcache **rheadhtbl;
     71 static u_long rheadhash;
     72 
     73 #define TRUE	1
     74 #define	FALSE	0
     75 
     76 #define	NETFAMILY(rp) \
     77 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
     78 
     79 /*
     80  * Static array that defines which nfs rpc's are nonidempotent
     81  */
     82 int nonidempotent[NFS_NPROCS] = {
     83 	FALSE,
     84 	FALSE,
     85 	TRUE,
     86 	FALSE,
     87 	FALSE,
     88 	FALSE,
     89 	FALSE,
     90 	FALSE,
     91 	TRUE,
     92 	TRUE,
     93 	TRUE,
     94 	TRUE,
     95 	TRUE,
     96 	TRUE,
     97 	TRUE,
     98 	TRUE,
     99 	FALSE,
    100 	FALSE,
    101 	FALSE,
    102 	FALSE,
    103 	FALSE,
    104 	FALSE,
    105 	FALSE,
    106 };
    107 
    108 /* True iff the rpc reply is an nfs status ONLY! */
    109 static int repliesstatus[NFS_NPROCS] = {
    110 	FALSE,
    111 	FALSE,
    112 	FALSE,
    113 	FALSE,
    114 	FALSE,
    115 	FALSE,
    116 	FALSE,
    117 	FALSE,
    118 	FALSE,
    119 	FALSE,
    120 	TRUE,
    121 	TRUE,
    122 	TRUE,
    123 	TRUE,
    124 	FALSE,
    125 	TRUE,
    126 	FALSE,
    127 	FALSE,
    128 	FALSE,
    129 	FALSE,
    130 	FALSE,
    131 	FALSE,
    132 	TRUE,
    133 };
    134 
    135 /*
    136  * Initialize the server request cache list
    137  */
    138 nfsrv_initcache()
    139 {
    140 
    141 	rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
    142 }
    143 
    144 /*
    145  * Look for the request in the cache
    146  * If found then
    147  *    return action and optionally reply
    148  * else
    149  *    insert it in the cache
    150  *
    151  * The rules are as follows:
    152  * - if in progress, return DROP request
    153  * - if completed within DELAY of the current time, return DROP it
    154  * - if completed a longer time ago return REPLY if the reply was cached or
    155  *   return DOIT
    156  * Update/add new request at end of lru list
    157  */
    158 nfsrv_getcache(nam, nd, repp)
    159 	struct mbuf *nam;
    160 	register struct nfsd *nd;
    161 	struct mbuf **repp;
    162 {
    163 	register struct nfsrvcache *rp, *rq, **rpp;
    164 	struct mbuf *mb;
    165 	struct sockaddr_in *saddr;
    166 	caddr_t bpos;
    167 	int ret;
    168 
    169 	if (nd->nd_nqlflag != NQL_NOVAL)
    170 		return (RC_DOIT);
    171 	rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
    172 loop:
    173 	for (rp = *rpp; rp; rp = rp->rc_forw) {
    174 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    175 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
    176 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    177 				rp->rc_flag |= RC_WANTED;
    178 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    179 				goto loop;
    180 			}
    181 			rp->rc_flag |= RC_LOCKED;
    182 			/* If not at end of LRU chain, move it there */
    183 			if (rp->rc_next) {
    184 				/* remove from LRU chain */
    185 				*rp->rc_prev = rp->rc_next;
    186 				rp->rc_next->rc_prev = rp->rc_prev;
    187 				/* and replace at end of it */
    188 				rp->rc_next = NULL;
    189 				rp->rc_prev = nfsrvlrutail;
    190 				*nfsrvlrutail = rp;
    191 				nfsrvlrutail = &rp->rc_next;
    192 			}
    193 			if (rp->rc_state == RC_UNUSED)
    194 				panic("nfsrv cache");
    195 			if (rp->rc_state == RC_INPROG) {
    196 				nfsstats.srvcache_inproghits++;
    197 				ret = RC_DROPIT;
    198 			} else if (rp->rc_flag & RC_REPSTATUS) {
    199 				nfsstats.srvcache_nonidemdonehits++;
    200 				nfs_rephead(0, nd, rp->rc_status,
    201 				   0, (u_quad_t *)0, repp, &mb, &bpos);
    202 				ret = RC_REPLY;
    203 			} else if (rp->rc_flag & RC_REPMBUF) {
    204 				nfsstats.srvcache_nonidemdonehits++;
    205 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
    206 						M_WAIT);
    207 				ret = RC_REPLY;
    208 			} else {
    209 				nfsstats.srvcache_idemdonehits++;
    210 				rp->rc_state = RC_INPROG;
    211 				ret = RC_DOIT;
    212 			}
    213 			rp->rc_flag &= ~RC_LOCKED;
    214 			if (rp->rc_flag & RC_WANTED) {
    215 				rp->rc_flag &= ~RC_WANTED;
    216 				wakeup((caddr_t)rp);
    217 			}
    218 			return (ret);
    219 		}
    220 	}
    221 	nfsstats.srvcache_misses++;
    222 	if (numnfsrvcache < desirednfsrvcache) {
    223 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
    224 		    M_NFSD, M_WAITOK);
    225 		bzero((char *)rp, sizeof *rp);
    226 		numnfsrvcache++;
    227 		rp->rc_flag = RC_LOCKED;
    228 	} else {
    229 		rp = nfsrvlruhead;
    230 		while ((rp->rc_flag & RC_LOCKED) != 0) {
    231 			rp->rc_flag |= RC_WANTED;
    232 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    233 			rp = nfsrvlruhead;
    234 		}
    235 		rp->rc_flag |= RC_LOCKED;
    236 		/* remove from hash chain */
    237 		if (rq = rp->rc_forw)
    238 			rq->rc_back = rp->rc_back;
    239 		*rp->rc_back = rq;
    240 		/* remove from LRU chain */
    241 		*rp->rc_prev = rp->rc_next;
    242 		rp->rc_next->rc_prev = rp->rc_prev;
    243 		if (rp->rc_flag & RC_REPMBUF)
    244 			m_freem(rp->rc_reply);
    245 		if (rp->rc_flag & RC_NAM)
    246 			MFREE(rp->rc_nam, mb);
    247 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
    248 	}
    249 	/* place at end of LRU list */
    250 	rp->rc_next = NULL;
    251 	rp->rc_prev = nfsrvlrutail;
    252 	*nfsrvlrutail = rp;
    253 	nfsrvlrutail = &rp->rc_next;
    254 	rp->rc_state = RC_INPROG;
    255 	rp->rc_xid = nd->nd_retxid;
    256 	saddr = mtod(nam, struct sockaddr_in *);
    257 	switch (saddr->sin_family) {
    258 	case AF_INET:
    259 		rp->rc_flag |= RC_INETADDR;
    260 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
    261 		break;
    262 	case AF_ISO:
    263 	default:
    264 		rp->rc_flag |= RC_NAM;
    265 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
    266 		break;
    267 	};
    268 	rp->rc_proc = nd->nd_procnum;
    269 	/* insert into hash chain */
    270 	if (rq = *rpp)
    271 		rq->rc_back = &rp->rc_forw;
    272 	rp->rc_forw = rq;
    273 	rp->rc_back = rpp;
    274 	*rpp = rp;
    275 	rp->rc_flag &= ~RC_LOCKED;
    276 	if (rp->rc_flag & RC_WANTED) {
    277 		rp->rc_flag &= ~RC_WANTED;
    278 		wakeup((caddr_t)rp);
    279 	}
    280 	return (RC_DOIT);
    281 }
    282 
    283 /*
    284  * Update a request cache entry after the rpc has been done
    285  */
    286 void
    287 nfsrv_updatecache(nam, nd, repvalid, repmbuf)
    288 	struct mbuf *nam;
    289 	register struct nfsd *nd;
    290 	int repvalid;
    291 	struct mbuf *repmbuf;
    292 {
    293 	register struct nfsrvcache *rp;
    294 
    295 	if (nd->nd_nqlflag != NQL_NOVAL)
    296 		return;
    297 loop:
    298 	for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
    299 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    300 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
    301 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    302 				rp->rc_flag |= RC_WANTED;
    303 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    304 				goto loop;
    305 			}
    306 			rp->rc_flag |= RC_LOCKED;
    307 			rp->rc_state = RC_DONE;
    308 			/*
    309 			 * If we have a valid reply update status and save
    310 			 * the reply for non-idempotent rpc's.
    311 			 */
    312 			if (repvalid && nonidempotent[nd->nd_procnum]) {
    313 				if (repliesstatus[nd->nd_procnum]) {
    314 					rp->rc_status = nd->nd_repstat;
    315 					rp->rc_flag |= RC_REPSTATUS;
    316 				} else {
    317 					rp->rc_reply = m_copym(repmbuf,
    318 						0, M_COPYALL, M_WAIT);
    319 					rp->rc_flag |= RC_REPMBUF;
    320 				}
    321 			}
    322 			rp->rc_flag &= ~RC_LOCKED;
    323 			if (rp->rc_flag & RC_WANTED) {
    324 				rp->rc_flag &= ~RC_WANTED;
    325 				wakeup((caddr_t)rp);
    326 			}
    327 			return;
    328 		}
    329 	}
    330 }
    331 
    332 /*
    333  * Clean out the cache. Called when the last nfsd terminates.
    334  */
    335 void
    336 nfsrv_cleancache()
    337 {
    338 	register struct nfsrvcache *rp, *nextrp;
    339 
    340 	for (rp = nfsrvlruhead; rp; rp = nextrp) {
    341 		nextrp = rp->rc_next;
    342 		free(rp, M_NFSD);
    343 	}
    344 	bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
    345 	nfsrvlruhead = NULL;
    346 	nfsrvlrutail = &nfsrvlruhead;
    347 	numnfsrvcache = 0;
    348 }
    349