nfs_srvcache.c revision 1.12
1/*	$OpenBSD: nfs_srvcache.c,v 1.12 2004/12/26 21:22:14 miod Exp $	*/
2/*	$NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl Exp $	*/
3
4/*
5 * Copyright (c) 1989, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Rick Macklem at The University of Guelph.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *	@(#)nfs_srvcache.c	8.3 (Berkeley) 3/30/95
36 */
37
38/*
39 * Reference: Chet Juszczak, "Improving the Performance and Correctness
40 *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
41 *		pages 53-63. San Diego, February 1989.
42 */
43#include <sys/param.h>
44#include <sys/vnode.h>
45#include <sys/mount.h>
46#include <sys/kernel.h>
47#include <sys/systm.h>
48#include <sys/proc.h>
49#include <sys/mbuf.h>
50#include <sys/malloc.h>
51#include <sys/socket.h>
52#include <sys/socketvar.h>
53
54#include <netinet/in.h>
55#include <nfs/nfsm_subs.h>
56#include <nfs/rpcv2.h>
57#include <nfs/nfsproto.h>
58#include <nfs/nfs.h>
59#include <nfs/nfsrvcache.h>
60#include <nfs/nfs_var.h>
61
62extern struct nfsstats nfsstats;
63extern int nfsv2_procid[NFS_NPROCS];
64long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
65
66#define	NFSRCHASH(xid) \
67	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
68LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
69TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
70u_long nfsrvhash;
71
72#define TRUE	1
73#define	FALSE	0
74
75#define	NETFAMILY(rp) \
76		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_UNSPEC)
77
78/*
79 * Static array that defines which nfs rpc's are nonidempotent
80 */
81int 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! */
111static 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 */
135void
136nfsrv_initcache()
137{
138
139	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash);
140	TAILQ_INIT(&nfsrvlruhead);
141}
142
143/*
144 * Look for the request in the cache
145 * If found then
146 *    return action and optionally reply
147 * else
148 *    insert it in the cache
149 *
150 * The rules are as follows:
151 * - if in progress, return DROP request
152 * - if completed within DELAY of the current time, return DROP it
153 * - if completed a longer time ago return REPLY if the reply was cached or
154 *   return DOIT
155 * Update/add new request at end of lru list
156 */
157int
158nfsrv_getcache(nd, slp, repp)
159	struct nfsrv_descript *nd;
160	struct nfssvc_sock *slp;
161	struct mbuf **repp;
162{
163	struct nfsrvcache *rp;
164	struct mbuf *mb;
165	struct sockaddr_in *saddr;
166	caddr_t bpos;
167	int ret;
168
169	/*
170	 * Don't cache recent requests for reliable transport protocols.
171	 * (Maybe we should for the case of a reconnect, but..)
172	 */
173	if (!nd->nd_nam2)
174		return (RC_DOIT);
175loop:
176	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
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			if ((rp->rc_flag & RC_LOCKED) != 0) {
180				rp->rc_flag |= RC_WANTED;
181				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
182				goto loop;
183			}
184			rp->rc_flag |= RC_LOCKED;
185			/* If not at end of LRU chain, move it there */
186			if (TAILQ_NEXT(rp, rc_lru)) {
187				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
188				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
189			}
190			if (rp->rc_state == RC_UNUSED)
191				panic("nfsrv cache");
192			if (rp->rc_state == RC_INPROG) {
193				nfsstats.srvcache_inproghits++;
194				ret = RC_DROPIT;
195			} else if (rp->rc_flag & RC_REPSTATUS) {
196				nfsstats.srvcache_nonidemdonehits++;
197				nfs_rephead(0, nd, slp, rp->rc_status,
198				   (u_quad_t *)0, repp, &mb, &bpos);
199				ret = RC_REPLY;
200			} else if (rp->rc_flag & RC_REPMBUF) {
201				nfsstats.srvcache_nonidemdonehits++;
202				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
203						M_WAIT);
204				ret = RC_REPLY;
205			} else {
206				nfsstats.srvcache_idemdonehits++;
207				rp->rc_state = RC_INPROG;
208				ret = RC_DOIT;
209			}
210			rp->rc_flag &= ~RC_LOCKED;
211			if (rp->rc_flag & RC_WANTED) {
212				rp->rc_flag &= ~RC_WANTED;
213				wakeup((caddr_t)rp);
214			}
215			return (ret);
216		}
217	}
218	nfsstats.srvcache_misses++;
219	if (numnfsrvcache < desirednfsrvcache) {
220		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
221		    M_NFSD, M_WAITOK);
222		bzero((char *)rp, sizeof *rp);
223		numnfsrvcache++;
224		rp->rc_flag = RC_LOCKED;
225	} else {
226		rp = TAILQ_FIRST(&nfsrvlruhead);
227		while ((rp->rc_flag & RC_LOCKED) != 0) {
228			rp->rc_flag |= RC_WANTED;
229			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
230			rp = TAILQ_FIRST(&nfsrvlruhead);
231		}
232		rp->rc_flag |= RC_LOCKED;
233		LIST_REMOVE(rp, rc_hash);
234		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
235		if (rp->rc_flag & RC_REPMBUF)
236			m_freem(rp->rc_reply);
237		if (rp->rc_flag & RC_NAM)
238			MFREE(rp->rc_nam, mb);
239		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
240	}
241	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
242	rp->rc_state = RC_INPROG;
243	rp->rc_xid = nd->nd_retxid;
244	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
245	switch (saddr->sin_family) {
246	case AF_INET:
247		rp->rc_flag |= RC_INETADDR;
248		rp->rc_inetaddr = saddr->sin_addr.s_addr;
249		break;
250	default:
251		rp->rc_flag |= RC_NAM;
252		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
253		break;
254	};
255	rp->rc_proc = nd->nd_procnum;
256	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
257	rp->rc_flag &= ~RC_LOCKED;
258	if (rp->rc_flag & RC_WANTED) {
259		rp->rc_flag &= ~RC_WANTED;
260		wakeup((caddr_t)rp);
261	}
262	return (RC_DOIT);
263}
264
265/*
266 * Update a request cache entry after the rpc has been done
267 */
268void
269nfsrv_updatecache(nd, repvalid, repmbuf)
270	struct nfsrv_descript *nd;
271	int repvalid;
272	struct mbuf *repmbuf;
273{
274	struct nfsrvcache *rp;
275
276	if (!nd->nd_nam2)
277		return;
278loop:
279	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
280	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
281		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
282			if ((rp->rc_flag & RC_LOCKED) != 0) {
283				rp->rc_flag |= RC_WANTED;
284				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
285				goto loop;
286			}
287			rp->rc_flag |= RC_LOCKED;
288			rp->rc_state = RC_DONE;
289			/*
290			 * If we have a valid reply update status and save
291			 * the reply for non-idempotent rpc's.
292			 */
293			if (repvalid && nonidempotent[nd->nd_procnum]) {
294				if ((nd->nd_flag & ND_NFSV3) == 0 &&
295				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
296					rp->rc_status = nd->nd_repstat;
297					rp->rc_flag |= RC_REPSTATUS;
298				} else {
299					rp->rc_reply = m_copym(repmbuf,
300						0, M_COPYALL, M_WAIT);
301					rp->rc_flag |= RC_REPMBUF;
302				}
303			}
304			rp->rc_flag &= ~RC_LOCKED;
305			if (rp->rc_flag & RC_WANTED) {
306				rp->rc_flag &= ~RC_WANTED;
307				wakeup((caddr_t)rp);
308			}
309			return;
310		}
311	}
312}
313
314/*
315 * Clean out the cache. Called when the last nfsd terminates.
316 */
317void
318nfsrv_cleancache()
319{
320	struct nfsrvcache *rp, *nextrp;
321
322	for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != NULL; rp = nextrp) {
323		nextrp = TAILQ_NEXT(rp, rc_lru);
324		LIST_REMOVE(rp, rc_hash);
325		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
326		free(rp, M_NFSD);
327	}
328	numnfsrvcache = 0;
329}
330