Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.23
      1 /*	$NetBSD: nfs_srvcache.c,v 1.23 2003/05/21 13:50:54 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.23 2003/05/21 13:50:54 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 && nd->nd_procnum == rp->rc_proc &&
    189 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
    190 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    191 				rp->rc_flag |= RC_WANTED;
    192 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    193 				goto loop;
    194 			}
    195 			rp->rc_flag |= RC_LOCKED;
    196 			/* If not at end of LRU chain, move it there */
    197 			if (TAILQ_NEXT(rp, rc_lru)) {
    198 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    199 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
    200 			}
    201 			if (rp->rc_state == RC_UNUSED)
    202 				panic("nfsrv cache");
    203 			if (rp->rc_state == RC_INPROG) {
    204 				nfsstats.srvcache_inproghits++;
    205 				ret = RC_DROPIT;
    206 			} else if (rp->rc_flag & RC_REPSTATUS) {
    207 				nfsstats.srvcache_nonidemdonehits++;
    208 				nfs_rephead(0, nd, slp, rp->rc_status,
    209 				   0, (u_quad_t *)0, repp, &mb, &bpos);
    210 				ret = RC_REPLY;
    211 			} else if (rp->rc_flag & RC_REPMBUF) {
    212 				nfsstats.srvcache_nonidemdonehits++;
    213 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
    214 						M_WAIT);
    215 				ret = RC_REPLY;
    216 			} else {
    217 				nfsstats.srvcache_idemdonehits++;
    218 				rp->rc_state = RC_INPROG;
    219 				ret = RC_DOIT;
    220 			}
    221 			rp->rc_flag &= ~RC_LOCKED;
    222 			if (rp->rc_flag & RC_WANTED) {
    223 				rp->rc_flag &= ~RC_WANTED;
    224 				wakeup((caddr_t)rp);
    225 			}
    226 			return (ret);
    227 		}
    228 	}
    229 	nfsstats.srvcache_misses++;
    230 	if (numnfsrvcache < desirednfsrvcache) {
    231 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
    232 		    M_NFSD, M_WAITOK);
    233 		memset((char *)rp, 0, sizeof *rp);
    234 		numnfsrvcache++;
    235 		rp->rc_flag = RC_LOCKED;
    236 	} else {
    237 		rp = TAILQ_FIRST(&nfsrvlruhead);
    238 		while ((rp->rc_flag & RC_LOCKED) != 0) {
    239 			rp->rc_flag |= RC_WANTED;
    240 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    241 			rp = TAILQ_FIRST(&nfsrvlruhead);
    242 		}
    243 		rp->rc_flag |= RC_LOCKED;
    244 		LIST_REMOVE(rp, rc_hash);
    245 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    246 		if (rp->rc_flag & RC_REPMBUF)
    247 			m_freem(rp->rc_reply);
    248 		if (rp->rc_flag & RC_NAM)
    249 			(void) m_free(rp->rc_nam);
    250 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
    251 	}
    252 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
    253 	rp->rc_state = RC_INPROG;
    254 	rp->rc_xid = nd->nd_retxid;
    255 	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
    256 	switch (saddr->sin_family) {
    257 	case AF_INET:
    258 		rp->rc_flag |= RC_INETADDR;
    259 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
    260 		break;
    261 	case AF_ISO:
    262 	default:
    263 		rp->rc_flag |= RC_NAM;
    264 		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
    265 		break;
    266 	};
    267 	rp->rc_proc = nd->nd_procnum;
    268 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
    269 	rp->rc_flag &= ~RC_LOCKED;
    270 	if (rp->rc_flag & RC_WANTED) {
    271 		rp->rc_flag &= ~RC_WANTED;
    272 		wakeup((caddr_t)rp);
    273 	}
    274 	return (RC_DOIT);
    275 }
    276 
    277 /*
    278  * Update a request cache entry after the rpc has been done
    279  */
    280 void
    281 nfsrv_updatecache(nd, repvalid, repmbuf)
    282 	struct nfsrv_descript *nd;
    283 	int repvalid;
    284 	struct mbuf *repmbuf;
    285 {
    286 	struct nfsrvcache *rp;
    287 
    288 	if (!nd->nd_nam2)
    289 		return;
    290 loop:
    291 	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
    292 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    293 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
    294 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    295 				rp->rc_flag |= RC_WANTED;
    296 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    297 				goto loop;
    298 			}
    299 			rp->rc_flag |= RC_LOCKED;
    300 			rp->rc_state = RC_DONE;
    301 			/*
    302 			 * If we have a valid reply update status and save
    303 			 * the reply for non-idempotent rpc's.
    304 			 */
    305 			if (repvalid && nonidempotent[nd->nd_procnum]) {
    306 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
    307 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
    308 					rp->rc_status = nd->nd_repstat;
    309 					rp->rc_flag |= RC_REPSTATUS;
    310 				} else {
    311 					rp->rc_reply = m_copym(repmbuf,
    312 						0, M_COPYALL, M_WAIT);
    313 					rp->rc_flag |= RC_REPMBUF;
    314 				}
    315 			}
    316 			rp->rc_flag &= ~RC_LOCKED;
    317 			if (rp->rc_flag & RC_WANTED) {
    318 				rp->rc_flag &= ~RC_WANTED;
    319 				wakeup((caddr_t)rp);
    320 			}
    321 			return;
    322 		}
    323 	}
    324 }
    325 
    326 /*
    327  * Clean out the cache. Called when the last nfsd terminates.
    328  */
    329 void
    330 nfsrv_cleancache()
    331 {
    332 	struct nfsrvcache *rp, *nextrp;
    333 
    334 	for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
    335 		nextrp = TAILQ_NEXT(rp, rc_lru);
    336 		LIST_REMOVE(rp, rc_hash);
    337 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    338 		free(rp, M_NFSD);
    339 	}
    340 	numnfsrvcache = 0;
    341 }
    342