xref: /dragonfly/sys/vfs/nfs/nfs_srvcache.c (revision 81c11cd3)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	@(#)nfs_srvcache.c	8.3 (Berkeley) 3/30/95
37  * $FreeBSD: src/sys/nfs/nfs_srvcache.c,v 1.21 2000/02/13 03:32:06 peter Exp $
38  * $DragonFly: src/sys/vfs/nfs/nfs_srvcache.c,v 1.13 2008/01/05 14:02:41 swildner Exp $
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/malloc.h>
48 #include <sys/mount.h>
49 #include <sys/systm.h>
50 #include <sys/mbuf.h>
51 #include <sys/socket.h>
52 #include <sys/socketvar.h>	/* for dup_sockaddr */
53 
54 #include <netinet/in.h>
55 #include "rpcv2.h"
56 #include "nfsproto.h"
57 #include "nfs.h"
58 #include "nfsrvcache.h"
59 
60 #ifndef NFS_NOSERVER
61 static long numnfsrvcache;
62 static long desirednfsrvcache = NFSRVCACHESIZ;
63 
64 #define	NFSRCHASH(xid) \
65 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
66 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
67 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
68 static u_long nfsrvhash;
69 
70 #define TRUE	1
71 #define	FALSE	0
72 
73 #define	NETFAMILY(rp) \
74 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
75 
76 struct lwkt_token srvcache_token = LWKT_TOKEN_MP_INITIALIZER(srvcache_token);
77 
78 /*
79  * Static array that defines which nfs rpc's are nonidempotent
80  */
81 static int nonidempotent[NFS_NPROCS] = {
82 	FALSE,
83 	FALSE,
84 	TRUE,
85 	FALSE,
86 	FALSE,
87 	FALSE,
88 	FALSE,
89 	TRUE,
90 	TRUE,
91 	TRUE,
92 	TRUE,
93 	TRUE,
94 	TRUE,
95 	TRUE,
96 	TRUE,
97 	TRUE,
98 	FALSE,
99 	FALSE,
100 	FALSE,
101 	FALSE,
102 	FALSE,
103 	FALSE,
104 	FALSE,
105 	FALSE,
106 	FALSE,
107 	FALSE,
108 };
109 
110 /* True iff the rpc reply is an nfs status ONLY! */
111 static int nfsv2_repstat[NFS_NPROCS] = {
112 	FALSE,
113 	FALSE,
114 	FALSE,
115 	FALSE,
116 	FALSE,
117 	FALSE,
118 	FALSE,
119 	FALSE,
120 	FALSE,
121 	FALSE,
122 	TRUE,
123 	TRUE,
124 	TRUE,
125 	TRUE,
126 	FALSE,
127 	TRUE,
128 	FALSE,
129 	FALSE,
130 };
131 
132 /*
133  * Initialize the server request cache list
134  */
135 void
136 nfsrv_initcache(void)
137 {
138 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
139 	TAILQ_INIT(&nfsrvlruhead);
140 }
141 
142 /*
143  * Look for the request in the cache
144  * If found then
145  *    return action and optionally reply
146  * else
147  *    insert it in the cache
148  *
149  * The rules are as follows:
150  * - if in progress, return DROP request
151  * - if completed within DELAY of the current time, return DROP it
152  * - if completed a longer time ago return REPLY if the reply was cached or
153  *   return DOIT
154  * Update/add new request at end of lru list
155  */
156 int
157 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
158 	       struct mbuf **repp)
159 {
160 	struct nfsrvcache *rp;
161 	struct mbuf *mb;
162 	struct sockaddr_in *saddr;
163 	caddr_t bpos;
164 	int ret;
165 
166 	/*
167 	 * Don't cache recent requests for reliable transport protocols.
168 	 * (Maybe we should for the case of a reconnect, but..)
169 	 */
170 	if (!nd->nd_nam2)
171 		return (RC_DOIT);
172 
173 	lwkt_gettoken(&srvcache_token);
174 loop:
175 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
176 	    rp = rp->rc_hash.le_next) {
177 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
178 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
179 		        NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
180 			if ((rp->rc_flag & RC_LOCKED) != 0) {
181 				rp->rc_flag |= RC_WANTED;
182 				tsleep((caddr_t)rp, 0, "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 (TAILQ_NEXT(rp, rc_lru) != NULL) {
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, slp, rp->rc_status,
199 					    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 						MB_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 			lwkt_reltoken(&srvcache_token);
217 			return (ret);
218 		}
219 	}
220 
221 	nfsstats.srvcache_misses++;
222 	NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
223 	if (numnfsrvcache < desirednfsrvcache) {
224 		rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
225 		numnfsrvcache++;
226 		rp->rc_flag = RC_LOCKED;
227 	} else {
228 		rp = TAILQ_FIRST(&nfsrvlruhead);
229 		while ((rp->rc_flag & RC_LOCKED) != 0) {
230 			rp->rc_flag |= RC_WANTED;
231 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
232 			rp = TAILQ_FIRST(&nfsrvlruhead);
233 		}
234 		rp->rc_flag |= RC_LOCKED;
235 		LIST_REMOVE(rp, rc_hash);
236 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
237 		if (rp->rc_flag & RC_REPMBUF) {
238 			m_freem(rp->rc_reply);
239 			rp->rc_reply = NULL;
240 			rp->rc_flag &= ~RC_REPMBUF;
241 		}
242 		if (rp->rc_flag & RC_NAM) {
243 			FREE(rp->rc_nam, M_SONAME);
244 			rp->rc_nam = NULL;
245 			rp->rc_flag &= ~RC_NAM;
246 		}
247 	}
248 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
249 
250 	rp->rc_state = RC_INPROG;
251 	rp->rc_xid = nd->nd_retxid;
252 	saddr = (struct sockaddr_in *)nd->nd_nam;
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 = dup_sockaddr(nd->nd_nam);
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 	lwkt_reltoken(&srvcache_token);
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(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
281 {
282 	struct nfsrvcache *rp;
283 
284 	if (!nd->nd_nam2)
285 		return;
286 
287 	lwkt_gettoken(&srvcache_token);
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 			NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
294 			if ((rp->rc_flag & RC_LOCKED) != 0) {
295 				rp->rc_flag |= RC_WANTED;
296 				tsleep((caddr_t)rp, 0, "nfsrc", 0);
297 				goto loop;
298 			}
299 			rp->rc_flag |= RC_LOCKED;
300 			if (rp->rc_state == RC_DONE) {
301 				/*
302 				 * This can occur if the cache is too small.
303 				 * Retransmits of the same request aren't
304 				 * dropped so we may see the operation
305 				 * complete more then once.
306 				 */
307 				if (rp->rc_flag & RC_REPMBUF) {
308 					m_freem(rp->rc_reply);
309 					rp->rc_reply = NULL;
310 					rp->rc_flag &= ~RC_REPMBUF;
311 				}
312 			}
313 			rp->rc_state = RC_DONE;
314 
315 			/*
316 			 * If we have a valid reply update status and save
317 			 * the reply for non-idempotent rpc's.
318 			 */
319 			if (repvalid && nonidempotent[nd->nd_procnum]) {
320 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
321 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
322 					rp->rc_status = nd->nd_repstat;
323 					rp->rc_flag |= RC_REPSTATUS;
324 				} else {
325 					if (rp->rc_flag & RC_REPMBUF) {
326 						m_freem(rp->rc_reply);
327 						rp->rc_reply = NULL;
328 						rp->rc_flag &= ~RC_REPMBUF;
329 					}
330 					rp->rc_reply = m_copym(repmbuf, 0,
331 							M_COPYALL, MB_WAIT);
332 					rp->rc_flag |= RC_REPMBUF;
333 				}
334 			}
335 			rp->rc_flag &= ~RC_LOCKED;
336 			if (rp->rc_flag & RC_WANTED) {
337 				rp->rc_flag &= ~RC_WANTED;
338 				wakeup((caddr_t)rp);
339 			}
340 			break;
341 		}
342 	}
343 	lwkt_reltoken(&srvcache_token);
344 	NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
345 }
346 
347 /*
348  * Clean out the cache. Called when the last nfsd terminates.
349  */
350 void
351 nfsrv_cleancache(void)
352 {
353 	struct nfsrvcache *rp;
354 
355 	lwkt_gettoken(&srvcache_token);
356 	while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
357 		if (rp->rc_flag & RC_LOCKED) {
358 			rp->rc_flag |= RC_WANTED;
359 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
360 			continue;
361 		}
362 		LIST_REMOVE(rp, rc_hash);
363 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
364 		if (rp->rc_flag & RC_REPMBUF) {
365 			m_freem(rp->rc_reply);
366 			rp->rc_reply = NULL;
367 			rp->rc_flag &= ~RC_REPMBUF;
368 		}
369 		if (rp->rc_flag & RC_NAM) {
370 			kfree(rp->rc_nam, M_SONAME);
371 			rp->rc_nam = NULL;
372 			rp->rc_flag &= ~RC_NAM;
373 		}
374 		kfree(rp, M_NFSD);
375 	}
376 	numnfsrvcache = 0;
377 	lwkt_reltoken(&srvcache_token);
378 }
379 
380 #endif /* NFS_NOSERVER */
381