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