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