nfs_srvcache.c revision 1.7
1/*	$OpenBSD: nfs_srvcache.c,v 1.7 2001/06/25 03:28:09 csapuntz 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. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the University of
22 *	California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *	@(#)nfs_srvcache.c	8.3 (Berkeley) 3/30/95
40 */
41
42/*
43 * Reference: Chet Juszczak, "Improving the Performance and Correctness
44 *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
45 *		pages 53-63. San Diego, February 1989.
46 */
47#include <sys/param.h>
48#include <sys/vnode.h>
49#include <sys/mount.h>
50#include <sys/kernel.h>
51#include <sys/systm.h>
52#include <sys/proc.h>
53#include <sys/mbuf.h>
54#include <sys/malloc.h>
55#include <sys/socket.h>
56#include <sys/socketvar.h>
57
58#include <netinet/in.h>
59#ifdef ISO
60#include <netiso/iso.h>
61#endif
62#include <nfs/nfsm_subs.h>
63#include <nfs/rpcv2.h>
64#include <nfs/nfsproto.h>
65#include <nfs/nfs.h>
66#include <nfs/nfsrvcache.h>
67#include <nfs/nfs_var.h>
68
69extern struct nfsstats nfsstats;
70extern int nfsv2_procid[NFS_NPROCS];
71long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
72
73#define	NFSRCHASH(xid) \
74	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
75LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
76TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
77u_long nfsrvhash;
78
79#define TRUE	1
80#define	FALSE	0
81
82#define	NETFAMILY(rp) \
83		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
84
85/*
86 * Static array that defines which nfs rpc's are nonidempotent
87 */
88int nonidempotent[NFS_NPROCS] = {
89	FALSE,
90	FALSE,
91	TRUE,
92	FALSE,
93	FALSE,
94	FALSE,
95	FALSE,
96	TRUE,
97	TRUE,
98	TRUE,
99	TRUE,
100	TRUE,
101	TRUE,
102	TRUE,
103	TRUE,
104	TRUE,
105	FALSE,
106	FALSE,
107	FALSE,
108	FALSE,
109	FALSE,
110	FALSE,
111	FALSE,
112	FALSE,
113	FALSE,
114	FALSE,
115};
116
117/* True iff the rpc reply is an nfs status ONLY! */
118static int nfsv2_repstat[NFS_NPROCS] = {
119	FALSE,
120	FALSE,
121	FALSE,
122	FALSE,
123	FALSE,
124	FALSE,
125	FALSE,
126	FALSE,
127	FALSE,
128	FALSE,
129	TRUE,
130	TRUE,
131	TRUE,
132	TRUE,
133	FALSE,
134	TRUE,
135	FALSE,
136	FALSE,
137};
138
139/*
140 * Initialize the server request cache list
141 */
142void
143nfsrv_initcache()
144{
145
146	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash);
147	TAILQ_INIT(&nfsrvlruhead);
148}
149
150/*
151 * Look for the request in the cache
152 * If found then
153 *    return action and optionally reply
154 * else
155 *    insert it in the cache
156 *
157 * The rules are as follows:
158 * - if in progress, return DROP request
159 * - if completed within DELAY of the current time, return DROP it
160 * - if completed a longer time ago return REPLY if the reply was cached or
161 *   return DOIT
162 * Update/add new request at end of lru list
163 */
164int
165nfsrv_getcache(nd, slp, repp)
166	register struct nfsrv_descript *nd;
167	struct nfssvc_sock *slp;
168	struct mbuf **repp;
169{
170	register struct nfsrvcache *rp;
171	struct mbuf *mb;
172	struct sockaddr_in *saddr;
173	caddr_t bpos;
174	int ret;
175
176	/*
177	 * Don't cache recent requests for reliable transport protocols.
178	 * (Maybe we should for the case of a reconnect, but..)
179	 */
180	if (!nd->nd_nam2)
181		return (RC_DOIT);
182loop:
183	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
184	    rp = rp->rc_hash.le_next) {
185	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
186		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
187			if ((rp->rc_flag & RC_LOCKED) != 0) {
188				rp->rc_flag |= RC_WANTED;
189				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
190				goto loop;
191			}
192			rp->rc_flag |= RC_LOCKED;
193			/* If not at end of LRU chain, move it there */
194			if (rp->rc_lru.tqe_next) {
195				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
196				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
197			}
198			if (rp->rc_state == RC_UNUSED)
199				panic("nfsrv cache");
200			if (rp->rc_state == RC_INPROG) {
201				nfsstats.srvcache_inproghits++;
202				ret = RC_DROPIT;
203			} else if (rp->rc_flag & RC_REPSTATUS) {
204				nfsstats.srvcache_nonidemdonehits++;
205				nfs_rephead(0, nd, slp, rp->rc_status,
206				   0, (u_quad_t *)0, repp, &mb, &bpos);
207				ret = RC_REPLY;
208			} else if (rp->rc_flag & RC_REPMBUF) {
209				nfsstats.srvcache_nonidemdonehits++;
210				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
211						M_WAIT);
212				ret = RC_REPLY;
213			} else {
214				nfsstats.srvcache_idemdonehits++;
215				rp->rc_state = RC_INPROG;
216				ret = RC_DOIT;
217			}
218			rp->rc_flag &= ~RC_LOCKED;
219			if (rp->rc_flag & RC_WANTED) {
220				rp->rc_flag &= ~RC_WANTED;
221				wakeup((caddr_t)rp);
222			}
223			return (ret);
224		}
225	}
226	nfsstats.srvcache_misses++;
227	if (numnfsrvcache < desirednfsrvcache) {
228		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
229		    M_NFSD, M_WAITOK);
230		bzero((char *)rp, sizeof *rp);
231		numnfsrvcache++;
232		rp->rc_flag = RC_LOCKED;
233	} else {
234		rp = nfsrvlruhead.tqh_first;
235		while ((rp->rc_flag & RC_LOCKED) != 0) {
236			rp->rc_flag |= RC_WANTED;
237			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
238			rp = nfsrvlruhead.tqh_first;
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		if (rp->rc_flag & RC_NAM)
246			MFREE(rp->rc_nam, mb);
247		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
248	}
249	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
250	rp->rc_state = RC_INPROG;
251	rp->rc_xid = nd->nd_retxid;
252	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
253	switch (saddr->sin_family) {
254	case AF_INET:
255		rp->rc_flag |= RC_INETADDR;
256		rp->rc_inetaddr = saddr->sin_addr.s_addr;
257		break;
258	case AF_ISO:
259	default:
260		rp->rc_flag |= RC_NAM;
261		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
262		break;
263	};
264	rp->rc_proc = nd->nd_procnum;
265	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
266	rp->rc_flag &= ~RC_LOCKED;
267	if (rp->rc_flag & RC_WANTED) {
268		rp->rc_flag &= ~RC_WANTED;
269		wakeup((caddr_t)rp);
270	}
271	return (RC_DOIT);
272}
273
274/*
275 * Update a request cache entry after the rpc has been done
276 */
277void
278nfsrv_updatecache(nd, repvalid, repmbuf)
279	register struct nfsrv_descript *nd;
280	int repvalid;
281	struct mbuf *repmbuf;
282{
283	register struct nfsrvcache *rp;
284
285	if (!nd->nd_nam2)
286		return;
287loop:
288	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
289	    rp = rp->rc_hash.le_next) {
290	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
291		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
292			if ((rp->rc_flag & RC_LOCKED) != 0) {
293				rp->rc_flag |= RC_WANTED;
294				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
295				goto loop;
296			}
297			rp->rc_flag |= RC_LOCKED;
298			rp->rc_state = RC_DONE;
299			/*
300			 * If we have a valid reply update status and save
301			 * the reply for non-idempotent rpc's.
302			 */
303			if (repvalid && nonidempotent[nd->nd_procnum]) {
304				if ((nd->nd_flag & ND_NFSV3) == 0 &&
305				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
306					rp->rc_status = nd->nd_repstat;
307					rp->rc_flag |= RC_REPSTATUS;
308				} else {
309					rp->rc_reply = m_copym(repmbuf,
310						0, M_COPYALL, M_WAIT);
311					rp->rc_flag |= RC_REPMBUF;
312				}
313			}
314			rp->rc_flag &= ~RC_LOCKED;
315			if (rp->rc_flag & RC_WANTED) {
316				rp->rc_flag &= ~RC_WANTED;
317				wakeup((caddr_t)rp);
318			}
319			return;
320		}
321	}
322}
323
324/*
325 * Clean out the cache. Called when the last nfsd terminates.
326 */
327void
328nfsrv_cleancache()
329{
330	register struct nfsrvcache *rp, *nextrp;
331
332	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
333		nextrp = rp->rc_lru.tqe_next;
334		LIST_REMOVE(rp, rc_hash);
335		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
336		free(rp, M_NFSD);
337	}
338	numnfsrvcache = 0;
339}
340