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