nfs_srvcache.c revision 1.5
1/*	$OpenBSD: nfs_srvcache.c,v 1.5 1996/04/21 22:30:28 deraadt 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/nqnfs.h>
68#include <nfs/nfs_var.h>
69
70extern struct nfsstats nfsstats;
71extern int nfsv2_procid[NFS_NPROCS];
72long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
73
74#define	NFSRCHASH(xid) \
75	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
76LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
77TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
78u_long nfsrvhash;
79
80#define TRUE	1
81#define	FALSE	0
82
83#define	NETFAMILY(rp) \
84		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
85
86/*
87 * Static array that defines which nfs rpc's are nonidempotent
88 */
89int nonidempotent[NFS_NPROCS] = {
90	FALSE,
91	FALSE,
92	TRUE,
93	FALSE,
94	FALSE,
95	FALSE,
96	FALSE,
97	TRUE,
98	TRUE,
99	TRUE,
100	TRUE,
101	TRUE,
102	TRUE,
103	TRUE,
104	TRUE,
105	TRUE,
106	FALSE,
107	FALSE,
108	FALSE,
109	FALSE,
110	FALSE,
111	FALSE,
112	FALSE,
113	FALSE,
114	FALSE,
115	FALSE,
116};
117
118/* True iff the rpc reply is an nfs status ONLY! */
119static int nfsv2_repstat[NFS_NPROCS] = {
120	FALSE,
121	FALSE,
122	FALSE,
123	FALSE,
124	FALSE,
125	FALSE,
126	FALSE,
127	FALSE,
128	FALSE,
129	FALSE,
130	TRUE,
131	TRUE,
132	TRUE,
133	TRUE,
134	FALSE,
135	TRUE,
136	FALSE,
137	FALSE,
138};
139
140/*
141 * Initialize the server request cache list
142 */
143void
144nfsrv_initcache()
145{
146
147	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
148	TAILQ_INIT(&nfsrvlruhead);
149}
150
151/*
152 * Look for the request in the cache
153 * If found then
154 *    return action and optionally reply
155 * else
156 *    insert it in the cache
157 *
158 * The rules are as follows:
159 * - if in progress, return DROP request
160 * - if completed within DELAY of the current time, return DROP it
161 * - if completed a longer time ago return REPLY if the reply was cached or
162 *   return DOIT
163 * Update/add new request at end of lru list
164 */
165int
166nfsrv_getcache(nd, slp, repp)
167	register struct nfsrv_descript *nd;
168	struct nfssvc_sock *slp;
169	struct mbuf **repp;
170{
171	register struct nfsrvcache *rp;
172	struct mbuf *mb;
173	struct sockaddr_in *saddr;
174	caddr_t bpos;
175	int ret;
176
177	/*
178	 * Don't cache recent requests for reliable transport protocols.
179	 * (Maybe we should for the case of a reconnect, but..)
180	 */
181	if (!nd->nd_nam2)
182		return (RC_DOIT);
183loop:
184	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
185	    rp = rp->rc_hash.le_next) {
186	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
187		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
188			if ((rp->rc_flag & RC_LOCKED) != 0) {
189				rp->rc_flag |= RC_WANTED;
190				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
191				goto loop;
192			}
193			rp->rc_flag |= RC_LOCKED;
194			/* If not at end of LRU chain, move it there */
195			if (rp->rc_lru.tqe_next) {
196				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
197				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
198			}
199			if (rp->rc_state == RC_UNUSED)
200				panic("nfsrv cache");
201			if (rp->rc_state == RC_INPROG) {
202				nfsstats.srvcache_inproghits++;
203				ret = RC_DROPIT;
204			} else if (rp->rc_flag & RC_REPSTATUS) {
205				nfsstats.srvcache_nonidemdonehits++;
206				nfs_rephead(0, nd, slp, rp->rc_status,
207				   0, (u_quad_t *)0, repp, &mb, &bpos);
208				ret = RC_REPLY;
209			} else if (rp->rc_flag & RC_REPMBUF) {
210				nfsstats.srvcache_nonidemdonehits++;
211				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
212						M_WAIT);
213				ret = RC_REPLY;
214			} else {
215				nfsstats.srvcache_idemdonehits++;
216				rp->rc_state = RC_INPROG;
217				ret = RC_DOIT;
218			}
219			rp->rc_flag &= ~RC_LOCKED;
220			if (rp->rc_flag & RC_WANTED) {
221				rp->rc_flag &= ~RC_WANTED;
222				wakeup((caddr_t)rp);
223			}
224			return (ret);
225		}
226	}
227	nfsstats.srvcache_misses++;
228	if (numnfsrvcache < desirednfsrvcache) {
229		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
230		    M_NFSD, M_WAITOK);
231		bzero((char *)rp, sizeof *rp);
232		numnfsrvcache++;
233		rp->rc_flag = RC_LOCKED;
234	} else {
235		rp = nfsrvlruhead.tqh_first;
236		while ((rp->rc_flag & RC_LOCKED) != 0) {
237			rp->rc_flag |= RC_WANTED;
238			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
239			rp = nfsrvlruhead.tqh_first;
240		}
241		rp->rc_flag |= RC_LOCKED;
242		LIST_REMOVE(rp, rc_hash);
243		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
244		if (rp->rc_flag & RC_REPMBUF)
245			m_freem(rp->rc_reply);
246		if (rp->rc_flag & RC_NAM)
247			MFREE(rp->rc_nam, mb);
248		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
249	}
250	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
251	rp->rc_state = RC_INPROG;
252	rp->rc_xid = nd->nd_retxid;
253	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
254	switch (saddr->sin_family) {
255	case AF_INET:
256		rp->rc_flag |= RC_INETADDR;
257		rp->rc_inetaddr = saddr->sin_addr.s_addr;
258		break;
259	case AF_ISO:
260	default:
261		rp->rc_flag |= RC_NAM;
262		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
263		break;
264	};
265	rp->rc_proc = nd->nd_procnum;
266	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
267	rp->rc_flag &= ~RC_LOCKED;
268	if (rp->rc_flag & RC_WANTED) {
269		rp->rc_flag &= ~RC_WANTED;
270		wakeup((caddr_t)rp);
271	}
272	return (RC_DOIT);
273}
274
275/*
276 * Update a request cache entry after the rpc has been done
277 */
278void
279nfsrv_updatecache(nd, repvalid, repmbuf)
280	register struct nfsrv_descript *nd;
281	int repvalid;
282	struct mbuf *repmbuf;
283{
284	register struct nfsrvcache *rp;
285
286	if (!nd->nd_nam2)
287		return;
288loop:
289	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
290	    rp = rp->rc_hash.le_next) {
291	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
292		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
293			if ((rp->rc_flag & RC_LOCKED) != 0) {
294				rp->rc_flag |= RC_WANTED;
295				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
296				goto loop;
297			}
298			rp->rc_flag |= RC_LOCKED;
299			rp->rc_state = RC_DONE;
300			/*
301			 * If we have a valid reply update status and save
302			 * the reply for non-idempotent rpc's.
303			 */
304			if (repvalid && nonidempotent[nd->nd_procnum]) {
305				if ((nd->nd_flag & ND_NFSV3) == 0 &&
306				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
307					rp->rc_status = nd->nd_repstat;
308					rp->rc_flag |= RC_REPSTATUS;
309				} else {
310					rp->rc_reply = m_copym(repmbuf,
311						0, M_COPYALL, M_WAIT);
312					rp->rc_flag |= RC_REPMBUF;
313				}
314			}
315			rp->rc_flag &= ~RC_LOCKED;
316			if (rp->rc_flag & RC_WANTED) {
317				rp->rc_flag &= ~RC_WANTED;
318				wakeup((caddr_t)rp);
319			}
320			return;
321		}
322	}
323}
324
325/*
326 * Clean out the cache. Called when the last nfsd terminates.
327 */
328void
329nfsrv_cleancache()
330{
331	register struct nfsrvcache *rp, *nextrp;
332
333	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
334		nextrp = rp->rc_lru.tqe_next;
335		LIST_REMOVE(rp, rc_hash);
336		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
337		free(rp, M_NFSD);
338	}
339	numnfsrvcache = 0;
340}
341