Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.17
      1 /*	$NetBSD: nfs_srvcache.c,v 1.17 2000/11/08 14:28:15 ad 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 "opt_iso.h"
     47 
     48 #include <sys/param.h>
     49 #include <sys/vnode.h>
     50 #include <sys/mount.h>
     51 #include <sys/kernel.h>
     52 #include <sys/systm.h>
     53 #include <sys/proc.h>
     54 #include <sys/mbuf.h>
     55 #include <sys/malloc.h>
     56 #include <sys/socket.h>
     57 #include <sys/socketvar.h>
     58 
     59 #include <netinet/in.h>
     60 #ifdef ISO
     61 #include <netiso/iso.h>
     62 #endif
     63 #include <nfs/nfsm_subs.h>
     64 #include <nfs/rpcv2.h>
     65 #include <nfs/nfsproto.h>
     66 #include <nfs/nfs.h>
     67 #include <nfs/nfsrvcache.h>
     68 #include <nfs/nqnfs.h>
     69 #include <nfs/nfs_var.h>
     70 
     71 extern struct nfsstats nfsstats;
     72 extern int nfsv2_procid[NFS_NPROCS];
     73 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
     74 
     75 #define	NFSRCHASH(xid) \
     76 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
     77 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
     78 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
     79 u_long nfsrvhash;
     80 
     81 #define TRUE	1
     82 #define	FALSE	0
     83 
     84 #define	NETFAMILY(rp) \
     85 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
     86 
     87 /*
     88  * Static array that defines which nfs rpc's are nonidempotent
     89  */
     90 int nonidempotent[NFS_NPROCS] = {
     91 	FALSE,
     92 	FALSE,
     93 	TRUE,
     94 	FALSE,
     95 	FALSE,
     96 	FALSE,
     97 	FALSE,
     98 	TRUE,
     99 	TRUE,
    100 	TRUE,
    101 	TRUE,
    102 	TRUE,
    103 	TRUE,
    104 	TRUE,
    105 	TRUE,
    106 	TRUE,
    107 	FALSE,
    108 	FALSE,
    109 	FALSE,
    110 	FALSE,
    111 	FALSE,
    112 	FALSE,
    113 	FALSE,
    114 	FALSE,
    115 	FALSE,
    116 	FALSE,
    117 };
    118 
    119 /* True iff the rpc reply is an nfs status ONLY! */
    120 static int nfsv2_repstat[NFS_NPROCS] = {
    121 	FALSE,
    122 	FALSE,
    123 	FALSE,
    124 	FALSE,
    125 	FALSE,
    126 	FALSE,
    127 	FALSE,
    128 	FALSE,
    129 	FALSE,
    130 	FALSE,
    131 	TRUE,
    132 	TRUE,
    133 	TRUE,
    134 	TRUE,
    135 	FALSE,
    136 	TRUE,
    137 	FALSE,
    138 	FALSE,
    139 };
    140 
    141 /*
    142  * Initialize the server request cache list
    143  */
    144 void
    145 nfsrv_initcache()
    146 {
    147 
    148 	nfsrvhashtbl = hashinit(desirednfsrvcache, HASH_LIST, M_NFSD,
    149 	    M_WAITOK, &nfsrvhash);
    150 	TAILQ_INIT(&nfsrvlruhead);
    151 }
    152 
    153 /*
    154  * Look for the request in the cache
    155  * If found then
    156  *    return action and optionally reply
    157  * else
    158  *    insert it in the cache
    159  *
    160  * The rules are as follows:
    161  * - if in progress, return DROP request
    162  * - if completed within DELAY of the current time, return DROP it
    163  * - if completed a longer time ago return REPLY if the reply was cached or
    164  *   return DOIT
    165  * Update/add new request at end of lru list
    166  */
    167 int
    168 nfsrv_getcache(nd, slp, repp)
    169 	struct nfsrv_descript *nd;
    170 	struct nfssvc_sock *slp;
    171 	struct mbuf **repp;
    172 {
    173 	struct nfsrvcache *rp;
    174 	struct mbuf *mb;
    175 	struct sockaddr_in *saddr;
    176 	caddr_t bpos;
    177 	int ret;
    178 
    179 	/*
    180 	 * Don't cache recent requests for reliable transport protocols.
    181 	 * (Maybe we should for the case of a reconnect, but..)
    182 	 */
    183 	if (!nd->nd_nam2)
    184 		return (RC_DOIT);
    185 loop:
    186 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
    187 	    rp = rp->rc_hash.le_next) {
    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 (rp->rc_lru.tqe_next) {
    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 = nfsrvlruhead.tqh_first;
    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 = nfsrvlruhead.tqh_first;
    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 			MFREE(rp->rc_nam, mb);
    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 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
    292 	    rp = rp->rc_hash.le_next) {
    293 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
    294 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
    295 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    296 				rp->rc_flag |= RC_WANTED;
    297 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    298 				goto loop;
    299 			}
    300 			rp->rc_flag |= RC_LOCKED;
    301 			rp->rc_state = RC_DONE;
    302 			/*
    303 			 * If we have a valid reply update status and save
    304 			 * the reply for non-idempotent rpc's.
    305 			 */
    306 			if (repvalid && nonidempotent[nd->nd_procnum]) {
    307 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
    308 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
    309 					rp->rc_status = nd->nd_repstat;
    310 					rp->rc_flag |= RC_REPSTATUS;
    311 				} else {
    312 					rp->rc_reply = m_copym(repmbuf,
    313 						0, M_COPYALL, M_WAIT);
    314 					rp->rc_flag |= RC_REPMBUF;
    315 				}
    316 			}
    317 			rp->rc_flag &= ~RC_LOCKED;
    318 			if (rp->rc_flag & RC_WANTED) {
    319 				rp->rc_flag &= ~RC_WANTED;
    320 				wakeup((caddr_t)rp);
    321 			}
    322 			return;
    323 		}
    324 	}
    325 }
    326 
    327 /*
    328  * Clean out the cache. Called when the last nfsd terminates.
    329  */
    330 void
    331 nfsrv_cleancache()
    332 {
    333 	struct nfsrvcache *rp, *nextrp;
    334 
    335 	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
    336 		nextrp = rp->rc_lru.tqe_next;
    337 		LIST_REMOVE(rp, rc_hash);
    338 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
    339 		free(rp, M_NFSD);
    340 	}
    341 	numnfsrvcache = 0;
    342 }
    343