xref: /dragonfly/sys/vfs/nfs/nfs_srvcache.c (revision 8edfbc5e)
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  * Look for the request in the cache
150  * If found then
151  *    return action and optionally reply
152  * else
153  *    insert it in the cache
154  *
155  * The rules are as follows:
156  * - if in progress, return DROP request
157  * - if completed within DELAY of the current time, return DROP it
158  * - if completed a longer time ago return REPLY if the reply was cached or
159  *   return DOIT
160  * Update/add new request at end of lru list
161  */
162 int
163 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
164 	       struct mbuf **repp)
165 {
166 	struct nfsrvcache *rp;
167 	struct mbuf *mb;
168 	struct sockaddr_in *saddr;
169 	caddr_t bpos;
170 	int ret;
171 
172 	/*
173 	 * Don't cache recent requests for reliable transport protocols.
174 	 * (Maybe we should for the case of a reconnect, but..)
175 	 */
176 	if (!nd->nd_nam2)
177 		return (RC_DOIT);
178 
179 	lwkt_gettoken(&srvcache_token);
180 loop:
181 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
182 	    rp = rp->rc_hash.le_next) {
183 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
184 		netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
185 		        NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
186 			if ((rp->rc_flag & RC_LOCKED) != 0) {
187 				rp->rc_flag |= RC_WANTED;
188 				tsleep((caddr_t)rp, 0, "nfsrc", 0);
189 				goto loop;
190 			}
191 			rp->rc_flag |= RC_LOCKED;
192 			/* If not at end of LRU chain, move it there */
193 			if (TAILQ_NEXT(rp, rc_lru) != NULL) {
194 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
195 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
196 			}
197 			if (rp->rc_state == RC_UNUSED)
198 				panic("nfsrv cache");
199 			if (rp->rc_state == RC_INPROG) {
200 				nfsstats.srvcache_inproghits++;
201 				ret = RC_DROPIT;
202 			} else if (rp->rc_flag & RC_REPSTATUS) {
203 				nfsstats.srvcache_nonidemdonehits++;
204 				nfs_rephead(0, nd, slp, rp->rc_status,
205 					    repp, &mb, &bpos);
206 				ret = RC_REPLY;
207 			} else if (rp->rc_flag & RC_REPMBUF) {
208 				nfsstats.srvcache_nonidemdonehits++;
209 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
210 						M_WAITOK);
211 				ret = RC_REPLY;
212 			} else {
213 				nfsstats.srvcache_idemdonehits++;
214 				rp->rc_state = RC_INPROG;
215 				ret = RC_DOIT;
216 			}
217 			rp->rc_flag &= ~RC_LOCKED;
218 			if (rp->rc_flag & RC_WANTED) {
219 				rp->rc_flag &= ~RC_WANTED;
220 				wakeup((caddr_t)rp);
221 			}
222 			lwkt_reltoken(&srvcache_token);
223 			return (ret);
224 		}
225 	}
226 
227 	nfsstats.srvcache_misses++;
228 	NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
229 	if (numnfsrvcache < desirednfsrvcache) {
230 		rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
231 		numnfsrvcache++;
232 		rp->rc_flag = RC_LOCKED;
233 	} else {
234 		rp = TAILQ_FIRST(&nfsrvlruhead);
235 		while ((rp->rc_flag & RC_LOCKED) != 0) {
236 			rp->rc_flag |= RC_WANTED;
237 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
238 			rp = TAILQ_FIRST(&nfsrvlruhead);
239 		}
240 		rp->rc_flag |= RC_LOCKED;
241 		LIST_REMOVE(rp, rc_hash);
242 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
243 		if (rp->rc_flag & RC_REPMBUF) {
244 			m_freem(rp->rc_reply);
245 			rp->rc_reply = NULL;
246 			rp->rc_flag &= ~RC_REPMBUF;
247 		}
248 		if (rp->rc_flag & RC_NAM) {
249 			kfree(rp->rc_nam, M_SONAME);
250 			rp->rc_nam = NULL;
251 			rp->rc_flag &= ~RC_NAM;
252 		}
253 	}
254 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
255 
256 	rp->rc_state = RC_INPROG;
257 	rp->rc_xid = nd->nd_retxid;
258 	saddr = (struct sockaddr_in *)nd->nd_nam;
259 	switch (saddr->sin_family) {
260 	case AF_INET:
261 		rp->rc_flag |= RC_INETADDR;
262 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
263 		break;
264 	default:
265 		rp->rc_flag |= RC_NAM;
266 		rp->rc_nam = dup_sockaddr(nd->nd_nam);
267 		break;
268 	}
269 	rp->rc_proc = nd->nd_procnum;
270 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
271 	rp->rc_flag &= ~RC_LOCKED;
272 	if (rp->rc_flag & RC_WANTED) {
273 		rp->rc_flag &= ~RC_WANTED;
274 		wakeup((caddr_t)rp);
275 	}
276 	lwkt_reltoken(&srvcache_token);
277 
278 	return (RC_DOIT);
279 }
280 
281 /*
282  * Update a request cache entry after the rpc has been done
283  */
284 void
285 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
286 {
287 	struct nfsrvcache *rp;
288 
289 	if (!nd->nd_nam2)
290 		return;
291 
292 	lwkt_gettoken(&srvcache_token);
293 loop:
294 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
295 	    rp = rp->rc_hash.le_next) {
296 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
297 		netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
298 			NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
299 			if ((rp->rc_flag & RC_LOCKED) != 0) {
300 				rp->rc_flag |= RC_WANTED;
301 				tsleep((caddr_t)rp, 0, "nfsrc", 0);
302 				goto loop;
303 			}
304 			rp->rc_flag |= RC_LOCKED;
305 			if (rp->rc_state == RC_DONE) {
306 				/*
307 				 * This can occur if the cache is too small.
308 				 * Retransmits of the same request aren't
309 				 * dropped so we may see the operation
310 				 * complete more then once.
311 				 */
312 				if (rp->rc_flag & RC_REPMBUF) {
313 					m_freem(rp->rc_reply);
314 					rp->rc_reply = NULL;
315 					rp->rc_flag &= ~RC_REPMBUF;
316 				}
317 			}
318 			rp->rc_state = RC_DONE;
319 
320 			/*
321 			 * If we have a valid reply update status and save
322 			 * the reply for non-idempotent rpc's.
323 			 */
324 			if (repvalid && nonidempotent[nd->nd_procnum]) {
325 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
326 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
327 					rp->rc_status = nd->nd_repstat;
328 					rp->rc_flag |= RC_REPSTATUS;
329 				} else {
330 					if (rp->rc_flag & RC_REPMBUF) {
331 						m_freem(rp->rc_reply);
332 						rp->rc_reply = NULL;
333 						rp->rc_flag &= ~RC_REPMBUF;
334 					}
335 					rp->rc_reply = m_copym(repmbuf, 0,
336 							M_COPYALL, M_WAITOK);
337 					rp->rc_flag |= RC_REPMBUF;
338 				}
339 			}
340 			rp->rc_flag &= ~RC_LOCKED;
341 			if (rp->rc_flag & RC_WANTED) {
342 				rp->rc_flag &= ~RC_WANTED;
343 				wakeup((caddr_t)rp);
344 			}
345 			break;
346 		}
347 	}
348 	lwkt_reltoken(&srvcache_token);
349 	NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
350 }
351 
352 /*
353  * Clean out the cache. Called when the last nfsd terminates.
354  */
355 void
356 nfsrv_cleancache(void)
357 {
358 	struct nfsrvcache *rp;
359 
360 	lwkt_gettoken(&srvcache_token);
361 	while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
362 		if (rp->rc_flag & RC_LOCKED) {
363 			rp->rc_flag |= RC_WANTED;
364 			tsleep((caddr_t)rp, 0, "nfsrc", 0);
365 			continue;
366 		}
367 		LIST_REMOVE(rp, rc_hash);
368 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
369 		if (rp->rc_flag & RC_REPMBUF) {
370 			m_freem(rp->rc_reply);
371 			rp->rc_reply = NULL;
372 			rp->rc_flag &= ~RC_REPMBUF;
373 		}
374 		if (rp->rc_flag & RC_NAM) {
375 			kfree(rp->rc_nam, M_SONAME);
376 			rp->rc_nam = NULL;
377 			rp->rc_flag &= ~RC_NAM;
378 		}
379 		kfree(rp, M_NFSD);
380 	}
381 	numnfsrvcache = 0;
382 	lwkt_reltoken(&srvcache_token);
383 }
384 
385 #endif /* NFS_NOSERVER */
386