xref: /netbsd-src/sys/nfs/nfs_srvcache.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*	$NetBSD: nfs_srvcache.c,v 1.10 1994/12/13 17:17:03 mycroft Exp $	*/
2 
3 /*
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Rick Macklem at The University of Guelph.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)nfs_srvcache.c	8.2 (Berkeley) 8/18/94
39  */
40 
41 /*
42  * Reference: Chet Juszczak, "Improving the Performance and Correctness
43  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
44  *		pages 53-63. San Diego, February 1989.
45  */
46 #include <sys/param.h>
47 #include <sys/vnode.h>
48 #include <sys/mount.h>
49 #include <sys/kernel.h>
50 #include <sys/systm.h>
51 #include <sys/proc.h>
52 #include <sys/mbuf.h>
53 #include <sys/malloc.h>
54 #include <sys/socket.h>
55 #include <sys/socketvar.h>
56 
57 #include <netinet/in.h>
58 #ifdef ISO
59 #include <netiso/iso.h>
60 #endif
61 #include <nfs/nfsm_subs.h>
62 #include <nfs/rpcv2.h>
63 #include <nfs/nfsv2.h>
64 #include <nfs/nfs.h>
65 #include <nfs/nfsrvcache.h>
66 #include <nfs/nqnfs.h>
67 
68 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
69 
70 #define	NFSRCHASH(xid) \
71 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
72 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
73 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
74 u_long nfsrvhash;
75 
76 #define TRUE	1
77 #define	FALSE	0
78 
79 #define	NETFAMILY(rp) \
80 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
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 	FALSE,
105 	FALSE,
106 	FALSE,
107 	FALSE,
108 	FALSE,
109 };
110 
111 /* True iff the rpc reply is an nfs status ONLY! */
112 static int repliesstatus[NFS_NPROCS] = {
113 	FALSE,
114 	FALSE,
115 	FALSE,
116 	FALSE,
117 	FALSE,
118 	FALSE,
119 	FALSE,
120 	FALSE,
121 	FALSE,
122 	FALSE,
123 	TRUE,
124 	TRUE,
125 	TRUE,
126 	TRUE,
127 	FALSE,
128 	TRUE,
129 	FALSE,
130 	FALSE,
131 	FALSE,
132 	FALSE,
133 	FALSE,
134 	FALSE,
135 	TRUE,
136 };
137 
138 /*
139  * Initialize the server request cache list
140  */
141 nfsrv_initcache()
142 {
143 
144 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
145 	TAILQ_INIT(&nfsrvlruhead);
146 }
147 
148 /*
149  * Look for the request in the cache
150  * If found then
151  *    return action and optionally reply
152  * else
153  *    insert it in the cache
154  *
155  * The rules are as follows:
156  * - if in progress, return DROP request
157  * - if completed within DELAY of the current time, return DROP it
158  * - if completed a longer time ago return REPLY if the reply was cached or
159  *   return DOIT
160  * Update/add new request at end of lru list
161  */
162 nfsrv_getcache(nam, nd, repp)
163 	struct mbuf *nam;
164 	register struct nfsd *nd;
165 	struct mbuf **repp;
166 {
167 	register struct nfsrvcache *rp;
168 	struct mbuf *mb;
169 	struct sockaddr_in *saddr;
170 	caddr_t bpos;
171 	int ret;
172 
173 	if (nd->nd_nqlflag != NQL_NOVAL)
174 		return (RC_DOIT);
175 loop:
176 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
177 	    rp = rp->rc_hash.le_next) {
178 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
179 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
180 			if ((rp->rc_flag & RC_LOCKED) != 0) {
181 				rp->rc_flag |= RC_WANTED;
182 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
183 				goto loop;
184 			}
185 			rp->rc_flag |= RC_LOCKED;
186 			/* If not at end of LRU chain, move it there */
187 			if (rp->rc_lru.tqe_next) {
188 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
189 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
190 			}
191 			if (rp->rc_state == RC_UNUSED)
192 				panic("nfsrv cache");
193 			if (rp->rc_state == RC_INPROG) {
194 				nfsstats.srvcache_inproghits++;
195 				ret = RC_DROPIT;
196 			} else if (rp->rc_flag & RC_REPSTATUS) {
197 				nfsstats.srvcache_nonidemdonehits++;
198 				nfs_rephead(0, nd, rp->rc_status,
199 				   0, (u_quad_t *)0, repp, &mb, &bpos);
200 				ret = RC_REPLY;
201 			} else if (rp->rc_flag & RC_REPMBUF) {
202 				nfsstats.srvcache_nonidemdonehits++;
203 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
204 						M_WAIT);
205 				ret = RC_REPLY;
206 			} else {
207 				nfsstats.srvcache_idemdonehits++;
208 				rp->rc_state = RC_INPROG;
209 				ret = RC_DOIT;
210 			}
211 			rp->rc_flag &= ~RC_LOCKED;
212 			if (rp->rc_flag & RC_WANTED) {
213 				rp->rc_flag &= ~RC_WANTED;
214 				wakeup((caddr_t)rp);
215 			}
216 			return (ret);
217 		}
218 	}
219 	nfsstats.srvcache_misses++;
220 	if (numnfsrvcache < desirednfsrvcache) {
221 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
222 		    M_NFSD, M_WAITOK);
223 		bzero((char *)rp, sizeof *rp);
224 		numnfsrvcache++;
225 		rp->rc_flag = RC_LOCKED;
226 	} else {
227 		rp = nfsrvlruhead.tqh_first;
228 		while ((rp->rc_flag & RC_LOCKED) != 0) {
229 			rp->rc_flag |= RC_WANTED;
230 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
231 			rp = nfsrvlruhead.tqh_first;
232 		}
233 		rp->rc_flag |= RC_LOCKED;
234 		LIST_REMOVE(rp, rc_hash);
235 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
236 		if (rp->rc_flag & RC_REPMBUF)
237 			m_freem(rp->rc_reply);
238 		if (rp->rc_flag & RC_NAM)
239 			MFREE(rp->rc_nam, mb);
240 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
241 	}
242 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
243 	rp->rc_state = RC_INPROG;
244 	rp->rc_xid = nd->nd_retxid;
245 	saddr = mtod(nam, struct sockaddr_in *);
246 	switch (saddr->sin_family) {
247 	case AF_INET:
248 		rp->rc_flag |= RC_INETADDR;
249 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
250 		break;
251 	case AF_ISO:
252 	default:
253 		rp->rc_flag |= RC_NAM;
254 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
255 		break;
256 	};
257 	rp->rc_proc = nd->nd_procnum;
258 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
259 	rp->rc_flag &= ~RC_LOCKED;
260 	if (rp->rc_flag & RC_WANTED) {
261 		rp->rc_flag &= ~RC_WANTED;
262 		wakeup((caddr_t)rp);
263 	}
264 	return (RC_DOIT);
265 }
266 
267 /*
268  * Update a request cache entry after the rpc has been done
269  */
270 void
271 nfsrv_updatecache(nam, nd, repvalid, repmbuf)
272 	struct mbuf *nam;
273 	register struct nfsd *nd;
274 	int repvalid;
275 	struct mbuf *repmbuf;
276 {
277 	register struct nfsrvcache *rp;
278 
279 	if (nd->nd_nqlflag != NQL_NOVAL)
280 		return;
281 loop:
282 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
283 	    rp = rp->rc_hash.le_next) {
284 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
285 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
286 			if ((rp->rc_flag & RC_LOCKED) != 0) {
287 				rp->rc_flag |= RC_WANTED;
288 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
289 				goto loop;
290 			}
291 			rp->rc_flag |= RC_LOCKED;
292 			rp->rc_state = RC_DONE;
293 			/*
294 			 * If we have a valid reply update status and save
295 			 * the reply for non-idempotent rpc's.
296 			 */
297 			if (repvalid && nonidempotent[nd->nd_procnum]) {
298 				if (repliesstatus[nd->nd_procnum]) {
299 					rp->rc_status = nd->nd_repstat;
300 					rp->rc_flag |= RC_REPSTATUS;
301 				} else {
302 					rp->rc_reply = m_copym(repmbuf,
303 						0, M_COPYALL, M_WAIT);
304 					rp->rc_flag |= RC_REPMBUF;
305 				}
306 			}
307 			rp->rc_flag &= ~RC_LOCKED;
308 			if (rp->rc_flag & RC_WANTED) {
309 				rp->rc_flag &= ~RC_WANTED;
310 				wakeup((caddr_t)rp);
311 			}
312 			return;
313 		}
314 	}
315 }
316 
317 /*
318  * Clean out the cache. Called when the last nfsd terminates.
319  */
320 void
321 nfsrv_cleancache()
322 {
323 	register struct nfsrvcache *rp, *nextrp;
324 
325 	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
326 		nextrp = rp->rc_lru.tqe_next;
327 		LIST_REMOVE(rp, rc_hash);
328 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
329 		free(rp, M_NFSD);
330 	}
331 	numnfsrvcache = 0;
332 }
333