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