xref: /386bsd/usr/src/kernel/nfs/nfs_srvcache.c (revision a2142627)
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  *	$Id: nfs_srvcache.c,v 1.1 94/10/20 10:57:33 root Exp $
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 "sys/param.h"
46 #include "sys/file.h"
47 #include "sys/mount.h"
48 #include "sys/time.h"
49 #include "uio.h"
50 #include "sys/errno.h"	/* for inlines */
51 #include "proc.h"
52 #include "mbuf.h"
53 #include "socketvar.h"
54 
55 #include "in.h"
56 
57 #include "namei.h"
58 #include "vnode.h"
59 #include "nfs_msubs.h"
60 #include "nfs_v2.h"
61 #include "nfs_srvcache.h"
62 #include "nfs.h"
63 #include "prototypes.h"		/* needs inlines */
64 
65 #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
66 #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
67 #else
68 #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
69 #endif
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  * Static array that defines which nfs rpc's are nonidempotent
84  */
85 int nonidempotent[NFS_NPROCS] = {
86 	FALSE,
87 	FALSE,
88 	TRUE,
89 	FALSE,
90 	FALSE,
91 	FALSE,
92 	FALSE,
93 	FALSE,
94 	TRUE,
95 	TRUE,
96 	TRUE,
97 	TRUE,
98 	TRUE,
99 	TRUE,
100 	TRUE,
101 	TRUE,
102 	FALSE,
103 	FALSE,
104 };
105 
106 /* True iff the rpc reply is an nfs status ONLY! */
107 static int repliesstatus[NFS_NPROCS] = {
108 	FALSE,
109 	FALSE,
110 	FALSE,
111 	FALSE,
112 	FALSE,
113 	FALSE,
114 	FALSE,
115 	FALSE,
116 	FALSE,
117 	FALSE,
118 	TRUE,
119 	TRUE,
120 	TRUE,
121 	TRUE,
122 	FALSE,
123 	TRUE,
124 	FALSE,
125 	FALSE,
126 };
127 
128 /*
129  * Initialize the server request cache list
130  */
nfsrv_initcache()131 nfsrv_initcache()
132 {
133 	register int i;
134 	register struct nfsrvcache *rp = nfsrvcache;
135 	register struct nfsrvcache *hp = &nfsrvcachehead;
136 	register union  rhead *rh = rhead;
137 
138 	for (i = NFSRCHSZ; --i >= 0; rh++) {
139 		rh->rh_head[0] = rh;
140 		rh->rh_head[1] = rh;
141 	}
142 	hp->rc_next = hp->rc_prev = hp;
143 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
144 		rp->rc_state = RC_UNUSED;
145 		rp->rc_flag = 0;
146 		rp->rc_forw = rp;
147 		rp->rc_back = rp;
148 		rp->rc_next = hp->rc_next;
149 		hp->rc_next->rc_prev = rp;
150 		rp->rc_prev = hp;
151 		hp->rc_next = rp;
152 		rp++;
153 	}
154 }
155 
156 /*
157  * Look for the request in the cache
158  * If found then
159  *    return action and optionally reply
160  * else
161  *    insert it in the cache
162  *
163  * The rules are as follows:
164  * - if in progress, return DROP request
165  * - if completed within DELAY of the current time, return DROP it
166  * - if completed a longer time ago return REPLY if the reply was cached or
167  *   return DOIT
168  * Update/add new request at end of lru list
169  */
170 nfsrv_getcache(nam, xid, proc, repp)
171 	struct mbuf *nam;
172 	u_long xid;
173 	int proc;
174 	struct mbuf **repp;
175 {
176 	register struct nfsrvcache *rp;
177 	register union  rhead *rh;
178 	struct mbuf *mb;
179 	caddr_t bpos;
180 	int ret;
181 
182 	rh = &rhead[NFSRCHASH(xid)];
183 loop:
184 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
185 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
186 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
187 			if ((rp->rc_flag & RC_LOCKED) != 0) {
188 				rp->rc_flag |= RC_WANTED;
189 				(void) tsleep((caddr_t)rp, PSOCK-1, "nfsrc", 0);
190 				goto loop;
191 			}
192 			rp->rc_flag |= RC_LOCKED;
193 			put_at_head(rp);
194 			if (rp->rc_state == RC_UNUSED)
195 				panic("nfsrv cache");
196 			if (rp->rc_state == RC_INPROG ||
197 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
198 				nfsstats.srvcache_inproghits++;
199 				ret = RC_DROPIT;
200 			} else if (rp->rc_flag & RC_REPSTATUS) {
201 				nfsstats.srvcache_idemdonehits++;
202 				nfs_rephead(0, xid, rp->rc_status, repp, &mb,
203 					&bpos);
204 				rp->rc_timestamp = time.tv_sec;
205 				ret = RC_REPLY;
206 			} else if (rp->rc_flag & RC_REPMBUF) {
207 				nfsstats.srvcache_idemdonehits++;
208 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
209 						M_WAIT);
210 				rp->rc_timestamp = time.tv_sec;
211 				ret = RC_REPLY;
212 			} else {
213 				nfsstats.srvcache_nonidemdonehits++;
214 				rp->rc_state = RC_INPROG;
215 				ret = RC_DOIT;
216 			}
217 			rp->rc_flag &= ~RC_LOCKED;
218 			if (rp->rc_flag & RC_WANTED) {
219 				rp->rc_flag &= ~RC_WANTED;
220 				wakeup((caddr_t)rp);
221 			}
222 			return (ret);
223 		}
224 	}
225 	nfsstats.srvcache_misses++;
226 	rp = nfsrvcachehead.rc_prev;
227 	while ((rp->rc_flag & RC_LOCKED) != 0) {
228 		rp->rc_flag |= RC_WANTED;
229 		(void) tsleep((caddr_t)rp, PSOCK-1, "nfsrc", 0);
230 	}
231 	remque(rp);
232 	put_at_head(rp);
233 	if (rp->rc_flag & RC_REPMBUF)
234 		mb = rp->rc_reply;
235 	else
236 		mb = (struct mbuf *)0;
237 	rp->rc_flag = 0;
238 	rp->rc_state = RC_INPROG;
239 	rp->rc_xid = xid;
240 	memcpy((caddr_t)&rp->rc_nam, (caddr_t)nam, sizeof (struct mbuf));
241 	rp->rc_proc = proc;
242 	insque(rp, rh);
243 	if (mb)
244 		m_freem(mb);
245 	return (RC_DOIT);
246 }
247 
248 /*
249  * Update a request cache entry after the rpc has been done
250  */
251 nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
252 	struct mbuf *nam;
253 	u_long xid;
254 	int proc;
255 	int repvalid;
256 	int repstat;
257 	struct mbuf *repmbuf;
258 {
259 	register struct nfsrvcache *rp;
260 	register union	rhead *rh;
261 
262 	rh = &rhead[NFSRCHASH(xid)];
263 loop:
264 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
265 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
266 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
267 			if ((rp->rc_flag & RC_LOCKED) != 0) {
268 				rp->rc_flag |= RC_WANTED;
269 				(void) tsleep((caddr_t)rp, PSOCK-1, "nfsrc", 0);
270 				goto loop;
271 			}
272 			rp->rc_flag |= RC_LOCKED;
273 			rp->rc_state = RC_DONE;
274 			/*
275 			 * If we have a valid reply update status and save
276 			 * the reply for non-idempotent rpc's.
277 			 * Otherwise invalidate entry by setting the timestamp
278 			 * to nil.
279 			 */
280 			if (repvalid) {
281 				rp->rc_timestamp = time.tv_sec;
282 				if (nonidempotent[proc]) {
283 					if (repliesstatus[proc]) {
284 						rp->rc_status = repstat;
285 						rp->rc_flag |= RC_REPSTATUS;
286 					} else {
287 						rp->rc_reply = m_copym(repmbuf,
288 							0, M_COPYALL, M_WAIT);
289 						rp->rc_flag |= RC_REPMBUF;
290 					}
291 				}
292 			} else {
293 				rp->rc_timestamp = 0;
294 			}
295 			rp->rc_flag &= ~RC_LOCKED;
296 			if (rp->rc_flag & RC_WANTED) {
297 				rp->rc_flag &= ~RC_WANTED;
298 				wakeup((caddr_t)rp);
299 			}
300 			return;
301 		}
302 	}
303 }
304