nfs_srvcache.c revision 1.15
1/*	$OpenBSD: nfs_srvcache.c,v 1.15 2007/10/29 11:16:49 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/mount.h>
45#include <sys/kernel.h>
46#include <sys/systm.h>
47#include <sys/proc.h>
48#include <sys/mbuf.h>
49#include <sys/malloc.h>
50#include <sys/socket.h>
51
52#include <netinet/in.h>
53#include <nfs/rpcv2.h>
54#include <nfs/nfsproto.h>
55#include <nfs/nfs.h>
56#include <nfs/nfsrvcache.h>
57#include <nfs/nfs_var.h>
58
59extern struct nfsstats nfsstats;
60extern int nfsv2_procid[NFS_NPROCS];
61long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
62
63#define	NFSRCHASH(xid)	\
64	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
65LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
66TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
67u_long nfsrvhash;
68
69#define	TRUE	1
70#define	FALSE	0
71
72#define	NETFAMILY(rp)	\
73	(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_UNSPEC)
74
75/* Array that defines which nfs rpc's are nonidempotent */
76int 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! */
106int 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/* Initialize the server request cache list */
128void
129nfsrv_initcache(void)
130{
131
132	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash);
133	TAILQ_INIT(&nfsrvlruhead);
134}
135
136/*
137 * Look for the request in the cache
138 * If found then
139 *    return action and optionally reply
140 * else
141 *    insert it in the cache
142 *
143 * The rules are as follows:
144 * - if in progress, return DROP request
145 * - if completed within DELAY of the current time, return DROP it
146 * - if completed a longer time ago return REPLY if the reply was cached or
147 *   return DOIT
148 * Update/add new request at end of lru list
149 */
150int
151nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
152    struct mbuf **repp)
153{
154	struct nfsrvcache *rp;
155	struct mbuf *mb;
156	struct sockaddr_in *saddr;
157	caddr_t bpos;
158	int ret;
159
160	/*
161	 * Don't cache recent requests for reliable transport protocols.
162	 * (Maybe we should for the case of a reconnect, but..)
163	 */
164	if (!nd->nd_nam2)
165		return (RC_DOIT);
166loop:
167	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
168	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
169		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
170			if ((rp->rc_flag & RC_LOCKED) != 0) {
171				rp->rc_flag |= RC_WANTED;
172				tsleep(rp, PZERO-1, "nfsrc", 0);
173				goto loop;
174			}
175			rp->rc_flag |= RC_LOCKED;
176			/* If not at end of LRU chain, move it there */
177			if (TAILQ_NEXT(rp, rc_lru)) {
178				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
179				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
180			}
181			if (rp->rc_state == RC_UNUSED)
182				panic("nfsrv cache");
183			if (rp->rc_state == RC_INPROG) {
184				nfsstats.srvcache_inproghits++;
185				ret = RC_DROPIT;
186			} else if (rp->rc_flag & RC_REPSTATUS) {
187				nfsstats.srvcache_nonidemdonehits++;
188				nfs_rephead(0, nd, slp, rp->rc_status,
189				   repp, &mb, &bpos);
190				ret = RC_REPLY;
191			} else if (rp->rc_flag & RC_REPMBUF) {
192				nfsstats.srvcache_nonidemdonehits++;
193				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
194						M_WAIT);
195				ret = RC_REPLY;
196			} else {
197				nfsstats.srvcache_idemdonehits++;
198				rp->rc_state = RC_INPROG;
199				ret = RC_DOIT;
200			}
201			rp->rc_flag &= ~RC_LOCKED;
202			if (rp->rc_flag & RC_WANTED) {
203				rp->rc_flag &= ~RC_WANTED;
204				wakeup(rp);
205			}
206			return (ret);
207		}
208	}
209	nfsstats.srvcache_misses++;
210	if (numnfsrvcache < desirednfsrvcache) {
211		rp = malloc(sizeof(*rp), M_NFSD, M_WAITOK|M_ZERO);
212		numnfsrvcache++;
213		rp->rc_flag = RC_LOCKED;
214	} else {
215		rp = TAILQ_FIRST(&nfsrvlruhead);
216		while ((rp->rc_flag & RC_LOCKED) != 0) {
217			rp->rc_flag |= RC_WANTED;
218			tsleep(rp, PZERO-1, "nfsrc", 0);
219			rp = TAILQ_FIRST(&nfsrvlruhead);
220		}
221		rp->rc_flag |= RC_LOCKED;
222		LIST_REMOVE(rp, rc_hash);
223		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
224		if (rp->rc_flag & RC_REPMBUF)
225			m_freem(rp->rc_reply);
226		if (rp->rc_flag & RC_NAM)
227			MFREE(rp->rc_nam, mb);
228		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
229	}
230	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
231	rp->rc_state = RC_INPROG;
232	rp->rc_xid = nd->nd_retxid;
233	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
234	switch (saddr->sin_family) {
235	case AF_INET:
236		rp->rc_flag |= RC_INETADDR;
237		rp->rc_inetaddr = saddr->sin_addr.s_addr;
238		break;
239	default:
240		rp->rc_flag |= RC_NAM;
241		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
242		break;
243	};
244	rp->rc_proc = nd->nd_procnum;
245	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
246	rp->rc_flag &= ~RC_LOCKED;
247	if (rp->rc_flag & RC_WANTED) {
248		rp->rc_flag &= ~RC_WANTED;
249		wakeup(rp);
250	}
251	return (RC_DOIT);
252}
253
254/* Update a request cache entry after the rpc has been done */
255void
256nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid,
257    struct mbuf *repmbuf)
258{
259	struct nfsrvcache *rp;
260
261	if (!nd->nd_nam2)
262		return;
263loop:
264	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
265	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
266		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
267			if ((rp->rc_flag & RC_LOCKED) != 0) {
268				rp->rc_flag |= RC_WANTED;
269				tsleep(rp, PZERO-1, "nfsrc", 0);
270				goto loop;
271			}
272			rp->rc_flag |= RC_LOCKED;
273			rp->rc_state = RC_DONE;
274			/*
275			 * If we have a valid reply update status and save
276			 * the reply for non-idempotent rpc's.
277			 */
278			if (repvalid && nonidempotent[nd->nd_procnum]) {
279				if ((nd->nd_flag & ND_NFSV3) == 0 &&
280				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
281					rp->rc_status = nd->nd_repstat;
282					rp->rc_flag |= RC_REPSTATUS;
283				} else {
284					rp->rc_reply = m_copym(repmbuf,
285						0, M_COPYALL, M_WAIT);
286					rp->rc_flag |= RC_REPMBUF;
287				}
288			}
289			rp->rc_flag &= ~RC_LOCKED;
290			if (rp->rc_flag & RC_WANTED) {
291				rp->rc_flag &= ~RC_WANTED;
292				wakeup(rp);
293			}
294			return;
295		}
296	}
297}
298
299/* Clean out the cache. Called when the last nfsd terminates. */
300void
301nfsrv_cleancache(void)
302{
303	struct nfsrvcache *rp, *nextrp;
304
305	for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != NULL; rp = nextrp) {
306		nextrp = TAILQ_NEXT(rp, rc_lru);
307		LIST_REMOVE(rp, rc_hash);
308		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
309		free(rp, M_NFSD);
310	}
311	numnfsrvcache = 0;
312}
313