xref: /netbsd-src/sys/nfs/nfs_srvcache.c (revision 811e6386f8c5e4a3521c7003da29ec8673e344fa)
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