xref: /netbsd-src/sys/nfs/nfs_srvcache.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl 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.3 (Berkeley) 3/30/95
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/nfsproto.h>
64 #include <nfs/nfs.h>
65 #include <nfs/nfsrvcache.h>
66 #include <nfs/nqnfs.h>
67 #include <nfs/nfs_var.h>
68 
69 extern struct nfsstats nfsstats;
70 extern int nfsv2_procid[NFS_NPROCS];
71 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
72 
73 #define	NFSRCHASH(xid) \
74 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
75 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
76 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
77 u_long nfsrvhash;
78 
79 #define TRUE	1
80 #define	FALSE	0
81 
82 #define	NETFAMILY(rp) \
83 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
84 
85 /*
86  * Static array that defines which nfs rpc's are nonidempotent
87  */
88 int nonidempotent[NFS_NPROCS] = {
89 	FALSE,
90 	FALSE,
91 	TRUE,
92 	FALSE,
93 	FALSE,
94 	FALSE,
95 	FALSE,
96 	TRUE,
97 	TRUE,
98 	TRUE,
99 	TRUE,
100 	TRUE,
101 	TRUE,
102 	TRUE,
103 	TRUE,
104 	TRUE,
105 	FALSE,
106 	FALSE,
107 	FALSE,
108 	FALSE,
109 	FALSE,
110 	FALSE,
111 	FALSE,
112 	FALSE,
113 	FALSE,
114 	FALSE,
115 };
116 
117 /* True iff the rpc reply is an nfs status ONLY! */
118 static int nfsv2_repstat[NFS_NPROCS] = {
119 	FALSE,
120 	FALSE,
121 	FALSE,
122 	FALSE,
123 	FALSE,
124 	FALSE,
125 	FALSE,
126 	FALSE,
127 	FALSE,
128 	FALSE,
129 	TRUE,
130 	TRUE,
131 	TRUE,
132 	TRUE,
133 	FALSE,
134 	TRUE,
135 	FALSE,
136 	FALSE,
137 };
138 
139 /*
140  * Initialize the server request cache list
141  */
142 void
143 nfsrv_initcache()
144 {
145 
146 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
147 	TAILQ_INIT(&nfsrvlruhead);
148 }
149 
150 /*
151  * Look for the request in the cache
152  * If found then
153  *    return action and optionally reply
154  * else
155  *    insert it in the cache
156  *
157  * The rules are as follows:
158  * - if in progress, return DROP request
159  * - if completed within DELAY of the current time, return DROP it
160  * - if completed a longer time ago return REPLY if the reply was cached or
161  *   return DOIT
162  * Update/add new request at end of lru list
163  */
164 int
165 nfsrv_getcache(nd, slp, repp)
166 	register struct nfsrv_descript *nd;
167 	struct nfssvc_sock *slp;
168 	struct mbuf **repp;
169 {
170 	register struct nfsrvcache *rp;
171 	struct mbuf *mb;
172 	struct sockaddr_in *saddr;
173 	caddr_t bpos;
174 	int ret;
175 
176 	/*
177 	 * Don't cache recent requests for reliable transport protocols.
178 	 * (Maybe we should for the case of a reconnect, but..)
179 	 */
180 	if (!nd->nd_nam2)
181 		return (RC_DOIT);
182 loop:
183 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
184 	    rp = rp->rc_hash.le_next) {
185 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
186 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
187 			if ((rp->rc_flag & RC_LOCKED) != 0) {
188 				rp->rc_flag |= RC_WANTED;
189 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
190 				goto loop;
191 			}
192 			rp->rc_flag |= RC_LOCKED;
193 			/* If not at end of LRU chain, move it there */
194 			if (rp->rc_lru.tqe_next) {
195 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
196 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
197 			}
198 			if (rp->rc_state == RC_UNUSED)
199 				panic("nfsrv cache");
200 			if (rp->rc_state == RC_INPROG) {
201 				nfsstats.srvcache_inproghits++;
202 				ret = RC_DROPIT;
203 			} else if (rp->rc_flag & RC_REPSTATUS) {
204 				nfsstats.srvcache_nonidemdonehits++;
205 				nfs_rephead(0, nd, slp, rp->rc_status,
206 				   0, (u_quad_t *)0, repp, &mb, &bpos);
207 				ret = RC_REPLY;
208 			} else if (rp->rc_flag & RC_REPMBUF) {
209 				nfsstats.srvcache_nonidemdonehits++;
210 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
211 						M_WAIT);
212 				ret = RC_REPLY;
213 			} else {
214 				nfsstats.srvcache_idemdonehits++;
215 				rp->rc_state = RC_INPROG;
216 				ret = RC_DOIT;
217 			}
218 			rp->rc_flag &= ~RC_LOCKED;
219 			if (rp->rc_flag & RC_WANTED) {
220 				rp->rc_flag &= ~RC_WANTED;
221 				wakeup((caddr_t)rp);
222 			}
223 			return (ret);
224 		}
225 	}
226 	nfsstats.srvcache_misses++;
227 	if (numnfsrvcache < desirednfsrvcache) {
228 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
229 		    M_NFSD, M_WAITOK);
230 		bzero((char *)rp, sizeof *rp);
231 		numnfsrvcache++;
232 		rp->rc_flag = RC_LOCKED;
233 	} else {
234 		rp = nfsrvlruhead.tqh_first;
235 		while ((rp->rc_flag & RC_LOCKED) != 0) {
236 			rp->rc_flag |= RC_WANTED;
237 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
238 			rp = nfsrvlruhead.tqh_first;
239 		}
240 		rp->rc_flag |= RC_LOCKED;
241 		LIST_REMOVE(rp, rc_hash);
242 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
243 		if (rp->rc_flag & RC_REPMBUF)
244 			m_freem(rp->rc_reply);
245 		if (rp->rc_flag & RC_NAM)
246 			MFREE(rp->rc_nam, mb);
247 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
248 	}
249 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
250 	rp->rc_state = RC_INPROG;
251 	rp->rc_xid = nd->nd_retxid;
252 	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
253 	switch (saddr->sin_family) {
254 	case AF_INET:
255 		rp->rc_flag |= RC_INETADDR;
256 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
257 		break;
258 	case AF_ISO:
259 	default:
260 		rp->rc_flag |= RC_NAM;
261 		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
262 		break;
263 	};
264 	rp->rc_proc = nd->nd_procnum;
265 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
266 	rp->rc_flag &= ~RC_LOCKED;
267 	if (rp->rc_flag & RC_WANTED) {
268 		rp->rc_flag &= ~RC_WANTED;
269 		wakeup((caddr_t)rp);
270 	}
271 	return (RC_DOIT);
272 }
273 
274 /*
275  * Update a request cache entry after the rpc has been done
276  */
277 void
278 nfsrv_updatecache(nd, repvalid, repmbuf)
279 	register struct nfsrv_descript *nd;
280 	int repvalid;
281 	struct mbuf *repmbuf;
282 {
283 	register struct nfsrvcache *rp;
284 
285 	if (!nd->nd_nam2)
286 		return;
287 loop:
288 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
289 	    rp = rp->rc_hash.le_next) {
290 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
291 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
292 			if ((rp->rc_flag & RC_LOCKED) != 0) {
293 				rp->rc_flag |= RC_WANTED;
294 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
295 				goto loop;
296 			}
297 			rp->rc_flag |= RC_LOCKED;
298 			rp->rc_state = RC_DONE;
299 			/*
300 			 * If we have a valid reply update status and save
301 			 * the reply for non-idempotent rpc's.
302 			 */
303 			if (repvalid && nonidempotent[nd->nd_procnum]) {
304 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
305 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
306 					rp->rc_status = nd->nd_repstat;
307 					rp->rc_flag |= RC_REPSTATUS;
308 				} else {
309 					rp->rc_reply = m_copym(repmbuf,
310 						0, M_COPYALL, M_WAIT);
311 					rp->rc_flag |= RC_REPMBUF;
312 				}
313 			}
314 			rp->rc_flag &= ~RC_LOCKED;
315 			if (rp->rc_flag & RC_WANTED) {
316 				rp->rc_flag &= ~RC_WANTED;
317 				wakeup((caddr_t)rp);
318 			}
319 			return;
320 		}
321 	}
322 }
323 
324 /*
325  * Clean out the cache. Called when the last nfsd terminates.
326  */
327 void
328 nfsrv_cleancache()
329 {
330 	register struct nfsrvcache *rp, *nextrp;
331 
332 	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
333 		nextrp = rp->rc_lru.tqe_next;
334 		LIST_REMOVE(rp, rc_hash);
335 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
336 		free(rp, M_NFSD);
337 	}
338 	numnfsrvcache = 0;
339 }
340