xref: /netbsd-src/sys/nfs/nfs_srvcache.c (revision 0b9f50897e9a9c6709320fafb4c3787fddcc0a45)
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.5 1993/09/07 15:41:44 ws 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 "param.h"
47 #include "namei.h"
48 #include "vnode.h"
49 #include "mount.h"
50 #include "kernel.h"
51 #include "systm.h"
52 #include "mbuf.h"
53 #include "socket.h"
54 #include "socketvar.h"
55 
56 #include "../netinet/in.h"
57 
58 #include "nfsm_subs.h"
59 #include "nfsv2.h"
60 #include "nfsrvcache.h"
61 #include "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