nfs_srvcache.c revision 1.14
1/*	$OpenBSD: nfs_srvcache.c,v 1.14 2007/10/28 14:12:41 thib 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				   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 = malloc(sizeof(*rp), M_NFSD, M_WAITOK|M_ZERO);
221		numnfsrvcache++;
222		rp->rc_flag = RC_LOCKED;
223	} else {
224		rp = TAILQ_FIRST(&nfsrvlruhead);
225		while ((rp->rc_flag & RC_LOCKED) != 0) {
226			rp->rc_flag |= RC_WANTED;
227			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
228			rp = TAILQ_FIRST(&nfsrvlruhead);
229		}
230		rp->rc_flag |= RC_LOCKED;
231		LIST_REMOVE(rp, rc_hash);
232		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
233		if (rp->rc_flag & RC_REPMBUF)
234			m_freem(rp->rc_reply);
235		if (rp->rc_flag & RC_NAM)
236			MFREE(rp->rc_nam, mb);
237		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
238	}
239	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
240	rp->rc_state = RC_INPROG;
241	rp->rc_xid = nd->nd_retxid;
242	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
243	switch (saddr->sin_family) {
244	case AF_INET:
245		rp->rc_flag |= RC_INETADDR;
246		rp->rc_inetaddr = saddr->sin_addr.s_addr;
247		break;
248	default:
249		rp->rc_flag |= RC_NAM;
250		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
251		break;
252	};
253	rp->rc_proc = nd->nd_procnum;
254	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
255	rp->rc_flag &= ~RC_LOCKED;
256	if (rp->rc_flag & RC_WANTED) {
257		rp->rc_flag &= ~RC_WANTED;
258		wakeup((caddr_t)rp);
259	}
260	return (RC_DOIT);
261}
262
263/*
264 * Update a request cache entry after the rpc has been done
265 */
266void
267nfsrv_updatecache(nd, repvalid, repmbuf)
268	struct nfsrv_descript *nd;
269	int repvalid;
270	struct mbuf *repmbuf;
271{
272	struct nfsrvcache *rp;
273
274	if (!nd->nd_nam2)
275		return;
276loop:
277	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
278	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
279		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
280			if ((rp->rc_flag & RC_LOCKED) != 0) {
281				rp->rc_flag |= RC_WANTED;
282				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
283				goto loop;
284			}
285			rp->rc_flag |= RC_LOCKED;
286			rp->rc_state = RC_DONE;
287			/*
288			 * If we have a valid reply update status and save
289			 * the reply for non-idempotent rpc's.
290			 */
291			if (repvalid && nonidempotent[nd->nd_procnum]) {
292				if ((nd->nd_flag & ND_NFSV3) == 0 &&
293				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
294					rp->rc_status = nd->nd_repstat;
295					rp->rc_flag |= RC_REPSTATUS;
296				} else {
297					rp->rc_reply = m_copym(repmbuf,
298						0, M_COPYALL, M_WAIT);
299					rp->rc_flag |= RC_REPMBUF;
300				}
301			}
302			rp->rc_flag &= ~RC_LOCKED;
303			if (rp->rc_flag & RC_WANTED) {
304				rp->rc_flag &= ~RC_WANTED;
305				wakeup((caddr_t)rp);
306			}
307			return;
308		}
309	}
310}
311
312/*
313 * Clean out the cache. Called when the last nfsd terminates.
314 */
315void
316nfsrv_cleancache()
317{
318	struct nfsrvcache *rp, *nextrp;
319
320	for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != NULL; rp = nextrp) {
321		nextrp = TAILQ_NEXT(rp, rc_lru);
322		LIST_REMOVE(rp, rc_hash);
323		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
324		free(rp, M_NFSD);
325	}
326	numnfsrvcache = 0;
327}
328