xref: /dragonfly/sys/vfs/nfs/nfs_srvcache.c (revision 6a3cbbc2)
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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *	@(#)nfs_srvcache.c	8.3 (Berkeley) 3/30/95
33  * $FreeBSD: src/sys/nfs/nfs_srvcache.c,v 1.21 2000/02/13 03:32:06 peter Exp $
34  */
35 
36 /*
37  * Reference: Chet Juszczak, "Improving the Performance and Correctness
38  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
39  *		pages 53-63. San Diego, February 1989.
40  */
41 #include <sys/param.h>
42 #include <sys/malloc.h>
43 #include <sys/mount.h>
44 #include <sys/systm.h>
45 #include <sys/mbuf.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>	/* for dup_sockaddr */
48 
49 #include <netinet/in.h>
50 #include "rpcv2.h"
51 #include "nfsproto.h"
52 #include "nfs.h"
53 #include "nfsrvcache.h"
54 
55 #ifndef NFS_NOSERVER
56 static long numnfsrvcache;
57 static long desirednfsrvcache;
58 
59 #define	NFSRCHASH(xid) \
60 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
61 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
62 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
63 static u_long nfsrvhash;
64 
65 #define TRUE	1
66 #define	FALSE	0
67 
68 struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token);
69 
70 /*
71  * Static array that defines which nfs rpc's are nonidempotent
72  */
73 static int nonidempotent[NFS_NPROCS] = {
74 	FALSE,
75 	FALSE,
76 	TRUE,
77 	FALSE,
78 	FALSE,
79 	FALSE,
80 	FALSE,
81 	TRUE,
82 	TRUE,
83 	TRUE,
84 	TRUE,
85 	TRUE,
86 	TRUE,
87 	TRUE,
88 	TRUE,
89 	TRUE,
90 	FALSE,
91 	FALSE,
92 	FALSE,
93 	FALSE,
94 	FALSE,
95 	FALSE,
96 	FALSE,
97 	FALSE,
98 	FALSE,
99 	FALSE,
100 };
101 
102 /* True iff the rpc reply is an nfs status ONLY! */
103 static int nfsv2_repstat[NFS_NPROCS] = {
104 	FALSE,
105 	FALSE,
106 	FALSE,
107 	FALSE,
108 	FALSE,
109 	FALSE,
110 	FALSE,
111 	FALSE,
112 	FALSE,
113 	FALSE,
114 	TRUE,
115 	TRUE,
116 	TRUE,
117 	TRUE,
118 	FALSE,
119 	TRUE,
120 	FALSE,
121 	FALSE,
122 };
123 
124 /*
125  * Size the NFS server's duplicate request cache at 1/2 the nmbclusters,
126  * within a (64, 2048) range. This is to prevent all mbuf clusters being
127  * tied up in the NFS dupreq cache for small values of nmbclusters.
128  */
129 static void
130 nfsrvcache_size_change(void)
131 {
132 	desirednfsrvcache = nmbclusters / 2;
133 	desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE);
134 	desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE);
135 }
136 
137 /*
138  * Initialize the server request cache list
139  */
140 void
141 nfsrv_initcache(void)
142 {
143 	nfsrvcache_size_change();
144 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
145 	TAILQ_INIT(&nfsrvlruhead);
146 }
147 
148 /*
149  * Destroy the server request cache list
150  */
151 void
152 nfsrv_destroycache(void)
153 {
154 	KKASSERT(TAILQ_EMPTY(&nfsrvlruhead));
155 	hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
156 }
157 
158 /*
159  * Look for the request in the cache
160  * If found then
161  *    return action and optionally reply
162  * else
163  *    insert it in the cache
164  *
165  * The rules are as follows:
166  * - if in progress, return DROP request
167  * - if completed within DELAY of the current time, return DROP it
168  * - if completed a longer time ago return REPLY if the reply was cached or
169  *   return DOIT
170  * Update/add new request at end of lru list
171  */
172 int
173 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
174 	       struct mbuf **repp)
175 {
176 	struct nfsrvcache *rp;
177 	struct mbuf *mb;
178 	struct sockaddr_in *saddr;
179 	caddr_t bpos;
180 	int ret;
181 
182 	/*
183 	 * Don't cache recent requests for reliable transport protocols.
184 	 * (Maybe we should for the case of a reconnect, but..)
185 	 */
186 	if (!nd->nd_nam2)
187 		return (RC_DOIT);
188 
189 	lwkt_gettoken(&srvcache_token);
190 loop:
191 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
192 	    rp = rp->rc_hash.le_next) {
193 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
194 		netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
195 		        NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
196 			if ((rp->rc_flag & RC_LOCKED) != 0) {
197 				rp->rc_flag |= RC_WANTED;
198 				tsleep((caddr_t)rp, 0, "nfsrc", 0);
199 				goto loop;
200 			}
201 			rp->rc_flag |= RC_LOCKED;
202 			/* If not at end of LRU chain, move it there */
203 			if (TAILQ_NEXT(rp, rc_lru) != NULL) {
204 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
205 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
206 			}
207 			if (rp->rc_state == RC_UNUSED)
208 				panic("nfsrv cache");
209 			if (rp->rc_state == RC_INPROG) {
210 				nfsstats.srvcache_inproghits++;
211 				ret = RC_DROPIT;
212 			} else if (rp->rc_flag & RC_REPSTATUS) {
213 				nfsstats.srvcache_nonidemdonehits++;
214 				nfs_rephead(0, nd, slp, rp->rc_status,
215 					    repp, &mb, &bpos);
216 				ret = RC_REPLY;
217 			} else if (rp->rc_flag & RC_REPMBUF) {
218 				nfsstats.srvcache_nonidemdonehits++;
219 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
220 						M_WAITOK);
221 				ret = RC_REPLY;
222 			} else {
223 				nfsstats.srvcache_idemdonehits++;
224 				rp->rc_state = RC_INPROG;
225 				ret = RC_DOIT;
226 			}
227 			rp->rc_flag &= ~RC_LOCKED;
228 			if (rp->rc_flag & RC_WANTED) {
229 				rp->rc_flag &= ~RC_WANTED;
230 				wakeup((caddr_t)rp);
231 			}
232 			lwkt_reltoken(&srvcache_token);
233 			return (ret);
234 		}
235 	}
236 
237 	nfsstats.srvcache_misses++;
238 	NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
239 	if (numnfsrvcache < desirednfsrvcache) {
240 		rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
241 		numnfsrvcache++;
242 		rp->rc_flag = RC_LOCKED;
243 	} else {
244 		rp = TAILQ_FIRST(&nfsrvlruhead);
245 		while ((rp->rc_flag & RC_LOCKED) != 0) {
246 			rp->rc_flag |= RC_WANTED;
247 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
248 			rp = TAILQ_FIRST(&nfsrvlruhead);
249 		}
250 		rp->rc_flag |= RC_LOCKED;
251 		LIST_REMOVE(rp, rc_hash);
252 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
253 		if (rp->rc_flag & RC_REPMBUF) {
254 			m_freem(rp->rc_reply);
255 			rp->rc_reply = NULL;
256 			rp->rc_flag &= ~RC_REPMBUF;
257 		}
258 		if (rp->rc_flag & RC_NAM) {
259 			kfree(rp->rc_nam, M_SONAME);
260 			rp->rc_nam = NULL;
261 			rp->rc_flag &= ~RC_NAM;
262 		}
263 	}
264 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
265 
266 	rp->rc_state = RC_INPROG;
267 	rp->rc_xid = nd->nd_retxid;
268 	saddr = (struct sockaddr_in *)nd->nd_nam;
269 	switch (saddr->sin_family) {
270 	case AF_INET:
271 		rp->rc_flag |= RC_INETADDR;
272 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
273 		break;
274 	default:
275 		rp->rc_flag |= RC_NAM;
276 		rp->rc_nam = dup_sockaddr(nd->nd_nam);
277 		break;
278 	}
279 	rp->rc_proc = nd->nd_procnum;
280 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
281 	rp->rc_flag &= ~RC_LOCKED;
282 	if (rp->rc_flag & RC_WANTED) {
283 		rp->rc_flag &= ~RC_WANTED;
284 		wakeup((caddr_t)rp);
285 	}
286 	lwkt_reltoken(&srvcache_token);
287 
288 	return (RC_DOIT);
289 }
290 
291 /*
292  * Update a request cache entry after the rpc has been done
293  */
294 void
295 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
296 {
297 	struct nfsrvcache *rp;
298 
299 	if (!nd->nd_nam2)
300 		return;
301 
302 	lwkt_gettoken(&srvcache_token);
303 loop:
304 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
305 	    rp = rp->rc_hash.le_next) {
306 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
307 		netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
308 			NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
309 			if ((rp->rc_flag & RC_LOCKED) != 0) {
310 				rp->rc_flag |= RC_WANTED;
311 				tsleep((caddr_t)rp, 0, "nfsrc", 0);
312 				goto loop;
313 			}
314 			rp->rc_flag |= RC_LOCKED;
315 			if (rp->rc_state == RC_DONE) {
316 				/*
317 				 * This can occur if the cache is too small.
318 				 * Retransmits of the same request aren't
319 				 * dropped so we may see the operation
320 				 * complete more then once.
321 				 */
322 				if (rp->rc_flag & RC_REPMBUF) {
323 					m_freem(rp->rc_reply);
324 					rp->rc_reply = NULL;
325 					rp->rc_flag &= ~RC_REPMBUF;
326 				}
327 			}
328 			rp->rc_state = RC_DONE;
329 
330 			/*
331 			 * If we have a valid reply update status and save
332 			 * the reply for non-idempotent rpc's.
333 			 */
334 			if (repvalid && nonidempotent[nd->nd_procnum]) {
335 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
336 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
337 					rp->rc_status = nd->nd_repstat;
338 					rp->rc_flag |= RC_REPSTATUS;
339 				} else {
340 					if (rp->rc_flag & RC_REPMBUF) {
341 						m_freem(rp->rc_reply);
342 						rp->rc_reply = NULL;
343 						rp->rc_flag &= ~RC_REPMBUF;
344 					}
345 					rp->rc_reply = m_copym(repmbuf, 0,
346 							M_COPYALL, M_WAITOK);
347 					rp->rc_flag |= RC_REPMBUF;
348 				}
349 			}
350 			rp->rc_flag &= ~RC_LOCKED;
351 			if (rp->rc_flag & RC_WANTED) {
352 				rp->rc_flag &= ~RC_WANTED;
353 				wakeup((caddr_t)rp);
354 			}
355 			break;
356 		}
357 	}
358 	lwkt_reltoken(&srvcache_token);
359 	NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
360 }
361 
362 /*
363  * Clean out the cache. Called when the last nfsd terminates.
364  */
365 void
366 nfsrv_cleancache(void)
367 {
368 	struct nfsrvcache *rp;
369 
370 	lwkt_gettoken(&srvcache_token);
371 	while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
372 		if (rp->rc_flag & RC_LOCKED) {
373 			rp->rc_flag |= RC_WANTED;
374 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
375 			continue;
376 		}
377 		LIST_REMOVE(rp, rc_hash);
378 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
379 		if (rp->rc_flag & RC_REPMBUF) {
380 			m_freem(rp->rc_reply);
381 			rp->rc_reply = NULL;
382 			rp->rc_flag &= ~RC_REPMBUF;
383 		}
384 		if (rp->rc_flag & RC_NAM) {
385 			kfree(rp->rc_nam, M_SONAME);
386 			rp->rc_nam = NULL;
387 			rp->rc_flag &= ~RC_NAM;
388 		}
389 		kfree(rp, M_NFSD);
390 	}
391 	numnfsrvcache = 0;
392 	lwkt_reltoken(&srvcache_token);
393 }
394 
395 #endif /* NFS_NOSERVER */
396