xref: /dragonfly/sys/vfs/nfs/nfs_srvcache.c (revision e0b1d537)
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 #define	NETFAMILY(rp) \
69 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
70 
71 struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token);
72 
73 /*
74  * Static array that defines which nfs rpc's are nonidempotent
75  */
76 static int nonidempotent[NFS_NPROCS] = {
77 	FALSE,
78 	FALSE,
79 	TRUE,
80 	FALSE,
81 	FALSE,
82 	FALSE,
83 	FALSE,
84 	TRUE,
85 	TRUE,
86 	TRUE,
87 	TRUE,
88 	TRUE,
89 	TRUE,
90 	TRUE,
91 	TRUE,
92 	TRUE,
93 	FALSE,
94 	FALSE,
95 	FALSE,
96 	FALSE,
97 	FALSE,
98 	FALSE,
99 	FALSE,
100 	FALSE,
101 	FALSE,
102 	FALSE,
103 };
104 
105 /* True iff the rpc reply is an nfs status ONLY! */
106 static int nfsv2_repstat[NFS_NPROCS] = {
107 	FALSE,
108 	FALSE,
109 	FALSE,
110 	FALSE,
111 	FALSE,
112 	FALSE,
113 	FALSE,
114 	FALSE,
115 	FALSE,
116 	FALSE,
117 	TRUE,
118 	TRUE,
119 	TRUE,
120 	TRUE,
121 	FALSE,
122 	TRUE,
123 	FALSE,
124 	FALSE,
125 };
126 
127 /*
128  * Size the NFS server's duplicate request cache at 1/2 the nmbclusters,
129  * within a (64, 2048) range. This is to prevent all mbuf clusters being
130  * tied up in the NFS dupreq cache for small values of nmbclusters.
131  */
132 static void
133 nfsrvcache_size_change(void)
134 {
135 	desirednfsrvcache = nmbclusters / 2;
136 	desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE);
137 	desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE);
138 }
139 
140 /*
141  * Initialize the server request cache list
142  */
143 void
144 nfsrv_initcache(void)
145 {
146 	nfsrvcache_size_change();
147 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &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(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
167 	       struct mbuf **repp)
168 {
169 	struct nfsrvcache *rp;
170 	struct mbuf *mb;
171 	struct sockaddr_in *saddr;
172 	caddr_t bpos;
173 	int ret;
174 
175 	/*
176 	 * Don't cache recent requests for reliable transport protocols.
177 	 * (Maybe we should for the case of a reconnect, but..)
178 	 */
179 	if (!nd->nd_nam2)
180 		return (RC_DOIT);
181 
182 	lwkt_gettoken(&srvcache_token);
183 loop:
184 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
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 		        NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
189 			if ((rp->rc_flag & RC_LOCKED) != 0) {
190 				rp->rc_flag |= RC_WANTED;
191 				tsleep((caddr_t)rp, 0, "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 (TAILQ_NEXT(rp, rc_lru) != NULL) {
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 					    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 						MB_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 			lwkt_reltoken(&srvcache_token);
226 			return (ret);
227 		}
228 	}
229 
230 	nfsstats.srvcache_misses++;
231 	NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
232 	if (numnfsrvcache < desirednfsrvcache) {
233 		rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
234 		numnfsrvcache++;
235 		rp->rc_flag = RC_LOCKED;
236 	} else {
237 		rp = TAILQ_FIRST(&nfsrvlruhead);
238 		while ((rp->rc_flag & RC_LOCKED) != 0) {
239 			rp->rc_flag |= RC_WANTED;
240 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
241 			rp = TAILQ_FIRST(&nfsrvlruhead);
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 			rp->rc_reply = NULL;
249 			rp->rc_flag &= ~RC_REPMBUF;
250 		}
251 		if (rp->rc_flag & RC_NAM) {
252 			kfree(rp->rc_nam, M_SONAME);
253 			rp->rc_nam = NULL;
254 			rp->rc_flag &= ~RC_NAM;
255 		}
256 	}
257 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
258 
259 	rp->rc_state = RC_INPROG;
260 	rp->rc_xid = nd->nd_retxid;
261 	saddr = (struct sockaddr_in *)nd->nd_nam;
262 	switch (saddr->sin_family) {
263 	case AF_INET:
264 		rp->rc_flag |= RC_INETADDR;
265 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
266 		break;
267 	case AF_ISO:
268 	default:
269 		rp->rc_flag |= RC_NAM;
270 		rp->rc_nam = dup_sockaddr(nd->nd_nam);
271 		break;
272 	}
273 	rp->rc_proc = nd->nd_procnum;
274 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
275 	rp->rc_flag &= ~RC_LOCKED;
276 	if (rp->rc_flag & RC_WANTED) {
277 		rp->rc_flag &= ~RC_WANTED;
278 		wakeup((caddr_t)rp);
279 	}
280 	lwkt_reltoken(&srvcache_token);
281 
282 	return (RC_DOIT);
283 }
284 
285 /*
286  * Update a request cache entry after the rpc has been done
287  */
288 void
289 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
290 {
291 	struct nfsrvcache *rp;
292 
293 	if (!nd->nd_nam2)
294 		return;
295 
296 	lwkt_gettoken(&srvcache_token);
297 loop:
298 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
299 	    rp = rp->rc_hash.le_next) {
300 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
301 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
302 			NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
303 			if ((rp->rc_flag & RC_LOCKED) != 0) {
304 				rp->rc_flag |= RC_WANTED;
305 				tsleep((caddr_t)rp, 0, "nfsrc", 0);
306 				goto loop;
307 			}
308 			rp->rc_flag |= RC_LOCKED;
309 			if (rp->rc_state == RC_DONE) {
310 				/*
311 				 * This can occur if the cache is too small.
312 				 * Retransmits of the same request aren't
313 				 * dropped so we may see the operation
314 				 * complete more then once.
315 				 */
316 				if (rp->rc_flag & RC_REPMBUF) {
317 					m_freem(rp->rc_reply);
318 					rp->rc_reply = NULL;
319 					rp->rc_flag &= ~RC_REPMBUF;
320 				}
321 			}
322 			rp->rc_state = RC_DONE;
323 
324 			/*
325 			 * If we have a valid reply update status and save
326 			 * the reply for non-idempotent rpc's.
327 			 */
328 			if (repvalid && nonidempotent[nd->nd_procnum]) {
329 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
330 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
331 					rp->rc_status = nd->nd_repstat;
332 					rp->rc_flag |= RC_REPSTATUS;
333 				} else {
334 					if (rp->rc_flag & RC_REPMBUF) {
335 						m_freem(rp->rc_reply);
336 						rp->rc_reply = NULL;
337 						rp->rc_flag &= ~RC_REPMBUF;
338 					}
339 					rp->rc_reply = m_copym(repmbuf, 0,
340 							M_COPYALL, MB_WAIT);
341 					rp->rc_flag |= RC_REPMBUF;
342 				}
343 			}
344 			rp->rc_flag &= ~RC_LOCKED;
345 			if (rp->rc_flag & RC_WANTED) {
346 				rp->rc_flag &= ~RC_WANTED;
347 				wakeup((caddr_t)rp);
348 			}
349 			break;
350 		}
351 	}
352 	lwkt_reltoken(&srvcache_token);
353 	NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
354 }
355 
356 /*
357  * Clean out the cache. Called when the last nfsd terminates.
358  */
359 void
360 nfsrv_cleancache(void)
361 {
362 	struct nfsrvcache *rp;
363 
364 	lwkt_gettoken(&srvcache_token);
365 	while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
366 		if (rp->rc_flag & RC_LOCKED) {
367 			rp->rc_flag |= RC_WANTED;
368 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
369 			continue;
370 		}
371 		LIST_REMOVE(rp, rc_hash);
372 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
373 		if (rp->rc_flag & RC_REPMBUF) {
374 			m_freem(rp->rc_reply);
375 			rp->rc_reply = NULL;
376 			rp->rc_flag &= ~RC_REPMBUF;
377 		}
378 		if (rp->rc_flag & RC_NAM) {
379 			kfree(rp->rc_nam, M_SONAME);
380 			rp->rc_nam = NULL;
381 			rp->rc_flag &= ~RC_NAM;
382 		}
383 		kfree(rp, M_NFSD);
384 	}
385 	numnfsrvcache = 0;
386 	lwkt_reltoken(&srvcache_token);
387 }
388 
389 #endif /* NFS_NOSERVER */
390