xref: /netbsd-src/sys/nfs/nfs_srvcache.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: nfs_srvcache.c,v 1.16 2000/03/30 12:51:16 augustss 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 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 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, M_NFSD, M_WAITOK, &nfsrvhash);
149 	TAILQ_INIT(&nfsrvlruhead);
150 }
151 
152 /*
153  * Look for the request in the cache
154  * If found then
155  *    return action and optionally reply
156  * else
157  *    insert it in the cache
158  *
159  * The rules are as follows:
160  * - if in progress, return DROP request
161  * - if completed within DELAY of the current time, return DROP it
162  * - if completed a longer time ago return REPLY if the reply was cached or
163  *   return DOIT
164  * Update/add new request at end of lru list
165  */
166 int
167 nfsrv_getcache(nd, slp, repp)
168 	struct nfsrv_descript *nd;
169 	struct nfssvc_sock *slp;
170 	struct mbuf **repp;
171 {
172 	struct nfsrvcache *rp;
173 	struct mbuf *mb;
174 	struct sockaddr_in *saddr;
175 	caddr_t bpos;
176 	int ret;
177 
178 	/*
179 	 * Don't cache recent requests for reliable transport protocols.
180 	 * (Maybe we should for the case of a reconnect, but..)
181 	 */
182 	if (!nd->nd_nam2)
183 		return (RC_DOIT);
184 loop:
185 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
186 	    rp = rp->rc_hash.le_next) {
187 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
188 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
189 			if ((rp->rc_flag & RC_LOCKED) != 0) {
190 				rp->rc_flag |= RC_WANTED;
191 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
192 				goto loop;
193 			}
194 			rp->rc_flag |= RC_LOCKED;
195 			/* If not at end of LRU chain, move it there */
196 			if (rp->rc_lru.tqe_next) {
197 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
198 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
199 			}
200 			if (rp->rc_state == RC_UNUSED)
201 				panic("nfsrv cache");
202 			if (rp->rc_state == RC_INPROG) {
203 				nfsstats.srvcache_inproghits++;
204 				ret = RC_DROPIT;
205 			} else if (rp->rc_flag & RC_REPSTATUS) {
206 				nfsstats.srvcache_nonidemdonehits++;
207 				nfs_rephead(0, nd, slp, rp->rc_status,
208 				   0, (u_quad_t *)0, repp, &mb, &bpos);
209 				ret = RC_REPLY;
210 			} else if (rp->rc_flag & RC_REPMBUF) {
211 				nfsstats.srvcache_nonidemdonehits++;
212 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
213 						M_WAIT);
214 				ret = RC_REPLY;
215 			} else {
216 				nfsstats.srvcache_idemdonehits++;
217 				rp->rc_state = RC_INPROG;
218 				ret = RC_DOIT;
219 			}
220 			rp->rc_flag &= ~RC_LOCKED;
221 			if (rp->rc_flag & RC_WANTED) {
222 				rp->rc_flag &= ~RC_WANTED;
223 				wakeup((caddr_t)rp);
224 			}
225 			return (ret);
226 		}
227 	}
228 	nfsstats.srvcache_misses++;
229 	if (numnfsrvcache < desirednfsrvcache) {
230 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
231 		    M_NFSD, M_WAITOK);
232 		memset((char *)rp, 0, sizeof *rp);
233 		numnfsrvcache++;
234 		rp->rc_flag = RC_LOCKED;
235 	} else {
236 		rp = nfsrvlruhead.tqh_first;
237 		while ((rp->rc_flag & RC_LOCKED) != 0) {
238 			rp->rc_flag |= RC_WANTED;
239 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
240 			rp = nfsrvlruhead.tqh_first;
241 		}
242 		rp->rc_flag |= RC_LOCKED;
243 		LIST_REMOVE(rp, rc_hash);
244 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
245 		if (rp->rc_flag & RC_REPMBUF)
246 			m_freem(rp->rc_reply);
247 		if (rp->rc_flag & RC_NAM)
248 			MFREE(rp->rc_nam, mb);
249 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
250 	}
251 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
252 	rp->rc_state = RC_INPROG;
253 	rp->rc_xid = nd->nd_retxid;
254 	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
255 	switch (saddr->sin_family) {
256 	case AF_INET:
257 		rp->rc_flag |= RC_INETADDR;
258 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
259 		break;
260 	case AF_ISO:
261 	default:
262 		rp->rc_flag |= RC_NAM;
263 		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
264 		break;
265 	};
266 	rp->rc_proc = nd->nd_procnum;
267 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
268 	rp->rc_flag &= ~RC_LOCKED;
269 	if (rp->rc_flag & RC_WANTED) {
270 		rp->rc_flag &= ~RC_WANTED;
271 		wakeup((caddr_t)rp);
272 	}
273 	return (RC_DOIT);
274 }
275 
276 /*
277  * Update a request cache entry after the rpc has been done
278  */
279 void
280 nfsrv_updatecache(nd, repvalid, repmbuf)
281 	struct nfsrv_descript *nd;
282 	int repvalid;
283 	struct mbuf *repmbuf;
284 {
285 	struct nfsrvcache *rp;
286 
287 	if (!nd->nd_nam2)
288 		return;
289 loop:
290 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
291 	    rp = rp->rc_hash.le_next) {
292 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
293 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
294 			if ((rp->rc_flag & RC_LOCKED) != 0) {
295 				rp->rc_flag |= RC_WANTED;
296 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
297 				goto loop;
298 			}
299 			rp->rc_flag |= RC_LOCKED;
300 			rp->rc_state = RC_DONE;
301 			/*
302 			 * If we have a valid reply update status and save
303 			 * the reply for non-idempotent rpc's.
304 			 */
305 			if (repvalid && nonidempotent[nd->nd_procnum]) {
306 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
307 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
308 					rp->rc_status = nd->nd_repstat;
309 					rp->rc_flag |= RC_REPSTATUS;
310 				} else {
311 					rp->rc_reply = m_copym(repmbuf,
312 						0, M_COPYALL, M_WAIT);
313 					rp->rc_flag |= RC_REPMBUF;
314 				}
315 			}
316 			rp->rc_flag &= ~RC_LOCKED;
317 			if (rp->rc_flag & RC_WANTED) {
318 				rp->rc_flag &= ~RC_WANTED;
319 				wakeup((caddr_t)rp);
320 			}
321 			return;
322 		}
323 	}
324 }
325 
326 /*
327  * Clean out the cache. Called when the last nfsd terminates.
328  */
329 void
330 nfsrv_cleancache()
331 {
332 	struct nfsrvcache *rp, *nextrp;
333 
334 	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
335 		nextrp = rp->rc_lru.tqe_next;
336 		LIST_REMOVE(rp, rc_hash);
337 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
338 		free(rp, M_NFSD);
339 	}
340 	numnfsrvcache = 0;
341 }
342