]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_srvcache.c
xnu-344.49.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
26/*
27 * Copyright (c) 1989, 1993
28 * The Regents of the University of California. All rights reserved.
29 *
30 * This code is derived from software contributed to Berkeley by
31 * Rick Macklem at The University of Guelph.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 *
61 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95
62 * FreeBSD-Id: nfs_srvcache.c,v 1.15 1997/10/12 20:25:46 phk Exp $
63 */
64
65#ifndef NFS_NOSERVER
66/*
67 * Reference: Chet Juszczak, "Improving the Performance and Correctness
68 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
69 * pages 53-63. San Diego, February 1989.
70 */
71#include <sys/param.h>
72#include <sys/vnode.h>
73#include <sys/mount.h>
74#include <sys/kernel.h>
75#include <sys/systm.h>
76#include <sys/proc.h>
77#include <sys/mbuf.h>
78#include <sys/malloc.h>
79#include <sys/socket.h>
80#include <sys/socketvar.h> /* for dup_sockaddr */
81
82#include <netinet/in.h>
83#if ISO
84#include <netiso/iso.h>
85#endif
86#include <nfs/rpcv2.h>
87#include <nfs/nfsproto.h>
88#include <nfs/nfs.h>
89#include <nfs/nfsrvcache.h>
90
91extern struct nfsstats nfsstats;
92extern int nfsv2_procid[NFS_NPROCS];
93long numnfsrvcache;
94static long desirednfsrvcache = NFSRVCACHESIZ;
95
96#define NFSRCHASH(xid) \
97 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
98LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
99TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
100u_long nfsrvhash;
101
102#define TRUE 1
103#define FALSE 0
104
105#define NETFAMILY(rp) \
106 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
107
108/*
109 * Static array that defines which nfs rpc's are nonidempotent
110 */
111static int nonidempotent[NFS_NPROCS] = {
112 FALSE,
113 FALSE,
114 TRUE,
115 FALSE,
116 FALSE,
117 FALSE,
118 FALSE,
119 TRUE,
120 TRUE,
121 TRUE,
122 TRUE,
123 TRUE,
124 TRUE,
125 TRUE,
126 TRUE,
127 TRUE,
128 FALSE,
129 FALSE,
130 FALSE,
131 FALSE,
132 FALSE,
133 FALSE,
134 FALSE,
135 FALSE,
136 FALSE,
137 FALSE,
138};
139
140/* True iff the rpc reply is an nfs status ONLY! */
141static int nfsv2_repstat[NFS_NPROCS] = {
142 FALSE,
143 FALSE,
144 FALSE,
145 FALSE,
146 FALSE,
147 FALSE,
148 FALSE,
149 FALSE,
150 FALSE,
151 FALSE,
152 TRUE,
153 TRUE,
154 TRUE,
155 TRUE,
156 FALSE,
157 TRUE,
158 FALSE,
159 FALSE,
160};
161
162/*
163 * Initialize the server request cache list
164 */
165void
166nfsrv_initcache()
167{
168
169 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
170 TAILQ_INIT(&nfsrvlruhead);
171}
172
173/*
174 * Look for the request in the cache
175 * If found then
176 * return action and optionally reply
177 * else
178 * insert it in the cache
179 *
180 * The rules are as follows:
181 * - if in progress, return DROP request
182 * - if completed within DELAY of the current time, return DROP it
183 * - if completed a longer time ago return REPLY if the reply was cached or
184 * return DOIT
185 * Update/add new request at end of lru list
186 */
187int
188nfsrv_getcache(nd, slp, repp)
189 register struct nfsrv_descript *nd;
190 struct nfssvc_sock *slp;
191 struct mbuf **repp;
192{
193 register struct nfsrvcache *rp;
194 struct mbuf *mb;
195 struct sockaddr_in *saddr;
196 caddr_t bpos;
197 int ret;
198
199 /*
200 * Don't cache recent requests for reliable transport protocols.
201 * (Maybe we should for the case of a reconnect, but..)
202 */
203 if (!nd->nd_nam2)
204 return (RC_DOIT);
205loop:
206 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
207 rp = rp->rc_hash.le_next) {
208 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
209 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
210 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
211 if ((rp->rc_flag & RC_LOCKED) != 0) {
212 rp->rc_flag |= RC_WANTED;
213 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
214 goto loop;
215 }
216 rp->rc_flag |= RC_LOCKED;
217 /* If not at end of LRU chain, move it there */
218 if (rp->rc_lru.tqe_next) {
219 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
220 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
221 }
222 if (rp->rc_state == RC_UNUSED)
223 panic("nfsrv cache");
224 if (rp->rc_state == RC_INPROG) {
225 nfsstats.srvcache_inproghits++;
226 ret = RC_DROPIT;
227 } else if (rp->rc_flag & RC_REPSTATUS) {
228 nfsstats.srvcache_nonidemdonehits++;
229 nfs_rephead(0, nd, slp, rp->rc_status,
230 0, (u_quad_t *)0, repp, &mb, &bpos);
231 ret = RC_REPLY;
232 } else if (rp->rc_flag & RC_REPMBUF) {
233 nfsstats.srvcache_nonidemdonehits++;
234 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
235 M_WAIT);
236 ret = RC_REPLY;
237 } else {
238 nfsstats.srvcache_idemdonehits++;
239 rp->rc_state = RC_INPROG;
240 ret = RC_DOIT;
241 }
242 rp->rc_flag &= ~RC_LOCKED;
243 if (rp->rc_flag & RC_WANTED) {
244 rp->rc_flag &= ~RC_WANTED;
245 wakeup((caddr_t)rp);
246 }
247 return (ret);
248 }
249 }
250 nfsstats.srvcache_misses++;
251 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
252 if (numnfsrvcache < desirednfsrvcache) {
253 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
254 bzero((char *)rp, sizeof *rp);
255 numnfsrvcache++;
256 rp->rc_flag = RC_LOCKED;
257 } else {
258 rp = nfsrvlruhead.tqh_first;
259 while ((rp->rc_flag & RC_LOCKED) != 0) {
260 rp->rc_flag |= RC_WANTED;
261 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
262 rp = nfsrvlruhead.tqh_first;
263 }
264 rp->rc_flag |= RC_LOCKED;
265 LIST_REMOVE(rp, rc_hash);
266 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
267 if (rp->rc_flag & RC_REPMBUF)
268 m_freem(rp->rc_reply);
269 if (rp->rc_flag & RC_NAM)
270 MFREE(rp->rc_nam, mb);
271 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
272 }
273 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
274 rp->rc_state = RC_INPROG;
275 rp->rc_xid = nd->nd_retxid;
276 saddr = mtod(nd->nd_nam, struct sockaddr_in *);
277 switch (saddr->sin_family) {
278 case AF_INET:
279 rp->rc_flag |= RC_INETADDR;
280 rp->rc_inetaddr = saddr->sin_addr.s_addr;
281 break;
282 case AF_ISO:
283 default:
284 rp->rc_flag |= RC_NAM;
285 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
286 break;
287 };
288 rp->rc_proc = nd->nd_procnum;
289 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
290 rp->rc_flag &= ~RC_LOCKED;
291 if (rp->rc_flag & RC_WANTED) {
292 rp->rc_flag &= ~RC_WANTED;
293 wakeup((caddr_t)rp);
294 }
295 return (RC_DOIT);
296}
297
298/*
299 * Update a request cache entry after the rpc has been done
300 */
301void
302nfsrv_updatecache(nd, repvalid, repmbuf)
303 register struct nfsrv_descript *nd;
304 int repvalid;
305 struct mbuf *repmbuf;
306{
307 register struct nfsrvcache *rp;
308
309 if (!nd->nd_nam2)
310 return;
311loop:
312 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
313 rp = rp->rc_hash.le_next) {
314 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
315 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
316 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
317 if ((rp->rc_flag & RC_LOCKED) != 0) {
318 rp->rc_flag |= RC_WANTED;
319 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
320 goto loop;
321 }
322 rp->rc_flag |= RC_LOCKED;
323 rp->rc_state = RC_DONE;
324 /*
325 * If we have a valid reply update status and save
326 * the reply for non-idempotent rpc's.
327 */
328 if (repvalid && nonidempotent[nd->nd_procnum]) {
329 if ((nd->nd_flag & ND_NFSV3) == 0 &&
330 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
331 rp->rc_status = nd->nd_repstat;
332 rp->rc_flag |= RC_REPSTATUS;
333 } else {
334 rp->rc_reply = m_copym(repmbuf,
335 0, M_COPYALL, M_WAIT);
336 rp->rc_flag |= RC_REPMBUF;
337 }
338 }
339 rp->rc_flag &= ~RC_LOCKED;
340 if (rp->rc_flag & RC_WANTED) {
341 rp->rc_flag &= ~RC_WANTED;
342 wakeup((caddr_t)rp);
343 }
344 return;
345 }
346 }
347 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
348}
349
350/*
351 * Clean out the cache. Called when the last nfsd terminates.
352 */
353void
354nfsrv_cleancache()
355{
356 register struct nfsrvcache *rp, *nextrp;
357
358 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
359 nextrp = rp->rc_lru.tqe_next;
360 LIST_REMOVE(rp, rc_hash);
361 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
362 _FREE(rp, M_NFSD);
363 }
364 numnfsrvcache = 0;
365}
366
367#endif /* NFS_NOSERVER */