Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.12
      1 /*	$NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl 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.3 (Berkeley) 3/30/95
     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/nfsproto.h>
     64 #include <nfs/nfs.h>
     65 #include <nfs/nfsrvcache.h>
     66 #include <nfs/nqnfs.h>
     67 #include <nfs/nfs_var.h>
     68 
     69 extern struct nfsstats nfsstats;
     70 extern int nfsv2_procid[NFS_NPROCS];
     71 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
     72 
     73 #define	NFSRCHASH(xid) \
     74 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
     75 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
     76 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
     77 u_long nfsrvhash;
     78 
     79 #define TRUE	1
     80 #define	FALSE	0
     81 
     82 #define	NETFAMILY(rp) \
     83 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
     84 
     85 /*
     86  * Static array that defines which nfs rpc's are nonidempotent
     87  */
     88 int nonidempotent[NFS_NPROCS] = {
     89 	FALSE,
     90 	FALSE,
     91 	TRUE,
     92 	FALSE,
     93 	FALSE,
     94 	FALSE,
     95 	FALSE,
     96 	TRUE,
     97 	TRUE,
     98 	TRUE,
     99 	TRUE,
    100 	TRUE,
    101 	TRUE,
    102 	TRUE,
    103 	TRUE,
    104 	TRUE,
    105 	FALSE,
    106 	FALSE,
    107 	FALSE,
    108 	FALSE,
    109 	FALSE,
    110 	FALSE,
    111 	FALSE,
    112 	FALSE,
    113 	FALSE,
    114 	FALSE,
    115 };
    116 
    117 /* True iff the rpc reply is an nfs status ONLY! */
    118 static int nfsv2_repstat[NFS_NPROCS] = {
    119 	FALSE,
    120 	FALSE,
    121 	FALSE,
    122 	FALSE,
    123 	FALSE,
    124 	FALSE,
    125 	FALSE,
    126 	FALSE,
    127 	FALSE,
    128 	FALSE,
    129 	TRUE,
    130 	TRUE,
    131 	TRUE,
    132 	TRUE,
    133 	FALSE,
    134 	TRUE,
    135 	FALSE,
    136 	FALSE,
    137 };
    138 
    139 /*
    140  * Initialize the server request cache list
    141  */
    142 void
    143 nfsrv_initcache()
    144 {
    145 
    146 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
    147 	TAILQ_INIT(&nfsrvlruhead);
    148 }
    149 
    150 /*
    151  * Look for the request in the cache
    152  * If found then
    153  *    return action and optionally reply
    154  * else
    155  *    insert it in the cache
    156  *
    157  * The rules are as follows:
    158  * - if in progress, return DROP request
    159  * - if completed within DELAY of the current time, return DROP it
    160  * - if completed a longer time ago return REPLY if the reply was cached or
    161  *   return DOIT
    162  * Update/add new request at end of lru list
    163  */
    164 int
    165 nfsrv_getcache(nd, slp, repp)
    166 	register struct nfsrv_descript *nd;
    167 	struct nfssvc_sock *slp;
    168 	struct mbuf **repp;
    169 {
    170 	register struct nfsrvcache *rp;
    171 	struct mbuf *mb;
    172 	struct sockaddr_in *saddr;
    173 	caddr_t bpos;
    174 	int ret;
    175 
    176 	/*
    177 	 * Don't cache recent requests for reliable transport protocols.
    178 	 * (Maybe we should for the case of a reconnect, but..)
    179 	 */
    180 	if (!nd->nd_nam2)
    181 		return (RC_DOIT);
    182 loop:
    183 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
    184 	    rp = rp->rc_hash.le_next) {
    185 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    186 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
    187 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    188 				rp->rc_flag |= RC_WANTED;
    189 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    190 				goto loop;
    191 			}
    192 			rp->rc_flag |= RC_LOCKED;
    193 			/* If not at end of LRU chain, move it there */
    194 			if (rp->rc_lru.tqe_next) {
    195 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    196 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
    197 			}
    198 			if (rp->rc_state == RC_UNUSED)
    199 				panic("nfsrv cache");
    200 			if (rp->rc_state == RC_INPROG) {
    201 				nfsstats.srvcache_inproghits++;
    202 				ret = RC_DROPIT;
    203 			} else if (rp->rc_flag & RC_REPSTATUS) {
    204 				nfsstats.srvcache_nonidemdonehits++;
    205 				nfs_rephead(0, nd, slp, rp->rc_status,
    206 				   0, (u_quad_t *)0, repp, &mb, &bpos);
    207 				ret = RC_REPLY;
    208 			} else if (rp->rc_flag & RC_REPMBUF) {
    209 				nfsstats.srvcache_nonidemdonehits++;
    210 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
    211 						M_WAIT);
    212 				ret = RC_REPLY;
    213 			} else {
    214 				nfsstats.srvcache_idemdonehits++;
    215 				rp->rc_state = RC_INPROG;
    216 				ret = RC_DOIT;
    217 			}
    218 			rp->rc_flag &= ~RC_LOCKED;
    219 			if (rp->rc_flag & RC_WANTED) {
    220 				rp->rc_flag &= ~RC_WANTED;
    221 				wakeup((caddr_t)rp);
    222 			}
    223 			return (ret);
    224 		}
    225 	}
    226 	nfsstats.srvcache_misses++;
    227 	if (numnfsrvcache < desirednfsrvcache) {
    228 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
    229 		    M_NFSD, M_WAITOK);
    230 		bzero((char *)rp, sizeof *rp);
    231 		numnfsrvcache++;
    232 		rp->rc_flag = RC_LOCKED;
    233 	} else {
    234 		rp = nfsrvlruhead.tqh_first;
    235 		while ((rp->rc_flag & RC_LOCKED) != 0) {
    236 			rp->rc_flag |= RC_WANTED;
    237 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    238 			rp = nfsrvlruhead.tqh_first;
    239 		}
    240 		rp->rc_flag |= RC_LOCKED;
    241 		LIST_REMOVE(rp, rc_hash);
    242 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    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 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
    250 	rp->rc_state = RC_INPROG;
    251 	rp->rc_xid = nd->nd_retxid;
    252 	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
    253 	switch (saddr->sin_family) {
    254 	case AF_INET:
    255 		rp->rc_flag |= RC_INETADDR;
    256 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
    257 		break;
    258 	case AF_ISO:
    259 	default:
    260 		rp->rc_flag |= RC_NAM;
    261 		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
    262 		break;
    263 	};
    264 	rp->rc_proc = nd->nd_procnum;
    265 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
    266 	rp->rc_flag &= ~RC_LOCKED;
    267 	if (rp->rc_flag & RC_WANTED) {
    268 		rp->rc_flag &= ~RC_WANTED;
    269 		wakeup((caddr_t)rp);
    270 	}
    271 	return (RC_DOIT);
    272 }
    273 
    274 /*
    275  * Update a request cache entry after the rpc has been done
    276  */
    277 void
    278 nfsrv_updatecache(nd, repvalid, repmbuf)
    279 	register struct nfsrv_descript *nd;
    280 	int repvalid;
    281 	struct mbuf *repmbuf;
    282 {
    283 	register struct nfsrvcache *rp;
    284 
    285 	if (!nd->nd_nam2)
    286 		return;
    287 loop:
    288 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
    289 	    rp = rp->rc_hash.le_next) {
    290 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    291 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
    292 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    293 				rp->rc_flag |= RC_WANTED;
    294 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    295 				goto loop;
    296 			}
    297 			rp->rc_flag |= RC_LOCKED;
    298 			rp->rc_state = RC_DONE;
    299 			/*
    300 			 * If we have a valid reply update status and save
    301 			 * the reply for non-idempotent rpc's.
    302 			 */
    303 			if (repvalid && nonidempotent[nd->nd_procnum]) {
    304 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
    305 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
    306 					rp->rc_status = nd->nd_repstat;
    307 					rp->rc_flag |= RC_REPSTATUS;
    308 				} else {
    309 					rp->rc_reply = m_copym(repmbuf,
    310 						0, M_COPYALL, M_WAIT);
    311 					rp->rc_flag |= RC_REPMBUF;
    312 				}
    313 			}
    314 			rp->rc_flag &= ~RC_LOCKED;
    315 			if (rp->rc_flag & RC_WANTED) {
    316 				rp->rc_flag &= ~RC_WANTED;
    317 				wakeup((caddr_t)rp);
    318 			}
    319 			return;
    320 		}
    321 	}
    322 }
    323 
    324 /*
    325  * Clean out the cache. Called when the last nfsd terminates.
    326  */
    327 void
    328 nfsrv_cleancache()
    329 {
    330 	register struct nfsrvcache *rp, *nextrp;
    331 
    332 	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
    333 		nextrp = rp->rc_lru.tqe_next;
    334 		LIST_REMOVE(rp, rc_hash);
    335 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    336 		free(rp, M_NFSD);
    337 	}
    338 	numnfsrvcache = 0;
    339 }
    340