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