Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.6
      1 /*
      2  * Copyright (c) 1989 The Regents of the University of California.
      3  * 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  *	from: @(#)nfs_srvcache.c	7.11 (Berkeley) 4/16/91
     37  *	$Id: nfs_srvcache.c,v 1.6 1993/12/18 00:45:20 mycroft Exp $
     38  */
     39 
     40 /*
     41  * Reference: Chet Juszczak, "Improving the Performance and Correctness
     42  *            of an NFS Server", in Proc. Winter 1989 USENIX Conference,
     43  *            pages 53-63. San Diego, February 1989.
     44  */
     45 
     46 #include <sys/param.h>
     47 #include <sys/namei.h>
     48 #include <sys/vnode.h>
     49 #include <sys/mount.h>
     50 #include <sys/kernel.h>
     51 #include <sys/systm.h>
     52 #include <sys/mbuf.h>
     53 #include <sys/socket.h>
     54 #include <sys/socketvar.h>
     55 
     56 #include <netinet/in.h>
     57 
     58 #include <nfs/nfsm_subs.h>
     59 #include <nfs/nfsv2.h>
     60 #include <nfs/nfsrvcache.h>
     61 #include <nfs/nfs.h>
     62 
     63 #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
     64 #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
     65 #else
     66 #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
     67 #endif
     68 
     69 extern int nonidempotent[NFS_NPROCS];
     70 
     71 union rhead {
     72 	union  rhead *rh_head[2];
     73 	struct nfsrvcache *rh_chain[2];
     74 } rhead[NFSRCHSZ];
     75 
     76 static struct nfsrvcache nfsrvcachehead;
     77 static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
     78 
     79 #define TRUE	1
     80 #define	FALSE	0
     81 
     82 
     83 /* True iff the rpc reply is an nfs status ONLY! */
     84 static int repliesstatus[NFS_NPROCS] = {
     85 	FALSE,
     86 	FALSE,
     87 	FALSE,
     88 	FALSE,
     89 	FALSE,
     90 	FALSE,
     91 	FALSE,
     92 	FALSE,
     93 	FALSE,
     94 	FALSE,
     95 	TRUE,
     96 	TRUE,
     97 	TRUE,
     98 	TRUE,
     99 	FALSE,
    100 	TRUE,
    101 	FALSE,
    102 	FALSE,
    103 };
    104 
    105 /*
    106  * Initialize the server request cache list
    107  */
    108 nfsrv_initcache()
    109 {
    110 	register int i;
    111 	register struct nfsrvcache *rp = nfsrvcache;
    112 	register struct nfsrvcache *hp = &nfsrvcachehead;
    113 	register union  rhead *rh = rhead;
    114 
    115 	for (i = NFSRCHSZ; --i >= 0; rh++) {
    116 		rh->rh_head[0] = rh;
    117 		rh->rh_head[1] = rh;
    118 	}
    119 	hp->rc_next = hp->rc_prev = hp;
    120 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
    121 		rp->rc_state = RC_UNUSED;
    122 		rp->rc_flag = 0;
    123 		rp->rc_forw = rp;
    124 		rp->rc_back = rp;
    125 		rp->rc_next = hp->rc_next;
    126 		hp->rc_next->rc_prev = rp;
    127 		rp->rc_prev = hp;
    128 		hp->rc_next = rp;
    129 		rp++;
    130 	}
    131 }
    132 
    133 /*
    134  * Look for the request in the cache
    135  * If found then
    136  *    return action and optionally reply
    137  * else
    138  *    insert it in the cache
    139  *
    140  * The rules are as follows:
    141  * - if in progress, return DROP request
    142  * - if completed within DELAY of the current time, return DROP it
    143  * - if completed a longer time ago return REPLY if the reply was cached or
    144  *   return DOIT
    145  * Update/add new request at end of lru list
    146  */
    147 nfsrv_getcache(nam, xid, proc, repp)
    148 	struct mbuf *nam;
    149 	u_long xid;
    150 	int proc;
    151 	struct mbuf **repp;
    152 {
    153 	register struct nfsrvcache *rp;
    154 	register union  rhead *rh;
    155 	struct mbuf *mb;
    156 	caddr_t bpos;
    157 	int ret;
    158 
    159 	rh = &rhead[NFSRCHASH(xid)];
    160 loop:
    161 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
    162 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
    163 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
    164 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    165 				rp->rc_flag |= RC_WANTED;
    166 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    167 				goto loop;
    168 			}
    169 			rp->rc_flag |= RC_LOCKED;
    170 			put_at_head(rp);
    171 			if (rp->rc_state == RC_UNUSED)
    172 				panic("nfsrv cache");
    173 			if (rp->rc_state == RC_INPROG ||
    174 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
    175 				nfsstats.srvcache_inproghits++;
    176 				ret = RC_DROPIT;
    177 			} else if (rp->rc_flag & RC_REPSTATUS) {
    178 				nfsstats.srvcache_idemdonehits++;
    179 				nfs_rephead(0, xid, rp->rc_status, repp, &mb,
    180 					&bpos);
    181 				rp->rc_timestamp = time.tv_sec;
    182 				ret = RC_REPLY;
    183 			} else if (rp->rc_flag & RC_REPMBUF) {
    184 				nfsstats.srvcache_idemdonehits++;
    185 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
    186 						M_WAIT);
    187 				rp->rc_timestamp = time.tv_sec;
    188 				ret = RC_REPLY;
    189 			} else {
    190 				nfsstats.srvcache_nonidemdonehits++;
    191 				rp->rc_state = RC_INPROG;
    192 				ret = RC_DOIT;
    193 			}
    194 			rp->rc_flag &= ~RC_LOCKED;
    195 			if (rp->rc_flag & RC_WANTED) {
    196 				rp->rc_flag &= ~RC_WANTED;
    197 				wakeup((caddr_t)rp);
    198 			}
    199 			return (ret);
    200 		}
    201 	}
    202 	nfsstats.srvcache_misses++;
    203 	rp = nfsrvcachehead.rc_prev;
    204 	while ((rp->rc_flag & RC_LOCKED) != 0) {
    205 		rp->rc_flag |= RC_WANTED;
    206 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    207 	}
    208 	remque(rp);
    209 	put_at_head(rp);
    210 	if (rp->rc_flag & RC_REPMBUF)
    211 		mb = rp->rc_reply;
    212 	else
    213 		mb = (struct mbuf *)0;
    214 	rp->rc_flag = 0;
    215 	rp->rc_state = RC_INPROG;
    216 	rp->rc_xid = xid;
    217 	bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
    218 	rp->rc_nam.m_data = rp->rc_nam.m_dat; /* for now; hopefully correct? certainly better */
    219 	rp->rc_proc = proc;
    220 	insque(rp, rh);
    221 	if (mb)
    222 		m_freem(mb);
    223 	return (RC_DOIT);
    224 }
    225 
    226 /*
    227  * Update a request cache entry after the rpc has been done
    228  */
    229 nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
    230 	struct mbuf *nam;
    231 	u_long xid;
    232 	int proc;
    233 	int repvalid;
    234 	int repstat;
    235 	struct mbuf *repmbuf;
    236 {
    237 	register struct nfsrvcache *rp;
    238 	register union	rhead *rh;
    239 
    240 	rh = &rhead[NFSRCHASH(xid)];
    241 loop:
    242 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
    243 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
    244 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
    245 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    246 				rp->rc_flag |= RC_WANTED;
    247 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    248 				goto loop;
    249 			}
    250 			rp->rc_flag |= RC_LOCKED;
    251 			rp->rc_state = RC_DONE;
    252 			/*
    253 			 * If we have a valid reply update status and save
    254 			 * the reply for non-idempotent rpc's.
    255 			 * Otherwise invalidate entry by setting the timestamp
    256 			 * to nil.
    257 			 */
    258 			if (repvalid) {
    259 				rp->rc_timestamp = time.tv_sec;
    260 				if (nonidempotent[proc]) {
    261 					if (repliesstatus[proc]) {
    262 						rp->rc_status = repstat;
    263 						rp->rc_flag |= RC_REPSTATUS;
    264 					} else {
    265 						rp->rc_reply = m_copym(repmbuf,
    266 							0, M_COPYALL, M_WAIT);
    267 						rp->rc_flag |= RC_REPMBUF;
    268 					}
    269 				}
    270 			} else {
    271 				rp->rc_timestamp = 0;
    272 			}
    273 			rp->rc_flag &= ~RC_LOCKED;
    274 			if (rp->rc_flag & RC_WANTED) {
    275 				rp->rc_flag &= ~RC_WANTED;
    276 				wakeup((caddr_t)rp);
    277 			}
    278 			return;
    279 		}
    280 	}
    281 }
    282