Home | History | Annotate | Line # | Download | only in nfs
nfs_srvcache.c revision 1.1
      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 union rhead {
     69 	union  rhead *rh_head[2];
     70 	struct nfsrvcache *rh_chain[2];
     71 } rhead[NFSRCHSZ];
     72 
     73 static struct nfsrvcache nfsrvcachehead;
     74 static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
     75 
     76 #define TRUE	1
     77 #define	FALSE	0
     78 
     79 /*
     80  * Static array that defines which nfs rpc's are nonidempotent
     81  */
     82 int nonidempotent[NFS_NPROCS] = {
     83 	FALSE,
     84 	FALSE,
     85 	TRUE,
     86 	FALSE,
     87 	FALSE,
     88 	FALSE,
     89 	FALSE,
     90 	FALSE,
     91 	TRUE,
     92 	TRUE,
     93 	TRUE,
     94 	TRUE,
     95 	TRUE,
     96 	TRUE,
     97 	TRUE,
     98 	TRUE,
     99 	FALSE,
    100 	FALSE,
    101 };
    102 
    103 /* True iff the rpc reply is an nfs status ONLY! */
    104 static int repliesstatus[NFS_NPROCS] = {
    105 	FALSE,
    106 	FALSE,
    107 	FALSE,
    108 	FALSE,
    109 	FALSE,
    110 	FALSE,
    111 	FALSE,
    112 	FALSE,
    113 	FALSE,
    114 	FALSE,
    115 	TRUE,
    116 	TRUE,
    117 	TRUE,
    118 	TRUE,
    119 	FALSE,
    120 	TRUE,
    121 	FALSE,
    122 	FALSE,
    123 };
    124 
    125 /*
    126  * Initialize the server request cache list
    127  */
    128 nfsrv_initcache()
    129 {
    130 	register int i;
    131 	register struct nfsrvcache *rp = nfsrvcache;
    132 	register struct nfsrvcache *hp = &nfsrvcachehead;
    133 	register union  rhead *rh = rhead;
    134 
    135 	for (i = NFSRCHSZ; --i >= 0; rh++) {
    136 		rh->rh_head[0] = rh;
    137 		rh->rh_head[1] = rh;
    138 	}
    139 	hp->rc_next = hp->rc_prev = hp;
    140 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
    141 		rp->rc_state = RC_UNUSED;
    142 		rp->rc_flag = 0;
    143 		rp->rc_forw = rp;
    144 		rp->rc_back = rp;
    145 		rp->rc_next = hp->rc_next;
    146 		hp->rc_next->rc_prev = rp;
    147 		rp->rc_prev = hp;
    148 		hp->rc_next = rp;
    149 		rp++;
    150 	}
    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 nfsrv_getcache(nam, xid, proc, repp)
    168 	struct mbuf *nam;
    169 	u_long xid;
    170 	int proc;
    171 	struct mbuf **repp;
    172 {
    173 	register struct nfsrvcache *rp;
    174 	register union  rhead *rh;
    175 	struct mbuf *mb;
    176 	caddr_t bpos;
    177 	int ret;
    178 
    179 	rh = &rhead[NFSRCHASH(xid)];
    180 loop:
    181 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
    182 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
    183 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
    184 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    185 				rp->rc_flag |= RC_WANTED;
    186 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    187 				goto loop;
    188 			}
    189 			rp->rc_flag |= RC_LOCKED;
    190 			put_at_head(rp);
    191 			if (rp->rc_state == RC_UNUSED)
    192 				panic("nfsrv cache");
    193 			if (rp->rc_state == RC_INPROG ||
    194 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
    195 				nfsstats.srvcache_inproghits++;
    196 				ret = RC_DROPIT;
    197 			} else if (rp->rc_flag & RC_REPSTATUS) {
    198 				nfsstats.srvcache_idemdonehits++;
    199 				nfs_rephead(0, xid, rp->rc_status, repp, &mb,
    200 					&bpos);
    201 				rp->rc_timestamp = time.tv_sec;
    202 				ret = RC_REPLY;
    203 			} else if (rp->rc_flag & RC_REPMBUF) {
    204 				nfsstats.srvcache_idemdonehits++;
    205 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
    206 						M_WAIT);
    207 				rp->rc_timestamp = time.tv_sec;
    208 				ret = RC_REPLY;
    209 			} else {
    210 				nfsstats.srvcache_nonidemdonehits++;
    211 				rp->rc_state = RC_INPROG;
    212 				ret = RC_DOIT;
    213 			}
    214 			rp->rc_flag &= ~RC_LOCKED;
    215 			if (rp->rc_flag & RC_WANTED) {
    216 				rp->rc_flag &= ~RC_WANTED;
    217 				wakeup((caddr_t)rp);
    218 			}
    219 			return (ret);
    220 		}
    221 	}
    222 	nfsstats.srvcache_misses++;
    223 	rp = nfsrvcachehead.rc_prev;
    224 	while ((rp->rc_flag & RC_LOCKED) != 0) {
    225 		rp->rc_flag |= RC_WANTED;
    226 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    227 	}
    228 	remque(rp);
    229 	put_at_head(rp);
    230 	if (rp->rc_flag & RC_REPMBUF)
    231 		mb = rp->rc_reply;
    232 	else
    233 		mb = (struct mbuf *)0;
    234 	rp->rc_flag = 0;
    235 	rp->rc_state = RC_INPROG;
    236 	rp->rc_xid = xid;
    237 	bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
    238 	rp->rc_proc = proc;
    239 	insque(rp, rh);
    240 	if (mb)
    241 		m_freem(mb);
    242 	return (RC_DOIT);
    243 }
    244 
    245 /*
    246  * Update a request cache entry after the rpc has been done
    247  */
    248 nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
    249 	struct mbuf *nam;
    250 	u_long xid;
    251 	int proc;
    252 	int repvalid;
    253 	int repstat;
    254 	struct mbuf *repmbuf;
    255 {
    256 	register struct nfsrvcache *rp;
    257 	register union	rhead *rh;
    258 
    259 	rh = &rhead[NFSRCHASH(xid)];
    260 loop:
    261 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
    262 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
    263 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
    264 			if ((rp->rc_flag & RC_LOCKED) != 0) {
    265 				rp->rc_flag |= RC_WANTED;
    266 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
    267 				goto loop;
    268 			}
    269 			rp->rc_flag |= RC_LOCKED;
    270 			rp->rc_state = RC_DONE;
    271 			/*
    272 			 * If we have a valid reply update status and save
    273 			 * the reply for non-idempotent rpc's.
    274 			 * Otherwise invalidate entry by setting the timestamp
    275 			 * to nil.
    276 			 */
    277 			if (repvalid) {
    278 				rp->rc_timestamp = time.tv_sec;
    279 				if (nonidempotent[proc]) {
    280 					if (repliesstatus[proc]) {
    281 						rp->rc_status = repstat;
    282 						rp->rc_flag |= RC_REPSTATUS;
    283 					} else {
    284 						rp->rc_reply = m_copym(repmbuf,
    285 							0, M_COPYALL, M_WAIT);
    286 						rp->rc_flag |= RC_REPMBUF;
    287 					}
    288 				}
    289 			} else {
    290 				rp->rc_timestamp = 0;
    291 			}
    292 			rp->rc_flag &= ~RC_LOCKED;
    293 			if (rp->rc_flag & RC_WANTED) {
    294 				rp->rc_flag &= ~RC_WANTED;
    295 				wakeup((caddr_t)rp);
    296 			}
    297 			return;
    298 		}
    299 	}
    300 }
    301